55020d92101c7516dc769647a76890dfed752940
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61
62 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
63
64
65 /* for DigField() */
66 #define DF_NO_PUSH              0
67 #define DF_DIG                  1
68 #define DF_SNAP                 2
69
70 /* for MovePlayer() */
71 #define MP_NO_ACTION            0
72 #define MP_MOVING               1
73 #define MP_ACTION               2
74 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
75
76 /* for ScrollPlayer() */
77 #define SCROLL_INIT             0
78 #define SCROLL_GO_ON            1
79
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START          0
82 #define EX_TYPE_NONE            0
83 #define EX_TYPE_NORMAL          (1 << 0)
84 #define EX_TYPE_CENTER          (1 << 1)
85 #define EX_TYPE_BORDER          (1 << 2)
86 #define EX_TYPE_CROSS           (1 << 3)
87 #define EX_TYPE_DYNA            (1 << 4)
88 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
89
90 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
91 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
92 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
93 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
94
95 /* special positions in the game control window (relative to control window) */
96 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
97 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
98 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
99 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
100 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
101 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
102 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
103 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
104 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
105 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
106 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
107 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
108 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
109 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
110 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
111 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
112
113 /* special positions in the game control window (relative to main window) */
114 #define DX_LEVEL1               (DX + XX_LEVEL1)
115 #define DX_LEVEL2               (DX + XX_LEVEL2)
116 #define DX_LEVEL                (DX + XX_LEVEL)
117 #define DY_LEVEL                (DY + YY_LEVEL)
118 #define DX_EMERALDS             (DX + XX_EMERALDS)
119 #define DY_EMERALDS             (DY + YY_EMERALDS)
120 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
121 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
122 #define DX_KEYS                 (DX + XX_KEYS)
123 #define DY_KEYS                 (DY + YY_KEYS)
124 #define DX_SCORE                (DX + XX_SCORE)
125 #define DY_SCORE                (DY + YY_SCORE)
126 #define DX_TIME1                (DX + XX_TIME1)
127 #define DX_TIME2                (DX + XX_TIME2)
128 #define DX_TIME                 (DX + XX_TIME)
129 #define DY_TIME                 (DY + YY_TIME)
130
131 #if 1
132 /* game panel display and control definitions */
133
134 #define GAME_PANEL_LEVEL_NUMBER                 0
135 #define GAME_PANEL_GEMS                         1
136 #define GAME_PANEL_INVENTORY_COUNT              2
137 #define GAME_PANEL_INVENTORY_FIRST_1            3
138 #define GAME_PANEL_INVENTORY_FIRST_2            4
139 #define GAME_PANEL_INVENTORY_FIRST_3            5
140 #define GAME_PANEL_INVENTORY_FIRST_4            6
141 #define GAME_PANEL_INVENTORY_FIRST_5            7
142 #define GAME_PANEL_INVENTORY_FIRST_6            8
143 #define GAME_PANEL_INVENTORY_FIRST_7            9
144 #define GAME_PANEL_INVENTORY_FIRST_8            10
145 #define GAME_PANEL_INVENTORY_LAST_1             11
146 #define GAME_PANEL_INVENTORY_LAST_2             12
147 #define GAME_PANEL_INVENTORY_LAST_3             13
148 #define GAME_PANEL_INVENTORY_LAST_4             14
149 #define GAME_PANEL_INVENTORY_LAST_5             15
150 #define GAME_PANEL_INVENTORY_LAST_6             16
151 #define GAME_PANEL_INVENTORY_LAST_7             17
152 #define GAME_PANEL_INVENTORY_LAST_8             18
153 #define GAME_PANEL_KEY_1                        19
154 #define GAME_PANEL_KEY_2                        20
155 #define GAME_PANEL_KEY_3                        21
156 #define GAME_PANEL_KEY_4                        22
157 #define GAME_PANEL_KEY_5                        23
158 #define GAME_PANEL_KEY_6                        24
159 #define GAME_PANEL_KEY_7                        25
160 #define GAME_PANEL_KEY_8                        26
161 #define GAME_PANEL_KEY_WHITE                    27
162 #define GAME_PANEL_KEY_WHITE_COUNT              28
163 #define GAME_PANEL_SCORE                        29
164 #define GAME_PANEL_HIGHSCORE                    30
165 #define GAME_PANEL_TIME                         31
166 #define GAME_PANEL_TIME_HH                      32
167 #define GAME_PANEL_TIME_MM                      33
168 #define GAME_PANEL_TIME_SS                      34
169 #define GAME_PANEL_SHIELD_NORMAL                35
170 #define GAME_PANEL_SHIELD_NORMAL_TIME           36
171 #define GAME_PANEL_SHIELD_DEADLY                37
172 #define GAME_PANEL_SHIELD_DEADLY_TIME           38
173 #define GAME_PANEL_EXIT                         39
174 #define GAME_PANEL_EMC_MAGIC_BALL               40
175 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        41
176 #define GAME_PANEL_LIGHT_SWITCH                 42
177 #define GAME_PANEL_LIGHT_SWITCH_TIME            43
178 #define GAME_PANEL_TIMEGATE_SWITCH              44
179 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         45
180 #define GAME_PANEL_SWITCHGATE_SWITCH            46
181 #define GAME_PANEL_EMC_LENSES                   47
182 #define GAME_PANEL_EMC_LENSES_TIME              48
183 #define GAME_PANEL_EMC_MAGNIFIER                49
184 #define GAME_PANEL_EMC_MAGNIFIER_TIME           50
185 #define GAME_PANEL_BALLOON_SWITCH               51
186 #define GAME_PANEL_DYNABOMB_NUMBER              52
187 #define GAME_PANEL_DYNABOMB_SIZE                53
188 #define GAME_PANEL_DYNABOMB_POWER               54
189 #define GAME_PANEL_PENGUINS                     55
190 #define GAME_PANEL_SOKOBAN_OBJECTS              56
191 #define GAME_PANEL_SOKOBAN_FIELDS               57
192 #define GAME_PANEL_ROBOT_WHEEL                  58
193 #define GAME_PANEL_CONVEYOR_BELT_1              59
194 #define GAME_PANEL_CONVEYOR_BELT_2              60
195 #define GAME_PANEL_CONVEYOR_BELT_3              61
196 #define GAME_PANEL_CONVEYOR_BELT_4              62
197 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       63
198 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       64
199 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       65
200 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       66
201 #define GAME_PANEL_MAGIC_WALL                   67
202 #define GAME_PANEL_MAGIC_WALL_TIME              68
203 #define GAME_PANEL_GRAVITY_STATE                69
204 #define GAME_PANEL_GRAPHIC_1                    70
205 #define GAME_PANEL_GRAPHIC_2                    71
206 #define GAME_PANEL_GRAPHIC_3                    72
207 #define GAME_PANEL_GRAPHIC_4                    73
208 #define GAME_PANEL_GRAPHIC_5                    74
209 #define GAME_PANEL_GRAPHIC_6                    75
210 #define GAME_PANEL_GRAPHIC_7                    76
211 #define GAME_PANEL_GRAPHIC_8                    77
212 #define GAME_PANEL_ELEMENT_1                    78
213 #define GAME_PANEL_ELEMENT_2                    79
214 #define GAME_PANEL_ELEMENT_3                    80
215 #define GAME_PANEL_ELEMENT_4                    81
216 #define GAME_PANEL_ELEMENT_5                    82
217 #define GAME_PANEL_ELEMENT_6                    83
218 #define GAME_PANEL_ELEMENT_7                    84
219 #define GAME_PANEL_ELEMENT_8                    85
220 #define GAME_PANEL_ELEMENT_COUNT_1              86
221 #define GAME_PANEL_ELEMENT_COUNT_2              87
222 #define GAME_PANEL_ELEMENT_COUNT_3              88
223 #define GAME_PANEL_ELEMENT_COUNT_4              89
224 #define GAME_PANEL_ELEMENT_COUNT_5              90
225 #define GAME_PANEL_ELEMENT_COUNT_6              91
226 #define GAME_PANEL_ELEMENT_COUNT_7              92
227 #define GAME_PANEL_ELEMENT_COUNT_8              93
228 #define GAME_PANEL_CE_SCORE_1                   94
229 #define GAME_PANEL_CE_SCORE_2                   95
230 #define GAME_PANEL_CE_SCORE_3                   96
231 #define GAME_PANEL_CE_SCORE_4                   97
232 #define GAME_PANEL_CE_SCORE_5                   98
233 #define GAME_PANEL_CE_SCORE_6                   99
234 #define GAME_PANEL_CE_SCORE_7                   100
235 #define GAME_PANEL_CE_SCORE_8                   101
236 #define GAME_PANEL_CE_SCORE_1_ELEMENT           102
237 #define GAME_PANEL_CE_SCORE_2_ELEMENT           103
238 #define GAME_PANEL_CE_SCORE_3_ELEMENT           104
239 #define GAME_PANEL_CE_SCORE_4_ELEMENT           105
240 #define GAME_PANEL_CE_SCORE_5_ELEMENT           106
241 #define GAME_PANEL_CE_SCORE_6_ELEMENT           107
242 #define GAME_PANEL_CE_SCORE_7_ELEMENT           108
243 #define GAME_PANEL_CE_SCORE_8_ELEMENT           109
244 #define GAME_PANEL_PLAYER_NAME                  110
245 #define GAME_PANEL_LEVEL_NAME                   111
246 #define GAME_PANEL_LEVEL_AUTHOR                 112
247
248 #define NUM_GAME_PANEL_CONTROLS                 113
249
250 struct GamePanelOrderInfo
251 {
252   int nr;
253   int sort_priority;
254 };
255
256 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
257
258 struct GamePanelControlInfo
259 {
260   int nr;
261
262   struct TextPosInfo *pos;
263   int type;
264
265   int value, last_value;
266   int frame, last_frame;
267   int gfx_frame;
268   int gfx_random;
269 };
270
271 static struct GamePanelControlInfo game_panel_controls[] =
272 {
273   {
274     GAME_PANEL_LEVEL_NUMBER,
275     &game.panel.level_number,
276     TYPE_INTEGER,
277   },
278   {
279     GAME_PANEL_GEMS,
280     &game.panel.gems,
281     TYPE_INTEGER,
282   },
283   {
284     GAME_PANEL_INVENTORY_COUNT,
285     &game.panel.inventory_count,
286     TYPE_INTEGER,
287   },
288   {
289     GAME_PANEL_INVENTORY_FIRST_1,
290     &game.panel.inventory_first[0],
291     TYPE_ELEMENT,
292   },
293   {
294     GAME_PANEL_INVENTORY_FIRST_2,
295     &game.panel.inventory_first[1],
296     TYPE_ELEMENT,
297   },
298   {
299     GAME_PANEL_INVENTORY_FIRST_3,
300     &game.panel.inventory_first[2],
301     TYPE_ELEMENT,
302   },
303   {
304     GAME_PANEL_INVENTORY_FIRST_4,
305     &game.panel.inventory_first[3],
306     TYPE_ELEMENT,
307   },
308   {
309     GAME_PANEL_INVENTORY_FIRST_5,
310     &game.panel.inventory_first[4],
311     TYPE_ELEMENT,
312   },
313   {
314     GAME_PANEL_INVENTORY_FIRST_6,
315     &game.panel.inventory_first[5],
316     TYPE_ELEMENT,
317   },
318   {
319     GAME_PANEL_INVENTORY_FIRST_7,
320     &game.panel.inventory_first[6],
321     TYPE_ELEMENT,
322   },
323   {
324     GAME_PANEL_INVENTORY_FIRST_8,
325     &game.panel.inventory_first[7],
326     TYPE_ELEMENT,
327   },
328   {
329     GAME_PANEL_INVENTORY_LAST_1,
330     &game.panel.inventory_last[0],
331     TYPE_ELEMENT,
332   },
333   {
334     GAME_PANEL_INVENTORY_LAST_2,
335     &game.panel.inventory_last[1],
336     TYPE_ELEMENT,
337   },
338   {
339     GAME_PANEL_INVENTORY_LAST_3,
340     &game.panel.inventory_last[2],
341     TYPE_ELEMENT,
342   },
343   {
344     GAME_PANEL_INVENTORY_LAST_4,
345     &game.panel.inventory_last[3],
346     TYPE_ELEMENT,
347   },
348   {
349     GAME_PANEL_INVENTORY_LAST_5,
350     &game.panel.inventory_last[4],
351     TYPE_ELEMENT,
352   },
353   {
354     GAME_PANEL_INVENTORY_LAST_6,
355     &game.panel.inventory_last[5],
356     TYPE_ELEMENT,
357   },
358   {
359     GAME_PANEL_INVENTORY_LAST_7,
360     &game.panel.inventory_last[6],
361     TYPE_ELEMENT,
362   },
363   {
364     GAME_PANEL_INVENTORY_LAST_8,
365     &game.panel.inventory_last[7],
366     TYPE_ELEMENT,
367   },
368   {
369     GAME_PANEL_KEY_1,
370     &game.panel.key[0],
371     TYPE_ELEMENT,
372   },
373   {
374     GAME_PANEL_KEY_2,
375     &game.panel.key[1],
376     TYPE_ELEMENT,
377   },
378   {
379     GAME_PANEL_KEY_3,
380     &game.panel.key[2],
381     TYPE_ELEMENT,
382   },
383   {
384     GAME_PANEL_KEY_4,
385     &game.panel.key[3],
386     TYPE_ELEMENT,
387   },
388   {
389     GAME_PANEL_KEY_5,
390     &game.panel.key[4],
391     TYPE_ELEMENT,
392   },
393   {
394     GAME_PANEL_KEY_6,
395     &game.panel.key[5],
396     TYPE_ELEMENT,
397   },
398   {
399     GAME_PANEL_KEY_7,
400     &game.panel.key[6],
401     TYPE_ELEMENT,
402   },
403   {
404     GAME_PANEL_KEY_8,
405     &game.panel.key[7],
406     TYPE_ELEMENT,
407   },
408   {
409     GAME_PANEL_KEY_WHITE,
410     &game.panel.key_white,
411     TYPE_ELEMENT,
412   },
413   {
414     GAME_PANEL_KEY_WHITE_COUNT,
415     &game.panel.key_white_count,
416     TYPE_INTEGER,
417   },
418   {
419     GAME_PANEL_SCORE,
420     &game.panel.score,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HIGHSCORE,
425     &game.panel.highscore,
426     TYPE_INTEGER,
427   },
428   {
429     GAME_PANEL_TIME,
430     &game.panel.time,
431     TYPE_INTEGER,
432   },
433   {
434     GAME_PANEL_TIME_HH,
435     &game.panel.time_hh,
436     TYPE_INTEGER,
437   },
438   {
439     GAME_PANEL_TIME_MM,
440     &game.panel.time_mm,
441     TYPE_INTEGER,
442   },
443   {
444     GAME_PANEL_TIME_SS,
445     &game.panel.time_ss,
446     TYPE_INTEGER,
447   },
448   {
449     GAME_PANEL_SHIELD_NORMAL,
450     &game.panel.shield_normal,
451     TYPE_ELEMENT,
452   },
453   {
454     GAME_PANEL_SHIELD_NORMAL_TIME,
455     &game.panel.shield_normal_time,
456     TYPE_INTEGER,
457   },
458   {
459     GAME_PANEL_SHIELD_DEADLY,
460     &game.panel.shield_deadly,
461     TYPE_ELEMENT,
462   },
463   {
464     GAME_PANEL_SHIELD_DEADLY_TIME,
465     &game.panel.shield_deadly_time,
466     TYPE_INTEGER,
467   },
468   {
469     GAME_PANEL_EXIT,
470     &game.panel.exit,
471     TYPE_ELEMENT,
472   },
473   {
474     GAME_PANEL_EMC_MAGIC_BALL,
475     &game.panel.emc_magic_ball,
476     TYPE_ELEMENT,
477   },
478   {
479     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
480     &game.panel.emc_magic_ball_switch,
481     TYPE_ELEMENT,
482   },
483   {
484     GAME_PANEL_LIGHT_SWITCH,
485     &game.panel.light_switch,
486     TYPE_ELEMENT,
487   },
488   {
489     GAME_PANEL_LIGHT_SWITCH_TIME,
490     &game.panel.light_switch_time,
491     TYPE_INTEGER,
492   },
493   {
494     GAME_PANEL_TIMEGATE_SWITCH,
495     &game.panel.timegate_switch,
496     TYPE_ELEMENT,
497   },
498   {
499     GAME_PANEL_TIMEGATE_SWITCH_TIME,
500     &game.panel.timegate_switch_time,
501     TYPE_INTEGER,
502   },
503   {
504     GAME_PANEL_SWITCHGATE_SWITCH,
505     &game.panel.switchgate_switch,
506     TYPE_ELEMENT,
507   },
508   {
509     GAME_PANEL_EMC_LENSES,
510     &game.panel.emc_lenses,
511     TYPE_ELEMENT,
512   },
513   {
514     GAME_PANEL_EMC_LENSES_TIME,
515     &game.panel.emc_lenses_time,
516     TYPE_INTEGER,
517   },
518   {
519     GAME_PANEL_EMC_MAGNIFIER,
520     &game.panel.emc_magnifier,
521     TYPE_ELEMENT,
522   },
523   {
524     GAME_PANEL_EMC_MAGNIFIER_TIME,
525     &game.panel.emc_magnifier_time,
526     TYPE_INTEGER,
527   },
528   {
529     GAME_PANEL_BALLOON_SWITCH,
530     &game.panel.balloon_switch,
531     TYPE_ELEMENT,
532   },
533   {
534     GAME_PANEL_DYNABOMB_NUMBER,
535     &game.panel.dynabomb_number,
536     TYPE_INTEGER,
537   },
538   {
539     GAME_PANEL_DYNABOMB_SIZE,
540     &game.panel.dynabomb_size,
541     TYPE_INTEGER,
542   },
543   {
544     GAME_PANEL_DYNABOMB_POWER,
545     &game.panel.dynabomb_power,
546     TYPE_ELEMENT,
547   },
548   {
549     GAME_PANEL_PENGUINS,
550     &game.panel.penguins,
551     TYPE_INTEGER,
552   },
553   {
554     GAME_PANEL_SOKOBAN_OBJECTS,
555     &game.panel.sokoban_objects,
556     TYPE_INTEGER,
557   },
558   {
559     GAME_PANEL_SOKOBAN_FIELDS,
560     &game.panel.sokoban_fields,
561     TYPE_INTEGER,
562   },
563   {
564     GAME_PANEL_ROBOT_WHEEL,
565     &game.panel.robot_wheel,
566     TYPE_ELEMENT,
567   },
568   {
569     GAME_PANEL_CONVEYOR_BELT_1,
570     &game.panel.conveyor_belt[0],
571     TYPE_ELEMENT,
572   },
573   {
574     GAME_PANEL_CONVEYOR_BELT_2,
575     &game.panel.conveyor_belt[1],
576     TYPE_ELEMENT,
577   },
578   {
579     GAME_PANEL_CONVEYOR_BELT_3,
580     &game.panel.conveyor_belt[2],
581     TYPE_ELEMENT,
582   },
583   {
584     GAME_PANEL_CONVEYOR_BELT_4,
585     &game.panel.conveyor_belt[3],
586     TYPE_ELEMENT,
587   },
588   {
589     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
590     &game.panel.conveyor_belt_switch[0],
591     TYPE_ELEMENT,
592   },
593   {
594     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
595     &game.panel.conveyor_belt_switch[1],
596     TYPE_ELEMENT,
597   },
598   {
599     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
600     &game.panel.conveyor_belt_switch[2],
601     TYPE_ELEMENT,
602   },
603   {
604     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
605     &game.panel.conveyor_belt_switch[3],
606     TYPE_ELEMENT,
607   },
608   {
609     GAME_PANEL_MAGIC_WALL,
610     &game.panel.magic_wall,
611     TYPE_ELEMENT,
612   },
613   {
614     GAME_PANEL_MAGIC_WALL_TIME,
615     &game.panel.magic_wall_time,
616     TYPE_INTEGER,
617   },
618   {
619     GAME_PANEL_GRAVITY_STATE,
620     &game.panel.gravity_state,
621     TYPE_STRING,
622   },
623   {
624     GAME_PANEL_GRAPHIC_1,
625     &game.panel.graphic[0],
626     TYPE_ELEMENT,
627   },
628   {
629     GAME_PANEL_GRAPHIC_2,
630     &game.panel.graphic[1],
631     TYPE_ELEMENT,
632   },
633   {
634     GAME_PANEL_GRAPHIC_3,
635     &game.panel.graphic[2],
636     TYPE_ELEMENT,
637   },
638   {
639     GAME_PANEL_GRAPHIC_4,
640     &game.panel.graphic[3],
641     TYPE_ELEMENT,
642   },
643   {
644     GAME_PANEL_GRAPHIC_5,
645     &game.panel.graphic[4],
646     TYPE_ELEMENT,
647   },
648   {
649     GAME_PANEL_GRAPHIC_6,
650     &game.panel.graphic[5],
651     TYPE_ELEMENT,
652   },
653   {
654     GAME_PANEL_GRAPHIC_7,
655     &game.panel.graphic[6],
656     TYPE_ELEMENT,
657   },
658   {
659     GAME_PANEL_GRAPHIC_8,
660     &game.panel.graphic[7],
661     TYPE_ELEMENT,
662   },
663   {
664     GAME_PANEL_ELEMENT_1,
665     &game.panel.element[0],
666     TYPE_ELEMENT,
667   },
668   {
669     GAME_PANEL_ELEMENT_2,
670     &game.panel.element[1],
671     TYPE_ELEMENT,
672   },
673   {
674     GAME_PANEL_ELEMENT_3,
675     &game.panel.element[2],
676     TYPE_ELEMENT,
677   },
678   {
679     GAME_PANEL_ELEMENT_4,
680     &game.panel.element[3],
681     TYPE_ELEMENT,
682   },
683   {
684     GAME_PANEL_ELEMENT_5,
685     &game.panel.element[4],
686     TYPE_ELEMENT,
687   },
688   {
689     GAME_PANEL_ELEMENT_6,
690     &game.panel.element[5],
691     TYPE_ELEMENT,
692   },
693   {
694     GAME_PANEL_ELEMENT_7,
695     &game.panel.element[6],
696     TYPE_ELEMENT,
697   },
698   {
699     GAME_PANEL_ELEMENT_8,
700     &game.panel.element[7],
701     TYPE_ELEMENT,
702   },
703   {
704     GAME_PANEL_ELEMENT_COUNT_1,
705     &game.panel.element_count[0],
706     TYPE_INTEGER,
707   },
708   {
709     GAME_PANEL_ELEMENT_COUNT_2,
710     &game.panel.element_count[1],
711     TYPE_INTEGER,
712   },
713   {
714     GAME_PANEL_ELEMENT_COUNT_3,
715     &game.panel.element_count[2],
716     TYPE_INTEGER,
717   },
718   {
719     GAME_PANEL_ELEMENT_COUNT_4,
720     &game.panel.element_count[3],
721     TYPE_INTEGER,
722   },
723   {
724     GAME_PANEL_ELEMENT_COUNT_5,
725     &game.panel.element_count[4],
726     TYPE_INTEGER,
727   },
728   {
729     GAME_PANEL_ELEMENT_COUNT_6,
730     &game.panel.element_count[5],
731     TYPE_INTEGER,
732   },
733   {
734     GAME_PANEL_ELEMENT_COUNT_7,
735     &game.panel.element_count[6],
736     TYPE_INTEGER,
737   },
738   {
739     GAME_PANEL_ELEMENT_COUNT_8,
740     &game.panel.element_count[7],
741     TYPE_INTEGER,
742   },
743   {
744     GAME_PANEL_CE_SCORE_1,
745     &game.panel.ce_score[0],
746     TYPE_INTEGER,
747   },
748   {
749     GAME_PANEL_CE_SCORE_2,
750     &game.panel.ce_score[1],
751     TYPE_INTEGER,
752   },
753   {
754     GAME_PANEL_CE_SCORE_3,
755     &game.panel.ce_score[2],
756     TYPE_INTEGER,
757   },
758   {
759     GAME_PANEL_CE_SCORE_4,
760     &game.panel.ce_score[3],
761     TYPE_INTEGER,
762   },
763   {
764     GAME_PANEL_CE_SCORE_5,
765     &game.panel.ce_score[4],
766     TYPE_INTEGER,
767   },
768   {
769     GAME_PANEL_CE_SCORE_6,
770     &game.panel.ce_score[5],
771     TYPE_INTEGER,
772   },
773   {
774     GAME_PANEL_CE_SCORE_7,
775     &game.panel.ce_score[6],
776     TYPE_INTEGER,
777   },
778   {
779     GAME_PANEL_CE_SCORE_8,
780     &game.panel.ce_score[7],
781     TYPE_INTEGER,
782   },
783   {
784     GAME_PANEL_CE_SCORE_1_ELEMENT,
785     &game.panel.ce_score_element[0],
786     TYPE_ELEMENT,
787   },
788   {
789     GAME_PANEL_CE_SCORE_2_ELEMENT,
790     &game.panel.ce_score_element[1],
791     TYPE_ELEMENT,
792   },
793   {
794     GAME_PANEL_CE_SCORE_3_ELEMENT,
795     &game.panel.ce_score_element[2],
796     TYPE_ELEMENT,
797   },
798   {
799     GAME_PANEL_CE_SCORE_4_ELEMENT,
800     &game.panel.ce_score_element[3],
801     TYPE_ELEMENT,
802   },
803   {
804     GAME_PANEL_CE_SCORE_5_ELEMENT,
805     &game.panel.ce_score_element[4],
806     TYPE_ELEMENT,
807   },
808   {
809     GAME_PANEL_CE_SCORE_6_ELEMENT,
810     &game.panel.ce_score_element[5],
811     TYPE_ELEMENT,
812   },
813   {
814     GAME_PANEL_CE_SCORE_7_ELEMENT,
815     &game.panel.ce_score_element[6],
816     TYPE_ELEMENT,
817   },
818   {
819     GAME_PANEL_CE_SCORE_8_ELEMENT,
820     &game.panel.ce_score_element[7],
821     TYPE_ELEMENT,
822   },
823   {
824     GAME_PANEL_PLAYER_NAME,
825     &game.panel.player_name,
826     TYPE_STRING,
827   },
828   {
829     GAME_PANEL_LEVEL_NAME,
830     &game.panel.level_name,
831     TYPE_STRING,
832   },
833   {
834     GAME_PANEL_LEVEL_AUTHOR,
835     &game.panel.level_author,
836     TYPE_STRING,
837   },
838
839   {
840     -1,
841     NULL,
842     -1,
843   }
844 };
845 #endif
846
847
848 /* values for delayed check of falling and moving elements and for collision */
849 #define CHECK_DELAY_MOVING      3
850 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
851 #define CHECK_DELAY_COLLISION   2
852 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
853
854 /* values for initial player move delay (initial delay counter value) */
855 #define INITIAL_MOVE_DELAY_OFF  -1
856 #define INITIAL_MOVE_DELAY_ON   0
857
858 /* values for player movement speed (which is in fact a delay value) */
859 #define MOVE_DELAY_MIN_SPEED    32
860 #define MOVE_DELAY_NORMAL_SPEED 8
861 #define MOVE_DELAY_HIGH_SPEED   4
862 #define MOVE_DELAY_MAX_SPEED    1
863
864 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
865 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
866
867 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
868 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
869
870 /* values for other actions */
871 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
872 #define MOVE_STEPSIZE_MIN       (1)
873 #define MOVE_STEPSIZE_MAX       (TILEX)
874
875 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
876 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
877
878 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
879
880 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
881                                  RND(element_info[e].push_delay_random))
882 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
883                                  RND(element_info[e].drop_delay_random))
884 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
885                                  RND(element_info[e].move_delay_random))
886 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
887                                     (element_info[e].move_delay_random))
888 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
889                                  RND(element_info[e].ce_value_random_initial))
890 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
891 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
892                                  RND((c)->delay_random * (c)->delay_frames))
893 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
894                                  RND((c)->delay_random))
895
896
897 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
898          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
899
900 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
901         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
902          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
903          (be) + (e) - EL_SELF)
904
905 #define GET_PLAYER_FROM_BITS(p)                                         \
906         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
907
908 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
909         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
910          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
911          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
912          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
913          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
914          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
915          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
916          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
917          (e))
918
919 #define CAN_GROW_INTO(e)                                                \
920         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
921
922 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
923                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
934                                         (CAN_MOVE_INTO_ACID(e) &&       \
935                                          Feld[x][y] == EL_ACID) ||      \
936                                         (condition)))
937
938 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
939                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
940                                         (condition) ||                  \
941                                         (CAN_MOVE_INTO_ACID(e) &&       \
942                                          Feld[x][y] == EL_ACID) ||      \
943                                         (DONT_COLLIDE_WITH(e) &&        \
944                                          IS_PLAYER(x, y) &&             \
945                                          !PLAYER_ENEMY_PROTECTED(x, y))))
946
947 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
949
950 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
951         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
952
953 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
954         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
955
956 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
957         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
958                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
959
960 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
961         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
962
963 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
965
966 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
967         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
968
969 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
970         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
971
972 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
973         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
974
975 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
977                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
978                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
979                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
980                                                  IS_FOOD_PENGUIN(Feld[x][y])))
981 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
985         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
986
987 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
988         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
989
990 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
991         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
992                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
993
994 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
995
996 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
997                 (!IS_PLAYER(x, y) &&                                    \
998                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
999
1000 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1001         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1002
1003 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1004 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1005
1006 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1007 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1008 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1009 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1010
1011 /* game button identifiers */
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define SOUND_CTRL_ID_MUSIC             3
1016 #define SOUND_CTRL_ID_LOOPS             4
1017 #define SOUND_CTRL_ID_SIMPLE            5
1018
1019 #define NUM_GAME_BUTTONS                6
1020
1021
1022 /* forward declaration for internal use */
1023
1024 static void CreateField(int, int, int);
1025
1026 static void ResetGfxAnimation(int, int);
1027
1028 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1029 static void AdvanceFrameAndPlayerCounters(int);
1030
1031 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1032 static boolean MovePlayer(struct PlayerInfo *, int, int);
1033 static void ScrollPlayer(struct PlayerInfo *, int);
1034 static void ScrollScreen(struct PlayerInfo *, int);
1035
1036 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1037
1038 static void InitBeltMovement(void);
1039 static void CloseAllOpenTimegates(void);
1040 static void CheckGravityMovement(struct PlayerInfo *);
1041 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1042 static void KillPlayerUnlessEnemyProtected(int, int);
1043 static void KillPlayerUnlessExplosionProtected(int, int);
1044
1045 static void TestIfPlayerTouchesCustomElement(int, int);
1046 static void TestIfElementTouchesCustomElement(int, int);
1047 static void TestIfElementHitsCustomElement(int, int, int);
1048 #if 0
1049 static void TestIfElementSmashesCustomElement(int, int, int);
1050 #endif
1051
1052 static void HandleElementChange(int, int, int);
1053 static void ExecuteCustomElementAction(int, int, int, int);
1054 static boolean ChangeElement(int, int, int, int);
1055
1056 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1057 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1058         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1059 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1060         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1061 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1062         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1063 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1064         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1065
1066 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1067 #define CheckElementChange(x, y, e, te, ev)                             \
1068         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1069 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1070         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1071 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1072         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1073
1074 static void PlayLevelSound(int, int, int);
1075 static void PlayLevelSoundNearest(int, int, int);
1076 static void PlayLevelSoundAction(int, int, int);
1077 static void PlayLevelSoundElementAction(int, int, int, int);
1078 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1079 static void PlayLevelSoundActionIfLoop(int, int, int);
1080 static void StopLevelSoundActionIfLoop(int, int, int);
1081 static void PlayLevelMusic();
1082
1083 static void MapGameButtons();
1084 static void HandleGameButtons(struct GadgetInfo *);
1085
1086 int AmoebeNachbarNr(int, int);
1087 void AmoebeUmwandeln(int, int);
1088 void ContinueMoving(int, int);
1089 void Bang(int, int);
1090 void InitMovDir(int, int);
1091 void InitAmoebaNr(int, int);
1092 int NewHiScore(void);
1093
1094 void TestIfGoodThingHitsBadThing(int, int, int);
1095 void TestIfBadThingHitsGoodThing(int, int, int);
1096 void TestIfPlayerTouchesBadThing(int, int);
1097 void TestIfPlayerRunsIntoBadThing(int, int, int);
1098 void TestIfBadThingTouchesPlayer(int, int);
1099 void TestIfBadThingRunsIntoPlayer(int, int, int);
1100 void TestIfFriendTouchesBadThing(int, int);
1101 void TestIfBadThingTouchesFriend(int, int);
1102 void TestIfBadThingTouchesOtherBadThing(int, int);
1103
1104 void KillPlayer(struct PlayerInfo *);
1105 void BuryPlayer(struct PlayerInfo *);
1106 void RemovePlayer(struct PlayerInfo *);
1107
1108 boolean SnapField(struct PlayerInfo *, int, int);
1109 boolean DropElement(struct PlayerInfo *);
1110
1111 static int getInvisibleActiveFromInvisibleElement(int);
1112 static int getInvisibleFromInvisibleActiveElement(int);
1113
1114 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1115
1116 /* for detection of endless loops, caused by custom element programming */
1117 /* (using maximal playfield width x 10 is just a rough approximation) */
1118 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1119
1120 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1121 {                                                                       \
1122   if (recursion_loop_detected)                                          \
1123     return (rc);                                                        \
1124                                                                         \
1125   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1126   {                                                                     \
1127     recursion_loop_detected = TRUE;                                     \
1128     recursion_loop_element = (e);                                       \
1129   }                                                                     \
1130                                                                         \
1131   recursion_loop_depth++;                                               \
1132 }
1133
1134 #define RECURSION_LOOP_DETECTION_END()                                  \
1135 {                                                                       \
1136   recursion_loop_depth--;                                               \
1137 }
1138
1139 static int recursion_loop_depth;
1140 static boolean recursion_loop_detected;
1141 static boolean recursion_loop_element;
1142
1143
1144 /* ------------------------------------------------------------------------- */
1145 /* definition of elements that automatically change to other elements after  */
1146 /* a specified time, eventually calling a function when changing             */
1147 /* ------------------------------------------------------------------------- */
1148
1149 /* forward declaration for changer functions */
1150 static void InitBuggyBase(int, int);
1151 static void WarnBuggyBase(int, int);
1152
1153 static void InitTrap(int, int);
1154 static void ActivateTrap(int, int);
1155 static void ChangeActiveTrap(int, int);
1156
1157 static void InitRobotWheel(int, int);
1158 static void RunRobotWheel(int, int);
1159 static void StopRobotWheel(int, int);
1160
1161 static void InitTimegateWheel(int, int);
1162 static void RunTimegateWheel(int, int);
1163
1164 static void InitMagicBallDelay(int, int);
1165 static void ActivateMagicBall(int, int);
1166
1167 struct ChangingElementInfo
1168 {
1169   int element;
1170   int target_element;
1171   int change_delay;
1172   void (*pre_change_function)(int x, int y);
1173   void (*change_function)(int x, int y);
1174   void (*post_change_function)(int x, int y);
1175 };
1176
1177 static struct ChangingElementInfo change_delay_list[] =
1178 {
1179   {
1180     EL_NUT_BREAKING,
1181     EL_EMERALD,
1182     6,
1183     NULL,
1184     NULL,
1185     NULL
1186   },
1187   {
1188     EL_PEARL_BREAKING,
1189     EL_EMPTY,
1190     8,
1191     NULL,
1192     NULL,
1193     NULL
1194   },
1195   {
1196     EL_EXIT_OPENING,
1197     EL_EXIT_OPEN,
1198     29,
1199     NULL,
1200     NULL,
1201     NULL
1202   },
1203   {
1204     EL_EXIT_CLOSING,
1205     EL_EXIT_CLOSED,
1206     29,
1207     NULL,
1208     NULL,
1209     NULL
1210   },
1211   {
1212     EL_STEEL_EXIT_OPENING,
1213     EL_STEEL_EXIT_OPEN,
1214     29,
1215     NULL,
1216     NULL,
1217     NULL
1218   },
1219   {
1220     EL_STEEL_EXIT_CLOSING,
1221     EL_STEEL_EXIT_CLOSED,
1222     29,
1223     NULL,
1224     NULL,
1225     NULL
1226   },
1227   {
1228     EL_EM_EXIT_OPENING,
1229     EL_EM_EXIT_OPEN,
1230     29,
1231     NULL,
1232     NULL,
1233     NULL
1234   },
1235   {
1236     EL_EM_EXIT_CLOSING,
1237 #if 1
1238     EL_EMPTY,
1239 #else
1240     EL_EM_EXIT_CLOSED,
1241 #endif
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EM_STEEL_EXIT_OPENING,
1249     EL_EM_STEEL_EXIT_OPEN,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_EM_STEEL_EXIT_CLOSING,
1257 #if 1
1258     EL_STEELWALL,
1259 #else
1260     EL_EM_STEEL_EXIT_CLOSED,
1261 #endif
1262     29,
1263     NULL,
1264     NULL,
1265     NULL
1266   },
1267   {
1268     EL_SP_EXIT_OPENING,
1269     EL_SP_EXIT_OPEN,
1270     29,
1271     NULL,
1272     NULL,
1273     NULL
1274   },
1275   {
1276     EL_SP_EXIT_CLOSING,
1277     EL_SP_EXIT_CLOSED,
1278     29,
1279     NULL,
1280     NULL,
1281     NULL
1282   },
1283   {
1284     EL_SWITCHGATE_OPENING,
1285     EL_SWITCHGATE_OPEN,
1286     29,
1287     NULL,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SWITCHGATE_CLOSING,
1293     EL_SWITCHGATE_CLOSED,
1294     29,
1295     NULL,
1296     NULL,
1297     NULL
1298   },
1299   {
1300     EL_TIMEGATE_OPENING,
1301     EL_TIMEGATE_OPEN,
1302     29,
1303     NULL,
1304     NULL,
1305     NULL
1306   },
1307   {
1308     EL_TIMEGATE_CLOSING,
1309     EL_TIMEGATE_CLOSED,
1310     29,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315
1316   {
1317     EL_ACID_SPLASH_LEFT,
1318     EL_EMPTY,
1319     8,
1320     NULL,
1321     NULL,
1322     NULL
1323   },
1324   {
1325     EL_ACID_SPLASH_RIGHT,
1326     EL_EMPTY,
1327     8,
1328     NULL,
1329     NULL,
1330     NULL
1331   },
1332   {
1333     EL_SP_BUGGY_BASE,
1334     EL_SP_BUGGY_BASE_ACTIVATING,
1335     0,
1336     InitBuggyBase,
1337     NULL,
1338     NULL
1339   },
1340   {
1341     EL_SP_BUGGY_BASE_ACTIVATING,
1342     EL_SP_BUGGY_BASE_ACTIVE,
1343     0,
1344     InitBuggyBase,
1345     NULL,
1346     NULL
1347   },
1348   {
1349     EL_SP_BUGGY_BASE_ACTIVE,
1350     EL_SP_BUGGY_BASE,
1351     0,
1352     InitBuggyBase,
1353     WarnBuggyBase,
1354     NULL
1355   },
1356   {
1357     EL_TRAP,
1358     EL_TRAP_ACTIVE,
1359     0,
1360     InitTrap,
1361     NULL,
1362     ActivateTrap
1363   },
1364   {
1365     EL_TRAP_ACTIVE,
1366     EL_TRAP,
1367     31,
1368     NULL,
1369     ChangeActiveTrap,
1370     NULL
1371   },
1372   {
1373     EL_ROBOT_WHEEL_ACTIVE,
1374     EL_ROBOT_WHEEL,
1375     0,
1376     InitRobotWheel,
1377     RunRobotWheel,
1378     StopRobotWheel
1379   },
1380   {
1381     EL_TIMEGATE_SWITCH_ACTIVE,
1382     EL_TIMEGATE_SWITCH,
1383     0,
1384     InitTimegateWheel,
1385     RunTimegateWheel,
1386     NULL
1387   },
1388   {
1389     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1390     EL_DC_TIMEGATE_SWITCH,
1391     0,
1392     InitTimegateWheel,
1393     RunTimegateWheel,
1394     NULL
1395   },
1396   {
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     EL_EMC_MAGIC_BALL_ACTIVE,
1399     0,
1400     InitMagicBallDelay,
1401     NULL,
1402     ActivateMagicBall
1403   },
1404   {
1405     EL_EMC_SPRING_BUMPER_ACTIVE,
1406     EL_EMC_SPRING_BUMPER,
1407     8,
1408     NULL,
1409     NULL,
1410     NULL
1411   },
1412   {
1413     EL_DIAGONAL_SHRINKING,
1414     EL_UNDEFINED,
1415     0,
1416     NULL,
1417     NULL,
1418     NULL
1419   },
1420   {
1421     EL_DIAGONAL_GROWING,
1422     EL_UNDEFINED,
1423     0,
1424     NULL,
1425     NULL,
1426     NULL,
1427   },
1428
1429   {
1430     EL_UNDEFINED,
1431     EL_UNDEFINED,
1432     -1,
1433     NULL,
1434     NULL,
1435     NULL
1436   }
1437 };
1438
1439 struct
1440 {
1441   int element;
1442   int push_delay_fixed, push_delay_random;
1443 }
1444 push_delay_list[] =
1445 {
1446   { EL_SPRING,                  0, 0 },
1447   { EL_BALLOON,                 0, 0 },
1448
1449   { EL_SOKOBAN_OBJECT,          2, 0 },
1450   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1451   { EL_SATELLITE,               2, 0 },
1452   { EL_SP_DISK_YELLOW,          2, 0 },
1453
1454   { EL_UNDEFINED,               0, 0 },
1455 };
1456
1457 struct
1458 {
1459   int element;
1460   int move_stepsize;
1461 }
1462 move_stepsize_list[] =
1463 {
1464   { EL_AMOEBA_DROP,             2 },
1465   { EL_AMOEBA_DROPPING,         2 },
1466   { EL_QUICKSAND_FILLING,       1 },
1467   { EL_QUICKSAND_EMPTYING,      1 },
1468   { EL_QUICKSAND_FAST_FILLING,  2 },
1469   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1470   { EL_MAGIC_WALL_FILLING,      2 },
1471   { EL_MAGIC_WALL_EMPTYING,     2 },
1472   { EL_BD_MAGIC_WALL_FILLING,   2 },
1473   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1474   { EL_DC_MAGIC_WALL_FILLING,   2 },
1475   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1476
1477   { EL_UNDEFINED,               0 },
1478 };
1479
1480 struct
1481 {
1482   int element;
1483   int count;
1484 }
1485 collect_count_list[] =
1486 {
1487   { EL_EMERALD,                 1 },
1488   { EL_BD_DIAMOND,              1 },
1489   { EL_EMERALD_YELLOW,          1 },
1490   { EL_EMERALD_RED,             1 },
1491   { EL_EMERALD_PURPLE,          1 },
1492   { EL_DIAMOND,                 3 },
1493   { EL_SP_INFOTRON,             1 },
1494   { EL_PEARL,                   5 },
1495   { EL_CRYSTAL,                 8 },
1496
1497   { EL_UNDEFINED,               0 },
1498 };
1499
1500 struct
1501 {
1502   int element;
1503   int direction;
1504 }
1505 access_direction_list[] =
1506 {
1507   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1508   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1509   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1510   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1511   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1513   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1514   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1515   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1516   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1517   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1518
1519   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1520   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1521   { EL_SP_PORT_UP,                                                   MV_DOWN },
1522   { EL_SP_PORT_DOWN,                                         MV_UP           },
1523   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1524   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1525   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1526   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1527   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1528   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1529   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1530   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1531   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1532   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1533   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1534   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1535   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1536   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1537   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1538
1539   { EL_UNDEFINED,                       MV_NONE                              }
1540 };
1541
1542 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1543
1544 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1545 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1546 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1547                                  IS_JUST_CHANGING(x, y))
1548
1549 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1550
1551 /* static variables for playfield scan mode (scanning forward or backward) */
1552 static int playfield_scan_start_x = 0;
1553 static int playfield_scan_start_y = 0;
1554 static int playfield_scan_delta_x = 1;
1555 static int playfield_scan_delta_y = 1;
1556
1557 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1558                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1559                                      (y) += playfield_scan_delta_y)     \
1560                                 for ((x) = playfield_scan_start_x;      \
1561                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1562                                      (x) += playfield_scan_delta_x)
1563
1564 #ifdef DEBUG
1565 void DEBUG_SetMaximumDynamite()
1566 {
1567   int i;
1568
1569   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1570     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1571       local_player->inventory_element[local_player->inventory_size++] =
1572         EL_DYNAMITE;
1573 }
1574 #endif
1575
1576 static void InitPlayfieldScanModeVars()
1577 {
1578   if (game.use_reverse_scan_direction)
1579   {
1580     playfield_scan_start_x = lev_fieldx - 1;
1581     playfield_scan_start_y = lev_fieldy - 1;
1582
1583     playfield_scan_delta_x = -1;
1584     playfield_scan_delta_y = -1;
1585   }
1586   else
1587   {
1588     playfield_scan_start_x = 0;
1589     playfield_scan_start_y = 0;
1590
1591     playfield_scan_delta_x = 1;
1592     playfield_scan_delta_y = 1;
1593   }
1594 }
1595
1596 static void InitPlayfieldScanMode(int mode)
1597 {
1598   game.use_reverse_scan_direction =
1599     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1600
1601   InitPlayfieldScanModeVars();
1602 }
1603
1604 static int get_move_delay_from_stepsize(int move_stepsize)
1605 {
1606   move_stepsize =
1607     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1608
1609   /* make sure that stepsize value is always a power of 2 */
1610   move_stepsize = (1 << log_2(move_stepsize));
1611
1612   return TILEX / move_stepsize;
1613 }
1614
1615 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1616                                boolean init_game)
1617 {
1618   int player_nr = player->index_nr;
1619   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1620   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1621
1622   /* do no immediately change move delay -- the player might just be moving */
1623   player->move_delay_value_next = move_delay;
1624
1625   /* information if player can move must be set separately */
1626   player->cannot_move = cannot_move;
1627
1628   if (init_game)
1629   {
1630     player->move_delay       = game.initial_move_delay[player_nr];
1631     player->move_delay_value = game.initial_move_delay_value[player_nr];
1632
1633     player->move_delay_value_next = -1;
1634
1635     player->move_delay_reset_counter = 0;
1636   }
1637 }
1638
1639 void GetPlayerConfig()
1640 {
1641   GameFrameDelay = setup.game_frame_delay;
1642
1643   if (!audio.sound_available)
1644     setup.sound_simple = FALSE;
1645
1646   if (!audio.loops_available)
1647     setup.sound_loops = FALSE;
1648
1649   if (!audio.music_available)
1650     setup.sound_music = FALSE;
1651
1652   if (!video.fullscreen_available)
1653     setup.fullscreen = FALSE;
1654
1655   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656
1657   SetAudioMode(setup.sound);
1658   InitJoysticks();
1659 }
1660
1661 int GetElementFromGroupElement(int element)
1662 {
1663   if (IS_GROUP_ELEMENT(element))
1664   {
1665     struct ElementGroupInfo *group = element_info[element].group;
1666     int last_anim_random_frame = gfx.anim_random_frame;
1667     int element_pos;
1668
1669     if (group->choice_mode == ANIM_RANDOM)
1670       gfx.anim_random_frame = RND(group->num_elements_resolved);
1671
1672     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1673                                     group->choice_mode, 0,
1674                                     group->choice_pos);
1675
1676     if (group->choice_mode == ANIM_RANDOM)
1677       gfx.anim_random_frame = last_anim_random_frame;
1678
1679     group->choice_pos++;
1680
1681     element = group->element_resolved[element_pos];
1682   }
1683
1684   return element;
1685 }
1686
1687 static void InitPlayerField(int x, int y, int element, boolean init_game)
1688 {
1689   if (element == EL_SP_MURPHY)
1690   {
1691     if (init_game)
1692     {
1693       if (stored_player[0].present)
1694       {
1695         Feld[x][y] = EL_SP_MURPHY_CLONE;
1696
1697         return;
1698       }
1699       else
1700       {
1701         stored_player[0].use_murphy = TRUE;
1702
1703         if (!level.use_artwork_element[0])
1704           stored_player[0].artwork_element = EL_SP_MURPHY;
1705       }
1706
1707       Feld[x][y] = EL_PLAYER_1;
1708     }
1709   }
1710
1711   if (init_game)
1712   {
1713     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1714     int jx = player->jx, jy = player->jy;
1715
1716     player->present = TRUE;
1717
1718     player->block_last_field = (element == EL_SP_MURPHY ?
1719                                 level.sp_block_last_field :
1720                                 level.block_last_field);
1721
1722     /* ---------- initialize player's last field block delay --------------- */
1723
1724     /* always start with reliable default value (no adjustment needed) */
1725     player->block_delay_adjustment = 0;
1726
1727     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1728     if (player->block_last_field && element == EL_SP_MURPHY)
1729       player->block_delay_adjustment = 1;
1730
1731     /* special case 2: in game engines before 3.1.1, blocking was different */
1732     if (game.use_block_last_field_bug)
1733       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1734
1735     if (!options.network || player->connected)
1736     {
1737       player->active = TRUE;
1738
1739       /* remove potentially duplicate players */
1740       if (StorePlayer[jx][jy] == Feld[x][y])
1741         StorePlayer[jx][jy] = 0;
1742
1743       StorePlayer[x][y] = Feld[x][y];
1744
1745       if (options.debug)
1746       {
1747         printf("Player %d activated.\n", player->element_nr);
1748         printf("[Local player is %d and currently %s.]\n",
1749                local_player->element_nr,
1750                local_player->active ? "active" : "not active");
1751       }
1752     }
1753
1754     Feld[x][y] = EL_EMPTY;
1755
1756     player->jx = player->last_jx = x;
1757     player->jy = player->last_jy = y;
1758   }
1759 }
1760
1761 static void InitField(int x, int y, boolean init_game)
1762 {
1763   int element = Feld[x][y];
1764
1765   switch (element)
1766   {
1767     case EL_SP_MURPHY:
1768     case EL_PLAYER_1:
1769     case EL_PLAYER_2:
1770     case EL_PLAYER_3:
1771     case EL_PLAYER_4:
1772       InitPlayerField(x, y, element, init_game);
1773       break;
1774
1775     case EL_SOKOBAN_FIELD_PLAYER:
1776       element = Feld[x][y] = EL_PLAYER_1;
1777       InitField(x, y, init_game);
1778
1779       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1780       InitField(x, y, init_game);
1781       break;
1782
1783     case EL_SOKOBAN_FIELD_EMPTY:
1784       local_player->sokobanfields_still_needed++;
1785       break;
1786
1787     case EL_STONEBLOCK:
1788       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1789         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1790       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1791         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1792       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1793         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1794       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1795         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1796       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1797         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1798       break;
1799
1800     case EL_BUG:
1801     case EL_BUG_RIGHT:
1802     case EL_BUG_UP:
1803     case EL_BUG_LEFT:
1804     case EL_BUG_DOWN:
1805     case EL_SPACESHIP:
1806     case EL_SPACESHIP_RIGHT:
1807     case EL_SPACESHIP_UP:
1808     case EL_SPACESHIP_LEFT:
1809     case EL_SPACESHIP_DOWN:
1810     case EL_BD_BUTTERFLY:
1811     case EL_BD_BUTTERFLY_RIGHT:
1812     case EL_BD_BUTTERFLY_UP:
1813     case EL_BD_BUTTERFLY_LEFT:
1814     case EL_BD_BUTTERFLY_DOWN:
1815     case EL_BD_FIREFLY:
1816     case EL_BD_FIREFLY_RIGHT:
1817     case EL_BD_FIREFLY_UP:
1818     case EL_BD_FIREFLY_LEFT:
1819     case EL_BD_FIREFLY_DOWN:
1820     case EL_PACMAN_RIGHT:
1821     case EL_PACMAN_UP:
1822     case EL_PACMAN_LEFT:
1823     case EL_PACMAN_DOWN:
1824     case EL_YAMYAM:
1825     case EL_YAMYAM_LEFT:
1826     case EL_YAMYAM_RIGHT:
1827     case EL_YAMYAM_UP:
1828     case EL_YAMYAM_DOWN:
1829     case EL_DARK_YAMYAM:
1830     case EL_ROBOT:
1831     case EL_PACMAN:
1832     case EL_SP_SNIKSNAK:
1833     case EL_SP_ELECTRON:
1834     case EL_MOLE:
1835     case EL_MOLE_LEFT:
1836     case EL_MOLE_RIGHT:
1837     case EL_MOLE_UP:
1838     case EL_MOLE_DOWN:
1839       InitMovDir(x, y);
1840       break;
1841
1842     case EL_AMOEBA_FULL:
1843     case EL_BD_AMOEBA:
1844       InitAmoebaNr(x, y);
1845       break;
1846
1847     case EL_AMOEBA_DROP:
1848       if (y == lev_fieldy - 1)
1849       {
1850         Feld[x][y] = EL_AMOEBA_GROWING;
1851         Store[x][y] = EL_AMOEBA_WET;
1852       }
1853       break;
1854
1855     case EL_DYNAMITE_ACTIVE:
1856     case EL_SP_DISK_RED_ACTIVE:
1857     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1858     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1859     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1860     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1861       MovDelay[x][y] = 96;
1862       break;
1863
1864     case EL_EM_DYNAMITE_ACTIVE:
1865       MovDelay[x][y] = 32;
1866       break;
1867
1868     case EL_LAMP:
1869       local_player->lights_still_needed++;
1870       break;
1871
1872     case EL_PENGUIN:
1873       local_player->friends_still_needed++;
1874       break;
1875
1876     case EL_PIG:
1877     case EL_DRAGON:
1878       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1879       break;
1880
1881     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1882     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1883     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1884     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1885     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1886     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1887     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1888     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1889     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1890     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1891     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1892     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1893       if (init_game)
1894       {
1895         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1896         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1897         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1898
1899         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1900         {
1901           game.belt_dir[belt_nr] = belt_dir;
1902           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1903         }
1904         else    /* more than one switch -- set it like the first switch */
1905         {
1906           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1907         }
1908       }
1909       break;
1910
1911 #if !USE_BOTH_SWITCHGATE_SWITCHES
1912     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1913       if (init_game)
1914         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1915       break;
1916
1917     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1918       if (init_game)
1919         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1920       break;
1921 #endif
1922
1923     case EL_LIGHT_SWITCH_ACTIVE:
1924       if (init_game)
1925         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1926       break;
1927
1928     case EL_INVISIBLE_STEELWALL:
1929     case EL_INVISIBLE_WALL:
1930     case EL_INVISIBLE_SAND:
1931       if (game.light_time_left > 0 ||
1932           game.lenses_time_left > 0)
1933         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1934       break;
1935
1936     case EL_EMC_MAGIC_BALL:
1937       if (game.ball_state)
1938         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1939       break;
1940
1941     case EL_EMC_MAGIC_BALL_SWITCH:
1942       if (game.ball_state)
1943         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1944       break;
1945
1946     default:
1947       if (IS_CUSTOM_ELEMENT(element))
1948       {
1949         if (CAN_MOVE(element))
1950           InitMovDir(x, y);
1951
1952 #if USE_NEW_CUSTOM_VALUE
1953         if (!element_info[element].use_last_ce_value || init_game)
1954           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1955 #endif
1956       }
1957       else if (IS_GROUP_ELEMENT(element))
1958       {
1959         Feld[x][y] = GetElementFromGroupElement(element);
1960
1961         InitField(x, y, init_game);
1962       }
1963
1964       break;
1965   }
1966
1967   if (!init_game)
1968     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1969 }
1970
1971 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1972 {
1973   InitField(x, y, init_game);
1974
1975   /* not needed to call InitMovDir() -- already done by InitField()! */
1976   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1977       CAN_MOVE(Feld[x][y]))
1978     InitMovDir(x, y);
1979 }
1980
1981 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1982 {
1983   int old_element = Feld[x][y];
1984
1985   InitField(x, y, init_game);
1986
1987   /* not needed to call InitMovDir() -- already done by InitField()! */
1988   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1989       CAN_MOVE(old_element) &&
1990       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1991     InitMovDir(x, y);
1992
1993   /* this case is in fact a combination of not less than three bugs:
1994      first, it calls InitMovDir() for elements that can move, although this is
1995      already done by InitField(); then, it checks the element that was at this
1996      field _before_ the call to InitField() (which can change it); lastly, it
1997      was not called for "mole with direction" elements, which were treated as
1998      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1999   */
2000 }
2001
2002 #if 1
2003
2004 static int get_key_element_from_nr(int key_nr)
2005 {
2006   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2007                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2008                           EL_EM_KEY_1 : EL_KEY_1);
2009
2010   return key_base_element + key_nr;
2011 }
2012
2013 static int get_next_dropped_element(struct PlayerInfo *player)
2014 {
2015   return (player->inventory_size > 0 ?
2016           player->inventory_element[player->inventory_size - 1] :
2017           player->inventory_infinite_element != EL_UNDEFINED ?
2018           player->inventory_infinite_element :
2019           player->dynabombs_left > 0 ?
2020           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2021           EL_UNDEFINED);
2022 }
2023
2024 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2025 {
2026   /* pos >= 0: get element from bottom of the stack;
2027      pos <  0: get element from top of the stack */
2028
2029   if (pos < 0)
2030   {
2031     int min_inventory_size = -pos;
2032     int inventory_pos = player->inventory_size - min_inventory_size;
2033     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2034
2035     return (player->inventory_size >= min_inventory_size ?
2036             player->inventory_element[inventory_pos] :
2037             player->inventory_infinite_element != EL_UNDEFINED ?
2038             player->inventory_infinite_element :
2039             player->dynabombs_left >= min_dynabombs_left ?
2040             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2041             EL_UNDEFINED);
2042   }
2043   else
2044   {
2045     int min_dynabombs_left = pos + 1;
2046     int min_inventory_size = pos + 1 - player->dynabombs_left;
2047     int inventory_pos = pos - player->dynabombs_left;
2048
2049     return (player->inventory_infinite_element != EL_UNDEFINED ?
2050             player->inventory_infinite_element :
2051             player->dynabombs_left >= min_dynabombs_left ?
2052             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2053             player->inventory_size >= min_inventory_size ?
2054             player->inventory_element[inventory_pos] :
2055             EL_UNDEFINED);
2056   }
2057 }
2058
2059 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2060 {
2061   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2062   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2063   int compare_result;
2064
2065   if (gpo1->sort_priority != gpo2->sort_priority)
2066     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2067   else
2068     compare_result = gpo1->nr - gpo2->nr;
2069
2070   return compare_result;
2071 }
2072
2073 void InitGameControlValues()
2074 {
2075   int i;
2076
2077   for (i = 0; game_panel_controls[i].nr != -1; i++)
2078   {
2079     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2080     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2081     struct TextPosInfo *pos = gpc->pos;
2082     int nr = gpc->nr;
2083     int type = gpc->type;
2084
2085     if (nr != i)
2086     {
2087       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2088       Error(ERR_EXIT, "this should not happen -- please debug");
2089     }
2090
2091     /* force update of game controls after initialization */
2092     gpc->value = gpc->last_value = -1;
2093     gpc->frame = gpc->last_frame = -1;
2094     gpc->gfx_frame = -1;
2095
2096     /* determine panel value width for later calculation of alignment */
2097     if (type == TYPE_INTEGER || type == TYPE_STRING)
2098     {
2099       pos->width = pos->size * getFontWidth(pos->font);
2100       pos->height = getFontHeight(pos->font);
2101     }
2102     else if (type == TYPE_ELEMENT)
2103     {
2104       pos->width = pos->size;
2105       pos->height = pos->size;
2106     }
2107
2108     /* fill structure for game panel draw order */
2109     gpo->nr = gpc->nr;
2110     gpo->sort_priority = pos->sort_priority;
2111   }
2112
2113   /* sort game panel controls according to sort_priority and control number */
2114   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2115         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2116 }
2117
2118 void UpdatePlayfieldElementCount()
2119 {
2120   boolean use_element_count = FALSE;
2121   int i, j, x, y;
2122
2123   /* first check if it is needed at all to calculate playfield element count */
2124   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2125     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2126       use_element_count = TRUE;
2127
2128   if (!use_element_count)
2129     return;
2130
2131   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2132     element_info[i].element_count = 0;
2133
2134   SCAN_PLAYFIELD(x, y)
2135   {
2136     element_info[Feld[x][y]].element_count++;
2137   }
2138
2139   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2140     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2141       if (IS_IN_GROUP(j, i))
2142         element_info[EL_GROUP_START + i].element_count +=
2143           element_info[j].element_count;
2144 }
2145
2146 void UpdateGameControlValues()
2147 {
2148   int i, k;
2149   int time = (local_player->LevelSolved ?
2150               local_player->LevelSolved_CountingTime :
2151               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2152               level.native_em_level->lev->time :
2153               level.time == 0 ? TimePlayed : TimeLeft);
2154   int score = (local_player->LevelSolved ?
2155                local_player->LevelSolved_CountingScore :
2156                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2157                level.native_em_level->lev->score :
2158                local_player->score);
2159   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2160               level.native_em_level->lev->required :
2161               local_player->gems_still_needed);
2162   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2163                      level.native_em_level->lev->required > 0 :
2164                      local_player->gems_still_needed > 0 ||
2165                      local_player->sokobanfields_still_needed > 0 ||
2166                      local_player->lights_still_needed > 0);
2167
2168   UpdatePlayfieldElementCount();
2169
2170   /* update game panel control values */
2171
2172   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2173   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2174
2175   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2176   for (i = 0; i < MAX_NUM_KEYS; i++)
2177     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2178   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2179   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2180
2181   if (game.centered_player_nr == -1)
2182   {
2183     for (i = 0; i < MAX_PLAYERS; i++)
2184     {
2185       for (k = 0; k < MAX_NUM_KEYS; k++)
2186       {
2187         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2188         {
2189           if (level.native_em_level->ply[i]->keys & (1 << k))
2190             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2191               get_key_element_from_nr(k);
2192         }
2193         else if (stored_player[i].key[k])
2194           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2195             get_key_element_from_nr(k);
2196       }
2197
2198       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2199         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2200           level.native_em_level->ply[i]->dynamite;
2201       else
2202         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2203           stored_player[i].inventory_size;
2204
2205       if (stored_player[i].num_white_keys > 0)
2206         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2207           EL_DC_KEY_WHITE;
2208
2209       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2210         stored_player[i].num_white_keys;
2211     }
2212   }
2213   else
2214   {
2215     int player_nr = game.centered_player_nr;
2216
2217     for (k = 0; k < MAX_NUM_KEYS; k++)
2218     {
2219       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2220       {
2221         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2222           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2223             get_key_element_from_nr(k);
2224       }
2225       else if (stored_player[player_nr].key[k])
2226         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2227           get_key_element_from_nr(k);
2228     }
2229
2230     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2231       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2232         level.native_em_level->ply[player_nr]->dynamite;
2233     else
2234       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2235         stored_player[player_nr].inventory_size;
2236
2237     if (stored_player[player_nr].num_white_keys > 0)
2238       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2239
2240     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2241       stored_player[player_nr].num_white_keys;
2242   }
2243
2244   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2245   {
2246     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2247       get_inventory_element_from_pos(local_player, i);
2248     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2249       get_inventory_element_from_pos(local_player, -i - 1);
2250   }
2251
2252   game_panel_controls[GAME_PANEL_SCORE].value = score;
2253   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2254
2255   game_panel_controls[GAME_PANEL_TIME].value = time;
2256
2257   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2258   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2259   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2260
2261   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2262     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2263      EL_EMPTY);
2264   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2265     local_player->shield_normal_time_left;
2266   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2267     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2268      EL_EMPTY);
2269   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2270     local_player->shield_deadly_time_left;
2271
2272   game_panel_controls[GAME_PANEL_EXIT].value =
2273     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2274
2275   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2276     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2277   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2278     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2279      EL_EMC_MAGIC_BALL_SWITCH);
2280
2281   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2282     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2283   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2284     game.light_time_left;
2285
2286   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2287     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2288   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2289     game.timegate_time_left;
2290
2291   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2292     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2293
2294   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2295     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2296   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2297     game.lenses_time_left;
2298
2299   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2300     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2301   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2302     game.magnify_time_left;
2303
2304   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2305     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2306      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2307      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2308      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2309      EL_BALLOON_SWITCH_NONE);
2310
2311   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2312     local_player->dynabomb_count;
2313   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2314     local_player->dynabomb_size;
2315   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2316     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2317
2318   game_panel_controls[GAME_PANEL_PENGUINS].value =
2319     local_player->friends_still_needed;
2320
2321   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2322     local_player->sokobanfields_still_needed;
2323   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2324     local_player->sokobanfields_still_needed;
2325
2326   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2327     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2328
2329   for (i = 0; i < NUM_BELTS; i++)
2330   {
2331     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2332       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2333        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2334     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2335       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2336   }
2337
2338   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2339     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2340   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2341     game.magic_wall_time_left;
2342
2343 #if USE_PLAYER_GRAVITY
2344   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2345     local_player->gravity;
2346 #else
2347   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2348 #endif
2349
2350   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2351     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2352
2353   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2354     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2355       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2356        game.panel.element[i].id : EL_UNDEFINED);
2357
2358   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2359     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2360       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2361        element_info[game.panel.element_count[i].id].element_count : 0);
2362
2363   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2364     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2365       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2366        element_info[game.panel.ce_score[i].id].collect_score : 0);
2367
2368   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2369     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2370       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2371        element_info[game.panel.ce_score_element[i].id].collect_score :
2372        EL_UNDEFINED);
2373
2374   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2375   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2376   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2377
2378   /* update game panel control frames */
2379
2380   for (i = 0; game_panel_controls[i].nr != -1; i++)
2381   {
2382     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2383
2384     if (gpc->type == TYPE_ELEMENT)
2385     {
2386       int last_anim_random_frame = gfx.anim_random_frame;
2387       int element = gpc->value;
2388       int graphic = el2panelimg(element);
2389
2390       if (gpc->value != gpc->last_value)
2391       {
2392         gpc->gfx_frame = 0;
2393         gpc->gfx_random = INIT_GFX_RANDOM();
2394       }
2395       else
2396       {
2397         gpc->gfx_frame++;
2398
2399         if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2400             IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2401           gpc->gfx_random = INIT_GFX_RANDOM();
2402       }
2403
2404       if (ANIM_MODE(graphic) == ANIM_RANDOM)
2405         gfx.anim_random_frame = gpc->gfx_random;
2406
2407       if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2408         gpc->gfx_frame = element_info[element].collect_score;
2409
2410       gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2411                                             gpc->gfx_frame);
2412
2413       if (ANIM_MODE(graphic) == ANIM_RANDOM)
2414         gfx.anim_random_frame = last_anim_random_frame;
2415     }
2416   }
2417 }
2418
2419 void DisplayGameControlValues()
2420 {
2421   boolean redraw_panel = FALSE;
2422   int i;
2423
2424   for (i = 0; game_panel_controls[i].nr != -1; i++)
2425   {
2426     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2427
2428     if (PANEL_DEACTIVATED(gpc->pos))
2429       continue;
2430
2431     if (gpc->value == gpc->last_value &&
2432         gpc->frame == gpc->last_frame)
2433       continue;
2434
2435     redraw_panel = TRUE;
2436   }
2437
2438   if (!redraw_panel)
2439     return;
2440
2441   /* copy default game door content to main double buffer */
2442   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2443              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2444
2445   /* redraw game control buttons */
2446 #if 1
2447   RedrawGameButtons();
2448 #else
2449   UnmapGameButtons();
2450   MapGameButtons();
2451 #endif
2452
2453   game_status = GAME_MODE_PSEUDO_PANEL;
2454
2455 #if 1
2456   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2457 #else
2458   for (i = 0; game_panel_controls[i].nr != -1; i++)
2459 #endif
2460   {
2461 #if 1
2462     int nr = game_panel_order[i].nr;
2463     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2464 #else
2465     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2466     int nr = gpc->nr;
2467 #endif
2468     struct TextPosInfo *pos = gpc->pos;
2469     int type = gpc->type;
2470     int value = gpc->value;
2471     int frame = gpc->frame;
2472 #if 0
2473     int last_value = gpc->last_value;
2474     int last_frame = gpc->last_frame;
2475 #endif
2476     int size = pos->size;
2477     int font = pos->font;
2478     boolean draw_masked = pos->draw_masked;
2479     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2480
2481     if (PANEL_DEACTIVATED(pos))
2482       continue;
2483
2484 #if 0
2485     if (value == last_value && frame == last_frame)
2486       continue;
2487 #endif
2488
2489     gpc->last_value = value;
2490     gpc->last_frame = frame;
2491
2492 #if 0
2493     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2494 #endif
2495
2496     if (type == TYPE_INTEGER)
2497     {
2498       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2499           nr == GAME_PANEL_TIME)
2500       {
2501         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2502
2503         if (use_dynamic_size)           /* use dynamic number of digits */
2504         {
2505           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2506           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2507           int size2 = size1 + 1;
2508           int font1 = pos->font;
2509           int font2 = pos->font_alt;
2510
2511           size = (value < value_change ? size1 : size2);
2512           font = (value < value_change ? font1 : font2);
2513
2514 #if 0
2515           /* clear background if value just changed its size (dynamic digits) */
2516           if ((last_value < value_change) != (value < value_change))
2517           {
2518             int width1 = size1 * getFontWidth(font1);
2519             int width2 = size2 * getFontWidth(font2);
2520             int max_width = MAX(width1, width2);
2521             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2522
2523             pos->width = max_width;
2524
2525             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2526                                        max_width, max_height);
2527           }
2528 #endif
2529         }
2530       }
2531
2532 #if 1
2533       /* correct text size if "digits" is zero or less */
2534       if (size <= 0)
2535         size = strlen(int2str(value, size));
2536
2537       /* dynamically correct text alignment */
2538       pos->width = size * getFontWidth(font);
2539 #endif
2540
2541       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2542                   int2str(value, size), font, mask_mode);
2543     }
2544     else if (type == TYPE_ELEMENT)
2545     {
2546       int element, graphic;
2547       Bitmap *src_bitmap;
2548       int src_x, src_y;
2549       int width, height;
2550       int dst_x = PANEL_XPOS(pos);
2551       int dst_y = PANEL_YPOS(pos);
2552
2553 #if 1
2554       if (value != EL_UNDEFINED && value != EL_EMPTY)
2555       {
2556         element = value;
2557         graphic = el2panelimg(value);
2558
2559         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2560
2561 #if 1
2562         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2563           size = TILESIZE;
2564 #endif
2565
2566         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2567                               &src_x, &src_y);
2568
2569         width  = graphic_info[graphic].width  * size / TILESIZE;
2570         height = graphic_info[graphic].height * size / TILESIZE;
2571
2572         if (draw_masked)
2573         {
2574           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2575                         dst_x - src_x, dst_y - src_y);
2576           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2577                            dst_x, dst_y);
2578         }
2579         else
2580         {
2581           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2582                      dst_x, dst_y);
2583         }
2584       }
2585 #else
2586       if (value == EL_UNDEFINED || value == EL_EMPTY)
2587       {
2588         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2589         graphic = el2panelimg(element);
2590
2591         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2592         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2593         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2594       }
2595       else
2596       {
2597         element = value;
2598         graphic = el2panelimg(value);
2599
2600         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2601       }
2602
2603       width  = graphic_info[graphic].width  * size / TILESIZE;
2604       height = graphic_info[graphic].height * size / TILESIZE;
2605
2606       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2607 #endif
2608     }
2609     else if (type == TYPE_STRING)
2610     {
2611       boolean active = (value != 0);
2612       char *state_normal = "off";
2613       char *state_active = "on";
2614       char *state = (active ? state_active : state_normal);
2615       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2616                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2617                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2618                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2619
2620       if (nr == GAME_PANEL_GRAVITY_STATE)
2621       {
2622         int font1 = pos->font;          /* (used for normal state) */
2623         int font2 = pos->font_alt;      /* (used for active state) */
2624 #if 0
2625         int size1 = strlen(state_normal);
2626         int size2 = strlen(state_active);
2627         int width1 = size1 * getFontWidth(font1);
2628         int width2 = size2 * getFontWidth(font2);
2629         int max_width = MAX(width1, width2);
2630         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2631
2632         pos->width = max_width;
2633
2634         /* clear background for values that may have changed its size */
2635         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2636                                    max_width, max_height);
2637 #endif
2638
2639         font = (active ? font2 : font1);
2640       }
2641
2642       if (s != NULL)
2643       {
2644         char *s_cut;
2645
2646 #if 1
2647         if (size <= 0)
2648         {
2649           /* don't truncate output if "chars" is zero or less */
2650           size = strlen(s);
2651
2652           /* dynamically correct text alignment */
2653           pos->width = size * getFontWidth(font);
2654         }
2655 #endif
2656
2657         s_cut = getStringCopyN(s, size);
2658
2659         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2660                     s_cut, font, mask_mode);
2661
2662         free(s_cut);
2663       }
2664     }
2665
2666     redraw_mask |= REDRAW_DOOR_1;
2667   }
2668
2669   game_status = GAME_MODE_PLAYING;
2670 }
2671
2672 void UpdateAndDisplayGameControlValues()
2673 {
2674   if (tape.warp_forward)
2675     return;
2676
2677   UpdateGameControlValues();
2678   DisplayGameControlValues();
2679 }
2680
2681 void DrawGameValue_Emeralds(int value)
2682 {
2683   struct TextPosInfo *pos = &game.panel.gems;
2684 #if 1
2685   int font_nr = pos->font;
2686 #else
2687   int font_nr = FONT_TEXT_2;
2688 #endif
2689   int font_width = getFontWidth(font_nr);
2690   int chars = pos->size;
2691
2692 #if 1
2693   return;       /* !!! USE NEW STUFF !!! */
2694 #endif
2695
2696   if (PANEL_DEACTIVATED(pos))
2697     return;
2698
2699   pos->width = chars * font_width;
2700
2701   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2702 }
2703
2704 void DrawGameValue_Dynamite(int value)
2705 {
2706   struct TextPosInfo *pos = &game.panel.inventory_count;
2707 #if 1
2708   int font_nr = pos->font;
2709 #else
2710   int font_nr = FONT_TEXT_2;
2711 #endif
2712   int font_width = getFontWidth(font_nr);
2713   int chars = pos->size;
2714
2715 #if 1
2716   return;       /* !!! USE NEW STUFF !!! */
2717 #endif
2718
2719   if (PANEL_DEACTIVATED(pos))
2720     return;
2721
2722   pos->width = chars * font_width;
2723
2724   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2725 }
2726
2727 void DrawGameValue_Score(int value)
2728 {
2729   struct TextPosInfo *pos = &game.panel.score;
2730 #if 1
2731   int font_nr = pos->font;
2732 #else
2733   int font_nr = FONT_TEXT_2;
2734 #endif
2735   int font_width = getFontWidth(font_nr);
2736   int chars = pos->size;
2737
2738 #if 1
2739   return;       /* !!! USE NEW STUFF !!! */
2740 #endif
2741
2742   if (PANEL_DEACTIVATED(pos))
2743     return;
2744
2745   pos->width = chars * font_width;
2746
2747   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2748 }
2749
2750 void DrawGameValue_Time(int value)
2751 {
2752   struct TextPosInfo *pos = &game.panel.time;
2753   static int last_value = -1;
2754   int chars1 = 3;
2755   int chars2 = 4;
2756   int chars = pos->size;
2757 #if 1
2758   int font1_nr = pos->font;
2759   int font2_nr = pos->font_alt;
2760 #else
2761   int font1_nr = FONT_TEXT_2;
2762   int font2_nr = FONT_TEXT_1;
2763 #endif
2764   int font_nr = font1_nr;
2765   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2766
2767 #if 1
2768   return;       /* !!! USE NEW STUFF !!! */
2769 #endif
2770
2771   if (PANEL_DEACTIVATED(pos))
2772     return;
2773
2774   if (use_dynamic_chars)                /* use dynamic number of chars */
2775   {
2776     chars   = (value < 1000 ? chars1   : chars2);
2777     font_nr = (value < 1000 ? font1_nr : font2_nr);
2778   }
2779
2780   /* clear background if value just changed its size (dynamic chars only) */
2781   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2782   {
2783     int width1 = chars1 * getFontWidth(font1_nr);
2784     int width2 = chars2 * getFontWidth(font2_nr);
2785     int max_width = MAX(width1, width2);
2786     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2787
2788     pos->width = max_width;
2789
2790     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2791                                max_width, max_height);
2792   }
2793
2794   pos->width = chars * getFontWidth(font_nr);
2795
2796   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2797
2798   last_value = value;
2799 }
2800
2801 void DrawGameValue_Level(int value)
2802 {
2803   struct TextPosInfo *pos = &game.panel.level_number;
2804   int chars1 = 2;
2805   int chars2 = 3;
2806   int chars = pos->size;
2807 #if 1
2808   int font1_nr = pos->font;
2809   int font2_nr = pos->font_alt;
2810 #else
2811   int font1_nr = FONT_TEXT_2;
2812   int font2_nr = FONT_TEXT_1;
2813 #endif
2814   int font_nr = font1_nr;
2815   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2816
2817 #if 1
2818   return;       /* !!! USE NEW STUFF !!! */
2819 #endif
2820
2821   if (PANEL_DEACTIVATED(pos))
2822     return;
2823
2824   if (use_dynamic_chars)                /* use dynamic number of chars */
2825   {
2826     chars   = (level_nr < 100 ? chars1   : chars2);
2827     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2828   }
2829
2830   pos->width = chars * getFontWidth(font_nr);
2831
2832   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2833 }
2834
2835 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2836 {
2837 #if 0
2838   struct TextPosInfo *pos = &game.panel.keys;
2839 #endif
2840 #if 0
2841   int base_key_graphic = EL_KEY_1;
2842 #endif
2843   int i;
2844
2845 #if 1
2846   return;       /* !!! USE NEW STUFF !!! */
2847 #endif
2848
2849 #if 0
2850   if (PANEL_DEACTIVATED(pos))
2851     return;
2852 #endif
2853
2854 #if 0
2855   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2856     base_key_graphic = EL_EM_KEY_1;
2857 #endif
2858
2859 #if 0
2860   pos->width = 4 * MINI_TILEX;
2861 #endif
2862
2863 #if 1
2864   for (i = 0; i < MAX_NUM_KEYS; i++)
2865 #else
2866   /* currently only 4 of 8 possible keys are displayed */
2867   for (i = 0; i < STD_NUM_KEYS; i++)
2868 #endif
2869   {
2870 #if 1
2871     struct TextPosInfo *pos = &game.panel.key[i];
2872 #endif
2873     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2874     int src_y = DOOR_GFX_PAGEY1 + 123;
2875 #if 1
2876     int dst_x = PANEL_XPOS(pos);
2877     int dst_y = PANEL_YPOS(pos);
2878 #else
2879     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2880     int dst_y = PANEL_YPOS(pos);
2881 #endif
2882
2883 #if 1
2884     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2885                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2886                    EL_KEY_1) + i;
2887     int graphic = el2edimg(element);
2888 #endif
2889
2890 #if 1
2891     if (PANEL_DEACTIVATED(pos))
2892       continue;
2893 #endif
2894
2895 #if 0
2896     /* masked blit with tiles from half-size scaled bitmap does not work yet
2897        (no mask bitmap created for these sizes after loading and scaling) --
2898        solution: load without creating mask, scale, then create final mask */
2899
2900     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2901                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2902
2903     if (key[i])
2904     {
2905 #if 0
2906       int graphic = el2edimg(base_key_graphic + i);
2907 #endif
2908       Bitmap *src_bitmap;
2909       int src_x, src_y;
2910
2911       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2912
2913       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2914                     dst_x - src_x, dst_y - src_y);
2915       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2916                        dst_x, dst_y);
2917     }
2918 #else
2919 #if 1
2920     if (key[i])
2921       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2922     else
2923       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2924                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2925 #else
2926     if (key[i])
2927       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2928     else
2929       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2930                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2931 #endif
2932 #endif
2933   }
2934 }
2935
2936 #else
2937
2938 void DrawGameValue_Emeralds(int value)
2939 {
2940   int font_nr = FONT_TEXT_2;
2941   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2942
2943   if (PANEL_DEACTIVATED(game.panel.gems))
2944     return;
2945
2946   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2947 }
2948
2949 void DrawGameValue_Dynamite(int value)
2950 {
2951   int font_nr = FONT_TEXT_2;
2952   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2953
2954   if (PANEL_DEACTIVATED(game.panel.inventory_count))
2955     return;
2956
2957   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2958 }
2959
2960 void DrawGameValue_Score(int value)
2961 {
2962   int font_nr = FONT_TEXT_2;
2963   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2964
2965   if (PANEL_DEACTIVATED(game.panel.score))
2966     return;
2967
2968   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2969 }
2970
2971 void DrawGameValue_Time(int value)
2972 {
2973   int font1_nr = FONT_TEXT_2;
2974 #if 1
2975   int font2_nr = FONT_TEXT_1;
2976 #else
2977   int font2_nr = FONT_LEVEL_NUMBER;
2978 #endif
2979   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2980   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2981
2982   if (PANEL_DEACTIVATED(game.panel.time))
2983     return;
2984
2985   /* clear background if value just changed its size */
2986   if (value == 999 || value == 1000)
2987     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2988
2989   if (value < 1000)
2990     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2991   else
2992     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2993 }
2994
2995 void DrawGameValue_Level(int value)
2996 {
2997   int font1_nr = FONT_TEXT_2;
2998 #if 1
2999   int font2_nr = FONT_TEXT_1;
3000 #else
3001   int font2_nr = FONT_LEVEL_NUMBER;
3002 #endif
3003
3004   if (PANEL_DEACTIVATED(game.panel.level))
3005     return;
3006
3007   if (level_nr < 100)
3008     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3009   else
3010     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3011 }
3012
3013 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3014 {
3015   int base_key_graphic = EL_KEY_1;
3016   int i;
3017
3018   if (PANEL_DEACTIVATED(game.panel.keys))
3019     return;
3020
3021   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3022     base_key_graphic = EL_EM_KEY_1;
3023
3024   /* currently only 4 of 8 possible keys are displayed */
3025   for (i = 0; i < STD_NUM_KEYS; i++)
3026   {
3027     int x = XX_KEYS + i * MINI_TILEX;
3028     int y = YY_KEYS;
3029
3030     if (key[i])
3031       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3032     else
3033       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3034                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3035   }
3036 }
3037
3038 #endif
3039
3040 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3041                        int key_bits)
3042 {
3043   int key[MAX_NUM_KEYS];
3044   int i;
3045
3046   /* prevent EM engine from updating time/score values parallel to GameWon() */
3047   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3048       local_player->LevelSolved)
3049     return;
3050
3051   for (i = 0; i < MAX_NUM_KEYS; i++)
3052     key[i] = key_bits & (1 << i);
3053
3054   DrawGameValue_Level(level_nr);
3055
3056   DrawGameValue_Emeralds(emeralds);
3057   DrawGameValue_Dynamite(dynamite);
3058   DrawGameValue_Score(score);
3059   DrawGameValue_Time(time);
3060
3061   DrawGameValue_Keys(key);
3062 }
3063
3064 void UpdateGameDoorValues()
3065 {
3066   UpdateGameControlValues();
3067 }
3068
3069 void DrawGameDoorValues()
3070 {
3071   DisplayGameControlValues();
3072 }
3073
3074 void DrawGameDoorValues_OLD()
3075 {
3076   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3077   int dynamite_value = 0;
3078   int score_value = (local_player->LevelSolved ? local_player->score_final :
3079                      local_player->score);
3080   int gems_value = local_player->gems_still_needed;
3081   int key_bits = 0;
3082   int i, j;
3083
3084   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3085   {
3086     DrawGameDoorValues_EM();
3087
3088     return;
3089   }
3090
3091   if (game.centered_player_nr == -1)
3092   {
3093     for (i = 0; i < MAX_PLAYERS; i++)
3094     {
3095       for (j = 0; j < MAX_NUM_KEYS; j++)
3096         if (stored_player[i].key[j])
3097           key_bits |= (1 << j);
3098
3099       dynamite_value += stored_player[i].inventory_size;
3100     }
3101   }
3102   else
3103   {
3104     int player_nr = game.centered_player_nr;
3105
3106     for (i = 0; i < MAX_NUM_KEYS; i++)
3107       if (stored_player[player_nr].key[i])
3108         key_bits |= (1 << i);
3109
3110     dynamite_value = stored_player[player_nr].inventory_size;
3111   }
3112
3113   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3114                     key_bits);
3115 }
3116
3117
3118 /*
3119   =============================================================================
3120   InitGameEngine()
3121   -----------------------------------------------------------------------------
3122   initialize game engine due to level / tape version number
3123   =============================================================================
3124 */
3125
3126 static void InitGameEngine()
3127 {
3128   int i, j, k, l, x, y;
3129
3130   /* set game engine from tape file when re-playing, else from level file */
3131   game.engine_version = (tape.playing ? tape.engine_version :
3132                          level.game_version);
3133
3134   /* ---------------------------------------------------------------------- */
3135   /* set flags for bugs and changes according to active game engine version */
3136   /* ---------------------------------------------------------------------- */
3137
3138   /*
3139     Summary of bugfix/change:
3140     Fixed handling for custom elements that change when pushed by the player.
3141
3142     Fixed/changed in version:
3143     3.1.0
3144
3145     Description:
3146     Before 3.1.0, custom elements that "change when pushing" changed directly
3147     after the player started pushing them (until then handled in "DigField()").
3148     Since 3.1.0, these custom elements are not changed until the "pushing"
3149     move of the element is finished (now handled in "ContinueMoving()").
3150
3151     Affected levels/tapes:
3152     The first condition is generally needed for all levels/tapes before version
3153     3.1.0, which might use the old behaviour before it was changed; known tapes
3154     that are affected are some tapes from the level set "Walpurgis Gardens" by
3155     Jamie Cullen.
3156     The second condition is an exception from the above case and is needed for
3157     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3158     above (including some development versions of 3.1.0), but before it was
3159     known that this change would break tapes like the above and was fixed in
3160     3.1.1, so that the changed behaviour was active although the engine version
3161     while recording maybe was before 3.1.0. There is at least one tape that is
3162     affected by this exception, which is the tape for the one-level set "Bug
3163     Machine" by Juergen Bonhagen.
3164   */
3165
3166   game.use_change_when_pushing_bug =
3167     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3168      !(tape.playing &&
3169        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3170        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3171
3172   /*
3173     Summary of bugfix/change:
3174     Fixed handling for blocking the field the player leaves when moving.
3175
3176     Fixed/changed in version:
3177     3.1.1
3178
3179     Description:
3180     Before 3.1.1, when "block last field when moving" was enabled, the field
3181     the player is leaving when moving was blocked for the time of the move,
3182     and was directly unblocked afterwards. This resulted in the last field
3183     being blocked for exactly one less than the number of frames of one player
3184     move. Additionally, even when blocking was disabled, the last field was
3185     blocked for exactly one frame.
3186     Since 3.1.1, due to changes in player movement handling, the last field
3187     is not blocked at all when blocking is disabled. When blocking is enabled,
3188     the last field is blocked for exactly the number of frames of one player
3189     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3190     last field is blocked for exactly one more than the number of frames of
3191     one player move.
3192
3193     Affected levels/tapes:
3194     (!!! yet to be determined -- probably many !!!)
3195   */
3196
3197   game.use_block_last_field_bug =
3198     (game.engine_version < VERSION_IDENT(3,1,1,0));
3199
3200   /*
3201     Summary of bugfix/change:
3202     Changed behaviour of CE changes with multiple changes per single frame.
3203
3204     Fixed/changed in version:
3205     3.2.0-6
3206
3207     Description:
3208     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3209     This resulted in race conditions where CEs seem to behave strange in some
3210     situations (where triggered CE changes were just skipped because there was
3211     already a CE change on that tile in the playfield in that engine frame).
3212     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3213     (The number of changes per frame must be limited in any case, because else
3214     it is easily possible to define CE changes that would result in an infinite
3215     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3216     should be set large enough so that it would only be reached in cases where
3217     the corresponding CE change conditions run into a loop. Therefore, it seems
3218     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3219     maximal number of change pages for custom elements.)
3220
3221     Affected levels/tapes:
3222     Probably many.
3223   */
3224
3225 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3226   game.max_num_changes_per_frame = 1;
3227 #else
3228   game.max_num_changes_per_frame =
3229     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3230 #endif
3231
3232   /* ---------------------------------------------------------------------- */
3233
3234   /* default scan direction: scan playfield from top/left to bottom/right */
3235   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3236
3237   /* dynamically adjust element properties according to game engine version */
3238   InitElementPropertiesEngine(game.engine_version);
3239
3240 #if 0
3241   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3242   printf("          tape version == %06d [%s] [file: %06d]\n",
3243          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3244          tape.file_version);
3245   printf("       => game.engine_version == %06d\n", game.engine_version);
3246 #endif
3247
3248   /* ---------- initialize player's initial move delay --------------------- */
3249
3250   /* dynamically adjust player properties according to level information */
3251   for (i = 0; i < MAX_PLAYERS; i++)
3252     game.initial_move_delay_value[i] =
3253       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3254
3255   /* dynamically adjust player properties according to game engine version */
3256   for (i = 0; i < MAX_PLAYERS; i++)
3257     game.initial_move_delay[i] =
3258       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3259        game.initial_move_delay_value[i] : 0);
3260
3261   /* ---------- initialize player's initial push delay --------------------- */
3262
3263   /* dynamically adjust player properties according to game engine version */
3264   game.initial_push_delay_value =
3265     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3266
3267   /* ---------- initialize changing elements ------------------------------- */
3268
3269   /* initialize changing elements information */
3270   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3271   {
3272     struct ElementInfo *ei = &element_info[i];
3273
3274     /* this pointer might have been changed in the level editor */
3275     ei->change = &ei->change_page[0];
3276
3277     if (!IS_CUSTOM_ELEMENT(i))
3278     {
3279       ei->change->target_element = EL_EMPTY_SPACE;
3280       ei->change->delay_fixed = 0;
3281       ei->change->delay_random = 0;
3282       ei->change->delay_frames = 1;
3283     }
3284
3285     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3286     {
3287       ei->has_change_event[j] = FALSE;
3288
3289       ei->event_page_nr[j] = 0;
3290       ei->event_page[j] = &ei->change_page[0];
3291     }
3292   }
3293
3294   /* add changing elements from pre-defined list */
3295   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3296   {
3297     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3298     struct ElementInfo *ei = &element_info[ch_delay->element];
3299
3300     ei->change->target_element       = ch_delay->target_element;
3301     ei->change->delay_fixed          = ch_delay->change_delay;
3302
3303     ei->change->pre_change_function  = ch_delay->pre_change_function;
3304     ei->change->change_function      = ch_delay->change_function;
3305     ei->change->post_change_function = ch_delay->post_change_function;
3306
3307     ei->change->can_change = TRUE;
3308     ei->change->can_change_or_has_action = TRUE;
3309
3310     ei->has_change_event[CE_DELAY] = TRUE;
3311
3312     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3313     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3314   }
3315
3316   /* ---------- initialize internal run-time variables ------------- */
3317
3318   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3319   {
3320     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3321
3322     for (j = 0; j < ei->num_change_pages; j++)
3323     {
3324       ei->change_page[j].can_change_or_has_action =
3325         (ei->change_page[j].can_change |
3326          ei->change_page[j].has_action);
3327     }
3328   }
3329
3330   /* add change events from custom element configuration */
3331   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3332   {
3333     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3334
3335     for (j = 0; j < ei->num_change_pages; j++)
3336     {
3337       if (!ei->change_page[j].can_change_or_has_action)
3338         continue;
3339
3340       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3341       {
3342         /* only add event page for the first page found with this event */
3343         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3344         {
3345           ei->has_change_event[k] = TRUE;
3346
3347           ei->event_page_nr[k] = j;
3348           ei->event_page[k] = &ei->change_page[j];
3349         }
3350       }
3351     }
3352   }
3353
3354   /* ---------- initialize run-time trigger player and element ------------- */
3355
3356   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3357   {
3358     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3359
3360     for (j = 0; j < ei->num_change_pages; j++)
3361     {
3362       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3363       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3364       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
3365       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3366       ei->change_page[j].actual_trigger_ce_value = 0;
3367       ei->change_page[j].actual_trigger_ce_score = 0;
3368     }
3369   }
3370
3371   /* ---------- initialize trigger events ---------------------------------- */
3372
3373   /* initialize trigger events information */
3374   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3375     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3376       trigger_events[i][j] = FALSE;
3377
3378   /* add trigger events from element change event properties */
3379   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3380   {
3381     struct ElementInfo *ei = &element_info[i];
3382
3383     for (j = 0; j < ei->num_change_pages; j++)
3384     {
3385       if (!ei->change_page[j].can_change_or_has_action)
3386         continue;
3387
3388       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3389       {
3390         int trigger_element = ei->change_page[j].trigger_element;
3391
3392         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3393         {
3394           if (ei->change_page[j].has_event[k])
3395           {
3396             if (IS_GROUP_ELEMENT(trigger_element))
3397             {
3398               struct ElementGroupInfo *group =
3399                 element_info[trigger_element].group;
3400
3401               for (l = 0; l < group->num_elements_resolved; l++)
3402                 trigger_events[group->element_resolved[l]][k] = TRUE;
3403             }
3404             else if (trigger_element == EL_ANY_ELEMENT)
3405               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3406                 trigger_events[l][k] = TRUE;
3407             else
3408               trigger_events[trigger_element][k] = TRUE;
3409           }
3410         }
3411       }
3412     }
3413   }
3414
3415   /* ---------- initialize push delay -------------------------------------- */
3416
3417   /* initialize push delay values to default */
3418   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3419   {
3420     if (!IS_CUSTOM_ELEMENT(i))
3421     {
3422       /* set default push delay values (corrected since version 3.0.7-1) */
3423       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3424       {
3425         element_info[i].push_delay_fixed = 2;
3426         element_info[i].push_delay_random = 8;
3427       }
3428       else
3429       {
3430         element_info[i].push_delay_fixed = 8;
3431         element_info[i].push_delay_random = 8;
3432       }
3433     }
3434   }
3435
3436   /* set push delay value for certain elements from pre-defined list */
3437   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3438   {
3439     int e = push_delay_list[i].element;
3440
3441     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3442     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3443   }
3444
3445   /* set push delay value for Supaplex elements for newer engine versions */
3446   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3447   {
3448     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3449     {
3450       if (IS_SP_ELEMENT(i))
3451       {
3452         /* set SP push delay to just enough to push under a falling zonk */
3453         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3454
3455         element_info[i].push_delay_fixed  = delay;
3456         element_info[i].push_delay_random = 0;
3457       }
3458     }
3459   }
3460
3461   /* ---------- initialize move stepsize ----------------------------------- */
3462
3463   /* initialize move stepsize values to default */
3464   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3465     if (!IS_CUSTOM_ELEMENT(i))
3466       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3467
3468   /* set move stepsize value for certain elements from pre-defined list */
3469   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3470   {
3471     int e = move_stepsize_list[i].element;
3472
3473     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3474   }
3475
3476   /* ---------- initialize collect score ----------------------------------- */
3477
3478   /* initialize collect score values for custom elements from initial value */
3479   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3480     if (IS_CUSTOM_ELEMENT(i))
3481       element_info[i].collect_score = element_info[i].collect_score_initial;
3482
3483   /* ---------- initialize collect count ----------------------------------- */
3484
3485   /* initialize collect count values for non-custom elements */
3486   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3487     if (!IS_CUSTOM_ELEMENT(i))
3488       element_info[i].collect_count_initial = 0;
3489
3490   /* add collect count values for all elements from pre-defined list */
3491   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3492     element_info[collect_count_list[i].element].collect_count_initial =
3493       collect_count_list[i].count;
3494
3495   /* ---------- initialize access direction -------------------------------- */
3496
3497   /* initialize access direction values to default (access from every side) */
3498   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3499     if (!IS_CUSTOM_ELEMENT(i))
3500       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3501
3502   /* set access direction value for certain elements from pre-defined list */
3503   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3504     element_info[access_direction_list[i].element].access_direction =
3505       access_direction_list[i].direction;
3506
3507   /* ---------- initialize explosion content ------------------------------- */
3508   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3509   {
3510     if (IS_CUSTOM_ELEMENT(i))
3511       continue;
3512
3513     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3514     {
3515       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3516
3517       element_info[i].content.e[x][y] =
3518         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3519          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3520          i == EL_PLAYER_3 ? EL_EMERALD :
3521          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3522          i == EL_MOLE ? EL_EMERALD_RED :
3523          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3524          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3525          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3526          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3527          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3528          i == EL_WALL_EMERALD ? EL_EMERALD :
3529          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3530          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3531          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3532          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3533          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3534          i == EL_WALL_PEARL ? EL_PEARL :
3535          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3536          EL_EMPTY);
3537     }
3538   }
3539
3540   /* ---------- initialize recursion detection ------------------------------ */
3541   recursion_loop_depth = 0;
3542   recursion_loop_detected = FALSE;
3543   recursion_loop_element = EL_UNDEFINED;
3544
3545   /* ---------- initialize graphics engine ---------------------------------- */
3546   game.scroll_delay_value =
3547     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3548      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3549   game.scroll_delay_value =
3550     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3551 }
3552
3553 int get_num_special_action(int element, int action_first, int action_last)
3554 {
3555   int num_special_action = 0;
3556   int i, j;
3557
3558   for (i = action_first; i <= action_last; i++)
3559   {
3560     boolean found = FALSE;
3561
3562     for (j = 0; j < NUM_DIRECTIONS; j++)
3563       if (el_act_dir2img(element, i, j) !=
3564           el_act_dir2img(element, ACTION_DEFAULT, j))
3565         found = TRUE;
3566
3567     if (found)
3568       num_special_action++;
3569     else
3570       break;
3571   }
3572
3573   return num_special_action;
3574 }
3575
3576
3577 /*
3578   =============================================================================
3579   InitGame()
3580   -----------------------------------------------------------------------------
3581   initialize and start new game
3582   =============================================================================
3583 */
3584
3585 void InitGame()
3586 {
3587   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3588   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3589   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3590 #if 0
3591   boolean do_fading = (game_status == GAME_MODE_MAIN);
3592 #endif
3593   int i, j, x, y;
3594
3595   game_status = GAME_MODE_PLAYING;
3596
3597   InitGameEngine();
3598   InitGameControlValues();
3599
3600   /* don't play tapes over network */
3601   network_playing = (options.network && !tape.playing);
3602
3603   for (i = 0; i < MAX_PLAYERS; i++)
3604   {
3605     struct PlayerInfo *player = &stored_player[i];
3606
3607     player->index_nr = i;
3608     player->index_bit = (1 << i);
3609     player->element_nr = EL_PLAYER_1 + i;
3610
3611     player->present = FALSE;
3612     player->active = FALSE;
3613     player->killed = FALSE;
3614
3615     player->action = 0;
3616     player->effective_action = 0;
3617     player->programmed_action = 0;
3618
3619     player->score = 0;
3620     player->score_final = 0;
3621
3622     player->gems_still_needed = level.gems_needed;
3623     player->sokobanfields_still_needed = 0;
3624     player->lights_still_needed = 0;
3625     player->friends_still_needed = 0;
3626
3627     for (j = 0; j < MAX_NUM_KEYS; j++)
3628       player->key[j] = FALSE;
3629
3630     player->num_white_keys = 0;
3631
3632     player->dynabomb_count = 0;
3633     player->dynabomb_size = 1;
3634     player->dynabombs_left = 0;
3635     player->dynabomb_xl = FALSE;
3636
3637     player->MovDir = MV_NONE;
3638     player->MovPos = 0;
3639     player->GfxPos = 0;
3640     player->GfxDir = MV_NONE;
3641     player->GfxAction = ACTION_DEFAULT;
3642     player->Frame = 0;
3643     player->StepFrame = 0;
3644
3645     player->use_murphy = FALSE;
3646     player->artwork_element =
3647       (level.use_artwork_element[i] ? level.artwork_element[i] :
3648        player->element_nr);
3649
3650     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3651     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3652
3653     player->gravity = level.initial_player_gravity[i];
3654
3655     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3656
3657     player->actual_frame_counter = 0;
3658
3659     player->step_counter = 0;
3660
3661     player->last_move_dir = MV_NONE;
3662
3663     player->is_active = FALSE;
3664
3665     player->is_waiting = FALSE;
3666     player->is_moving = FALSE;
3667     player->is_auto_moving = FALSE;
3668     player->is_digging = FALSE;
3669     player->is_snapping = FALSE;
3670     player->is_collecting = FALSE;
3671     player->is_pushing = FALSE;
3672     player->is_switching = FALSE;
3673     player->is_dropping = FALSE;
3674     player->is_dropping_pressed = FALSE;
3675
3676     player->is_bored = FALSE;
3677     player->is_sleeping = FALSE;
3678
3679     player->frame_counter_bored = -1;
3680     player->frame_counter_sleeping = -1;
3681
3682     player->anim_delay_counter = 0;
3683     player->post_delay_counter = 0;
3684
3685     player->dir_waiting = MV_NONE;
3686     player->action_waiting = ACTION_DEFAULT;
3687     player->last_action_waiting = ACTION_DEFAULT;
3688     player->special_action_bored = ACTION_DEFAULT;
3689     player->special_action_sleeping = ACTION_DEFAULT;
3690
3691     player->switch_x = -1;
3692     player->switch_y = -1;
3693
3694     player->drop_x = -1;
3695     player->drop_y = -1;
3696
3697     player->show_envelope = 0;
3698
3699     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3700
3701     player->push_delay       = -1;      /* initialized when pushing starts */
3702     player->push_delay_value = game.initial_push_delay_value;
3703
3704     player->drop_delay = 0;
3705     player->drop_pressed_delay = 0;
3706
3707     player->last_jx = -1;
3708     player->last_jy = -1;
3709     player->jx = -1;
3710     player->jy = -1;
3711
3712     player->shield_normal_time_left = 0;
3713     player->shield_deadly_time_left = 0;
3714
3715     player->inventory_infinite_element = EL_UNDEFINED;
3716     player->inventory_size = 0;
3717
3718     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3719     SnapField(player, 0, 0);
3720
3721     player->LevelSolved = FALSE;
3722     player->GameOver = FALSE;
3723
3724     player->LevelSolved_GameWon = FALSE;
3725     player->LevelSolved_GameEnd = FALSE;
3726     player->LevelSolved_PanelOff = FALSE;
3727     player->LevelSolved_SaveTape = FALSE;
3728     player->LevelSolved_SaveScore = FALSE;
3729     player->LevelSolved_CountingTime = 0;
3730     player->LevelSolved_CountingScore = 0;
3731   }
3732
3733   network_player_action_received = FALSE;
3734
3735 #if defined(NETWORK_AVALIABLE)
3736   /* initial null action */
3737   if (network_playing)
3738     SendToServer_MovePlayer(MV_NONE);
3739 #endif
3740
3741   ZX = ZY = -1;
3742   ExitX = ExitY = -1;
3743
3744   FrameCounter = 0;
3745   TimeFrames = 0;
3746   TimePlayed = 0;
3747   TimeLeft = level.time;
3748   TapeTime = 0;
3749
3750   ScreenMovDir = MV_NONE;
3751   ScreenMovPos = 0;
3752   ScreenGfxPos = 0;
3753
3754   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3755
3756   AllPlayersGone = FALSE;
3757
3758   game.yamyam_content_nr = 0;
3759   game.robot_wheel_active = FALSE;
3760   game.magic_wall_active = FALSE;
3761   game.magic_wall_time_left = 0;
3762   game.light_time_left = 0;
3763   game.timegate_time_left = 0;
3764   game.switchgate_pos = 0;
3765   game.wind_direction = level.wind_direction_initial;
3766
3767 #if !USE_PLAYER_GRAVITY
3768   game.gravity = FALSE;
3769   game.explosions_delayed = TRUE;
3770 #endif
3771
3772   game.lenses_time_left = 0;
3773   game.magnify_time_left = 0;
3774
3775   game.ball_state = level.ball_state_initial;
3776   game.ball_content_nr = 0;
3777
3778   game.envelope_active = FALSE;
3779
3780   /* set focus to local player for network games, else to all players */
3781   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3782   game.centered_player_nr_next = game.centered_player_nr;
3783   game.set_centered_player = FALSE;
3784
3785   if (network_playing && tape.recording)
3786   {
3787     /* store client dependent player focus when recording network games */
3788     tape.centered_player_nr_next = game.centered_player_nr_next;
3789     tape.set_centered_player = TRUE;
3790   }
3791
3792   for (i = 0; i < NUM_BELTS; i++)
3793   {
3794     game.belt_dir[i] = MV_NONE;
3795     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3796   }
3797
3798   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3799     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3800
3801   SCAN_PLAYFIELD(x, y)
3802   {
3803     Feld[x][y] = level.field[x][y];
3804     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3805     ChangeDelay[x][y] = 0;
3806     ChangePage[x][y] = -1;
3807 #if USE_NEW_CUSTOM_VALUE
3808     CustomValue[x][y] = 0;              /* initialized in InitField() */
3809 #endif
3810     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3811     AmoebaNr[x][y] = 0;
3812     WasJustMoving[x][y] = 0;
3813     WasJustFalling[x][y] = 0;
3814     CheckCollision[x][y] = 0;
3815     CheckImpact[x][y] = 0;
3816     Stop[x][y] = FALSE;
3817     Pushed[x][y] = FALSE;
3818
3819     ChangeCount[x][y] = 0;
3820     ChangeEvent[x][y] = -1;
3821
3822     ExplodePhase[x][y] = 0;
3823     ExplodeDelay[x][y] = 0;
3824     ExplodeField[x][y] = EX_TYPE_NONE;
3825
3826     RunnerVisit[x][y] = 0;
3827     PlayerVisit[x][y] = 0;
3828
3829     GfxFrame[x][y] = 0;
3830     GfxRandom[x][y] = INIT_GFX_RANDOM();
3831     GfxElement[x][y] = EL_UNDEFINED;
3832     GfxAction[x][y] = ACTION_DEFAULT;
3833     GfxDir[x][y] = MV_NONE;
3834   }
3835
3836   SCAN_PLAYFIELD(x, y)
3837   {
3838     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3839       emulate_bd = FALSE;
3840     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3841       emulate_sb = FALSE;
3842     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3843       emulate_sp = FALSE;
3844
3845     InitField(x, y, TRUE);
3846
3847     ResetGfxAnimation(x, y);
3848   }
3849
3850   InitBeltMovement();
3851
3852   for (i = 0; i < MAX_PLAYERS; i++)
3853   {
3854     struct PlayerInfo *player = &stored_player[i];
3855
3856     /* set number of special actions for bored and sleeping animation */
3857     player->num_special_action_bored =
3858       get_num_special_action(player->artwork_element,
3859                              ACTION_BORING_1, ACTION_BORING_LAST);
3860     player->num_special_action_sleeping =
3861       get_num_special_action(player->artwork_element,
3862                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3863   }
3864
3865   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3866                     emulate_sb ? EMU_SOKOBAN :
3867                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3868
3869 #if USE_NEW_ALL_SLIPPERY
3870   /* initialize type of slippery elements */
3871   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3872   {
3873     if (!IS_CUSTOM_ELEMENT(i))
3874     {
3875       /* default: elements slip down either to the left or right randomly */
3876       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3877
3878       /* SP style elements prefer to slip down on the left side */
3879       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3880         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3881
3882       /* BD style elements prefer to slip down on the left side */
3883       if (game.emulation == EMU_BOULDERDASH)
3884         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3885     }
3886   }
3887 #endif
3888
3889   /* initialize explosion and ignition delay */
3890   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3891   {
3892     if (!IS_CUSTOM_ELEMENT(i))
3893     {
3894       int num_phase = 8;
3895       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3896                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3897                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3898       int last_phase = (num_phase + 1) * delay;
3899       int half_phase = (num_phase / 2) * delay;
3900
3901       element_info[i].explosion_delay = last_phase - 1;
3902       element_info[i].ignition_delay = half_phase;
3903
3904       if (i == EL_BLACK_ORB)
3905         element_info[i].ignition_delay = 1;
3906     }
3907
3908 #if 0
3909     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3910       element_info[i].explosion_delay = 1;
3911
3912     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3913       element_info[i].ignition_delay = 1;
3914 #endif
3915   }
3916
3917   /* correct non-moving belts to start moving left */
3918   for (i = 0; i < NUM_BELTS; i++)
3919     if (game.belt_dir[i] == MV_NONE)
3920       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3921
3922   /* check if any connected player was not found in playfield */
3923   for (i = 0; i < MAX_PLAYERS; i++)
3924   {
3925     struct PlayerInfo *player = &stored_player[i];
3926
3927     if (player->connected && !player->present)
3928     {
3929       for (j = 0; j < MAX_PLAYERS; j++)
3930       {
3931         struct PlayerInfo *some_player = &stored_player[j];
3932         int jx = some_player->jx, jy = some_player->jy;
3933
3934         /* assign first free player found that is present in the playfield */
3935         if (some_player->present && !some_player->connected)
3936         {
3937           player->present = TRUE;
3938           player->active = TRUE;
3939
3940           some_player->present = FALSE;
3941           some_player->active = FALSE;
3942
3943           player->artwork_element = some_player->artwork_element;
3944
3945           player->block_last_field       = some_player->block_last_field;
3946           player->block_delay_adjustment = some_player->block_delay_adjustment;
3947
3948           StorePlayer[jx][jy] = player->element_nr;
3949           player->jx = player->last_jx = jx;
3950           player->jy = player->last_jy = jy;
3951
3952           break;
3953         }
3954       }
3955     }
3956   }
3957
3958   if (tape.playing)
3959   {
3960     /* when playing a tape, eliminate all players who do not participate */
3961
3962     for (i = 0; i < MAX_PLAYERS; i++)
3963     {
3964       if (stored_player[i].active && !tape.player_participates[i])
3965       {
3966         struct PlayerInfo *player = &stored_player[i];
3967         int jx = player->jx, jy = player->jy;
3968
3969         player->active = FALSE;
3970         StorePlayer[jx][jy] = 0;
3971         Feld[jx][jy] = EL_EMPTY;
3972       }
3973     }
3974   }
3975   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
3976   {
3977     /* when in single player mode, eliminate all but the first active player */
3978
3979     for (i = 0; i < MAX_PLAYERS; i++)
3980     {
3981       if (stored_player[i].active)
3982       {
3983         for (j = i + 1; j < MAX_PLAYERS; j++)
3984         {
3985           if (stored_player[j].active)
3986           {
3987             struct PlayerInfo *player = &stored_player[j];
3988             int jx = player->jx, jy = player->jy;
3989
3990             player->active = FALSE;
3991             player->present = FALSE;
3992
3993             StorePlayer[jx][jy] = 0;
3994             Feld[jx][jy] = EL_EMPTY;
3995           }
3996         }
3997       }
3998     }
3999   }
4000
4001   /* when recording the game, store which players take part in the game */
4002   if (tape.recording)
4003   {
4004     for (i = 0; i < MAX_PLAYERS; i++)
4005       if (stored_player[i].active)
4006         tape.player_participates[i] = TRUE;
4007   }
4008
4009   if (options.debug)
4010   {
4011     for (i = 0; i < MAX_PLAYERS; i++)
4012     {
4013       struct PlayerInfo *player = &stored_player[i];
4014
4015       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4016              i+1,
4017              player->present,
4018              player->connected,
4019              player->active);
4020       if (local_player == player)
4021         printf("Player  %d is local player.\n", i+1);
4022     }
4023   }
4024
4025   if (BorderElement == EL_EMPTY)
4026   {
4027     SBX_Left = 0;
4028     SBX_Right = lev_fieldx - SCR_FIELDX;
4029     SBY_Upper = 0;
4030     SBY_Lower = lev_fieldy - SCR_FIELDY;
4031   }
4032   else
4033   {
4034     SBX_Left = -1;
4035     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4036     SBY_Upper = -1;
4037     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4038   }
4039
4040   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4041     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4042
4043   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4044     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4045
4046   /* if local player not found, look for custom element that might create
4047      the player (make some assumptions about the right custom element) */
4048   if (!local_player->present)
4049   {
4050     int start_x = 0, start_y = 0;
4051     int found_rating = 0;
4052     int found_element = EL_UNDEFINED;
4053     int player_nr = local_player->index_nr;
4054
4055     SCAN_PLAYFIELD(x, y)
4056     {
4057       int element = Feld[x][y];
4058       int content;
4059       int xx, yy;
4060       boolean is_player;
4061
4062       if (level.use_start_element[player_nr] &&
4063           level.start_element[player_nr] == element &&
4064           found_rating < 4)
4065       {
4066         start_x = x;
4067         start_y = y;
4068
4069         found_rating = 4;
4070         found_element = element;
4071       }
4072
4073       if (!IS_CUSTOM_ELEMENT(element))
4074         continue;
4075
4076       if (CAN_CHANGE(element))
4077       {
4078         for (i = 0; i < element_info[element].num_change_pages; i++)
4079         {
4080           /* check for player created from custom element as single target */
4081           content = element_info[element].change_page[i].target_element;
4082           is_player = ELEM_IS_PLAYER(content);
4083
4084           if (is_player && (found_rating < 3 ||
4085                             (found_rating == 3 && element < found_element)))
4086           {
4087             start_x = x;
4088             start_y = y;
4089
4090             found_rating = 3;
4091             found_element = element;
4092           }
4093         }
4094       }
4095
4096       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4097       {
4098         /* check for player created from custom element as explosion content */
4099         content = element_info[element].content.e[xx][yy];
4100         is_player = ELEM_IS_PLAYER(content);
4101
4102         if (is_player && (found_rating < 2 ||
4103                           (found_rating == 2 && element < found_element)))
4104         {
4105           start_x = x + xx - 1;
4106           start_y = y + yy - 1;
4107
4108           found_rating = 2;
4109           found_element = element;
4110         }
4111
4112         if (!CAN_CHANGE(element))
4113           continue;
4114
4115         for (i = 0; i < element_info[element].num_change_pages; i++)
4116         {
4117           /* check for player created from custom element as extended target */
4118           content =
4119             element_info[element].change_page[i].target_content.e[xx][yy];
4120
4121           is_player = ELEM_IS_PLAYER(content);
4122
4123           if (is_player && (found_rating < 1 ||
4124                             (found_rating == 1 && element < found_element)))
4125           {
4126             start_x = x + xx - 1;
4127             start_y = y + yy - 1;
4128
4129             found_rating = 1;
4130             found_element = element;
4131           }
4132         }
4133       }
4134     }
4135
4136     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4137                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4138                 start_x - MIDPOSX);
4139
4140     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4141                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4142                 start_y - MIDPOSY);
4143   }
4144   else
4145   {
4146     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4147                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4148                 local_player->jx - MIDPOSX);
4149
4150     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4151                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4152                 local_player->jy - MIDPOSY);
4153   }
4154
4155 #if 0
4156   /* do not use PLAYING mask for fading out from main screen */
4157   game_status = GAME_MODE_MAIN;
4158 #endif
4159
4160   StopAnimation();
4161
4162   if (!game.restart_level)
4163     CloseDoor(DOOR_CLOSE_1);
4164
4165 #if 1
4166   if (level_editor_test_game)
4167     FadeSkipNextFadeIn();
4168   else
4169     FadeSetEnterScreen();
4170 #else
4171   if (level_editor_test_game)
4172     fading = fading_none;
4173   else
4174     fading = menu.destination;
4175 #endif
4176
4177 #if 1
4178   FadeOut(REDRAW_FIELD);
4179 #else
4180   if (do_fading)
4181     FadeOut(REDRAW_FIELD);
4182 #endif
4183
4184 #if 0
4185   game_status = GAME_MODE_PLAYING;
4186 #endif
4187
4188   /* !!! FIX THIS (START) !!! */
4189   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4190   {
4191     InitGameEngine_EM();
4192
4193     /* blit playfield from scroll buffer to normal back buffer for fading in */
4194     BlitScreenToBitmap_EM(backbuffer);
4195   }
4196   else
4197   {
4198     DrawLevel();
4199     DrawAllPlayers();
4200
4201     /* after drawing the level, correct some elements */
4202     if (game.timegate_time_left == 0)
4203       CloseAllOpenTimegates();
4204
4205     /* blit playfield from scroll buffer to normal back buffer for fading in */
4206     if (setup.soft_scrolling)
4207       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4208
4209     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4210   }
4211   /* !!! FIX THIS (END) !!! */
4212
4213 #if 1
4214   FadeIn(REDRAW_FIELD);
4215 #else
4216   if (do_fading)
4217     FadeIn(REDRAW_FIELD);
4218
4219   BackToFront();
4220 #endif
4221
4222   if (!game.restart_level)
4223   {
4224     /* copy default game door content to main double buffer */
4225     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4226                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4227   }
4228
4229   SetPanelBackground();
4230   SetDrawBackgroundMask(REDRAW_DOOR_1);
4231
4232 #if 1
4233   UpdateAndDisplayGameControlValues();
4234 #else
4235   UpdateGameDoorValues();
4236   DrawGameDoorValues();
4237 #endif
4238
4239   if (!game.restart_level)
4240   {
4241     UnmapGameButtons();
4242     UnmapTapeButtons();
4243     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4244     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4245     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4246     MapGameButtons();
4247     MapTapeButtons();
4248
4249     /* copy actual game door content to door double buffer for OpenDoor() */
4250     BlitBitmap(drawto, bitmap_db_door,
4251                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4252
4253     OpenDoor(DOOR_OPEN_ALL);
4254
4255     PlaySound(SND_GAME_STARTING);
4256
4257     if (setup.sound_music)
4258       PlayLevelMusic();
4259
4260     KeyboardAutoRepeatOffUnlessAutoplay();
4261
4262     if (options.debug)
4263     {
4264       for (i = 0; i < MAX_PLAYERS; i++)
4265         printf("Player %d %sactive.\n",
4266                i + 1, (stored_player[i].active ? "" : "not "));
4267     }
4268   }
4269
4270 #if 1
4271   UnmapAllGadgets();
4272
4273   MapGameButtons();
4274   MapTapeButtons();
4275 #endif
4276
4277   game.restart_level = FALSE;
4278 }
4279
4280 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4281 {
4282   /* this is used for non-R'n'D game engines to update certain engine values */
4283
4284   /* needed to determine if sounds are played within the visible screen area */
4285   scroll_x = actual_scroll_x;
4286   scroll_y = actual_scroll_y;
4287 }
4288
4289 void InitMovDir(int x, int y)
4290 {
4291   int i, element = Feld[x][y];
4292   static int xy[4][2] =
4293   {
4294     {  0, +1 },
4295     { +1,  0 },
4296     {  0, -1 },
4297     { -1,  0 }
4298   };
4299   static int direction[3][4] =
4300   {
4301     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4302     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4303     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4304   };
4305
4306   switch (element)
4307   {
4308     case EL_BUG_RIGHT:
4309     case EL_BUG_UP:
4310     case EL_BUG_LEFT:
4311     case EL_BUG_DOWN:
4312       Feld[x][y] = EL_BUG;
4313       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4314       break;
4315
4316     case EL_SPACESHIP_RIGHT:
4317     case EL_SPACESHIP_UP:
4318     case EL_SPACESHIP_LEFT:
4319     case EL_SPACESHIP_DOWN:
4320       Feld[x][y] = EL_SPACESHIP;
4321       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4322       break;
4323
4324     case EL_BD_BUTTERFLY_RIGHT:
4325     case EL_BD_BUTTERFLY_UP:
4326     case EL_BD_BUTTERFLY_LEFT:
4327     case EL_BD_BUTTERFLY_DOWN:
4328       Feld[x][y] = EL_BD_BUTTERFLY;
4329       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4330       break;
4331
4332     case EL_BD_FIREFLY_RIGHT:
4333     case EL_BD_FIREFLY_UP:
4334     case EL_BD_FIREFLY_LEFT:
4335     case EL_BD_FIREFLY_DOWN:
4336       Feld[x][y] = EL_BD_FIREFLY;
4337       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4338       break;
4339
4340     case EL_PACMAN_RIGHT:
4341     case EL_PACMAN_UP:
4342     case EL_PACMAN_LEFT:
4343     case EL_PACMAN_DOWN:
4344       Feld[x][y] = EL_PACMAN;
4345       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4346       break;
4347
4348     case EL_YAMYAM_LEFT:
4349     case EL_YAMYAM_RIGHT:
4350     case EL_YAMYAM_UP:
4351     case EL_YAMYAM_DOWN:
4352       Feld[x][y] = EL_YAMYAM;
4353       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4354       break;
4355
4356     case EL_SP_SNIKSNAK:
4357       MovDir[x][y] = MV_UP;
4358       break;
4359
4360     case EL_SP_ELECTRON:
4361       MovDir[x][y] = MV_LEFT;
4362       break;
4363
4364     case EL_MOLE_LEFT:
4365     case EL_MOLE_RIGHT:
4366     case EL_MOLE_UP:
4367     case EL_MOLE_DOWN:
4368       Feld[x][y] = EL_MOLE;
4369       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4370       break;
4371
4372     default:
4373       if (IS_CUSTOM_ELEMENT(element))
4374       {
4375         struct ElementInfo *ei = &element_info[element];
4376         int move_direction_initial = ei->move_direction_initial;
4377         int move_pattern = ei->move_pattern;
4378
4379         if (move_direction_initial == MV_START_PREVIOUS)
4380         {
4381           if (MovDir[x][y] != MV_NONE)
4382             return;
4383
4384           move_direction_initial = MV_START_AUTOMATIC;
4385         }
4386
4387         if (move_direction_initial == MV_START_RANDOM)
4388           MovDir[x][y] = 1 << RND(4);
4389         else if (move_direction_initial & MV_ANY_DIRECTION)
4390           MovDir[x][y] = move_direction_initial;
4391         else if (move_pattern == MV_ALL_DIRECTIONS ||
4392                  move_pattern == MV_TURNING_LEFT ||
4393                  move_pattern == MV_TURNING_RIGHT ||
4394                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4395                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4396                  move_pattern == MV_TURNING_RANDOM)
4397           MovDir[x][y] = 1 << RND(4);
4398         else if (move_pattern == MV_HORIZONTAL)
4399           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4400         else if (move_pattern == MV_VERTICAL)
4401           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4402         else if (move_pattern & MV_ANY_DIRECTION)
4403           MovDir[x][y] = element_info[element].move_pattern;
4404         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4405                  move_pattern == MV_ALONG_RIGHT_SIDE)
4406         {
4407           /* use random direction as default start direction */
4408           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4409             MovDir[x][y] = 1 << RND(4);
4410
4411           for (i = 0; i < NUM_DIRECTIONS; i++)
4412           {
4413             int x1 = x + xy[i][0];
4414             int y1 = y + xy[i][1];
4415
4416             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4417             {
4418               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4419                 MovDir[x][y] = direction[0][i];
4420               else
4421                 MovDir[x][y] = direction[1][i];
4422
4423               break;
4424             }
4425           }
4426         }                
4427       }
4428       else
4429       {
4430         MovDir[x][y] = 1 << RND(4);
4431
4432         if (element != EL_BUG &&
4433             element != EL_SPACESHIP &&
4434             element != EL_BD_BUTTERFLY &&
4435             element != EL_BD_FIREFLY)
4436           break;
4437
4438         for (i = 0; i < NUM_DIRECTIONS; i++)
4439         {
4440           int x1 = x + xy[i][0];
4441           int y1 = y + xy[i][1];
4442
4443           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4444           {
4445             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4446             {
4447               MovDir[x][y] = direction[0][i];
4448               break;
4449             }
4450             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4451                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4452             {
4453               MovDir[x][y] = direction[1][i];
4454               break;
4455             }
4456           }
4457         }
4458       }
4459       break;
4460   }
4461
4462   GfxDir[x][y] = MovDir[x][y];
4463 }
4464
4465 void InitAmoebaNr(int x, int y)
4466 {
4467   int i;
4468   int group_nr = AmoebeNachbarNr(x, y);
4469
4470   if (group_nr == 0)
4471   {
4472     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4473     {
4474       if (AmoebaCnt[i] == 0)
4475       {
4476         group_nr = i;
4477         break;
4478       }
4479     }
4480   }
4481
4482   AmoebaNr[x][y] = group_nr;
4483   AmoebaCnt[group_nr]++;
4484   AmoebaCnt2[group_nr]++;
4485 }
4486
4487 static void PlayerWins(struct PlayerInfo *player)
4488 {
4489   player->LevelSolved = TRUE;
4490   player->GameOver = TRUE;
4491
4492   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4493                          level.native_em_level->lev->score : player->score);
4494
4495   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4496   player->LevelSolved_CountingScore = player->score_final;
4497 }
4498
4499 void GameWon()
4500 {
4501   static int time, time_final;
4502   static int score, score_final;
4503   static int game_over_delay_1 = 0;
4504   static int game_over_delay_2 = 0;
4505   int game_over_delay_value_1 = 50;
4506   int game_over_delay_value_2 = 50;
4507
4508   if (!local_player->LevelSolved_GameWon)
4509   {
4510     int i;
4511
4512     /* do not start end game actions before the player stops moving (to exit) */
4513     if (local_player->MovPos)
4514       return;
4515
4516     local_player->LevelSolved_GameWon = TRUE;
4517     local_player->LevelSolved_SaveTape = tape.recording;
4518     local_player->LevelSolved_SaveScore = !tape.playing;
4519
4520     if (tape.auto_play)         /* tape might already be stopped here */
4521       tape.auto_play_level_solved = TRUE;
4522
4523 #if 1
4524     TapeStop();
4525 #endif
4526
4527     game_over_delay_1 = game_over_delay_value_1;
4528     game_over_delay_2 = game_over_delay_value_2;
4529
4530     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4531     score = score_final = local_player->score_final;
4532
4533     if (TimeLeft > 0)
4534     {
4535       time_final = 0;
4536       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4537     }
4538     else if (level.time == 0 && TimePlayed < 999)
4539     {
4540       time_final = 999;
4541       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4542     }
4543
4544     local_player->score_final = score_final;
4545
4546     if (level_editor_test_game)
4547     {
4548       time = time_final;
4549       score = score_final;
4550
4551 #if 1
4552       local_player->LevelSolved_CountingTime = time;
4553       local_player->LevelSolved_CountingScore = score;
4554
4555       game_panel_controls[GAME_PANEL_TIME].value = time;
4556       game_panel_controls[GAME_PANEL_SCORE].value = score;
4557
4558       DisplayGameControlValues();
4559 #else
4560       DrawGameValue_Time(time);
4561       DrawGameValue_Score(score);
4562 #endif
4563     }
4564
4565     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4566     {
4567       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4568       {
4569         /* close exit door after last player */
4570         if ((AllPlayersGone &&
4571              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4572               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4573               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4574             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4575             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4576         {
4577           int element = Feld[ExitX][ExitY];
4578
4579 #if 0
4580           if (element == EL_EM_EXIT_OPEN ||
4581               element == EL_EM_STEEL_EXIT_OPEN)
4582           {
4583             Bang(ExitX, ExitY);
4584           }
4585           else
4586 #endif
4587           {
4588             Feld[ExitX][ExitY] =
4589               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4590                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4591                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4592                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4593                EL_EM_STEEL_EXIT_CLOSING);
4594
4595             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4596           }
4597         }
4598
4599         /* player disappears */
4600         DrawLevelField(ExitX, ExitY);
4601       }
4602
4603       for (i = 0; i < MAX_PLAYERS; i++)
4604       {
4605         struct PlayerInfo *player = &stored_player[i];
4606
4607         if (player->present)
4608         {
4609           RemovePlayer(player);
4610
4611           /* player disappears */
4612           DrawLevelField(player->jx, player->jy);
4613         }
4614       }
4615     }
4616
4617     PlaySound(SND_GAME_WINNING);
4618   }
4619
4620   if (game_over_delay_1 > 0)
4621   {
4622     game_over_delay_1--;
4623
4624     return;
4625   }
4626
4627   if (time != time_final)
4628   {
4629     int time_to_go = ABS(time_final - time);
4630     int time_count_dir = (time < time_final ? +1 : -1);
4631     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4632
4633     time  += time_count_steps * time_count_dir;
4634     score += time_count_steps * level.score[SC_TIME_BONUS];
4635
4636 #if 1
4637     local_player->LevelSolved_CountingTime = time;
4638     local_player->LevelSolved_CountingScore = score;
4639
4640     game_panel_controls[GAME_PANEL_TIME].value = time;
4641     game_panel_controls[GAME_PANEL_SCORE].value = score;
4642
4643     DisplayGameControlValues();
4644 #else
4645     DrawGameValue_Time(time);
4646     DrawGameValue_Score(score);
4647 #endif
4648
4649     if (time == time_final)
4650       StopSound(SND_GAME_LEVELTIME_BONUS);
4651     else if (setup.sound_loops)
4652       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4653     else
4654       PlaySound(SND_GAME_LEVELTIME_BONUS);
4655
4656     return;
4657   }
4658
4659   local_player->LevelSolved_PanelOff = TRUE;
4660
4661   if (game_over_delay_2 > 0)
4662   {
4663     game_over_delay_2--;
4664
4665     return;
4666   }
4667
4668 #if 1
4669   GameEnd();
4670 #endif
4671 }
4672
4673 void GameEnd()
4674 {
4675   int hi_pos;
4676   boolean raise_level = FALSE;
4677
4678   local_player->LevelSolved_GameEnd = TRUE;
4679
4680   CloseDoor(DOOR_CLOSE_1);
4681
4682   if (local_player->LevelSolved_SaveTape)
4683   {
4684 #if 0
4685     TapeStop();
4686 #endif
4687
4688 #if 1
4689     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4690 #else
4691     SaveTape(tape.level_nr);            /* ask to save tape */
4692 #endif
4693   }
4694
4695   if (level_editor_test_game)
4696   {
4697     game_status = GAME_MODE_MAIN;
4698
4699 #if 1
4700     DrawAndFadeInMainMenu(REDRAW_FIELD);
4701 #else
4702     DrawMainMenu();
4703 #endif
4704
4705     return;
4706   }
4707
4708   if (!local_player->LevelSolved_SaveScore)
4709   {
4710 #if 1
4711     FadeOut(REDRAW_FIELD);
4712 #endif
4713
4714     game_status = GAME_MODE_MAIN;
4715
4716     DrawAndFadeInMainMenu(REDRAW_FIELD);
4717
4718     return;
4719   }
4720
4721   if (level_nr == leveldir_current->handicap_level)
4722   {
4723     leveldir_current->handicap_level++;
4724     SaveLevelSetup_SeriesInfo();
4725   }
4726
4727   if (level_nr < leveldir_current->last_level)
4728     raise_level = TRUE;                 /* advance to next level */
4729
4730   if ((hi_pos = NewHiScore()) >= 0) 
4731   {
4732     game_status = GAME_MODE_SCORES;
4733
4734     DrawHallOfFame(hi_pos);
4735
4736     if (raise_level)
4737     {
4738       level_nr++;
4739       TapeErase();
4740     }
4741   }
4742   else
4743   {
4744 #if 1
4745     FadeOut(REDRAW_FIELD);
4746 #endif
4747
4748     game_status = GAME_MODE_MAIN;
4749
4750     if (raise_level)
4751     {
4752       level_nr++;
4753       TapeErase();
4754     }
4755
4756     DrawAndFadeInMainMenu(REDRAW_FIELD);
4757   }
4758 }
4759
4760 int NewHiScore()
4761 {
4762   int k, l;
4763   int position = -1;
4764
4765   LoadScore(level_nr);
4766
4767   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4768       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4769     return -1;
4770
4771   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4772   {
4773     if (local_player->score_final > highscore[k].Score)
4774     {
4775       /* player has made it to the hall of fame */
4776
4777       if (k < MAX_SCORE_ENTRIES - 1)
4778       {
4779         int m = MAX_SCORE_ENTRIES - 1;
4780
4781 #ifdef ONE_PER_NAME
4782         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4783           if (strEqual(setup.player_name, highscore[l].Name))
4784             m = l;
4785         if (m == k)     /* player's new highscore overwrites his old one */
4786           goto put_into_list;
4787 #endif
4788
4789         for (l = m; l > k; l--)
4790         {
4791           strcpy(highscore[l].Name, highscore[l - 1].Name);
4792           highscore[l].Score = highscore[l - 1].Score;
4793         }
4794       }
4795
4796 #ifdef ONE_PER_NAME
4797       put_into_list:
4798 #endif
4799       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4800       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4801       highscore[k].Score = local_player->score_final; 
4802       position = k;
4803       break;
4804     }
4805
4806 #ifdef ONE_PER_NAME
4807     else if (!strncmp(setup.player_name, highscore[k].Name,
4808                       MAX_PLAYER_NAME_LEN))
4809       break;    /* player already there with a higher score */
4810 #endif
4811
4812   }
4813
4814   if (position >= 0) 
4815     SaveScore(level_nr);
4816
4817   return position;
4818 }
4819
4820 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4821 {
4822   int element = Feld[x][y];
4823   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4824   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4825   int horiz_move = (dx != 0);
4826   int sign = (horiz_move ? dx : dy);
4827   int step = sign * element_info[element].move_stepsize;
4828
4829   /* special values for move stepsize for spring and things on conveyor belt */
4830   if (horiz_move)
4831   {
4832     if (CAN_FALL(element) &&
4833         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4834       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4835     else if (element == EL_SPRING)
4836       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4837   }
4838
4839   return step;
4840 }
4841
4842 inline static int getElementMoveStepsize(int x, int y)
4843 {
4844   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4845 }
4846
4847 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4848 {
4849   if (player->GfxAction != action || player->GfxDir != dir)
4850   {
4851 #if 0
4852     printf("Player frame reset! (%d => %d, %d => %d)\n",
4853            player->GfxAction, action, player->GfxDir, dir);
4854 #endif
4855
4856     player->GfxAction = action;
4857     player->GfxDir = dir;
4858     player->Frame = 0;
4859     player->StepFrame = 0;
4860   }
4861 }
4862
4863 #if USE_GFX_RESET_GFX_ANIMATION
4864 static void ResetGfxFrame(int x, int y, boolean redraw)
4865 {
4866   int element = Feld[x][y];
4867   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4868   int last_gfx_frame = GfxFrame[x][y];
4869
4870   if (graphic_info[graphic].anim_global_sync)
4871     GfxFrame[x][y] = FrameCounter;
4872   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4873     GfxFrame[x][y] = CustomValue[x][y];
4874   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4875     GfxFrame[x][y] = element_info[element].collect_score;
4876   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4877     GfxFrame[x][y] = ChangeDelay[x][y];
4878
4879   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4880     DrawLevelGraphicAnimation(x, y, graphic);
4881 }
4882 #endif
4883
4884 static void ResetGfxAnimation(int x, int y)
4885 {
4886   GfxAction[x][y] = ACTION_DEFAULT;
4887   GfxDir[x][y] = MovDir[x][y];
4888   GfxFrame[x][y] = 0;
4889
4890 #if USE_GFX_RESET_GFX_ANIMATION
4891   ResetGfxFrame(x, y, FALSE);
4892 #endif
4893 }
4894
4895 static void ResetRandomAnimationValue(int x, int y)
4896 {
4897   GfxRandom[x][y] = INIT_GFX_RANDOM();
4898 }
4899
4900 void InitMovingField(int x, int y, int direction)
4901 {
4902   int element = Feld[x][y];
4903   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4904   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4905   int newx = x + dx;
4906   int newy = y + dy;
4907   boolean is_moving_before, is_moving_after;
4908 #if 0
4909   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4910 #endif
4911
4912   /* check if element was/is moving or being moved before/after mode change */
4913 #if 1
4914 #if 1
4915   is_moving_before = (WasJustMoving[x][y] != 0);
4916 #else
4917   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4918   is_moving_before = WasJustMoving[x][y];
4919 #endif
4920 #else
4921   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4922 #endif
4923   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4924
4925   /* reset animation only for moving elements which change direction of moving
4926      or which just started or stopped moving
4927      (else CEs with property "can move" / "not moving" are reset each frame) */
4928 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4929 #if 1
4930   if (is_moving_before != is_moving_after ||
4931       direction != MovDir[x][y])
4932     ResetGfxAnimation(x, y);
4933 #else
4934   if ((is_moving_before || is_moving_after) && !continues_moving)
4935     ResetGfxAnimation(x, y);
4936 #endif
4937 #else
4938   if (!continues_moving)
4939     ResetGfxAnimation(x, y);
4940 #endif
4941
4942   MovDir[x][y] = direction;
4943   GfxDir[x][y] = direction;
4944
4945 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4946   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4947                      direction == MV_DOWN && CAN_FALL(element) ?
4948                      ACTION_FALLING : ACTION_MOVING);
4949 #else
4950   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4951                      ACTION_FALLING : ACTION_MOVING);
4952 #endif
4953
4954   /* this is needed for CEs with property "can move" / "not moving" */
4955
4956   if (is_moving_after)
4957   {
4958     if (Feld[newx][newy] == EL_EMPTY)
4959       Feld[newx][newy] = EL_BLOCKED;
4960
4961     MovDir[newx][newy] = MovDir[x][y];
4962
4963 #if USE_NEW_CUSTOM_VALUE
4964     CustomValue[newx][newy] = CustomValue[x][y];
4965 #endif
4966
4967     GfxFrame[newx][newy] = GfxFrame[x][y];
4968     GfxRandom[newx][newy] = GfxRandom[x][y];
4969     GfxAction[newx][newy] = GfxAction[x][y];
4970     GfxDir[newx][newy] = GfxDir[x][y];
4971   }
4972 }
4973
4974 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4975 {
4976   int direction = MovDir[x][y];
4977   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4978   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4979
4980   *goes_to_x = newx;
4981   *goes_to_y = newy;
4982 }
4983
4984 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4985 {
4986   int oldx = x, oldy = y;
4987   int direction = MovDir[x][y];
4988
4989   if (direction == MV_LEFT)
4990     oldx++;
4991   else if (direction == MV_RIGHT)
4992     oldx--;
4993   else if (direction == MV_UP)
4994     oldy++;
4995   else if (direction == MV_DOWN)
4996     oldy--;
4997
4998   *comes_from_x = oldx;
4999   *comes_from_y = oldy;
5000 }
5001
5002 int MovingOrBlocked2Element(int x, int y)
5003 {
5004   int element = Feld[x][y];
5005
5006   if (element == EL_BLOCKED)
5007   {
5008     int oldx, oldy;
5009
5010     Blocked2Moving(x, y, &oldx, &oldy);
5011     return Feld[oldx][oldy];
5012   }
5013   else
5014     return element;
5015 }
5016
5017 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5018 {
5019   /* like MovingOrBlocked2Element(), but if element is moving
5020      and (x,y) is the field the moving element is just leaving,
5021      return EL_BLOCKED instead of the element value */
5022   int element = Feld[x][y];
5023
5024   if (IS_MOVING(x, y))
5025   {
5026     if (element == EL_BLOCKED)
5027     {
5028       int oldx, oldy;
5029
5030       Blocked2Moving(x, y, &oldx, &oldy);
5031       return Feld[oldx][oldy];
5032     }
5033     else
5034       return EL_BLOCKED;
5035   }
5036   else
5037     return element;
5038 }
5039
5040 static void RemoveField(int x, int y)
5041 {
5042   Feld[x][y] = EL_EMPTY;
5043
5044   MovPos[x][y] = 0;
5045   MovDir[x][y] = 0;
5046   MovDelay[x][y] = 0;
5047
5048 #if USE_NEW_CUSTOM_VALUE
5049   CustomValue[x][y] = 0;
5050 #endif
5051
5052   AmoebaNr[x][y] = 0;
5053   ChangeDelay[x][y] = 0;
5054   ChangePage[x][y] = -1;
5055   Pushed[x][y] = FALSE;
5056
5057 #if 0
5058   ExplodeField[x][y] = EX_TYPE_NONE;
5059 #endif
5060
5061   GfxElement[x][y] = EL_UNDEFINED;
5062   GfxAction[x][y] = ACTION_DEFAULT;
5063   GfxDir[x][y] = MV_NONE;
5064 }
5065
5066 void RemoveMovingField(int x, int y)
5067 {
5068   int oldx = x, oldy = y, newx = x, newy = y;
5069   int element = Feld[x][y];
5070   int next_element = EL_UNDEFINED;
5071
5072   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5073     return;
5074
5075   if (IS_MOVING(x, y))
5076   {
5077     Moving2Blocked(x, y, &newx, &newy);
5078
5079     if (Feld[newx][newy] != EL_BLOCKED)
5080     {
5081       /* element is moving, but target field is not free (blocked), but
5082          already occupied by something different (example: acid pool);
5083          in this case, only remove the moving field, but not the target */
5084
5085       RemoveField(oldx, oldy);
5086
5087       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5088
5089       DrawLevelField(oldx, oldy);
5090
5091       return;
5092     }
5093   }
5094   else if (element == EL_BLOCKED)
5095   {
5096     Blocked2Moving(x, y, &oldx, &oldy);
5097     if (!IS_MOVING(oldx, oldy))
5098       return;
5099   }
5100
5101   if (element == EL_BLOCKED &&
5102       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5103        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5104        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5105        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5106        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5107        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5108     next_element = get_next_element(Feld[oldx][oldy]);
5109
5110   RemoveField(oldx, oldy);
5111   RemoveField(newx, newy);
5112
5113   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5114
5115   if (next_element != EL_UNDEFINED)
5116     Feld[oldx][oldy] = next_element;
5117
5118   DrawLevelField(oldx, oldy);
5119   DrawLevelField(newx, newy);
5120 }
5121
5122 void DrawDynamite(int x, int y)
5123 {
5124   int sx = SCREENX(x), sy = SCREENY(y);
5125   int graphic = el2img(Feld[x][y]);
5126   int frame;
5127
5128   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5129     return;
5130
5131   if (IS_WALKABLE_INSIDE(Back[x][y]))
5132     return;
5133
5134   if (Back[x][y])
5135     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5136   else if (Store[x][y])
5137     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5138
5139   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5140
5141   if (Back[x][y] || Store[x][y])
5142     DrawGraphicThruMask(sx, sy, graphic, frame);
5143   else
5144     DrawGraphic(sx, sy, graphic, frame);
5145 }
5146
5147 void CheckDynamite(int x, int y)
5148 {
5149   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5150   {
5151     MovDelay[x][y]--;
5152
5153     if (MovDelay[x][y] != 0)
5154     {
5155       DrawDynamite(x, y);
5156       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5157
5158       return;
5159     }
5160   }
5161
5162   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5163
5164   Bang(x, y);
5165 }
5166
5167 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5168 {
5169   boolean num_checked_players = 0;
5170   int i;
5171
5172   for (i = 0; i < MAX_PLAYERS; i++)
5173   {
5174     if (stored_player[i].active)
5175     {
5176       int sx = stored_player[i].jx;
5177       int sy = stored_player[i].jy;
5178
5179       if (num_checked_players == 0)
5180       {
5181         *sx1 = *sx2 = sx;
5182         *sy1 = *sy2 = sy;
5183       }
5184       else
5185       {
5186         *sx1 = MIN(*sx1, sx);
5187         *sy1 = MIN(*sy1, sy);
5188         *sx2 = MAX(*sx2, sx);
5189         *sy2 = MAX(*sy2, sy);
5190       }
5191
5192       num_checked_players++;
5193     }
5194   }
5195 }
5196
5197 static boolean checkIfAllPlayersFitToScreen_RND()
5198 {
5199   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5200
5201   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5202
5203   return (sx2 - sx1 < SCR_FIELDX &&
5204           sy2 - sy1 < SCR_FIELDY);
5205 }
5206
5207 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5208 {
5209   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5210
5211   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5212
5213   *sx = (sx1 + sx2) / 2;
5214   *sy = (sy1 + sy2) / 2;
5215 }
5216
5217 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5218                         boolean center_screen, boolean quick_relocation)
5219 {
5220   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5221   boolean no_delay = (tape.warp_forward);
5222   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5223   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5224
5225   if (quick_relocation)
5226   {
5227     int offset = game.scroll_delay_value;
5228
5229     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5230     {
5231       if (!level.shifted_relocation || center_screen)
5232       {
5233         /* quick relocation (without scrolling), with centering of screen */
5234
5235         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5236                     x > SBX_Right + MIDPOSX ? SBX_Right :
5237                     x - MIDPOSX);
5238
5239         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5240                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5241                     y - MIDPOSY);
5242       }
5243       else
5244       {
5245         /* quick relocation (without scrolling), but do not center screen */
5246
5247         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5248                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5249                                old_x - MIDPOSX);
5250
5251         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5252                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5253                                old_y - MIDPOSY);
5254
5255         int offset_x = x + (scroll_x - center_scroll_x);
5256         int offset_y = y + (scroll_y - center_scroll_y);
5257
5258         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5259                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5260                     offset_x - MIDPOSX);
5261
5262         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5263                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5264                     offset_y - MIDPOSY);
5265       }
5266     }
5267     else
5268     {
5269       /* quick relocation (without scrolling), inside visible screen area */
5270
5271       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5272           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5273         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5274
5275       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5276           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5277         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5278
5279       /* don't scroll over playfield boundaries */
5280       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5281         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5282
5283       /* don't scroll over playfield boundaries */
5284       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5285         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5286     }
5287
5288     RedrawPlayfield(TRUE, 0,0,0,0);
5289   }
5290   else
5291   {
5292 #if 1
5293     int scroll_xx, scroll_yy;
5294
5295     if (!level.shifted_relocation || center_screen)
5296     {
5297       /* visible relocation (with scrolling), with centering of screen */
5298
5299       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5300                    x > SBX_Right + MIDPOSX ? SBX_Right :
5301                    x - MIDPOSX);
5302
5303       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5304                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5305                    y - MIDPOSY);
5306     }
5307     else
5308     {
5309       /* visible relocation (with scrolling), but do not center screen */
5310
5311       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5312                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5313                              old_x - MIDPOSX);
5314
5315       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5316                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5317                              old_y - MIDPOSY);
5318
5319       int offset_x = x + (scroll_x - center_scroll_x);
5320       int offset_y = y + (scroll_y - center_scroll_y);
5321
5322       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5323                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5324                    offset_x - MIDPOSX);
5325
5326       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5327                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5328                    offset_y - MIDPOSY);
5329     }
5330
5331 #else
5332
5333     /* visible relocation (with scrolling), with centering of screen */
5334
5335     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5336                      x > SBX_Right + MIDPOSX ? SBX_Right :
5337                      x - MIDPOSX);
5338
5339     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5340                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5341                      y - MIDPOSY);
5342 #endif
5343
5344     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5345
5346     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5347     {
5348       int dx = 0, dy = 0;
5349       int fx = FX, fy = FY;
5350
5351       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5352       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5353
5354       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5355         break;
5356
5357       scroll_x -= dx;
5358       scroll_y -= dy;
5359
5360       fx += dx * TILEX / 2;
5361       fy += dy * TILEY / 2;
5362
5363       ScrollLevel(dx, dy);
5364       DrawAllPlayers();
5365
5366       /* scroll in two steps of half tile size to make things smoother */
5367       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5368       FlushDisplay();
5369       Delay(wait_delay_value);
5370
5371       /* scroll second step to align at full tile size */
5372       BackToFront();
5373       Delay(wait_delay_value);
5374     }
5375
5376     DrawAllPlayers();
5377     BackToFront();
5378     Delay(wait_delay_value);
5379   }
5380 }
5381
5382 void RelocatePlayer(int jx, int jy, int el_player_raw)
5383 {
5384   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5385   int player_nr = GET_PLAYER_NR(el_player);
5386   struct PlayerInfo *player = &stored_player[player_nr];
5387   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5388   boolean no_delay = (tape.warp_forward);
5389   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5390   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5391   int old_jx = player->jx;
5392   int old_jy = player->jy;
5393   int old_element = Feld[old_jx][old_jy];
5394   int element = Feld[jx][jy];
5395   boolean player_relocated = (old_jx != jx || old_jy != jy);
5396
5397   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5398   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5399   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5400   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5401   int leave_side_horiz = move_dir_horiz;
5402   int leave_side_vert  = move_dir_vert;
5403   int enter_side = enter_side_horiz | enter_side_vert;
5404   int leave_side = leave_side_horiz | leave_side_vert;
5405
5406   if (player->GameOver)         /* do not reanimate dead player */
5407     return;
5408
5409   if (!player_relocated)        /* no need to relocate the player */
5410     return;
5411
5412   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5413   {
5414     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5415     DrawLevelField(jx, jy);
5416   }
5417
5418   if (player->present)
5419   {
5420     while (player->MovPos)
5421     {
5422       ScrollPlayer(player, SCROLL_GO_ON);
5423       ScrollScreen(NULL, SCROLL_GO_ON);
5424
5425       AdvanceFrameAndPlayerCounters(player->index_nr);
5426
5427       DrawPlayer(player);
5428
5429       BackToFront();
5430       Delay(wait_delay_value);
5431     }
5432
5433     DrawPlayer(player);         /* needed here only to cleanup last field */
5434     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5435
5436     player->is_moving = FALSE;
5437   }
5438
5439   if (IS_CUSTOM_ELEMENT(old_element))
5440     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5441                                CE_LEFT_BY_PLAYER,
5442                                player->index_bit, leave_side);
5443
5444   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5445                                       CE_PLAYER_LEAVES_X,
5446                                       player->index_bit, leave_side);
5447
5448   Feld[jx][jy] = el_player;
5449   InitPlayerField(jx, jy, el_player, TRUE);
5450
5451   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5452   {
5453     Feld[jx][jy] = element;
5454     InitField(jx, jy, FALSE);
5455   }
5456
5457   /* only visually relocate centered player */
5458   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5459                      FALSE, level.instant_relocation);
5460
5461   TestIfPlayerTouchesBadThing(jx, jy);
5462   TestIfPlayerTouchesCustomElement(jx, jy);
5463
5464   if (IS_CUSTOM_ELEMENT(element))
5465     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5466                                player->index_bit, enter_side);
5467
5468   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5469                                       player->index_bit, enter_side);
5470 }
5471
5472 void Explode(int ex, int ey, int phase, int mode)
5473 {
5474   int x, y;
5475   int last_phase;
5476   int border_element;
5477
5478   /* !!! eliminate this variable !!! */
5479   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5480
5481   if (game.explosions_delayed)
5482   {
5483     ExplodeField[ex][ey] = mode;
5484     return;
5485   }
5486
5487   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5488   {
5489     int center_element = Feld[ex][ey];
5490     int artwork_element, explosion_element;     /* set these values later */
5491
5492 #if 0
5493     /* --- This is only really needed (and now handled) in "Impact()". --- */
5494     /* do not explode moving elements that left the explode field in time */
5495     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5496         center_element == EL_EMPTY &&
5497         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5498       return;
5499 #endif
5500
5501 #if 0
5502     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5503     if (mode == EX_TYPE_NORMAL ||
5504         mode == EX_TYPE_CENTER ||
5505         mode == EX_TYPE_CROSS)
5506       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5507 #endif
5508
5509     /* remove things displayed in background while burning dynamite */
5510     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5511       Back[ex][ey] = 0;
5512
5513     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5514     {
5515       /* put moving element to center field (and let it explode there) */
5516       center_element = MovingOrBlocked2Element(ex, ey);
5517       RemoveMovingField(ex, ey);
5518       Feld[ex][ey] = center_element;
5519     }
5520
5521     /* now "center_element" is finally determined -- set related values now */
5522     artwork_element = center_element;           /* for custom player artwork */
5523     explosion_element = center_element;         /* for custom player artwork */
5524
5525     if (IS_PLAYER(ex, ey))
5526     {
5527       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5528
5529       artwork_element = stored_player[player_nr].artwork_element;
5530
5531       if (level.use_explosion_element[player_nr])
5532       {
5533         explosion_element = level.explosion_element[player_nr];
5534         artwork_element = explosion_element;
5535       }
5536     }
5537
5538 #if 1
5539     if (mode == EX_TYPE_NORMAL ||
5540         mode == EX_TYPE_CENTER ||
5541         mode == EX_TYPE_CROSS)
5542       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5543 #endif
5544
5545     last_phase = element_info[explosion_element].explosion_delay + 1;
5546
5547     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5548     {
5549       int xx = x - ex + 1;
5550       int yy = y - ey + 1;
5551       int element;
5552
5553       if (!IN_LEV_FIELD(x, y) ||
5554           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5555           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5556         continue;
5557
5558       element = Feld[x][y];
5559
5560       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5561       {
5562         element = MovingOrBlocked2Element(x, y);
5563
5564         if (!IS_EXPLOSION_PROOF(element))
5565           RemoveMovingField(x, y);
5566       }
5567
5568       /* indestructible elements can only explode in center (but not flames) */
5569       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5570                                            mode == EX_TYPE_BORDER)) ||
5571           element == EL_FLAMES)
5572         continue;
5573
5574       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5575          behaviour, for example when touching a yamyam that explodes to rocks
5576          with active deadly shield, a rock is created under the player !!! */
5577       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5578 #if 0
5579       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5580           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5581            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5582 #else
5583       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5584 #endif
5585       {
5586         if (IS_ACTIVE_BOMB(element))
5587         {
5588           /* re-activate things under the bomb like gate or penguin */
5589           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5590           Back[x][y] = 0;
5591         }
5592
5593         continue;
5594       }
5595
5596       /* save walkable background elements while explosion on same tile */
5597       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5598           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5599         Back[x][y] = element;
5600
5601       /* ignite explodable elements reached by other explosion */
5602       if (element == EL_EXPLOSION)
5603         element = Store2[x][y];
5604
5605       if (AmoebaNr[x][y] &&
5606           (element == EL_AMOEBA_FULL ||
5607            element == EL_BD_AMOEBA ||
5608            element == EL_AMOEBA_GROWING))
5609       {
5610         AmoebaCnt[AmoebaNr[x][y]]--;
5611         AmoebaCnt2[AmoebaNr[x][y]]--;
5612       }
5613
5614       RemoveField(x, y);
5615
5616       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5617       {
5618         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5619
5620         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5621
5622         if (PLAYERINFO(ex, ey)->use_murphy)
5623           Store[x][y] = EL_EMPTY;
5624       }
5625
5626       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5627          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5628       else if (ELEM_IS_PLAYER(center_element))
5629         Store[x][y] = EL_EMPTY;
5630       else if (center_element == EL_YAMYAM)
5631         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5632       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5633         Store[x][y] = element_info[center_element].content.e[xx][yy];
5634 #if 1
5635       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5636          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5637          otherwise) -- FIX THIS !!! */
5638       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5639         Store[x][y] = element_info[element].content.e[1][1];
5640 #else
5641       else if (!CAN_EXPLODE(element))
5642         Store[x][y] = element_info[element].content.e[1][1];
5643 #endif
5644       else
5645         Store[x][y] = EL_EMPTY;
5646
5647       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5648           center_element == EL_AMOEBA_TO_DIAMOND)
5649         Store2[x][y] = element;
5650
5651       Feld[x][y] = EL_EXPLOSION;
5652       GfxElement[x][y] = artwork_element;
5653
5654       ExplodePhase[x][y] = 1;
5655       ExplodeDelay[x][y] = last_phase;
5656
5657       Stop[x][y] = TRUE;
5658     }
5659
5660     if (center_element == EL_YAMYAM)
5661       game.yamyam_content_nr =
5662         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5663
5664     return;
5665   }
5666
5667   if (Stop[ex][ey])
5668     return;
5669
5670   x = ex;
5671   y = ey;
5672
5673   if (phase == 1)
5674     GfxFrame[x][y] = 0;         /* restart explosion animation */
5675
5676   last_phase = ExplodeDelay[x][y];
5677
5678   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5679
5680 #ifdef DEBUG
5681
5682   /* activate this even in non-DEBUG version until cause for crash in
5683      getGraphicAnimationFrame() (see below) is found and eliminated */
5684
5685 #endif
5686 #if 1
5687
5688 #if 1
5689   /* this can happen if the player leaves an explosion just in time */
5690   if (GfxElement[x][y] == EL_UNDEFINED)
5691     GfxElement[x][y] = EL_EMPTY;
5692 #else
5693   if (GfxElement[x][y] == EL_UNDEFINED)
5694   {
5695     printf("\n\n");
5696     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5697     printf("Explode(): This should never happen!\n");
5698     printf("\n\n");
5699
5700     GfxElement[x][y] = EL_EMPTY;
5701   }
5702 #endif
5703
5704 #endif
5705
5706   border_element = Store2[x][y];
5707   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5708     border_element = StorePlayer[x][y];
5709
5710   if (phase == element_info[border_element].ignition_delay ||
5711       phase == last_phase)
5712   {
5713     boolean border_explosion = FALSE;
5714
5715     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5716         !PLAYER_EXPLOSION_PROTECTED(x, y))
5717     {
5718       KillPlayerUnlessExplosionProtected(x, y);
5719       border_explosion = TRUE;
5720     }
5721     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5722     {
5723       Feld[x][y] = Store2[x][y];
5724       Store2[x][y] = 0;
5725       Bang(x, y);
5726       border_explosion = TRUE;
5727     }
5728     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5729     {
5730       AmoebeUmwandeln(x, y);
5731       Store2[x][y] = 0;
5732       border_explosion = TRUE;
5733     }
5734
5735     /* if an element just explodes due to another explosion (chain-reaction),
5736        do not immediately end the new explosion when it was the last frame of
5737        the explosion (as it would be done in the following "if"-statement!) */
5738     if (border_explosion && phase == last_phase)
5739       return;
5740   }
5741
5742   if (phase == last_phase)
5743   {
5744     int element;
5745
5746     element = Feld[x][y] = Store[x][y];
5747     Store[x][y] = Store2[x][y] = 0;
5748     GfxElement[x][y] = EL_UNDEFINED;
5749
5750     /* player can escape from explosions and might therefore be still alive */
5751     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5752         element <= EL_PLAYER_IS_EXPLODING_4)
5753     {
5754       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5755       int explosion_element = EL_PLAYER_1 + player_nr;
5756       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5757       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5758
5759       if (level.use_explosion_element[player_nr])
5760         explosion_element = level.explosion_element[player_nr];
5761
5762       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5763                     element_info[explosion_element].content.e[xx][yy]);
5764     }
5765
5766     /* restore probably existing indestructible background element */
5767     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5768       element = Feld[x][y] = Back[x][y];
5769     Back[x][y] = 0;
5770
5771     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5772     GfxDir[x][y] = MV_NONE;
5773     ChangeDelay[x][y] = 0;
5774     ChangePage[x][y] = -1;
5775
5776 #if USE_NEW_CUSTOM_VALUE
5777     CustomValue[x][y] = 0;
5778 #endif
5779
5780     InitField_WithBug2(x, y, FALSE);
5781
5782     DrawLevelField(x, y);
5783
5784     TestIfElementTouchesCustomElement(x, y);
5785
5786     if (GFX_CRUMBLED(element))
5787       DrawLevelFieldCrumbledSandNeighbours(x, y);
5788
5789     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5790       StorePlayer[x][y] = 0;
5791
5792     if (ELEM_IS_PLAYER(element))
5793       RelocatePlayer(x, y, element);
5794   }
5795   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5796   {
5797     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5798     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5799
5800     if (phase == delay)
5801       DrawLevelFieldCrumbledSand(x, y);
5802
5803     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5804     {
5805       DrawLevelElement(x, y, Back[x][y]);
5806       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5807     }
5808     else if (IS_WALKABLE_UNDER(Back[x][y]))
5809     {
5810       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5811       DrawLevelElementThruMask(x, y, Back[x][y]);
5812     }
5813     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5814       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5815   }
5816 }
5817
5818 void DynaExplode(int ex, int ey)
5819 {
5820   int i, j;
5821   int dynabomb_element = Feld[ex][ey];
5822   int dynabomb_size = 1;
5823   boolean dynabomb_xl = FALSE;
5824   struct PlayerInfo *player;
5825   static int xy[4][2] =
5826   {
5827     { 0, -1 },
5828     { -1, 0 },
5829     { +1, 0 },
5830     { 0, +1 }
5831   };
5832
5833   if (IS_ACTIVE_BOMB(dynabomb_element))
5834   {
5835     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5836     dynabomb_size = player->dynabomb_size;
5837     dynabomb_xl = player->dynabomb_xl;
5838     player->dynabombs_left++;
5839   }
5840
5841   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5842
5843   for (i = 0; i < NUM_DIRECTIONS; i++)
5844   {
5845     for (j = 1; j <= dynabomb_size; j++)
5846     {
5847       int x = ex + j * xy[i][0];
5848       int y = ey + j * xy[i][1];
5849       int element;
5850
5851       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5852         break;
5853
5854       element = Feld[x][y];
5855
5856       /* do not restart explosions of fields with active bombs */
5857       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5858         continue;
5859
5860       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5861
5862       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5863           !IS_DIGGABLE(element) && !dynabomb_xl)
5864         break;
5865     }
5866   }
5867 }
5868
5869 void Bang(int x, int y)
5870 {
5871   int element = MovingOrBlocked2Element(x, y);
5872   int explosion_type = EX_TYPE_NORMAL;
5873
5874   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5875   {
5876     struct PlayerInfo *player = PLAYERINFO(x, y);
5877
5878     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5879                             player->element_nr);
5880
5881     if (level.use_explosion_element[player->index_nr])
5882     {
5883       int explosion_element = level.explosion_element[player->index_nr];
5884
5885       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5886         explosion_type = EX_TYPE_CROSS;
5887       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5888         explosion_type = EX_TYPE_CENTER;
5889     }
5890   }
5891
5892   switch (element)
5893   {
5894     case EL_BUG:
5895     case EL_SPACESHIP:
5896     case EL_BD_BUTTERFLY:
5897     case EL_BD_FIREFLY:
5898     case EL_YAMYAM:
5899     case EL_DARK_YAMYAM:
5900     case EL_ROBOT:
5901     case EL_PACMAN:
5902     case EL_MOLE:
5903       RaiseScoreElement(element);
5904       break;
5905
5906     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5907     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5908     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5909     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5910     case EL_DYNABOMB_INCREASE_NUMBER:
5911     case EL_DYNABOMB_INCREASE_SIZE:
5912     case EL_DYNABOMB_INCREASE_POWER:
5913       explosion_type = EX_TYPE_DYNA;
5914       break;
5915
5916     case EL_DC_LANDMINE:
5917 #if 0
5918     case EL_EM_EXIT_OPEN:
5919     case EL_EM_STEEL_EXIT_OPEN:
5920 #endif
5921       explosion_type = EX_TYPE_CENTER;
5922       break;
5923
5924     case EL_PENGUIN:
5925     case EL_LAMP:
5926     case EL_LAMP_ACTIVE:
5927     case EL_AMOEBA_TO_DIAMOND:
5928       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5929         explosion_type = EX_TYPE_CENTER;
5930       break;
5931
5932     default:
5933       if (element_info[element].explosion_type == EXPLODES_CROSS)
5934         explosion_type = EX_TYPE_CROSS;
5935       else if (element_info[element].explosion_type == EXPLODES_1X1)
5936         explosion_type = EX_TYPE_CENTER;
5937       break;
5938   }
5939
5940   if (explosion_type == EX_TYPE_DYNA)
5941     DynaExplode(x, y);
5942   else
5943     Explode(x, y, EX_PHASE_START, explosion_type);
5944
5945   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5946 }
5947
5948 void SplashAcid(int x, int y)
5949 {
5950   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5951       (!IN_LEV_FIELD(x - 1, y - 2) ||
5952        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5953     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5954
5955   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5956       (!IN_LEV_FIELD(x + 1, y - 2) ||
5957        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5958     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5959
5960   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5961 }
5962
5963 static void InitBeltMovement()
5964 {
5965   static int belt_base_element[4] =
5966   {
5967     EL_CONVEYOR_BELT_1_LEFT,
5968     EL_CONVEYOR_BELT_2_LEFT,
5969     EL_CONVEYOR_BELT_3_LEFT,
5970     EL_CONVEYOR_BELT_4_LEFT
5971   };
5972   static int belt_base_active_element[4] =
5973   {
5974     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5975     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5976     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5977     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5978   };
5979
5980   int x, y, i, j;
5981
5982   /* set frame order for belt animation graphic according to belt direction */
5983   for (i = 0; i < NUM_BELTS; i++)
5984   {
5985     int belt_nr = i;
5986
5987     for (j = 0; j < NUM_BELT_PARTS; j++)
5988     {
5989       int element = belt_base_active_element[belt_nr] + j;
5990       int graphic_1 = el2img(element);
5991       int graphic_2 = el2panelimg(element);
5992
5993       if (game.belt_dir[i] == MV_LEFT)
5994       {
5995         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5996         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5997       }
5998       else
5999       {
6000         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6001         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6002       }
6003     }
6004   }
6005
6006   SCAN_PLAYFIELD(x, y)
6007   {
6008     int element = Feld[x][y];
6009
6010     for (i = 0; i < NUM_BELTS; i++)
6011     {
6012       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6013       {
6014         int e_belt_nr = getBeltNrFromBeltElement(element);
6015         int belt_nr = i;
6016
6017         if (e_belt_nr == belt_nr)
6018         {
6019           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6020
6021           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6022         }
6023       }
6024     }
6025   }
6026 }
6027
6028 static void ToggleBeltSwitch(int x, int y)
6029 {
6030   static int belt_base_element[4] =
6031   {
6032     EL_CONVEYOR_BELT_1_LEFT,
6033     EL_CONVEYOR_BELT_2_LEFT,
6034     EL_CONVEYOR_BELT_3_LEFT,
6035     EL_CONVEYOR_BELT_4_LEFT
6036   };
6037   static int belt_base_active_element[4] =
6038   {
6039     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6040     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6041     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6042     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6043   };
6044   static int belt_base_switch_element[4] =
6045   {
6046     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6047     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6048     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6049     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6050   };
6051   static int belt_move_dir[4] =
6052   {
6053     MV_LEFT,
6054     MV_NONE,
6055     MV_RIGHT,
6056     MV_NONE,
6057   };
6058
6059   int element = Feld[x][y];
6060   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6061   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6062   int belt_dir = belt_move_dir[belt_dir_nr];
6063   int xx, yy, i;
6064
6065   if (!IS_BELT_SWITCH(element))
6066     return;
6067
6068   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6069   game.belt_dir[belt_nr] = belt_dir;
6070
6071   if (belt_dir_nr == 3)
6072     belt_dir_nr = 1;
6073
6074   /* set frame order for belt animation graphic according to belt direction */
6075   for (i = 0; i < NUM_BELT_PARTS; i++)
6076   {
6077     int element = belt_base_active_element[belt_nr] + i;
6078     int graphic_1 = el2img(element);
6079     int graphic_2 = el2panelimg(element);
6080
6081     if (belt_dir == MV_LEFT)
6082     {
6083       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6084       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6085     }
6086     else
6087     {
6088       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6089       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6090     }
6091   }
6092
6093   SCAN_PLAYFIELD(xx, yy)
6094   {
6095     int element = Feld[xx][yy];
6096
6097     if (IS_BELT_SWITCH(element))
6098     {
6099       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6100
6101       if (e_belt_nr == belt_nr)
6102       {
6103         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6104         DrawLevelField(xx, yy);
6105       }
6106     }
6107     else if (IS_BELT(element) && belt_dir != MV_NONE)
6108     {
6109       int e_belt_nr = getBeltNrFromBeltElement(element);
6110
6111       if (e_belt_nr == belt_nr)
6112       {
6113         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6114
6115         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6116         DrawLevelField(xx, yy);
6117       }
6118     }
6119     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6120     {
6121       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6122
6123       if (e_belt_nr == belt_nr)
6124       {
6125         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6126
6127         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6128         DrawLevelField(xx, yy);
6129       }
6130     }
6131   }
6132 }
6133
6134 static void ToggleSwitchgateSwitch(int x, int y)
6135 {
6136   int xx, yy;
6137
6138   game.switchgate_pos = !game.switchgate_pos;
6139
6140   SCAN_PLAYFIELD(xx, yy)
6141   {
6142     int element = Feld[xx][yy];
6143
6144 #if !USE_BOTH_SWITCHGATE_SWITCHES
6145     if (element == EL_SWITCHGATE_SWITCH_UP ||
6146         element == EL_SWITCHGATE_SWITCH_DOWN)
6147     {
6148       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6149       DrawLevelField(xx, yy);
6150     }
6151     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6152              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6153     {
6154       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6155       DrawLevelField(xx, yy);
6156     }
6157 #else
6158     if (element == EL_SWITCHGATE_SWITCH_UP)
6159     {
6160       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6161       DrawLevelField(xx, yy);
6162     }
6163     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6164     {
6165       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6166       DrawLevelField(xx, yy);
6167     }
6168     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6169     {
6170       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6171       DrawLevelField(xx, yy);
6172     }
6173     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6174     {
6175       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6176       DrawLevelField(xx, yy);
6177     }
6178 #endif
6179     else if (element == EL_SWITCHGATE_OPEN ||
6180              element == EL_SWITCHGATE_OPENING)
6181     {
6182       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6183
6184       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6185     }
6186     else if (element == EL_SWITCHGATE_CLOSED ||
6187              element == EL_SWITCHGATE_CLOSING)
6188     {
6189       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6190
6191       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6192     }
6193   }
6194 }
6195
6196 static int getInvisibleActiveFromInvisibleElement(int element)
6197 {
6198   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6199           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6200           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6201           element);
6202 }
6203
6204 static int getInvisibleFromInvisibleActiveElement(int element)
6205 {
6206   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6207           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6208           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6209           element);
6210 }
6211
6212 static void RedrawAllLightSwitchesAndInvisibleElements()
6213 {
6214   int x, y;
6215
6216   SCAN_PLAYFIELD(x, y)
6217   {
6218     int element = Feld[x][y];
6219
6220     if (element == EL_LIGHT_SWITCH &&
6221         game.light_time_left > 0)
6222     {
6223       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6224       DrawLevelField(x, y);
6225     }
6226     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6227              game.light_time_left == 0)
6228     {
6229       Feld[x][y] = EL_LIGHT_SWITCH;
6230       DrawLevelField(x, y);
6231     }
6232     else if (element == EL_EMC_DRIPPER &&
6233              game.light_time_left > 0)
6234     {
6235       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6236       DrawLevelField(x, y);
6237     }
6238     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6239              game.light_time_left == 0)
6240     {
6241       Feld[x][y] = EL_EMC_DRIPPER;
6242       DrawLevelField(x, y);
6243     }
6244     else if (element == EL_INVISIBLE_STEELWALL ||
6245              element == EL_INVISIBLE_WALL ||
6246              element == EL_INVISIBLE_SAND)
6247     {
6248       if (game.light_time_left > 0)
6249         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6250
6251       DrawLevelField(x, y);
6252
6253       /* uncrumble neighbour fields, if needed */
6254       if (element == EL_INVISIBLE_SAND)
6255         DrawLevelFieldCrumbledSandNeighbours(x, y);
6256     }
6257     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6258              element == EL_INVISIBLE_WALL_ACTIVE ||
6259              element == EL_INVISIBLE_SAND_ACTIVE)
6260     {
6261       if (game.light_time_left == 0)
6262         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6263
6264       DrawLevelField(x, y);
6265
6266       /* re-crumble neighbour fields, if needed */
6267       if (element == EL_INVISIBLE_SAND)
6268         DrawLevelFieldCrumbledSandNeighbours(x, y);
6269     }
6270   }
6271 }
6272
6273 static void RedrawAllInvisibleElementsForLenses()
6274 {
6275   int x, y;
6276
6277   SCAN_PLAYFIELD(x, y)
6278   {
6279     int element = Feld[x][y];
6280
6281     if (element == EL_EMC_DRIPPER &&
6282         game.lenses_time_left > 0)
6283     {
6284       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6285       DrawLevelField(x, y);
6286     }
6287     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6288              game.lenses_time_left == 0)
6289     {
6290       Feld[x][y] = EL_EMC_DRIPPER;
6291       DrawLevelField(x, y);
6292     }
6293     else if (element == EL_INVISIBLE_STEELWALL ||
6294              element == EL_INVISIBLE_WALL ||
6295              element == EL_INVISIBLE_SAND)
6296     {
6297       if (game.lenses_time_left > 0)
6298         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6299
6300       DrawLevelField(x, y);
6301
6302       /* uncrumble neighbour fields, if needed */
6303       if (element == EL_INVISIBLE_SAND)
6304         DrawLevelFieldCrumbledSandNeighbours(x, y);
6305     }
6306     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6307              element == EL_INVISIBLE_WALL_ACTIVE ||
6308              element == EL_INVISIBLE_SAND_ACTIVE)
6309     {
6310       if (game.lenses_time_left == 0)
6311         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6312
6313       DrawLevelField(x, y);
6314
6315       /* re-crumble neighbour fields, if needed */
6316       if (element == EL_INVISIBLE_SAND)
6317         DrawLevelFieldCrumbledSandNeighbours(x, y);
6318     }
6319   }
6320 }
6321
6322 static void RedrawAllInvisibleElementsForMagnifier()
6323 {
6324   int x, y;
6325
6326   SCAN_PLAYFIELD(x, y)
6327   {
6328     int element = Feld[x][y];
6329
6330     if (element == EL_EMC_FAKE_GRASS &&
6331         game.magnify_time_left > 0)
6332     {
6333       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6334       DrawLevelField(x, y);
6335     }
6336     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6337              game.magnify_time_left == 0)
6338     {
6339       Feld[x][y] = EL_EMC_FAKE_GRASS;
6340       DrawLevelField(x, y);
6341     }
6342     else if (IS_GATE_GRAY(element) &&
6343              game.magnify_time_left > 0)
6344     {
6345       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6346                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6347                     IS_EM_GATE_GRAY(element) ?
6348                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6349                     IS_EMC_GATE_GRAY(element) ?
6350                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6351                     element);
6352       DrawLevelField(x, y);
6353     }
6354     else if (IS_GATE_GRAY_ACTIVE(element) &&
6355              game.magnify_time_left == 0)
6356     {
6357       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6358                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6359                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6360                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6361                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6362                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6363                     element);
6364       DrawLevelField(x, y);
6365     }
6366   }
6367 }
6368
6369 static void ToggleLightSwitch(int x, int y)
6370 {
6371   int element = Feld[x][y];
6372
6373   game.light_time_left =
6374     (element == EL_LIGHT_SWITCH ?
6375      level.time_light * FRAMES_PER_SECOND : 0);
6376
6377   RedrawAllLightSwitchesAndInvisibleElements();
6378 }
6379
6380 static void ActivateTimegateSwitch(int x, int y)
6381 {
6382   int xx, yy;
6383
6384   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6385
6386   SCAN_PLAYFIELD(xx, yy)
6387   {
6388     int element = Feld[xx][yy];
6389
6390     if (element == EL_TIMEGATE_CLOSED ||
6391         element == EL_TIMEGATE_CLOSING)
6392     {
6393       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6394       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6395     }
6396
6397     /*
6398     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6399     {
6400       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6401       DrawLevelField(xx, yy);
6402     }
6403     */
6404
6405   }
6406
6407 #if 1
6408   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6409                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6410 #else
6411   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6412 #endif
6413 }
6414
6415 void Impact(int x, int y)
6416 {
6417   boolean last_line = (y == lev_fieldy - 1);
6418   boolean object_hit = FALSE;
6419   boolean impact = (last_line || object_hit);
6420   int element = Feld[x][y];
6421   int smashed = EL_STEELWALL;
6422
6423   if (!last_line)       /* check if element below was hit */
6424   {
6425     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6426       return;
6427
6428     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6429                                          MovDir[x][y + 1] != MV_DOWN ||
6430                                          MovPos[x][y + 1] <= TILEY / 2));
6431
6432     /* do not smash moving elements that left the smashed field in time */
6433     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6434         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6435       object_hit = FALSE;
6436
6437 #if USE_QUICKSAND_IMPACT_BUGFIX
6438     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6439     {
6440       RemoveMovingField(x, y + 1);
6441       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6442       Feld[x][y + 2] = EL_ROCK;
6443       DrawLevelField(x, y + 2);
6444
6445       object_hit = TRUE;
6446     }
6447
6448     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6449     {
6450       RemoveMovingField(x, y + 1);
6451       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6452       Feld[x][y + 2] = EL_ROCK;
6453       DrawLevelField(x, y + 2);
6454
6455       object_hit = TRUE;
6456     }
6457 #endif
6458
6459     if (object_hit)
6460       smashed = MovingOrBlocked2Element(x, y + 1);
6461
6462     impact = (last_line || object_hit);
6463   }
6464
6465   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6466   {
6467     SplashAcid(x, y + 1);
6468     return;
6469   }
6470
6471   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6472   /* only reset graphic animation if graphic really changes after impact */
6473   if (impact &&
6474       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6475   {
6476     ResetGfxAnimation(x, y);
6477     DrawLevelField(x, y);
6478   }
6479
6480   if (impact && CAN_EXPLODE_IMPACT(element))
6481   {
6482     Bang(x, y);
6483     return;
6484   }
6485   else if (impact && element == EL_PEARL &&
6486            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6487   {
6488     ResetGfxAnimation(x, y);
6489
6490     Feld[x][y] = EL_PEARL_BREAKING;
6491     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6492     return;
6493   }
6494   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6495   {
6496     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6497
6498     return;
6499   }
6500
6501   if (impact && element == EL_AMOEBA_DROP)
6502   {
6503     if (object_hit && IS_PLAYER(x, y + 1))
6504       KillPlayerUnlessEnemyProtected(x, y + 1);
6505     else if (object_hit && smashed == EL_PENGUIN)
6506       Bang(x, y + 1);
6507     else
6508     {
6509       Feld[x][y] = EL_AMOEBA_GROWING;
6510       Store[x][y] = EL_AMOEBA_WET;
6511
6512       ResetRandomAnimationValue(x, y);
6513     }
6514     return;
6515   }
6516
6517   if (object_hit)               /* check which object was hit */
6518   {
6519     if ((CAN_PASS_MAGIC_WALL(element) && 
6520          (smashed == EL_MAGIC_WALL ||
6521           smashed == EL_BD_MAGIC_WALL)) ||
6522         (CAN_PASS_DC_MAGIC_WALL(element) &&
6523          smashed == EL_DC_MAGIC_WALL))
6524     {
6525       int xx, yy;
6526       int activated_magic_wall =
6527         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6528          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6529          EL_DC_MAGIC_WALL_ACTIVE);
6530
6531       /* activate magic wall / mill */
6532       SCAN_PLAYFIELD(xx, yy)
6533       {
6534         if (Feld[xx][yy] == smashed)
6535           Feld[xx][yy] = activated_magic_wall;
6536       }
6537
6538       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6539       game.magic_wall_active = TRUE;
6540
6541       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6542                             SND_MAGIC_WALL_ACTIVATING :
6543                             smashed == EL_BD_MAGIC_WALL ?
6544                             SND_BD_MAGIC_WALL_ACTIVATING :
6545                             SND_DC_MAGIC_WALL_ACTIVATING));
6546     }
6547
6548     if (IS_PLAYER(x, y + 1))
6549     {
6550       if (CAN_SMASH_PLAYER(element))
6551       {
6552         KillPlayerUnlessEnemyProtected(x, y + 1);
6553         return;
6554       }
6555     }
6556     else if (smashed == EL_PENGUIN)
6557     {
6558       if (CAN_SMASH_PLAYER(element))
6559       {
6560         Bang(x, y + 1);
6561         return;
6562       }
6563     }
6564     else if (element == EL_BD_DIAMOND)
6565     {
6566       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6567       {
6568         Bang(x, y + 1);
6569         return;
6570       }
6571     }
6572     else if (((element == EL_SP_INFOTRON ||
6573                element == EL_SP_ZONK) &&
6574               (smashed == EL_SP_SNIKSNAK ||
6575                smashed == EL_SP_ELECTRON ||
6576                smashed == EL_SP_DISK_ORANGE)) ||
6577              (element == EL_SP_INFOTRON &&
6578               smashed == EL_SP_DISK_YELLOW))
6579     {
6580       Bang(x, y + 1);
6581       return;
6582     }
6583     else if (CAN_SMASH_EVERYTHING(element))
6584     {
6585       if (IS_CLASSIC_ENEMY(smashed) ||
6586           CAN_EXPLODE_SMASHED(smashed))
6587       {
6588         Bang(x, y + 1);
6589         return;
6590       }
6591       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6592       {
6593         if (smashed == EL_LAMP ||
6594             smashed == EL_LAMP_ACTIVE)
6595         {
6596           Bang(x, y + 1);
6597           return;
6598         }
6599         else if (smashed == EL_NUT)
6600         {
6601           Feld[x][y + 1] = EL_NUT_BREAKING;
6602           PlayLevelSound(x, y, SND_NUT_BREAKING);
6603           RaiseScoreElement(EL_NUT);
6604           return;
6605         }
6606         else if (smashed == EL_PEARL)
6607         {
6608           ResetGfxAnimation(x, y);
6609
6610           Feld[x][y + 1] = EL_PEARL_BREAKING;
6611           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6612           return;
6613         }
6614         else if (smashed == EL_DIAMOND)
6615         {
6616           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6617           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6618           return;
6619         }
6620         else if (IS_BELT_SWITCH(smashed))
6621         {
6622           ToggleBeltSwitch(x, y + 1);
6623         }
6624         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6625                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6626                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6627                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6628         {
6629           ToggleSwitchgateSwitch(x, y + 1);
6630         }
6631         else if (smashed == EL_LIGHT_SWITCH ||
6632                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6633         {
6634           ToggleLightSwitch(x, y + 1);
6635         }
6636         else
6637         {
6638 #if 0
6639           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6640 #endif
6641
6642           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6643
6644           CheckElementChangeBySide(x, y + 1, smashed, element,
6645                                    CE_SWITCHED, CH_SIDE_TOP);
6646           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6647                                             CH_SIDE_TOP);
6648         }
6649       }
6650       else
6651       {
6652         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6653       }
6654     }
6655   }
6656
6657   /* play sound of magic wall / mill */
6658   if (!last_line &&
6659       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6660        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6661        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6662   {
6663     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6664       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6665     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6666       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6667     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6668       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6669
6670     return;
6671   }
6672
6673   /* play sound of object that hits the ground */
6674   if (last_line || object_hit)
6675     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6676 }
6677
6678 inline static void TurnRoundExt(int x, int y)
6679 {
6680   static struct
6681   {
6682     int dx, dy;
6683   } move_xy[] =
6684   {
6685     {  0,  0 },
6686     { -1,  0 },
6687     { +1,  0 },
6688     {  0,  0 },
6689     {  0, -1 },
6690     {  0,  0 }, { 0, 0 }, { 0, 0 },
6691     {  0, +1 }
6692   };
6693   static struct
6694   {
6695     int left, right, back;
6696   } turn[] =
6697   {
6698     { 0,        0,              0        },
6699     { MV_DOWN,  MV_UP,          MV_RIGHT },
6700     { MV_UP,    MV_DOWN,        MV_LEFT  },
6701     { 0,        0,              0        },
6702     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6703     { 0,        0,              0        },
6704     { 0,        0,              0        },
6705     { 0,        0,              0        },
6706     { MV_RIGHT, MV_LEFT,        MV_UP    }
6707   };
6708
6709   int element = Feld[x][y];
6710   int move_pattern = element_info[element].move_pattern;
6711
6712   int old_move_dir = MovDir[x][y];
6713   int left_dir  = turn[old_move_dir].left;
6714   int right_dir = turn[old_move_dir].right;
6715   int back_dir  = turn[old_move_dir].back;
6716
6717   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6718   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6719   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6720   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6721
6722   int left_x  = x + left_dx,  left_y  = y + left_dy;
6723   int right_x = x + right_dx, right_y = y + right_dy;
6724   int move_x  = x + move_dx,  move_y  = y + move_dy;
6725
6726   int xx, yy;
6727
6728   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6729   {
6730     TestIfBadThingTouchesOtherBadThing(x, y);
6731
6732     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6733       MovDir[x][y] = right_dir;
6734     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6735       MovDir[x][y] = left_dir;
6736
6737     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6738       MovDelay[x][y] = 9;
6739     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6740       MovDelay[x][y] = 1;
6741   }
6742   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6743   {
6744     TestIfBadThingTouchesOtherBadThing(x, y);
6745
6746     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6747       MovDir[x][y] = left_dir;
6748     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6749       MovDir[x][y] = right_dir;
6750
6751     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6752       MovDelay[x][y] = 9;
6753     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6754       MovDelay[x][y] = 1;
6755   }
6756   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6757   {
6758     TestIfBadThingTouchesOtherBadThing(x, y);
6759
6760     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6761       MovDir[x][y] = left_dir;
6762     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6763       MovDir[x][y] = right_dir;
6764
6765     if (MovDir[x][y] != old_move_dir)
6766       MovDelay[x][y] = 9;
6767   }
6768   else if (element == EL_YAMYAM)
6769   {
6770     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6771     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6772
6773     if (can_turn_left && can_turn_right)
6774       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6775     else if (can_turn_left)
6776       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6777     else if (can_turn_right)
6778       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6779     else
6780       MovDir[x][y] = back_dir;
6781
6782     MovDelay[x][y] = 16 + 16 * RND(3);
6783   }
6784   else if (element == EL_DARK_YAMYAM)
6785   {
6786     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6787                                                          left_x, left_y);
6788     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6789                                                          right_x, right_y);
6790
6791     if (can_turn_left && can_turn_right)
6792       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6793     else if (can_turn_left)
6794       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6795     else if (can_turn_right)
6796       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6797     else
6798       MovDir[x][y] = back_dir;
6799
6800     MovDelay[x][y] = 16 + 16 * RND(3);
6801   }
6802   else if (element == EL_PACMAN)
6803   {
6804     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6805     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6806
6807     if (can_turn_left && can_turn_right)
6808       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6809     else if (can_turn_left)
6810       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6811     else if (can_turn_right)
6812       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6813     else
6814       MovDir[x][y] = back_dir;
6815
6816     MovDelay[x][y] = 6 + RND(40);
6817   }
6818   else if (element == EL_PIG)
6819   {
6820     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6821     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6822     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6823     boolean should_turn_left, should_turn_right, should_move_on;
6824     int rnd_value = 24;
6825     int rnd = RND(rnd_value);
6826
6827     should_turn_left = (can_turn_left &&
6828                         (!can_move_on ||
6829                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6830                                                    y + back_dy + left_dy)));
6831     should_turn_right = (can_turn_right &&
6832                          (!can_move_on ||
6833                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6834                                                     y + back_dy + right_dy)));
6835     should_move_on = (can_move_on &&
6836                       (!can_turn_left ||
6837                        !can_turn_right ||
6838                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6839                                                  y + move_dy + left_dy) ||
6840                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6841                                                  y + move_dy + right_dy)));
6842
6843     if (should_turn_left || should_turn_right || should_move_on)
6844     {
6845       if (should_turn_left && should_turn_right && should_move_on)
6846         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6847                         rnd < 2 * rnd_value / 3 ? right_dir :
6848                         old_move_dir);
6849       else if (should_turn_left && should_turn_right)
6850         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6851       else if (should_turn_left && should_move_on)
6852         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6853       else if (should_turn_right && should_move_on)
6854         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6855       else if (should_turn_left)
6856         MovDir[x][y] = left_dir;
6857       else if (should_turn_right)
6858         MovDir[x][y] = right_dir;
6859       else if (should_move_on)
6860         MovDir[x][y] = old_move_dir;
6861     }
6862     else if (can_move_on && rnd > rnd_value / 8)
6863       MovDir[x][y] = old_move_dir;
6864     else if (can_turn_left && can_turn_right)
6865       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6866     else if (can_turn_left && rnd > rnd_value / 8)
6867       MovDir[x][y] = left_dir;
6868     else if (can_turn_right && rnd > rnd_value/8)
6869       MovDir[x][y] = right_dir;
6870     else
6871       MovDir[x][y] = back_dir;
6872
6873     xx = x + move_xy[MovDir[x][y]].dx;
6874     yy = y + move_xy[MovDir[x][y]].dy;
6875
6876     if (!IN_LEV_FIELD(xx, yy) ||
6877         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6878       MovDir[x][y] = old_move_dir;
6879
6880     MovDelay[x][y] = 0;
6881   }
6882   else if (element == EL_DRAGON)
6883   {
6884     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6885     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6886     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6887     int rnd_value = 24;
6888     int rnd = RND(rnd_value);
6889
6890     if (can_move_on && rnd > rnd_value / 8)
6891       MovDir[x][y] = old_move_dir;
6892     else if (can_turn_left && can_turn_right)
6893       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6894     else if (can_turn_left && rnd > rnd_value / 8)
6895       MovDir[x][y] = left_dir;
6896     else if (can_turn_right && rnd > rnd_value / 8)
6897       MovDir[x][y] = right_dir;
6898     else
6899       MovDir[x][y] = back_dir;
6900
6901     xx = x + move_xy[MovDir[x][y]].dx;
6902     yy = y + move_xy[MovDir[x][y]].dy;
6903
6904     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6905       MovDir[x][y] = old_move_dir;
6906
6907     MovDelay[x][y] = 0;
6908   }
6909   else if (element == EL_MOLE)
6910   {
6911     boolean can_move_on =
6912       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6913                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6914                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6915     if (!can_move_on)
6916     {
6917       boolean can_turn_left =
6918         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6919                               IS_AMOEBOID(Feld[left_x][left_y])));
6920
6921       boolean can_turn_right =
6922         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6923                               IS_AMOEBOID(Feld[right_x][right_y])));
6924
6925       if (can_turn_left && can_turn_right)
6926         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6927       else if (can_turn_left)
6928         MovDir[x][y] = left_dir;
6929       else
6930         MovDir[x][y] = right_dir;
6931     }
6932
6933     if (MovDir[x][y] != old_move_dir)
6934       MovDelay[x][y] = 9;
6935   }
6936   else if (element == EL_BALLOON)
6937   {
6938     MovDir[x][y] = game.wind_direction;
6939     MovDelay[x][y] = 0;
6940   }
6941   else if (element == EL_SPRING)
6942   {
6943 #if USE_NEW_SPRING_BUMPER
6944     if (MovDir[x][y] & MV_HORIZONTAL)
6945     {
6946       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6947           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6948       {
6949         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6950         ResetGfxAnimation(move_x, move_y);
6951         DrawLevelField(move_x, move_y);
6952
6953         MovDir[x][y] = back_dir;
6954       }
6955       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6956                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6957         MovDir[x][y] = MV_NONE;
6958     }
6959 #else
6960     if (MovDir[x][y] & MV_HORIZONTAL &&
6961         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6962          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6963       MovDir[x][y] = MV_NONE;
6964 #endif
6965
6966     MovDelay[x][y] = 0;
6967   }
6968   else if (element == EL_ROBOT ||
6969            element == EL_SATELLITE ||
6970            element == EL_PENGUIN ||
6971            element == EL_EMC_ANDROID)
6972   {
6973     int attr_x = -1, attr_y = -1;
6974
6975     if (AllPlayersGone)
6976     {
6977       attr_x = ExitX;
6978       attr_y = ExitY;
6979     }
6980     else
6981     {
6982       int i;
6983
6984       for (i = 0; i < MAX_PLAYERS; i++)
6985       {
6986         struct PlayerInfo *player = &stored_player[i];
6987         int jx = player->jx, jy = player->jy;
6988
6989         if (!player->active)
6990           continue;
6991
6992         if (attr_x == -1 ||
6993             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6994         {
6995           attr_x = jx;
6996           attr_y = jy;
6997         }
6998       }
6999     }
7000
7001     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7002         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7003          game.engine_version < VERSION_IDENT(3,1,0,0)))
7004     {
7005       attr_x = ZX;
7006       attr_y = ZY;
7007     }
7008
7009     if (element == EL_PENGUIN)
7010     {
7011       int i;
7012       static int xy[4][2] =
7013       {
7014         { 0, -1 },
7015         { -1, 0 },
7016         { +1, 0 },
7017         { 0, +1 }
7018       };
7019
7020       for (i = 0; i < NUM_DIRECTIONS; i++)
7021       {
7022         int ex = x + xy[i][0];
7023         int ey = y + xy[i][1];
7024
7025         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7026                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7027                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7028                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7029         {
7030           attr_x = ex;
7031           attr_y = ey;
7032           break;
7033         }
7034       }
7035     }
7036
7037     MovDir[x][y] = MV_NONE;
7038     if (attr_x < x)
7039       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7040     else if (attr_x > x)
7041       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7042     if (attr_y < y)
7043       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7044     else if (attr_y > y)
7045       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7046
7047     if (element == EL_ROBOT)
7048     {
7049       int newx, newy;
7050
7051       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7052         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7053       Moving2Blocked(x, y, &newx, &newy);
7054
7055       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7056         MovDelay[x][y] = 8 + 8 * !RND(3);
7057       else
7058         MovDelay[x][y] = 16;
7059     }
7060     else if (element == EL_PENGUIN)
7061     {
7062       int newx, newy;
7063
7064       MovDelay[x][y] = 1;
7065
7066       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7067       {
7068         boolean first_horiz = RND(2);
7069         int new_move_dir = MovDir[x][y];
7070
7071         MovDir[x][y] =
7072           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7073         Moving2Blocked(x, y, &newx, &newy);
7074
7075         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7076           return;
7077
7078         MovDir[x][y] =
7079           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7080         Moving2Blocked(x, y, &newx, &newy);
7081
7082         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7083           return;
7084
7085         MovDir[x][y] = old_move_dir;
7086         return;
7087       }
7088     }
7089     else if (element == EL_SATELLITE)
7090     {
7091       int newx, newy;
7092
7093       MovDelay[x][y] = 1;
7094
7095       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7096       {
7097         boolean first_horiz = RND(2);
7098         int new_move_dir = MovDir[x][y];
7099
7100         MovDir[x][y] =
7101           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7102         Moving2Blocked(x, y, &newx, &newy);
7103
7104         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7105           return;
7106
7107         MovDir[x][y] =
7108           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7109         Moving2Blocked(x, y, &newx, &newy);
7110
7111         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7112           return;
7113
7114         MovDir[x][y] = old_move_dir;
7115         return;
7116       }
7117     }
7118     else if (element == EL_EMC_ANDROID)
7119     {
7120       static int check_pos[16] =
7121       {
7122         -1,             /*  0 => (invalid)          */
7123         7,              /*  1 => MV_LEFT            */
7124         3,              /*  2 => MV_RIGHT           */
7125         -1,             /*  3 => (invalid)          */
7126         1,              /*  4 =>            MV_UP   */
7127         0,              /*  5 => MV_LEFT  | MV_UP   */
7128         2,              /*  6 => MV_RIGHT | MV_UP   */
7129         -1,             /*  7 => (invalid)          */
7130         5,              /*  8 =>            MV_DOWN */
7131         6,              /*  9 => MV_LEFT  | MV_DOWN */
7132         4,              /* 10 => MV_RIGHT | MV_DOWN */
7133         -1,             /* 11 => (invalid)          */
7134         -1,             /* 12 => (invalid)          */
7135         -1,             /* 13 => (invalid)          */
7136         -1,             /* 14 => (invalid)          */
7137         -1,             /* 15 => (invalid)          */
7138       };
7139       static struct
7140       {
7141         int dx, dy;
7142         int dir;
7143       } check_xy[8] =
7144       {
7145         { -1, -1,       MV_LEFT  | MV_UP   },
7146         {  0, -1,                  MV_UP   },
7147         { +1, -1,       MV_RIGHT | MV_UP   },
7148         { +1,  0,       MV_RIGHT           },
7149         { +1, +1,       MV_RIGHT | MV_DOWN },
7150         {  0, +1,                  MV_DOWN },
7151         { -1, +1,       MV_LEFT  | MV_DOWN },
7152         { -1,  0,       MV_LEFT            },
7153       };
7154       int start_pos, check_order;
7155       boolean can_clone = FALSE;
7156       int i;
7157
7158       /* check if there is any free field around current position */
7159       for (i = 0; i < 8; i++)
7160       {
7161         int newx = x + check_xy[i].dx;
7162         int newy = y + check_xy[i].dy;
7163
7164         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7165         {
7166           can_clone = TRUE;
7167
7168           break;
7169         }
7170       }
7171
7172       if (can_clone)            /* randomly find an element to clone */
7173       {
7174         can_clone = FALSE;
7175
7176         start_pos = check_pos[RND(8)];
7177         check_order = (RND(2) ? -1 : +1);
7178
7179         for (i = 0; i < 8; i++)
7180         {
7181           int pos_raw = start_pos + i * check_order;
7182           int pos = (pos_raw + 8) % 8;
7183           int newx = x + check_xy[pos].dx;
7184           int newy = y + check_xy[pos].dy;
7185
7186           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7187           {
7188             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7189             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7190
7191             Store[x][y] = Feld[newx][newy];
7192
7193             can_clone = TRUE;
7194
7195             break;
7196           }
7197         }
7198       }
7199
7200       if (can_clone)            /* randomly find a direction to move */
7201       {
7202         can_clone = FALSE;
7203
7204         start_pos = check_pos[RND(8)];
7205         check_order = (RND(2) ? -1 : +1);
7206
7207         for (i = 0; i < 8; i++)
7208         {
7209           int pos_raw = start_pos + i * check_order;
7210           int pos = (pos_raw + 8) % 8;
7211           int newx = x + check_xy[pos].dx;
7212           int newy = y + check_xy[pos].dy;
7213           int new_move_dir = check_xy[pos].dir;
7214
7215           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7216           {
7217             MovDir[x][y] = new_move_dir;
7218             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7219
7220             can_clone = TRUE;
7221
7222             break;
7223           }
7224         }
7225       }
7226
7227       if (can_clone)            /* cloning and moving successful */
7228         return;
7229
7230       /* cannot clone -- try to move towards player */
7231
7232       start_pos = check_pos[MovDir[x][y] & 0x0f];
7233       check_order = (RND(2) ? -1 : +1);
7234
7235       for (i = 0; i < 3; i++)
7236       {
7237         /* first check start_pos, then previous/next or (next/previous) pos */
7238         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7239         int pos = (pos_raw + 8) % 8;
7240         int newx = x + check_xy[pos].dx;
7241         int newy = y + check_xy[pos].dy;
7242         int new_move_dir = check_xy[pos].dir;
7243
7244         if (IS_PLAYER(newx, newy))
7245           break;
7246
7247         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7248         {
7249           MovDir[x][y] = new_move_dir;
7250           MovDelay[x][y] = level.android_move_time * 8 + 1;
7251
7252           break;
7253         }
7254       }
7255     }
7256   }
7257   else if (move_pattern == MV_TURNING_LEFT ||
7258            move_pattern == MV_TURNING_RIGHT ||
7259            move_pattern == MV_TURNING_LEFT_RIGHT ||
7260            move_pattern == MV_TURNING_RIGHT_LEFT ||
7261            move_pattern == MV_TURNING_RANDOM ||
7262            move_pattern == MV_ALL_DIRECTIONS)
7263   {
7264     boolean can_turn_left =
7265       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7266     boolean can_turn_right =
7267       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7268
7269     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7270       return;
7271
7272     if (move_pattern == MV_TURNING_LEFT)
7273       MovDir[x][y] = left_dir;
7274     else if (move_pattern == MV_TURNING_RIGHT)
7275       MovDir[x][y] = right_dir;
7276     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7277       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7278     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7279       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7280     else if (move_pattern == MV_TURNING_RANDOM)
7281       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7282                       can_turn_right && !can_turn_left ? right_dir :
7283                       RND(2) ? left_dir : right_dir);
7284     else if (can_turn_left && can_turn_right)
7285       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7286     else if (can_turn_left)
7287       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7288     else if (can_turn_right)
7289       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7290     else
7291       MovDir[x][y] = back_dir;
7292
7293     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7294   }
7295   else if (move_pattern == MV_HORIZONTAL ||
7296            move_pattern == MV_VERTICAL)
7297   {
7298     if (move_pattern & old_move_dir)
7299       MovDir[x][y] = back_dir;
7300     else if (move_pattern == MV_HORIZONTAL)
7301       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7302     else if (move_pattern == MV_VERTICAL)
7303       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7304
7305     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7306   }
7307   else if (move_pattern & MV_ANY_DIRECTION)
7308   {
7309     MovDir[x][y] = move_pattern;
7310     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7311   }
7312   else if (move_pattern & MV_WIND_DIRECTION)
7313   {
7314     MovDir[x][y] = game.wind_direction;
7315     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7316   }
7317   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7318   {
7319     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7320       MovDir[x][y] = left_dir;
7321     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7322       MovDir[x][y] = right_dir;
7323
7324     if (MovDir[x][y] != old_move_dir)
7325       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7326   }
7327   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7328   {
7329     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7330       MovDir[x][y] = right_dir;
7331     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7332       MovDir[x][y] = left_dir;
7333
7334     if (MovDir[x][y] != old_move_dir)
7335       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7336   }
7337   else if (move_pattern == MV_TOWARDS_PLAYER ||
7338            move_pattern == MV_AWAY_FROM_PLAYER)
7339   {
7340     int attr_x = -1, attr_y = -1;
7341     int newx, newy;
7342     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7343
7344     if (AllPlayersGone)
7345     {
7346       attr_x = ExitX;
7347       attr_y = ExitY;
7348     }
7349     else
7350     {
7351       int i;
7352
7353       for (i = 0; i < MAX_PLAYERS; i++)
7354       {
7355         struct PlayerInfo *player = &stored_player[i];
7356         int jx = player->jx, jy = player->jy;
7357
7358         if (!player->active)
7359           continue;
7360
7361         if (attr_x == -1 ||
7362             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7363         {
7364           attr_x = jx;
7365           attr_y = jy;
7366         }
7367       }
7368     }
7369
7370     MovDir[x][y] = MV_NONE;
7371     if (attr_x < x)
7372       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7373     else if (attr_x > x)
7374       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7375     if (attr_y < y)
7376       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7377     else if (attr_y > y)
7378       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7379
7380     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7381
7382     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7383     {
7384       boolean first_horiz = RND(2);
7385       int new_move_dir = MovDir[x][y];
7386
7387       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7388       {
7389         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7390         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7391
7392         return;
7393       }
7394
7395       MovDir[x][y] =
7396         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7397       Moving2Blocked(x, y, &newx, &newy);
7398
7399       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7400         return;
7401
7402       MovDir[x][y] =
7403         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7404       Moving2Blocked(x, y, &newx, &newy);
7405
7406       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7407         return;
7408
7409       MovDir[x][y] = old_move_dir;
7410     }
7411   }
7412   else if (move_pattern == MV_WHEN_PUSHED ||
7413            move_pattern == MV_WHEN_DROPPED)
7414   {
7415     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7416       MovDir[x][y] = MV_NONE;
7417
7418     MovDelay[x][y] = 0;
7419   }
7420   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7421   {
7422     static int test_xy[7][2] =
7423     {
7424       { 0, -1 },
7425       { -1, 0 },
7426       { +1, 0 },
7427       { 0, +1 },
7428       { 0, -1 },
7429       { -1, 0 },
7430       { +1, 0 },
7431     };
7432     static int test_dir[7] =
7433     {
7434       MV_UP,
7435       MV_LEFT,
7436       MV_RIGHT,
7437       MV_DOWN,
7438       MV_UP,
7439       MV_LEFT,
7440       MV_RIGHT,
7441     };
7442     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7443     int move_preference = -1000000;     /* start with very low preference */
7444     int new_move_dir = MV_NONE;
7445     int start_test = RND(4);
7446     int i;
7447
7448     for (i = 0; i < NUM_DIRECTIONS; i++)
7449     {
7450       int move_dir = test_dir[start_test + i];
7451       int move_dir_preference;
7452
7453       xx = x + test_xy[start_test + i][0];
7454       yy = y + test_xy[start_test + i][1];
7455
7456       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7457           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7458       {
7459         new_move_dir = move_dir;
7460
7461         break;
7462       }
7463
7464       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7465         continue;
7466
7467       move_dir_preference = -1 * RunnerVisit[xx][yy];
7468       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7469         move_dir_preference = PlayerVisit[xx][yy];
7470
7471       if (move_dir_preference > move_preference)
7472       {
7473         /* prefer field that has not been visited for the longest time */
7474         move_preference = move_dir_preference;
7475         new_move_dir = move_dir;
7476       }
7477       else if (move_dir_preference == move_preference &&
7478                move_dir == old_move_dir)
7479       {
7480         /* prefer last direction when all directions are preferred equally */
7481         move_preference = move_dir_preference;
7482         new_move_dir = move_dir;
7483       }
7484     }
7485
7486     MovDir[x][y] = new_move_dir;
7487     if (old_move_dir != new_move_dir)
7488       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7489   }
7490 }
7491
7492 static void TurnRound(int x, int y)
7493 {
7494   int direction = MovDir[x][y];
7495
7496   TurnRoundExt(x, y);
7497
7498   GfxDir[x][y] = MovDir[x][y];
7499
7500   if (direction != MovDir[x][y])
7501     GfxFrame[x][y] = 0;
7502
7503   if (MovDelay[x][y])
7504     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7505
7506   ResetGfxFrame(x, y, FALSE);
7507 }
7508
7509 static boolean JustBeingPushed(int x, int y)
7510 {
7511   int i;
7512
7513   for (i = 0; i < MAX_PLAYERS; i++)
7514   {
7515     struct PlayerInfo *player = &stored_player[i];
7516
7517     if (player->active && player->is_pushing && player->MovPos)
7518     {
7519       int next_jx = player->jx + (player->jx - player->last_jx);
7520       int next_jy = player->jy + (player->jy - player->last_jy);
7521
7522       if (x == next_jx && y == next_jy)
7523         return TRUE;
7524     }
7525   }
7526
7527   return FALSE;
7528 }
7529
7530 void StartMoving(int x, int y)
7531 {
7532   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7533   int element = Feld[x][y];
7534
7535   if (Stop[x][y])
7536     return;
7537
7538   if (MovDelay[x][y] == 0)
7539     GfxAction[x][y] = ACTION_DEFAULT;
7540
7541   if (CAN_FALL(element) && y < lev_fieldy - 1)
7542   {
7543     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7544         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7545       if (JustBeingPushed(x, y))
7546         return;
7547
7548     if (element == EL_QUICKSAND_FULL)
7549     {
7550       if (IS_FREE(x, y + 1))
7551       {
7552         InitMovingField(x, y, MV_DOWN);
7553         started_moving = TRUE;
7554
7555         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7556 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7557         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7558           Store[x][y] = EL_ROCK;
7559 #else
7560         Store[x][y] = EL_ROCK;
7561 #endif
7562
7563         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7564       }
7565       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7566       {
7567         if (!MovDelay[x][y])
7568         {
7569           MovDelay[x][y] = TILEY + 1;
7570
7571           ResetGfxAnimation(x, y);
7572           ResetGfxAnimation(x, y + 1);
7573         }
7574
7575         if (MovDelay[x][y])
7576         {
7577           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7578           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7579
7580           MovDelay[x][y]--;
7581           if (MovDelay[x][y])
7582             return;
7583         }
7584
7585         Feld[x][y] = EL_QUICKSAND_EMPTY;
7586         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7587         Store[x][y + 1] = Store[x][y];
7588         Store[x][y] = 0;
7589
7590         PlayLevelSoundAction(x, y, ACTION_FILLING);
7591       }
7592       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7593       {
7594         if (!MovDelay[x][y])
7595         {
7596           MovDelay[x][y] = TILEY + 1;
7597
7598           ResetGfxAnimation(x, y);
7599           ResetGfxAnimation(x, y + 1);
7600         }
7601
7602         if (MovDelay[x][y])
7603         {
7604           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7605           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7606
7607           MovDelay[x][y]--;
7608           if (MovDelay[x][y])
7609             return;
7610         }
7611
7612         Feld[x][y] = EL_QUICKSAND_EMPTY;
7613         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7614         Store[x][y + 1] = Store[x][y];
7615         Store[x][y] = 0;
7616
7617         PlayLevelSoundAction(x, y, ACTION_FILLING);
7618       }
7619     }
7620     else if (element == EL_QUICKSAND_FAST_FULL)
7621     {
7622       if (IS_FREE(x, y + 1))
7623       {
7624         InitMovingField(x, y, MV_DOWN);
7625         started_moving = TRUE;
7626
7627         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7628 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7629         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7630           Store[x][y] = EL_ROCK;
7631 #else
7632         Store[x][y] = EL_ROCK;
7633 #endif
7634
7635         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7636       }
7637       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7638       {
7639         if (!MovDelay[x][y])
7640         {
7641           MovDelay[x][y] = TILEY + 1;
7642
7643           ResetGfxAnimation(x, y);
7644           ResetGfxAnimation(x, y + 1);
7645         }
7646
7647         if (MovDelay[x][y])
7648         {
7649           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7650           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7651
7652           MovDelay[x][y]--;
7653           if (MovDelay[x][y])
7654             return;
7655         }
7656
7657         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7658         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7659         Store[x][y + 1] = Store[x][y];
7660         Store[x][y] = 0;
7661
7662         PlayLevelSoundAction(x, y, ACTION_FILLING);
7663       }
7664       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7665       {
7666         if (!MovDelay[x][y])
7667         {
7668           MovDelay[x][y] = TILEY + 1;
7669
7670           ResetGfxAnimation(x, y);
7671           ResetGfxAnimation(x, y + 1);
7672         }
7673
7674         if (MovDelay[x][y])
7675         {
7676           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7677           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7678
7679           MovDelay[x][y]--;
7680           if (MovDelay[x][y])
7681             return;
7682         }
7683
7684         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7685         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7686         Store[x][y + 1] = Store[x][y];
7687         Store[x][y] = 0;
7688
7689         PlayLevelSoundAction(x, y, ACTION_FILLING);
7690       }
7691     }
7692     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7693              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7694     {
7695       InitMovingField(x, y, MV_DOWN);
7696       started_moving = TRUE;
7697
7698       Feld[x][y] = EL_QUICKSAND_FILLING;
7699       Store[x][y] = element;
7700
7701       PlayLevelSoundAction(x, y, ACTION_FILLING);
7702     }
7703     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7704              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7705     {
7706       InitMovingField(x, y, MV_DOWN);
7707       started_moving = TRUE;
7708
7709       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7710       Store[x][y] = element;
7711
7712       PlayLevelSoundAction(x, y, ACTION_FILLING);
7713     }
7714     else if (element == EL_MAGIC_WALL_FULL)
7715     {
7716       if (IS_FREE(x, y + 1))
7717       {
7718         InitMovingField(x, y, MV_DOWN);
7719         started_moving = TRUE;
7720
7721         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7722         Store[x][y] = EL_CHANGED(Store[x][y]);
7723       }
7724       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7725       {
7726         if (!MovDelay[x][y])
7727           MovDelay[x][y] = TILEY/4 + 1;
7728
7729         if (MovDelay[x][y])
7730         {
7731           MovDelay[x][y]--;
7732           if (MovDelay[x][y])
7733             return;
7734         }
7735
7736         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7737         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7738         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7739         Store[x][y] = 0;
7740       }
7741     }
7742     else if (element == EL_BD_MAGIC_WALL_FULL)
7743     {
7744       if (IS_FREE(x, y + 1))
7745       {
7746         InitMovingField(x, y, MV_DOWN);
7747         started_moving = TRUE;
7748
7749         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7750         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7751       }
7752       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7753       {
7754         if (!MovDelay[x][y])
7755           MovDelay[x][y] = TILEY/4 + 1;
7756
7757         if (MovDelay[x][y])
7758         {
7759           MovDelay[x][y]--;
7760           if (MovDelay[x][y])
7761             return;
7762         }
7763
7764         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7765         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7766         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7767         Store[x][y] = 0;
7768       }
7769     }
7770     else if (element == EL_DC_MAGIC_WALL_FULL)
7771     {
7772       if (IS_FREE(x, y + 1))
7773       {
7774         InitMovingField(x, y, MV_DOWN);
7775         started_moving = TRUE;
7776
7777         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7778         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7779       }
7780       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7781       {
7782         if (!MovDelay[x][y])
7783           MovDelay[x][y] = TILEY/4 + 1;
7784
7785         if (MovDelay[x][y])
7786         {
7787           MovDelay[x][y]--;
7788           if (MovDelay[x][y])
7789             return;
7790         }
7791
7792         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7793         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7794         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7795         Store[x][y] = 0;
7796       }
7797     }
7798     else if ((CAN_PASS_MAGIC_WALL(element) &&
7799               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7800                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7801              (CAN_PASS_DC_MAGIC_WALL(element) &&
7802               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7803
7804     {
7805       InitMovingField(x, y, MV_DOWN);
7806       started_moving = TRUE;
7807
7808       Feld[x][y] =
7809         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7810          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7811          EL_DC_MAGIC_WALL_FILLING);
7812       Store[x][y] = element;
7813     }
7814     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7815     {
7816       SplashAcid(x, y + 1);
7817
7818       InitMovingField(x, y, MV_DOWN);
7819       started_moving = TRUE;
7820
7821       Store[x][y] = EL_ACID;
7822     }
7823     else if (
7824 #if USE_FIX_IMPACT_COLLISION
7825              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7826               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7827 #else
7828              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7829               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7830 #endif
7831              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7832               CAN_FALL(element) && WasJustFalling[x][y] &&
7833               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7834
7835              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7836               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7837               (Feld[x][y + 1] == EL_BLOCKED)))
7838     {
7839       /* this is needed for a special case not covered by calling "Impact()"
7840          from "ContinueMoving()": if an element moves to a tile directly below
7841          another element which was just falling on that tile (which was empty
7842          in the previous frame), the falling element above would just stop
7843          instead of smashing the element below (in previous version, the above
7844          element was just checked for "moving" instead of "falling", resulting
7845          in incorrect smashes caused by horizontal movement of the above
7846          element; also, the case of the player being the element to smash was
7847          simply not covered here... :-/ ) */
7848
7849       CheckCollision[x][y] = 0;
7850       CheckImpact[x][y] = 0;
7851
7852       Impact(x, y);
7853     }
7854     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7855     {
7856       if (MovDir[x][y] == MV_NONE)
7857       {
7858         InitMovingField(x, y, MV_DOWN);
7859         started_moving = TRUE;
7860       }
7861     }
7862     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7863     {
7864       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7865         MovDir[x][y] = MV_DOWN;
7866
7867       InitMovingField(x, y, MV_DOWN);
7868       started_moving = TRUE;
7869     }
7870     else if (element == EL_AMOEBA_DROP)
7871     {
7872       Feld[x][y] = EL_AMOEBA_GROWING;
7873       Store[x][y] = EL_AMOEBA_WET;
7874     }
7875     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7876               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7877              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7878              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7879     {
7880       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7881                                 (IS_FREE(x - 1, y + 1) ||
7882                                  Feld[x - 1][y + 1] == EL_ACID));
7883       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7884                                 (IS_FREE(x + 1, y + 1) ||
7885                                  Feld[x + 1][y + 1] == EL_ACID));
7886       boolean can_fall_any  = (can_fall_left || can_fall_right);
7887       boolean can_fall_both = (can_fall_left && can_fall_right);
7888       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7889
7890 #if USE_NEW_ALL_SLIPPERY
7891       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7892       {
7893         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7894           can_fall_right = FALSE;
7895         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7896           can_fall_left = FALSE;
7897         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7898           can_fall_right = FALSE;
7899         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7900           can_fall_left = FALSE;
7901
7902         can_fall_any  = (can_fall_left || can_fall_right);
7903         can_fall_both = FALSE;
7904       }
7905 #else
7906       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7907       {
7908         if (slippery_type == SLIPPERY_ONLY_LEFT)
7909           can_fall_right = FALSE;
7910         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7911           can_fall_left = FALSE;
7912         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7913           can_fall_right = FALSE;
7914         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7915           can_fall_left = FALSE;
7916
7917         can_fall_any  = (can_fall_left || can_fall_right);
7918         can_fall_both = (can_fall_left && can_fall_right);
7919       }
7920 #endif
7921
7922 #if USE_NEW_ALL_SLIPPERY
7923 #else
7924 #if USE_NEW_SP_SLIPPERY
7925       /* !!! better use the same properties as for custom elements here !!! */
7926       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7927                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7928       {
7929         can_fall_right = FALSE;         /* slip down on left side */
7930         can_fall_both = FALSE;
7931       }
7932 #endif
7933 #endif
7934
7935 #if USE_NEW_ALL_SLIPPERY
7936       if (can_fall_both)
7937       {
7938         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7939           can_fall_right = FALSE;       /* slip down on left side */
7940         else
7941           can_fall_left = !(can_fall_right = RND(2));
7942
7943         can_fall_both = FALSE;
7944       }
7945 #else
7946       if (can_fall_both)
7947       {
7948         if (game.emulation == EMU_BOULDERDASH ||
7949             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7950           can_fall_right = FALSE;       /* slip down on left side */
7951         else
7952           can_fall_left = !(can_fall_right = RND(2));
7953
7954         can_fall_both = FALSE;
7955       }
7956 #endif
7957
7958       if (can_fall_any)
7959       {
7960         /* if not determined otherwise, prefer left side for slipping down */
7961         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7962         started_moving = TRUE;
7963       }
7964     }
7965 #if 0
7966     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7967 #else
7968     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7969 #endif
7970     {
7971       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7972       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7973       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7974       int belt_dir = game.belt_dir[belt_nr];
7975
7976       if ((belt_dir == MV_LEFT  && left_is_free) ||
7977           (belt_dir == MV_RIGHT && right_is_free))
7978       {
7979         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7980
7981         InitMovingField(x, y, belt_dir);
7982         started_moving = TRUE;
7983
7984         Pushed[x][y] = TRUE;
7985         Pushed[nextx][y] = TRUE;
7986
7987         GfxAction[x][y] = ACTION_DEFAULT;
7988       }
7989       else
7990       {
7991         MovDir[x][y] = 0;       /* if element was moving, stop it */
7992       }
7993     }
7994   }
7995
7996   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7997 #if 0
7998   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7999 #else
8000   if (CAN_MOVE(element) && !started_moving)
8001 #endif
8002   {
8003     int move_pattern = element_info[element].move_pattern;
8004     int newx, newy;
8005
8006 #if 0
8007 #if DEBUG
8008     if (MovDir[x][y] == MV_NONE)
8009     {
8010       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8011              x, y, element, element_info[element].token_name);
8012       printf("StartMoving(): This should never happen!\n");
8013     }
8014 #endif
8015 #endif
8016
8017     Moving2Blocked(x, y, &newx, &newy);
8018
8019     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8020       return;
8021
8022     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8023         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8024     {
8025       WasJustMoving[x][y] = 0;
8026       CheckCollision[x][y] = 0;
8027
8028       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8029
8030       if (Feld[x][y] != element)        /* element has changed */
8031         return;
8032     }
8033
8034     if (!MovDelay[x][y])        /* start new movement phase */
8035     {
8036       /* all objects that can change their move direction after each step
8037          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8038
8039       if (element != EL_YAMYAM &&
8040           element != EL_DARK_YAMYAM &&
8041           element != EL_PACMAN &&
8042           !(move_pattern & MV_ANY_DIRECTION) &&
8043           move_pattern != MV_TURNING_LEFT &&
8044           move_pattern != MV_TURNING_RIGHT &&
8045           move_pattern != MV_TURNING_LEFT_RIGHT &&
8046           move_pattern != MV_TURNING_RIGHT_LEFT &&
8047           move_pattern != MV_TURNING_RANDOM)
8048       {
8049         TurnRound(x, y);
8050
8051         if (MovDelay[x][y] && (element == EL_BUG ||
8052                                element == EL_SPACESHIP ||
8053                                element == EL_SP_SNIKSNAK ||
8054                                element == EL_SP_ELECTRON ||
8055                                element == EL_MOLE))
8056           DrawLevelField(x, y);
8057       }
8058     }
8059
8060     if (MovDelay[x][y])         /* wait some time before next movement */
8061     {
8062       MovDelay[x][y]--;
8063
8064       if (element == EL_ROBOT ||
8065           element == EL_YAMYAM ||
8066           element == EL_DARK_YAMYAM)
8067       {
8068         DrawLevelElementAnimationIfNeeded(x, y, element);
8069         PlayLevelSoundAction(x, y, ACTION_WAITING);
8070       }
8071       else if (element == EL_SP_ELECTRON)
8072         DrawLevelElementAnimationIfNeeded(x, y, element);
8073       else if (element == EL_DRAGON)
8074       {
8075         int i;
8076         int dir = MovDir[x][y];
8077         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8078         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8079         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8080                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8081                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8082                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8083         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8084
8085         GfxAction[x][y] = ACTION_ATTACKING;
8086
8087         if (IS_PLAYER(x, y))
8088           DrawPlayerField(x, y);
8089         else
8090           DrawLevelField(x, y);
8091
8092         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8093
8094         for (i = 1; i <= 3; i++)
8095         {
8096           int xx = x + i * dx;
8097           int yy = y + i * dy;
8098           int sx = SCREENX(xx);
8099           int sy = SCREENY(yy);
8100           int flame_graphic = graphic + (i - 1);
8101
8102           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8103             break;
8104
8105           if (MovDelay[x][y])
8106           {
8107             int flamed = MovingOrBlocked2Element(xx, yy);
8108
8109             /* !!! */
8110 #if 0
8111             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8112               Bang(xx, yy);
8113             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8114               RemoveMovingField(xx, yy);
8115             else
8116               RemoveField(xx, yy);
8117 #else
8118             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8119               Bang(xx, yy);
8120             else
8121               RemoveMovingField(xx, yy);
8122 #endif
8123
8124             ChangeDelay[xx][yy] = 0;
8125
8126             Feld[xx][yy] = EL_FLAMES;
8127
8128             if (IN_SCR_FIELD(sx, sy))
8129             {
8130               DrawLevelFieldCrumbledSand(xx, yy);
8131               DrawGraphic(sx, sy, flame_graphic, frame);
8132             }
8133           }
8134           else
8135           {
8136             if (Feld[xx][yy] == EL_FLAMES)
8137               Feld[xx][yy] = EL_EMPTY;
8138             DrawLevelField(xx, yy);
8139           }
8140         }
8141       }
8142
8143       if (MovDelay[x][y])       /* element still has to wait some time */
8144       {
8145         PlayLevelSoundAction(x, y, ACTION_WAITING);
8146
8147         return;
8148       }
8149     }
8150
8151     /* now make next step */
8152
8153     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8154
8155     if (DONT_COLLIDE_WITH(element) &&
8156         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8157         !PLAYER_ENEMY_PROTECTED(newx, newy))
8158     {
8159       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8160
8161       return;
8162     }
8163
8164     else if (CAN_MOVE_INTO_ACID(element) &&
8165              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8166              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8167              (MovDir[x][y] == MV_DOWN ||
8168               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8169     {
8170       SplashAcid(newx, newy);
8171       Store[x][y] = EL_ACID;
8172     }
8173     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8174     {
8175       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8176           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8177           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8178           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8179       {
8180         RemoveField(x, y);
8181         DrawLevelField(x, y);
8182
8183         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8184         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8185           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8186
8187         local_player->friends_still_needed--;
8188         if (!local_player->friends_still_needed &&
8189             !local_player->GameOver && AllPlayersGone)
8190           PlayerWins(local_player);
8191
8192         return;
8193       }
8194       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8195       {
8196         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8197           DrawLevelField(newx, newy);
8198         else
8199           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8200       }
8201       else if (!IS_FREE(newx, newy))
8202       {
8203         GfxAction[x][y] = ACTION_WAITING;
8204
8205         if (IS_PLAYER(x, y))
8206           DrawPlayerField(x, y);
8207         else
8208           DrawLevelField(x, y);
8209
8210         return;
8211       }
8212     }
8213     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8214     {
8215       if (IS_FOOD_PIG(Feld[newx][newy]))
8216       {
8217         if (IS_MOVING(newx, newy))
8218           RemoveMovingField(newx, newy);
8219         else
8220         {
8221           Feld[newx][newy] = EL_EMPTY;
8222           DrawLevelField(newx, newy);
8223         }
8224
8225         PlayLevelSound(x, y, SND_PIG_DIGGING);
8226       }
8227       else if (!IS_FREE(newx, newy))
8228       {
8229         if (IS_PLAYER(x, y))
8230           DrawPlayerField(x, y);
8231         else
8232           DrawLevelField(x, y);
8233
8234         return;
8235       }
8236     }
8237     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8238     {
8239       if (Store[x][y] != EL_EMPTY)
8240       {
8241         boolean can_clone = FALSE;
8242         int xx, yy;
8243
8244         /* check if element to clone is still there */
8245         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8246         {
8247           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8248           {
8249             can_clone = TRUE;
8250
8251             break;
8252           }
8253         }
8254
8255         /* cannot clone or target field not free anymore -- do not clone */
8256         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8257           Store[x][y] = EL_EMPTY;
8258       }
8259
8260       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8261       {
8262         if (IS_MV_DIAGONAL(MovDir[x][y]))
8263         {
8264           int diagonal_move_dir = MovDir[x][y];
8265           int stored = Store[x][y];
8266           int change_delay = 8;
8267           int graphic;
8268
8269           /* android is moving diagonally */
8270
8271           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8272
8273           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8274           GfxElement[x][y] = EL_EMC_ANDROID;
8275           GfxAction[x][y] = ACTION_SHRINKING;
8276           GfxDir[x][y] = diagonal_move_dir;
8277           ChangeDelay[x][y] = change_delay;
8278
8279           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8280                                    GfxDir[x][y]);
8281
8282           DrawLevelGraphicAnimation(x, y, graphic);
8283           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8284
8285           if (Feld[newx][newy] == EL_ACID)
8286           {
8287             SplashAcid(newx, newy);
8288
8289             return;
8290           }
8291
8292           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8293
8294           Store[newx][newy] = EL_EMC_ANDROID;
8295           GfxElement[newx][newy] = EL_EMC_ANDROID;
8296           GfxAction[newx][newy] = ACTION_GROWING;
8297           GfxDir[newx][newy] = diagonal_move_dir;
8298           ChangeDelay[newx][newy] = change_delay;
8299
8300           graphic = el_act_dir2img(GfxElement[newx][newy],
8301                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8302
8303           DrawLevelGraphicAnimation(newx, newy, graphic);
8304           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8305
8306           return;
8307         }
8308         else
8309         {
8310           Feld[newx][newy] = EL_EMPTY;
8311           DrawLevelField(newx, newy);
8312
8313           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8314         }
8315       }
8316       else if (!IS_FREE(newx, newy))
8317       {
8318 #if 0
8319         if (IS_PLAYER(x, y))
8320           DrawPlayerField(x, y);
8321         else
8322           DrawLevelField(x, y);
8323 #endif
8324
8325         return;
8326       }
8327     }
8328     else if (IS_CUSTOM_ELEMENT(element) &&
8329              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8330     {
8331       int new_element = Feld[newx][newy];
8332
8333       if (!IS_FREE(newx, newy))
8334       {
8335         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8336                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8337                       ACTION_BREAKING);
8338
8339         /* no element can dig solid indestructible elements */
8340         if (IS_INDESTRUCTIBLE(new_element) &&
8341             !IS_DIGGABLE(new_element) &&
8342             !IS_COLLECTIBLE(new_element))
8343           return;
8344
8345         if (AmoebaNr[newx][newy] &&
8346             (new_element == EL_AMOEBA_FULL ||
8347              new_element == EL_BD_AMOEBA ||
8348              new_element == EL_AMOEBA_GROWING))
8349         {
8350           AmoebaCnt[AmoebaNr[newx][newy]]--;
8351           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8352         }
8353
8354         if (IS_MOVING(newx, newy))
8355           RemoveMovingField(newx, newy);
8356         else
8357         {
8358           RemoveField(newx, newy);
8359           DrawLevelField(newx, newy);
8360         }
8361
8362         /* if digged element was about to explode, prevent the explosion */
8363         ExplodeField[newx][newy] = EX_TYPE_NONE;
8364
8365         PlayLevelSoundAction(x, y, action);
8366       }
8367
8368       Store[newx][newy] = EL_EMPTY;
8369 #if 1
8370       /* this makes it possible to leave the removed element again */
8371       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8372         Store[newx][newy] = new_element;
8373 #else
8374       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8375       {
8376         int move_leave_element = element_info[element].move_leave_element;
8377
8378         /* this makes it possible to leave the removed element again */
8379         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8380                              new_element : move_leave_element);
8381       }
8382 #endif
8383
8384       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8385       {
8386         RunnerVisit[x][y] = FrameCounter;
8387         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8388       }
8389     }
8390     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8391     {
8392       if (!IS_FREE(newx, newy))
8393       {
8394         if (IS_PLAYER(x, y))
8395           DrawPlayerField(x, y);
8396         else
8397           DrawLevelField(x, y);
8398
8399         return;
8400       }
8401       else
8402       {
8403         boolean wanna_flame = !RND(10);
8404         int dx = newx - x, dy = newy - y;
8405         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8406         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8407         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8408                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8409         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8410                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8411
8412         if ((wanna_flame ||
8413              IS_CLASSIC_ENEMY(element1) ||
8414              IS_CLASSIC_ENEMY(element2)) &&
8415             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8416             element1 != EL_FLAMES && element2 != EL_FLAMES)
8417         {
8418           ResetGfxAnimation(x, y);
8419           GfxAction[x][y] = ACTION_ATTACKING;
8420
8421           if (IS_PLAYER(x, y))
8422             DrawPlayerField(x, y);
8423           else
8424             DrawLevelField(x, y);
8425
8426           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8427
8428           MovDelay[x][y] = 50;
8429
8430           /* !!! */
8431 #if 0
8432           RemoveField(newx, newy);
8433 #endif
8434           Feld[newx][newy] = EL_FLAMES;
8435           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8436           {
8437 #if 0
8438             RemoveField(newx1, newy1);
8439 #endif
8440             Feld[newx1][newy1] = EL_FLAMES;
8441           }
8442           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8443           {
8444 #if 0
8445             RemoveField(newx2, newy2);
8446 #endif
8447             Feld[newx2][newy2] = EL_FLAMES;
8448           }
8449
8450           return;
8451         }
8452       }
8453     }
8454     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8455              Feld[newx][newy] == EL_DIAMOND)
8456     {
8457       if (IS_MOVING(newx, newy))
8458         RemoveMovingField(newx, newy);
8459       else
8460       {
8461         Feld[newx][newy] = EL_EMPTY;
8462         DrawLevelField(newx, newy);
8463       }
8464
8465       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8466     }
8467     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8468              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8469     {
8470       if (AmoebaNr[newx][newy])
8471       {
8472         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8473         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8474             Feld[newx][newy] == EL_BD_AMOEBA)
8475           AmoebaCnt[AmoebaNr[newx][newy]]--;
8476       }
8477
8478 #if 0
8479       /* !!! test !!! */
8480       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8481       {
8482         RemoveMovingField(newx, newy);
8483       }
8484 #else
8485       if (IS_MOVING(newx, newy))
8486       {
8487         RemoveMovingField(newx, newy);
8488       }
8489 #endif
8490       else
8491       {
8492         Feld[newx][newy] = EL_EMPTY;
8493         DrawLevelField(newx, newy);
8494       }
8495
8496       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8497     }
8498     else if ((element == EL_PACMAN || element == EL_MOLE)
8499              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8500     {
8501       if (AmoebaNr[newx][newy])
8502       {
8503         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8504         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8505             Feld[newx][newy] == EL_BD_AMOEBA)
8506           AmoebaCnt[AmoebaNr[newx][newy]]--;
8507       }
8508
8509       if (element == EL_MOLE)
8510       {
8511         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8512         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8513
8514         ResetGfxAnimation(x, y);
8515         GfxAction[x][y] = ACTION_DIGGING;
8516         DrawLevelField(x, y);
8517
8518         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8519
8520         return;                         /* wait for shrinking amoeba */
8521       }
8522       else      /* element == EL_PACMAN */
8523       {
8524         Feld[newx][newy] = EL_EMPTY;
8525         DrawLevelField(newx, newy);
8526         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8527       }
8528     }
8529     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8530              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8531               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8532     {
8533       /* wait for shrinking amoeba to completely disappear */
8534       return;
8535     }
8536     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8537     {
8538       /* object was running against a wall */
8539
8540       TurnRound(x, y);
8541
8542 #if 0
8543       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8544       if (move_pattern & MV_ANY_DIRECTION &&
8545           move_pattern == MovDir[x][y])
8546       {
8547         int blocking_element =
8548           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8549
8550         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8551                                  MovDir[x][y]);
8552
8553         element = Feld[x][y];   /* element might have changed */
8554       }
8555 #endif
8556
8557       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8558         DrawLevelElementAnimation(x, y, element);
8559
8560       if (DONT_TOUCH(element))
8561         TestIfBadThingTouchesPlayer(x, y);
8562
8563       return;
8564     }
8565
8566     InitMovingField(x, y, MovDir[x][y]);
8567
8568     PlayLevelSoundAction(x, y, ACTION_MOVING);
8569   }
8570
8571   if (MovDir[x][y])
8572     ContinueMoving(x, y);
8573 }
8574
8575 void ContinueMoving(int x, int y)
8576 {
8577   int element = Feld[x][y];
8578   struct ElementInfo *ei = &element_info[element];
8579   int direction = MovDir[x][y];
8580   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8581   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8582   int newx = x + dx, newy = y + dy;
8583   int stored = Store[x][y];
8584   int stored_new = Store[newx][newy];
8585   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8586   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8587   boolean last_line = (newy == lev_fieldy - 1);
8588
8589   MovPos[x][y] += getElementMoveStepsize(x, y);
8590
8591   if (pushed_by_player) /* special case: moving object pushed by player */
8592     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8593
8594   if (ABS(MovPos[x][y]) < TILEX)
8595   {
8596 #if 0
8597     int ee = Feld[x][y];
8598     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8599     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8600
8601     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8602            x, y, ABS(MovPos[x][y]),
8603            ee, gg, ff,
8604            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8605 #endif
8606
8607     DrawLevelField(x, y);
8608
8609     return;     /* element is still moving */
8610   }
8611
8612   /* element reached destination field */
8613
8614   Feld[x][y] = EL_EMPTY;
8615   Feld[newx][newy] = element;
8616   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8617
8618   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8619   {
8620     element = Feld[newx][newy] = EL_ACID;
8621   }
8622   else if (element == EL_MOLE)
8623   {
8624     Feld[x][y] = EL_SAND;
8625
8626     DrawLevelFieldCrumbledSandNeighbours(x, y);
8627   }
8628   else if (element == EL_QUICKSAND_FILLING)
8629   {
8630     element = Feld[newx][newy] = get_next_element(element);
8631     Store[newx][newy] = Store[x][y];
8632   }
8633   else if (element == EL_QUICKSAND_EMPTYING)
8634   {
8635     Feld[x][y] = get_next_element(element);
8636     element = Feld[newx][newy] = Store[x][y];
8637   }
8638   else if (element == EL_QUICKSAND_FAST_FILLING)
8639   {
8640     element = Feld[newx][newy] = get_next_element(element);
8641     Store[newx][newy] = Store[x][y];
8642   }
8643   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8644   {
8645     Feld[x][y] = get_next_element(element);
8646     element = Feld[newx][newy] = Store[x][y];
8647   }
8648   else if (element == EL_MAGIC_WALL_FILLING)
8649   {
8650     element = Feld[newx][newy] = get_next_element(element);
8651     if (!game.magic_wall_active)
8652       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8653     Store[newx][newy] = Store[x][y];
8654   }
8655   else if (element == EL_MAGIC_WALL_EMPTYING)
8656   {
8657     Feld[x][y] = get_next_element(element);
8658     if (!game.magic_wall_active)
8659       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8660     element = Feld[newx][newy] = Store[x][y];
8661
8662 #if USE_NEW_CUSTOM_VALUE
8663     InitField(newx, newy, FALSE);
8664 #endif
8665   }
8666   else if (element == EL_BD_MAGIC_WALL_FILLING)
8667   {
8668     element = Feld[newx][newy] = get_next_element(element);
8669     if (!game.magic_wall_active)
8670       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8671     Store[newx][newy] = Store[x][y];
8672   }
8673   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8674   {
8675     Feld[x][y] = get_next_element(element);
8676     if (!game.magic_wall_active)
8677       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8678     element = Feld[newx][newy] = Store[x][y];
8679
8680 #if USE_NEW_CUSTOM_VALUE
8681     InitField(newx, newy, FALSE);
8682 #endif
8683   }
8684   else if (element == EL_DC_MAGIC_WALL_FILLING)
8685   {
8686     element = Feld[newx][newy] = get_next_element(element);
8687     if (!game.magic_wall_active)
8688       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8689     Store[newx][newy] = Store[x][y];
8690   }
8691   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8692   {
8693     Feld[x][y] = get_next_element(element);
8694     if (!game.magic_wall_active)
8695       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8696     element = Feld[newx][newy] = Store[x][y];
8697
8698 #if USE_NEW_CUSTOM_VALUE
8699     InitField(newx, newy, FALSE);
8700 #endif
8701   }
8702   else if (element == EL_AMOEBA_DROPPING)
8703   {
8704     Feld[x][y] = get_next_element(element);
8705     element = Feld[newx][newy] = Store[x][y];
8706   }
8707   else if (element == EL_SOKOBAN_OBJECT)
8708   {
8709     if (Back[x][y])
8710       Feld[x][y] = Back[x][y];
8711
8712     if (Back[newx][newy])
8713       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8714
8715     Back[x][y] = Back[newx][newy] = 0;
8716   }
8717
8718   Store[x][y] = EL_EMPTY;
8719   MovPos[x][y] = 0;
8720   MovDir[x][y] = 0;
8721   MovDelay[x][y] = 0;
8722
8723   MovDelay[newx][newy] = 0;
8724
8725   if (CAN_CHANGE_OR_HAS_ACTION(element))
8726   {
8727     /* copy element change control values to new field */
8728     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8729     ChangePage[newx][newy]  = ChangePage[x][y];
8730     ChangeCount[newx][newy] = ChangeCount[x][y];
8731     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8732   }
8733
8734 #if USE_NEW_CUSTOM_VALUE
8735   CustomValue[newx][newy] = CustomValue[x][y];
8736 #endif
8737
8738   ChangeDelay[x][y] = 0;
8739   ChangePage[x][y] = -1;
8740   ChangeCount[x][y] = 0;
8741   ChangeEvent[x][y] = -1;
8742
8743 #if USE_NEW_CUSTOM_VALUE
8744   CustomValue[x][y] = 0;
8745 #endif
8746
8747   /* copy animation control values to new field */
8748   GfxFrame[newx][newy]  = GfxFrame[x][y];
8749   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8750   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8751   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8752
8753   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8754
8755   /* some elements can leave other elements behind after moving */
8756 #if 1
8757   if (ei->move_leave_element != EL_EMPTY &&
8758       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8759       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8760 #else
8761   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8762       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8763       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8764 #endif
8765   {
8766     int move_leave_element = ei->move_leave_element;
8767
8768 #if 1
8769 #if 1
8770     /* this makes it possible to leave the removed element again */
8771     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8772       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8773 #else
8774     /* this makes it possible to leave the removed element again */
8775     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8776       move_leave_element = stored;
8777 #endif
8778 #else
8779     /* this makes it possible to leave the removed element again */
8780     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8781         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8782       move_leave_element = stored;
8783 #endif
8784
8785     Feld[x][y] = move_leave_element;
8786
8787     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8788       MovDir[x][y] = direction;
8789
8790     InitField(x, y, FALSE);
8791
8792     if (GFX_CRUMBLED(Feld[x][y]))
8793       DrawLevelFieldCrumbledSandNeighbours(x, y);
8794
8795     if (ELEM_IS_PLAYER(move_leave_element))
8796       RelocatePlayer(x, y, move_leave_element);
8797   }
8798
8799   /* do this after checking for left-behind element */
8800   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8801
8802   if (!CAN_MOVE(element) ||
8803       (CAN_FALL(element) && direction == MV_DOWN &&
8804        (element == EL_SPRING ||
8805         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8806         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8807     GfxDir[x][y] = MovDir[newx][newy] = 0;
8808
8809   DrawLevelField(x, y);
8810   DrawLevelField(newx, newy);
8811
8812   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8813
8814   /* prevent pushed element from moving on in pushed direction */
8815   if (pushed_by_player && CAN_MOVE(element) &&
8816       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8817       !(element_info[element].move_pattern & direction))
8818     TurnRound(newx, newy);
8819
8820   /* prevent elements on conveyor belt from moving on in last direction */
8821   if (pushed_by_conveyor && CAN_FALL(element) &&
8822       direction & MV_HORIZONTAL)
8823     MovDir[newx][newy] = 0;
8824
8825   if (!pushed_by_player)
8826   {
8827     int nextx = newx + dx, nexty = newy + dy;
8828     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8829
8830     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8831
8832     if (CAN_FALL(element) && direction == MV_DOWN)
8833       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8834
8835     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8836       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8837
8838 #if USE_FIX_IMPACT_COLLISION
8839     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8840       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8841 #endif
8842   }
8843
8844   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8845   {
8846     TestIfBadThingTouchesPlayer(newx, newy);
8847     TestIfBadThingTouchesFriend(newx, newy);
8848
8849     if (!IS_CUSTOM_ELEMENT(element))
8850       TestIfBadThingTouchesOtherBadThing(newx, newy);
8851   }
8852   else if (element == EL_PENGUIN)
8853     TestIfFriendTouchesBadThing(newx, newy);
8854
8855   /* give the player one last chance (one more frame) to move away */
8856   if (CAN_FALL(element) && direction == MV_DOWN &&
8857       (last_line || (!IS_FREE(x, newy + 1) &&
8858                      (!IS_PLAYER(x, newy + 1) ||
8859                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8860     Impact(x, newy);
8861
8862   if (pushed_by_player && !game.use_change_when_pushing_bug)
8863   {
8864     int push_side = MV_DIR_OPPOSITE(direction);
8865     struct PlayerInfo *player = PLAYERINFO(x, y);
8866
8867     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8868                                player->index_bit, push_side);
8869     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8870                                         player->index_bit, push_side);
8871   }
8872
8873   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8874     MovDelay[newx][newy] = 1;
8875
8876   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8877
8878   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8879
8880 #if 0
8881   if (ChangePage[newx][newy] != -1)             /* delayed change */
8882   {
8883     int page = ChangePage[newx][newy];
8884     struct ElementChangeInfo *change = &ei->change_page[page];
8885
8886     ChangePage[newx][newy] = -1;
8887
8888     if (change->can_change)
8889     {
8890       if (ChangeElement(newx, newy, element, page))
8891       {
8892         if (change->post_change_function)
8893           change->post_change_function(newx, newy);
8894       }
8895     }
8896
8897     if (change->has_action)
8898       ExecuteCustomElementAction(newx, newy, element, page);
8899   }
8900 #endif
8901
8902   TestIfElementHitsCustomElement(newx, newy, direction);
8903   TestIfPlayerTouchesCustomElement(newx, newy);
8904   TestIfElementTouchesCustomElement(newx, newy);
8905
8906   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8907       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8908     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8909                              MV_DIR_OPPOSITE(direction));
8910 }
8911
8912 int AmoebeNachbarNr(int ax, int ay)
8913 {
8914   int i;
8915   int element = Feld[ax][ay];
8916   int group_nr = 0;
8917   static int xy[4][2] =
8918   {
8919     { 0, -1 },
8920     { -1, 0 },
8921     { +1, 0 },
8922     { 0, +1 }
8923   };
8924
8925   for (i = 0; i < NUM_DIRECTIONS; i++)
8926   {
8927     int x = ax + xy[i][0];
8928     int y = ay + xy[i][1];
8929
8930     if (!IN_LEV_FIELD(x, y))
8931       continue;
8932
8933     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8934       group_nr = AmoebaNr[x][y];
8935   }
8936
8937   return group_nr;
8938 }
8939
8940 void AmoebenVereinigen(int ax, int ay)
8941 {
8942   int i, x, y, xx, yy;
8943   int new_group_nr = AmoebaNr[ax][ay];
8944   static int xy[4][2] =
8945   {
8946     { 0, -1 },
8947     { -1, 0 },
8948     { +1, 0 },
8949     { 0, +1 }
8950   };
8951
8952   if (new_group_nr == 0)
8953     return;
8954
8955   for (i = 0; i < NUM_DIRECTIONS; i++)
8956   {
8957     x = ax + xy[i][0];
8958     y = ay + xy[i][1];
8959
8960     if (!IN_LEV_FIELD(x, y))
8961       continue;
8962
8963     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8964          Feld[x][y] == EL_BD_AMOEBA ||
8965          Feld[x][y] == EL_AMOEBA_DEAD) &&
8966         AmoebaNr[x][y] != new_group_nr)
8967     {
8968       int old_group_nr = AmoebaNr[x][y];
8969
8970       if (old_group_nr == 0)
8971         return;
8972
8973       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8974       AmoebaCnt[old_group_nr] = 0;
8975       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8976       AmoebaCnt2[old_group_nr] = 0;
8977
8978       SCAN_PLAYFIELD(xx, yy)
8979       {
8980         if (AmoebaNr[xx][yy] == old_group_nr)
8981           AmoebaNr[xx][yy] = new_group_nr;
8982       }
8983     }
8984   }
8985 }
8986
8987 void AmoebeUmwandeln(int ax, int ay)
8988 {
8989   int i, x, y;
8990
8991   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8992   {
8993     int group_nr = AmoebaNr[ax][ay];
8994
8995 #ifdef DEBUG
8996     if (group_nr == 0)
8997     {
8998       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8999       printf("AmoebeUmwandeln(): This should never happen!\n");
9000       return;
9001     }
9002 #endif
9003
9004     SCAN_PLAYFIELD(x, y)
9005     {
9006       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9007       {
9008         AmoebaNr[x][y] = 0;
9009         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9010       }
9011     }
9012
9013     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9014                             SND_AMOEBA_TURNING_TO_GEM :
9015                             SND_AMOEBA_TURNING_TO_ROCK));
9016     Bang(ax, ay);
9017   }
9018   else
9019   {
9020     static int xy[4][2] =
9021     {
9022       { 0, -1 },
9023       { -1, 0 },
9024       { +1, 0 },
9025       { 0, +1 }
9026     };
9027
9028     for (i = 0; i < NUM_DIRECTIONS; i++)
9029     {
9030       x = ax + xy[i][0];
9031       y = ay + xy[i][1];
9032
9033       if (!IN_LEV_FIELD(x, y))
9034         continue;
9035
9036       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9037       {
9038         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9039                               SND_AMOEBA_TURNING_TO_GEM :
9040                               SND_AMOEBA_TURNING_TO_ROCK));
9041         Bang(x, y);
9042       }
9043     }
9044   }
9045 }
9046
9047 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9048 {
9049   int x, y;
9050   int group_nr = AmoebaNr[ax][ay];
9051   boolean done = FALSE;
9052
9053 #ifdef DEBUG
9054   if (group_nr == 0)
9055   {
9056     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9057     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9058     return;
9059   }
9060 #endif
9061
9062   SCAN_PLAYFIELD(x, y)
9063   {
9064     if (AmoebaNr[x][y] == group_nr &&
9065         (Feld[x][y] == EL_AMOEBA_DEAD ||
9066          Feld[x][y] == EL_BD_AMOEBA ||
9067          Feld[x][y] == EL_AMOEBA_GROWING))
9068     {
9069       AmoebaNr[x][y] = 0;
9070       Feld[x][y] = new_element;
9071       InitField(x, y, FALSE);
9072       DrawLevelField(x, y);
9073       done = TRUE;
9074     }
9075   }
9076
9077   if (done)
9078     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9079                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9080                             SND_BD_AMOEBA_TURNING_TO_GEM));
9081 }
9082
9083 void AmoebeWaechst(int x, int y)
9084 {
9085   static unsigned long sound_delay = 0;
9086   static unsigned long sound_delay_value = 0;
9087
9088   if (!MovDelay[x][y])          /* start new growing cycle */
9089   {
9090     MovDelay[x][y] = 7;
9091
9092     if (DelayReached(&sound_delay, sound_delay_value))
9093     {
9094       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9095       sound_delay_value = 30;
9096     }
9097   }
9098
9099   if (MovDelay[x][y])           /* wait some time before growing bigger */
9100   {
9101     MovDelay[x][y]--;
9102     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9103     {
9104       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9105                                            6 - MovDelay[x][y]);
9106
9107       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9108     }
9109
9110     if (!MovDelay[x][y])
9111     {
9112       Feld[x][y] = Store[x][y];
9113       Store[x][y] = 0;
9114       DrawLevelField(x, y);
9115     }
9116   }
9117 }
9118
9119 void AmoebaDisappearing(int x, int y)
9120 {
9121   static unsigned long sound_delay = 0;
9122   static unsigned long sound_delay_value = 0;
9123
9124   if (!MovDelay[x][y])          /* start new shrinking cycle */
9125   {
9126     MovDelay[x][y] = 7;
9127
9128     if (DelayReached(&sound_delay, sound_delay_value))
9129       sound_delay_value = 30;
9130   }
9131
9132   if (MovDelay[x][y])           /* wait some time before shrinking */
9133   {
9134     MovDelay[x][y]--;
9135     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9136     {
9137       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9138                                            6 - MovDelay[x][y]);
9139
9140       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9141     }
9142
9143     if (!MovDelay[x][y])
9144     {
9145       Feld[x][y] = EL_EMPTY;
9146       DrawLevelField(x, y);
9147
9148       /* don't let mole enter this field in this cycle;
9149          (give priority to objects falling to this field from above) */
9150       Stop[x][y] = TRUE;
9151     }
9152   }
9153 }
9154
9155 void AmoebeAbleger(int ax, int ay)
9156 {
9157   int i;
9158   int element = Feld[ax][ay];
9159   int graphic = el2img(element);
9160   int newax = ax, neway = ay;
9161   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9162   static int xy[4][2] =
9163   {
9164     { 0, -1 },
9165     { -1, 0 },
9166     { +1, 0 },
9167     { 0, +1 }
9168   };
9169
9170   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9171   {
9172     Feld[ax][ay] = EL_AMOEBA_DEAD;
9173     DrawLevelField(ax, ay);
9174     return;
9175   }
9176
9177   if (IS_ANIMATED(graphic))
9178     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9179
9180   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9181     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9182
9183   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9184   {
9185     MovDelay[ax][ay]--;
9186     if (MovDelay[ax][ay])
9187       return;
9188   }
9189
9190   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9191   {
9192     int start = RND(4);
9193     int x = ax + xy[start][0];
9194     int y = ay + xy[start][1];
9195
9196     if (!IN_LEV_FIELD(x, y))
9197       return;
9198
9199     if (IS_FREE(x, y) ||
9200         CAN_GROW_INTO(Feld[x][y]) ||
9201         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9202         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9203     {
9204       newax = x;
9205       neway = y;
9206     }
9207
9208     if (newax == ax && neway == ay)
9209       return;
9210   }
9211   else                          /* normal or "filled" (BD style) amoeba */
9212   {
9213     int start = RND(4);
9214     boolean waiting_for_player = FALSE;
9215
9216     for (i = 0; i < NUM_DIRECTIONS; i++)
9217     {
9218       int j = (start + i) % 4;
9219       int x = ax + xy[j][0];
9220       int y = ay + xy[j][1];
9221
9222       if (!IN_LEV_FIELD(x, y))
9223         continue;
9224
9225       if (IS_FREE(x, y) ||
9226           CAN_GROW_INTO(Feld[x][y]) ||
9227           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9228           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9229       {
9230         newax = x;
9231         neway = y;
9232         break;
9233       }
9234       else if (IS_PLAYER(x, y))
9235         waiting_for_player = TRUE;
9236     }
9237
9238     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9239     {
9240       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9241       {
9242         Feld[ax][ay] = EL_AMOEBA_DEAD;
9243         DrawLevelField(ax, ay);
9244         AmoebaCnt[AmoebaNr[ax][ay]]--;
9245
9246         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9247         {
9248           if (element == EL_AMOEBA_FULL)
9249             AmoebeUmwandeln(ax, ay);
9250           else if (element == EL_BD_AMOEBA)
9251             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9252         }
9253       }
9254       return;
9255     }
9256     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9257     {
9258       /* amoeba gets larger by growing in some direction */
9259
9260       int new_group_nr = AmoebaNr[ax][ay];
9261
9262 #ifdef DEBUG
9263   if (new_group_nr == 0)
9264   {
9265     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9266     printf("AmoebeAbleger(): This should never happen!\n");
9267     return;
9268   }
9269 #endif
9270
9271       AmoebaNr[newax][neway] = new_group_nr;
9272       AmoebaCnt[new_group_nr]++;
9273       AmoebaCnt2[new_group_nr]++;
9274
9275       /* if amoeba touches other amoeba(s) after growing, unify them */
9276       AmoebenVereinigen(newax, neway);
9277
9278       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9279       {
9280         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9281         return;
9282       }
9283     }
9284   }
9285
9286   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9287       (neway == lev_fieldy - 1 && newax != ax))
9288   {
9289     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9290     Store[newax][neway] = element;
9291   }
9292   else if (neway == ay || element == EL_EMC_DRIPPER)
9293   {
9294     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9295
9296     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9297   }
9298   else
9299   {
9300     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9301     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9302     Store[ax][ay] = EL_AMOEBA_DROP;
9303     ContinueMoving(ax, ay);
9304     return;
9305   }
9306
9307   DrawLevelField(newax, neway);
9308 }
9309
9310 void Life(int ax, int ay)
9311 {
9312   int x1, y1, x2, y2;
9313   int life_time = 40;
9314   int element = Feld[ax][ay];
9315   int graphic = el2img(element);
9316   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9317                          level.biomaze);
9318   boolean changed = FALSE;
9319
9320   if (IS_ANIMATED(graphic))
9321     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9322
9323   if (Stop[ax][ay])
9324     return;
9325
9326   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9327     MovDelay[ax][ay] = life_time;
9328
9329   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9330   {
9331     MovDelay[ax][ay]--;
9332     if (MovDelay[ax][ay])
9333       return;
9334   }
9335
9336   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9337   {
9338     int xx = ax+x1, yy = ay+y1;
9339     int nachbarn = 0;
9340
9341     if (!IN_LEV_FIELD(xx, yy))
9342       continue;
9343
9344     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9345     {
9346       int x = xx+x2, y = yy+y2;
9347
9348       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9349         continue;
9350
9351       if (((Feld[x][y] == element ||
9352             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9353            !Stop[x][y]) ||
9354           (IS_FREE(x, y) && Stop[x][y]))
9355         nachbarn++;
9356     }
9357
9358     if (xx == ax && yy == ay)           /* field in the middle */
9359     {
9360       if (nachbarn < life_parameter[0] ||
9361           nachbarn > life_parameter[1])
9362       {
9363         Feld[xx][yy] = EL_EMPTY;
9364         if (!Stop[xx][yy])
9365           DrawLevelField(xx, yy);
9366         Stop[xx][yy] = TRUE;
9367         changed = TRUE;
9368       }
9369     }
9370     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9371     {                                   /* free border field */
9372       if (nachbarn >= life_parameter[2] &&
9373           nachbarn <= life_parameter[3])
9374       {
9375         Feld[xx][yy] = element;
9376         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9377         if (!Stop[xx][yy])
9378           DrawLevelField(xx, yy);
9379         Stop[xx][yy] = TRUE;
9380         changed = TRUE;
9381       }
9382     }
9383   }
9384
9385   if (changed)
9386     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9387                    SND_GAME_OF_LIFE_GROWING);
9388 }
9389
9390 static void InitRobotWheel(int x, int y)
9391 {
9392   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9393 }
9394
9395 static void RunRobotWheel(int x, int y)
9396 {
9397   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9398 }
9399
9400 static void StopRobotWheel(int x, int y)
9401 {
9402   if (ZX == x && ZY == y)
9403   {
9404     ZX = ZY = -1;
9405
9406     game.robot_wheel_active = FALSE;
9407   }
9408 }
9409
9410 static void InitTimegateWheel(int x, int y)
9411 {
9412   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9413 }
9414
9415 static void RunTimegateWheel(int x, int y)
9416 {
9417   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9418 }
9419
9420 static void InitMagicBallDelay(int x, int y)
9421 {
9422 #if 1
9423   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9424 #else
9425   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9426 #endif
9427 }
9428
9429 static void ActivateMagicBall(int bx, int by)
9430 {
9431   int x, y;
9432
9433   if (level.ball_random)
9434   {
9435     int pos_border = RND(8);    /* select one of the eight border elements */
9436     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9437     int xx = pos_content % 3;
9438     int yy = pos_content / 3;
9439
9440     x = bx - 1 + xx;
9441     y = by - 1 + yy;
9442
9443     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9444       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9445   }
9446   else
9447   {
9448     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9449     {
9450       int xx = x - bx + 1;
9451       int yy = y - by + 1;
9452
9453       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9454         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9455     }
9456   }
9457
9458   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9459 }
9460
9461 void CheckExit(int x, int y)
9462 {
9463   if (local_player->gems_still_needed > 0 ||
9464       local_player->sokobanfields_still_needed > 0 ||
9465       local_player->lights_still_needed > 0)
9466   {
9467     int element = Feld[x][y];
9468     int graphic = el2img(element);
9469
9470     if (IS_ANIMATED(graphic))
9471       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9472
9473     return;
9474   }
9475
9476   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9477     return;
9478
9479   Feld[x][y] = EL_EXIT_OPENING;
9480
9481   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9482 }
9483
9484 void CheckExitEM(int x, int y)
9485 {
9486   if (local_player->gems_still_needed > 0 ||
9487       local_player->sokobanfields_still_needed > 0 ||
9488       local_player->lights_still_needed > 0)
9489   {
9490     int element = Feld[x][y];
9491     int graphic = el2img(element);
9492
9493     if (IS_ANIMATED(graphic))
9494       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9495
9496     return;
9497   }
9498
9499   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9500     return;
9501
9502   Feld[x][y] = EL_EM_EXIT_OPENING;
9503
9504   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9505 }
9506
9507 void CheckExitSteel(int x, int y)
9508 {
9509   if (local_player->gems_still_needed > 0 ||
9510       local_player->sokobanfields_still_needed > 0 ||
9511       local_player->lights_still_needed > 0)
9512   {
9513     int element = Feld[x][y];
9514     int graphic = el2img(element);
9515
9516     if (IS_ANIMATED(graphic))
9517       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9518
9519     return;
9520   }
9521
9522   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9523     return;
9524
9525   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9526
9527   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9528 }
9529
9530 void CheckExitSteelEM(int x, int y)
9531 {
9532   if (local_player->gems_still_needed > 0 ||
9533       local_player->sokobanfields_still_needed > 0 ||
9534       local_player->lights_still_needed > 0)
9535   {
9536     int element = Feld[x][y];
9537     int graphic = el2img(element);
9538
9539     if (IS_ANIMATED(graphic))
9540       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9541
9542     return;
9543   }
9544
9545   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9546     return;
9547
9548   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9549
9550   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9551 }
9552
9553 void CheckExitSP(int x, int y)
9554 {
9555   if (local_player->gems_still_needed > 0)
9556   {
9557     int element = Feld[x][y];
9558     int graphic = el2img(element);
9559
9560     if (IS_ANIMATED(graphic))
9561       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9562
9563     return;
9564   }
9565
9566   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9567     return;
9568
9569   Feld[x][y] = EL_SP_EXIT_OPENING;
9570
9571   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9572 }
9573
9574 static void CloseAllOpenTimegates()
9575 {
9576   int x, y;
9577
9578   SCAN_PLAYFIELD(x, y)
9579   {
9580     int element = Feld[x][y];
9581
9582     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9583     {
9584       Feld[x][y] = EL_TIMEGATE_CLOSING;
9585
9586       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9587     }
9588   }
9589 }
9590
9591 void DrawTwinkleOnField(int x, int y)
9592 {
9593   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9594     return;
9595
9596   if (Feld[x][y] == EL_BD_DIAMOND)
9597     return;
9598
9599   if (MovDelay[x][y] == 0)      /* next animation frame */
9600     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9601
9602   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9603   {
9604     MovDelay[x][y]--;
9605
9606     DrawLevelElementAnimation(x, y, Feld[x][y]);
9607
9608     if (MovDelay[x][y] != 0)
9609     {
9610       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9611                                            10 - MovDelay[x][y]);
9612
9613       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9614     }
9615   }
9616 }
9617
9618 void MauerWaechst(int x, int y)
9619 {
9620   int delay = 6;
9621
9622   if (!MovDelay[x][y])          /* next animation frame */
9623     MovDelay[x][y] = 3 * delay;
9624
9625   if (MovDelay[x][y])           /* wait some time before next frame */
9626   {
9627     MovDelay[x][y]--;
9628
9629     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9630     {
9631       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9632       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9633
9634       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9635     }
9636
9637     if (!MovDelay[x][y])
9638     {
9639       if (MovDir[x][y] == MV_LEFT)
9640       {
9641         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9642           DrawLevelField(x - 1, y);
9643       }
9644       else if (MovDir[x][y] == MV_RIGHT)
9645       {
9646         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9647           DrawLevelField(x + 1, y);
9648       }
9649       else if (MovDir[x][y] == MV_UP)
9650       {
9651         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9652           DrawLevelField(x, y - 1);
9653       }
9654       else
9655       {
9656         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9657           DrawLevelField(x, y + 1);
9658       }
9659
9660       Feld[x][y] = Store[x][y];
9661       Store[x][y] = 0;
9662       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9663       DrawLevelField(x, y);
9664     }
9665   }
9666 }
9667
9668 void MauerAbleger(int ax, int ay)
9669 {
9670   int element = Feld[ax][ay];
9671   int graphic = el2img(element);
9672   boolean oben_frei = FALSE, unten_frei = FALSE;
9673   boolean links_frei = FALSE, rechts_frei = FALSE;
9674   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9675   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9676   boolean new_wall = FALSE;
9677
9678   if (IS_ANIMATED(graphic))
9679     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9680
9681   if (!MovDelay[ax][ay])        /* start building new wall */
9682     MovDelay[ax][ay] = 6;
9683
9684   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9685   {
9686     MovDelay[ax][ay]--;
9687     if (MovDelay[ax][ay])
9688       return;
9689   }
9690
9691   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9692     oben_frei = TRUE;
9693   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9694     unten_frei = TRUE;
9695   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9696     links_frei = TRUE;
9697   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9698     rechts_frei = TRUE;
9699
9700   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9701       element == EL_EXPANDABLE_WALL_ANY)
9702   {
9703     if (oben_frei)
9704     {
9705       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9706       Store[ax][ay-1] = element;
9707       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9708       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9709         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9710                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9711       new_wall = TRUE;
9712     }
9713     if (unten_frei)
9714     {
9715       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9716       Store[ax][ay+1] = element;
9717       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9718       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9719         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9720                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9721       new_wall = TRUE;
9722     }
9723   }
9724
9725   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9726       element == EL_EXPANDABLE_WALL_ANY ||
9727       element == EL_EXPANDABLE_WALL ||
9728       element == EL_BD_EXPANDABLE_WALL)
9729   {
9730     if (links_frei)
9731     {
9732       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9733       Store[ax-1][ay] = element;
9734       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9735       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9736         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9737                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9738       new_wall = TRUE;
9739     }
9740
9741     if (rechts_frei)
9742     {
9743       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9744       Store[ax+1][ay] = element;
9745       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9746       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9747         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9748                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9749       new_wall = TRUE;
9750     }
9751   }
9752
9753   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9754     DrawLevelField(ax, ay);
9755
9756   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9757     oben_massiv = TRUE;
9758   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9759     unten_massiv = TRUE;
9760   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9761     links_massiv = TRUE;
9762   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9763     rechts_massiv = TRUE;
9764
9765   if (((oben_massiv && unten_massiv) ||
9766        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9767        element == EL_EXPANDABLE_WALL) &&
9768       ((links_massiv && rechts_massiv) ||
9769        element == EL_EXPANDABLE_WALL_VERTICAL))
9770     Feld[ax][ay] = EL_WALL;
9771
9772   if (new_wall)
9773     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9774 }
9775
9776 void MauerAblegerStahl(int ax, int ay)
9777 {
9778   int element = Feld[ax][ay];
9779   int graphic = el2img(element);
9780   boolean oben_frei = FALSE, unten_frei = FALSE;
9781   boolean links_frei = FALSE, rechts_frei = FALSE;
9782   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9783   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9784   boolean new_wall = FALSE;
9785
9786   if (IS_ANIMATED(graphic))
9787     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9788
9789   if (!MovDelay[ax][ay])        /* start building new wall */
9790     MovDelay[ax][ay] = 6;
9791
9792   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9793   {
9794     MovDelay[ax][ay]--;
9795     if (MovDelay[ax][ay])
9796       return;
9797   }
9798
9799   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9800     oben_frei = TRUE;
9801   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9802     unten_frei = TRUE;
9803   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9804     links_frei = TRUE;
9805   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9806     rechts_frei = TRUE;
9807
9808   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9809       element == EL_EXPANDABLE_STEELWALL_ANY)
9810   {
9811     if (oben_frei)
9812     {
9813       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9814       Store[ax][ay-1] = element;
9815       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9816       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9817         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9818                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9819       new_wall = TRUE;
9820     }
9821     if (unten_frei)
9822     {
9823       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9824       Store[ax][ay+1] = element;
9825       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9826       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9827         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9828                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9829       new_wall = TRUE;
9830     }
9831   }
9832
9833   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9834       element == EL_EXPANDABLE_STEELWALL_ANY)
9835   {
9836     if (links_frei)
9837     {
9838       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9839       Store[ax-1][ay] = element;
9840       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9841       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9842         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9843                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9844       new_wall = TRUE;
9845     }
9846
9847     if (rechts_frei)
9848     {
9849       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9850       Store[ax+1][ay] = element;
9851       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9852       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9853         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9854                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9855       new_wall = TRUE;
9856     }
9857   }
9858
9859   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9860     oben_massiv = TRUE;
9861   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9862     unten_massiv = TRUE;
9863   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9864     links_massiv = TRUE;
9865   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9866     rechts_massiv = TRUE;
9867
9868   if (((oben_massiv && unten_massiv) ||
9869        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9870       ((links_massiv && rechts_massiv) ||
9871        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9872     Feld[ax][ay] = EL_STEELWALL;
9873
9874   if (new_wall)
9875     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9876 }
9877
9878 void CheckForDragon(int x, int y)
9879 {
9880   int i, j;
9881   boolean dragon_found = FALSE;
9882   static int xy[4][2] =
9883   {
9884     { 0, -1 },
9885     { -1, 0 },
9886     { +1, 0 },
9887     { 0, +1 }
9888   };
9889
9890   for (i = 0; i < NUM_DIRECTIONS; i++)
9891   {
9892     for (j = 0; j < 4; j++)
9893     {
9894       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9895
9896       if (IN_LEV_FIELD(xx, yy) &&
9897           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9898       {
9899         if (Feld[xx][yy] == EL_DRAGON)
9900           dragon_found = TRUE;
9901       }
9902       else
9903         break;
9904     }
9905   }
9906
9907   if (!dragon_found)
9908   {
9909     for (i = 0; i < NUM_DIRECTIONS; i++)
9910     {
9911       for (j = 0; j < 3; j++)
9912       {
9913         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9914   
9915         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9916         {
9917           Feld[xx][yy] = EL_EMPTY;
9918           DrawLevelField(xx, yy);
9919         }
9920         else
9921           break;
9922       }
9923     }
9924   }
9925 }
9926
9927 static void InitBuggyBase(int x, int y)
9928 {
9929   int element = Feld[x][y];
9930   int activating_delay = FRAMES_PER_SECOND / 4;
9931
9932   ChangeDelay[x][y] =
9933     (element == EL_SP_BUGGY_BASE ?
9934      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9935      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9936      activating_delay :
9937      element == EL_SP_BUGGY_BASE_ACTIVE ?
9938      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9939 }
9940
9941 static void WarnBuggyBase(int x, int y)
9942 {
9943   int i;
9944   static int xy[4][2] =
9945   {
9946     { 0, -1 },
9947     { -1, 0 },
9948     { +1, 0 },
9949     { 0, +1 }
9950   };
9951
9952   for (i = 0; i < NUM_DIRECTIONS; i++)
9953   {
9954     int xx = x + xy[i][0];
9955     int yy = y + xy[i][1];
9956
9957     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9958     {
9959       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9960
9961       break;
9962     }
9963   }
9964 }
9965
9966 static void InitTrap(int x, int y)
9967 {
9968   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9969 }
9970
9971 static void ActivateTrap(int x, int y)
9972 {
9973   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9974 }
9975
9976 static void ChangeActiveTrap(int x, int y)
9977 {
9978   int graphic = IMG_TRAP_ACTIVE;
9979
9980   /* if new animation frame was drawn, correct crumbled sand border */
9981   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9982     DrawLevelFieldCrumbledSand(x, y);
9983 }
9984
9985 static int getSpecialActionElement(int element, int number, int base_element)
9986 {
9987   return (element != EL_EMPTY ? element :
9988           number != -1 ? base_element + number - 1 :
9989           EL_EMPTY);
9990 }
9991
9992 static int getModifiedActionNumber(int value_old, int operator, int operand,
9993                                    int value_min, int value_max)
9994 {
9995   int value_new = (operator == CA_MODE_SET      ? operand :
9996                    operator == CA_MODE_ADD      ? value_old + operand :
9997                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9998                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9999                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10000                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10001                    value_old);
10002
10003   return (value_new < value_min ? value_min :
10004           value_new > value_max ? value_max :
10005           value_new);
10006 }
10007
10008 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10009 {
10010   struct ElementInfo *ei = &element_info[element];
10011   struct ElementChangeInfo *change = &ei->change_page[page];
10012   int target_element = change->target_element;
10013   int action_type = change->action_type;
10014   int action_mode = change->action_mode;
10015   int action_arg = change->action_arg;
10016   int i;
10017
10018   if (!change->has_action)
10019     return;
10020
10021   /* ---------- determine action paramater values -------------------------- */
10022
10023   int level_time_value =
10024     (level.time > 0 ? TimeLeft :
10025      TimePlayed);
10026
10027   int action_arg_element =
10028     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10029      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10030      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10031      EL_EMPTY);
10032
10033   int action_arg_direction =
10034     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10035      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10036      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10037      change->actual_trigger_side :
10038      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10039      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10040      MV_NONE);
10041
10042   int action_arg_number_min =
10043     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10044      CA_ARG_MIN);
10045
10046   int action_arg_number_max =
10047     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10048      action_type == CA_SET_LEVEL_GEMS ? 999 :
10049      action_type == CA_SET_LEVEL_TIME ? 9999 :
10050      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10051      action_type == CA_SET_CE_VALUE ? 9999 :
10052      action_type == CA_SET_CE_SCORE ? 9999 :
10053      CA_ARG_MAX);
10054
10055   int action_arg_number_reset =
10056     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10057      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10058      action_type == CA_SET_LEVEL_TIME ? level.time :
10059      action_type == CA_SET_LEVEL_SCORE ? 0 :
10060 #if USE_NEW_CUSTOM_VALUE
10061      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10062 #else
10063      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10064 #endif
10065      action_type == CA_SET_CE_SCORE ? 0 :
10066      0);
10067
10068   int action_arg_number =
10069     (action_arg <= CA_ARG_MAX ? action_arg :
10070      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10071      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10072      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10073      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10074      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10075      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10076 #if USE_NEW_CUSTOM_VALUE
10077      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10078 #else
10079      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10080 #endif
10081      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10082      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10083      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10084      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10085      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10086      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10087      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10088      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10089      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10090      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10091      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10092      -1);
10093
10094   int action_arg_number_old =
10095     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10096      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10097      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10098      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10099      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10100      0);
10101
10102   int action_arg_number_new =
10103     getModifiedActionNumber(action_arg_number_old,
10104                             action_mode, action_arg_number,
10105                             action_arg_number_min, action_arg_number_max);
10106
10107 #if 1
10108   int trigger_player_bits = change->actual_trigger_player_bits;
10109 #else
10110   int trigger_player_bits =
10111     (change->actual_trigger_player >= EL_PLAYER_1 &&
10112      change->actual_trigger_player <= EL_PLAYER_4 ?
10113      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10114      PLAYER_BITS_ANY);
10115 #endif
10116
10117   int action_arg_player_bits =
10118     (action_arg >= CA_ARG_PLAYER_1 &&
10119      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10120      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10121      PLAYER_BITS_ANY);
10122
10123   /* ---------- execute action  -------------------------------------------- */
10124
10125   switch (action_type)
10126   {
10127     case CA_NO_ACTION:
10128     {
10129       return;
10130     }
10131
10132     /* ---------- level actions  ------------------------------------------- */
10133
10134     case CA_RESTART_LEVEL:
10135     {
10136       game.restart_level = TRUE;
10137
10138       break;
10139     }
10140
10141     case CA_SHOW_ENVELOPE:
10142     {
10143       int element = getSpecialActionElement(action_arg_element,
10144                                             action_arg_number, EL_ENVELOPE_1);
10145
10146       if (IS_ENVELOPE(element))
10147         local_player->show_envelope = element;
10148
10149       break;
10150     }
10151
10152     case CA_SET_LEVEL_TIME:
10153     {
10154       if (level.time > 0)       /* only modify limited time value */
10155       {
10156         TimeLeft = action_arg_number_new;
10157
10158 #if 1
10159         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10160
10161         DisplayGameControlValues();
10162 #else
10163         DrawGameValue_Time(TimeLeft);
10164 #endif
10165
10166         if (!TimeLeft && setup.time_limit)
10167           for (i = 0; i < MAX_PLAYERS; i++)
10168             KillPlayer(&stored_player[i]);
10169       }
10170
10171       break;
10172     }
10173
10174     case CA_SET_LEVEL_SCORE:
10175     {
10176       local_player->score = action_arg_number_new;
10177
10178 #if 1
10179       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10180
10181       DisplayGameControlValues();
10182 #else
10183       DrawGameValue_Score(local_player->score);
10184 #endif
10185
10186       break;
10187     }
10188
10189     case CA_SET_LEVEL_GEMS:
10190     {
10191       local_player->gems_still_needed = action_arg_number_new;
10192
10193 #if 1
10194       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10195
10196       DisplayGameControlValues();
10197 #else
10198       DrawGameValue_Emeralds(local_player->gems_still_needed);
10199 #endif
10200
10201       break;
10202     }
10203
10204 #if !USE_PLAYER_GRAVITY
10205     case CA_SET_LEVEL_GRAVITY:
10206     {
10207       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10208                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10209                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10210                       game.gravity);
10211       break;
10212     }
10213 #endif
10214
10215     case CA_SET_LEVEL_WIND:
10216     {
10217       game.wind_direction = action_arg_direction;
10218
10219       break;
10220     }
10221
10222     /* ---------- player actions  ------------------------------------------ */
10223
10224     case CA_MOVE_PLAYER:
10225     {
10226       /* automatically move to the next field in specified direction */
10227       for (i = 0; i < MAX_PLAYERS; i++)
10228         if (trigger_player_bits & (1 << i))
10229           stored_player[i].programmed_action = action_arg_direction;
10230
10231       break;
10232     }
10233
10234     case CA_EXIT_PLAYER:
10235     {
10236       for (i = 0; i < MAX_PLAYERS; i++)
10237         if (action_arg_player_bits & (1 << i))
10238           PlayerWins(&stored_player[i]);
10239
10240       break;
10241     }
10242
10243     case CA_KILL_PLAYER:
10244     {
10245       for (i = 0; i < MAX_PLAYERS; i++)
10246         if (action_arg_player_bits & (1 << i))
10247           KillPlayer(&stored_player[i]);
10248
10249       break;
10250     }
10251
10252     case CA_SET_PLAYER_KEYS:
10253     {
10254       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10255       int element = getSpecialActionElement(action_arg_element,
10256                                             action_arg_number, EL_KEY_1);
10257
10258       if (IS_KEY(element))
10259       {
10260         for (i = 0; i < MAX_PLAYERS; i++)
10261         {
10262           if (trigger_player_bits & (1 << i))
10263           {
10264             stored_player[i].key[KEY_NR(element)] = key_state;
10265
10266             DrawGameDoorValues();
10267           }
10268         }
10269       }
10270
10271       break;
10272     }
10273
10274     case CA_SET_PLAYER_SPEED:
10275     {
10276       for (i = 0; i < MAX_PLAYERS; i++)
10277       {
10278         if (trigger_player_bits & (1 << i))
10279         {
10280           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10281
10282           if (action_arg == CA_ARG_SPEED_FASTER &&
10283               stored_player[i].cannot_move)
10284           {
10285             action_arg_number = STEPSIZE_VERY_SLOW;
10286           }
10287           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10288                    action_arg == CA_ARG_SPEED_FASTER)
10289           {
10290             action_arg_number = 2;
10291             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10292                            CA_MODE_MULTIPLY);
10293           }
10294           else if (action_arg == CA_ARG_NUMBER_RESET)
10295           {
10296             action_arg_number = level.initial_player_stepsize[i];
10297           }
10298
10299           move_stepsize =
10300             getModifiedActionNumber(move_stepsize,
10301                                     action_mode,
10302                                     action_arg_number,
10303                                     action_arg_number_min,
10304                                     action_arg_number_max);
10305
10306           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10307         }
10308       }
10309
10310       break;
10311     }
10312
10313     case CA_SET_PLAYER_SHIELD:
10314     {
10315       for (i = 0; i < MAX_PLAYERS; i++)
10316       {
10317         if (trigger_player_bits & (1 << i))
10318         {
10319           if (action_arg == CA_ARG_SHIELD_OFF)
10320           {
10321             stored_player[i].shield_normal_time_left = 0;
10322             stored_player[i].shield_deadly_time_left = 0;
10323           }
10324           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10325           {
10326             stored_player[i].shield_normal_time_left = 999999;
10327           }
10328           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10329           {
10330             stored_player[i].shield_normal_time_left = 999999;
10331             stored_player[i].shield_deadly_time_left = 999999;
10332           }
10333         }
10334       }
10335
10336       break;
10337     }
10338
10339 #if USE_PLAYER_GRAVITY
10340     case CA_SET_PLAYER_GRAVITY:
10341     {
10342       for (i = 0; i < MAX_PLAYERS; i++)
10343       {
10344         if (trigger_player_bits & (1 << i))
10345         {
10346           stored_player[i].gravity =
10347             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10348              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10349              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10350              stored_player[i].gravity);
10351         }
10352       }
10353
10354       break;
10355     }
10356 #endif
10357
10358     case CA_SET_PLAYER_ARTWORK:
10359     {
10360       for (i = 0; i < MAX_PLAYERS; i++)
10361       {
10362         if (trigger_player_bits & (1 << i))
10363         {
10364           int artwork_element = action_arg_element;
10365
10366           if (action_arg == CA_ARG_ELEMENT_RESET)
10367             artwork_element =
10368               (level.use_artwork_element[i] ? level.artwork_element[i] :
10369                stored_player[i].element_nr);
10370
10371 #if USE_GFX_RESET_PLAYER_ARTWORK
10372           if (stored_player[i].artwork_element != artwork_element)
10373             stored_player[i].Frame = 0;
10374 #endif
10375
10376           stored_player[i].artwork_element = artwork_element;
10377
10378           SetPlayerWaiting(&stored_player[i], FALSE);
10379
10380           /* set number of special actions for bored and sleeping animation */
10381           stored_player[i].num_special_action_bored =
10382             get_num_special_action(artwork_element,
10383                                    ACTION_BORING_1, ACTION_BORING_LAST);
10384           stored_player[i].num_special_action_sleeping =
10385             get_num_special_action(artwork_element,
10386                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10387         }
10388       }
10389
10390       break;
10391     }
10392
10393     /* ---------- CE actions  ---------------------------------------------- */
10394
10395     case CA_SET_CE_VALUE:
10396     {
10397 #if USE_NEW_CUSTOM_VALUE
10398       int last_ce_value = CustomValue[x][y];
10399
10400       CustomValue[x][y] = action_arg_number_new;
10401
10402       if (CustomValue[x][y] != last_ce_value)
10403       {
10404         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10405         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10406
10407         if (CustomValue[x][y] == 0)
10408         {
10409           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10410           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10411         }
10412       }
10413 #endif
10414
10415       break;
10416     }
10417
10418     case CA_SET_CE_SCORE:
10419     {
10420 #if USE_NEW_CUSTOM_VALUE
10421       int last_ce_score = ei->collect_score;
10422
10423       ei->collect_score = action_arg_number_new;
10424
10425       if (ei->collect_score != last_ce_score)
10426       {
10427         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10428         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10429
10430         if (ei->collect_score == 0)
10431         {
10432           int xx, yy;
10433
10434           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10435           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10436
10437           /*
10438             This is a very special case that seems to be a mixture between
10439             CheckElementChange() and CheckTriggeredElementChange(): while
10440             the first one only affects single elements that are triggered
10441             directly, the second one affects multiple elements in the playfield
10442             that are triggered indirectly by another element. This is a third
10443             case: Changing the CE score always affects multiple identical CEs,
10444             so every affected CE must be checked, not only the single CE for
10445             which the CE score was changed in the first place (as every instance
10446             of that CE shares the same CE score, and therefore also can change)!
10447           */
10448           SCAN_PLAYFIELD(xx, yy)
10449           {
10450             if (Feld[xx][yy] == element)
10451               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10452                                  CE_SCORE_GETS_ZERO);
10453           }
10454         }
10455       }
10456 #endif
10457
10458       break;
10459     }
10460
10461     /* ---------- engine actions  ------------------------------------------ */
10462
10463     case CA_SET_ENGINE_SCAN_MODE:
10464     {
10465       InitPlayfieldScanMode(action_arg);
10466
10467       break;
10468     }
10469
10470     default:
10471       break;
10472   }
10473 }
10474
10475 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10476 {
10477   int old_element = Feld[x][y];
10478   int new_element = GetElementFromGroupElement(element);
10479   int previous_move_direction = MovDir[x][y];
10480 #if USE_NEW_CUSTOM_VALUE
10481   int last_ce_value = CustomValue[x][y];
10482 #endif
10483   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10484   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10485   boolean add_player_onto_element = (new_element_is_player &&
10486 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10487                                      /* this breaks SnakeBite when a snake is
10488                                         halfway through a door that closes */
10489                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10490                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10491 #endif
10492                                      IS_WALKABLE(old_element));
10493
10494 #if 0
10495   /* check if element under the player changes from accessible to unaccessible
10496      (needed for special case of dropping element which then changes) */
10497   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10498       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10499   {
10500     Bang(x, y);
10501
10502     return;
10503   }
10504 #endif
10505
10506   if (!add_player_onto_element)
10507   {
10508     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10509       RemoveMovingField(x, y);
10510     else
10511       RemoveField(x, y);
10512
10513     Feld[x][y] = new_element;
10514
10515 #if !USE_GFX_RESET_GFX_ANIMATION
10516     ResetGfxAnimation(x, y);
10517     ResetRandomAnimationValue(x, y);
10518 #endif
10519
10520     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10521       MovDir[x][y] = previous_move_direction;
10522
10523 #if USE_NEW_CUSTOM_VALUE
10524     if (element_info[new_element].use_last_ce_value)
10525       CustomValue[x][y] = last_ce_value;
10526 #endif
10527
10528     InitField_WithBug1(x, y, FALSE);
10529
10530     new_element = Feld[x][y];   /* element may have changed */
10531
10532 #if USE_GFX_RESET_GFX_ANIMATION
10533     ResetGfxAnimation(x, y);
10534     ResetRandomAnimationValue(x, y);
10535 #endif
10536
10537     DrawLevelField(x, y);
10538
10539     if (GFX_CRUMBLED(new_element))
10540       DrawLevelFieldCrumbledSandNeighbours(x, y);
10541   }
10542
10543 #if 1
10544   /* check if element under the player changes from accessible to unaccessible
10545      (needed for special case of dropping element which then changes) */
10546   /* (must be checked after creating new element for walkable group elements) */
10547 #if USE_FIX_KILLED_BY_NON_WALKABLE
10548   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10549       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10550   {
10551     Bang(x, y);
10552
10553     return;
10554   }
10555 #else
10556   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10557       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10558   {
10559     Bang(x, y);
10560
10561     return;
10562   }
10563 #endif
10564 #endif
10565
10566   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10567   if (new_element_is_player)
10568     RelocatePlayer(x, y, new_element);
10569
10570   if (is_change)
10571     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10572
10573   TestIfBadThingTouchesPlayer(x, y);
10574   TestIfPlayerTouchesCustomElement(x, y);
10575   TestIfElementTouchesCustomElement(x, y);
10576 }
10577
10578 static void CreateField(int x, int y, int element)
10579 {
10580   CreateFieldExt(x, y, element, FALSE);
10581 }
10582
10583 static void CreateElementFromChange(int x, int y, int element)
10584 {
10585   element = GET_VALID_RUNTIME_ELEMENT(element);
10586
10587 #if USE_STOP_CHANGED_ELEMENTS
10588   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10589   {
10590     int old_element = Feld[x][y];
10591
10592     /* prevent changed element from moving in same engine frame
10593        unless both old and new element can either fall or move */
10594     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10595         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10596       Stop[x][y] = TRUE;
10597   }
10598 #endif
10599
10600   CreateFieldExt(x, y, element, TRUE);
10601 }
10602
10603 static boolean ChangeElement(int x, int y, int element, int page)
10604 {
10605   struct ElementInfo *ei = &element_info[element];
10606   struct ElementChangeInfo *change = &ei->change_page[page];
10607   int ce_value = CustomValue[x][y];
10608   int ce_score = ei->collect_score;
10609   int target_element;
10610   int old_element = Feld[x][y];
10611
10612   /* always use default change event to prevent running into a loop */
10613   if (ChangeEvent[x][y] == -1)
10614     ChangeEvent[x][y] = CE_DELAY;
10615
10616   if (ChangeEvent[x][y] == CE_DELAY)
10617   {
10618     /* reset actual trigger element, trigger player and action element */
10619     change->actual_trigger_element = EL_EMPTY;
10620     change->actual_trigger_player = EL_PLAYER_1;
10621     change->actual_trigger_player_bits = CH_PLAYER_1;
10622     change->actual_trigger_side = CH_SIDE_NONE;
10623     change->actual_trigger_ce_value = 0;
10624     change->actual_trigger_ce_score = 0;
10625   }
10626
10627   /* do not change elements more than a specified maximum number of changes */
10628   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10629     return FALSE;
10630
10631   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10632
10633   if (change->explode)
10634   {
10635     Bang(x, y);
10636
10637     return TRUE;
10638   }
10639
10640   if (change->use_target_content)
10641   {
10642     boolean complete_replace = TRUE;
10643     boolean can_replace[3][3];
10644     int xx, yy;
10645
10646     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10647     {
10648       boolean is_empty;
10649       boolean is_walkable;
10650       boolean is_diggable;
10651       boolean is_collectible;
10652       boolean is_removable;
10653       boolean is_destructible;
10654       int ex = x + xx - 1;
10655       int ey = y + yy - 1;
10656       int content_element = change->target_content.e[xx][yy];
10657       int e;
10658
10659       can_replace[xx][yy] = TRUE;
10660
10661       if (ex == x && ey == y)   /* do not check changing element itself */
10662         continue;
10663
10664       if (content_element == EL_EMPTY_SPACE)
10665       {
10666         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10667
10668         continue;
10669       }
10670
10671       if (!IN_LEV_FIELD(ex, ey))
10672       {
10673         can_replace[xx][yy] = FALSE;
10674         complete_replace = FALSE;
10675
10676         continue;
10677       }
10678
10679       e = Feld[ex][ey];
10680
10681       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10682         e = MovingOrBlocked2Element(ex, ey);
10683
10684       is_empty = (IS_FREE(ex, ey) ||
10685                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10686
10687       is_walkable     = (is_empty || IS_WALKABLE(e));
10688       is_diggable     = (is_empty || IS_DIGGABLE(e));
10689       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10690       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10691       is_removable    = (is_diggable || is_collectible);
10692
10693       can_replace[xx][yy] =
10694         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10695           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10696           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10697           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10698           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10699           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10700          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10701
10702       if (!can_replace[xx][yy])
10703         complete_replace = FALSE;
10704     }
10705
10706     if (!change->only_if_complete || complete_replace)
10707     {
10708       boolean something_has_changed = FALSE;
10709
10710       if (change->only_if_complete && change->use_random_replace &&
10711           RND(100) < change->random_percentage)
10712         return FALSE;
10713
10714       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10715       {
10716         int ex = x + xx - 1;
10717         int ey = y + yy - 1;
10718         int content_element;
10719
10720         if (can_replace[xx][yy] && (!change->use_random_replace ||
10721                                     RND(100) < change->random_percentage))
10722         {
10723           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10724             RemoveMovingField(ex, ey);
10725
10726           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10727
10728           content_element = change->target_content.e[xx][yy];
10729           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10730                                               ce_value, ce_score);
10731
10732           CreateElementFromChange(ex, ey, target_element);
10733
10734           something_has_changed = TRUE;
10735
10736           /* for symmetry reasons, freeze newly created border elements */
10737           if (ex != x || ey != y)
10738             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10739         }
10740       }
10741
10742       if (something_has_changed)
10743       {
10744         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10745         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10746       }
10747     }
10748   }
10749   else
10750   {
10751     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10752                                         ce_value, ce_score);
10753
10754     if (element == EL_DIAGONAL_GROWING ||
10755         element == EL_DIAGONAL_SHRINKING)
10756     {
10757       target_element = Store[x][y];
10758
10759       Store[x][y] = EL_EMPTY;
10760     }
10761
10762     CreateElementFromChange(x, y, target_element);
10763
10764     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10765     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10766   }
10767
10768   /* this uses direct change before indirect change */
10769   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10770
10771   return TRUE;
10772 }
10773
10774 #if USE_NEW_DELAYED_ACTION
10775
10776 static void HandleElementChange(int x, int y, int page)
10777 {
10778   int element = MovingOrBlocked2Element(x, y);
10779   struct ElementInfo *ei = &element_info[element];
10780   struct ElementChangeInfo *change = &ei->change_page[page];
10781
10782 #ifdef DEBUG
10783   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10784       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10785   {
10786     printf("\n\n");
10787     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10788            x, y, element, element_info[element].token_name);
10789     printf("HandleElementChange(): This should never happen!\n");
10790     printf("\n\n");
10791   }
10792 #endif
10793
10794   /* this can happen with classic bombs on walkable, changing elements */
10795   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10796   {
10797 #if 0
10798     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10799       ChangeDelay[x][y] = 0;
10800 #endif
10801
10802     return;
10803   }
10804
10805   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10806   {
10807     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10808
10809     if (change->can_change)
10810     {
10811 #if 1
10812       /* !!! not clear why graphic animation should be reset at all here !!! */
10813       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10814 #if USE_GFX_RESET_WHEN_NOT_MOVING
10815       /* when a custom element is about to change (for example by change delay),
10816          do not reset graphic animation when the custom element is moving */
10817       if (!IS_MOVING(x, y))
10818 #endif
10819       {
10820         ResetGfxAnimation(x, y);
10821         ResetRandomAnimationValue(x, y);
10822       }
10823 #endif
10824
10825       if (change->pre_change_function)
10826         change->pre_change_function(x, y);
10827     }
10828   }
10829
10830   ChangeDelay[x][y]--;
10831
10832   if (ChangeDelay[x][y] != 0)           /* continue element change */
10833   {
10834     if (change->can_change)
10835     {
10836       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10837
10838       if (IS_ANIMATED(graphic))
10839         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10840
10841       if (change->change_function)
10842         change->change_function(x, y);
10843     }
10844   }
10845   else                                  /* finish element change */
10846   {
10847     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10848     {
10849       page = ChangePage[x][y];
10850       ChangePage[x][y] = -1;
10851
10852       change = &ei->change_page[page];
10853     }
10854
10855     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10856     {
10857       ChangeDelay[x][y] = 1;            /* try change after next move step */
10858       ChangePage[x][y] = page;          /* remember page to use for change */
10859
10860       return;
10861     }
10862
10863     if (change->can_change)
10864     {
10865       if (ChangeElement(x, y, element, page))
10866       {
10867         if (change->post_change_function)
10868           change->post_change_function(x, y);
10869       }
10870     }
10871
10872     if (change->has_action)
10873       ExecuteCustomElementAction(x, y, element, page);
10874   }
10875 }
10876
10877 #else
10878
10879 static void HandleElementChange(int x, int y, int page)
10880 {
10881   int element = MovingOrBlocked2Element(x, y);
10882   struct ElementInfo *ei = &element_info[element];
10883   struct ElementChangeInfo *change = &ei->change_page[page];
10884
10885 #ifdef DEBUG
10886   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10887   {
10888     printf("\n\n");
10889     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10890            x, y, element, element_info[element].token_name);
10891     printf("HandleElementChange(): This should never happen!\n");
10892     printf("\n\n");
10893   }
10894 #endif
10895
10896   /* this can happen with classic bombs on walkable, changing elements */
10897   if (!CAN_CHANGE(element))
10898   {
10899 #if 0
10900     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10901       ChangeDelay[x][y] = 0;
10902 #endif
10903
10904     return;
10905   }
10906
10907   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10908   {
10909     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10910
10911     ResetGfxAnimation(x, y);
10912     ResetRandomAnimationValue(x, y);
10913
10914     if (change->pre_change_function)
10915       change->pre_change_function(x, y);
10916   }
10917
10918   ChangeDelay[x][y]--;
10919
10920   if (ChangeDelay[x][y] != 0)           /* continue element change */
10921   {
10922     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10923
10924     if (IS_ANIMATED(graphic))
10925       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10926
10927     if (change->change_function)
10928       change->change_function(x, y);
10929   }
10930   else                                  /* finish element change */
10931   {
10932     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10933     {
10934       page = ChangePage[x][y];
10935       ChangePage[x][y] = -1;
10936
10937       change = &ei->change_page[page];
10938     }
10939
10940     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10941     {
10942       ChangeDelay[x][y] = 1;            /* try change after next move step */
10943       ChangePage[x][y] = page;          /* remember page to use for change */
10944
10945       return;
10946     }
10947
10948     if (ChangeElement(x, y, element, page))
10949     {
10950       if (change->post_change_function)
10951         change->post_change_function(x, y);
10952     }
10953   }
10954 }
10955
10956 #endif
10957
10958 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10959                                               int trigger_element,
10960                                               int trigger_event,
10961                                               int trigger_player,
10962                                               int trigger_side,
10963                                               int trigger_page)
10964 {
10965   boolean change_done_any = FALSE;
10966   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10967   int i;
10968
10969   if (!(trigger_events[trigger_element][trigger_event]))
10970     return FALSE;
10971
10972 #if 0
10973   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10974          trigger_event, recursion_loop_depth, recursion_loop_detected,
10975          recursion_loop_element, EL_NAME(recursion_loop_element));
10976 #endif
10977
10978   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10979
10980   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10981   {
10982     int element = EL_CUSTOM_START + i;
10983     boolean change_done = FALSE;
10984     int p;
10985
10986     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10987         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10988       continue;
10989
10990     for (p = 0; p < element_info[element].num_change_pages; p++)
10991     {
10992       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10993
10994       if (change->can_change_or_has_action &&
10995           change->has_event[trigger_event] &&
10996           change->trigger_side & trigger_side &&
10997           change->trigger_player & trigger_player &&
10998           change->trigger_page & trigger_page_bits &&
10999           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11000       {
11001         change->actual_trigger_element = trigger_element;
11002         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11003         change->actual_trigger_player_bits = trigger_player;
11004         change->actual_trigger_side = trigger_side;
11005         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11006         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11007
11008         if ((change->can_change && !change_done) || change->has_action)
11009         {
11010           int x, y;
11011
11012           SCAN_PLAYFIELD(x, y)
11013           {
11014             if (Feld[x][y] == element)
11015             {
11016               if (change->can_change && !change_done)
11017               {
11018                 ChangeDelay[x][y] = 1;
11019                 ChangeEvent[x][y] = trigger_event;
11020
11021                 HandleElementChange(x, y, p);
11022               }
11023 #if USE_NEW_DELAYED_ACTION
11024               else if (change->has_action)
11025               {
11026                 ExecuteCustomElementAction(x, y, element, p);
11027                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11028               }
11029 #else
11030               if (change->has_action)
11031               {
11032                 ExecuteCustomElementAction(x, y, element, p);
11033                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11034               }
11035 #endif
11036             }
11037           }
11038
11039           if (change->can_change)
11040           {
11041             change_done = TRUE;
11042             change_done_any = TRUE;
11043           }
11044         }
11045       }
11046     }
11047   }
11048
11049   RECURSION_LOOP_DETECTION_END();
11050
11051   return change_done_any;
11052 }
11053
11054 static boolean CheckElementChangeExt(int x, int y,
11055                                      int element,
11056                                      int trigger_element,
11057                                      int trigger_event,
11058                                      int trigger_player,
11059                                      int trigger_side)
11060 {
11061   boolean change_done = FALSE;
11062   int p;
11063
11064   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11065       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11066     return FALSE;
11067
11068   if (Feld[x][y] == EL_BLOCKED)
11069   {
11070     Blocked2Moving(x, y, &x, &y);
11071     element = Feld[x][y];
11072   }
11073
11074 #if 0
11075   /* check if element has already changed */
11076   if (Feld[x][y] != element)
11077     return FALSE;
11078 #else
11079   /* check if element has already changed or is about to change after moving */
11080   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11081        Feld[x][y] != element) ||
11082
11083       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11084        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11085         ChangePage[x][y] != -1)))
11086     return FALSE;
11087 #endif
11088
11089 #if 0
11090   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11091          trigger_event, recursion_loop_depth, recursion_loop_detected,
11092          recursion_loop_element, EL_NAME(recursion_loop_element));
11093 #endif
11094
11095   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11096
11097   for (p = 0; p < element_info[element].num_change_pages; p++)
11098   {
11099     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11100
11101     /* check trigger element for all events where the element that is checked
11102        for changing interacts with a directly adjacent element -- this is
11103        different to element changes that affect other elements to change on the
11104        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11105     boolean check_trigger_element =
11106       (trigger_event == CE_TOUCHING_X ||
11107        trigger_event == CE_HITTING_X ||
11108        trigger_event == CE_HIT_BY_X ||
11109 #if 1
11110        /* this one was forgotten until 3.2.3 */
11111        trigger_event == CE_DIGGING_X);
11112 #endif
11113
11114     if (change->can_change_or_has_action &&
11115         change->has_event[trigger_event] &&
11116         change->trigger_side & trigger_side &&
11117         change->trigger_player & trigger_player &&
11118         (!check_trigger_element ||
11119          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11120     {
11121       change->actual_trigger_element = trigger_element;
11122       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11123       change->actual_trigger_player_bits = trigger_player;
11124       change->actual_trigger_side = trigger_side;
11125       change->actual_trigger_ce_value = CustomValue[x][y];
11126       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11127
11128       /* special case: trigger element not at (x,y) position for some events */
11129       if (check_trigger_element)
11130       {
11131         static struct
11132         {
11133           int dx, dy;
11134         } move_xy[] =
11135           {
11136             {  0,  0 },
11137             { -1,  0 },
11138             { +1,  0 },
11139             {  0,  0 },
11140             {  0, -1 },
11141             {  0,  0 }, { 0, 0 }, { 0, 0 },
11142             {  0, +1 }
11143           };
11144
11145         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11146         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11147
11148         change->actual_trigger_ce_value = CustomValue[xx][yy];
11149         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11150       }
11151
11152       if (change->can_change && !change_done)
11153       {
11154         ChangeDelay[x][y] = 1;
11155         ChangeEvent[x][y] = trigger_event;
11156
11157         HandleElementChange(x, y, p);
11158
11159         change_done = TRUE;
11160       }
11161 #if USE_NEW_DELAYED_ACTION
11162       else if (change->has_action)
11163       {
11164         ExecuteCustomElementAction(x, y, element, p);
11165         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11166       }
11167 #else
11168       if (change->has_action)
11169       {
11170         ExecuteCustomElementAction(x, y, element, p);
11171         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11172       }
11173 #endif
11174     }
11175   }
11176
11177   RECURSION_LOOP_DETECTION_END();
11178
11179   return change_done;
11180 }
11181
11182 static void PlayPlayerSound(struct PlayerInfo *player)
11183 {
11184   int jx = player->jx, jy = player->jy;
11185   int sound_element = player->artwork_element;
11186   int last_action = player->last_action_waiting;
11187   int action = player->action_waiting;
11188
11189   if (player->is_waiting)
11190   {
11191     if (action != last_action)
11192       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11193     else
11194       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11195   }
11196   else
11197   {
11198     if (action != last_action)
11199       StopSound(element_info[sound_element].sound[last_action]);
11200
11201     if (last_action == ACTION_SLEEPING)
11202       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11203   }
11204 }
11205
11206 static void PlayAllPlayersSound()
11207 {
11208   int i;
11209
11210   for (i = 0; i < MAX_PLAYERS; i++)
11211     if (stored_player[i].active)
11212       PlayPlayerSound(&stored_player[i]);
11213 }
11214
11215 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11216 {
11217   boolean last_waiting = player->is_waiting;
11218   int move_dir = player->MovDir;
11219
11220   player->dir_waiting = move_dir;
11221   player->last_action_waiting = player->action_waiting;
11222
11223   if (is_waiting)
11224   {
11225     if (!last_waiting)          /* not waiting -> waiting */
11226     {
11227       player->is_waiting = TRUE;
11228
11229       player->frame_counter_bored =
11230         FrameCounter +
11231         game.player_boring_delay_fixed +
11232         GetSimpleRandom(game.player_boring_delay_random);
11233       player->frame_counter_sleeping =
11234         FrameCounter +
11235         game.player_sleeping_delay_fixed +
11236         GetSimpleRandom(game.player_sleeping_delay_random);
11237
11238       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11239     }
11240
11241     if (game.player_sleeping_delay_fixed +
11242         game.player_sleeping_delay_random > 0 &&
11243         player->anim_delay_counter == 0 &&
11244         player->post_delay_counter == 0 &&
11245         FrameCounter >= player->frame_counter_sleeping)
11246       player->is_sleeping = TRUE;
11247     else if (game.player_boring_delay_fixed +
11248              game.player_boring_delay_random > 0 &&
11249              FrameCounter >= player->frame_counter_bored)
11250       player->is_bored = TRUE;
11251
11252     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11253                               player->is_bored ? ACTION_BORING :
11254                               ACTION_WAITING);
11255
11256     if (player->is_sleeping && player->use_murphy)
11257     {
11258       /* special case for sleeping Murphy when leaning against non-free tile */
11259
11260       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11261           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11262            !IS_MOVING(player->jx - 1, player->jy)))
11263         move_dir = MV_LEFT;
11264       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11265                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11266                 !IS_MOVING(player->jx + 1, player->jy)))
11267         move_dir = MV_RIGHT;
11268       else
11269         player->is_sleeping = FALSE;
11270
11271       player->dir_waiting = move_dir;
11272     }
11273
11274     if (player->is_sleeping)
11275     {
11276       if (player->num_special_action_sleeping > 0)
11277       {
11278         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11279         {
11280           int last_special_action = player->special_action_sleeping;
11281           int num_special_action = player->num_special_action_sleeping;
11282           int special_action =
11283             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11284              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11285              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11286              last_special_action + 1 : ACTION_SLEEPING);
11287           int special_graphic =
11288             el_act_dir2img(player->artwork_element, special_action, move_dir);
11289
11290           player->anim_delay_counter =
11291             graphic_info[special_graphic].anim_delay_fixed +
11292             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11293           player->post_delay_counter =
11294             graphic_info[special_graphic].post_delay_fixed +
11295             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11296
11297           player->special_action_sleeping = special_action;
11298         }
11299
11300         if (player->anim_delay_counter > 0)
11301         {
11302           player->action_waiting = player->special_action_sleeping;
11303           player->anim_delay_counter--;
11304         }
11305         else if (player->post_delay_counter > 0)
11306         {
11307           player->post_delay_counter--;
11308         }
11309       }
11310     }
11311     else if (player->is_bored)
11312     {
11313       if (player->num_special_action_bored > 0)
11314       {
11315         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11316         {
11317           int special_action =
11318             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11319           int special_graphic =
11320             el_act_dir2img(player->artwork_element, special_action, move_dir);
11321
11322           player->anim_delay_counter =
11323             graphic_info[special_graphic].anim_delay_fixed +
11324             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11325           player->post_delay_counter =
11326             graphic_info[special_graphic].post_delay_fixed +
11327             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11328
11329           player->special_action_bored = special_action;
11330         }
11331
11332         if (player->anim_delay_counter > 0)
11333         {
11334           player->action_waiting = player->special_action_bored;
11335           player->anim_delay_counter--;
11336         }
11337         else if (player->post_delay_counter > 0)
11338         {
11339           player->post_delay_counter--;
11340         }
11341       }
11342     }
11343   }
11344   else if (last_waiting)        /* waiting -> not waiting */
11345   {
11346     player->is_waiting = FALSE;
11347     player->is_bored = FALSE;
11348     player->is_sleeping = FALSE;
11349
11350     player->frame_counter_bored = -1;
11351     player->frame_counter_sleeping = -1;
11352
11353     player->anim_delay_counter = 0;
11354     player->post_delay_counter = 0;
11355
11356     player->dir_waiting = player->MovDir;
11357     player->action_waiting = ACTION_DEFAULT;
11358
11359     player->special_action_bored = ACTION_DEFAULT;
11360     player->special_action_sleeping = ACTION_DEFAULT;
11361   }
11362 }
11363
11364 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11365 {
11366   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11367   int left      = player_action & JOY_LEFT;
11368   int right     = player_action & JOY_RIGHT;
11369   int up        = player_action & JOY_UP;
11370   int down      = player_action & JOY_DOWN;
11371   int button1   = player_action & JOY_BUTTON_1;
11372   int button2   = player_action & JOY_BUTTON_2;
11373   int dx        = (left ? -1 : right ? 1 : 0);
11374   int dy        = (up   ? -1 : down  ? 1 : 0);
11375
11376   if (!player->active || tape.pausing)
11377     return 0;
11378
11379   if (player_action)
11380   {
11381     if (button1)
11382       snapped = SnapField(player, dx, dy);
11383     else
11384     {
11385       if (button2)
11386         dropped = DropElement(player);
11387
11388       moved = MovePlayer(player, dx, dy);
11389     }
11390
11391     if (tape.single_step && tape.recording && !tape.pausing)
11392     {
11393       if (button1 || (dropped && !moved))
11394       {
11395         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11396         SnapField(player, 0, 0);                /* stop snapping */
11397       }
11398     }
11399
11400     SetPlayerWaiting(player, FALSE);
11401
11402     return player_action;
11403   }
11404   else
11405   {
11406     /* no actions for this player (no input at player's configured device) */
11407
11408     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11409     SnapField(player, 0, 0);
11410     CheckGravityMovementWhenNotMoving(player);
11411
11412     if (player->MovPos == 0)
11413       SetPlayerWaiting(player, TRUE);
11414
11415     if (player->MovPos == 0)    /* needed for tape.playing */
11416       player->is_moving = FALSE;
11417
11418     player->is_dropping = FALSE;
11419     player->is_dropping_pressed = FALSE;
11420     player->drop_pressed_delay = 0;
11421
11422     return 0;
11423   }
11424 }
11425
11426 static void CheckLevelTime()
11427 {
11428   int i;
11429
11430   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11431   {
11432     if (level.native_em_level->lev->home == 0)  /* all players at home */
11433     {
11434       PlayerWins(local_player);
11435
11436       AllPlayersGone = TRUE;
11437
11438       level.native_em_level->lev->home = -1;
11439     }
11440
11441     if (level.native_em_level->ply[0]->alive == 0 &&
11442         level.native_em_level->ply[1]->alive == 0 &&
11443         level.native_em_level->ply[2]->alive == 0 &&
11444         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11445       AllPlayersGone = TRUE;
11446   }
11447
11448   if (TimeFrames >= FRAMES_PER_SECOND)
11449   {
11450     TimeFrames = 0;
11451     TapeTime++;
11452
11453     for (i = 0; i < MAX_PLAYERS; i++)
11454     {
11455       struct PlayerInfo *player = &stored_player[i];
11456
11457       if (SHIELD_ON(player))
11458       {
11459         player->shield_normal_time_left--;
11460
11461         if (player->shield_deadly_time_left > 0)
11462           player->shield_deadly_time_left--;
11463       }
11464     }
11465
11466     if (!local_player->LevelSolved && !level.use_step_counter)
11467     {
11468       TimePlayed++;
11469
11470       if (TimeLeft > 0)
11471       {
11472         TimeLeft--;
11473
11474         if (TimeLeft <= 10 && setup.time_limit)
11475           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11476
11477 #if 1
11478         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11479
11480         DisplayGameControlValues();
11481 #else
11482         DrawGameValue_Time(TimeLeft);
11483 #endif
11484
11485         if (!TimeLeft && setup.time_limit)
11486         {
11487           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11488             level.native_em_level->lev->killed_out_of_time = TRUE;
11489           else
11490             for (i = 0; i < MAX_PLAYERS; i++)
11491               KillPlayer(&stored_player[i]);
11492         }
11493       }
11494 #if 1
11495       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11496       {
11497         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11498
11499         DisplayGameControlValues();
11500       }
11501 #else
11502       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11503         DrawGameValue_Time(TimePlayed);
11504 #endif
11505
11506       level.native_em_level->lev->time =
11507         (level.time == 0 ? TimePlayed : TimeLeft);
11508     }
11509
11510     if (tape.recording || tape.playing)
11511       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11512   }
11513
11514 #if 1
11515   UpdateAndDisplayGameControlValues();
11516 #else
11517   UpdateGameDoorValues();
11518   DrawGameDoorValues();
11519 #endif
11520 }
11521
11522 void AdvanceFrameAndPlayerCounters(int player_nr)
11523 {
11524   int i;
11525
11526   /* advance frame counters (global frame counter and time frame counter) */
11527   FrameCounter++;
11528   TimeFrames++;
11529
11530   /* advance player counters (counters for move delay, move animation etc.) */
11531   for (i = 0; i < MAX_PLAYERS; i++)
11532   {
11533     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11534     int move_delay_value = stored_player[i].move_delay_value;
11535     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11536
11537     if (!advance_player_counters)       /* not all players may be affected */
11538       continue;
11539
11540 #if USE_NEW_PLAYER_ANIM
11541     if (move_frames == 0)       /* less than one move per game frame */
11542     {
11543       int stepsize = TILEX / move_delay_value;
11544       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11545       int count = (stored_player[i].is_moving ?
11546                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11547
11548       if (count % delay == 0)
11549         move_frames = 1;
11550     }
11551 #endif
11552
11553     stored_player[i].Frame += move_frames;
11554
11555     if (stored_player[i].MovPos != 0)
11556       stored_player[i].StepFrame += move_frames;
11557
11558     if (stored_player[i].move_delay > 0)
11559       stored_player[i].move_delay--;
11560
11561     /* due to bugs in previous versions, counter must count up, not down */
11562     if (stored_player[i].push_delay != -1)
11563       stored_player[i].push_delay++;
11564
11565     if (stored_player[i].drop_delay > 0)
11566       stored_player[i].drop_delay--;
11567
11568     if (stored_player[i].is_dropping_pressed)
11569       stored_player[i].drop_pressed_delay++;
11570   }
11571 }
11572
11573 void StartGameActions(boolean init_network_game, boolean record_tape,
11574                       long random_seed)
11575 {
11576   unsigned long new_random_seed = InitRND(random_seed);
11577
11578   if (record_tape)
11579     TapeStartRecording(new_random_seed);
11580
11581 #if defined(NETWORK_AVALIABLE)
11582   if (init_network_game)
11583   {
11584     SendToServer_StartPlaying();
11585
11586     return;
11587   }
11588 #endif
11589
11590   InitGame();
11591 }
11592
11593 void GameActions()
11594 {
11595   static unsigned long game_frame_delay = 0;
11596   unsigned long game_frame_delay_value;
11597   byte *recorded_player_action;
11598   byte summarized_player_action = 0;
11599   byte tape_action[MAX_PLAYERS];
11600   int i;
11601
11602   /* detect endless loops, caused by custom element programming */
11603   if (recursion_loop_detected && recursion_loop_depth == 0)
11604   {
11605     char *message = getStringCat3("Internal Error ! Element ",
11606                                   EL_NAME(recursion_loop_element),
11607                                   " caused endless loop ! Quit the game ?");
11608
11609     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11610           EL_NAME(recursion_loop_element));
11611
11612     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11613
11614     recursion_loop_detected = FALSE;    /* if game should be continued */
11615
11616     free(message);
11617
11618     return;
11619   }
11620
11621   if (game.restart_level)
11622     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11623
11624   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11625   {
11626     if (level.native_em_level->lev->home == 0)  /* all players at home */
11627     {
11628       PlayerWins(local_player);
11629
11630       AllPlayersGone = TRUE;
11631
11632       level.native_em_level->lev->home = -1;
11633     }
11634
11635     if (level.native_em_level->ply[0]->alive == 0 &&
11636         level.native_em_level->ply[1]->alive == 0 &&
11637         level.native_em_level->ply[2]->alive == 0 &&
11638         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11639       AllPlayersGone = TRUE;
11640   }
11641
11642   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11643     GameWon();
11644
11645   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11646     TapeStop();
11647
11648   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11649     return;
11650
11651   game_frame_delay_value =
11652     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11653
11654   if (tape.playing && tape.warp_forward && !tape.pausing)
11655     game_frame_delay_value = 0;
11656
11657   /* ---------- main game synchronization point ---------- */
11658
11659   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11660
11661   if (network_playing && !network_player_action_received)
11662   {
11663     /* try to get network player actions in time */
11664
11665 #if defined(NETWORK_AVALIABLE)
11666     /* last chance to get network player actions without main loop delay */
11667     HandleNetworking();
11668 #endif
11669
11670     /* game was quit by network peer */
11671     if (game_status != GAME_MODE_PLAYING)
11672       return;
11673
11674     if (!network_player_action_received)
11675       return;           /* failed to get network player actions in time */
11676
11677     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11678   }
11679
11680   if (tape.pausing)
11681     return;
11682
11683   /* at this point we know that we really continue executing the game */
11684
11685   network_player_action_received = FALSE;
11686
11687   /* when playing tape, read previously recorded player input from tape data */
11688   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11689
11690 #if 1
11691   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11692   if (tape.pausing)
11693     return;
11694 #endif
11695
11696   if (tape.set_centered_player)
11697   {
11698     game.centered_player_nr_next = tape.centered_player_nr_next;
11699     game.set_centered_player = TRUE;
11700   }
11701
11702   for (i = 0; i < MAX_PLAYERS; i++)
11703   {
11704     summarized_player_action |= stored_player[i].action;
11705
11706     if (!network_playing)
11707       stored_player[i].effective_action = stored_player[i].action;
11708   }
11709
11710 #if defined(NETWORK_AVALIABLE)
11711   if (network_playing)
11712     SendToServer_MovePlayer(summarized_player_action);
11713 #endif
11714
11715   if (!options.network && !setup.team_mode)
11716     local_player->effective_action = summarized_player_action;
11717
11718   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11719   {
11720     for (i = 0; i < MAX_PLAYERS; i++)
11721       stored_player[i].effective_action =
11722         (i == game.centered_player_nr ? summarized_player_action : 0);
11723   }
11724
11725   if (recorded_player_action != NULL)
11726     for (i = 0; i < MAX_PLAYERS; i++)
11727       stored_player[i].effective_action = recorded_player_action[i];
11728
11729   for (i = 0; i < MAX_PLAYERS; i++)
11730   {
11731     tape_action[i] = stored_player[i].effective_action;
11732
11733     /* (this can only happen in the R'n'D game engine) */
11734     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11735       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11736   }
11737
11738   /* only record actions from input devices, but not programmed actions */
11739   if (tape.recording)
11740     TapeRecordAction(tape_action);
11741
11742   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11743   {
11744     GameActions_EM_Main();
11745   }
11746   else
11747   {
11748     GameActions_RND();
11749   }
11750 }
11751
11752 void GameActions_EM_Main()
11753 {
11754   byte effective_action[MAX_PLAYERS];
11755   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11756   int i;
11757
11758   for (i = 0; i < MAX_PLAYERS; i++)
11759     effective_action[i] = stored_player[i].effective_action;
11760
11761   GameActions_EM(effective_action, warp_mode);
11762
11763   CheckLevelTime();
11764
11765   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11766 }
11767
11768 void GameActions_RND()
11769 {
11770   int magic_wall_x = 0, magic_wall_y = 0;
11771   int i, x, y, element, graphic;
11772
11773   InitPlayfieldScanModeVars();
11774
11775 #if USE_ONE_MORE_CHANGE_PER_FRAME
11776   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11777   {
11778     SCAN_PLAYFIELD(x, y)
11779     {
11780       ChangeCount[x][y] = 0;
11781       ChangeEvent[x][y] = -1;
11782     }
11783   }
11784 #endif
11785
11786   if (game.set_centered_player)
11787   {
11788     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11789
11790     /* switching to "all players" only possible if all players fit to screen */
11791     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11792     {
11793       game.centered_player_nr_next = game.centered_player_nr;
11794       game.set_centered_player = FALSE;
11795     }
11796
11797     /* do not switch focus to non-existing (or non-active) player */
11798     if (game.centered_player_nr_next >= 0 &&
11799         !stored_player[game.centered_player_nr_next].active)
11800     {
11801       game.centered_player_nr_next = game.centered_player_nr;
11802       game.set_centered_player = FALSE;
11803     }
11804   }
11805
11806   if (game.set_centered_player &&
11807       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11808   {
11809     int sx, sy;
11810
11811     if (game.centered_player_nr_next == -1)
11812     {
11813       setScreenCenteredToAllPlayers(&sx, &sy);
11814     }
11815     else
11816     {
11817       sx = stored_player[game.centered_player_nr_next].jx;
11818       sy = stored_player[game.centered_player_nr_next].jy;
11819     }
11820
11821     game.centered_player_nr = game.centered_player_nr_next;
11822     game.set_centered_player = FALSE;
11823
11824     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11825     DrawGameDoorValues();
11826   }
11827
11828   for (i = 0; i < MAX_PLAYERS; i++)
11829   {
11830     int actual_player_action = stored_player[i].effective_action;
11831
11832 #if 1
11833     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11834        - rnd_equinox_tetrachloride 048
11835        - rnd_equinox_tetrachloride_ii 096
11836        - rnd_emanuel_schmieg 002
11837        - doctor_sloan_ww 001, 020
11838     */
11839     if (stored_player[i].MovPos == 0)
11840       CheckGravityMovement(&stored_player[i]);
11841 #endif
11842
11843     /* overwrite programmed action with tape action */
11844     if (stored_player[i].programmed_action)
11845       actual_player_action = stored_player[i].programmed_action;
11846
11847     PlayerActions(&stored_player[i], actual_player_action);
11848
11849     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11850   }
11851
11852   ScrollScreen(NULL, SCROLL_GO_ON);
11853
11854   /* for backwards compatibility, the following code emulates a fixed bug that
11855      occured when pushing elements (causing elements that just made their last
11856      pushing step to already (if possible) make their first falling step in the
11857      same game frame, which is bad); this code is also needed to use the famous
11858      "spring push bug" which is used in older levels and might be wanted to be
11859      used also in newer levels, but in this case the buggy pushing code is only
11860      affecting the "spring" element and no other elements */
11861
11862   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11863   {
11864     for (i = 0; i < MAX_PLAYERS; i++)
11865     {
11866       struct PlayerInfo *player = &stored_player[i];
11867       int x = player->jx;
11868       int y = player->jy;
11869
11870       if (player->active && player->is_pushing && player->is_moving &&
11871           IS_MOVING(x, y) &&
11872           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11873            Feld[x][y] == EL_SPRING))
11874       {
11875         ContinueMoving(x, y);
11876
11877         /* continue moving after pushing (this is actually a bug) */
11878         if (!IS_MOVING(x, y))
11879           Stop[x][y] = FALSE;
11880       }
11881     }
11882   }
11883
11884 #if 0
11885   debug_print_timestamp(0, "start main loop profiling");
11886 #endif
11887
11888   SCAN_PLAYFIELD(x, y)
11889   {
11890     ChangeCount[x][y] = 0;
11891     ChangeEvent[x][y] = -1;
11892
11893     /* this must be handled before main playfield loop */
11894     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11895     {
11896       MovDelay[x][y]--;
11897       if (MovDelay[x][y] <= 0)
11898         RemoveField(x, y);
11899     }
11900
11901 #if USE_NEW_SNAP_DELAY
11902     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11903     {
11904       MovDelay[x][y]--;
11905       if (MovDelay[x][y] <= 0)
11906       {
11907         RemoveField(x, y);
11908         DrawLevelField(x, y);
11909
11910         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11911       }
11912     }
11913 #endif
11914
11915 #if DEBUG
11916     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11917     {
11918       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11919       printf("GameActions(): This should never happen!\n");
11920
11921       ChangePage[x][y] = -1;
11922     }
11923 #endif
11924
11925     Stop[x][y] = FALSE;
11926     if (WasJustMoving[x][y] > 0)
11927       WasJustMoving[x][y]--;
11928     if (WasJustFalling[x][y] > 0)
11929       WasJustFalling[x][y]--;
11930     if (CheckCollision[x][y] > 0)
11931       CheckCollision[x][y]--;
11932     if (CheckImpact[x][y] > 0)
11933       CheckImpact[x][y]--;
11934
11935     GfxFrame[x][y]++;
11936
11937     /* reset finished pushing action (not done in ContinueMoving() to allow
11938        continuous pushing animation for elements with zero push delay) */
11939     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11940     {
11941       ResetGfxAnimation(x, y);
11942       DrawLevelField(x, y);
11943     }
11944
11945 #if DEBUG
11946     if (IS_BLOCKED(x, y))
11947     {
11948       int oldx, oldy;
11949
11950       Blocked2Moving(x, y, &oldx, &oldy);
11951       if (!IS_MOVING(oldx, oldy))
11952       {
11953         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11954         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11955         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11956         printf("GameActions(): This should never happen!\n");
11957       }
11958     }
11959 #endif
11960   }
11961
11962 #if 0
11963   debug_print_timestamp(0, "- time for pre-main loop:");
11964 #endif
11965
11966 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
11967   SCAN_PLAYFIELD(x, y)
11968   {
11969     element = Feld[x][y];
11970     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11971
11972 #if 1
11973     {
11974 #if 1
11975       int element2 = element;
11976       int graphic2 = graphic;
11977 #else
11978       int element2 = Feld[x][y];
11979       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11980 #endif
11981       int last_gfx_frame = GfxFrame[x][y];
11982
11983       if (graphic_info[graphic2].anim_global_sync)
11984         GfxFrame[x][y] = FrameCounter;
11985       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11986         GfxFrame[x][y] = CustomValue[x][y];
11987       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11988         GfxFrame[x][y] = element_info[element2].collect_score;
11989       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11990         GfxFrame[x][y] = ChangeDelay[x][y];
11991
11992       if (redraw && GfxFrame[x][y] != last_gfx_frame)
11993         DrawLevelGraphicAnimation(x, y, graphic2);
11994     }
11995 #else
11996     ResetGfxFrame(x, y, TRUE);
11997 #endif
11998
11999 #if 1
12000     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12001         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12002       ResetRandomAnimationValue(x, y);
12003 #endif
12004
12005 #if 1
12006     SetRandomAnimationValue(x, y);
12007 #endif
12008
12009 #if 1
12010     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12011 #endif
12012   }
12013 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12014
12015 #if 0
12016   debug_print_timestamp(0, "- time for TEST loop:     -->");
12017 #endif
12018
12019   SCAN_PLAYFIELD(x, y)
12020   {
12021     element = Feld[x][y];
12022     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12023
12024     ResetGfxFrame(x, y, TRUE);
12025
12026     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12027         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12028       ResetRandomAnimationValue(x, y);
12029
12030     SetRandomAnimationValue(x, y);
12031
12032     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12033
12034     if (IS_INACTIVE(element))
12035     {
12036       if (IS_ANIMATED(graphic))
12037         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12038
12039       continue;
12040     }
12041
12042     /* this may take place after moving, so 'element' may have changed */
12043     if (IS_CHANGING(x, y) &&
12044         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12045     {
12046       int page = element_info[element].event_page_nr[CE_DELAY];
12047
12048 #if 1
12049       HandleElementChange(x, y, page);
12050 #else
12051       if (CAN_CHANGE(element))
12052         HandleElementChange(x, y, page);
12053
12054       if (HAS_ACTION(element))
12055         ExecuteCustomElementAction(x, y, element, page);
12056 #endif
12057
12058       element = Feld[x][y];
12059       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12060     }
12061
12062 #if 0   // ---------------------------------------------------------------------
12063
12064     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12065     {
12066       StartMoving(x, y);
12067
12068       element = Feld[x][y];
12069       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12070
12071       if (IS_ANIMATED(graphic) &&
12072           !IS_MOVING(x, y) &&
12073           !Stop[x][y])
12074         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12075
12076       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12077         DrawTwinkleOnField(x, y);
12078     }
12079     else if (IS_MOVING(x, y))
12080       ContinueMoving(x, y);
12081     else
12082     {
12083       switch (element)
12084       {
12085         case EL_ACID:
12086         case EL_EXIT_OPEN:
12087         case EL_EM_EXIT_OPEN:
12088         case EL_SP_EXIT_OPEN:
12089         case EL_STEEL_EXIT_OPEN:
12090         case EL_EM_STEEL_EXIT_OPEN:
12091         case EL_SP_TERMINAL:
12092         case EL_SP_TERMINAL_ACTIVE:
12093         case EL_EXTRA_TIME:
12094         case EL_SHIELD_NORMAL:
12095         case EL_SHIELD_DEADLY:
12096           if (IS_ANIMATED(graphic))
12097             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12098           break;
12099
12100         case EL_DYNAMITE_ACTIVE:
12101         case EL_EM_DYNAMITE_ACTIVE:
12102         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12103         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12104         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12105         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12106         case EL_SP_DISK_RED_ACTIVE:
12107           CheckDynamite(x, y);
12108           break;
12109
12110         case EL_AMOEBA_GROWING:
12111           AmoebeWaechst(x, y);
12112           break;
12113
12114         case EL_AMOEBA_SHRINKING:
12115           AmoebaDisappearing(x, y);
12116           break;
12117
12118 #if !USE_NEW_AMOEBA_CODE
12119         case EL_AMOEBA_WET:
12120         case EL_AMOEBA_DRY:
12121         case EL_AMOEBA_FULL:
12122         case EL_BD_AMOEBA:
12123         case EL_EMC_DRIPPER:
12124           AmoebeAbleger(x, y);
12125           break;
12126 #endif
12127
12128         case EL_GAME_OF_LIFE:
12129         case EL_BIOMAZE:
12130           Life(x, y);
12131           break;
12132
12133         case EL_EXIT_CLOSED:
12134           CheckExit(x, y);
12135           break;
12136
12137         case EL_EM_EXIT_CLOSED:
12138           CheckExitEM(x, y);
12139           break;
12140
12141         case EL_STEEL_EXIT_CLOSED:
12142           CheckExitSteel(x, y);
12143           break;
12144
12145         case EL_EM_STEEL_EXIT_CLOSED:
12146           CheckExitSteelEM(x, y);
12147           break;
12148
12149         case EL_SP_EXIT_CLOSED:
12150           CheckExitSP(x, y);
12151           break;
12152
12153         case EL_EXPANDABLE_WALL_GROWING:
12154         case EL_EXPANDABLE_STEELWALL_GROWING:
12155           MauerWaechst(x, y);
12156           break;
12157
12158         case EL_EXPANDABLE_WALL:
12159         case EL_EXPANDABLE_WALL_HORIZONTAL:
12160         case EL_EXPANDABLE_WALL_VERTICAL:
12161         case EL_EXPANDABLE_WALL_ANY:
12162         case EL_BD_EXPANDABLE_WALL:
12163           MauerAbleger(x, y);
12164           break;
12165
12166         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12167         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12168         case EL_EXPANDABLE_STEELWALL_ANY:
12169           MauerAblegerStahl(x, y);
12170           break;
12171
12172         case EL_FLAMES:
12173           CheckForDragon(x, y);
12174           break;
12175
12176         case EL_EXPLOSION:
12177           break;
12178
12179         case EL_ELEMENT_SNAPPING:
12180         case EL_DIAGONAL_SHRINKING:
12181         case EL_DIAGONAL_GROWING:
12182         {
12183           graphic =
12184             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12185
12186           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12187           break;
12188         }
12189
12190         default:
12191           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12192             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12193           break;
12194       }
12195     }
12196
12197 #else   // ---------------------------------------------------------------------
12198
12199     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12200     {
12201       StartMoving(x, y);
12202
12203       element = Feld[x][y];
12204       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12205
12206       if (IS_ANIMATED(graphic) &&
12207           !IS_MOVING(x, y) &&
12208           !Stop[x][y])
12209         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12210
12211       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12212         DrawTwinkleOnField(x, y);
12213     }
12214     else if ((element == EL_ACID ||
12215               element == EL_EXIT_OPEN ||
12216               element == EL_EM_EXIT_OPEN ||
12217               element == EL_SP_EXIT_OPEN ||
12218               element == EL_STEEL_EXIT_OPEN ||
12219               element == EL_EM_STEEL_EXIT_OPEN ||
12220               element == EL_SP_TERMINAL ||
12221               element == EL_SP_TERMINAL_ACTIVE ||
12222               element == EL_EXTRA_TIME ||
12223               element == EL_SHIELD_NORMAL ||
12224               element == EL_SHIELD_DEADLY) &&
12225              IS_ANIMATED(graphic))
12226       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12227     else if (IS_MOVING(x, y))
12228       ContinueMoving(x, y);
12229     else if (IS_ACTIVE_BOMB(element))
12230       CheckDynamite(x, y);
12231     else if (element == EL_AMOEBA_GROWING)
12232       AmoebeWaechst(x, y);
12233     else if (element == EL_AMOEBA_SHRINKING)
12234       AmoebaDisappearing(x, y);
12235
12236 #if !USE_NEW_AMOEBA_CODE
12237     else if (IS_AMOEBALIVE(element))
12238       AmoebeAbleger(x, y);
12239 #endif
12240
12241     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12242       Life(x, y);
12243     else if (element == EL_EXIT_CLOSED)
12244       CheckExit(x, y);
12245     else if (element == EL_EM_EXIT_CLOSED)
12246       CheckExitEM(x, y);
12247     else if (element == EL_STEEL_EXIT_CLOSED)
12248       CheckExitSteel(x, y);
12249     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12250       CheckExitSteelEM(x, y);
12251     else if (element == EL_SP_EXIT_CLOSED)
12252       CheckExitSP(x, y);
12253     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12254              element == EL_EXPANDABLE_STEELWALL_GROWING)
12255       MauerWaechst(x, y);
12256     else if (element == EL_EXPANDABLE_WALL ||
12257              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12258              element == EL_EXPANDABLE_WALL_VERTICAL ||
12259              element == EL_EXPANDABLE_WALL_ANY ||
12260              element == EL_BD_EXPANDABLE_WALL)
12261       MauerAbleger(x, y);
12262     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12263              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12264              element == EL_EXPANDABLE_STEELWALL_ANY)
12265       MauerAblegerStahl(x, y);
12266     else if (element == EL_FLAMES)
12267       CheckForDragon(x, y);
12268     else if (element == EL_EXPLOSION)
12269       ; /* drawing of correct explosion animation is handled separately */
12270     else if (element == EL_ELEMENT_SNAPPING ||
12271              element == EL_DIAGONAL_SHRINKING ||
12272              element == EL_DIAGONAL_GROWING)
12273     {
12274       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12275
12276       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12277     }
12278     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12279       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12280
12281 #endif  // ---------------------------------------------------------------------
12282
12283     if (IS_BELT_ACTIVE(element))
12284       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12285
12286     if (game.magic_wall_active)
12287     {
12288       int jx = local_player->jx, jy = local_player->jy;
12289
12290       /* play the element sound at the position nearest to the player */
12291       if ((element == EL_MAGIC_WALL_FULL ||
12292            element == EL_MAGIC_WALL_ACTIVE ||
12293            element == EL_MAGIC_WALL_EMPTYING ||
12294            element == EL_BD_MAGIC_WALL_FULL ||
12295            element == EL_BD_MAGIC_WALL_ACTIVE ||
12296            element == EL_BD_MAGIC_WALL_EMPTYING ||
12297            element == EL_DC_MAGIC_WALL_FULL ||
12298            element == EL_DC_MAGIC_WALL_ACTIVE ||
12299            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12300           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12301       {
12302         magic_wall_x = x;
12303         magic_wall_y = y;
12304       }
12305     }
12306   }
12307
12308 #if 0
12309   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12310 #endif
12311
12312 #if USE_NEW_AMOEBA_CODE
12313   /* new experimental amoeba growth stuff */
12314   if (!(FrameCounter % 8))
12315   {
12316     static unsigned long random = 1684108901;
12317
12318     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12319     {
12320       x = RND(lev_fieldx);
12321       y = RND(lev_fieldy);
12322       element = Feld[x][y];
12323
12324       if (!IS_PLAYER(x,y) &&
12325           (element == EL_EMPTY ||
12326            CAN_GROW_INTO(element) ||
12327            element == EL_QUICKSAND_EMPTY ||
12328            element == EL_QUICKSAND_FAST_EMPTY ||
12329            element == EL_ACID_SPLASH_LEFT ||
12330            element == EL_ACID_SPLASH_RIGHT))
12331       {
12332         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12333             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12334             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12335             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12336           Feld[x][y] = EL_AMOEBA_DROP;
12337       }
12338
12339       random = random * 129 + 1;
12340     }
12341   }
12342 #endif
12343
12344 #if 0
12345   if (game.explosions_delayed)
12346 #endif
12347   {
12348     game.explosions_delayed = FALSE;
12349
12350     SCAN_PLAYFIELD(x, y)
12351     {
12352       element = Feld[x][y];
12353
12354       if (ExplodeField[x][y])
12355         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12356       else if (element == EL_EXPLOSION)
12357         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12358
12359       ExplodeField[x][y] = EX_TYPE_NONE;
12360     }
12361
12362     game.explosions_delayed = TRUE;
12363   }
12364
12365   if (game.magic_wall_active)
12366   {
12367     if (!(game.magic_wall_time_left % 4))
12368     {
12369       int element = Feld[magic_wall_x][magic_wall_y];
12370
12371       if (element == EL_BD_MAGIC_WALL_FULL ||
12372           element == EL_BD_MAGIC_WALL_ACTIVE ||
12373           element == EL_BD_MAGIC_WALL_EMPTYING)
12374         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12375       else if (element == EL_DC_MAGIC_WALL_FULL ||
12376                element == EL_DC_MAGIC_WALL_ACTIVE ||
12377                element == EL_DC_MAGIC_WALL_EMPTYING)
12378         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12379       else
12380         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12381     }
12382
12383     if (game.magic_wall_time_left > 0)
12384     {
12385       game.magic_wall_time_left--;
12386
12387       if (!game.magic_wall_time_left)
12388       {
12389         SCAN_PLAYFIELD(x, y)
12390         {
12391           element = Feld[x][y];
12392
12393           if (element == EL_MAGIC_WALL_ACTIVE ||
12394               element == EL_MAGIC_WALL_FULL)
12395           {
12396             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12397             DrawLevelField(x, y);
12398           }
12399           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12400                    element == EL_BD_MAGIC_WALL_FULL)
12401           {
12402             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12403             DrawLevelField(x, y);
12404           }
12405           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12406                    element == EL_DC_MAGIC_WALL_FULL)
12407           {
12408             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12409             DrawLevelField(x, y);
12410           }
12411         }
12412
12413         game.magic_wall_active = FALSE;
12414       }
12415     }
12416   }
12417
12418   if (game.light_time_left > 0)
12419   {
12420     game.light_time_left--;
12421
12422     if (game.light_time_left == 0)
12423       RedrawAllLightSwitchesAndInvisibleElements();
12424   }
12425
12426   if (game.timegate_time_left > 0)
12427   {
12428     game.timegate_time_left--;
12429
12430     if (game.timegate_time_left == 0)
12431       CloseAllOpenTimegates();
12432   }
12433
12434   if (game.lenses_time_left > 0)
12435   {
12436     game.lenses_time_left--;
12437
12438     if (game.lenses_time_left == 0)
12439       RedrawAllInvisibleElementsForLenses();
12440   }
12441
12442   if (game.magnify_time_left > 0)
12443   {
12444     game.magnify_time_left--;
12445
12446     if (game.magnify_time_left == 0)
12447       RedrawAllInvisibleElementsForMagnifier();
12448   }
12449
12450   for (i = 0; i < MAX_PLAYERS; i++)
12451   {
12452     struct PlayerInfo *player = &stored_player[i];
12453
12454     if (SHIELD_ON(player))
12455     {
12456       if (player->shield_deadly_time_left)
12457         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12458       else if (player->shield_normal_time_left)
12459         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12460     }
12461   }
12462
12463   CheckLevelTime();
12464
12465   DrawAllPlayers();
12466   PlayAllPlayersSound();
12467
12468   if (options.debug)                    /* calculate frames per second */
12469   {
12470     static unsigned long fps_counter = 0;
12471     static int fps_frames = 0;
12472     unsigned long fps_delay_ms = Counter() - fps_counter;
12473
12474     fps_frames++;
12475
12476     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
12477     {
12478       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12479
12480       fps_frames = 0;
12481       fps_counter = Counter();
12482     }
12483
12484     redraw_mask |= REDRAW_FPS;
12485   }
12486
12487   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12488
12489   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12490   {
12491     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12492
12493     local_player->show_envelope = 0;
12494   }
12495
12496 #if 0
12497   debug_print_timestamp(0, "stop main loop profiling ");
12498   printf("----------------------------------------------------------\n");
12499 #endif
12500
12501   /* use random number generator in every frame to make it less predictable */
12502   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12503     RND(1);
12504 }
12505
12506 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12507 {
12508   int min_x = x, min_y = y, max_x = x, max_y = y;
12509   int i;
12510
12511   for (i = 0; i < MAX_PLAYERS; i++)
12512   {
12513     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12514
12515     if (!stored_player[i].active || &stored_player[i] == player)
12516       continue;
12517
12518     min_x = MIN(min_x, jx);
12519     min_y = MIN(min_y, jy);
12520     max_x = MAX(max_x, jx);
12521     max_y = MAX(max_y, jy);
12522   }
12523
12524   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12525 }
12526
12527 static boolean AllPlayersInVisibleScreen()
12528 {
12529   int i;
12530
12531   for (i = 0; i < MAX_PLAYERS; i++)
12532   {
12533     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12534
12535     if (!stored_player[i].active)
12536       continue;
12537
12538     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12539       return FALSE;
12540   }
12541
12542   return TRUE;
12543 }
12544
12545 void ScrollLevel(int dx, int dy)
12546 {
12547 #if 1
12548   static Bitmap *bitmap_db_field2 = NULL;
12549   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12550   int x, y;
12551 #else
12552   int i, x, y;
12553 #endif
12554
12555 #if 0
12556   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12557   /* only horizontal XOR vertical scroll direction allowed */
12558   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12559     return;
12560 #endif
12561
12562 #if 1
12563   if (bitmap_db_field2 == NULL)
12564     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12565
12566   /* needed when blitting directly to same bitmap -- should not be needed with
12567      recent SDL libraries, but apparently does not work in 1.2.11 directly */
12568   BlitBitmap(drawto_field, bitmap_db_field2,
12569              FX + TILEX * (dx == -1) - softscroll_offset,
12570              FY + TILEY * (dy == -1) - softscroll_offset,
12571              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12572              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12573              FX + TILEX * (dx == 1) - softscroll_offset,
12574              FY + TILEY * (dy == 1) - softscroll_offset);
12575   BlitBitmap(bitmap_db_field2, drawto_field,
12576              FX + TILEX * (dx == 1) - softscroll_offset,
12577              FY + TILEY * (dy == 1) - softscroll_offset,
12578              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12579              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12580              FX + TILEX * (dx == 1) - softscroll_offset,
12581              FY + TILEY * (dy == 1) - softscroll_offset);
12582
12583 #else
12584
12585 #if 0
12586   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12587   int xsize = (BX2 - BX1 + 1);
12588   int ysize = (BY2 - BY1 + 1);
12589   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12590   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12591   int step  = (start < end ? +1 : -1);
12592
12593   for (i = start; i != end; i += step)
12594   {
12595     BlitBitmap(drawto_field, drawto_field,
12596                FX + TILEX * (dx != 0 ? i + step : 0),
12597                FY + TILEY * (dy != 0 ? i + step : 0),
12598                TILEX * (dx != 0 ? 1 : xsize),
12599                TILEY * (dy != 0 ? 1 : ysize),
12600                FX + TILEX * (dx != 0 ? i : 0),
12601                FY + TILEY * (dy != 0 ? i : 0));
12602   }
12603
12604 #else
12605
12606   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12607
12608   BlitBitmap(drawto_field, drawto_field,
12609              FX + TILEX * (dx == -1) - softscroll_offset,
12610              FY + TILEY * (dy == -1) - softscroll_offset,
12611              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12612              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12613              FX + TILEX * (dx == 1) - softscroll_offset,
12614              FY + TILEY * (dy == 1) - softscroll_offset);
12615 #endif
12616 #endif
12617
12618   if (dx != 0)
12619   {
12620     x = (dx == 1 ? BX1 : BX2);
12621     for (y = BY1; y <= BY2; y++)
12622       DrawScreenField(x, y);
12623   }
12624
12625   if (dy != 0)
12626   {
12627     y = (dy == 1 ? BY1 : BY2);
12628     for (x = BX1; x <= BX2; x++)
12629       DrawScreenField(x, y);
12630   }
12631
12632   redraw_mask |= REDRAW_FIELD;
12633 }
12634
12635 static boolean canFallDown(struct PlayerInfo *player)
12636 {
12637   int jx = player->jx, jy = player->jy;
12638
12639   return (IN_LEV_FIELD(jx, jy + 1) &&
12640           (IS_FREE(jx, jy + 1) ||
12641            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12642           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12643           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12644 }
12645
12646 static boolean canPassField(int x, int y, int move_dir)
12647 {
12648   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12649   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12650   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12651   int nextx = x + dx;
12652   int nexty = y + dy;
12653   int element = Feld[x][y];
12654
12655   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12656           !CAN_MOVE(element) &&
12657           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12658           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12659           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12660 }
12661
12662 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12663 {
12664   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12665   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12666   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12667   int newx = x + dx;
12668   int newy = y + dy;
12669
12670   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12671           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12672           (IS_DIGGABLE(Feld[newx][newy]) ||
12673            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12674            canPassField(newx, newy, move_dir)));
12675 }
12676
12677 static void CheckGravityMovement(struct PlayerInfo *player)
12678 {
12679 #if USE_PLAYER_GRAVITY
12680   if (player->gravity && !player->programmed_action)
12681 #else
12682   if (game.gravity && !player->programmed_action)
12683 #endif
12684   {
12685     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12686     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12687     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12688     int jx = player->jx, jy = player->jy;
12689     boolean player_is_moving_to_valid_field =
12690       (!player_is_snapping &&
12691        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12692         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12693     boolean player_can_fall_down = canFallDown(player);
12694
12695     if (player_can_fall_down &&
12696         !player_is_moving_to_valid_field)
12697       player->programmed_action = MV_DOWN;
12698   }
12699 }
12700
12701 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12702 {
12703   return CheckGravityMovement(player);
12704
12705 #if USE_PLAYER_GRAVITY
12706   if (player->gravity && !player->programmed_action)
12707 #else
12708   if (game.gravity && !player->programmed_action)
12709 #endif
12710   {
12711     int jx = player->jx, jy = player->jy;
12712     boolean field_under_player_is_free =
12713       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12714     boolean player_is_standing_on_valid_field =
12715       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12716        (IS_WALKABLE(Feld[jx][jy]) &&
12717         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12718
12719     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12720       player->programmed_action = MV_DOWN;
12721   }
12722 }
12723
12724 /*
12725   MovePlayerOneStep()
12726   -----------------------------------------------------------------------------
12727   dx, dy:               direction (non-diagonal) to try to move the player to
12728   real_dx, real_dy:     direction as read from input device (can be diagonal)
12729 */
12730
12731 boolean MovePlayerOneStep(struct PlayerInfo *player,
12732                           int dx, int dy, int real_dx, int real_dy)
12733 {
12734   int jx = player->jx, jy = player->jy;
12735   int new_jx = jx + dx, new_jy = jy + dy;
12736 #if !USE_FIXED_DONT_RUN_INTO
12737   int element;
12738 #endif
12739   int can_move;
12740   boolean player_can_move = !player->cannot_move;
12741
12742   if (!player->active || (!dx && !dy))
12743     return MP_NO_ACTION;
12744
12745   player->MovDir = (dx < 0 ? MV_LEFT :
12746                     dx > 0 ? MV_RIGHT :
12747                     dy < 0 ? MV_UP :
12748                     dy > 0 ? MV_DOWN :  MV_NONE);
12749
12750   if (!IN_LEV_FIELD(new_jx, new_jy))
12751     return MP_NO_ACTION;
12752
12753   if (!player_can_move)
12754   {
12755     if (player->MovPos == 0)
12756     {
12757       player->is_moving = FALSE;
12758       player->is_digging = FALSE;
12759       player->is_collecting = FALSE;
12760       player->is_snapping = FALSE;
12761       player->is_pushing = FALSE;
12762     }
12763   }
12764
12765 #if 1
12766   if (!options.network && game.centered_player_nr == -1 &&
12767       !AllPlayersInSight(player, new_jx, new_jy))
12768     return MP_NO_ACTION;
12769 #else
12770   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12771     return MP_NO_ACTION;
12772 #endif
12773
12774 #if !USE_FIXED_DONT_RUN_INTO
12775   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12776
12777   /* (moved to DigField()) */
12778   if (player_can_move && DONT_RUN_INTO(element))
12779   {
12780     if (element == EL_ACID && dx == 0 && dy == 1)
12781     {
12782       SplashAcid(new_jx, new_jy);
12783       Feld[jx][jy] = EL_PLAYER_1;
12784       InitMovingField(jx, jy, MV_DOWN);
12785       Store[jx][jy] = EL_ACID;
12786       ContinueMoving(jx, jy);
12787       BuryPlayer(player);
12788     }
12789     else
12790       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12791
12792     return MP_MOVING;
12793   }
12794 #endif
12795
12796   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12797   if (can_move != MP_MOVING)
12798     return can_move;
12799
12800   /* check if DigField() has caused relocation of the player */
12801   if (player->jx != jx || player->jy != jy)
12802     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12803
12804   StorePlayer[jx][jy] = 0;
12805   player->last_jx = jx;
12806   player->last_jy = jy;
12807   player->jx = new_jx;
12808   player->jy = new_jy;
12809   StorePlayer[new_jx][new_jy] = player->element_nr;
12810
12811   if (player->move_delay_value_next != -1)
12812   {
12813     player->move_delay_value = player->move_delay_value_next;
12814     player->move_delay_value_next = -1;
12815   }
12816
12817   player->MovPos =
12818     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12819
12820   player->step_counter++;
12821
12822   PlayerVisit[jx][jy] = FrameCounter;
12823
12824 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12825   player->is_moving = TRUE;
12826 #endif
12827
12828 #if 1
12829   /* should better be called in MovePlayer(), but this breaks some tapes */
12830   ScrollPlayer(player, SCROLL_INIT);
12831 #endif
12832
12833   return MP_MOVING;
12834 }
12835
12836 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12837 {
12838   int jx = player->jx, jy = player->jy;
12839   int old_jx = jx, old_jy = jy;
12840   int moved = MP_NO_ACTION;
12841
12842   if (!player->active)
12843     return FALSE;
12844
12845   if (!dx && !dy)
12846   {
12847     if (player->MovPos == 0)
12848     {
12849       player->is_moving = FALSE;
12850       player->is_digging = FALSE;
12851       player->is_collecting = FALSE;
12852       player->is_snapping = FALSE;
12853       player->is_pushing = FALSE;
12854     }
12855
12856     return FALSE;
12857   }
12858
12859   if (player->move_delay > 0)
12860     return FALSE;
12861
12862   player->move_delay = -1;              /* set to "uninitialized" value */
12863
12864   /* store if player is automatically moved to next field */
12865   player->is_auto_moving = (player->programmed_action != MV_NONE);
12866
12867   /* remove the last programmed player action */
12868   player->programmed_action = 0;
12869
12870   if (player->MovPos)
12871   {
12872     /* should only happen if pre-1.2 tape recordings are played */
12873     /* this is only for backward compatibility */
12874
12875     int original_move_delay_value = player->move_delay_value;
12876
12877 #if DEBUG
12878     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12879            tape.counter);
12880 #endif
12881
12882     /* scroll remaining steps with finest movement resolution */
12883     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12884
12885     while (player->MovPos)
12886     {
12887       ScrollPlayer(player, SCROLL_GO_ON);
12888       ScrollScreen(NULL, SCROLL_GO_ON);
12889
12890       AdvanceFrameAndPlayerCounters(player->index_nr);
12891
12892       DrawAllPlayers();
12893       BackToFront();
12894     }
12895
12896     player->move_delay_value = original_move_delay_value;
12897   }
12898
12899   player->is_active = FALSE;
12900
12901   if (player->last_move_dir & MV_HORIZONTAL)
12902   {
12903     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12904       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12905   }
12906   else
12907   {
12908     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12909       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12910   }
12911
12912 #if USE_FIXED_BORDER_RUNNING_GFX
12913   if (!moved && !player->is_active)
12914   {
12915     player->is_moving = FALSE;
12916     player->is_digging = FALSE;
12917     player->is_collecting = FALSE;
12918     player->is_snapping = FALSE;
12919     player->is_pushing = FALSE;
12920   }
12921 #endif
12922
12923   jx = player->jx;
12924   jy = player->jy;
12925
12926 #if 1
12927   if (moved & MP_MOVING && !ScreenMovPos &&
12928       (player->index_nr == game.centered_player_nr ||
12929        game.centered_player_nr == -1))
12930 #else
12931   if (moved & MP_MOVING && !ScreenMovPos &&
12932       (player == local_player || !options.network))
12933 #endif
12934   {
12935     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12936     int offset = game.scroll_delay_value;
12937
12938     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12939     {
12940       /* actual player has left the screen -- scroll in that direction */
12941       if (jx != old_jx)         /* player has moved horizontally */
12942         scroll_x += (jx - old_jx);
12943       else                      /* player has moved vertically */
12944         scroll_y += (jy - old_jy);
12945     }
12946     else
12947     {
12948       if (jx != old_jx)         /* player has moved horizontally */
12949       {
12950         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12951             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12952           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12953
12954         /* don't scroll over playfield boundaries */
12955         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12956           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12957
12958         /* don't scroll more than one field at a time */
12959         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12960
12961         /* don't scroll against the player's moving direction */
12962         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12963             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12964           scroll_x = old_scroll_x;
12965       }
12966       else                      /* player has moved vertically */
12967       {
12968         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12969             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12970           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12971
12972         /* don't scroll over playfield boundaries */
12973         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12974           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12975
12976         /* don't scroll more than one field at a time */
12977         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12978
12979         /* don't scroll against the player's moving direction */
12980         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12981             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12982           scroll_y = old_scroll_y;
12983       }
12984     }
12985
12986     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12987     {
12988 #if 1
12989       if (!options.network && game.centered_player_nr == -1 &&
12990           !AllPlayersInVisibleScreen())
12991       {
12992         scroll_x = old_scroll_x;
12993         scroll_y = old_scroll_y;
12994       }
12995       else
12996 #else
12997       if (!options.network && !AllPlayersInVisibleScreen())
12998       {
12999         scroll_x = old_scroll_x;
13000         scroll_y = old_scroll_y;
13001       }
13002       else
13003 #endif
13004       {
13005         ScrollScreen(player, SCROLL_INIT);
13006         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13007       }
13008     }
13009   }
13010
13011   player->StepFrame = 0;
13012
13013   if (moved & MP_MOVING)
13014   {
13015     if (old_jx != jx && old_jy == jy)
13016       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13017     else if (old_jx == jx && old_jy != jy)
13018       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13019
13020     DrawLevelField(jx, jy);     /* for "crumbled sand" */
13021
13022     player->last_move_dir = player->MovDir;
13023     player->is_moving = TRUE;
13024     player->is_snapping = FALSE;
13025     player->is_switching = FALSE;
13026     player->is_dropping = FALSE;
13027     player->is_dropping_pressed = FALSE;
13028     player->drop_pressed_delay = 0;
13029
13030 #if 0
13031     /* should better be called here than above, but this breaks some tapes */
13032     ScrollPlayer(player, SCROLL_INIT);
13033 #endif
13034   }
13035   else
13036   {
13037     CheckGravityMovementWhenNotMoving(player);
13038
13039     player->is_moving = FALSE;
13040
13041     /* at this point, the player is allowed to move, but cannot move right now
13042        (e.g. because of something blocking the way) -- ensure that the player
13043        is also allowed to move in the next frame (in old versions before 3.1.1,
13044        the player was forced to wait again for eight frames before next try) */
13045
13046     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13047       player->move_delay = 0;   /* allow direct movement in the next frame */
13048   }
13049
13050   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13051     player->move_delay = player->move_delay_value;
13052
13053   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13054   {
13055     TestIfPlayerTouchesBadThing(jx, jy);
13056     TestIfPlayerTouchesCustomElement(jx, jy);
13057   }
13058
13059   if (!player->active)
13060     RemovePlayer(player);
13061
13062   return moved;
13063 }
13064
13065 void ScrollPlayer(struct PlayerInfo *player, int mode)
13066 {
13067   int jx = player->jx, jy = player->jy;
13068   int last_jx = player->last_jx, last_jy = player->last_jy;
13069   int move_stepsize = TILEX / player->move_delay_value;
13070
13071 #if USE_NEW_PLAYER_SPEED
13072   if (!player->active)
13073     return;
13074
13075   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13076     return;
13077 #else
13078   if (!player->active || player->MovPos == 0)
13079     return;
13080 #endif
13081
13082   if (mode == SCROLL_INIT)
13083   {
13084     player->actual_frame_counter = FrameCounter;
13085     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13086
13087     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13088         Feld[last_jx][last_jy] == EL_EMPTY)
13089     {
13090       int last_field_block_delay = 0;   /* start with no blocking at all */
13091       int block_delay_adjustment = player->block_delay_adjustment;
13092
13093       /* if player blocks last field, add delay for exactly one move */
13094       if (player->block_last_field)
13095       {
13096         last_field_block_delay += player->move_delay_value;
13097
13098         /* when blocking enabled, prevent moving up despite gravity */
13099 #if USE_PLAYER_GRAVITY
13100         if (player->gravity && player->MovDir == MV_UP)
13101           block_delay_adjustment = -1;
13102 #else
13103         if (game.gravity && player->MovDir == MV_UP)
13104           block_delay_adjustment = -1;
13105 #endif
13106       }
13107
13108       /* add block delay adjustment (also possible when not blocking) */
13109       last_field_block_delay += block_delay_adjustment;
13110
13111       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13112       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13113     }
13114
13115 #if USE_NEW_PLAYER_SPEED
13116     if (player->MovPos != 0)    /* player has not yet reached destination */
13117       return;
13118 #else
13119     return;
13120 #endif
13121   }
13122   else if (!FrameReached(&player->actual_frame_counter, 1))
13123     return;
13124
13125 #if USE_NEW_PLAYER_SPEED
13126   if (player->MovPos != 0)
13127   {
13128     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13129     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13130
13131     /* before DrawPlayer() to draw correct player graphic for this case */
13132     if (player->MovPos == 0)
13133       CheckGravityMovement(player);
13134   }
13135 #else
13136   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13137   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13138
13139   /* before DrawPlayer() to draw correct player graphic for this case */
13140   if (player->MovPos == 0)
13141     CheckGravityMovement(player);
13142 #endif
13143
13144   if (player->MovPos == 0)      /* player reached destination field */
13145   {
13146     if (player->move_delay_reset_counter > 0)
13147     {
13148       player->move_delay_reset_counter--;
13149
13150       if (player->move_delay_reset_counter == 0)
13151       {
13152         /* continue with normal speed after quickly moving through gate */
13153         HALVE_PLAYER_SPEED(player);
13154
13155         /* be able to make the next move without delay */
13156         player->move_delay = 0;
13157       }
13158     }
13159
13160     player->last_jx = jx;
13161     player->last_jy = jy;
13162
13163     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13164         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13165         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13166         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13167         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13168         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13169     {
13170       DrawPlayer(player);       /* needed here only to cleanup last field */
13171       RemovePlayer(player);
13172
13173       if (local_player->friends_still_needed == 0 ||
13174           IS_SP_ELEMENT(Feld[jx][jy]))
13175         PlayerWins(player);
13176     }
13177
13178     /* this breaks one level: "machine", level 000 */
13179     {
13180       int move_direction = player->MovDir;
13181       int enter_side = MV_DIR_OPPOSITE(move_direction);
13182       int leave_side = move_direction;
13183       int old_jx = last_jx;
13184       int old_jy = last_jy;
13185       int old_element = Feld[old_jx][old_jy];
13186       int new_element = Feld[jx][jy];
13187
13188       if (IS_CUSTOM_ELEMENT(old_element))
13189         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13190                                    CE_LEFT_BY_PLAYER,
13191                                    player->index_bit, leave_side);
13192
13193       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13194                                           CE_PLAYER_LEAVES_X,
13195                                           player->index_bit, leave_side);
13196
13197       if (IS_CUSTOM_ELEMENT(new_element))
13198         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13199                                    player->index_bit, enter_side);
13200
13201       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13202                                           CE_PLAYER_ENTERS_X,
13203                                           player->index_bit, enter_side);
13204
13205       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13206                                         CE_MOVE_OF_X, move_direction);
13207     }
13208
13209     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13210     {
13211       TestIfPlayerTouchesBadThing(jx, jy);
13212       TestIfPlayerTouchesCustomElement(jx, jy);
13213
13214       /* needed because pushed element has not yet reached its destination,
13215          so it would trigger a change event at its previous field location */
13216       if (!player->is_pushing)
13217         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13218
13219       if (!player->active)
13220         RemovePlayer(player);
13221     }
13222
13223     if (!local_player->LevelSolved && level.use_step_counter)
13224     {
13225       int i;
13226
13227       TimePlayed++;
13228
13229       if (TimeLeft > 0)
13230       {
13231         TimeLeft--;
13232
13233         if (TimeLeft <= 10 && setup.time_limit)
13234           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13235
13236 #if 1
13237         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13238
13239         DisplayGameControlValues();
13240 #else
13241         DrawGameValue_Time(TimeLeft);
13242 #endif
13243
13244         if (!TimeLeft && setup.time_limit)
13245           for (i = 0; i < MAX_PLAYERS; i++)
13246             KillPlayer(&stored_player[i]);
13247       }
13248 #if 1
13249       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13250       {
13251         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13252
13253         DisplayGameControlValues();
13254       }
13255 #else
13256       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13257         DrawGameValue_Time(TimePlayed);
13258 #endif
13259     }
13260
13261     if (tape.single_step && tape.recording && !tape.pausing &&
13262         !player->programmed_action)
13263       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13264   }
13265 }
13266
13267 void ScrollScreen(struct PlayerInfo *player, int mode)
13268 {
13269   static unsigned long screen_frame_counter = 0;
13270
13271   if (mode == SCROLL_INIT)
13272   {
13273     /* set scrolling step size according to actual player's moving speed */
13274     ScrollStepSize = TILEX / player->move_delay_value;
13275
13276     screen_frame_counter = FrameCounter;
13277     ScreenMovDir = player->MovDir;
13278     ScreenMovPos = player->MovPos;
13279     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13280     return;
13281   }
13282   else if (!FrameReached(&screen_frame_counter, 1))
13283     return;
13284
13285   if (ScreenMovPos)
13286   {
13287     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13288     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13289     redraw_mask |= REDRAW_FIELD;
13290   }
13291   else
13292     ScreenMovDir = MV_NONE;
13293 }
13294
13295 void TestIfPlayerTouchesCustomElement(int x, int y)
13296 {
13297   static int xy[4][2] =
13298   {
13299     { 0, -1 },
13300     { -1, 0 },
13301     { +1, 0 },
13302     { 0, +1 }
13303   };
13304   static int trigger_sides[4][2] =
13305   {
13306     /* center side       border side */
13307     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13308     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13309     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13310     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13311   };
13312   static int touch_dir[4] =
13313   {
13314     MV_LEFT | MV_RIGHT,
13315     MV_UP   | MV_DOWN,
13316     MV_UP   | MV_DOWN,
13317     MV_LEFT | MV_RIGHT
13318   };
13319   int center_element = Feld[x][y];      /* should always be non-moving! */
13320   int i;
13321
13322   for (i = 0; i < NUM_DIRECTIONS; i++)
13323   {
13324     int xx = x + xy[i][0];
13325     int yy = y + xy[i][1];
13326     int center_side = trigger_sides[i][0];
13327     int border_side = trigger_sides[i][1];
13328     int border_element;
13329
13330     if (!IN_LEV_FIELD(xx, yy))
13331       continue;
13332
13333     if (IS_PLAYER(x, y))
13334     {
13335       struct PlayerInfo *player = PLAYERINFO(x, y);
13336
13337       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13338         border_element = Feld[xx][yy];          /* may be moving! */
13339       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13340         border_element = Feld[xx][yy];
13341       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13342         border_element = MovingOrBlocked2Element(xx, yy);
13343       else
13344         continue;               /* center and border element do not touch */
13345
13346       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13347                                  player->index_bit, border_side);
13348       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13349                                           CE_PLAYER_TOUCHES_X,
13350                                           player->index_bit, border_side);
13351     }
13352     else if (IS_PLAYER(xx, yy))
13353     {
13354       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13355
13356       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13357       {
13358         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13359           continue;             /* center and border element do not touch */
13360       }
13361
13362       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13363                                  player->index_bit, center_side);
13364       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13365                                           CE_PLAYER_TOUCHES_X,
13366                                           player->index_bit, center_side);
13367       break;
13368     }
13369   }
13370 }
13371
13372 #if USE_ELEMENT_TOUCHING_BUGFIX
13373
13374 void TestIfElementTouchesCustomElement(int x, int y)
13375 {
13376   static int xy[4][2] =
13377   {
13378     { 0, -1 },
13379     { -1, 0 },
13380     { +1, 0 },
13381     { 0, +1 }
13382   };
13383   static int trigger_sides[4][2] =
13384   {
13385     /* center side      border side */
13386     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13387     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13388     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13389     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13390   };
13391   static int touch_dir[4] =
13392   {
13393     MV_LEFT | MV_RIGHT,
13394     MV_UP   | MV_DOWN,
13395     MV_UP   | MV_DOWN,
13396     MV_LEFT | MV_RIGHT
13397   };
13398   boolean change_center_element = FALSE;
13399   int center_element = Feld[x][y];      /* should always be non-moving! */
13400   int border_element_old[NUM_DIRECTIONS];
13401   int i;
13402
13403   for (i = 0; i < NUM_DIRECTIONS; i++)
13404   {
13405     int xx = x + xy[i][0];
13406     int yy = y + xy[i][1];
13407     int border_element;
13408
13409     border_element_old[i] = -1;
13410
13411     if (!IN_LEV_FIELD(xx, yy))
13412       continue;
13413
13414     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13415       border_element = Feld[xx][yy];    /* may be moving! */
13416     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13417       border_element = Feld[xx][yy];
13418     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13419       border_element = MovingOrBlocked2Element(xx, yy);
13420     else
13421       continue;                 /* center and border element do not touch */
13422
13423     border_element_old[i] = border_element;
13424   }
13425
13426   for (i = 0; i < NUM_DIRECTIONS; i++)
13427   {
13428     int xx = x + xy[i][0];
13429     int yy = y + xy[i][1];
13430     int center_side = trigger_sides[i][0];
13431     int border_element = border_element_old[i];
13432
13433     if (border_element == -1)
13434       continue;
13435
13436     /* check for change of border element */
13437     CheckElementChangeBySide(xx, yy, border_element, center_element,
13438                              CE_TOUCHING_X, center_side);
13439   }
13440
13441   for (i = 0; i < NUM_DIRECTIONS; i++)
13442   {
13443     int border_side = trigger_sides[i][1];
13444     int border_element = border_element_old[i];
13445
13446     if (border_element == -1)
13447       continue;
13448
13449     /* check for change of center element (but change it only once) */
13450     if (!change_center_element)
13451       change_center_element =
13452         CheckElementChangeBySide(x, y, center_element, border_element,
13453                                  CE_TOUCHING_X, border_side);
13454   }
13455 }
13456
13457 #else
13458
13459 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13460 {
13461   static int xy[4][2] =
13462   {
13463     { 0, -1 },
13464     { -1, 0 },
13465     { +1, 0 },
13466     { 0, +1 }
13467   };
13468   static int trigger_sides[4][2] =
13469   {
13470     /* center side      border side */
13471     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13472     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13473     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13474     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13475   };
13476   static int touch_dir[4] =
13477   {
13478     MV_LEFT | MV_RIGHT,
13479     MV_UP   | MV_DOWN,
13480     MV_UP   | MV_DOWN,
13481     MV_LEFT | MV_RIGHT
13482   };
13483   boolean change_center_element = FALSE;
13484   int center_element = Feld[x][y];      /* should always be non-moving! */
13485   int i;
13486
13487   for (i = 0; i < NUM_DIRECTIONS; i++)
13488   {
13489     int xx = x + xy[i][0];
13490     int yy = y + xy[i][1];
13491     int center_side = trigger_sides[i][0];
13492     int border_side = trigger_sides[i][1];
13493     int border_element;
13494
13495     if (!IN_LEV_FIELD(xx, yy))
13496       continue;
13497
13498     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13499       border_element = Feld[xx][yy];    /* may be moving! */
13500     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13501       border_element = Feld[xx][yy];
13502     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13503       border_element = MovingOrBlocked2Element(xx, yy);
13504     else
13505       continue;                 /* center and border element do not touch */
13506
13507     /* check for change of center element (but change it only once) */
13508     if (!change_center_element)
13509       change_center_element =
13510         CheckElementChangeBySide(x, y, center_element, border_element,
13511                                  CE_TOUCHING_X, border_side);
13512
13513     /* check for change of border element */
13514     CheckElementChangeBySide(xx, yy, border_element, center_element,
13515                              CE_TOUCHING_X, center_side);
13516   }
13517 }
13518
13519 #endif
13520
13521 void TestIfElementHitsCustomElement(int x, int y, int direction)
13522 {
13523   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13524   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13525   int hitx = x + dx, hity = y + dy;
13526   int hitting_element = Feld[x][y];
13527   int touched_element;
13528
13529   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13530     return;
13531
13532   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13533                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13534
13535   if (IN_LEV_FIELD(hitx, hity))
13536   {
13537     int opposite_direction = MV_DIR_OPPOSITE(direction);
13538     int hitting_side = direction;
13539     int touched_side = opposite_direction;
13540     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13541                           MovDir[hitx][hity] != direction ||
13542                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13543
13544     object_hit = TRUE;
13545
13546     if (object_hit)
13547     {
13548       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13549                                CE_HITTING_X, touched_side);
13550
13551       CheckElementChangeBySide(hitx, hity, touched_element,
13552                                hitting_element, CE_HIT_BY_X, hitting_side);
13553
13554       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13555                                CE_HIT_BY_SOMETHING, opposite_direction);
13556     }
13557   }
13558
13559   /* "hitting something" is also true when hitting the playfield border */
13560   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13561                            CE_HITTING_SOMETHING, direction);
13562 }
13563
13564 #if 0
13565 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13566 {
13567   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13568   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13569   int hitx = x + dx, hity = y + dy;
13570   int hitting_element = Feld[x][y];
13571   int touched_element;
13572 #if 0
13573   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13574                         !IS_FREE(hitx, hity) &&
13575                         (!IS_MOVING(hitx, hity) ||
13576                          MovDir[hitx][hity] != direction ||
13577                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
13578 #endif
13579
13580   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13581     return;
13582
13583 #if 0
13584   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13585     return;
13586 #endif
13587
13588   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13589                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13590
13591   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13592                            EP_CAN_SMASH_EVERYTHING, direction);
13593
13594   if (IN_LEV_FIELD(hitx, hity))
13595   {
13596     int opposite_direction = MV_DIR_OPPOSITE(direction);
13597     int hitting_side = direction;
13598     int touched_side = opposite_direction;
13599 #if 0
13600     int touched_element = MovingOrBlocked2Element(hitx, hity);
13601 #endif
13602 #if 1
13603     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13604                           MovDir[hitx][hity] != direction ||
13605                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13606
13607     object_hit = TRUE;
13608 #endif
13609
13610     if (object_hit)
13611     {
13612       int i;
13613
13614       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13615                                CE_SMASHED_BY_SOMETHING, opposite_direction);
13616
13617       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13618                                CE_OTHER_IS_SMASHING, touched_side);
13619
13620       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13621                                CE_OTHER_GETS_SMASHED, hitting_side);
13622     }
13623   }
13624 }
13625 #endif
13626
13627 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13628 {
13629   int i, kill_x = -1, kill_y = -1;
13630
13631   int bad_element = -1;
13632   static int test_xy[4][2] =
13633   {
13634     { 0, -1 },
13635     { -1, 0 },
13636     { +1, 0 },
13637     { 0, +1 }
13638   };
13639   static int test_dir[4] =
13640   {
13641     MV_UP,
13642     MV_LEFT,
13643     MV_RIGHT,
13644     MV_DOWN
13645   };
13646
13647   for (i = 0; i < NUM_DIRECTIONS; i++)
13648   {
13649     int test_x, test_y, test_move_dir, test_element;
13650
13651     test_x = good_x + test_xy[i][0];
13652     test_y = good_y + test_xy[i][1];
13653
13654     if (!IN_LEV_FIELD(test_x, test_y))
13655       continue;
13656
13657     test_move_dir =
13658       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13659
13660     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13661
13662     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13663        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13664     */
13665     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13666         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13667     {
13668       kill_x = test_x;
13669       kill_y = test_y;
13670       bad_element = test_element;
13671
13672       break;
13673     }
13674   }
13675
13676   if (kill_x != -1 || kill_y != -1)
13677   {
13678     if (IS_PLAYER(good_x, good_y))
13679     {
13680       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13681
13682       if (player->shield_deadly_time_left > 0 &&
13683           !IS_INDESTRUCTIBLE(bad_element))
13684         Bang(kill_x, kill_y);
13685       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13686         KillPlayer(player);
13687     }
13688     else
13689       Bang(good_x, good_y);
13690   }
13691 }
13692
13693 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13694 {
13695   int i, kill_x = -1, kill_y = -1;
13696   int bad_element = Feld[bad_x][bad_y];
13697   static int test_xy[4][2] =
13698   {
13699     { 0, -1 },
13700     { -1, 0 },
13701     { +1, 0 },
13702     { 0, +1 }
13703   };
13704   static int touch_dir[4] =
13705   {
13706     MV_LEFT | MV_RIGHT,
13707     MV_UP   | MV_DOWN,
13708     MV_UP   | MV_DOWN,
13709     MV_LEFT | MV_RIGHT
13710   };
13711   static int test_dir[4] =
13712   {
13713     MV_UP,
13714     MV_LEFT,
13715     MV_RIGHT,
13716     MV_DOWN
13717   };
13718
13719   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13720     return;
13721
13722   for (i = 0; i < NUM_DIRECTIONS; i++)
13723   {
13724     int test_x, test_y, test_move_dir, test_element;
13725
13726     test_x = bad_x + test_xy[i][0];
13727     test_y = bad_y + test_xy[i][1];
13728     if (!IN_LEV_FIELD(test_x, test_y))
13729       continue;
13730
13731     test_move_dir =
13732       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13733
13734     test_element = Feld[test_x][test_y];
13735
13736     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13737        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13738     */
13739     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13740         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13741     {
13742       /* good thing is player or penguin that does not move away */
13743       if (IS_PLAYER(test_x, test_y))
13744       {
13745         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13746
13747         if (bad_element == EL_ROBOT && player->is_moving)
13748           continue;     /* robot does not kill player if he is moving */
13749
13750         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13751         {
13752           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13753             continue;           /* center and border element do not touch */
13754         }
13755
13756         kill_x = test_x;
13757         kill_y = test_y;
13758         break;
13759       }
13760       else if (test_element == EL_PENGUIN)
13761       {
13762         kill_x = test_x;
13763         kill_y = test_y;
13764         break;
13765       }
13766     }
13767   }
13768
13769   if (kill_x != -1 || kill_y != -1)
13770   {
13771     if (IS_PLAYER(kill_x, kill_y))
13772     {
13773       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13774
13775       if (player->shield_deadly_time_left > 0 &&
13776           !IS_INDESTRUCTIBLE(bad_element))
13777         Bang(bad_x, bad_y);
13778       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13779         KillPlayer(player);
13780     }
13781     else
13782       Bang(kill_x, kill_y);
13783   }
13784 }
13785
13786 void TestIfPlayerTouchesBadThing(int x, int y)
13787 {
13788   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13789 }
13790
13791 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13792 {
13793   TestIfGoodThingHitsBadThing(x, y, move_dir);
13794 }
13795
13796 void TestIfBadThingTouchesPlayer(int x, int y)
13797 {
13798   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13799 }
13800
13801 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13802 {
13803   TestIfBadThingHitsGoodThing(x, y, move_dir);
13804 }
13805
13806 void TestIfFriendTouchesBadThing(int x, int y)
13807 {
13808   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13809 }
13810
13811 void TestIfBadThingTouchesFriend(int x, int y)
13812 {
13813   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13814 }
13815
13816 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13817 {
13818   int i, kill_x = bad_x, kill_y = bad_y;
13819   static int xy[4][2] =
13820   {
13821     { 0, -1 },
13822     { -1, 0 },
13823     { +1, 0 },
13824     { 0, +1 }
13825   };
13826
13827   for (i = 0; i < NUM_DIRECTIONS; i++)
13828   {
13829     int x, y, element;
13830
13831     x = bad_x + xy[i][0];
13832     y = bad_y + xy[i][1];
13833     if (!IN_LEV_FIELD(x, y))
13834       continue;
13835
13836     element = Feld[x][y];
13837     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13838         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13839     {
13840       kill_x = x;
13841       kill_y = y;
13842       break;
13843     }
13844   }
13845
13846   if (kill_x != bad_x || kill_y != bad_y)
13847     Bang(bad_x, bad_y);
13848 }
13849
13850 void KillPlayer(struct PlayerInfo *player)
13851 {
13852   int jx = player->jx, jy = player->jy;
13853
13854   if (!player->active)
13855     return;
13856
13857   /* the following code was introduced to prevent an infinite loop when calling
13858      -> Bang()
13859      -> CheckTriggeredElementChangeExt()
13860      -> ExecuteCustomElementAction()
13861      -> KillPlayer()
13862      -> (infinitely repeating the above sequence of function calls)
13863      which occurs when killing the player while having a CE with the setting
13864      "kill player X when explosion of <player X>"; the solution using a new
13865      field "player->killed" was chosen for backwards compatibility, although
13866      clever use of the fields "player->active" etc. would probably also work */
13867 #if 1
13868   if (player->killed)
13869     return;
13870 #endif
13871
13872   player->killed = TRUE;
13873
13874   /* remove accessible field at the player's position */
13875   Feld[jx][jy] = EL_EMPTY;
13876
13877   /* deactivate shield (else Bang()/Explode() would not work right) */
13878   player->shield_normal_time_left = 0;
13879   player->shield_deadly_time_left = 0;
13880
13881   Bang(jx, jy);
13882   BuryPlayer(player);
13883 }
13884
13885 static void KillPlayerUnlessEnemyProtected(int x, int y)
13886 {
13887   if (!PLAYER_ENEMY_PROTECTED(x, y))
13888     KillPlayer(PLAYERINFO(x, y));
13889 }
13890
13891 static void KillPlayerUnlessExplosionProtected(int x, int y)
13892 {
13893   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13894     KillPlayer(PLAYERINFO(x, y));
13895 }
13896
13897 void BuryPlayer(struct PlayerInfo *player)
13898 {
13899   int jx = player->jx, jy = player->jy;
13900
13901   if (!player->active)
13902     return;
13903
13904   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13905   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13906
13907   player->GameOver = TRUE;
13908   RemovePlayer(player);
13909 }
13910
13911 void RemovePlayer(struct PlayerInfo *player)
13912 {
13913   int jx = player->jx, jy = player->jy;
13914   int i, found = FALSE;
13915
13916   player->present = FALSE;
13917   player->active = FALSE;
13918
13919   if (!ExplodeField[jx][jy])
13920     StorePlayer[jx][jy] = 0;
13921
13922   if (player->is_moving)
13923     DrawLevelField(player->last_jx, player->last_jy);
13924
13925   for (i = 0; i < MAX_PLAYERS; i++)
13926     if (stored_player[i].active)
13927       found = TRUE;
13928
13929   if (!found)
13930     AllPlayersGone = TRUE;
13931
13932   ExitX = ZX = jx;
13933   ExitY = ZY = jy;
13934 }
13935
13936 #if USE_NEW_SNAP_DELAY
13937 static void setFieldForSnapping(int x, int y, int element, int direction)
13938 {
13939   struct ElementInfo *ei = &element_info[element];
13940   int direction_bit = MV_DIR_TO_BIT(direction);
13941   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13942   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13943                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13944
13945   Feld[x][y] = EL_ELEMENT_SNAPPING;
13946   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13947
13948   ResetGfxAnimation(x, y);
13949
13950   GfxElement[x][y] = element;
13951   GfxAction[x][y] = action;
13952   GfxDir[x][y] = direction;
13953   GfxFrame[x][y] = -1;
13954 }
13955 #endif
13956
13957 /*
13958   =============================================================================
13959   checkDiagonalPushing()
13960   -----------------------------------------------------------------------------
13961   check if diagonal input device direction results in pushing of object
13962   (by checking if the alternative direction is walkable, diggable, ...)
13963   =============================================================================
13964 */
13965
13966 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13967                                     int x, int y, int real_dx, int real_dy)
13968 {
13969   int jx, jy, dx, dy, xx, yy;
13970
13971   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13972     return TRUE;
13973
13974   /* diagonal direction: check alternative direction */
13975   jx = player->jx;
13976   jy = player->jy;
13977   dx = x - jx;
13978   dy = y - jy;
13979   xx = jx + (dx == 0 ? real_dx : 0);
13980   yy = jy + (dy == 0 ? real_dy : 0);
13981
13982   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13983 }
13984
13985 /*
13986   =============================================================================
13987   DigField()
13988   -----------------------------------------------------------------------------
13989   x, y:                 field next to player (non-diagonal) to try to dig to
13990   real_dx, real_dy:     direction as read from input device (can be diagonal)
13991   =============================================================================
13992 */
13993
13994 int DigField(struct PlayerInfo *player,
13995              int oldx, int oldy, int x, int y,
13996              int real_dx, int real_dy, int mode)
13997 {
13998   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13999   boolean player_was_pushing = player->is_pushing;
14000   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14001   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14002   int jx = oldx, jy = oldy;
14003   int dx = x - jx, dy = y - jy;
14004   int nextx = x + dx, nexty = y + dy;
14005   int move_direction = (dx == -1 ? MV_LEFT  :
14006                         dx == +1 ? MV_RIGHT :
14007                         dy == -1 ? MV_UP    :
14008                         dy == +1 ? MV_DOWN  : MV_NONE);
14009   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14010   int dig_side = MV_DIR_OPPOSITE(move_direction);
14011   int old_element = Feld[jx][jy];
14012 #if USE_FIXED_DONT_RUN_INTO
14013   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14014 #else
14015   int element;
14016 #endif
14017   int collect_count;
14018
14019   if (is_player)                /* function can also be called by EL_PENGUIN */
14020   {
14021     if (player->MovPos == 0)
14022     {
14023       player->is_digging = FALSE;
14024       player->is_collecting = FALSE;
14025     }
14026
14027     if (player->MovPos == 0)    /* last pushing move finished */
14028       player->is_pushing = FALSE;
14029
14030     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14031     {
14032       player->is_switching = FALSE;
14033       player->push_delay = -1;
14034
14035       return MP_NO_ACTION;
14036     }
14037   }
14038
14039 #if !USE_FIXED_DONT_RUN_INTO
14040   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14041     return MP_NO_ACTION;
14042 #endif
14043
14044   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14045     old_element = Back[jx][jy];
14046
14047   /* in case of element dropped at player position, check background */
14048   else if (Back[jx][jy] != EL_EMPTY &&
14049            game.engine_version >= VERSION_IDENT(2,2,0,0))
14050     old_element = Back[jx][jy];
14051
14052   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14053     return MP_NO_ACTION;        /* field has no opening in this direction */
14054
14055   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14056     return MP_NO_ACTION;        /* field has no opening in this direction */
14057
14058 #if USE_FIXED_DONT_RUN_INTO
14059   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14060   {
14061     SplashAcid(x, y);
14062
14063     Feld[jx][jy] = player->artwork_element;
14064     InitMovingField(jx, jy, MV_DOWN);
14065     Store[jx][jy] = EL_ACID;
14066     ContinueMoving(jx, jy);
14067     BuryPlayer(player);
14068
14069     return MP_DONT_RUN_INTO;
14070   }
14071 #endif
14072
14073 #if USE_FIXED_DONT_RUN_INTO
14074   if (player_can_move && DONT_RUN_INTO(element))
14075   {
14076     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14077
14078     return MP_DONT_RUN_INTO;
14079   }
14080 #endif
14081
14082 #if USE_FIXED_DONT_RUN_INTO
14083   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14084     return MP_NO_ACTION;
14085 #endif
14086
14087 #if !USE_FIXED_DONT_RUN_INTO
14088   element = Feld[x][y];
14089 #endif
14090
14091   collect_count = element_info[element].collect_count_initial;
14092
14093   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14094     return MP_NO_ACTION;
14095
14096   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14097     player_can_move = player_can_move_or_snap;
14098
14099   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14100       game.engine_version >= VERSION_IDENT(2,2,0,0))
14101   {
14102     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14103                                player->index_bit, dig_side);
14104     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14105                                         player->index_bit, dig_side);
14106
14107     if (element == EL_DC_LANDMINE)
14108       Bang(x, y);
14109
14110     if (Feld[x][y] != element)          /* field changed by snapping */
14111       return MP_ACTION;
14112
14113     return MP_NO_ACTION;
14114   }
14115
14116 #if USE_PLAYER_GRAVITY
14117   if (player->gravity && is_player && !player->is_auto_moving &&
14118       canFallDown(player) && move_direction != MV_DOWN &&
14119       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14120     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14121 #else
14122   if (game.gravity && is_player && !player->is_auto_moving &&
14123       canFallDown(player) && move_direction != MV_DOWN &&
14124       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14125     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14126 #endif
14127
14128   if (player_can_move &&
14129       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14130   {
14131     int sound_element = SND_ELEMENT(element);
14132     int sound_action = ACTION_WALKING;
14133
14134     if (IS_RND_GATE(element))
14135     {
14136       if (!player->key[RND_GATE_NR(element)])
14137         return MP_NO_ACTION;
14138     }
14139     else if (IS_RND_GATE_GRAY(element))
14140     {
14141       if (!player->key[RND_GATE_GRAY_NR(element)])
14142         return MP_NO_ACTION;
14143     }
14144     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14145     {
14146       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14147         return MP_NO_ACTION;
14148     }
14149     else if (element == EL_EXIT_OPEN ||
14150              element == EL_EM_EXIT_OPEN ||
14151              element == EL_STEEL_EXIT_OPEN ||
14152              element == EL_EM_STEEL_EXIT_OPEN ||
14153              element == EL_SP_EXIT_OPEN ||
14154              element == EL_SP_EXIT_OPENING)
14155     {
14156       sound_action = ACTION_PASSING;    /* player is passing exit */
14157     }
14158     else if (element == EL_EMPTY)
14159     {
14160       sound_action = ACTION_MOVING;             /* nothing to walk on */
14161     }
14162
14163     /* play sound from background or player, whatever is available */
14164     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14165       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14166     else
14167       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14168   }
14169   else if (player_can_move &&
14170            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14171   {
14172     if (!ACCESS_FROM(element, opposite_direction))
14173       return MP_NO_ACTION;      /* field not accessible from this direction */
14174
14175     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
14176       return MP_NO_ACTION;
14177
14178     if (IS_EM_GATE(element))
14179     {
14180       if (!player->key[EM_GATE_NR(element)])
14181         return MP_NO_ACTION;
14182     }
14183     else if (IS_EM_GATE_GRAY(element))
14184     {
14185       if (!player->key[EM_GATE_GRAY_NR(element)])
14186         return MP_NO_ACTION;
14187     }
14188     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14189     {
14190       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14191         return MP_NO_ACTION;
14192     }
14193     else if (IS_EMC_GATE(element))
14194     {
14195       if (!player->key[EMC_GATE_NR(element)])
14196         return MP_NO_ACTION;
14197     }
14198     else if (IS_EMC_GATE_GRAY(element))
14199     {
14200       if (!player->key[EMC_GATE_GRAY_NR(element)])
14201         return MP_NO_ACTION;
14202     }
14203     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14204     {
14205       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14206         return MP_NO_ACTION;
14207     }
14208     else if (element == EL_DC_GATE_WHITE ||
14209              element == EL_DC_GATE_WHITE_GRAY ||
14210              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14211     {
14212       if (player->num_white_keys == 0)
14213         return MP_NO_ACTION;
14214
14215       player->num_white_keys--;
14216     }
14217     else if (IS_SP_PORT(element))
14218     {
14219       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14220           element == EL_SP_GRAVITY_PORT_RIGHT ||
14221           element == EL_SP_GRAVITY_PORT_UP ||
14222           element == EL_SP_GRAVITY_PORT_DOWN)
14223 #if USE_PLAYER_GRAVITY
14224         player->gravity = !player->gravity;
14225 #else
14226         game.gravity = !game.gravity;
14227 #endif
14228       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14229                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14230                element == EL_SP_GRAVITY_ON_PORT_UP ||
14231                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14232 #if USE_PLAYER_GRAVITY
14233         player->gravity = TRUE;
14234 #else
14235         game.gravity = TRUE;
14236 #endif
14237       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14238                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14239                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14240                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14241 #if USE_PLAYER_GRAVITY
14242         player->gravity = FALSE;
14243 #else
14244         game.gravity = FALSE;
14245 #endif
14246     }
14247
14248     /* automatically move to the next field with double speed */
14249     player->programmed_action = move_direction;
14250
14251     if (player->move_delay_reset_counter == 0)
14252     {
14253       player->move_delay_reset_counter = 2;     /* two double speed steps */
14254
14255       DOUBLE_PLAYER_SPEED(player);
14256     }
14257
14258     PlayLevelSoundAction(x, y, ACTION_PASSING);
14259   }
14260   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14261   {
14262     RemoveField(x, y);
14263
14264     if (mode != DF_SNAP)
14265     {
14266       GfxElement[x][y] = GFX_ELEMENT(element);
14267       player->is_digging = TRUE;
14268     }
14269
14270     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14271
14272     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14273                                         player->index_bit, dig_side);
14274
14275     if (mode == DF_SNAP)
14276     {
14277 #if USE_NEW_SNAP_DELAY
14278       if (level.block_snap_field)
14279         setFieldForSnapping(x, y, element, move_direction);
14280       else
14281         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14282 #else
14283       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14284 #endif
14285
14286       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14287                                           player->index_bit, dig_side);
14288     }
14289   }
14290   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14291   {
14292     RemoveField(x, y);
14293
14294     if (is_player && mode != DF_SNAP)
14295     {
14296       GfxElement[x][y] = element;
14297       player->is_collecting = TRUE;
14298     }
14299
14300     if (element == EL_SPEED_PILL)
14301     {
14302       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14303     }
14304     else if (element == EL_EXTRA_TIME && level.time > 0)
14305     {
14306       TimeLeft += level.extra_time;
14307
14308 #if 1
14309       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14310
14311       DisplayGameControlValues();
14312 #else
14313       DrawGameValue_Time(TimeLeft);
14314 #endif
14315     }
14316     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14317     {
14318       player->shield_normal_time_left += level.shield_normal_time;
14319       if (element == EL_SHIELD_DEADLY)
14320         player->shield_deadly_time_left += level.shield_deadly_time;
14321     }
14322     else if (element == EL_DYNAMITE ||
14323              element == EL_EM_DYNAMITE ||
14324              element == EL_SP_DISK_RED)
14325     {
14326       if (player->inventory_size < MAX_INVENTORY_SIZE)
14327         player->inventory_element[player->inventory_size++] = element;
14328
14329       DrawGameDoorValues();
14330     }
14331     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14332     {
14333       player->dynabomb_count++;
14334       player->dynabombs_left++;
14335     }
14336     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14337     {
14338       player->dynabomb_size++;
14339     }
14340     else if (element == EL_DYNABOMB_INCREASE_POWER)
14341     {
14342       player->dynabomb_xl = TRUE;
14343     }
14344     else if (IS_KEY(element))
14345     {
14346       player->key[KEY_NR(element)] = TRUE;
14347
14348       DrawGameDoorValues();
14349     }
14350     else if (element == EL_DC_KEY_WHITE)
14351     {
14352       player->num_white_keys++;
14353
14354       /* display white keys? */
14355       /* DrawGameDoorValues(); */
14356     }
14357     else if (IS_ENVELOPE(element))
14358     {
14359       player->show_envelope = element;
14360     }
14361     else if (element == EL_EMC_LENSES)
14362     {
14363       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14364
14365       RedrawAllInvisibleElementsForLenses();
14366     }
14367     else if (element == EL_EMC_MAGNIFIER)
14368     {
14369       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14370
14371       RedrawAllInvisibleElementsForMagnifier();
14372     }
14373     else if (IS_DROPPABLE(element) ||
14374              IS_THROWABLE(element))     /* can be collected and dropped */
14375     {
14376       int i;
14377
14378       if (collect_count == 0)
14379         player->inventory_infinite_element = element;
14380       else
14381         for (i = 0; i < collect_count; i++)
14382           if (player->inventory_size < MAX_INVENTORY_SIZE)
14383             player->inventory_element[player->inventory_size++] = element;
14384
14385       DrawGameDoorValues();
14386     }
14387     else if (collect_count > 0)
14388     {
14389       local_player->gems_still_needed -= collect_count;
14390       if (local_player->gems_still_needed < 0)
14391         local_player->gems_still_needed = 0;
14392
14393 #if 1
14394       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14395
14396       DisplayGameControlValues();
14397 #else
14398       DrawGameValue_Emeralds(local_player->gems_still_needed);
14399 #endif
14400     }
14401
14402     RaiseScoreElement(element);
14403     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14404
14405     if (is_player)
14406       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14407                                           player->index_bit, dig_side);
14408
14409     if (mode == DF_SNAP)
14410     {
14411 #if USE_NEW_SNAP_DELAY
14412       if (level.block_snap_field)
14413         setFieldForSnapping(x, y, element, move_direction);
14414       else
14415         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14416 #else
14417       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14418 #endif
14419
14420       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14421                                           player->index_bit, dig_side);
14422     }
14423   }
14424   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14425   {
14426     if (mode == DF_SNAP && element != EL_BD_ROCK)
14427       return MP_NO_ACTION;
14428
14429     if (CAN_FALL(element) && dy)
14430       return MP_NO_ACTION;
14431
14432     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14433         !(element == EL_SPRING && level.use_spring_bug))
14434       return MP_NO_ACTION;
14435
14436     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14437         ((move_direction & MV_VERTICAL &&
14438           ((element_info[element].move_pattern & MV_LEFT &&
14439             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14440            (element_info[element].move_pattern & MV_RIGHT &&
14441             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14442          (move_direction & MV_HORIZONTAL &&
14443           ((element_info[element].move_pattern & MV_UP &&
14444             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14445            (element_info[element].move_pattern & MV_DOWN &&
14446             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14447       return MP_NO_ACTION;
14448
14449     /* do not push elements already moving away faster than player */
14450     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14451         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14452       return MP_NO_ACTION;
14453
14454     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14455     {
14456       if (player->push_delay_value == -1 || !player_was_pushing)
14457         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14458     }
14459     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14460     {
14461       if (player->push_delay_value == -1)
14462         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14463     }
14464     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14465     {
14466       if (!player->is_pushing)
14467         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14468     }
14469
14470     player->is_pushing = TRUE;
14471     player->is_active = TRUE;
14472
14473     if (!(IN_LEV_FIELD(nextx, nexty) &&
14474           (IS_FREE(nextx, nexty) ||
14475            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
14476             IS_SB_ELEMENT(element)))))
14477       return MP_NO_ACTION;
14478
14479     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14480       return MP_NO_ACTION;
14481
14482     if (player->push_delay == -1)       /* new pushing; restart delay */
14483       player->push_delay = 0;
14484
14485     if (player->push_delay < player->push_delay_value &&
14486         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14487         element != EL_SPRING && element != EL_BALLOON)
14488     {
14489       /* make sure that there is no move delay before next try to push */
14490       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14491         player->move_delay = 0;
14492
14493       return MP_NO_ACTION;
14494     }
14495
14496     if (IS_SB_ELEMENT(element))
14497     {
14498       if (element == EL_SOKOBAN_FIELD_FULL)
14499       {
14500         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14501         local_player->sokobanfields_still_needed++;
14502       }
14503
14504       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14505       {
14506         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14507         local_player->sokobanfields_still_needed--;
14508       }
14509
14510       Feld[x][y] = EL_SOKOBAN_OBJECT;
14511
14512       if (Back[x][y] == Back[nextx][nexty])
14513         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14514       else if (Back[x][y] != 0)
14515         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14516                                     ACTION_EMPTYING);
14517       else
14518         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14519                                     ACTION_FILLING);
14520
14521       if (local_player->sokobanfields_still_needed == 0 &&
14522           game.emulation == EMU_SOKOBAN)
14523       {
14524         PlayerWins(player);
14525
14526         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14527       }
14528     }
14529     else
14530       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14531
14532     InitMovingField(x, y, move_direction);
14533     GfxAction[x][y] = ACTION_PUSHING;
14534
14535     if (mode == DF_SNAP)
14536       ContinueMoving(x, y);
14537     else
14538       MovPos[x][y] = (dx != 0 ? dx : dy);
14539
14540     Pushed[x][y] = TRUE;
14541     Pushed[nextx][nexty] = TRUE;
14542
14543     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14544       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14545     else
14546       player->push_delay_value = -1;    /* get new value later */
14547
14548     /* check for element change _after_ element has been pushed */
14549     if (game.use_change_when_pushing_bug)
14550     {
14551       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14552                                  player->index_bit, dig_side);
14553       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14554                                           player->index_bit, dig_side);
14555     }
14556   }
14557   else if (IS_SWITCHABLE(element))
14558   {
14559     if (PLAYER_SWITCHING(player, x, y))
14560     {
14561       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14562                                           player->index_bit, dig_side);
14563
14564       return MP_ACTION;
14565     }
14566
14567     player->is_switching = TRUE;
14568     player->switch_x = x;
14569     player->switch_y = y;
14570
14571     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14572
14573     if (element == EL_ROBOT_WHEEL)
14574     {
14575       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14576       ZX = x;
14577       ZY = y;
14578
14579       game.robot_wheel_active = TRUE;
14580
14581       DrawLevelField(x, y);
14582     }
14583     else if (element == EL_SP_TERMINAL)
14584     {
14585       int xx, yy;
14586
14587       SCAN_PLAYFIELD(xx, yy)
14588       {
14589         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14590           Bang(xx, yy);
14591         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14592           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14593       }
14594     }
14595     else if (IS_BELT_SWITCH(element))
14596     {
14597       ToggleBeltSwitch(x, y);
14598     }
14599     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14600              element == EL_SWITCHGATE_SWITCH_DOWN ||
14601              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14602              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14603     {
14604       ToggleSwitchgateSwitch(x, y);
14605     }
14606     else if (element == EL_LIGHT_SWITCH ||
14607              element == EL_LIGHT_SWITCH_ACTIVE)
14608     {
14609       ToggleLightSwitch(x, y);
14610     }
14611     else if (element == EL_TIMEGATE_SWITCH ||
14612              element == EL_DC_TIMEGATE_SWITCH)
14613     {
14614       ActivateTimegateSwitch(x, y);
14615     }
14616     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14617              element == EL_BALLOON_SWITCH_RIGHT ||
14618              element == EL_BALLOON_SWITCH_UP    ||
14619              element == EL_BALLOON_SWITCH_DOWN  ||
14620              element == EL_BALLOON_SWITCH_NONE  ||
14621              element == EL_BALLOON_SWITCH_ANY)
14622     {
14623       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14624                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14625                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14626                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14627                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14628                              move_direction);
14629     }
14630     else if (element == EL_LAMP)
14631     {
14632       Feld[x][y] = EL_LAMP_ACTIVE;
14633       local_player->lights_still_needed--;
14634
14635       ResetGfxAnimation(x, y);
14636       DrawLevelField(x, y);
14637     }
14638     else if (element == EL_TIME_ORB_FULL)
14639     {
14640       Feld[x][y] = EL_TIME_ORB_EMPTY;
14641
14642       if (level.time > 0 || level.use_time_orb_bug)
14643       {
14644         TimeLeft += level.time_orb_time;
14645
14646 #if 1
14647         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14648
14649         DisplayGameControlValues();
14650 #else
14651         DrawGameValue_Time(TimeLeft);
14652 #endif
14653       }
14654
14655       ResetGfxAnimation(x, y);
14656       DrawLevelField(x, y);
14657     }
14658     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14659              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14660     {
14661       int xx, yy;
14662
14663       game.ball_state = !game.ball_state;
14664
14665       SCAN_PLAYFIELD(xx, yy)
14666       {
14667         int e = Feld[xx][yy];
14668
14669         if (game.ball_state)
14670         {
14671           if (e == EL_EMC_MAGIC_BALL)
14672             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14673           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14674             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14675         }
14676         else
14677         {
14678           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14679             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14680           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14681             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14682         }
14683       }
14684     }
14685
14686     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14687                                         player->index_bit, dig_side);
14688
14689     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14690                                         player->index_bit, dig_side);
14691
14692     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14693                                         player->index_bit, dig_side);
14694
14695     return MP_ACTION;
14696   }
14697   else
14698   {
14699     if (!PLAYER_SWITCHING(player, x, y))
14700     {
14701       player->is_switching = TRUE;
14702       player->switch_x = x;
14703       player->switch_y = y;
14704
14705       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14706                                  player->index_bit, dig_side);
14707       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14708                                           player->index_bit, dig_side);
14709
14710       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14711                                  player->index_bit, dig_side);
14712       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14713                                           player->index_bit, dig_side);
14714     }
14715
14716     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14717                                player->index_bit, dig_side);
14718     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14719                                         player->index_bit, dig_side);
14720
14721     return MP_NO_ACTION;
14722   }
14723
14724   player->push_delay = -1;
14725
14726   if (is_player)                /* function can also be called by EL_PENGUIN */
14727   {
14728     if (Feld[x][y] != element)          /* really digged/collected something */
14729     {
14730       player->is_collecting = !player->is_digging;
14731       player->is_active = TRUE;
14732     }
14733   }
14734
14735   return MP_MOVING;
14736 }
14737
14738 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14739 {
14740   int jx = player->jx, jy = player->jy;
14741   int x = jx + dx, y = jy + dy;
14742   int snap_direction = (dx == -1 ? MV_LEFT  :
14743                         dx == +1 ? MV_RIGHT :
14744                         dy == -1 ? MV_UP    :
14745                         dy == +1 ? MV_DOWN  : MV_NONE);
14746   boolean can_continue_snapping = (level.continuous_snapping &&
14747                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14748
14749   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14750     return FALSE;
14751
14752   if (!player->active || !IN_LEV_FIELD(x, y))
14753     return FALSE;
14754
14755   if (dx && dy)
14756     return FALSE;
14757
14758   if (!dx && !dy)
14759   {
14760     if (player->MovPos == 0)
14761       player->is_pushing = FALSE;
14762
14763     player->is_snapping = FALSE;
14764
14765     if (player->MovPos == 0)
14766     {
14767       player->is_moving = FALSE;
14768       player->is_digging = FALSE;
14769       player->is_collecting = FALSE;
14770     }
14771
14772     return FALSE;
14773   }
14774
14775 #if USE_NEW_CONTINUOUS_SNAPPING
14776   /* prevent snapping with already pressed snap key when not allowed */
14777   if (player->is_snapping && !can_continue_snapping)
14778     return FALSE;
14779 #else
14780   if (player->is_snapping)
14781     return FALSE;
14782 #endif
14783
14784   player->MovDir = snap_direction;
14785
14786   if (player->MovPos == 0)
14787   {
14788     player->is_moving = FALSE;
14789     player->is_digging = FALSE;
14790     player->is_collecting = FALSE;
14791   }
14792
14793   player->is_dropping = FALSE;
14794   player->is_dropping_pressed = FALSE;
14795   player->drop_pressed_delay = 0;
14796
14797   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14798     return FALSE;
14799
14800   player->is_snapping = TRUE;
14801   player->is_active = TRUE;
14802
14803   if (player->MovPos == 0)
14804   {
14805     player->is_moving = FALSE;
14806     player->is_digging = FALSE;
14807     player->is_collecting = FALSE;
14808   }
14809
14810   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14811     DrawLevelField(player->last_jx, player->last_jy);
14812
14813   DrawLevelField(x, y);
14814
14815   return TRUE;
14816 }
14817
14818 boolean DropElement(struct PlayerInfo *player)
14819 {
14820   int old_element, new_element;
14821   int dropx = player->jx, dropy = player->jy;
14822   int drop_direction = player->MovDir;
14823   int drop_side = drop_direction;
14824 #if 1
14825   int drop_element = get_next_dropped_element(player);
14826 #else
14827   int drop_element = (player->inventory_size > 0 ?
14828                       player->inventory_element[player->inventory_size - 1] :
14829                       player->inventory_infinite_element != EL_UNDEFINED ?
14830                       player->inventory_infinite_element :
14831                       player->dynabombs_left > 0 ?
14832                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14833                       EL_UNDEFINED);
14834 #endif
14835
14836   player->is_dropping_pressed = TRUE;
14837
14838   /* do not drop an element on top of another element; when holding drop key
14839      pressed without moving, dropped element must move away before the next
14840      element can be dropped (this is especially important if the next element
14841      is dynamite, which can be placed on background for historical reasons) */
14842   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14843     return MP_ACTION;
14844
14845   if (IS_THROWABLE(drop_element))
14846   {
14847     dropx += GET_DX_FROM_DIR(drop_direction);
14848     dropy += GET_DY_FROM_DIR(drop_direction);
14849
14850     if (!IN_LEV_FIELD(dropx, dropy))
14851       return FALSE;
14852   }
14853
14854   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14855   new_element = drop_element;           /* default: no change when dropping */
14856
14857   /* check if player is active, not moving and ready to drop */
14858   if (!player->active || player->MovPos || player->drop_delay > 0)
14859     return FALSE;
14860
14861   /* check if player has anything that can be dropped */
14862   if (new_element == EL_UNDEFINED)
14863     return FALSE;
14864
14865   /* check if drop key was pressed long enough for EM style dynamite */
14866   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14867     return FALSE;
14868
14869   /* check if anything can be dropped at the current position */
14870   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14871     return FALSE;
14872
14873   /* collected custom elements can only be dropped on empty fields */
14874   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14875     return FALSE;
14876
14877   if (old_element != EL_EMPTY)
14878     Back[dropx][dropy] = old_element;   /* store old element on this field */
14879
14880   ResetGfxAnimation(dropx, dropy);
14881   ResetRandomAnimationValue(dropx, dropy);
14882
14883   if (player->inventory_size > 0 ||
14884       player->inventory_infinite_element != EL_UNDEFINED)
14885   {
14886     if (player->inventory_size > 0)
14887     {
14888       player->inventory_size--;
14889
14890       DrawGameDoorValues();
14891
14892       if (new_element == EL_DYNAMITE)
14893         new_element = EL_DYNAMITE_ACTIVE;
14894       else if (new_element == EL_EM_DYNAMITE)
14895         new_element = EL_EM_DYNAMITE_ACTIVE;
14896       else if (new_element == EL_SP_DISK_RED)
14897         new_element = EL_SP_DISK_RED_ACTIVE;
14898     }
14899
14900     Feld[dropx][dropy] = new_element;
14901
14902     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14903       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14904                           el2img(Feld[dropx][dropy]), 0);
14905
14906     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14907
14908     /* needed if previous element just changed to "empty" in the last frame */
14909     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14910
14911     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14912                                player->index_bit, drop_side);
14913     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14914                                         CE_PLAYER_DROPS_X,
14915                                         player->index_bit, drop_side);
14916
14917     TestIfElementTouchesCustomElement(dropx, dropy);
14918   }
14919   else          /* player is dropping a dyna bomb */
14920   {
14921     player->dynabombs_left--;
14922
14923     Feld[dropx][dropy] = new_element;
14924
14925     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14926       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14927                           el2img(Feld[dropx][dropy]), 0);
14928
14929     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14930   }
14931
14932   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14933     InitField_WithBug1(dropx, dropy, FALSE);
14934
14935   new_element = Feld[dropx][dropy];     /* element might have changed */
14936
14937   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14938       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14939   {
14940     int move_direction, nextx, nexty;
14941
14942     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14943       MovDir[dropx][dropy] = drop_direction;
14944
14945     move_direction = MovDir[dropx][dropy];
14946     nextx = dropx + GET_DX_FROM_DIR(move_direction);
14947     nexty = dropy + GET_DY_FROM_DIR(move_direction);
14948
14949     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14950
14951 #if USE_FIX_IMPACT_COLLISION
14952     /* do not cause impact style collision by dropping elements that can fall */
14953     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14954 #else
14955     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14956 #endif
14957   }
14958
14959   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14960   player->is_dropping = TRUE;
14961
14962   player->drop_pressed_delay = 0;
14963   player->is_dropping_pressed = FALSE;
14964
14965   player->drop_x = dropx;
14966   player->drop_y = dropy;
14967
14968   return TRUE;
14969 }
14970
14971 /* ------------------------------------------------------------------------- */
14972 /* game sound playing functions                                              */
14973 /* ------------------------------------------------------------------------- */
14974
14975 static int *loop_sound_frame = NULL;
14976 static int *loop_sound_volume = NULL;
14977
14978 void InitPlayLevelSound()
14979 {
14980   int num_sounds = getSoundListSize();
14981
14982   checked_free(loop_sound_frame);
14983   checked_free(loop_sound_volume);
14984
14985   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14986   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14987 }
14988
14989 static void PlayLevelSound(int x, int y, int nr)
14990 {
14991   int sx = SCREENX(x), sy = SCREENY(y);
14992   int volume, stereo_position;
14993   int max_distance = 8;
14994   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14995
14996   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14997       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14998     return;
14999
15000   if (!IN_LEV_FIELD(x, y) ||
15001       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15002       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15003     return;
15004
15005   volume = SOUND_MAX_VOLUME;
15006
15007   if (!IN_SCR_FIELD(sx, sy))
15008   {
15009     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15010     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15011
15012     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15013   }
15014
15015   stereo_position = (SOUND_MAX_LEFT +
15016                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15017                      (SCR_FIELDX + 2 * max_distance));
15018
15019   if (IS_LOOP_SOUND(nr))
15020   {
15021     /* This assures that quieter loop sounds do not overwrite louder ones,
15022        while restarting sound volume comparison with each new game frame. */
15023
15024     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15025       return;
15026
15027     loop_sound_volume[nr] = volume;
15028     loop_sound_frame[nr] = FrameCounter;
15029   }
15030
15031   PlaySoundExt(nr, volume, stereo_position, type);
15032 }
15033
15034 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15035 {
15036   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15037                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15038                  y < LEVELY(BY1) ? LEVELY(BY1) :
15039                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15040                  sound_action);
15041 }
15042
15043 static void PlayLevelSoundAction(int x, int y, int action)
15044 {
15045   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15046 }
15047
15048 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15049 {
15050   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15051
15052   if (sound_effect != SND_UNDEFINED)
15053     PlayLevelSound(x, y, sound_effect);
15054 }
15055
15056 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15057                                               int action)
15058 {
15059   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15060
15061   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15062     PlayLevelSound(x, y, sound_effect);
15063 }
15064
15065 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15066 {
15067   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15068
15069   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15070     PlayLevelSound(x, y, sound_effect);
15071 }
15072
15073 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15074 {
15075   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15076
15077   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15078     StopSound(sound_effect);
15079 }
15080
15081 static void PlayLevelMusic()
15082 {
15083   if (levelset.music[level_nr] != MUS_UNDEFINED)
15084     PlayMusic(levelset.music[level_nr]);        /* from config file */
15085   else
15086     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
15087 }
15088
15089 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15090 {
15091   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15092   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15093   int x = xx - 1 - offset;
15094   int y = yy - 1 - offset;
15095
15096   switch (sample)
15097   {
15098     case SAMPLE_blank:
15099       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15100       break;
15101
15102     case SAMPLE_roll:
15103       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15104       break;
15105
15106     case SAMPLE_stone:
15107       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15108       break;
15109
15110     case SAMPLE_nut:
15111       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15112       break;
15113
15114     case SAMPLE_crack:
15115       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15116       break;
15117
15118     case SAMPLE_bug:
15119       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15120       break;
15121
15122     case SAMPLE_tank:
15123       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15124       break;
15125
15126     case SAMPLE_android_clone:
15127       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15128       break;
15129
15130     case SAMPLE_android_move:
15131       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15132       break;
15133
15134     case SAMPLE_spring:
15135       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15136       break;
15137
15138     case SAMPLE_slurp:
15139       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15140       break;
15141
15142     case SAMPLE_eater:
15143       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15144       break;
15145
15146     case SAMPLE_eater_eat:
15147       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15148       break;
15149
15150     case SAMPLE_alien:
15151       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15152       break;
15153
15154     case SAMPLE_collect:
15155       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15156       break;
15157
15158     case SAMPLE_diamond:
15159       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15160       break;
15161
15162     case SAMPLE_squash:
15163       /* !!! CHECK THIS !!! */
15164 #if 1
15165       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15166 #else
15167       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15168 #endif
15169       break;
15170
15171     case SAMPLE_wonderfall:
15172       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15173       break;
15174
15175     case SAMPLE_drip:
15176       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15177       break;
15178
15179     case SAMPLE_push:
15180       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15181       break;
15182
15183     case SAMPLE_dirt:
15184       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15185       break;
15186
15187     case SAMPLE_acid:
15188       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15189       break;
15190
15191     case SAMPLE_ball:
15192       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15193       break;
15194
15195     case SAMPLE_grow:
15196       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15197       break;
15198
15199     case SAMPLE_wonder:
15200       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15201       break;
15202
15203     case SAMPLE_door:
15204       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15205       break;
15206
15207     case SAMPLE_exit_open:
15208       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15209       break;
15210
15211     case SAMPLE_exit_leave:
15212       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15213       break;
15214
15215     case SAMPLE_dynamite:
15216       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15217       break;
15218
15219     case SAMPLE_tick:
15220       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15221       break;
15222
15223     case SAMPLE_press:
15224       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15225       break;
15226
15227     case SAMPLE_wheel:
15228       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15229       break;
15230
15231     case SAMPLE_boom:
15232       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15233       break;
15234
15235     case SAMPLE_die:
15236       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15237       break;
15238
15239     case SAMPLE_time:
15240       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15241       break;
15242
15243     default:
15244       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15245       break;
15246   }
15247 }
15248
15249 #if 0
15250 void ChangeTime(int value)
15251 {
15252   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15253
15254   *time += value;
15255
15256   /* EMC game engine uses value from time counter of RND game engine */
15257   level.native_em_level->lev->time = *time;
15258
15259   DrawGameValue_Time(*time);
15260 }
15261
15262 void RaiseScore(int value)
15263 {
15264   /* EMC game engine and RND game engine have separate score counters */
15265   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15266                 &level.native_em_level->lev->score : &local_player->score);
15267
15268   *score += value;
15269
15270   DrawGameValue_Score(*score);
15271 }
15272 #endif
15273
15274 void RaiseScore(int value)
15275 {
15276   local_player->score += value;
15277
15278 #if 1
15279   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15280
15281   DisplayGameControlValues();
15282 #else
15283   DrawGameValue_Score(local_player->score);
15284 #endif
15285 }
15286
15287 void RaiseScoreElement(int element)
15288 {
15289   switch (element)
15290   {
15291     case EL_EMERALD:
15292     case EL_BD_DIAMOND:
15293     case EL_EMERALD_YELLOW:
15294     case EL_EMERALD_RED:
15295     case EL_EMERALD_PURPLE:
15296     case EL_SP_INFOTRON:
15297       RaiseScore(level.score[SC_EMERALD]);
15298       break;
15299     case EL_DIAMOND:
15300       RaiseScore(level.score[SC_DIAMOND]);
15301       break;
15302     case EL_CRYSTAL:
15303       RaiseScore(level.score[SC_CRYSTAL]);
15304       break;
15305     case EL_PEARL:
15306       RaiseScore(level.score[SC_PEARL]);
15307       break;
15308     case EL_BUG:
15309     case EL_BD_BUTTERFLY:
15310     case EL_SP_ELECTRON:
15311       RaiseScore(level.score[SC_BUG]);
15312       break;
15313     case EL_SPACESHIP:
15314     case EL_BD_FIREFLY:
15315     case EL_SP_SNIKSNAK:
15316       RaiseScore(level.score[SC_SPACESHIP]);
15317       break;
15318     case EL_YAMYAM:
15319     case EL_DARK_YAMYAM:
15320       RaiseScore(level.score[SC_YAMYAM]);
15321       break;
15322     case EL_ROBOT:
15323       RaiseScore(level.score[SC_ROBOT]);
15324       break;
15325     case EL_PACMAN:
15326       RaiseScore(level.score[SC_PACMAN]);
15327       break;
15328     case EL_NUT:
15329       RaiseScore(level.score[SC_NUT]);
15330       break;
15331     case EL_DYNAMITE:
15332     case EL_EM_DYNAMITE:
15333     case EL_SP_DISK_RED:
15334     case EL_DYNABOMB_INCREASE_NUMBER:
15335     case EL_DYNABOMB_INCREASE_SIZE:
15336     case EL_DYNABOMB_INCREASE_POWER:
15337       RaiseScore(level.score[SC_DYNAMITE]);
15338       break;
15339     case EL_SHIELD_NORMAL:
15340     case EL_SHIELD_DEADLY:
15341       RaiseScore(level.score[SC_SHIELD]);
15342       break;
15343     case EL_EXTRA_TIME:
15344       RaiseScore(level.extra_time_score);
15345       break;
15346     case EL_KEY_1:
15347     case EL_KEY_2:
15348     case EL_KEY_3:
15349     case EL_KEY_4:
15350     case EL_EM_KEY_1:
15351     case EL_EM_KEY_2:
15352     case EL_EM_KEY_3:
15353     case EL_EM_KEY_4:
15354     case EL_EMC_KEY_5:
15355     case EL_EMC_KEY_6:
15356     case EL_EMC_KEY_7:
15357     case EL_EMC_KEY_8:
15358     case EL_DC_KEY_WHITE:
15359       RaiseScore(level.score[SC_KEY]);
15360       break;
15361     default:
15362       RaiseScore(element_info[element].collect_score);
15363       break;
15364   }
15365 }
15366
15367 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15368 {
15369   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15370   {
15371 #if defined(NETWORK_AVALIABLE)
15372     if (options.network)
15373       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15374     else
15375 #endif
15376     {
15377       if (quick_quit)
15378       {
15379 #if 1
15380
15381 #if 1
15382         FadeSkipNextFadeIn();
15383 #else
15384         fading = fading_none;
15385 #endif
15386
15387 #else
15388         OpenDoor(DOOR_CLOSE_1);
15389 #endif
15390
15391         game_status = GAME_MODE_MAIN;
15392
15393 #if 1
15394         DrawAndFadeInMainMenu(REDRAW_FIELD);
15395 #else
15396         DrawMainMenu();
15397 #endif
15398       }
15399       else
15400       {
15401 #if 0
15402         FadeOut(REDRAW_FIELD);
15403 #endif
15404
15405         game_status = GAME_MODE_MAIN;
15406
15407         DrawAndFadeInMainMenu(REDRAW_FIELD);
15408       }
15409     }
15410   }
15411   else          /* continue playing the game */
15412   {
15413     if (tape.playing && tape.deactivate_display)
15414       TapeDeactivateDisplayOff(TRUE);
15415
15416     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15417
15418     if (tape.playing && tape.deactivate_display)
15419       TapeDeactivateDisplayOn();
15420   }
15421 }
15422
15423 void RequestQuitGame(boolean ask_if_really_quit)
15424 {
15425   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15426   boolean skip_request = AllPlayersGone || quick_quit;
15427
15428   RequestQuitGameExt(skip_request, quick_quit,
15429                      "Do you really want to quit the game ?");
15430 }
15431
15432
15433 /* ------------------------------------------------------------------------- */
15434 /* random generator functions                                                */
15435 /* ------------------------------------------------------------------------- */
15436
15437 unsigned int InitEngineRandom_RND(long seed)
15438 {
15439   game.num_random_calls = 0;
15440
15441 #if 0
15442   unsigned int rnd_seed = InitEngineRandom(seed);
15443
15444   printf("::: START RND: %d\n", rnd_seed);
15445
15446   return rnd_seed;
15447 #else
15448
15449   return InitEngineRandom(seed);
15450
15451 #endif
15452
15453 }
15454
15455 unsigned int RND(int max)
15456 {
15457   if (max > 0)
15458   {
15459     game.num_random_calls++;
15460
15461     return GetEngineRandom(max);
15462   }
15463
15464   return 0;
15465 }
15466
15467
15468 /* ------------------------------------------------------------------------- */
15469 /* game engine snapshot handling functions                                   */
15470 /* ------------------------------------------------------------------------- */
15471
15472 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
15473
15474 struct EngineSnapshotInfo
15475 {
15476   /* runtime values for custom element collect score */
15477   int collect_score[NUM_CUSTOM_ELEMENTS];
15478
15479   /* runtime values for group element choice position */
15480   int choice_pos[NUM_GROUP_ELEMENTS];
15481
15482   /* runtime values for belt position animations */
15483   int belt_graphic[4 * NUM_BELT_PARTS];
15484   int belt_anim_mode[4 * NUM_BELT_PARTS];
15485 };
15486
15487 struct EngineSnapshotNodeInfo
15488 {
15489   void *buffer_orig;
15490   void *buffer_copy;
15491   int size;
15492 };
15493
15494 static struct EngineSnapshotInfo engine_snapshot_rnd;
15495 static ListNode *engine_snapshot_list = NULL;
15496 static char *snapshot_level_identifier = NULL;
15497 static int snapshot_level_nr = -1;
15498
15499 void FreeEngineSnapshot()
15500 {
15501   while (engine_snapshot_list != NULL)
15502     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15503                        checked_free);
15504
15505   setString(&snapshot_level_identifier, NULL);
15506   snapshot_level_nr = -1;
15507 }
15508
15509 static void SaveEngineSnapshotValues_RND()
15510 {
15511   static int belt_base_active_element[4] =
15512   {
15513     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15514     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15515     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15516     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15517   };
15518   int i, j;
15519
15520   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15521   {
15522     int element = EL_CUSTOM_START + i;
15523
15524     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15525   }
15526
15527   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15528   {
15529     int element = EL_GROUP_START + i;
15530
15531     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15532   }
15533
15534   for (i = 0; i < 4; i++)
15535   {
15536     for (j = 0; j < NUM_BELT_PARTS; j++)
15537     {
15538       int element = belt_base_active_element[i] + j;
15539       int graphic = el2img(element);
15540       int anim_mode = graphic_info[graphic].anim_mode;
15541
15542       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15543       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15544     }
15545   }
15546 }
15547
15548 static void LoadEngineSnapshotValues_RND()
15549 {
15550   unsigned long num_random_calls = game.num_random_calls;
15551   int i, j;
15552
15553   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15554   {
15555     int element = EL_CUSTOM_START + i;
15556
15557     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15558   }
15559
15560   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15561   {
15562     int element = EL_GROUP_START + i;
15563
15564     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15565   }
15566
15567   for (i = 0; i < 4; i++)
15568   {
15569     for (j = 0; j < NUM_BELT_PARTS; j++)
15570     {
15571       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15572       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15573
15574       graphic_info[graphic].anim_mode = anim_mode;
15575     }
15576   }
15577
15578   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15579   {
15580     InitRND(tape.random_seed);
15581     for (i = 0; i < num_random_calls; i++)
15582       RND(1);
15583   }
15584
15585   if (game.num_random_calls != num_random_calls)
15586   {
15587     Error(ERR_INFO, "number of random calls out of sync");
15588     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15589     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15590     Error(ERR_EXIT, "this should not happen -- please debug");
15591   }
15592 }
15593
15594 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15595 {
15596   struct EngineSnapshotNodeInfo *bi =
15597     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15598
15599   bi->buffer_orig = buffer;
15600   bi->buffer_copy = checked_malloc(size);
15601   bi->size = size;
15602
15603   memcpy(bi->buffer_copy, buffer, size);
15604
15605   addNodeToList(&engine_snapshot_list, NULL, bi);
15606 }
15607
15608 void SaveEngineSnapshot()
15609 {
15610   FreeEngineSnapshot();         /* free previous snapshot, if needed */
15611
15612   if (level_editor_test_game)   /* do not save snapshots from editor */
15613     return;
15614
15615   /* copy some special values to a structure better suited for the snapshot */
15616
15617   SaveEngineSnapshotValues_RND();
15618   SaveEngineSnapshotValues_EM();
15619
15620   /* save values stored in special snapshot structure */
15621
15622   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15623   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15624
15625   /* save further RND engine values */
15626
15627   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15628   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15629   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15630
15631   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15632   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15633   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15634   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15635
15636   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15637   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15638   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15639   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15640   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15641
15642   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15643   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15644   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15645
15646   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15647
15648   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15649
15650   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15651   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15652
15653   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15654   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15655   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15656   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15657   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15658   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15659   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15660   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15661   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15662   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15663   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15664   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15665   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15666   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15667   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15668   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15669   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15670   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15671
15672   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15673   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15674
15675   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15676   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15677   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15678
15679   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15680   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15681
15682   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15683   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15684   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15685   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15686   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15687
15688   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15689   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15690
15691   /* save level identification information */
15692
15693   setString(&snapshot_level_identifier, leveldir_current->identifier);
15694   snapshot_level_nr = level_nr;
15695
15696 #if 0
15697   ListNode *node = engine_snapshot_list;
15698   int num_bytes = 0;
15699
15700   while (node != NULL)
15701   {
15702     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15703
15704     node = node->next;
15705   }
15706
15707   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15708 #endif
15709 }
15710
15711 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15712 {
15713   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15714 }
15715
15716 void LoadEngineSnapshot()
15717 {
15718   ListNode *node = engine_snapshot_list;
15719
15720   if (engine_snapshot_list == NULL)
15721     return;
15722
15723   while (node != NULL)
15724   {
15725     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15726
15727     node = node->next;
15728   }
15729
15730   /* restore special values from snapshot structure */
15731
15732   LoadEngineSnapshotValues_RND();
15733   LoadEngineSnapshotValues_EM();
15734 }
15735
15736 boolean CheckEngineSnapshot()
15737 {
15738   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15739           snapshot_level_nr == level_nr);
15740 }
15741
15742
15743 /* ---------- new game button stuff ---------------------------------------- */
15744
15745 /* graphic position values for game buttons */
15746 #define GAME_BUTTON_XSIZE       30
15747 #define GAME_BUTTON_YSIZE       30
15748 #define GAME_BUTTON_XPOS        5
15749 #define GAME_BUTTON_YPOS        215
15750 #define SOUND_BUTTON_XPOS       5
15751 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15752
15753 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15754 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15755 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15756 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15757 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15758 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15759
15760 static struct
15761 {
15762   int *x, *y;
15763   int gd_x, gd_y;
15764   int gadget_id;
15765   char *infotext;
15766 } gamebutton_info[NUM_GAME_BUTTONS] =
15767 {
15768 #if 1
15769   {
15770     &game.button.stop.x,        &game.button.stop.y,
15771     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15772     GAME_CTRL_ID_STOP,
15773     "stop game"
15774   },
15775   {
15776     &game.button.pause.x,       &game.button.pause.y,
15777     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15778     GAME_CTRL_ID_PAUSE,
15779     "pause game"
15780   },
15781   {
15782     &game.button.play.x,        &game.button.play.y,
15783     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15784     GAME_CTRL_ID_PLAY,
15785     "play game"
15786   },
15787   {
15788     &game.button.sound_music.x, &game.button.sound_music.y,
15789     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15790     SOUND_CTRL_ID_MUSIC,
15791     "background music on/off"
15792   },
15793   {
15794     &game.button.sound_loops.x, &game.button.sound_loops.y,
15795     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15796     SOUND_CTRL_ID_LOOPS,
15797     "sound loops on/off"
15798   },
15799   {
15800     &game.button.sound_simple.x,&game.button.sound_simple.y,
15801     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15802     SOUND_CTRL_ID_SIMPLE,
15803     "normal sounds on/off"
15804   }
15805 #else
15806   {
15807     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15808     GAME_CTRL_ID_STOP,
15809     "stop game"
15810   },
15811   {
15812     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15813     GAME_CTRL_ID_PAUSE,
15814     "pause game"
15815   },
15816   {
15817     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15818     GAME_CTRL_ID_PLAY,
15819     "play game"
15820   },
15821   {
15822     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15823     SOUND_CTRL_ID_MUSIC,
15824     "background music on/off"
15825   },
15826   {
15827     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15828     SOUND_CTRL_ID_LOOPS,
15829     "sound loops on/off"
15830   },
15831   {
15832     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15833     SOUND_CTRL_ID_SIMPLE,
15834     "normal sounds on/off"
15835   }
15836 #endif
15837 };
15838
15839 void CreateGameButtons()
15840 {
15841   int i;
15842
15843   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15844   {
15845     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15846     struct GadgetInfo *gi;
15847     int button_type;
15848     boolean checked;
15849     unsigned long event_mask;
15850     int x, y;
15851     int gd_xoffset, gd_yoffset;
15852     int gd_x1, gd_x2, gd_y1, gd_y2;
15853     int id = i;
15854
15855     x = DX + *gamebutton_info[i].x;
15856     y = DY + *gamebutton_info[i].y;
15857     gd_xoffset = gamebutton_info[i].gd_x;
15858     gd_yoffset = gamebutton_info[i].gd_y;
15859     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15860     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15861
15862     if (id == GAME_CTRL_ID_STOP ||
15863         id == GAME_CTRL_ID_PAUSE ||
15864         id == GAME_CTRL_ID_PLAY)
15865     {
15866       button_type = GD_TYPE_NORMAL_BUTTON;
15867       checked = FALSE;
15868       event_mask = GD_EVENT_RELEASED;
15869       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15870       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15871     }
15872     else
15873     {
15874       button_type = GD_TYPE_CHECK_BUTTON;
15875       checked =
15876         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15877          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15878          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15879       event_mask = GD_EVENT_PRESSED;
15880       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
15881       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15882     }
15883
15884     gi = CreateGadget(GDI_CUSTOM_ID, id,
15885                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15886 #if 1
15887                       GDI_X, x,
15888                       GDI_Y, y,
15889 #else
15890                       GDI_X, DX + gd_xoffset,
15891                       GDI_Y, DY + gd_yoffset,
15892 #endif
15893                       GDI_WIDTH, GAME_BUTTON_XSIZE,
15894                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
15895                       GDI_TYPE, button_type,
15896                       GDI_STATE, GD_BUTTON_UNPRESSED,
15897                       GDI_CHECKED, checked,
15898                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15899                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15900                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15901                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15902                       GDI_DIRECT_DRAW, FALSE,
15903                       GDI_EVENT_MASK, event_mask,
15904                       GDI_CALLBACK_ACTION, HandleGameButtons,
15905                       GDI_END);
15906
15907     if (gi == NULL)
15908       Error(ERR_EXIT, "cannot create gadget");
15909
15910     game_gadget[id] = gi;
15911   }
15912 }
15913
15914 void FreeGameButtons()
15915 {
15916   int i;
15917
15918   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15919     FreeGadget(game_gadget[i]);
15920 }
15921
15922 static void MapGameButtons()
15923 {
15924   int i;
15925
15926   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15927     MapGadget(game_gadget[i]);
15928 }
15929
15930 void UnmapGameButtons()
15931 {
15932   int i;
15933
15934   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15935     UnmapGadget(game_gadget[i]);
15936 }
15937
15938 void RedrawGameButtons()
15939 {
15940   int i;
15941
15942   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15943     RedrawGadget(game_gadget[i]);
15944 }
15945
15946 static void HandleGameButtons(struct GadgetInfo *gi)
15947 {
15948   int id = gi->custom_id;
15949
15950   if (game_status != GAME_MODE_PLAYING)
15951     return;
15952
15953   switch (id)
15954   {
15955     case GAME_CTRL_ID_STOP:
15956       if (tape.playing)
15957         TapeStop();
15958       else
15959         RequestQuitGame(TRUE);
15960       break;
15961
15962     case GAME_CTRL_ID_PAUSE:
15963       if (options.network)
15964       {
15965 #if defined(NETWORK_AVALIABLE)
15966         if (tape.pausing)
15967           SendToServer_ContinuePlaying();
15968         else
15969           SendToServer_PausePlaying();
15970 #endif
15971       }
15972       else
15973         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15974       break;
15975
15976     case GAME_CTRL_ID_PLAY:
15977       if (tape.pausing)
15978       {
15979 #if defined(NETWORK_AVALIABLE)
15980         if (options.network)
15981           SendToServer_ContinuePlaying();
15982         else
15983 #endif
15984         {
15985           tape.pausing = FALSE;
15986           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15987         }
15988       }
15989       break;
15990
15991     case SOUND_CTRL_ID_MUSIC:
15992       if (setup.sound_music)
15993       { 
15994         setup.sound_music = FALSE;
15995         FadeMusic();
15996       }
15997       else if (audio.music_available)
15998       { 
15999         setup.sound = setup.sound_music = TRUE;
16000
16001         SetAudioMode(setup.sound);
16002
16003         PlayLevelMusic();
16004       }
16005       break;
16006
16007     case SOUND_CTRL_ID_LOOPS:
16008       if (setup.sound_loops)
16009         setup.sound_loops = FALSE;
16010       else if (audio.loops_available)
16011       {
16012         setup.sound = setup.sound_loops = TRUE;
16013         SetAudioMode(setup.sound);
16014       }
16015       break;
16016
16017     case SOUND_CTRL_ID_SIMPLE:
16018       if (setup.sound_simple)
16019         setup.sound_simple = FALSE;
16020       else if (audio.sound_available)
16021       {
16022         setup.sound = setup.sound_simple = TRUE;
16023         SetAudioMode(setup.sound);
16024       }
16025       break;
16026
16027     default:
16028       break;
16029   }
16030 }