rnd-20070917-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61
62 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
63
64
65 /* for DigField() */
66 #define DF_NO_PUSH              0
67 #define DF_DIG                  1
68 #define DF_SNAP                 2
69
70 /* for MovePlayer() */
71 #define MP_NO_ACTION            0
72 #define MP_MOVING               1
73 #define MP_ACTION               2
74 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
75
76 /* for ScrollPlayer() */
77 #define SCROLL_INIT             0
78 #define SCROLL_GO_ON            1
79
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START          0
82 #define EX_TYPE_NONE            0
83 #define EX_TYPE_NORMAL          (1 << 0)
84 #define EX_TYPE_CENTER          (1 << 1)
85 #define EX_TYPE_BORDER          (1 << 2)
86 #define EX_TYPE_CROSS           (1 << 3)
87 #define EX_TYPE_DYNA            (1 << 4)
88 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
89
90 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
91 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
92 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
93 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
94
95 /* special positions in the game control window (relative to control window) */
96 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
97 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
98 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
99 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
100 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
101 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
102 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
103 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
104 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
105 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
106 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
107 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
108 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
109 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
110 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
111 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
112
113 /* special positions in the game control window (relative to main window) */
114 #define DX_LEVEL1               (DX + XX_LEVEL1)
115 #define DX_LEVEL2               (DX + XX_LEVEL2)
116 #define DX_LEVEL                (DX + XX_LEVEL)
117 #define DY_LEVEL                (DY + YY_LEVEL)
118 #define DX_EMERALDS             (DX + XX_EMERALDS)
119 #define DY_EMERALDS             (DY + YY_EMERALDS)
120 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
121 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
122 #define DX_KEYS                 (DX + XX_KEYS)
123 #define DY_KEYS                 (DY + YY_KEYS)
124 #define DX_SCORE                (DX + XX_SCORE)
125 #define DY_SCORE                (DY + YY_SCORE)
126 #define DX_TIME1                (DX + XX_TIME1)
127 #define DX_TIME2                (DX + XX_TIME2)
128 #define DX_TIME                 (DX + XX_TIME)
129 #define DY_TIME                 (DY + YY_TIME)
130
131 #if 1
132 /* game panel display and control definitions */
133
134 #define GAME_PANEL_LEVEL_NUMBER                 0
135 #define GAME_PANEL_GEMS                         1
136 #define GAME_PANEL_INVENTORY_COUNT              2
137 #define GAME_PANEL_INVENTORY_FIRST_1            3
138 #define GAME_PANEL_INVENTORY_FIRST_2            4
139 #define GAME_PANEL_INVENTORY_FIRST_3            5
140 #define GAME_PANEL_INVENTORY_FIRST_4            6
141 #define GAME_PANEL_INVENTORY_FIRST_5            7
142 #define GAME_PANEL_INVENTORY_FIRST_6            8
143 #define GAME_PANEL_INVENTORY_FIRST_7            9
144 #define GAME_PANEL_INVENTORY_FIRST_8            10
145 #define GAME_PANEL_INVENTORY_LAST_1             11
146 #define GAME_PANEL_INVENTORY_LAST_2             12
147 #define GAME_PANEL_INVENTORY_LAST_3             13
148 #define GAME_PANEL_INVENTORY_LAST_4             14
149 #define GAME_PANEL_INVENTORY_LAST_5             15
150 #define GAME_PANEL_INVENTORY_LAST_6             16
151 #define GAME_PANEL_INVENTORY_LAST_7             17
152 #define GAME_PANEL_INVENTORY_LAST_8             18
153 #define GAME_PANEL_KEY_1                        19
154 #define GAME_PANEL_KEY_2                        20
155 #define GAME_PANEL_KEY_3                        21
156 #define GAME_PANEL_KEY_4                        22
157 #define GAME_PANEL_KEY_5                        23
158 #define GAME_PANEL_KEY_6                        24
159 #define GAME_PANEL_KEY_7                        25
160 #define GAME_PANEL_KEY_8                        26
161 #define GAME_PANEL_KEY_WHITE                    27
162 #define GAME_PANEL_KEY_WHITE_COUNT              28
163 #define GAME_PANEL_SCORE                        29
164 #define GAME_PANEL_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           MovDelay[x][y] = TILEY + 1;
7569
7570         if (MovDelay[x][y])
7571         {
7572           MovDelay[x][y]--;
7573           if (MovDelay[x][y])
7574             return;
7575         }
7576
7577         Feld[x][y] = EL_QUICKSAND_EMPTY;
7578         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7579         Store[x][y + 1] = Store[x][y];
7580         Store[x][y] = 0;
7581
7582         PlayLevelSoundAction(x, y, ACTION_FILLING);
7583       }
7584     }
7585     else if (element == EL_QUICKSAND_FAST_FULL)
7586     {
7587       if (IS_FREE(x, y + 1))
7588       {
7589         InitMovingField(x, y, MV_DOWN);
7590         started_moving = TRUE;
7591
7592         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7593 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7594         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7595           Store[x][y] = EL_ROCK;
7596 #else
7597         Store[x][y] = EL_ROCK;
7598 #endif
7599
7600         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7601       }
7602       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7603       {
7604         if (!MovDelay[x][y])
7605           MovDelay[x][y] = TILEY + 1;
7606
7607         if (MovDelay[x][y])
7608         {
7609           MovDelay[x][y]--;
7610           if (MovDelay[x][y])
7611             return;
7612         }
7613
7614         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7615         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7616         Store[x][y + 1] = Store[x][y];
7617         Store[x][y] = 0;
7618
7619         PlayLevelSoundAction(x, y, ACTION_FILLING);
7620       }
7621     }
7622     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7623              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7624     {
7625       InitMovingField(x, y, MV_DOWN);
7626       started_moving = TRUE;
7627
7628       Feld[x][y] = EL_QUICKSAND_FILLING;
7629       Store[x][y] = element;
7630
7631       PlayLevelSoundAction(x, y, ACTION_FILLING);
7632     }
7633     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7634              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7635     {
7636       InitMovingField(x, y, MV_DOWN);
7637       started_moving = TRUE;
7638
7639       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7640       Store[x][y] = element;
7641
7642       PlayLevelSoundAction(x, y, ACTION_FILLING);
7643     }
7644     else if (element == EL_MAGIC_WALL_FULL)
7645     {
7646       if (IS_FREE(x, y + 1))
7647       {
7648         InitMovingField(x, y, MV_DOWN);
7649         started_moving = TRUE;
7650
7651         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7652         Store[x][y] = EL_CHANGED(Store[x][y]);
7653       }
7654       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7655       {
7656         if (!MovDelay[x][y])
7657           MovDelay[x][y] = TILEY/4 + 1;
7658
7659         if (MovDelay[x][y])
7660         {
7661           MovDelay[x][y]--;
7662           if (MovDelay[x][y])
7663             return;
7664         }
7665
7666         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7667         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7668         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7669         Store[x][y] = 0;
7670       }
7671     }
7672     else if (element == EL_BD_MAGIC_WALL_FULL)
7673     {
7674       if (IS_FREE(x, y + 1))
7675       {
7676         InitMovingField(x, y, MV_DOWN);
7677         started_moving = TRUE;
7678
7679         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7680         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7681       }
7682       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7683       {
7684         if (!MovDelay[x][y])
7685           MovDelay[x][y] = TILEY/4 + 1;
7686
7687         if (MovDelay[x][y])
7688         {
7689           MovDelay[x][y]--;
7690           if (MovDelay[x][y])
7691             return;
7692         }
7693
7694         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7695         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7696         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7697         Store[x][y] = 0;
7698       }
7699     }
7700     else if (element == EL_DC_MAGIC_WALL_FULL)
7701     {
7702       if (IS_FREE(x, y + 1))
7703       {
7704         InitMovingField(x, y, MV_DOWN);
7705         started_moving = TRUE;
7706
7707         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7708         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7709       }
7710       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7711       {
7712         if (!MovDelay[x][y])
7713           MovDelay[x][y] = TILEY/4 + 1;
7714
7715         if (MovDelay[x][y])
7716         {
7717           MovDelay[x][y]--;
7718           if (MovDelay[x][y])
7719             return;
7720         }
7721
7722         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7723         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7724         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7725         Store[x][y] = 0;
7726       }
7727     }
7728     else if ((CAN_PASS_MAGIC_WALL(element) &&
7729               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7730                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7731              (CAN_PASS_DC_MAGIC_WALL(element) &&
7732               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7733
7734     {
7735       InitMovingField(x, y, MV_DOWN);
7736       started_moving = TRUE;
7737
7738       Feld[x][y] =
7739         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7740          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7741          EL_DC_MAGIC_WALL_FILLING);
7742       Store[x][y] = element;
7743     }
7744     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7745     {
7746       SplashAcid(x, y + 1);
7747
7748       InitMovingField(x, y, MV_DOWN);
7749       started_moving = TRUE;
7750
7751       Store[x][y] = EL_ACID;
7752     }
7753     else if (
7754 #if USE_FIX_IMPACT_COLLISION
7755              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7756               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7757 #else
7758              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7759               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7760 #endif
7761              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7762               CAN_FALL(element) && WasJustFalling[x][y] &&
7763               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7764
7765              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7766               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7767               (Feld[x][y + 1] == EL_BLOCKED)))
7768     {
7769       /* this is needed for a special case not covered by calling "Impact()"
7770          from "ContinueMoving()": if an element moves to a tile directly below
7771          another element which was just falling on that tile (which was empty
7772          in the previous frame), the falling element above would just stop
7773          instead of smashing the element below (in previous version, the above
7774          element was just checked for "moving" instead of "falling", resulting
7775          in incorrect smashes caused by horizontal movement of the above
7776          element; also, the case of the player being the element to smash was
7777          simply not covered here... :-/ ) */
7778
7779       CheckCollision[x][y] = 0;
7780       CheckImpact[x][y] = 0;
7781
7782       Impact(x, y);
7783     }
7784     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7785     {
7786       if (MovDir[x][y] == MV_NONE)
7787       {
7788         InitMovingField(x, y, MV_DOWN);
7789         started_moving = TRUE;
7790       }
7791     }
7792     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7793     {
7794       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7795         MovDir[x][y] = MV_DOWN;
7796
7797       InitMovingField(x, y, MV_DOWN);
7798       started_moving = TRUE;
7799     }
7800     else if (element == EL_AMOEBA_DROP)
7801     {
7802       Feld[x][y] = EL_AMOEBA_GROWING;
7803       Store[x][y] = EL_AMOEBA_WET;
7804     }
7805     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7806               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7807              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7808              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7809     {
7810       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7811                                 (IS_FREE(x - 1, y + 1) ||
7812                                  Feld[x - 1][y + 1] == EL_ACID));
7813       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7814                                 (IS_FREE(x + 1, y + 1) ||
7815                                  Feld[x + 1][y + 1] == EL_ACID));
7816       boolean can_fall_any  = (can_fall_left || can_fall_right);
7817       boolean can_fall_both = (can_fall_left && can_fall_right);
7818       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7819
7820 #if USE_NEW_ALL_SLIPPERY
7821       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7822       {
7823         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7824           can_fall_right = FALSE;
7825         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7826           can_fall_left = FALSE;
7827         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7828           can_fall_right = FALSE;
7829         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7830           can_fall_left = FALSE;
7831
7832         can_fall_any  = (can_fall_left || can_fall_right);
7833         can_fall_both = FALSE;
7834       }
7835 #else
7836       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7837       {
7838         if (slippery_type == SLIPPERY_ONLY_LEFT)
7839           can_fall_right = FALSE;
7840         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7841           can_fall_left = FALSE;
7842         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7843           can_fall_right = FALSE;
7844         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7845           can_fall_left = FALSE;
7846
7847         can_fall_any  = (can_fall_left || can_fall_right);
7848         can_fall_both = (can_fall_left && can_fall_right);
7849       }
7850 #endif
7851
7852 #if USE_NEW_ALL_SLIPPERY
7853 #else
7854 #if USE_NEW_SP_SLIPPERY
7855       /* !!! better use the same properties as for custom elements here !!! */
7856       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7857                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7858       {
7859         can_fall_right = FALSE;         /* slip down on left side */
7860         can_fall_both = FALSE;
7861       }
7862 #endif
7863 #endif
7864
7865 #if USE_NEW_ALL_SLIPPERY
7866       if (can_fall_both)
7867       {
7868         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7869           can_fall_right = FALSE;       /* slip down on left side */
7870         else
7871           can_fall_left = !(can_fall_right = RND(2));
7872
7873         can_fall_both = FALSE;
7874       }
7875 #else
7876       if (can_fall_both)
7877       {
7878         if (game.emulation == EMU_BOULDERDASH ||
7879             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7880           can_fall_right = FALSE;       /* slip down on left side */
7881         else
7882           can_fall_left = !(can_fall_right = RND(2));
7883
7884         can_fall_both = FALSE;
7885       }
7886 #endif
7887
7888       if (can_fall_any)
7889       {
7890         /* if not determined otherwise, prefer left side for slipping down */
7891         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7892         started_moving = TRUE;
7893       }
7894     }
7895 #if 0
7896     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7897 #else
7898     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7899 #endif
7900     {
7901       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7902       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7903       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7904       int belt_dir = game.belt_dir[belt_nr];
7905
7906       if ((belt_dir == MV_LEFT  && left_is_free) ||
7907           (belt_dir == MV_RIGHT && right_is_free))
7908       {
7909         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7910
7911         InitMovingField(x, y, belt_dir);
7912         started_moving = TRUE;
7913
7914         Pushed[x][y] = TRUE;
7915         Pushed[nextx][y] = TRUE;
7916
7917         GfxAction[x][y] = ACTION_DEFAULT;
7918       }
7919       else
7920       {
7921         MovDir[x][y] = 0;       /* if element was moving, stop it */
7922       }
7923     }
7924   }
7925
7926   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7927 #if 0
7928   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7929 #else
7930   if (CAN_MOVE(element) && !started_moving)
7931 #endif
7932   {
7933     int move_pattern = element_info[element].move_pattern;
7934     int newx, newy;
7935
7936 #if 0
7937 #if DEBUG
7938     if (MovDir[x][y] == MV_NONE)
7939     {
7940       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7941              x, y, element, element_info[element].token_name);
7942       printf("StartMoving(): This should never happen!\n");
7943     }
7944 #endif
7945 #endif
7946
7947     Moving2Blocked(x, y, &newx, &newy);
7948
7949     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7950       return;
7951
7952     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7953         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7954     {
7955       WasJustMoving[x][y] = 0;
7956       CheckCollision[x][y] = 0;
7957
7958       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7959
7960       if (Feld[x][y] != element)        /* element has changed */
7961         return;
7962     }
7963
7964     if (!MovDelay[x][y])        /* start new movement phase */
7965     {
7966       /* all objects that can change their move direction after each step
7967          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7968
7969       if (element != EL_YAMYAM &&
7970           element != EL_DARK_YAMYAM &&
7971           element != EL_PACMAN &&
7972           !(move_pattern & MV_ANY_DIRECTION) &&
7973           move_pattern != MV_TURNING_LEFT &&
7974           move_pattern != MV_TURNING_RIGHT &&
7975           move_pattern != MV_TURNING_LEFT_RIGHT &&
7976           move_pattern != MV_TURNING_RIGHT_LEFT &&
7977           move_pattern != MV_TURNING_RANDOM)
7978       {
7979         TurnRound(x, y);
7980
7981         if (MovDelay[x][y] && (element == EL_BUG ||
7982                                element == EL_SPACESHIP ||
7983                                element == EL_SP_SNIKSNAK ||
7984                                element == EL_SP_ELECTRON ||
7985                                element == EL_MOLE))
7986           DrawLevelField(x, y);
7987       }
7988     }
7989
7990     if (MovDelay[x][y])         /* wait some time before next movement */
7991     {
7992       MovDelay[x][y]--;
7993
7994       if (element == EL_ROBOT ||
7995           element == EL_YAMYAM ||
7996           element == EL_DARK_YAMYAM)
7997       {
7998         DrawLevelElementAnimationIfNeeded(x, y, element);
7999         PlayLevelSoundAction(x, y, ACTION_WAITING);
8000       }
8001       else if (element == EL_SP_ELECTRON)
8002         DrawLevelElementAnimationIfNeeded(x, y, element);
8003       else if (element == EL_DRAGON)
8004       {
8005         int i;
8006         int dir = MovDir[x][y];
8007         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8008         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8009         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8010                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8011                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8012                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8013         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8014
8015         GfxAction[x][y] = ACTION_ATTACKING;
8016
8017         if (IS_PLAYER(x, y))
8018           DrawPlayerField(x, y);
8019         else
8020           DrawLevelField(x, y);
8021
8022         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8023
8024         for (i = 1; i <= 3; i++)
8025         {
8026           int xx = x + i * dx;
8027           int yy = y + i * dy;
8028           int sx = SCREENX(xx);
8029           int sy = SCREENY(yy);
8030           int flame_graphic = graphic + (i - 1);
8031
8032           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8033             break;
8034
8035           if (MovDelay[x][y])
8036           {
8037             int flamed = MovingOrBlocked2Element(xx, yy);
8038
8039             /* !!! */
8040 #if 0
8041             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8042               Bang(xx, yy);
8043             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8044               RemoveMovingField(xx, yy);
8045             else
8046               RemoveField(xx, yy);
8047 #else
8048             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8049               Bang(xx, yy);
8050             else
8051               RemoveMovingField(xx, yy);
8052 #endif
8053
8054             ChangeDelay[xx][yy] = 0;
8055
8056             Feld[xx][yy] = EL_FLAMES;
8057
8058             if (IN_SCR_FIELD(sx, sy))
8059             {
8060               DrawLevelFieldCrumbledSand(xx, yy);
8061               DrawGraphic(sx, sy, flame_graphic, frame);
8062             }
8063           }
8064           else
8065           {
8066             if (Feld[xx][yy] == EL_FLAMES)
8067               Feld[xx][yy] = EL_EMPTY;
8068             DrawLevelField(xx, yy);
8069           }
8070         }
8071       }
8072
8073       if (MovDelay[x][y])       /* element still has to wait some time */
8074       {
8075         PlayLevelSoundAction(x, y, ACTION_WAITING);
8076
8077         return;
8078       }
8079     }
8080
8081     /* now make next step */
8082
8083     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8084
8085     if (DONT_COLLIDE_WITH(element) &&
8086         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8087         !PLAYER_ENEMY_PROTECTED(newx, newy))
8088     {
8089       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8090
8091       return;
8092     }
8093
8094     else if (CAN_MOVE_INTO_ACID(element) &&
8095              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8096              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8097              (MovDir[x][y] == MV_DOWN ||
8098               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8099     {
8100       SplashAcid(newx, newy);
8101       Store[x][y] = EL_ACID;
8102     }
8103     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8104     {
8105       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8106           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8107           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8108           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8109       {
8110         RemoveField(x, y);
8111         DrawLevelField(x, y);
8112
8113         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8114         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8115           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8116
8117         local_player->friends_still_needed--;
8118         if (!local_player->friends_still_needed &&
8119             !local_player->GameOver && AllPlayersGone)
8120           PlayerWins(local_player);
8121
8122         return;
8123       }
8124       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8125       {
8126         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8127           DrawLevelField(newx, newy);
8128         else
8129           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8130       }
8131       else if (!IS_FREE(newx, newy))
8132       {
8133         GfxAction[x][y] = ACTION_WAITING;
8134
8135         if (IS_PLAYER(x, y))
8136           DrawPlayerField(x, y);
8137         else
8138           DrawLevelField(x, y);
8139
8140         return;
8141       }
8142     }
8143     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8144     {
8145       if (IS_FOOD_PIG(Feld[newx][newy]))
8146       {
8147         if (IS_MOVING(newx, newy))
8148           RemoveMovingField(newx, newy);
8149         else
8150         {
8151           Feld[newx][newy] = EL_EMPTY;
8152           DrawLevelField(newx, newy);
8153         }
8154
8155         PlayLevelSound(x, y, SND_PIG_DIGGING);
8156       }
8157       else if (!IS_FREE(newx, newy))
8158       {
8159         if (IS_PLAYER(x, y))
8160           DrawPlayerField(x, y);
8161         else
8162           DrawLevelField(x, y);
8163
8164         return;
8165       }
8166     }
8167     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8168     {
8169       if (Store[x][y] != EL_EMPTY)
8170       {
8171         boolean can_clone = FALSE;
8172         int xx, yy;
8173
8174         /* check if element to clone is still there */
8175         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8176         {
8177           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8178           {
8179             can_clone = TRUE;
8180
8181             break;
8182           }
8183         }
8184
8185         /* cannot clone or target field not free anymore -- do not clone */
8186         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8187           Store[x][y] = EL_EMPTY;
8188       }
8189
8190       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8191       {
8192         if (IS_MV_DIAGONAL(MovDir[x][y]))
8193         {
8194           int diagonal_move_dir = MovDir[x][y];
8195           int stored = Store[x][y];
8196           int change_delay = 8;
8197           int graphic;
8198
8199           /* android is moving diagonally */
8200
8201           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8202
8203           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8204           GfxElement[x][y] = EL_EMC_ANDROID;
8205           GfxAction[x][y] = ACTION_SHRINKING;
8206           GfxDir[x][y] = diagonal_move_dir;
8207           ChangeDelay[x][y] = change_delay;
8208
8209           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8210                                    GfxDir[x][y]);
8211
8212           DrawLevelGraphicAnimation(x, y, graphic);
8213           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8214
8215           if (Feld[newx][newy] == EL_ACID)
8216           {
8217             SplashAcid(newx, newy);
8218
8219             return;
8220           }
8221
8222           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8223
8224           Store[newx][newy] = EL_EMC_ANDROID;
8225           GfxElement[newx][newy] = EL_EMC_ANDROID;
8226           GfxAction[newx][newy] = ACTION_GROWING;
8227           GfxDir[newx][newy] = diagonal_move_dir;
8228           ChangeDelay[newx][newy] = change_delay;
8229
8230           graphic = el_act_dir2img(GfxElement[newx][newy],
8231                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8232
8233           DrawLevelGraphicAnimation(newx, newy, graphic);
8234           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8235
8236           return;
8237         }
8238         else
8239         {
8240           Feld[newx][newy] = EL_EMPTY;
8241           DrawLevelField(newx, newy);
8242
8243           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8244         }
8245       }
8246       else if (!IS_FREE(newx, newy))
8247       {
8248 #if 0
8249         if (IS_PLAYER(x, y))
8250           DrawPlayerField(x, y);
8251         else
8252           DrawLevelField(x, y);
8253 #endif
8254
8255         return;
8256       }
8257     }
8258     else if (IS_CUSTOM_ELEMENT(element) &&
8259              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8260     {
8261       int new_element = Feld[newx][newy];
8262
8263       if (!IS_FREE(newx, newy))
8264       {
8265         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8266                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8267                       ACTION_BREAKING);
8268
8269         /* no element can dig solid indestructible elements */
8270         if (IS_INDESTRUCTIBLE(new_element) &&
8271             !IS_DIGGABLE(new_element) &&
8272             !IS_COLLECTIBLE(new_element))
8273           return;
8274
8275         if (AmoebaNr[newx][newy] &&
8276             (new_element == EL_AMOEBA_FULL ||
8277              new_element == EL_BD_AMOEBA ||
8278              new_element == EL_AMOEBA_GROWING))
8279         {
8280           AmoebaCnt[AmoebaNr[newx][newy]]--;
8281           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8282         }
8283
8284         if (IS_MOVING(newx, newy))
8285           RemoveMovingField(newx, newy);
8286         else
8287         {
8288           RemoveField(newx, newy);
8289           DrawLevelField(newx, newy);
8290         }
8291
8292         /* if digged element was about to explode, prevent the explosion */
8293         ExplodeField[newx][newy] = EX_TYPE_NONE;
8294
8295         PlayLevelSoundAction(x, y, action);
8296       }
8297
8298       Store[newx][newy] = EL_EMPTY;
8299 #if 1
8300       /* this makes it possible to leave the removed element again */
8301       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8302         Store[newx][newy] = new_element;
8303 #else
8304       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8305       {
8306         int move_leave_element = element_info[element].move_leave_element;
8307
8308         /* this makes it possible to leave the removed element again */
8309         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8310                              new_element : move_leave_element);
8311       }
8312 #endif
8313
8314       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8315       {
8316         RunnerVisit[x][y] = FrameCounter;
8317         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8318       }
8319     }
8320     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8321     {
8322       if (!IS_FREE(newx, newy))
8323       {
8324         if (IS_PLAYER(x, y))
8325           DrawPlayerField(x, y);
8326         else
8327           DrawLevelField(x, y);
8328
8329         return;
8330       }
8331       else
8332       {
8333         boolean wanna_flame = !RND(10);
8334         int dx = newx - x, dy = newy - y;
8335         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8336         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8337         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8338                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8339         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8340                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8341
8342         if ((wanna_flame ||
8343              IS_CLASSIC_ENEMY(element1) ||
8344              IS_CLASSIC_ENEMY(element2)) &&
8345             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8346             element1 != EL_FLAMES && element2 != EL_FLAMES)
8347         {
8348           ResetGfxAnimation(x, y);
8349           GfxAction[x][y] = ACTION_ATTACKING;
8350
8351           if (IS_PLAYER(x, y))
8352             DrawPlayerField(x, y);
8353           else
8354             DrawLevelField(x, y);
8355
8356           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8357
8358           MovDelay[x][y] = 50;
8359
8360           /* !!! */
8361 #if 0
8362           RemoveField(newx, newy);
8363 #endif
8364           Feld[newx][newy] = EL_FLAMES;
8365           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8366           {
8367 #if 0
8368             RemoveField(newx1, newy1);
8369 #endif
8370             Feld[newx1][newy1] = EL_FLAMES;
8371           }
8372           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8373           {
8374 #if 0
8375             RemoveField(newx2, newy2);
8376 #endif
8377             Feld[newx2][newy2] = EL_FLAMES;
8378           }
8379
8380           return;
8381         }
8382       }
8383     }
8384     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8385              Feld[newx][newy] == EL_DIAMOND)
8386     {
8387       if (IS_MOVING(newx, newy))
8388         RemoveMovingField(newx, newy);
8389       else
8390       {
8391         Feld[newx][newy] = EL_EMPTY;
8392         DrawLevelField(newx, newy);
8393       }
8394
8395       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8396     }
8397     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8398              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8399     {
8400       if (AmoebaNr[newx][newy])
8401       {
8402         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8403         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8404             Feld[newx][newy] == EL_BD_AMOEBA)
8405           AmoebaCnt[AmoebaNr[newx][newy]]--;
8406       }
8407
8408 #if 0
8409       /* !!! test !!! */
8410       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8411       {
8412         RemoveMovingField(newx, newy);
8413       }
8414 #else
8415       if (IS_MOVING(newx, newy))
8416       {
8417         RemoveMovingField(newx, newy);
8418       }
8419 #endif
8420       else
8421       {
8422         Feld[newx][newy] = EL_EMPTY;
8423         DrawLevelField(newx, newy);
8424       }
8425
8426       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8427     }
8428     else if ((element == EL_PACMAN || element == EL_MOLE)
8429              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8430     {
8431       if (AmoebaNr[newx][newy])
8432       {
8433         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8434         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8435             Feld[newx][newy] == EL_BD_AMOEBA)
8436           AmoebaCnt[AmoebaNr[newx][newy]]--;
8437       }
8438
8439       if (element == EL_MOLE)
8440       {
8441         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8442         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8443
8444         ResetGfxAnimation(x, y);
8445         GfxAction[x][y] = ACTION_DIGGING;
8446         DrawLevelField(x, y);
8447
8448         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8449
8450         return;                         /* wait for shrinking amoeba */
8451       }
8452       else      /* element == EL_PACMAN */
8453       {
8454         Feld[newx][newy] = EL_EMPTY;
8455         DrawLevelField(newx, newy);
8456         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8457       }
8458     }
8459     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8460              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8461               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8462     {
8463       /* wait for shrinking amoeba to completely disappear */
8464       return;
8465     }
8466     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8467     {
8468       /* object was running against a wall */
8469
8470       TurnRound(x, y);
8471
8472 #if 0
8473       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8474       if (move_pattern & MV_ANY_DIRECTION &&
8475           move_pattern == MovDir[x][y])
8476       {
8477         int blocking_element =
8478           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8479
8480         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8481                                  MovDir[x][y]);
8482
8483         element = Feld[x][y];   /* element might have changed */
8484       }
8485 #endif
8486
8487       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8488         DrawLevelElementAnimation(x, y, element);
8489
8490       if (DONT_TOUCH(element))
8491         TestIfBadThingTouchesPlayer(x, y);
8492
8493       return;
8494     }
8495
8496     InitMovingField(x, y, MovDir[x][y]);
8497
8498     PlayLevelSoundAction(x, y, ACTION_MOVING);
8499   }
8500
8501   if (MovDir[x][y])
8502     ContinueMoving(x, y);
8503 }
8504
8505 void ContinueMoving(int x, int y)
8506 {
8507   int element = Feld[x][y];
8508   struct ElementInfo *ei = &element_info[element];
8509   int direction = MovDir[x][y];
8510   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8511   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8512   int newx = x + dx, newy = y + dy;
8513   int stored = Store[x][y];
8514   int stored_new = Store[newx][newy];
8515   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8516   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8517   boolean last_line = (newy == lev_fieldy - 1);
8518
8519   MovPos[x][y] += getElementMoveStepsize(x, y);
8520
8521   if (pushed_by_player) /* special case: moving object pushed by player */
8522     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8523
8524   if (ABS(MovPos[x][y]) < TILEX)
8525   {
8526 #if 0
8527     int ee = Feld[x][y];
8528     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8529     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8530
8531     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8532            x, y, ABS(MovPos[x][y]),
8533            ee, gg, ff,
8534            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8535 #endif
8536
8537     DrawLevelField(x, y);
8538
8539     return;     /* element is still moving */
8540   }
8541
8542   /* element reached destination field */
8543
8544   Feld[x][y] = EL_EMPTY;
8545   Feld[newx][newy] = element;
8546   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8547
8548   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8549   {
8550     element = Feld[newx][newy] = EL_ACID;
8551   }
8552   else if (element == EL_MOLE)
8553   {
8554     Feld[x][y] = EL_SAND;
8555
8556     DrawLevelFieldCrumbledSandNeighbours(x, y);
8557   }
8558   else if (element == EL_QUICKSAND_FILLING)
8559   {
8560     element = Feld[newx][newy] = get_next_element(element);
8561     Store[newx][newy] = Store[x][y];
8562   }
8563   else if (element == EL_QUICKSAND_EMPTYING)
8564   {
8565     Feld[x][y] = get_next_element(element);
8566     element = Feld[newx][newy] = Store[x][y];
8567   }
8568   else if (element == EL_QUICKSAND_FAST_FILLING)
8569   {
8570     element = Feld[newx][newy] = get_next_element(element);
8571     Store[newx][newy] = Store[x][y];
8572   }
8573   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8574   {
8575     Feld[x][y] = get_next_element(element);
8576     element = Feld[newx][newy] = Store[x][y];
8577   }
8578   else if (element == EL_MAGIC_WALL_FILLING)
8579   {
8580     element = Feld[newx][newy] = get_next_element(element);
8581     if (!game.magic_wall_active)
8582       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8583     Store[newx][newy] = Store[x][y];
8584   }
8585   else if (element == EL_MAGIC_WALL_EMPTYING)
8586   {
8587     Feld[x][y] = get_next_element(element);
8588     if (!game.magic_wall_active)
8589       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8590     element = Feld[newx][newy] = Store[x][y];
8591
8592 #if USE_NEW_CUSTOM_VALUE
8593     InitField(newx, newy, FALSE);
8594 #endif
8595   }
8596   else if (element == EL_BD_MAGIC_WALL_FILLING)
8597   {
8598     element = Feld[newx][newy] = get_next_element(element);
8599     if (!game.magic_wall_active)
8600       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8601     Store[newx][newy] = Store[x][y];
8602   }
8603   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8604   {
8605     Feld[x][y] = get_next_element(element);
8606     if (!game.magic_wall_active)
8607       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8608     element = Feld[newx][newy] = Store[x][y];
8609
8610 #if USE_NEW_CUSTOM_VALUE
8611     InitField(newx, newy, FALSE);
8612 #endif
8613   }
8614   else if (element == EL_DC_MAGIC_WALL_FILLING)
8615   {
8616     element = Feld[newx][newy] = get_next_element(element);
8617     if (!game.magic_wall_active)
8618       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8619     Store[newx][newy] = Store[x][y];
8620   }
8621   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8622   {
8623     Feld[x][y] = get_next_element(element);
8624     if (!game.magic_wall_active)
8625       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8626     element = Feld[newx][newy] = Store[x][y];
8627
8628 #if USE_NEW_CUSTOM_VALUE
8629     InitField(newx, newy, FALSE);
8630 #endif
8631   }
8632   else if (element == EL_AMOEBA_DROPPING)
8633   {
8634     Feld[x][y] = get_next_element(element);
8635     element = Feld[newx][newy] = Store[x][y];
8636   }
8637   else if (element == EL_SOKOBAN_OBJECT)
8638   {
8639     if (Back[x][y])
8640       Feld[x][y] = Back[x][y];
8641
8642     if (Back[newx][newy])
8643       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8644
8645     Back[x][y] = Back[newx][newy] = 0;
8646   }
8647
8648   Store[x][y] = EL_EMPTY;
8649   MovPos[x][y] = 0;
8650   MovDir[x][y] = 0;
8651   MovDelay[x][y] = 0;
8652
8653   MovDelay[newx][newy] = 0;
8654
8655   if (CAN_CHANGE_OR_HAS_ACTION(element))
8656   {
8657     /* copy element change control values to new field */
8658     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8659     ChangePage[newx][newy]  = ChangePage[x][y];
8660     ChangeCount[newx][newy] = ChangeCount[x][y];
8661     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8662   }
8663
8664 #if USE_NEW_CUSTOM_VALUE
8665   CustomValue[newx][newy] = CustomValue[x][y];
8666 #endif
8667
8668   ChangeDelay[x][y] = 0;
8669   ChangePage[x][y] = -1;
8670   ChangeCount[x][y] = 0;
8671   ChangeEvent[x][y] = -1;
8672
8673 #if USE_NEW_CUSTOM_VALUE
8674   CustomValue[x][y] = 0;
8675 #endif
8676
8677   /* copy animation control values to new field */
8678   GfxFrame[newx][newy]  = GfxFrame[x][y];
8679   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8680   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8681   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8682
8683   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8684
8685   /* some elements can leave other elements behind after moving */
8686 #if 1
8687   if (ei->move_leave_element != EL_EMPTY &&
8688       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8689       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8690 #else
8691   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8692       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8693       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8694 #endif
8695   {
8696     int move_leave_element = ei->move_leave_element;
8697
8698 #if 1
8699 #if 1
8700     /* this makes it possible to leave the removed element again */
8701     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8702       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8703 #else
8704     /* this makes it possible to leave the removed element again */
8705     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8706       move_leave_element = stored;
8707 #endif
8708 #else
8709     /* this makes it possible to leave the removed element again */
8710     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8711         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8712       move_leave_element = stored;
8713 #endif
8714
8715     Feld[x][y] = move_leave_element;
8716
8717     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8718       MovDir[x][y] = direction;
8719
8720     InitField(x, y, FALSE);
8721
8722     if (GFX_CRUMBLED(Feld[x][y]))
8723       DrawLevelFieldCrumbledSandNeighbours(x, y);
8724
8725     if (ELEM_IS_PLAYER(move_leave_element))
8726       RelocatePlayer(x, y, move_leave_element);
8727   }
8728
8729   /* do this after checking for left-behind element */
8730   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8731
8732   if (!CAN_MOVE(element) ||
8733       (CAN_FALL(element) && direction == MV_DOWN &&
8734        (element == EL_SPRING ||
8735         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8736         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8737     GfxDir[x][y] = MovDir[newx][newy] = 0;
8738
8739   DrawLevelField(x, y);
8740   DrawLevelField(newx, newy);
8741
8742   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8743
8744   /* prevent pushed element from moving on in pushed direction */
8745   if (pushed_by_player && CAN_MOVE(element) &&
8746       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8747       !(element_info[element].move_pattern & direction))
8748     TurnRound(newx, newy);
8749
8750   /* prevent elements on conveyor belt from moving on in last direction */
8751   if (pushed_by_conveyor && CAN_FALL(element) &&
8752       direction & MV_HORIZONTAL)
8753     MovDir[newx][newy] = 0;
8754
8755   if (!pushed_by_player)
8756   {
8757     int nextx = newx + dx, nexty = newy + dy;
8758     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8759
8760     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8761
8762     if (CAN_FALL(element) && direction == MV_DOWN)
8763       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8764
8765     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8766       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8767
8768 #if USE_FIX_IMPACT_COLLISION
8769     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8770       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8771 #endif
8772   }
8773
8774   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8775   {
8776     TestIfBadThingTouchesPlayer(newx, newy);
8777     TestIfBadThingTouchesFriend(newx, newy);
8778
8779     if (!IS_CUSTOM_ELEMENT(element))
8780       TestIfBadThingTouchesOtherBadThing(newx, newy);
8781   }
8782   else if (element == EL_PENGUIN)
8783     TestIfFriendTouchesBadThing(newx, newy);
8784
8785   /* give the player one last chance (one more frame) to move away */
8786   if (CAN_FALL(element) && direction == MV_DOWN &&
8787       (last_line || (!IS_FREE(x, newy + 1) &&
8788                      (!IS_PLAYER(x, newy + 1) ||
8789                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8790     Impact(x, newy);
8791
8792   if (pushed_by_player && !game.use_change_when_pushing_bug)
8793   {
8794     int push_side = MV_DIR_OPPOSITE(direction);
8795     struct PlayerInfo *player = PLAYERINFO(x, y);
8796
8797     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8798                                player->index_bit, push_side);
8799     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8800                                         player->index_bit, push_side);
8801   }
8802
8803   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8804     MovDelay[newx][newy] = 1;
8805
8806   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8807
8808   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8809
8810 #if 0
8811   if (ChangePage[newx][newy] != -1)             /* delayed change */
8812   {
8813     int page = ChangePage[newx][newy];
8814     struct ElementChangeInfo *change = &ei->change_page[page];
8815
8816     ChangePage[newx][newy] = -1;
8817
8818     if (change->can_change)
8819     {
8820       if (ChangeElement(newx, newy, element, page))
8821       {
8822         if (change->post_change_function)
8823           change->post_change_function(newx, newy);
8824       }
8825     }
8826
8827     if (change->has_action)
8828       ExecuteCustomElementAction(newx, newy, element, page);
8829   }
8830 #endif
8831
8832   TestIfElementHitsCustomElement(newx, newy, direction);
8833   TestIfPlayerTouchesCustomElement(newx, newy);
8834   TestIfElementTouchesCustomElement(newx, newy);
8835
8836   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8837       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8838     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8839                              MV_DIR_OPPOSITE(direction));
8840 }
8841
8842 int AmoebeNachbarNr(int ax, int ay)
8843 {
8844   int i;
8845   int element = Feld[ax][ay];
8846   int group_nr = 0;
8847   static int xy[4][2] =
8848   {
8849     { 0, -1 },
8850     { -1, 0 },
8851     { +1, 0 },
8852     { 0, +1 }
8853   };
8854
8855   for (i = 0; i < NUM_DIRECTIONS; i++)
8856   {
8857     int x = ax + xy[i][0];
8858     int y = ay + xy[i][1];
8859
8860     if (!IN_LEV_FIELD(x, y))
8861       continue;
8862
8863     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8864       group_nr = AmoebaNr[x][y];
8865   }
8866
8867   return group_nr;
8868 }
8869
8870 void AmoebenVereinigen(int ax, int ay)
8871 {
8872   int i, x, y, xx, yy;
8873   int new_group_nr = AmoebaNr[ax][ay];
8874   static int xy[4][2] =
8875   {
8876     { 0, -1 },
8877     { -1, 0 },
8878     { +1, 0 },
8879     { 0, +1 }
8880   };
8881
8882   if (new_group_nr == 0)
8883     return;
8884
8885   for (i = 0; i < NUM_DIRECTIONS; i++)
8886   {
8887     x = ax + xy[i][0];
8888     y = ay + xy[i][1];
8889
8890     if (!IN_LEV_FIELD(x, y))
8891       continue;
8892
8893     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8894          Feld[x][y] == EL_BD_AMOEBA ||
8895          Feld[x][y] == EL_AMOEBA_DEAD) &&
8896         AmoebaNr[x][y] != new_group_nr)
8897     {
8898       int old_group_nr = AmoebaNr[x][y];
8899
8900       if (old_group_nr == 0)
8901         return;
8902
8903       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8904       AmoebaCnt[old_group_nr] = 0;
8905       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8906       AmoebaCnt2[old_group_nr] = 0;
8907
8908       SCAN_PLAYFIELD(xx, yy)
8909       {
8910         if (AmoebaNr[xx][yy] == old_group_nr)
8911           AmoebaNr[xx][yy] = new_group_nr;
8912       }
8913     }
8914   }
8915 }
8916
8917 void AmoebeUmwandeln(int ax, int ay)
8918 {
8919   int i, x, y;
8920
8921   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8922   {
8923     int group_nr = AmoebaNr[ax][ay];
8924
8925 #ifdef DEBUG
8926     if (group_nr == 0)
8927     {
8928       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8929       printf("AmoebeUmwandeln(): This should never happen!\n");
8930       return;
8931     }
8932 #endif
8933
8934     SCAN_PLAYFIELD(x, y)
8935     {
8936       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8937       {
8938         AmoebaNr[x][y] = 0;
8939         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8940       }
8941     }
8942
8943     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8944                             SND_AMOEBA_TURNING_TO_GEM :
8945                             SND_AMOEBA_TURNING_TO_ROCK));
8946     Bang(ax, ay);
8947   }
8948   else
8949   {
8950     static int xy[4][2] =
8951     {
8952       { 0, -1 },
8953       { -1, 0 },
8954       { +1, 0 },
8955       { 0, +1 }
8956     };
8957
8958     for (i = 0; i < NUM_DIRECTIONS; i++)
8959     {
8960       x = ax + xy[i][0];
8961       y = ay + xy[i][1];
8962
8963       if (!IN_LEV_FIELD(x, y))
8964         continue;
8965
8966       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8967       {
8968         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8969                               SND_AMOEBA_TURNING_TO_GEM :
8970                               SND_AMOEBA_TURNING_TO_ROCK));
8971         Bang(x, y);
8972       }
8973     }
8974   }
8975 }
8976
8977 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8978 {
8979   int x, y;
8980   int group_nr = AmoebaNr[ax][ay];
8981   boolean done = FALSE;
8982
8983 #ifdef DEBUG
8984   if (group_nr == 0)
8985   {
8986     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8987     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8988     return;
8989   }
8990 #endif
8991
8992   SCAN_PLAYFIELD(x, y)
8993   {
8994     if (AmoebaNr[x][y] == group_nr &&
8995         (Feld[x][y] == EL_AMOEBA_DEAD ||
8996          Feld[x][y] == EL_BD_AMOEBA ||
8997          Feld[x][y] == EL_AMOEBA_GROWING))
8998     {
8999       AmoebaNr[x][y] = 0;
9000       Feld[x][y] = new_element;
9001       InitField(x, y, FALSE);
9002       DrawLevelField(x, y);
9003       done = TRUE;
9004     }
9005   }
9006
9007   if (done)
9008     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9009                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9010                             SND_BD_AMOEBA_TURNING_TO_GEM));
9011 }
9012
9013 void AmoebeWaechst(int x, int y)
9014 {
9015   static unsigned long sound_delay = 0;
9016   static unsigned long sound_delay_value = 0;
9017
9018   if (!MovDelay[x][y])          /* start new growing cycle */
9019   {
9020     MovDelay[x][y] = 7;
9021
9022     if (DelayReached(&sound_delay, sound_delay_value))
9023     {
9024       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9025       sound_delay_value = 30;
9026     }
9027   }
9028
9029   if (MovDelay[x][y])           /* wait some time before growing bigger */
9030   {
9031     MovDelay[x][y]--;
9032     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9033     {
9034       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9035                                            6 - MovDelay[x][y]);
9036
9037       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9038     }
9039
9040     if (!MovDelay[x][y])
9041     {
9042       Feld[x][y] = Store[x][y];
9043       Store[x][y] = 0;
9044       DrawLevelField(x, y);
9045     }
9046   }
9047 }
9048
9049 void AmoebaDisappearing(int x, int y)
9050 {
9051   static unsigned long sound_delay = 0;
9052   static unsigned long sound_delay_value = 0;
9053
9054   if (!MovDelay[x][y])          /* start new shrinking cycle */
9055   {
9056     MovDelay[x][y] = 7;
9057
9058     if (DelayReached(&sound_delay, sound_delay_value))
9059       sound_delay_value = 30;
9060   }
9061
9062   if (MovDelay[x][y])           /* wait some time before shrinking */
9063   {
9064     MovDelay[x][y]--;
9065     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9066     {
9067       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9068                                            6 - MovDelay[x][y]);
9069
9070       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9071     }
9072
9073     if (!MovDelay[x][y])
9074     {
9075       Feld[x][y] = EL_EMPTY;
9076       DrawLevelField(x, y);
9077
9078       /* don't let mole enter this field in this cycle;
9079          (give priority to objects falling to this field from above) */
9080       Stop[x][y] = TRUE;
9081     }
9082   }
9083 }
9084
9085 void AmoebeAbleger(int ax, int ay)
9086 {
9087   int i;
9088   int element = Feld[ax][ay];
9089   int graphic = el2img(element);
9090   int newax = ax, neway = ay;
9091   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9092   static int xy[4][2] =
9093   {
9094     { 0, -1 },
9095     { -1, 0 },
9096     { +1, 0 },
9097     { 0, +1 }
9098   };
9099
9100   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9101   {
9102     Feld[ax][ay] = EL_AMOEBA_DEAD;
9103     DrawLevelField(ax, ay);
9104     return;
9105   }
9106
9107   if (IS_ANIMATED(graphic))
9108     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9109
9110   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9111     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9112
9113   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9114   {
9115     MovDelay[ax][ay]--;
9116     if (MovDelay[ax][ay])
9117       return;
9118   }
9119
9120   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9121   {
9122     int start = RND(4);
9123     int x = ax + xy[start][0];
9124     int y = ay + xy[start][1];
9125
9126     if (!IN_LEV_FIELD(x, y))
9127       return;
9128
9129     if (IS_FREE(x, y) ||
9130         CAN_GROW_INTO(Feld[x][y]) ||
9131         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9132         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9133     {
9134       newax = x;
9135       neway = y;
9136     }
9137
9138     if (newax == ax && neway == ay)
9139       return;
9140   }
9141   else                          /* normal or "filled" (BD style) amoeba */
9142   {
9143     int start = RND(4);
9144     boolean waiting_for_player = FALSE;
9145
9146     for (i = 0; i < NUM_DIRECTIONS; i++)
9147     {
9148       int j = (start + i) % 4;
9149       int x = ax + xy[j][0];
9150       int y = ay + xy[j][1];
9151
9152       if (!IN_LEV_FIELD(x, y))
9153         continue;
9154
9155       if (IS_FREE(x, y) ||
9156           CAN_GROW_INTO(Feld[x][y]) ||
9157           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9158           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9159       {
9160         newax = x;
9161         neway = y;
9162         break;
9163       }
9164       else if (IS_PLAYER(x, y))
9165         waiting_for_player = TRUE;
9166     }
9167
9168     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9169     {
9170       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9171       {
9172         Feld[ax][ay] = EL_AMOEBA_DEAD;
9173         DrawLevelField(ax, ay);
9174         AmoebaCnt[AmoebaNr[ax][ay]]--;
9175
9176         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9177         {
9178           if (element == EL_AMOEBA_FULL)
9179             AmoebeUmwandeln(ax, ay);
9180           else if (element == EL_BD_AMOEBA)
9181             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9182         }
9183       }
9184       return;
9185     }
9186     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9187     {
9188       /* amoeba gets larger by growing in some direction */
9189
9190       int new_group_nr = AmoebaNr[ax][ay];
9191
9192 #ifdef DEBUG
9193   if (new_group_nr == 0)
9194   {
9195     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9196     printf("AmoebeAbleger(): This should never happen!\n");
9197     return;
9198   }
9199 #endif
9200
9201       AmoebaNr[newax][neway] = new_group_nr;
9202       AmoebaCnt[new_group_nr]++;
9203       AmoebaCnt2[new_group_nr]++;
9204
9205       /* if amoeba touches other amoeba(s) after growing, unify them */
9206       AmoebenVereinigen(newax, neway);
9207
9208       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9209       {
9210         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9211         return;
9212       }
9213     }
9214   }
9215
9216   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9217       (neway == lev_fieldy - 1 && newax != ax))
9218   {
9219     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9220     Store[newax][neway] = element;
9221   }
9222   else if (neway == ay || element == EL_EMC_DRIPPER)
9223   {
9224     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9225
9226     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9227   }
9228   else
9229   {
9230     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9231     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9232     Store[ax][ay] = EL_AMOEBA_DROP;
9233     ContinueMoving(ax, ay);
9234     return;
9235   }
9236
9237   DrawLevelField(newax, neway);
9238 }
9239
9240 void Life(int ax, int ay)
9241 {
9242   int x1, y1, x2, y2;
9243   int life_time = 40;
9244   int element = Feld[ax][ay];
9245   int graphic = el2img(element);
9246   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9247                          level.biomaze);
9248   boolean changed = FALSE;
9249
9250   if (IS_ANIMATED(graphic))
9251     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9252
9253   if (Stop[ax][ay])
9254     return;
9255
9256   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9257     MovDelay[ax][ay] = life_time;
9258
9259   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9260   {
9261     MovDelay[ax][ay]--;
9262     if (MovDelay[ax][ay])
9263       return;
9264   }
9265
9266   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9267   {
9268     int xx = ax+x1, yy = ay+y1;
9269     int nachbarn = 0;
9270
9271     if (!IN_LEV_FIELD(xx, yy))
9272       continue;
9273
9274     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9275     {
9276       int x = xx+x2, y = yy+y2;
9277
9278       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9279         continue;
9280
9281       if (((Feld[x][y] == element ||
9282             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9283            !Stop[x][y]) ||
9284           (IS_FREE(x, y) && Stop[x][y]))
9285         nachbarn++;
9286     }
9287
9288     if (xx == ax && yy == ay)           /* field in the middle */
9289     {
9290       if (nachbarn < life_parameter[0] ||
9291           nachbarn > life_parameter[1])
9292       {
9293         Feld[xx][yy] = EL_EMPTY;
9294         if (!Stop[xx][yy])
9295           DrawLevelField(xx, yy);
9296         Stop[xx][yy] = TRUE;
9297         changed = TRUE;
9298       }
9299     }
9300     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9301     {                                   /* free border field */
9302       if (nachbarn >= life_parameter[2] &&
9303           nachbarn <= life_parameter[3])
9304       {
9305         Feld[xx][yy] = element;
9306         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9307         if (!Stop[xx][yy])
9308           DrawLevelField(xx, yy);
9309         Stop[xx][yy] = TRUE;
9310         changed = TRUE;
9311       }
9312     }
9313   }
9314
9315   if (changed)
9316     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9317                    SND_GAME_OF_LIFE_GROWING);
9318 }
9319
9320 static void InitRobotWheel(int x, int y)
9321 {
9322   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9323 }
9324
9325 static void RunRobotWheel(int x, int y)
9326 {
9327   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9328 }
9329
9330 static void StopRobotWheel(int x, int y)
9331 {
9332   if (ZX == x && ZY == y)
9333   {
9334     ZX = ZY = -1;
9335
9336     game.robot_wheel_active = FALSE;
9337   }
9338 }
9339
9340 static void InitTimegateWheel(int x, int y)
9341 {
9342   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9343 }
9344
9345 static void RunTimegateWheel(int x, int y)
9346 {
9347   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9348 }
9349
9350 static void InitMagicBallDelay(int x, int y)
9351 {
9352 #if 1
9353   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9354 #else
9355   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9356 #endif
9357 }
9358
9359 static void ActivateMagicBall(int bx, int by)
9360 {
9361   int x, y;
9362
9363   if (level.ball_random)
9364   {
9365     int pos_border = RND(8);    /* select one of the eight border elements */
9366     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9367     int xx = pos_content % 3;
9368     int yy = pos_content / 3;
9369
9370     x = bx - 1 + xx;
9371     y = by - 1 + yy;
9372
9373     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9374       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9375   }
9376   else
9377   {
9378     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9379     {
9380       int xx = x - bx + 1;
9381       int yy = y - by + 1;
9382
9383       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9384         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9385     }
9386   }
9387
9388   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9389 }
9390
9391 void CheckExit(int x, int y)
9392 {
9393   if (local_player->gems_still_needed > 0 ||
9394       local_player->sokobanfields_still_needed > 0 ||
9395       local_player->lights_still_needed > 0)
9396   {
9397     int element = Feld[x][y];
9398     int graphic = el2img(element);
9399
9400     if (IS_ANIMATED(graphic))
9401       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9402
9403     return;
9404   }
9405
9406   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9407     return;
9408
9409   Feld[x][y] = EL_EXIT_OPENING;
9410
9411   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9412 }
9413
9414 void CheckExitEM(int x, int y)
9415 {
9416   if (local_player->gems_still_needed > 0 ||
9417       local_player->sokobanfields_still_needed > 0 ||
9418       local_player->lights_still_needed > 0)
9419   {
9420     int element = Feld[x][y];
9421     int graphic = el2img(element);
9422
9423     if (IS_ANIMATED(graphic))
9424       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9425
9426     return;
9427   }
9428
9429   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9430     return;
9431
9432   Feld[x][y] = EL_EM_EXIT_OPENING;
9433
9434   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9435 }
9436
9437 void CheckExitSteel(int x, int y)
9438 {
9439   if (local_player->gems_still_needed > 0 ||
9440       local_player->sokobanfields_still_needed > 0 ||
9441       local_player->lights_still_needed > 0)
9442   {
9443     int element = Feld[x][y];
9444     int graphic = el2img(element);
9445
9446     if (IS_ANIMATED(graphic))
9447       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9448
9449     return;
9450   }
9451
9452   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9453     return;
9454
9455   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9456
9457   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9458 }
9459
9460 void CheckExitSteelEM(int x, int y)
9461 {
9462   if (local_player->gems_still_needed > 0 ||
9463       local_player->sokobanfields_still_needed > 0 ||
9464       local_player->lights_still_needed > 0)
9465   {
9466     int element = Feld[x][y];
9467     int graphic = el2img(element);
9468
9469     if (IS_ANIMATED(graphic))
9470       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9471
9472     return;
9473   }
9474
9475   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9476     return;
9477
9478   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9479
9480   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9481 }
9482
9483 void CheckExitSP(int x, int y)
9484 {
9485   if (local_player->gems_still_needed > 0)
9486   {
9487     int element = Feld[x][y];
9488     int graphic = el2img(element);
9489
9490     if (IS_ANIMATED(graphic))
9491       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9492
9493     return;
9494   }
9495
9496   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9497     return;
9498
9499   Feld[x][y] = EL_SP_EXIT_OPENING;
9500
9501   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9502 }
9503
9504 static void CloseAllOpenTimegates()
9505 {
9506   int x, y;
9507
9508   SCAN_PLAYFIELD(x, y)
9509   {
9510     int element = Feld[x][y];
9511
9512     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9513     {
9514       Feld[x][y] = EL_TIMEGATE_CLOSING;
9515
9516       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9517     }
9518   }
9519 }
9520
9521 void DrawTwinkleOnField(int x, int y)
9522 {
9523   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9524     return;
9525
9526   if (Feld[x][y] == EL_BD_DIAMOND)
9527     return;
9528
9529   if (MovDelay[x][y] == 0)      /* next animation frame */
9530     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9531
9532   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9533   {
9534     MovDelay[x][y]--;
9535
9536     DrawLevelElementAnimation(x, y, Feld[x][y]);
9537
9538     if (MovDelay[x][y] != 0)
9539     {
9540       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9541                                            10 - MovDelay[x][y]);
9542
9543       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9544     }
9545   }
9546 }
9547
9548 void MauerWaechst(int x, int y)
9549 {
9550   int delay = 6;
9551
9552   if (!MovDelay[x][y])          /* next animation frame */
9553     MovDelay[x][y] = 3 * delay;
9554
9555   if (MovDelay[x][y])           /* wait some time before next frame */
9556   {
9557     MovDelay[x][y]--;
9558
9559     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9560     {
9561       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9562       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9563
9564       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9565     }
9566
9567     if (!MovDelay[x][y])
9568     {
9569       if (MovDir[x][y] == MV_LEFT)
9570       {
9571         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9572           DrawLevelField(x - 1, y);
9573       }
9574       else if (MovDir[x][y] == MV_RIGHT)
9575       {
9576         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9577           DrawLevelField(x + 1, y);
9578       }
9579       else if (MovDir[x][y] == MV_UP)
9580       {
9581         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9582           DrawLevelField(x, y - 1);
9583       }
9584       else
9585       {
9586         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9587           DrawLevelField(x, y + 1);
9588       }
9589
9590       Feld[x][y] = Store[x][y];
9591       Store[x][y] = 0;
9592       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9593       DrawLevelField(x, y);
9594     }
9595   }
9596 }
9597
9598 void MauerAbleger(int ax, int ay)
9599 {
9600   int element = Feld[ax][ay];
9601   int graphic = el2img(element);
9602   boolean oben_frei = FALSE, unten_frei = FALSE;
9603   boolean links_frei = FALSE, rechts_frei = FALSE;
9604   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9605   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9606   boolean new_wall = FALSE;
9607
9608   if (IS_ANIMATED(graphic))
9609     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9610
9611   if (!MovDelay[ax][ay])        /* start building new wall */
9612     MovDelay[ax][ay] = 6;
9613
9614   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9615   {
9616     MovDelay[ax][ay]--;
9617     if (MovDelay[ax][ay])
9618       return;
9619   }
9620
9621   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9622     oben_frei = TRUE;
9623   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9624     unten_frei = TRUE;
9625   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9626     links_frei = TRUE;
9627   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9628     rechts_frei = TRUE;
9629
9630   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9631       element == EL_EXPANDABLE_WALL_ANY)
9632   {
9633     if (oben_frei)
9634     {
9635       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9636       Store[ax][ay-1] = element;
9637       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9638       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9639         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9640                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9641       new_wall = TRUE;
9642     }
9643     if (unten_frei)
9644     {
9645       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9646       Store[ax][ay+1] = element;
9647       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9648       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9649         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9650                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9651       new_wall = TRUE;
9652     }
9653   }
9654
9655   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9656       element == EL_EXPANDABLE_WALL_ANY ||
9657       element == EL_EXPANDABLE_WALL ||
9658       element == EL_BD_EXPANDABLE_WALL)
9659   {
9660     if (links_frei)
9661     {
9662       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9663       Store[ax-1][ay] = element;
9664       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9665       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9666         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9667                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9668       new_wall = TRUE;
9669     }
9670
9671     if (rechts_frei)
9672     {
9673       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9674       Store[ax+1][ay] = element;
9675       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9676       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9677         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9678                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9679       new_wall = TRUE;
9680     }
9681   }
9682
9683   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9684     DrawLevelField(ax, ay);
9685
9686   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9687     oben_massiv = TRUE;
9688   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9689     unten_massiv = TRUE;
9690   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9691     links_massiv = TRUE;
9692   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9693     rechts_massiv = TRUE;
9694
9695   if (((oben_massiv && unten_massiv) ||
9696        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9697        element == EL_EXPANDABLE_WALL) &&
9698       ((links_massiv && rechts_massiv) ||
9699        element == EL_EXPANDABLE_WALL_VERTICAL))
9700     Feld[ax][ay] = EL_WALL;
9701
9702   if (new_wall)
9703     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9704 }
9705
9706 void MauerAblegerStahl(int ax, int ay)
9707 {
9708   int element = Feld[ax][ay];
9709   int graphic = el2img(element);
9710   boolean oben_frei = FALSE, unten_frei = FALSE;
9711   boolean links_frei = FALSE, rechts_frei = FALSE;
9712   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9713   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9714   boolean new_wall = FALSE;
9715
9716   if (IS_ANIMATED(graphic))
9717     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9718
9719   if (!MovDelay[ax][ay])        /* start building new wall */
9720     MovDelay[ax][ay] = 6;
9721
9722   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9723   {
9724     MovDelay[ax][ay]--;
9725     if (MovDelay[ax][ay])
9726       return;
9727   }
9728
9729   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9730     oben_frei = TRUE;
9731   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9732     unten_frei = TRUE;
9733   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9734     links_frei = TRUE;
9735   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9736     rechts_frei = TRUE;
9737
9738   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9739       element == EL_EXPANDABLE_STEELWALL_ANY)
9740   {
9741     if (oben_frei)
9742     {
9743       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9744       Store[ax][ay-1] = element;
9745       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9746       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9747         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9748                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9749       new_wall = TRUE;
9750     }
9751     if (unten_frei)
9752     {
9753       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9754       Store[ax][ay+1] = element;
9755       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9756       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9757         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9758                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9759       new_wall = TRUE;
9760     }
9761   }
9762
9763   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9764       element == EL_EXPANDABLE_STEELWALL_ANY)
9765   {
9766     if (links_frei)
9767     {
9768       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9769       Store[ax-1][ay] = element;
9770       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9771       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9772         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9773                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9774       new_wall = TRUE;
9775     }
9776
9777     if (rechts_frei)
9778     {
9779       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9780       Store[ax+1][ay] = element;
9781       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9782       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9783         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9784                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9785       new_wall = TRUE;
9786     }
9787   }
9788
9789   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9790     oben_massiv = TRUE;
9791   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9792     unten_massiv = TRUE;
9793   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9794     links_massiv = TRUE;
9795   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9796     rechts_massiv = TRUE;
9797
9798   if (((oben_massiv && unten_massiv) ||
9799        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9800       ((links_massiv && rechts_massiv) ||
9801        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9802     Feld[ax][ay] = EL_WALL;
9803
9804   if (new_wall)
9805     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9806 }
9807
9808 void CheckForDragon(int x, int y)
9809 {
9810   int i, j;
9811   boolean dragon_found = FALSE;
9812   static int xy[4][2] =
9813   {
9814     { 0, -1 },
9815     { -1, 0 },
9816     { +1, 0 },
9817     { 0, +1 }
9818   };
9819
9820   for (i = 0; i < NUM_DIRECTIONS; i++)
9821   {
9822     for (j = 0; j < 4; j++)
9823     {
9824       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9825
9826       if (IN_LEV_FIELD(xx, yy) &&
9827           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9828       {
9829         if (Feld[xx][yy] == EL_DRAGON)
9830           dragon_found = TRUE;
9831       }
9832       else
9833         break;
9834     }
9835   }
9836
9837   if (!dragon_found)
9838   {
9839     for (i = 0; i < NUM_DIRECTIONS; i++)
9840     {
9841       for (j = 0; j < 3; j++)
9842       {
9843         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9844   
9845         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9846         {
9847           Feld[xx][yy] = EL_EMPTY;
9848           DrawLevelField(xx, yy);
9849         }
9850         else
9851           break;
9852       }
9853     }
9854   }
9855 }
9856
9857 static void InitBuggyBase(int x, int y)
9858 {
9859   int element = Feld[x][y];
9860   int activating_delay = FRAMES_PER_SECOND / 4;
9861
9862   ChangeDelay[x][y] =
9863     (element == EL_SP_BUGGY_BASE ?
9864      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9865      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9866      activating_delay :
9867      element == EL_SP_BUGGY_BASE_ACTIVE ?
9868      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9869 }
9870
9871 static void WarnBuggyBase(int x, int y)
9872 {
9873   int i;
9874   static int xy[4][2] =
9875   {
9876     { 0, -1 },
9877     { -1, 0 },
9878     { +1, 0 },
9879     { 0, +1 }
9880   };
9881
9882   for (i = 0; i < NUM_DIRECTIONS; i++)
9883   {
9884     int xx = x + xy[i][0];
9885     int yy = y + xy[i][1];
9886
9887     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9888     {
9889       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9890
9891       break;
9892     }
9893   }
9894 }
9895
9896 static void InitTrap(int x, int y)
9897 {
9898   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9899 }
9900
9901 static void ActivateTrap(int x, int y)
9902 {
9903   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9904 }
9905
9906 static void ChangeActiveTrap(int x, int y)
9907 {
9908   int graphic = IMG_TRAP_ACTIVE;
9909
9910   /* if new animation frame was drawn, correct crumbled sand border */
9911   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9912     DrawLevelFieldCrumbledSand(x, y);
9913 }
9914
9915 static int getSpecialActionElement(int element, int number, int base_element)
9916 {
9917   return (element != EL_EMPTY ? element :
9918           number != -1 ? base_element + number - 1 :
9919           EL_EMPTY);
9920 }
9921
9922 static int getModifiedActionNumber(int value_old, int operator, int operand,
9923                                    int value_min, int value_max)
9924 {
9925   int value_new = (operator == CA_MODE_SET      ? operand :
9926                    operator == CA_MODE_ADD      ? value_old + operand :
9927                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9928                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9929                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9930                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9931                    value_old);
9932
9933   return (value_new < value_min ? value_min :
9934           value_new > value_max ? value_max :
9935           value_new);
9936 }
9937
9938 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9939 {
9940   struct ElementInfo *ei = &element_info[element];
9941   struct ElementChangeInfo *change = &ei->change_page[page];
9942   int target_element = change->target_element;
9943   int action_type = change->action_type;
9944   int action_mode = change->action_mode;
9945   int action_arg = change->action_arg;
9946   int i;
9947
9948   if (!change->has_action)
9949     return;
9950
9951   /* ---------- determine action paramater values -------------------------- */
9952
9953   int level_time_value =
9954     (level.time > 0 ? TimeLeft :
9955      TimePlayed);
9956
9957   int action_arg_element =
9958     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9959      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9960      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9961      EL_EMPTY);
9962
9963   int action_arg_direction =
9964     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9965      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9966      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9967      change->actual_trigger_side :
9968      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9969      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9970      MV_NONE);
9971
9972   int action_arg_number_min =
9973     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9974      CA_ARG_MIN);
9975
9976   int action_arg_number_max =
9977     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9978      action_type == CA_SET_LEVEL_GEMS ? 999 :
9979      action_type == CA_SET_LEVEL_TIME ? 9999 :
9980      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9981      action_type == CA_SET_CE_VALUE ? 9999 :
9982      action_type == CA_SET_CE_SCORE ? 9999 :
9983      CA_ARG_MAX);
9984
9985   int action_arg_number_reset =
9986     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9987      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9988      action_type == CA_SET_LEVEL_TIME ? level.time :
9989      action_type == CA_SET_LEVEL_SCORE ? 0 :
9990 #if USE_NEW_CUSTOM_VALUE
9991      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9992 #else
9993      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9994 #endif
9995      action_type == CA_SET_CE_SCORE ? 0 :
9996      0);
9997
9998   int action_arg_number =
9999     (action_arg <= CA_ARG_MAX ? action_arg :
10000      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10001      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10002      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10003      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10004      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10005      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10006 #if USE_NEW_CUSTOM_VALUE
10007      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10008 #else
10009      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10010 #endif
10011      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10012      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10013      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10014      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10015      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10016      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10017      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10018      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10019      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10020      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10021      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10022      -1);
10023
10024   int action_arg_number_old =
10025     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10026      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10027      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10028      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10029      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10030      0);
10031
10032   int action_arg_number_new =
10033     getModifiedActionNumber(action_arg_number_old,
10034                             action_mode, action_arg_number,
10035                             action_arg_number_min, action_arg_number_max);
10036
10037 #if 1
10038   int trigger_player_bits = change->actual_trigger_player_bits;
10039 #else
10040   int trigger_player_bits =
10041     (change->actual_trigger_player >= EL_PLAYER_1 &&
10042      change->actual_trigger_player <= EL_PLAYER_4 ?
10043      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10044      PLAYER_BITS_ANY);
10045 #endif
10046
10047   int action_arg_player_bits =
10048     (action_arg >= CA_ARG_PLAYER_1 &&
10049      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10050      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10051      PLAYER_BITS_ANY);
10052
10053   /* ---------- execute action  -------------------------------------------- */
10054
10055   switch (action_type)
10056   {
10057     case CA_NO_ACTION:
10058     {
10059       return;
10060     }
10061
10062     /* ---------- level actions  ------------------------------------------- */
10063
10064     case CA_RESTART_LEVEL:
10065     {
10066       game.restart_level = TRUE;
10067
10068       break;
10069     }
10070
10071     case CA_SHOW_ENVELOPE:
10072     {
10073       int element = getSpecialActionElement(action_arg_element,
10074                                             action_arg_number, EL_ENVELOPE_1);
10075
10076       if (IS_ENVELOPE(element))
10077         local_player->show_envelope = element;
10078
10079       break;
10080     }
10081
10082     case CA_SET_LEVEL_TIME:
10083     {
10084       if (level.time > 0)       /* only modify limited time value */
10085       {
10086         TimeLeft = action_arg_number_new;
10087
10088 #if 1
10089         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10090
10091         DisplayGameControlValues();
10092 #else
10093         DrawGameValue_Time(TimeLeft);
10094 #endif
10095
10096         if (!TimeLeft && setup.time_limit)
10097           for (i = 0; i < MAX_PLAYERS; i++)
10098             KillPlayer(&stored_player[i]);
10099       }
10100
10101       break;
10102     }
10103
10104     case CA_SET_LEVEL_SCORE:
10105     {
10106       local_player->score = action_arg_number_new;
10107
10108 #if 1
10109       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10110
10111       DisplayGameControlValues();
10112 #else
10113       DrawGameValue_Score(local_player->score);
10114 #endif
10115
10116       break;
10117     }
10118
10119     case CA_SET_LEVEL_GEMS:
10120     {
10121       local_player->gems_still_needed = action_arg_number_new;
10122
10123 #if 1
10124       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10125
10126       DisplayGameControlValues();
10127 #else
10128       DrawGameValue_Emeralds(local_player->gems_still_needed);
10129 #endif
10130
10131       break;
10132     }
10133
10134 #if !USE_PLAYER_GRAVITY
10135     case CA_SET_LEVEL_GRAVITY:
10136     {
10137       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10138                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10139                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10140                       game.gravity);
10141       break;
10142     }
10143 #endif
10144
10145     case CA_SET_LEVEL_WIND:
10146     {
10147       game.wind_direction = action_arg_direction;
10148
10149       break;
10150     }
10151
10152     /* ---------- player actions  ------------------------------------------ */
10153
10154     case CA_MOVE_PLAYER:
10155     {
10156       /* automatically move to the next field in specified direction */
10157       for (i = 0; i < MAX_PLAYERS; i++)
10158         if (trigger_player_bits & (1 << i))
10159           stored_player[i].programmed_action = action_arg_direction;
10160
10161       break;
10162     }
10163
10164     case CA_EXIT_PLAYER:
10165     {
10166       for (i = 0; i < MAX_PLAYERS; i++)
10167         if (action_arg_player_bits & (1 << i))
10168           PlayerWins(&stored_player[i]);
10169
10170       break;
10171     }
10172
10173     case CA_KILL_PLAYER:
10174     {
10175       for (i = 0; i < MAX_PLAYERS; i++)
10176         if (action_arg_player_bits & (1 << i))
10177           KillPlayer(&stored_player[i]);
10178
10179       break;
10180     }
10181
10182     case CA_SET_PLAYER_KEYS:
10183     {
10184       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10185       int element = getSpecialActionElement(action_arg_element,
10186                                             action_arg_number, EL_KEY_1);
10187
10188       if (IS_KEY(element))
10189       {
10190         for (i = 0; i < MAX_PLAYERS; i++)
10191         {
10192           if (trigger_player_bits & (1 << i))
10193           {
10194             stored_player[i].key[KEY_NR(element)] = key_state;
10195
10196             DrawGameDoorValues();
10197           }
10198         }
10199       }
10200
10201       break;
10202     }
10203
10204     case CA_SET_PLAYER_SPEED:
10205     {
10206       for (i = 0; i < MAX_PLAYERS; i++)
10207       {
10208         if (trigger_player_bits & (1 << i))
10209         {
10210           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10211
10212           if (action_arg == CA_ARG_SPEED_FASTER &&
10213               stored_player[i].cannot_move)
10214           {
10215             action_arg_number = STEPSIZE_VERY_SLOW;
10216           }
10217           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10218                    action_arg == CA_ARG_SPEED_FASTER)
10219           {
10220             action_arg_number = 2;
10221             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10222                            CA_MODE_MULTIPLY);
10223           }
10224           else if (action_arg == CA_ARG_NUMBER_RESET)
10225           {
10226             action_arg_number = level.initial_player_stepsize[i];
10227           }
10228
10229           move_stepsize =
10230             getModifiedActionNumber(move_stepsize,
10231                                     action_mode,
10232                                     action_arg_number,
10233                                     action_arg_number_min,
10234                                     action_arg_number_max);
10235
10236           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10237         }
10238       }
10239
10240       break;
10241     }
10242
10243     case CA_SET_PLAYER_SHIELD:
10244     {
10245       for (i = 0; i < MAX_PLAYERS; i++)
10246       {
10247         if (trigger_player_bits & (1 << i))
10248         {
10249           if (action_arg == CA_ARG_SHIELD_OFF)
10250           {
10251             stored_player[i].shield_normal_time_left = 0;
10252             stored_player[i].shield_deadly_time_left = 0;
10253           }
10254           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10255           {
10256             stored_player[i].shield_normal_time_left = 999999;
10257           }
10258           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10259           {
10260             stored_player[i].shield_normal_time_left = 999999;
10261             stored_player[i].shield_deadly_time_left = 999999;
10262           }
10263         }
10264       }
10265
10266       break;
10267     }
10268
10269 #if USE_PLAYER_GRAVITY
10270     case CA_SET_PLAYER_GRAVITY:
10271     {
10272       for (i = 0; i < MAX_PLAYERS; i++)
10273       {
10274         if (trigger_player_bits & (1 << i))
10275         {
10276           stored_player[i].gravity =
10277             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10278              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10279              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10280              stored_player[i].gravity);
10281         }
10282       }
10283
10284       break;
10285     }
10286 #endif
10287
10288     case CA_SET_PLAYER_ARTWORK:
10289     {
10290       for (i = 0; i < MAX_PLAYERS; i++)
10291       {
10292         if (trigger_player_bits & (1 << i))
10293         {
10294           int artwork_element = action_arg_element;
10295
10296           if (action_arg == CA_ARG_ELEMENT_RESET)
10297             artwork_element =
10298               (level.use_artwork_element[i] ? level.artwork_element[i] :
10299                stored_player[i].element_nr);
10300
10301 #if USE_GFX_RESET_PLAYER_ARTWORK
10302           if (stored_player[i].artwork_element != artwork_element)
10303             stored_player[i].Frame = 0;
10304 #endif
10305
10306           stored_player[i].artwork_element = artwork_element;
10307
10308           SetPlayerWaiting(&stored_player[i], FALSE);
10309
10310           /* set number of special actions for bored and sleeping animation */
10311           stored_player[i].num_special_action_bored =
10312             get_num_special_action(artwork_element,
10313                                    ACTION_BORING_1, ACTION_BORING_LAST);
10314           stored_player[i].num_special_action_sleeping =
10315             get_num_special_action(artwork_element,
10316                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10317         }
10318       }
10319
10320       break;
10321     }
10322
10323     /* ---------- CE actions  ---------------------------------------------- */
10324
10325     case CA_SET_CE_VALUE:
10326     {
10327 #if USE_NEW_CUSTOM_VALUE
10328       int last_ce_value = CustomValue[x][y];
10329
10330       CustomValue[x][y] = action_arg_number_new;
10331
10332       if (CustomValue[x][y] != last_ce_value)
10333       {
10334         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10335         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10336
10337         if (CustomValue[x][y] == 0)
10338         {
10339           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10340           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10341         }
10342       }
10343 #endif
10344
10345       break;
10346     }
10347
10348     case CA_SET_CE_SCORE:
10349     {
10350 #if USE_NEW_CUSTOM_VALUE
10351       int last_ce_score = ei->collect_score;
10352
10353       ei->collect_score = action_arg_number_new;
10354
10355       if (ei->collect_score != last_ce_score)
10356       {
10357         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10358         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10359
10360         if (ei->collect_score == 0)
10361         {
10362           int xx, yy;
10363
10364           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10365           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10366
10367           /*
10368             This is a very special case that seems to be a mixture between
10369             CheckElementChange() and CheckTriggeredElementChange(): while
10370             the first one only affects single elements that are triggered
10371             directly, the second one affects multiple elements in the playfield
10372             that are triggered indirectly by another element. This is a third
10373             case: Changing the CE score always affects multiple identical CEs,
10374             so every affected CE must be checked, not only the single CE for
10375             which the CE score was changed in the first place (as every instance
10376             of that CE shares the same CE score, and therefore also can change)!
10377           */
10378           SCAN_PLAYFIELD(xx, yy)
10379           {
10380             if (Feld[xx][yy] == element)
10381               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10382                                  CE_SCORE_GETS_ZERO);
10383           }
10384         }
10385       }
10386 #endif
10387
10388       break;
10389     }
10390
10391     /* ---------- engine actions  ------------------------------------------ */
10392
10393     case CA_SET_ENGINE_SCAN_MODE:
10394     {
10395       InitPlayfieldScanMode(action_arg);
10396
10397       break;
10398     }
10399
10400     default:
10401       break;
10402   }
10403 }
10404
10405 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10406 {
10407   int old_element = Feld[x][y];
10408   int new_element = GetElementFromGroupElement(element);
10409   int previous_move_direction = MovDir[x][y];
10410 #if USE_NEW_CUSTOM_VALUE
10411   int last_ce_value = CustomValue[x][y];
10412 #endif
10413   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10414   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10415   boolean add_player_onto_element = (new_element_is_player &&
10416 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10417                                      /* this breaks SnakeBite when a snake is
10418                                         halfway through a door that closes */
10419                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10420                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10421 #endif
10422                                      IS_WALKABLE(old_element));
10423
10424 #if 0
10425   /* check if element under the player changes from accessible to unaccessible
10426      (needed for special case of dropping element which then changes) */
10427   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10428       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10429   {
10430     Bang(x, y);
10431
10432     return;
10433   }
10434 #endif
10435
10436   if (!add_player_onto_element)
10437   {
10438     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10439       RemoveMovingField(x, y);
10440     else
10441       RemoveField(x, y);
10442
10443     Feld[x][y] = new_element;
10444
10445 #if !USE_GFX_RESET_GFX_ANIMATION
10446     ResetGfxAnimation(x, y);
10447     ResetRandomAnimationValue(x, y);
10448 #endif
10449
10450     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10451       MovDir[x][y] = previous_move_direction;
10452
10453 #if USE_NEW_CUSTOM_VALUE
10454     if (element_info[new_element].use_last_ce_value)
10455       CustomValue[x][y] = last_ce_value;
10456 #endif
10457
10458     InitField_WithBug1(x, y, FALSE);
10459
10460     new_element = Feld[x][y];   /* element may have changed */
10461
10462 #if USE_GFX_RESET_GFX_ANIMATION
10463     ResetGfxAnimation(x, y);
10464     ResetRandomAnimationValue(x, y);
10465 #endif
10466
10467     DrawLevelField(x, y);
10468
10469     if (GFX_CRUMBLED(new_element))
10470       DrawLevelFieldCrumbledSandNeighbours(x, y);
10471   }
10472
10473 #if 1
10474   /* check if element under the player changes from accessible to unaccessible
10475      (needed for special case of dropping element which then changes) */
10476   /* (must be checked after creating new element for walkable group elements) */
10477 #if USE_FIX_KILLED_BY_NON_WALKABLE
10478   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10479       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10480   {
10481     Bang(x, y);
10482
10483     return;
10484   }
10485 #else
10486   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10487       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10488   {
10489     Bang(x, y);
10490
10491     return;
10492   }
10493 #endif
10494 #endif
10495
10496   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10497   if (new_element_is_player)
10498     RelocatePlayer(x, y, new_element);
10499
10500   if (is_change)
10501     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10502
10503   TestIfBadThingTouchesPlayer(x, y);
10504   TestIfPlayerTouchesCustomElement(x, y);
10505   TestIfElementTouchesCustomElement(x, y);
10506 }
10507
10508 static void CreateField(int x, int y, int element)
10509 {
10510   CreateFieldExt(x, y, element, FALSE);
10511 }
10512
10513 static void CreateElementFromChange(int x, int y, int element)
10514 {
10515   element = GET_VALID_RUNTIME_ELEMENT(element);
10516
10517 #if USE_STOP_CHANGED_ELEMENTS
10518   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10519   {
10520     int old_element = Feld[x][y];
10521
10522     /* prevent changed element from moving in same engine frame
10523        unless both old and new element can either fall or move */
10524     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10525         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10526       Stop[x][y] = TRUE;
10527   }
10528 #endif
10529
10530   CreateFieldExt(x, y, element, TRUE);
10531 }
10532
10533 static boolean ChangeElement(int x, int y, int element, int page)
10534 {
10535   struct ElementInfo *ei = &element_info[element];
10536   struct ElementChangeInfo *change = &ei->change_page[page];
10537   int ce_value = CustomValue[x][y];
10538   int ce_score = ei->collect_score;
10539   int target_element;
10540   int old_element = Feld[x][y];
10541
10542   /* always use default change event to prevent running into a loop */
10543   if (ChangeEvent[x][y] == -1)
10544     ChangeEvent[x][y] = CE_DELAY;
10545
10546   if (ChangeEvent[x][y] == CE_DELAY)
10547   {
10548     /* reset actual trigger element, trigger player and action element */
10549     change->actual_trigger_element = EL_EMPTY;
10550     change->actual_trigger_player = EL_PLAYER_1;
10551     change->actual_trigger_player_bits = CH_PLAYER_1;
10552     change->actual_trigger_side = CH_SIDE_NONE;
10553     change->actual_trigger_ce_value = 0;
10554     change->actual_trigger_ce_score = 0;
10555   }
10556
10557   /* do not change elements more than a specified maximum number of changes */
10558   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10559     return FALSE;
10560
10561   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10562
10563   if (change->explode)
10564   {
10565     Bang(x, y);
10566
10567     return TRUE;
10568   }
10569
10570   if (change->use_target_content)
10571   {
10572     boolean complete_replace = TRUE;
10573     boolean can_replace[3][3];
10574     int xx, yy;
10575
10576     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10577     {
10578       boolean is_empty;
10579       boolean is_walkable;
10580       boolean is_diggable;
10581       boolean is_collectible;
10582       boolean is_removable;
10583       boolean is_destructible;
10584       int ex = x + xx - 1;
10585       int ey = y + yy - 1;
10586       int content_element = change->target_content.e[xx][yy];
10587       int e;
10588
10589       can_replace[xx][yy] = TRUE;
10590
10591       if (ex == x && ey == y)   /* do not check changing element itself */
10592         continue;
10593
10594       if (content_element == EL_EMPTY_SPACE)
10595       {
10596         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10597
10598         continue;
10599       }
10600
10601       if (!IN_LEV_FIELD(ex, ey))
10602       {
10603         can_replace[xx][yy] = FALSE;
10604         complete_replace = FALSE;
10605
10606         continue;
10607       }
10608
10609       e = Feld[ex][ey];
10610
10611       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10612         e = MovingOrBlocked2Element(ex, ey);
10613
10614       is_empty = (IS_FREE(ex, ey) ||
10615                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10616
10617       is_walkable     = (is_empty || IS_WALKABLE(e));
10618       is_diggable     = (is_empty || IS_DIGGABLE(e));
10619       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10620       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10621       is_removable    = (is_diggable || is_collectible);
10622
10623       can_replace[xx][yy] =
10624         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10625           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10626           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10627           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10628           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10629           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10630          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10631
10632       if (!can_replace[xx][yy])
10633         complete_replace = FALSE;
10634     }
10635
10636     if (!change->only_if_complete || complete_replace)
10637     {
10638       boolean something_has_changed = FALSE;
10639
10640       if (change->only_if_complete && change->use_random_replace &&
10641           RND(100) < change->random_percentage)
10642         return FALSE;
10643
10644       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10645       {
10646         int ex = x + xx - 1;
10647         int ey = y + yy - 1;
10648         int content_element;
10649
10650         if (can_replace[xx][yy] && (!change->use_random_replace ||
10651                                     RND(100) < change->random_percentage))
10652         {
10653           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10654             RemoveMovingField(ex, ey);
10655
10656           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10657
10658           content_element = change->target_content.e[xx][yy];
10659           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10660                                               ce_value, ce_score);
10661
10662           CreateElementFromChange(ex, ey, target_element);
10663
10664           something_has_changed = TRUE;
10665
10666           /* for symmetry reasons, freeze newly created border elements */
10667           if (ex != x || ey != y)
10668             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10669         }
10670       }
10671
10672       if (something_has_changed)
10673       {
10674         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10675         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10676       }
10677     }
10678   }
10679   else
10680   {
10681     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10682                                         ce_value, ce_score);
10683
10684     if (element == EL_DIAGONAL_GROWING ||
10685         element == EL_DIAGONAL_SHRINKING)
10686     {
10687       target_element = Store[x][y];
10688
10689       Store[x][y] = EL_EMPTY;
10690     }
10691
10692     CreateElementFromChange(x, y, target_element);
10693
10694     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10695     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10696   }
10697
10698   /* this uses direct change before indirect change */
10699   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10700
10701   return TRUE;
10702 }
10703
10704 #if USE_NEW_DELAYED_ACTION
10705
10706 static void HandleElementChange(int x, int y, int page)
10707 {
10708   int element = MovingOrBlocked2Element(x, y);
10709   struct ElementInfo *ei = &element_info[element];
10710   struct ElementChangeInfo *change = &ei->change_page[page];
10711
10712 #ifdef DEBUG
10713   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10714       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10715   {
10716     printf("\n\n");
10717     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10718            x, y, element, element_info[element].token_name);
10719     printf("HandleElementChange(): This should never happen!\n");
10720     printf("\n\n");
10721   }
10722 #endif
10723
10724   /* this can happen with classic bombs on walkable, changing elements */
10725   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10726   {
10727 #if 0
10728     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10729       ChangeDelay[x][y] = 0;
10730 #endif
10731
10732     return;
10733   }
10734
10735   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10736   {
10737     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10738
10739     if (change->can_change)
10740     {
10741 #if 1
10742       /* !!! not clear why graphic animation should be reset at all here !!! */
10743       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10744 #if USE_GFX_RESET_WHEN_NOT_MOVING
10745       /* when a custom element is about to change (for example by change delay),
10746          do not reset graphic animation when the custom element is moving */
10747       if (!IS_MOVING(x, y))
10748 #endif
10749       {
10750         ResetGfxAnimation(x, y);
10751         ResetRandomAnimationValue(x, y);
10752       }
10753 #endif
10754
10755       if (change->pre_change_function)
10756         change->pre_change_function(x, y);
10757     }
10758   }
10759
10760   ChangeDelay[x][y]--;
10761
10762   if (ChangeDelay[x][y] != 0)           /* continue element change */
10763   {
10764     if (change->can_change)
10765     {
10766       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10767
10768       if (IS_ANIMATED(graphic))
10769         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10770
10771       if (change->change_function)
10772         change->change_function(x, y);
10773     }
10774   }
10775   else                                  /* finish element change */
10776   {
10777     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10778     {
10779       page = ChangePage[x][y];
10780       ChangePage[x][y] = -1;
10781
10782       change = &ei->change_page[page];
10783     }
10784
10785     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10786     {
10787       ChangeDelay[x][y] = 1;            /* try change after next move step */
10788       ChangePage[x][y] = page;          /* remember page to use for change */
10789
10790       return;
10791     }
10792
10793     if (change->can_change)
10794     {
10795       if (ChangeElement(x, y, element, page))
10796       {
10797         if (change->post_change_function)
10798           change->post_change_function(x, y);
10799       }
10800     }
10801
10802     if (change->has_action)
10803       ExecuteCustomElementAction(x, y, element, page);
10804   }
10805 }
10806
10807 #else
10808
10809 static void HandleElementChange(int x, int y, int page)
10810 {
10811   int element = MovingOrBlocked2Element(x, y);
10812   struct ElementInfo *ei = &element_info[element];
10813   struct ElementChangeInfo *change = &ei->change_page[page];
10814
10815 #ifdef DEBUG
10816   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10817   {
10818     printf("\n\n");
10819     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10820            x, y, element, element_info[element].token_name);
10821     printf("HandleElementChange(): This should never happen!\n");
10822     printf("\n\n");
10823   }
10824 #endif
10825
10826   /* this can happen with classic bombs on walkable, changing elements */
10827   if (!CAN_CHANGE(element))
10828   {
10829 #if 0
10830     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10831       ChangeDelay[x][y] = 0;
10832 #endif
10833
10834     return;
10835   }
10836
10837   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10838   {
10839     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10840
10841     ResetGfxAnimation(x, y);
10842     ResetRandomAnimationValue(x, y);
10843
10844     if (change->pre_change_function)
10845       change->pre_change_function(x, y);
10846   }
10847
10848   ChangeDelay[x][y]--;
10849
10850   if (ChangeDelay[x][y] != 0)           /* continue element change */
10851   {
10852     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10853
10854     if (IS_ANIMATED(graphic))
10855       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10856
10857     if (change->change_function)
10858       change->change_function(x, y);
10859   }
10860   else                                  /* finish element change */
10861   {
10862     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10863     {
10864       page = ChangePage[x][y];
10865       ChangePage[x][y] = -1;
10866
10867       change = &ei->change_page[page];
10868     }
10869
10870     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10871     {
10872       ChangeDelay[x][y] = 1;            /* try change after next move step */
10873       ChangePage[x][y] = page;          /* remember page to use for change */
10874
10875       return;
10876     }
10877
10878     if (ChangeElement(x, y, element, page))
10879     {
10880       if (change->post_change_function)
10881         change->post_change_function(x, y);
10882     }
10883   }
10884 }
10885
10886 #endif
10887
10888 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10889                                               int trigger_element,
10890                                               int trigger_event,
10891                                               int trigger_player,
10892                                               int trigger_side,
10893                                               int trigger_page)
10894 {
10895   boolean change_done_any = FALSE;
10896   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10897   int i;
10898
10899   if (!(trigger_events[trigger_element][trigger_event]))
10900     return FALSE;
10901
10902 #if 0
10903   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10904          trigger_event, recursion_loop_depth, recursion_loop_detected,
10905          recursion_loop_element, EL_NAME(recursion_loop_element));
10906 #endif
10907
10908   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10909
10910   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10911   {
10912     int element = EL_CUSTOM_START + i;
10913     boolean change_done = FALSE;
10914     int p;
10915
10916     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10917         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10918       continue;
10919
10920     for (p = 0; p < element_info[element].num_change_pages; p++)
10921     {
10922       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10923
10924       if (change->can_change_or_has_action &&
10925           change->has_event[trigger_event] &&
10926           change->trigger_side & trigger_side &&
10927           change->trigger_player & trigger_player &&
10928           change->trigger_page & trigger_page_bits &&
10929           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10930       {
10931         change->actual_trigger_element = trigger_element;
10932         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10933         change->actual_trigger_player_bits = trigger_player;
10934         change->actual_trigger_side = trigger_side;
10935         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10936         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10937
10938         if ((change->can_change && !change_done) || change->has_action)
10939         {
10940           int x, y;
10941
10942           SCAN_PLAYFIELD(x, y)
10943           {
10944             if (Feld[x][y] == element)
10945             {
10946               if (change->can_change && !change_done)
10947               {
10948                 ChangeDelay[x][y] = 1;
10949                 ChangeEvent[x][y] = trigger_event;
10950
10951                 HandleElementChange(x, y, p);
10952               }
10953 #if USE_NEW_DELAYED_ACTION
10954               else if (change->has_action)
10955               {
10956                 ExecuteCustomElementAction(x, y, element, p);
10957                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10958               }
10959 #else
10960               if (change->has_action)
10961               {
10962                 ExecuteCustomElementAction(x, y, element, p);
10963                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10964               }
10965 #endif
10966             }
10967           }
10968
10969           if (change->can_change)
10970           {
10971             change_done = TRUE;
10972             change_done_any = TRUE;
10973           }
10974         }
10975       }
10976     }
10977   }
10978
10979   RECURSION_LOOP_DETECTION_END();
10980
10981   return change_done_any;
10982 }
10983
10984 static boolean CheckElementChangeExt(int x, int y,
10985                                      int element,
10986                                      int trigger_element,
10987                                      int trigger_event,
10988                                      int trigger_player,
10989                                      int trigger_side)
10990 {
10991   boolean change_done = FALSE;
10992   int p;
10993
10994   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10995       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10996     return FALSE;
10997
10998   if (Feld[x][y] == EL_BLOCKED)
10999   {
11000     Blocked2Moving(x, y, &x, &y);
11001     element = Feld[x][y];
11002   }
11003
11004 #if 0
11005   /* check if element has already changed */
11006   if (Feld[x][y] != element)
11007     return FALSE;
11008 #else
11009   /* check if element has already changed or is about to change after moving */
11010   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11011        Feld[x][y] != element) ||
11012
11013       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11014        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11015         ChangePage[x][y] != -1)))
11016     return FALSE;
11017 #endif
11018
11019 #if 0
11020   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11021          trigger_event, recursion_loop_depth, recursion_loop_detected,
11022          recursion_loop_element, EL_NAME(recursion_loop_element));
11023 #endif
11024
11025   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11026
11027   for (p = 0; p < element_info[element].num_change_pages; p++)
11028   {
11029     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11030
11031     /* check trigger element for all events where the element that is checked
11032        for changing interacts with a directly adjacent element -- this is
11033        different to element changes that affect other elements to change on the
11034        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11035     boolean check_trigger_element =
11036       (trigger_event == CE_TOUCHING_X ||
11037        trigger_event == CE_HITTING_X ||
11038        trigger_event == CE_HIT_BY_X ||
11039 #if 1
11040        /* this one was forgotten until 3.2.3 */
11041        trigger_event == CE_DIGGING_X);
11042 #endif
11043
11044     if (change->can_change_or_has_action &&
11045         change->has_event[trigger_event] &&
11046         change->trigger_side & trigger_side &&
11047         change->trigger_player & trigger_player &&
11048         (!check_trigger_element ||
11049          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11050     {
11051       change->actual_trigger_element = trigger_element;
11052       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11053       change->actual_trigger_player_bits = trigger_player;
11054       change->actual_trigger_side = trigger_side;
11055       change->actual_trigger_ce_value = CustomValue[x][y];
11056       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11057
11058       /* special case: trigger element not at (x,y) position for some events */
11059       if (check_trigger_element)
11060       {
11061         static struct
11062         {
11063           int dx, dy;
11064         } move_xy[] =
11065           {
11066             {  0,  0 },
11067             { -1,  0 },
11068             { +1,  0 },
11069             {  0,  0 },
11070             {  0, -1 },
11071             {  0,  0 }, { 0, 0 }, { 0, 0 },
11072             {  0, +1 }
11073           };
11074
11075         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11076         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11077
11078         change->actual_trigger_ce_value = CustomValue[xx][yy];
11079         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11080       }
11081
11082       if (change->can_change && !change_done)
11083       {
11084         ChangeDelay[x][y] = 1;
11085         ChangeEvent[x][y] = trigger_event;
11086
11087         HandleElementChange(x, y, p);
11088
11089         change_done = TRUE;
11090       }
11091 #if USE_NEW_DELAYED_ACTION
11092       else if (change->has_action)
11093       {
11094         ExecuteCustomElementAction(x, y, element, p);
11095         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11096       }
11097 #else
11098       if (change->has_action)
11099       {
11100         ExecuteCustomElementAction(x, y, element, p);
11101         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11102       }
11103 #endif
11104     }
11105   }
11106
11107   RECURSION_LOOP_DETECTION_END();
11108
11109   return change_done;
11110 }
11111
11112 static void PlayPlayerSound(struct PlayerInfo *player)
11113 {
11114   int jx = player->jx, jy = player->jy;
11115   int sound_element = player->artwork_element;
11116   int last_action = player->last_action_waiting;
11117   int action = player->action_waiting;
11118
11119   if (player->is_waiting)
11120   {
11121     if (action != last_action)
11122       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11123     else
11124       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11125   }
11126   else
11127   {
11128     if (action != last_action)
11129       StopSound(element_info[sound_element].sound[last_action]);
11130
11131     if (last_action == ACTION_SLEEPING)
11132       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11133   }
11134 }
11135
11136 static void PlayAllPlayersSound()
11137 {
11138   int i;
11139
11140   for (i = 0; i < MAX_PLAYERS; i++)
11141     if (stored_player[i].active)
11142       PlayPlayerSound(&stored_player[i]);
11143 }
11144
11145 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11146 {
11147   boolean last_waiting = player->is_waiting;
11148   int move_dir = player->MovDir;
11149
11150   player->dir_waiting = move_dir;
11151   player->last_action_waiting = player->action_waiting;
11152
11153   if (is_waiting)
11154   {
11155     if (!last_waiting)          /* not waiting -> waiting */
11156     {
11157       player->is_waiting = TRUE;
11158
11159       player->frame_counter_bored =
11160         FrameCounter +
11161         game.player_boring_delay_fixed +
11162         GetSimpleRandom(game.player_boring_delay_random);
11163       player->frame_counter_sleeping =
11164         FrameCounter +
11165         game.player_sleeping_delay_fixed +
11166         GetSimpleRandom(game.player_sleeping_delay_random);
11167
11168       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11169     }
11170
11171     if (game.player_sleeping_delay_fixed +
11172         game.player_sleeping_delay_random > 0 &&
11173         player->anim_delay_counter == 0 &&
11174         player->post_delay_counter == 0 &&
11175         FrameCounter >= player->frame_counter_sleeping)
11176       player->is_sleeping = TRUE;
11177     else if (game.player_boring_delay_fixed +
11178              game.player_boring_delay_random > 0 &&
11179              FrameCounter >= player->frame_counter_bored)
11180       player->is_bored = TRUE;
11181
11182     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11183                               player->is_bored ? ACTION_BORING :
11184                               ACTION_WAITING);
11185
11186     if (player->is_sleeping && player->use_murphy)
11187     {
11188       /* special case for sleeping Murphy when leaning against non-free tile */
11189
11190       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11191           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11192            !IS_MOVING(player->jx - 1, player->jy)))
11193         move_dir = MV_LEFT;
11194       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11195                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11196                 !IS_MOVING(player->jx + 1, player->jy)))
11197         move_dir = MV_RIGHT;
11198       else
11199         player->is_sleeping = FALSE;
11200
11201       player->dir_waiting = move_dir;
11202     }
11203
11204     if (player->is_sleeping)
11205     {
11206       if (player->num_special_action_sleeping > 0)
11207       {
11208         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11209         {
11210           int last_special_action = player->special_action_sleeping;
11211           int num_special_action = player->num_special_action_sleeping;
11212           int special_action =
11213             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11214              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11215              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11216              last_special_action + 1 : ACTION_SLEEPING);
11217           int special_graphic =
11218             el_act_dir2img(player->artwork_element, special_action, move_dir);
11219
11220           player->anim_delay_counter =
11221             graphic_info[special_graphic].anim_delay_fixed +
11222             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11223           player->post_delay_counter =
11224             graphic_info[special_graphic].post_delay_fixed +
11225             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11226
11227           player->special_action_sleeping = special_action;
11228         }
11229
11230         if (player->anim_delay_counter > 0)
11231         {
11232           player->action_waiting = player->special_action_sleeping;
11233           player->anim_delay_counter--;
11234         }
11235         else if (player->post_delay_counter > 0)
11236         {
11237           player->post_delay_counter--;
11238         }
11239       }
11240     }
11241     else if (player->is_bored)
11242     {
11243       if (player->num_special_action_bored > 0)
11244       {
11245         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11246         {
11247           int special_action =
11248             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11249           int special_graphic =
11250             el_act_dir2img(player->artwork_element, special_action, move_dir);
11251
11252           player->anim_delay_counter =
11253             graphic_info[special_graphic].anim_delay_fixed +
11254             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11255           player->post_delay_counter =
11256             graphic_info[special_graphic].post_delay_fixed +
11257             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11258
11259           player->special_action_bored = special_action;
11260         }
11261
11262         if (player->anim_delay_counter > 0)
11263         {
11264           player->action_waiting = player->special_action_bored;
11265           player->anim_delay_counter--;
11266         }
11267         else if (player->post_delay_counter > 0)
11268         {
11269           player->post_delay_counter--;
11270         }
11271       }
11272     }
11273   }
11274   else if (last_waiting)        /* waiting -> not waiting */
11275   {
11276     player->is_waiting = FALSE;
11277     player->is_bored = FALSE;
11278     player->is_sleeping = FALSE;
11279
11280     player->frame_counter_bored = -1;
11281     player->frame_counter_sleeping = -1;
11282
11283     player->anim_delay_counter = 0;
11284     player->post_delay_counter = 0;
11285
11286     player->dir_waiting = player->MovDir;
11287     player->action_waiting = ACTION_DEFAULT;
11288
11289     player->special_action_bored = ACTION_DEFAULT;
11290     player->special_action_sleeping = ACTION_DEFAULT;
11291   }
11292 }
11293
11294 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11295 {
11296   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11297   int left      = player_action & JOY_LEFT;
11298   int right     = player_action & JOY_RIGHT;
11299   int up        = player_action & JOY_UP;
11300   int down      = player_action & JOY_DOWN;
11301   int button1   = player_action & JOY_BUTTON_1;
11302   int button2   = player_action & JOY_BUTTON_2;
11303   int dx        = (left ? -1 : right ? 1 : 0);
11304   int dy        = (up   ? -1 : down  ? 1 : 0);
11305
11306   if (!player->active || tape.pausing)
11307     return 0;
11308
11309   if (player_action)
11310   {
11311     if (button1)
11312       snapped = SnapField(player, dx, dy);
11313     else
11314     {
11315       if (button2)
11316         dropped = DropElement(player);
11317
11318       moved = MovePlayer(player, dx, dy);
11319     }
11320
11321     if (tape.single_step && tape.recording && !tape.pausing)
11322     {
11323       if (button1 || (dropped && !moved))
11324       {
11325         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11326         SnapField(player, 0, 0);                /* stop snapping */
11327       }
11328     }
11329
11330     SetPlayerWaiting(player, FALSE);
11331
11332     return player_action;
11333   }
11334   else
11335   {
11336     /* no actions for this player (no input at player's configured device) */
11337
11338     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11339     SnapField(player, 0, 0);
11340     CheckGravityMovementWhenNotMoving(player);
11341
11342     if (player->MovPos == 0)
11343       SetPlayerWaiting(player, TRUE);
11344
11345     if (player->MovPos == 0)    /* needed for tape.playing */
11346       player->is_moving = FALSE;
11347
11348     player->is_dropping = FALSE;
11349     player->is_dropping_pressed = FALSE;
11350     player->drop_pressed_delay = 0;
11351
11352     return 0;
11353   }
11354 }
11355
11356 static void CheckLevelTime()
11357 {
11358   int i;
11359
11360   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11361   {
11362     if (level.native_em_level->lev->home == 0)  /* all players at home */
11363     {
11364       PlayerWins(local_player);
11365
11366       AllPlayersGone = TRUE;
11367
11368       level.native_em_level->lev->home = -1;
11369     }
11370
11371     if (level.native_em_level->ply[0]->alive == 0 &&
11372         level.native_em_level->ply[1]->alive == 0 &&
11373         level.native_em_level->ply[2]->alive == 0 &&
11374         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11375       AllPlayersGone = TRUE;
11376   }
11377
11378   if (TimeFrames >= FRAMES_PER_SECOND)
11379   {
11380     TimeFrames = 0;
11381     TapeTime++;
11382
11383     for (i = 0; i < MAX_PLAYERS; i++)
11384     {
11385       struct PlayerInfo *player = &stored_player[i];
11386
11387       if (SHIELD_ON(player))
11388       {
11389         player->shield_normal_time_left--;
11390
11391         if (player->shield_deadly_time_left > 0)
11392           player->shield_deadly_time_left--;
11393       }
11394     }
11395
11396     if (!local_player->LevelSolved && !level.use_step_counter)
11397     {
11398       TimePlayed++;
11399
11400       if (TimeLeft > 0)
11401       {
11402         TimeLeft--;
11403
11404         if (TimeLeft <= 10 && setup.time_limit)
11405           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11406
11407 #if 1
11408         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11409
11410         DisplayGameControlValues();
11411 #else
11412         DrawGameValue_Time(TimeLeft);
11413 #endif
11414
11415         if (!TimeLeft && setup.time_limit)
11416         {
11417           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11418             level.native_em_level->lev->killed_out_of_time = TRUE;
11419           else
11420             for (i = 0; i < MAX_PLAYERS; i++)
11421               KillPlayer(&stored_player[i]);
11422         }
11423       }
11424 #if 1
11425       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11426       {
11427         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11428
11429         DisplayGameControlValues();
11430       }
11431 #else
11432       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11433         DrawGameValue_Time(TimePlayed);
11434 #endif
11435
11436       level.native_em_level->lev->time =
11437         (level.time == 0 ? TimePlayed : TimeLeft);
11438     }
11439
11440     if (tape.recording || tape.playing)
11441       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11442   }
11443
11444 #if 1
11445   UpdateAndDisplayGameControlValues();
11446 #else
11447   UpdateGameDoorValues();
11448   DrawGameDoorValues();
11449 #endif
11450 }
11451
11452 void AdvanceFrameAndPlayerCounters(int player_nr)
11453 {
11454   int i;
11455
11456   /* advance frame counters (global frame counter and time frame counter) */
11457   FrameCounter++;
11458   TimeFrames++;
11459
11460   /* advance player counters (counters for move delay, move animation etc.) */
11461   for (i = 0; i < MAX_PLAYERS; i++)
11462   {
11463     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11464     int move_delay_value = stored_player[i].move_delay_value;
11465     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11466
11467     if (!advance_player_counters)       /* not all players may be affected */
11468       continue;
11469
11470 #if USE_NEW_PLAYER_ANIM
11471     if (move_frames == 0)       /* less than one move per game frame */
11472     {
11473       int stepsize = TILEX / move_delay_value;
11474       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11475       int count = (stored_player[i].is_moving ?
11476                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11477
11478       if (count % delay == 0)
11479         move_frames = 1;
11480     }
11481 #endif
11482
11483     stored_player[i].Frame += move_frames;
11484
11485     if (stored_player[i].MovPos != 0)
11486       stored_player[i].StepFrame += move_frames;
11487
11488     if (stored_player[i].move_delay > 0)
11489       stored_player[i].move_delay--;
11490
11491     /* due to bugs in previous versions, counter must count up, not down */
11492     if (stored_player[i].push_delay != -1)
11493       stored_player[i].push_delay++;
11494
11495     if (stored_player[i].drop_delay > 0)
11496       stored_player[i].drop_delay--;
11497
11498     if (stored_player[i].is_dropping_pressed)
11499       stored_player[i].drop_pressed_delay++;
11500   }
11501 }
11502
11503 void StartGameActions(boolean init_network_game, boolean record_tape,
11504                       long random_seed)
11505 {
11506   unsigned long new_random_seed = InitRND(random_seed);
11507
11508   if (record_tape)
11509     TapeStartRecording(new_random_seed);
11510
11511 #if defined(NETWORK_AVALIABLE)
11512   if (init_network_game)
11513   {
11514     SendToServer_StartPlaying();
11515
11516     return;
11517   }
11518 #endif
11519
11520   InitGame();
11521 }
11522
11523 void GameActions()
11524 {
11525   static unsigned long game_frame_delay = 0;
11526   unsigned long game_frame_delay_value;
11527   byte *recorded_player_action;
11528   byte summarized_player_action = 0;
11529   byte tape_action[MAX_PLAYERS];
11530   int i;
11531
11532   /* detect endless loops, caused by custom element programming */
11533   if (recursion_loop_detected && recursion_loop_depth == 0)
11534   {
11535     char *message = getStringCat3("Internal Error ! Element ",
11536                                   EL_NAME(recursion_loop_element),
11537                                   " caused endless loop ! Quit the game ?");
11538
11539     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11540           EL_NAME(recursion_loop_element));
11541
11542     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11543
11544     recursion_loop_detected = FALSE;    /* if game should be continued */
11545
11546     free(message);
11547
11548     return;
11549   }
11550
11551   if (game.restart_level)
11552     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11553
11554   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11555   {
11556     if (level.native_em_level->lev->home == 0)  /* all players at home */
11557     {
11558       PlayerWins(local_player);
11559
11560       AllPlayersGone = TRUE;
11561
11562       level.native_em_level->lev->home = -1;
11563     }
11564
11565     if (level.native_em_level->ply[0]->alive == 0 &&
11566         level.native_em_level->ply[1]->alive == 0 &&
11567         level.native_em_level->ply[2]->alive == 0 &&
11568         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11569       AllPlayersGone = TRUE;
11570   }
11571
11572   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11573     GameWon();
11574
11575   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11576     TapeStop();
11577
11578   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11579     return;
11580
11581   game_frame_delay_value =
11582     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11583
11584   if (tape.playing && tape.warp_forward && !tape.pausing)
11585     game_frame_delay_value = 0;
11586
11587   /* ---------- main game synchronization point ---------- */
11588
11589   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11590
11591   if (network_playing && !network_player_action_received)
11592   {
11593     /* try to get network player actions in time */
11594
11595 #if defined(NETWORK_AVALIABLE)
11596     /* last chance to get network player actions without main loop delay */
11597     HandleNetworking();
11598 #endif
11599
11600     /* game was quit by network peer */
11601     if (game_status != GAME_MODE_PLAYING)
11602       return;
11603
11604     if (!network_player_action_received)
11605       return;           /* failed to get network player actions in time */
11606
11607     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11608   }
11609
11610   if (tape.pausing)
11611     return;
11612
11613   /* at this point we know that we really continue executing the game */
11614
11615   network_player_action_received = FALSE;
11616
11617   /* when playing tape, read previously recorded player input from tape data */
11618   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11619
11620 #if 1
11621   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11622   if (tape.pausing)
11623     return;
11624 #endif
11625
11626   if (tape.set_centered_player)
11627   {
11628     game.centered_player_nr_next = tape.centered_player_nr_next;
11629     game.set_centered_player = TRUE;
11630   }
11631
11632   for (i = 0; i < MAX_PLAYERS; i++)
11633   {
11634     summarized_player_action |= stored_player[i].action;
11635
11636     if (!network_playing)
11637       stored_player[i].effective_action = stored_player[i].action;
11638   }
11639
11640 #if defined(NETWORK_AVALIABLE)
11641   if (network_playing)
11642     SendToServer_MovePlayer(summarized_player_action);
11643 #endif
11644
11645   if (!options.network && !setup.team_mode)
11646     local_player->effective_action = summarized_player_action;
11647
11648   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11649   {
11650     for (i = 0; i < MAX_PLAYERS; i++)
11651       stored_player[i].effective_action =
11652         (i == game.centered_player_nr ? summarized_player_action : 0);
11653   }
11654
11655   if (recorded_player_action != NULL)
11656     for (i = 0; i < MAX_PLAYERS; i++)
11657       stored_player[i].effective_action = recorded_player_action[i];
11658
11659   for (i = 0; i < MAX_PLAYERS; i++)
11660   {
11661     tape_action[i] = stored_player[i].effective_action;
11662
11663     /* (this can only happen in the R'n'D game engine) */
11664     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11665       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11666   }
11667
11668   /* only record actions from input devices, but not programmed actions */
11669   if (tape.recording)
11670     TapeRecordAction(tape_action);
11671
11672   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11673   {
11674     GameActions_EM_Main();
11675   }
11676   else
11677   {
11678     GameActions_RND();
11679   }
11680 }
11681
11682 void GameActions_EM_Main()
11683 {
11684   byte effective_action[MAX_PLAYERS];
11685   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11686   int i;
11687
11688   for (i = 0; i < MAX_PLAYERS; i++)
11689     effective_action[i] = stored_player[i].effective_action;
11690
11691   GameActions_EM(effective_action, warp_mode);
11692
11693   CheckLevelTime();
11694
11695   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11696 }
11697
11698 void GameActions_RND()
11699 {
11700   int magic_wall_x = 0, magic_wall_y = 0;
11701   int i, x, y, element, graphic;
11702
11703   InitPlayfieldScanModeVars();
11704
11705 #if USE_ONE_MORE_CHANGE_PER_FRAME
11706   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11707   {
11708     SCAN_PLAYFIELD(x, y)
11709     {
11710       ChangeCount[x][y] = 0;
11711       ChangeEvent[x][y] = -1;
11712     }
11713   }
11714 #endif
11715
11716   if (game.set_centered_player)
11717   {
11718     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11719
11720     /* switching to "all players" only possible if all players fit to screen */
11721     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11722     {
11723       game.centered_player_nr_next = game.centered_player_nr;
11724       game.set_centered_player = FALSE;
11725     }
11726
11727     /* do not switch focus to non-existing (or non-active) player */
11728     if (game.centered_player_nr_next >= 0 &&
11729         !stored_player[game.centered_player_nr_next].active)
11730     {
11731       game.centered_player_nr_next = game.centered_player_nr;
11732       game.set_centered_player = FALSE;
11733     }
11734   }
11735
11736   if (game.set_centered_player &&
11737       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11738   {
11739     int sx, sy;
11740
11741     if (game.centered_player_nr_next == -1)
11742     {
11743       setScreenCenteredToAllPlayers(&sx, &sy);
11744     }
11745     else
11746     {
11747       sx = stored_player[game.centered_player_nr_next].jx;
11748       sy = stored_player[game.centered_player_nr_next].jy;
11749     }
11750
11751     game.centered_player_nr = game.centered_player_nr_next;
11752     game.set_centered_player = FALSE;
11753
11754     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11755     DrawGameDoorValues();
11756   }
11757
11758   for (i = 0; i < MAX_PLAYERS; i++)
11759   {
11760     int actual_player_action = stored_player[i].effective_action;
11761
11762 #if 1
11763     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11764        - rnd_equinox_tetrachloride 048
11765        - rnd_equinox_tetrachloride_ii 096
11766        - rnd_emanuel_schmieg 002
11767        - doctor_sloan_ww 001, 020
11768     */
11769     if (stored_player[i].MovPos == 0)
11770       CheckGravityMovement(&stored_player[i]);
11771 #endif
11772
11773     /* overwrite programmed action with tape action */
11774     if (stored_player[i].programmed_action)
11775       actual_player_action = stored_player[i].programmed_action;
11776
11777     PlayerActions(&stored_player[i], actual_player_action);
11778
11779     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11780   }
11781
11782   ScrollScreen(NULL, SCROLL_GO_ON);
11783
11784   /* for backwards compatibility, the following code emulates a fixed bug that
11785      occured when pushing elements (causing elements that just made their last
11786      pushing step to already (if possible) make their first falling step in the
11787      same game frame, which is bad); this code is also needed to use the famous
11788      "spring push bug" which is used in older levels and might be wanted to be
11789      used also in newer levels, but in this case the buggy pushing code is only
11790      affecting the "spring" element and no other elements */
11791
11792   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11793   {
11794     for (i = 0; i < MAX_PLAYERS; i++)
11795     {
11796       struct PlayerInfo *player = &stored_player[i];
11797       int x = player->jx;
11798       int y = player->jy;
11799
11800       if (player->active && player->is_pushing && player->is_moving &&
11801           IS_MOVING(x, y) &&
11802           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11803            Feld[x][y] == EL_SPRING))
11804       {
11805         ContinueMoving(x, y);
11806
11807         /* continue moving after pushing (this is actually a bug) */
11808         if (!IS_MOVING(x, y))
11809           Stop[x][y] = FALSE;
11810       }
11811     }
11812   }
11813
11814 #if 0
11815   debug_print_timestamp(0, "start main loop profiling");
11816 #endif
11817
11818   SCAN_PLAYFIELD(x, y)
11819   {
11820     ChangeCount[x][y] = 0;
11821     ChangeEvent[x][y] = -1;
11822
11823     /* this must be handled before main playfield loop */
11824     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11825     {
11826       MovDelay[x][y]--;
11827       if (MovDelay[x][y] <= 0)
11828         RemoveField(x, y);
11829     }
11830
11831 #if USE_NEW_SNAP_DELAY
11832     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11833     {
11834       MovDelay[x][y]--;
11835       if (MovDelay[x][y] <= 0)
11836       {
11837         RemoveField(x, y);
11838         DrawLevelField(x, y);
11839
11840         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11841       }
11842     }
11843 #endif
11844
11845 #if DEBUG
11846     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11847     {
11848       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11849       printf("GameActions(): This should never happen!\n");
11850
11851       ChangePage[x][y] = -1;
11852     }
11853 #endif
11854
11855     Stop[x][y] = FALSE;
11856     if (WasJustMoving[x][y] > 0)
11857       WasJustMoving[x][y]--;
11858     if (WasJustFalling[x][y] > 0)
11859       WasJustFalling[x][y]--;
11860     if (CheckCollision[x][y] > 0)
11861       CheckCollision[x][y]--;
11862     if (CheckImpact[x][y] > 0)
11863       CheckImpact[x][y]--;
11864
11865     GfxFrame[x][y]++;
11866
11867     /* reset finished pushing action (not done in ContinueMoving() to allow
11868        continuous pushing animation for elements with zero push delay) */
11869     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11870     {
11871       ResetGfxAnimation(x, y);
11872       DrawLevelField(x, y);
11873     }
11874
11875 #if DEBUG
11876     if (IS_BLOCKED(x, y))
11877     {
11878       int oldx, oldy;
11879
11880       Blocked2Moving(x, y, &oldx, &oldy);
11881       if (!IS_MOVING(oldx, oldy))
11882       {
11883         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11884         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11885         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11886         printf("GameActions(): This should never happen!\n");
11887       }
11888     }
11889 #endif
11890   }
11891
11892 #if 0
11893   debug_print_timestamp(0, "- time for pre-main loop:");
11894 #endif
11895
11896 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
11897   SCAN_PLAYFIELD(x, y)
11898   {
11899     element = Feld[x][y];
11900     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11901
11902 #if 1
11903     {
11904 #if 1
11905       int element2 = element;
11906       int graphic2 = graphic;
11907 #else
11908       int element2 = Feld[x][y];
11909       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11910 #endif
11911       int last_gfx_frame = GfxFrame[x][y];
11912
11913       if (graphic_info[graphic2].anim_global_sync)
11914         GfxFrame[x][y] = FrameCounter;
11915       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11916         GfxFrame[x][y] = CustomValue[x][y];
11917       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11918         GfxFrame[x][y] = element_info[element2].collect_score;
11919       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11920         GfxFrame[x][y] = ChangeDelay[x][y];
11921
11922       if (redraw && GfxFrame[x][y] != last_gfx_frame)
11923         DrawLevelGraphicAnimation(x, y, graphic2);
11924     }
11925 #else
11926     ResetGfxFrame(x, y, TRUE);
11927 #endif
11928
11929 #if 1
11930     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11931         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11932       ResetRandomAnimationValue(x, y);
11933 #endif
11934
11935 #if 1
11936     SetRandomAnimationValue(x, y);
11937 #endif
11938
11939 #if 1
11940     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11941 #endif
11942   }
11943 #endif  // -------------------- !!! TEST ONLY !!! --------------------
11944
11945 #if 0
11946   debug_print_timestamp(0, "- time for TEST loop:     -->");
11947 #endif
11948
11949   SCAN_PLAYFIELD(x, y)
11950   {
11951     element = Feld[x][y];
11952     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11953
11954     ResetGfxFrame(x, y, TRUE);
11955
11956     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11957         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11958       ResetRandomAnimationValue(x, y);
11959
11960     SetRandomAnimationValue(x, y);
11961
11962     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11963
11964     if (IS_INACTIVE(element))
11965     {
11966       if (IS_ANIMATED(graphic))
11967         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11968
11969       continue;
11970     }
11971
11972     /* this may take place after moving, so 'element' may have changed */
11973     if (IS_CHANGING(x, y) &&
11974         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11975     {
11976       int page = element_info[element].event_page_nr[CE_DELAY];
11977
11978 #if 1
11979       HandleElementChange(x, y, page);
11980 #else
11981       if (CAN_CHANGE(element))
11982         HandleElementChange(x, y, page);
11983
11984       if (HAS_ACTION(element))
11985         ExecuteCustomElementAction(x, y, element, page);
11986 #endif
11987
11988       element = Feld[x][y];
11989       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11990     }
11991
11992 #if 0   // ---------------------------------------------------------------------
11993
11994     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11995     {
11996       StartMoving(x, y);
11997
11998       element = Feld[x][y];
11999       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12000
12001       if (IS_ANIMATED(graphic) &&
12002           !IS_MOVING(x, y) &&
12003           !Stop[x][y])
12004         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12005
12006       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12007         DrawTwinkleOnField(x, y);
12008     }
12009     else if (IS_MOVING(x, y))
12010       ContinueMoving(x, y);
12011     else
12012     {
12013       switch (element)
12014       {
12015         case EL_ACID:
12016         case EL_EXIT_OPEN:
12017         case EL_EM_EXIT_OPEN:
12018         case EL_SP_EXIT_OPEN:
12019         case EL_STEEL_EXIT_OPEN:
12020         case EL_EM_STEEL_EXIT_OPEN:
12021         case EL_SP_TERMINAL:
12022         case EL_SP_TERMINAL_ACTIVE:
12023         case EL_EXTRA_TIME:
12024         case EL_SHIELD_NORMAL:
12025         case EL_SHIELD_DEADLY:
12026           if (IS_ANIMATED(graphic))
12027             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12028           break;
12029
12030         case EL_DYNAMITE_ACTIVE:
12031         case EL_EM_DYNAMITE_ACTIVE:
12032         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12033         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12034         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12035         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12036         case EL_SP_DISK_RED_ACTIVE:
12037           CheckDynamite(x, y);
12038           break;
12039
12040         case EL_AMOEBA_GROWING:
12041           AmoebeWaechst(x, y);
12042           break;
12043
12044         case EL_AMOEBA_SHRINKING:
12045           AmoebaDisappearing(x, y);
12046           break;
12047
12048 #if !USE_NEW_AMOEBA_CODE
12049         case EL_AMOEBA_WET:
12050         case EL_AMOEBA_DRY:
12051         case EL_AMOEBA_FULL:
12052         case EL_BD_AMOEBA:
12053         case EL_EMC_DRIPPER:
12054           AmoebeAbleger(x, y);
12055           break;
12056 #endif
12057
12058         case EL_GAME_OF_LIFE:
12059         case EL_BIOMAZE:
12060           Life(x, y);
12061           break;
12062
12063         case EL_EXIT_CLOSED:
12064           CheckExit(x, y);
12065           break;
12066
12067         case EL_EM_EXIT_CLOSED:
12068           CheckExitEM(x, y);
12069           break;
12070
12071         case EL_STEEL_EXIT_CLOSED:
12072           CheckExitSteel(x, y);
12073           break;
12074
12075         case EL_EM_STEEL_EXIT_CLOSED:
12076           CheckExitSteelEM(x, y);
12077           break;
12078
12079         case EL_SP_EXIT_CLOSED:
12080           CheckExitSP(x, y);
12081           break;
12082
12083         case EL_EXPANDABLE_WALL_GROWING:
12084         case EL_EXPANDABLE_STEELWALL_GROWING:
12085           MauerWaechst(x, y);
12086           break;
12087
12088         case EL_EXPANDABLE_WALL:
12089         case EL_EXPANDABLE_WALL_HORIZONTAL:
12090         case EL_EXPANDABLE_WALL_VERTICAL:
12091         case EL_EXPANDABLE_WALL_ANY:
12092         case EL_BD_EXPANDABLE_WALL:
12093           MauerAbleger(x, y);
12094           break;
12095
12096         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12097         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12098         case EL_EXPANDABLE_STEELWALL_ANY:
12099           MauerAblegerStahl(x, y);
12100           break;
12101
12102         case EL_FLAMES:
12103           CheckForDragon(x, y);
12104           break;
12105
12106         case EL_EXPLOSION:
12107           break;
12108
12109         case EL_ELEMENT_SNAPPING:
12110         case EL_DIAGONAL_SHRINKING:
12111         case EL_DIAGONAL_GROWING:
12112         {
12113           graphic =
12114             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12115
12116           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12117           break;
12118         }
12119
12120         default:
12121           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12122             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12123           break;
12124       }
12125     }
12126
12127 #else   // ---------------------------------------------------------------------
12128
12129     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12130     {
12131       StartMoving(x, y);
12132
12133       element = Feld[x][y];
12134       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12135
12136       if (IS_ANIMATED(graphic) &&
12137           !IS_MOVING(x, y) &&
12138           !Stop[x][y])
12139         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12140
12141       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12142         DrawTwinkleOnField(x, y);
12143     }
12144     else if ((element == EL_ACID ||
12145               element == EL_EXIT_OPEN ||
12146               element == EL_EM_EXIT_OPEN ||
12147               element == EL_SP_EXIT_OPEN ||
12148               element == EL_STEEL_EXIT_OPEN ||
12149               element == EL_EM_STEEL_EXIT_OPEN ||
12150               element == EL_SP_TERMINAL ||
12151               element == EL_SP_TERMINAL_ACTIVE ||
12152               element == EL_EXTRA_TIME ||
12153               element == EL_SHIELD_NORMAL ||
12154               element == EL_SHIELD_DEADLY) &&
12155              IS_ANIMATED(graphic))
12156       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12157     else if (IS_MOVING(x, y))
12158       ContinueMoving(x, y);
12159     else if (IS_ACTIVE_BOMB(element))
12160       CheckDynamite(x, y);
12161     else if (element == EL_AMOEBA_GROWING)
12162       AmoebeWaechst(x, y);
12163     else if (element == EL_AMOEBA_SHRINKING)
12164       AmoebaDisappearing(x, y);
12165
12166 #if !USE_NEW_AMOEBA_CODE
12167     else if (IS_AMOEBALIVE(element))
12168       AmoebeAbleger(x, y);
12169 #endif
12170
12171     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12172       Life(x, y);
12173     else if (element == EL_EXIT_CLOSED)
12174       CheckExit(x, y);
12175     else if (element == EL_EM_EXIT_CLOSED)
12176       CheckExitEM(x, y);
12177     else if (element == EL_STEEL_EXIT_CLOSED)
12178       CheckExitSteel(x, y);
12179     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12180       CheckExitSteelEM(x, y);
12181     else if (element == EL_SP_EXIT_CLOSED)
12182       CheckExitSP(x, y);
12183     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12184              element == EL_EXPANDABLE_STEELWALL_GROWING)
12185       MauerWaechst(x, y);
12186     else if (element == EL_EXPANDABLE_WALL ||
12187              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12188              element == EL_EXPANDABLE_WALL_VERTICAL ||
12189              element == EL_EXPANDABLE_WALL_ANY ||
12190              element == EL_BD_EXPANDABLE_WALL)
12191       MauerAbleger(x, y);
12192     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12193              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12194              element == EL_EXPANDABLE_STEELWALL_ANY)
12195       MauerAblegerStahl(x, y);
12196     else if (element == EL_FLAMES)
12197       CheckForDragon(x, y);
12198     else if (element == EL_EXPLOSION)
12199       ; /* drawing of correct explosion animation is handled separately */
12200     else if (element == EL_ELEMENT_SNAPPING ||
12201              element == EL_DIAGONAL_SHRINKING ||
12202              element == EL_DIAGONAL_GROWING)
12203     {
12204       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12205
12206       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12207     }
12208     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12209       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12210
12211 #endif  // ---------------------------------------------------------------------
12212
12213     if (IS_BELT_ACTIVE(element))
12214       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12215
12216     if (game.magic_wall_active)
12217     {
12218       int jx = local_player->jx, jy = local_player->jy;
12219
12220       /* play the element sound at the position nearest to the player */
12221       if ((element == EL_MAGIC_WALL_FULL ||
12222            element == EL_MAGIC_WALL_ACTIVE ||
12223            element == EL_MAGIC_WALL_EMPTYING ||
12224            element == EL_BD_MAGIC_WALL_FULL ||
12225            element == EL_BD_MAGIC_WALL_ACTIVE ||
12226            element == EL_BD_MAGIC_WALL_EMPTYING ||
12227            element == EL_DC_MAGIC_WALL_FULL ||
12228            element == EL_DC_MAGIC_WALL_ACTIVE ||
12229            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12230           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12231       {
12232         magic_wall_x = x;
12233         magic_wall_y = y;
12234       }
12235     }
12236   }
12237
12238 #if 0
12239   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12240 #endif
12241
12242 #if USE_NEW_AMOEBA_CODE
12243   /* new experimental amoeba growth stuff */
12244   if (!(FrameCounter % 8))
12245   {
12246     static unsigned long random = 1684108901;
12247
12248     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12249     {
12250       x = RND(lev_fieldx);
12251       y = RND(lev_fieldy);
12252       element = Feld[x][y];
12253
12254       if (!IS_PLAYER(x,y) &&
12255           (element == EL_EMPTY ||
12256            CAN_GROW_INTO(element) ||
12257            element == EL_QUICKSAND_EMPTY ||
12258            element == EL_QUICKSAND_FAST_EMPTY ||
12259            element == EL_ACID_SPLASH_LEFT ||
12260            element == EL_ACID_SPLASH_RIGHT))
12261       {
12262         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12263             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12264             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12265             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12266           Feld[x][y] = EL_AMOEBA_DROP;
12267       }
12268
12269       random = random * 129 + 1;
12270     }
12271   }
12272 #endif
12273
12274 #if 0
12275   if (game.explosions_delayed)
12276 #endif
12277   {
12278     game.explosions_delayed = FALSE;
12279
12280     SCAN_PLAYFIELD(x, y)
12281     {
12282       element = Feld[x][y];
12283
12284       if (ExplodeField[x][y])
12285         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12286       else if (element == EL_EXPLOSION)
12287         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12288
12289       ExplodeField[x][y] = EX_TYPE_NONE;
12290     }
12291
12292     game.explosions_delayed = TRUE;
12293   }
12294
12295   if (game.magic_wall_active)
12296   {
12297     if (!(game.magic_wall_time_left % 4))
12298     {
12299       int element = Feld[magic_wall_x][magic_wall_y];
12300
12301       if (element == EL_BD_MAGIC_WALL_FULL ||
12302           element == EL_BD_MAGIC_WALL_ACTIVE ||
12303           element == EL_BD_MAGIC_WALL_EMPTYING)
12304         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12305       else if (element == EL_DC_MAGIC_WALL_FULL ||
12306                element == EL_DC_MAGIC_WALL_ACTIVE ||
12307                element == EL_DC_MAGIC_WALL_EMPTYING)
12308         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12309       else
12310         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12311     }
12312
12313     if (game.magic_wall_time_left > 0)
12314     {
12315       game.magic_wall_time_left--;
12316
12317       if (!game.magic_wall_time_left)
12318       {
12319         SCAN_PLAYFIELD(x, y)
12320         {
12321           element = Feld[x][y];
12322
12323           if (element == EL_MAGIC_WALL_ACTIVE ||
12324               element == EL_MAGIC_WALL_FULL)
12325           {
12326             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12327             DrawLevelField(x, y);
12328           }
12329           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12330                    element == EL_BD_MAGIC_WALL_FULL)
12331           {
12332             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12333             DrawLevelField(x, y);
12334           }
12335           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12336                    element == EL_DC_MAGIC_WALL_FULL)
12337           {
12338             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12339             DrawLevelField(x, y);
12340           }
12341         }
12342
12343         game.magic_wall_active = FALSE;
12344       }
12345     }
12346   }
12347
12348   if (game.light_time_left > 0)
12349   {
12350     game.light_time_left--;
12351
12352     if (game.light_time_left == 0)
12353       RedrawAllLightSwitchesAndInvisibleElements();
12354   }
12355
12356   if (game.timegate_time_left > 0)
12357   {
12358     game.timegate_time_left--;
12359
12360     if (game.timegate_time_left == 0)
12361       CloseAllOpenTimegates();
12362   }
12363
12364   if (game.lenses_time_left > 0)
12365   {
12366     game.lenses_time_left--;
12367
12368     if (game.lenses_time_left == 0)
12369       RedrawAllInvisibleElementsForLenses();
12370   }
12371
12372   if (game.magnify_time_left > 0)
12373   {
12374     game.magnify_time_left--;
12375
12376     if (game.magnify_time_left == 0)
12377       RedrawAllInvisibleElementsForMagnifier();
12378   }
12379
12380   for (i = 0; i < MAX_PLAYERS; i++)
12381   {
12382     struct PlayerInfo *player = &stored_player[i];
12383
12384     if (SHIELD_ON(player))
12385     {
12386       if (player->shield_deadly_time_left)
12387         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12388       else if (player->shield_normal_time_left)
12389         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12390     }
12391   }
12392
12393   CheckLevelTime();
12394
12395   DrawAllPlayers();
12396   PlayAllPlayersSound();
12397
12398   if (options.debug)                    /* calculate frames per second */
12399   {
12400     static unsigned long fps_counter = 0;
12401     static int fps_frames = 0;
12402     unsigned long fps_delay_ms = Counter() - fps_counter;
12403
12404     fps_frames++;
12405
12406     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
12407     {
12408       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12409
12410       fps_frames = 0;
12411       fps_counter = Counter();
12412     }
12413
12414     redraw_mask |= REDRAW_FPS;
12415   }
12416
12417   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12418
12419   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12420   {
12421     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12422
12423     local_player->show_envelope = 0;
12424   }
12425
12426 #if 0
12427   debug_print_timestamp(0, "stop main loop profiling ");
12428   printf("----------------------------------------------------------\n");
12429 #endif
12430
12431   /* use random number generator in every frame to make it less predictable */
12432   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12433     RND(1);
12434 }
12435
12436 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12437 {
12438   int min_x = x, min_y = y, max_x = x, max_y = y;
12439   int i;
12440
12441   for (i = 0; i < MAX_PLAYERS; i++)
12442   {
12443     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12444
12445     if (!stored_player[i].active || &stored_player[i] == player)
12446       continue;
12447
12448     min_x = MIN(min_x, jx);
12449     min_y = MIN(min_y, jy);
12450     max_x = MAX(max_x, jx);
12451     max_y = MAX(max_y, jy);
12452   }
12453
12454   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12455 }
12456
12457 static boolean AllPlayersInVisibleScreen()
12458 {
12459   int i;
12460
12461   for (i = 0; i < MAX_PLAYERS; i++)
12462   {
12463     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12464
12465     if (!stored_player[i].active)
12466       continue;
12467
12468     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12469       return FALSE;
12470   }
12471
12472   return TRUE;
12473 }
12474
12475 void ScrollLevel(int dx, int dy)
12476 {
12477 #if 1
12478   static Bitmap *bitmap_db_field2 = NULL;
12479   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12480   int x, y;
12481 #else
12482   int i, x, y;
12483 #endif
12484
12485 #if 0
12486   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12487   /* only horizontal XOR vertical scroll direction allowed */
12488   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12489     return;
12490 #endif
12491
12492 #if 1
12493   if (bitmap_db_field2 == NULL)
12494     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12495
12496   /* needed when blitting directly to same bitmap -- should not be needed with
12497      recent SDL libraries, but apparently does not work in 1.2.11 directly */
12498   BlitBitmap(drawto_field, bitmap_db_field2,
12499              FX + TILEX * (dx == -1) - softscroll_offset,
12500              FY + TILEY * (dy == -1) - softscroll_offset,
12501              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12502              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12503              FX + TILEX * (dx == 1) - softscroll_offset,
12504              FY + TILEY * (dy == 1) - softscroll_offset);
12505   BlitBitmap(bitmap_db_field2, drawto_field,
12506              FX + TILEX * (dx == 1) - softscroll_offset,
12507              FY + TILEY * (dy == 1) - softscroll_offset,
12508              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12509              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12510              FX + TILEX * (dx == 1) - softscroll_offset,
12511              FY + TILEY * (dy == 1) - softscroll_offset);
12512
12513 #else
12514
12515 #if 0
12516   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12517   int xsize = (BX2 - BX1 + 1);
12518   int ysize = (BY2 - BY1 + 1);
12519   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12520   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12521   int step  = (start < end ? +1 : -1);
12522
12523   for (i = start; i != end; i += step)
12524   {
12525     BlitBitmap(drawto_field, drawto_field,
12526                FX + TILEX * (dx != 0 ? i + step : 0),
12527                FY + TILEY * (dy != 0 ? i + step : 0),
12528                TILEX * (dx != 0 ? 1 : xsize),
12529                TILEY * (dy != 0 ? 1 : ysize),
12530                FX + TILEX * (dx != 0 ? i : 0),
12531                FY + TILEY * (dy != 0 ? i : 0));
12532   }
12533
12534 #else
12535
12536   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12537
12538   BlitBitmap(drawto_field, drawto_field,
12539              FX + TILEX * (dx == -1) - softscroll_offset,
12540              FY + TILEY * (dy == -1) - softscroll_offset,
12541              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12542              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12543              FX + TILEX * (dx == 1) - softscroll_offset,
12544              FY + TILEY * (dy == 1) - softscroll_offset);
12545 #endif
12546 #endif
12547
12548   if (dx != 0)
12549   {
12550     x = (dx == 1 ? BX1 : BX2);
12551     for (y = BY1; y <= BY2; y++)
12552       DrawScreenField(x, y);
12553   }
12554
12555   if (dy != 0)
12556   {
12557     y = (dy == 1 ? BY1 : BY2);
12558     for (x = BX1; x <= BX2; x++)
12559       DrawScreenField(x, y);
12560   }
12561
12562   redraw_mask |= REDRAW_FIELD;
12563 }
12564
12565 static boolean canFallDown(struct PlayerInfo *player)
12566 {
12567   int jx = player->jx, jy = player->jy;
12568
12569   return (IN_LEV_FIELD(jx, jy + 1) &&
12570           (IS_FREE(jx, jy + 1) ||
12571            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12572           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12573           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12574 }
12575
12576 static boolean canPassField(int x, int y, int move_dir)
12577 {
12578   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12579   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12580   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12581   int nextx = x + dx;
12582   int nexty = y + dy;
12583   int element = Feld[x][y];
12584
12585   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12586           !CAN_MOVE(element) &&
12587           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12588           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12589           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12590 }
12591
12592 static boolean canMoveToValidFieldWithGravity(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 newx = x + dx;
12598   int newy = y + dy;
12599
12600   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12601           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12602           (IS_DIGGABLE(Feld[newx][newy]) ||
12603            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12604            canPassField(newx, newy, move_dir)));
12605 }
12606
12607 static void CheckGravityMovement(struct PlayerInfo *player)
12608 {
12609 #if USE_PLAYER_GRAVITY
12610   if (player->gravity && !player->programmed_action)
12611 #else
12612   if (game.gravity && !player->programmed_action)
12613 #endif
12614   {
12615     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12616     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12617     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12618     int jx = player->jx, jy = player->jy;
12619     boolean player_is_moving_to_valid_field =
12620       (!player_is_snapping &&
12621        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12622         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12623     boolean player_can_fall_down = canFallDown(player);
12624
12625     if (player_can_fall_down &&
12626         !player_is_moving_to_valid_field)
12627       player->programmed_action = MV_DOWN;
12628   }
12629 }
12630
12631 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12632 {
12633   return CheckGravityMovement(player);
12634
12635 #if USE_PLAYER_GRAVITY
12636   if (player->gravity && !player->programmed_action)
12637 #else
12638   if (game.gravity && !player->programmed_action)
12639 #endif
12640   {
12641     int jx = player->jx, jy = player->jy;
12642     boolean field_under_player_is_free =
12643       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12644     boolean player_is_standing_on_valid_field =
12645       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12646        (IS_WALKABLE(Feld[jx][jy]) &&
12647         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12648
12649     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12650       player->programmed_action = MV_DOWN;
12651   }
12652 }
12653
12654 /*
12655   MovePlayerOneStep()
12656   -----------------------------------------------------------------------------
12657   dx, dy:               direction (non-diagonal) to try to move the player to
12658   real_dx, real_dy:     direction as read from input device (can be diagonal)
12659 */
12660
12661 boolean MovePlayerOneStep(struct PlayerInfo *player,
12662                           int dx, int dy, int real_dx, int real_dy)
12663 {
12664   int jx = player->jx, jy = player->jy;
12665   int new_jx = jx + dx, new_jy = jy + dy;
12666 #if !USE_FIXED_DONT_RUN_INTO
12667   int element;
12668 #endif
12669   int can_move;
12670   boolean player_can_move = !player->cannot_move;
12671
12672   if (!player->active || (!dx && !dy))
12673     return MP_NO_ACTION;
12674
12675   player->MovDir = (dx < 0 ? MV_LEFT :
12676                     dx > 0 ? MV_RIGHT :
12677                     dy < 0 ? MV_UP :
12678                     dy > 0 ? MV_DOWN :  MV_NONE);
12679
12680   if (!IN_LEV_FIELD(new_jx, new_jy))
12681     return MP_NO_ACTION;
12682
12683   if (!player_can_move)
12684   {
12685     if (player->MovPos == 0)
12686     {
12687       player->is_moving = FALSE;
12688       player->is_digging = FALSE;
12689       player->is_collecting = FALSE;
12690       player->is_snapping = FALSE;
12691       player->is_pushing = FALSE;
12692     }
12693   }
12694
12695 #if 1
12696   if (!options.network && game.centered_player_nr == -1 &&
12697       !AllPlayersInSight(player, new_jx, new_jy))
12698     return MP_NO_ACTION;
12699 #else
12700   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12701     return MP_NO_ACTION;
12702 #endif
12703
12704 #if !USE_FIXED_DONT_RUN_INTO
12705   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12706
12707   /* (moved to DigField()) */
12708   if (player_can_move && DONT_RUN_INTO(element))
12709   {
12710     if (element == EL_ACID && dx == 0 && dy == 1)
12711     {
12712       SplashAcid(new_jx, new_jy);
12713       Feld[jx][jy] = EL_PLAYER_1;
12714       InitMovingField(jx, jy, MV_DOWN);
12715       Store[jx][jy] = EL_ACID;
12716       ContinueMoving(jx, jy);
12717       BuryPlayer(player);
12718     }
12719     else
12720       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12721
12722     return MP_MOVING;
12723   }
12724 #endif
12725
12726   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12727   if (can_move != MP_MOVING)
12728     return can_move;
12729
12730   /* check if DigField() has caused relocation of the player */
12731   if (player->jx != jx || player->jy != jy)
12732     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12733
12734   StorePlayer[jx][jy] = 0;
12735   player->last_jx = jx;
12736   player->last_jy = jy;
12737   player->jx = new_jx;
12738   player->jy = new_jy;
12739   StorePlayer[new_jx][new_jy] = player->element_nr;
12740
12741   if (player->move_delay_value_next != -1)
12742   {
12743     player->move_delay_value = player->move_delay_value_next;
12744     player->move_delay_value_next = -1;
12745   }
12746
12747   player->MovPos =
12748     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12749
12750   player->step_counter++;
12751
12752   PlayerVisit[jx][jy] = FrameCounter;
12753
12754 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12755   player->is_moving = TRUE;
12756 #endif
12757
12758 #if 1
12759   /* should better be called in MovePlayer(), but this breaks some tapes */
12760   ScrollPlayer(player, SCROLL_INIT);
12761 #endif
12762
12763   return MP_MOVING;
12764 }
12765
12766 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12767 {
12768   int jx = player->jx, jy = player->jy;
12769   int old_jx = jx, old_jy = jy;
12770   int moved = MP_NO_ACTION;
12771
12772   if (!player->active)
12773     return FALSE;
12774
12775   if (!dx && !dy)
12776   {
12777     if (player->MovPos == 0)
12778     {
12779       player->is_moving = FALSE;
12780       player->is_digging = FALSE;
12781       player->is_collecting = FALSE;
12782       player->is_snapping = FALSE;
12783       player->is_pushing = FALSE;
12784     }
12785
12786     return FALSE;
12787   }
12788
12789   if (player->move_delay > 0)
12790     return FALSE;
12791
12792   player->move_delay = -1;              /* set to "uninitialized" value */
12793
12794   /* store if player is automatically moved to next field */
12795   player->is_auto_moving = (player->programmed_action != MV_NONE);
12796
12797   /* remove the last programmed player action */
12798   player->programmed_action = 0;
12799
12800   if (player->MovPos)
12801   {
12802     /* should only happen if pre-1.2 tape recordings are played */
12803     /* this is only for backward compatibility */
12804
12805     int original_move_delay_value = player->move_delay_value;
12806
12807 #if DEBUG
12808     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12809            tape.counter);
12810 #endif
12811
12812     /* scroll remaining steps with finest movement resolution */
12813     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12814
12815     while (player->MovPos)
12816     {
12817       ScrollPlayer(player, SCROLL_GO_ON);
12818       ScrollScreen(NULL, SCROLL_GO_ON);
12819
12820       AdvanceFrameAndPlayerCounters(player->index_nr);
12821
12822       DrawAllPlayers();
12823       BackToFront();
12824     }
12825
12826     player->move_delay_value = original_move_delay_value;
12827   }
12828
12829   player->is_active = FALSE;
12830
12831   if (player->last_move_dir & MV_HORIZONTAL)
12832   {
12833     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12834       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12835   }
12836   else
12837   {
12838     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12839       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12840   }
12841
12842 #if USE_FIXED_BORDER_RUNNING_GFX
12843   if (!moved && !player->is_active)
12844   {
12845     player->is_moving = FALSE;
12846     player->is_digging = FALSE;
12847     player->is_collecting = FALSE;
12848     player->is_snapping = FALSE;
12849     player->is_pushing = FALSE;
12850   }
12851 #endif
12852
12853   jx = player->jx;
12854   jy = player->jy;
12855
12856 #if 1
12857   if (moved & MP_MOVING && !ScreenMovPos &&
12858       (player->index_nr == game.centered_player_nr ||
12859        game.centered_player_nr == -1))
12860 #else
12861   if (moved & MP_MOVING && !ScreenMovPos &&
12862       (player == local_player || !options.network))
12863 #endif
12864   {
12865     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12866     int offset = game.scroll_delay_value;
12867
12868     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12869     {
12870       /* actual player has left the screen -- scroll in that direction */
12871       if (jx != old_jx)         /* player has moved horizontally */
12872         scroll_x += (jx - old_jx);
12873       else                      /* player has moved vertically */
12874         scroll_y += (jy - old_jy);
12875     }
12876     else
12877     {
12878       if (jx != old_jx)         /* player has moved horizontally */
12879       {
12880         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12881             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12882           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12883
12884         /* don't scroll over playfield boundaries */
12885         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12886           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12887
12888         /* don't scroll more than one field at a time */
12889         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12890
12891         /* don't scroll against the player's moving direction */
12892         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12893             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12894           scroll_x = old_scroll_x;
12895       }
12896       else                      /* player has moved vertically */
12897       {
12898         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12899             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12900           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12901
12902         /* don't scroll over playfield boundaries */
12903         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12904           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12905
12906         /* don't scroll more than one field at a time */
12907         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12908
12909         /* don't scroll against the player's moving direction */
12910         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12911             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12912           scroll_y = old_scroll_y;
12913       }
12914     }
12915
12916     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12917     {
12918 #if 1
12919       if (!options.network && game.centered_player_nr == -1 &&
12920           !AllPlayersInVisibleScreen())
12921       {
12922         scroll_x = old_scroll_x;
12923         scroll_y = old_scroll_y;
12924       }
12925       else
12926 #else
12927       if (!options.network && !AllPlayersInVisibleScreen())
12928       {
12929         scroll_x = old_scroll_x;
12930         scroll_y = old_scroll_y;
12931       }
12932       else
12933 #endif
12934       {
12935         ScrollScreen(player, SCROLL_INIT);
12936         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12937       }
12938     }
12939   }
12940
12941   player->StepFrame = 0;
12942
12943   if (moved & MP_MOVING)
12944   {
12945     if (old_jx != jx && old_jy == jy)
12946       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12947     else if (old_jx == jx && old_jy != jy)
12948       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12949
12950     DrawLevelField(jx, jy);     /* for "crumbled sand" */
12951
12952     player->last_move_dir = player->MovDir;
12953     player->is_moving = TRUE;
12954     player->is_snapping = FALSE;
12955     player->is_switching = FALSE;
12956     player->is_dropping = FALSE;
12957     player->is_dropping_pressed = FALSE;
12958     player->drop_pressed_delay = 0;
12959
12960 #if 0
12961     /* should better be called here than above, but this breaks some tapes */
12962     ScrollPlayer(player, SCROLL_INIT);
12963 #endif
12964   }
12965   else
12966   {
12967     CheckGravityMovementWhenNotMoving(player);
12968
12969     player->is_moving = FALSE;
12970
12971     /* at this point, the player is allowed to move, but cannot move right now
12972        (e.g. because of something blocking the way) -- ensure that the player
12973        is also allowed to move in the next frame (in old versions before 3.1.1,
12974        the player was forced to wait again for eight frames before next try) */
12975
12976     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12977       player->move_delay = 0;   /* allow direct movement in the next frame */
12978   }
12979
12980   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12981     player->move_delay = player->move_delay_value;
12982
12983   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12984   {
12985     TestIfPlayerTouchesBadThing(jx, jy);
12986     TestIfPlayerTouchesCustomElement(jx, jy);
12987   }
12988
12989   if (!player->active)
12990     RemovePlayer(player);
12991
12992   return moved;
12993 }
12994
12995 void ScrollPlayer(struct PlayerInfo *player, int mode)
12996 {
12997   int jx = player->jx, jy = player->jy;
12998   int last_jx = player->last_jx, last_jy = player->last_jy;
12999   int move_stepsize = TILEX / player->move_delay_value;
13000
13001 #if USE_NEW_PLAYER_SPEED
13002   if (!player->active)
13003     return;
13004
13005   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13006     return;
13007 #else
13008   if (!player->active || player->MovPos == 0)
13009     return;
13010 #endif
13011
13012   if (mode == SCROLL_INIT)
13013   {
13014     player->actual_frame_counter = FrameCounter;
13015     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13016
13017     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13018         Feld[last_jx][last_jy] == EL_EMPTY)
13019     {
13020       int last_field_block_delay = 0;   /* start with no blocking at all */
13021       int block_delay_adjustment = player->block_delay_adjustment;
13022
13023       /* if player blocks last field, add delay for exactly one move */
13024       if (player->block_last_field)
13025       {
13026         last_field_block_delay += player->move_delay_value;
13027
13028         /* when blocking enabled, prevent moving up despite gravity */
13029 #if USE_PLAYER_GRAVITY
13030         if (player->gravity && player->MovDir == MV_UP)
13031           block_delay_adjustment = -1;
13032 #else
13033         if (game.gravity && player->MovDir == MV_UP)
13034           block_delay_adjustment = -1;
13035 #endif
13036       }
13037
13038       /* add block delay adjustment (also possible when not blocking) */
13039       last_field_block_delay += block_delay_adjustment;
13040
13041       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13042       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13043     }
13044
13045 #if USE_NEW_PLAYER_SPEED
13046     if (player->MovPos != 0)    /* player has not yet reached destination */
13047       return;
13048 #else
13049     return;
13050 #endif
13051   }
13052   else if (!FrameReached(&player->actual_frame_counter, 1))
13053     return;
13054
13055 #if USE_NEW_PLAYER_SPEED
13056   if (player->MovPos != 0)
13057   {
13058     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13059     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13060
13061     /* before DrawPlayer() to draw correct player graphic for this case */
13062     if (player->MovPos == 0)
13063       CheckGravityMovement(player);
13064   }
13065 #else
13066   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13067   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13068
13069   /* before DrawPlayer() to draw correct player graphic for this case */
13070   if (player->MovPos == 0)
13071     CheckGravityMovement(player);
13072 #endif
13073
13074   if (player->MovPos == 0)      /* player reached destination field */
13075   {
13076     if (player->move_delay_reset_counter > 0)
13077     {
13078       player->move_delay_reset_counter--;
13079
13080       if (player->move_delay_reset_counter == 0)
13081       {
13082         /* continue with normal speed after quickly moving through gate */
13083         HALVE_PLAYER_SPEED(player);
13084
13085         /* be able to make the next move without delay */
13086         player->move_delay = 0;
13087       }
13088     }
13089
13090     player->last_jx = jx;
13091     player->last_jy = jy;
13092
13093     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13094         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13095         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13096         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13097         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13098         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13099     {
13100       DrawPlayer(player);       /* needed here only to cleanup last field */
13101       RemovePlayer(player);
13102
13103       if (local_player->friends_still_needed == 0 ||
13104           IS_SP_ELEMENT(Feld[jx][jy]))
13105         PlayerWins(player);
13106     }
13107
13108     /* this breaks one level: "machine", level 000 */
13109     {
13110       int move_direction = player->MovDir;
13111       int enter_side = MV_DIR_OPPOSITE(move_direction);
13112       int leave_side = move_direction;
13113       int old_jx = last_jx;
13114       int old_jy = last_jy;
13115       int old_element = Feld[old_jx][old_jy];
13116       int new_element = Feld[jx][jy];
13117
13118       if (IS_CUSTOM_ELEMENT(old_element))
13119         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13120                                    CE_LEFT_BY_PLAYER,
13121                                    player->index_bit, leave_side);
13122
13123       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13124                                           CE_PLAYER_LEAVES_X,
13125                                           player->index_bit, leave_side);
13126
13127       if (IS_CUSTOM_ELEMENT(new_element))
13128         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13129                                    player->index_bit, enter_side);
13130
13131       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13132                                           CE_PLAYER_ENTERS_X,
13133                                           player->index_bit, enter_side);
13134
13135       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13136                                         CE_MOVE_OF_X, move_direction);
13137     }
13138
13139     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13140     {
13141       TestIfPlayerTouchesBadThing(jx, jy);
13142       TestIfPlayerTouchesCustomElement(jx, jy);
13143
13144       /* needed because pushed element has not yet reached its destination,
13145          so it would trigger a change event at its previous field location */
13146       if (!player->is_pushing)
13147         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13148
13149       if (!player->active)
13150         RemovePlayer(player);
13151     }
13152
13153     if (!local_player->LevelSolved && level.use_step_counter)
13154     {
13155       int i;
13156
13157       TimePlayed++;
13158
13159       if (TimeLeft > 0)
13160       {
13161         TimeLeft--;
13162
13163         if (TimeLeft <= 10 && setup.time_limit)
13164           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13165
13166 #if 1
13167         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13168
13169         DisplayGameControlValues();
13170 #else
13171         DrawGameValue_Time(TimeLeft);
13172 #endif
13173
13174         if (!TimeLeft && setup.time_limit)
13175           for (i = 0; i < MAX_PLAYERS; i++)
13176             KillPlayer(&stored_player[i]);
13177       }
13178 #if 1
13179       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13180       {
13181         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13182
13183         DisplayGameControlValues();
13184       }
13185 #else
13186       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13187         DrawGameValue_Time(TimePlayed);
13188 #endif
13189     }
13190
13191     if (tape.single_step && tape.recording && !tape.pausing &&
13192         !player->programmed_action)
13193       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13194   }
13195 }
13196
13197 void ScrollScreen(struct PlayerInfo *player, int mode)
13198 {
13199   static unsigned long screen_frame_counter = 0;
13200
13201   if (mode == SCROLL_INIT)
13202   {
13203     /* set scrolling step size according to actual player's moving speed */
13204     ScrollStepSize = TILEX / player->move_delay_value;
13205
13206     screen_frame_counter = FrameCounter;
13207     ScreenMovDir = player->MovDir;
13208     ScreenMovPos = player->MovPos;
13209     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13210     return;
13211   }
13212   else if (!FrameReached(&screen_frame_counter, 1))
13213     return;
13214
13215   if (ScreenMovPos)
13216   {
13217     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13218     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13219     redraw_mask |= REDRAW_FIELD;
13220   }
13221   else
13222     ScreenMovDir = MV_NONE;
13223 }
13224
13225 void TestIfPlayerTouchesCustomElement(int x, int y)
13226 {
13227   static int xy[4][2] =
13228   {
13229     { 0, -1 },
13230     { -1, 0 },
13231     { +1, 0 },
13232     { 0, +1 }
13233   };
13234   static int trigger_sides[4][2] =
13235   {
13236     /* center side       border side */
13237     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13238     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13239     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13240     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13241   };
13242   static int touch_dir[4] =
13243   {
13244     MV_LEFT | MV_RIGHT,
13245     MV_UP   | MV_DOWN,
13246     MV_UP   | MV_DOWN,
13247     MV_LEFT | MV_RIGHT
13248   };
13249   int center_element = Feld[x][y];      /* should always be non-moving! */
13250   int i;
13251
13252   for (i = 0; i < NUM_DIRECTIONS; i++)
13253   {
13254     int xx = x + xy[i][0];
13255     int yy = y + xy[i][1];
13256     int center_side = trigger_sides[i][0];
13257     int border_side = trigger_sides[i][1];
13258     int border_element;
13259
13260     if (!IN_LEV_FIELD(xx, yy))
13261       continue;
13262
13263     if (IS_PLAYER(x, y))
13264     {
13265       struct PlayerInfo *player = PLAYERINFO(x, y);
13266
13267       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13268         border_element = Feld[xx][yy];          /* may be moving! */
13269       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13270         border_element = Feld[xx][yy];
13271       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13272         border_element = MovingOrBlocked2Element(xx, yy);
13273       else
13274         continue;               /* center and border element do not touch */
13275
13276       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13277                                  player->index_bit, border_side);
13278       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13279                                           CE_PLAYER_TOUCHES_X,
13280                                           player->index_bit, border_side);
13281     }
13282     else if (IS_PLAYER(xx, yy))
13283     {
13284       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13285
13286       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13287       {
13288         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13289           continue;             /* center and border element do not touch */
13290       }
13291
13292       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13293                                  player->index_bit, center_side);
13294       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13295                                           CE_PLAYER_TOUCHES_X,
13296                                           player->index_bit, center_side);
13297       break;
13298     }
13299   }
13300 }
13301
13302 #if USE_ELEMENT_TOUCHING_BUGFIX
13303
13304 void TestIfElementTouchesCustomElement(int x, int y)
13305 {
13306   static int xy[4][2] =
13307   {
13308     { 0, -1 },
13309     { -1, 0 },
13310     { +1, 0 },
13311     { 0, +1 }
13312   };
13313   static int trigger_sides[4][2] =
13314   {
13315     /* center side      border side */
13316     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13317     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13318     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13319     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13320   };
13321   static int touch_dir[4] =
13322   {
13323     MV_LEFT | MV_RIGHT,
13324     MV_UP   | MV_DOWN,
13325     MV_UP   | MV_DOWN,
13326     MV_LEFT | MV_RIGHT
13327   };
13328   boolean change_center_element = FALSE;
13329   int center_element = Feld[x][y];      /* should always be non-moving! */
13330   int border_element_old[NUM_DIRECTIONS];
13331   int i;
13332
13333   for (i = 0; i < NUM_DIRECTIONS; i++)
13334   {
13335     int xx = x + xy[i][0];
13336     int yy = y + xy[i][1];
13337     int border_element;
13338
13339     border_element_old[i] = -1;
13340
13341     if (!IN_LEV_FIELD(xx, yy))
13342       continue;
13343
13344     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13345       border_element = Feld[xx][yy];    /* may be moving! */
13346     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13347       border_element = Feld[xx][yy];
13348     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13349       border_element = MovingOrBlocked2Element(xx, yy);
13350     else
13351       continue;                 /* center and border element do not touch */
13352
13353     border_element_old[i] = border_element;
13354   }
13355
13356   for (i = 0; i < NUM_DIRECTIONS; i++)
13357   {
13358     int xx = x + xy[i][0];
13359     int yy = y + xy[i][1];
13360     int center_side = trigger_sides[i][0];
13361     int border_element = border_element_old[i];
13362
13363     if (border_element == -1)
13364       continue;
13365
13366     /* check for change of border element */
13367     CheckElementChangeBySide(xx, yy, border_element, center_element,
13368                              CE_TOUCHING_X, center_side);
13369   }
13370
13371   for (i = 0; i < NUM_DIRECTIONS; i++)
13372   {
13373     int border_side = trigger_sides[i][1];
13374     int border_element = border_element_old[i];
13375
13376     if (border_element == -1)
13377       continue;
13378
13379     /* check for change of center element (but change it only once) */
13380     if (!change_center_element)
13381       change_center_element =
13382         CheckElementChangeBySide(x, y, center_element, border_element,
13383                                  CE_TOUCHING_X, border_side);
13384   }
13385 }
13386
13387 #else
13388
13389 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13390 {
13391   static int xy[4][2] =
13392   {
13393     { 0, -1 },
13394     { -1, 0 },
13395     { +1, 0 },
13396     { 0, +1 }
13397   };
13398   static int trigger_sides[4][2] =
13399   {
13400     /* center side      border side */
13401     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13402     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13403     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13404     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13405   };
13406   static int touch_dir[4] =
13407   {
13408     MV_LEFT | MV_RIGHT,
13409     MV_UP   | MV_DOWN,
13410     MV_UP   | MV_DOWN,
13411     MV_LEFT | MV_RIGHT
13412   };
13413   boolean change_center_element = FALSE;
13414   int center_element = Feld[x][y];      /* should always be non-moving! */
13415   int i;
13416
13417   for (i = 0; i < NUM_DIRECTIONS; i++)
13418   {
13419     int xx = x + xy[i][0];
13420     int yy = y + xy[i][1];
13421     int center_side = trigger_sides[i][0];
13422     int border_side = trigger_sides[i][1];
13423     int border_element;
13424
13425     if (!IN_LEV_FIELD(xx, yy))
13426       continue;
13427
13428     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13429       border_element = Feld[xx][yy];    /* may be moving! */
13430     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13431       border_element = Feld[xx][yy];
13432     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13433       border_element = MovingOrBlocked2Element(xx, yy);
13434     else
13435       continue;                 /* center and border element do not touch */
13436
13437     /* check for change of center element (but change it only once) */
13438     if (!change_center_element)
13439       change_center_element =
13440         CheckElementChangeBySide(x, y, center_element, border_element,
13441                                  CE_TOUCHING_X, border_side);
13442
13443     /* check for change of border element */
13444     CheckElementChangeBySide(xx, yy, border_element, center_element,
13445                              CE_TOUCHING_X, center_side);
13446   }
13447 }
13448
13449 #endif
13450
13451 void TestIfElementHitsCustomElement(int x, int y, int direction)
13452 {
13453   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13454   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13455   int hitx = x + dx, hity = y + dy;
13456   int hitting_element = Feld[x][y];
13457   int touched_element;
13458
13459   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13460     return;
13461
13462   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13463                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13464
13465   if (IN_LEV_FIELD(hitx, hity))
13466   {
13467     int opposite_direction = MV_DIR_OPPOSITE(direction);
13468     int hitting_side = direction;
13469     int touched_side = opposite_direction;
13470     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13471                           MovDir[hitx][hity] != direction ||
13472                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13473
13474     object_hit = TRUE;
13475
13476     if (object_hit)
13477     {
13478       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13479                                CE_HITTING_X, touched_side);
13480
13481       CheckElementChangeBySide(hitx, hity, touched_element,
13482                                hitting_element, CE_HIT_BY_X, hitting_side);
13483
13484       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13485                                CE_HIT_BY_SOMETHING, opposite_direction);
13486     }
13487   }
13488
13489   /* "hitting something" is also true when hitting the playfield border */
13490   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13491                            CE_HITTING_SOMETHING, direction);
13492 }
13493
13494 #if 0
13495 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13496 {
13497   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13498   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13499   int hitx = x + dx, hity = y + dy;
13500   int hitting_element = Feld[x][y];
13501   int touched_element;
13502 #if 0
13503   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13504                         !IS_FREE(hitx, hity) &&
13505                         (!IS_MOVING(hitx, hity) ||
13506                          MovDir[hitx][hity] != direction ||
13507                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
13508 #endif
13509
13510   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13511     return;
13512
13513 #if 0
13514   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13515     return;
13516 #endif
13517
13518   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13519                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13520
13521   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13522                            EP_CAN_SMASH_EVERYTHING, direction);
13523
13524   if (IN_LEV_FIELD(hitx, hity))
13525   {
13526     int opposite_direction = MV_DIR_OPPOSITE(direction);
13527     int hitting_side = direction;
13528     int touched_side = opposite_direction;
13529 #if 0
13530     int touched_element = MovingOrBlocked2Element(hitx, hity);
13531 #endif
13532 #if 1
13533     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13534                           MovDir[hitx][hity] != direction ||
13535                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13536
13537     object_hit = TRUE;
13538 #endif
13539
13540     if (object_hit)
13541     {
13542       int i;
13543
13544       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13545                                CE_SMASHED_BY_SOMETHING, opposite_direction);
13546
13547       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13548                                CE_OTHER_IS_SMASHING, touched_side);
13549
13550       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13551                                CE_OTHER_GETS_SMASHED, hitting_side);
13552     }
13553   }
13554 }
13555 #endif
13556
13557 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13558 {
13559   int i, kill_x = -1, kill_y = -1;
13560
13561   int bad_element = -1;
13562   static int test_xy[4][2] =
13563   {
13564     { 0, -1 },
13565     { -1, 0 },
13566     { +1, 0 },
13567     { 0, +1 }
13568   };
13569   static int test_dir[4] =
13570   {
13571     MV_UP,
13572     MV_LEFT,
13573     MV_RIGHT,
13574     MV_DOWN
13575   };
13576
13577   for (i = 0; i < NUM_DIRECTIONS; i++)
13578   {
13579     int test_x, test_y, test_move_dir, test_element;
13580
13581     test_x = good_x + test_xy[i][0];
13582     test_y = good_y + test_xy[i][1];
13583
13584     if (!IN_LEV_FIELD(test_x, test_y))
13585       continue;
13586
13587     test_move_dir =
13588       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13589
13590     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13591
13592     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13593        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13594     */
13595     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13596         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13597     {
13598       kill_x = test_x;
13599       kill_y = test_y;
13600       bad_element = test_element;
13601
13602       break;
13603     }
13604   }
13605
13606   if (kill_x != -1 || kill_y != -1)
13607   {
13608     if (IS_PLAYER(good_x, good_y))
13609     {
13610       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13611
13612       if (player->shield_deadly_time_left > 0 &&
13613           !IS_INDESTRUCTIBLE(bad_element))
13614         Bang(kill_x, kill_y);
13615       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13616         KillPlayer(player);
13617     }
13618     else
13619       Bang(good_x, good_y);
13620   }
13621 }
13622
13623 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13624 {
13625   int i, kill_x = -1, kill_y = -1;
13626   int bad_element = Feld[bad_x][bad_y];
13627   static int test_xy[4][2] =
13628   {
13629     { 0, -1 },
13630     { -1, 0 },
13631     { +1, 0 },
13632     { 0, +1 }
13633   };
13634   static int touch_dir[4] =
13635   {
13636     MV_LEFT | MV_RIGHT,
13637     MV_UP   | MV_DOWN,
13638     MV_UP   | MV_DOWN,
13639     MV_LEFT | MV_RIGHT
13640   };
13641   static int test_dir[4] =
13642   {
13643     MV_UP,
13644     MV_LEFT,
13645     MV_RIGHT,
13646     MV_DOWN
13647   };
13648
13649   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13650     return;
13651
13652   for (i = 0; i < NUM_DIRECTIONS; i++)
13653   {
13654     int test_x, test_y, test_move_dir, test_element;
13655
13656     test_x = bad_x + test_xy[i][0];
13657     test_y = bad_y + test_xy[i][1];
13658     if (!IN_LEV_FIELD(test_x, test_y))
13659       continue;
13660
13661     test_move_dir =
13662       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13663
13664     test_element = Feld[test_x][test_y];
13665
13666     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13667        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13668     */
13669     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13670         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13671     {
13672       /* good thing is player or penguin that does not move away */
13673       if (IS_PLAYER(test_x, test_y))
13674       {
13675         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13676
13677         if (bad_element == EL_ROBOT && player->is_moving)
13678           continue;     /* robot does not kill player if he is moving */
13679
13680         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13681         {
13682           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13683             continue;           /* center and border element do not touch */
13684         }
13685
13686         kill_x = test_x;
13687         kill_y = test_y;
13688         break;
13689       }
13690       else if (test_element == EL_PENGUIN)
13691       {
13692         kill_x = test_x;
13693         kill_y = test_y;
13694         break;
13695       }
13696     }
13697   }
13698
13699   if (kill_x != -1 || kill_y != -1)
13700   {
13701     if (IS_PLAYER(kill_x, kill_y))
13702     {
13703       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13704
13705       if (player->shield_deadly_time_left > 0 &&
13706           !IS_INDESTRUCTIBLE(bad_element))
13707         Bang(bad_x, bad_y);
13708       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13709         KillPlayer(player);
13710     }
13711     else
13712       Bang(kill_x, kill_y);
13713   }
13714 }
13715
13716 void TestIfPlayerTouchesBadThing(int x, int y)
13717 {
13718   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13719 }
13720
13721 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13722 {
13723   TestIfGoodThingHitsBadThing(x, y, move_dir);
13724 }
13725
13726 void TestIfBadThingTouchesPlayer(int x, int y)
13727 {
13728   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13729 }
13730
13731 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13732 {
13733   TestIfBadThingHitsGoodThing(x, y, move_dir);
13734 }
13735
13736 void TestIfFriendTouchesBadThing(int x, int y)
13737 {
13738   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13739 }
13740
13741 void TestIfBadThingTouchesFriend(int x, int y)
13742 {
13743   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13744 }
13745
13746 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13747 {
13748   int i, kill_x = bad_x, kill_y = bad_y;
13749   static int xy[4][2] =
13750   {
13751     { 0, -1 },
13752     { -1, 0 },
13753     { +1, 0 },
13754     { 0, +1 }
13755   };
13756
13757   for (i = 0; i < NUM_DIRECTIONS; i++)
13758   {
13759     int x, y, element;
13760
13761     x = bad_x + xy[i][0];
13762     y = bad_y + xy[i][1];
13763     if (!IN_LEV_FIELD(x, y))
13764       continue;
13765
13766     element = Feld[x][y];
13767     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13768         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13769     {
13770       kill_x = x;
13771       kill_y = y;
13772       break;
13773     }
13774   }
13775
13776   if (kill_x != bad_x || kill_y != bad_y)
13777     Bang(bad_x, bad_y);
13778 }
13779
13780 void KillPlayer(struct PlayerInfo *player)
13781 {
13782   int jx = player->jx, jy = player->jy;
13783
13784   if (!player->active)
13785     return;
13786
13787   /* the following code was introduced to prevent an infinite loop when calling
13788      -> Bang()
13789      -> CheckTriggeredElementChangeExt()
13790      -> ExecuteCustomElementAction()
13791      -> KillPlayer()
13792      -> (infinitely repeating the above sequence of function calls)
13793      which occurs when killing the player while having a CE with the setting
13794      "kill player X when explosion of <player X>"; the solution using a new
13795      field "player->killed" was chosen for backwards compatibility, although
13796      clever use of the fields "player->active" etc. would probably also work */
13797 #if 1
13798   if (player->killed)
13799     return;
13800 #endif
13801
13802   player->killed = TRUE;
13803
13804   /* remove accessible field at the player's position */
13805   Feld[jx][jy] = EL_EMPTY;
13806
13807   /* deactivate shield (else Bang()/Explode() would not work right) */
13808   player->shield_normal_time_left = 0;
13809   player->shield_deadly_time_left = 0;
13810
13811   Bang(jx, jy);
13812   BuryPlayer(player);
13813 }
13814
13815 static void KillPlayerUnlessEnemyProtected(int x, int y)
13816 {
13817   if (!PLAYER_ENEMY_PROTECTED(x, y))
13818     KillPlayer(PLAYERINFO(x, y));
13819 }
13820
13821 static void KillPlayerUnlessExplosionProtected(int x, int y)
13822 {
13823   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13824     KillPlayer(PLAYERINFO(x, y));
13825 }
13826
13827 void BuryPlayer(struct PlayerInfo *player)
13828 {
13829   int jx = player->jx, jy = player->jy;
13830
13831   if (!player->active)
13832     return;
13833
13834   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13835   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13836
13837   player->GameOver = TRUE;
13838   RemovePlayer(player);
13839 }
13840
13841 void RemovePlayer(struct PlayerInfo *player)
13842 {
13843   int jx = player->jx, jy = player->jy;
13844   int i, found = FALSE;
13845
13846   player->present = FALSE;
13847   player->active = FALSE;
13848
13849   if (!ExplodeField[jx][jy])
13850     StorePlayer[jx][jy] = 0;
13851
13852   if (player->is_moving)
13853     DrawLevelField(player->last_jx, player->last_jy);
13854
13855   for (i = 0; i < MAX_PLAYERS; i++)
13856     if (stored_player[i].active)
13857       found = TRUE;
13858
13859   if (!found)
13860     AllPlayersGone = TRUE;
13861
13862   ExitX = ZX = jx;
13863   ExitY = ZY = jy;
13864 }
13865
13866 #if USE_NEW_SNAP_DELAY
13867 static void setFieldForSnapping(int x, int y, int element, int direction)
13868 {
13869   struct ElementInfo *ei = &element_info[element];
13870   int direction_bit = MV_DIR_TO_BIT(direction);
13871   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13872   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13873                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13874
13875   Feld[x][y] = EL_ELEMENT_SNAPPING;
13876   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13877
13878   ResetGfxAnimation(x, y);
13879
13880   GfxElement[x][y] = element;
13881   GfxAction[x][y] = action;
13882   GfxDir[x][y] = direction;
13883   GfxFrame[x][y] = -1;
13884 }
13885 #endif
13886
13887 /*
13888   =============================================================================
13889   checkDiagonalPushing()
13890   -----------------------------------------------------------------------------
13891   check if diagonal input device direction results in pushing of object
13892   (by checking if the alternative direction is walkable, diggable, ...)
13893   =============================================================================
13894 */
13895
13896 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13897                                     int x, int y, int real_dx, int real_dy)
13898 {
13899   int jx, jy, dx, dy, xx, yy;
13900
13901   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13902     return TRUE;
13903
13904   /* diagonal direction: check alternative direction */
13905   jx = player->jx;
13906   jy = player->jy;
13907   dx = x - jx;
13908   dy = y - jy;
13909   xx = jx + (dx == 0 ? real_dx : 0);
13910   yy = jy + (dy == 0 ? real_dy : 0);
13911
13912   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13913 }
13914
13915 /*
13916   =============================================================================
13917   DigField()
13918   -----------------------------------------------------------------------------
13919   x, y:                 field next to player (non-diagonal) to try to dig to
13920   real_dx, real_dy:     direction as read from input device (can be diagonal)
13921   =============================================================================
13922 */
13923
13924 int DigField(struct PlayerInfo *player,
13925              int oldx, int oldy, int x, int y,
13926              int real_dx, int real_dy, int mode)
13927 {
13928   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13929   boolean player_was_pushing = player->is_pushing;
13930   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13931   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13932   int jx = oldx, jy = oldy;
13933   int dx = x - jx, dy = y - jy;
13934   int nextx = x + dx, nexty = y + dy;
13935   int move_direction = (dx == -1 ? MV_LEFT  :
13936                         dx == +1 ? MV_RIGHT :
13937                         dy == -1 ? MV_UP    :
13938                         dy == +1 ? MV_DOWN  : MV_NONE);
13939   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13940   int dig_side = MV_DIR_OPPOSITE(move_direction);
13941   int old_element = Feld[jx][jy];
13942 #if USE_FIXED_DONT_RUN_INTO
13943   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13944 #else
13945   int element;
13946 #endif
13947   int collect_count;
13948
13949   if (is_player)                /* function can also be called by EL_PENGUIN */
13950   {
13951     if (player->MovPos == 0)
13952     {
13953       player->is_digging = FALSE;
13954       player->is_collecting = FALSE;
13955     }
13956
13957     if (player->MovPos == 0)    /* last pushing move finished */
13958       player->is_pushing = FALSE;
13959
13960     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13961     {
13962       player->is_switching = FALSE;
13963       player->push_delay = -1;
13964
13965       return MP_NO_ACTION;
13966     }
13967   }
13968
13969 #if !USE_FIXED_DONT_RUN_INTO
13970   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13971     return MP_NO_ACTION;
13972 #endif
13973
13974   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13975     old_element = Back[jx][jy];
13976
13977   /* in case of element dropped at player position, check background */
13978   else if (Back[jx][jy] != EL_EMPTY &&
13979            game.engine_version >= VERSION_IDENT(2,2,0,0))
13980     old_element = Back[jx][jy];
13981
13982   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13983     return MP_NO_ACTION;        /* field has no opening in this direction */
13984
13985   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13986     return MP_NO_ACTION;        /* field has no opening in this direction */
13987
13988 #if USE_FIXED_DONT_RUN_INTO
13989   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13990   {
13991     SplashAcid(x, y);
13992
13993     Feld[jx][jy] = player->artwork_element;
13994     InitMovingField(jx, jy, MV_DOWN);
13995     Store[jx][jy] = EL_ACID;
13996     ContinueMoving(jx, jy);
13997     BuryPlayer(player);
13998
13999     return MP_DONT_RUN_INTO;
14000   }
14001 #endif
14002
14003 #if USE_FIXED_DONT_RUN_INTO
14004   if (player_can_move && DONT_RUN_INTO(element))
14005   {
14006     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14007
14008     return MP_DONT_RUN_INTO;
14009   }
14010 #endif
14011
14012 #if USE_FIXED_DONT_RUN_INTO
14013   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14014     return MP_NO_ACTION;
14015 #endif
14016
14017 #if !USE_FIXED_DONT_RUN_INTO
14018   element = Feld[x][y];
14019 #endif
14020
14021   collect_count = element_info[element].collect_count_initial;
14022
14023   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14024     return MP_NO_ACTION;
14025
14026   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14027     player_can_move = player_can_move_or_snap;
14028
14029   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14030       game.engine_version >= VERSION_IDENT(2,2,0,0))
14031   {
14032     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14033                                player->index_bit, dig_side);
14034     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14035                                         player->index_bit, dig_side);
14036
14037     if (element == EL_DC_LANDMINE)
14038       Bang(x, y);
14039
14040     if (Feld[x][y] != element)          /* field changed by snapping */
14041       return MP_ACTION;
14042
14043     return MP_NO_ACTION;
14044   }
14045
14046 #if USE_PLAYER_GRAVITY
14047   if (player->gravity && is_player && !player->is_auto_moving &&
14048       canFallDown(player) && move_direction != MV_DOWN &&
14049       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14050     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14051 #else
14052   if (game.gravity && is_player && !player->is_auto_moving &&
14053       canFallDown(player) && move_direction != MV_DOWN &&
14054       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14055     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14056 #endif
14057
14058   if (player_can_move &&
14059       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14060   {
14061     int sound_element = SND_ELEMENT(element);
14062     int sound_action = ACTION_WALKING;
14063
14064     if (IS_RND_GATE(element))
14065     {
14066       if (!player->key[RND_GATE_NR(element)])
14067         return MP_NO_ACTION;
14068     }
14069     else if (IS_RND_GATE_GRAY(element))
14070     {
14071       if (!player->key[RND_GATE_GRAY_NR(element)])
14072         return MP_NO_ACTION;
14073     }
14074     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14075     {
14076       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14077         return MP_NO_ACTION;
14078     }
14079     else if (element == EL_EXIT_OPEN ||
14080              element == EL_EM_EXIT_OPEN ||
14081              element == EL_STEEL_EXIT_OPEN ||
14082              element == EL_EM_STEEL_EXIT_OPEN ||
14083              element == EL_SP_EXIT_OPEN ||
14084              element == EL_SP_EXIT_OPENING)
14085     {
14086       sound_action = ACTION_PASSING;    /* player is passing exit */
14087     }
14088     else if (element == EL_EMPTY)
14089     {
14090       sound_action = ACTION_MOVING;             /* nothing to walk on */
14091     }
14092
14093     /* play sound from background or player, whatever is available */
14094     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14095       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14096     else
14097       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14098   }
14099   else if (player_can_move &&
14100            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14101   {
14102     if (!ACCESS_FROM(element, opposite_direction))
14103       return MP_NO_ACTION;      /* field not accessible from this direction */
14104
14105     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
14106       return MP_NO_ACTION;
14107
14108     if (IS_EM_GATE(element))
14109     {
14110       if (!player->key[EM_GATE_NR(element)])
14111         return MP_NO_ACTION;
14112     }
14113     else if (IS_EM_GATE_GRAY(element))
14114     {
14115       if (!player->key[EM_GATE_GRAY_NR(element)])
14116         return MP_NO_ACTION;
14117     }
14118     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14119     {
14120       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14121         return MP_NO_ACTION;
14122     }
14123     else if (IS_EMC_GATE(element))
14124     {
14125       if (!player->key[EMC_GATE_NR(element)])
14126         return MP_NO_ACTION;
14127     }
14128     else if (IS_EMC_GATE_GRAY(element))
14129     {
14130       if (!player->key[EMC_GATE_GRAY_NR(element)])
14131         return MP_NO_ACTION;
14132     }
14133     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14134     {
14135       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14136         return MP_NO_ACTION;
14137     }
14138     else if (element == EL_DC_GATE_WHITE ||
14139              element == EL_DC_GATE_WHITE_GRAY ||
14140              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14141     {
14142       if (player->num_white_keys == 0)
14143         return MP_NO_ACTION;
14144
14145       player->num_white_keys--;
14146     }
14147     else if (IS_SP_PORT(element))
14148     {
14149       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14150           element == EL_SP_GRAVITY_PORT_RIGHT ||
14151           element == EL_SP_GRAVITY_PORT_UP ||
14152           element == EL_SP_GRAVITY_PORT_DOWN)
14153 #if USE_PLAYER_GRAVITY
14154         player->gravity = !player->gravity;
14155 #else
14156         game.gravity = !game.gravity;
14157 #endif
14158       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14159                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14160                element == EL_SP_GRAVITY_ON_PORT_UP ||
14161                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14162 #if USE_PLAYER_GRAVITY
14163         player->gravity = TRUE;
14164 #else
14165         game.gravity = TRUE;
14166 #endif
14167       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14168                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14169                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14170                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14171 #if USE_PLAYER_GRAVITY
14172         player->gravity = FALSE;
14173 #else
14174         game.gravity = FALSE;
14175 #endif
14176     }
14177
14178     /* automatically move to the next field with double speed */
14179     player->programmed_action = move_direction;
14180
14181     if (player->move_delay_reset_counter == 0)
14182     {
14183       player->move_delay_reset_counter = 2;     /* two double speed steps */
14184
14185       DOUBLE_PLAYER_SPEED(player);
14186     }
14187
14188     PlayLevelSoundAction(x, y, ACTION_PASSING);
14189   }
14190   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14191   {
14192     RemoveField(x, y);
14193
14194     if (mode != DF_SNAP)
14195     {
14196       GfxElement[x][y] = GFX_ELEMENT(element);
14197       player->is_digging = TRUE;
14198     }
14199
14200     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14201
14202     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14203                                         player->index_bit, dig_side);
14204
14205     if (mode == DF_SNAP)
14206     {
14207 #if USE_NEW_SNAP_DELAY
14208       if (level.block_snap_field)
14209         setFieldForSnapping(x, y, element, move_direction);
14210       else
14211         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14212 #else
14213       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14214 #endif
14215
14216       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14217                                           player->index_bit, dig_side);
14218     }
14219   }
14220   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14221   {
14222     RemoveField(x, y);
14223
14224     if (is_player && mode != DF_SNAP)
14225     {
14226       GfxElement[x][y] = element;
14227       player->is_collecting = TRUE;
14228     }
14229
14230     if (element == EL_SPEED_PILL)
14231     {
14232       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14233     }
14234     else if (element == EL_EXTRA_TIME && level.time > 0)
14235     {
14236       TimeLeft += level.extra_time;
14237
14238 #if 1
14239       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14240
14241       DisplayGameControlValues();
14242 #else
14243       DrawGameValue_Time(TimeLeft);
14244 #endif
14245     }
14246     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14247     {
14248       player->shield_normal_time_left += level.shield_normal_time;
14249       if (element == EL_SHIELD_DEADLY)
14250         player->shield_deadly_time_left += level.shield_deadly_time;
14251     }
14252     else if (element == EL_DYNAMITE ||
14253              element == EL_EM_DYNAMITE ||
14254              element == EL_SP_DISK_RED)
14255     {
14256       if (player->inventory_size < MAX_INVENTORY_SIZE)
14257         player->inventory_element[player->inventory_size++] = element;
14258
14259       DrawGameDoorValues();
14260     }
14261     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14262     {
14263       player->dynabomb_count++;
14264       player->dynabombs_left++;
14265     }
14266     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14267     {
14268       player->dynabomb_size++;
14269     }
14270     else if (element == EL_DYNABOMB_INCREASE_POWER)
14271     {
14272       player->dynabomb_xl = TRUE;
14273     }
14274     else if (IS_KEY(element))
14275     {
14276       player->key[KEY_NR(element)] = TRUE;
14277
14278       DrawGameDoorValues();
14279     }
14280     else if (element == EL_DC_KEY_WHITE)
14281     {
14282       player->num_white_keys++;
14283
14284       /* display white keys? */
14285       /* DrawGameDoorValues(); */
14286     }
14287     else if (IS_ENVELOPE(element))
14288     {
14289       player->show_envelope = element;
14290     }
14291     else if (element == EL_EMC_LENSES)
14292     {
14293       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14294
14295       RedrawAllInvisibleElementsForLenses();
14296     }
14297     else if (element == EL_EMC_MAGNIFIER)
14298     {
14299       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14300
14301       RedrawAllInvisibleElementsForMagnifier();
14302     }
14303     else if (IS_DROPPABLE(element) ||
14304              IS_THROWABLE(element))     /* can be collected and dropped */
14305     {
14306       int i;
14307
14308       if (collect_count == 0)
14309         player->inventory_infinite_element = element;
14310       else
14311         for (i = 0; i < collect_count; i++)
14312           if (player->inventory_size < MAX_INVENTORY_SIZE)
14313             player->inventory_element[player->inventory_size++] = element;
14314
14315       DrawGameDoorValues();
14316     }
14317     else if (collect_count > 0)
14318     {
14319       local_player->gems_still_needed -= collect_count;
14320       if (local_player->gems_still_needed < 0)
14321         local_player->gems_still_needed = 0;
14322
14323 #if 1
14324       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14325
14326       DisplayGameControlValues();
14327 #else
14328       DrawGameValue_Emeralds(local_player->gems_still_needed);
14329 #endif
14330     }
14331
14332     RaiseScoreElement(element);
14333     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14334
14335     if (is_player)
14336       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14337                                           player->index_bit, dig_side);
14338
14339     if (mode == DF_SNAP)
14340     {
14341 #if USE_NEW_SNAP_DELAY
14342       if (level.block_snap_field)
14343         setFieldForSnapping(x, y, element, move_direction);
14344       else
14345         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14346 #else
14347       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14348 #endif
14349
14350       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14351                                           player->index_bit, dig_side);
14352     }
14353   }
14354   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14355   {
14356     if (mode == DF_SNAP && element != EL_BD_ROCK)
14357       return MP_NO_ACTION;
14358
14359     if (CAN_FALL(element) && dy)
14360       return MP_NO_ACTION;
14361
14362     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14363         !(element == EL_SPRING && level.use_spring_bug))
14364       return MP_NO_ACTION;
14365
14366     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14367         ((move_direction & MV_VERTICAL &&
14368           ((element_info[element].move_pattern & MV_LEFT &&
14369             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14370            (element_info[element].move_pattern & MV_RIGHT &&
14371             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14372          (move_direction & MV_HORIZONTAL &&
14373           ((element_info[element].move_pattern & MV_UP &&
14374             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14375            (element_info[element].move_pattern & MV_DOWN &&
14376             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14377       return MP_NO_ACTION;
14378
14379     /* do not push elements already moving away faster than player */
14380     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14381         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14382       return MP_NO_ACTION;
14383
14384     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14385     {
14386       if (player->push_delay_value == -1 || !player_was_pushing)
14387         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14388     }
14389     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14390     {
14391       if (player->push_delay_value == -1)
14392         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14393     }
14394     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14395     {
14396       if (!player->is_pushing)
14397         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14398     }
14399
14400     player->is_pushing = TRUE;
14401     player->is_active = TRUE;
14402
14403     if (!(IN_LEV_FIELD(nextx, nexty) &&
14404           (IS_FREE(nextx, nexty) ||
14405            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
14406             IS_SB_ELEMENT(element)))))
14407       return MP_NO_ACTION;
14408
14409     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14410       return MP_NO_ACTION;
14411
14412     if (player->push_delay == -1)       /* new pushing; restart delay */
14413       player->push_delay = 0;
14414
14415     if (player->push_delay < player->push_delay_value &&
14416         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14417         element != EL_SPRING && element != EL_BALLOON)
14418     {
14419       /* make sure that there is no move delay before next try to push */
14420       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14421         player->move_delay = 0;
14422
14423       return MP_NO_ACTION;
14424     }
14425
14426     if (IS_SB_ELEMENT(element))
14427     {
14428       if (element == EL_SOKOBAN_FIELD_FULL)
14429       {
14430         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14431         local_player->sokobanfields_still_needed++;
14432       }
14433
14434       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14435       {
14436         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14437         local_player->sokobanfields_still_needed--;
14438       }
14439
14440       Feld[x][y] = EL_SOKOBAN_OBJECT;
14441
14442       if (Back[x][y] == Back[nextx][nexty])
14443         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14444       else if (Back[x][y] != 0)
14445         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14446                                     ACTION_EMPTYING);
14447       else
14448         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14449                                     ACTION_FILLING);
14450
14451       if (local_player->sokobanfields_still_needed == 0 &&
14452           game.emulation == EMU_SOKOBAN)
14453       {
14454         PlayerWins(player);
14455
14456         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14457       }
14458     }
14459     else
14460       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14461
14462     InitMovingField(x, y, move_direction);
14463     GfxAction[x][y] = ACTION_PUSHING;
14464
14465     if (mode == DF_SNAP)
14466       ContinueMoving(x, y);
14467     else
14468       MovPos[x][y] = (dx != 0 ? dx : dy);
14469
14470     Pushed[x][y] = TRUE;
14471     Pushed[nextx][nexty] = TRUE;
14472
14473     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14474       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14475     else
14476       player->push_delay_value = -1;    /* get new value later */
14477
14478     /* check for element change _after_ element has been pushed */
14479     if (game.use_change_when_pushing_bug)
14480     {
14481       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14482                                  player->index_bit, dig_side);
14483       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14484                                           player->index_bit, dig_side);
14485     }
14486   }
14487   else if (IS_SWITCHABLE(element))
14488   {
14489     if (PLAYER_SWITCHING(player, x, y))
14490     {
14491       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14492                                           player->index_bit, dig_side);
14493
14494       return MP_ACTION;
14495     }
14496
14497     player->is_switching = TRUE;
14498     player->switch_x = x;
14499     player->switch_y = y;
14500
14501     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14502
14503     if (element == EL_ROBOT_WHEEL)
14504     {
14505       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14506       ZX = x;
14507       ZY = y;
14508
14509       game.robot_wheel_active = TRUE;
14510
14511       DrawLevelField(x, y);
14512     }
14513     else if (element == EL_SP_TERMINAL)
14514     {
14515       int xx, yy;
14516
14517       SCAN_PLAYFIELD(xx, yy)
14518       {
14519         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14520           Bang(xx, yy);
14521         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14522           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14523       }
14524     }
14525     else if (IS_BELT_SWITCH(element))
14526     {
14527       ToggleBeltSwitch(x, y);
14528     }
14529     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14530              element == EL_SWITCHGATE_SWITCH_DOWN ||
14531              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14532              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14533     {
14534       ToggleSwitchgateSwitch(x, y);
14535     }
14536     else if (element == EL_LIGHT_SWITCH ||
14537              element == EL_LIGHT_SWITCH_ACTIVE)
14538     {
14539       ToggleLightSwitch(x, y);
14540     }
14541     else if (element == EL_TIMEGATE_SWITCH ||
14542              element == EL_DC_TIMEGATE_SWITCH)
14543     {
14544       ActivateTimegateSwitch(x, y);
14545     }
14546     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14547              element == EL_BALLOON_SWITCH_RIGHT ||
14548              element == EL_BALLOON_SWITCH_UP    ||
14549              element == EL_BALLOON_SWITCH_DOWN  ||
14550              element == EL_BALLOON_SWITCH_NONE  ||
14551              element == EL_BALLOON_SWITCH_ANY)
14552     {
14553       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14554                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14555                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14556                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14557                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14558                              move_direction);
14559     }
14560     else if (element == EL_LAMP)
14561     {
14562       Feld[x][y] = EL_LAMP_ACTIVE;
14563       local_player->lights_still_needed--;
14564
14565       ResetGfxAnimation(x, y);
14566       DrawLevelField(x, y);
14567     }
14568     else if (element == EL_TIME_ORB_FULL)
14569     {
14570       Feld[x][y] = EL_TIME_ORB_EMPTY;
14571
14572       if (level.time > 0 || level.use_time_orb_bug)
14573       {
14574         TimeLeft += level.time_orb_time;
14575
14576 #if 1
14577         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14578
14579         DisplayGameControlValues();
14580 #else
14581         DrawGameValue_Time(TimeLeft);
14582 #endif
14583       }
14584
14585       ResetGfxAnimation(x, y);
14586       DrawLevelField(x, y);
14587     }
14588     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14589              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14590     {
14591       int xx, yy;
14592
14593       game.ball_state = !game.ball_state;
14594
14595       SCAN_PLAYFIELD(xx, yy)
14596       {
14597         int e = Feld[xx][yy];
14598
14599         if (game.ball_state)
14600         {
14601           if (e == EL_EMC_MAGIC_BALL)
14602             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14603           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14604             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14605         }
14606         else
14607         {
14608           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14609             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14610           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14611             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14612         }
14613       }
14614     }
14615
14616     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14617                                         player->index_bit, dig_side);
14618
14619     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14620                                         player->index_bit, dig_side);
14621
14622     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14623                                         player->index_bit, dig_side);
14624
14625     return MP_ACTION;
14626   }
14627   else
14628   {
14629     if (!PLAYER_SWITCHING(player, x, y))
14630     {
14631       player->is_switching = TRUE;
14632       player->switch_x = x;
14633       player->switch_y = y;
14634
14635       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14636                                  player->index_bit, dig_side);
14637       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14638                                           player->index_bit, dig_side);
14639
14640       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14641                                  player->index_bit, dig_side);
14642       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14643                                           player->index_bit, dig_side);
14644     }
14645
14646     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14647                                player->index_bit, dig_side);
14648     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14649                                         player->index_bit, dig_side);
14650
14651     return MP_NO_ACTION;
14652   }
14653
14654   player->push_delay = -1;
14655
14656   if (is_player)                /* function can also be called by EL_PENGUIN */
14657   {
14658     if (Feld[x][y] != element)          /* really digged/collected something */
14659     {
14660       player->is_collecting = !player->is_digging;
14661       player->is_active = TRUE;
14662     }
14663   }
14664
14665   return MP_MOVING;
14666 }
14667
14668 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14669 {
14670   int jx = player->jx, jy = player->jy;
14671   int x = jx + dx, y = jy + dy;
14672   int snap_direction = (dx == -1 ? MV_LEFT  :
14673                         dx == +1 ? MV_RIGHT :
14674                         dy == -1 ? MV_UP    :
14675                         dy == +1 ? MV_DOWN  : MV_NONE);
14676   boolean can_continue_snapping = (level.continuous_snapping &&
14677                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14678
14679   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14680     return FALSE;
14681
14682   if (!player->active || !IN_LEV_FIELD(x, y))
14683     return FALSE;
14684
14685   if (dx && dy)
14686     return FALSE;
14687
14688   if (!dx && !dy)
14689   {
14690     if (player->MovPos == 0)
14691       player->is_pushing = FALSE;
14692
14693     player->is_snapping = FALSE;
14694
14695     if (player->MovPos == 0)
14696     {
14697       player->is_moving = FALSE;
14698       player->is_digging = FALSE;
14699       player->is_collecting = FALSE;
14700     }
14701
14702     return FALSE;
14703   }
14704
14705 #if USE_NEW_CONTINUOUS_SNAPPING
14706   /* prevent snapping with already pressed snap key when not allowed */
14707   if (player->is_snapping && !can_continue_snapping)
14708     return FALSE;
14709 #else
14710   if (player->is_snapping)
14711     return FALSE;
14712 #endif
14713
14714   player->MovDir = snap_direction;
14715
14716   if (player->MovPos == 0)
14717   {
14718     player->is_moving = FALSE;
14719     player->is_digging = FALSE;
14720     player->is_collecting = FALSE;
14721   }
14722
14723   player->is_dropping = FALSE;
14724   player->is_dropping_pressed = FALSE;
14725   player->drop_pressed_delay = 0;
14726
14727   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14728     return FALSE;
14729
14730   player->is_snapping = TRUE;
14731   player->is_active = TRUE;
14732
14733   if (player->MovPos == 0)
14734   {
14735     player->is_moving = FALSE;
14736     player->is_digging = FALSE;
14737     player->is_collecting = FALSE;
14738   }
14739
14740   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14741     DrawLevelField(player->last_jx, player->last_jy);
14742
14743   DrawLevelField(x, y);
14744
14745   return TRUE;
14746 }
14747
14748 boolean DropElement(struct PlayerInfo *player)
14749 {
14750   int old_element, new_element;
14751   int dropx = player->jx, dropy = player->jy;
14752   int drop_direction = player->MovDir;
14753   int drop_side = drop_direction;
14754 #if 1
14755   int drop_element = get_next_dropped_element(player);
14756 #else
14757   int drop_element = (player->inventory_size > 0 ?
14758                       player->inventory_element[player->inventory_size - 1] :
14759                       player->inventory_infinite_element != EL_UNDEFINED ?
14760                       player->inventory_infinite_element :
14761                       player->dynabombs_left > 0 ?
14762                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14763                       EL_UNDEFINED);
14764 #endif
14765
14766   player->is_dropping_pressed = TRUE;
14767
14768   /* do not drop an element on top of another element; when holding drop key
14769      pressed without moving, dropped element must move away before the next
14770      element can be dropped (this is especially important if the next element
14771      is dynamite, which can be placed on background for historical reasons) */
14772   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14773     return MP_ACTION;
14774
14775   if (IS_THROWABLE(drop_element))
14776   {
14777     dropx += GET_DX_FROM_DIR(drop_direction);
14778     dropy += GET_DY_FROM_DIR(drop_direction);
14779
14780     if (!IN_LEV_FIELD(dropx, dropy))
14781       return FALSE;
14782   }
14783
14784   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14785   new_element = drop_element;           /* default: no change when dropping */
14786
14787   /* check if player is active, not moving and ready to drop */
14788   if (!player->active || player->MovPos || player->drop_delay > 0)
14789     return FALSE;
14790
14791   /* check if player has anything that can be dropped */
14792   if (new_element == EL_UNDEFINED)
14793     return FALSE;
14794
14795   /* check if drop key was pressed long enough for EM style dynamite */
14796   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14797     return FALSE;
14798
14799   /* check if anything can be dropped at the current position */
14800   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14801     return FALSE;
14802
14803   /* collected custom elements can only be dropped on empty fields */
14804   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14805     return FALSE;
14806
14807   if (old_element != EL_EMPTY)
14808     Back[dropx][dropy] = old_element;   /* store old element on this field */
14809
14810   ResetGfxAnimation(dropx, dropy);
14811   ResetRandomAnimationValue(dropx, dropy);
14812
14813   if (player->inventory_size > 0 ||
14814       player->inventory_infinite_element != EL_UNDEFINED)
14815   {
14816     if (player->inventory_size > 0)
14817     {
14818       player->inventory_size--;
14819
14820       DrawGameDoorValues();
14821
14822       if (new_element == EL_DYNAMITE)
14823         new_element = EL_DYNAMITE_ACTIVE;
14824       else if (new_element == EL_EM_DYNAMITE)
14825         new_element = EL_EM_DYNAMITE_ACTIVE;
14826       else if (new_element == EL_SP_DISK_RED)
14827         new_element = EL_SP_DISK_RED_ACTIVE;
14828     }
14829
14830     Feld[dropx][dropy] = new_element;
14831
14832     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14833       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14834                           el2img(Feld[dropx][dropy]), 0);
14835
14836     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14837
14838     /* needed if previous element just changed to "empty" in the last frame */
14839     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14840
14841     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14842                                player->index_bit, drop_side);
14843     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14844                                         CE_PLAYER_DROPS_X,
14845                                         player->index_bit, drop_side);
14846
14847     TestIfElementTouchesCustomElement(dropx, dropy);
14848   }
14849   else          /* player is dropping a dyna bomb */
14850   {
14851     player->dynabombs_left--;
14852
14853     Feld[dropx][dropy] = new_element;
14854
14855     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14856       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14857                           el2img(Feld[dropx][dropy]), 0);
14858
14859     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14860   }
14861
14862   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14863     InitField_WithBug1(dropx, dropy, FALSE);
14864
14865   new_element = Feld[dropx][dropy];     /* element might have changed */
14866
14867   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14868       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14869   {
14870     int move_direction, nextx, nexty;
14871
14872     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14873       MovDir[dropx][dropy] = drop_direction;
14874
14875     move_direction = MovDir[dropx][dropy];
14876     nextx = dropx + GET_DX_FROM_DIR(move_direction);
14877     nexty = dropy + GET_DY_FROM_DIR(move_direction);
14878
14879     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14880
14881 #if USE_FIX_IMPACT_COLLISION
14882     /* do not cause impact style collision by dropping elements that can fall */
14883     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14884 #else
14885     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14886 #endif
14887   }
14888
14889   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14890   player->is_dropping = TRUE;
14891
14892   player->drop_pressed_delay = 0;
14893   player->is_dropping_pressed = FALSE;
14894
14895   player->drop_x = dropx;
14896   player->drop_y = dropy;
14897
14898   return TRUE;
14899 }
14900
14901 /* ------------------------------------------------------------------------- */
14902 /* game sound playing functions                                              */
14903 /* ------------------------------------------------------------------------- */
14904
14905 static int *loop_sound_frame = NULL;
14906 static int *loop_sound_volume = NULL;
14907
14908 void InitPlayLevelSound()
14909 {
14910   int num_sounds = getSoundListSize();
14911
14912   checked_free(loop_sound_frame);
14913   checked_free(loop_sound_volume);
14914
14915   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14916   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14917 }
14918
14919 static void PlayLevelSound(int x, int y, int nr)
14920 {
14921   int sx = SCREENX(x), sy = SCREENY(y);
14922   int volume, stereo_position;
14923   int max_distance = 8;
14924   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14925
14926   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14927       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14928     return;
14929
14930   if (!IN_LEV_FIELD(x, y) ||
14931       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14932       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14933     return;
14934
14935   volume = SOUND_MAX_VOLUME;
14936
14937   if (!IN_SCR_FIELD(sx, sy))
14938   {
14939     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14940     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14941
14942     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14943   }
14944
14945   stereo_position = (SOUND_MAX_LEFT +
14946                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14947                      (SCR_FIELDX + 2 * max_distance));
14948
14949   if (IS_LOOP_SOUND(nr))
14950   {
14951     /* This assures that quieter loop sounds do not overwrite louder ones,
14952        while restarting sound volume comparison with each new game frame. */
14953
14954     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14955       return;
14956
14957     loop_sound_volume[nr] = volume;
14958     loop_sound_frame[nr] = FrameCounter;
14959   }
14960
14961   PlaySoundExt(nr, volume, stereo_position, type);
14962 }
14963
14964 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14965 {
14966   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14967                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14968                  y < LEVELY(BY1) ? LEVELY(BY1) :
14969                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14970                  sound_action);
14971 }
14972
14973 static void PlayLevelSoundAction(int x, int y, int action)
14974 {
14975   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14976 }
14977
14978 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14979 {
14980   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14981
14982   if (sound_effect != SND_UNDEFINED)
14983     PlayLevelSound(x, y, sound_effect);
14984 }
14985
14986 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14987                                               int action)
14988 {
14989   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14990
14991   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14992     PlayLevelSound(x, y, sound_effect);
14993 }
14994
14995 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14996 {
14997   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14998
14999   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15000     PlayLevelSound(x, y, sound_effect);
15001 }
15002
15003 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15004 {
15005   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15006
15007   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15008     StopSound(sound_effect);
15009 }
15010
15011 static void PlayLevelMusic()
15012 {
15013   if (levelset.music[level_nr] != MUS_UNDEFINED)
15014     PlayMusic(levelset.music[level_nr]);        /* from config file */
15015   else
15016     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
15017 }
15018
15019 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15020 {
15021   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15022   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15023   int x = xx - 1 - offset;
15024   int y = yy - 1 - offset;
15025
15026   switch (sample)
15027   {
15028     case SAMPLE_blank:
15029       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15030       break;
15031
15032     case SAMPLE_roll:
15033       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15034       break;
15035
15036     case SAMPLE_stone:
15037       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15038       break;
15039
15040     case SAMPLE_nut:
15041       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15042       break;
15043
15044     case SAMPLE_crack:
15045       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15046       break;
15047
15048     case SAMPLE_bug:
15049       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15050       break;
15051
15052     case SAMPLE_tank:
15053       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15054       break;
15055
15056     case SAMPLE_android_clone:
15057       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15058       break;
15059
15060     case SAMPLE_android_move:
15061       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15062       break;
15063
15064     case SAMPLE_spring:
15065       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15066       break;
15067
15068     case SAMPLE_slurp:
15069       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15070       break;
15071
15072     case SAMPLE_eater:
15073       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15074       break;
15075
15076     case SAMPLE_eater_eat:
15077       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15078       break;
15079
15080     case SAMPLE_alien:
15081       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15082       break;
15083
15084     case SAMPLE_collect:
15085       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15086       break;
15087
15088     case SAMPLE_diamond:
15089       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15090       break;
15091
15092     case SAMPLE_squash:
15093       /* !!! CHECK THIS !!! */
15094 #if 1
15095       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15096 #else
15097       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15098 #endif
15099       break;
15100
15101     case SAMPLE_wonderfall:
15102       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15103       break;
15104
15105     case SAMPLE_drip:
15106       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15107       break;
15108
15109     case SAMPLE_push:
15110       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15111       break;
15112
15113     case SAMPLE_dirt:
15114       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15115       break;
15116
15117     case SAMPLE_acid:
15118       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15119       break;
15120
15121     case SAMPLE_ball:
15122       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15123       break;
15124
15125     case SAMPLE_grow:
15126       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15127       break;
15128
15129     case SAMPLE_wonder:
15130       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15131       break;
15132
15133     case SAMPLE_door:
15134       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15135       break;
15136
15137     case SAMPLE_exit_open:
15138       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15139       break;
15140
15141     case SAMPLE_exit_leave:
15142       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15143       break;
15144
15145     case SAMPLE_dynamite:
15146       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15147       break;
15148
15149     case SAMPLE_tick:
15150       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15151       break;
15152
15153     case SAMPLE_press:
15154       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15155       break;
15156
15157     case SAMPLE_wheel:
15158       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15159       break;
15160
15161     case SAMPLE_boom:
15162       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15163       break;
15164
15165     case SAMPLE_die:
15166       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15167       break;
15168
15169     case SAMPLE_time:
15170       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15171       break;
15172
15173     default:
15174       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15175       break;
15176   }
15177 }
15178
15179 #if 0
15180 void ChangeTime(int value)
15181 {
15182   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15183
15184   *time += value;
15185
15186   /* EMC game engine uses value from time counter of RND game engine */
15187   level.native_em_level->lev->time = *time;
15188
15189   DrawGameValue_Time(*time);
15190 }
15191
15192 void RaiseScore(int value)
15193 {
15194   /* EMC game engine and RND game engine have separate score counters */
15195   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15196                 &level.native_em_level->lev->score : &local_player->score);
15197
15198   *score += value;
15199
15200   DrawGameValue_Score(*score);
15201 }
15202 #endif
15203
15204 void RaiseScore(int value)
15205 {
15206   local_player->score += value;
15207
15208 #if 1
15209   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15210
15211   DisplayGameControlValues();
15212 #else
15213   DrawGameValue_Score(local_player->score);
15214 #endif
15215 }
15216
15217 void RaiseScoreElement(int element)
15218 {
15219   switch (element)
15220   {
15221     case EL_EMERALD:
15222     case EL_BD_DIAMOND:
15223     case EL_EMERALD_YELLOW:
15224     case EL_EMERALD_RED:
15225     case EL_EMERALD_PURPLE:
15226     case EL_SP_INFOTRON:
15227       RaiseScore(level.score[SC_EMERALD]);
15228       break;
15229     case EL_DIAMOND:
15230       RaiseScore(level.score[SC_DIAMOND]);
15231       break;
15232     case EL_CRYSTAL:
15233       RaiseScore(level.score[SC_CRYSTAL]);
15234       break;
15235     case EL_PEARL:
15236       RaiseScore(level.score[SC_PEARL]);
15237       break;
15238     case EL_BUG:
15239     case EL_BD_BUTTERFLY:
15240     case EL_SP_ELECTRON:
15241       RaiseScore(level.score[SC_BUG]);
15242       break;
15243     case EL_SPACESHIP:
15244     case EL_BD_FIREFLY:
15245     case EL_SP_SNIKSNAK:
15246       RaiseScore(level.score[SC_SPACESHIP]);
15247       break;
15248     case EL_YAMYAM:
15249     case EL_DARK_YAMYAM:
15250       RaiseScore(level.score[SC_YAMYAM]);
15251       break;
15252     case EL_ROBOT:
15253       RaiseScore(level.score[SC_ROBOT]);
15254       break;
15255     case EL_PACMAN:
15256       RaiseScore(level.score[SC_PACMAN]);
15257       break;
15258     case EL_NUT:
15259       RaiseScore(level.score[SC_NUT]);
15260       break;
15261     case EL_DYNAMITE:
15262     case EL_EM_DYNAMITE:
15263     case EL_SP_DISK_RED:
15264     case EL_DYNABOMB_INCREASE_NUMBER:
15265     case EL_DYNABOMB_INCREASE_SIZE:
15266     case EL_DYNABOMB_INCREASE_POWER:
15267       RaiseScore(level.score[SC_DYNAMITE]);
15268       break;
15269     case EL_SHIELD_NORMAL:
15270     case EL_SHIELD_DEADLY:
15271       RaiseScore(level.score[SC_SHIELD]);
15272       break;
15273     case EL_EXTRA_TIME:
15274       RaiseScore(level.extra_time_score);
15275       break;
15276     case EL_KEY_1:
15277     case EL_KEY_2:
15278     case EL_KEY_3:
15279     case EL_KEY_4:
15280     case EL_EM_KEY_1:
15281     case EL_EM_KEY_2:
15282     case EL_EM_KEY_3:
15283     case EL_EM_KEY_4:
15284     case EL_EMC_KEY_5:
15285     case EL_EMC_KEY_6:
15286     case EL_EMC_KEY_7:
15287     case EL_EMC_KEY_8:
15288     case EL_DC_KEY_WHITE:
15289       RaiseScore(level.score[SC_KEY]);
15290       break;
15291     default:
15292       RaiseScore(element_info[element].collect_score);
15293       break;
15294   }
15295 }
15296
15297 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15298 {
15299   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15300   {
15301 #if defined(NETWORK_AVALIABLE)
15302     if (options.network)
15303       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15304     else
15305 #endif
15306     {
15307       if (quick_quit)
15308       {
15309 #if 1
15310
15311 #if 1
15312         FadeSkipNextFadeIn();
15313 #else
15314         fading = fading_none;
15315 #endif
15316
15317 #else
15318         OpenDoor(DOOR_CLOSE_1);
15319 #endif
15320
15321         game_status = GAME_MODE_MAIN;
15322
15323 #if 1
15324         DrawAndFadeInMainMenu(REDRAW_FIELD);
15325 #else
15326         DrawMainMenu();
15327 #endif
15328       }
15329       else
15330       {
15331 #if 0
15332         FadeOut(REDRAW_FIELD);
15333 #endif
15334
15335         game_status = GAME_MODE_MAIN;
15336
15337         DrawAndFadeInMainMenu(REDRAW_FIELD);
15338       }
15339     }
15340   }
15341   else          /* continue playing the game */
15342   {
15343     if (tape.playing && tape.deactivate_display)
15344       TapeDeactivateDisplayOff(TRUE);
15345
15346     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15347
15348     if (tape.playing && tape.deactivate_display)
15349       TapeDeactivateDisplayOn();
15350   }
15351 }
15352
15353 void RequestQuitGame(boolean ask_if_really_quit)
15354 {
15355   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15356   boolean skip_request = AllPlayersGone || quick_quit;
15357
15358   RequestQuitGameExt(skip_request, quick_quit,
15359                      "Do you really want to quit the game ?");
15360 }
15361
15362
15363 /* ------------------------------------------------------------------------- */
15364 /* random generator functions                                                */
15365 /* ------------------------------------------------------------------------- */
15366
15367 unsigned int InitEngineRandom_RND(long seed)
15368 {
15369   game.num_random_calls = 0;
15370
15371 #if 0
15372   unsigned int rnd_seed = InitEngineRandom(seed);
15373
15374   printf("::: START RND: %d\n", rnd_seed);
15375
15376   return rnd_seed;
15377 #else
15378
15379   return InitEngineRandom(seed);
15380
15381 #endif
15382
15383 }
15384
15385 unsigned int RND(int max)
15386 {
15387   if (max > 0)
15388   {
15389     game.num_random_calls++;
15390
15391     return GetEngineRandom(max);
15392   }
15393
15394   return 0;
15395 }
15396
15397
15398 /* ------------------------------------------------------------------------- */
15399 /* game engine snapshot handling functions                                   */
15400 /* ------------------------------------------------------------------------- */
15401
15402 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
15403
15404 struct EngineSnapshotInfo
15405 {
15406   /* runtime values for custom element collect score */
15407   int collect_score[NUM_CUSTOM_ELEMENTS];
15408
15409   /* runtime values for group element choice position */
15410   int choice_pos[NUM_GROUP_ELEMENTS];
15411
15412   /* runtime values for belt position animations */
15413   int belt_graphic[4 * NUM_BELT_PARTS];
15414   int belt_anim_mode[4 * NUM_BELT_PARTS];
15415 };
15416
15417 struct EngineSnapshotNodeInfo
15418 {
15419   void *buffer_orig;
15420   void *buffer_copy;
15421   int size;
15422 };
15423
15424 static struct EngineSnapshotInfo engine_snapshot_rnd;
15425 static ListNode *engine_snapshot_list = NULL;
15426 static char *snapshot_level_identifier = NULL;
15427 static int snapshot_level_nr = -1;
15428
15429 void FreeEngineSnapshot()
15430 {
15431   while (engine_snapshot_list != NULL)
15432     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15433                        checked_free);
15434
15435   setString(&snapshot_level_identifier, NULL);
15436   snapshot_level_nr = -1;
15437 }
15438
15439 static void SaveEngineSnapshotValues_RND()
15440 {
15441   static int belt_base_active_element[4] =
15442   {
15443     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15444     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15445     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15446     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15447   };
15448   int i, j;
15449
15450   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15451   {
15452     int element = EL_CUSTOM_START + i;
15453
15454     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15455   }
15456
15457   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15458   {
15459     int element = EL_GROUP_START + i;
15460
15461     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15462   }
15463
15464   for (i = 0; i < 4; i++)
15465   {
15466     for (j = 0; j < NUM_BELT_PARTS; j++)
15467     {
15468       int element = belt_base_active_element[i] + j;
15469       int graphic = el2img(element);
15470       int anim_mode = graphic_info[graphic].anim_mode;
15471
15472       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15473       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15474     }
15475   }
15476 }
15477
15478 static void LoadEngineSnapshotValues_RND()
15479 {
15480   unsigned long num_random_calls = game.num_random_calls;
15481   int i, j;
15482
15483   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15484   {
15485     int element = EL_CUSTOM_START + i;
15486
15487     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15488   }
15489
15490   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15491   {
15492     int element = EL_GROUP_START + i;
15493
15494     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15495   }
15496
15497   for (i = 0; i < 4; i++)
15498   {
15499     for (j = 0; j < NUM_BELT_PARTS; j++)
15500     {
15501       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15502       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15503
15504       graphic_info[graphic].anim_mode = anim_mode;
15505     }
15506   }
15507
15508   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15509   {
15510     InitRND(tape.random_seed);
15511     for (i = 0; i < num_random_calls; i++)
15512       RND(1);
15513   }
15514
15515   if (game.num_random_calls != num_random_calls)
15516   {
15517     Error(ERR_INFO, "number of random calls out of sync");
15518     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15519     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15520     Error(ERR_EXIT, "this should not happen -- please debug");
15521   }
15522 }
15523
15524 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15525 {
15526   struct EngineSnapshotNodeInfo *bi =
15527     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15528
15529   bi->buffer_orig = buffer;
15530   bi->buffer_copy = checked_malloc(size);
15531   bi->size = size;
15532
15533   memcpy(bi->buffer_copy, buffer, size);
15534
15535   addNodeToList(&engine_snapshot_list, NULL, bi);
15536 }
15537
15538 void SaveEngineSnapshot()
15539 {
15540   FreeEngineSnapshot();         /* free previous snapshot, if needed */
15541
15542   if (level_editor_test_game)   /* do not save snapshots from editor */
15543     return;
15544
15545   /* copy some special values to a structure better suited for the snapshot */
15546
15547   SaveEngineSnapshotValues_RND();
15548   SaveEngineSnapshotValues_EM();
15549
15550   /* save values stored in special snapshot structure */
15551
15552   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15553   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15554
15555   /* save further RND engine values */
15556
15557   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15558   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15559   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15560
15561   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15562   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15563   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15564   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15565
15566   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15567   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15568   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15569   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15570   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15571
15572   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15573   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15574   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15575
15576   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15577
15578   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15579
15580   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15581   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15582
15583   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15584   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15585   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15586   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15587   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15588   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15589   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15590   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15591   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15592   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15593   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15594   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15595   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15596   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15597   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15598   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15599   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15600   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15601
15602   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15603   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15604
15605   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15606   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15607   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15608
15609   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15610   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15611
15612   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15613   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15614   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15615   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15616   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15617
15618   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15619   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15620
15621   /* save level identification information */
15622
15623   setString(&snapshot_level_identifier, leveldir_current->identifier);
15624   snapshot_level_nr = level_nr;
15625
15626 #if 0
15627   ListNode *node = engine_snapshot_list;
15628   int num_bytes = 0;
15629
15630   while (node != NULL)
15631   {
15632     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15633
15634     node = node->next;
15635   }
15636
15637   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15638 #endif
15639 }
15640
15641 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15642 {
15643   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15644 }
15645
15646 void LoadEngineSnapshot()
15647 {
15648   ListNode *node = engine_snapshot_list;
15649
15650   if (engine_snapshot_list == NULL)
15651     return;
15652
15653   while (node != NULL)
15654   {
15655     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15656
15657     node = node->next;
15658   }
15659
15660   /* restore special values from snapshot structure */
15661
15662   LoadEngineSnapshotValues_RND();
15663   LoadEngineSnapshotValues_EM();
15664 }
15665
15666 boolean CheckEngineSnapshot()
15667 {
15668   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15669           snapshot_level_nr == level_nr);
15670 }
15671
15672
15673 /* ---------- new game button stuff ---------------------------------------- */
15674
15675 /* graphic position values for game buttons */
15676 #define GAME_BUTTON_XSIZE       30
15677 #define GAME_BUTTON_YSIZE       30
15678 #define GAME_BUTTON_XPOS        5
15679 #define GAME_BUTTON_YPOS        215
15680 #define SOUND_BUTTON_XPOS       5
15681 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15682
15683 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15684 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15685 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15686 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15687 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15688 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15689
15690 static struct
15691 {
15692   int *x, *y;
15693   int gd_x, gd_y;
15694   int gadget_id;
15695   char *infotext;
15696 } gamebutton_info[NUM_GAME_BUTTONS] =
15697 {
15698 #if 1
15699   {
15700     &game.button.stop.x,        &game.button.stop.y,
15701     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15702     GAME_CTRL_ID_STOP,
15703     "stop game"
15704   },
15705   {
15706     &game.button.pause.x,       &game.button.pause.y,
15707     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15708     GAME_CTRL_ID_PAUSE,
15709     "pause game"
15710   },
15711   {
15712     &game.button.play.x,        &game.button.play.y,
15713     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15714     GAME_CTRL_ID_PLAY,
15715     "play game"
15716   },
15717   {
15718     &game.button.sound_music.x, &game.button.sound_music.y,
15719     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15720     SOUND_CTRL_ID_MUSIC,
15721     "background music on/off"
15722   },
15723   {
15724     &game.button.sound_loops.x, &game.button.sound_loops.y,
15725     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15726     SOUND_CTRL_ID_LOOPS,
15727     "sound loops on/off"
15728   },
15729   {
15730     &game.button.sound_simple.x,&game.button.sound_simple.y,
15731     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15732     SOUND_CTRL_ID_SIMPLE,
15733     "normal sounds on/off"
15734   }
15735 #else
15736   {
15737     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15738     GAME_CTRL_ID_STOP,
15739     "stop game"
15740   },
15741   {
15742     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15743     GAME_CTRL_ID_PAUSE,
15744     "pause game"
15745   },
15746   {
15747     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15748     GAME_CTRL_ID_PLAY,
15749     "play game"
15750   },
15751   {
15752     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15753     SOUND_CTRL_ID_MUSIC,
15754     "background music on/off"
15755   },
15756   {
15757     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15758     SOUND_CTRL_ID_LOOPS,
15759     "sound loops on/off"
15760   },
15761   {
15762     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15763     SOUND_CTRL_ID_SIMPLE,
15764     "normal sounds on/off"
15765   }
15766 #endif
15767 };
15768
15769 void CreateGameButtons()
15770 {
15771   int i;
15772
15773   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15774   {
15775     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15776     struct GadgetInfo *gi;
15777     int button_type;
15778     boolean checked;
15779     unsigned long event_mask;
15780     int x, y;
15781     int gd_xoffset, gd_yoffset;
15782     int gd_x1, gd_x2, gd_y1, gd_y2;
15783     int id = i;
15784
15785     x = DX + *gamebutton_info[i].x;
15786     y = DY + *gamebutton_info[i].y;
15787     gd_xoffset = gamebutton_info[i].gd_x;
15788     gd_yoffset = gamebutton_info[i].gd_y;
15789     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15790     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15791
15792     if (id == GAME_CTRL_ID_STOP ||
15793         id == GAME_CTRL_ID_PAUSE ||
15794         id == GAME_CTRL_ID_PLAY)
15795     {
15796       button_type = GD_TYPE_NORMAL_BUTTON;
15797       checked = FALSE;
15798       event_mask = GD_EVENT_RELEASED;
15799       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15800       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15801     }
15802     else
15803     {
15804       button_type = GD_TYPE_CHECK_BUTTON;
15805       checked =
15806         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15807          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15808          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15809       event_mask = GD_EVENT_PRESSED;
15810       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
15811       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15812     }
15813
15814     gi = CreateGadget(GDI_CUSTOM_ID, id,
15815                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15816 #if 1
15817                       GDI_X, x,
15818                       GDI_Y, y,
15819 #else
15820                       GDI_X, DX + gd_xoffset,
15821                       GDI_Y, DY + gd_yoffset,
15822 #endif
15823                       GDI_WIDTH, GAME_BUTTON_XSIZE,
15824                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
15825                       GDI_TYPE, button_type,
15826                       GDI_STATE, GD_BUTTON_UNPRESSED,
15827                       GDI_CHECKED, checked,
15828                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15829                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15830                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15831                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15832                       GDI_DIRECT_DRAW, FALSE,
15833                       GDI_EVENT_MASK, event_mask,
15834                       GDI_CALLBACK_ACTION, HandleGameButtons,
15835                       GDI_END);
15836
15837     if (gi == NULL)
15838       Error(ERR_EXIT, "cannot create gadget");
15839
15840     game_gadget[id] = gi;
15841   }
15842 }
15843
15844 void FreeGameButtons()
15845 {
15846   int i;
15847
15848   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15849     FreeGadget(game_gadget[i]);
15850 }
15851
15852 static void MapGameButtons()
15853 {
15854   int i;
15855
15856   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15857     MapGadget(game_gadget[i]);
15858 }
15859
15860 void UnmapGameButtons()
15861 {
15862   int i;
15863
15864   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15865     UnmapGadget(game_gadget[i]);
15866 }
15867
15868 void RedrawGameButtons()
15869 {
15870   int i;
15871
15872   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15873     RedrawGadget(game_gadget[i]);
15874 }
15875
15876 static void HandleGameButtons(struct GadgetInfo *gi)
15877 {
15878   int id = gi->custom_id;
15879
15880   if (game_status != GAME_MODE_PLAYING)
15881     return;
15882
15883   switch (id)
15884   {
15885     case GAME_CTRL_ID_STOP:
15886       if (tape.playing)
15887         TapeStop();
15888       else
15889         RequestQuitGame(TRUE);
15890       break;
15891
15892     case GAME_CTRL_ID_PAUSE:
15893       if (options.network)
15894       {
15895 #if defined(NETWORK_AVALIABLE)
15896         if (tape.pausing)
15897           SendToServer_ContinuePlaying();
15898         else
15899           SendToServer_PausePlaying();
15900 #endif
15901       }
15902       else
15903         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15904       break;
15905
15906     case GAME_CTRL_ID_PLAY:
15907       if (tape.pausing)
15908       {
15909 #if defined(NETWORK_AVALIABLE)
15910         if (options.network)
15911           SendToServer_ContinuePlaying();
15912         else
15913 #endif
15914         {
15915           tape.pausing = FALSE;
15916           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15917         }
15918       }
15919       break;
15920
15921     case SOUND_CTRL_ID_MUSIC:
15922       if (setup.sound_music)
15923       { 
15924         setup.sound_music = FALSE;
15925         FadeMusic();
15926       }
15927       else if (audio.music_available)
15928       { 
15929         setup.sound = setup.sound_music = TRUE;
15930
15931         SetAudioMode(setup.sound);
15932
15933         PlayLevelMusic();
15934       }
15935       break;
15936
15937     case SOUND_CTRL_ID_LOOPS:
15938       if (setup.sound_loops)
15939         setup.sound_loops = FALSE;
15940       else if (audio.loops_available)
15941       {
15942         setup.sound = setup.sound_loops = TRUE;
15943         SetAudioMode(setup.sound);
15944       }
15945       break;
15946
15947     case SOUND_CTRL_ID_SIMPLE:
15948       if (setup.sound_simple)
15949         setup.sound_simple = FALSE;
15950       else if (audio.sound_available)
15951       {
15952         setup.sound = setup.sound_simple = TRUE;
15953         SetAudioMode(setup.sound);
15954       }
15955       break;
15956
15957     default:
15958       break;
15959   }
15960 }