rnd-20070917-2-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_HIGHSCORE                    30
165 #define GAME_PANEL_TIME                         31
166 #define GAME_PANEL_TIME_HH                      32
167 #define GAME_PANEL_TIME_MM                      33
168 #define GAME_PANEL_TIME_SS                      34
169 #define GAME_PANEL_SHIELD_NORMAL                35
170 #define GAME_PANEL_SHIELD_NORMAL_TIME           36
171 #define GAME_PANEL_SHIELD_DEADLY                37
172 #define GAME_PANEL_SHIELD_DEADLY_TIME           38
173 #define GAME_PANEL_EXIT                         39
174 #define GAME_PANEL_EMC_MAGIC_BALL               40
175 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        41
176 #define GAME_PANEL_LIGHT_SWITCH                 42
177 #define GAME_PANEL_LIGHT_SWITCH_TIME            43
178 #define GAME_PANEL_TIMEGATE_SWITCH              44
179 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         45
180 #define GAME_PANEL_SWITCHGATE_SWITCH            46
181 #define GAME_PANEL_EMC_LENSES                   47
182 #define GAME_PANEL_EMC_LENSES_TIME              48
183 #define GAME_PANEL_EMC_MAGNIFIER                49
184 #define GAME_PANEL_EMC_MAGNIFIER_TIME           50
185 #define GAME_PANEL_BALLOON_SWITCH               51
186 #define GAME_PANEL_DYNABOMB_NUMBER              52
187 #define GAME_PANEL_DYNABOMB_SIZE                53
188 #define GAME_PANEL_DYNABOMB_POWER               54
189 #define GAME_PANEL_PENGUINS                     55
190 #define GAME_PANEL_SOKOBAN_OBJECTS              56
191 #define GAME_PANEL_SOKOBAN_FIELDS               57
192 #define GAME_PANEL_ROBOT_WHEEL                  58
193 #define GAME_PANEL_CONVEYOR_BELT_1              59
194 #define GAME_PANEL_CONVEYOR_BELT_2              60
195 #define GAME_PANEL_CONVEYOR_BELT_3              61
196 #define GAME_PANEL_CONVEYOR_BELT_4              62
197 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       63
198 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       64
199 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       65
200 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       66
201 #define GAME_PANEL_MAGIC_WALL                   67
202 #define GAME_PANEL_MAGIC_WALL_TIME              68
203 #define GAME_PANEL_GRAVITY_STATE                69
204 #define GAME_PANEL_GRAPHIC_1                    70
205 #define GAME_PANEL_GRAPHIC_2                    71
206 #define GAME_PANEL_GRAPHIC_3                    72
207 #define GAME_PANEL_GRAPHIC_4                    73
208 #define GAME_PANEL_GRAPHIC_5                    74
209 #define GAME_PANEL_GRAPHIC_6                    75
210 #define GAME_PANEL_GRAPHIC_7                    76
211 #define GAME_PANEL_GRAPHIC_8                    77
212 #define GAME_PANEL_ELEMENT_1                    78
213 #define GAME_PANEL_ELEMENT_2                    79
214 #define GAME_PANEL_ELEMENT_3                    80
215 #define GAME_PANEL_ELEMENT_4                    81
216 #define GAME_PANEL_ELEMENT_5                    82
217 #define GAME_PANEL_ELEMENT_6                    83
218 #define GAME_PANEL_ELEMENT_7                    84
219 #define GAME_PANEL_ELEMENT_8                    85
220 #define GAME_PANEL_ELEMENT_COUNT_1              86
221 #define GAME_PANEL_ELEMENT_COUNT_2              87
222 #define GAME_PANEL_ELEMENT_COUNT_3              88
223 #define GAME_PANEL_ELEMENT_COUNT_4              89
224 #define GAME_PANEL_ELEMENT_COUNT_5              90
225 #define GAME_PANEL_ELEMENT_COUNT_6              91
226 #define GAME_PANEL_ELEMENT_COUNT_7              92
227 #define GAME_PANEL_ELEMENT_COUNT_8              93
228 #define GAME_PANEL_CE_SCORE_1                   94
229 #define GAME_PANEL_CE_SCORE_2                   95
230 #define GAME_PANEL_CE_SCORE_3                   96
231 #define GAME_PANEL_CE_SCORE_4                   97
232 #define GAME_PANEL_CE_SCORE_5                   98
233 #define GAME_PANEL_CE_SCORE_6                   99
234 #define GAME_PANEL_CE_SCORE_7                   100
235 #define GAME_PANEL_CE_SCORE_8                   101
236 #define GAME_PANEL_CE_SCORE_1_ELEMENT           102
237 #define GAME_PANEL_CE_SCORE_2_ELEMENT           103
238 #define GAME_PANEL_CE_SCORE_3_ELEMENT           104
239 #define GAME_PANEL_CE_SCORE_4_ELEMENT           105
240 #define GAME_PANEL_CE_SCORE_5_ELEMENT           106
241 #define GAME_PANEL_CE_SCORE_6_ELEMENT           107
242 #define GAME_PANEL_CE_SCORE_7_ELEMENT           108
243 #define GAME_PANEL_CE_SCORE_8_ELEMENT           109
244 #define GAME_PANEL_PLAYER_NAME                  110
245 #define GAME_PANEL_LEVEL_NAME                   111
246 #define GAME_PANEL_LEVEL_AUTHOR                 112
247
248 #define NUM_GAME_PANEL_CONTROLS                 113
249
250 struct GamePanelOrderInfo
251 {
252   int nr;
253   int sort_priority;
254 };
255
256 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
257
258 struct GamePanelControlInfo
259 {
260   int nr;
261
262   struct TextPosInfo *pos;
263   int type;
264
265   int value, last_value;
266   int frame, last_frame;
267   int gfx_frame;
268   int gfx_random;
269 };
270
271 static struct GamePanelControlInfo game_panel_controls[] =
272 {
273   {
274     GAME_PANEL_LEVEL_NUMBER,
275     &game.panel.level_number,
276     TYPE_INTEGER,
277   },
278   {
279     GAME_PANEL_GEMS,
280     &game.panel.gems,
281     TYPE_INTEGER,
282   },
283   {
284     GAME_PANEL_INVENTORY_COUNT,
285     &game.panel.inventory_count,
286     TYPE_INTEGER,
287   },
288   {
289     GAME_PANEL_INVENTORY_FIRST_1,
290     &game.panel.inventory_first[0],
291     TYPE_ELEMENT,
292   },
293   {
294     GAME_PANEL_INVENTORY_FIRST_2,
295     &game.panel.inventory_first[1],
296     TYPE_ELEMENT,
297   },
298   {
299     GAME_PANEL_INVENTORY_FIRST_3,
300     &game.panel.inventory_first[2],
301     TYPE_ELEMENT,
302   },
303   {
304     GAME_PANEL_INVENTORY_FIRST_4,
305     &game.panel.inventory_first[3],
306     TYPE_ELEMENT,
307   },
308   {
309     GAME_PANEL_INVENTORY_FIRST_5,
310     &game.panel.inventory_first[4],
311     TYPE_ELEMENT,
312   },
313   {
314     GAME_PANEL_INVENTORY_FIRST_6,
315     &game.panel.inventory_first[5],
316     TYPE_ELEMENT,
317   },
318   {
319     GAME_PANEL_INVENTORY_FIRST_7,
320     &game.panel.inventory_first[6],
321     TYPE_ELEMENT,
322   },
323   {
324     GAME_PANEL_INVENTORY_FIRST_8,
325     &game.panel.inventory_first[7],
326     TYPE_ELEMENT,
327   },
328   {
329     GAME_PANEL_INVENTORY_LAST_1,
330     &game.panel.inventory_last[0],
331     TYPE_ELEMENT,
332   },
333   {
334     GAME_PANEL_INVENTORY_LAST_2,
335     &game.panel.inventory_last[1],
336     TYPE_ELEMENT,
337   },
338   {
339     GAME_PANEL_INVENTORY_LAST_3,
340     &game.panel.inventory_last[2],
341     TYPE_ELEMENT,
342   },
343   {
344     GAME_PANEL_INVENTORY_LAST_4,
345     &game.panel.inventory_last[3],
346     TYPE_ELEMENT,
347   },
348   {
349     GAME_PANEL_INVENTORY_LAST_5,
350     &game.panel.inventory_last[4],
351     TYPE_ELEMENT,
352   },
353   {
354     GAME_PANEL_INVENTORY_LAST_6,
355     &game.panel.inventory_last[5],
356     TYPE_ELEMENT,
357   },
358   {
359     GAME_PANEL_INVENTORY_LAST_7,
360     &game.panel.inventory_last[6],
361     TYPE_ELEMENT,
362   },
363   {
364     GAME_PANEL_INVENTORY_LAST_8,
365     &game.panel.inventory_last[7],
366     TYPE_ELEMENT,
367   },
368   {
369     GAME_PANEL_KEY_1,
370     &game.panel.key[0],
371     TYPE_ELEMENT,
372   },
373   {
374     GAME_PANEL_KEY_2,
375     &game.panel.key[1],
376     TYPE_ELEMENT,
377   },
378   {
379     GAME_PANEL_KEY_3,
380     &game.panel.key[2],
381     TYPE_ELEMENT,
382   },
383   {
384     GAME_PANEL_KEY_4,
385     &game.panel.key[3],
386     TYPE_ELEMENT,
387   },
388   {
389     GAME_PANEL_KEY_5,
390     &game.panel.key[4],
391     TYPE_ELEMENT,
392   },
393   {
394     GAME_PANEL_KEY_6,
395     &game.panel.key[5],
396     TYPE_ELEMENT,
397   },
398   {
399     GAME_PANEL_KEY_7,
400     &game.panel.key[6],
401     TYPE_ELEMENT,
402   },
403   {
404     GAME_PANEL_KEY_8,
405     &game.panel.key[7],
406     TYPE_ELEMENT,
407   },
408   {
409     GAME_PANEL_KEY_WHITE,
410     &game.panel.key_white,
411     TYPE_ELEMENT,
412   },
413   {
414     GAME_PANEL_KEY_WHITE_COUNT,
415     &game.panel.key_white_count,
416     TYPE_INTEGER,
417   },
418   {
419     GAME_PANEL_SCORE,
420     &game.panel.score,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HIGHSCORE,
425     &game.panel.highscore,
426     TYPE_INTEGER,
427   },
428   {
429     GAME_PANEL_TIME,
430     &game.panel.time,
431     TYPE_INTEGER,
432   },
433   {
434     GAME_PANEL_TIME_HH,
435     &game.panel.time_hh,
436     TYPE_INTEGER,
437   },
438   {
439     GAME_PANEL_TIME_MM,
440     &game.panel.time_mm,
441     TYPE_INTEGER,
442   },
443   {
444     GAME_PANEL_TIME_SS,
445     &game.panel.time_ss,
446     TYPE_INTEGER,
447   },
448   {
449     GAME_PANEL_SHIELD_NORMAL,
450     &game.panel.shield_normal,
451     TYPE_ELEMENT,
452   },
453   {
454     GAME_PANEL_SHIELD_NORMAL_TIME,
455     &game.panel.shield_normal_time,
456     TYPE_INTEGER,
457   },
458   {
459     GAME_PANEL_SHIELD_DEADLY,
460     &game.panel.shield_deadly,
461     TYPE_ELEMENT,
462   },
463   {
464     GAME_PANEL_SHIELD_DEADLY_TIME,
465     &game.panel.shield_deadly_time,
466     TYPE_INTEGER,
467   },
468   {
469     GAME_PANEL_EXIT,
470     &game.panel.exit,
471     TYPE_ELEMENT,
472   },
473   {
474     GAME_PANEL_EMC_MAGIC_BALL,
475     &game.panel.emc_magic_ball,
476     TYPE_ELEMENT,
477   },
478   {
479     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
480     &game.panel.emc_magic_ball_switch,
481     TYPE_ELEMENT,
482   },
483   {
484     GAME_PANEL_LIGHT_SWITCH,
485     &game.panel.light_switch,
486     TYPE_ELEMENT,
487   },
488   {
489     GAME_PANEL_LIGHT_SWITCH_TIME,
490     &game.panel.light_switch_time,
491     TYPE_INTEGER,
492   },
493   {
494     GAME_PANEL_TIMEGATE_SWITCH,
495     &game.panel.timegate_switch,
496     TYPE_ELEMENT,
497   },
498   {
499     GAME_PANEL_TIMEGATE_SWITCH_TIME,
500     &game.panel.timegate_switch_time,
501     TYPE_INTEGER,
502   },
503   {
504     GAME_PANEL_SWITCHGATE_SWITCH,
505     &game.panel.switchgate_switch,
506     TYPE_ELEMENT,
507   },
508   {
509     GAME_PANEL_EMC_LENSES,
510     &game.panel.emc_lenses,
511     TYPE_ELEMENT,
512   },
513   {
514     GAME_PANEL_EMC_LENSES_TIME,
515     &game.panel.emc_lenses_time,
516     TYPE_INTEGER,
517   },
518   {
519     GAME_PANEL_EMC_MAGNIFIER,
520     &game.panel.emc_magnifier,
521     TYPE_ELEMENT,
522   },
523   {
524     GAME_PANEL_EMC_MAGNIFIER_TIME,
525     &game.panel.emc_magnifier_time,
526     TYPE_INTEGER,
527   },
528   {
529     GAME_PANEL_BALLOON_SWITCH,
530     &game.panel.balloon_switch,
531     TYPE_ELEMENT,
532   },
533   {
534     GAME_PANEL_DYNABOMB_NUMBER,
535     &game.panel.dynabomb_number,
536     TYPE_INTEGER,
537   },
538   {
539     GAME_PANEL_DYNABOMB_SIZE,
540     &game.panel.dynabomb_size,
541     TYPE_INTEGER,
542   },
543   {
544     GAME_PANEL_DYNABOMB_POWER,
545     &game.panel.dynabomb_power,
546     TYPE_ELEMENT,
547   },
548   {
549     GAME_PANEL_PENGUINS,
550     &game.panel.penguins,
551     TYPE_INTEGER,
552   },
553   {
554     GAME_PANEL_SOKOBAN_OBJECTS,
555     &game.panel.sokoban_objects,
556     TYPE_INTEGER,
557   },
558   {
559     GAME_PANEL_SOKOBAN_FIELDS,
560     &game.panel.sokoban_fields,
561     TYPE_INTEGER,
562   },
563   {
564     GAME_PANEL_ROBOT_WHEEL,
565     &game.panel.robot_wheel,
566     TYPE_ELEMENT,
567   },
568   {
569     GAME_PANEL_CONVEYOR_BELT_1,
570     &game.panel.conveyor_belt[0],
571     TYPE_ELEMENT,
572   },
573   {
574     GAME_PANEL_CONVEYOR_BELT_2,
575     &game.panel.conveyor_belt[1],
576     TYPE_ELEMENT,
577   },
578   {
579     GAME_PANEL_CONVEYOR_BELT_3,
580     &game.panel.conveyor_belt[2],
581     TYPE_ELEMENT,
582   },
583   {
584     GAME_PANEL_CONVEYOR_BELT_4,
585     &game.panel.conveyor_belt[3],
586     TYPE_ELEMENT,
587   },
588   {
589     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
590     &game.panel.conveyor_belt_switch[0],
591     TYPE_ELEMENT,
592   },
593   {
594     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
595     &game.panel.conveyor_belt_switch[1],
596     TYPE_ELEMENT,
597   },
598   {
599     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
600     &game.panel.conveyor_belt_switch[2],
601     TYPE_ELEMENT,
602   },
603   {
604     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
605     &game.panel.conveyor_belt_switch[3],
606     TYPE_ELEMENT,
607   },
608   {
609     GAME_PANEL_MAGIC_WALL,
610     &game.panel.magic_wall,
611     TYPE_ELEMENT,
612   },
613   {
614     GAME_PANEL_MAGIC_WALL_TIME,
615     &game.panel.magic_wall_time,
616     TYPE_INTEGER,
617   },
618   {
619     GAME_PANEL_GRAVITY_STATE,
620     &game.panel.gravity_state,
621     TYPE_STRING,
622   },
623   {
624     GAME_PANEL_GRAPHIC_1,
625     &game.panel.graphic[0],
626     TYPE_ELEMENT,
627   },
628   {
629     GAME_PANEL_GRAPHIC_2,
630     &game.panel.graphic[1],
631     TYPE_ELEMENT,
632   },
633   {
634     GAME_PANEL_GRAPHIC_3,
635     &game.panel.graphic[2],
636     TYPE_ELEMENT,
637   },
638   {
639     GAME_PANEL_GRAPHIC_4,
640     &game.panel.graphic[3],
641     TYPE_ELEMENT,
642   },
643   {
644     GAME_PANEL_GRAPHIC_5,
645     &game.panel.graphic[4],
646     TYPE_ELEMENT,
647   },
648   {
649     GAME_PANEL_GRAPHIC_6,
650     &game.panel.graphic[5],
651     TYPE_ELEMENT,
652   },
653   {
654     GAME_PANEL_GRAPHIC_7,
655     &game.panel.graphic[6],
656     TYPE_ELEMENT,
657   },
658   {
659     GAME_PANEL_GRAPHIC_8,
660     &game.panel.graphic[7],
661     TYPE_ELEMENT,
662   },
663   {
664     GAME_PANEL_ELEMENT_1,
665     &game.panel.element[0],
666     TYPE_ELEMENT,
667   },
668   {
669     GAME_PANEL_ELEMENT_2,
670     &game.panel.element[1],
671     TYPE_ELEMENT,
672   },
673   {
674     GAME_PANEL_ELEMENT_3,
675     &game.panel.element[2],
676     TYPE_ELEMENT,
677   },
678   {
679     GAME_PANEL_ELEMENT_4,
680     &game.panel.element[3],
681     TYPE_ELEMENT,
682   },
683   {
684     GAME_PANEL_ELEMENT_5,
685     &game.panel.element[4],
686     TYPE_ELEMENT,
687   },
688   {
689     GAME_PANEL_ELEMENT_6,
690     &game.panel.element[5],
691     TYPE_ELEMENT,
692   },
693   {
694     GAME_PANEL_ELEMENT_7,
695     &game.panel.element[6],
696     TYPE_ELEMENT,
697   },
698   {
699     GAME_PANEL_ELEMENT_8,
700     &game.panel.element[7],
701     TYPE_ELEMENT,
702   },
703   {
704     GAME_PANEL_ELEMENT_COUNT_1,
705     &game.panel.element_count[0],
706     TYPE_INTEGER,
707   },
708   {
709     GAME_PANEL_ELEMENT_COUNT_2,
710     &game.panel.element_count[1],
711     TYPE_INTEGER,
712   },
713   {
714     GAME_PANEL_ELEMENT_COUNT_3,
715     &game.panel.element_count[2],
716     TYPE_INTEGER,
717   },
718   {
719     GAME_PANEL_ELEMENT_COUNT_4,
720     &game.panel.element_count[3],
721     TYPE_INTEGER,
722   },
723   {
724     GAME_PANEL_ELEMENT_COUNT_5,
725     &game.panel.element_count[4],
726     TYPE_INTEGER,
727   },
728   {
729     GAME_PANEL_ELEMENT_COUNT_6,
730     &game.panel.element_count[5],
731     TYPE_INTEGER,
732   },
733   {
734     GAME_PANEL_ELEMENT_COUNT_7,
735     &game.panel.element_count[6],
736     TYPE_INTEGER,
737   },
738   {
739     GAME_PANEL_ELEMENT_COUNT_8,
740     &game.panel.element_count[7],
741     TYPE_INTEGER,
742   },
743   {
744     GAME_PANEL_CE_SCORE_1,
745     &game.panel.ce_score[0],
746     TYPE_INTEGER,
747   },
748   {
749     GAME_PANEL_CE_SCORE_2,
750     &game.panel.ce_score[1],
751     TYPE_INTEGER,
752   },
753   {
754     GAME_PANEL_CE_SCORE_3,
755     &game.panel.ce_score[2],
756     TYPE_INTEGER,
757   },
758   {
759     GAME_PANEL_CE_SCORE_4,
760     &game.panel.ce_score[3],
761     TYPE_INTEGER,
762   },
763   {
764     GAME_PANEL_CE_SCORE_5,
765     &game.panel.ce_score[4],
766     TYPE_INTEGER,
767   },
768   {
769     GAME_PANEL_CE_SCORE_6,
770     &game.panel.ce_score[5],
771     TYPE_INTEGER,
772   },
773   {
774     GAME_PANEL_CE_SCORE_7,
775     &game.panel.ce_score[6],
776     TYPE_INTEGER,
777   },
778   {
779     GAME_PANEL_CE_SCORE_8,
780     &game.panel.ce_score[7],
781     TYPE_INTEGER,
782   },
783   {
784     GAME_PANEL_CE_SCORE_1_ELEMENT,
785     &game.panel.ce_score_element[0],
786     TYPE_ELEMENT,
787   },
788   {
789     GAME_PANEL_CE_SCORE_2_ELEMENT,
790     &game.panel.ce_score_element[1],
791     TYPE_ELEMENT,
792   },
793   {
794     GAME_PANEL_CE_SCORE_3_ELEMENT,
795     &game.panel.ce_score_element[2],
796     TYPE_ELEMENT,
797   },
798   {
799     GAME_PANEL_CE_SCORE_4_ELEMENT,
800     &game.panel.ce_score_element[3],
801     TYPE_ELEMENT,
802   },
803   {
804     GAME_PANEL_CE_SCORE_5_ELEMENT,
805     &game.panel.ce_score_element[4],
806     TYPE_ELEMENT,
807   },
808   {
809     GAME_PANEL_CE_SCORE_6_ELEMENT,
810     &game.panel.ce_score_element[5],
811     TYPE_ELEMENT,
812   },
813   {
814     GAME_PANEL_CE_SCORE_7_ELEMENT,
815     &game.panel.ce_score_element[6],
816     TYPE_ELEMENT,
817   },
818   {
819     GAME_PANEL_CE_SCORE_8_ELEMENT,
820     &game.panel.ce_score_element[7],
821     TYPE_ELEMENT,
822   },
823   {
824     GAME_PANEL_PLAYER_NAME,
825     &game.panel.player_name,
826     TYPE_STRING,
827   },
828   {
829     GAME_PANEL_LEVEL_NAME,
830     &game.panel.level_name,
831     TYPE_STRING,
832   },
833   {
834     GAME_PANEL_LEVEL_AUTHOR,
835     &game.panel.level_author,
836     TYPE_STRING,
837   },
838
839   {
840     -1,
841     NULL,
842     -1,
843   }
844 };
845 #endif
846
847
848 /* values for delayed check of falling and moving elements and for collision */
849 #define CHECK_DELAY_MOVING      3
850 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
851 #define CHECK_DELAY_COLLISION   2
852 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
853
854 /* values for initial player move delay (initial delay counter value) */
855 #define INITIAL_MOVE_DELAY_OFF  -1
856 #define INITIAL_MOVE_DELAY_ON   0
857
858 /* values for player movement speed (which is in fact a delay value) */
859 #define MOVE_DELAY_MIN_SPEED    32
860 #define MOVE_DELAY_NORMAL_SPEED 8
861 #define MOVE_DELAY_HIGH_SPEED   4
862 #define MOVE_DELAY_MAX_SPEED    1
863
864 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
865 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
866
867 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
868 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
869
870 /* values for other actions */
871 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
872 #define MOVE_STEPSIZE_MIN       (1)
873 #define MOVE_STEPSIZE_MAX       (TILEX)
874
875 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
876 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
877
878 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
879
880 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
881                                  RND(element_info[e].push_delay_random))
882 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
883                                  RND(element_info[e].drop_delay_random))
884 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
885                                  RND(element_info[e].move_delay_random))
886 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
887                                     (element_info[e].move_delay_random))
888 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
889                                  RND(element_info[e].ce_value_random_initial))
890 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
891 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
892                                  RND((c)->delay_random * (c)->delay_frames))
893 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
894                                  RND((c)->delay_random))
895
896
897 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
898          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
899
900 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
901         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
902          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
903          (be) + (e) - EL_SELF)
904
905 #define GET_PLAYER_FROM_BITS(p)                                         \
906         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
907
908 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
909         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
910          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
911          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
912          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
913          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
914          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
915          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
916          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
917          (e))
918
919 #define CAN_GROW_INTO(e)                                                \
920         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
921
922 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
923                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
934                                         (CAN_MOVE_INTO_ACID(e) &&       \
935                                          Feld[x][y] == EL_ACID) ||      \
936                                         (condition)))
937
938 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
939                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
940                                         (condition) ||                  \
941                                         (CAN_MOVE_INTO_ACID(e) &&       \
942                                          Feld[x][y] == EL_ACID) ||      \
943                                         (DONT_COLLIDE_WITH(e) &&        \
944                                          IS_PLAYER(x, y) &&             \
945                                          !PLAYER_ENEMY_PROTECTED(x, y))))
946
947 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
949
950 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
951         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
952
953 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
954         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
955
956 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
957         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
958                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
959
960 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
961         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
962
963 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
965
966 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
967         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
968
969 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
970         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
971
972 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
973         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
974
975 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
977                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
978                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
979                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
980                                                  IS_FOOD_PENGUIN(Feld[x][y])))
981 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
985         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
986
987 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
988         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
989
990 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
991         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
992                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
993
994 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
995
996 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
997                 (!IS_PLAYER(x, y) &&                                    \
998                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
999
1000 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1001         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1002
1003 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1004 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1005
1006 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1007 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1008 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1009 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1010
1011 /* game button identifiers */
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define SOUND_CTRL_ID_MUSIC             3
1016 #define SOUND_CTRL_ID_LOOPS             4
1017 #define SOUND_CTRL_ID_SIMPLE            5
1018
1019 #define NUM_GAME_BUTTONS                6
1020
1021
1022 /* forward declaration for internal use */
1023
1024 static void CreateField(int, int, int);
1025
1026 static void ResetGfxAnimation(int, int);
1027
1028 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1029 static void AdvanceFrameAndPlayerCounters(int);
1030
1031 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1032 static boolean MovePlayer(struct PlayerInfo *, int, int);
1033 static void ScrollPlayer(struct PlayerInfo *, int);
1034 static void ScrollScreen(struct PlayerInfo *, int);
1035
1036 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1037
1038 static void InitBeltMovement(void);
1039 static void CloseAllOpenTimegates(void);
1040 static void CheckGravityMovement(struct PlayerInfo *);
1041 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1042 static void KillPlayerUnlessEnemyProtected(int, int);
1043 static void KillPlayerUnlessExplosionProtected(int, int);
1044
1045 static void TestIfPlayerTouchesCustomElement(int, int);
1046 static void TestIfElementTouchesCustomElement(int, int);
1047 static void TestIfElementHitsCustomElement(int, int, int);
1048 #if 0
1049 static void TestIfElementSmashesCustomElement(int, int, int);
1050 #endif
1051
1052 static void HandleElementChange(int, int, int);
1053 static void ExecuteCustomElementAction(int, int, int, int);
1054 static boolean ChangeElement(int, int, int, int);
1055
1056 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1057 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1058         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1059 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1060         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1061 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1062         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1063 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1064         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1065
1066 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1067 #define CheckElementChange(x, y, e, te, ev)                             \
1068         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1069 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1070         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1071 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1072         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1073
1074 static void PlayLevelSound(int, int, int);
1075 static void PlayLevelSoundNearest(int, int, int);
1076 static void PlayLevelSoundAction(int, int, int);
1077 static void PlayLevelSoundElementAction(int, int, int, int);
1078 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1079 static void PlayLevelSoundActionIfLoop(int, int, int);
1080 static void StopLevelSoundActionIfLoop(int, int, int);
1081 static void PlayLevelMusic();
1082
1083 static void MapGameButtons();
1084 static void HandleGameButtons(struct GadgetInfo *);
1085
1086 int AmoebeNachbarNr(int, int);
1087 void AmoebeUmwandeln(int, int);
1088 void ContinueMoving(int, int);
1089 void Bang(int, int);
1090 void InitMovDir(int, int);
1091 void InitAmoebaNr(int, int);
1092 int NewHiScore(void);
1093
1094 void TestIfGoodThingHitsBadThing(int, int, int);
1095 void TestIfBadThingHitsGoodThing(int, int, int);
1096 void TestIfPlayerTouchesBadThing(int, int);
1097 void TestIfPlayerRunsIntoBadThing(int, int, int);
1098 void TestIfBadThingTouchesPlayer(int, int);
1099 void TestIfBadThingRunsIntoPlayer(int, int, int);
1100 void TestIfFriendTouchesBadThing(int, int);
1101 void TestIfBadThingTouchesFriend(int, int);
1102 void TestIfBadThingTouchesOtherBadThing(int, int);
1103
1104 void KillPlayer(struct PlayerInfo *);
1105 void BuryPlayer(struct PlayerInfo *);
1106 void RemovePlayer(struct PlayerInfo *);
1107
1108 boolean SnapField(struct PlayerInfo *, int, int);
1109 boolean DropElement(struct PlayerInfo *);
1110
1111 static int getInvisibleActiveFromInvisibleElement(int);
1112 static int getInvisibleFromInvisibleActiveElement(int);
1113
1114 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1115
1116 /* for detection of endless loops, caused by custom element programming */
1117 /* (using maximal playfield width x 10 is just a rough approximation) */
1118 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1119
1120 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1121 {                                                                       \
1122   if (recursion_loop_detected)                                          \
1123     return (rc);                                                        \
1124                                                                         \
1125   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1126   {                                                                     \
1127     recursion_loop_detected = TRUE;                                     \
1128     recursion_loop_element = (e);                                       \
1129   }                                                                     \
1130                                                                         \
1131   recursion_loop_depth++;                                               \
1132 }
1133
1134 #define RECURSION_LOOP_DETECTION_END()                                  \
1135 {                                                                       \
1136   recursion_loop_depth--;                                               \
1137 }
1138
1139 static int recursion_loop_depth;
1140 static boolean recursion_loop_detected;
1141 static boolean recursion_loop_element;
1142
1143
1144 /* ------------------------------------------------------------------------- */
1145 /* definition of elements that automatically change to other elements after  */
1146 /* a specified time, eventually calling a function when changing             */
1147 /* ------------------------------------------------------------------------- */
1148
1149 /* forward declaration for changer functions */
1150 static void InitBuggyBase(int, int);
1151 static void WarnBuggyBase(int, int);
1152
1153 static void InitTrap(int, int);
1154 static void ActivateTrap(int, int);
1155 static void ChangeActiveTrap(int, int);
1156
1157 static void InitRobotWheel(int, int);
1158 static void RunRobotWheel(int, int);
1159 static void StopRobotWheel(int, int);
1160
1161 static void InitTimegateWheel(int, int);
1162 static void RunTimegateWheel(int, int);
1163
1164 static void InitMagicBallDelay(int, int);
1165 static void ActivateMagicBall(int, int);
1166
1167 struct ChangingElementInfo
1168 {
1169   int element;
1170   int target_element;
1171   int change_delay;
1172   void (*pre_change_function)(int x, int y);
1173   void (*change_function)(int x, int y);
1174   void (*post_change_function)(int x, int y);
1175 };
1176
1177 static struct ChangingElementInfo change_delay_list[] =
1178 {
1179   {
1180     EL_NUT_BREAKING,
1181     EL_EMERALD,
1182     6,
1183     NULL,
1184     NULL,
1185     NULL
1186   },
1187   {
1188     EL_PEARL_BREAKING,
1189     EL_EMPTY,
1190     8,
1191     NULL,
1192     NULL,
1193     NULL
1194   },
1195   {
1196     EL_EXIT_OPENING,
1197     EL_EXIT_OPEN,
1198     29,
1199     NULL,
1200     NULL,
1201     NULL
1202   },
1203   {
1204     EL_EXIT_CLOSING,
1205     EL_EXIT_CLOSED,
1206     29,
1207     NULL,
1208     NULL,
1209     NULL
1210   },
1211   {
1212     EL_STEEL_EXIT_OPENING,
1213     EL_STEEL_EXIT_OPEN,
1214     29,
1215     NULL,
1216     NULL,
1217     NULL
1218   },
1219   {
1220     EL_STEEL_EXIT_CLOSING,
1221     EL_STEEL_EXIT_CLOSED,
1222     29,
1223     NULL,
1224     NULL,
1225     NULL
1226   },
1227   {
1228     EL_EM_EXIT_OPENING,
1229     EL_EM_EXIT_OPEN,
1230     29,
1231     NULL,
1232     NULL,
1233     NULL
1234   },
1235   {
1236     EL_EM_EXIT_CLOSING,
1237 #if 1
1238     EL_EMPTY,
1239 #else
1240     EL_EM_EXIT_CLOSED,
1241 #endif
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EM_STEEL_EXIT_OPENING,
1249     EL_EM_STEEL_EXIT_OPEN,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_EM_STEEL_EXIT_CLOSING,
1257 #if 1
1258     EL_STEELWALL,
1259 #else
1260     EL_EM_STEEL_EXIT_CLOSED,
1261 #endif
1262     29,
1263     NULL,
1264     NULL,
1265     NULL
1266   },
1267   {
1268     EL_SP_EXIT_OPENING,
1269     EL_SP_EXIT_OPEN,
1270     29,
1271     NULL,
1272     NULL,
1273     NULL
1274   },
1275   {
1276     EL_SP_EXIT_CLOSING,
1277     EL_SP_EXIT_CLOSED,
1278     29,
1279     NULL,
1280     NULL,
1281     NULL
1282   },
1283   {
1284     EL_SWITCHGATE_OPENING,
1285     EL_SWITCHGATE_OPEN,
1286     29,
1287     NULL,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SWITCHGATE_CLOSING,
1293     EL_SWITCHGATE_CLOSED,
1294     29,
1295     NULL,
1296     NULL,
1297     NULL
1298   },
1299   {
1300     EL_TIMEGATE_OPENING,
1301     EL_TIMEGATE_OPEN,
1302     29,
1303     NULL,
1304     NULL,
1305     NULL
1306   },
1307   {
1308     EL_TIMEGATE_CLOSING,
1309     EL_TIMEGATE_CLOSED,
1310     29,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315
1316   {
1317     EL_ACID_SPLASH_LEFT,
1318     EL_EMPTY,
1319     8,
1320     NULL,
1321     NULL,
1322     NULL
1323   },
1324   {
1325     EL_ACID_SPLASH_RIGHT,
1326     EL_EMPTY,
1327     8,
1328     NULL,
1329     NULL,
1330     NULL
1331   },
1332   {
1333     EL_SP_BUGGY_BASE,
1334     EL_SP_BUGGY_BASE_ACTIVATING,
1335     0,
1336     InitBuggyBase,
1337     NULL,
1338     NULL
1339   },
1340   {
1341     EL_SP_BUGGY_BASE_ACTIVATING,
1342     EL_SP_BUGGY_BASE_ACTIVE,
1343     0,
1344     InitBuggyBase,
1345     NULL,
1346     NULL
1347   },
1348   {
1349     EL_SP_BUGGY_BASE_ACTIVE,
1350     EL_SP_BUGGY_BASE,
1351     0,
1352     InitBuggyBase,
1353     WarnBuggyBase,
1354     NULL
1355   },
1356   {
1357     EL_TRAP,
1358     EL_TRAP_ACTIVE,
1359     0,
1360     InitTrap,
1361     NULL,
1362     ActivateTrap
1363   },
1364   {
1365     EL_TRAP_ACTIVE,
1366     EL_TRAP,
1367     31,
1368     NULL,
1369     ChangeActiveTrap,
1370     NULL
1371   },
1372   {
1373     EL_ROBOT_WHEEL_ACTIVE,
1374     EL_ROBOT_WHEEL,
1375     0,
1376     InitRobotWheel,
1377     RunRobotWheel,
1378     StopRobotWheel
1379   },
1380   {
1381     EL_TIMEGATE_SWITCH_ACTIVE,
1382     EL_TIMEGATE_SWITCH,
1383     0,
1384     InitTimegateWheel,
1385     RunTimegateWheel,
1386     NULL
1387   },
1388   {
1389     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1390     EL_DC_TIMEGATE_SWITCH,
1391     0,
1392     InitTimegateWheel,
1393     RunTimegateWheel,
1394     NULL
1395   },
1396   {
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     EL_EMC_MAGIC_BALL_ACTIVE,
1399     0,
1400     InitMagicBallDelay,
1401     NULL,
1402     ActivateMagicBall
1403   },
1404   {
1405     EL_EMC_SPRING_BUMPER_ACTIVE,
1406     EL_EMC_SPRING_BUMPER,
1407     8,
1408     NULL,
1409     NULL,
1410     NULL
1411   },
1412   {
1413     EL_DIAGONAL_SHRINKING,
1414     EL_UNDEFINED,
1415     0,
1416     NULL,
1417     NULL,
1418     NULL
1419   },
1420   {
1421     EL_DIAGONAL_GROWING,
1422     EL_UNDEFINED,
1423     0,
1424     NULL,
1425     NULL,
1426     NULL,
1427   },
1428
1429   {
1430     EL_UNDEFINED,
1431     EL_UNDEFINED,
1432     -1,
1433     NULL,
1434     NULL,
1435     NULL
1436   }
1437 };
1438
1439 struct
1440 {
1441   int element;
1442   int push_delay_fixed, push_delay_random;
1443 }
1444 push_delay_list[] =
1445 {
1446   { EL_SPRING,                  0, 0 },
1447   { EL_BALLOON,                 0, 0 },
1448
1449   { EL_SOKOBAN_OBJECT,          2, 0 },
1450   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1451   { EL_SATELLITE,               2, 0 },
1452   { EL_SP_DISK_YELLOW,          2, 0 },
1453
1454   { EL_UNDEFINED,               0, 0 },
1455 };
1456
1457 struct
1458 {
1459   int element;
1460   int move_stepsize;
1461 }
1462 move_stepsize_list[] =
1463 {
1464   { EL_AMOEBA_DROP,             2 },
1465   { EL_AMOEBA_DROPPING,         2 },
1466   { EL_QUICKSAND_FILLING,       1 },
1467   { EL_QUICKSAND_EMPTYING,      1 },
1468   { EL_QUICKSAND_FAST_FILLING,  2 },
1469   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1470   { EL_MAGIC_WALL_FILLING,      2 },
1471   { EL_MAGIC_WALL_EMPTYING,     2 },
1472   { EL_BD_MAGIC_WALL_FILLING,   2 },
1473   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1474   { EL_DC_MAGIC_WALL_FILLING,   2 },
1475   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1476
1477   { EL_UNDEFINED,               0 },
1478 };
1479
1480 struct
1481 {
1482   int element;
1483   int count;
1484 }
1485 collect_count_list[] =
1486 {
1487   { EL_EMERALD,                 1 },
1488   { EL_BD_DIAMOND,              1 },
1489   { EL_EMERALD_YELLOW,          1 },
1490   { EL_EMERALD_RED,             1 },
1491   { EL_EMERALD_PURPLE,          1 },
1492   { EL_DIAMOND,                 3 },
1493   { EL_SP_INFOTRON,             1 },
1494   { EL_PEARL,                   5 },
1495   { EL_CRYSTAL,                 8 },
1496
1497   { EL_UNDEFINED,               0 },
1498 };
1499
1500 struct
1501 {
1502   int element;
1503   int direction;
1504 }
1505 access_direction_list[] =
1506 {
1507   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1508   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1509   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1510   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1511   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1513   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1514   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1515   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1516   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1517   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1518
1519   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1520   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1521   { EL_SP_PORT_UP,                                                   MV_DOWN },
1522   { EL_SP_PORT_DOWN,                                         MV_UP           },
1523   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1524   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1525   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1526   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1527   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1528   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1529   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1530   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1531   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1532   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1533   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1534   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1535   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1536   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1537   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1538
1539   { EL_UNDEFINED,                       MV_NONE                              }
1540 };
1541
1542 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1543
1544 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1545 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1546 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1547                                  IS_JUST_CHANGING(x, y))
1548
1549 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1550
1551 /* static variables for playfield scan mode (scanning forward or backward) */
1552 static int playfield_scan_start_x = 0;
1553 static int playfield_scan_start_y = 0;
1554 static int playfield_scan_delta_x = 1;
1555 static int playfield_scan_delta_y = 1;
1556
1557 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1558                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1559                                      (y) += playfield_scan_delta_y)     \
1560                                 for ((x) = playfield_scan_start_x;      \
1561                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1562                                      (x) += playfield_scan_delta_x)
1563
1564 #ifdef DEBUG
1565 void DEBUG_SetMaximumDynamite()
1566 {
1567   int i;
1568
1569   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1570     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1571       local_player->inventory_element[local_player->inventory_size++] =
1572         EL_DYNAMITE;
1573 }
1574 #endif
1575
1576 static void InitPlayfieldScanModeVars()
1577 {
1578   if (game.use_reverse_scan_direction)
1579   {
1580     playfield_scan_start_x = lev_fieldx - 1;
1581     playfield_scan_start_y = lev_fieldy - 1;
1582
1583     playfield_scan_delta_x = -1;
1584     playfield_scan_delta_y = -1;
1585   }
1586   else
1587   {
1588     playfield_scan_start_x = 0;
1589     playfield_scan_start_y = 0;
1590
1591     playfield_scan_delta_x = 1;
1592     playfield_scan_delta_y = 1;
1593   }
1594 }
1595
1596 static void InitPlayfieldScanMode(int mode)
1597 {
1598   game.use_reverse_scan_direction =
1599     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1600
1601   InitPlayfieldScanModeVars();
1602 }
1603
1604 static int get_move_delay_from_stepsize(int move_stepsize)
1605 {
1606   move_stepsize =
1607     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1608
1609   /* make sure that stepsize value is always a power of 2 */
1610   move_stepsize = (1 << log_2(move_stepsize));
1611
1612   return TILEX / move_stepsize;
1613 }
1614
1615 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1616                                boolean init_game)
1617 {
1618   int player_nr = player->index_nr;
1619   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1620   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1621
1622   /* do no immediately change move delay -- the player might just be moving */
1623   player->move_delay_value_next = move_delay;
1624
1625   /* information if player can move must be set separately */
1626   player->cannot_move = cannot_move;
1627
1628   if (init_game)
1629   {
1630     player->move_delay       = game.initial_move_delay[player_nr];
1631     player->move_delay_value = game.initial_move_delay_value[player_nr];
1632
1633     player->move_delay_value_next = -1;
1634
1635     player->move_delay_reset_counter = 0;
1636   }
1637 }
1638
1639 void GetPlayerConfig()
1640 {
1641   GameFrameDelay = setup.game_frame_delay;
1642
1643   if (!audio.sound_available)
1644     setup.sound_simple = FALSE;
1645
1646   if (!audio.loops_available)
1647     setup.sound_loops = FALSE;
1648
1649   if (!audio.music_available)
1650     setup.sound_music = FALSE;
1651
1652   if (!video.fullscreen_available)
1653     setup.fullscreen = FALSE;
1654
1655   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656
1657   SetAudioMode(setup.sound);
1658   InitJoysticks();
1659 }
1660
1661 int GetElementFromGroupElement(int element)
1662 {
1663   if (IS_GROUP_ELEMENT(element))
1664   {
1665     struct ElementGroupInfo *group = element_info[element].group;
1666     int last_anim_random_frame = gfx.anim_random_frame;
1667     int element_pos;
1668
1669     if (group->choice_mode == ANIM_RANDOM)
1670       gfx.anim_random_frame = RND(group->num_elements_resolved);
1671
1672     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1673                                     group->choice_mode, 0,
1674                                     group->choice_pos);
1675
1676     if (group->choice_mode == ANIM_RANDOM)
1677       gfx.anim_random_frame = last_anim_random_frame;
1678
1679     group->choice_pos++;
1680
1681     element = group->element_resolved[element_pos];
1682   }
1683
1684   return element;
1685 }
1686
1687 static void InitPlayerField(int x, int y, int element, boolean init_game)
1688 {
1689   if (element == EL_SP_MURPHY)
1690   {
1691     if (init_game)
1692     {
1693       if (stored_player[0].present)
1694       {
1695         Feld[x][y] = EL_SP_MURPHY_CLONE;
1696
1697         return;
1698       }
1699       else
1700       {
1701         stored_player[0].use_murphy = TRUE;
1702
1703         if (!level.use_artwork_element[0])
1704           stored_player[0].artwork_element = EL_SP_MURPHY;
1705       }
1706
1707       Feld[x][y] = EL_PLAYER_1;
1708     }
1709   }
1710
1711   if (init_game)
1712   {
1713     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1714     int jx = player->jx, jy = player->jy;
1715
1716     player->present = TRUE;
1717
1718     player->block_last_field = (element == EL_SP_MURPHY ?
1719                                 level.sp_block_last_field :
1720                                 level.block_last_field);
1721
1722     /* ---------- initialize player's last field block delay --------------- */
1723
1724     /* always start with reliable default value (no adjustment needed) */
1725     player->block_delay_adjustment = 0;
1726
1727     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1728     if (player->block_last_field && element == EL_SP_MURPHY)
1729       player->block_delay_adjustment = 1;
1730
1731     /* special case 2: in game engines before 3.1.1, blocking was different */
1732     if (game.use_block_last_field_bug)
1733       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1734
1735     if (!options.network || player->connected)
1736     {
1737       player->active = TRUE;
1738
1739       /* remove potentially duplicate players */
1740       if (StorePlayer[jx][jy] == Feld[x][y])
1741         StorePlayer[jx][jy] = 0;
1742
1743       StorePlayer[x][y] = Feld[x][y];
1744
1745       if (options.debug)
1746       {
1747         printf("Player %d activated.\n", player->element_nr);
1748         printf("[Local player is %d and currently %s.]\n",
1749                local_player->element_nr,
1750                local_player->active ? "active" : "not active");
1751       }
1752     }
1753
1754     Feld[x][y] = EL_EMPTY;
1755
1756     player->jx = player->last_jx = x;
1757     player->jy = player->last_jy = y;
1758   }
1759 }
1760
1761 static void InitField(int x, int y, boolean init_game)
1762 {
1763   int element = Feld[x][y];
1764
1765   switch (element)
1766   {
1767     case EL_SP_MURPHY:
1768     case EL_PLAYER_1:
1769     case EL_PLAYER_2:
1770     case EL_PLAYER_3:
1771     case EL_PLAYER_4:
1772       InitPlayerField(x, y, element, init_game);
1773       break;
1774
1775     case EL_SOKOBAN_FIELD_PLAYER:
1776       element = Feld[x][y] = EL_PLAYER_1;
1777       InitField(x, y, init_game);
1778
1779       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1780       InitField(x, y, init_game);
1781       break;
1782
1783     case EL_SOKOBAN_FIELD_EMPTY:
1784       local_player->sokobanfields_still_needed++;
1785       break;
1786
1787     case EL_STONEBLOCK:
1788       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1789         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1790       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1791         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1792       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1793         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1794       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1795         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1796       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1797         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1798       break;
1799
1800     case EL_BUG:
1801     case EL_BUG_RIGHT:
1802     case EL_BUG_UP:
1803     case EL_BUG_LEFT:
1804     case EL_BUG_DOWN:
1805     case EL_SPACESHIP:
1806     case EL_SPACESHIP_RIGHT:
1807     case EL_SPACESHIP_UP:
1808     case EL_SPACESHIP_LEFT:
1809     case EL_SPACESHIP_DOWN:
1810     case EL_BD_BUTTERFLY:
1811     case EL_BD_BUTTERFLY_RIGHT:
1812     case EL_BD_BUTTERFLY_UP:
1813     case EL_BD_BUTTERFLY_LEFT:
1814     case EL_BD_BUTTERFLY_DOWN:
1815     case EL_BD_FIREFLY:
1816     case EL_BD_FIREFLY_RIGHT:
1817     case EL_BD_FIREFLY_UP:
1818     case EL_BD_FIREFLY_LEFT:
1819     case EL_BD_FIREFLY_DOWN:
1820     case EL_PACMAN_RIGHT:
1821     case EL_PACMAN_UP:
1822     case EL_PACMAN_LEFT:
1823     case EL_PACMAN_DOWN:
1824     case EL_YAMYAM:
1825     case EL_YAMYAM_LEFT:
1826     case EL_YAMYAM_RIGHT:
1827     case EL_YAMYAM_UP:
1828     case EL_YAMYAM_DOWN:
1829     case EL_DARK_YAMYAM:
1830     case EL_ROBOT:
1831     case EL_PACMAN:
1832     case EL_SP_SNIKSNAK:
1833     case EL_SP_ELECTRON:
1834     case EL_MOLE:
1835     case EL_MOLE_LEFT:
1836     case EL_MOLE_RIGHT:
1837     case EL_MOLE_UP:
1838     case EL_MOLE_DOWN:
1839       InitMovDir(x, y);
1840       break;
1841
1842     case EL_AMOEBA_FULL:
1843     case EL_BD_AMOEBA:
1844       InitAmoebaNr(x, y);
1845       break;
1846
1847     case EL_AMOEBA_DROP:
1848       if (y == lev_fieldy - 1)
1849       {
1850         Feld[x][y] = EL_AMOEBA_GROWING;
1851         Store[x][y] = EL_AMOEBA_WET;
1852       }
1853       break;
1854
1855     case EL_DYNAMITE_ACTIVE:
1856     case EL_SP_DISK_RED_ACTIVE:
1857     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1858     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1859     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1860     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1861       MovDelay[x][y] = 96;
1862       break;
1863
1864     case EL_EM_DYNAMITE_ACTIVE:
1865       MovDelay[x][y] = 32;
1866       break;
1867
1868     case EL_LAMP:
1869       local_player->lights_still_needed++;
1870       break;
1871
1872     case EL_PENGUIN:
1873       local_player->friends_still_needed++;
1874       break;
1875
1876     case EL_PIG:
1877     case EL_DRAGON:
1878       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1879       break;
1880
1881     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1882     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1883     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1884     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1885     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1886     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1887     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1888     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1889     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1890     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1891     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1892     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1893       if (init_game)
1894       {
1895         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1896         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1897         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1898
1899         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1900         {
1901           game.belt_dir[belt_nr] = belt_dir;
1902           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1903         }
1904         else    /* more than one switch -- set it like the first switch */
1905         {
1906           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1907         }
1908       }
1909       break;
1910
1911 #if !USE_BOTH_SWITCHGATE_SWITCHES
1912     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1913       if (init_game)
1914         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1915       break;
1916
1917     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1918       if (init_game)
1919         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1920       break;
1921 #endif
1922
1923     case EL_LIGHT_SWITCH_ACTIVE:
1924       if (init_game)
1925         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1926       break;
1927
1928     case EL_INVISIBLE_STEELWALL:
1929     case EL_INVISIBLE_WALL:
1930     case EL_INVISIBLE_SAND:
1931       if (game.light_time_left > 0 ||
1932           game.lenses_time_left > 0)
1933         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1934       break;
1935
1936     case EL_EMC_MAGIC_BALL:
1937       if (game.ball_state)
1938         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1939       break;
1940
1941     case EL_EMC_MAGIC_BALL_SWITCH:
1942       if (game.ball_state)
1943         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1944       break;
1945
1946     default:
1947       if (IS_CUSTOM_ELEMENT(element))
1948       {
1949         if (CAN_MOVE(element))
1950           InitMovDir(x, y);
1951
1952 #if USE_NEW_CUSTOM_VALUE
1953         if (!element_info[element].use_last_ce_value || init_game)
1954           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1955 #endif
1956       }
1957       else if (IS_GROUP_ELEMENT(element))
1958       {
1959         Feld[x][y] = GetElementFromGroupElement(element);
1960
1961         InitField(x, y, init_game);
1962       }
1963
1964       break;
1965   }
1966
1967   if (!init_game)
1968     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1969 }
1970
1971 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1972 {
1973   InitField(x, y, init_game);
1974
1975   /* not needed to call InitMovDir() -- already done by InitField()! */
1976   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1977       CAN_MOVE(Feld[x][y]))
1978     InitMovDir(x, y);
1979 }
1980
1981 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1982 {
1983   int old_element = Feld[x][y];
1984
1985   InitField(x, y, init_game);
1986
1987   /* not needed to call InitMovDir() -- already done by InitField()! */
1988   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1989       CAN_MOVE(old_element) &&
1990       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1991     InitMovDir(x, y);
1992
1993   /* this case is in fact a combination of not less than three bugs:
1994      first, it calls InitMovDir() for elements that can move, although this is
1995      already done by InitField(); then, it checks the element that was at this
1996      field _before_ the call to InitField() (which can change it); lastly, it
1997      was not called for "mole with direction" elements, which were treated as
1998      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1999   */
2000 }
2001
2002 #if 1
2003
2004 static int get_key_element_from_nr(int key_nr)
2005 {
2006   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2007                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2008                           EL_EM_KEY_1 : EL_KEY_1);
2009
2010   return key_base_element + key_nr;
2011 }
2012
2013 static int get_next_dropped_element(struct PlayerInfo *player)
2014 {
2015   return (player->inventory_size > 0 ?
2016           player->inventory_element[player->inventory_size - 1] :
2017           player->inventory_infinite_element != EL_UNDEFINED ?
2018           player->inventory_infinite_element :
2019           player->dynabombs_left > 0 ?
2020           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2021           EL_UNDEFINED);
2022 }
2023
2024 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2025 {
2026   /* pos >= 0: get element from bottom of the stack;
2027      pos <  0: get element from top of the stack */
2028
2029   if (pos < 0)
2030   {
2031     int min_inventory_size = -pos;
2032     int inventory_pos = player->inventory_size - min_inventory_size;
2033     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2034
2035     return (player->inventory_size >= min_inventory_size ?
2036             player->inventory_element[inventory_pos] :
2037             player->inventory_infinite_element != EL_UNDEFINED ?
2038             player->inventory_infinite_element :
2039             player->dynabombs_left >= min_dynabombs_left ?
2040             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2041             EL_UNDEFINED);
2042   }
2043   else
2044   {
2045     int min_dynabombs_left = pos + 1;
2046     int min_inventory_size = pos + 1 - player->dynabombs_left;
2047     int inventory_pos = pos - player->dynabombs_left;
2048
2049     return (player->inventory_infinite_element != EL_UNDEFINED ?
2050             player->inventory_infinite_element :
2051             player->dynabombs_left >= min_dynabombs_left ?
2052             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2053             player->inventory_size >= min_inventory_size ?
2054             player->inventory_element[inventory_pos] :
2055             EL_UNDEFINED);
2056   }
2057 }
2058
2059 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2060 {
2061   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2062   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2063   int compare_result;
2064
2065   if (gpo1->sort_priority != gpo2->sort_priority)
2066     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2067   else
2068     compare_result = gpo1->nr - gpo2->nr;
2069
2070   return compare_result;
2071 }
2072
2073 void InitGameControlValues()
2074 {
2075   int i;
2076
2077   for (i = 0; game_panel_controls[i].nr != -1; i++)
2078   {
2079     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2080     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2081     struct TextPosInfo *pos = gpc->pos;
2082     int nr = gpc->nr;
2083     int type = gpc->type;
2084
2085     if (nr != i)
2086     {
2087       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2088       Error(ERR_EXIT, "this should not happen -- please debug");
2089     }
2090
2091     /* force update of game controls after initialization */
2092     gpc->value = gpc->last_value = -1;
2093     gpc->frame = gpc->last_frame = -1;
2094     gpc->gfx_frame = -1;
2095
2096     /* determine panel value width for later calculation of alignment */
2097     if (type == TYPE_INTEGER || type == TYPE_STRING)
2098     {
2099       pos->width = pos->size * getFontWidth(pos->font);
2100       pos->height = getFontHeight(pos->font);
2101     }
2102     else if (type == TYPE_ELEMENT)
2103     {
2104       pos->width = pos->size;
2105       pos->height = pos->size;
2106     }
2107
2108     /* fill structure for game panel draw order */
2109     gpo->nr = gpc->nr;
2110     gpo->sort_priority = pos->sort_priority;
2111   }
2112
2113   /* sort game panel controls according to sort_priority and control number */
2114   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2115         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2116 }
2117
2118 void UpdatePlayfieldElementCount()
2119 {
2120   boolean use_element_count = FALSE;
2121   int i, j, x, y;
2122
2123   /* first check if it is needed at all to calculate playfield element count */
2124   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2125     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2126       use_element_count = TRUE;
2127
2128   if (!use_element_count)
2129     return;
2130
2131   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2132     element_info[i].element_count = 0;
2133
2134   SCAN_PLAYFIELD(x, y)
2135   {
2136     element_info[Feld[x][y]].element_count++;
2137   }
2138
2139   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2140     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2141       if (IS_IN_GROUP(j, i))
2142         element_info[EL_GROUP_START + i].element_count +=
2143           element_info[j].element_count;
2144 }
2145
2146 void UpdateGameControlValues()
2147 {
2148   int i, k;
2149   int time = (local_player->LevelSolved ?
2150               local_player->LevelSolved_CountingTime :
2151               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2152               level.native_em_level->lev->time :
2153               level.time == 0 ? TimePlayed : TimeLeft);
2154   int score = (local_player->LevelSolved ?
2155                local_player->LevelSolved_CountingScore :
2156                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2157                level.native_em_level->lev->score :
2158                local_player->score);
2159   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2160               level.native_em_level->lev->required :
2161               local_player->gems_still_needed);
2162   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2163                      level.native_em_level->lev->required > 0 :
2164                      local_player->gems_still_needed > 0 ||
2165                      local_player->sokobanfields_still_needed > 0 ||
2166                      local_player->lights_still_needed > 0);
2167
2168   UpdatePlayfieldElementCount();
2169
2170   /* update game panel control values */
2171
2172   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2173   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2174
2175   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2176   for (i = 0; i < MAX_NUM_KEYS; i++)
2177     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2178   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2179   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2180
2181   if (game.centered_player_nr == -1)
2182   {
2183     for (i = 0; i < MAX_PLAYERS; i++)
2184     {
2185       for (k = 0; k < MAX_NUM_KEYS; k++)
2186       {
2187         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2188         {
2189           if (level.native_em_level->ply[i]->keys & (1 << k))
2190             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2191               get_key_element_from_nr(k);
2192         }
2193         else if (stored_player[i].key[k])
2194           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2195             get_key_element_from_nr(k);
2196       }
2197
2198       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2199         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2200           level.native_em_level->ply[i]->dynamite;
2201       else
2202         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2203           stored_player[i].inventory_size;
2204
2205       if (stored_player[i].num_white_keys > 0)
2206         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2207           EL_DC_KEY_WHITE;
2208
2209       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2210         stored_player[i].num_white_keys;
2211     }
2212   }
2213   else
2214   {
2215     int player_nr = game.centered_player_nr;
2216
2217     for (k = 0; k < MAX_NUM_KEYS; k++)
2218     {
2219       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2220       {
2221         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2222           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2223             get_key_element_from_nr(k);
2224       }
2225       else if (stored_player[player_nr].key[k])
2226         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2227           get_key_element_from_nr(k);
2228     }
2229
2230     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2231       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2232         level.native_em_level->ply[player_nr]->dynamite;
2233     else
2234       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2235         stored_player[player_nr].inventory_size;
2236
2237     if (stored_player[player_nr].num_white_keys > 0)
2238       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2239
2240     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2241       stored_player[player_nr].num_white_keys;
2242   }
2243
2244   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2245   {
2246     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2247       get_inventory_element_from_pos(local_player, i);
2248     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2249       get_inventory_element_from_pos(local_player, -i - 1);
2250   }
2251
2252   game_panel_controls[GAME_PANEL_SCORE].value = score;
2253   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2254
2255   game_panel_controls[GAME_PANEL_TIME].value = time;
2256
2257   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2258   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2259   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2260
2261   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2262     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2263      EL_EMPTY);
2264   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2265     local_player->shield_normal_time_left;
2266   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2267     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2268      EL_EMPTY);
2269   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2270     local_player->shield_deadly_time_left;
2271
2272   game_panel_controls[GAME_PANEL_EXIT].value =
2273     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2274
2275   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2276     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2277   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2278     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2279      EL_EMC_MAGIC_BALL_SWITCH);
2280
2281   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2282     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2283   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2284     game.light_time_left;
2285
2286   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2287     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2288   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2289     game.timegate_time_left;
2290
2291   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2292     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2293
2294   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2295     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2296   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2297     game.lenses_time_left;
2298
2299   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2300     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2301   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2302     game.magnify_time_left;
2303
2304   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2305     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2306      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2307      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2308      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2309      EL_BALLOON_SWITCH_NONE);
2310
2311   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2312     local_player->dynabomb_count;
2313   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2314     local_player->dynabomb_size;
2315   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2316     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2317
2318   game_panel_controls[GAME_PANEL_PENGUINS].value =
2319     local_player->friends_still_needed;
2320
2321   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2322     local_player->sokobanfields_still_needed;
2323   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2324     local_player->sokobanfields_still_needed;
2325
2326   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2327     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2328
2329   for (i = 0; i < NUM_BELTS; i++)
2330   {
2331     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2332       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2333        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2334     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2335       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2336   }
2337
2338   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2339     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2340   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2341     game.magic_wall_time_left;
2342
2343 #if USE_PLAYER_GRAVITY
2344   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2345     local_player->gravity;
2346 #else
2347   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2348 #endif
2349
2350   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2351     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2352
2353   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2354     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2355       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2356        game.panel.element[i].id : EL_UNDEFINED);
2357
2358   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2359     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2360       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2361        element_info[game.panel.element_count[i].id].element_count : 0);
2362
2363   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2364     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2365       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2366        element_info[game.panel.ce_score[i].id].collect_score : 0);
2367
2368   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2369     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2370       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2371        element_info[game.panel.ce_score_element[i].id].collect_score :
2372        EL_UNDEFINED);
2373
2374   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2375   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2376   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2377
2378   /* update game panel control frames */
2379
2380   for (i = 0; game_panel_controls[i].nr != -1; i++)
2381   {
2382     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2383
2384     if (gpc->type == TYPE_ELEMENT)
2385     {
2386       int last_anim_random_frame = gfx.anim_random_frame;
2387       int element = gpc->value;
2388       int graphic = el2panelimg(element);
2389
2390       if (gpc->value != gpc->last_value)
2391       {
2392         gpc->gfx_frame = 0;
2393         gpc->gfx_random = INIT_GFX_RANDOM();
2394       }
2395       else
2396       {
2397         gpc->gfx_frame++;
2398
2399         if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2400             IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2401           gpc->gfx_random = INIT_GFX_RANDOM();
2402       }
2403
2404       if (ANIM_MODE(graphic) == ANIM_RANDOM)
2405         gfx.anim_random_frame = gpc->gfx_random;
2406
2407       if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2408         gpc->gfx_frame = element_info[element].collect_score;
2409
2410       gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2411                                             gpc->gfx_frame);
2412
2413       if (ANIM_MODE(graphic) == ANIM_RANDOM)
2414         gfx.anim_random_frame = last_anim_random_frame;
2415     }
2416   }
2417 }
2418
2419 void DisplayGameControlValues()
2420 {
2421   boolean redraw_panel = FALSE;
2422   int i;
2423
2424   for (i = 0; game_panel_controls[i].nr != -1; i++)
2425   {
2426     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2427
2428     if (PANEL_DEACTIVATED(gpc->pos))
2429       continue;
2430
2431     if (gpc->value == gpc->last_value &&
2432         gpc->frame == gpc->last_frame)
2433       continue;
2434
2435     redraw_panel = TRUE;
2436   }
2437
2438   if (!redraw_panel)
2439     return;
2440
2441   /* copy default game door content to main double buffer */
2442   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2443              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2444
2445   /* redraw game control buttons */
2446 #if 1
2447   RedrawGameButtons();
2448 #else
2449   UnmapGameButtons();
2450   MapGameButtons();
2451 #endif
2452
2453   game_status = GAME_MODE_PSEUDO_PANEL;
2454
2455 #if 1
2456   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2457 #else
2458   for (i = 0; game_panel_controls[i].nr != -1; i++)
2459 #endif
2460   {
2461 #if 1
2462     int nr = game_panel_order[i].nr;
2463     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2464 #else
2465     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2466     int nr = gpc->nr;
2467 #endif
2468     struct TextPosInfo *pos = gpc->pos;
2469     int type = gpc->type;
2470     int value = gpc->value;
2471     int frame = gpc->frame;
2472 #if 0
2473     int last_value = gpc->last_value;
2474     int last_frame = gpc->last_frame;
2475 #endif
2476     int size = pos->size;
2477     int font = pos->font;
2478     boolean draw_masked = pos->draw_masked;
2479     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2480
2481     if (PANEL_DEACTIVATED(pos))
2482       continue;
2483
2484 #if 0
2485     if (value == last_value && frame == last_frame)
2486       continue;
2487 #endif
2488
2489     gpc->last_value = value;
2490     gpc->last_frame = frame;
2491
2492 #if 0
2493     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2494 #endif
2495
2496     if (type == TYPE_INTEGER)
2497     {
2498       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2499           nr == GAME_PANEL_TIME)
2500       {
2501         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2502
2503         if (use_dynamic_size)           /* use dynamic number of digits */
2504         {
2505           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2506           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2507           int size2 = size1 + 1;
2508           int font1 = pos->font;
2509           int font2 = pos->font_alt;
2510
2511           size = (value < value_change ? size1 : size2);
2512           font = (value < value_change ? font1 : font2);
2513
2514 #if 0
2515           /* clear background if value just changed its size (dynamic digits) */
2516           if ((last_value < value_change) != (value < value_change))
2517           {
2518             int width1 = size1 * getFontWidth(font1);
2519             int width2 = size2 * getFontWidth(font2);
2520             int max_width = MAX(width1, width2);
2521             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2522
2523             pos->width = max_width;
2524
2525             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2526                                        max_width, max_height);
2527           }
2528 #endif
2529         }
2530       }
2531
2532 #if 1
2533       /* correct text size if "digits" is zero or less */
2534       if (size <= 0)
2535         size = strlen(int2str(value, size));
2536
2537       /* dynamically correct text alignment */
2538       pos->width = size * getFontWidth(font);
2539 #endif
2540
2541       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2542                   int2str(value, size), font, mask_mode);
2543     }
2544     else if (type == TYPE_ELEMENT)
2545     {
2546       int element, graphic;
2547       Bitmap *src_bitmap;
2548       int src_x, src_y;
2549       int width, height;
2550       int dst_x = PANEL_XPOS(pos);
2551       int dst_y = PANEL_YPOS(pos);
2552
2553 #if 1
2554       if (value != EL_UNDEFINED && value != EL_EMPTY)
2555       {
2556         element = value;
2557         graphic = el2panelimg(value);
2558
2559         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2560
2561 #if 1
2562         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2563           size = TILESIZE;
2564 #endif
2565
2566         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2567                               &src_x, &src_y);
2568
2569         width  = graphic_info[graphic].width  * size / TILESIZE;
2570         height = graphic_info[graphic].height * size / TILESIZE;
2571
2572         if (draw_masked)
2573         {
2574           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2575                         dst_x - src_x, dst_y - src_y);
2576           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2577                            dst_x, dst_y);
2578         }
2579         else
2580         {
2581           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2582                      dst_x, dst_y);
2583         }
2584       }
2585 #else
2586       if (value == EL_UNDEFINED || value == EL_EMPTY)
2587       {
2588         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2589         graphic = el2panelimg(element);
2590
2591         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2592         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2593         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2594       }
2595       else
2596       {
2597         element = value;
2598         graphic = el2panelimg(value);
2599
2600         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2601       }
2602
2603       width  = graphic_info[graphic].width  * size / TILESIZE;
2604       height = graphic_info[graphic].height * size / TILESIZE;
2605
2606       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2607 #endif
2608     }
2609     else if (type == TYPE_STRING)
2610     {
2611       boolean active = (value != 0);
2612       char *state_normal = "off";
2613       char *state_active = "on";
2614       char *state = (active ? state_active : state_normal);
2615       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2616                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2617                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2618                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2619
2620       if (nr == GAME_PANEL_GRAVITY_STATE)
2621       {
2622         int font1 = pos->font;          /* (used for normal state) */
2623         int font2 = pos->font_alt;      /* (used for active state) */
2624 #if 0
2625         int size1 = strlen(state_normal);
2626         int size2 = strlen(state_active);
2627         int width1 = size1 * getFontWidth(font1);
2628         int width2 = size2 * getFontWidth(font2);
2629         int max_width = MAX(width1, width2);
2630         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2631
2632         pos->width = max_width;
2633
2634         /* clear background for values that may have changed its size */
2635         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2636                                    max_width, max_height);
2637 #endif
2638
2639         font = (active ? font2 : font1);
2640       }
2641
2642       if (s != NULL)
2643       {
2644         char *s_cut;
2645
2646 #if 1
2647         if (size <= 0)
2648         {
2649           /* don't truncate output if "chars" is zero or less */
2650           size = strlen(s);
2651
2652           /* dynamically correct text alignment */
2653           pos->width = size * getFontWidth(font);
2654         }
2655 #endif
2656
2657         s_cut = getStringCopyN(s, size);
2658
2659         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2660                     s_cut, font, mask_mode);
2661
2662         free(s_cut);
2663       }
2664     }
2665
2666     redraw_mask |= REDRAW_DOOR_1;
2667   }
2668
2669   game_status = GAME_MODE_PLAYING;
2670 }
2671
2672 void UpdateAndDisplayGameControlValues()
2673 {
2674   if (tape.warp_forward)
2675     return;
2676
2677   UpdateGameControlValues();
2678   DisplayGameControlValues();
2679 }
2680
2681 void DrawGameValue_Emeralds(int value)
2682 {
2683   struct TextPosInfo *pos = &game.panel.gems;
2684 #if 1
2685   int font_nr = pos->font;
2686 #else
2687   int font_nr = FONT_TEXT_2;
2688 #endif
2689   int font_width = getFontWidth(font_nr);
2690   int chars = pos->size;
2691
2692 #if 1
2693   return;       /* !!! USE NEW STUFF !!! */
2694 #endif
2695
2696   if (PANEL_DEACTIVATED(pos))
2697     return;
2698
2699   pos->width = chars * font_width;
2700
2701   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2702 }
2703
2704 void DrawGameValue_Dynamite(int value)
2705 {
2706   struct TextPosInfo *pos = &game.panel.inventory_count;
2707 #if 1
2708   int font_nr = pos->font;
2709 #else
2710   int font_nr = FONT_TEXT_2;
2711 #endif
2712   int font_width = getFontWidth(font_nr);
2713   int chars = pos->size;
2714
2715 #if 1
2716   return;       /* !!! USE NEW STUFF !!! */
2717 #endif
2718
2719   if (PANEL_DEACTIVATED(pos))
2720     return;
2721
2722   pos->width = chars * font_width;
2723
2724   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2725 }
2726
2727 void DrawGameValue_Score(int value)
2728 {
2729   struct TextPosInfo *pos = &game.panel.score;
2730 #if 1
2731   int font_nr = pos->font;
2732 #else
2733   int font_nr = FONT_TEXT_2;
2734 #endif
2735   int font_width = getFontWidth(font_nr);
2736   int chars = pos->size;
2737
2738 #if 1
2739   return;       /* !!! USE NEW STUFF !!! */
2740 #endif
2741
2742   if (PANEL_DEACTIVATED(pos))
2743     return;
2744
2745   pos->width = chars * font_width;
2746
2747   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2748 }
2749
2750 void DrawGameValue_Time(int value)
2751 {
2752   struct TextPosInfo *pos = &game.panel.time;
2753   static int last_value = -1;
2754   int chars1 = 3;
2755   int chars2 = 4;
2756   int chars = pos->size;
2757 #if 1
2758   int font1_nr = pos->font;
2759   int font2_nr = pos->font_alt;
2760 #else
2761   int font1_nr = FONT_TEXT_2;
2762   int font2_nr = FONT_TEXT_1;
2763 #endif
2764   int font_nr = font1_nr;
2765   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2766
2767 #if 1
2768   return;       /* !!! USE NEW STUFF !!! */
2769 #endif
2770
2771   if (PANEL_DEACTIVATED(pos))
2772     return;
2773
2774   if (use_dynamic_chars)                /* use dynamic number of chars */
2775   {
2776     chars   = (value < 1000 ? chars1   : chars2);
2777     font_nr = (value < 1000 ? font1_nr : font2_nr);
2778   }
2779
2780   /* clear background if value just changed its size (dynamic chars only) */
2781   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2782   {
2783     int width1 = chars1 * getFontWidth(font1_nr);
2784     int width2 = chars2 * getFontWidth(font2_nr);
2785     int max_width = MAX(width1, width2);
2786     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2787
2788     pos->width = max_width;
2789
2790     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2791                                max_width, max_height);
2792   }
2793
2794   pos->width = chars * getFontWidth(font_nr);
2795
2796   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2797
2798   last_value = value;
2799 }
2800
2801 void DrawGameValue_Level(int value)
2802 {
2803   struct TextPosInfo *pos = &game.panel.level_number;
2804   int chars1 = 2;
2805   int chars2 = 3;
2806   int chars = pos->size;
2807 #if 1
2808   int font1_nr = pos->font;
2809   int font2_nr = pos->font_alt;
2810 #else
2811   int font1_nr = FONT_TEXT_2;
2812   int font2_nr = FONT_TEXT_1;
2813 #endif
2814   int font_nr = font1_nr;
2815   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2816
2817 #if 1
2818   return;       /* !!! USE NEW STUFF !!! */
2819 #endif
2820
2821   if (PANEL_DEACTIVATED(pos))
2822     return;
2823
2824   if (use_dynamic_chars)                /* use dynamic number of chars */
2825   {
2826     chars   = (level_nr < 100 ? chars1   : chars2);
2827     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2828   }
2829
2830   pos->width = chars * getFontWidth(font_nr);
2831
2832   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2833 }
2834
2835 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2836 {
2837 #if 0
2838   struct TextPosInfo *pos = &game.panel.keys;
2839 #endif
2840 #if 0
2841   int base_key_graphic = EL_KEY_1;
2842 #endif
2843   int i;
2844
2845 #if 1
2846   return;       /* !!! USE NEW STUFF !!! */
2847 #endif
2848
2849 #if 0
2850   if (PANEL_DEACTIVATED(pos))
2851     return;
2852 #endif
2853
2854 #if 0
2855   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2856     base_key_graphic = EL_EM_KEY_1;
2857 #endif
2858
2859 #if 0
2860   pos->width = 4 * MINI_TILEX;
2861 #endif
2862
2863 #if 1
2864   for (i = 0; i < MAX_NUM_KEYS; i++)
2865 #else
2866   /* currently only 4 of 8 possible keys are displayed */
2867   for (i = 0; i < STD_NUM_KEYS; i++)
2868 #endif
2869   {
2870 #if 1
2871     struct TextPosInfo *pos = &game.panel.key[i];
2872 #endif
2873     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2874     int src_y = DOOR_GFX_PAGEY1 + 123;
2875 #if 1
2876     int dst_x = PANEL_XPOS(pos);
2877     int dst_y = PANEL_YPOS(pos);
2878 #else
2879     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2880     int dst_y = PANEL_YPOS(pos);
2881 #endif
2882
2883 #if 1
2884     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2885                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2886                    EL_KEY_1) + i;
2887     int graphic = el2edimg(element);
2888 #endif
2889
2890 #if 1
2891     if (PANEL_DEACTIVATED(pos))
2892       continue;
2893 #endif
2894
2895 #if 0
2896     /* masked blit with tiles from half-size scaled bitmap does not work yet
2897        (no mask bitmap created for these sizes after loading and scaling) --
2898        solution: load without creating mask, scale, then create final mask */
2899
2900     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2901                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2902
2903     if (key[i])
2904     {
2905 #if 0
2906       int graphic = el2edimg(base_key_graphic + i);
2907 #endif
2908       Bitmap *src_bitmap;
2909       int src_x, src_y;
2910
2911       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2912
2913       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2914                     dst_x - src_x, dst_y - src_y);
2915       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2916                        dst_x, dst_y);
2917     }
2918 #else
2919 #if 1
2920     if (key[i])
2921       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2922     else
2923       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2924                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2925 #else
2926     if (key[i])
2927       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2928     else
2929       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2930                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2931 #endif
2932 #endif
2933   }
2934 }
2935
2936 #else
2937
2938 void DrawGameValue_Emeralds(int value)
2939 {
2940   int font_nr = FONT_TEXT_2;
2941   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2942
2943   if (PANEL_DEACTIVATED(game.panel.gems))
2944     return;
2945
2946   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2947 }
2948
2949 void DrawGameValue_Dynamite(int value)
2950 {
2951   int font_nr = FONT_TEXT_2;
2952   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2953
2954   if (PANEL_DEACTIVATED(game.panel.inventory_count))
2955     return;
2956
2957   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2958 }
2959
2960 void DrawGameValue_Score(int value)
2961 {
2962   int font_nr = FONT_TEXT_2;
2963   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2964
2965   if (PANEL_DEACTIVATED(game.panel.score))
2966     return;
2967
2968   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2969 }
2970
2971 void DrawGameValue_Time(int value)
2972 {
2973   int font1_nr = FONT_TEXT_2;
2974 #if 1
2975   int font2_nr = FONT_TEXT_1;
2976 #else
2977   int font2_nr = FONT_LEVEL_NUMBER;
2978 #endif
2979   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2980   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2981
2982   if (PANEL_DEACTIVATED(game.panel.time))
2983     return;
2984
2985   /* clear background if value just changed its size */
2986   if (value == 999 || value == 1000)
2987     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2988
2989   if (value < 1000)
2990     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2991   else
2992     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2993 }
2994
2995 void DrawGameValue_Level(int value)
2996 {
2997   int font1_nr = FONT_TEXT_2;
2998 #if 1
2999   int font2_nr = FONT_TEXT_1;
3000 #else
3001   int font2_nr = FONT_LEVEL_NUMBER;
3002 #endif
3003
3004   if (PANEL_DEACTIVATED(game.panel.level))
3005     return;
3006
3007   if (level_nr < 100)
3008     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3009   else
3010     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3011 }
3012
3013 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3014 {
3015   int base_key_graphic = EL_KEY_1;
3016   int i;
3017
3018   if (PANEL_DEACTIVATED(game.panel.keys))
3019     return;
3020
3021   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3022     base_key_graphic = EL_EM_KEY_1;
3023
3024   /* currently only 4 of 8 possible keys are displayed */
3025   for (i = 0; i < STD_NUM_KEYS; i++)
3026   {
3027     int x = XX_KEYS + i * MINI_TILEX;
3028     int y = YY_KEYS;
3029
3030     if (key[i])
3031       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3032     else
3033       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3034                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3035   }
3036 }
3037
3038 #endif
3039
3040 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3041                        int key_bits)
3042 {
3043   int key[MAX_NUM_KEYS];
3044   int i;
3045
3046   /* prevent EM engine from updating time/score values parallel to GameWon() */
3047   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3048       local_player->LevelSolved)
3049     return;
3050
3051   for (i = 0; i < MAX_NUM_KEYS; i++)
3052     key[i] = key_bits & (1 << i);
3053
3054   DrawGameValue_Level(level_nr);
3055
3056   DrawGameValue_Emeralds(emeralds);
3057   DrawGameValue_Dynamite(dynamite);
3058   DrawGameValue_Score(score);
3059   DrawGameValue_Time(time);
3060
3061   DrawGameValue_Keys(key);
3062 }
3063
3064 void UpdateGameDoorValues()
3065 {
3066   UpdateGameControlValues();
3067 }
3068
3069 void DrawGameDoorValues()
3070 {
3071   DisplayGameControlValues();
3072 }
3073
3074 void DrawGameDoorValues_OLD()
3075 {
3076   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3077   int dynamite_value = 0;
3078   int score_value = (local_player->LevelSolved ? local_player->score_final :
3079                      local_player->score);
3080   int gems_value = local_player->gems_still_needed;
3081   int key_bits = 0;
3082   int i, j;
3083
3084   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3085   {
3086     DrawGameDoorValues_EM();
3087
3088     return;
3089   }
3090
3091   if (game.centered_player_nr == -1)
3092   {
3093     for (i = 0; i < MAX_PLAYERS; i++)
3094     {
3095       for (j = 0; j < MAX_NUM_KEYS; j++)
3096         if (stored_player[i].key[j])
3097           key_bits |= (1 << j);
3098
3099       dynamite_value += stored_player[i].inventory_size;
3100     }
3101   }
3102   else
3103   {
3104     int player_nr = game.centered_player_nr;
3105
3106     for (i = 0; i < MAX_NUM_KEYS; i++)
3107       if (stored_player[player_nr].key[i])
3108         key_bits |= (1 << i);
3109
3110     dynamite_value = stored_player[player_nr].inventory_size;
3111   }
3112
3113   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3114                     key_bits);
3115 }
3116
3117
3118 /*
3119   =============================================================================
3120   InitGameEngine()
3121   -----------------------------------------------------------------------------
3122   initialize game engine due to level / tape version number
3123   =============================================================================
3124 */
3125
3126 static void InitGameEngine()
3127 {
3128   int i, j, k, l, x, y;
3129
3130   /* set game engine from tape file when re-playing, else from level file */
3131   game.engine_version = (tape.playing ? tape.engine_version :
3132                          level.game_version);
3133
3134   /* ---------------------------------------------------------------------- */
3135   /* set flags for bugs and changes according to active game engine version */
3136   /* ---------------------------------------------------------------------- */
3137
3138   /*
3139     Summary of bugfix/change:
3140     Fixed handling for custom elements that change when pushed by the player.
3141
3142     Fixed/changed in version:
3143     3.1.0
3144
3145     Description:
3146     Before 3.1.0, custom elements that "change when pushing" changed directly
3147     after the player started pushing them (until then handled in "DigField()").
3148     Since 3.1.0, these custom elements are not changed until the "pushing"
3149     move of the element is finished (now handled in "ContinueMoving()").
3150
3151     Affected levels/tapes:
3152     The first condition is generally needed for all levels/tapes before version
3153     3.1.0, which might use the old behaviour before it was changed; known tapes
3154     that are affected are some tapes from the level set "Walpurgis Gardens" by
3155     Jamie Cullen.
3156     The second condition is an exception from the above case and is needed for
3157     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3158     above (including some development versions of 3.1.0), but before it was
3159     known that this change would break tapes like the above and was fixed in
3160     3.1.1, so that the changed behaviour was active although the engine version
3161     while recording maybe was before 3.1.0. There is at least one tape that is
3162     affected by this exception, which is the tape for the one-level set "Bug
3163     Machine" by Juergen Bonhagen.
3164   */
3165
3166   game.use_change_when_pushing_bug =
3167     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3168      !(tape.playing &&
3169        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3170        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3171
3172   /*
3173     Summary of bugfix/change:
3174     Fixed handling for blocking the field the player leaves when moving.
3175
3176     Fixed/changed in version:
3177     3.1.1
3178
3179     Description:
3180     Before 3.1.1, when "block last field when moving" was enabled, the field
3181     the player is leaving when moving was blocked for the time of the move,
3182     and was directly unblocked afterwards. This resulted in the last field
3183     being blocked for exactly one less than the number of frames of one player
3184     move. Additionally, even when blocking was disabled, the last field was
3185     blocked for exactly one frame.
3186     Since 3.1.1, due to changes in player movement handling, the last field
3187     is not blocked at all when blocking is disabled. When blocking is enabled,
3188     the last field is blocked for exactly the number of frames of one player
3189     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3190     last field is blocked for exactly one more than the number of frames of
3191     one player move.
3192
3193     Affected levels/tapes:
3194     (!!! yet to be determined -- probably many !!!)
3195   */
3196
3197   game.use_block_last_field_bug =
3198     (game.engine_version < VERSION_IDENT(3,1,1,0));
3199
3200   /*
3201     Summary of bugfix/change:
3202     Changed behaviour of CE changes with multiple changes per single frame.
3203
3204     Fixed/changed in version:
3205     3.2.0-6
3206
3207     Description:
3208     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3209     This resulted in race conditions where CEs seem to behave strange in some
3210     situations (where triggered CE changes were just skipped because there was
3211     already a CE change on that tile in the playfield in that engine frame).
3212     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3213     (The number of changes per frame must be limited in any case, because else
3214     it is easily possible to define CE changes that would result in an infinite
3215     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3216     should be set large enough so that it would only be reached in cases where
3217     the corresponding CE change conditions run into a loop. Therefore, it seems
3218     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3219     maximal number of change pages for custom elements.)
3220
3221     Affected levels/tapes:
3222     Probably many.
3223   */
3224
3225 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3226   game.max_num_changes_per_frame = 1;
3227 #else
3228   game.max_num_changes_per_frame =
3229     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3230 #endif
3231
3232   /* ---------------------------------------------------------------------- */
3233
3234   /* default scan direction: scan playfield from top/left to bottom/right */
3235   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3236
3237   /* dynamically adjust element properties according to game engine version */
3238   InitElementPropertiesEngine(game.engine_version);
3239
3240 #if 0
3241   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3242   printf("          tape version == %06d [%s] [file: %06d]\n",
3243          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3244          tape.file_version);
3245   printf("       => game.engine_version == %06d\n", game.engine_version);
3246 #endif
3247
3248   /* ---------- initialize player's initial move delay --------------------- */
3249
3250   /* dynamically adjust player properties according to level information */
3251   for (i = 0; i < MAX_PLAYERS; i++)
3252     game.initial_move_delay_value[i] =
3253       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3254
3255   /* dynamically adjust player properties according to game engine version */
3256   for (i = 0; i < MAX_PLAYERS; i++)
3257     game.initial_move_delay[i] =
3258       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3259        game.initial_move_delay_value[i] : 0);
3260
3261   /* ---------- initialize player's initial push delay --------------------- */
3262
3263   /* dynamically adjust player properties according to game engine version */
3264   game.initial_push_delay_value =
3265     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3266
3267   /* ---------- initialize changing elements ------------------------------- */
3268
3269   /* initialize changing elements information */
3270   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3271   {
3272     struct ElementInfo *ei = &element_info[i];
3273
3274     /* this pointer might have been changed in the level editor */
3275     ei->change = &ei->change_page[0];
3276
3277     if (!IS_CUSTOM_ELEMENT(i))
3278     {
3279       ei->change->target_element = EL_EMPTY_SPACE;
3280       ei->change->delay_fixed = 0;
3281       ei->change->delay_random = 0;
3282       ei->change->delay_frames = 1;
3283     }
3284
3285     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3286     {
3287       ei->has_change_event[j] = FALSE;
3288
3289       ei->event_page_nr[j] = 0;
3290       ei->event_page[j] = &ei->change_page[0];
3291     }
3292   }
3293
3294   /* add changing elements from pre-defined list */
3295   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3296   {
3297     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3298     struct ElementInfo *ei = &element_info[ch_delay->element];
3299
3300     ei->change->target_element       = ch_delay->target_element;
3301     ei->change->delay_fixed          = ch_delay->change_delay;
3302
3303     ei->change->pre_change_function  = ch_delay->pre_change_function;
3304     ei->change->change_function      = ch_delay->change_function;
3305     ei->change->post_change_function = ch_delay->post_change_function;
3306
3307     ei->change->can_change = TRUE;
3308     ei->change->can_change_or_has_action = TRUE;
3309
3310     ei->has_change_event[CE_DELAY] = TRUE;
3311
3312     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3313     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3314   }
3315
3316   /* ---------- initialize internal run-time variables ------------- */
3317
3318   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3319   {
3320     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3321
3322     for (j = 0; j < ei->num_change_pages; j++)
3323     {
3324       ei->change_page[j].can_change_or_has_action =
3325         (ei->change_page[j].can_change |
3326          ei->change_page[j].has_action);
3327     }
3328   }
3329
3330   /* add change events from custom element configuration */
3331   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3332   {
3333     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3334
3335     for (j = 0; j < ei->num_change_pages; j++)
3336     {
3337       if (!ei->change_page[j].can_change_or_has_action)
3338         continue;
3339
3340       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3341       {
3342         /* only add event page for the first page found with this event */
3343         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3344         {
3345           ei->has_change_event[k] = TRUE;
3346
3347           ei->event_page_nr[k] = j;
3348           ei->event_page[k] = &ei->change_page[j];
3349         }
3350       }
3351     }
3352   }
3353
3354   /* ---------- initialize run-time trigger player and element ------------- */
3355
3356   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3357   {
3358     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3359
3360     for (j = 0; j < ei->num_change_pages; j++)
3361     {
3362       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3363       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3364       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
3365       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3366       ei->change_page[j].actual_trigger_ce_value = 0;
3367       ei->change_page[j].actual_trigger_ce_score = 0;
3368     }
3369   }
3370
3371   /* ---------- initialize trigger events ---------------------------------- */
3372
3373   /* initialize trigger events information */
3374   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3375     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3376       trigger_events[i][j] = FALSE;
3377
3378   /* add trigger events from element change event properties */
3379   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3380   {
3381     struct ElementInfo *ei = &element_info[i];
3382
3383     for (j = 0; j < ei->num_change_pages; j++)
3384     {
3385       if (!ei->change_page[j].can_change_or_has_action)
3386         continue;
3387
3388       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3389       {
3390         int trigger_element = ei->change_page[j].trigger_element;
3391
3392         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3393         {
3394           if (ei->change_page[j].has_event[k])
3395           {
3396             if (IS_GROUP_ELEMENT(trigger_element))
3397             {
3398               struct ElementGroupInfo *group =
3399                 element_info[trigger_element].group;
3400
3401               for (l = 0; l < group->num_elements_resolved; l++)
3402                 trigger_events[group->element_resolved[l]][k] = TRUE;
3403             }
3404             else if (trigger_element == EL_ANY_ELEMENT)
3405               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3406                 trigger_events[l][k] = TRUE;
3407             else
3408               trigger_events[trigger_element][k] = TRUE;
3409           }
3410         }
3411       }
3412     }
3413   }
3414
3415   /* ---------- initialize push delay -------------------------------------- */
3416
3417   /* initialize push delay values to default */
3418   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3419   {
3420     if (!IS_CUSTOM_ELEMENT(i))
3421     {
3422       /* set default push delay values (corrected since version 3.0.7-1) */
3423       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3424       {
3425         element_info[i].push_delay_fixed = 2;
3426         element_info[i].push_delay_random = 8;
3427       }
3428       else
3429       {
3430         element_info[i].push_delay_fixed = 8;
3431         element_info[i].push_delay_random = 8;
3432       }
3433     }
3434   }
3435
3436   /* set push delay value for certain elements from pre-defined list */
3437   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3438   {
3439     int e = push_delay_list[i].element;
3440
3441     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3442     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3443   }
3444
3445   /* set push delay value for Supaplex elements for newer engine versions */
3446   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3447   {
3448     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3449     {
3450       if (IS_SP_ELEMENT(i))
3451       {
3452         /* set SP push delay to just enough to push under a falling zonk */
3453         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3454
3455         element_info[i].push_delay_fixed  = delay;
3456         element_info[i].push_delay_random = 0;
3457       }
3458     }
3459   }
3460
3461   /* ---------- initialize move stepsize ----------------------------------- */
3462
3463   /* initialize move stepsize values to default */
3464   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3465     if (!IS_CUSTOM_ELEMENT(i))
3466       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3467
3468   /* set move stepsize value for certain elements from pre-defined list */
3469   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3470   {
3471     int e = move_stepsize_list[i].element;
3472
3473     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3474   }
3475
3476   /* ---------- initialize collect score ----------------------------------- */
3477
3478   /* initialize collect score values for custom elements from initial value */
3479   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3480     if (IS_CUSTOM_ELEMENT(i))
3481       element_info[i].collect_score = element_info[i].collect_score_initial;
3482
3483   /* ---------- initialize collect count ----------------------------------- */
3484
3485   /* initialize collect count values for non-custom elements */
3486   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3487     if (!IS_CUSTOM_ELEMENT(i))
3488       element_info[i].collect_count_initial = 0;
3489
3490   /* add collect count values for all elements from pre-defined list */
3491   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3492     element_info[collect_count_list[i].element].collect_count_initial =
3493       collect_count_list[i].count;
3494
3495   /* ---------- initialize access direction -------------------------------- */
3496
3497   /* initialize access direction values to default (access from every side) */
3498   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3499     if (!IS_CUSTOM_ELEMENT(i))
3500       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3501
3502   /* set access direction value for certain elements from pre-defined list */
3503   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3504     element_info[access_direction_list[i].element].access_direction =
3505       access_direction_list[i].direction;
3506
3507   /* ---------- initialize explosion content ------------------------------- */
3508   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3509   {
3510     if (IS_CUSTOM_ELEMENT(i))
3511       continue;
3512
3513     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3514     {
3515       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3516
3517       element_info[i].content.e[x][y] =
3518         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3519          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3520          i == EL_PLAYER_3 ? EL_EMERALD :
3521          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3522          i == EL_MOLE ? EL_EMERALD_RED :
3523          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3524          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3525          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3526          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3527          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3528          i == EL_WALL_EMERALD ? EL_EMERALD :
3529          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3530          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3531          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3532          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3533          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3534          i == EL_WALL_PEARL ? EL_PEARL :
3535          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3536          EL_EMPTY);
3537     }
3538   }
3539
3540   /* ---------- initialize recursion detection ------------------------------ */
3541   recursion_loop_depth = 0;
3542   recursion_loop_detected = FALSE;
3543   recursion_loop_element = EL_UNDEFINED;
3544
3545   /* ---------- initialize graphics engine ---------------------------------- */
3546   game.scroll_delay_value =
3547     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3548      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3549   game.scroll_delay_value =
3550     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3551 }
3552
3553 int get_num_special_action(int element, int action_first, int action_last)
3554 {
3555   int num_special_action = 0;
3556   int i, j;
3557
3558   for (i = action_first; i <= action_last; i++)
3559   {
3560     boolean found = FALSE;
3561
3562     for (j = 0; j < NUM_DIRECTIONS; j++)
3563       if (el_act_dir2img(element, i, j) !=
3564           el_act_dir2img(element, ACTION_DEFAULT, j))
3565         found = TRUE;
3566
3567     if (found)
3568       num_special_action++;
3569     else
3570       break;
3571   }
3572
3573   return num_special_action;
3574 }
3575
3576
3577 /*
3578   =============================================================================
3579   InitGame()
3580   -----------------------------------------------------------------------------
3581   initialize and start new game
3582   =============================================================================
3583 */
3584
3585 void InitGame()
3586 {
3587   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3588   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3589   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3590 #if 0
3591   boolean do_fading = (game_status == GAME_MODE_MAIN);
3592 #endif
3593   int i, j, x, y;
3594
3595   game_status = GAME_MODE_PLAYING;
3596
3597   InitGameEngine();
3598   InitGameControlValues();
3599
3600   /* don't play tapes over network */
3601   network_playing = (options.network && !tape.playing);
3602
3603   for (i = 0; i < MAX_PLAYERS; i++)
3604   {
3605     struct PlayerInfo *player = &stored_player[i];
3606
3607     player->index_nr = i;
3608     player->index_bit = (1 << i);
3609     player->element_nr = EL_PLAYER_1 + i;
3610
3611     player->present = FALSE;
3612     player->active = FALSE;
3613     player->killed = FALSE;
3614
3615     player->action = 0;
3616     player->effective_action = 0;
3617     player->programmed_action = 0;
3618
3619     player->score = 0;
3620     player->score_final = 0;
3621
3622     player->gems_still_needed = level.gems_needed;
3623     player->sokobanfields_still_needed = 0;
3624     player->lights_still_needed = 0;
3625     player->friends_still_needed = 0;
3626
3627     for (j = 0; j < MAX_NUM_KEYS; j++)
3628       player->key[j] = FALSE;
3629
3630     player->num_white_keys = 0;
3631
3632     player->dynabomb_count = 0;
3633     player->dynabomb_size = 1;
3634     player->dynabombs_left = 0;
3635     player->dynabomb_xl = FALSE;
3636
3637     player->MovDir = MV_NONE;
3638     player->MovPos = 0;
3639     player->GfxPos = 0;
3640     player->GfxDir = MV_NONE;
3641     player->GfxAction = ACTION_DEFAULT;
3642     player->Frame = 0;
3643     player->StepFrame = 0;
3644
3645     player->use_murphy = FALSE;
3646     player->artwork_element =
3647       (level.use_artwork_element[i] ? level.artwork_element[i] :
3648        player->element_nr);
3649
3650     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3651     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3652
3653     player->gravity = level.initial_player_gravity[i];
3654
3655     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3656
3657     player->actual_frame_counter = 0;
3658
3659     player->step_counter = 0;
3660
3661     player->last_move_dir = MV_NONE;
3662
3663     player->is_active = FALSE;
3664
3665     player->is_waiting = FALSE;
3666     player->is_moving = FALSE;
3667     player->is_auto_moving = FALSE;
3668     player->is_digging = FALSE;
3669     player->is_snapping = FALSE;
3670     player->is_collecting = FALSE;
3671     player->is_pushing = FALSE;
3672     player->is_switching = FALSE;
3673     player->is_dropping = FALSE;
3674     player->is_dropping_pressed = FALSE;
3675
3676     player->is_bored = FALSE;
3677     player->is_sleeping = FALSE;
3678
3679     player->frame_counter_bored = -1;
3680     player->frame_counter_sleeping = -1;
3681
3682     player->anim_delay_counter = 0;
3683     player->post_delay_counter = 0;
3684
3685     player->dir_waiting = MV_NONE;
3686     player->action_waiting = ACTION_DEFAULT;
3687     player->last_action_waiting = ACTION_DEFAULT;
3688     player->special_action_bored = ACTION_DEFAULT;
3689     player->special_action_sleeping = ACTION_DEFAULT;
3690
3691     player->switch_x = -1;
3692     player->switch_y = -1;
3693
3694     player->drop_x = -1;
3695     player->drop_y = -1;
3696
3697     player->show_envelope = 0;
3698
3699     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3700
3701     player->push_delay       = -1;      /* initialized when pushing starts */
3702     player->push_delay_value = game.initial_push_delay_value;
3703
3704     player->drop_delay = 0;
3705     player->drop_pressed_delay = 0;
3706
3707     player->last_jx = -1;
3708     player->last_jy = -1;
3709     player->jx = -1;
3710     player->jy = -1;
3711
3712     player->shield_normal_time_left = 0;
3713     player->shield_deadly_time_left = 0;
3714
3715     player->inventory_infinite_element = EL_UNDEFINED;
3716     player->inventory_size = 0;
3717
3718     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3719     SnapField(player, 0, 0);
3720
3721     player->LevelSolved = FALSE;
3722     player->GameOver = FALSE;
3723
3724     player->LevelSolved_GameWon = FALSE;
3725     player->LevelSolved_GameEnd = FALSE;
3726     player->LevelSolved_PanelOff = FALSE;
3727     player->LevelSolved_SaveTape = FALSE;
3728     player->LevelSolved_SaveScore = FALSE;
3729     player->LevelSolved_CountingTime = 0;
3730     player->LevelSolved_CountingScore = 0;
3731   }
3732
3733   network_player_action_received = FALSE;
3734
3735 #if defined(NETWORK_AVALIABLE)
3736   /* initial null action */
3737   if (network_playing)
3738     SendToServer_MovePlayer(MV_NONE);
3739 #endif
3740
3741   ZX = ZY = -1;
3742   ExitX = ExitY = -1;
3743
3744   FrameCounter = 0;
3745   TimeFrames = 0;
3746   TimePlayed = 0;
3747   TimeLeft = level.time;
3748   TapeTime = 0;
3749
3750   ScreenMovDir = MV_NONE;
3751   ScreenMovPos = 0;
3752   ScreenGfxPos = 0;
3753
3754   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3755
3756   AllPlayersGone = FALSE;
3757
3758   game.yamyam_content_nr = 0;
3759   game.robot_wheel_active = FALSE;
3760   game.magic_wall_active = FALSE;
3761   game.magic_wall_time_left = 0;
3762   game.light_time_left = 0;
3763   game.timegate_time_left = 0;
3764   game.switchgate_pos = 0;
3765   game.wind_direction = level.wind_direction_initial;
3766
3767 #if !USE_PLAYER_GRAVITY
3768   game.gravity = FALSE;
3769   game.explosions_delayed = TRUE;
3770 #endif
3771
3772   game.lenses_time_left = 0;
3773   game.magnify_time_left = 0;
3774
3775   game.ball_state = level.ball_state_initial;
3776   game.ball_content_nr = 0;
3777
3778   game.envelope_active = FALSE;
3779
3780   /* set focus to local player for network games, else to all players */
3781   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3782   game.centered_player_nr_next = game.centered_player_nr;
3783   game.set_centered_player = FALSE;
3784
3785   if (network_playing && tape.recording)
3786   {
3787     /* store client dependent player focus when recording network games */
3788     tape.centered_player_nr_next = game.centered_player_nr_next;
3789     tape.set_centered_player = TRUE;
3790   }
3791
3792   for (i = 0; i < NUM_BELTS; i++)
3793   {
3794     game.belt_dir[i] = MV_NONE;
3795     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3796   }
3797
3798   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3799     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3800
3801   SCAN_PLAYFIELD(x, y)
3802   {
3803     Feld[x][y] = level.field[x][y];
3804     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3805     ChangeDelay[x][y] = 0;
3806     ChangePage[x][y] = -1;
3807 #if USE_NEW_CUSTOM_VALUE
3808     CustomValue[x][y] = 0;              /* initialized in InitField() */
3809 #endif
3810     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3811     AmoebaNr[x][y] = 0;
3812     WasJustMoving[x][y] = 0;
3813     WasJustFalling[x][y] = 0;
3814     CheckCollision[x][y] = 0;
3815     CheckImpact[x][y] = 0;
3816     Stop[x][y] = FALSE;
3817     Pushed[x][y] = FALSE;
3818
3819     ChangeCount[x][y] = 0;
3820     ChangeEvent[x][y] = -1;
3821
3822     ExplodePhase[x][y] = 0;
3823     ExplodeDelay[x][y] = 0;
3824     ExplodeField[x][y] = EX_TYPE_NONE;
3825
3826     RunnerVisit[x][y] = 0;
3827     PlayerVisit[x][y] = 0;
3828
3829     GfxFrame[x][y] = 0;
3830     GfxRandom[x][y] = INIT_GFX_RANDOM();
3831     GfxElement[x][y] = EL_UNDEFINED;
3832     GfxAction[x][y] = ACTION_DEFAULT;
3833     GfxDir[x][y] = MV_NONE;
3834   }
3835
3836   SCAN_PLAYFIELD(x, y)
3837   {
3838     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3839       emulate_bd = FALSE;
3840     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3841       emulate_sb = FALSE;
3842     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3843       emulate_sp = FALSE;
3844
3845     InitField(x, y, TRUE);
3846
3847     ResetGfxAnimation(x, y);
3848   }
3849
3850   InitBeltMovement();
3851
3852   for (i = 0; i < MAX_PLAYERS; i++)
3853   {
3854     struct PlayerInfo *player = &stored_player[i];
3855
3856     /* set number of special actions for bored and sleeping animation */
3857     player->num_special_action_bored =
3858       get_num_special_action(player->artwork_element,
3859                              ACTION_BORING_1, ACTION_BORING_LAST);
3860     player->num_special_action_sleeping =
3861       get_num_special_action(player->artwork_element,
3862                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3863   }
3864
3865   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3866                     emulate_sb ? EMU_SOKOBAN :
3867                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3868
3869 #if USE_NEW_ALL_SLIPPERY
3870   /* initialize type of slippery elements */
3871   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3872   {
3873     if (!IS_CUSTOM_ELEMENT(i))
3874     {
3875       /* default: elements slip down either to the left or right randomly */
3876       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3877
3878       /* SP style elements prefer to slip down on the left side */
3879       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3880         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3881
3882       /* BD style elements prefer to slip down on the left side */
3883       if (game.emulation == EMU_BOULDERDASH)
3884         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3885     }
3886   }
3887 #endif
3888
3889   /* initialize explosion and ignition delay */
3890   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3891   {
3892     if (!IS_CUSTOM_ELEMENT(i))
3893     {
3894       int num_phase = 8;
3895       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3896                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3897                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3898       int last_phase = (num_phase + 1) * delay;
3899       int half_phase = (num_phase / 2) * delay;
3900
3901       element_info[i].explosion_delay = last_phase - 1;
3902       element_info[i].ignition_delay = half_phase;
3903
3904       if (i == EL_BLACK_ORB)
3905         element_info[i].ignition_delay = 1;
3906     }
3907
3908 #if 0
3909     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3910       element_info[i].explosion_delay = 1;
3911
3912     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3913       element_info[i].ignition_delay = 1;
3914 #endif
3915   }
3916
3917   /* correct non-moving belts to start moving left */
3918   for (i = 0; i < NUM_BELTS; i++)
3919     if (game.belt_dir[i] == MV_NONE)
3920       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3921
3922   /* check if any connected player was not found in playfield */
3923   for (i = 0; i < MAX_PLAYERS; i++)
3924   {
3925     struct PlayerInfo *player = &stored_player[i];
3926
3927     if (player->connected && !player->present)
3928     {
3929       for (j = 0; j < MAX_PLAYERS; j++)
3930       {
3931         struct PlayerInfo *some_player = &stored_player[j];
3932         int jx = some_player->jx, jy = some_player->jy;
3933
3934         /* assign first free player found that is present in the playfield */
3935         if (some_player->present && !some_player->connected)
3936         {
3937           player->present = TRUE;
3938           player->active = TRUE;
3939
3940           some_player->present = FALSE;
3941           some_player->active = FALSE;
3942
3943           player->artwork_element = some_player->artwork_element;
3944
3945           player->block_last_field       = some_player->block_last_field;
3946           player->block_delay_adjustment = some_player->block_delay_adjustment;
3947
3948           StorePlayer[jx][jy] = player->element_nr;
3949           player->jx = player->last_jx = jx;
3950           player->jy = player->last_jy = jy;
3951
3952           break;
3953         }
3954       }
3955     }
3956   }
3957
3958   if (tape.playing)
3959   {
3960     /* when playing a tape, eliminate all players who do not participate */
3961
3962     for (i = 0; i < MAX_PLAYERS; i++)
3963     {
3964       if (stored_player[i].active && !tape.player_participates[i])
3965       {
3966         struct PlayerInfo *player = &stored_player[i];
3967         int jx = player->jx, jy = player->jy;
3968
3969         player->active = FALSE;
3970         StorePlayer[jx][jy] = 0;
3971         Feld[jx][jy] = EL_EMPTY;
3972       }
3973     }
3974   }
3975   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
3976   {
3977     /* when in single player mode, eliminate all but the first active player */
3978
3979     for (i = 0; i < MAX_PLAYERS; i++)
3980     {
3981       if (stored_player[i].active)
3982       {
3983         for (j = i + 1; j < MAX_PLAYERS; j++)
3984         {
3985           if (stored_player[j].active)
3986           {
3987             struct PlayerInfo *player = &stored_player[j];
3988             int jx = player->jx, jy = player->jy;
3989
3990             player->active = FALSE;
3991             player->present = FALSE;
3992
3993             StorePlayer[jx][jy] = 0;
3994             Feld[jx][jy] = EL_EMPTY;
3995           }
3996         }
3997       }
3998     }
3999   }
4000
4001   /* when recording the game, store which players take part in the game */
4002   if (tape.recording)
4003   {
4004     for (i = 0; i < MAX_PLAYERS; i++)
4005       if (stored_player[i].active)
4006         tape.player_participates[i] = TRUE;
4007   }
4008
4009   if (options.debug)
4010   {
4011     for (i = 0; i < MAX_PLAYERS; i++)
4012     {
4013       struct PlayerInfo *player = &stored_player[i];
4014
4015       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4016              i+1,
4017              player->present,
4018              player->connected,
4019              player->active);
4020       if (local_player == player)
4021         printf("Player  %d is local player.\n", i+1);
4022     }
4023   }
4024
4025   if (BorderElement == EL_EMPTY)
4026   {
4027     SBX_Left = 0;
4028     SBX_Right = lev_fieldx - SCR_FIELDX;
4029     SBY_Upper = 0;
4030     SBY_Lower = lev_fieldy - SCR_FIELDY;
4031   }
4032   else
4033   {
4034     SBX_Left = -1;
4035     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4036     SBY_Upper = -1;
4037     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4038   }
4039
4040   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4041     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4042
4043   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4044     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4045
4046   /* if local player not found, look for custom element that might create
4047      the player (make some assumptions about the right custom element) */
4048   if (!local_player->present)
4049   {
4050     int start_x = 0, start_y = 0;
4051     int found_rating = 0;
4052     int found_element = EL_UNDEFINED;
4053     int player_nr = local_player->index_nr;
4054
4055     SCAN_PLAYFIELD(x, y)
4056     {
4057       int element = Feld[x][y];
4058       int content;
4059       int xx, yy;
4060       boolean is_player;
4061
4062       if (level.use_start_element[player_nr] &&
4063           level.start_element[player_nr] == element &&
4064           found_rating < 4)
4065       {
4066         start_x = x;
4067         start_y = y;
4068
4069         found_rating = 4;
4070         found_element = element;
4071       }
4072
4073       if (!IS_CUSTOM_ELEMENT(element))
4074         continue;
4075
4076       if (CAN_CHANGE(element))
4077       {
4078         for (i = 0; i < element_info[element].num_change_pages; i++)
4079         {
4080           /* check for player created from custom element as single target */
4081           content = element_info[element].change_page[i].target_element;
4082           is_player = ELEM_IS_PLAYER(content);
4083
4084           if (is_player && (found_rating < 3 ||
4085                             (found_rating == 3 && element < found_element)))
4086           {
4087             start_x = x;
4088             start_y = y;
4089
4090             found_rating = 3;
4091             found_element = element;
4092           }
4093         }
4094       }
4095
4096       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4097       {
4098         /* check for player created from custom element as explosion content */
4099         content = element_info[element].content.e[xx][yy];
4100         is_player = ELEM_IS_PLAYER(content);
4101
4102         if (is_player && (found_rating < 2 ||
4103                           (found_rating == 2 && element < found_element)))
4104         {
4105           start_x = x + xx - 1;
4106           start_y = y + yy - 1;
4107
4108           found_rating = 2;
4109           found_element = element;
4110         }
4111
4112         if (!CAN_CHANGE(element))
4113           continue;
4114
4115         for (i = 0; i < element_info[element].num_change_pages; i++)
4116         {
4117           /* check for player created from custom element as extended target */
4118           content =
4119             element_info[element].change_page[i].target_content.e[xx][yy];
4120
4121           is_player = ELEM_IS_PLAYER(content);
4122
4123           if (is_player && (found_rating < 1 ||
4124                             (found_rating == 1 && element < found_element)))
4125           {
4126             start_x = x + xx - 1;
4127             start_y = y + yy - 1;
4128
4129             found_rating = 1;
4130             found_element = element;
4131           }
4132         }
4133       }
4134     }
4135
4136     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4137                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4138                 start_x - MIDPOSX);
4139
4140     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4141                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4142                 start_y - MIDPOSY);
4143   }
4144   else
4145   {
4146     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4147                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4148                 local_player->jx - MIDPOSX);
4149
4150     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4151                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4152                 local_player->jy - MIDPOSY);
4153   }
4154
4155 #if 0
4156   /* do not use PLAYING mask for fading out from main screen */
4157   game_status = GAME_MODE_MAIN;
4158 #endif
4159
4160   StopAnimation();
4161
4162   if (!game.restart_level)
4163     CloseDoor(DOOR_CLOSE_1);
4164
4165 #if 1
4166   if (level_editor_test_game)
4167     FadeSkipNextFadeIn();
4168   else
4169     FadeSetEnterScreen();
4170 #else
4171   if (level_editor_test_game)
4172     fading = fading_none;
4173   else
4174     fading = menu.destination;
4175 #endif
4176
4177 #if 1
4178   FadeOut(REDRAW_FIELD);
4179 #else
4180   if (do_fading)
4181     FadeOut(REDRAW_FIELD);
4182 #endif
4183
4184 #if 0
4185   game_status = GAME_MODE_PLAYING;
4186 #endif
4187
4188   /* !!! FIX THIS (START) !!! */
4189   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4190   {
4191     InitGameEngine_EM();
4192
4193     /* blit playfield from scroll buffer to normal back buffer for fading in */
4194     BlitScreenToBitmap_EM(backbuffer);
4195   }
4196   else
4197   {
4198     DrawLevel();
4199     DrawAllPlayers();
4200
4201     /* after drawing the level, correct some elements */
4202     if (game.timegate_time_left == 0)
4203       CloseAllOpenTimegates();
4204
4205     /* blit playfield from scroll buffer to normal back buffer for fading in */
4206     if (setup.soft_scrolling)
4207       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4208
4209     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4210   }
4211   /* !!! FIX THIS (END) !!! */
4212
4213 #if 1
4214   FadeIn(REDRAW_FIELD);
4215 #else
4216   if (do_fading)
4217     FadeIn(REDRAW_FIELD);
4218
4219   BackToFront();
4220 #endif
4221
4222   if (!game.restart_level)
4223   {
4224     /* copy default game door content to main double buffer */
4225     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4226                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4227   }
4228
4229   SetPanelBackground();
4230   SetDrawBackgroundMask(REDRAW_DOOR_1);
4231
4232 #if 1
4233   UpdateAndDisplayGameControlValues();
4234 #else
4235   UpdateGameDoorValues();
4236   DrawGameDoorValues();
4237 #endif
4238
4239   if (!game.restart_level)
4240   {
4241     UnmapGameButtons();
4242     UnmapTapeButtons();
4243     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4244     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4245     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4246     MapGameButtons();
4247     MapTapeButtons();
4248
4249     /* copy actual game door content to door double buffer for OpenDoor() */
4250     BlitBitmap(drawto, bitmap_db_door,
4251                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4252
4253     OpenDoor(DOOR_OPEN_ALL);
4254
4255     PlaySound(SND_GAME_STARTING);
4256
4257     if (setup.sound_music)
4258       PlayLevelMusic();
4259
4260     KeyboardAutoRepeatOffUnlessAutoplay();
4261
4262     if (options.debug)
4263     {
4264       for (i = 0; i < MAX_PLAYERS; i++)
4265         printf("Player %d %sactive.\n",
4266                i + 1, (stored_player[i].active ? "" : "not "));
4267     }
4268   }
4269
4270 #if 1
4271   UnmapAllGadgets();
4272
4273   MapGameButtons();
4274   MapTapeButtons();
4275 #endif
4276
4277   game.restart_level = FALSE;
4278 }
4279
4280 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4281 {
4282   /* this is used for non-R'n'D game engines to update certain engine values */
4283
4284   /* needed to determine if sounds are played within the visible screen area */
4285   scroll_x = actual_scroll_x;
4286   scroll_y = actual_scroll_y;
4287 }
4288
4289 void InitMovDir(int x, int y)
4290 {
4291   int i, element = Feld[x][y];
4292   static int xy[4][2] =
4293   {
4294     {  0, +1 },
4295     { +1,  0 },
4296     {  0, -1 },
4297     { -1,  0 }
4298   };
4299   static int direction[3][4] =
4300   {
4301     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4302     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4303     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4304   };
4305
4306   switch (element)
4307   {
4308     case EL_BUG_RIGHT:
4309     case EL_BUG_UP:
4310     case EL_BUG_LEFT:
4311     case EL_BUG_DOWN:
4312       Feld[x][y] = EL_BUG;
4313       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4314       break;
4315
4316     case EL_SPACESHIP_RIGHT:
4317     case EL_SPACESHIP_UP:
4318     case EL_SPACESHIP_LEFT:
4319     case EL_SPACESHIP_DOWN:
4320       Feld[x][y] = EL_SPACESHIP;
4321       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4322       break;
4323
4324     case EL_BD_BUTTERFLY_RIGHT:
4325     case EL_BD_BUTTERFLY_UP:
4326     case EL_BD_BUTTERFLY_LEFT:
4327     case EL_BD_BUTTERFLY_DOWN:
4328       Feld[x][y] = EL_BD_BUTTERFLY;
4329       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4330       break;
4331
4332     case EL_BD_FIREFLY_RIGHT:
4333     case EL_BD_FIREFLY_UP:
4334     case EL_BD_FIREFLY_LEFT:
4335     case EL_BD_FIREFLY_DOWN:
4336       Feld[x][y] = EL_BD_FIREFLY;
4337       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4338       break;
4339
4340     case EL_PACMAN_RIGHT:
4341     case EL_PACMAN_UP:
4342     case EL_PACMAN_LEFT:
4343     case EL_PACMAN_DOWN:
4344       Feld[x][y] = EL_PACMAN;
4345       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4346       break;
4347
4348     case EL_YAMYAM_LEFT:
4349     case EL_YAMYAM_RIGHT:
4350     case EL_YAMYAM_UP:
4351     case EL_YAMYAM_DOWN:
4352       Feld[x][y] = EL_YAMYAM;
4353       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4354       break;
4355
4356     case EL_SP_SNIKSNAK:
4357       MovDir[x][y] = MV_UP;
4358       break;
4359
4360     case EL_SP_ELECTRON:
4361       MovDir[x][y] = MV_LEFT;
4362       break;
4363
4364     case EL_MOLE_LEFT:
4365     case EL_MOLE_RIGHT:
4366     case EL_MOLE_UP:
4367     case EL_MOLE_DOWN:
4368       Feld[x][y] = EL_MOLE;
4369       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4370       break;
4371
4372     default:
4373       if (IS_CUSTOM_ELEMENT(element))
4374       {
4375         struct ElementInfo *ei = &element_info[element];
4376         int move_direction_initial = ei->move_direction_initial;
4377         int move_pattern = ei->move_pattern;
4378
4379         if (move_direction_initial == MV_START_PREVIOUS)
4380         {
4381           if (MovDir[x][y] != MV_NONE)
4382             return;
4383
4384           move_direction_initial = MV_START_AUTOMATIC;
4385         }
4386
4387         if (move_direction_initial == MV_START_RANDOM)
4388           MovDir[x][y] = 1 << RND(4);
4389         else if (move_direction_initial & MV_ANY_DIRECTION)
4390           MovDir[x][y] = move_direction_initial;
4391         else if (move_pattern == MV_ALL_DIRECTIONS ||
4392                  move_pattern == MV_TURNING_LEFT ||
4393                  move_pattern == MV_TURNING_RIGHT ||
4394                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4395                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4396                  move_pattern == MV_TURNING_RANDOM)
4397           MovDir[x][y] = 1 << RND(4);
4398         else if (move_pattern == MV_HORIZONTAL)
4399           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4400         else if (move_pattern == MV_VERTICAL)
4401           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4402         else if (move_pattern & MV_ANY_DIRECTION)
4403           MovDir[x][y] = element_info[element].move_pattern;
4404         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4405                  move_pattern == MV_ALONG_RIGHT_SIDE)
4406         {
4407           /* use random direction as default start direction */
4408           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4409             MovDir[x][y] = 1 << RND(4);
4410
4411           for (i = 0; i < NUM_DIRECTIONS; i++)
4412           {
4413             int x1 = x + xy[i][0];
4414             int y1 = y + xy[i][1];
4415
4416             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4417             {
4418               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4419                 MovDir[x][y] = direction[0][i];
4420               else
4421                 MovDir[x][y] = direction[1][i];
4422
4423               break;
4424             }
4425           }
4426         }                
4427       }
4428       else
4429       {
4430         MovDir[x][y] = 1 << RND(4);
4431
4432         if (element != EL_BUG &&
4433             element != EL_SPACESHIP &&
4434             element != EL_BD_BUTTERFLY &&
4435             element != EL_BD_FIREFLY)
4436           break;
4437
4438         for (i = 0; i < NUM_DIRECTIONS; i++)
4439         {
4440           int x1 = x + xy[i][0];
4441           int y1 = y + xy[i][1];
4442
4443           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4444           {
4445             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4446             {
4447               MovDir[x][y] = direction[0][i];
4448               break;
4449             }
4450             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4451                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4452             {
4453               MovDir[x][y] = direction[1][i];
4454               break;
4455             }
4456           }
4457         }
4458       }
4459       break;
4460   }
4461
4462   GfxDir[x][y] = MovDir[x][y];
4463 }
4464
4465 void InitAmoebaNr(int x, int y)
4466 {
4467   int i;
4468   int group_nr = AmoebeNachbarNr(x, y);
4469
4470   if (group_nr == 0)
4471   {
4472     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4473     {
4474       if (AmoebaCnt[i] == 0)
4475       {
4476         group_nr = i;
4477         break;
4478       }
4479     }
4480   }
4481
4482   AmoebaNr[x][y] = group_nr;
4483   AmoebaCnt[group_nr]++;
4484   AmoebaCnt2[group_nr]++;
4485 }
4486
4487 static void PlayerWins(struct PlayerInfo *player)
4488 {
4489   player->LevelSolved = TRUE;
4490   player->GameOver = TRUE;
4491
4492   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4493                          level.native_em_level->lev->score : player->score);
4494
4495   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4496   player->LevelSolved_CountingScore = player->score_final;
4497 }
4498
4499 void GameWon()
4500 {
4501   static int time, time_final;
4502   static int score, score_final;
4503   static int game_over_delay_1 = 0;
4504   static int game_over_delay_2 = 0;
4505   int game_over_delay_value_1 = 50;
4506   int game_over_delay_value_2 = 50;
4507
4508   if (!local_player->LevelSolved_GameWon)
4509   {
4510     int i;
4511
4512     /* do not start end game actions before the player stops moving (to exit) */
4513     if (local_player->MovPos)
4514       return;
4515
4516     local_player->LevelSolved_GameWon = TRUE;
4517     local_player->LevelSolved_SaveTape = tape.recording;
4518     local_player->LevelSolved_SaveScore = !tape.playing;
4519
4520     if (tape.auto_play)         /* tape might already be stopped here */
4521       tape.auto_play_level_solved = TRUE;
4522
4523 #if 1
4524     TapeStop();
4525 #endif
4526
4527     game_over_delay_1 = game_over_delay_value_1;
4528     game_over_delay_2 = game_over_delay_value_2;
4529
4530     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4531     score = score_final = local_player->score_final;
4532
4533     if (TimeLeft > 0)
4534     {
4535       time_final = 0;
4536       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4537     }
4538     else if (level.time == 0 && TimePlayed < 999)
4539     {
4540       time_final = 999;
4541       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4542     }
4543
4544     local_player->score_final = score_final;
4545
4546     if (level_editor_test_game)
4547     {
4548       time = time_final;
4549       score = score_final;
4550
4551 #if 1
4552       local_player->LevelSolved_CountingTime = time;
4553       local_player->LevelSolved_CountingScore = score;
4554
4555       game_panel_controls[GAME_PANEL_TIME].value = time;
4556       game_panel_controls[GAME_PANEL_SCORE].value = score;
4557
4558       DisplayGameControlValues();
4559 #else
4560       DrawGameValue_Time(time);
4561       DrawGameValue_Score(score);
4562 #endif
4563     }
4564
4565     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4566     {
4567       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4568       {
4569         /* close exit door after last player */
4570         if ((AllPlayersGone &&
4571              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4572               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4573               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4574             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4575             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4576         {
4577           int element = Feld[ExitX][ExitY];
4578
4579 #if 0
4580           if (element == EL_EM_EXIT_OPEN ||
4581               element == EL_EM_STEEL_EXIT_OPEN)
4582           {
4583             Bang(ExitX, ExitY);
4584           }
4585           else
4586 #endif
4587           {
4588             Feld[ExitX][ExitY] =
4589               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4590                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4591                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4592                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4593                EL_EM_STEEL_EXIT_CLOSING);
4594
4595             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4596           }
4597         }
4598
4599         /* player disappears */
4600         DrawLevelField(ExitX, ExitY);
4601       }
4602
4603       for (i = 0; i < MAX_PLAYERS; i++)
4604       {
4605         struct PlayerInfo *player = &stored_player[i];
4606
4607         if (player->present)
4608         {
4609           RemovePlayer(player);
4610
4611           /* player disappears */
4612           DrawLevelField(player->jx, player->jy);
4613         }
4614       }
4615     }
4616
4617     PlaySound(SND_GAME_WINNING);
4618   }
4619
4620   if (game_over_delay_1 > 0)
4621   {
4622     game_over_delay_1--;
4623
4624     return;
4625   }
4626
4627   if (time != time_final)
4628   {
4629     int time_to_go = ABS(time_final - time);
4630     int time_count_dir = (time < time_final ? +1 : -1);
4631     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4632
4633     time  += time_count_steps * time_count_dir;
4634     score += time_count_steps * level.score[SC_TIME_BONUS];
4635
4636 #if 1
4637     local_player->LevelSolved_CountingTime = time;
4638     local_player->LevelSolved_CountingScore = score;
4639
4640     game_panel_controls[GAME_PANEL_TIME].value = time;
4641     game_panel_controls[GAME_PANEL_SCORE].value = score;
4642
4643     DisplayGameControlValues();
4644 #else
4645     DrawGameValue_Time(time);
4646     DrawGameValue_Score(score);
4647 #endif
4648
4649     if (time == time_final)
4650       StopSound(SND_GAME_LEVELTIME_BONUS);
4651     else if (setup.sound_loops)
4652       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4653     else
4654       PlaySound(SND_GAME_LEVELTIME_BONUS);
4655
4656     return;
4657   }
4658
4659   local_player->LevelSolved_PanelOff = TRUE;
4660
4661   if (game_over_delay_2 > 0)
4662   {
4663     game_over_delay_2--;
4664
4665     return;
4666   }
4667
4668 #if 1
4669   GameEnd();
4670 #endif
4671 }
4672
4673 void GameEnd()
4674 {
4675   int hi_pos;
4676   boolean raise_level = FALSE;
4677
4678   local_player->LevelSolved_GameEnd = TRUE;
4679
4680   CloseDoor(DOOR_CLOSE_1);
4681
4682   if (local_player->LevelSolved_SaveTape)
4683   {
4684 #if 0
4685     TapeStop();
4686 #endif
4687
4688 #if 1
4689     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4690 #else
4691     SaveTape(tape.level_nr);            /* ask to save tape */
4692 #endif
4693   }
4694
4695   if (level_editor_test_game)
4696   {
4697     game_status = GAME_MODE_MAIN;
4698
4699 #if 1
4700     DrawAndFadeInMainMenu(REDRAW_FIELD);
4701 #else
4702     DrawMainMenu();
4703 #endif
4704
4705     return;
4706   }
4707
4708   if (!local_player->LevelSolved_SaveScore)
4709   {
4710 #if 1
4711     FadeOut(REDRAW_FIELD);
4712 #endif
4713
4714     game_status = GAME_MODE_MAIN;
4715
4716     DrawAndFadeInMainMenu(REDRAW_FIELD);
4717
4718     return;
4719   }
4720
4721   if (level_nr == leveldir_current->handicap_level)
4722   {
4723     leveldir_current->handicap_level++;
4724     SaveLevelSetup_SeriesInfo();
4725   }
4726
4727   if (level_nr < leveldir_current->last_level)
4728     raise_level = TRUE;                 /* advance to next level */
4729
4730   if ((hi_pos = NewHiScore()) >= 0) 
4731   {
4732     game_status = GAME_MODE_SCORES;
4733
4734     DrawHallOfFame(hi_pos);
4735
4736     if (raise_level)
4737     {
4738       level_nr++;
4739       TapeErase();
4740     }
4741   }
4742   else
4743   {
4744 #if 1
4745     FadeOut(REDRAW_FIELD);
4746 #endif
4747
4748     game_status = GAME_MODE_MAIN;
4749
4750     if (raise_level)
4751     {
4752       level_nr++;
4753       TapeErase();
4754     }
4755
4756     DrawAndFadeInMainMenu(REDRAW_FIELD);
4757   }
4758 }
4759
4760 int NewHiScore()
4761 {
4762   int k, l;
4763   int position = -1;
4764
4765   LoadScore(level_nr);
4766
4767   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4768       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4769     return -1;
4770
4771   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4772   {
4773     if (local_player->score_final > highscore[k].Score)
4774     {
4775       /* player has made it to the hall of fame */
4776
4777       if (k < MAX_SCORE_ENTRIES - 1)
4778       {
4779         int m = MAX_SCORE_ENTRIES - 1;
4780
4781 #ifdef ONE_PER_NAME
4782         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4783           if (strEqual(setup.player_name, highscore[l].Name))
4784             m = l;
4785         if (m == k)     /* player's new highscore overwrites his old one */
4786           goto put_into_list;
4787 #endif
4788
4789         for (l = m; l > k; l--)
4790         {
4791           strcpy(highscore[l].Name, highscore[l - 1].Name);
4792           highscore[l].Score = highscore[l - 1].Score;
4793         }
4794       }
4795
4796 #ifdef ONE_PER_NAME
4797       put_into_list:
4798 #endif
4799       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4800       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4801       highscore[k].Score = local_player->score_final; 
4802       position = k;
4803       break;
4804     }
4805
4806 #ifdef ONE_PER_NAME
4807     else if (!strncmp(setup.player_name, highscore[k].Name,
4808                       MAX_PLAYER_NAME_LEN))
4809       break;    /* player already there with a higher score */
4810 #endif
4811
4812   }
4813
4814   if (position >= 0) 
4815     SaveScore(level_nr);
4816
4817   return position;
4818 }
4819
4820 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4821 {
4822   int element = Feld[x][y];
4823   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4824   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4825   int horiz_move = (dx != 0);
4826   int sign = (horiz_move ? dx : dy);
4827   int step = sign * element_info[element].move_stepsize;
4828
4829   /* special values for move stepsize for spring and things on conveyor belt */
4830   if (horiz_move)
4831   {
4832     if (CAN_FALL(element) &&
4833         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4834       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4835     else if (element == EL_SPRING)
4836       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4837   }
4838
4839   return step;
4840 }
4841
4842 inline static int getElementMoveStepsize(int x, int y)
4843 {
4844   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4845 }
4846
4847 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4848 {
4849   if (player->GfxAction != action || player->GfxDir != dir)
4850   {
4851 #if 0
4852     printf("Player frame reset! (%d => %d, %d => %d)\n",
4853            player->GfxAction, action, player->GfxDir, dir);
4854 #endif
4855
4856     player->GfxAction = action;
4857     player->GfxDir = dir;
4858     player->Frame = 0;
4859     player->StepFrame = 0;
4860   }
4861 }
4862
4863 #if USE_GFX_RESET_GFX_ANIMATION
4864 static void ResetGfxFrame(int x, int y, boolean redraw)
4865 {
4866   int element = Feld[x][y];
4867   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4868   int last_gfx_frame = GfxFrame[x][y];
4869
4870   if (graphic_info[graphic].anim_global_sync)
4871     GfxFrame[x][y] = FrameCounter;
4872   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4873     GfxFrame[x][y] = CustomValue[x][y];
4874   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4875     GfxFrame[x][y] = element_info[element].collect_score;
4876   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4877     GfxFrame[x][y] = ChangeDelay[x][y];
4878
4879   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4880     DrawLevelGraphicAnimation(x, y, graphic);
4881 }
4882 #endif
4883
4884 static void ResetGfxAnimation(int x, int y)
4885 {
4886   GfxAction[x][y] = ACTION_DEFAULT;
4887   GfxDir[x][y] = MovDir[x][y];
4888   GfxFrame[x][y] = 0;
4889
4890 #if USE_GFX_RESET_GFX_ANIMATION
4891   ResetGfxFrame(x, y, FALSE);
4892 #endif
4893 }
4894
4895 static void ResetRandomAnimationValue(int x, int y)
4896 {
4897   GfxRandom[x][y] = INIT_GFX_RANDOM();
4898 }
4899
4900 void InitMovingField(int x, int y, int direction)
4901 {
4902   int element = Feld[x][y];
4903   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4904   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4905   int newx = x + dx;
4906   int newy = y + dy;
4907   boolean is_moving_before, is_moving_after;
4908 #if 0
4909   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4910 #endif
4911
4912   /* check if element was/is moving or being moved before/after mode change */
4913 #if 1
4914 #if 1
4915   is_moving_before = (WasJustMoving[x][y] != 0);
4916 #else
4917   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4918   is_moving_before = WasJustMoving[x][y];
4919 #endif
4920 #else
4921   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4922 #endif
4923   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4924
4925   /* reset animation only for moving elements which change direction of moving
4926      or which just started or stopped moving
4927      (else CEs with property "can move" / "not moving" are reset each frame) */
4928 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4929 #if 1
4930   if (is_moving_before != is_moving_after ||
4931       direction != MovDir[x][y])
4932     ResetGfxAnimation(x, y);
4933 #else
4934   if ((is_moving_before || is_moving_after) && !continues_moving)
4935     ResetGfxAnimation(x, y);
4936 #endif
4937 #else
4938   if (!continues_moving)
4939     ResetGfxAnimation(x, y);
4940 #endif
4941
4942   MovDir[x][y] = direction;
4943   GfxDir[x][y] = direction;
4944
4945 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4946   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4947                      direction == MV_DOWN && CAN_FALL(element) ?
4948                      ACTION_FALLING : ACTION_MOVING);
4949 #else
4950   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4951                      ACTION_FALLING : ACTION_MOVING);
4952 #endif
4953
4954   /* this is needed for CEs with property "can move" / "not moving" */
4955
4956   if (is_moving_after)
4957   {
4958     if (Feld[newx][newy] == EL_EMPTY)
4959       Feld[newx][newy] = EL_BLOCKED;
4960
4961     MovDir[newx][newy] = MovDir[x][y];
4962
4963 #if USE_NEW_CUSTOM_VALUE
4964     CustomValue[newx][newy] = CustomValue[x][y];
4965 #endif
4966
4967     GfxFrame[newx][newy] = GfxFrame[x][y];
4968     GfxRandom[newx][newy] = GfxRandom[x][y];
4969     GfxAction[newx][newy] = GfxAction[x][y];
4970     GfxDir[newx][newy] = GfxDir[x][y];
4971   }
4972 }
4973
4974 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4975 {
4976   int direction = MovDir[x][y];
4977   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4978   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4979
4980   *goes_to_x = newx;
4981   *goes_to_y = newy;
4982 }
4983
4984 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4985 {
4986   int oldx = x, oldy = y;
4987   int direction = MovDir[x][y];
4988
4989   if (direction == MV_LEFT)
4990     oldx++;
4991   else if (direction == MV_RIGHT)
4992     oldx--;
4993   else if (direction == MV_UP)
4994     oldy++;
4995   else if (direction == MV_DOWN)
4996     oldy--;
4997
4998   *comes_from_x = oldx;
4999   *comes_from_y = oldy;
5000 }
5001
5002 int MovingOrBlocked2Element(int x, int y)
5003 {
5004   int element = Feld[x][y];
5005
5006   if (element == EL_BLOCKED)
5007   {
5008     int oldx, oldy;
5009
5010     Blocked2Moving(x, y, &oldx, &oldy);
5011     return Feld[oldx][oldy];
5012   }
5013   else
5014     return element;
5015 }
5016
5017 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5018 {
5019   /* like MovingOrBlocked2Element(), but if element is moving
5020      and (x,y) is the field the moving element is just leaving,
5021      return EL_BLOCKED instead of the element value */
5022   int element = Feld[x][y];
5023
5024   if (IS_MOVING(x, y))
5025   {
5026     if (element == EL_BLOCKED)
5027     {
5028       int oldx, oldy;
5029
5030       Blocked2Moving(x, y, &oldx, &oldy);
5031       return Feld[oldx][oldy];
5032     }
5033     else
5034       return EL_BLOCKED;
5035   }
5036   else
5037     return element;
5038 }
5039
5040 static void RemoveField(int x, int y)
5041 {
5042   Feld[x][y] = EL_EMPTY;
5043
5044   MovPos[x][y] = 0;
5045   MovDir[x][y] = 0;
5046   MovDelay[x][y] = 0;
5047
5048 #if USE_NEW_CUSTOM_VALUE
5049   CustomValue[x][y] = 0;
5050 #endif
5051
5052   AmoebaNr[x][y] = 0;
5053   ChangeDelay[x][y] = 0;
5054   ChangePage[x][y] = -1;
5055   Pushed[x][y] = FALSE;
5056
5057 #if 0
5058   ExplodeField[x][y] = EX_TYPE_NONE;
5059 #endif
5060
5061   GfxElement[x][y] = EL_UNDEFINED;
5062   GfxAction[x][y] = ACTION_DEFAULT;
5063   GfxDir[x][y] = MV_NONE;
5064 }
5065
5066 void RemoveMovingField(int x, int y)
5067 {
5068   int oldx = x, oldy = y, newx = x, newy = y;
5069   int element = Feld[x][y];
5070   int next_element = EL_UNDEFINED;
5071
5072   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5073     return;
5074
5075   if (IS_MOVING(x, y))
5076   {
5077     Moving2Blocked(x, y, &newx, &newy);
5078
5079     if (Feld[newx][newy] != EL_BLOCKED)
5080     {
5081       /* element is moving, but target field is not free (blocked), but
5082          already occupied by something different (example: acid pool);
5083          in this case, only remove the moving field, but not the target */
5084
5085       RemoveField(oldx, oldy);
5086
5087       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5088
5089       DrawLevelField(oldx, oldy);
5090
5091       return;
5092     }
5093   }
5094   else if (element == EL_BLOCKED)
5095   {
5096     Blocked2Moving(x, y, &oldx, &oldy);
5097     if (!IS_MOVING(oldx, oldy))
5098       return;
5099   }
5100
5101   if (element == EL_BLOCKED &&
5102       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5103        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5104        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5105        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5106        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5107        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5108     next_element = get_next_element(Feld[oldx][oldy]);
5109
5110   RemoveField(oldx, oldy);
5111   RemoveField(newx, newy);
5112
5113   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5114
5115   if (next_element != EL_UNDEFINED)
5116     Feld[oldx][oldy] = next_element;
5117
5118   DrawLevelField(oldx, oldy);
5119   DrawLevelField(newx, newy);
5120 }
5121
5122 void DrawDynamite(int x, int y)
5123 {
5124   int sx = SCREENX(x), sy = SCREENY(y);
5125   int graphic = el2img(Feld[x][y]);
5126   int frame;
5127
5128   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5129     return;
5130
5131   if (IS_WALKABLE_INSIDE(Back[x][y]))
5132     return;
5133
5134   if (Back[x][y])
5135     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5136   else if (Store[x][y])
5137     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5138
5139   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5140
5141   if (Back[x][y] || Store[x][y])
5142     DrawGraphicThruMask(sx, sy, graphic, frame);
5143   else
5144     DrawGraphic(sx, sy, graphic, frame);
5145 }
5146
5147 void CheckDynamite(int x, int y)
5148 {
5149   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5150   {
5151     MovDelay[x][y]--;
5152
5153     if (MovDelay[x][y] != 0)
5154     {
5155       DrawDynamite(x, y);
5156       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5157
5158       return;
5159     }
5160   }
5161
5162   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5163
5164   Bang(x, y);
5165 }
5166
5167 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5168 {
5169   boolean num_checked_players = 0;
5170   int i;
5171
5172   for (i = 0; i < MAX_PLAYERS; i++)
5173   {
5174     if (stored_player[i].active)
5175     {
5176       int sx = stored_player[i].jx;
5177       int sy = stored_player[i].jy;
5178
5179       if (num_checked_players == 0)
5180       {
5181         *sx1 = *sx2 = sx;
5182         *sy1 = *sy2 = sy;
5183       }
5184       else
5185       {
5186         *sx1 = MIN(*sx1, sx);
5187         *sy1 = MIN(*sy1, sy);
5188         *sx2 = MAX(*sx2, sx);
5189         *sy2 = MAX(*sy2, sy);
5190       }
5191
5192       num_checked_players++;
5193     }
5194   }
5195 }
5196
5197 static boolean checkIfAllPlayersFitToScreen_RND()
5198 {
5199   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5200
5201   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5202
5203   return (sx2 - sx1 < SCR_FIELDX &&
5204           sy2 - sy1 < SCR_FIELDY);
5205 }
5206
5207 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5208 {
5209   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5210
5211   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5212
5213   *sx = (sx1 + sx2) / 2;
5214   *sy = (sy1 + sy2) / 2;
5215 }
5216
5217 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5218                         boolean center_screen, boolean quick_relocation)
5219 {
5220   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5221   boolean no_delay = (tape.warp_forward);
5222   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5223   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5224
5225   if (quick_relocation)
5226   {
5227     int offset = game.scroll_delay_value;
5228
5229     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5230     {
5231       if (!level.shifted_relocation || center_screen)
5232       {
5233         /* quick relocation (without scrolling), with centering of screen */
5234
5235         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5236                     x > SBX_Right + MIDPOSX ? SBX_Right :
5237                     x - MIDPOSX);
5238
5239         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5240                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5241                     y - MIDPOSY);
5242       }
5243       else
5244       {
5245         /* quick relocation (without scrolling), but do not center screen */
5246
5247         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5248                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5249                                old_x - MIDPOSX);
5250
5251         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5252                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5253                                old_y - MIDPOSY);
5254
5255         int offset_x = x + (scroll_x - center_scroll_x);
5256         int offset_y = y + (scroll_y - center_scroll_y);
5257
5258         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5259                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5260                     offset_x - MIDPOSX);
5261
5262         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5263                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5264                     offset_y - MIDPOSY);
5265       }
5266     }
5267     else
5268     {
5269       /* quick relocation (without scrolling), inside visible screen area */
5270
5271       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5272           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5273         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5274
5275       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5276           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5277         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5278
5279       /* don't scroll over playfield boundaries */
5280       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5281         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5282
5283       /* don't scroll over playfield boundaries */
5284       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5285         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5286     }
5287
5288     RedrawPlayfield(TRUE, 0,0,0,0);
5289   }
5290   else
5291   {
5292 #if 1
5293     int scroll_xx, scroll_yy;
5294
5295     if (!level.shifted_relocation || center_screen)
5296     {
5297       /* visible relocation (with scrolling), with centering of screen */
5298
5299       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5300                    x > SBX_Right + MIDPOSX ? SBX_Right :
5301                    x - MIDPOSX);
5302
5303       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5304                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5305                    y - MIDPOSY);
5306     }
5307     else
5308     {
5309       /* visible relocation (with scrolling), but do not center screen */
5310
5311       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5312                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5313                              old_x - MIDPOSX);
5314
5315       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5316                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5317                              old_y - MIDPOSY);
5318
5319       int offset_x = x + (scroll_x - center_scroll_x);
5320       int offset_y = y + (scroll_y - center_scroll_y);
5321
5322       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5323                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5324                    offset_x - MIDPOSX);
5325
5326       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5327                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5328                    offset_y - MIDPOSY);
5329     }
5330
5331 #else
5332
5333     /* visible relocation (with scrolling), with centering of screen */
5334
5335     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5336                      x > SBX_Right + MIDPOSX ? SBX_Right :
5337                      x - MIDPOSX);
5338
5339     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5340                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5341                      y - MIDPOSY);
5342 #endif
5343
5344     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5345
5346     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5347     {
5348       int dx = 0, dy = 0;
5349       int fx = FX, fy = FY;
5350
5351       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5352       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5353
5354       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5355         break;
5356
5357       scroll_x -= dx;
5358       scroll_y -= dy;
5359
5360       fx += dx * TILEX / 2;
5361       fy += dy * TILEY / 2;
5362
5363       ScrollLevel(dx, dy);
5364       DrawAllPlayers();
5365
5366       /* scroll in two steps of half tile size to make things smoother */
5367       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5368       FlushDisplay();
5369       Delay(wait_delay_value);
5370
5371       /* scroll second step to align at full tile size */
5372       BackToFront();
5373       Delay(wait_delay_value);
5374     }
5375
5376     DrawAllPlayers();
5377     BackToFront();
5378     Delay(wait_delay_value);
5379   }
5380 }
5381
5382 void RelocatePlayer(int jx, int jy, int el_player_raw)
5383 {
5384   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5385   int player_nr = GET_PLAYER_NR(el_player);
5386   struct PlayerInfo *player = &stored_player[player_nr];
5387   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5388   boolean no_delay = (tape.warp_forward);
5389   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5390   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5391   int old_jx = player->jx;
5392   int old_jy = player->jy;
5393   int old_element = Feld[old_jx][old_jy];
5394   int element = Feld[jx][jy];
5395   boolean player_relocated = (old_jx != jx || old_jy != jy);
5396
5397   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5398   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5399   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5400   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5401   int leave_side_horiz = move_dir_horiz;
5402   int leave_side_vert  = move_dir_vert;
5403   int enter_side = enter_side_horiz | enter_side_vert;
5404   int leave_side = leave_side_horiz | leave_side_vert;
5405
5406   if (player->GameOver)         /* do not reanimate dead player */
5407     return;
5408
5409   if (!player_relocated)        /* no need to relocate the player */
5410     return;
5411
5412   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5413   {
5414     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5415     DrawLevelField(jx, jy);
5416   }
5417
5418   if (player->present)
5419   {
5420     while (player->MovPos)
5421     {
5422       ScrollPlayer(player, SCROLL_GO_ON);
5423       ScrollScreen(NULL, SCROLL_GO_ON);
5424
5425       AdvanceFrameAndPlayerCounters(player->index_nr);
5426
5427       DrawPlayer(player);
5428
5429       BackToFront();
5430       Delay(wait_delay_value);
5431     }
5432
5433     DrawPlayer(player);         /* needed here only to cleanup last field */
5434     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5435
5436     player->is_moving = FALSE;
5437   }
5438
5439   if (IS_CUSTOM_ELEMENT(old_element))
5440     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5441                                CE_LEFT_BY_PLAYER,
5442                                player->index_bit, leave_side);
5443
5444   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5445                                       CE_PLAYER_LEAVES_X,
5446                                       player->index_bit, leave_side);
5447
5448   Feld[jx][jy] = el_player;
5449   InitPlayerField(jx, jy, el_player, TRUE);
5450
5451   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5452   {
5453     Feld[jx][jy] = element;
5454     InitField(jx, jy, FALSE);
5455   }
5456
5457   /* only visually relocate centered player */
5458   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5459                      FALSE, level.instant_relocation);
5460
5461   TestIfPlayerTouchesBadThing(jx, jy);
5462   TestIfPlayerTouchesCustomElement(jx, jy);
5463
5464   if (IS_CUSTOM_ELEMENT(element))
5465     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5466                                player->index_bit, enter_side);
5467
5468   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5469                                       player->index_bit, enter_side);
5470 }
5471
5472 void Explode(int ex, int ey, int phase, int mode)
5473 {
5474   int x, y;
5475   int last_phase;
5476   int border_element;
5477
5478   /* !!! eliminate this variable !!! */
5479   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5480
5481   if (game.explosions_delayed)
5482   {
5483     ExplodeField[ex][ey] = mode;
5484     return;
5485   }
5486
5487   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5488   {
5489     int center_element = Feld[ex][ey];
5490     int artwork_element, explosion_element;     /* set these values later */
5491
5492 #if 0
5493     /* --- This is only really needed (and now handled) in "Impact()". --- */
5494     /* do not explode moving elements that left the explode field in time */
5495     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5496         center_element == EL_EMPTY &&
5497         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5498       return;
5499 #endif
5500
5501 #if 0
5502     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5503     if (mode == EX_TYPE_NORMAL ||
5504         mode == EX_TYPE_CENTER ||
5505         mode == EX_TYPE_CROSS)
5506       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5507 #endif
5508
5509     /* remove things displayed in background while burning dynamite */
5510     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5511       Back[ex][ey] = 0;
5512
5513     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5514     {
5515       /* put moving element to center field (and let it explode there) */
5516       center_element = MovingOrBlocked2Element(ex, ey);
5517       RemoveMovingField(ex, ey);
5518       Feld[ex][ey] = center_element;
5519     }
5520
5521     /* now "center_element" is finally determined -- set related values now */
5522     artwork_element = center_element;           /* for custom player artwork */
5523     explosion_element = center_element;         /* for custom player artwork */
5524
5525     if (IS_PLAYER(ex, ey))
5526     {
5527       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5528
5529       artwork_element = stored_player[player_nr].artwork_element;
5530
5531       if (level.use_explosion_element[player_nr])
5532       {
5533         explosion_element = level.explosion_element[player_nr];
5534         artwork_element = explosion_element;
5535       }
5536     }
5537
5538 #if 1
5539     if (mode == EX_TYPE_NORMAL ||
5540         mode == EX_TYPE_CENTER ||
5541         mode == EX_TYPE_CROSS)
5542       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5543 #endif
5544
5545     last_phase = element_info[explosion_element].explosion_delay + 1;
5546
5547     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5548     {
5549       int xx = x - ex + 1;
5550       int yy = y - ey + 1;
5551       int element;
5552
5553       if (!IN_LEV_FIELD(x, y) ||
5554           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5555           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5556         continue;
5557
5558       element = Feld[x][y];
5559
5560       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5561       {
5562         element = MovingOrBlocked2Element(x, y);
5563
5564         if (!IS_EXPLOSION_PROOF(element))
5565           RemoveMovingField(x, y);
5566       }
5567
5568       /* indestructible elements can only explode in center (but not flames) */
5569       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5570                                            mode == EX_TYPE_BORDER)) ||
5571           element == EL_FLAMES)
5572         continue;
5573
5574       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5575          behaviour, for example when touching a yamyam that explodes to rocks
5576          with active deadly shield, a rock is created under the player !!! */
5577       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5578 #if 0
5579       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5580           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5581            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5582 #else
5583       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5584 #endif
5585       {
5586         if (IS_ACTIVE_BOMB(element))
5587         {
5588           /* re-activate things under the bomb like gate or penguin */
5589           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5590           Back[x][y] = 0;
5591         }
5592
5593         continue;
5594       }
5595
5596       /* save walkable background elements while explosion on same tile */
5597       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5598           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5599         Back[x][y] = element;
5600
5601       /* ignite explodable elements reached by other explosion */
5602       if (element == EL_EXPLOSION)
5603         element = Store2[x][y];
5604
5605       if (AmoebaNr[x][y] &&
5606           (element == EL_AMOEBA_FULL ||
5607            element == EL_BD_AMOEBA ||
5608            element == EL_AMOEBA_GROWING))
5609       {
5610         AmoebaCnt[AmoebaNr[x][y]]--;
5611         AmoebaCnt2[AmoebaNr[x][y]]--;
5612       }
5613
5614       RemoveField(x, y);
5615
5616       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5617       {
5618         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5619
5620         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5621
5622         if (PLAYERINFO(ex, ey)->use_murphy)
5623           Store[x][y] = EL_EMPTY;
5624       }
5625
5626       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5627          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5628       else if (ELEM_IS_PLAYER(center_element))
5629         Store[x][y] = EL_EMPTY;
5630       else if (center_element == EL_YAMYAM)
5631         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5632       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5633         Store[x][y] = element_info[center_element].content.e[xx][yy];
5634 #if 1
5635       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5636          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5637          otherwise) -- FIX THIS !!! */
5638       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5639         Store[x][y] = element_info[element].content.e[1][1];
5640 #else
5641       else if (!CAN_EXPLODE(element))
5642         Store[x][y] = element_info[element].content.e[1][1];
5643 #endif
5644       else
5645         Store[x][y] = EL_EMPTY;
5646
5647       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5648           center_element == EL_AMOEBA_TO_DIAMOND)
5649         Store2[x][y] = element;
5650
5651       Feld[x][y] = EL_EXPLOSION;
5652       GfxElement[x][y] = artwork_element;
5653
5654       ExplodePhase[x][y] = 1;
5655       ExplodeDelay[x][y] = last_phase;
5656
5657       Stop[x][y] = TRUE;
5658     }
5659
5660     if (center_element == EL_YAMYAM)
5661       game.yamyam_content_nr =
5662         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5663
5664     return;
5665   }
5666
5667   if (Stop[ex][ey])
5668     return;
5669
5670   x = ex;
5671   y = ey;
5672
5673   if (phase == 1)
5674     GfxFrame[x][y] = 0;         /* restart explosion animation */
5675
5676   last_phase = ExplodeDelay[x][y];
5677
5678   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5679
5680 #ifdef DEBUG
5681
5682   /* activate this even in non-DEBUG version until cause for crash in
5683      getGraphicAnimationFrame() (see below) is found and eliminated */
5684
5685 #endif
5686 #if 1
5687
5688 #if 1
5689   /* this can happen if the player leaves an explosion just in time */
5690   if (GfxElement[x][y] == EL_UNDEFINED)
5691     GfxElement[x][y] = EL_EMPTY;
5692 #else
5693   if (GfxElement[x][y] == EL_UNDEFINED)
5694   {
5695     printf("\n\n");
5696     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5697     printf("Explode(): This should never happen!\n");
5698     printf("\n\n");
5699
5700     GfxElement[x][y] = EL_EMPTY;
5701   }
5702 #endif
5703
5704 #endif
5705
5706   border_element = Store2[x][y];
5707   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5708     border_element = StorePlayer[x][y];
5709
5710   if (phase == element_info[border_element].ignition_delay ||
5711       phase == last_phase)
5712   {
5713     boolean border_explosion = FALSE;
5714
5715     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5716         !PLAYER_EXPLOSION_PROTECTED(x, y))
5717     {
5718       KillPlayerUnlessExplosionProtected(x, y);
5719       border_explosion = TRUE;
5720     }
5721     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5722     {
5723       Feld[x][y] = Store2[x][y];
5724       Store2[x][y] = 0;
5725       Bang(x, y);
5726       border_explosion = TRUE;
5727     }
5728     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5729     {
5730       AmoebeUmwandeln(x, y);
5731       Store2[x][y] = 0;
5732       border_explosion = TRUE;
5733     }
5734
5735     /* if an element just explodes due to another explosion (chain-reaction),
5736        do not immediately end the new explosion when it was the last frame of
5737        the explosion (as it would be done in the following "if"-statement!) */
5738     if (border_explosion && phase == last_phase)
5739       return;
5740   }
5741
5742   if (phase == last_phase)
5743   {
5744     int element;
5745
5746     element = Feld[x][y] = Store[x][y];
5747     Store[x][y] = Store2[x][y] = 0;
5748     GfxElement[x][y] = EL_UNDEFINED;
5749
5750     /* player can escape from explosions and might therefore be still alive */
5751     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5752         element <= EL_PLAYER_IS_EXPLODING_4)
5753     {
5754       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5755       int explosion_element = EL_PLAYER_1 + player_nr;
5756       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5757       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5758
5759       if (level.use_explosion_element[player_nr])
5760         explosion_element = level.explosion_element[player_nr];
5761
5762       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5763                     element_info[explosion_element].content.e[xx][yy]);
5764     }
5765
5766     /* restore probably existing indestructible background element */
5767     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5768       element = Feld[x][y] = Back[x][y];
5769     Back[x][y] = 0;
5770
5771     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5772     GfxDir[x][y] = MV_NONE;
5773     ChangeDelay[x][y] = 0;
5774     ChangePage[x][y] = -1;
5775
5776 #if USE_NEW_CUSTOM_VALUE
5777     CustomValue[x][y] = 0;
5778 #endif
5779
5780     InitField_WithBug2(x, y, FALSE);
5781
5782     DrawLevelField(x, y);
5783
5784     TestIfElementTouchesCustomElement(x, y);
5785
5786     if (GFX_CRUMBLED(element))
5787       DrawLevelFieldCrumbledSandNeighbours(x, y);
5788
5789     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5790       StorePlayer[x][y] = 0;
5791
5792     if (ELEM_IS_PLAYER(element))
5793       RelocatePlayer(x, y, element);
5794   }
5795   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5796   {
5797     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5798     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5799
5800     if (phase == delay)
5801       DrawLevelFieldCrumbledSand(x, y);
5802
5803     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5804     {
5805       DrawLevelElement(x, y, Back[x][y]);
5806       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5807     }
5808     else if (IS_WALKABLE_UNDER(Back[x][y]))
5809     {
5810       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5811       DrawLevelElementThruMask(x, y, Back[x][y]);
5812     }
5813     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5814       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5815   }
5816 }
5817
5818 void DynaExplode(int ex, int ey)
5819 {
5820   int i, j;
5821   int dynabomb_element = Feld[ex][ey];
5822   int dynabomb_size = 1;
5823   boolean dynabomb_xl = FALSE;
5824   struct PlayerInfo *player;
5825   static int xy[4][2] =
5826   {
5827     { 0, -1 },
5828     { -1, 0 },
5829     { +1, 0 },
5830     { 0, +1 }
5831   };
5832
5833   if (IS_ACTIVE_BOMB(dynabomb_element))
5834   {
5835     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5836     dynabomb_size = player->dynabomb_size;
5837     dynabomb_xl = player->dynabomb_xl;
5838     player->dynabombs_left++;
5839   }
5840
5841   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5842
5843   for (i = 0; i < NUM_DIRECTIONS; i++)
5844   {
5845     for (j = 1; j <= dynabomb_size; j++)
5846     {
5847       int x = ex + j * xy[i][0];
5848       int y = ey + j * xy[i][1];
5849       int element;
5850
5851       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5852         break;
5853
5854       element = Feld[x][y];
5855
5856       /* do not restart explosions of fields with active bombs */
5857       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5858         continue;
5859
5860       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5861
5862       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5863           !IS_DIGGABLE(element) && !dynabomb_xl)
5864         break;
5865     }
5866   }
5867 }
5868
5869 void Bang(int x, int y)
5870 {
5871   int element = MovingOrBlocked2Element(x, y);
5872   int explosion_type = EX_TYPE_NORMAL;
5873
5874   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5875   {
5876     struct PlayerInfo *player = PLAYERINFO(x, y);
5877
5878     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5879                             player->element_nr);
5880
5881     if (level.use_explosion_element[player->index_nr])
5882     {
5883       int explosion_element = level.explosion_element[player->index_nr];
5884
5885       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5886         explosion_type = EX_TYPE_CROSS;
5887       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5888         explosion_type = EX_TYPE_CENTER;
5889     }
5890   }
5891
5892   switch (element)
5893   {
5894     case EL_BUG:
5895     case EL_SPACESHIP:
5896     case EL_BD_BUTTERFLY:
5897     case EL_BD_FIREFLY:
5898     case EL_YAMYAM:
5899     case EL_DARK_YAMYAM:
5900     case EL_ROBOT:
5901     case EL_PACMAN:
5902     case EL_MOLE:
5903       RaiseScoreElement(element);
5904       break;
5905
5906     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5907     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5908     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5909     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5910     case EL_DYNABOMB_INCREASE_NUMBER:
5911     case EL_DYNABOMB_INCREASE_SIZE:
5912     case EL_DYNABOMB_INCREASE_POWER:
5913       explosion_type = EX_TYPE_DYNA;
5914       break;
5915
5916     case EL_DC_LANDMINE:
5917 #if 0
5918     case EL_EM_EXIT_OPEN:
5919     case EL_EM_STEEL_EXIT_OPEN:
5920 #endif
5921       explosion_type = EX_TYPE_CENTER;
5922       break;
5923
5924     case EL_PENGUIN:
5925     case EL_LAMP:
5926     case EL_LAMP_ACTIVE:
5927     case EL_AMOEBA_TO_DIAMOND:
5928       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5929         explosion_type = EX_TYPE_CENTER;
5930       break;
5931
5932     default:
5933       if (element_info[element].explosion_type == EXPLODES_CROSS)
5934         explosion_type = EX_TYPE_CROSS;
5935       else if (element_info[element].explosion_type == EXPLODES_1X1)
5936         explosion_type = EX_TYPE_CENTER;
5937       break;
5938   }
5939
5940   if (explosion_type == EX_TYPE_DYNA)
5941     DynaExplode(x, y);
5942   else
5943     Explode(x, y, EX_PHASE_START, explosion_type);
5944
5945   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5946 }
5947
5948 void SplashAcid(int x, int y)
5949 {
5950   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5951       (!IN_LEV_FIELD(x - 1, y - 2) ||
5952        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5953     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5954
5955   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5956       (!IN_LEV_FIELD(x + 1, y - 2) ||
5957        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5958     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5959
5960   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5961 }
5962
5963 static void InitBeltMovement()
5964 {
5965   static int belt_base_element[4] =
5966   {
5967     EL_CONVEYOR_BELT_1_LEFT,
5968     EL_CONVEYOR_BELT_2_LEFT,
5969     EL_CONVEYOR_BELT_3_LEFT,
5970     EL_CONVEYOR_BELT_4_LEFT
5971   };
5972   static int belt_base_active_element[4] =
5973   {
5974     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5975     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5976     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5977     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5978   };
5979
5980   int x, y, i, j;
5981
5982   /* set frame order for belt animation graphic according to belt direction */
5983   for (i = 0; i < NUM_BELTS; i++)
5984   {
5985     int belt_nr = i;
5986
5987     for (j = 0; j < NUM_BELT_PARTS; j++)
5988     {
5989       int element = belt_base_active_element[belt_nr] + j;
5990       int graphic_1 = el2img(element);
5991       int graphic_2 = el2panelimg(element);
5992
5993       if (game.belt_dir[i] == MV_LEFT)
5994       {
5995         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5996         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5997       }
5998       else
5999       {
6000         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6001         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6002       }
6003     }
6004   }
6005
6006   SCAN_PLAYFIELD(x, y)
6007   {
6008     int element = Feld[x][y];
6009
6010     for (i = 0; i < NUM_BELTS; i++)
6011     {
6012       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6013       {
6014         int e_belt_nr = getBeltNrFromBeltElement(element);
6015         int belt_nr = i;
6016
6017         if (e_belt_nr == belt_nr)
6018         {
6019           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6020
6021           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6022         }
6023       }
6024     }
6025   }
6026 }
6027
6028 static void ToggleBeltSwitch(int x, int y)
6029 {
6030   static int belt_base_element[4] =
6031   {
6032     EL_CONVEYOR_BELT_1_LEFT,
6033     EL_CONVEYOR_BELT_2_LEFT,
6034     EL_CONVEYOR_BELT_3_LEFT,
6035     EL_CONVEYOR_BELT_4_LEFT
6036   };
6037   static int belt_base_active_element[4] =
6038   {
6039     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6040     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6041     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6042     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6043   };
6044   static int belt_base_switch_element[4] =
6045   {
6046     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6047     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6048     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6049     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6050   };
6051   static int belt_move_dir[4] =
6052   {
6053     MV_LEFT,
6054     MV_NONE,
6055     MV_RIGHT,
6056     MV_NONE,
6057   };
6058
6059   int element = Feld[x][y];
6060   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6061   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6062   int belt_dir = belt_move_dir[belt_dir_nr];
6063   int xx, yy, i;
6064
6065   if (!IS_BELT_SWITCH(element))
6066     return;
6067
6068   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6069   game.belt_dir[belt_nr] = belt_dir;
6070
6071   if (belt_dir_nr == 3)
6072     belt_dir_nr = 1;
6073
6074   /* set frame order for belt animation graphic according to belt direction */
6075   for (i = 0; i < NUM_BELT_PARTS; i++)
6076   {
6077     int element = belt_base_active_element[belt_nr] + i;
6078     int graphic_1 = el2img(element);
6079     int graphic_2 = el2panelimg(element);
6080
6081     if (belt_dir == MV_LEFT)
6082     {
6083       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6084       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6085     }
6086     else
6087     {
6088       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6089       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6090     }
6091   }
6092
6093   SCAN_PLAYFIELD(xx, yy)
6094   {
6095     int element = Feld[xx][yy];
6096
6097     if (IS_BELT_SWITCH(element))
6098     {
6099       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6100
6101       if (e_belt_nr == belt_nr)
6102       {
6103         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6104         DrawLevelField(xx, yy);
6105       }
6106     }
6107     else if (IS_BELT(element) && belt_dir != MV_NONE)
6108     {
6109       int e_belt_nr = getBeltNrFromBeltElement(element);
6110
6111       if (e_belt_nr == belt_nr)
6112       {
6113         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6114
6115         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6116         DrawLevelField(xx, yy);
6117       }
6118     }
6119     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6120     {
6121       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6122
6123       if (e_belt_nr == belt_nr)
6124       {
6125         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6126
6127         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6128         DrawLevelField(xx, yy);
6129       }
6130     }
6131   }
6132 }
6133
6134 static void ToggleSwitchgateSwitch(int x, int y)
6135 {
6136   int xx, yy;
6137
6138   game.switchgate_pos = !game.switchgate_pos;
6139
6140   SCAN_PLAYFIELD(xx, yy)
6141   {
6142     int element = Feld[xx][yy];
6143
6144 #if !USE_BOTH_SWITCHGATE_SWITCHES
6145     if (element == EL_SWITCHGATE_SWITCH_UP ||
6146         element == EL_SWITCHGATE_SWITCH_DOWN)
6147     {
6148       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6149       DrawLevelField(xx, yy);
6150     }
6151     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6152              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6153     {
6154       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6155       DrawLevelField(xx, yy);
6156     }
6157 #else
6158     if (element == EL_SWITCHGATE_SWITCH_UP)
6159     {
6160       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6161       DrawLevelField(xx, yy);
6162     }
6163     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6164     {
6165       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6166       DrawLevelField(xx, yy);
6167     }
6168     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6169     {
6170       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6171       DrawLevelField(xx, yy);
6172     }
6173     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6174     {
6175       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6176       DrawLevelField(xx, yy);
6177     }
6178 #endif
6179     else if (element == EL_SWITCHGATE_OPEN ||
6180              element == EL_SWITCHGATE_OPENING)
6181     {
6182       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6183
6184       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6185     }
6186     else if (element == EL_SWITCHGATE_CLOSED ||
6187              element == EL_SWITCHGATE_CLOSING)
6188     {
6189       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6190
6191       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6192     }
6193   }
6194 }
6195
6196 static int getInvisibleActiveFromInvisibleElement(int element)
6197 {
6198   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6199           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6200           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6201           element);
6202 }
6203
6204 static int getInvisibleFromInvisibleActiveElement(int element)
6205 {
6206   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6207           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6208           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6209           element);
6210 }
6211
6212 static void RedrawAllLightSwitchesAndInvisibleElements()
6213 {
6214   int x, y;
6215
6216   SCAN_PLAYFIELD(x, y)
6217   {
6218     int element = Feld[x][y];
6219
6220     if (element == EL_LIGHT_SWITCH &&
6221         game.light_time_left > 0)
6222     {
6223       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6224       DrawLevelField(x, y);
6225     }
6226     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6227              game.light_time_left == 0)
6228     {
6229       Feld[x][y] = EL_LIGHT_SWITCH;
6230       DrawLevelField(x, y);
6231     }
6232     else if (element == EL_EMC_DRIPPER &&
6233              game.light_time_left > 0)
6234     {
6235       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6236       DrawLevelField(x, y);
6237     }
6238     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6239              game.light_time_left == 0)
6240     {
6241       Feld[x][y] = EL_EMC_DRIPPER;
6242       DrawLevelField(x, y);
6243     }
6244     else if (element == EL_INVISIBLE_STEELWALL ||
6245              element == EL_INVISIBLE_WALL ||
6246              element == EL_INVISIBLE_SAND)
6247     {
6248       if (game.light_time_left > 0)
6249         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6250
6251       DrawLevelField(x, y);
6252
6253       /* uncrumble neighbour fields, if needed */
6254       if (element == EL_INVISIBLE_SAND)
6255         DrawLevelFieldCrumbledSandNeighbours(x, y);
6256     }
6257     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6258              element == EL_INVISIBLE_WALL_ACTIVE ||
6259              element == EL_INVISIBLE_SAND_ACTIVE)
6260     {
6261       if (game.light_time_left == 0)
6262         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6263
6264       DrawLevelField(x, y);
6265
6266       /* re-crumble neighbour fields, if needed */
6267       if (element == EL_INVISIBLE_SAND)
6268         DrawLevelFieldCrumbledSandNeighbours(x, y);
6269     }
6270   }
6271 }
6272
6273 static void RedrawAllInvisibleElementsForLenses()
6274 {
6275   int x, y;
6276
6277   SCAN_PLAYFIELD(x, y)
6278   {
6279     int element = Feld[x][y];
6280
6281     if (element == EL_EMC_DRIPPER &&
6282         game.lenses_time_left > 0)
6283     {
6284       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6285       DrawLevelField(x, y);
6286     }
6287     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6288              game.lenses_time_left == 0)
6289     {
6290       Feld[x][y] = EL_EMC_DRIPPER;
6291       DrawLevelField(x, y);
6292     }
6293     else if (element == EL_INVISIBLE_STEELWALL ||
6294              element == EL_INVISIBLE_WALL ||
6295              element == EL_INVISIBLE_SAND)
6296     {
6297       if (game.lenses_time_left > 0)
6298         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6299
6300       DrawLevelField(x, y);
6301
6302       /* uncrumble neighbour fields, if needed */
6303       if (element == EL_INVISIBLE_SAND)
6304         DrawLevelFieldCrumbledSandNeighbours(x, y);
6305     }
6306     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6307              element == EL_INVISIBLE_WALL_ACTIVE ||
6308              element == EL_INVISIBLE_SAND_ACTIVE)
6309     {
6310       if (game.lenses_time_left == 0)
6311         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6312
6313       DrawLevelField(x, y);
6314
6315       /* re-crumble neighbour fields, if needed */
6316       if (element == EL_INVISIBLE_SAND)
6317         DrawLevelFieldCrumbledSandNeighbours(x, y);
6318     }
6319   }
6320 }
6321
6322 static void RedrawAllInvisibleElementsForMagnifier()
6323 {
6324   int x, y;
6325
6326   SCAN_PLAYFIELD(x, y)
6327   {
6328     int element = Feld[x][y];
6329
6330     if (element == EL_EMC_FAKE_GRASS &&
6331         game.magnify_time_left > 0)
6332     {
6333       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6334       DrawLevelField(x, y);
6335     }
6336     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6337              game.magnify_time_left == 0)
6338     {
6339       Feld[x][y] = EL_EMC_FAKE_GRASS;
6340       DrawLevelField(x, y);
6341     }
6342     else if (IS_GATE_GRAY(element) &&
6343              game.magnify_time_left > 0)
6344     {
6345       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6346                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6347                     IS_EM_GATE_GRAY(element) ?
6348                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6349                     IS_EMC_GATE_GRAY(element) ?
6350                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6351                     element);
6352       DrawLevelField(x, y);
6353     }
6354     else if (IS_GATE_GRAY_ACTIVE(element) &&
6355              game.magnify_time_left == 0)
6356     {
6357       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6358                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6359                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6360                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6361                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6362                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6363                     element);
6364       DrawLevelField(x, y);
6365     }
6366   }
6367 }
6368
6369 static void ToggleLightSwitch(int x, int y)
6370 {
6371   int element = Feld[x][y];
6372
6373   game.light_time_left =
6374     (element == EL_LIGHT_SWITCH ?
6375      level.time_light * FRAMES_PER_SECOND : 0);
6376
6377   RedrawAllLightSwitchesAndInvisibleElements();
6378 }
6379
6380 static void ActivateTimegateSwitch(int x, int y)
6381 {
6382   int xx, yy;
6383
6384   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6385
6386   SCAN_PLAYFIELD(xx, yy)
6387   {
6388     int element = Feld[xx][yy];
6389
6390     if (element == EL_TIMEGATE_CLOSED ||
6391         element == EL_TIMEGATE_CLOSING)
6392     {
6393       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6394       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6395     }
6396
6397     /*
6398     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6399     {
6400       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6401       DrawLevelField(xx, yy);
6402     }
6403     */
6404
6405   }
6406
6407 #if 1
6408   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6409                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6410 #else
6411   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6412 #endif
6413 }
6414
6415 void Impact(int x, int y)
6416 {
6417   boolean last_line = (y == lev_fieldy - 1);
6418   boolean object_hit = FALSE;
6419   boolean impact = (last_line || object_hit);
6420   int element = Feld[x][y];
6421   int smashed = EL_STEELWALL;
6422
6423   if (!last_line)       /* check if element below was hit */
6424   {
6425     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6426       return;
6427
6428     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6429                                          MovDir[x][y + 1] != MV_DOWN ||
6430                                          MovPos[x][y + 1] <= TILEY / 2));
6431
6432     /* do not smash moving elements that left the smashed field in time */
6433     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6434         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6435       object_hit = FALSE;
6436
6437 #if USE_QUICKSAND_IMPACT_BUGFIX
6438     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6439     {
6440       RemoveMovingField(x, y + 1);
6441       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6442       Feld[x][y + 2] = EL_ROCK;
6443       DrawLevelField(x, y + 2);
6444
6445       object_hit = TRUE;
6446     }
6447
6448     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6449     {
6450       RemoveMovingField(x, y + 1);
6451       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6452       Feld[x][y + 2] = EL_ROCK;
6453       DrawLevelField(x, y + 2);
6454
6455       object_hit = TRUE;
6456     }
6457 #endif
6458
6459     if (object_hit)
6460       smashed = MovingOrBlocked2Element(x, y + 1);
6461
6462     impact = (last_line || object_hit);
6463   }
6464
6465   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6466   {
6467     SplashAcid(x, y + 1);
6468     return;
6469   }
6470
6471   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6472   /* only reset graphic animation if graphic really changes after impact */
6473   if (impact &&
6474       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6475   {
6476     ResetGfxAnimation(x, y);
6477     DrawLevelField(x, y);
6478   }
6479
6480   if (impact && CAN_EXPLODE_IMPACT(element))
6481   {
6482     Bang(x, y);
6483     return;
6484   }
6485   else if (impact && element == EL_PEARL &&
6486            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6487   {
6488     ResetGfxAnimation(x, y);
6489
6490     Feld[x][y] = EL_PEARL_BREAKING;
6491     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6492     return;
6493   }
6494   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6495   {
6496     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6497
6498     return;
6499   }
6500
6501   if (impact && element == EL_AMOEBA_DROP)
6502   {
6503     if (object_hit && IS_PLAYER(x, y + 1))
6504       KillPlayerUnlessEnemyProtected(x, y + 1);
6505     else if (object_hit && smashed == EL_PENGUIN)
6506       Bang(x, y + 1);
6507     else
6508     {
6509       Feld[x][y] = EL_AMOEBA_GROWING;
6510       Store[x][y] = EL_AMOEBA_WET;
6511
6512       ResetRandomAnimationValue(x, y);
6513     }
6514     return;
6515   }
6516
6517   if (object_hit)               /* check which object was hit */
6518   {
6519     if ((CAN_PASS_MAGIC_WALL(element) && 
6520          (smashed == EL_MAGIC_WALL ||
6521           smashed == EL_BD_MAGIC_WALL)) ||
6522         (CAN_PASS_DC_MAGIC_WALL(element) &&
6523          smashed == EL_DC_MAGIC_WALL))
6524     {
6525       int xx, yy;
6526       int activated_magic_wall =
6527         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6528          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6529          EL_DC_MAGIC_WALL_ACTIVE);
6530
6531       /* activate magic wall / mill */
6532       SCAN_PLAYFIELD(xx, yy)
6533       {
6534         if (Feld[xx][yy] == smashed)
6535           Feld[xx][yy] = activated_magic_wall;
6536       }
6537
6538       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6539       game.magic_wall_active = TRUE;
6540
6541       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6542                             SND_MAGIC_WALL_ACTIVATING :
6543                             smashed == EL_BD_MAGIC_WALL ?
6544                             SND_BD_MAGIC_WALL_ACTIVATING :
6545                             SND_DC_MAGIC_WALL_ACTIVATING));
6546     }
6547
6548     if (IS_PLAYER(x, y + 1))
6549     {
6550       if (CAN_SMASH_PLAYER(element))
6551       {
6552         KillPlayerUnlessEnemyProtected(x, y + 1);
6553         return;
6554       }
6555     }
6556     else if (smashed == EL_PENGUIN)
6557     {
6558       if (CAN_SMASH_PLAYER(element))
6559       {
6560         Bang(x, y + 1);
6561         return;
6562       }
6563     }
6564     else if (element == EL_BD_DIAMOND)
6565     {
6566       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6567       {
6568         Bang(x, y + 1);
6569         return;
6570       }
6571     }
6572     else if (((element == EL_SP_INFOTRON ||
6573                element == EL_SP_ZONK) &&
6574               (smashed == EL_SP_SNIKSNAK ||
6575                smashed == EL_SP_ELECTRON ||
6576                smashed == EL_SP_DISK_ORANGE)) ||
6577              (element == EL_SP_INFOTRON &&
6578               smashed == EL_SP_DISK_YELLOW))
6579     {
6580       Bang(x, y + 1);
6581       return;
6582     }
6583     else if (CAN_SMASH_EVERYTHING(element))
6584     {
6585       if (IS_CLASSIC_ENEMY(smashed) ||
6586           CAN_EXPLODE_SMASHED(smashed))
6587       {
6588         Bang(x, y + 1);
6589         return;
6590       }
6591       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6592       {
6593         if (smashed == EL_LAMP ||
6594             smashed == EL_LAMP_ACTIVE)
6595         {
6596           Bang(x, y + 1);
6597           return;
6598         }
6599         else if (smashed == EL_NUT)
6600         {
6601           Feld[x][y + 1] = EL_NUT_BREAKING;
6602           PlayLevelSound(x, y, SND_NUT_BREAKING);
6603           RaiseScoreElement(EL_NUT);
6604           return;
6605         }
6606         else if (smashed == EL_PEARL)
6607         {
6608           ResetGfxAnimation(x, y);
6609
6610           Feld[x][y + 1] = EL_PEARL_BREAKING;
6611           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6612           return;
6613         }
6614         else if (smashed == EL_DIAMOND)
6615         {
6616           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6617           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6618           return;
6619         }
6620         else if (IS_BELT_SWITCH(smashed))
6621         {
6622           ToggleBeltSwitch(x, y + 1);
6623         }
6624         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6625                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6626                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6627                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6628         {
6629           ToggleSwitchgateSwitch(x, y + 1);
6630         }
6631         else if (smashed == EL_LIGHT_SWITCH ||
6632                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6633         {
6634           ToggleLightSwitch(x, y + 1);
6635         }
6636         else
6637         {
6638 #if 0
6639           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6640 #endif
6641
6642           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6643
6644           CheckElementChangeBySide(x, y + 1, smashed, element,
6645                                    CE_SWITCHED, CH_SIDE_TOP);
6646           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6647                                             CH_SIDE_TOP);
6648         }
6649       }
6650       else
6651       {
6652         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6653       }
6654     }
6655   }
6656
6657   /* play sound of magic wall / mill */
6658   if (!last_line &&
6659       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6660        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6661        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6662   {
6663     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6664       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6665     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6666       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6667     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6668       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6669
6670     return;
6671   }
6672
6673   /* play sound of object that hits the ground */
6674   if (last_line || object_hit)
6675     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6676 }
6677
6678 inline static void TurnRoundExt(int x, int y)
6679 {
6680   static struct
6681   {
6682     int dx, dy;
6683   } move_xy[] =
6684   {
6685     {  0,  0 },
6686     { -1,  0 },
6687     { +1,  0 },
6688     {  0,  0 },
6689     {  0, -1 },
6690     {  0,  0 }, { 0, 0 }, { 0, 0 },
6691     {  0, +1 }
6692   };
6693   static struct
6694   {
6695     int left, right, back;
6696   } turn[] =
6697   {
6698     { 0,        0,              0        },
6699     { MV_DOWN,  MV_UP,          MV_RIGHT },
6700     { MV_UP,    MV_DOWN,        MV_LEFT  },
6701     { 0,        0,              0        },
6702     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6703     { 0,        0,              0        },
6704     { 0,        0,              0        },
6705     { 0,        0,              0        },
6706     { MV_RIGHT, MV_LEFT,        MV_UP    }
6707   };
6708
6709   int element = Feld[x][y];
6710   int move_pattern = element_info[element].move_pattern;
6711
6712   int old_move_dir = MovDir[x][y];
6713   int left_dir  = turn[old_move_dir].left;
6714   int right_dir = turn[old_move_dir].right;
6715   int back_dir  = turn[old_move_dir].back;
6716
6717   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6718   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6719   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6720   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6721
6722   int left_x  = x + left_dx,  left_y  = y + left_dy;
6723   int right_x = x + right_dx, right_y = y + right_dy;
6724   int move_x  = x + move_dx,  move_y  = y + move_dy;
6725
6726   int xx, yy;
6727
6728   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6729   {
6730     TestIfBadThingTouchesOtherBadThing(x, y);
6731
6732     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6733       MovDir[x][y] = right_dir;
6734     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6735       MovDir[x][y] = left_dir;
6736
6737     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6738       MovDelay[x][y] = 9;
6739     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6740       MovDelay[x][y] = 1;
6741   }
6742   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6743   {
6744     TestIfBadThingTouchesOtherBadThing(x, y);
6745
6746     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6747       MovDir[x][y] = left_dir;
6748     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6749       MovDir[x][y] = right_dir;
6750
6751     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6752       MovDelay[x][y] = 9;
6753     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6754       MovDelay[x][y] = 1;
6755   }
6756   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6757   {
6758     TestIfBadThingTouchesOtherBadThing(x, y);
6759
6760     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6761       MovDir[x][y] = left_dir;
6762     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6763       MovDir[x][y] = right_dir;
6764
6765     if (MovDir[x][y] != old_move_dir)
6766       MovDelay[x][y] = 9;
6767   }
6768   else if (element == EL_YAMYAM)
6769   {
6770     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6771     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6772
6773     if (can_turn_left && can_turn_right)
6774       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6775     else if (can_turn_left)
6776       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6777     else if (can_turn_right)
6778       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6779     else
6780       MovDir[x][y] = back_dir;
6781
6782     MovDelay[x][y] = 16 + 16 * RND(3);
6783   }
6784   else if (element == EL_DARK_YAMYAM)
6785   {
6786     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6787                                                          left_x, left_y);
6788     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6789                                                          right_x, right_y);
6790
6791     if (can_turn_left && can_turn_right)
6792       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6793     else if (can_turn_left)
6794       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6795     else if (can_turn_right)
6796       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6797     else
6798       MovDir[x][y] = back_dir;
6799
6800     MovDelay[x][y] = 16 + 16 * RND(3);
6801   }
6802   else if (element == EL_PACMAN)
6803   {
6804     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6805     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6806
6807     if (can_turn_left && can_turn_right)
6808       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6809     else if (can_turn_left)
6810       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6811     else if (can_turn_right)
6812       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6813     else
6814       MovDir[x][y] = back_dir;
6815
6816     MovDelay[x][y] = 6 + RND(40);
6817   }
6818   else if (element == EL_PIG)
6819   {
6820     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6821     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6822     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6823     boolean should_turn_left, should_turn_right, should_move_on;
6824     int rnd_value = 24;
6825     int rnd = RND(rnd_value);
6826
6827     should_turn_left = (can_turn_left &&
6828                         (!can_move_on ||
6829                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6830                                                    y + back_dy + left_dy)));
6831     should_turn_right = (can_turn_right &&
6832                          (!can_move_on ||
6833                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6834                                                     y + back_dy + right_dy)));
6835     should_move_on = (can_move_on &&
6836                       (!can_turn_left ||
6837                        !can_turn_right ||
6838                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6839                                                  y + move_dy + left_dy) ||
6840                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6841                                                  y + move_dy + right_dy)));
6842
6843     if (should_turn_left || should_turn_right || should_move_on)
6844     {
6845       if (should_turn_left && should_turn_right && should_move_on)
6846         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6847                         rnd < 2 * rnd_value / 3 ? right_dir :
6848                         old_move_dir);
6849       else if (should_turn_left && should_turn_right)
6850         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6851       else if (should_turn_left && should_move_on)
6852         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6853       else if (should_turn_right && should_move_on)
6854         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6855       else if (should_turn_left)
6856         MovDir[x][y] = left_dir;
6857       else if (should_turn_right)
6858         MovDir[x][y] = right_dir;
6859       else if (should_move_on)
6860         MovDir[x][y] = old_move_dir;
6861     }
6862     else if (can_move_on && rnd > rnd_value / 8)
6863       MovDir[x][y] = old_move_dir;
6864     else if (can_turn_left && can_turn_right)
6865       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6866     else if (can_turn_left && rnd > rnd_value / 8)
6867       MovDir[x][y] = left_dir;
6868     else if (can_turn_right && rnd > rnd_value/8)
6869       MovDir[x][y] = right_dir;
6870     else
6871       MovDir[x][y] = back_dir;
6872
6873     xx = x + move_xy[MovDir[x][y]].dx;
6874     yy = y + move_xy[MovDir[x][y]].dy;
6875
6876     if (!IN_LEV_FIELD(xx, yy) ||
6877         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6878       MovDir[x][y] = old_move_dir;
6879
6880     MovDelay[x][y] = 0;
6881   }
6882   else if (element == EL_DRAGON)
6883   {
6884     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6885     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6886     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6887     int rnd_value = 24;
6888     int rnd = RND(rnd_value);
6889
6890     if (can_move_on && rnd > rnd_value / 8)
6891       MovDir[x][y] = old_move_dir;
6892     else if (can_turn_left && can_turn_right)
6893       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6894     else if (can_turn_left && rnd > rnd_value / 8)
6895       MovDir[x][y] = left_dir;
6896     else if (can_turn_right && rnd > rnd_value / 8)
6897       MovDir[x][y] = right_dir;
6898     else
6899       MovDir[x][y] = back_dir;
6900
6901     xx = x + move_xy[MovDir[x][y]].dx;
6902     yy = y + move_xy[MovDir[x][y]].dy;
6903
6904     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6905       MovDir[x][y] = old_move_dir;
6906
6907     MovDelay[x][y] = 0;
6908   }
6909   else if (element == EL_MOLE)
6910   {
6911     boolean can_move_on =
6912       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6913                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6914                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6915     if (!can_move_on)
6916     {
6917       boolean can_turn_left =
6918         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6919                               IS_AMOEBOID(Feld[left_x][left_y])));
6920
6921       boolean can_turn_right =
6922         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6923                               IS_AMOEBOID(Feld[right_x][right_y])));
6924
6925       if (can_turn_left && can_turn_right)
6926         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6927       else if (can_turn_left)
6928         MovDir[x][y] = left_dir;
6929       else
6930         MovDir[x][y] = right_dir;
6931     }
6932
6933     if (MovDir[x][y] != old_move_dir)
6934       MovDelay[x][y] = 9;
6935   }
6936   else if (element == EL_BALLOON)
6937   {
6938     MovDir[x][y] = game.wind_direction;
6939     MovDelay[x][y] = 0;
6940   }
6941   else if (element == EL_SPRING)
6942   {
6943 #if USE_NEW_SPRING_BUMPER
6944     if (MovDir[x][y] & MV_HORIZONTAL)
6945     {
6946       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6947           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6948       {
6949         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6950         ResetGfxAnimation(move_x, move_y);
6951         DrawLevelField(move_x, move_y);
6952
6953         MovDir[x][y] = back_dir;
6954       }
6955       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6956                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6957         MovDir[x][y] = MV_NONE;
6958     }
6959 #else
6960     if (MovDir[x][y] & MV_HORIZONTAL &&
6961         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6962          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6963       MovDir[x][y] = MV_NONE;
6964 #endif
6965
6966     MovDelay[x][y] = 0;
6967   }
6968   else if (element == EL_ROBOT ||
6969            element == EL_SATELLITE ||
6970            element == EL_PENGUIN ||
6971            element == EL_EMC_ANDROID)
6972   {
6973     int attr_x = -1, attr_y = -1;
6974
6975     if (AllPlayersGone)
6976     {
6977       attr_x = ExitX;
6978       attr_y = ExitY;
6979     }
6980     else
6981     {
6982       int i;
6983
6984       for (i = 0; i < MAX_PLAYERS; i++)
6985       {
6986         struct PlayerInfo *player = &stored_player[i];
6987         int jx = player->jx, jy = player->jy;
6988
6989         if (!player->active)
6990           continue;
6991
6992         if (attr_x == -1 ||
6993             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6994         {
6995           attr_x = jx;
6996           attr_y = jy;
6997         }
6998       }
6999     }
7000
7001     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7002         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7003          game.engine_version < VERSION_IDENT(3,1,0,0)))
7004     {
7005       attr_x = ZX;
7006       attr_y = ZY;
7007     }
7008
7009     if (element == EL_PENGUIN)
7010     {
7011       int i;
7012       static int xy[4][2] =
7013       {
7014         { 0, -1 },
7015         { -1, 0 },
7016         { +1, 0 },
7017         { 0, +1 }
7018       };
7019
7020       for (i = 0; i < NUM_DIRECTIONS; i++)
7021       {
7022         int ex = x + xy[i][0];
7023         int ey = y + xy[i][1];
7024
7025         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7026                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7027                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7028                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7029         {
7030           attr_x = ex;
7031           attr_y = ey;
7032           break;
7033         }
7034       }
7035     }
7036
7037     MovDir[x][y] = MV_NONE;
7038     if (attr_x < x)
7039       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7040     else if (attr_x > x)
7041       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7042     if (attr_y < y)
7043       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7044     else if (attr_y > y)
7045       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7046
7047     if (element == EL_ROBOT)
7048     {
7049       int newx, newy;
7050
7051       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7052         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7053       Moving2Blocked(x, y, &newx, &newy);
7054
7055       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7056         MovDelay[x][y] = 8 + 8 * !RND(3);
7057       else
7058         MovDelay[x][y] = 16;
7059     }
7060     else if (element == EL_PENGUIN)
7061     {
7062       int newx, newy;
7063
7064       MovDelay[x][y] = 1;
7065
7066       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7067       {
7068         boolean first_horiz = RND(2);
7069         int new_move_dir = MovDir[x][y];
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] =
7079           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7080         Moving2Blocked(x, y, &newx, &newy);
7081
7082         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7083           return;
7084
7085         MovDir[x][y] = old_move_dir;
7086         return;
7087       }
7088     }
7089     else if (element == EL_SATELLITE)
7090     {
7091       int newx, newy;
7092
7093       MovDelay[x][y] = 1;
7094
7095       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7096       {
7097         boolean first_horiz = RND(2);
7098         int new_move_dir = MovDir[x][y];
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] =
7108           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7109         Moving2Blocked(x, y, &newx, &newy);
7110
7111         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7112           return;
7113
7114         MovDir[x][y] = old_move_dir;
7115         return;
7116       }
7117     }
7118     else if (element == EL_EMC_ANDROID)
7119     {
7120       static int check_pos[16] =
7121       {
7122         -1,             /*  0 => (invalid)          */
7123         7,              /*  1 => MV_LEFT            */
7124         3,              /*  2 => MV_RIGHT           */
7125         -1,             /*  3 => (invalid)          */
7126         1,              /*  4 =>            MV_UP   */
7127         0,              /*  5 => MV_LEFT  | MV_UP   */
7128         2,              /*  6 => MV_RIGHT | MV_UP   */
7129         -1,             /*  7 => (invalid)          */
7130         5,              /*  8 =>            MV_DOWN */
7131         6,              /*  9 => MV_LEFT  | MV_DOWN */
7132         4,              /* 10 => MV_RIGHT | MV_DOWN */
7133         -1,             /* 11 => (invalid)          */
7134         -1,             /* 12 => (invalid)          */
7135         -1,             /* 13 => (invalid)          */
7136         -1,             /* 14 => (invalid)          */
7137         -1,             /* 15 => (invalid)          */
7138       };
7139       static struct
7140       {
7141         int dx, dy;
7142         int dir;
7143       } check_xy[8] =
7144       {
7145         { -1, -1,       MV_LEFT  | MV_UP   },
7146         {  0, -1,                  MV_UP   },
7147         { +1, -1,       MV_RIGHT | MV_UP   },
7148         { +1,  0,       MV_RIGHT           },
7149         { +1, +1,       MV_RIGHT | MV_DOWN },
7150         {  0, +1,                  MV_DOWN },
7151         { -1, +1,       MV_LEFT  | MV_DOWN },
7152         { -1,  0,       MV_LEFT            },
7153       };
7154       int start_pos, check_order;
7155       boolean can_clone = FALSE;
7156       int i;
7157
7158       /* check if there is any free field around current position */
7159       for (i = 0; i < 8; i++)
7160       {
7161         int newx = x + check_xy[i].dx;
7162         int newy = y + check_xy[i].dy;
7163
7164         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7165         {
7166           can_clone = TRUE;
7167
7168           break;
7169         }
7170       }
7171
7172       if (can_clone)            /* randomly find an element to clone */
7173       {
7174         can_clone = FALSE;
7175
7176         start_pos = check_pos[RND(8)];
7177         check_order = (RND(2) ? -1 : +1);
7178
7179         for (i = 0; i < 8; i++)
7180         {
7181           int pos_raw = start_pos + i * check_order;
7182           int pos = (pos_raw + 8) % 8;
7183           int newx = x + check_xy[pos].dx;
7184           int newy = y + check_xy[pos].dy;
7185
7186           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7187           {
7188             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7189             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7190
7191             Store[x][y] = Feld[newx][newy];
7192
7193             can_clone = TRUE;
7194
7195             break;
7196           }
7197         }
7198       }
7199
7200       if (can_clone)            /* randomly find a direction to move */
7201       {
7202         can_clone = FALSE;
7203
7204         start_pos = check_pos[RND(8)];
7205         check_order = (RND(2) ? -1 : +1);
7206
7207         for (i = 0; i < 8; i++)
7208         {
7209           int pos_raw = start_pos + i * check_order;
7210           int pos = (pos_raw + 8) % 8;
7211           int newx = x + check_xy[pos].dx;
7212           int newy = y + check_xy[pos].dy;
7213           int new_move_dir = check_xy[pos].dir;
7214
7215           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7216           {
7217             MovDir[x][y] = new_move_dir;
7218             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7219
7220             can_clone = TRUE;
7221
7222             break;
7223           }
7224         }
7225       }
7226
7227       if (can_clone)            /* cloning and moving successful */
7228         return;
7229
7230       /* cannot clone -- try to move towards player */
7231
7232       start_pos = check_pos[MovDir[x][y] & 0x0f];
7233       check_order = (RND(2) ? -1 : +1);
7234
7235       for (i = 0; i < 3; i++)
7236       {
7237         /* first check start_pos, then previous/next or (next/previous) pos */
7238         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7239         int pos = (pos_raw + 8) % 8;
7240         int newx = x + check_xy[pos].dx;
7241         int newy = y + check_xy[pos].dy;
7242         int new_move_dir = check_xy[pos].dir;
7243
7244         if (IS_PLAYER(newx, newy))
7245           break;
7246
7247         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7248         {
7249           MovDir[x][y] = new_move_dir;
7250           MovDelay[x][y] = level.android_move_time * 8 + 1;
7251
7252           break;
7253         }
7254       }
7255     }
7256   }
7257   else if (move_pattern == MV_TURNING_LEFT ||
7258            move_pattern == MV_TURNING_RIGHT ||
7259            move_pattern == MV_TURNING_LEFT_RIGHT ||
7260            move_pattern == MV_TURNING_RIGHT_LEFT ||
7261            move_pattern == MV_TURNING_RANDOM ||
7262            move_pattern == MV_ALL_DIRECTIONS)
7263   {
7264     boolean can_turn_left =
7265       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7266     boolean can_turn_right =
7267       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7268
7269     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7270       return;
7271
7272     if (move_pattern == MV_TURNING_LEFT)
7273       MovDir[x][y] = left_dir;
7274     else if (move_pattern == MV_TURNING_RIGHT)
7275       MovDir[x][y] = right_dir;
7276     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7277       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7278     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7279       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7280     else if (move_pattern == MV_TURNING_RANDOM)
7281       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7282                       can_turn_right && !can_turn_left ? right_dir :
7283                       RND(2) ? left_dir : right_dir);
7284     else if (can_turn_left && can_turn_right)
7285       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7286     else if (can_turn_left)
7287       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7288     else if (can_turn_right)
7289       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7290     else
7291       MovDir[x][y] = back_dir;
7292
7293     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7294   }
7295   else if (move_pattern == MV_HORIZONTAL ||
7296            move_pattern == MV_VERTICAL)
7297   {
7298     if (move_pattern & old_move_dir)
7299       MovDir[x][y] = back_dir;
7300     else if (move_pattern == MV_HORIZONTAL)
7301       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7302     else if (move_pattern == MV_VERTICAL)
7303       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7304
7305     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7306   }
7307   else if (move_pattern & MV_ANY_DIRECTION)
7308   {
7309     MovDir[x][y] = move_pattern;
7310     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7311   }
7312   else if (move_pattern & MV_WIND_DIRECTION)
7313   {
7314     MovDir[x][y] = game.wind_direction;
7315     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7316   }
7317   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7318   {
7319     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7320       MovDir[x][y] = left_dir;
7321     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7322       MovDir[x][y] = right_dir;
7323
7324     if (MovDir[x][y] != old_move_dir)
7325       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7326   }
7327   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7328   {
7329     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7330       MovDir[x][y] = right_dir;
7331     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7332       MovDir[x][y] = left_dir;
7333
7334     if (MovDir[x][y] != old_move_dir)
7335       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7336   }
7337   else if (move_pattern == MV_TOWARDS_PLAYER ||
7338            move_pattern == MV_AWAY_FROM_PLAYER)
7339   {
7340     int attr_x = -1, attr_y = -1;
7341     int newx, newy;
7342     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7343
7344     if (AllPlayersGone)
7345     {
7346       attr_x = ExitX;
7347       attr_y = ExitY;
7348     }
7349     else
7350     {
7351       int i;
7352
7353       for (i = 0; i < MAX_PLAYERS; i++)
7354       {
7355         struct PlayerInfo *player = &stored_player[i];
7356         int jx = player->jx, jy = player->jy;
7357
7358         if (!player->active)
7359           continue;
7360
7361         if (attr_x == -1 ||
7362             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7363         {
7364           attr_x = jx;
7365           attr_y = jy;
7366         }
7367       }
7368     }
7369
7370     MovDir[x][y] = MV_NONE;
7371     if (attr_x < x)
7372       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7373     else if (attr_x > x)
7374       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7375     if (attr_y < y)
7376       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7377     else if (attr_y > y)
7378       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7379
7380     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7381
7382     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7383     {
7384       boolean first_horiz = RND(2);
7385       int new_move_dir = MovDir[x][y];
7386
7387       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7388       {
7389         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7390         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7391
7392         return;
7393       }
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] =
7403         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7404       Moving2Blocked(x, y, &newx, &newy);
7405
7406       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7407         return;
7408
7409       MovDir[x][y] = old_move_dir;
7410     }
7411   }
7412   else if (move_pattern == MV_WHEN_PUSHED ||
7413            move_pattern == MV_WHEN_DROPPED)
7414   {
7415     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7416       MovDir[x][y] = MV_NONE;
7417
7418     MovDelay[x][y] = 0;
7419   }
7420   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7421   {
7422     static int test_xy[7][2] =
7423     {
7424       { 0, -1 },
7425       { -1, 0 },
7426       { +1, 0 },
7427       { 0, +1 },
7428       { 0, -1 },
7429       { -1, 0 },
7430       { +1, 0 },
7431     };
7432     static int test_dir[7] =
7433     {
7434       MV_UP,
7435       MV_LEFT,
7436       MV_RIGHT,
7437       MV_DOWN,
7438       MV_UP,
7439       MV_LEFT,
7440       MV_RIGHT,
7441     };
7442     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7443     int move_preference = -1000000;     /* start with very low preference */
7444     int new_move_dir = MV_NONE;
7445     int start_test = RND(4);
7446     int i;
7447
7448     for (i = 0; i < NUM_DIRECTIONS; i++)
7449     {
7450       int move_dir = test_dir[start_test + i];
7451       int move_dir_preference;
7452
7453       xx = x + test_xy[start_test + i][0];
7454       yy = y + test_xy[start_test + i][1];
7455
7456       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7457           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7458       {
7459         new_move_dir = move_dir;
7460
7461         break;
7462       }
7463
7464       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7465         continue;
7466
7467       move_dir_preference = -1 * RunnerVisit[xx][yy];
7468       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7469         move_dir_preference = PlayerVisit[xx][yy];
7470
7471       if (move_dir_preference > move_preference)
7472       {
7473         /* prefer field that has not been visited for the longest time */
7474         move_preference = move_dir_preference;
7475         new_move_dir = move_dir;
7476       }
7477       else if (move_dir_preference == move_preference &&
7478                move_dir == old_move_dir)
7479       {
7480         /* prefer last direction when all directions are preferred equally */
7481         move_preference = move_dir_preference;
7482         new_move_dir = move_dir;
7483       }
7484     }
7485
7486     MovDir[x][y] = new_move_dir;
7487     if (old_move_dir != new_move_dir)
7488       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7489   }
7490 }
7491
7492 static void TurnRound(int x, int y)
7493 {
7494   int direction = MovDir[x][y];
7495
7496   TurnRoundExt(x, y);
7497
7498   GfxDir[x][y] = MovDir[x][y];
7499
7500   if (direction != MovDir[x][y])
7501     GfxFrame[x][y] = 0;
7502
7503   if (MovDelay[x][y])
7504     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7505
7506   ResetGfxFrame(x, y, FALSE);
7507 }
7508
7509 static boolean JustBeingPushed(int x, int y)
7510 {
7511   int i;
7512
7513   for (i = 0; i < MAX_PLAYERS; i++)
7514   {
7515     struct PlayerInfo *player = &stored_player[i];
7516
7517     if (player->active && player->is_pushing && player->MovPos)
7518     {
7519       int next_jx = player->jx + (player->jx - player->last_jx);
7520       int next_jy = player->jy + (player->jy - player->last_jy);
7521
7522       if (x == next_jx && y == next_jy)
7523         return TRUE;
7524     }
7525   }
7526
7527   return FALSE;
7528 }
7529
7530 void StartMoving(int x, int y)
7531 {
7532   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7533   int element = Feld[x][y];
7534
7535   if (Stop[x][y])
7536     return;
7537
7538   if (MovDelay[x][y] == 0)
7539     GfxAction[x][y] = ACTION_DEFAULT;
7540
7541   if (CAN_FALL(element) && y < lev_fieldy - 1)
7542   {
7543     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7544         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7545       if (JustBeingPushed(x, y))
7546         return;
7547
7548     if (element == EL_QUICKSAND_FULL)
7549     {
7550       if (IS_FREE(x, y + 1))
7551       {
7552         InitMovingField(x, y, MV_DOWN);
7553         started_moving = TRUE;
7554
7555         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7556 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7557         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7558           Store[x][y] = EL_ROCK;
7559 #else
7560         Store[x][y] = EL_ROCK;
7561 #endif
7562
7563         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7564       }
7565       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7566       {
7567         if (!MovDelay[x][y])
7568         {
7569           MovDelay[x][y] = TILEY + 1;
7570
7571           ResetGfxAnimation(x, y);
7572           ResetGfxAnimation(x, y + 1);
7573         }
7574
7575         if (MovDelay[x][y])
7576         {
7577           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7578           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7579
7580           MovDelay[x][y]--;
7581           if (MovDelay[x][y])
7582             return;
7583         }
7584
7585         Feld[x][y] = EL_QUICKSAND_EMPTY;
7586         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7587         Store[x][y + 1] = Store[x][y];
7588         Store[x][y] = 0;
7589
7590         PlayLevelSoundAction(x, y, ACTION_FILLING);
7591       }
7592     }
7593     else if (element == EL_QUICKSAND_FAST_FULL)
7594     {
7595       if (IS_FREE(x, y + 1))
7596       {
7597         InitMovingField(x, y, MV_DOWN);
7598         started_moving = TRUE;
7599
7600         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7601 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7602         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7603           Store[x][y] = EL_ROCK;
7604 #else
7605         Store[x][y] = EL_ROCK;
7606 #endif
7607
7608         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7609       }
7610       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7611       {
7612         if (!MovDelay[x][y])
7613         {
7614           MovDelay[x][y] = TILEY + 1;
7615
7616           ResetGfxAnimation(x, y);
7617           ResetGfxAnimation(x, y + 1);
7618         }
7619
7620         if (MovDelay[x][y])
7621         {
7622           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7623           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7624
7625           MovDelay[x][y]--;
7626           if (MovDelay[x][y])
7627             return;
7628         }
7629
7630         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7631         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7632         Store[x][y + 1] = Store[x][y];
7633         Store[x][y] = 0;
7634
7635         PlayLevelSoundAction(x, y, ACTION_FILLING);
7636       }
7637     }
7638     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7639              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7640     {
7641       InitMovingField(x, y, MV_DOWN);
7642       started_moving = TRUE;
7643
7644       Feld[x][y] = EL_QUICKSAND_FILLING;
7645       Store[x][y] = element;
7646
7647       PlayLevelSoundAction(x, y, ACTION_FILLING);
7648     }
7649     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7650              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7651     {
7652       InitMovingField(x, y, MV_DOWN);
7653       started_moving = TRUE;
7654
7655       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7656       Store[x][y] = element;
7657
7658       PlayLevelSoundAction(x, y, ACTION_FILLING);
7659     }
7660     else if (element == EL_MAGIC_WALL_FULL)
7661     {
7662       if (IS_FREE(x, y + 1))
7663       {
7664         InitMovingField(x, y, MV_DOWN);
7665         started_moving = TRUE;
7666
7667         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7668         Store[x][y] = EL_CHANGED(Store[x][y]);
7669       }
7670       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7671       {
7672         if (!MovDelay[x][y])
7673           MovDelay[x][y] = TILEY/4 + 1;
7674
7675         if (MovDelay[x][y])
7676         {
7677           MovDelay[x][y]--;
7678           if (MovDelay[x][y])
7679             return;
7680         }
7681
7682         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7683         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7684         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7685         Store[x][y] = 0;
7686       }
7687     }
7688     else if (element == EL_BD_MAGIC_WALL_FULL)
7689     {
7690       if (IS_FREE(x, y + 1))
7691       {
7692         InitMovingField(x, y, MV_DOWN);
7693         started_moving = TRUE;
7694
7695         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7696         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7697       }
7698       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7699       {
7700         if (!MovDelay[x][y])
7701           MovDelay[x][y] = TILEY/4 + 1;
7702
7703         if (MovDelay[x][y])
7704         {
7705           MovDelay[x][y]--;
7706           if (MovDelay[x][y])
7707             return;
7708         }
7709
7710         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7711         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7712         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7713         Store[x][y] = 0;
7714       }
7715     }
7716     else if (element == EL_DC_MAGIC_WALL_FULL)
7717     {
7718       if (IS_FREE(x, y + 1))
7719       {
7720         InitMovingField(x, y, MV_DOWN);
7721         started_moving = TRUE;
7722
7723         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7724         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7725       }
7726       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7727       {
7728         if (!MovDelay[x][y])
7729           MovDelay[x][y] = TILEY/4 + 1;
7730
7731         if (MovDelay[x][y])
7732         {
7733           MovDelay[x][y]--;
7734           if (MovDelay[x][y])
7735             return;
7736         }
7737
7738         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7739         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7740         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7741         Store[x][y] = 0;
7742       }
7743     }
7744     else if ((CAN_PASS_MAGIC_WALL(element) &&
7745               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7746                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7747              (CAN_PASS_DC_MAGIC_WALL(element) &&
7748               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7749
7750     {
7751       InitMovingField(x, y, MV_DOWN);
7752       started_moving = TRUE;
7753
7754       Feld[x][y] =
7755         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7756          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7757          EL_DC_MAGIC_WALL_FILLING);
7758       Store[x][y] = element;
7759     }
7760     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7761     {
7762       SplashAcid(x, y + 1);
7763
7764       InitMovingField(x, y, MV_DOWN);
7765       started_moving = TRUE;
7766
7767       Store[x][y] = EL_ACID;
7768     }
7769     else if (
7770 #if USE_FIX_IMPACT_COLLISION
7771              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7772               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7773 #else
7774              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7775               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7776 #endif
7777              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7778               CAN_FALL(element) && WasJustFalling[x][y] &&
7779               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7780
7781              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7782               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7783               (Feld[x][y + 1] == EL_BLOCKED)))
7784     {
7785       /* this is needed for a special case not covered by calling "Impact()"
7786          from "ContinueMoving()": if an element moves to a tile directly below
7787          another element which was just falling on that tile (which was empty
7788          in the previous frame), the falling element above would just stop
7789          instead of smashing the element below (in previous version, the above
7790          element was just checked for "moving" instead of "falling", resulting
7791          in incorrect smashes caused by horizontal movement of the above
7792          element; also, the case of the player being the element to smash was
7793          simply not covered here... :-/ ) */
7794
7795       CheckCollision[x][y] = 0;
7796       CheckImpact[x][y] = 0;
7797
7798       Impact(x, y);
7799     }
7800     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7801     {
7802       if (MovDir[x][y] == MV_NONE)
7803       {
7804         InitMovingField(x, y, MV_DOWN);
7805         started_moving = TRUE;
7806       }
7807     }
7808     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7809     {
7810       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7811         MovDir[x][y] = MV_DOWN;
7812
7813       InitMovingField(x, y, MV_DOWN);
7814       started_moving = TRUE;
7815     }
7816     else if (element == EL_AMOEBA_DROP)
7817     {
7818       Feld[x][y] = EL_AMOEBA_GROWING;
7819       Store[x][y] = EL_AMOEBA_WET;
7820     }
7821     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7822               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7823              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7824              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7825     {
7826       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7827                                 (IS_FREE(x - 1, y + 1) ||
7828                                  Feld[x - 1][y + 1] == EL_ACID));
7829       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7830                                 (IS_FREE(x + 1, y + 1) ||
7831                                  Feld[x + 1][y + 1] == EL_ACID));
7832       boolean can_fall_any  = (can_fall_left || can_fall_right);
7833       boolean can_fall_both = (can_fall_left && can_fall_right);
7834       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7835
7836 #if USE_NEW_ALL_SLIPPERY
7837       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7838       {
7839         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7840           can_fall_right = FALSE;
7841         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7842           can_fall_left = FALSE;
7843         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7844           can_fall_right = FALSE;
7845         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7846           can_fall_left = FALSE;
7847
7848         can_fall_any  = (can_fall_left || can_fall_right);
7849         can_fall_both = FALSE;
7850       }
7851 #else
7852       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7853       {
7854         if (slippery_type == SLIPPERY_ONLY_LEFT)
7855           can_fall_right = FALSE;
7856         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7857           can_fall_left = FALSE;
7858         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7859           can_fall_right = FALSE;
7860         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7861           can_fall_left = FALSE;
7862
7863         can_fall_any  = (can_fall_left || can_fall_right);
7864         can_fall_both = (can_fall_left && can_fall_right);
7865       }
7866 #endif
7867
7868 #if USE_NEW_ALL_SLIPPERY
7869 #else
7870 #if USE_NEW_SP_SLIPPERY
7871       /* !!! better use the same properties as for custom elements here !!! */
7872       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7873                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7874       {
7875         can_fall_right = FALSE;         /* slip down on left side */
7876         can_fall_both = FALSE;
7877       }
7878 #endif
7879 #endif
7880
7881 #if USE_NEW_ALL_SLIPPERY
7882       if (can_fall_both)
7883       {
7884         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7885           can_fall_right = FALSE;       /* slip down on left side */
7886         else
7887           can_fall_left = !(can_fall_right = RND(2));
7888
7889         can_fall_both = FALSE;
7890       }
7891 #else
7892       if (can_fall_both)
7893       {
7894         if (game.emulation == EMU_BOULDERDASH ||
7895             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7896           can_fall_right = FALSE;       /* slip down on left side */
7897         else
7898           can_fall_left = !(can_fall_right = RND(2));
7899
7900         can_fall_both = FALSE;
7901       }
7902 #endif
7903
7904       if (can_fall_any)
7905       {
7906         /* if not determined otherwise, prefer left side for slipping down */
7907         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7908         started_moving = TRUE;
7909       }
7910     }
7911 #if 0
7912     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7913 #else
7914     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7915 #endif
7916     {
7917       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7918       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7919       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7920       int belt_dir = game.belt_dir[belt_nr];
7921
7922       if ((belt_dir == MV_LEFT  && left_is_free) ||
7923           (belt_dir == MV_RIGHT && right_is_free))
7924       {
7925         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7926
7927         InitMovingField(x, y, belt_dir);
7928         started_moving = TRUE;
7929
7930         Pushed[x][y] = TRUE;
7931         Pushed[nextx][y] = TRUE;
7932
7933         GfxAction[x][y] = ACTION_DEFAULT;
7934       }
7935       else
7936       {
7937         MovDir[x][y] = 0;       /* if element was moving, stop it */
7938       }
7939     }
7940   }
7941
7942   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7943 #if 0
7944   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7945 #else
7946   if (CAN_MOVE(element) && !started_moving)
7947 #endif
7948   {
7949     int move_pattern = element_info[element].move_pattern;
7950     int newx, newy;
7951
7952 #if 0
7953 #if DEBUG
7954     if (MovDir[x][y] == MV_NONE)
7955     {
7956       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7957              x, y, element, element_info[element].token_name);
7958       printf("StartMoving(): This should never happen!\n");
7959     }
7960 #endif
7961 #endif
7962
7963     Moving2Blocked(x, y, &newx, &newy);
7964
7965     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7966       return;
7967
7968     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7969         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7970     {
7971       WasJustMoving[x][y] = 0;
7972       CheckCollision[x][y] = 0;
7973
7974       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7975
7976       if (Feld[x][y] != element)        /* element has changed */
7977         return;
7978     }
7979
7980     if (!MovDelay[x][y])        /* start new movement phase */
7981     {
7982       /* all objects that can change their move direction after each step
7983          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7984
7985       if (element != EL_YAMYAM &&
7986           element != EL_DARK_YAMYAM &&
7987           element != EL_PACMAN &&
7988           !(move_pattern & MV_ANY_DIRECTION) &&
7989           move_pattern != MV_TURNING_LEFT &&
7990           move_pattern != MV_TURNING_RIGHT &&
7991           move_pattern != MV_TURNING_LEFT_RIGHT &&
7992           move_pattern != MV_TURNING_RIGHT_LEFT &&
7993           move_pattern != MV_TURNING_RANDOM)
7994       {
7995         TurnRound(x, y);
7996
7997         if (MovDelay[x][y] && (element == EL_BUG ||
7998                                element == EL_SPACESHIP ||
7999                                element == EL_SP_SNIKSNAK ||
8000                                element == EL_SP_ELECTRON ||
8001                                element == EL_MOLE))
8002           DrawLevelField(x, y);
8003       }
8004     }
8005
8006     if (MovDelay[x][y])         /* wait some time before next movement */
8007     {
8008       MovDelay[x][y]--;
8009
8010       if (element == EL_ROBOT ||
8011           element == EL_YAMYAM ||
8012           element == EL_DARK_YAMYAM)
8013       {
8014         DrawLevelElementAnimationIfNeeded(x, y, element);
8015         PlayLevelSoundAction(x, y, ACTION_WAITING);
8016       }
8017       else if (element == EL_SP_ELECTRON)
8018         DrawLevelElementAnimationIfNeeded(x, y, element);
8019       else if (element == EL_DRAGON)
8020       {
8021         int i;
8022         int dir = MovDir[x][y];
8023         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8024         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8025         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8026                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8027                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8028                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8029         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8030
8031         GfxAction[x][y] = ACTION_ATTACKING;
8032
8033         if (IS_PLAYER(x, y))
8034           DrawPlayerField(x, y);
8035         else
8036           DrawLevelField(x, y);
8037
8038         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8039
8040         for (i = 1; i <= 3; i++)
8041         {
8042           int xx = x + i * dx;
8043           int yy = y + i * dy;
8044           int sx = SCREENX(xx);
8045           int sy = SCREENY(yy);
8046           int flame_graphic = graphic + (i - 1);
8047
8048           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8049             break;
8050
8051           if (MovDelay[x][y])
8052           {
8053             int flamed = MovingOrBlocked2Element(xx, yy);
8054
8055             /* !!! */
8056 #if 0
8057             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8058               Bang(xx, yy);
8059             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8060               RemoveMovingField(xx, yy);
8061             else
8062               RemoveField(xx, yy);
8063 #else
8064             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8065               Bang(xx, yy);
8066             else
8067               RemoveMovingField(xx, yy);
8068 #endif
8069
8070             ChangeDelay[xx][yy] = 0;
8071
8072             Feld[xx][yy] = EL_FLAMES;
8073
8074             if (IN_SCR_FIELD(sx, sy))
8075             {
8076               DrawLevelFieldCrumbledSand(xx, yy);
8077               DrawGraphic(sx, sy, flame_graphic, frame);
8078             }
8079           }
8080           else
8081           {
8082             if (Feld[xx][yy] == EL_FLAMES)
8083               Feld[xx][yy] = EL_EMPTY;
8084             DrawLevelField(xx, yy);
8085           }
8086         }
8087       }
8088
8089       if (MovDelay[x][y])       /* element still has to wait some time */
8090       {
8091         PlayLevelSoundAction(x, y, ACTION_WAITING);
8092
8093         return;
8094       }
8095     }
8096
8097     /* now make next step */
8098
8099     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8100
8101     if (DONT_COLLIDE_WITH(element) &&
8102         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8103         !PLAYER_ENEMY_PROTECTED(newx, newy))
8104     {
8105       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8106
8107       return;
8108     }
8109
8110     else if (CAN_MOVE_INTO_ACID(element) &&
8111              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8112              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8113              (MovDir[x][y] == MV_DOWN ||
8114               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8115     {
8116       SplashAcid(newx, newy);
8117       Store[x][y] = EL_ACID;
8118     }
8119     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8120     {
8121       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8122           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8123           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8124           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8125       {
8126         RemoveField(x, y);
8127         DrawLevelField(x, y);
8128
8129         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8130         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8131           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8132
8133         local_player->friends_still_needed--;
8134         if (!local_player->friends_still_needed &&
8135             !local_player->GameOver && AllPlayersGone)
8136           PlayerWins(local_player);
8137
8138         return;
8139       }
8140       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8141       {
8142         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8143           DrawLevelField(newx, newy);
8144         else
8145           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8146       }
8147       else if (!IS_FREE(newx, newy))
8148       {
8149         GfxAction[x][y] = ACTION_WAITING;
8150
8151         if (IS_PLAYER(x, y))
8152           DrawPlayerField(x, y);
8153         else
8154           DrawLevelField(x, y);
8155
8156         return;
8157       }
8158     }
8159     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8160     {
8161       if (IS_FOOD_PIG(Feld[newx][newy]))
8162       {
8163         if (IS_MOVING(newx, newy))
8164           RemoveMovingField(newx, newy);
8165         else
8166         {
8167           Feld[newx][newy] = EL_EMPTY;
8168           DrawLevelField(newx, newy);
8169         }
8170
8171         PlayLevelSound(x, y, SND_PIG_DIGGING);
8172       }
8173       else if (!IS_FREE(newx, newy))
8174       {
8175         if (IS_PLAYER(x, y))
8176           DrawPlayerField(x, y);
8177         else
8178           DrawLevelField(x, y);
8179
8180         return;
8181       }
8182     }
8183     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8184     {
8185       if (Store[x][y] != EL_EMPTY)
8186       {
8187         boolean can_clone = FALSE;
8188         int xx, yy;
8189
8190         /* check if element to clone is still there */
8191         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8192         {
8193           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8194           {
8195             can_clone = TRUE;
8196
8197             break;
8198           }
8199         }
8200
8201         /* cannot clone or target field not free anymore -- do not clone */
8202         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8203           Store[x][y] = EL_EMPTY;
8204       }
8205
8206       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8207       {
8208         if (IS_MV_DIAGONAL(MovDir[x][y]))
8209         {
8210           int diagonal_move_dir = MovDir[x][y];
8211           int stored = Store[x][y];
8212           int change_delay = 8;
8213           int graphic;
8214
8215           /* android is moving diagonally */
8216
8217           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8218
8219           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8220           GfxElement[x][y] = EL_EMC_ANDROID;
8221           GfxAction[x][y] = ACTION_SHRINKING;
8222           GfxDir[x][y] = diagonal_move_dir;
8223           ChangeDelay[x][y] = change_delay;
8224
8225           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8226                                    GfxDir[x][y]);
8227
8228           DrawLevelGraphicAnimation(x, y, graphic);
8229           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8230
8231           if (Feld[newx][newy] == EL_ACID)
8232           {
8233             SplashAcid(newx, newy);
8234
8235             return;
8236           }
8237
8238           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8239
8240           Store[newx][newy] = EL_EMC_ANDROID;
8241           GfxElement[newx][newy] = EL_EMC_ANDROID;
8242           GfxAction[newx][newy] = ACTION_GROWING;
8243           GfxDir[newx][newy] = diagonal_move_dir;
8244           ChangeDelay[newx][newy] = change_delay;
8245
8246           graphic = el_act_dir2img(GfxElement[newx][newy],
8247                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8248
8249           DrawLevelGraphicAnimation(newx, newy, graphic);
8250           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8251
8252           return;
8253         }
8254         else
8255         {
8256           Feld[newx][newy] = EL_EMPTY;
8257           DrawLevelField(newx, newy);
8258
8259           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8260         }
8261       }
8262       else if (!IS_FREE(newx, newy))
8263       {
8264 #if 0
8265         if (IS_PLAYER(x, y))
8266           DrawPlayerField(x, y);
8267         else
8268           DrawLevelField(x, y);
8269 #endif
8270
8271         return;
8272       }
8273     }
8274     else if (IS_CUSTOM_ELEMENT(element) &&
8275              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8276     {
8277       int new_element = Feld[newx][newy];
8278
8279       if (!IS_FREE(newx, newy))
8280       {
8281         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8282                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8283                       ACTION_BREAKING);
8284
8285         /* no element can dig solid indestructible elements */
8286         if (IS_INDESTRUCTIBLE(new_element) &&
8287             !IS_DIGGABLE(new_element) &&
8288             !IS_COLLECTIBLE(new_element))
8289           return;
8290
8291         if (AmoebaNr[newx][newy] &&
8292             (new_element == EL_AMOEBA_FULL ||
8293              new_element == EL_BD_AMOEBA ||
8294              new_element == EL_AMOEBA_GROWING))
8295         {
8296           AmoebaCnt[AmoebaNr[newx][newy]]--;
8297           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8298         }
8299
8300         if (IS_MOVING(newx, newy))
8301           RemoveMovingField(newx, newy);
8302         else
8303         {
8304           RemoveField(newx, newy);
8305           DrawLevelField(newx, newy);
8306         }
8307
8308         /* if digged element was about to explode, prevent the explosion */
8309         ExplodeField[newx][newy] = EX_TYPE_NONE;
8310
8311         PlayLevelSoundAction(x, y, action);
8312       }
8313
8314       Store[newx][newy] = EL_EMPTY;
8315 #if 1
8316       /* this makes it possible to leave the removed element again */
8317       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8318         Store[newx][newy] = new_element;
8319 #else
8320       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8321       {
8322         int move_leave_element = element_info[element].move_leave_element;
8323
8324         /* this makes it possible to leave the removed element again */
8325         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8326                              new_element : move_leave_element);
8327       }
8328 #endif
8329
8330       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8331       {
8332         RunnerVisit[x][y] = FrameCounter;
8333         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8334       }
8335     }
8336     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8337     {
8338       if (!IS_FREE(newx, newy))
8339       {
8340         if (IS_PLAYER(x, y))
8341           DrawPlayerField(x, y);
8342         else
8343           DrawLevelField(x, y);
8344
8345         return;
8346       }
8347       else
8348       {
8349         boolean wanna_flame = !RND(10);
8350         int dx = newx - x, dy = newy - y;
8351         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8352         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8353         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8354                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8355         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8356                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8357
8358         if ((wanna_flame ||
8359              IS_CLASSIC_ENEMY(element1) ||
8360              IS_CLASSIC_ENEMY(element2)) &&
8361             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8362             element1 != EL_FLAMES && element2 != EL_FLAMES)
8363         {
8364           ResetGfxAnimation(x, y);
8365           GfxAction[x][y] = ACTION_ATTACKING;
8366
8367           if (IS_PLAYER(x, y))
8368             DrawPlayerField(x, y);
8369           else
8370             DrawLevelField(x, y);
8371
8372           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8373
8374           MovDelay[x][y] = 50;
8375
8376           /* !!! */
8377 #if 0
8378           RemoveField(newx, newy);
8379 #endif
8380           Feld[newx][newy] = EL_FLAMES;
8381           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8382           {
8383 #if 0
8384             RemoveField(newx1, newy1);
8385 #endif
8386             Feld[newx1][newy1] = EL_FLAMES;
8387           }
8388           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8389           {
8390 #if 0
8391             RemoveField(newx2, newy2);
8392 #endif
8393             Feld[newx2][newy2] = EL_FLAMES;
8394           }
8395
8396           return;
8397         }
8398       }
8399     }
8400     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8401              Feld[newx][newy] == EL_DIAMOND)
8402     {
8403       if (IS_MOVING(newx, newy))
8404         RemoveMovingField(newx, newy);
8405       else
8406       {
8407         Feld[newx][newy] = EL_EMPTY;
8408         DrawLevelField(newx, newy);
8409       }
8410
8411       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8412     }
8413     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8414              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8415     {
8416       if (AmoebaNr[newx][newy])
8417       {
8418         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8419         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8420             Feld[newx][newy] == EL_BD_AMOEBA)
8421           AmoebaCnt[AmoebaNr[newx][newy]]--;
8422       }
8423
8424 #if 0
8425       /* !!! test !!! */
8426       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8427       {
8428         RemoveMovingField(newx, newy);
8429       }
8430 #else
8431       if (IS_MOVING(newx, newy))
8432       {
8433         RemoveMovingField(newx, newy);
8434       }
8435 #endif
8436       else
8437       {
8438         Feld[newx][newy] = EL_EMPTY;
8439         DrawLevelField(newx, newy);
8440       }
8441
8442       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8443     }
8444     else if ((element == EL_PACMAN || element == EL_MOLE)
8445              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8446     {
8447       if (AmoebaNr[newx][newy])
8448       {
8449         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8450         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8451             Feld[newx][newy] == EL_BD_AMOEBA)
8452           AmoebaCnt[AmoebaNr[newx][newy]]--;
8453       }
8454
8455       if (element == EL_MOLE)
8456       {
8457         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8458         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8459
8460         ResetGfxAnimation(x, y);
8461         GfxAction[x][y] = ACTION_DIGGING;
8462         DrawLevelField(x, y);
8463
8464         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8465
8466         return;                         /* wait for shrinking amoeba */
8467       }
8468       else      /* element == EL_PACMAN */
8469       {
8470         Feld[newx][newy] = EL_EMPTY;
8471         DrawLevelField(newx, newy);
8472         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8473       }
8474     }
8475     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8476              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8477               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8478     {
8479       /* wait for shrinking amoeba to completely disappear */
8480       return;
8481     }
8482     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8483     {
8484       /* object was running against a wall */
8485
8486       TurnRound(x, y);
8487
8488 #if 0
8489       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8490       if (move_pattern & MV_ANY_DIRECTION &&
8491           move_pattern == MovDir[x][y])
8492       {
8493         int blocking_element =
8494           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8495
8496         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8497                                  MovDir[x][y]);
8498
8499         element = Feld[x][y];   /* element might have changed */
8500       }
8501 #endif
8502
8503       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8504         DrawLevelElementAnimation(x, y, element);
8505
8506       if (DONT_TOUCH(element))
8507         TestIfBadThingTouchesPlayer(x, y);
8508
8509       return;
8510     }
8511
8512     InitMovingField(x, y, MovDir[x][y]);
8513
8514     PlayLevelSoundAction(x, y, ACTION_MOVING);
8515   }
8516
8517   if (MovDir[x][y])
8518     ContinueMoving(x, y);
8519 }
8520
8521 void ContinueMoving(int x, int y)
8522 {
8523   int element = Feld[x][y];
8524   struct ElementInfo *ei = &element_info[element];
8525   int direction = MovDir[x][y];
8526   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8527   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8528   int newx = x + dx, newy = y + dy;
8529   int stored = Store[x][y];
8530   int stored_new = Store[newx][newy];
8531   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8532   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8533   boolean last_line = (newy == lev_fieldy - 1);
8534
8535   MovPos[x][y] += getElementMoveStepsize(x, y);
8536
8537   if (pushed_by_player) /* special case: moving object pushed by player */
8538     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8539
8540   if (ABS(MovPos[x][y]) < TILEX)
8541   {
8542 #if 0
8543     int ee = Feld[x][y];
8544     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8545     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8546
8547     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8548            x, y, ABS(MovPos[x][y]),
8549            ee, gg, ff,
8550            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8551 #endif
8552
8553     DrawLevelField(x, y);
8554
8555     return;     /* element is still moving */
8556   }
8557
8558   /* element reached destination field */
8559
8560   Feld[x][y] = EL_EMPTY;
8561   Feld[newx][newy] = element;
8562   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8563
8564   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8565   {
8566     element = Feld[newx][newy] = EL_ACID;
8567   }
8568   else if (element == EL_MOLE)
8569   {
8570     Feld[x][y] = EL_SAND;
8571
8572     DrawLevelFieldCrumbledSandNeighbours(x, y);
8573   }
8574   else if (element == EL_QUICKSAND_FILLING)
8575   {
8576     element = Feld[newx][newy] = get_next_element(element);
8577     Store[newx][newy] = Store[x][y];
8578   }
8579   else if (element == EL_QUICKSAND_EMPTYING)
8580   {
8581     Feld[x][y] = get_next_element(element);
8582     element = Feld[newx][newy] = Store[x][y];
8583   }
8584   else if (element == EL_QUICKSAND_FAST_FILLING)
8585   {
8586     element = Feld[newx][newy] = get_next_element(element);
8587     Store[newx][newy] = Store[x][y];
8588   }
8589   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8590   {
8591     Feld[x][y] = get_next_element(element);
8592     element = Feld[newx][newy] = Store[x][y];
8593   }
8594   else if (element == EL_MAGIC_WALL_FILLING)
8595   {
8596     element = Feld[newx][newy] = get_next_element(element);
8597     if (!game.magic_wall_active)
8598       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8599     Store[newx][newy] = Store[x][y];
8600   }
8601   else if (element == EL_MAGIC_WALL_EMPTYING)
8602   {
8603     Feld[x][y] = get_next_element(element);
8604     if (!game.magic_wall_active)
8605       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8606     element = Feld[newx][newy] = Store[x][y];
8607
8608 #if USE_NEW_CUSTOM_VALUE
8609     InitField(newx, newy, FALSE);
8610 #endif
8611   }
8612   else if (element == EL_BD_MAGIC_WALL_FILLING)
8613   {
8614     element = Feld[newx][newy] = get_next_element(element);
8615     if (!game.magic_wall_active)
8616       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8617     Store[newx][newy] = Store[x][y];
8618   }
8619   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8620   {
8621     Feld[x][y] = get_next_element(element);
8622     if (!game.magic_wall_active)
8623       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8624     element = Feld[newx][newy] = Store[x][y];
8625
8626 #if USE_NEW_CUSTOM_VALUE
8627     InitField(newx, newy, FALSE);
8628 #endif
8629   }
8630   else if (element == EL_DC_MAGIC_WALL_FILLING)
8631   {
8632     element = Feld[newx][newy] = get_next_element(element);
8633     if (!game.magic_wall_active)
8634       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8635     Store[newx][newy] = Store[x][y];
8636   }
8637   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8638   {
8639     Feld[x][y] = get_next_element(element);
8640     if (!game.magic_wall_active)
8641       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8642     element = Feld[newx][newy] = Store[x][y];
8643
8644 #if USE_NEW_CUSTOM_VALUE
8645     InitField(newx, newy, FALSE);
8646 #endif
8647   }
8648   else if (element == EL_AMOEBA_DROPPING)
8649   {
8650     Feld[x][y] = get_next_element(element);
8651     element = Feld[newx][newy] = Store[x][y];
8652   }
8653   else if (element == EL_SOKOBAN_OBJECT)
8654   {
8655     if (Back[x][y])
8656       Feld[x][y] = Back[x][y];
8657
8658     if (Back[newx][newy])
8659       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8660
8661     Back[x][y] = Back[newx][newy] = 0;
8662   }
8663
8664   Store[x][y] = EL_EMPTY;
8665   MovPos[x][y] = 0;
8666   MovDir[x][y] = 0;
8667   MovDelay[x][y] = 0;
8668
8669   MovDelay[newx][newy] = 0;
8670
8671   if (CAN_CHANGE_OR_HAS_ACTION(element))
8672   {
8673     /* copy element change control values to new field */
8674     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8675     ChangePage[newx][newy]  = ChangePage[x][y];
8676     ChangeCount[newx][newy] = ChangeCount[x][y];
8677     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8678   }
8679
8680 #if USE_NEW_CUSTOM_VALUE
8681   CustomValue[newx][newy] = CustomValue[x][y];
8682 #endif
8683
8684   ChangeDelay[x][y] = 0;
8685   ChangePage[x][y] = -1;
8686   ChangeCount[x][y] = 0;
8687   ChangeEvent[x][y] = -1;
8688
8689 #if USE_NEW_CUSTOM_VALUE
8690   CustomValue[x][y] = 0;
8691 #endif
8692
8693   /* copy animation control values to new field */
8694   GfxFrame[newx][newy]  = GfxFrame[x][y];
8695   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8696   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8697   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8698
8699   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8700
8701   /* some elements can leave other elements behind after moving */
8702 #if 1
8703   if (ei->move_leave_element != EL_EMPTY &&
8704       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8705       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8706 #else
8707   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8708       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8709       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8710 #endif
8711   {
8712     int move_leave_element = ei->move_leave_element;
8713
8714 #if 1
8715 #if 1
8716     /* this makes it possible to leave the removed element again */
8717     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8718       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8719 #else
8720     /* this makes it possible to leave the removed element again */
8721     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8722       move_leave_element = stored;
8723 #endif
8724 #else
8725     /* this makes it possible to leave the removed element again */
8726     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8727         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8728       move_leave_element = stored;
8729 #endif
8730
8731     Feld[x][y] = move_leave_element;
8732
8733     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8734       MovDir[x][y] = direction;
8735
8736     InitField(x, y, FALSE);
8737
8738     if (GFX_CRUMBLED(Feld[x][y]))
8739       DrawLevelFieldCrumbledSandNeighbours(x, y);
8740
8741     if (ELEM_IS_PLAYER(move_leave_element))
8742       RelocatePlayer(x, y, move_leave_element);
8743   }
8744
8745   /* do this after checking for left-behind element */
8746   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8747
8748   if (!CAN_MOVE(element) ||
8749       (CAN_FALL(element) && direction == MV_DOWN &&
8750        (element == EL_SPRING ||
8751         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8752         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8753     GfxDir[x][y] = MovDir[newx][newy] = 0;
8754
8755   DrawLevelField(x, y);
8756   DrawLevelField(newx, newy);
8757
8758   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8759
8760   /* prevent pushed element from moving on in pushed direction */
8761   if (pushed_by_player && CAN_MOVE(element) &&
8762       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8763       !(element_info[element].move_pattern & direction))
8764     TurnRound(newx, newy);
8765
8766   /* prevent elements on conveyor belt from moving on in last direction */
8767   if (pushed_by_conveyor && CAN_FALL(element) &&
8768       direction & MV_HORIZONTAL)
8769     MovDir[newx][newy] = 0;
8770
8771   if (!pushed_by_player)
8772   {
8773     int nextx = newx + dx, nexty = newy + dy;
8774     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8775
8776     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8777
8778     if (CAN_FALL(element) && direction == MV_DOWN)
8779       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8780
8781     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8782       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8783
8784 #if USE_FIX_IMPACT_COLLISION
8785     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8786       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8787 #endif
8788   }
8789
8790   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8791   {
8792     TestIfBadThingTouchesPlayer(newx, newy);
8793     TestIfBadThingTouchesFriend(newx, newy);
8794
8795     if (!IS_CUSTOM_ELEMENT(element))
8796       TestIfBadThingTouchesOtherBadThing(newx, newy);
8797   }
8798   else if (element == EL_PENGUIN)
8799     TestIfFriendTouchesBadThing(newx, newy);
8800
8801   /* give the player one last chance (one more frame) to move away */
8802   if (CAN_FALL(element) && direction == MV_DOWN &&
8803       (last_line || (!IS_FREE(x, newy + 1) &&
8804                      (!IS_PLAYER(x, newy + 1) ||
8805                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8806     Impact(x, newy);
8807
8808   if (pushed_by_player && !game.use_change_when_pushing_bug)
8809   {
8810     int push_side = MV_DIR_OPPOSITE(direction);
8811     struct PlayerInfo *player = PLAYERINFO(x, y);
8812
8813     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8814                                player->index_bit, push_side);
8815     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8816                                         player->index_bit, push_side);
8817   }
8818
8819   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8820     MovDelay[newx][newy] = 1;
8821
8822   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8823
8824   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8825
8826 #if 0
8827   if (ChangePage[newx][newy] != -1)             /* delayed change */
8828   {
8829     int page = ChangePage[newx][newy];
8830     struct ElementChangeInfo *change = &ei->change_page[page];
8831
8832     ChangePage[newx][newy] = -1;
8833
8834     if (change->can_change)
8835     {
8836       if (ChangeElement(newx, newy, element, page))
8837       {
8838         if (change->post_change_function)
8839           change->post_change_function(newx, newy);
8840       }
8841     }
8842
8843     if (change->has_action)
8844       ExecuteCustomElementAction(newx, newy, element, page);
8845   }
8846 #endif
8847
8848   TestIfElementHitsCustomElement(newx, newy, direction);
8849   TestIfPlayerTouchesCustomElement(newx, newy);
8850   TestIfElementTouchesCustomElement(newx, newy);
8851
8852   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8853       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8854     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8855                              MV_DIR_OPPOSITE(direction));
8856 }
8857
8858 int AmoebeNachbarNr(int ax, int ay)
8859 {
8860   int i;
8861   int element = Feld[ax][ay];
8862   int group_nr = 0;
8863   static int xy[4][2] =
8864   {
8865     { 0, -1 },
8866     { -1, 0 },
8867     { +1, 0 },
8868     { 0, +1 }
8869   };
8870
8871   for (i = 0; i < NUM_DIRECTIONS; i++)
8872   {
8873     int x = ax + xy[i][0];
8874     int y = ay + xy[i][1];
8875
8876     if (!IN_LEV_FIELD(x, y))
8877       continue;
8878
8879     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8880       group_nr = AmoebaNr[x][y];
8881   }
8882
8883   return group_nr;
8884 }
8885
8886 void AmoebenVereinigen(int ax, int ay)
8887 {
8888   int i, x, y, xx, yy;
8889   int new_group_nr = AmoebaNr[ax][ay];
8890   static int xy[4][2] =
8891   {
8892     { 0, -1 },
8893     { -1, 0 },
8894     { +1, 0 },
8895     { 0, +1 }
8896   };
8897
8898   if (new_group_nr == 0)
8899     return;
8900
8901   for (i = 0; i < NUM_DIRECTIONS; i++)
8902   {
8903     x = ax + xy[i][0];
8904     y = ay + xy[i][1];
8905
8906     if (!IN_LEV_FIELD(x, y))
8907       continue;
8908
8909     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8910          Feld[x][y] == EL_BD_AMOEBA ||
8911          Feld[x][y] == EL_AMOEBA_DEAD) &&
8912         AmoebaNr[x][y] != new_group_nr)
8913     {
8914       int old_group_nr = AmoebaNr[x][y];
8915
8916       if (old_group_nr == 0)
8917         return;
8918
8919       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8920       AmoebaCnt[old_group_nr] = 0;
8921       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8922       AmoebaCnt2[old_group_nr] = 0;
8923
8924       SCAN_PLAYFIELD(xx, yy)
8925       {
8926         if (AmoebaNr[xx][yy] == old_group_nr)
8927           AmoebaNr[xx][yy] = new_group_nr;
8928       }
8929     }
8930   }
8931 }
8932
8933 void AmoebeUmwandeln(int ax, int ay)
8934 {
8935   int i, x, y;
8936
8937   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8938   {
8939     int group_nr = AmoebaNr[ax][ay];
8940
8941 #ifdef DEBUG
8942     if (group_nr == 0)
8943     {
8944       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8945       printf("AmoebeUmwandeln(): This should never happen!\n");
8946       return;
8947     }
8948 #endif
8949
8950     SCAN_PLAYFIELD(x, y)
8951     {
8952       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8953       {
8954         AmoebaNr[x][y] = 0;
8955         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8956       }
8957     }
8958
8959     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8960                             SND_AMOEBA_TURNING_TO_GEM :
8961                             SND_AMOEBA_TURNING_TO_ROCK));
8962     Bang(ax, ay);
8963   }
8964   else
8965   {
8966     static int xy[4][2] =
8967     {
8968       { 0, -1 },
8969       { -1, 0 },
8970       { +1, 0 },
8971       { 0, +1 }
8972     };
8973
8974     for (i = 0; i < NUM_DIRECTIONS; i++)
8975     {
8976       x = ax + xy[i][0];
8977       y = ay + xy[i][1];
8978
8979       if (!IN_LEV_FIELD(x, y))
8980         continue;
8981
8982       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8983       {
8984         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8985                               SND_AMOEBA_TURNING_TO_GEM :
8986                               SND_AMOEBA_TURNING_TO_ROCK));
8987         Bang(x, y);
8988       }
8989     }
8990   }
8991 }
8992
8993 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8994 {
8995   int x, y;
8996   int group_nr = AmoebaNr[ax][ay];
8997   boolean done = FALSE;
8998
8999 #ifdef DEBUG
9000   if (group_nr == 0)
9001   {
9002     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9003     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9004     return;
9005   }
9006 #endif
9007
9008   SCAN_PLAYFIELD(x, y)
9009   {
9010     if (AmoebaNr[x][y] == group_nr &&
9011         (Feld[x][y] == EL_AMOEBA_DEAD ||
9012          Feld[x][y] == EL_BD_AMOEBA ||
9013          Feld[x][y] == EL_AMOEBA_GROWING))
9014     {
9015       AmoebaNr[x][y] = 0;
9016       Feld[x][y] = new_element;
9017       InitField(x, y, FALSE);
9018       DrawLevelField(x, y);
9019       done = TRUE;
9020     }
9021   }
9022
9023   if (done)
9024     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9025                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9026                             SND_BD_AMOEBA_TURNING_TO_GEM));
9027 }
9028
9029 void AmoebeWaechst(int x, int y)
9030 {
9031   static unsigned long sound_delay = 0;
9032   static unsigned long sound_delay_value = 0;
9033
9034   if (!MovDelay[x][y])          /* start new growing cycle */
9035   {
9036     MovDelay[x][y] = 7;
9037
9038     if (DelayReached(&sound_delay, sound_delay_value))
9039     {
9040       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9041       sound_delay_value = 30;
9042     }
9043   }
9044
9045   if (MovDelay[x][y])           /* wait some time before growing bigger */
9046   {
9047     MovDelay[x][y]--;
9048     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9049     {
9050       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9051                                            6 - MovDelay[x][y]);
9052
9053       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9054     }
9055
9056     if (!MovDelay[x][y])
9057     {
9058       Feld[x][y] = Store[x][y];
9059       Store[x][y] = 0;
9060       DrawLevelField(x, y);
9061     }
9062   }
9063 }
9064
9065 void AmoebaDisappearing(int x, int y)
9066 {
9067   static unsigned long sound_delay = 0;
9068   static unsigned long sound_delay_value = 0;
9069
9070   if (!MovDelay[x][y])          /* start new shrinking cycle */
9071   {
9072     MovDelay[x][y] = 7;
9073
9074     if (DelayReached(&sound_delay, sound_delay_value))
9075       sound_delay_value = 30;
9076   }
9077
9078   if (MovDelay[x][y])           /* wait some time before shrinking */
9079   {
9080     MovDelay[x][y]--;
9081     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9082     {
9083       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9084                                            6 - MovDelay[x][y]);
9085
9086       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9087     }
9088
9089     if (!MovDelay[x][y])
9090     {
9091       Feld[x][y] = EL_EMPTY;
9092       DrawLevelField(x, y);
9093
9094       /* don't let mole enter this field in this cycle;
9095          (give priority to objects falling to this field from above) */
9096       Stop[x][y] = TRUE;
9097     }
9098   }
9099 }
9100
9101 void AmoebeAbleger(int ax, int ay)
9102 {
9103   int i;
9104   int element = Feld[ax][ay];
9105   int graphic = el2img(element);
9106   int newax = ax, neway = ay;
9107   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9108   static int xy[4][2] =
9109   {
9110     { 0, -1 },
9111     { -1, 0 },
9112     { +1, 0 },
9113     { 0, +1 }
9114   };
9115
9116   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9117   {
9118     Feld[ax][ay] = EL_AMOEBA_DEAD;
9119     DrawLevelField(ax, ay);
9120     return;
9121   }
9122
9123   if (IS_ANIMATED(graphic))
9124     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9125
9126   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9127     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9128
9129   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9130   {
9131     MovDelay[ax][ay]--;
9132     if (MovDelay[ax][ay])
9133       return;
9134   }
9135
9136   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9137   {
9138     int start = RND(4);
9139     int x = ax + xy[start][0];
9140     int y = ay + xy[start][1];
9141
9142     if (!IN_LEV_FIELD(x, y))
9143       return;
9144
9145     if (IS_FREE(x, y) ||
9146         CAN_GROW_INTO(Feld[x][y]) ||
9147         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9148         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9149     {
9150       newax = x;
9151       neway = y;
9152     }
9153
9154     if (newax == ax && neway == ay)
9155       return;
9156   }
9157   else                          /* normal or "filled" (BD style) amoeba */
9158   {
9159     int start = RND(4);
9160     boolean waiting_for_player = FALSE;
9161
9162     for (i = 0; i < NUM_DIRECTIONS; i++)
9163     {
9164       int j = (start + i) % 4;
9165       int x = ax + xy[j][0];
9166       int y = ay + xy[j][1];
9167
9168       if (!IN_LEV_FIELD(x, y))
9169         continue;
9170
9171       if (IS_FREE(x, y) ||
9172           CAN_GROW_INTO(Feld[x][y]) ||
9173           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9174           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9175       {
9176         newax = x;
9177         neway = y;
9178         break;
9179       }
9180       else if (IS_PLAYER(x, y))
9181         waiting_for_player = TRUE;
9182     }
9183
9184     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9185     {
9186       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9187       {
9188         Feld[ax][ay] = EL_AMOEBA_DEAD;
9189         DrawLevelField(ax, ay);
9190         AmoebaCnt[AmoebaNr[ax][ay]]--;
9191
9192         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9193         {
9194           if (element == EL_AMOEBA_FULL)
9195             AmoebeUmwandeln(ax, ay);
9196           else if (element == EL_BD_AMOEBA)
9197             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9198         }
9199       }
9200       return;
9201     }
9202     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9203     {
9204       /* amoeba gets larger by growing in some direction */
9205
9206       int new_group_nr = AmoebaNr[ax][ay];
9207
9208 #ifdef DEBUG
9209   if (new_group_nr == 0)
9210   {
9211     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9212     printf("AmoebeAbleger(): This should never happen!\n");
9213     return;
9214   }
9215 #endif
9216
9217       AmoebaNr[newax][neway] = new_group_nr;
9218       AmoebaCnt[new_group_nr]++;
9219       AmoebaCnt2[new_group_nr]++;
9220
9221       /* if amoeba touches other amoeba(s) after growing, unify them */
9222       AmoebenVereinigen(newax, neway);
9223
9224       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9225       {
9226         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9227         return;
9228       }
9229     }
9230   }
9231
9232   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9233       (neway == lev_fieldy - 1 && newax != ax))
9234   {
9235     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9236     Store[newax][neway] = element;
9237   }
9238   else if (neway == ay || element == EL_EMC_DRIPPER)
9239   {
9240     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9241
9242     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9243   }
9244   else
9245   {
9246     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9247     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9248     Store[ax][ay] = EL_AMOEBA_DROP;
9249     ContinueMoving(ax, ay);
9250     return;
9251   }
9252
9253   DrawLevelField(newax, neway);
9254 }
9255
9256 void Life(int ax, int ay)
9257 {
9258   int x1, y1, x2, y2;
9259   int life_time = 40;
9260   int element = Feld[ax][ay];
9261   int graphic = el2img(element);
9262   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9263                          level.biomaze);
9264   boolean changed = FALSE;
9265
9266   if (IS_ANIMATED(graphic))
9267     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9268
9269   if (Stop[ax][ay])
9270     return;
9271
9272   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9273     MovDelay[ax][ay] = life_time;
9274
9275   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9276   {
9277     MovDelay[ax][ay]--;
9278     if (MovDelay[ax][ay])
9279       return;
9280   }
9281
9282   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9283   {
9284     int xx = ax+x1, yy = ay+y1;
9285     int nachbarn = 0;
9286
9287     if (!IN_LEV_FIELD(xx, yy))
9288       continue;
9289
9290     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9291     {
9292       int x = xx+x2, y = yy+y2;
9293
9294       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9295         continue;
9296
9297       if (((Feld[x][y] == element ||
9298             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9299            !Stop[x][y]) ||
9300           (IS_FREE(x, y) && Stop[x][y]))
9301         nachbarn++;
9302     }
9303
9304     if (xx == ax && yy == ay)           /* field in the middle */
9305     {
9306       if (nachbarn < life_parameter[0] ||
9307           nachbarn > life_parameter[1])
9308       {
9309         Feld[xx][yy] = EL_EMPTY;
9310         if (!Stop[xx][yy])
9311           DrawLevelField(xx, yy);
9312         Stop[xx][yy] = TRUE;
9313         changed = TRUE;
9314       }
9315     }
9316     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9317     {                                   /* free border field */
9318       if (nachbarn >= life_parameter[2] &&
9319           nachbarn <= life_parameter[3])
9320       {
9321         Feld[xx][yy] = element;
9322         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9323         if (!Stop[xx][yy])
9324           DrawLevelField(xx, yy);
9325         Stop[xx][yy] = TRUE;
9326         changed = TRUE;
9327       }
9328     }
9329   }
9330
9331   if (changed)
9332     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9333                    SND_GAME_OF_LIFE_GROWING);
9334 }
9335
9336 static void InitRobotWheel(int x, int y)
9337 {
9338   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9339 }
9340
9341 static void RunRobotWheel(int x, int y)
9342 {
9343   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9344 }
9345
9346 static void StopRobotWheel(int x, int y)
9347 {
9348   if (ZX == x && ZY == y)
9349   {
9350     ZX = ZY = -1;
9351
9352     game.robot_wheel_active = FALSE;
9353   }
9354 }
9355
9356 static void InitTimegateWheel(int x, int y)
9357 {
9358   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9359 }
9360
9361 static void RunTimegateWheel(int x, int y)
9362 {
9363   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9364 }
9365
9366 static void InitMagicBallDelay(int x, int y)
9367 {
9368 #if 1
9369   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9370 #else
9371   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9372 #endif
9373 }
9374
9375 static void ActivateMagicBall(int bx, int by)
9376 {
9377   int x, y;
9378
9379   if (level.ball_random)
9380   {
9381     int pos_border = RND(8);    /* select one of the eight border elements */
9382     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9383     int xx = pos_content % 3;
9384     int yy = pos_content / 3;
9385
9386     x = bx - 1 + xx;
9387     y = by - 1 + yy;
9388
9389     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9390       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9391   }
9392   else
9393   {
9394     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9395     {
9396       int xx = x - bx + 1;
9397       int yy = y - by + 1;
9398
9399       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9400         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9401     }
9402   }
9403
9404   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9405 }
9406
9407 void CheckExit(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_EXIT_OPENING;
9426
9427   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9428 }
9429
9430 void CheckExitEM(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_EM_EXIT_OPENING;
9449
9450   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9451 }
9452
9453 void CheckExitSteel(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_STEEL_EXIT_OPENING;
9472
9473   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9474 }
9475
9476 void CheckExitSteelEM(int x, int y)
9477 {
9478   if (local_player->gems_still_needed > 0 ||
9479       local_player->sokobanfields_still_needed > 0 ||
9480       local_player->lights_still_needed > 0)
9481   {
9482     int element = Feld[x][y];
9483     int graphic = el2img(element);
9484
9485     if (IS_ANIMATED(graphic))
9486       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9487
9488     return;
9489   }
9490
9491   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9492     return;
9493
9494   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9495
9496   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9497 }
9498
9499 void CheckExitSP(int x, int y)
9500 {
9501   if (local_player->gems_still_needed > 0)
9502   {
9503     int element = Feld[x][y];
9504     int graphic = el2img(element);
9505
9506     if (IS_ANIMATED(graphic))
9507       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9508
9509     return;
9510   }
9511
9512   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9513     return;
9514
9515   Feld[x][y] = EL_SP_EXIT_OPENING;
9516
9517   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9518 }
9519
9520 static void CloseAllOpenTimegates()
9521 {
9522   int x, y;
9523
9524   SCAN_PLAYFIELD(x, y)
9525   {
9526     int element = Feld[x][y];
9527
9528     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9529     {
9530       Feld[x][y] = EL_TIMEGATE_CLOSING;
9531
9532       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9533     }
9534   }
9535 }
9536
9537 void DrawTwinkleOnField(int x, int y)
9538 {
9539   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9540     return;
9541
9542   if (Feld[x][y] == EL_BD_DIAMOND)
9543     return;
9544
9545   if (MovDelay[x][y] == 0)      /* next animation frame */
9546     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9547
9548   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9549   {
9550     MovDelay[x][y]--;
9551
9552     DrawLevelElementAnimation(x, y, Feld[x][y]);
9553
9554     if (MovDelay[x][y] != 0)
9555     {
9556       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9557                                            10 - MovDelay[x][y]);
9558
9559       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9560     }
9561   }
9562 }
9563
9564 void MauerWaechst(int x, int y)
9565 {
9566   int delay = 6;
9567
9568   if (!MovDelay[x][y])          /* next animation frame */
9569     MovDelay[x][y] = 3 * delay;
9570
9571   if (MovDelay[x][y])           /* wait some time before next frame */
9572   {
9573     MovDelay[x][y]--;
9574
9575     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9576     {
9577       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9578       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9579
9580       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9581     }
9582
9583     if (!MovDelay[x][y])
9584     {
9585       if (MovDir[x][y] == MV_LEFT)
9586       {
9587         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9588           DrawLevelField(x - 1, y);
9589       }
9590       else if (MovDir[x][y] == MV_RIGHT)
9591       {
9592         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9593           DrawLevelField(x + 1, y);
9594       }
9595       else if (MovDir[x][y] == MV_UP)
9596       {
9597         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9598           DrawLevelField(x, y - 1);
9599       }
9600       else
9601       {
9602         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9603           DrawLevelField(x, y + 1);
9604       }
9605
9606       Feld[x][y] = Store[x][y];
9607       Store[x][y] = 0;
9608       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9609       DrawLevelField(x, y);
9610     }
9611   }
9612 }
9613
9614 void MauerAbleger(int ax, int ay)
9615 {
9616   int element = Feld[ax][ay];
9617   int graphic = el2img(element);
9618   boolean oben_frei = FALSE, unten_frei = FALSE;
9619   boolean links_frei = FALSE, rechts_frei = FALSE;
9620   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9621   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9622   boolean new_wall = FALSE;
9623
9624   if (IS_ANIMATED(graphic))
9625     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9626
9627   if (!MovDelay[ax][ay])        /* start building new wall */
9628     MovDelay[ax][ay] = 6;
9629
9630   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9631   {
9632     MovDelay[ax][ay]--;
9633     if (MovDelay[ax][ay])
9634       return;
9635   }
9636
9637   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9638     oben_frei = TRUE;
9639   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9640     unten_frei = TRUE;
9641   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9642     links_frei = TRUE;
9643   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9644     rechts_frei = TRUE;
9645
9646   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9647       element == EL_EXPANDABLE_WALL_ANY)
9648   {
9649     if (oben_frei)
9650     {
9651       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9652       Store[ax][ay-1] = element;
9653       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9654       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9655         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9656                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9657       new_wall = TRUE;
9658     }
9659     if (unten_frei)
9660     {
9661       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9662       Store[ax][ay+1] = element;
9663       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9664       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9665         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9666                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9667       new_wall = TRUE;
9668     }
9669   }
9670
9671   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9672       element == EL_EXPANDABLE_WALL_ANY ||
9673       element == EL_EXPANDABLE_WALL ||
9674       element == EL_BD_EXPANDABLE_WALL)
9675   {
9676     if (links_frei)
9677     {
9678       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9679       Store[ax-1][ay] = element;
9680       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9681       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9682         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9683                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9684       new_wall = TRUE;
9685     }
9686
9687     if (rechts_frei)
9688     {
9689       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9690       Store[ax+1][ay] = element;
9691       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9692       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9693         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9694                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9695       new_wall = TRUE;
9696     }
9697   }
9698
9699   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9700     DrawLevelField(ax, ay);
9701
9702   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9703     oben_massiv = TRUE;
9704   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9705     unten_massiv = TRUE;
9706   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9707     links_massiv = TRUE;
9708   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9709     rechts_massiv = TRUE;
9710
9711   if (((oben_massiv && unten_massiv) ||
9712        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9713        element == EL_EXPANDABLE_WALL) &&
9714       ((links_massiv && rechts_massiv) ||
9715        element == EL_EXPANDABLE_WALL_VERTICAL))
9716     Feld[ax][ay] = EL_WALL;
9717
9718   if (new_wall)
9719     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9720 }
9721
9722 void MauerAblegerStahl(int ax, int ay)
9723 {
9724   int element = Feld[ax][ay];
9725   int graphic = el2img(element);
9726   boolean oben_frei = FALSE, unten_frei = FALSE;
9727   boolean links_frei = FALSE, rechts_frei = FALSE;
9728   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9729   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9730   boolean new_wall = FALSE;
9731
9732   if (IS_ANIMATED(graphic))
9733     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9734
9735   if (!MovDelay[ax][ay])        /* start building new wall */
9736     MovDelay[ax][ay] = 6;
9737
9738   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9739   {
9740     MovDelay[ax][ay]--;
9741     if (MovDelay[ax][ay])
9742       return;
9743   }
9744
9745   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9746     oben_frei = TRUE;
9747   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9748     unten_frei = TRUE;
9749   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9750     links_frei = TRUE;
9751   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9752     rechts_frei = TRUE;
9753
9754   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9755       element == EL_EXPANDABLE_STEELWALL_ANY)
9756   {
9757     if (oben_frei)
9758     {
9759       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9760       Store[ax][ay-1] = element;
9761       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9762       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9763         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9764                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9765       new_wall = TRUE;
9766     }
9767     if (unten_frei)
9768     {
9769       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9770       Store[ax][ay+1] = element;
9771       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9772       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9773         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9774                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9775       new_wall = TRUE;
9776     }
9777   }
9778
9779   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9780       element == EL_EXPANDABLE_STEELWALL_ANY)
9781   {
9782     if (links_frei)
9783     {
9784       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9785       Store[ax-1][ay] = element;
9786       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9787       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9788         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9789                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9790       new_wall = TRUE;
9791     }
9792
9793     if (rechts_frei)
9794     {
9795       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9796       Store[ax+1][ay] = element;
9797       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9798       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9799         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9800                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9801       new_wall = TRUE;
9802     }
9803   }
9804
9805   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9806     oben_massiv = TRUE;
9807   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9808     unten_massiv = TRUE;
9809   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9810     links_massiv = TRUE;
9811   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9812     rechts_massiv = TRUE;
9813
9814   if (((oben_massiv && unten_massiv) ||
9815        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9816       ((links_massiv && rechts_massiv) ||
9817        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9818     Feld[ax][ay] = EL_WALL;
9819
9820   if (new_wall)
9821     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9822 }
9823
9824 void CheckForDragon(int x, int y)
9825 {
9826   int i, j;
9827   boolean dragon_found = FALSE;
9828   static int xy[4][2] =
9829   {
9830     { 0, -1 },
9831     { -1, 0 },
9832     { +1, 0 },
9833     { 0, +1 }
9834   };
9835
9836   for (i = 0; i < NUM_DIRECTIONS; i++)
9837   {
9838     for (j = 0; j < 4; j++)
9839     {
9840       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9841
9842       if (IN_LEV_FIELD(xx, yy) &&
9843           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9844       {
9845         if (Feld[xx][yy] == EL_DRAGON)
9846           dragon_found = TRUE;
9847       }
9848       else
9849         break;
9850     }
9851   }
9852
9853   if (!dragon_found)
9854   {
9855     for (i = 0; i < NUM_DIRECTIONS; i++)
9856     {
9857       for (j = 0; j < 3; j++)
9858       {
9859         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9860   
9861         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9862         {
9863           Feld[xx][yy] = EL_EMPTY;
9864           DrawLevelField(xx, yy);
9865         }
9866         else
9867           break;
9868       }
9869     }
9870   }
9871 }
9872
9873 static void InitBuggyBase(int x, int y)
9874 {
9875   int element = Feld[x][y];
9876   int activating_delay = FRAMES_PER_SECOND / 4;
9877
9878   ChangeDelay[x][y] =
9879     (element == EL_SP_BUGGY_BASE ?
9880      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9881      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9882      activating_delay :
9883      element == EL_SP_BUGGY_BASE_ACTIVE ?
9884      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9885 }
9886
9887 static void WarnBuggyBase(int x, int y)
9888 {
9889   int i;
9890   static int xy[4][2] =
9891   {
9892     { 0, -1 },
9893     { -1, 0 },
9894     { +1, 0 },
9895     { 0, +1 }
9896   };
9897
9898   for (i = 0; i < NUM_DIRECTIONS; i++)
9899   {
9900     int xx = x + xy[i][0];
9901     int yy = y + xy[i][1];
9902
9903     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9904     {
9905       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9906
9907       break;
9908     }
9909   }
9910 }
9911
9912 static void InitTrap(int x, int y)
9913 {
9914   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9915 }
9916
9917 static void ActivateTrap(int x, int y)
9918 {
9919   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9920 }
9921
9922 static void ChangeActiveTrap(int x, int y)
9923 {
9924   int graphic = IMG_TRAP_ACTIVE;
9925
9926   /* if new animation frame was drawn, correct crumbled sand border */
9927   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9928     DrawLevelFieldCrumbledSand(x, y);
9929 }
9930
9931 static int getSpecialActionElement(int element, int number, int base_element)
9932 {
9933   return (element != EL_EMPTY ? element :
9934           number != -1 ? base_element + number - 1 :
9935           EL_EMPTY);
9936 }
9937
9938 static int getModifiedActionNumber(int value_old, int operator, int operand,
9939                                    int value_min, int value_max)
9940 {
9941   int value_new = (operator == CA_MODE_SET      ? operand :
9942                    operator == CA_MODE_ADD      ? value_old + operand :
9943                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9944                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9945                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9946                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9947                    value_old);
9948
9949   return (value_new < value_min ? value_min :
9950           value_new > value_max ? value_max :
9951           value_new);
9952 }
9953
9954 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9955 {
9956   struct ElementInfo *ei = &element_info[element];
9957   struct ElementChangeInfo *change = &ei->change_page[page];
9958   int target_element = change->target_element;
9959   int action_type = change->action_type;
9960   int action_mode = change->action_mode;
9961   int action_arg = change->action_arg;
9962   int i;
9963
9964   if (!change->has_action)
9965     return;
9966
9967   /* ---------- determine action paramater values -------------------------- */
9968
9969   int level_time_value =
9970     (level.time > 0 ? TimeLeft :
9971      TimePlayed);
9972
9973   int action_arg_element =
9974     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9975      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9976      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9977      EL_EMPTY);
9978
9979   int action_arg_direction =
9980     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9981      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9982      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9983      change->actual_trigger_side :
9984      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9985      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9986      MV_NONE);
9987
9988   int action_arg_number_min =
9989     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9990      CA_ARG_MIN);
9991
9992   int action_arg_number_max =
9993     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9994      action_type == CA_SET_LEVEL_GEMS ? 999 :
9995      action_type == CA_SET_LEVEL_TIME ? 9999 :
9996      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9997      action_type == CA_SET_CE_VALUE ? 9999 :
9998      action_type == CA_SET_CE_SCORE ? 9999 :
9999      CA_ARG_MAX);
10000
10001   int action_arg_number_reset =
10002     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10003      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10004      action_type == CA_SET_LEVEL_TIME ? level.time :
10005      action_type == CA_SET_LEVEL_SCORE ? 0 :
10006 #if USE_NEW_CUSTOM_VALUE
10007      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10008 #else
10009      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10010 #endif
10011      action_type == CA_SET_CE_SCORE ? 0 :
10012      0);
10013
10014   int action_arg_number =
10015     (action_arg <= CA_ARG_MAX ? action_arg :
10016      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10017      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10018      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10019      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10020      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10021      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10022 #if USE_NEW_CUSTOM_VALUE
10023      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10024 #else
10025      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10026 #endif
10027      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10028      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10029      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10030      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10031      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10032      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10033      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10034      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10035      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10036      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10037      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10038      -1);
10039
10040   int action_arg_number_old =
10041     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10042      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10043      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10044      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10045      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10046      0);
10047
10048   int action_arg_number_new =
10049     getModifiedActionNumber(action_arg_number_old,
10050                             action_mode, action_arg_number,
10051                             action_arg_number_min, action_arg_number_max);
10052
10053 #if 1
10054   int trigger_player_bits = change->actual_trigger_player_bits;
10055 #else
10056   int trigger_player_bits =
10057     (change->actual_trigger_player >= EL_PLAYER_1 &&
10058      change->actual_trigger_player <= EL_PLAYER_4 ?
10059      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10060      PLAYER_BITS_ANY);
10061 #endif
10062
10063   int action_arg_player_bits =
10064     (action_arg >= CA_ARG_PLAYER_1 &&
10065      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10066      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10067      PLAYER_BITS_ANY);
10068
10069   /* ---------- execute action  -------------------------------------------- */
10070
10071   switch (action_type)
10072   {
10073     case CA_NO_ACTION:
10074     {
10075       return;
10076     }
10077
10078     /* ---------- level actions  ------------------------------------------- */
10079
10080     case CA_RESTART_LEVEL:
10081     {
10082       game.restart_level = TRUE;
10083
10084       break;
10085     }
10086
10087     case CA_SHOW_ENVELOPE:
10088     {
10089       int element = getSpecialActionElement(action_arg_element,
10090                                             action_arg_number, EL_ENVELOPE_1);
10091
10092       if (IS_ENVELOPE(element))
10093         local_player->show_envelope = element;
10094
10095       break;
10096     }
10097
10098     case CA_SET_LEVEL_TIME:
10099     {
10100       if (level.time > 0)       /* only modify limited time value */
10101       {
10102         TimeLeft = action_arg_number_new;
10103
10104 #if 1
10105         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10106
10107         DisplayGameControlValues();
10108 #else
10109         DrawGameValue_Time(TimeLeft);
10110 #endif
10111
10112         if (!TimeLeft && setup.time_limit)
10113           for (i = 0; i < MAX_PLAYERS; i++)
10114             KillPlayer(&stored_player[i]);
10115       }
10116
10117       break;
10118     }
10119
10120     case CA_SET_LEVEL_SCORE:
10121     {
10122       local_player->score = action_arg_number_new;
10123
10124 #if 1
10125       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10126
10127       DisplayGameControlValues();
10128 #else
10129       DrawGameValue_Score(local_player->score);
10130 #endif
10131
10132       break;
10133     }
10134
10135     case CA_SET_LEVEL_GEMS:
10136     {
10137       local_player->gems_still_needed = action_arg_number_new;
10138
10139 #if 1
10140       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10141
10142       DisplayGameControlValues();
10143 #else
10144       DrawGameValue_Emeralds(local_player->gems_still_needed);
10145 #endif
10146
10147       break;
10148     }
10149
10150 #if !USE_PLAYER_GRAVITY
10151     case CA_SET_LEVEL_GRAVITY:
10152     {
10153       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10154                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10155                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10156                       game.gravity);
10157       break;
10158     }
10159 #endif
10160
10161     case CA_SET_LEVEL_WIND:
10162     {
10163       game.wind_direction = action_arg_direction;
10164
10165       break;
10166     }
10167
10168     /* ---------- player actions  ------------------------------------------ */
10169
10170     case CA_MOVE_PLAYER:
10171     {
10172       /* automatically move to the next field in specified direction */
10173       for (i = 0; i < MAX_PLAYERS; i++)
10174         if (trigger_player_bits & (1 << i))
10175           stored_player[i].programmed_action = action_arg_direction;
10176
10177       break;
10178     }
10179
10180     case CA_EXIT_PLAYER:
10181     {
10182       for (i = 0; i < MAX_PLAYERS; i++)
10183         if (action_arg_player_bits & (1 << i))
10184           PlayerWins(&stored_player[i]);
10185
10186       break;
10187     }
10188
10189     case CA_KILL_PLAYER:
10190     {
10191       for (i = 0; i < MAX_PLAYERS; i++)
10192         if (action_arg_player_bits & (1 << i))
10193           KillPlayer(&stored_player[i]);
10194
10195       break;
10196     }
10197
10198     case CA_SET_PLAYER_KEYS:
10199     {
10200       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10201       int element = getSpecialActionElement(action_arg_element,
10202                                             action_arg_number, EL_KEY_1);
10203
10204       if (IS_KEY(element))
10205       {
10206         for (i = 0; i < MAX_PLAYERS; i++)
10207         {
10208           if (trigger_player_bits & (1 << i))
10209           {
10210             stored_player[i].key[KEY_NR(element)] = key_state;
10211
10212             DrawGameDoorValues();
10213           }
10214         }
10215       }
10216
10217       break;
10218     }
10219
10220     case CA_SET_PLAYER_SPEED:
10221     {
10222       for (i = 0; i < MAX_PLAYERS; i++)
10223       {
10224         if (trigger_player_bits & (1 << i))
10225         {
10226           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10227
10228           if (action_arg == CA_ARG_SPEED_FASTER &&
10229               stored_player[i].cannot_move)
10230           {
10231             action_arg_number = STEPSIZE_VERY_SLOW;
10232           }
10233           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10234                    action_arg == CA_ARG_SPEED_FASTER)
10235           {
10236             action_arg_number = 2;
10237             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10238                            CA_MODE_MULTIPLY);
10239           }
10240           else if (action_arg == CA_ARG_NUMBER_RESET)
10241           {
10242             action_arg_number = level.initial_player_stepsize[i];
10243           }
10244
10245           move_stepsize =
10246             getModifiedActionNumber(move_stepsize,
10247                                     action_mode,
10248                                     action_arg_number,
10249                                     action_arg_number_min,
10250                                     action_arg_number_max);
10251
10252           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10253         }
10254       }
10255
10256       break;
10257     }
10258
10259     case CA_SET_PLAYER_SHIELD:
10260     {
10261       for (i = 0; i < MAX_PLAYERS; i++)
10262       {
10263         if (trigger_player_bits & (1 << i))
10264         {
10265           if (action_arg == CA_ARG_SHIELD_OFF)
10266           {
10267             stored_player[i].shield_normal_time_left = 0;
10268             stored_player[i].shield_deadly_time_left = 0;
10269           }
10270           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10271           {
10272             stored_player[i].shield_normal_time_left = 999999;
10273           }
10274           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10275           {
10276             stored_player[i].shield_normal_time_left = 999999;
10277             stored_player[i].shield_deadly_time_left = 999999;
10278           }
10279         }
10280       }
10281
10282       break;
10283     }
10284
10285 #if USE_PLAYER_GRAVITY
10286     case CA_SET_PLAYER_GRAVITY:
10287     {
10288       for (i = 0; i < MAX_PLAYERS; i++)
10289       {
10290         if (trigger_player_bits & (1 << i))
10291         {
10292           stored_player[i].gravity =
10293             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10294              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10295              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10296              stored_player[i].gravity);
10297         }
10298       }
10299
10300       break;
10301     }
10302 #endif
10303
10304     case CA_SET_PLAYER_ARTWORK:
10305     {
10306       for (i = 0; i < MAX_PLAYERS; i++)
10307       {
10308         if (trigger_player_bits & (1 << i))
10309         {
10310           int artwork_element = action_arg_element;
10311
10312           if (action_arg == CA_ARG_ELEMENT_RESET)
10313             artwork_element =
10314               (level.use_artwork_element[i] ? level.artwork_element[i] :
10315                stored_player[i].element_nr);
10316
10317 #if USE_GFX_RESET_PLAYER_ARTWORK
10318           if (stored_player[i].artwork_element != artwork_element)
10319             stored_player[i].Frame = 0;
10320 #endif
10321
10322           stored_player[i].artwork_element = artwork_element;
10323
10324           SetPlayerWaiting(&stored_player[i], FALSE);
10325
10326           /* set number of special actions for bored and sleeping animation */
10327           stored_player[i].num_special_action_bored =
10328             get_num_special_action(artwork_element,
10329                                    ACTION_BORING_1, ACTION_BORING_LAST);
10330           stored_player[i].num_special_action_sleeping =
10331             get_num_special_action(artwork_element,
10332                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10333         }
10334       }
10335
10336       break;
10337     }
10338
10339     /* ---------- CE actions  ---------------------------------------------- */
10340
10341     case CA_SET_CE_VALUE:
10342     {
10343 #if USE_NEW_CUSTOM_VALUE
10344       int last_ce_value = CustomValue[x][y];
10345
10346       CustomValue[x][y] = action_arg_number_new;
10347
10348       if (CustomValue[x][y] != last_ce_value)
10349       {
10350         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10351         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10352
10353         if (CustomValue[x][y] == 0)
10354         {
10355           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10356           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10357         }
10358       }
10359 #endif
10360
10361       break;
10362     }
10363
10364     case CA_SET_CE_SCORE:
10365     {
10366 #if USE_NEW_CUSTOM_VALUE
10367       int last_ce_score = ei->collect_score;
10368
10369       ei->collect_score = action_arg_number_new;
10370
10371       if (ei->collect_score != last_ce_score)
10372       {
10373         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10374         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10375
10376         if (ei->collect_score == 0)
10377         {
10378           int xx, yy;
10379
10380           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10381           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10382
10383           /*
10384             This is a very special case that seems to be a mixture between
10385             CheckElementChange() and CheckTriggeredElementChange(): while
10386             the first one only affects single elements that are triggered
10387             directly, the second one affects multiple elements in the playfield
10388             that are triggered indirectly by another element. This is a third
10389             case: Changing the CE score always affects multiple identical CEs,
10390             so every affected CE must be checked, not only the single CE for
10391             which the CE score was changed in the first place (as every instance
10392             of that CE shares the same CE score, and therefore also can change)!
10393           */
10394           SCAN_PLAYFIELD(xx, yy)
10395           {
10396             if (Feld[xx][yy] == element)
10397               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10398                                  CE_SCORE_GETS_ZERO);
10399           }
10400         }
10401       }
10402 #endif
10403
10404       break;
10405     }
10406
10407     /* ---------- engine actions  ------------------------------------------ */
10408
10409     case CA_SET_ENGINE_SCAN_MODE:
10410     {
10411       InitPlayfieldScanMode(action_arg);
10412
10413       break;
10414     }
10415
10416     default:
10417       break;
10418   }
10419 }
10420
10421 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10422 {
10423   int old_element = Feld[x][y];
10424   int new_element = GetElementFromGroupElement(element);
10425   int previous_move_direction = MovDir[x][y];
10426 #if USE_NEW_CUSTOM_VALUE
10427   int last_ce_value = CustomValue[x][y];
10428 #endif
10429   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10430   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10431   boolean add_player_onto_element = (new_element_is_player &&
10432 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10433                                      /* this breaks SnakeBite when a snake is
10434                                         halfway through a door that closes */
10435                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10436                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10437 #endif
10438                                      IS_WALKABLE(old_element));
10439
10440 #if 0
10441   /* check if element under the player changes from accessible to unaccessible
10442      (needed for special case of dropping element which then changes) */
10443   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10444       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10445   {
10446     Bang(x, y);
10447
10448     return;
10449   }
10450 #endif
10451
10452   if (!add_player_onto_element)
10453   {
10454     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10455       RemoveMovingField(x, y);
10456     else
10457       RemoveField(x, y);
10458
10459     Feld[x][y] = new_element;
10460
10461 #if !USE_GFX_RESET_GFX_ANIMATION
10462     ResetGfxAnimation(x, y);
10463     ResetRandomAnimationValue(x, y);
10464 #endif
10465
10466     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10467       MovDir[x][y] = previous_move_direction;
10468
10469 #if USE_NEW_CUSTOM_VALUE
10470     if (element_info[new_element].use_last_ce_value)
10471       CustomValue[x][y] = last_ce_value;
10472 #endif
10473
10474     InitField_WithBug1(x, y, FALSE);
10475
10476     new_element = Feld[x][y];   /* element may have changed */
10477
10478 #if USE_GFX_RESET_GFX_ANIMATION
10479     ResetGfxAnimation(x, y);
10480     ResetRandomAnimationValue(x, y);
10481 #endif
10482
10483     DrawLevelField(x, y);
10484
10485     if (GFX_CRUMBLED(new_element))
10486       DrawLevelFieldCrumbledSandNeighbours(x, y);
10487   }
10488
10489 #if 1
10490   /* check if element under the player changes from accessible to unaccessible
10491      (needed for special case of dropping element which then changes) */
10492   /* (must be checked after creating new element for walkable group elements) */
10493 #if USE_FIX_KILLED_BY_NON_WALKABLE
10494   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10495       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10496   {
10497     Bang(x, y);
10498
10499     return;
10500   }
10501 #else
10502   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10503       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10504   {
10505     Bang(x, y);
10506
10507     return;
10508   }
10509 #endif
10510 #endif
10511
10512   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10513   if (new_element_is_player)
10514     RelocatePlayer(x, y, new_element);
10515
10516   if (is_change)
10517     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10518
10519   TestIfBadThingTouchesPlayer(x, y);
10520   TestIfPlayerTouchesCustomElement(x, y);
10521   TestIfElementTouchesCustomElement(x, y);
10522 }
10523
10524 static void CreateField(int x, int y, int element)
10525 {
10526   CreateFieldExt(x, y, element, FALSE);
10527 }
10528
10529 static void CreateElementFromChange(int x, int y, int element)
10530 {
10531   element = GET_VALID_RUNTIME_ELEMENT(element);
10532
10533 #if USE_STOP_CHANGED_ELEMENTS
10534   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10535   {
10536     int old_element = Feld[x][y];
10537
10538     /* prevent changed element from moving in same engine frame
10539        unless both old and new element can either fall or move */
10540     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10541         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10542       Stop[x][y] = TRUE;
10543   }
10544 #endif
10545
10546   CreateFieldExt(x, y, element, TRUE);
10547 }
10548
10549 static boolean ChangeElement(int x, int y, int element, int page)
10550 {
10551   struct ElementInfo *ei = &element_info[element];
10552   struct ElementChangeInfo *change = &ei->change_page[page];
10553   int ce_value = CustomValue[x][y];
10554   int ce_score = ei->collect_score;
10555   int target_element;
10556   int old_element = Feld[x][y];
10557
10558   /* always use default change event to prevent running into a loop */
10559   if (ChangeEvent[x][y] == -1)
10560     ChangeEvent[x][y] = CE_DELAY;
10561
10562   if (ChangeEvent[x][y] == CE_DELAY)
10563   {
10564     /* reset actual trigger element, trigger player and action element */
10565     change->actual_trigger_element = EL_EMPTY;
10566     change->actual_trigger_player = EL_PLAYER_1;
10567     change->actual_trigger_player_bits = CH_PLAYER_1;
10568     change->actual_trigger_side = CH_SIDE_NONE;
10569     change->actual_trigger_ce_value = 0;
10570     change->actual_trigger_ce_score = 0;
10571   }
10572
10573   /* do not change elements more than a specified maximum number of changes */
10574   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10575     return FALSE;
10576
10577   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10578
10579   if (change->explode)
10580   {
10581     Bang(x, y);
10582
10583     return TRUE;
10584   }
10585
10586   if (change->use_target_content)
10587   {
10588     boolean complete_replace = TRUE;
10589     boolean can_replace[3][3];
10590     int xx, yy;
10591
10592     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10593     {
10594       boolean is_empty;
10595       boolean is_walkable;
10596       boolean is_diggable;
10597       boolean is_collectible;
10598       boolean is_removable;
10599       boolean is_destructible;
10600       int ex = x + xx - 1;
10601       int ey = y + yy - 1;
10602       int content_element = change->target_content.e[xx][yy];
10603       int e;
10604
10605       can_replace[xx][yy] = TRUE;
10606
10607       if (ex == x && ey == y)   /* do not check changing element itself */
10608         continue;
10609
10610       if (content_element == EL_EMPTY_SPACE)
10611       {
10612         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10613
10614         continue;
10615       }
10616
10617       if (!IN_LEV_FIELD(ex, ey))
10618       {
10619         can_replace[xx][yy] = FALSE;
10620         complete_replace = FALSE;
10621
10622         continue;
10623       }
10624
10625       e = Feld[ex][ey];
10626
10627       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10628         e = MovingOrBlocked2Element(ex, ey);
10629
10630       is_empty = (IS_FREE(ex, ey) ||
10631                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10632
10633       is_walkable     = (is_empty || IS_WALKABLE(e));
10634       is_diggable     = (is_empty || IS_DIGGABLE(e));
10635       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10636       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10637       is_removable    = (is_diggable || is_collectible);
10638
10639       can_replace[xx][yy] =
10640         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10641           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10642           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10643           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10644           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10645           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10646          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10647
10648       if (!can_replace[xx][yy])
10649         complete_replace = FALSE;
10650     }
10651
10652     if (!change->only_if_complete || complete_replace)
10653     {
10654       boolean something_has_changed = FALSE;
10655
10656       if (change->only_if_complete && change->use_random_replace &&
10657           RND(100) < change->random_percentage)
10658         return FALSE;
10659
10660       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10661       {
10662         int ex = x + xx - 1;
10663         int ey = y + yy - 1;
10664         int content_element;
10665
10666         if (can_replace[xx][yy] && (!change->use_random_replace ||
10667                                     RND(100) < change->random_percentage))
10668         {
10669           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10670             RemoveMovingField(ex, ey);
10671
10672           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10673
10674           content_element = change->target_content.e[xx][yy];
10675           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10676                                               ce_value, ce_score);
10677
10678           CreateElementFromChange(ex, ey, target_element);
10679
10680           something_has_changed = TRUE;
10681
10682           /* for symmetry reasons, freeze newly created border elements */
10683           if (ex != x || ey != y)
10684             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10685         }
10686       }
10687
10688       if (something_has_changed)
10689       {
10690         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10691         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10692       }
10693     }
10694   }
10695   else
10696   {
10697     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10698                                         ce_value, ce_score);
10699
10700     if (element == EL_DIAGONAL_GROWING ||
10701         element == EL_DIAGONAL_SHRINKING)
10702     {
10703       target_element = Store[x][y];
10704
10705       Store[x][y] = EL_EMPTY;
10706     }
10707
10708     CreateElementFromChange(x, y, target_element);
10709
10710     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10711     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10712   }
10713
10714   /* this uses direct change before indirect change */
10715   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10716
10717   return TRUE;
10718 }
10719
10720 #if USE_NEW_DELAYED_ACTION
10721
10722 static void HandleElementChange(int x, int y, int page)
10723 {
10724   int element = MovingOrBlocked2Element(x, y);
10725   struct ElementInfo *ei = &element_info[element];
10726   struct ElementChangeInfo *change = &ei->change_page[page];
10727
10728 #ifdef DEBUG
10729   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10730       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10731   {
10732     printf("\n\n");
10733     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10734            x, y, element, element_info[element].token_name);
10735     printf("HandleElementChange(): This should never happen!\n");
10736     printf("\n\n");
10737   }
10738 #endif
10739
10740   /* this can happen with classic bombs on walkable, changing elements */
10741   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10742   {
10743 #if 0
10744     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10745       ChangeDelay[x][y] = 0;
10746 #endif
10747
10748     return;
10749   }
10750
10751   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10752   {
10753     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10754
10755     if (change->can_change)
10756     {
10757 #if 1
10758       /* !!! not clear why graphic animation should be reset at all here !!! */
10759       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10760 #if USE_GFX_RESET_WHEN_NOT_MOVING
10761       /* when a custom element is about to change (for example by change delay),
10762          do not reset graphic animation when the custom element is moving */
10763       if (!IS_MOVING(x, y))
10764 #endif
10765       {
10766         ResetGfxAnimation(x, y);
10767         ResetRandomAnimationValue(x, y);
10768       }
10769 #endif
10770
10771       if (change->pre_change_function)
10772         change->pre_change_function(x, y);
10773     }
10774   }
10775
10776   ChangeDelay[x][y]--;
10777
10778   if (ChangeDelay[x][y] != 0)           /* continue element change */
10779   {
10780     if (change->can_change)
10781     {
10782       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10783
10784       if (IS_ANIMATED(graphic))
10785         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10786
10787       if (change->change_function)
10788         change->change_function(x, y);
10789     }
10790   }
10791   else                                  /* finish element change */
10792   {
10793     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10794     {
10795       page = ChangePage[x][y];
10796       ChangePage[x][y] = -1;
10797
10798       change = &ei->change_page[page];
10799     }
10800
10801     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10802     {
10803       ChangeDelay[x][y] = 1;            /* try change after next move step */
10804       ChangePage[x][y] = page;          /* remember page to use for change */
10805
10806       return;
10807     }
10808
10809     if (change->can_change)
10810     {
10811       if (ChangeElement(x, y, element, page))
10812       {
10813         if (change->post_change_function)
10814           change->post_change_function(x, y);
10815       }
10816     }
10817
10818     if (change->has_action)
10819       ExecuteCustomElementAction(x, y, element, page);
10820   }
10821 }
10822
10823 #else
10824
10825 static void HandleElementChange(int x, int y, int page)
10826 {
10827   int element = MovingOrBlocked2Element(x, y);
10828   struct ElementInfo *ei = &element_info[element];
10829   struct ElementChangeInfo *change = &ei->change_page[page];
10830
10831 #ifdef DEBUG
10832   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10833   {
10834     printf("\n\n");
10835     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10836            x, y, element, element_info[element].token_name);
10837     printf("HandleElementChange(): This should never happen!\n");
10838     printf("\n\n");
10839   }
10840 #endif
10841
10842   /* this can happen with classic bombs on walkable, changing elements */
10843   if (!CAN_CHANGE(element))
10844   {
10845 #if 0
10846     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10847       ChangeDelay[x][y] = 0;
10848 #endif
10849
10850     return;
10851   }
10852
10853   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10854   {
10855     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10856
10857     ResetGfxAnimation(x, y);
10858     ResetRandomAnimationValue(x, y);
10859
10860     if (change->pre_change_function)
10861       change->pre_change_function(x, y);
10862   }
10863
10864   ChangeDelay[x][y]--;
10865
10866   if (ChangeDelay[x][y] != 0)           /* continue element change */
10867   {
10868     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10869
10870     if (IS_ANIMATED(graphic))
10871       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10872
10873     if (change->change_function)
10874       change->change_function(x, y);
10875   }
10876   else                                  /* finish element change */
10877   {
10878     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10879     {
10880       page = ChangePage[x][y];
10881       ChangePage[x][y] = -1;
10882
10883       change = &ei->change_page[page];
10884     }
10885
10886     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10887     {
10888       ChangeDelay[x][y] = 1;            /* try change after next move step */
10889       ChangePage[x][y] = page;          /* remember page to use for change */
10890
10891       return;
10892     }
10893
10894     if (ChangeElement(x, y, element, page))
10895     {
10896       if (change->post_change_function)
10897         change->post_change_function(x, y);
10898     }
10899   }
10900 }
10901
10902 #endif
10903
10904 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10905                                               int trigger_element,
10906                                               int trigger_event,
10907                                               int trigger_player,
10908                                               int trigger_side,
10909                                               int trigger_page)
10910 {
10911   boolean change_done_any = FALSE;
10912   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10913   int i;
10914
10915   if (!(trigger_events[trigger_element][trigger_event]))
10916     return FALSE;
10917
10918 #if 0
10919   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10920          trigger_event, recursion_loop_depth, recursion_loop_detected,
10921          recursion_loop_element, EL_NAME(recursion_loop_element));
10922 #endif
10923
10924   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10925
10926   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10927   {
10928     int element = EL_CUSTOM_START + i;
10929     boolean change_done = FALSE;
10930     int p;
10931
10932     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10933         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10934       continue;
10935
10936     for (p = 0; p < element_info[element].num_change_pages; p++)
10937     {
10938       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10939
10940       if (change->can_change_or_has_action &&
10941           change->has_event[trigger_event] &&
10942           change->trigger_side & trigger_side &&
10943           change->trigger_player & trigger_player &&
10944           change->trigger_page & trigger_page_bits &&
10945           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10946       {
10947         change->actual_trigger_element = trigger_element;
10948         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10949         change->actual_trigger_player_bits = trigger_player;
10950         change->actual_trigger_side = trigger_side;
10951         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10952         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10953
10954         if ((change->can_change && !change_done) || change->has_action)
10955         {
10956           int x, y;
10957
10958           SCAN_PLAYFIELD(x, y)
10959           {
10960             if (Feld[x][y] == element)
10961             {
10962               if (change->can_change && !change_done)
10963               {
10964                 ChangeDelay[x][y] = 1;
10965                 ChangeEvent[x][y] = trigger_event;
10966
10967                 HandleElementChange(x, y, p);
10968               }
10969 #if USE_NEW_DELAYED_ACTION
10970               else if (change->has_action)
10971               {
10972                 ExecuteCustomElementAction(x, y, element, p);
10973                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10974               }
10975 #else
10976               if (change->has_action)
10977               {
10978                 ExecuteCustomElementAction(x, y, element, p);
10979                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10980               }
10981 #endif
10982             }
10983           }
10984
10985           if (change->can_change)
10986           {
10987             change_done = TRUE;
10988             change_done_any = TRUE;
10989           }
10990         }
10991       }
10992     }
10993   }
10994
10995   RECURSION_LOOP_DETECTION_END();
10996
10997   return change_done_any;
10998 }
10999
11000 static boolean CheckElementChangeExt(int x, int y,
11001                                      int element,
11002                                      int trigger_element,
11003                                      int trigger_event,
11004                                      int trigger_player,
11005                                      int trigger_side)
11006 {
11007   boolean change_done = FALSE;
11008   int p;
11009
11010   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11011       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11012     return FALSE;
11013
11014   if (Feld[x][y] == EL_BLOCKED)
11015   {
11016     Blocked2Moving(x, y, &x, &y);
11017     element = Feld[x][y];
11018   }
11019
11020 #if 0
11021   /* check if element has already changed */
11022   if (Feld[x][y] != element)
11023     return FALSE;
11024 #else
11025   /* check if element has already changed or is about to change after moving */
11026   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11027        Feld[x][y] != element) ||
11028
11029       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11030        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11031         ChangePage[x][y] != -1)))
11032     return FALSE;
11033 #endif
11034
11035 #if 0
11036   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11037          trigger_event, recursion_loop_depth, recursion_loop_detected,
11038          recursion_loop_element, EL_NAME(recursion_loop_element));
11039 #endif
11040
11041   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11042
11043   for (p = 0; p < element_info[element].num_change_pages; p++)
11044   {
11045     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11046
11047     /* check trigger element for all events where the element that is checked
11048        for changing interacts with a directly adjacent element -- this is
11049        different to element changes that affect other elements to change on the
11050        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11051     boolean check_trigger_element =
11052       (trigger_event == CE_TOUCHING_X ||
11053        trigger_event == CE_HITTING_X ||
11054        trigger_event == CE_HIT_BY_X ||
11055 #if 1
11056        /* this one was forgotten until 3.2.3 */
11057        trigger_event == CE_DIGGING_X);
11058 #endif
11059
11060     if (change->can_change_or_has_action &&
11061         change->has_event[trigger_event] &&
11062         change->trigger_side & trigger_side &&
11063         change->trigger_player & trigger_player &&
11064         (!check_trigger_element ||
11065          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11066     {
11067       change->actual_trigger_element = trigger_element;
11068       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11069       change->actual_trigger_player_bits = trigger_player;
11070       change->actual_trigger_side = trigger_side;
11071       change->actual_trigger_ce_value = CustomValue[x][y];
11072       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11073
11074       /* special case: trigger element not at (x,y) position for some events */
11075       if (check_trigger_element)
11076       {
11077         static struct
11078         {
11079           int dx, dy;
11080         } move_xy[] =
11081           {
11082             {  0,  0 },
11083             { -1,  0 },
11084             { +1,  0 },
11085             {  0,  0 },
11086             {  0, -1 },
11087             {  0,  0 }, { 0, 0 }, { 0, 0 },
11088             {  0, +1 }
11089           };
11090
11091         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11092         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11093
11094         change->actual_trigger_ce_value = CustomValue[xx][yy];
11095         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11096       }
11097
11098       if (change->can_change && !change_done)
11099       {
11100         ChangeDelay[x][y] = 1;
11101         ChangeEvent[x][y] = trigger_event;
11102
11103         HandleElementChange(x, y, p);
11104
11105         change_done = TRUE;
11106       }
11107 #if USE_NEW_DELAYED_ACTION
11108       else if (change->has_action)
11109       {
11110         ExecuteCustomElementAction(x, y, element, p);
11111         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11112       }
11113 #else
11114       if (change->has_action)
11115       {
11116         ExecuteCustomElementAction(x, y, element, p);
11117         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11118       }
11119 #endif
11120     }
11121   }
11122
11123   RECURSION_LOOP_DETECTION_END();
11124
11125   return change_done;
11126 }
11127
11128 static void PlayPlayerSound(struct PlayerInfo *player)
11129 {
11130   int jx = player->jx, jy = player->jy;
11131   int sound_element = player->artwork_element;
11132   int last_action = player->last_action_waiting;
11133   int action = player->action_waiting;
11134
11135   if (player->is_waiting)
11136   {
11137     if (action != last_action)
11138       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11139     else
11140       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11141   }
11142   else
11143   {
11144     if (action != last_action)
11145       StopSound(element_info[sound_element].sound[last_action]);
11146
11147     if (last_action == ACTION_SLEEPING)
11148       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11149   }
11150 }
11151
11152 static void PlayAllPlayersSound()
11153 {
11154   int i;
11155
11156   for (i = 0; i < MAX_PLAYERS; i++)
11157     if (stored_player[i].active)
11158       PlayPlayerSound(&stored_player[i]);
11159 }
11160
11161 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11162 {
11163   boolean last_waiting = player->is_waiting;
11164   int move_dir = player->MovDir;
11165
11166   player->dir_waiting = move_dir;
11167   player->last_action_waiting = player->action_waiting;
11168
11169   if (is_waiting)
11170   {
11171     if (!last_waiting)          /* not waiting -> waiting */
11172     {
11173       player->is_waiting = TRUE;
11174
11175       player->frame_counter_bored =
11176         FrameCounter +
11177         game.player_boring_delay_fixed +
11178         GetSimpleRandom(game.player_boring_delay_random);
11179       player->frame_counter_sleeping =
11180         FrameCounter +
11181         game.player_sleeping_delay_fixed +
11182         GetSimpleRandom(game.player_sleeping_delay_random);
11183
11184       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11185     }
11186
11187     if (game.player_sleeping_delay_fixed +
11188         game.player_sleeping_delay_random > 0 &&
11189         player->anim_delay_counter == 0 &&
11190         player->post_delay_counter == 0 &&
11191         FrameCounter >= player->frame_counter_sleeping)
11192       player->is_sleeping = TRUE;
11193     else if (game.player_boring_delay_fixed +
11194              game.player_boring_delay_random > 0 &&
11195              FrameCounter >= player->frame_counter_bored)
11196       player->is_bored = TRUE;
11197
11198     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11199                               player->is_bored ? ACTION_BORING :
11200                               ACTION_WAITING);
11201
11202     if (player->is_sleeping && player->use_murphy)
11203     {
11204       /* special case for sleeping Murphy when leaning against non-free tile */
11205
11206       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11207           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11208            !IS_MOVING(player->jx - 1, player->jy)))
11209         move_dir = MV_LEFT;
11210       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11211                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11212                 !IS_MOVING(player->jx + 1, player->jy)))
11213         move_dir = MV_RIGHT;
11214       else
11215         player->is_sleeping = FALSE;
11216
11217       player->dir_waiting = move_dir;
11218     }
11219
11220     if (player->is_sleeping)
11221     {
11222       if (player->num_special_action_sleeping > 0)
11223       {
11224         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11225         {
11226           int last_special_action = player->special_action_sleeping;
11227           int num_special_action = player->num_special_action_sleeping;
11228           int special_action =
11229             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11230              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11231              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11232              last_special_action + 1 : ACTION_SLEEPING);
11233           int special_graphic =
11234             el_act_dir2img(player->artwork_element, special_action, move_dir);
11235
11236           player->anim_delay_counter =
11237             graphic_info[special_graphic].anim_delay_fixed +
11238             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11239           player->post_delay_counter =
11240             graphic_info[special_graphic].post_delay_fixed +
11241             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11242
11243           player->special_action_sleeping = special_action;
11244         }
11245
11246         if (player->anim_delay_counter > 0)
11247         {
11248           player->action_waiting = player->special_action_sleeping;
11249           player->anim_delay_counter--;
11250         }
11251         else if (player->post_delay_counter > 0)
11252         {
11253           player->post_delay_counter--;
11254         }
11255       }
11256     }
11257     else if (player->is_bored)
11258     {
11259       if (player->num_special_action_bored > 0)
11260       {
11261         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11262         {
11263           int special_action =
11264             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11265           int special_graphic =
11266             el_act_dir2img(player->artwork_element, special_action, move_dir);
11267
11268           player->anim_delay_counter =
11269             graphic_info[special_graphic].anim_delay_fixed +
11270             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11271           player->post_delay_counter =
11272             graphic_info[special_graphic].post_delay_fixed +
11273             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11274
11275           player->special_action_bored = special_action;
11276         }
11277
11278         if (player->anim_delay_counter > 0)
11279         {
11280           player->action_waiting = player->special_action_bored;
11281           player->anim_delay_counter--;
11282         }
11283         else if (player->post_delay_counter > 0)
11284         {
11285           player->post_delay_counter--;
11286         }
11287       }
11288     }
11289   }
11290   else if (last_waiting)        /* waiting -> not waiting */
11291   {
11292     player->is_waiting = FALSE;
11293     player->is_bored = FALSE;
11294     player->is_sleeping = FALSE;
11295
11296     player->frame_counter_bored = -1;
11297     player->frame_counter_sleeping = -1;
11298
11299     player->anim_delay_counter = 0;
11300     player->post_delay_counter = 0;
11301
11302     player->dir_waiting = player->MovDir;
11303     player->action_waiting = ACTION_DEFAULT;
11304
11305     player->special_action_bored = ACTION_DEFAULT;
11306     player->special_action_sleeping = ACTION_DEFAULT;
11307   }
11308 }
11309
11310 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11311 {
11312   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11313   int left      = player_action & JOY_LEFT;
11314   int right     = player_action & JOY_RIGHT;
11315   int up        = player_action & JOY_UP;
11316   int down      = player_action & JOY_DOWN;
11317   int button1   = player_action & JOY_BUTTON_1;
11318   int button2   = player_action & JOY_BUTTON_2;
11319   int dx        = (left ? -1 : right ? 1 : 0);
11320   int dy        = (up   ? -1 : down  ? 1 : 0);
11321
11322   if (!player->active || tape.pausing)
11323     return 0;
11324
11325   if (player_action)
11326   {
11327     if (button1)
11328       snapped = SnapField(player, dx, dy);
11329     else
11330     {
11331       if (button2)
11332         dropped = DropElement(player);
11333
11334       moved = MovePlayer(player, dx, dy);
11335     }
11336
11337     if (tape.single_step && tape.recording && !tape.pausing)
11338     {
11339       if (button1 || (dropped && !moved))
11340       {
11341         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11342         SnapField(player, 0, 0);                /* stop snapping */
11343       }
11344     }
11345
11346     SetPlayerWaiting(player, FALSE);
11347
11348     return player_action;
11349   }
11350   else
11351   {
11352     /* no actions for this player (no input at player's configured device) */
11353
11354     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11355     SnapField(player, 0, 0);
11356     CheckGravityMovementWhenNotMoving(player);
11357
11358     if (player->MovPos == 0)
11359       SetPlayerWaiting(player, TRUE);
11360
11361     if (player->MovPos == 0)    /* needed for tape.playing */
11362       player->is_moving = FALSE;
11363
11364     player->is_dropping = FALSE;
11365     player->is_dropping_pressed = FALSE;
11366     player->drop_pressed_delay = 0;
11367
11368     return 0;
11369   }
11370 }
11371
11372 static void CheckLevelTime()
11373 {
11374   int i;
11375
11376   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11377   {
11378     if (level.native_em_level->lev->home == 0)  /* all players at home */
11379     {
11380       PlayerWins(local_player);
11381
11382       AllPlayersGone = TRUE;
11383
11384       level.native_em_level->lev->home = -1;
11385     }
11386
11387     if (level.native_em_level->ply[0]->alive == 0 &&
11388         level.native_em_level->ply[1]->alive == 0 &&
11389         level.native_em_level->ply[2]->alive == 0 &&
11390         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11391       AllPlayersGone = TRUE;
11392   }
11393
11394   if (TimeFrames >= FRAMES_PER_SECOND)
11395   {
11396     TimeFrames = 0;
11397     TapeTime++;
11398
11399     for (i = 0; i < MAX_PLAYERS; i++)
11400     {
11401       struct PlayerInfo *player = &stored_player[i];
11402
11403       if (SHIELD_ON(player))
11404       {
11405         player->shield_normal_time_left--;
11406
11407         if (player->shield_deadly_time_left > 0)
11408           player->shield_deadly_time_left--;
11409       }
11410     }
11411
11412     if (!local_player->LevelSolved && !level.use_step_counter)
11413     {
11414       TimePlayed++;
11415
11416       if (TimeLeft > 0)
11417       {
11418         TimeLeft--;
11419
11420         if (TimeLeft <= 10 && setup.time_limit)
11421           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11422
11423 #if 1
11424         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11425
11426         DisplayGameControlValues();
11427 #else
11428         DrawGameValue_Time(TimeLeft);
11429 #endif
11430
11431         if (!TimeLeft && setup.time_limit)
11432         {
11433           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11434             level.native_em_level->lev->killed_out_of_time = TRUE;
11435           else
11436             for (i = 0; i < MAX_PLAYERS; i++)
11437               KillPlayer(&stored_player[i]);
11438         }
11439       }
11440 #if 1
11441       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11442       {
11443         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11444
11445         DisplayGameControlValues();
11446       }
11447 #else
11448       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11449         DrawGameValue_Time(TimePlayed);
11450 #endif
11451
11452       level.native_em_level->lev->time =
11453         (level.time == 0 ? TimePlayed : TimeLeft);
11454     }
11455
11456     if (tape.recording || tape.playing)
11457       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11458   }
11459
11460 #if 1
11461   UpdateAndDisplayGameControlValues();
11462 #else
11463   UpdateGameDoorValues();
11464   DrawGameDoorValues();
11465 #endif
11466 }
11467
11468 void AdvanceFrameAndPlayerCounters(int player_nr)
11469 {
11470   int i;
11471
11472   /* advance frame counters (global frame counter and time frame counter) */
11473   FrameCounter++;
11474   TimeFrames++;
11475
11476   /* advance player counters (counters for move delay, move animation etc.) */
11477   for (i = 0; i < MAX_PLAYERS; i++)
11478   {
11479     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11480     int move_delay_value = stored_player[i].move_delay_value;
11481     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11482
11483     if (!advance_player_counters)       /* not all players may be affected */
11484       continue;
11485
11486 #if USE_NEW_PLAYER_ANIM
11487     if (move_frames == 0)       /* less than one move per game frame */
11488     {
11489       int stepsize = TILEX / move_delay_value;
11490       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11491       int count = (stored_player[i].is_moving ?
11492                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11493
11494       if (count % delay == 0)
11495         move_frames = 1;
11496     }
11497 #endif
11498
11499     stored_player[i].Frame += move_frames;
11500
11501     if (stored_player[i].MovPos != 0)
11502       stored_player[i].StepFrame += move_frames;
11503
11504     if (stored_player[i].move_delay > 0)
11505       stored_player[i].move_delay--;
11506
11507     /* due to bugs in previous versions, counter must count up, not down */
11508     if (stored_player[i].push_delay != -1)
11509       stored_player[i].push_delay++;
11510
11511     if (stored_player[i].drop_delay > 0)
11512       stored_player[i].drop_delay--;
11513
11514     if (stored_player[i].is_dropping_pressed)
11515       stored_player[i].drop_pressed_delay++;
11516   }
11517 }
11518
11519 void StartGameActions(boolean init_network_game, boolean record_tape,
11520                       long random_seed)
11521 {
11522   unsigned long new_random_seed = InitRND(random_seed);
11523
11524   if (record_tape)
11525     TapeStartRecording(new_random_seed);
11526
11527 #if defined(NETWORK_AVALIABLE)
11528   if (init_network_game)
11529   {
11530     SendToServer_StartPlaying();
11531
11532     return;
11533   }
11534 #endif
11535
11536   InitGame();
11537 }
11538
11539 void GameActions()
11540 {
11541   static unsigned long game_frame_delay = 0;
11542   unsigned long game_frame_delay_value;
11543   byte *recorded_player_action;
11544   byte summarized_player_action = 0;
11545   byte tape_action[MAX_PLAYERS];
11546   int i;
11547
11548   /* detect endless loops, caused by custom element programming */
11549   if (recursion_loop_detected && recursion_loop_depth == 0)
11550   {
11551     char *message = getStringCat3("Internal Error ! Element ",
11552                                   EL_NAME(recursion_loop_element),
11553                                   " caused endless loop ! Quit the game ?");
11554
11555     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11556           EL_NAME(recursion_loop_element));
11557
11558     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11559
11560     recursion_loop_detected = FALSE;    /* if game should be continued */
11561
11562     free(message);
11563
11564     return;
11565   }
11566
11567   if (game.restart_level)
11568     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11569
11570   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11571   {
11572     if (level.native_em_level->lev->home == 0)  /* all players at home */
11573     {
11574       PlayerWins(local_player);
11575
11576       AllPlayersGone = TRUE;
11577
11578       level.native_em_level->lev->home = -1;
11579     }
11580
11581     if (level.native_em_level->ply[0]->alive == 0 &&
11582         level.native_em_level->ply[1]->alive == 0 &&
11583         level.native_em_level->ply[2]->alive == 0 &&
11584         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11585       AllPlayersGone = TRUE;
11586   }
11587
11588   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11589     GameWon();
11590
11591   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11592     TapeStop();
11593
11594   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11595     return;
11596
11597   game_frame_delay_value =
11598     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11599
11600   if (tape.playing && tape.warp_forward && !tape.pausing)
11601     game_frame_delay_value = 0;
11602
11603   /* ---------- main game synchronization point ---------- */
11604
11605   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11606
11607   if (network_playing && !network_player_action_received)
11608   {
11609     /* try to get network player actions in time */
11610
11611 #if defined(NETWORK_AVALIABLE)
11612     /* last chance to get network player actions without main loop delay */
11613     HandleNetworking();
11614 #endif
11615
11616     /* game was quit by network peer */
11617     if (game_status != GAME_MODE_PLAYING)
11618       return;
11619
11620     if (!network_player_action_received)
11621       return;           /* failed to get network player actions in time */
11622
11623     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11624   }
11625
11626   if (tape.pausing)
11627     return;
11628
11629   /* at this point we know that we really continue executing the game */
11630
11631   network_player_action_received = FALSE;
11632
11633   /* when playing tape, read previously recorded player input from tape data */
11634   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11635
11636 #if 1
11637   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11638   if (tape.pausing)
11639     return;
11640 #endif
11641
11642   if (tape.set_centered_player)
11643   {
11644     game.centered_player_nr_next = tape.centered_player_nr_next;
11645     game.set_centered_player = TRUE;
11646   }
11647
11648   for (i = 0; i < MAX_PLAYERS; i++)
11649   {
11650     summarized_player_action |= stored_player[i].action;
11651
11652     if (!network_playing)
11653       stored_player[i].effective_action = stored_player[i].action;
11654   }
11655
11656 #if defined(NETWORK_AVALIABLE)
11657   if (network_playing)
11658     SendToServer_MovePlayer(summarized_player_action);
11659 #endif
11660
11661   if (!options.network && !setup.team_mode)
11662     local_player->effective_action = summarized_player_action;
11663
11664   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11665   {
11666     for (i = 0; i < MAX_PLAYERS; i++)
11667       stored_player[i].effective_action =
11668         (i == game.centered_player_nr ? summarized_player_action : 0);
11669   }
11670
11671   if (recorded_player_action != NULL)
11672     for (i = 0; i < MAX_PLAYERS; i++)
11673       stored_player[i].effective_action = recorded_player_action[i];
11674
11675   for (i = 0; i < MAX_PLAYERS; i++)
11676   {
11677     tape_action[i] = stored_player[i].effective_action;
11678
11679     /* (this can only happen in the R'n'D game engine) */
11680     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11681       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11682   }
11683
11684   /* only record actions from input devices, but not programmed actions */
11685   if (tape.recording)
11686     TapeRecordAction(tape_action);
11687
11688   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11689   {
11690     GameActions_EM_Main();
11691   }
11692   else
11693   {
11694     GameActions_RND();
11695   }
11696 }
11697
11698 void GameActions_EM_Main()
11699 {
11700   byte effective_action[MAX_PLAYERS];
11701   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11702   int i;
11703
11704   for (i = 0; i < MAX_PLAYERS; i++)
11705     effective_action[i] = stored_player[i].effective_action;
11706
11707   GameActions_EM(effective_action, warp_mode);
11708
11709   CheckLevelTime();
11710
11711   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11712 }
11713
11714 void GameActions_RND()
11715 {
11716   int magic_wall_x = 0, magic_wall_y = 0;
11717   int i, x, y, element, graphic;
11718
11719   InitPlayfieldScanModeVars();
11720
11721 #if USE_ONE_MORE_CHANGE_PER_FRAME
11722   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11723   {
11724     SCAN_PLAYFIELD(x, y)
11725     {
11726       ChangeCount[x][y] = 0;
11727       ChangeEvent[x][y] = -1;
11728     }
11729   }
11730 #endif
11731
11732   if (game.set_centered_player)
11733   {
11734     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11735
11736     /* switching to "all players" only possible if all players fit to screen */
11737     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11738     {
11739       game.centered_player_nr_next = game.centered_player_nr;
11740       game.set_centered_player = FALSE;
11741     }
11742
11743     /* do not switch focus to non-existing (or non-active) player */
11744     if (game.centered_player_nr_next >= 0 &&
11745         !stored_player[game.centered_player_nr_next].active)
11746     {
11747       game.centered_player_nr_next = game.centered_player_nr;
11748       game.set_centered_player = FALSE;
11749     }
11750   }
11751
11752   if (game.set_centered_player &&
11753       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11754   {
11755     int sx, sy;
11756
11757     if (game.centered_player_nr_next == -1)
11758     {
11759       setScreenCenteredToAllPlayers(&sx, &sy);
11760     }
11761     else
11762     {
11763       sx = stored_player[game.centered_player_nr_next].jx;
11764       sy = stored_player[game.centered_player_nr_next].jy;
11765     }
11766
11767     game.centered_player_nr = game.centered_player_nr_next;
11768     game.set_centered_player = FALSE;
11769
11770     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11771     DrawGameDoorValues();
11772   }
11773
11774   for (i = 0; i < MAX_PLAYERS; i++)
11775   {
11776     int actual_player_action = stored_player[i].effective_action;
11777
11778 #if 1
11779     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11780        - rnd_equinox_tetrachloride 048
11781        - rnd_equinox_tetrachloride_ii 096
11782        - rnd_emanuel_schmieg 002
11783        - doctor_sloan_ww 001, 020
11784     */
11785     if (stored_player[i].MovPos == 0)
11786       CheckGravityMovement(&stored_player[i]);
11787 #endif
11788
11789     /* overwrite programmed action with tape action */
11790     if (stored_player[i].programmed_action)
11791       actual_player_action = stored_player[i].programmed_action;
11792
11793     PlayerActions(&stored_player[i], actual_player_action);
11794
11795     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11796   }
11797
11798   ScrollScreen(NULL, SCROLL_GO_ON);
11799
11800   /* for backwards compatibility, the following code emulates a fixed bug that
11801      occured when pushing elements (causing elements that just made their last
11802      pushing step to already (if possible) make their first falling step in the
11803      same game frame, which is bad); this code is also needed to use the famous
11804      "spring push bug" which is used in older levels and might be wanted to be
11805      used also in newer levels, but in this case the buggy pushing code is only
11806      affecting the "spring" element and no other elements */
11807
11808   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11809   {
11810     for (i = 0; i < MAX_PLAYERS; i++)
11811     {
11812       struct PlayerInfo *player = &stored_player[i];
11813       int x = player->jx;
11814       int y = player->jy;
11815
11816       if (player->active && player->is_pushing && player->is_moving &&
11817           IS_MOVING(x, y) &&
11818           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11819            Feld[x][y] == EL_SPRING))
11820       {
11821         ContinueMoving(x, y);
11822
11823         /* continue moving after pushing (this is actually a bug) */
11824         if (!IS_MOVING(x, y))
11825           Stop[x][y] = FALSE;
11826       }
11827     }
11828   }
11829
11830 #if 0
11831   debug_print_timestamp(0, "start main loop profiling");
11832 #endif
11833
11834   SCAN_PLAYFIELD(x, y)
11835   {
11836     ChangeCount[x][y] = 0;
11837     ChangeEvent[x][y] = -1;
11838
11839     /* this must be handled before main playfield loop */
11840     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11841     {
11842       MovDelay[x][y]--;
11843       if (MovDelay[x][y] <= 0)
11844         RemoveField(x, y);
11845     }
11846
11847 #if USE_NEW_SNAP_DELAY
11848     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11849     {
11850       MovDelay[x][y]--;
11851       if (MovDelay[x][y] <= 0)
11852       {
11853         RemoveField(x, y);
11854         DrawLevelField(x, y);
11855
11856         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11857       }
11858     }
11859 #endif
11860
11861 #if DEBUG
11862     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11863     {
11864       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11865       printf("GameActions(): This should never happen!\n");
11866
11867       ChangePage[x][y] = -1;
11868     }
11869 #endif
11870
11871     Stop[x][y] = FALSE;
11872     if (WasJustMoving[x][y] > 0)
11873       WasJustMoving[x][y]--;
11874     if (WasJustFalling[x][y] > 0)
11875       WasJustFalling[x][y]--;
11876     if (CheckCollision[x][y] > 0)
11877       CheckCollision[x][y]--;
11878     if (CheckImpact[x][y] > 0)
11879       CheckImpact[x][y]--;
11880
11881     GfxFrame[x][y]++;
11882
11883     /* reset finished pushing action (not done in ContinueMoving() to allow
11884        continuous pushing animation for elements with zero push delay) */
11885     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11886     {
11887       ResetGfxAnimation(x, y);
11888       DrawLevelField(x, y);
11889     }
11890
11891 #if DEBUG
11892     if (IS_BLOCKED(x, y))
11893     {
11894       int oldx, oldy;
11895
11896       Blocked2Moving(x, y, &oldx, &oldy);
11897       if (!IS_MOVING(oldx, oldy))
11898       {
11899         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11900         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11901         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11902         printf("GameActions(): This should never happen!\n");
11903       }
11904     }
11905 #endif
11906   }
11907
11908 #if 0
11909   debug_print_timestamp(0, "- time for pre-main loop:");
11910 #endif
11911
11912 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
11913   SCAN_PLAYFIELD(x, y)
11914   {
11915     element = Feld[x][y];
11916     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11917
11918 #if 1
11919     {
11920 #if 1
11921       int element2 = element;
11922       int graphic2 = graphic;
11923 #else
11924       int element2 = Feld[x][y];
11925       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11926 #endif
11927       int last_gfx_frame = GfxFrame[x][y];
11928
11929       if (graphic_info[graphic2].anim_global_sync)
11930         GfxFrame[x][y] = FrameCounter;
11931       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11932         GfxFrame[x][y] = CustomValue[x][y];
11933       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11934         GfxFrame[x][y] = element_info[element2].collect_score;
11935       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11936         GfxFrame[x][y] = ChangeDelay[x][y];
11937
11938       if (redraw && GfxFrame[x][y] != last_gfx_frame)
11939         DrawLevelGraphicAnimation(x, y, graphic2);
11940     }
11941 #else
11942     ResetGfxFrame(x, y, TRUE);
11943 #endif
11944
11945 #if 1
11946     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11947         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11948       ResetRandomAnimationValue(x, y);
11949 #endif
11950
11951 #if 1
11952     SetRandomAnimationValue(x, y);
11953 #endif
11954
11955 #if 1
11956     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11957 #endif
11958   }
11959 #endif  // -------------------- !!! TEST ONLY !!! --------------------
11960
11961 #if 0
11962   debug_print_timestamp(0, "- time for TEST loop:     -->");
11963 #endif
11964
11965   SCAN_PLAYFIELD(x, y)
11966   {
11967     element = Feld[x][y];
11968     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11969
11970     ResetGfxFrame(x, y, TRUE);
11971
11972     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11973         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11974       ResetRandomAnimationValue(x, y);
11975
11976     SetRandomAnimationValue(x, y);
11977
11978     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11979
11980     if (IS_INACTIVE(element))
11981     {
11982       if (IS_ANIMATED(graphic))
11983         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11984
11985       continue;
11986     }
11987
11988     /* this may take place after moving, so 'element' may have changed */
11989     if (IS_CHANGING(x, y) &&
11990         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11991     {
11992       int page = element_info[element].event_page_nr[CE_DELAY];
11993
11994 #if 1
11995       HandleElementChange(x, y, page);
11996 #else
11997       if (CAN_CHANGE(element))
11998         HandleElementChange(x, y, page);
11999
12000       if (HAS_ACTION(element))
12001         ExecuteCustomElementAction(x, y, element, page);
12002 #endif
12003
12004       element = Feld[x][y];
12005       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12006     }
12007
12008 #if 0   // ---------------------------------------------------------------------
12009
12010     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12011     {
12012       StartMoving(x, y);
12013
12014       element = Feld[x][y];
12015       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12016
12017       if (IS_ANIMATED(graphic) &&
12018           !IS_MOVING(x, y) &&
12019           !Stop[x][y])
12020         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12021
12022       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12023         DrawTwinkleOnField(x, y);
12024     }
12025     else if (IS_MOVING(x, y))
12026       ContinueMoving(x, y);
12027     else
12028     {
12029       switch (element)
12030       {
12031         case EL_ACID:
12032         case EL_EXIT_OPEN:
12033         case EL_EM_EXIT_OPEN:
12034         case EL_SP_EXIT_OPEN:
12035         case EL_STEEL_EXIT_OPEN:
12036         case EL_EM_STEEL_EXIT_OPEN:
12037         case EL_SP_TERMINAL:
12038         case EL_SP_TERMINAL_ACTIVE:
12039         case EL_EXTRA_TIME:
12040         case EL_SHIELD_NORMAL:
12041         case EL_SHIELD_DEADLY:
12042           if (IS_ANIMATED(graphic))
12043             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12044           break;
12045
12046         case EL_DYNAMITE_ACTIVE:
12047         case EL_EM_DYNAMITE_ACTIVE:
12048         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12049         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12050         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12051         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12052         case EL_SP_DISK_RED_ACTIVE:
12053           CheckDynamite(x, y);
12054           break;
12055
12056         case EL_AMOEBA_GROWING:
12057           AmoebeWaechst(x, y);
12058           break;
12059
12060         case EL_AMOEBA_SHRINKING:
12061           AmoebaDisappearing(x, y);
12062           break;
12063
12064 #if !USE_NEW_AMOEBA_CODE
12065         case EL_AMOEBA_WET:
12066         case EL_AMOEBA_DRY:
12067         case EL_AMOEBA_FULL:
12068         case EL_BD_AMOEBA:
12069         case EL_EMC_DRIPPER:
12070           AmoebeAbleger(x, y);
12071           break;
12072 #endif
12073
12074         case EL_GAME_OF_LIFE:
12075         case EL_BIOMAZE:
12076           Life(x, y);
12077           break;
12078
12079         case EL_EXIT_CLOSED:
12080           CheckExit(x, y);
12081           break;
12082
12083         case EL_EM_EXIT_CLOSED:
12084           CheckExitEM(x, y);
12085           break;
12086
12087         case EL_STEEL_EXIT_CLOSED:
12088           CheckExitSteel(x, y);
12089           break;
12090
12091         case EL_EM_STEEL_EXIT_CLOSED:
12092           CheckExitSteelEM(x, y);
12093           break;
12094
12095         case EL_SP_EXIT_CLOSED:
12096           CheckExitSP(x, y);
12097           break;
12098
12099         case EL_EXPANDABLE_WALL_GROWING:
12100         case EL_EXPANDABLE_STEELWALL_GROWING:
12101           MauerWaechst(x, y);
12102           break;
12103
12104         case EL_EXPANDABLE_WALL:
12105         case EL_EXPANDABLE_WALL_HORIZONTAL:
12106         case EL_EXPANDABLE_WALL_VERTICAL:
12107         case EL_EXPANDABLE_WALL_ANY:
12108         case EL_BD_EXPANDABLE_WALL:
12109           MauerAbleger(x, y);
12110           break;
12111
12112         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12113         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12114         case EL_EXPANDABLE_STEELWALL_ANY:
12115           MauerAblegerStahl(x, y);
12116           break;
12117
12118         case EL_FLAMES:
12119           CheckForDragon(x, y);
12120           break;
12121
12122         case EL_EXPLOSION:
12123           break;
12124
12125         case EL_ELEMENT_SNAPPING:
12126         case EL_DIAGONAL_SHRINKING:
12127         case EL_DIAGONAL_GROWING:
12128         {
12129           graphic =
12130             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12131
12132           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12133           break;
12134         }
12135
12136         default:
12137           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12138             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12139           break;
12140       }
12141     }
12142
12143 #else   // ---------------------------------------------------------------------
12144
12145     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12146     {
12147       StartMoving(x, y);
12148
12149       element = Feld[x][y];
12150       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12151
12152       if (IS_ANIMATED(graphic) &&
12153           !IS_MOVING(x, y) &&
12154           !Stop[x][y])
12155         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12156
12157       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12158         DrawTwinkleOnField(x, y);
12159     }
12160     else if ((element == EL_ACID ||
12161               element == EL_EXIT_OPEN ||
12162               element == EL_EM_EXIT_OPEN ||
12163               element == EL_SP_EXIT_OPEN ||
12164               element == EL_STEEL_EXIT_OPEN ||
12165               element == EL_EM_STEEL_EXIT_OPEN ||
12166               element == EL_SP_TERMINAL ||
12167               element == EL_SP_TERMINAL_ACTIVE ||
12168               element == EL_EXTRA_TIME ||
12169               element == EL_SHIELD_NORMAL ||
12170               element == EL_SHIELD_DEADLY) &&
12171              IS_ANIMATED(graphic))
12172       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12173     else if (IS_MOVING(x, y))
12174       ContinueMoving(x, y);
12175     else if (IS_ACTIVE_BOMB(element))
12176       CheckDynamite(x, y);
12177     else if (element == EL_AMOEBA_GROWING)
12178       AmoebeWaechst(x, y);
12179     else if (element == EL_AMOEBA_SHRINKING)
12180       AmoebaDisappearing(x, y);
12181
12182 #if !USE_NEW_AMOEBA_CODE
12183     else if (IS_AMOEBALIVE(element))
12184       AmoebeAbleger(x, y);
12185 #endif
12186
12187     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12188       Life(x, y);
12189     else if (element == EL_EXIT_CLOSED)
12190       CheckExit(x, y);
12191     else if (element == EL_EM_EXIT_CLOSED)
12192       CheckExitEM(x, y);
12193     else if (element == EL_STEEL_EXIT_CLOSED)
12194       CheckExitSteel(x, y);
12195     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12196       CheckExitSteelEM(x, y);
12197     else if (element == EL_SP_EXIT_CLOSED)
12198       CheckExitSP(x, y);
12199     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12200              element == EL_EXPANDABLE_STEELWALL_GROWING)
12201       MauerWaechst(x, y);
12202     else if (element == EL_EXPANDABLE_WALL ||
12203              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12204              element == EL_EXPANDABLE_WALL_VERTICAL ||
12205              element == EL_EXPANDABLE_WALL_ANY ||
12206              element == EL_BD_EXPANDABLE_WALL)
12207       MauerAbleger(x, y);
12208     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12209              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12210              element == EL_EXPANDABLE_STEELWALL_ANY)
12211       MauerAblegerStahl(x, y);
12212     else if (element == EL_FLAMES)
12213       CheckForDragon(x, y);
12214     else if (element == EL_EXPLOSION)
12215       ; /* drawing of correct explosion animation is handled separately */
12216     else if (element == EL_ELEMENT_SNAPPING ||
12217              element == EL_DIAGONAL_SHRINKING ||
12218              element == EL_DIAGONAL_GROWING)
12219     {
12220       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12221
12222       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12223     }
12224     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12225       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12226
12227 #endif  // ---------------------------------------------------------------------
12228
12229     if (IS_BELT_ACTIVE(element))
12230       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12231
12232     if (game.magic_wall_active)
12233     {
12234       int jx = local_player->jx, jy = local_player->jy;
12235
12236       /* play the element sound at the position nearest to the player */
12237       if ((element == EL_MAGIC_WALL_FULL ||
12238            element == EL_MAGIC_WALL_ACTIVE ||
12239            element == EL_MAGIC_WALL_EMPTYING ||
12240            element == EL_BD_MAGIC_WALL_FULL ||
12241            element == EL_BD_MAGIC_WALL_ACTIVE ||
12242            element == EL_BD_MAGIC_WALL_EMPTYING ||
12243            element == EL_DC_MAGIC_WALL_FULL ||
12244            element == EL_DC_MAGIC_WALL_ACTIVE ||
12245            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12246           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12247       {
12248         magic_wall_x = x;
12249         magic_wall_y = y;
12250       }
12251     }
12252   }
12253
12254 #if 0
12255   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12256 #endif
12257
12258 #if USE_NEW_AMOEBA_CODE
12259   /* new experimental amoeba growth stuff */
12260   if (!(FrameCounter % 8))
12261   {
12262     static unsigned long random = 1684108901;
12263
12264     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12265     {
12266       x = RND(lev_fieldx);
12267       y = RND(lev_fieldy);
12268       element = Feld[x][y];
12269
12270       if (!IS_PLAYER(x,y) &&
12271           (element == EL_EMPTY ||
12272            CAN_GROW_INTO(element) ||
12273            element == EL_QUICKSAND_EMPTY ||
12274            element == EL_QUICKSAND_FAST_EMPTY ||
12275            element == EL_ACID_SPLASH_LEFT ||
12276            element == EL_ACID_SPLASH_RIGHT))
12277       {
12278         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12279             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12280             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12281             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12282           Feld[x][y] = EL_AMOEBA_DROP;
12283       }
12284
12285       random = random * 129 + 1;
12286     }
12287   }
12288 #endif
12289
12290 #if 0
12291   if (game.explosions_delayed)
12292 #endif
12293   {
12294     game.explosions_delayed = FALSE;
12295
12296     SCAN_PLAYFIELD(x, y)
12297     {
12298       element = Feld[x][y];
12299
12300       if (ExplodeField[x][y])
12301         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12302       else if (element == EL_EXPLOSION)
12303         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12304
12305       ExplodeField[x][y] = EX_TYPE_NONE;
12306     }
12307
12308     game.explosions_delayed = TRUE;
12309   }
12310
12311   if (game.magic_wall_active)
12312   {
12313     if (!(game.magic_wall_time_left % 4))
12314     {
12315       int element = Feld[magic_wall_x][magic_wall_y];
12316
12317       if (element == EL_BD_MAGIC_WALL_FULL ||
12318           element == EL_BD_MAGIC_WALL_ACTIVE ||
12319           element == EL_BD_MAGIC_WALL_EMPTYING)
12320         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12321       else if (element == EL_DC_MAGIC_WALL_FULL ||
12322                element == EL_DC_MAGIC_WALL_ACTIVE ||
12323                element == EL_DC_MAGIC_WALL_EMPTYING)
12324         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12325       else
12326         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12327     }
12328
12329     if (game.magic_wall_time_left > 0)
12330     {
12331       game.magic_wall_time_left--;
12332
12333       if (!game.magic_wall_time_left)
12334       {
12335         SCAN_PLAYFIELD(x, y)
12336         {
12337           element = Feld[x][y];
12338
12339           if (element == EL_MAGIC_WALL_ACTIVE ||
12340               element == EL_MAGIC_WALL_FULL)
12341           {
12342             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12343             DrawLevelField(x, y);
12344           }
12345           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12346                    element == EL_BD_MAGIC_WALL_FULL)
12347           {
12348             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12349             DrawLevelField(x, y);
12350           }
12351           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12352                    element == EL_DC_MAGIC_WALL_FULL)
12353           {
12354             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12355             DrawLevelField(x, y);
12356           }
12357         }
12358
12359         game.magic_wall_active = FALSE;
12360       }
12361     }
12362   }
12363
12364   if (game.light_time_left > 0)
12365   {
12366     game.light_time_left--;
12367
12368     if (game.light_time_left == 0)
12369       RedrawAllLightSwitchesAndInvisibleElements();
12370   }
12371
12372   if (game.timegate_time_left > 0)
12373   {
12374     game.timegate_time_left--;
12375
12376     if (game.timegate_time_left == 0)
12377       CloseAllOpenTimegates();
12378   }
12379
12380   if (game.lenses_time_left > 0)
12381   {
12382     game.lenses_time_left--;
12383
12384     if (game.lenses_time_left == 0)
12385       RedrawAllInvisibleElementsForLenses();
12386   }
12387
12388   if (game.magnify_time_left > 0)
12389   {
12390     game.magnify_time_left--;
12391
12392     if (game.magnify_time_left == 0)
12393       RedrawAllInvisibleElementsForMagnifier();
12394   }
12395
12396   for (i = 0; i < MAX_PLAYERS; i++)
12397   {
12398     struct PlayerInfo *player = &stored_player[i];
12399
12400     if (SHIELD_ON(player))
12401     {
12402       if (player->shield_deadly_time_left)
12403         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12404       else if (player->shield_normal_time_left)
12405         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12406     }
12407   }
12408
12409   CheckLevelTime();
12410
12411   DrawAllPlayers();
12412   PlayAllPlayersSound();
12413
12414   if (options.debug)                    /* calculate frames per second */
12415   {
12416     static unsigned long fps_counter = 0;
12417     static int fps_frames = 0;
12418     unsigned long fps_delay_ms = Counter() - fps_counter;
12419
12420     fps_frames++;
12421
12422     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
12423     {
12424       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12425
12426       fps_frames = 0;
12427       fps_counter = Counter();
12428     }
12429
12430     redraw_mask |= REDRAW_FPS;
12431   }
12432
12433   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12434
12435   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12436   {
12437     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12438
12439     local_player->show_envelope = 0;
12440   }
12441
12442 #if 0
12443   debug_print_timestamp(0, "stop main loop profiling ");
12444   printf("----------------------------------------------------------\n");
12445 #endif
12446
12447   /* use random number generator in every frame to make it less predictable */
12448   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12449     RND(1);
12450 }
12451
12452 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12453 {
12454   int min_x = x, min_y = y, max_x = x, max_y = y;
12455   int i;
12456
12457   for (i = 0; i < MAX_PLAYERS; i++)
12458   {
12459     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12460
12461     if (!stored_player[i].active || &stored_player[i] == player)
12462       continue;
12463
12464     min_x = MIN(min_x, jx);
12465     min_y = MIN(min_y, jy);
12466     max_x = MAX(max_x, jx);
12467     max_y = MAX(max_y, jy);
12468   }
12469
12470   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12471 }
12472
12473 static boolean AllPlayersInVisibleScreen()
12474 {
12475   int i;
12476
12477   for (i = 0; i < MAX_PLAYERS; i++)
12478   {
12479     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12480
12481     if (!stored_player[i].active)
12482       continue;
12483
12484     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12485       return FALSE;
12486   }
12487
12488   return TRUE;
12489 }
12490
12491 void ScrollLevel(int dx, int dy)
12492 {
12493 #if 1
12494   static Bitmap *bitmap_db_field2 = NULL;
12495   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12496   int x, y;
12497 #else
12498   int i, x, y;
12499 #endif
12500
12501 #if 0
12502   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12503   /* only horizontal XOR vertical scroll direction allowed */
12504   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12505     return;
12506 #endif
12507
12508 #if 1
12509   if (bitmap_db_field2 == NULL)
12510     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12511
12512   /* needed when blitting directly to same bitmap -- should not be needed with
12513      recent SDL libraries, but apparently does not work in 1.2.11 directly */
12514   BlitBitmap(drawto_field, bitmap_db_field2,
12515              FX + TILEX * (dx == -1) - softscroll_offset,
12516              FY + TILEY * (dy == -1) - softscroll_offset,
12517              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12518              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12519              FX + TILEX * (dx == 1) - softscroll_offset,
12520              FY + TILEY * (dy == 1) - softscroll_offset);
12521   BlitBitmap(bitmap_db_field2, drawto_field,
12522              FX + TILEX * (dx == 1) - softscroll_offset,
12523              FY + TILEY * (dy == 1) - softscroll_offset,
12524              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12525              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12526              FX + TILEX * (dx == 1) - softscroll_offset,
12527              FY + TILEY * (dy == 1) - softscroll_offset);
12528
12529 #else
12530
12531 #if 0
12532   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12533   int xsize = (BX2 - BX1 + 1);
12534   int ysize = (BY2 - BY1 + 1);
12535   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12536   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12537   int step  = (start < end ? +1 : -1);
12538
12539   for (i = start; i != end; i += step)
12540   {
12541     BlitBitmap(drawto_field, drawto_field,
12542                FX + TILEX * (dx != 0 ? i + step : 0),
12543                FY + TILEY * (dy != 0 ? i + step : 0),
12544                TILEX * (dx != 0 ? 1 : xsize),
12545                TILEY * (dy != 0 ? 1 : ysize),
12546                FX + TILEX * (dx != 0 ? i : 0),
12547                FY + TILEY * (dy != 0 ? i : 0));
12548   }
12549
12550 #else
12551
12552   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12553
12554   BlitBitmap(drawto_field, drawto_field,
12555              FX + TILEX * (dx == -1) - softscroll_offset,
12556              FY + TILEY * (dy == -1) - softscroll_offset,
12557              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12558              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12559              FX + TILEX * (dx == 1) - softscroll_offset,
12560              FY + TILEY * (dy == 1) - softscroll_offset);
12561 #endif
12562 #endif
12563
12564   if (dx != 0)
12565   {
12566     x = (dx == 1 ? BX1 : BX2);
12567     for (y = BY1; y <= BY2; y++)
12568       DrawScreenField(x, y);
12569   }
12570
12571   if (dy != 0)
12572   {
12573     y = (dy == 1 ? BY1 : BY2);
12574     for (x = BX1; x <= BX2; x++)
12575       DrawScreenField(x, y);
12576   }
12577
12578   redraw_mask |= REDRAW_FIELD;
12579 }
12580
12581 static boolean canFallDown(struct PlayerInfo *player)
12582 {
12583   int jx = player->jx, jy = player->jy;
12584
12585   return (IN_LEV_FIELD(jx, jy + 1) &&
12586           (IS_FREE(jx, jy + 1) ||
12587            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12588           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12589           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12590 }
12591
12592 static boolean canPassField(int x, int y, int move_dir)
12593 {
12594   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12595   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12596   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12597   int nextx = x + dx;
12598   int nexty = y + dy;
12599   int element = Feld[x][y];
12600
12601   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12602           !CAN_MOVE(element) &&
12603           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12604           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12605           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12606 }
12607
12608 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12609 {
12610   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12611   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12612   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12613   int newx = x + dx;
12614   int newy = y + dy;
12615
12616   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12617           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12618           (IS_DIGGABLE(Feld[newx][newy]) ||
12619            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12620            canPassField(newx, newy, move_dir)));
12621 }
12622
12623 static void CheckGravityMovement(struct PlayerInfo *player)
12624 {
12625 #if USE_PLAYER_GRAVITY
12626   if (player->gravity && !player->programmed_action)
12627 #else
12628   if (game.gravity && !player->programmed_action)
12629 #endif
12630   {
12631     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12632     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12633     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12634     int jx = player->jx, jy = player->jy;
12635     boolean player_is_moving_to_valid_field =
12636       (!player_is_snapping &&
12637        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12638         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12639     boolean player_can_fall_down = canFallDown(player);
12640
12641     if (player_can_fall_down &&
12642         !player_is_moving_to_valid_field)
12643       player->programmed_action = MV_DOWN;
12644   }
12645 }
12646
12647 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12648 {
12649   return CheckGravityMovement(player);
12650
12651 #if USE_PLAYER_GRAVITY
12652   if (player->gravity && !player->programmed_action)
12653 #else
12654   if (game.gravity && !player->programmed_action)
12655 #endif
12656   {
12657     int jx = player->jx, jy = player->jy;
12658     boolean field_under_player_is_free =
12659       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12660     boolean player_is_standing_on_valid_field =
12661       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12662        (IS_WALKABLE(Feld[jx][jy]) &&
12663         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12664
12665     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12666       player->programmed_action = MV_DOWN;
12667   }
12668 }
12669
12670 /*
12671   MovePlayerOneStep()
12672   -----------------------------------------------------------------------------
12673   dx, dy:               direction (non-diagonal) to try to move the player to
12674   real_dx, real_dy:     direction as read from input device (can be diagonal)
12675 */
12676
12677 boolean MovePlayerOneStep(struct PlayerInfo *player,
12678                           int dx, int dy, int real_dx, int real_dy)
12679 {
12680   int jx = player->jx, jy = player->jy;
12681   int new_jx = jx + dx, new_jy = jy + dy;
12682 #if !USE_FIXED_DONT_RUN_INTO
12683   int element;
12684 #endif
12685   int can_move;
12686   boolean player_can_move = !player->cannot_move;
12687
12688   if (!player->active || (!dx && !dy))
12689     return MP_NO_ACTION;
12690
12691   player->MovDir = (dx < 0 ? MV_LEFT :
12692                     dx > 0 ? MV_RIGHT :
12693                     dy < 0 ? MV_UP :
12694                     dy > 0 ? MV_DOWN :  MV_NONE);
12695
12696   if (!IN_LEV_FIELD(new_jx, new_jy))
12697     return MP_NO_ACTION;
12698
12699   if (!player_can_move)
12700   {
12701     if (player->MovPos == 0)
12702     {
12703       player->is_moving = FALSE;
12704       player->is_digging = FALSE;
12705       player->is_collecting = FALSE;
12706       player->is_snapping = FALSE;
12707       player->is_pushing = FALSE;
12708     }
12709   }
12710
12711 #if 1
12712   if (!options.network && game.centered_player_nr == -1 &&
12713       !AllPlayersInSight(player, new_jx, new_jy))
12714     return MP_NO_ACTION;
12715 #else
12716   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12717     return MP_NO_ACTION;
12718 #endif
12719
12720 #if !USE_FIXED_DONT_RUN_INTO
12721   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12722
12723   /* (moved to DigField()) */
12724   if (player_can_move && DONT_RUN_INTO(element))
12725   {
12726     if (element == EL_ACID && dx == 0 && dy == 1)
12727     {
12728       SplashAcid(new_jx, new_jy);
12729       Feld[jx][jy] = EL_PLAYER_1;
12730       InitMovingField(jx, jy, MV_DOWN);
12731       Store[jx][jy] = EL_ACID;
12732       ContinueMoving(jx, jy);
12733       BuryPlayer(player);
12734     }
12735     else
12736       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12737
12738     return MP_MOVING;
12739   }
12740 #endif
12741
12742   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12743   if (can_move != MP_MOVING)
12744     return can_move;
12745
12746   /* check if DigField() has caused relocation of the player */
12747   if (player->jx != jx || player->jy != jy)
12748     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12749
12750   StorePlayer[jx][jy] = 0;
12751   player->last_jx = jx;
12752   player->last_jy = jy;
12753   player->jx = new_jx;
12754   player->jy = new_jy;
12755   StorePlayer[new_jx][new_jy] = player->element_nr;
12756
12757   if (player->move_delay_value_next != -1)
12758   {
12759     player->move_delay_value = player->move_delay_value_next;
12760     player->move_delay_value_next = -1;
12761   }
12762
12763   player->MovPos =
12764     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12765
12766   player->step_counter++;
12767
12768   PlayerVisit[jx][jy] = FrameCounter;
12769
12770 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12771   player->is_moving = TRUE;
12772 #endif
12773
12774 #if 1
12775   /* should better be called in MovePlayer(), but this breaks some tapes */
12776   ScrollPlayer(player, SCROLL_INIT);
12777 #endif
12778
12779   return MP_MOVING;
12780 }
12781
12782 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12783 {
12784   int jx = player->jx, jy = player->jy;
12785   int old_jx = jx, old_jy = jy;
12786   int moved = MP_NO_ACTION;
12787
12788   if (!player->active)
12789     return FALSE;
12790
12791   if (!dx && !dy)
12792   {
12793     if (player->MovPos == 0)
12794     {
12795       player->is_moving = FALSE;
12796       player->is_digging = FALSE;
12797       player->is_collecting = FALSE;
12798       player->is_snapping = FALSE;
12799       player->is_pushing = FALSE;
12800     }
12801
12802     return FALSE;
12803   }
12804
12805   if (player->move_delay > 0)
12806     return FALSE;
12807
12808   player->move_delay = -1;              /* set to "uninitialized" value */
12809
12810   /* store if player is automatically moved to next field */
12811   player->is_auto_moving = (player->programmed_action != MV_NONE);
12812
12813   /* remove the last programmed player action */
12814   player->programmed_action = 0;
12815
12816   if (player->MovPos)
12817   {
12818     /* should only happen if pre-1.2 tape recordings are played */
12819     /* this is only for backward compatibility */
12820
12821     int original_move_delay_value = player->move_delay_value;
12822
12823 #if DEBUG
12824     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12825            tape.counter);
12826 #endif
12827
12828     /* scroll remaining steps with finest movement resolution */
12829     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12830
12831     while (player->MovPos)
12832     {
12833       ScrollPlayer(player, SCROLL_GO_ON);
12834       ScrollScreen(NULL, SCROLL_GO_ON);
12835
12836       AdvanceFrameAndPlayerCounters(player->index_nr);
12837
12838       DrawAllPlayers();
12839       BackToFront();
12840     }
12841
12842     player->move_delay_value = original_move_delay_value;
12843   }
12844
12845   player->is_active = FALSE;
12846
12847   if (player->last_move_dir & MV_HORIZONTAL)
12848   {
12849     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12850       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12851   }
12852   else
12853   {
12854     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12855       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12856   }
12857
12858 #if USE_FIXED_BORDER_RUNNING_GFX
12859   if (!moved && !player->is_active)
12860   {
12861     player->is_moving = FALSE;
12862     player->is_digging = FALSE;
12863     player->is_collecting = FALSE;
12864     player->is_snapping = FALSE;
12865     player->is_pushing = FALSE;
12866   }
12867 #endif
12868
12869   jx = player->jx;
12870   jy = player->jy;
12871
12872 #if 1
12873   if (moved & MP_MOVING && !ScreenMovPos &&
12874       (player->index_nr == game.centered_player_nr ||
12875        game.centered_player_nr == -1))
12876 #else
12877   if (moved & MP_MOVING && !ScreenMovPos &&
12878       (player == local_player || !options.network))
12879 #endif
12880   {
12881     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12882     int offset = game.scroll_delay_value;
12883
12884     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12885     {
12886       /* actual player has left the screen -- scroll in that direction */
12887       if (jx != old_jx)         /* player has moved horizontally */
12888         scroll_x += (jx - old_jx);
12889       else                      /* player has moved vertically */
12890         scroll_y += (jy - old_jy);
12891     }
12892     else
12893     {
12894       if (jx != old_jx)         /* player has moved horizontally */
12895       {
12896         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12897             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12898           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12899
12900         /* don't scroll over playfield boundaries */
12901         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12902           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12903
12904         /* don't scroll more than one field at a time */
12905         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12906
12907         /* don't scroll against the player's moving direction */
12908         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12909             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12910           scroll_x = old_scroll_x;
12911       }
12912       else                      /* player has moved vertically */
12913       {
12914         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12915             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12916           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12917
12918         /* don't scroll over playfield boundaries */
12919         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12920           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12921
12922         /* don't scroll more than one field at a time */
12923         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12924
12925         /* don't scroll against the player's moving direction */
12926         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12927             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12928           scroll_y = old_scroll_y;
12929       }
12930     }
12931
12932     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12933     {
12934 #if 1
12935       if (!options.network && game.centered_player_nr == -1 &&
12936           !AllPlayersInVisibleScreen())
12937       {
12938         scroll_x = old_scroll_x;
12939         scroll_y = old_scroll_y;
12940       }
12941       else
12942 #else
12943       if (!options.network && !AllPlayersInVisibleScreen())
12944       {
12945         scroll_x = old_scroll_x;
12946         scroll_y = old_scroll_y;
12947       }
12948       else
12949 #endif
12950       {
12951         ScrollScreen(player, SCROLL_INIT);
12952         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12953       }
12954     }
12955   }
12956
12957   player->StepFrame = 0;
12958
12959   if (moved & MP_MOVING)
12960   {
12961     if (old_jx != jx && old_jy == jy)
12962       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12963     else if (old_jx == jx && old_jy != jy)
12964       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12965
12966     DrawLevelField(jx, jy);     /* for "crumbled sand" */
12967
12968     player->last_move_dir = player->MovDir;
12969     player->is_moving = TRUE;
12970     player->is_snapping = FALSE;
12971     player->is_switching = FALSE;
12972     player->is_dropping = FALSE;
12973     player->is_dropping_pressed = FALSE;
12974     player->drop_pressed_delay = 0;
12975
12976 #if 0
12977     /* should better be called here than above, but this breaks some tapes */
12978     ScrollPlayer(player, SCROLL_INIT);
12979 #endif
12980   }
12981   else
12982   {
12983     CheckGravityMovementWhenNotMoving(player);
12984
12985     player->is_moving = FALSE;
12986
12987     /* at this point, the player is allowed to move, but cannot move right now
12988        (e.g. because of something blocking the way) -- ensure that the player
12989        is also allowed to move in the next frame (in old versions before 3.1.1,
12990        the player was forced to wait again for eight frames before next try) */
12991
12992     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12993       player->move_delay = 0;   /* allow direct movement in the next frame */
12994   }
12995
12996   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12997     player->move_delay = player->move_delay_value;
12998
12999   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13000   {
13001     TestIfPlayerTouchesBadThing(jx, jy);
13002     TestIfPlayerTouchesCustomElement(jx, jy);
13003   }
13004
13005   if (!player->active)
13006     RemovePlayer(player);
13007
13008   return moved;
13009 }
13010
13011 void ScrollPlayer(struct PlayerInfo *player, int mode)
13012 {
13013   int jx = player->jx, jy = player->jy;
13014   int last_jx = player->last_jx, last_jy = player->last_jy;
13015   int move_stepsize = TILEX / player->move_delay_value;
13016
13017 #if USE_NEW_PLAYER_SPEED
13018   if (!player->active)
13019     return;
13020
13021   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13022     return;
13023 #else
13024   if (!player->active || player->MovPos == 0)
13025     return;
13026 #endif
13027
13028   if (mode == SCROLL_INIT)
13029   {
13030     player->actual_frame_counter = FrameCounter;
13031     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13032
13033     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13034         Feld[last_jx][last_jy] == EL_EMPTY)
13035     {
13036       int last_field_block_delay = 0;   /* start with no blocking at all */
13037       int block_delay_adjustment = player->block_delay_adjustment;
13038
13039       /* if player blocks last field, add delay for exactly one move */
13040       if (player->block_last_field)
13041       {
13042         last_field_block_delay += player->move_delay_value;
13043
13044         /* when blocking enabled, prevent moving up despite gravity */
13045 #if USE_PLAYER_GRAVITY
13046         if (player->gravity && player->MovDir == MV_UP)
13047           block_delay_adjustment = -1;
13048 #else
13049         if (game.gravity && player->MovDir == MV_UP)
13050           block_delay_adjustment = -1;
13051 #endif
13052       }
13053
13054       /* add block delay adjustment (also possible when not blocking) */
13055       last_field_block_delay += block_delay_adjustment;
13056
13057       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13058       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13059     }
13060
13061 #if USE_NEW_PLAYER_SPEED
13062     if (player->MovPos != 0)    /* player has not yet reached destination */
13063       return;
13064 #else
13065     return;
13066 #endif
13067   }
13068   else if (!FrameReached(&player->actual_frame_counter, 1))
13069     return;
13070
13071 #if USE_NEW_PLAYER_SPEED
13072   if (player->MovPos != 0)
13073   {
13074     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13075     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13076
13077     /* before DrawPlayer() to draw correct player graphic for this case */
13078     if (player->MovPos == 0)
13079       CheckGravityMovement(player);
13080   }
13081 #else
13082   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13083   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13084
13085   /* before DrawPlayer() to draw correct player graphic for this case */
13086   if (player->MovPos == 0)
13087     CheckGravityMovement(player);
13088 #endif
13089
13090   if (player->MovPos == 0)      /* player reached destination field */
13091   {
13092     if (player->move_delay_reset_counter > 0)
13093     {
13094       player->move_delay_reset_counter--;
13095
13096       if (player->move_delay_reset_counter == 0)
13097       {
13098         /* continue with normal speed after quickly moving through gate */
13099         HALVE_PLAYER_SPEED(player);
13100
13101         /* be able to make the next move without delay */
13102         player->move_delay = 0;
13103       }
13104     }
13105
13106     player->last_jx = jx;
13107     player->last_jy = jy;
13108
13109     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13110         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13111         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13112         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13113         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13114         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13115     {
13116       DrawPlayer(player);       /* needed here only to cleanup last field */
13117       RemovePlayer(player);
13118
13119       if (local_player->friends_still_needed == 0 ||
13120           IS_SP_ELEMENT(Feld[jx][jy]))
13121         PlayerWins(player);
13122     }
13123
13124     /* this breaks one level: "machine", level 000 */
13125     {
13126       int move_direction = player->MovDir;
13127       int enter_side = MV_DIR_OPPOSITE(move_direction);
13128       int leave_side = move_direction;
13129       int old_jx = last_jx;
13130       int old_jy = last_jy;
13131       int old_element = Feld[old_jx][old_jy];
13132       int new_element = Feld[jx][jy];
13133
13134       if (IS_CUSTOM_ELEMENT(old_element))
13135         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13136                                    CE_LEFT_BY_PLAYER,
13137                                    player->index_bit, leave_side);
13138
13139       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13140                                           CE_PLAYER_LEAVES_X,
13141                                           player->index_bit, leave_side);
13142
13143       if (IS_CUSTOM_ELEMENT(new_element))
13144         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13145                                    player->index_bit, enter_side);
13146
13147       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13148                                           CE_PLAYER_ENTERS_X,
13149                                           player->index_bit, enter_side);
13150
13151       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13152                                         CE_MOVE_OF_X, move_direction);
13153     }
13154
13155     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13156     {
13157       TestIfPlayerTouchesBadThing(jx, jy);
13158       TestIfPlayerTouchesCustomElement(jx, jy);
13159
13160       /* needed because pushed element has not yet reached its destination,
13161          so it would trigger a change event at its previous field location */
13162       if (!player->is_pushing)
13163         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13164
13165       if (!player->active)
13166         RemovePlayer(player);
13167     }
13168
13169     if (!local_player->LevelSolved && level.use_step_counter)
13170     {
13171       int i;
13172
13173       TimePlayed++;
13174
13175       if (TimeLeft > 0)
13176       {
13177         TimeLeft--;
13178
13179         if (TimeLeft <= 10 && setup.time_limit)
13180           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13181
13182 #if 1
13183         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13184
13185         DisplayGameControlValues();
13186 #else
13187         DrawGameValue_Time(TimeLeft);
13188 #endif
13189
13190         if (!TimeLeft && setup.time_limit)
13191           for (i = 0; i < MAX_PLAYERS; i++)
13192             KillPlayer(&stored_player[i]);
13193       }
13194 #if 1
13195       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13196       {
13197         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13198
13199         DisplayGameControlValues();
13200       }
13201 #else
13202       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13203         DrawGameValue_Time(TimePlayed);
13204 #endif
13205     }
13206
13207     if (tape.single_step && tape.recording && !tape.pausing &&
13208         !player->programmed_action)
13209       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13210   }
13211 }
13212
13213 void ScrollScreen(struct PlayerInfo *player, int mode)
13214 {
13215   static unsigned long screen_frame_counter = 0;
13216
13217   if (mode == SCROLL_INIT)
13218   {
13219     /* set scrolling step size according to actual player's moving speed */
13220     ScrollStepSize = TILEX / player->move_delay_value;
13221
13222     screen_frame_counter = FrameCounter;
13223     ScreenMovDir = player->MovDir;
13224     ScreenMovPos = player->MovPos;
13225     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13226     return;
13227   }
13228   else if (!FrameReached(&screen_frame_counter, 1))
13229     return;
13230
13231   if (ScreenMovPos)
13232   {
13233     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13234     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13235     redraw_mask |= REDRAW_FIELD;
13236   }
13237   else
13238     ScreenMovDir = MV_NONE;
13239 }
13240
13241 void TestIfPlayerTouchesCustomElement(int x, int y)
13242 {
13243   static int xy[4][2] =
13244   {
13245     { 0, -1 },
13246     { -1, 0 },
13247     { +1, 0 },
13248     { 0, +1 }
13249   };
13250   static int trigger_sides[4][2] =
13251   {
13252     /* center side       border side */
13253     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13254     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13255     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13256     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13257   };
13258   static int touch_dir[4] =
13259   {
13260     MV_LEFT | MV_RIGHT,
13261     MV_UP   | MV_DOWN,
13262     MV_UP   | MV_DOWN,
13263     MV_LEFT | MV_RIGHT
13264   };
13265   int center_element = Feld[x][y];      /* should always be non-moving! */
13266   int i;
13267
13268   for (i = 0; i < NUM_DIRECTIONS; i++)
13269   {
13270     int xx = x + xy[i][0];
13271     int yy = y + xy[i][1];
13272     int center_side = trigger_sides[i][0];
13273     int border_side = trigger_sides[i][1];
13274     int border_element;
13275
13276     if (!IN_LEV_FIELD(xx, yy))
13277       continue;
13278
13279     if (IS_PLAYER(x, y))
13280     {
13281       struct PlayerInfo *player = PLAYERINFO(x, y);
13282
13283       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13284         border_element = Feld[xx][yy];          /* may be moving! */
13285       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13286         border_element = Feld[xx][yy];
13287       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13288         border_element = MovingOrBlocked2Element(xx, yy);
13289       else
13290         continue;               /* center and border element do not touch */
13291
13292       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13293                                  player->index_bit, border_side);
13294       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13295                                           CE_PLAYER_TOUCHES_X,
13296                                           player->index_bit, border_side);
13297     }
13298     else if (IS_PLAYER(xx, yy))
13299     {
13300       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13301
13302       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13303       {
13304         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13305           continue;             /* center and border element do not touch */
13306       }
13307
13308       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13309                                  player->index_bit, center_side);
13310       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13311                                           CE_PLAYER_TOUCHES_X,
13312                                           player->index_bit, center_side);
13313       break;
13314     }
13315   }
13316 }
13317
13318 #if USE_ELEMENT_TOUCHING_BUGFIX
13319
13320 void TestIfElementTouchesCustomElement(int x, int y)
13321 {
13322   static int xy[4][2] =
13323   {
13324     { 0, -1 },
13325     { -1, 0 },
13326     { +1, 0 },
13327     { 0, +1 }
13328   };
13329   static int trigger_sides[4][2] =
13330   {
13331     /* center side      border side */
13332     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13333     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13334     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13335     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13336   };
13337   static int touch_dir[4] =
13338   {
13339     MV_LEFT | MV_RIGHT,
13340     MV_UP   | MV_DOWN,
13341     MV_UP   | MV_DOWN,
13342     MV_LEFT | MV_RIGHT
13343   };
13344   boolean change_center_element = FALSE;
13345   int center_element = Feld[x][y];      /* should always be non-moving! */
13346   int border_element_old[NUM_DIRECTIONS];
13347   int i;
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 border_element;
13354
13355     border_element_old[i] = -1;
13356
13357     if (!IN_LEV_FIELD(xx, yy))
13358       continue;
13359
13360     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13361       border_element = Feld[xx][yy];    /* may be moving! */
13362     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13363       border_element = Feld[xx][yy];
13364     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13365       border_element = MovingOrBlocked2Element(xx, yy);
13366     else
13367       continue;                 /* center and border element do not touch */
13368
13369     border_element_old[i] = border_element;
13370   }
13371
13372   for (i = 0; i < NUM_DIRECTIONS; i++)
13373   {
13374     int xx = x + xy[i][0];
13375     int yy = y + xy[i][1];
13376     int center_side = trigger_sides[i][0];
13377     int border_element = border_element_old[i];
13378
13379     if (border_element == -1)
13380       continue;
13381
13382     /* check for change of border element */
13383     CheckElementChangeBySide(xx, yy, border_element, center_element,
13384                              CE_TOUCHING_X, center_side);
13385   }
13386
13387   for (i = 0; i < NUM_DIRECTIONS; i++)
13388   {
13389     int border_side = trigger_sides[i][1];
13390     int border_element = border_element_old[i];
13391
13392     if (border_element == -1)
13393       continue;
13394
13395     /* check for change of center element (but change it only once) */
13396     if (!change_center_element)
13397       change_center_element =
13398         CheckElementChangeBySide(x, y, center_element, border_element,
13399                                  CE_TOUCHING_X, border_side);
13400   }
13401 }
13402
13403 #else
13404
13405 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13406 {
13407   static int xy[4][2] =
13408   {
13409     { 0, -1 },
13410     { -1, 0 },
13411     { +1, 0 },
13412     { 0, +1 }
13413   };
13414   static int trigger_sides[4][2] =
13415   {
13416     /* center side      border side */
13417     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13418     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13419     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13420     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13421   };
13422   static int touch_dir[4] =
13423   {
13424     MV_LEFT | MV_RIGHT,
13425     MV_UP   | MV_DOWN,
13426     MV_UP   | MV_DOWN,
13427     MV_LEFT | MV_RIGHT
13428   };
13429   boolean change_center_element = FALSE;
13430   int center_element = Feld[x][y];      /* should always be non-moving! */
13431   int i;
13432
13433   for (i = 0; i < NUM_DIRECTIONS; i++)
13434   {
13435     int xx = x + xy[i][0];
13436     int yy = y + xy[i][1];
13437     int center_side = trigger_sides[i][0];
13438     int border_side = trigger_sides[i][1];
13439     int border_element;
13440
13441     if (!IN_LEV_FIELD(xx, yy))
13442       continue;
13443
13444     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13445       border_element = Feld[xx][yy];    /* may be moving! */
13446     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13447       border_element = Feld[xx][yy];
13448     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13449       border_element = MovingOrBlocked2Element(xx, yy);
13450     else
13451       continue;                 /* center and border element do not touch */
13452
13453     /* check for change of center element (but change it only once) */
13454     if (!change_center_element)
13455       change_center_element =
13456         CheckElementChangeBySide(x, y, center_element, border_element,
13457                                  CE_TOUCHING_X, border_side);
13458
13459     /* check for change of border element */
13460     CheckElementChangeBySide(xx, yy, border_element, center_element,
13461                              CE_TOUCHING_X, center_side);
13462   }
13463 }
13464
13465 #endif
13466
13467 void TestIfElementHitsCustomElement(int x, int y, int direction)
13468 {
13469   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13470   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13471   int hitx = x + dx, hity = y + dy;
13472   int hitting_element = Feld[x][y];
13473   int touched_element;
13474
13475   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13476     return;
13477
13478   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13479                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13480
13481   if (IN_LEV_FIELD(hitx, hity))
13482   {
13483     int opposite_direction = MV_DIR_OPPOSITE(direction);
13484     int hitting_side = direction;
13485     int touched_side = opposite_direction;
13486     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13487                           MovDir[hitx][hity] != direction ||
13488                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13489
13490     object_hit = TRUE;
13491
13492     if (object_hit)
13493     {
13494       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13495                                CE_HITTING_X, touched_side);
13496
13497       CheckElementChangeBySide(hitx, hity, touched_element,
13498                                hitting_element, CE_HIT_BY_X, hitting_side);
13499
13500       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13501                                CE_HIT_BY_SOMETHING, opposite_direction);
13502     }
13503   }
13504
13505   /* "hitting something" is also true when hitting the playfield border */
13506   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13507                            CE_HITTING_SOMETHING, direction);
13508 }
13509
13510 #if 0
13511 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13512 {
13513   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13514   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13515   int hitx = x + dx, hity = y + dy;
13516   int hitting_element = Feld[x][y];
13517   int touched_element;
13518 #if 0
13519   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13520                         !IS_FREE(hitx, hity) &&
13521                         (!IS_MOVING(hitx, hity) ||
13522                          MovDir[hitx][hity] != direction ||
13523                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
13524 #endif
13525
13526   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13527     return;
13528
13529 #if 0
13530   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13531     return;
13532 #endif
13533
13534   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13535                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13536
13537   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13538                            EP_CAN_SMASH_EVERYTHING, direction);
13539
13540   if (IN_LEV_FIELD(hitx, hity))
13541   {
13542     int opposite_direction = MV_DIR_OPPOSITE(direction);
13543     int hitting_side = direction;
13544     int touched_side = opposite_direction;
13545 #if 0
13546     int touched_element = MovingOrBlocked2Element(hitx, hity);
13547 #endif
13548 #if 1
13549     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13550                           MovDir[hitx][hity] != direction ||
13551                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13552
13553     object_hit = TRUE;
13554 #endif
13555
13556     if (object_hit)
13557     {
13558       int i;
13559
13560       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13561                                CE_SMASHED_BY_SOMETHING, opposite_direction);
13562
13563       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13564                                CE_OTHER_IS_SMASHING, touched_side);
13565
13566       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13567                                CE_OTHER_GETS_SMASHED, hitting_side);
13568     }
13569   }
13570 }
13571 #endif
13572
13573 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13574 {
13575   int i, kill_x = -1, kill_y = -1;
13576
13577   int bad_element = -1;
13578   static int test_xy[4][2] =
13579   {
13580     { 0, -1 },
13581     { -1, 0 },
13582     { +1, 0 },
13583     { 0, +1 }
13584   };
13585   static int test_dir[4] =
13586   {
13587     MV_UP,
13588     MV_LEFT,
13589     MV_RIGHT,
13590     MV_DOWN
13591   };
13592
13593   for (i = 0; i < NUM_DIRECTIONS; i++)
13594   {
13595     int test_x, test_y, test_move_dir, test_element;
13596
13597     test_x = good_x + test_xy[i][0];
13598     test_y = good_y + test_xy[i][1];
13599
13600     if (!IN_LEV_FIELD(test_x, test_y))
13601       continue;
13602
13603     test_move_dir =
13604       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13605
13606     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13607
13608     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13609        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13610     */
13611     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13612         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13613     {
13614       kill_x = test_x;
13615       kill_y = test_y;
13616       bad_element = test_element;
13617
13618       break;
13619     }
13620   }
13621
13622   if (kill_x != -1 || kill_y != -1)
13623   {
13624     if (IS_PLAYER(good_x, good_y))
13625     {
13626       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13627
13628       if (player->shield_deadly_time_left > 0 &&
13629           !IS_INDESTRUCTIBLE(bad_element))
13630         Bang(kill_x, kill_y);
13631       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13632         KillPlayer(player);
13633     }
13634     else
13635       Bang(good_x, good_y);
13636   }
13637 }
13638
13639 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13640 {
13641   int i, kill_x = -1, kill_y = -1;
13642   int bad_element = Feld[bad_x][bad_y];
13643   static int test_xy[4][2] =
13644   {
13645     { 0, -1 },
13646     { -1, 0 },
13647     { +1, 0 },
13648     { 0, +1 }
13649   };
13650   static int touch_dir[4] =
13651   {
13652     MV_LEFT | MV_RIGHT,
13653     MV_UP   | MV_DOWN,
13654     MV_UP   | MV_DOWN,
13655     MV_LEFT | MV_RIGHT
13656   };
13657   static int test_dir[4] =
13658   {
13659     MV_UP,
13660     MV_LEFT,
13661     MV_RIGHT,
13662     MV_DOWN
13663   };
13664
13665   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13666     return;
13667
13668   for (i = 0; i < NUM_DIRECTIONS; i++)
13669   {
13670     int test_x, test_y, test_move_dir, test_element;
13671
13672     test_x = bad_x + test_xy[i][0];
13673     test_y = bad_y + test_xy[i][1];
13674     if (!IN_LEV_FIELD(test_x, test_y))
13675       continue;
13676
13677     test_move_dir =
13678       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13679
13680     test_element = Feld[test_x][test_y];
13681
13682     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13683        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13684     */
13685     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13686         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13687     {
13688       /* good thing is player or penguin that does not move away */
13689       if (IS_PLAYER(test_x, test_y))
13690       {
13691         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13692
13693         if (bad_element == EL_ROBOT && player->is_moving)
13694           continue;     /* robot does not kill player if he is moving */
13695
13696         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13697         {
13698           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13699             continue;           /* center and border element do not touch */
13700         }
13701
13702         kill_x = test_x;
13703         kill_y = test_y;
13704         break;
13705       }
13706       else if (test_element == EL_PENGUIN)
13707       {
13708         kill_x = test_x;
13709         kill_y = test_y;
13710         break;
13711       }
13712     }
13713   }
13714
13715   if (kill_x != -1 || kill_y != -1)
13716   {
13717     if (IS_PLAYER(kill_x, kill_y))
13718     {
13719       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13720
13721       if (player->shield_deadly_time_left > 0 &&
13722           !IS_INDESTRUCTIBLE(bad_element))
13723         Bang(bad_x, bad_y);
13724       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13725         KillPlayer(player);
13726     }
13727     else
13728       Bang(kill_x, kill_y);
13729   }
13730 }
13731
13732 void TestIfPlayerTouchesBadThing(int x, int y)
13733 {
13734   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13735 }
13736
13737 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13738 {
13739   TestIfGoodThingHitsBadThing(x, y, move_dir);
13740 }
13741
13742 void TestIfBadThingTouchesPlayer(int x, int y)
13743 {
13744   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13745 }
13746
13747 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13748 {
13749   TestIfBadThingHitsGoodThing(x, y, move_dir);
13750 }
13751
13752 void TestIfFriendTouchesBadThing(int x, int y)
13753 {
13754   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13755 }
13756
13757 void TestIfBadThingTouchesFriend(int x, int y)
13758 {
13759   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13760 }
13761
13762 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13763 {
13764   int i, kill_x = bad_x, kill_y = bad_y;
13765   static int xy[4][2] =
13766   {
13767     { 0, -1 },
13768     { -1, 0 },
13769     { +1, 0 },
13770     { 0, +1 }
13771   };
13772
13773   for (i = 0; i < NUM_DIRECTIONS; i++)
13774   {
13775     int x, y, element;
13776
13777     x = bad_x + xy[i][0];
13778     y = bad_y + xy[i][1];
13779     if (!IN_LEV_FIELD(x, y))
13780       continue;
13781
13782     element = Feld[x][y];
13783     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13784         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13785     {
13786       kill_x = x;
13787       kill_y = y;
13788       break;
13789     }
13790   }
13791
13792   if (kill_x != bad_x || kill_y != bad_y)
13793     Bang(bad_x, bad_y);
13794 }
13795
13796 void KillPlayer(struct PlayerInfo *player)
13797 {
13798   int jx = player->jx, jy = player->jy;
13799
13800   if (!player->active)
13801     return;
13802
13803   /* the following code was introduced to prevent an infinite loop when calling
13804      -> Bang()
13805      -> CheckTriggeredElementChangeExt()
13806      -> ExecuteCustomElementAction()
13807      -> KillPlayer()
13808      -> (infinitely repeating the above sequence of function calls)
13809      which occurs when killing the player while having a CE with the setting
13810      "kill player X when explosion of <player X>"; the solution using a new
13811      field "player->killed" was chosen for backwards compatibility, although
13812      clever use of the fields "player->active" etc. would probably also work */
13813 #if 1
13814   if (player->killed)
13815     return;
13816 #endif
13817
13818   player->killed = TRUE;
13819
13820   /* remove accessible field at the player's position */
13821   Feld[jx][jy] = EL_EMPTY;
13822
13823   /* deactivate shield (else Bang()/Explode() would not work right) */
13824   player->shield_normal_time_left = 0;
13825   player->shield_deadly_time_left = 0;
13826
13827   Bang(jx, jy);
13828   BuryPlayer(player);
13829 }
13830
13831 static void KillPlayerUnlessEnemyProtected(int x, int y)
13832 {
13833   if (!PLAYER_ENEMY_PROTECTED(x, y))
13834     KillPlayer(PLAYERINFO(x, y));
13835 }
13836
13837 static void KillPlayerUnlessExplosionProtected(int x, int y)
13838 {
13839   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13840     KillPlayer(PLAYERINFO(x, y));
13841 }
13842
13843 void BuryPlayer(struct PlayerInfo *player)
13844 {
13845   int jx = player->jx, jy = player->jy;
13846
13847   if (!player->active)
13848     return;
13849
13850   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13851   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13852
13853   player->GameOver = TRUE;
13854   RemovePlayer(player);
13855 }
13856
13857 void RemovePlayer(struct PlayerInfo *player)
13858 {
13859   int jx = player->jx, jy = player->jy;
13860   int i, found = FALSE;
13861
13862   player->present = FALSE;
13863   player->active = FALSE;
13864
13865   if (!ExplodeField[jx][jy])
13866     StorePlayer[jx][jy] = 0;
13867
13868   if (player->is_moving)
13869     DrawLevelField(player->last_jx, player->last_jy);
13870
13871   for (i = 0; i < MAX_PLAYERS; i++)
13872     if (stored_player[i].active)
13873       found = TRUE;
13874
13875   if (!found)
13876     AllPlayersGone = TRUE;
13877
13878   ExitX = ZX = jx;
13879   ExitY = ZY = jy;
13880 }
13881
13882 #if USE_NEW_SNAP_DELAY
13883 static void setFieldForSnapping(int x, int y, int element, int direction)
13884 {
13885   struct ElementInfo *ei = &element_info[element];
13886   int direction_bit = MV_DIR_TO_BIT(direction);
13887   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13888   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13889                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13890
13891   Feld[x][y] = EL_ELEMENT_SNAPPING;
13892   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13893
13894   ResetGfxAnimation(x, y);
13895
13896   GfxElement[x][y] = element;
13897   GfxAction[x][y] = action;
13898   GfxDir[x][y] = direction;
13899   GfxFrame[x][y] = -1;
13900 }
13901 #endif
13902
13903 /*
13904   =============================================================================
13905   checkDiagonalPushing()
13906   -----------------------------------------------------------------------------
13907   check if diagonal input device direction results in pushing of object
13908   (by checking if the alternative direction is walkable, diggable, ...)
13909   =============================================================================
13910 */
13911
13912 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13913                                     int x, int y, int real_dx, int real_dy)
13914 {
13915   int jx, jy, dx, dy, xx, yy;
13916
13917   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13918     return TRUE;
13919
13920   /* diagonal direction: check alternative direction */
13921   jx = player->jx;
13922   jy = player->jy;
13923   dx = x - jx;
13924   dy = y - jy;
13925   xx = jx + (dx == 0 ? real_dx : 0);
13926   yy = jy + (dy == 0 ? real_dy : 0);
13927
13928   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13929 }
13930
13931 /*
13932   =============================================================================
13933   DigField()
13934   -----------------------------------------------------------------------------
13935   x, y:                 field next to player (non-diagonal) to try to dig to
13936   real_dx, real_dy:     direction as read from input device (can be diagonal)
13937   =============================================================================
13938 */
13939
13940 int DigField(struct PlayerInfo *player,
13941              int oldx, int oldy, int x, int y,
13942              int real_dx, int real_dy, int mode)
13943 {
13944   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13945   boolean player_was_pushing = player->is_pushing;
13946   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13947   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13948   int jx = oldx, jy = oldy;
13949   int dx = x - jx, dy = y - jy;
13950   int nextx = x + dx, nexty = y + dy;
13951   int move_direction = (dx == -1 ? MV_LEFT  :
13952                         dx == +1 ? MV_RIGHT :
13953                         dy == -1 ? MV_UP    :
13954                         dy == +1 ? MV_DOWN  : MV_NONE);
13955   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13956   int dig_side = MV_DIR_OPPOSITE(move_direction);
13957   int old_element = Feld[jx][jy];
13958 #if USE_FIXED_DONT_RUN_INTO
13959   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13960 #else
13961   int element;
13962 #endif
13963   int collect_count;
13964
13965   if (is_player)                /* function can also be called by EL_PENGUIN */
13966   {
13967     if (player->MovPos == 0)
13968     {
13969       player->is_digging = FALSE;
13970       player->is_collecting = FALSE;
13971     }
13972
13973     if (player->MovPos == 0)    /* last pushing move finished */
13974       player->is_pushing = FALSE;
13975
13976     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13977     {
13978       player->is_switching = FALSE;
13979       player->push_delay = -1;
13980
13981       return MP_NO_ACTION;
13982     }
13983   }
13984
13985 #if !USE_FIXED_DONT_RUN_INTO
13986   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13987     return MP_NO_ACTION;
13988 #endif
13989
13990   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13991     old_element = Back[jx][jy];
13992
13993   /* in case of element dropped at player position, check background */
13994   else if (Back[jx][jy] != EL_EMPTY &&
13995            game.engine_version >= VERSION_IDENT(2,2,0,0))
13996     old_element = Back[jx][jy];
13997
13998   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13999     return MP_NO_ACTION;        /* field has no opening in this direction */
14000
14001   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14002     return MP_NO_ACTION;        /* field has no opening in this direction */
14003
14004 #if USE_FIXED_DONT_RUN_INTO
14005   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14006   {
14007     SplashAcid(x, y);
14008
14009     Feld[jx][jy] = player->artwork_element;
14010     InitMovingField(jx, jy, MV_DOWN);
14011     Store[jx][jy] = EL_ACID;
14012     ContinueMoving(jx, jy);
14013     BuryPlayer(player);
14014
14015     return MP_DONT_RUN_INTO;
14016   }
14017 #endif
14018
14019 #if USE_FIXED_DONT_RUN_INTO
14020   if (player_can_move && DONT_RUN_INTO(element))
14021   {
14022     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14023
14024     return MP_DONT_RUN_INTO;
14025   }
14026 #endif
14027
14028 #if USE_FIXED_DONT_RUN_INTO
14029   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14030     return MP_NO_ACTION;
14031 #endif
14032
14033 #if !USE_FIXED_DONT_RUN_INTO
14034   element = Feld[x][y];
14035 #endif
14036
14037   collect_count = element_info[element].collect_count_initial;
14038
14039   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14040     return MP_NO_ACTION;
14041
14042   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14043     player_can_move = player_can_move_or_snap;
14044
14045   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14046       game.engine_version >= VERSION_IDENT(2,2,0,0))
14047   {
14048     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14049                                player->index_bit, dig_side);
14050     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14051                                         player->index_bit, dig_side);
14052
14053     if (element == EL_DC_LANDMINE)
14054       Bang(x, y);
14055
14056     if (Feld[x][y] != element)          /* field changed by snapping */
14057       return MP_ACTION;
14058
14059     return MP_NO_ACTION;
14060   }
14061
14062 #if USE_PLAYER_GRAVITY
14063   if (player->gravity && is_player && !player->is_auto_moving &&
14064       canFallDown(player) && move_direction != MV_DOWN &&
14065       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14066     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14067 #else
14068   if (game.gravity && is_player && !player->is_auto_moving &&
14069       canFallDown(player) && move_direction != MV_DOWN &&
14070       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14071     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14072 #endif
14073
14074   if (player_can_move &&
14075       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14076   {
14077     int sound_element = SND_ELEMENT(element);
14078     int sound_action = ACTION_WALKING;
14079
14080     if (IS_RND_GATE(element))
14081     {
14082       if (!player->key[RND_GATE_NR(element)])
14083         return MP_NO_ACTION;
14084     }
14085     else if (IS_RND_GATE_GRAY(element))
14086     {
14087       if (!player->key[RND_GATE_GRAY_NR(element)])
14088         return MP_NO_ACTION;
14089     }
14090     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14091     {
14092       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14093         return MP_NO_ACTION;
14094     }
14095     else if (element == EL_EXIT_OPEN ||
14096              element == EL_EM_EXIT_OPEN ||
14097              element == EL_STEEL_EXIT_OPEN ||
14098              element == EL_EM_STEEL_EXIT_OPEN ||
14099              element == EL_SP_EXIT_OPEN ||
14100              element == EL_SP_EXIT_OPENING)
14101     {
14102       sound_action = ACTION_PASSING;    /* player is passing exit */
14103     }
14104     else if (element == EL_EMPTY)
14105     {
14106       sound_action = ACTION_MOVING;             /* nothing to walk on */
14107     }
14108
14109     /* play sound from background or player, whatever is available */
14110     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14111       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14112     else
14113       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14114   }
14115   else if (player_can_move &&
14116            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14117   {
14118     if (!ACCESS_FROM(element, opposite_direction))
14119       return MP_NO_ACTION;      /* field not accessible from this direction */
14120
14121     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
14122       return MP_NO_ACTION;
14123
14124     if (IS_EM_GATE(element))
14125     {
14126       if (!player->key[EM_GATE_NR(element)])
14127         return MP_NO_ACTION;
14128     }
14129     else if (IS_EM_GATE_GRAY(element))
14130     {
14131       if (!player->key[EM_GATE_GRAY_NR(element)])
14132         return MP_NO_ACTION;
14133     }
14134     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14135     {
14136       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14137         return MP_NO_ACTION;
14138     }
14139     else if (IS_EMC_GATE(element))
14140     {
14141       if (!player->key[EMC_GATE_NR(element)])
14142         return MP_NO_ACTION;
14143     }
14144     else if (IS_EMC_GATE_GRAY(element))
14145     {
14146       if (!player->key[EMC_GATE_GRAY_NR(element)])
14147         return MP_NO_ACTION;
14148     }
14149     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14150     {
14151       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14152         return MP_NO_ACTION;
14153     }
14154     else if (element == EL_DC_GATE_WHITE ||
14155              element == EL_DC_GATE_WHITE_GRAY ||
14156              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14157     {
14158       if (player->num_white_keys == 0)
14159         return MP_NO_ACTION;
14160
14161       player->num_white_keys--;
14162     }
14163     else if (IS_SP_PORT(element))
14164     {
14165       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14166           element == EL_SP_GRAVITY_PORT_RIGHT ||
14167           element == EL_SP_GRAVITY_PORT_UP ||
14168           element == EL_SP_GRAVITY_PORT_DOWN)
14169 #if USE_PLAYER_GRAVITY
14170         player->gravity = !player->gravity;
14171 #else
14172         game.gravity = !game.gravity;
14173 #endif
14174       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14175                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14176                element == EL_SP_GRAVITY_ON_PORT_UP ||
14177                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14178 #if USE_PLAYER_GRAVITY
14179         player->gravity = TRUE;
14180 #else
14181         game.gravity = TRUE;
14182 #endif
14183       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14184                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14185                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14186                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14187 #if USE_PLAYER_GRAVITY
14188         player->gravity = FALSE;
14189 #else
14190         game.gravity = FALSE;
14191 #endif
14192     }
14193
14194     /* automatically move to the next field with double speed */
14195     player->programmed_action = move_direction;
14196
14197     if (player->move_delay_reset_counter == 0)
14198     {
14199       player->move_delay_reset_counter = 2;     /* two double speed steps */
14200
14201       DOUBLE_PLAYER_SPEED(player);
14202     }
14203
14204     PlayLevelSoundAction(x, y, ACTION_PASSING);
14205   }
14206   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14207   {
14208     RemoveField(x, y);
14209
14210     if (mode != DF_SNAP)
14211     {
14212       GfxElement[x][y] = GFX_ELEMENT(element);
14213       player->is_digging = TRUE;
14214     }
14215
14216     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14217
14218     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14219                                         player->index_bit, dig_side);
14220
14221     if (mode == DF_SNAP)
14222     {
14223 #if USE_NEW_SNAP_DELAY
14224       if (level.block_snap_field)
14225         setFieldForSnapping(x, y, element, move_direction);
14226       else
14227         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14228 #else
14229       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14230 #endif
14231
14232       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14233                                           player->index_bit, dig_side);
14234     }
14235   }
14236   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14237   {
14238     RemoveField(x, y);
14239
14240     if (is_player && mode != DF_SNAP)
14241     {
14242       GfxElement[x][y] = element;
14243       player->is_collecting = TRUE;
14244     }
14245
14246     if (element == EL_SPEED_PILL)
14247     {
14248       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14249     }
14250     else if (element == EL_EXTRA_TIME && level.time > 0)
14251     {
14252       TimeLeft += level.extra_time;
14253
14254 #if 1
14255       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14256
14257       DisplayGameControlValues();
14258 #else
14259       DrawGameValue_Time(TimeLeft);
14260 #endif
14261     }
14262     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14263     {
14264       player->shield_normal_time_left += level.shield_normal_time;
14265       if (element == EL_SHIELD_DEADLY)
14266         player->shield_deadly_time_left += level.shield_deadly_time;
14267     }
14268     else if (element == EL_DYNAMITE ||
14269              element == EL_EM_DYNAMITE ||
14270              element == EL_SP_DISK_RED)
14271     {
14272       if (player->inventory_size < MAX_INVENTORY_SIZE)
14273         player->inventory_element[player->inventory_size++] = element;
14274
14275       DrawGameDoorValues();
14276     }
14277     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14278     {
14279       player->dynabomb_count++;
14280       player->dynabombs_left++;
14281     }
14282     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14283     {
14284       player->dynabomb_size++;
14285     }
14286     else if (element == EL_DYNABOMB_INCREASE_POWER)
14287     {
14288       player->dynabomb_xl = TRUE;
14289     }
14290     else if (IS_KEY(element))
14291     {
14292       player->key[KEY_NR(element)] = TRUE;
14293
14294       DrawGameDoorValues();
14295     }
14296     else if (element == EL_DC_KEY_WHITE)
14297     {
14298       player->num_white_keys++;
14299
14300       /* display white keys? */
14301       /* DrawGameDoorValues(); */
14302     }
14303     else if (IS_ENVELOPE(element))
14304     {
14305       player->show_envelope = element;
14306     }
14307     else if (element == EL_EMC_LENSES)
14308     {
14309       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14310
14311       RedrawAllInvisibleElementsForLenses();
14312     }
14313     else if (element == EL_EMC_MAGNIFIER)
14314     {
14315       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14316
14317       RedrawAllInvisibleElementsForMagnifier();
14318     }
14319     else if (IS_DROPPABLE(element) ||
14320              IS_THROWABLE(element))     /* can be collected and dropped */
14321     {
14322       int i;
14323
14324       if (collect_count == 0)
14325         player->inventory_infinite_element = element;
14326       else
14327         for (i = 0; i < collect_count; i++)
14328           if (player->inventory_size < MAX_INVENTORY_SIZE)
14329             player->inventory_element[player->inventory_size++] = element;
14330
14331       DrawGameDoorValues();
14332     }
14333     else if (collect_count > 0)
14334     {
14335       local_player->gems_still_needed -= collect_count;
14336       if (local_player->gems_still_needed < 0)
14337         local_player->gems_still_needed = 0;
14338
14339 #if 1
14340       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14341
14342       DisplayGameControlValues();
14343 #else
14344       DrawGameValue_Emeralds(local_player->gems_still_needed);
14345 #endif
14346     }
14347
14348     RaiseScoreElement(element);
14349     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14350
14351     if (is_player)
14352       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14353                                           player->index_bit, dig_side);
14354
14355     if (mode == DF_SNAP)
14356     {
14357 #if USE_NEW_SNAP_DELAY
14358       if (level.block_snap_field)
14359         setFieldForSnapping(x, y, element, move_direction);
14360       else
14361         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14362 #else
14363       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14364 #endif
14365
14366       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14367                                           player->index_bit, dig_side);
14368     }
14369   }
14370   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14371   {
14372     if (mode == DF_SNAP && element != EL_BD_ROCK)
14373       return MP_NO_ACTION;
14374
14375     if (CAN_FALL(element) && dy)
14376       return MP_NO_ACTION;
14377
14378     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14379         !(element == EL_SPRING && level.use_spring_bug))
14380       return MP_NO_ACTION;
14381
14382     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14383         ((move_direction & MV_VERTICAL &&
14384           ((element_info[element].move_pattern & MV_LEFT &&
14385             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14386            (element_info[element].move_pattern & MV_RIGHT &&
14387             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14388          (move_direction & MV_HORIZONTAL &&
14389           ((element_info[element].move_pattern & MV_UP &&
14390             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14391            (element_info[element].move_pattern & MV_DOWN &&
14392             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14393       return MP_NO_ACTION;
14394
14395     /* do not push elements already moving away faster than player */
14396     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14397         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14398       return MP_NO_ACTION;
14399
14400     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14401     {
14402       if (player->push_delay_value == -1 || !player_was_pushing)
14403         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14404     }
14405     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14406     {
14407       if (player->push_delay_value == -1)
14408         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14409     }
14410     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14411     {
14412       if (!player->is_pushing)
14413         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14414     }
14415
14416     player->is_pushing = TRUE;
14417     player->is_active = TRUE;
14418
14419     if (!(IN_LEV_FIELD(nextx, nexty) &&
14420           (IS_FREE(nextx, nexty) ||
14421            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
14422             IS_SB_ELEMENT(element)))))
14423       return MP_NO_ACTION;
14424
14425     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14426       return MP_NO_ACTION;
14427
14428     if (player->push_delay == -1)       /* new pushing; restart delay */
14429       player->push_delay = 0;
14430
14431     if (player->push_delay < player->push_delay_value &&
14432         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14433         element != EL_SPRING && element != EL_BALLOON)
14434     {
14435       /* make sure that there is no move delay before next try to push */
14436       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14437         player->move_delay = 0;
14438
14439       return MP_NO_ACTION;
14440     }
14441
14442     if (IS_SB_ELEMENT(element))
14443     {
14444       if (element == EL_SOKOBAN_FIELD_FULL)
14445       {
14446         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14447         local_player->sokobanfields_still_needed++;
14448       }
14449
14450       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14451       {
14452         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14453         local_player->sokobanfields_still_needed--;
14454       }
14455
14456       Feld[x][y] = EL_SOKOBAN_OBJECT;
14457
14458       if (Back[x][y] == Back[nextx][nexty])
14459         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14460       else if (Back[x][y] != 0)
14461         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14462                                     ACTION_EMPTYING);
14463       else
14464         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14465                                     ACTION_FILLING);
14466
14467       if (local_player->sokobanfields_still_needed == 0 &&
14468           game.emulation == EMU_SOKOBAN)
14469       {
14470         PlayerWins(player);
14471
14472         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14473       }
14474     }
14475     else
14476       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14477
14478     InitMovingField(x, y, move_direction);
14479     GfxAction[x][y] = ACTION_PUSHING;
14480
14481     if (mode == DF_SNAP)
14482       ContinueMoving(x, y);
14483     else
14484       MovPos[x][y] = (dx != 0 ? dx : dy);
14485
14486     Pushed[x][y] = TRUE;
14487     Pushed[nextx][nexty] = TRUE;
14488
14489     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14490       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14491     else
14492       player->push_delay_value = -1;    /* get new value later */
14493
14494     /* check for element change _after_ element has been pushed */
14495     if (game.use_change_when_pushing_bug)
14496     {
14497       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14498                                  player->index_bit, dig_side);
14499       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14500                                           player->index_bit, dig_side);
14501     }
14502   }
14503   else if (IS_SWITCHABLE(element))
14504   {
14505     if (PLAYER_SWITCHING(player, x, y))
14506     {
14507       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14508                                           player->index_bit, dig_side);
14509
14510       return MP_ACTION;
14511     }
14512
14513     player->is_switching = TRUE;
14514     player->switch_x = x;
14515     player->switch_y = y;
14516
14517     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14518
14519     if (element == EL_ROBOT_WHEEL)
14520     {
14521       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14522       ZX = x;
14523       ZY = y;
14524
14525       game.robot_wheel_active = TRUE;
14526
14527       DrawLevelField(x, y);
14528     }
14529     else if (element == EL_SP_TERMINAL)
14530     {
14531       int xx, yy;
14532
14533       SCAN_PLAYFIELD(xx, yy)
14534       {
14535         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14536           Bang(xx, yy);
14537         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14538           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14539       }
14540     }
14541     else if (IS_BELT_SWITCH(element))
14542     {
14543       ToggleBeltSwitch(x, y);
14544     }
14545     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14546              element == EL_SWITCHGATE_SWITCH_DOWN ||
14547              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14548              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14549     {
14550       ToggleSwitchgateSwitch(x, y);
14551     }
14552     else if (element == EL_LIGHT_SWITCH ||
14553              element == EL_LIGHT_SWITCH_ACTIVE)
14554     {
14555       ToggleLightSwitch(x, y);
14556     }
14557     else if (element == EL_TIMEGATE_SWITCH ||
14558              element == EL_DC_TIMEGATE_SWITCH)
14559     {
14560       ActivateTimegateSwitch(x, y);
14561     }
14562     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14563              element == EL_BALLOON_SWITCH_RIGHT ||
14564              element == EL_BALLOON_SWITCH_UP    ||
14565              element == EL_BALLOON_SWITCH_DOWN  ||
14566              element == EL_BALLOON_SWITCH_NONE  ||
14567              element == EL_BALLOON_SWITCH_ANY)
14568     {
14569       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14570                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14571                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14572                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14573                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14574                              move_direction);
14575     }
14576     else if (element == EL_LAMP)
14577     {
14578       Feld[x][y] = EL_LAMP_ACTIVE;
14579       local_player->lights_still_needed--;
14580
14581       ResetGfxAnimation(x, y);
14582       DrawLevelField(x, y);
14583     }
14584     else if (element == EL_TIME_ORB_FULL)
14585     {
14586       Feld[x][y] = EL_TIME_ORB_EMPTY;
14587
14588       if (level.time > 0 || level.use_time_orb_bug)
14589       {
14590         TimeLeft += level.time_orb_time;
14591
14592 #if 1
14593         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14594
14595         DisplayGameControlValues();
14596 #else
14597         DrawGameValue_Time(TimeLeft);
14598 #endif
14599       }
14600
14601       ResetGfxAnimation(x, y);
14602       DrawLevelField(x, y);
14603     }
14604     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14605              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14606     {
14607       int xx, yy;
14608
14609       game.ball_state = !game.ball_state;
14610
14611       SCAN_PLAYFIELD(xx, yy)
14612       {
14613         int e = Feld[xx][yy];
14614
14615         if (game.ball_state)
14616         {
14617           if (e == EL_EMC_MAGIC_BALL)
14618             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14619           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14620             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14621         }
14622         else
14623         {
14624           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14625             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14626           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14627             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14628         }
14629       }
14630     }
14631
14632     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14633                                         player->index_bit, dig_side);
14634
14635     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14636                                         player->index_bit, dig_side);
14637
14638     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14639                                         player->index_bit, dig_side);
14640
14641     return MP_ACTION;
14642   }
14643   else
14644   {
14645     if (!PLAYER_SWITCHING(player, x, y))
14646     {
14647       player->is_switching = TRUE;
14648       player->switch_x = x;
14649       player->switch_y = y;
14650
14651       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14652                                  player->index_bit, dig_side);
14653       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14654                                           player->index_bit, dig_side);
14655
14656       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14657                                  player->index_bit, dig_side);
14658       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14659                                           player->index_bit, dig_side);
14660     }
14661
14662     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14663                                player->index_bit, dig_side);
14664     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14665                                         player->index_bit, dig_side);
14666
14667     return MP_NO_ACTION;
14668   }
14669
14670   player->push_delay = -1;
14671
14672   if (is_player)                /* function can also be called by EL_PENGUIN */
14673   {
14674     if (Feld[x][y] != element)          /* really digged/collected something */
14675     {
14676       player->is_collecting = !player->is_digging;
14677       player->is_active = TRUE;
14678     }
14679   }
14680
14681   return MP_MOVING;
14682 }
14683
14684 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14685 {
14686   int jx = player->jx, jy = player->jy;
14687   int x = jx + dx, y = jy + dy;
14688   int snap_direction = (dx == -1 ? MV_LEFT  :
14689                         dx == +1 ? MV_RIGHT :
14690                         dy == -1 ? MV_UP    :
14691                         dy == +1 ? MV_DOWN  : MV_NONE);
14692   boolean can_continue_snapping = (level.continuous_snapping &&
14693                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14694
14695   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14696     return FALSE;
14697
14698   if (!player->active || !IN_LEV_FIELD(x, y))
14699     return FALSE;
14700
14701   if (dx && dy)
14702     return FALSE;
14703
14704   if (!dx && !dy)
14705   {
14706     if (player->MovPos == 0)
14707       player->is_pushing = FALSE;
14708
14709     player->is_snapping = FALSE;
14710
14711     if (player->MovPos == 0)
14712     {
14713       player->is_moving = FALSE;
14714       player->is_digging = FALSE;
14715       player->is_collecting = FALSE;
14716     }
14717
14718     return FALSE;
14719   }
14720
14721 #if USE_NEW_CONTINUOUS_SNAPPING
14722   /* prevent snapping with already pressed snap key when not allowed */
14723   if (player->is_snapping && !can_continue_snapping)
14724     return FALSE;
14725 #else
14726   if (player->is_snapping)
14727     return FALSE;
14728 #endif
14729
14730   player->MovDir = snap_direction;
14731
14732   if (player->MovPos == 0)
14733   {
14734     player->is_moving = FALSE;
14735     player->is_digging = FALSE;
14736     player->is_collecting = FALSE;
14737   }
14738
14739   player->is_dropping = FALSE;
14740   player->is_dropping_pressed = FALSE;
14741   player->drop_pressed_delay = 0;
14742
14743   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14744     return FALSE;
14745
14746   player->is_snapping = TRUE;
14747   player->is_active = TRUE;
14748
14749   if (player->MovPos == 0)
14750   {
14751     player->is_moving = FALSE;
14752     player->is_digging = FALSE;
14753     player->is_collecting = FALSE;
14754   }
14755
14756   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14757     DrawLevelField(player->last_jx, player->last_jy);
14758
14759   DrawLevelField(x, y);
14760
14761   return TRUE;
14762 }
14763
14764 boolean DropElement(struct PlayerInfo *player)
14765 {
14766   int old_element, new_element;
14767   int dropx = player->jx, dropy = player->jy;
14768   int drop_direction = player->MovDir;
14769   int drop_side = drop_direction;
14770 #if 1
14771   int drop_element = get_next_dropped_element(player);
14772 #else
14773   int drop_element = (player->inventory_size > 0 ?
14774                       player->inventory_element[player->inventory_size - 1] :
14775                       player->inventory_infinite_element != EL_UNDEFINED ?
14776                       player->inventory_infinite_element :
14777                       player->dynabombs_left > 0 ?
14778                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14779                       EL_UNDEFINED);
14780 #endif
14781
14782   player->is_dropping_pressed = TRUE;
14783
14784   /* do not drop an element on top of another element; when holding drop key
14785      pressed without moving, dropped element must move away before the next
14786      element can be dropped (this is especially important if the next element
14787      is dynamite, which can be placed on background for historical reasons) */
14788   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14789     return MP_ACTION;
14790
14791   if (IS_THROWABLE(drop_element))
14792   {
14793     dropx += GET_DX_FROM_DIR(drop_direction);
14794     dropy += GET_DY_FROM_DIR(drop_direction);
14795
14796     if (!IN_LEV_FIELD(dropx, dropy))
14797       return FALSE;
14798   }
14799
14800   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14801   new_element = drop_element;           /* default: no change when dropping */
14802
14803   /* check if player is active, not moving and ready to drop */
14804   if (!player->active || player->MovPos || player->drop_delay > 0)
14805     return FALSE;
14806
14807   /* check if player has anything that can be dropped */
14808   if (new_element == EL_UNDEFINED)
14809     return FALSE;
14810
14811   /* check if drop key was pressed long enough for EM style dynamite */
14812   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14813     return FALSE;
14814
14815   /* check if anything can be dropped at the current position */
14816   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14817     return FALSE;
14818
14819   /* collected custom elements can only be dropped on empty fields */
14820   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14821     return FALSE;
14822
14823   if (old_element != EL_EMPTY)
14824     Back[dropx][dropy] = old_element;   /* store old element on this field */
14825
14826   ResetGfxAnimation(dropx, dropy);
14827   ResetRandomAnimationValue(dropx, dropy);
14828
14829   if (player->inventory_size > 0 ||
14830       player->inventory_infinite_element != EL_UNDEFINED)
14831   {
14832     if (player->inventory_size > 0)
14833     {
14834       player->inventory_size--;
14835
14836       DrawGameDoorValues();
14837
14838       if (new_element == EL_DYNAMITE)
14839         new_element = EL_DYNAMITE_ACTIVE;
14840       else if (new_element == EL_EM_DYNAMITE)
14841         new_element = EL_EM_DYNAMITE_ACTIVE;
14842       else if (new_element == EL_SP_DISK_RED)
14843         new_element = EL_SP_DISK_RED_ACTIVE;
14844     }
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     /* needed if previous element just changed to "empty" in the last frame */
14855     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14856
14857     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14858                                player->index_bit, drop_side);
14859     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14860                                         CE_PLAYER_DROPS_X,
14861                                         player->index_bit, drop_side);
14862
14863     TestIfElementTouchesCustomElement(dropx, dropy);
14864   }
14865   else          /* player is dropping a dyna bomb */
14866   {
14867     player->dynabombs_left--;
14868
14869     Feld[dropx][dropy] = new_element;
14870
14871     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14872       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14873                           el2img(Feld[dropx][dropy]), 0);
14874
14875     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14876   }
14877
14878   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14879     InitField_WithBug1(dropx, dropy, FALSE);
14880
14881   new_element = Feld[dropx][dropy];     /* element might have changed */
14882
14883   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14884       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14885   {
14886     int move_direction, nextx, nexty;
14887
14888     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14889       MovDir[dropx][dropy] = drop_direction;
14890
14891     move_direction = MovDir[dropx][dropy];
14892     nextx = dropx + GET_DX_FROM_DIR(move_direction);
14893     nexty = dropy + GET_DY_FROM_DIR(move_direction);
14894
14895     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14896
14897 #if USE_FIX_IMPACT_COLLISION
14898     /* do not cause impact style collision by dropping elements that can fall */
14899     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14900 #else
14901     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14902 #endif
14903   }
14904
14905   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14906   player->is_dropping = TRUE;
14907
14908   player->drop_pressed_delay = 0;
14909   player->is_dropping_pressed = FALSE;
14910
14911   player->drop_x = dropx;
14912   player->drop_y = dropy;
14913
14914   return TRUE;
14915 }
14916
14917 /* ------------------------------------------------------------------------- */
14918 /* game sound playing functions                                              */
14919 /* ------------------------------------------------------------------------- */
14920
14921 static int *loop_sound_frame = NULL;
14922 static int *loop_sound_volume = NULL;
14923
14924 void InitPlayLevelSound()
14925 {
14926   int num_sounds = getSoundListSize();
14927
14928   checked_free(loop_sound_frame);
14929   checked_free(loop_sound_volume);
14930
14931   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14932   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14933 }
14934
14935 static void PlayLevelSound(int x, int y, int nr)
14936 {
14937   int sx = SCREENX(x), sy = SCREENY(y);
14938   int volume, stereo_position;
14939   int max_distance = 8;
14940   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14941
14942   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14943       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14944     return;
14945
14946   if (!IN_LEV_FIELD(x, y) ||
14947       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14948       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14949     return;
14950
14951   volume = SOUND_MAX_VOLUME;
14952
14953   if (!IN_SCR_FIELD(sx, sy))
14954   {
14955     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14956     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14957
14958     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14959   }
14960
14961   stereo_position = (SOUND_MAX_LEFT +
14962                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14963                      (SCR_FIELDX + 2 * max_distance));
14964
14965   if (IS_LOOP_SOUND(nr))
14966   {
14967     /* This assures that quieter loop sounds do not overwrite louder ones,
14968        while restarting sound volume comparison with each new game frame. */
14969
14970     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14971       return;
14972
14973     loop_sound_volume[nr] = volume;
14974     loop_sound_frame[nr] = FrameCounter;
14975   }
14976
14977   PlaySoundExt(nr, volume, stereo_position, type);
14978 }
14979
14980 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14981 {
14982   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14983                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14984                  y < LEVELY(BY1) ? LEVELY(BY1) :
14985                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14986                  sound_action);
14987 }
14988
14989 static void PlayLevelSoundAction(int x, int y, int action)
14990 {
14991   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14992 }
14993
14994 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14995 {
14996   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14997
14998   if (sound_effect != SND_UNDEFINED)
14999     PlayLevelSound(x, y, sound_effect);
15000 }
15001
15002 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15003                                               int action)
15004 {
15005   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15006
15007   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15008     PlayLevelSound(x, y, sound_effect);
15009 }
15010
15011 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15012 {
15013   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15014
15015   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15016     PlayLevelSound(x, y, sound_effect);
15017 }
15018
15019 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15020 {
15021   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15022
15023   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15024     StopSound(sound_effect);
15025 }
15026
15027 static void PlayLevelMusic()
15028 {
15029   if (levelset.music[level_nr] != MUS_UNDEFINED)
15030     PlayMusic(levelset.music[level_nr]);        /* from config file */
15031   else
15032     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
15033 }
15034
15035 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15036 {
15037   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15038   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15039   int x = xx - 1 - offset;
15040   int y = yy - 1 - offset;
15041
15042   switch (sample)
15043   {
15044     case SAMPLE_blank:
15045       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15046       break;
15047
15048     case SAMPLE_roll:
15049       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15050       break;
15051
15052     case SAMPLE_stone:
15053       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15054       break;
15055
15056     case SAMPLE_nut:
15057       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15058       break;
15059
15060     case SAMPLE_crack:
15061       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15062       break;
15063
15064     case SAMPLE_bug:
15065       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15066       break;
15067
15068     case SAMPLE_tank:
15069       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15070       break;
15071
15072     case SAMPLE_android_clone:
15073       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15074       break;
15075
15076     case SAMPLE_android_move:
15077       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15078       break;
15079
15080     case SAMPLE_spring:
15081       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15082       break;
15083
15084     case SAMPLE_slurp:
15085       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15086       break;
15087
15088     case SAMPLE_eater:
15089       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15090       break;
15091
15092     case SAMPLE_eater_eat:
15093       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15094       break;
15095
15096     case SAMPLE_alien:
15097       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15098       break;
15099
15100     case SAMPLE_collect:
15101       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15102       break;
15103
15104     case SAMPLE_diamond:
15105       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15106       break;
15107
15108     case SAMPLE_squash:
15109       /* !!! CHECK THIS !!! */
15110 #if 1
15111       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15112 #else
15113       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15114 #endif
15115       break;
15116
15117     case SAMPLE_wonderfall:
15118       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15119       break;
15120
15121     case SAMPLE_drip:
15122       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15123       break;
15124
15125     case SAMPLE_push:
15126       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15127       break;
15128
15129     case SAMPLE_dirt:
15130       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15131       break;
15132
15133     case SAMPLE_acid:
15134       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15135       break;
15136
15137     case SAMPLE_ball:
15138       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15139       break;
15140
15141     case SAMPLE_grow:
15142       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15143       break;
15144
15145     case SAMPLE_wonder:
15146       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15147       break;
15148
15149     case SAMPLE_door:
15150       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15151       break;
15152
15153     case SAMPLE_exit_open:
15154       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15155       break;
15156
15157     case SAMPLE_exit_leave:
15158       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15159       break;
15160
15161     case SAMPLE_dynamite:
15162       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15163       break;
15164
15165     case SAMPLE_tick:
15166       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15167       break;
15168
15169     case SAMPLE_press:
15170       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15171       break;
15172
15173     case SAMPLE_wheel:
15174       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15175       break;
15176
15177     case SAMPLE_boom:
15178       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15179       break;
15180
15181     case SAMPLE_die:
15182       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15183       break;
15184
15185     case SAMPLE_time:
15186       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15187       break;
15188
15189     default:
15190       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15191       break;
15192   }
15193 }
15194
15195 #if 0
15196 void ChangeTime(int value)
15197 {
15198   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15199
15200   *time += value;
15201
15202   /* EMC game engine uses value from time counter of RND game engine */
15203   level.native_em_level->lev->time = *time;
15204
15205   DrawGameValue_Time(*time);
15206 }
15207
15208 void RaiseScore(int value)
15209 {
15210   /* EMC game engine and RND game engine have separate score counters */
15211   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15212                 &level.native_em_level->lev->score : &local_player->score);
15213
15214   *score += value;
15215
15216   DrawGameValue_Score(*score);
15217 }
15218 #endif
15219
15220 void RaiseScore(int value)
15221 {
15222   local_player->score += value;
15223
15224 #if 1
15225   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15226
15227   DisplayGameControlValues();
15228 #else
15229   DrawGameValue_Score(local_player->score);
15230 #endif
15231 }
15232
15233 void RaiseScoreElement(int element)
15234 {
15235   switch (element)
15236   {
15237     case EL_EMERALD:
15238     case EL_BD_DIAMOND:
15239     case EL_EMERALD_YELLOW:
15240     case EL_EMERALD_RED:
15241     case EL_EMERALD_PURPLE:
15242     case EL_SP_INFOTRON:
15243       RaiseScore(level.score[SC_EMERALD]);
15244       break;
15245     case EL_DIAMOND:
15246       RaiseScore(level.score[SC_DIAMOND]);
15247       break;
15248     case EL_CRYSTAL:
15249       RaiseScore(level.score[SC_CRYSTAL]);
15250       break;
15251     case EL_PEARL:
15252       RaiseScore(level.score[SC_PEARL]);
15253       break;
15254     case EL_BUG:
15255     case EL_BD_BUTTERFLY:
15256     case EL_SP_ELECTRON:
15257       RaiseScore(level.score[SC_BUG]);
15258       break;
15259     case EL_SPACESHIP:
15260     case EL_BD_FIREFLY:
15261     case EL_SP_SNIKSNAK:
15262       RaiseScore(level.score[SC_SPACESHIP]);
15263       break;
15264     case EL_YAMYAM:
15265     case EL_DARK_YAMYAM:
15266       RaiseScore(level.score[SC_YAMYAM]);
15267       break;
15268     case EL_ROBOT:
15269       RaiseScore(level.score[SC_ROBOT]);
15270       break;
15271     case EL_PACMAN:
15272       RaiseScore(level.score[SC_PACMAN]);
15273       break;
15274     case EL_NUT:
15275       RaiseScore(level.score[SC_NUT]);
15276       break;
15277     case EL_DYNAMITE:
15278     case EL_EM_DYNAMITE:
15279     case EL_SP_DISK_RED:
15280     case EL_DYNABOMB_INCREASE_NUMBER:
15281     case EL_DYNABOMB_INCREASE_SIZE:
15282     case EL_DYNABOMB_INCREASE_POWER:
15283       RaiseScore(level.score[SC_DYNAMITE]);
15284       break;
15285     case EL_SHIELD_NORMAL:
15286     case EL_SHIELD_DEADLY:
15287       RaiseScore(level.score[SC_SHIELD]);
15288       break;
15289     case EL_EXTRA_TIME:
15290       RaiseScore(level.extra_time_score);
15291       break;
15292     case EL_KEY_1:
15293     case EL_KEY_2:
15294     case EL_KEY_3:
15295     case EL_KEY_4:
15296     case EL_EM_KEY_1:
15297     case EL_EM_KEY_2:
15298     case EL_EM_KEY_3:
15299     case EL_EM_KEY_4:
15300     case EL_EMC_KEY_5:
15301     case EL_EMC_KEY_6:
15302     case EL_EMC_KEY_7:
15303     case EL_EMC_KEY_8:
15304     case EL_DC_KEY_WHITE:
15305       RaiseScore(level.score[SC_KEY]);
15306       break;
15307     default:
15308       RaiseScore(element_info[element].collect_score);
15309       break;
15310   }
15311 }
15312
15313 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15314 {
15315   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15316   {
15317 #if defined(NETWORK_AVALIABLE)
15318     if (options.network)
15319       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15320     else
15321 #endif
15322     {
15323       if (quick_quit)
15324       {
15325 #if 1
15326
15327 #if 1
15328         FadeSkipNextFadeIn();
15329 #else
15330         fading = fading_none;
15331 #endif
15332
15333 #else
15334         OpenDoor(DOOR_CLOSE_1);
15335 #endif
15336
15337         game_status = GAME_MODE_MAIN;
15338
15339 #if 1
15340         DrawAndFadeInMainMenu(REDRAW_FIELD);
15341 #else
15342         DrawMainMenu();
15343 #endif
15344       }
15345       else
15346       {
15347 #if 0
15348         FadeOut(REDRAW_FIELD);
15349 #endif
15350
15351         game_status = GAME_MODE_MAIN;
15352
15353         DrawAndFadeInMainMenu(REDRAW_FIELD);
15354       }
15355     }
15356   }
15357   else          /* continue playing the game */
15358   {
15359     if (tape.playing && tape.deactivate_display)
15360       TapeDeactivateDisplayOff(TRUE);
15361
15362     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15363
15364     if (tape.playing && tape.deactivate_display)
15365       TapeDeactivateDisplayOn();
15366   }
15367 }
15368
15369 void RequestQuitGame(boolean ask_if_really_quit)
15370 {
15371   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15372   boolean skip_request = AllPlayersGone || quick_quit;
15373
15374   RequestQuitGameExt(skip_request, quick_quit,
15375                      "Do you really want to quit the game ?");
15376 }
15377
15378
15379 /* ------------------------------------------------------------------------- */
15380 /* random generator functions                                                */
15381 /* ------------------------------------------------------------------------- */
15382
15383 unsigned int InitEngineRandom_RND(long seed)
15384 {
15385   game.num_random_calls = 0;
15386
15387 #if 0
15388   unsigned int rnd_seed = InitEngineRandom(seed);
15389
15390   printf("::: START RND: %d\n", rnd_seed);
15391
15392   return rnd_seed;
15393 #else
15394
15395   return InitEngineRandom(seed);
15396
15397 #endif
15398
15399 }
15400
15401 unsigned int RND(int max)
15402 {
15403   if (max > 0)
15404   {
15405     game.num_random_calls++;
15406
15407     return GetEngineRandom(max);
15408   }
15409
15410   return 0;
15411 }
15412
15413
15414 /* ------------------------------------------------------------------------- */
15415 /* game engine snapshot handling functions                                   */
15416 /* ------------------------------------------------------------------------- */
15417
15418 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
15419
15420 struct EngineSnapshotInfo
15421 {
15422   /* runtime values for custom element collect score */
15423   int collect_score[NUM_CUSTOM_ELEMENTS];
15424
15425   /* runtime values for group element choice position */
15426   int choice_pos[NUM_GROUP_ELEMENTS];
15427
15428   /* runtime values for belt position animations */
15429   int belt_graphic[4 * NUM_BELT_PARTS];
15430   int belt_anim_mode[4 * NUM_BELT_PARTS];
15431 };
15432
15433 struct EngineSnapshotNodeInfo
15434 {
15435   void *buffer_orig;
15436   void *buffer_copy;
15437   int size;
15438 };
15439
15440 static struct EngineSnapshotInfo engine_snapshot_rnd;
15441 static ListNode *engine_snapshot_list = NULL;
15442 static char *snapshot_level_identifier = NULL;
15443 static int snapshot_level_nr = -1;
15444
15445 void FreeEngineSnapshot()
15446 {
15447   while (engine_snapshot_list != NULL)
15448     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15449                        checked_free);
15450
15451   setString(&snapshot_level_identifier, NULL);
15452   snapshot_level_nr = -1;
15453 }
15454
15455 static void SaveEngineSnapshotValues_RND()
15456 {
15457   static int belt_base_active_element[4] =
15458   {
15459     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15460     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15461     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15462     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15463   };
15464   int i, j;
15465
15466   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15467   {
15468     int element = EL_CUSTOM_START + i;
15469
15470     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15471   }
15472
15473   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15474   {
15475     int element = EL_GROUP_START + i;
15476
15477     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15478   }
15479
15480   for (i = 0; i < 4; i++)
15481   {
15482     for (j = 0; j < NUM_BELT_PARTS; j++)
15483     {
15484       int element = belt_base_active_element[i] + j;
15485       int graphic = el2img(element);
15486       int anim_mode = graphic_info[graphic].anim_mode;
15487
15488       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15489       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15490     }
15491   }
15492 }
15493
15494 static void LoadEngineSnapshotValues_RND()
15495 {
15496   unsigned long num_random_calls = game.num_random_calls;
15497   int i, j;
15498
15499   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15500   {
15501     int element = EL_CUSTOM_START + i;
15502
15503     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15504   }
15505
15506   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15507   {
15508     int element = EL_GROUP_START + i;
15509
15510     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15511   }
15512
15513   for (i = 0; i < 4; i++)
15514   {
15515     for (j = 0; j < NUM_BELT_PARTS; j++)
15516     {
15517       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15518       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15519
15520       graphic_info[graphic].anim_mode = anim_mode;
15521     }
15522   }
15523
15524   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15525   {
15526     InitRND(tape.random_seed);
15527     for (i = 0; i < num_random_calls; i++)
15528       RND(1);
15529   }
15530
15531   if (game.num_random_calls != num_random_calls)
15532   {
15533     Error(ERR_INFO, "number of random calls out of sync");
15534     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15535     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15536     Error(ERR_EXIT, "this should not happen -- please debug");
15537   }
15538 }
15539
15540 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15541 {
15542   struct EngineSnapshotNodeInfo *bi =
15543     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15544
15545   bi->buffer_orig = buffer;
15546   bi->buffer_copy = checked_malloc(size);
15547   bi->size = size;
15548
15549   memcpy(bi->buffer_copy, buffer, size);
15550
15551   addNodeToList(&engine_snapshot_list, NULL, bi);
15552 }
15553
15554 void SaveEngineSnapshot()
15555 {
15556   FreeEngineSnapshot();         /* free previous snapshot, if needed */
15557
15558   if (level_editor_test_game)   /* do not save snapshots from editor */
15559     return;
15560
15561   /* copy some special values to a structure better suited for the snapshot */
15562
15563   SaveEngineSnapshotValues_RND();
15564   SaveEngineSnapshotValues_EM();
15565
15566   /* save values stored in special snapshot structure */
15567
15568   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15569   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15570
15571   /* save further RND engine values */
15572
15573   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15574   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15575   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15576
15577   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15578   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15579   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15580   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15581
15582   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15583   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15584   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15585   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15586   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15587
15588   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15589   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15590   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15591
15592   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15593
15594   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15595
15596   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15597   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15598
15599   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15600   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15601   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15602   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15603   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15604   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15605   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15606   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15607   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15608   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15609   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15610   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15611   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15612   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15613   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15614   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15615   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15616   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15617
15618   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15619   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15620
15621   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15622   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15623   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15624
15625   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15626   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15627
15628   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15629   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15630   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15631   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15632   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15633
15634   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15635   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15636
15637   /* save level identification information */
15638
15639   setString(&snapshot_level_identifier, leveldir_current->identifier);
15640   snapshot_level_nr = level_nr;
15641
15642 #if 0
15643   ListNode *node = engine_snapshot_list;
15644   int num_bytes = 0;
15645
15646   while (node != NULL)
15647   {
15648     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15649
15650     node = node->next;
15651   }
15652
15653   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15654 #endif
15655 }
15656
15657 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15658 {
15659   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15660 }
15661
15662 void LoadEngineSnapshot()
15663 {
15664   ListNode *node = engine_snapshot_list;
15665
15666   if (engine_snapshot_list == NULL)
15667     return;
15668
15669   while (node != NULL)
15670   {
15671     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15672
15673     node = node->next;
15674   }
15675
15676   /* restore special values from snapshot structure */
15677
15678   LoadEngineSnapshotValues_RND();
15679   LoadEngineSnapshotValues_EM();
15680 }
15681
15682 boolean CheckEngineSnapshot()
15683 {
15684   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15685           snapshot_level_nr == level_nr);
15686 }
15687
15688
15689 /* ---------- new game button stuff ---------------------------------------- */
15690
15691 /* graphic position values for game buttons */
15692 #define GAME_BUTTON_XSIZE       30
15693 #define GAME_BUTTON_YSIZE       30
15694 #define GAME_BUTTON_XPOS        5
15695 #define GAME_BUTTON_YPOS        215
15696 #define SOUND_BUTTON_XPOS       5
15697 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15698
15699 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15700 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15701 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15702 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15703 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15704 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15705
15706 static struct
15707 {
15708   int *x, *y;
15709   int gd_x, gd_y;
15710   int gadget_id;
15711   char *infotext;
15712 } gamebutton_info[NUM_GAME_BUTTONS] =
15713 {
15714 #if 1
15715   {
15716     &game.button.stop.x,        &game.button.stop.y,
15717     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15718     GAME_CTRL_ID_STOP,
15719     "stop game"
15720   },
15721   {
15722     &game.button.pause.x,       &game.button.pause.y,
15723     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15724     GAME_CTRL_ID_PAUSE,
15725     "pause game"
15726   },
15727   {
15728     &game.button.play.x,        &game.button.play.y,
15729     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15730     GAME_CTRL_ID_PLAY,
15731     "play game"
15732   },
15733   {
15734     &game.button.sound_music.x, &game.button.sound_music.y,
15735     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15736     SOUND_CTRL_ID_MUSIC,
15737     "background music on/off"
15738   },
15739   {
15740     &game.button.sound_loops.x, &game.button.sound_loops.y,
15741     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15742     SOUND_CTRL_ID_LOOPS,
15743     "sound loops on/off"
15744   },
15745   {
15746     &game.button.sound_simple.x,&game.button.sound_simple.y,
15747     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15748     SOUND_CTRL_ID_SIMPLE,
15749     "normal sounds on/off"
15750   }
15751 #else
15752   {
15753     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15754     GAME_CTRL_ID_STOP,
15755     "stop game"
15756   },
15757   {
15758     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15759     GAME_CTRL_ID_PAUSE,
15760     "pause game"
15761   },
15762   {
15763     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15764     GAME_CTRL_ID_PLAY,
15765     "play game"
15766   },
15767   {
15768     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15769     SOUND_CTRL_ID_MUSIC,
15770     "background music on/off"
15771   },
15772   {
15773     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15774     SOUND_CTRL_ID_LOOPS,
15775     "sound loops on/off"
15776   },
15777   {
15778     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15779     SOUND_CTRL_ID_SIMPLE,
15780     "normal sounds on/off"
15781   }
15782 #endif
15783 };
15784
15785 void CreateGameButtons()
15786 {
15787   int i;
15788
15789   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15790   {
15791     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15792     struct GadgetInfo *gi;
15793     int button_type;
15794     boolean checked;
15795     unsigned long event_mask;
15796     int x, y;
15797     int gd_xoffset, gd_yoffset;
15798     int gd_x1, gd_x2, gd_y1, gd_y2;
15799     int id = i;
15800
15801     x = DX + *gamebutton_info[i].x;
15802     y = DY + *gamebutton_info[i].y;
15803     gd_xoffset = gamebutton_info[i].gd_x;
15804     gd_yoffset = gamebutton_info[i].gd_y;
15805     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15806     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15807
15808     if (id == GAME_CTRL_ID_STOP ||
15809         id == GAME_CTRL_ID_PAUSE ||
15810         id == GAME_CTRL_ID_PLAY)
15811     {
15812       button_type = GD_TYPE_NORMAL_BUTTON;
15813       checked = FALSE;
15814       event_mask = GD_EVENT_RELEASED;
15815       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15816       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15817     }
15818     else
15819     {
15820       button_type = GD_TYPE_CHECK_BUTTON;
15821       checked =
15822         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15823          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15824          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15825       event_mask = GD_EVENT_PRESSED;
15826       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
15827       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15828     }
15829
15830     gi = CreateGadget(GDI_CUSTOM_ID, id,
15831                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15832 #if 1
15833                       GDI_X, x,
15834                       GDI_Y, y,
15835 #else
15836                       GDI_X, DX + gd_xoffset,
15837                       GDI_Y, DY + gd_yoffset,
15838 #endif
15839                       GDI_WIDTH, GAME_BUTTON_XSIZE,
15840                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
15841                       GDI_TYPE, button_type,
15842                       GDI_STATE, GD_BUTTON_UNPRESSED,
15843                       GDI_CHECKED, checked,
15844                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15845                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15846                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15847                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15848                       GDI_DIRECT_DRAW, FALSE,
15849                       GDI_EVENT_MASK, event_mask,
15850                       GDI_CALLBACK_ACTION, HandleGameButtons,
15851                       GDI_END);
15852
15853     if (gi == NULL)
15854       Error(ERR_EXIT, "cannot create gadget");
15855
15856     game_gadget[id] = gi;
15857   }
15858 }
15859
15860 void FreeGameButtons()
15861 {
15862   int i;
15863
15864   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15865     FreeGadget(game_gadget[i]);
15866 }
15867
15868 static void MapGameButtons()
15869 {
15870   int i;
15871
15872   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15873     MapGadget(game_gadget[i]);
15874 }
15875
15876 void UnmapGameButtons()
15877 {
15878   int i;
15879
15880   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15881     UnmapGadget(game_gadget[i]);
15882 }
15883
15884 void RedrawGameButtons()
15885 {
15886   int i;
15887
15888   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15889     RedrawGadget(game_gadget[i]);
15890 }
15891
15892 static void HandleGameButtons(struct GadgetInfo *gi)
15893 {
15894   int id = gi->custom_id;
15895
15896   if (game_status != GAME_MODE_PLAYING)
15897     return;
15898
15899   switch (id)
15900   {
15901     case GAME_CTRL_ID_STOP:
15902       if (tape.playing)
15903         TapeStop();
15904       else
15905         RequestQuitGame(TRUE);
15906       break;
15907
15908     case GAME_CTRL_ID_PAUSE:
15909       if (options.network)
15910       {
15911 #if defined(NETWORK_AVALIABLE)
15912         if (tape.pausing)
15913           SendToServer_ContinuePlaying();
15914         else
15915           SendToServer_PausePlaying();
15916 #endif
15917       }
15918       else
15919         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15920       break;
15921
15922     case GAME_CTRL_ID_PLAY:
15923       if (tape.pausing)
15924       {
15925 #if defined(NETWORK_AVALIABLE)
15926         if (options.network)
15927           SendToServer_ContinuePlaying();
15928         else
15929 #endif
15930         {
15931           tape.pausing = FALSE;
15932           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15933         }
15934       }
15935       break;
15936
15937     case SOUND_CTRL_ID_MUSIC:
15938       if (setup.sound_music)
15939       { 
15940         setup.sound_music = FALSE;
15941         FadeMusic();
15942       }
15943       else if (audio.music_available)
15944       { 
15945         setup.sound = setup.sound_music = TRUE;
15946
15947         SetAudioMode(setup.sound);
15948
15949         PlayLevelMusic();
15950       }
15951       break;
15952
15953     case SOUND_CTRL_ID_LOOPS:
15954       if (setup.sound_loops)
15955         setup.sound_loops = FALSE;
15956       else if (audio.loops_available)
15957       {
15958         setup.sound = setup.sound_loops = TRUE;
15959         SetAudioMode(setup.sound);
15960       }
15961       break;
15962
15963     case SOUND_CTRL_ID_SIMPLE:
15964       if (setup.sound_simple)
15965         setup.sound_simple = FALSE;
15966       else if (audio.sound_available)
15967       {
15968         setup.sound = setup.sound_simple = TRUE;
15969         SetAudioMode(setup.sound);
15970       }
15971       break;
15972
15973     default:
15974       break;
15975   }
15976 }