rnd-20070403-2-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61
62 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
63
64
65 /* for DigField() */
66 #define DF_NO_PUSH              0
67 #define DF_DIG                  1
68 #define DF_SNAP                 2
69
70 /* for MovePlayer() */
71 #define MP_NO_ACTION            0
72 #define MP_MOVING               1
73 #define MP_ACTION               2
74 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
75
76 /* for ScrollPlayer() */
77 #define SCROLL_INIT             0
78 #define SCROLL_GO_ON            1
79
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START          0
82 #define EX_TYPE_NONE            0
83 #define EX_TYPE_NORMAL          (1 << 0)
84 #define EX_TYPE_CENTER          (1 << 1)
85 #define EX_TYPE_BORDER          (1 << 2)
86 #define EX_TYPE_CROSS           (1 << 3)
87 #define EX_TYPE_DYNA            (1 << 4)
88 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
89
90 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
91 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
92 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
93 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
94
95 /* special positions in the game control window (relative to control window) */
96 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
97 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
98 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
99 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
100 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
101 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
102 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
103 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
104 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
105 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
106 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
107 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
108 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
109 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
110 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
111 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
112
113 /* special positions in the game control window (relative to main window) */
114 #define DX_LEVEL1               (DX + XX_LEVEL1)
115 #define DX_LEVEL2               (DX + XX_LEVEL2)
116 #define DX_LEVEL                (DX + XX_LEVEL)
117 #define DY_LEVEL                (DY + YY_LEVEL)
118 #define DX_EMERALDS             (DX + XX_EMERALDS)
119 #define DY_EMERALDS             (DY + YY_EMERALDS)
120 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
121 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
122 #define DX_KEYS                 (DX + XX_KEYS)
123 #define DY_KEYS                 (DY + YY_KEYS)
124 #define DX_SCORE                (DX + XX_SCORE)
125 #define DY_SCORE                (DY + YY_SCORE)
126 #define DX_TIME1                (DX + XX_TIME1)
127 #define DX_TIME2                (DX + XX_TIME2)
128 #define DX_TIME                 (DX + XX_TIME)
129 #define DY_TIME                 (DY + YY_TIME)
130
131 #if 1
132 /* game panel display and control definitions */
133
134 #define GAME_CONTROL_LEVEL_NUMBER               0
135 #define GAME_CONTROL_GEMS                       1
136 #define GAME_CONTROL_INVENTORY_COUNT            2
137 #define GAME_CONTROL_INVENTORY_FIRST_1          3
138 #define GAME_CONTROL_INVENTORY_FIRST_2          4
139 #define GAME_CONTROL_INVENTORY_FIRST_3          5
140 #define GAME_CONTROL_INVENTORY_FIRST_4          6
141 #define GAME_CONTROL_INVENTORY_FIRST_5          7
142 #define GAME_CONTROL_INVENTORY_FIRST_6          8
143 #define GAME_CONTROL_INVENTORY_FIRST_7          9
144 #define GAME_CONTROL_INVENTORY_FIRST_8          10
145 #define GAME_CONTROL_INVENTORY_LAST_1           11
146 #define GAME_CONTROL_INVENTORY_LAST_2           12
147 #define GAME_CONTROL_INVENTORY_LAST_3           13
148 #define GAME_CONTROL_INVENTORY_LAST_4           14
149 #define GAME_CONTROL_INVENTORY_LAST_5           15
150 #define GAME_CONTROL_INVENTORY_LAST_6           16
151 #define GAME_CONTROL_INVENTORY_LAST_7           17
152 #define GAME_CONTROL_INVENTORY_LAST_8           18
153 #define GAME_CONTROL_KEY_1                      19
154 #define GAME_CONTROL_KEY_2                      20
155 #define GAME_CONTROL_KEY_3                      21
156 #define GAME_CONTROL_KEY_4                      22
157 #define GAME_CONTROL_KEY_5                      23
158 #define GAME_CONTROL_KEY_6                      24
159 #define GAME_CONTROL_KEY_7                      25
160 #define GAME_CONTROL_KEY_8                      26
161 #define GAME_CONTROL_KEY_WHITE                  27
162 #define GAME_CONTROL_KEY_WHITE_COUNT            28
163 #define GAME_CONTROL_SCORE                      29
164 #define GAME_CONTROL_TIME                       30
165 #define GAME_CONTROL_TIME_HH                    31
166 #define GAME_CONTROL_TIME_MM                    32
167 #define GAME_CONTROL_TIME_SS                    33
168 #define GAME_CONTROL_SHIELD_NORMAL              34
169 #define GAME_CONTROL_SHIELD_NORMAL_TIME         35
170 #define GAME_CONTROL_SHIELD_DEADLY              36
171 #define GAME_CONTROL_SHIELD_DEADLY_TIME         37
172 #define GAME_CONTROL_EXIT                       38
173 #define GAME_CONTROL_EM_EXIT                    39
174 #define GAME_CONTROL_SP_EXIT                    40
175 #define GAME_CONTROL_STEEL_EXIT                 41
176 #define GAME_CONTROL_EM_STEEL_EXIT              42
177 #define GAME_CONTROL_EMC_MAGIC_BALL             43
178 #define GAME_CONTROL_EMC_MAGIC_BALL_SWITCH      44
179 #define GAME_CONTROL_LIGHT_SWITCH               45
180 #define GAME_CONTROL_LIGHT_SWITCH_TIME          46
181 #define GAME_CONTROL_TIMEGATE_SWITCH            47
182 #define GAME_CONTROL_TIMEGATE_SWITCH_TIME       48
183 #define GAME_CONTROL_SWITCHGATE_SWITCH          49
184 #define GAME_CONTROL_EMC_LENSES                 50
185 #define GAME_CONTROL_EMC_LENSES_TIME            51
186 #define GAME_CONTROL_EMC_MAGNIFIER              52
187 #define GAME_CONTROL_EMC_MAGNIFIER_TIME         53
188 #define GAME_CONTROL_BALLOON_SWITCH             54
189 #define GAME_CONTROL_DYNABOMB_NUMBER            55
190 #define GAME_CONTROL_DYNABOMB_SIZE              56
191 #define GAME_CONTROL_DYNABOMB_POWER             57
192 #define GAME_CONTROL_PENGUINS                   58
193 #define GAME_CONTROL_SOKOBAN_OBJECTS            59
194 #define GAME_CONTROL_SOKOBAN_FIELDS             60
195 #define GAME_CONTROL_ROBOT_WHEEL                61
196 #define GAME_CONTROL_CONVEYOR_BELT_1            62
197 #define GAME_CONTROL_CONVEYOR_BELT_1_SWITCH     63
198 #define GAME_CONTROL_CONVEYOR_BELT_2            64
199 #define GAME_CONTROL_CONVEYOR_BELT_2_SWITCH     65
200 #define GAME_CONTROL_CONVEYOR_BELT_3            66
201 #define GAME_CONTROL_CONVEYOR_BELT_3_SWITCH     67
202 #define GAME_CONTROL_CONVEYOR_BELT_4            68
203 #define GAME_CONTROL_CONVEYOR_BELT_4_SWITCH     69
204 #define GAME_CONTROL_MAGIC_WALL                 70
205 #define GAME_CONTROL_MAGIC_WALL_TIME            71
206 #define GAME_CONTROL_BD_MAGIC_WALL              72
207 #define GAME_CONTROL_DC_MAGIC_WALL              73
208 #define GAME_CONTROL_PLAYER_NAME                74
209 #define GAME_CONTROL_LEVEL_NAME                 75
210 #define GAME_CONTROL_LEVEL_AUTHOR               76
211
212 #define NUM_GAME_CONTROLS                       77
213
214 int game_control_value[NUM_GAME_CONTROLS];
215 int last_game_control_value[NUM_GAME_CONTROLS];
216
217 struct GameControlInfo
218 {
219   int nr;
220
221   struct TextPosInfo *pos;
222   int type;
223 };
224
225 static struct GameControlInfo game_controls[] =
226 {
227   {
228     GAME_CONTROL_LEVEL_NUMBER,
229     &game.panel.level_number,
230     TYPE_INTEGER,
231   },
232   {
233     GAME_CONTROL_GEMS,
234     &game.panel.gems,
235     TYPE_INTEGER,
236   },
237   {
238     GAME_CONTROL_INVENTORY_COUNT,
239     &game.panel.inventory_count,
240     TYPE_INTEGER,
241   },
242   {
243     GAME_CONTROL_INVENTORY_FIRST_1,
244     &game.panel.inventory_first_1,
245     TYPE_ELEMENT,
246   },
247   {
248     GAME_CONTROL_INVENTORY_FIRST_2,
249     &game.panel.inventory_first_2,
250     TYPE_ELEMENT,
251   },
252   {
253     GAME_CONTROL_INVENTORY_FIRST_3,
254     &game.panel.inventory_first_3,
255     TYPE_ELEMENT,
256   },
257   {
258     GAME_CONTROL_INVENTORY_FIRST_4,
259     &game.panel.inventory_first_4,
260     TYPE_ELEMENT,
261   },
262   {
263     GAME_CONTROL_INVENTORY_FIRST_5,
264     &game.panel.inventory_first_5,
265     TYPE_ELEMENT,
266   },
267   {
268     GAME_CONTROL_INVENTORY_FIRST_6,
269     &game.panel.inventory_first_6,
270     TYPE_ELEMENT,
271   },
272   {
273     GAME_CONTROL_INVENTORY_FIRST_7,
274     &game.panel.inventory_first_7,
275     TYPE_ELEMENT,
276   },
277   {
278     GAME_CONTROL_INVENTORY_FIRST_8,
279     &game.panel.inventory_first_8,
280     TYPE_ELEMENT,
281   },
282   {
283     GAME_CONTROL_INVENTORY_LAST_1,
284     &game.panel.inventory_last_1,
285     TYPE_ELEMENT,
286   },
287   {
288     GAME_CONTROL_INVENTORY_LAST_2,
289     &game.panel.inventory_last_2,
290     TYPE_ELEMENT,
291   },
292   {
293     GAME_CONTROL_INVENTORY_LAST_3,
294     &game.panel.inventory_last_3,
295     TYPE_ELEMENT,
296   },
297   {
298     GAME_CONTROL_INVENTORY_LAST_4,
299     &game.panel.inventory_last_4,
300     TYPE_ELEMENT,
301   },
302   {
303     GAME_CONTROL_INVENTORY_LAST_5,
304     &game.panel.inventory_last_5,
305     TYPE_ELEMENT,
306   },
307   {
308     GAME_CONTROL_INVENTORY_LAST_6,
309     &game.panel.inventory_last_6,
310     TYPE_ELEMENT,
311   },
312   {
313     GAME_CONTROL_INVENTORY_LAST_7,
314     &game.panel.inventory_last_7,
315     TYPE_ELEMENT,
316   },
317   {
318     GAME_CONTROL_INVENTORY_LAST_8,
319     &game.panel.inventory_last_8,
320     TYPE_ELEMENT,
321   },
322   {
323     GAME_CONTROL_KEY_1,
324     &game.panel.key[0],
325     TYPE_ELEMENT,
326   },
327   {
328     GAME_CONTROL_KEY_2,
329     &game.panel.key[1],
330     TYPE_ELEMENT,
331   },
332   {
333     GAME_CONTROL_KEY_3,
334     &game.panel.key[2],
335     TYPE_ELEMENT,
336   },
337   {
338     GAME_CONTROL_KEY_4,
339     &game.panel.key[3],
340     TYPE_ELEMENT,
341   },
342   {
343     GAME_CONTROL_KEY_5,
344     &game.panel.key[4],
345     TYPE_ELEMENT,
346   },
347   {
348     GAME_CONTROL_KEY_6,
349     &game.panel.key[5],
350     TYPE_ELEMENT,
351   },
352   {
353     GAME_CONTROL_KEY_7,
354     &game.panel.key[6],
355     TYPE_ELEMENT,
356   },
357   {
358     GAME_CONTROL_KEY_8,
359     &game.panel.key[7],
360     TYPE_ELEMENT,
361   },
362   {
363     GAME_CONTROL_KEY_WHITE,
364     &game.panel.key_white,
365     TYPE_ELEMENT,
366   },
367   {
368     GAME_CONTROL_KEY_WHITE_COUNT,
369     &game.panel.key_white_count,
370     TYPE_INTEGER,
371   },
372   {
373     GAME_CONTROL_SCORE,
374     &game.panel.score,
375     TYPE_INTEGER,
376   },
377   {
378     GAME_CONTROL_TIME,
379     &game.panel.time,
380     TYPE_INTEGER,
381   },
382   {
383     GAME_CONTROL_TIME_HH,
384     &game.panel.time_hh,
385     TYPE_INTEGER,
386   },
387   {
388     GAME_CONTROL_TIME_MM,
389     &game.panel.time_mm,
390     TYPE_INTEGER,
391   },
392   {
393     GAME_CONTROL_TIME_SS,
394     &game.panel.time_ss,
395     TYPE_INTEGER,
396   },
397   {
398     GAME_CONTROL_SHIELD_NORMAL,
399     &game.panel.shield_normal,
400     TYPE_ELEMENT,
401   },
402   {
403     GAME_CONTROL_SHIELD_NORMAL_TIME,
404     &game.panel.shield_normal_time,
405     TYPE_INTEGER,
406   },
407   {
408     GAME_CONTROL_SHIELD_DEADLY,
409     &game.panel.shield_deadly,
410     TYPE_ELEMENT,
411   },
412   {
413     GAME_CONTROL_SHIELD_DEADLY_TIME,
414     &game.panel.shield_deadly_time,
415     TYPE_INTEGER,
416   },
417   {
418     GAME_CONTROL_EXIT,
419     &game.panel.exit,
420     TYPE_ELEMENT,
421   },
422   {
423     GAME_CONTROL_EM_EXIT,
424     &game.panel.em_exit,
425     TYPE_ELEMENT,
426   },
427   {
428     GAME_CONTROL_SP_EXIT,
429     &game.panel.sp_exit,
430     TYPE_ELEMENT,
431   },
432   {
433     GAME_CONTROL_STEEL_EXIT,
434     &game.panel.steel_exit,
435     TYPE_ELEMENT,
436   },
437   {
438     GAME_CONTROL_EM_STEEL_EXIT,
439     &game.panel.em_steel_exit,
440     TYPE_ELEMENT,
441   },
442   {
443     GAME_CONTROL_EMC_MAGIC_BALL,
444     &game.panel.emc_magic_ball,
445     TYPE_ELEMENT,
446   },
447   {
448     GAME_CONTROL_EMC_MAGIC_BALL_SWITCH,
449     &game.panel.emc_magic_ball_switch,
450     TYPE_ELEMENT,
451   },
452   {
453     GAME_CONTROL_LIGHT_SWITCH,
454     &game.panel.light_switch,
455     TYPE_ELEMENT,
456   },
457   {
458     GAME_CONTROL_LIGHT_SWITCH_TIME,
459     &game.panel.light_switch_time,
460     TYPE_INTEGER,
461   },
462   {
463     GAME_CONTROL_TIMEGATE_SWITCH,
464     &game.panel.timegate_switch,
465     TYPE_ELEMENT,
466   },
467   {
468     GAME_CONTROL_TIMEGATE_SWITCH_TIME,
469     &game.panel.timegate_switch_time,
470     TYPE_INTEGER,
471   },
472   {
473     GAME_CONTROL_SWITCHGATE_SWITCH,
474     &game.panel.switchgate_switch,
475     TYPE_ELEMENT,
476   },
477   {
478     GAME_CONTROL_EMC_LENSES,
479     &game.panel.emc_lenses,
480     TYPE_ELEMENT,
481   },
482   {
483     GAME_CONTROL_EMC_LENSES_TIME,
484     &game.panel.emc_lenses_time,
485     TYPE_INTEGER,
486   },
487   {
488     GAME_CONTROL_EMC_MAGNIFIER,
489     &game.panel.emc_magnifier,
490     TYPE_ELEMENT,
491   },
492   {
493     GAME_CONTROL_EMC_MAGNIFIER_TIME,
494     &game.panel.emc_magnifier_time,
495     TYPE_INTEGER,
496   },
497   {
498     GAME_CONTROL_BALLOON_SWITCH,
499     &game.panel.balloon_switch,
500     TYPE_ELEMENT,
501   },
502   {
503     GAME_CONTROL_DYNABOMB_NUMBER,
504     &game.panel.dynabomb_number,
505     TYPE_INTEGER,
506   },
507   {
508     GAME_CONTROL_DYNABOMB_SIZE,
509     &game.panel.dynabomb_size,
510     TYPE_INTEGER,
511   },
512   {
513     GAME_CONTROL_DYNABOMB_POWER,
514     &game.panel.dynabomb_power,
515     TYPE_ELEMENT,
516   },
517   {
518     GAME_CONTROL_PENGUINS,
519     &game.panel.penguins,
520     TYPE_INTEGER,
521   },
522   {
523     GAME_CONTROL_SOKOBAN_OBJECTS,
524     &game.panel.sokoban_objects,
525     TYPE_INTEGER,
526   },
527   {
528     GAME_CONTROL_SOKOBAN_FIELDS,
529     &game.panel.sokoban_fields,
530     TYPE_INTEGER,
531   },
532   {
533     GAME_CONTROL_ROBOT_WHEEL,
534     &game.panel.robot_wheel,
535     TYPE_ELEMENT,
536   },
537   {
538     GAME_CONTROL_CONVEYOR_BELT_1,
539     &game.panel.conveyor_belt_1,
540     TYPE_ELEMENT,
541   },
542   {
543     GAME_CONTROL_CONVEYOR_BELT_1_SWITCH,
544     &game.panel.conveyor_belt_1_switch,
545     TYPE_ELEMENT,
546   },
547   {
548     GAME_CONTROL_CONVEYOR_BELT_2,
549     &game.panel.conveyor_belt_2,
550     TYPE_ELEMENT,
551   },
552   {
553     GAME_CONTROL_CONVEYOR_BELT_2_SWITCH,
554     &game.panel.conveyor_belt_2_switch,
555     TYPE_ELEMENT,
556   },
557   {
558     GAME_CONTROL_CONVEYOR_BELT_3,
559     &game.panel.conveyor_belt_3,
560     TYPE_ELEMENT,
561   },
562   {
563     GAME_CONTROL_CONVEYOR_BELT_3_SWITCH,
564     &game.panel.conveyor_belt_3_switch,
565     TYPE_ELEMENT,
566   },
567   {
568     GAME_CONTROL_CONVEYOR_BELT_4,
569     &game.panel.conveyor_belt_4,
570     TYPE_ELEMENT,
571   },
572   {
573     GAME_CONTROL_CONVEYOR_BELT_4_SWITCH,
574     &game.panel.conveyor_belt_4_switch,
575     TYPE_ELEMENT,
576   },
577   {
578     GAME_CONTROL_MAGIC_WALL,
579     &game.panel.magic_wall,
580     TYPE_ELEMENT,
581   },
582   {
583     GAME_CONTROL_MAGIC_WALL_TIME,
584     &game.panel.magic_wall_time,
585     TYPE_INTEGER,
586   },
587   {
588     GAME_CONTROL_BD_MAGIC_WALL,
589     &game.panel.bd_magic_wall,
590     TYPE_ELEMENT,
591   },
592   {
593     GAME_CONTROL_DC_MAGIC_WALL,
594     &game.panel.dc_magic_wall,
595     TYPE_ELEMENT,
596   },
597   {
598     GAME_CONTROL_PLAYER_NAME,
599     &game.panel.player_name,
600     TYPE_STRING,
601   },
602   {
603     GAME_CONTROL_LEVEL_NAME,
604     &game.panel.level_name,
605     TYPE_STRING,
606   },
607   {
608     GAME_CONTROL_LEVEL_AUTHOR,
609     &game.panel.level_author,
610     TYPE_STRING,
611   },
612
613   {
614     -1,
615     NULL,
616     -1,
617   }
618 };
619 #endif
620
621
622 /* values for delayed check of falling and moving elements and for collision */
623 #define CHECK_DELAY_MOVING      3
624 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
625 #define CHECK_DELAY_COLLISION   2
626 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
627
628 /* values for initial player move delay (initial delay counter value) */
629 #define INITIAL_MOVE_DELAY_OFF  -1
630 #define INITIAL_MOVE_DELAY_ON   0
631
632 /* values for player movement speed (which is in fact a delay value) */
633 #define MOVE_DELAY_MIN_SPEED    32
634 #define MOVE_DELAY_NORMAL_SPEED 8
635 #define MOVE_DELAY_HIGH_SPEED   4
636 #define MOVE_DELAY_MAX_SPEED    1
637
638 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
639 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
640
641 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
642 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
643
644 /* values for other actions */
645 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
646 #define MOVE_STEPSIZE_MIN       (1)
647 #define MOVE_STEPSIZE_MAX       (TILEX)
648
649 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
650 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
651
652 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
653
654 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
655                                  RND(element_info[e].push_delay_random))
656 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
657                                  RND(element_info[e].drop_delay_random))
658 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
659                                  RND(element_info[e].move_delay_random))
660 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
661                                     (element_info[e].move_delay_random))
662 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
663                                  RND(element_info[e].ce_value_random_initial))
664 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
665 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
666                                  RND((c)->delay_random * (c)->delay_frames))
667 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
668                                  RND((c)->delay_random))
669
670
671 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
672          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
673
674 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
675         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
676          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
677          (be) + (e) - EL_SELF)
678
679 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
680         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
681          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
682          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
683          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
684          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
685          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
686          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
687          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
688          (e))
689
690 #define CAN_GROW_INTO(e)                                                \
691         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
692
693 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
694                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
695                                         (condition)))
696
697 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
698                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
699                                         (CAN_MOVE_INTO_ACID(e) &&       \
700                                          Feld[x][y] == EL_ACID) ||      \
701                                         (condition)))
702
703 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
704                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
705                                         (CAN_MOVE_INTO_ACID(e) &&       \
706                                          Feld[x][y] == EL_ACID) ||      \
707                                         (condition)))
708
709 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
710                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
711                                         (condition) ||                  \
712                                         (CAN_MOVE_INTO_ACID(e) &&       \
713                                          Feld[x][y] == EL_ACID) ||      \
714                                         (DONT_COLLIDE_WITH(e) &&        \
715                                          IS_PLAYER(x, y) &&             \
716                                          !PLAYER_ENEMY_PROTECTED(x, y))))
717
718 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
719         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
720
721 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
722         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
723
724 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
725         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
726
727 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
728         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
729                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
730
731 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
732         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
733
734 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
735         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
736
737 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
738         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
739
740 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
741         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
742
743 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
744         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
745
746 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
747         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
748                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
749                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
750                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
751                                                  IS_FOOD_PENGUIN(Feld[x][y])))
752 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
753         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
754
755 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
756         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
757
758 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
759         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
760
761 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
762         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
763                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
764
765 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
766
767 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
768                 (!IS_PLAYER(x, y) &&                                    \
769                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
770
771 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
772         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
773
774 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
775 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
776
777 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
778 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
779 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
780 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
781
782 /* game button identifiers */
783 #define GAME_CTRL_ID_STOP               0
784 #define GAME_CTRL_ID_PAUSE              1
785 #define GAME_CTRL_ID_PLAY               2
786 #define SOUND_CTRL_ID_MUSIC             3
787 #define SOUND_CTRL_ID_LOOPS             4
788 #define SOUND_CTRL_ID_SIMPLE            5
789
790 #define NUM_GAME_BUTTONS                6
791
792
793 /* forward declaration for internal use */
794
795 static void CreateField(int, int, int);
796
797 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
798 static void AdvanceFrameAndPlayerCounters(int);
799
800 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
801 static boolean MovePlayer(struct PlayerInfo *, int, int);
802 static void ScrollPlayer(struct PlayerInfo *, int);
803 static void ScrollScreen(struct PlayerInfo *, int);
804
805 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
806
807 static void InitBeltMovement(void);
808 static void CloseAllOpenTimegates(void);
809 static void CheckGravityMovement(struct PlayerInfo *);
810 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
811 static void KillPlayerUnlessEnemyProtected(int, int);
812 static void KillPlayerUnlessExplosionProtected(int, int);
813
814 static void TestIfPlayerTouchesCustomElement(int, int);
815 static void TestIfElementTouchesCustomElement(int, int);
816 static void TestIfElementHitsCustomElement(int, int, int);
817 #if 0
818 static void TestIfElementSmashesCustomElement(int, int, int);
819 #endif
820
821 static void HandleElementChange(int, int, int);
822 static void ExecuteCustomElementAction(int, int, int, int);
823 static boolean ChangeElement(int, int, int, int);
824
825 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
826 #define CheckTriggeredElementChange(x, y, e, ev)                        \
827         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
828 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
829         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
830 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
831         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
832 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
833         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
834
835 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
836 #define CheckElementChange(x, y, e, te, ev)                             \
837         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
838 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
839         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
840 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
841         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
842
843 static void PlayLevelSound(int, int, int);
844 static void PlayLevelSoundNearest(int, int, int);
845 static void PlayLevelSoundAction(int, int, int);
846 static void PlayLevelSoundElementAction(int, int, int, int);
847 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
848 static void PlayLevelSoundActionIfLoop(int, int, int);
849 static void StopLevelSoundActionIfLoop(int, int, int);
850 static void PlayLevelMusic();
851
852 static void MapGameButtons();
853 static void HandleGameButtons(struct GadgetInfo *);
854
855 int AmoebeNachbarNr(int, int);
856 void AmoebeUmwandeln(int, int);
857 void ContinueMoving(int, int);
858 void Bang(int, int);
859 void InitMovDir(int, int);
860 void InitAmoebaNr(int, int);
861 int NewHiScore(void);
862
863 void TestIfGoodThingHitsBadThing(int, int, int);
864 void TestIfBadThingHitsGoodThing(int, int, int);
865 void TestIfPlayerTouchesBadThing(int, int);
866 void TestIfPlayerRunsIntoBadThing(int, int, int);
867 void TestIfBadThingTouchesPlayer(int, int);
868 void TestIfBadThingRunsIntoPlayer(int, int, int);
869 void TestIfFriendTouchesBadThing(int, int);
870 void TestIfBadThingTouchesFriend(int, int);
871 void TestIfBadThingTouchesOtherBadThing(int, int);
872
873 void KillPlayer(struct PlayerInfo *);
874 void BuryPlayer(struct PlayerInfo *);
875 void RemovePlayer(struct PlayerInfo *);
876
877 boolean SnapField(struct PlayerInfo *, int, int);
878 boolean DropElement(struct PlayerInfo *);
879
880 static int getInvisibleActiveFromInvisibleElement(int);
881 static int getInvisibleFromInvisibleActiveElement(int);
882
883 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
884
885 /* for detection of endless loops, caused by custom element programming */
886 /* (using maximal playfield width x 10 is just a rough approximation) */
887 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
888
889 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
890 {                                                                       \
891   if (recursion_loop_detected)                                          \
892     return (rc);                                                        \
893                                                                         \
894   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
895   {                                                                     \
896     recursion_loop_detected = TRUE;                                     \
897     recursion_loop_element = (e);                                       \
898   }                                                                     \
899                                                                         \
900   recursion_loop_depth++;                                               \
901 }
902
903 #define RECURSION_LOOP_DETECTION_END()                                  \
904 {                                                                       \
905   recursion_loop_depth--;                                               \
906 }
907
908 static int recursion_loop_depth;
909 static boolean recursion_loop_detected;
910 static boolean recursion_loop_element;
911
912
913 /* ------------------------------------------------------------------------- */
914 /* definition of elements that automatically change to other elements after  */
915 /* a specified time, eventually calling a function when changing             */
916 /* ------------------------------------------------------------------------- */
917
918 /* forward declaration for changer functions */
919 static void InitBuggyBase(int, int);
920 static void WarnBuggyBase(int, int);
921
922 static void InitTrap(int, int);
923 static void ActivateTrap(int, int);
924 static void ChangeActiveTrap(int, int);
925
926 static void InitRobotWheel(int, int);
927 static void RunRobotWheel(int, int);
928 static void StopRobotWheel(int, int);
929
930 static void InitTimegateWheel(int, int);
931 static void RunTimegateWheel(int, int);
932
933 static void InitMagicBallDelay(int, int);
934 static void ActivateMagicBall(int, int);
935
936 struct ChangingElementInfo
937 {
938   int element;
939   int target_element;
940   int change_delay;
941   void (*pre_change_function)(int x, int y);
942   void (*change_function)(int x, int y);
943   void (*post_change_function)(int x, int y);
944 };
945
946 static struct ChangingElementInfo change_delay_list[] =
947 {
948   {
949     EL_NUT_BREAKING,
950     EL_EMERALD,
951     6,
952     NULL,
953     NULL,
954     NULL
955   },
956   {
957     EL_PEARL_BREAKING,
958     EL_EMPTY,
959     8,
960     NULL,
961     NULL,
962     NULL
963   },
964   {
965     EL_EXIT_OPENING,
966     EL_EXIT_OPEN,
967     29,
968     NULL,
969     NULL,
970     NULL
971   },
972   {
973     EL_EXIT_CLOSING,
974     EL_EXIT_CLOSED,
975     29,
976     NULL,
977     NULL,
978     NULL
979   },
980   {
981     EL_STEEL_EXIT_OPENING,
982     EL_STEEL_EXIT_OPEN,
983     29,
984     NULL,
985     NULL,
986     NULL
987   },
988   {
989     EL_STEEL_EXIT_CLOSING,
990     EL_STEEL_EXIT_CLOSED,
991     29,
992     NULL,
993     NULL,
994     NULL
995   },
996   {
997     EL_EM_EXIT_OPENING,
998     EL_EM_EXIT_OPEN,
999     29,
1000     NULL,
1001     NULL,
1002     NULL
1003   },
1004   {
1005     EL_EM_EXIT_CLOSING,
1006 #if 1
1007     EL_EMPTY,
1008 #else
1009     EL_EM_EXIT_CLOSED,
1010 #endif
1011     29,
1012     NULL,
1013     NULL,
1014     NULL
1015   },
1016   {
1017     EL_EM_STEEL_EXIT_OPENING,
1018     EL_EM_STEEL_EXIT_OPEN,
1019     29,
1020     NULL,
1021     NULL,
1022     NULL
1023   },
1024   {
1025     EL_EM_STEEL_EXIT_CLOSING,
1026 #if 1
1027     EL_STEELWALL,
1028 #else
1029     EL_EM_STEEL_EXIT_CLOSED,
1030 #endif
1031     29,
1032     NULL,
1033     NULL,
1034     NULL
1035   },
1036   {
1037     EL_SP_EXIT_OPENING,
1038     EL_SP_EXIT_OPEN,
1039     29,
1040     NULL,
1041     NULL,
1042     NULL
1043   },
1044   {
1045     EL_SP_EXIT_CLOSING,
1046     EL_SP_EXIT_CLOSED,
1047     29,
1048     NULL,
1049     NULL,
1050     NULL
1051   },
1052   {
1053     EL_SWITCHGATE_OPENING,
1054     EL_SWITCHGATE_OPEN,
1055     29,
1056     NULL,
1057     NULL,
1058     NULL
1059   },
1060   {
1061     EL_SWITCHGATE_CLOSING,
1062     EL_SWITCHGATE_CLOSED,
1063     29,
1064     NULL,
1065     NULL,
1066     NULL
1067   },
1068   {
1069     EL_TIMEGATE_OPENING,
1070     EL_TIMEGATE_OPEN,
1071     29,
1072     NULL,
1073     NULL,
1074     NULL
1075   },
1076   {
1077     EL_TIMEGATE_CLOSING,
1078     EL_TIMEGATE_CLOSED,
1079     29,
1080     NULL,
1081     NULL,
1082     NULL
1083   },
1084
1085   {
1086     EL_ACID_SPLASH_LEFT,
1087     EL_EMPTY,
1088     8,
1089     NULL,
1090     NULL,
1091     NULL
1092   },
1093   {
1094     EL_ACID_SPLASH_RIGHT,
1095     EL_EMPTY,
1096     8,
1097     NULL,
1098     NULL,
1099     NULL
1100   },
1101   {
1102     EL_SP_BUGGY_BASE,
1103     EL_SP_BUGGY_BASE_ACTIVATING,
1104     0,
1105     InitBuggyBase,
1106     NULL,
1107     NULL
1108   },
1109   {
1110     EL_SP_BUGGY_BASE_ACTIVATING,
1111     EL_SP_BUGGY_BASE_ACTIVE,
1112     0,
1113     InitBuggyBase,
1114     NULL,
1115     NULL
1116   },
1117   {
1118     EL_SP_BUGGY_BASE_ACTIVE,
1119     EL_SP_BUGGY_BASE,
1120     0,
1121     InitBuggyBase,
1122     WarnBuggyBase,
1123     NULL
1124   },
1125   {
1126     EL_TRAP,
1127     EL_TRAP_ACTIVE,
1128     0,
1129     InitTrap,
1130     NULL,
1131     ActivateTrap
1132   },
1133   {
1134     EL_TRAP_ACTIVE,
1135     EL_TRAP,
1136     31,
1137     NULL,
1138     ChangeActiveTrap,
1139     NULL
1140   },
1141   {
1142     EL_ROBOT_WHEEL_ACTIVE,
1143     EL_ROBOT_WHEEL,
1144     0,
1145     InitRobotWheel,
1146     RunRobotWheel,
1147     StopRobotWheel
1148   },
1149   {
1150     EL_TIMEGATE_SWITCH_ACTIVE,
1151     EL_TIMEGATE_SWITCH,
1152     0,
1153     InitTimegateWheel,
1154     RunTimegateWheel,
1155     NULL
1156   },
1157   {
1158     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1159     EL_DC_TIMEGATE_SWITCH,
1160     0,
1161     InitTimegateWheel,
1162     RunTimegateWheel,
1163     NULL
1164   },
1165   {
1166     EL_EMC_MAGIC_BALL_ACTIVE,
1167     EL_EMC_MAGIC_BALL_ACTIVE,
1168     0,
1169     InitMagicBallDelay,
1170     NULL,
1171     ActivateMagicBall
1172   },
1173   {
1174     EL_EMC_SPRING_BUMPER_ACTIVE,
1175     EL_EMC_SPRING_BUMPER,
1176     8,
1177     NULL,
1178     NULL,
1179     NULL
1180   },
1181   {
1182     EL_DIAGONAL_SHRINKING,
1183     EL_UNDEFINED,
1184     0,
1185     NULL,
1186     NULL,
1187     NULL
1188   },
1189   {
1190     EL_DIAGONAL_GROWING,
1191     EL_UNDEFINED,
1192     0,
1193     NULL,
1194     NULL,
1195     NULL,
1196   },
1197
1198   {
1199     EL_UNDEFINED,
1200     EL_UNDEFINED,
1201     -1,
1202     NULL,
1203     NULL,
1204     NULL
1205   }
1206 };
1207
1208 struct
1209 {
1210   int element;
1211   int push_delay_fixed, push_delay_random;
1212 }
1213 push_delay_list[] =
1214 {
1215   { EL_SPRING,                  0, 0 },
1216   { EL_BALLOON,                 0, 0 },
1217
1218   { EL_SOKOBAN_OBJECT,          2, 0 },
1219   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1220   { EL_SATELLITE,               2, 0 },
1221   { EL_SP_DISK_YELLOW,          2, 0 },
1222
1223   { EL_UNDEFINED,               0, 0 },
1224 };
1225
1226 struct
1227 {
1228   int element;
1229   int move_stepsize;
1230 }
1231 move_stepsize_list[] =
1232 {
1233   { EL_AMOEBA_DROP,             2 },
1234   { EL_AMOEBA_DROPPING,         2 },
1235   { EL_QUICKSAND_FILLING,       1 },
1236   { EL_QUICKSAND_EMPTYING,      1 },
1237   { EL_QUICKSAND_FAST_FILLING,  2 },
1238   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1239   { EL_MAGIC_WALL_FILLING,      2 },
1240   { EL_MAGIC_WALL_EMPTYING,     2 },
1241   { EL_BD_MAGIC_WALL_FILLING,   2 },
1242   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1243   { EL_DC_MAGIC_WALL_FILLING,   2 },
1244   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1245
1246   { EL_UNDEFINED,               0 },
1247 };
1248
1249 struct
1250 {
1251   int element;
1252   int count;
1253 }
1254 collect_count_list[] =
1255 {
1256   { EL_EMERALD,                 1 },
1257   { EL_BD_DIAMOND,              1 },
1258   { EL_EMERALD_YELLOW,          1 },
1259   { EL_EMERALD_RED,             1 },
1260   { EL_EMERALD_PURPLE,          1 },
1261   { EL_DIAMOND,                 3 },
1262   { EL_SP_INFOTRON,             1 },
1263   { EL_PEARL,                   5 },
1264   { EL_CRYSTAL,                 8 },
1265
1266   { EL_UNDEFINED,               0 },
1267 };
1268
1269 struct
1270 {
1271   int element;
1272   int direction;
1273 }
1274 access_direction_list[] =
1275 {
1276   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1277   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1278   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1279   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1280   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1281   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1282   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1283   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1284   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1285   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1286   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1287
1288   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1289   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1290   { EL_SP_PORT_UP,                                                   MV_DOWN },
1291   { EL_SP_PORT_DOWN,                                         MV_UP           },
1292   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1293   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1294   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1295   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1296   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1297   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1298   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1299   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1300   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1301   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1302   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1303   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1304   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1305   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1306   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1307
1308   { EL_UNDEFINED,                       MV_NONE                              }
1309 };
1310
1311 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1312
1313 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1314 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1315 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1316                                  IS_JUST_CHANGING(x, y))
1317
1318 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1319
1320 /* static variables for playfield scan mode (scanning forward or backward) */
1321 static int playfield_scan_start_x = 0;
1322 static int playfield_scan_start_y = 0;
1323 static int playfield_scan_delta_x = 1;
1324 static int playfield_scan_delta_y = 1;
1325
1326 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1327                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1328                                      (y) += playfield_scan_delta_y)     \
1329                                 for ((x) = playfield_scan_start_x;      \
1330                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1331                                      (x) += playfield_scan_delta_x)
1332
1333 #ifdef DEBUG
1334 void DEBUG_SetMaximumDynamite()
1335 {
1336   int i;
1337
1338   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1339     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1340       local_player->inventory_element[local_player->inventory_size++] =
1341         EL_DYNAMITE;
1342 }
1343 #endif
1344
1345 static void InitPlayfieldScanModeVars()
1346 {
1347   if (game.use_reverse_scan_direction)
1348   {
1349     playfield_scan_start_x = lev_fieldx - 1;
1350     playfield_scan_start_y = lev_fieldy - 1;
1351
1352     playfield_scan_delta_x = -1;
1353     playfield_scan_delta_y = -1;
1354   }
1355   else
1356   {
1357     playfield_scan_start_x = 0;
1358     playfield_scan_start_y = 0;
1359
1360     playfield_scan_delta_x = 1;
1361     playfield_scan_delta_y = 1;
1362   }
1363 }
1364
1365 static void InitPlayfieldScanMode(int mode)
1366 {
1367   game.use_reverse_scan_direction =
1368     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1369
1370   InitPlayfieldScanModeVars();
1371 }
1372
1373 static int get_move_delay_from_stepsize(int move_stepsize)
1374 {
1375   move_stepsize =
1376     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1377
1378   /* make sure that stepsize value is always a power of 2 */
1379   move_stepsize = (1 << log_2(move_stepsize));
1380
1381   return TILEX / move_stepsize;
1382 }
1383
1384 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1385                                boolean init_game)
1386 {
1387   int player_nr = player->index_nr;
1388   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1389   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1390
1391   /* do no immediately change move delay -- the player might just be moving */
1392   player->move_delay_value_next = move_delay;
1393
1394   /* information if player can move must be set separately */
1395   player->cannot_move = cannot_move;
1396
1397   if (init_game)
1398   {
1399     player->move_delay       = game.initial_move_delay[player_nr];
1400     player->move_delay_value = game.initial_move_delay_value[player_nr];
1401
1402     player->move_delay_value_next = -1;
1403
1404     player->move_delay_reset_counter = 0;
1405   }
1406 }
1407
1408 void GetPlayerConfig()
1409 {
1410   GameFrameDelay = setup.game_frame_delay;
1411
1412   if (!audio.sound_available)
1413     setup.sound_simple = FALSE;
1414
1415   if (!audio.loops_available)
1416     setup.sound_loops = FALSE;
1417
1418   if (!audio.music_available)
1419     setup.sound_music = FALSE;
1420
1421   if (!video.fullscreen_available)
1422     setup.fullscreen = FALSE;
1423
1424   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1425
1426   SetAudioMode(setup.sound);
1427   InitJoysticks();
1428 }
1429
1430 int GetElementFromGroupElement(int element)
1431 {
1432   if (IS_GROUP_ELEMENT(element))
1433   {
1434     struct ElementGroupInfo *group = element_info[element].group;
1435     int last_anim_random_frame = gfx.anim_random_frame;
1436     int element_pos;
1437
1438     if (group->choice_mode == ANIM_RANDOM)
1439       gfx.anim_random_frame = RND(group->num_elements_resolved);
1440
1441     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1442                                     group->choice_mode, 0,
1443                                     group->choice_pos);
1444
1445     if (group->choice_mode == ANIM_RANDOM)
1446       gfx.anim_random_frame = last_anim_random_frame;
1447
1448     group->choice_pos++;
1449
1450     element = group->element_resolved[element_pos];
1451   }
1452
1453   return element;
1454 }
1455
1456 static void InitPlayerField(int x, int y, int element, boolean init_game)
1457 {
1458   if (element == EL_SP_MURPHY)
1459   {
1460     if (init_game)
1461     {
1462       if (stored_player[0].present)
1463       {
1464         Feld[x][y] = EL_SP_MURPHY_CLONE;
1465
1466         return;
1467       }
1468       else
1469       {
1470         stored_player[0].use_murphy = TRUE;
1471
1472         if (!level.use_artwork_element[0])
1473           stored_player[0].artwork_element = EL_SP_MURPHY;
1474       }
1475
1476       Feld[x][y] = EL_PLAYER_1;
1477     }
1478   }
1479
1480   if (init_game)
1481   {
1482     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1483     int jx = player->jx, jy = player->jy;
1484
1485     player->present = TRUE;
1486
1487     player->block_last_field = (element == EL_SP_MURPHY ?
1488                                 level.sp_block_last_field :
1489                                 level.block_last_field);
1490
1491     /* ---------- initialize player's last field block delay --------------- */
1492
1493     /* always start with reliable default value (no adjustment needed) */
1494     player->block_delay_adjustment = 0;
1495
1496     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1497     if (player->block_last_field && element == EL_SP_MURPHY)
1498       player->block_delay_adjustment = 1;
1499
1500     /* special case 2: in game engines before 3.1.1, blocking was different */
1501     if (game.use_block_last_field_bug)
1502       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1503
1504     if (!options.network || player->connected)
1505     {
1506       player->active = TRUE;
1507
1508       /* remove potentially duplicate players */
1509       if (StorePlayer[jx][jy] == Feld[x][y])
1510         StorePlayer[jx][jy] = 0;
1511
1512       StorePlayer[x][y] = Feld[x][y];
1513
1514       if (options.debug)
1515       {
1516         printf("Player %d activated.\n", player->element_nr);
1517         printf("[Local player is %d and currently %s.]\n",
1518                local_player->element_nr,
1519                local_player->active ? "active" : "not active");
1520       }
1521     }
1522
1523     Feld[x][y] = EL_EMPTY;
1524
1525     player->jx = player->last_jx = x;
1526     player->jy = player->last_jy = y;
1527   }
1528 }
1529
1530 static void InitField(int x, int y, boolean init_game)
1531 {
1532   int element = Feld[x][y];
1533
1534   switch (element)
1535   {
1536     case EL_SP_MURPHY:
1537     case EL_PLAYER_1:
1538     case EL_PLAYER_2:
1539     case EL_PLAYER_3:
1540     case EL_PLAYER_4:
1541       InitPlayerField(x, y, element, init_game);
1542       break;
1543
1544     case EL_SOKOBAN_FIELD_PLAYER:
1545       element = Feld[x][y] = EL_PLAYER_1;
1546       InitField(x, y, init_game);
1547
1548       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1549       InitField(x, y, init_game);
1550       break;
1551
1552     case EL_SOKOBAN_FIELD_EMPTY:
1553       local_player->sokobanfields_still_needed++;
1554       break;
1555
1556     case EL_STONEBLOCK:
1557       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1558         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1559       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1560         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1561       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1562         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1563       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1564         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1565       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1566         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1567       break;
1568
1569     case EL_BUG:
1570     case EL_BUG_RIGHT:
1571     case EL_BUG_UP:
1572     case EL_BUG_LEFT:
1573     case EL_BUG_DOWN:
1574     case EL_SPACESHIP:
1575     case EL_SPACESHIP_RIGHT:
1576     case EL_SPACESHIP_UP:
1577     case EL_SPACESHIP_LEFT:
1578     case EL_SPACESHIP_DOWN:
1579     case EL_BD_BUTTERFLY:
1580     case EL_BD_BUTTERFLY_RIGHT:
1581     case EL_BD_BUTTERFLY_UP:
1582     case EL_BD_BUTTERFLY_LEFT:
1583     case EL_BD_BUTTERFLY_DOWN:
1584     case EL_BD_FIREFLY:
1585     case EL_BD_FIREFLY_RIGHT:
1586     case EL_BD_FIREFLY_UP:
1587     case EL_BD_FIREFLY_LEFT:
1588     case EL_BD_FIREFLY_DOWN:
1589     case EL_PACMAN_RIGHT:
1590     case EL_PACMAN_UP:
1591     case EL_PACMAN_LEFT:
1592     case EL_PACMAN_DOWN:
1593     case EL_YAMYAM:
1594     case EL_YAMYAM_LEFT:
1595     case EL_YAMYAM_RIGHT:
1596     case EL_YAMYAM_UP:
1597     case EL_YAMYAM_DOWN:
1598     case EL_DARK_YAMYAM:
1599     case EL_ROBOT:
1600     case EL_PACMAN:
1601     case EL_SP_SNIKSNAK:
1602     case EL_SP_ELECTRON:
1603     case EL_MOLE:
1604     case EL_MOLE_LEFT:
1605     case EL_MOLE_RIGHT:
1606     case EL_MOLE_UP:
1607     case EL_MOLE_DOWN:
1608       InitMovDir(x, y);
1609       break;
1610
1611     case EL_AMOEBA_FULL:
1612     case EL_BD_AMOEBA:
1613       InitAmoebaNr(x, y);
1614       break;
1615
1616     case EL_AMOEBA_DROP:
1617       if (y == lev_fieldy - 1)
1618       {
1619         Feld[x][y] = EL_AMOEBA_GROWING;
1620         Store[x][y] = EL_AMOEBA_WET;
1621       }
1622       break;
1623
1624     case EL_DYNAMITE_ACTIVE:
1625     case EL_SP_DISK_RED_ACTIVE:
1626     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1627     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1628     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1629     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1630       MovDelay[x][y] = 96;
1631       break;
1632
1633     case EL_EM_DYNAMITE_ACTIVE:
1634       MovDelay[x][y] = 32;
1635       break;
1636
1637     case EL_LAMP:
1638       local_player->lights_still_needed++;
1639       break;
1640
1641     case EL_PENGUIN:
1642       local_player->friends_still_needed++;
1643       break;
1644
1645     case EL_PIG:
1646     case EL_DRAGON:
1647       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1648       break;
1649
1650     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1651     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1652     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1653     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1654     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1655     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1656     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1657     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1658     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1659     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1660     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1661     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1662       if (init_game)
1663       {
1664         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1665         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1666         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1667
1668         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1669         {
1670           game.belt_dir[belt_nr] = belt_dir;
1671           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1672         }
1673         else    /* more than one switch -- set it like the first switch */
1674         {
1675           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1676         }
1677       }
1678       break;
1679
1680 #if !USE_BOTH_SWITCHGATE_SWITCHES
1681     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1682       if (init_game)
1683         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1684       break;
1685
1686     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1687       if (init_game)
1688         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1689       break;
1690 #endif
1691
1692     case EL_LIGHT_SWITCH_ACTIVE:
1693       if (init_game)
1694         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1695       break;
1696
1697     case EL_INVISIBLE_STEELWALL:
1698     case EL_INVISIBLE_WALL:
1699     case EL_INVISIBLE_SAND:
1700       if (game.light_time_left > 0 ||
1701           game.lenses_time_left > 0)
1702         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1703       break;
1704
1705     case EL_EMC_MAGIC_BALL:
1706       if (game.ball_state)
1707         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1708       break;
1709
1710     case EL_EMC_MAGIC_BALL_SWITCH:
1711       if (game.ball_state)
1712         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1713       break;
1714
1715     default:
1716       if (IS_CUSTOM_ELEMENT(element))
1717       {
1718         if (CAN_MOVE(element))
1719           InitMovDir(x, y);
1720
1721 #if USE_NEW_CUSTOM_VALUE
1722         if (!element_info[element].use_last_ce_value || init_game)
1723           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1724 #endif
1725       }
1726       else if (IS_GROUP_ELEMENT(element))
1727       {
1728         Feld[x][y] = GetElementFromGroupElement(element);
1729
1730         InitField(x, y, init_game);
1731       }
1732
1733       break;
1734   }
1735
1736   if (!init_game)
1737     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1738 }
1739
1740 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1741 {
1742   InitField(x, y, init_game);
1743
1744   /* not needed to call InitMovDir() -- already done by InitField()! */
1745   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1746       CAN_MOVE(Feld[x][y]))
1747     InitMovDir(x, y);
1748 }
1749
1750 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1751 {
1752   int old_element = Feld[x][y];
1753
1754   InitField(x, y, init_game);
1755
1756   /* not needed to call InitMovDir() -- already done by InitField()! */
1757   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1758       CAN_MOVE(old_element) &&
1759       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1760     InitMovDir(x, y);
1761
1762   /* this case is in fact a combination of not less than three bugs:
1763      first, it calls InitMovDir() for elements that can move, although this is
1764      already done by InitField(); then, it checks the element that was at this
1765      field _before_ the call to InitField() (which can change it); lastly, it
1766      was not called for "mole with direction" elements, which were treated as
1767      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1768   */
1769 }
1770
1771 #if 1
1772
1773 static int get_key_element_from_nr(int key_nr)
1774 {
1775   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1776                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1777                           EL_EM_KEY_1 : EL_KEY_1);
1778
1779   return key_base_element + key_nr;
1780 }
1781
1782 static int get_next_drop_element(struct PlayerInfo *player)
1783 {
1784   return (player->inventory_size > 0 ?
1785           player->inventory_element[player->inventory_size - 1] :
1786           player->inventory_infinite_element != EL_UNDEFINED ?
1787           player->inventory_infinite_element :
1788           player->dynabombs_left > 0 ?
1789           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1790           EL_UNDEFINED);
1791 }
1792
1793 static int get_drop_element_from_pos(struct PlayerInfo *player, int pos)
1794 {
1795   /* pos >= 0: get element from bottom of the stack;
1796      pos <  0: get element from top of the stack */
1797
1798   if (pos < 0)
1799   {
1800     int min_inventory_size = -pos;
1801     int inventory_pos = player->inventory_size - min_inventory_size;
1802     int min_dynabombs_left = min_inventory_size - player->inventory_size;
1803
1804     return (player->inventory_size >= min_inventory_size ?
1805             player->inventory_element[inventory_pos] :
1806             player->inventory_infinite_element != EL_UNDEFINED ?
1807             player->inventory_infinite_element :
1808             player->dynabombs_left >= min_dynabombs_left ?
1809             EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1810             EL_UNDEFINED);
1811   }
1812   else
1813   {
1814     int min_dynabombs_left = pos + 1;
1815     int min_inventory_size = pos + 1 - player->dynabombs_left;
1816     int inventory_pos = pos - player->dynabombs_left;
1817
1818     return (player->inventory_infinite_element != EL_UNDEFINED ?
1819             player->inventory_infinite_element :
1820             player->dynabombs_left >= min_dynabombs_left ?
1821             EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1822             player->inventory_size >= min_inventory_size ?
1823             player->inventory_element[inventory_pos] :
1824             EL_UNDEFINED);
1825   }
1826 }
1827
1828 void InitGameControlValues()
1829 {
1830   int i;
1831
1832   for (i = 0; game_controls[i].nr != -1; i++)
1833   {
1834     int nr = game_controls[i].nr;
1835     int type = game_controls[i].type;
1836     struct TextPosInfo *pos = game_controls[i].pos;
1837
1838     /* force update of game controls after initialization */
1839     game_control_value[nr] = last_game_control_value[nr] = -1;
1840
1841     /* determine panel value width for later calculation of alignment */
1842     if (type == TYPE_INTEGER || type == TYPE_STRING)
1843     {
1844       pos->width = pos->size * getFontWidth(pos->font);
1845       pos->height = getFontHeight(pos->font);
1846     }
1847     else if (type == TYPE_ELEMENT)
1848     {
1849       pos->width = pos->size;
1850       pos->height = pos->size;
1851     }
1852   }
1853 }
1854
1855 void UpdateGameControlValues()
1856 {
1857   int i, k;
1858   int time = (level.time == 0 ? TimePlayed : TimeLeft);
1859   int score = (local_player->LevelSolved ? local_player->score_final :
1860                local_player->score);
1861
1862   game_control_value[GAME_CONTROL_LEVEL_NUMBER] = level_nr;
1863   game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
1864
1865   game_control_value[GAME_CONTROL_INVENTORY_COUNT] = 0;
1866   for (i = 0; i < MAX_NUM_KEYS; i++)
1867     game_control_value[GAME_CONTROL_KEY_1 + i] = EL_EMPTY;
1868   game_control_value[GAME_CONTROL_KEY_WHITE] = EL_EMPTY;
1869   game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] = 0;
1870
1871   if (game.centered_player_nr == -1)
1872   {
1873     for (i = 0; i < MAX_PLAYERS; i++)
1874     {
1875       for (k = 0; k < MAX_NUM_KEYS; k++)
1876         if (stored_player[i].key[k])
1877           game_control_value[GAME_CONTROL_KEY_1 + k] =
1878             get_key_element_from_nr(k);
1879
1880       game_control_value[GAME_CONTROL_INVENTORY_COUNT] +=
1881         stored_player[i].inventory_size;
1882
1883       if (stored_player[i].num_white_keys > 0)
1884         game_control_value[GAME_CONTROL_KEY_WHITE] = EL_DC_KEY_WHITE;
1885
1886       game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
1887         stored_player[i].num_white_keys;
1888     }
1889   }
1890   else
1891   {
1892     int player_nr = game.centered_player_nr;
1893
1894     for (k = 0; k < MAX_NUM_KEYS; k++)
1895       if (stored_player[player_nr].key[k])
1896         game_control_value[GAME_CONTROL_KEY_1 + k] =
1897           get_key_element_from_nr(k);
1898
1899     game_control_value[GAME_CONTROL_INVENTORY_COUNT] +=
1900       stored_player[player_nr].inventory_size;
1901
1902     if (stored_player[player_nr].num_white_keys > 0)
1903       game_control_value[GAME_CONTROL_KEY_WHITE] = EL_DC_KEY_WHITE;
1904
1905     game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
1906       stored_player[player_nr].num_white_keys;
1907   }
1908
1909   for (i = 0; i < 8; i++)
1910   {
1911     game_control_value[GAME_CONTROL_INVENTORY_FIRST_1 + i] =
1912       get_drop_element_from_pos(local_player, i);
1913     game_control_value[GAME_CONTROL_INVENTORY_LAST_1 + i] =
1914       get_drop_element_from_pos(local_player, -i - 1);
1915   }
1916
1917   game_control_value[GAME_CONTROL_SCORE] = score;
1918
1919   game_control_value[GAME_CONTROL_TIME] = time;
1920
1921   game_control_value[GAME_CONTROL_TIME_HH] = time / 3600;
1922   game_control_value[GAME_CONTROL_TIME_MM] = (time / 60) % 60;
1923   game_control_value[GAME_CONTROL_TIME_SS] = time % 60;
1924
1925   game_control_value[GAME_CONTROL_SHIELD_NORMAL] =
1926     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
1927      EL_EMPTY);
1928   game_control_value[GAME_CONTROL_SHIELD_NORMAL_TIME] =
1929     local_player->shield_normal_time_left;
1930   game_control_value[GAME_CONTROL_SHIELD_DEADLY] =
1931     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
1932      EL_EMPTY);
1933   game_control_value[GAME_CONTROL_SHIELD_DEADLY_TIME] =
1934     local_player->shield_deadly_time_left;
1935
1936   if (local_player->gems_still_needed > 0 ||
1937       local_player->sokobanfields_still_needed > 0 ||
1938       local_player->lights_still_needed > 0)
1939   {
1940     game_control_value[GAME_CONTROL_EXIT]          = EL_EXIT_CLOSED;
1941     game_control_value[GAME_CONTROL_EM_EXIT]       = EL_EM_EXIT_CLOSED;
1942     game_control_value[GAME_CONTROL_SP_EXIT]       = EL_SP_EXIT_CLOSED;
1943     game_control_value[GAME_CONTROL_STEEL_EXIT]    = EL_STEEL_EXIT_CLOSED;
1944     game_control_value[GAME_CONTROL_EM_STEEL_EXIT] = EL_EM_STEEL_EXIT_CLOSED;
1945   }
1946   else
1947   {
1948     game_control_value[GAME_CONTROL_EXIT]          = EL_EXIT_OPEN;
1949     game_control_value[GAME_CONTROL_EM_EXIT]       = EL_EM_EXIT_OPEN;
1950     game_control_value[GAME_CONTROL_SP_EXIT]       = EL_SP_EXIT_OPEN;
1951     game_control_value[GAME_CONTROL_STEEL_EXIT]    = EL_STEEL_EXIT_OPEN;
1952     game_control_value[GAME_CONTROL_EM_STEEL_EXIT] = EL_EM_STEEL_EXIT_OPEN;
1953   }
1954
1955   /* !!! TODO !!! */
1956   game_control_value[GAME_CONTROL_EMC_MAGIC_BALL] = EL_UNDEFINED;
1957   game_control_value[GAME_CONTROL_EMC_MAGIC_BALL_SWITCH] = EL_UNDEFINED;
1958
1959   game_control_value[GAME_CONTROL_LIGHT_SWITCH] =
1960     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
1961   game_control_value[GAME_CONTROL_LIGHT_SWITCH_TIME] = game.light_time_left;
1962
1963   game_control_value[GAME_CONTROL_TIMEGATE_SWITCH] =
1964     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
1965   game_control_value[GAME_CONTROL_TIMEGATE_SWITCH_TIME] =
1966     game.timegate_time_left;
1967
1968   /* !!! TODO !!! */
1969   game_control_value[GAME_CONTROL_SWITCHGATE_SWITCH] = EL_UNDEFINED;
1970
1971   game_control_value[GAME_CONTROL_EMC_LENSES] =
1972     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
1973   game_control_value[GAME_CONTROL_EMC_LENSES_TIME] = game.lenses_time_left;
1974
1975   game_control_value[GAME_CONTROL_EMC_MAGNIFIER] =
1976     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
1977   game_control_value[GAME_CONTROL_EMC_MAGNIFIER_TIME] = game.magnify_time_left;
1978
1979   game_control_value[GAME_CONTROL_BALLOON_SWITCH] =
1980     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
1981      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
1982      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
1983      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
1984      EL_BALLOON_SWITCH_NONE);
1985
1986   game_control_value[GAME_CONTROL_DYNABOMB_NUMBER] =
1987     local_player->dynabomb_count;
1988   game_control_value[GAME_CONTROL_DYNABOMB_SIZE] =
1989     local_player->dynabomb_size;
1990   game_control_value[GAME_CONTROL_DYNABOMB_POWER] =
1991     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
1992
1993   game_control_value[GAME_CONTROL_PENGUINS] =
1994     local_player->friends_still_needed;
1995
1996   game_control_value[GAME_CONTROL_SOKOBAN_OBJECTS] =
1997     local_player->sokobanfields_still_needed;
1998   game_control_value[GAME_CONTROL_SOKOBAN_FIELDS] =
1999     local_player->sokobanfields_still_needed;
2000
2001   /* !!! TODO !!! */
2002   game_control_value[GAME_CONTROL_ROBOT_WHEEL] = EL_UNDEFINED;
2003
2004   /* !!! TODO !!! */
2005   game_control_value[GAME_CONTROL_CONVEYOR_BELT_1] = EL_UNDEFINED;
2006   game_control_value[GAME_CONTROL_CONVEYOR_BELT_1_SWITCH] = EL_UNDEFINED;
2007   game_control_value[GAME_CONTROL_CONVEYOR_BELT_2] = EL_UNDEFINED;
2008   game_control_value[GAME_CONTROL_CONVEYOR_BELT_2_SWITCH] = EL_UNDEFINED;
2009   game_control_value[GAME_CONTROL_CONVEYOR_BELT_3] = EL_UNDEFINED;
2010   game_control_value[GAME_CONTROL_CONVEYOR_BELT_3_SWITCH] = EL_UNDEFINED;
2011   game_control_value[GAME_CONTROL_CONVEYOR_BELT_4] = EL_UNDEFINED;
2012   game_control_value[GAME_CONTROL_CONVEYOR_BELT_4_SWITCH] = EL_UNDEFINED;
2013
2014   /* !!! TODO !!! */
2015   game_control_value[GAME_CONTROL_MAGIC_WALL] = EL_UNDEFINED;
2016   game_control_value[GAME_CONTROL_MAGIC_WALL_TIME] =
2017     game.magic_wall_time_left;
2018   game_control_value[GAME_CONTROL_BD_MAGIC_WALL] = EL_UNDEFINED;
2019   game_control_value[GAME_CONTROL_DC_MAGIC_WALL] = EL_UNDEFINED;
2020
2021   game_control_value[GAME_CONTROL_PLAYER_NAME] = 0;
2022   game_control_value[GAME_CONTROL_LEVEL_NAME] = 0;
2023   game_control_value[GAME_CONTROL_LEVEL_AUTHOR] = 0;
2024 }
2025
2026 void DisplayGameControlValues()
2027 {
2028   int i;
2029
2030   game_status = GAME_MODE_PSEUDO_PANEL;
2031
2032   for (i = 0; game_controls[i].nr != -1; i++)
2033   {
2034     int nr = game_controls[i].nr;
2035     int type = game_controls[i].type;
2036     struct TextPosInfo *pos = game_controls[i].pos;
2037     int value = game_control_value[nr];
2038     int last_value = last_game_control_value[nr];
2039     int size = pos->size;
2040     int font = pos->font;
2041
2042     if (value == last_value)
2043       continue;
2044
2045     last_game_control_value[nr] = value;
2046
2047 #if 0
2048     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2049 #endif
2050
2051     if (PANEL_DEACTIVATED(pos))
2052       continue;
2053
2054     if (type == TYPE_INTEGER)
2055     {
2056       if (nr == GAME_CONTROL_LEVEL_NUMBER || nr == GAME_CONTROL_TIME)
2057       {
2058         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2059
2060         if (use_dynamic_size)           /* use dynamic number of digits */
2061         {
2062           int value_change = (nr == GAME_CONTROL_LEVEL_NUMBER ? 100 : 1000);
2063           int size1 = (nr == GAME_CONTROL_LEVEL_NUMBER ? 2 : 3);
2064           int size2 = size1 + 1;
2065           int font1 = pos->font;
2066           int font2 = pos->font_alt;
2067
2068           size = (value < value_change ? size1 : size2);
2069           font = (value < value_change ? font1 : font2);
2070
2071           /* clear background if value just changed its size (dynamic digits) */
2072           if ((last_value < value_change) != (value < value_change))
2073           {
2074             int width1 = size1 * getFontWidth(font1);
2075             int width2 = size2 * getFontWidth(font2);
2076             int max_width = MAX(width1, width2);
2077             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2078
2079             pos->width = max_width;
2080
2081             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2082                                        max_width, max_height);
2083           }
2084         }
2085
2086         pos->width = size * getFontWidth(font);
2087       }
2088
2089       DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, size), font);
2090     }
2091     else if (type == TYPE_ELEMENT)
2092     {
2093       int dst_x = PANEL_XPOS(pos);
2094       int dst_y = PANEL_YPOS(pos);
2095
2096       if (value == EL_UNDEFINED || value == EL_EMPTY)
2097       {
2098         int src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2099         int src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2100
2101         BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2102                    size, size, dst_x, dst_y);
2103       }
2104       else
2105       {
2106         int graphic = el2panelimg(value);
2107
2108         DrawSizedGraphicExt(drawto, dst_x, dst_y, graphic, size);
2109       }
2110     }
2111     else if (type == TYPE_STRING)
2112     {
2113       char *s = (nr == GAME_CONTROL_PLAYER_NAME  ? setup.player_name :
2114                  nr == GAME_CONTROL_LEVEL_NAME   ? level.name :
2115                  nr == GAME_CONTROL_LEVEL_AUTHOR ? level.author : NULL);
2116
2117       if (s != NULL)
2118       {
2119         char *s_cut = getStringCopyN(s, size);
2120
2121         DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), s_cut, font);
2122
2123         free(s_cut);
2124       }
2125     }
2126
2127     redraw_mask |= REDRAW_DOOR_1;
2128   }
2129
2130   game_status = GAME_MODE_PLAYING;
2131 }
2132
2133 void DrawGameValue_Emeralds(int value)
2134 {
2135   struct TextPosInfo *pos = &game.panel.gems;
2136 #if 1
2137   int font_nr = pos->font;
2138 #else
2139   int font_nr = FONT_TEXT_2;
2140 #endif
2141   int font_width = getFontWidth(font_nr);
2142   int chars = pos->size;
2143
2144 #if 1
2145   return;       /* !!! USE NEW STUFF !!! */
2146 #endif
2147
2148   if (PANEL_DEACTIVATED(pos))
2149     return;
2150
2151   pos->width = chars * font_width;
2152
2153   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2154 }
2155
2156 void DrawGameValue_Dynamite(int value)
2157 {
2158   struct TextPosInfo *pos = &game.panel.inventory_count;
2159 #if 1
2160   int font_nr = pos->font;
2161 #else
2162   int font_nr = FONT_TEXT_2;
2163 #endif
2164   int font_width = getFontWidth(font_nr);
2165   int chars = pos->size;
2166
2167 #if 1
2168   return;       /* !!! USE NEW STUFF !!! */
2169 #endif
2170
2171   if (PANEL_DEACTIVATED(pos))
2172     return;
2173
2174   pos->width = chars * font_width;
2175
2176   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2177 }
2178
2179 void DrawGameValue_Score(int value)
2180 {
2181   struct TextPosInfo *pos = &game.panel.score;
2182 #if 1
2183   int font_nr = pos->font;
2184 #else
2185   int font_nr = FONT_TEXT_2;
2186 #endif
2187   int font_width = getFontWidth(font_nr);
2188   int chars = pos->size;
2189
2190 #if 1
2191   return;       /* !!! USE NEW STUFF !!! */
2192 #endif
2193
2194   if (PANEL_DEACTIVATED(pos))
2195     return;
2196
2197   pos->width = chars * font_width;
2198
2199   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2200 }
2201
2202 void DrawGameValue_Time(int value)
2203 {
2204   struct TextPosInfo *pos = &game.panel.time;
2205   static int last_value = -1;
2206   int chars1 = 3;
2207   int chars2 = 4;
2208   int chars = pos->size;
2209 #if 1
2210   int font1_nr = pos->font;
2211   int font2_nr = pos->font_alt;
2212 #else
2213   int font1_nr = FONT_TEXT_2;
2214   int font2_nr = FONT_TEXT_1;
2215 #endif
2216   int font_nr = font1_nr;
2217   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2218
2219 #if 1
2220   return;       /* !!! USE NEW STUFF !!! */
2221 #endif
2222
2223   if (PANEL_DEACTIVATED(pos))
2224     return;
2225
2226   if (use_dynamic_chars)                /* use dynamic number of chars */
2227   {
2228     chars   = (value < 1000 ? chars1   : chars2);
2229     font_nr = (value < 1000 ? font1_nr : font2_nr);
2230   }
2231
2232   /* clear background if value just changed its size (dynamic chars only) */
2233   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2234   {
2235     int width1 = chars1 * getFontWidth(font1_nr);
2236     int width2 = chars2 * getFontWidth(font2_nr);
2237     int max_width = MAX(width1, width2);
2238     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2239
2240     pos->width = max_width;
2241
2242     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2243                                max_width, max_height);
2244   }
2245
2246   pos->width = chars * getFontWidth(font_nr);
2247
2248   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2249
2250   last_value = value;
2251 }
2252
2253 void DrawGameValue_Level(int value)
2254 {
2255   struct TextPosInfo *pos = &game.panel.level_number;
2256   int chars1 = 2;
2257   int chars2 = 3;
2258   int chars = pos->size;
2259 #if 1
2260   int font1_nr = pos->font;
2261   int font2_nr = pos->font_alt;
2262 #else
2263   int font1_nr = FONT_TEXT_2;
2264   int font2_nr = FONT_TEXT_1;
2265 #endif
2266   int font_nr = font1_nr;
2267   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2268
2269 #if 1
2270   return;       /* !!! USE NEW STUFF !!! */
2271 #endif
2272
2273   if (PANEL_DEACTIVATED(pos))
2274     return;
2275
2276   if (use_dynamic_chars)                /* use dynamic number of chars */
2277   {
2278     chars   = (level_nr < 100 ? chars1   : chars2);
2279     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2280   }
2281
2282   pos->width = chars * getFontWidth(font_nr);
2283
2284   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2285 }
2286
2287 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2288 {
2289 #if 0
2290   struct TextPosInfo *pos = &game.panel.keys;
2291 #endif
2292 #if 0
2293   int base_key_graphic = EL_KEY_1;
2294 #endif
2295   int i;
2296
2297 #if 1
2298   return;       /* !!! USE NEW STUFF !!! */
2299 #endif
2300
2301 #if 0
2302   if (PANEL_DEACTIVATED(pos))
2303     return;
2304 #endif
2305
2306 #if 0
2307   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2308     base_key_graphic = EL_EM_KEY_1;
2309 #endif
2310
2311 #if 0
2312   pos->width = 4 * MINI_TILEX;
2313 #endif
2314
2315 #if 1
2316   for (i = 0; i < MAX_NUM_KEYS; i++)
2317 #else
2318   /* currently only 4 of 8 possible keys are displayed */
2319   for (i = 0; i < STD_NUM_KEYS; i++)
2320 #endif
2321   {
2322 #if 1
2323     struct TextPosInfo *pos = &game.panel.key[i];
2324 #endif
2325     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2326     int src_y = DOOR_GFX_PAGEY1 + 123;
2327 #if 1
2328     int dst_x = PANEL_XPOS(pos);
2329     int dst_y = PANEL_YPOS(pos);
2330 #else
2331     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2332     int dst_y = PANEL_YPOS(pos);
2333 #endif
2334
2335 #if 1
2336     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2337                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2338                    EL_KEY_1) + i;
2339     int graphic = el2edimg(element);
2340 #endif
2341
2342 #if 1
2343     if (PANEL_DEACTIVATED(pos))
2344       continue;
2345 #endif
2346
2347 #if 0
2348     /* masked blit with tiles from half-size scaled bitmap does not work yet
2349        (no mask bitmap created for these sizes after loading and scaling) --
2350        solution: load without creating mask, scale, then create final mask */
2351
2352     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2353                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2354
2355     if (key[i])
2356     {
2357 #if 0
2358       int graphic = el2edimg(base_key_graphic + i);
2359 #endif
2360       Bitmap *src_bitmap;
2361       int src_x, src_y;
2362
2363       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2364
2365       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2366                     dst_x - src_x, dst_y - src_y);
2367       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2368                        dst_x, dst_y);
2369     }
2370 #else
2371 #if 1
2372     if (key[i])
2373       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2374     else
2375       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2376                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2377 #else
2378     if (key[i])
2379       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2380     else
2381       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2382                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2383 #endif
2384 #endif
2385   }
2386 }
2387
2388 #else
2389
2390 void DrawGameValue_Emeralds(int value)
2391 {
2392   int font_nr = FONT_TEXT_2;
2393   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2394
2395   if (PANEL_DEACTIVATED(game.panel.gems))
2396     return;
2397
2398   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2399 }
2400
2401 void DrawGameValue_Dynamite(int value)
2402 {
2403   int font_nr = FONT_TEXT_2;
2404   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2405
2406   if (PANEL_DEACTIVATED(game.panel.inventory_count))
2407     return;
2408
2409   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2410 }
2411
2412 void DrawGameValue_Score(int value)
2413 {
2414   int font_nr = FONT_TEXT_2;
2415   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2416
2417   if (PANEL_DEACTIVATED(game.panel.score))
2418     return;
2419
2420   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2421 }
2422
2423 void DrawGameValue_Time(int value)
2424 {
2425   int font1_nr = FONT_TEXT_2;
2426 #if 1
2427   int font2_nr = FONT_TEXT_1;
2428 #else
2429   int font2_nr = FONT_LEVEL_NUMBER;
2430 #endif
2431   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2432   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2433
2434   if (PANEL_DEACTIVATED(game.panel.time))
2435     return;
2436
2437   /* clear background if value just changed its size */
2438   if (value == 999 || value == 1000)
2439     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2440
2441   if (value < 1000)
2442     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2443   else
2444     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2445 }
2446
2447 void DrawGameValue_Level(int value)
2448 {
2449   int font1_nr = FONT_TEXT_2;
2450 #if 1
2451   int font2_nr = FONT_TEXT_1;
2452 #else
2453   int font2_nr = FONT_LEVEL_NUMBER;
2454 #endif
2455
2456   if (PANEL_DEACTIVATED(game.panel.level))
2457     return;
2458
2459   if (level_nr < 100)
2460     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2461   else
2462     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2463 }
2464
2465 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2466 {
2467   int base_key_graphic = EL_KEY_1;
2468   int i;
2469
2470   if (PANEL_DEACTIVATED(game.panel.keys))
2471     return;
2472
2473   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2474     base_key_graphic = EL_EM_KEY_1;
2475
2476   /* currently only 4 of 8 possible keys are displayed */
2477   for (i = 0; i < STD_NUM_KEYS; i++)
2478   {
2479     int x = XX_KEYS + i * MINI_TILEX;
2480     int y = YY_KEYS;
2481
2482     if (key[i])
2483       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2484     else
2485       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2486                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2487   }
2488 }
2489
2490 #endif
2491
2492 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2493                        int key_bits)
2494 {
2495   int key[MAX_NUM_KEYS];
2496   int i;
2497
2498   /* prevent EM engine from updating time/score values parallel to GameWon() */
2499   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2500       local_player->LevelSolved)
2501     return;
2502
2503   for (i = 0; i < MAX_NUM_KEYS; i++)
2504     key[i] = key_bits & (1 << i);
2505
2506   DrawGameValue_Level(level_nr);
2507
2508   DrawGameValue_Emeralds(emeralds);
2509   DrawGameValue_Dynamite(dynamite);
2510   DrawGameValue_Score(score);
2511   DrawGameValue_Time(time);
2512
2513   DrawGameValue_Keys(key);
2514 }
2515
2516 void DrawGameDoorValues()
2517 {
2518   UpdateGameControlValues();
2519   DisplayGameControlValues();
2520 }
2521
2522 void DrawGameDoorValues_OLD()
2523 {
2524   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2525   int dynamite_value = 0;
2526   int score_value = (local_player->LevelSolved ? local_player->score_final :
2527                      local_player->score);
2528   int gems_value = local_player->gems_still_needed;
2529   int key_bits = 0;
2530   int i, j;
2531
2532   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2533   {
2534     DrawGameDoorValues_EM();
2535
2536     return;
2537   }
2538
2539   if (game.centered_player_nr == -1)
2540   {
2541     for (i = 0; i < MAX_PLAYERS; i++)
2542     {
2543       for (j = 0; j < MAX_NUM_KEYS; j++)
2544         if (stored_player[i].key[j])
2545           key_bits |= (1 << j);
2546
2547       dynamite_value += stored_player[i].inventory_size;
2548     }
2549   }
2550   else
2551   {
2552     int player_nr = game.centered_player_nr;
2553
2554     for (i = 0; i < MAX_NUM_KEYS; i++)
2555       if (stored_player[player_nr].key[i])
2556         key_bits |= (1 << i);
2557
2558     dynamite_value = stored_player[player_nr].inventory_size;
2559   }
2560
2561   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2562                     key_bits);
2563 }
2564
2565
2566 /*
2567   =============================================================================
2568   InitGameEngine()
2569   -----------------------------------------------------------------------------
2570   initialize game engine due to level / tape version number
2571   =============================================================================
2572 */
2573
2574 static void InitGameEngine()
2575 {
2576   int i, j, k, l, x, y;
2577
2578   /* set game engine from tape file when re-playing, else from level file */
2579   game.engine_version = (tape.playing ? tape.engine_version :
2580                          level.game_version);
2581
2582   /* ---------------------------------------------------------------------- */
2583   /* set flags for bugs and changes according to active game engine version */
2584   /* ---------------------------------------------------------------------- */
2585
2586   /*
2587     Summary of bugfix/change:
2588     Fixed handling for custom elements that change when pushed by the player.
2589
2590     Fixed/changed in version:
2591     3.1.0
2592
2593     Description:
2594     Before 3.1.0, custom elements that "change when pushing" changed directly
2595     after the player started pushing them (until then handled in "DigField()").
2596     Since 3.1.0, these custom elements are not changed until the "pushing"
2597     move of the element is finished (now handled in "ContinueMoving()").
2598
2599     Affected levels/tapes:
2600     The first condition is generally needed for all levels/tapes before version
2601     3.1.0, which might use the old behaviour before it was changed; known tapes
2602     that are affected are some tapes from the level set "Walpurgis Gardens" by
2603     Jamie Cullen.
2604     The second condition is an exception from the above case and is needed for
2605     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2606     above (including some development versions of 3.1.0), but before it was
2607     known that this change would break tapes like the above and was fixed in
2608     3.1.1, so that the changed behaviour was active although the engine version
2609     while recording maybe was before 3.1.0. There is at least one tape that is
2610     affected by this exception, which is the tape for the one-level set "Bug
2611     Machine" by Juergen Bonhagen.
2612   */
2613
2614   game.use_change_when_pushing_bug =
2615     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2616      !(tape.playing &&
2617        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2618        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2619
2620   /*
2621     Summary of bugfix/change:
2622     Fixed handling for blocking the field the player leaves when moving.
2623
2624     Fixed/changed in version:
2625     3.1.1
2626
2627     Description:
2628     Before 3.1.1, when "block last field when moving" was enabled, the field
2629     the player is leaving when moving was blocked for the time of the move,
2630     and was directly unblocked afterwards. This resulted in the last field
2631     being blocked for exactly one less than the number of frames of one player
2632     move. Additionally, even when blocking was disabled, the last field was
2633     blocked for exactly one frame.
2634     Since 3.1.1, due to changes in player movement handling, the last field
2635     is not blocked at all when blocking is disabled. When blocking is enabled,
2636     the last field is blocked for exactly the number of frames of one player
2637     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2638     last field is blocked for exactly one more than the number of frames of
2639     one player move.
2640
2641     Affected levels/tapes:
2642     (!!! yet to be determined -- probably many !!!)
2643   */
2644
2645   game.use_block_last_field_bug =
2646     (game.engine_version < VERSION_IDENT(3,1,1,0));
2647
2648   /*
2649     Summary of bugfix/change:
2650     Changed behaviour of CE changes with multiple changes per single frame.
2651
2652     Fixed/changed in version:
2653     3.2.0-6
2654
2655     Description:
2656     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
2657     This resulted in race conditions where CEs seem to behave strange in some
2658     situations (where triggered CE changes were just skipped because there was
2659     already a CE change on that tile in the playfield in that engine frame).
2660     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
2661     (The number of changes per frame must be limited in any case, because else
2662     it is easily possible to define CE changes that would result in an infinite
2663     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
2664     should be set large enough so that it would only be reached in cases where
2665     the corresponding CE change conditions run into a loop. Therefore, it seems
2666     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
2667     maximal number of change pages for custom elements.)
2668
2669     Affected levels/tapes:
2670     Probably many.
2671   */
2672
2673 #if USE_ONLY_ONE_CHANGE_PER_FRAME
2674   game.max_num_changes_per_frame = 1;
2675 #else
2676   game.max_num_changes_per_frame =
2677     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
2678 #endif
2679
2680   /* ---------------------------------------------------------------------- */
2681
2682   /* default scan direction: scan playfield from top/left to bottom/right */
2683   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2684
2685   /* dynamically adjust element properties according to game engine version */
2686   InitElementPropertiesEngine(game.engine_version);
2687
2688 #if 0
2689   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2690   printf("          tape version == %06d [%s] [file: %06d]\n",
2691          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2692          tape.file_version);
2693   printf("       => game.engine_version == %06d\n", game.engine_version);
2694 #endif
2695
2696   /* ---------- initialize player's initial move delay --------------------- */
2697
2698   /* dynamically adjust player properties according to level information */
2699   for (i = 0; i < MAX_PLAYERS; i++)
2700     game.initial_move_delay_value[i] =
2701       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2702
2703   /* dynamically adjust player properties according to game engine version */
2704   for (i = 0; i < MAX_PLAYERS; i++)
2705     game.initial_move_delay[i] =
2706       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2707        game.initial_move_delay_value[i] : 0);
2708
2709   /* ---------- initialize player's initial push delay --------------------- */
2710
2711   /* dynamically adjust player properties according to game engine version */
2712   game.initial_push_delay_value =
2713     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2714
2715   /* ---------- initialize changing elements ------------------------------- */
2716
2717   /* initialize changing elements information */
2718   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2719   {
2720     struct ElementInfo *ei = &element_info[i];
2721
2722     /* this pointer might have been changed in the level editor */
2723     ei->change = &ei->change_page[0];
2724
2725     if (!IS_CUSTOM_ELEMENT(i))
2726     {
2727       ei->change->target_element = EL_EMPTY_SPACE;
2728       ei->change->delay_fixed = 0;
2729       ei->change->delay_random = 0;
2730       ei->change->delay_frames = 1;
2731     }
2732
2733     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2734     {
2735       ei->has_change_event[j] = FALSE;
2736
2737       ei->event_page_nr[j] = 0;
2738       ei->event_page[j] = &ei->change_page[0];
2739     }
2740   }
2741
2742   /* add changing elements from pre-defined list */
2743   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2744   {
2745     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2746     struct ElementInfo *ei = &element_info[ch_delay->element];
2747
2748     ei->change->target_element       = ch_delay->target_element;
2749     ei->change->delay_fixed          = ch_delay->change_delay;
2750
2751     ei->change->pre_change_function  = ch_delay->pre_change_function;
2752     ei->change->change_function      = ch_delay->change_function;
2753     ei->change->post_change_function = ch_delay->post_change_function;
2754
2755     ei->change->can_change = TRUE;
2756     ei->change->can_change_or_has_action = TRUE;
2757
2758     ei->has_change_event[CE_DELAY] = TRUE;
2759
2760     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2761     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2762   }
2763
2764   /* ---------- initialize internal run-time variables ------------- */
2765
2766   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2767   {
2768     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2769
2770     for (j = 0; j < ei->num_change_pages; j++)
2771     {
2772       ei->change_page[j].can_change_or_has_action =
2773         (ei->change_page[j].can_change |
2774          ei->change_page[j].has_action);
2775     }
2776   }
2777
2778   /* add change events from custom element configuration */
2779   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2780   {
2781     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2782
2783     for (j = 0; j < ei->num_change_pages; j++)
2784     {
2785       if (!ei->change_page[j].can_change_or_has_action)
2786         continue;
2787
2788       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2789       {
2790         /* only add event page for the first page found with this event */
2791         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2792         {
2793           ei->has_change_event[k] = TRUE;
2794
2795           ei->event_page_nr[k] = j;
2796           ei->event_page[k] = &ei->change_page[j];
2797         }
2798       }
2799     }
2800   }
2801
2802   /* ---------- initialize run-time trigger player and element ------------- */
2803
2804   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2805   {
2806     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2807
2808     for (j = 0; j < ei->num_change_pages; j++)
2809     {
2810       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2811       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
2812       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2813       ei->change_page[j].actual_trigger_ce_value = 0;
2814       ei->change_page[j].actual_trigger_ce_score = 0;
2815     }
2816   }
2817
2818   /* ---------- initialize trigger events ---------------------------------- */
2819
2820   /* initialize trigger events information */
2821   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2822     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2823       trigger_events[i][j] = FALSE;
2824
2825   /* add trigger events from element change event properties */
2826   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2827   {
2828     struct ElementInfo *ei = &element_info[i];
2829
2830     for (j = 0; j < ei->num_change_pages; j++)
2831     {
2832       if (!ei->change_page[j].can_change_or_has_action)
2833         continue;
2834
2835       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2836       {
2837         int trigger_element = ei->change_page[j].trigger_element;
2838
2839         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2840         {
2841           if (ei->change_page[j].has_event[k])
2842           {
2843             if (IS_GROUP_ELEMENT(trigger_element))
2844             {
2845               struct ElementGroupInfo *group =
2846                 element_info[trigger_element].group;
2847
2848               for (l = 0; l < group->num_elements_resolved; l++)
2849                 trigger_events[group->element_resolved[l]][k] = TRUE;
2850             }
2851             else if (trigger_element == EL_ANY_ELEMENT)
2852               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2853                 trigger_events[l][k] = TRUE;
2854             else
2855               trigger_events[trigger_element][k] = TRUE;
2856           }
2857         }
2858       }
2859     }
2860   }
2861
2862   /* ---------- initialize push delay -------------------------------------- */
2863
2864   /* initialize push delay values to default */
2865   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2866   {
2867     if (!IS_CUSTOM_ELEMENT(i))
2868     {
2869       /* set default push delay values (corrected since version 3.0.7-1) */
2870       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2871       {
2872         element_info[i].push_delay_fixed = 2;
2873         element_info[i].push_delay_random = 8;
2874       }
2875       else
2876       {
2877         element_info[i].push_delay_fixed = 8;
2878         element_info[i].push_delay_random = 8;
2879       }
2880     }
2881   }
2882
2883   /* set push delay value for certain elements from pre-defined list */
2884   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2885   {
2886     int e = push_delay_list[i].element;
2887
2888     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2889     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2890   }
2891
2892   /* set push delay value for Supaplex elements for newer engine versions */
2893   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2894   {
2895     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2896     {
2897       if (IS_SP_ELEMENT(i))
2898       {
2899         /* set SP push delay to just enough to push under a falling zonk */
2900         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2901
2902         element_info[i].push_delay_fixed  = delay;
2903         element_info[i].push_delay_random = 0;
2904       }
2905     }
2906   }
2907
2908   /* ---------- initialize move stepsize ----------------------------------- */
2909
2910   /* initialize move stepsize values to default */
2911   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2912     if (!IS_CUSTOM_ELEMENT(i))
2913       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2914
2915   /* set move stepsize value for certain elements from pre-defined list */
2916   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2917   {
2918     int e = move_stepsize_list[i].element;
2919
2920     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2921   }
2922
2923   /* ---------- initialize collect score ----------------------------------- */
2924
2925   /* initialize collect score values for custom elements from initial value */
2926   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2927     if (IS_CUSTOM_ELEMENT(i))
2928       element_info[i].collect_score = element_info[i].collect_score_initial;
2929
2930   /* ---------- initialize collect count ----------------------------------- */
2931
2932   /* initialize collect count values for non-custom elements */
2933   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2934     if (!IS_CUSTOM_ELEMENT(i))
2935       element_info[i].collect_count_initial = 0;
2936
2937   /* add collect count values for all elements from pre-defined list */
2938   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2939     element_info[collect_count_list[i].element].collect_count_initial =
2940       collect_count_list[i].count;
2941
2942   /* ---------- initialize access direction -------------------------------- */
2943
2944   /* initialize access direction values to default (access from every side) */
2945   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2946     if (!IS_CUSTOM_ELEMENT(i))
2947       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2948
2949   /* set access direction value for certain elements from pre-defined list */
2950   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2951     element_info[access_direction_list[i].element].access_direction =
2952       access_direction_list[i].direction;
2953
2954   /* ---------- initialize explosion content ------------------------------- */
2955   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2956   {
2957     if (IS_CUSTOM_ELEMENT(i))
2958       continue;
2959
2960     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2961     {
2962       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2963
2964       element_info[i].content.e[x][y] =
2965         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2966          i == EL_PLAYER_2 ? EL_EMERALD_RED :
2967          i == EL_PLAYER_3 ? EL_EMERALD :
2968          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2969          i == EL_MOLE ? EL_EMERALD_RED :
2970          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2971          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2972          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2973          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2974          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2975          i == EL_WALL_EMERALD ? EL_EMERALD :
2976          i == EL_WALL_DIAMOND ? EL_DIAMOND :
2977          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2978          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2979          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2980          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2981          i == EL_WALL_PEARL ? EL_PEARL :
2982          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2983          EL_EMPTY);
2984     }
2985   }
2986
2987   /* ---------- initialize recursion detection ------------------------------ */
2988   recursion_loop_depth = 0;
2989   recursion_loop_detected = FALSE;
2990   recursion_loop_element = EL_UNDEFINED;
2991
2992   /* ---------- initialize graphics engine ---------------------------------- */
2993   game.scroll_delay_value =
2994     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
2995      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
2996   game.scroll_delay_value =
2997     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
2998 }
2999
3000 int get_num_special_action(int element, int action_first, int action_last)
3001 {
3002   int num_special_action = 0;
3003   int i, j;
3004
3005   for (i = action_first; i <= action_last; i++)
3006   {
3007     boolean found = FALSE;
3008
3009     for (j = 0; j < NUM_DIRECTIONS; j++)
3010       if (el_act_dir2img(element, i, j) !=
3011           el_act_dir2img(element, ACTION_DEFAULT, j))
3012         found = TRUE;
3013
3014     if (found)
3015       num_special_action++;
3016     else
3017       break;
3018   }
3019
3020   return num_special_action;
3021 }
3022
3023
3024 /*
3025   =============================================================================
3026   InitGame()
3027   -----------------------------------------------------------------------------
3028   initialize and start new game
3029   =============================================================================
3030 */
3031
3032 void InitGame()
3033 {
3034   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3035   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3036   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3037 #if 0
3038   boolean do_fading = (game_status == GAME_MODE_MAIN);
3039 #endif
3040   int i, j, x, y;
3041
3042   game_status = GAME_MODE_PLAYING;
3043
3044   InitGameEngine();
3045   InitGameControlValues();
3046
3047   /* don't play tapes over network */
3048   network_playing = (options.network && !tape.playing);
3049
3050   for (i = 0; i < MAX_PLAYERS; i++)
3051   {
3052     struct PlayerInfo *player = &stored_player[i];
3053
3054     player->index_nr = i;
3055     player->index_bit = (1 << i);
3056     player->element_nr = EL_PLAYER_1 + i;
3057
3058     player->present = FALSE;
3059     player->active = FALSE;
3060     player->killed = FALSE;
3061
3062     player->action = 0;
3063     player->effective_action = 0;
3064     player->programmed_action = 0;
3065
3066     player->score = 0;
3067     player->score_final = 0;
3068
3069     player->gems_still_needed = level.gems_needed;
3070     player->sokobanfields_still_needed = 0;
3071     player->lights_still_needed = 0;
3072     player->friends_still_needed = 0;
3073
3074     for (j = 0; j < MAX_NUM_KEYS; j++)
3075       player->key[j] = FALSE;
3076
3077     player->num_white_keys = 0;
3078
3079     player->dynabomb_count = 0;
3080     player->dynabomb_size = 1;
3081     player->dynabombs_left = 0;
3082     player->dynabomb_xl = FALSE;
3083
3084     player->MovDir = MV_NONE;
3085     player->MovPos = 0;
3086     player->GfxPos = 0;
3087     player->GfxDir = MV_NONE;
3088     player->GfxAction = ACTION_DEFAULT;
3089     player->Frame = 0;
3090     player->StepFrame = 0;
3091
3092     player->use_murphy = FALSE;
3093     player->artwork_element =
3094       (level.use_artwork_element[i] ? level.artwork_element[i] :
3095        player->element_nr);
3096
3097     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3098     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3099
3100     player->gravity = level.initial_player_gravity[i];
3101
3102     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3103
3104     player->actual_frame_counter = 0;
3105
3106     player->step_counter = 0;
3107
3108     player->last_move_dir = MV_NONE;
3109
3110     player->is_active = FALSE;
3111
3112     player->is_waiting = FALSE;
3113     player->is_moving = FALSE;
3114     player->is_auto_moving = FALSE;
3115     player->is_digging = FALSE;
3116     player->is_snapping = FALSE;
3117     player->is_collecting = FALSE;
3118     player->is_pushing = FALSE;
3119     player->is_switching = FALSE;
3120     player->is_dropping = FALSE;
3121     player->is_dropping_pressed = FALSE;
3122
3123     player->is_bored = FALSE;
3124     player->is_sleeping = FALSE;
3125
3126     player->frame_counter_bored = -1;
3127     player->frame_counter_sleeping = -1;
3128
3129     player->anim_delay_counter = 0;
3130     player->post_delay_counter = 0;
3131
3132     player->dir_waiting = MV_NONE;
3133     player->action_waiting = ACTION_DEFAULT;
3134     player->last_action_waiting = ACTION_DEFAULT;
3135     player->special_action_bored = ACTION_DEFAULT;
3136     player->special_action_sleeping = ACTION_DEFAULT;
3137
3138     player->switch_x = -1;
3139     player->switch_y = -1;
3140
3141     player->drop_x = -1;
3142     player->drop_y = -1;
3143
3144     player->show_envelope = 0;
3145
3146     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3147
3148     player->push_delay       = -1;      /* initialized when pushing starts */
3149     player->push_delay_value = game.initial_push_delay_value;
3150
3151     player->drop_delay = 0;
3152     player->drop_pressed_delay = 0;
3153
3154     player->last_jx = -1;
3155     player->last_jy = -1;
3156     player->jx = -1;
3157     player->jy = -1;
3158
3159     player->shield_normal_time_left = 0;
3160     player->shield_deadly_time_left = 0;
3161
3162     player->inventory_infinite_element = EL_UNDEFINED;
3163     player->inventory_size = 0;
3164
3165     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3166     SnapField(player, 0, 0);
3167
3168     player->LevelSolved = FALSE;
3169     player->GameOver = FALSE;
3170
3171     player->LevelSolved_GameWon = FALSE;
3172     player->LevelSolved_GameEnd = FALSE;
3173     player->LevelSolved_PanelOff = FALSE;
3174     player->LevelSolved_SaveTape = FALSE;
3175     player->LevelSolved_SaveScore = FALSE;
3176   }
3177
3178   network_player_action_received = FALSE;
3179
3180 #if defined(NETWORK_AVALIABLE)
3181   /* initial null action */
3182   if (network_playing)
3183     SendToServer_MovePlayer(MV_NONE);
3184 #endif
3185
3186   ZX = ZY = -1;
3187   ExitX = ExitY = -1;
3188
3189   FrameCounter = 0;
3190   TimeFrames = 0;
3191   TimePlayed = 0;
3192   TimeLeft = level.time;
3193   TapeTime = 0;
3194
3195   ScreenMovDir = MV_NONE;
3196   ScreenMovPos = 0;
3197   ScreenGfxPos = 0;
3198
3199   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3200
3201   AllPlayersGone = FALSE;
3202
3203   game.yamyam_content_nr = 0;
3204   game.magic_wall_active = FALSE;
3205   game.magic_wall_time_left = 0;
3206   game.light_time_left = 0;
3207   game.timegate_time_left = 0;
3208   game.switchgate_pos = 0;
3209   game.wind_direction = level.wind_direction_initial;
3210
3211 #if !USE_PLAYER_GRAVITY
3212   game.gravity = FALSE;
3213   game.explosions_delayed = TRUE;
3214 #endif
3215
3216   game.lenses_time_left = 0;
3217   game.magnify_time_left = 0;
3218
3219   game.ball_state = level.ball_state_initial;
3220   game.ball_content_nr = 0;
3221
3222   game.envelope_active = FALSE;
3223
3224   /* set focus to local player for network games, else to all players */
3225   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3226   game.centered_player_nr_next = game.centered_player_nr;
3227   game.set_centered_player = FALSE;
3228
3229   if (network_playing && tape.recording)
3230   {
3231     /* store client dependent player focus when recording network games */
3232     tape.centered_player_nr_next = game.centered_player_nr_next;
3233     tape.set_centered_player = TRUE;
3234   }
3235
3236   for (i = 0; i < NUM_BELTS; i++)
3237   {
3238     game.belt_dir[i] = MV_NONE;
3239     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3240   }
3241
3242   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3243     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3244
3245   SCAN_PLAYFIELD(x, y)
3246   {
3247     Feld[x][y] = level.field[x][y];
3248     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3249     ChangeDelay[x][y] = 0;
3250     ChangePage[x][y] = -1;
3251 #if USE_NEW_CUSTOM_VALUE
3252     CustomValue[x][y] = 0;              /* initialized in InitField() */
3253 #endif
3254     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3255     AmoebaNr[x][y] = 0;
3256     WasJustMoving[x][y] = 0;
3257     WasJustFalling[x][y] = 0;
3258     CheckCollision[x][y] = 0;
3259     CheckImpact[x][y] = 0;
3260     Stop[x][y] = FALSE;
3261     Pushed[x][y] = FALSE;
3262
3263     ChangeCount[x][y] = 0;
3264     ChangeEvent[x][y] = -1;
3265
3266     ExplodePhase[x][y] = 0;
3267     ExplodeDelay[x][y] = 0;
3268     ExplodeField[x][y] = EX_TYPE_NONE;
3269
3270     RunnerVisit[x][y] = 0;
3271     PlayerVisit[x][y] = 0;
3272
3273     GfxFrame[x][y] = 0;
3274     GfxRandom[x][y] = INIT_GFX_RANDOM();
3275     GfxElement[x][y] = EL_UNDEFINED;
3276     GfxAction[x][y] = ACTION_DEFAULT;
3277     GfxDir[x][y] = MV_NONE;
3278   }
3279
3280   SCAN_PLAYFIELD(x, y)
3281   {
3282     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3283       emulate_bd = FALSE;
3284     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3285       emulate_sb = FALSE;
3286     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3287       emulate_sp = FALSE;
3288
3289     InitField(x, y, TRUE);
3290   }
3291
3292   InitBeltMovement();
3293
3294   for (i = 0; i < MAX_PLAYERS; i++)
3295   {
3296     struct PlayerInfo *player = &stored_player[i];
3297
3298     /* set number of special actions for bored and sleeping animation */
3299     player->num_special_action_bored =
3300       get_num_special_action(player->artwork_element,
3301                              ACTION_BORING_1, ACTION_BORING_LAST);
3302     player->num_special_action_sleeping =
3303       get_num_special_action(player->artwork_element,
3304                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3305   }
3306
3307   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3308                     emulate_sb ? EMU_SOKOBAN :
3309                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3310
3311 #if USE_NEW_ALL_SLIPPERY
3312   /* initialize type of slippery elements */
3313   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3314   {
3315     if (!IS_CUSTOM_ELEMENT(i))
3316     {
3317       /* default: elements slip down either to the left or right randomly */
3318       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3319
3320       /* SP style elements prefer to slip down on the left side */
3321       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3322         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3323
3324       /* BD style elements prefer to slip down on the left side */
3325       if (game.emulation == EMU_BOULDERDASH)
3326         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3327     }
3328   }
3329 #endif
3330
3331   /* initialize explosion and ignition delay */
3332   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3333   {
3334     if (!IS_CUSTOM_ELEMENT(i))
3335     {
3336       int num_phase = 8;
3337       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3338                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3339                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3340       int last_phase = (num_phase + 1) * delay;
3341       int half_phase = (num_phase / 2) * delay;
3342
3343       element_info[i].explosion_delay = last_phase - 1;
3344       element_info[i].ignition_delay = half_phase;
3345
3346       if (i == EL_BLACK_ORB)
3347         element_info[i].ignition_delay = 1;
3348     }
3349
3350 #if 0
3351     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3352       element_info[i].explosion_delay = 1;
3353
3354     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3355       element_info[i].ignition_delay = 1;
3356 #endif
3357   }
3358
3359   /* correct non-moving belts to start moving left */
3360   for (i = 0; i < NUM_BELTS; i++)
3361     if (game.belt_dir[i] == MV_NONE)
3362       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3363
3364   /* check if any connected player was not found in playfield */
3365   for (i = 0; i < MAX_PLAYERS; i++)
3366   {
3367     struct PlayerInfo *player = &stored_player[i];
3368
3369     if (player->connected && !player->present)
3370     {
3371       for (j = 0; j < MAX_PLAYERS; j++)
3372       {
3373         struct PlayerInfo *some_player = &stored_player[j];
3374         int jx = some_player->jx, jy = some_player->jy;
3375
3376         /* assign first free player found that is present in the playfield */
3377         if (some_player->present && !some_player->connected)
3378         {
3379           player->present = TRUE;
3380           player->active = TRUE;
3381
3382           some_player->present = FALSE;
3383           some_player->active = FALSE;
3384
3385           player->artwork_element = some_player->artwork_element;
3386
3387           player->block_last_field       = some_player->block_last_field;
3388           player->block_delay_adjustment = some_player->block_delay_adjustment;
3389
3390           StorePlayer[jx][jy] = player->element_nr;
3391           player->jx = player->last_jx = jx;
3392           player->jy = player->last_jy = jy;
3393
3394           break;
3395         }
3396       }
3397     }
3398   }
3399
3400   if (tape.playing)
3401   {
3402     /* when playing a tape, eliminate all players who do not participate */
3403
3404     for (i = 0; i < MAX_PLAYERS; i++)
3405     {
3406       if (stored_player[i].active && !tape.player_participates[i])
3407       {
3408         struct PlayerInfo *player = &stored_player[i];
3409         int jx = player->jx, jy = player->jy;
3410
3411         player->active = FALSE;
3412         StorePlayer[jx][jy] = 0;
3413         Feld[jx][jy] = EL_EMPTY;
3414       }
3415     }
3416   }
3417   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
3418   {
3419     /* when in single player mode, eliminate all but the first active player */
3420
3421     for (i = 0; i < MAX_PLAYERS; i++)
3422     {
3423       if (stored_player[i].active)
3424       {
3425         for (j = i + 1; j < MAX_PLAYERS; j++)
3426         {
3427           if (stored_player[j].active)
3428           {
3429             struct PlayerInfo *player = &stored_player[j];
3430             int jx = player->jx, jy = player->jy;
3431
3432             player->active = FALSE;
3433             player->present = FALSE;
3434
3435             StorePlayer[jx][jy] = 0;
3436             Feld[jx][jy] = EL_EMPTY;
3437           }
3438         }
3439       }
3440     }
3441   }
3442
3443   /* when recording the game, store which players take part in the game */
3444   if (tape.recording)
3445   {
3446     for (i = 0; i < MAX_PLAYERS; i++)
3447       if (stored_player[i].active)
3448         tape.player_participates[i] = TRUE;
3449   }
3450
3451   if (options.debug)
3452   {
3453     for (i = 0; i < MAX_PLAYERS; i++)
3454     {
3455       struct PlayerInfo *player = &stored_player[i];
3456
3457       printf("Player %d: present == %d, connected == %d, active == %d.\n",
3458              i+1,
3459              player->present,
3460              player->connected,
3461              player->active);
3462       if (local_player == player)
3463         printf("Player  %d is local player.\n", i+1);
3464     }
3465   }
3466
3467   if (BorderElement == EL_EMPTY)
3468   {
3469     SBX_Left = 0;
3470     SBX_Right = lev_fieldx - SCR_FIELDX;
3471     SBY_Upper = 0;
3472     SBY_Lower = lev_fieldy - SCR_FIELDY;
3473   }
3474   else
3475   {
3476     SBX_Left = -1;
3477     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3478     SBY_Upper = -1;
3479     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3480   }
3481
3482   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3483     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3484
3485   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3486     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3487
3488   /* if local player not found, look for custom element that might create
3489      the player (make some assumptions about the right custom element) */
3490   if (!local_player->present)
3491   {
3492     int start_x = 0, start_y = 0;
3493     int found_rating = 0;
3494     int found_element = EL_UNDEFINED;
3495     int player_nr = local_player->index_nr;
3496
3497     SCAN_PLAYFIELD(x, y)
3498     {
3499       int element = Feld[x][y];
3500       int content;
3501       int xx, yy;
3502       boolean is_player;
3503
3504       if (level.use_start_element[player_nr] &&
3505           level.start_element[player_nr] == element &&
3506           found_rating < 4)
3507       {
3508         start_x = x;
3509         start_y = y;
3510
3511         found_rating = 4;
3512         found_element = element;
3513       }
3514
3515       if (!IS_CUSTOM_ELEMENT(element))
3516         continue;
3517
3518       if (CAN_CHANGE(element))
3519       {
3520         for (i = 0; i < element_info[element].num_change_pages; i++)
3521         {
3522           /* check for player created from custom element as single target */
3523           content = element_info[element].change_page[i].target_element;
3524           is_player = ELEM_IS_PLAYER(content);
3525
3526           if (is_player && (found_rating < 3 ||
3527                             (found_rating == 3 && element < found_element)))
3528           {
3529             start_x = x;
3530             start_y = y;
3531
3532             found_rating = 3;
3533             found_element = element;
3534           }
3535         }
3536       }
3537
3538       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3539       {
3540         /* check for player created from custom element as explosion content */
3541         content = element_info[element].content.e[xx][yy];
3542         is_player = ELEM_IS_PLAYER(content);
3543
3544         if (is_player && (found_rating < 2 ||
3545                           (found_rating == 2 && element < found_element)))
3546         {
3547           start_x = x + xx - 1;
3548           start_y = y + yy - 1;
3549
3550           found_rating = 2;
3551           found_element = element;
3552         }
3553
3554         if (!CAN_CHANGE(element))
3555           continue;
3556
3557         for (i = 0; i < element_info[element].num_change_pages; i++)
3558         {
3559           /* check for player created from custom element as extended target */
3560           content =
3561             element_info[element].change_page[i].target_content.e[xx][yy];
3562
3563           is_player = ELEM_IS_PLAYER(content);
3564
3565           if (is_player && (found_rating < 1 ||
3566                             (found_rating == 1 && element < found_element)))
3567           {
3568             start_x = x + xx - 1;
3569             start_y = y + yy - 1;
3570
3571             found_rating = 1;
3572             found_element = element;
3573           }
3574         }
3575       }
3576     }
3577
3578     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3579                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3580                 start_x - MIDPOSX);
3581
3582     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3583                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3584                 start_y - MIDPOSY);
3585   }
3586   else
3587   {
3588     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3589                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3590                 local_player->jx - MIDPOSX);
3591
3592     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3593                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3594                 local_player->jy - MIDPOSY);
3595   }
3596
3597   StopAnimation();
3598
3599   if (!game.restart_level)
3600     CloseDoor(DOOR_CLOSE_1);
3601
3602 #if 1
3603   if (level_editor_test_game)
3604     FadeSkipNextFadeIn();
3605   else
3606     FadeSetEnterScreen();
3607 #else
3608   if (level_editor_test_game)
3609     fading = fading_none;
3610   else
3611     fading = menu.destination;
3612 #endif
3613
3614 #if 1
3615   FadeOut(REDRAW_FIELD);
3616 #else
3617   if (do_fading)
3618     FadeOut(REDRAW_FIELD);
3619 #endif
3620
3621   /* !!! FIX THIS (START) !!! */
3622   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3623   {
3624     InitGameEngine_EM();
3625
3626     /* blit playfield from scroll buffer to normal back buffer for fading in */
3627     BlitScreenToBitmap_EM(backbuffer);
3628   }
3629   else
3630   {
3631     DrawLevel();
3632     DrawAllPlayers();
3633
3634     /* after drawing the level, correct some elements */
3635     if (game.timegate_time_left == 0)
3636       CloseAllOpenTimegates();
3637
3638     /* blit playfield from scroll buffer to normal back buffer for fading in */
3639     if (setup.soft_scrolling)
3640       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3641
3642     redraw_mask |= REDRAW_FROM_BACKBUFFER;
3643   }
3644   /* !!! FIX THIS (END) !!! */
3645
3646 #if 1
3647   FadeIn(REDRAW_FIELD);
3648 #else
3649   if (do_fading)
3650     FadeIn(REDRAW_FIELD);
3651
3652   BackToFront();
3653 #endif
3654
3655   if (!game.restart_level)
3656   {
3657     /* copy default game door content to main double buffer */
3658     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3659                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3660   }
3661
3662   SetPanelBackground();
3663   SetDrawBackgroundMask(REDRAW_DOOR_1);
3664
3665   DrawGameDoorValues();
3666
3667   if (!game.restart_level)
3668   {
3669     UnmapGameButtons();
3670     UnmapTapeButtons();
3671     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3672     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3673     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3674     MapGameButtons();
3675     MapTapeButtons();
3676
3677     /* copy actual game door content to door double buffer for OpenDoor() */
3678     BlitBitmap(drawto, bitmap_db_door,
3679                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3680
3681     OpenDoor(DOOR_OPEN_ALL);
3682
3683     PlaySound(SND_GAME_STARTING);
3684
3685     if (setup.sound_music)
3686       PlayLevelMusic();
3687
3688     KeyboardAutoRepeatOffUnlessAutoplay();
3689
3690     if (options.debug)
3691     {
3692       for (i = 0; i < MAX_PLAYERS; i++)
3693         printf("Player %d %sactive.\n",
3694                i + 1, (stored_player[i].active ? "" : "not "));
3695     }
3696   }
3697
3698 #if 1
3699   UnmapAllGadgets();
3700
3701   MapGameButtons();
3702   MapTapeButtons();
3703 #endif
3704
3705   game.restart_level = FALSE;
3706 }
3707
3708 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3709 {
3710   /* this is used for non-R'n'D game engines to update certain engine values */
3711
3712   /* needed to determine if sounds are played within the visible screen area */
3713   scroll_x = actual_scroll_x;
3714   scroll_y = actual_scroll_y;
3715 }
3716
3717 void InitMovDir(int x, int y)
3718 {
3719   int i, element = Feld[x][y];
3720   static int xy[4][2] =
3721   {
3722     {  0, +1 },
3723     { +1,  0 },
3724     {  0, -1 },
3725     { -1,  0 }
3726   };
3727   static int direction[3][4] =
3728   {
3729     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
3730     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
3731     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
3732   };
3733
3734   switch (element)
3735   {
3736     case EL_BUG_RIGHT:
3737     case EL_BUG_UP:
3738     case EL_BUG_LEFT:
3739     case EL_BUG_DOWN:
3740       Feld[x][y] = EL_BUG;
3741       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3742       break;
3743
3744     case EL_SPACESHIP_RIGHT:
3745     case EL_SPACESHIP_UP:
3746     case EL_SPACESHIP_LEFT:
3747     case EL_SPACESHIP_DOWN:
3748       Feld[x][y] = EL_SPACESHIP;
3749       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3750       break;
3751
3752     case EL_BD_BUTTERFLY_RIGHT:
3753     case EL_BD_BUTTERFLY_UP:
3754     case EL_BD_BUTTERFLY_LEFT:
3755     case EL_BD_BUTTERFLY_DOWN:
3756       Feld[x][y] = EL_BD_BUTTERFLY;
3757       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3758       break;
3759
3760     case EL_BD_FIREFLY_RIGHT:
3761     case EL_BD_FIREFLY_UP:
3762     case EL_BD_FIREFLY_LEFT:
3763     case EL_BD_FIREFLY_DOWN:
3764       Feld[x][y] = EL_BD_FIREFLY;
3765       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3766       break;
3767
3768     case EL_PACMAN_RIGHT:
3769     case EL_PACMAN_UP:
3770     case EL_PACMAN_LEFT:
3771     case EL_PACMAN_DOWN:
3772       Feld[x][y] = EL_PACMAN;
3773       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3774       break;
3775
3776     case EL_YAMYAM_LEFT:
3777     case EL_YAMYAM_RIGHT:
3778     case EL_YAMYAM_UP:
3779     case EL_YAMYAM_DOWN:
3780       Feld[x][y] = EL_YAMYAM;
3781       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3782       break;
3783
3784     case EL_SP_SNIKSNAK:
3785       MovDir[x][y] = MV_UP;
3786       break;
3787
3788     case EL_SP_ELECTRON:
3789       MovDir[x][y] = MV_LEFT;
3790       break;
3791
3792     case EL_MOLE_LEFT:
3793     case EL_MOLE_RIGHT:
3794     case EL_MOLE_UP:
3795     case EL_MOLE_DOWN:
3796       Feld[x][y] = EL_MOLE;
3797       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3798       break;
3799
3800     default:
3801       if (IS_CUSTOM_ELEMENT(element))
3802       {
3803         struct ElementInfo *ei = &element_info[element];
3804         int move_direction_initial = ei->move_direction_initial;
3805         int move_pattern = ei->move_pattern;
3806
3807         if (move_direction_initial == MV_START_PREVIOUS)
3808         {
3809           if (MovDir[x][y] != MV_NONE)
3810             return;
3811
3812           move_direction_initial = MV_START_AUTOMATIC;
3813         }
3814
3815         if (move_direction_initial == MV_START_RANDOM)
3816           MovDir[x][y] = 1 << RND(4);
3817         else if (move_direction_initial & MV_ANY_DIRECTION)
3818           MovDir[x][y] = move_direction_initial;
3819         else if (move_pattern == MV_ALL_DIRECTIONS ||
3820                  move_pattern == MV_TURNING_LEFT ||
3821                  move_pattern == MV_TURNING_RIGHT ||
3822                  move_pattern == MV_TURNING_LEFT_RIGHT ||
3823                  move_pattern == MV_TURNING_RIGHT_LEFT ||
3824                  move_pattern == MV_TURNING_RANDOM)
3825           MovDir[x][y] = 1 << RND(4);
3826         else if (move_pattern == MV_HORIZONTAL)
3827           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3828         else if (move_pattern == MV_VERTICAL)
3829           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3830         else if (move_pattern & MV_ANY_DIRECTION)
3831           MovDir[x][y] = element_info[element].move_pattern;
3832         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
3833                  move_pattern == MV_ALONG_RIGHT_SIDE)
3834         {
3835           /* use random direction as default start direction */
3836           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3837             MovDir[x][y] = 1 << RND(4);
3838
3839           for (i = 0; i < NUM_DIRECTIONS; i++)
3840           {
3841             int x1 = x + xy[i][0];
3842             int y1 = y + xy[i][1];
3843
3844             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3845             {
3846               if (move_pattern == MV_ALONG_RIGHT_SIDE)
3847                 MovDir[x][y] = direction[0][i];
3848               else
3849                 MovDir[x][y] = direction[1][i];
3850
3851               break;
3852             }
3853           }
3854         }                
3855       }
3856       else
3857       {
3858         MovDir[x][y] = 1 << RND(4);
3859
3860         if (element != EL_BUG &&
3861             element != EL_SPACESHIP &&
3862             element != EL_BD_BUTTERFLY &&
3863             element != EL_BD_FIREFLY)
3864           break;
3865
3866         for (i = 0; i < NUM_DIRECTIONS; i++)
3867         {
3868           int x1 = x + xy[i][0];
3869           int y1 = y + xy[i][1];
3870
3871           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3872           {
3873             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3874             {
3875               MovDir[x][y] = direction[0][i];
3876               break;
3877             }
3878             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3879                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3880             {
3881               MovDir[x][y] = direction[1][i];
3882               break;
3883             }
3884           }
3885         }
3886       }
3887       break;
3888   }
3889
3890   GfxDir[x][y] = MovDir[x][y];
3891 }
3892
3893 void InitAmoebaNr(int x, int y)
3894 {
3895   int i;
3896   int group_nr = AmoebeNachbarNr(x, y);
3897
3898   if (group_nr == 0)
3899   {
3900     for (i = 1; i < MAX_NUM_AMOEBA; i++)
3901     {
3902       if (AmoebaCnt[i] == 0)
3903       {
3904         group_nr = i;
3905         break;
3906       }
3907     }
3908   }
3909
3910   AmoebaNr[x][y] = group_nr;
3911   AmoebaCnt[group_nr]++;
3912   AmoebaCnt2[group_nr]++;
3913 }
3914
3915 static void PlayerWins(struct PlayerInfo *player)
3916 {
3917   player->LevelSolved = TRUE;
3918   player->GameOver = TRUE;
3919
3920   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
3921                          level.native_em_level->lev->score : player->score);
3922 }
3923
3924 void GameWon()
3925 {
3926   static int time, time_final;
3927   static int score, score_final;
3928   static int game_over_delay_1 = 0;
3929   static int game_over_delay_2 = 0;
3930   int game_over_delay_value_1 = 50;
3931   int game_over_delay_value_2 = 50;
3932
3933   if (!local_player->LevelSolved_GameWon)
3934   {
3935     int i;
3936
3937     /* do not start end game actions before the player stops moving (to exit) */
3938     if (local_player->MovPos)
3939       return;
3940
3941     local_player->LevelSolved_GameWon = TRUE;
3942     local_player->LevelSolved_SaveTape = tape.recording;
3943     local_player->LevelSolved_SaveScore = !tape.playing;
3944
3945     if (tape.auto_play)         /* tape might already be stopped here */
3946       tape.auto_play_level_solved = TRUE;
3947
3948 #if 1
3949     TapeStop();
3950 #endif
3951
3952     game_over_delay_1 = game_over_delay_value_1;
3953     game_over_delay_2 = game_over_delay_value_2;
3954
3955     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3956     score = score_final = local_player->score_final;
3957
3958     if (TimeLeft > 0)
3959     {
3960       time_final = 0;
3961       score_final += TimeLeft * level.score[SC_TIME_BONUS];
3962     }
3963     else if (level.time == 0 && TimePlayed < 999)
3964     {
3965       time_final = 999;
3966       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3967     }
3968
3969     local_player->score_final = score_final;
3970
3971     if (level_editor_test_game)
3972     {
3973       time = time_final;
3974       score = score_final;
3975
3976 #if 1
3977       game_control_value[GAME_CONTROL_TIME] = time;
3978       game_control_value[GAME_CONTROL_SCORE] = score;
3979
3980       DisplayGameControlValues();
3981 #else
3982       DrawGameValue_Time(time);
3983       DrawGameValue_Score(score);
3984 #endif
3985     }
3986
3987     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3988     {
3989       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
3990       {
3991         /* close exit door after last player */
3992         if ((AllPlayersGone &&
3993              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3994               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3995               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3996             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3997             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3998         {
3999           int element = Feld[ExitX][ExitY];
4000
4001 #if 0
4002           if (element == EL_EM_EXIT_OPEN ||
4003               element == EL_EM_STEEL_EXIT_OPEN)
4004           {
4005             Bang(ExitX, ExitY);
4006           }
4007           else
4008 #endif
4009           {
4010             Feld[ExitX][ExitY] =
4011               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4012                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4013                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4014                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4015                EL_EM_STEEL_EXIT_CLOSING);
4016
4017             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4018           }
4019         }
4020
4021         /* player disappears */
4022         DrawLevelField(ExitX, ExitY);
4023       }
4024
4025       for (i = 0; i < MAX_PLAYERS; i++)
4026       {
4027         struct PlayerInfo *player = &stored_player[i];
4028
4029         if (player->present)
4030         {
4031           RemovePlayer(player);
4032
4033           /* player disappears */
4034           DrawLevelField(player->jx, player->jy);
4035         }
4036       }
4037     }
4038
4039     PlaySound(SND_GAME_WINNING);
4040   }
4041
4042   if (game_over_delay_1 > 0)
4043   {
4044     game_over_delay_1--;
4045
4046     return;
4047   }
4048
4049   if (time != time_final)
4050   {
4051     int time_to_go = ABS(time_final - time);
4052     int time_count_dir = (time < time_final ? +1 : -1);
4053     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4054
4055     time  += time_count_steps * time_count_dir;
4056     score += time_count_steps * level.score[SC_TIME_BONUS];
4057
4058 #if 1
4059     game_control_value[GAME_CONTROL_TIME] = time;
4060     game_control_value[GAME_CONTROL_SCORE] = score;
4061
4062     DisplayGameControlValues();
4063 #else
4064     DrawGameValue_Time(time);
4065     DrawGameValue_Score(score);
4066 #endif
4067
4068     if (time == time_final)
4069       StopSound(SND_GAME_LEVELTIME_BONUS);
4070     else if (setup.sound_loops)
4071       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4072     else
4073       PlaySound(SND_GAME_LEVELTIME_BONUS);
4074
4075     return;
4076   }
4077
4078   local_player->LevelSolved_PanelOff = TRUE;
4079
4080   if (game_over_delay_2 > 0)
4081   {
4082     game_over_delay_2--;
4083
4084     return;
4085   }
4086
4087 #if 1
4088   GameEnd();
4089 #endif
4090 }
4091
4092 void GameEnd()
4093 {
4094   int hi_pos;
4095   boolean raise_level = FALSE;
4096
4097   local_player->LevelSolved_GameEnd = TRUE;
4098
4099   CloseDoor(DOOR_CLOSE_1);
4100
4101   if (local_player->LevelSolved_SaveTape)
4102   {
4103 #if 0
4104     TapeStop();
4105 #endif
4106
4107 #if 1
4108     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4109 #else
4110     SaveTape(tape.level_nr);            /* ask to save tape */
4111 #endif
4112   }
4113
4114   if (level_editor_test_game)
4115   {
4116     game_status = GAME_MODE_MAIN;
4117
4118 #if 1
4119     DrawAndFadeInMainMenu(REDRAW_FIELD);
4120 #else
4121     DrawMainMenu();
4122 #endif
4123
4124     return;
4125   }
4126
4127   if (!local_player->LevelSolved_SaveScore)
4128   {
4129 #if 1
4130     FadeOut(REDRAW_FIELD);
4131 #endif
4132
4133     game_status = GAME_MODE_MAIN;
4134
4135     DrawAndFadeInMainMenu(REDRAW_FIELD);
4136
4137     return;
4138   }
4139
4140   if (level_nr == leveldir_current->handicap_level)
4141   {
4142     leveldir_current->handicap_level++;
4143     SaveLevelSetup_SeriesInfo();
4144   }
4145
4146   if (level_nr < leveldir_current->last_level)
4147     raise_level = TRUE;                 /* advance to next level */
4148
4149   if ((hi_pos = NewHiScore()) >= 0) 
4150   {
4151     game_status = GAME_MODE_SCORES;
4152
4153     DrawHallOfFame(hi_pos);
4154
4155     if (raise_level)
4156     {
4157       level_nr++;
4158       TapeErase();
4159     }
4160   }
4161   else
4162   {
4163 #if 1
4164     FadeOut(REDRAW_FIELD);
4165 #endif
4166
4167     game_status = GAME_MODE_MAIN;
4168
4169     if (raise_level)
4170     {
4171       level_nr++;
4172       TapeErase();
4173     }
4174
4175     DrawAndFadeInMainMenu(REDRAW_FIELD);
4176   }
4177 }
4178
4179 int NewHiScore()
4180 {
4181   int k, l;
4182   int position = -1;
4183
4184   LoadScore(level_nr);
4185
4186   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4187       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4188     return -1;
4189
4190   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4191   {
4192     if (local_player->score_final > highscore[k].Score)
4193     {
4194       /* player has made it to the hall of fame */
4195
4196       if (k < MAX_SCORE_ENTRIES - 1)
4197       {
4198         int m = MAX_SCORE_ENTRIES - 1;
4199
4200 #ifdef ONE_PER_NAME
4201         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4202           if (strEqual(setup.player_name, highscore[l].Name))
4203             m = l;
4204         if (m == k)     /* player's new highscore overwrites his old one */
4205           goto put_into_list;
4206 #endif
4207
4208         for (l = m; l > k; l--)
4209         {
4210           strcpy(highscore[l].Name, highscore[l - 1].Name);
4211           highscore[l].Score = highscore[l - 1].Score;
4212         }
4213       }
4214
4215 #ifdef ONE_PER_NAME
4216       put_into_list:
4217 #endif
4218       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4219       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4220       highscore[k].Score = local_player->score_final; 
4221       position = k;
4222       break;
4223     }
4224
4225 #ifdef ONE_PER_NAME
4226     else if (!strncmp(setup.player_name, highscore[k].Name,
4227                       MAX_PLAYER_NAME_LEN))
4228       break;    /* player already there with a higher score */
4229 #endif
4230
4231   }
4232
4233   if (position >= 0) 
4234     SaveScore(level_nr);
4235
4236   return position;
4237 }
4238
4239 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4240 {
4241   int element = Feld[x][y];
4242   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4243   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4244   int horiz_move = (dx != 0);
4245   int sign = (horiz_move ? dx : dy);
4246   int step = sign * element_info[element].move_stepsize;
4247
4248   /* special values for move stepsize for spring and things on conveyor belt */
4249   if (horiz_move)
4250   {
4251     if (CAN_FALL(element) &&
4252         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4253       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4254     else if (element == EL_SPRING)
4255       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4256   }
4257
4258   return step;
4259 }
4260
4261 inline static int getElementMoveStepsize(int x, int y)
4262 {
4263   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4264 }
4265
4266 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4267 {
4268   if (player->GfxAction != action || player->GfxDir != dir)
4269   {
4270 #if 0
4271     printf("Player frame reset! (%d => %d, %d => %d)\n",
4272            player->GfxAction, action, player->GfxDir, dir);
4273 #endif
4274
4275     player->GfxAction = action;
4276     player->GfxDir = dir;
4277     player->Frame = 0;
4278     player->StepFrame = 0;
4279   }
4280 }
4281
4282 #if USE_GFX_RESET_GFX_ANIMATION
4283 static void ResetGfxFrame(int x, int y, boolean redraw)
4284 {
4285   int element = Feld[x][y];
4286   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4287   int last_gfx_frame = GfxFrame[x][y];
4288
4289   if (graphic_info[graphic].anim_global_sync)
4290     GfxFrame[x][y] = FrameCounter;
4291   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4292     GfxFrame[x][y] = CustomValue[x][y];
4293   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4294     GfxFrame[x][y] = element_info[element].collect_score;
4295   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4296     GfxFrame[x][y] = ChangeDelay[x][y];
4297
4298   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4299     DrawLevelGraphicAnimation(x, y, graphic);
4300 }
4301 #endif
4302
4303 static void ResetGfxAnimation(int x, int y)
4304 {
4305   GfxAction[x][y] = ACTION_DEFAULT;
4306   GfxDir[x][y] = MovDir[x][y];
4307   GfxFrame[x][y] = 0;
4308
4309 #if USE_GFX_RESET_GFX_ANIMATION
4310   ResetGfxFrame(x, y, FALSE);
4311 #endif
4312 }
4313
4314 static void ResetRandomAnimationValue(int x, int y)
4315 {
4316   GfxRandom[x][y] = INIT_GFX_RANDOM();
4317 }
4318
4319 void InitMovingField(int x, int y, int direction)
4320 {
4321   int element = Feld[x][y];
4322   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4323   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4324   int newx = x + dx;
4325   int newy = y + dy;
4326   boolean is_moving_before, is_moving_after;
4327 #if 0
4328   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4329 #endif
4330
4331   /* check if element was/is moving or being moved before/after mode change */
4332 #if 1
4333 #if 1
4334   is_moving_before = (WasJustMoving[x][y] != 0);
4335 #else
4336   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4337   is_moving_before = WasJustMoving[x][y];
4338 #endif
4339 #else
4340   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4341 #endif
4342   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4343
4344   /* reset animation only for moving elements which change direction of moving
4345      or which just started or stopped moving
4346      (else CEs with property "can move" / "not moving" are reset each frame) */
4347 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4348 #if 1
4349   if (is_moving_before != is_moving_after ||
4350       direction != MovDir[x][y])
4351     ResetGfxAnimation(x, y);
4352 #else
4353   if ((is_moving_before || is_moving_after) && !continues_moving)
4354     ResetGfxAnimation(x, y);
4355 #endif
4356 #else
4357   if (!continues_moving)
4358     ResetGfxAnimation(x, y);
4359 #endif
4360
4361   MovDir[x][y] = direction;
4362   GfxDir[x][y] = direction;
4363
4364 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4365   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4366                      direction == MV_DOWN && CAN_FALL(element) ?
4367                      ACTION_FALLING : ACTION_MOVING);
4368 #else
4369   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4370                      ACTION_FALLING : ACTION_MOVING);
4371 #endif
4372
4373   /* this is needed for CEs with property "can move" / "not moving" */
4374
4375   if (is_moving_after)
4376   {
4377     if (Feld[newx][newy] == EL_EMPTY)
4378       Feld[newx][newy] = EL_BLOCKED;
4379
4380     MovDir[newx][newy] = MovDir[x][y];
4381
4382 #if USE_NEW_CUSTOM_VALUE
4383     CustomValue[newx][newy] = CustomValue[x][y];
4384 #endif
4385
4386     GfxFrame[newx][newy] = GfxFrame[x][y];
4387     GfxRandom[newx][newy] = GfxRandom[x][y];
4388     GfxAction[newx][newy] = GfxAction[x][y];
4389     GfxDir[newx][newy] = GfxDir[x][y];
4390   }
4391 }
4392
4393 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4394 {
4395   int direction = MovDir[x][y];
4396   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4397   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4398
4399   *goes_to_x = newx;
4400   *goes_to_y = newy;
4401 }
4402
4403 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4404 {
4405   int oldx = x, oldy = y;
4406   int direction = MovDir[x][y];
4407
4408   if (direction == MV_LEFT)
4409     oldx++;
4410   else if (direction == MV_RIGHT)
4411     oldx--;
4412   else if (direction == MV_UP)
4413     oldy++;
4414   else if (direction == MV_DOWN)
4415     oldy--;
4416
4417   *comes_from_x = oldx;
4418   *comes_from_y = oldy;
4419 }
4420
4421 int MovingOrBlocked2Element(int x, int y)
4422 {
4423   int element = Feld[x][y];
4424
4425   if (element == EL_BLOCKED)
4426   {
4427     int oldx, oldy;
4428
4429     Blocked2Moving(x, y, &oldx, &oldy);
4430     return Feld[oldx][oldy];
4431   }
4432   else
4433     return element;
4434 }
4435
4436 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4437 {
4438   /* like MovingOrBlocked2Element(), but if element is moving
4439      and (x,y) is the field the moving element is just leaving,
4440      return EL_BLOCKED instead of the element value */
4441   int element = Feld[x][y];
4442
4443   if (IS_MOVING(x, y))
4444   {
4445     if (element == EL_BLOCKED)
4446     {
4447       int oldx, oldy;
4448
4449       Blocked2Moving(x, y, &oldx, &oldy);
4450       return Feld[oldx][oldy];
4451     }
4452     else
4453       return EL_BLOCKED;
4454   }
4455   else
4456     return element;
4457 }
4458
4459 static void RemoveField(int x, int y)
4460 {
4461   Feld[x][y] = EL_EMPTY;
4462
4463   MovPos[x][y] = 0;
4464   MovDir[x][y] = 0;
4465   MovDelay[x][y] = 0;
4466
4467 #if USE_NEW_CUSTOM_VALUE
4468   CustomValue[x][y] = 0;
4469 #endif
4470
4471   AmoebaNr[x][y] = 0;
4472   ChangeDelay[x][y] = 0;
4473   ChangePage[x][y] = -1;
4474   Pushed[x][y] = FALSE;
4475
4476 #if 0
4477   ExplodeField[x][y] = EX_TYPE_NONE;
4478 #endif
4479
4480   GfxElement[x][y] = EL_UNDEFINED;
4481   GfxAction[x][y] = ACTION_DEFAULT;
4482   GfxDir[x][y] = MV_NONE;
4483 }
4484
4485 void RemoveMovingField(int x, int y)
4486 {
4487   int oldx = x, oldy = y, newx = x, newy = y;
4488   int element = Feld[x][y];
4489   int next_element = EL_UNDEFINED;
4490
4491   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4492     return;
4493
4494   if (IS_MOVING(x, y))
4495   {
4496     Moving2Blocked(x, y, &newx, &newy);
4497
4498     if (Feld[newx][newy] != EL_BLOCKED)
4499     {
4500       /* element is moving, but target field is not free (blocked), but
4501          already occupied by something different (example: acid pool);
4502          in this case, only remove the moving field, but not the target */
4503
4504       RemoveField(oldx, oldy);
4505
4506       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4507
4508       DrawLevelField(oldx, oldy);
4509
4510       return;
4511     }
4512   }
4513   else if (element == EL_BLOCKED)
4514   {
4515     Blocked2Moving(x, y, &oldx, &oldy);
4516     if (!IS_MOVING(oldx, oldy))
4517       return;
4518   }
4519
4520   if (element == EL_BLOCKED &&
4521       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4522        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4523        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4524        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4525        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4526        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4527     next_element = get_next_element(Feld[oldx][oldy]);
4528
4529   RemoveField(oldx, oldy);
4530   RemoveField(newx, newy);
4531
4532   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4533
4534   if (next_element != EL_UNDEFINED)
4535     Feld[oldx][oldy] = next_element;
4536
4537   DrawLevelField(oldx, oldy);
4538   DrawLevelField(newx, newy);
4539 }
4540
4541 void DrawDynamite(int x, int y)
4542 {
4543   int sx = SCREENX(x), sy = SCREENY(y);
4544   int graphic = el2img(Feld[x][y]);
4545   int frame;
4546
4547   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4548     return;
4549
4550   if (IS_WALKABLE_INSIDE(Back[x][y]))
4551     return;
4552
4553   if (Back[x][y])
4554     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4555   else if (Store[x][y])
4556     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4557
4558   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4559
4560   if (Back[x][y] || Store[x][y])
4561     DrawGraphicThruMask(sx, sy, graphic, frame);
4562   else
4563     DrawGraphic(sx, sy, graphic, frame);
4564 }
4565
4566 void CheckDynamite(int x, int y)
4567 {
4568   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4569   {
4570     MovDelay[x][y]--;
4571
4572     if (MovDelay[x][y] != 0)
4573     {
4574       DrawDynamite(x, y);
4575       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4576
4577       return;
4578     }
4579   }
4580
4581   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4582
4583   Bang(x, y);
4584 }
4585
4586 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4587 {
4588   boolean num_checked_players = 0;
4589   int i;
4590
4591   for (i = 0; i < MAX_PLAYERS; i++)
4592   {
4593     if (stored_player[i].active)
4594     {
4595       int sx = stored_player[i].jx;
4596       int sy = stored_player[i].jy;
4597
4598       if (num_checked_players == 0)
4599       {
4600         *sx1 = *sx2 = sx;
4601         *sy1 = *sy2 = sy;
4602       }
4603       else
4604       {
4605         *sx1 = MIN(*sx1, sx);
4606         *sy1 = MIN(*sy1, sy);
4607         *sx2 = MAX(*sx2, sx);
4608         *sy2 = MAX(*sy2, sy);
4609       }
4610
4611       num_checked_players++;
4612     }
4613   }
4614 }
4615
4616 static boolean checkIfAllPlayersFitToScreen_RND()
4617 {
4618   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4619
4620   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4621
4622   return (sx2 - sx1 < SCR_FIELDX &&
4623           sy2 - sy1 < SCR_FIELDY);
4624 }
4625
4626 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4627 {
4628   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4629
4630   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4631
4632   *sx = (sx1 + sx2) / 2;
4633   *sy = (sy1 + sy2) / 2;
4634 }
4635
4636 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4637                         boolean center_screen, boolean quick_relocation)
4638 {
4639   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4640   boolean no_delay = (tape.warp_forward);
4641   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4642   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4643
4644   if (quick_relocation)
4645   {
4646     int offset = game.scroll_delay_value;
4647
4648     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4649     {
4650       if (!level.shifted_relocation || center_screen)
4651       {
4652         /* quick relocation (without scrolling), with centering of screen */
4653
4654         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4655                     x > SBX_Right + MIDPOSX ? SBX_Right :
4656                     x - MIDPOSX);
4657
4658         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4659                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4660                     y - MIDPOSY);
4661       }
4662       else
4663       {
4664         /* quick relocation (without scrolling), but do not center screen */
4665
4666         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4667                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4668                                old_x - MIDPOSX);
4669
4670         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4671                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4672                                old_y - MIDPOSY);
4673
4674         int offset_x = x + (scroll_x - center_scroll_x);
4675         int offset_y = y + (scroll_y - center_scroll_y);
4676
4677         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4678                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4679                     offset_x - MIDPOSX);
4680
4681         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4682                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4683                     offset_y - MIDPOSY);
4684       }
4685     }
4686     else
4687     {
4688       /* quick relocation (without scrolling), inside visible screen area */
4689
4690       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
4691           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4692         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4693
4694       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
4695           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4696         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4697
4698       /* don't scroll over playfield boundaries */
4699       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4700         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4701
4702       /* don't scroll over playfield boundaries */
4703       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4704         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4705     }
4706
4707     RedrawPlayfield(TRUE, 0,0,0,0);
4708   }
4709   else
4710   {
4711 #if 1
4712     int scroll_xx, scroll_yy;
4713
4714     if (!level.shifted_relocation || center_screen)
4715     {
4716       /* visible relocation (with scrolling), with centering of screen */
4717
4718       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4719                    x > SBX_Right + MIDPOSX ? SBX_Right :
4720                    x - MIDPOSX);
4721
4722       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4723                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
4724                    y - MIDPOSY);
4725     }
4726     else
4727     {
4728       /* visible relocation (with scrolling), but do not center screen */
4729
4730       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4731                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
4732                              old_x - MIDPOSX);
4733
4734       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4735                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4736                              old_y - MIDPOSY);
4737
4738       int offset_x = x + (scroll_x - center_scroll_x);
4739       int offset_y = y + (scroll_y - center_scroll_y);
4740
4741       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4742                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4743                    offset_x - MIDPOSX);
4744
4745       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4746                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4747                    offset_y - MIDPOSY);
4748     }
4749
4750 #else
4751
4752     /* visible relocation (with scrolling), with centering of screen */
4753
4754     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4755                      x > SBX_Right + MIDPOSX ? SBX_Right :
4756                      x - MIDPOSX);
4757
4758     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4759                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
4760                      y - MIDPOSY);
4761 #endif
4762
4763     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
4764
4765     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4766     {
4767       int dx = 0, dy = 0;
4768       int fx = FX, fy = FY;
4769
4770       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4771       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4772
4773       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
4774         break;
4775
4776       scroll_x -= dx;
4777       scroll_y -= dy;
4778
4779       fx += dx * TILEX / 2;
4780       fy += dy * TILEY / 2;
4781
4782       ScrollLevel(dx, dy);
4783       DrawAllPlayers();
4784
4785       /* scroll in two steps of half tile size to make things smoother */
4786       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4787       FlushDisplay();
4788       Delay(wait_delay_value);
4789
4790       /* scroll second step to align at full tile size */
4791       BackToFront();
4792       Delay(wait_delay_value);
4793     }
4794
4795     DrawAllPlayers();
4796     BackToFront();
4797     Delay(wait_delay_value);
4798   }
4799 }
4800
4801 void RelocatePlayer(int jx, int jy, int el_player_raw)
4802 {
4803   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4804   int player_nr = GET_PLAYER_NR(el_player);
4805   struct PlayerInfo *player = &stored_player[player_nr];
4806   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4807   boolean no_delay = (tape.warp_forward);
4808   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4809   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4810   int old_jx = player->jx;
4811   int old_jy = player->jy;
4812   int old_element = Feld[old_jx][old_jy];
4813   int element = Feld[jx][jy];
4814   boolean player_relocated = (old_jx != jx || old_jy != jy);
4815
4816   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4817   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
4818   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4819   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
4820   int leave_side_horiz = move_dir_horiz;
4821   int leave_side_vert  = move_dir_vert;
4822   int enter_side = enter_side_horiz | enter_side_vert;
4823   int leave_side = leave_side_horiz | leave_side_vert;
4824
4825   if (player->GameOver)         /* do not reanimate dead player */
4826     return;
4827
4828   if (!player_relocated)        /* no need to relocate the player */
4829     return;
4830
4831   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
4832   {
4833     RemoveField(jx, jy);        /* temporarily remove newly placed player */
4834     DrawLevelField(jx, jy);
4835   }
4836
4837   if (player->present)
4838   {
4839     while (player->MovPos)
4840     {
4841       ScrollPlayer(player, SCROLL_GO_ON);
4842       ScrollScreen(NULL, SCROLL_GO_ON);
4843
4844       AdvanceFrameAndPlayerCounters(player->index_nr);
4845
4846       DrawPlayer(player);
4847
4848       BackToFront();
4849       Delay(wait_delay_value);
4850     }
4851
4852     DrawPlayer(player);         /* needed here only to cleanup last field */
4853     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
4854
4855     player->is_moving = FALSE;
4856   }
4857
4858   if (IS_CUSTOM_ELEMENT(old_element))
4859     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4860                                CE_LEFT_BY_PLAYER,
4861                                player->index_bit, leave_side);
4862
4863   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4864                                       CE_PLAYER_LEAVES_X,
4865                                       player->index_bit, leave_side);
4866
4867   Feld[jx][jy] = el_player;
4868   InitPlayerField(jx, jy, el_player, TRUE);
4869
4870   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4871   {
4872     Feld[jx][jy] = element;
4873     InitField(jx, jy, FALSE);
4874   }
4875
4876   /* only visually relocate centered player */
4877   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4878                      FALSE, level.instant_relocation);
4879
4880   TestIfPlayerTouchesBadThing(jx, jy);
4881   TestIfPlayerTouchesCustomElement(jx, jy);
4882
4883   if (IS_CUSTOM_ELEMENT(element))
4884     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4885                                player->index_bit, enter_side);
4886
4887   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4888                                       player->index_bit, enter_side);
4889 }
4890
4891 void Explode(int ex, int ey, int phase, int mode)
4892 {
4893   int x, y;
4894   int last_phase;
4895   int border_element;
4896
4897   /* !!! eliminate this variable !!! */
4898   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4899
4900   if (game.explosions_delayed)
4901   {
4902     ExplodeField[ex][ey] = mode;
4903     return;
4904   }
4905
4906   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
4907   {
4908     int center_element = Feld[ex][ey];
4909     int artwork_element, explosion_element;     /* set these values later */
4910
4911 #if 0
4912     /* --- This is only really needed (and now handled) in "Impact()". --- */
4913     /* do not explode moving elements that left the explode field in time */
4914     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4915         center_element == EL_EMPTY &&
4916         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4917       return;
4918 #endif
4919
4920 #if 0
4921     /* !!! at this place, the center element may be EL_BLOCKED !!! */
4922     if (mode == EX_TYPE_NORMAL ||
4923         mode == EX_TYPE_CENTER ||
4924         mode == EX_TYPE_CROSS)
4925       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4926 #endif
4927
4928     /* remove things displayed in background while burning dynamite */
4929     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4930       Back[ex][ey] = 0;
4931
4932     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4933     {
4934       /* put moving element to center field (and let it explode there) */
4935       center_element = MovingOrBlocked2Element(ex, ey);
4936       RemoveMovingField(ex, ey);
4937       Feld[ex][ey] = center_element;
4938     }
4939
4940     /* now "center_element" is finally determined -- set related values now */
4941     artwork_element = center_element;           /* for custom player artwork */
4942     explosion_element = center_element;         /* for custom player artwork */
4943
4944     if (IS_PLAYER(ex, ey))
4945     {
4946       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4947
4948       artwork_element = stored_player[player_nr].artwork_element;
4949
4950       if (level.use_explosion_element[player_nr])
4951       {
4952         explosion_element = level.explosion_element[player_nr];
4953         artwork_element = explosion_element;
4954       }
4955     }
4956
4957 #if 1
4958     if (mode == EX_TYPE_NORMAL ||
4959         mode == EX_TYPE_CENTER ||
4960         mode == EX_TYPE_CROSS)
4961       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4962 #endif
4963
4964     last_phase = element_info[explosion_element].explosion_delay + 1;
4965
4966     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4967     {
4968       int xx = x - ex + 1;
4969       int yy = y - ey + 1;
4970       int element;
4971
4972       if (!IN_LEV_FIELD(x, y) ||
4973           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4974           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
4975         continue;
4976
4977       element = Feld[x][y];
4978
4979       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4980       {
4981         element = MovingOrBlocked2Element(x, y);
4982
4983         if (!IS_EXPLOSION_PROOF(element))
4984           RemoveMovingField(x, y);
4985       }
4986
4987       /* indestructible elements can only explode in center (but not flames) */
4988       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4989                                            mode == EX_TYPE_BORDER)) ||
4990           element == EL_FLAMES)
4991         continue;
4992
4993       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4994          behaviour, for example when touching a yamyam that explodes to rocks
4995          with active deadly shield, a rock is created under the player !!! */
4996       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4997 #if 0
4998       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4999           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5000            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5001 #else
5002       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5003 #endif
5004       {
5005         if (IS_ACTIVE_BOMB(element))
5006         {
5007           /* re-activate things under the bomb like gate or penguin */
5008           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5009           Back[x][y] = 0;
5010         }
5011
5012         continue;
5013       }
5014
5015       /* save walkable background elements while explosion on same tile */
5016       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5017           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5018         Back[x][y] = element;
5019
5020       /* ignite explodable elements reached by other explosion */
5021       if (element == EL_EXPLOSION)
5022         element = Store2[x][y];
5023
5024       if (AmoebaNr[x][y] &&
5025           (element == EL_AMOEBA_FULL ||
5026            element == EL_BD_AMOEBA ||
5027            element == EL_AMOEBA_GROWING))
5028       {
5029         AmoebaCnt[AmoebaNr[x][y]]--;
5030         AmoebaCnt2[AmoebaNr[x][y]]--;
5031       }
5032
5033       RemoveField(x, y);
5034
5035       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5036       {
5037         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5038
5039         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5040
5041         if (PLAYERINFO(ex, ey)->use_murphy)
5042           Store[x][y] = EL_EMPTY;
5043       }
5044
5045       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5046          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5047       else if (ELEM_IS_PLAYER(center_element))
5048         Store[x][y] = EL_EMPTY;
5049       else if (center_element == EL_YAMYAM)
5050         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5051       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5052         Store[x][y] = element_info[center_element].content.e[xx][yy];
5053 #if 1
5054       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5055          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5056          otherwise) -- FIX THIS !!! */
5057       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5058         Store[x][y] = element_info[element].content.e[1][1];
5059 #else
5060       else if (!CAN_EXPLODE(element))
5061         Store[x][y] = element_info[element].content.e[1][1];
5062 #endif
5063       else
5064         Store[x][y] = EL_EMPTY;
5065
5066       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5067           center_element == EL_AMOEBA_TO_DIAMOND)
5068         Store2[x][y] = element;
5069
5070       Feld[x][y] = EL_EXPLOSION;
5071       GfxElement[x][y] = artwork_element;
5072
5073       ExplodePhase[x][y] = 1;
5074       ExplodeDelay[x][y] = last_phase;
5075
5076       Stop[x][y] = TRUE;
5077     }
5078
5079     if (center_element == EL_YAMYAM)
5080       game.yamyam_content_nr =
5081         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5082
5083     return;
5084   }
5085
5086   if (Stop[ex][ey])
5087     return;
5088
5089   x = ex;
5090   y = ey;
5091
5092   if (phase == 1)
5093     GfxFrame[x][y] = 0;         /* restart explosion animation */
5094
5095   last_phase = ExplodeDelay[x][y];
5096
5097   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5098
5099 #ifdef DEBUG
5100
5101   /* activate this even in non-DEBUG version until cause for crash in
5102      getGraphicAnimationFrame() (see below) is found and eliminated */
5103
5104 #endif
5105 #if 1
5106
5107 #if 1
5108   /* this can happen if the player leaves an explosion just in time */
5109   if (GfxElement[x][y] == EL_UNDEFINED)
5110     GfxElement[x][y] = EL_EMPTY;
5111 #else
5112   if (GfxElement[x][y] == EL_UNDEFINED)
5113   {
5114     printf("\n\n");
5115     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5116     printf("Explode(): This should never happen!\n");
5117     printf("\n\n");
5118
5119     GfxElement[x][y] = EL_EMPTY;
5120   }
5121 #endif
5122
5123 #endif
5124
5125   border_element = Store2[x][y];
5126   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5127     border_element = StorePlayer[x][y];
5128
5129   if (phase == element_info[border_element].ignition_delay ||
5130       phase == last_phase)
5131   {
5132     boolean border_explosion = FALSE;
5133
5134     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5135         !PLAYER_EXPLOSION_PROTECTED(x, y))
5136     {
5137       KillPlayerUnlessExplosionProtected(x, y);
5138       border_explosion = TRUE;
5139     }
5140     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5141     {
5142       Feld[x][y] = Store2[x][y];
5143       Store2[x][y] = 0;
5144       Bang(x, y);
5145       border_explosion = TRUE;
5146     }
5147     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5148     {
5149       AmoebeUmwandeln(x, y);
5150       Store2[x][y] = 0;
5151       border_explosion = TRUE;
5152     }
5153
5154     /* if an element just explodes due to another explosion (chain-reaction),
5155        do not immediately end the new explosion when it was the last frame of
5156        the explosion (as it would be done in the following "if"-statement!) */
5157     if (border_explosion && phase == last_phase)
5158       return;
5159   }
5160
5161   if (phase == last_phase)
5162   {
5163     int element;
5164
5165     element = Feld[x][y] = Store[x][y];
5166     Store[x][y] = Store2[x][y] = 0;
5167     GfxElement[x][y] = EL_UNDEFINED;
5168
5169     /* player can escape from explosions and might therefore be still alive */
5170     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5171         element <= EL_PLAYER_IS_EXPLODING_4)
5172     {
5173       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5174       int explosion_element = EL_PLAYER_1 + player_nr;
5175       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5176       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5177
5178       if (level.use_explosion_element[player_nr])
5179         explosion_element = level.explosion_element[player_nr];
5180
5181       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5182                     element_info[explosion_element].content.e[xx][yy]);
5183     }
5184
5185     /* restore probably existing indestructible background element */
5186     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5187       element = Feld[x][y] = Back[x][y];
5188     Back[x][y] = 0;
5189
5190     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5191     GfxDir[x][y] = MV_NONE;
5192     ChangeDelay[x][y] = 0;
5193     ChangePage[x][y] = -1;
5194
5195 #if USE_NEW_CUSTOM_VALUE
5196     CustomValue[x][y] = 0;
5197 #endif
5198
5199     InitField_WithBug2(x, y, FALSE);
5200
5201     DrawLevelField(x, y);
5202
5203     TestIfElementTouchesCustomElement(x, y);
5204
5205     if (GFX_CRUMBLED(element))
5206       DrawLevelFieldCrumbledSandNeighbours(x, y);
5207
5208     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5209       StorePlayer[x][y] = 0;
5210
5211     if (ELEM_IS_PLAYER(element))
5212       RelocatePlayer(x, y, element);
5213   }
5214   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5215   {
5216     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5217     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5218
5219     if (phase == delay)
5220       DrawLevelFieldCrumbledSand(x, y);
5221
5222     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5223     {
5224       DrawLevelElement(x, y, Back[x][y]);
5225       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5226     }
5227     else if (IS_WALKABLE_UNDER(Back[x][y]))
5228     {
5229       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5230       DrawLevelElementThruMask(x, y, Back[x][y]);
5231     }
5232     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5233       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5234   }
5235 }
5236
5237 void DynaExplode(int ex, int ey)
5238 {
5239   int i, j;
5240   int dynabomb_element = Feld[ex][ey];
5241   int dynabomb_size = 1;
5242   boolean dynabomb_xl = FALSE;
5243   struct PlayerInfo *player;
5244   static int xy[4][2] =
5245   {
5246     { 0, -1 },
5247     { -1, 0 },
5248     { +1, 0 },
5249     { 0, +1 }
5250   };
5251
5252   if (IS_ACTIVE_BOMB(dynabomb_element))
5253   {
5254     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5255     dynabomb_size = player->dynabomb_size;
5256     dynabomb_xl = player->dynabomb_xl;
5257     player->dynabombs_left++;
5258   }
5259
5260   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5261
5262   for (i = 0; i < NUM_DIRECTIONS; i++)
5263   {
5264     for (j = 1; j <= dynabomb_size; j++)
5265     {
5266       int x = ex + j * xy[i][0];
5267       int y = ey + j * xy[i][1];
5268       int element;
5269
5270       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5271         break;
5272
5273       element = Feld[x][y];
5274
5275       /* do not restart explosions of fields with active bombs */
5276       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5277         continue;
5278
5279       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5280
5281       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5282           !IS_DIGGABLE(element) && !dynabomb_xl)
5283         break;
5284     }
5285   }
5286 }
5287
5288 void Bang(int x, int y)
5289 {
5290   int element = MovingOrBlocked2Element(x, y);
5291   int explosion_type = EX_TYPE_NORMAL;
5292
5293   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5294   {
5295     struct PlayerInfo *player = PLAYERINFO(x, y);
5296
5297     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5298                             player->element_nr);
5299
5300     if (level.use_explosion_element[player->index_nr])
5301     {
5302       int explosion_element = level.explosion_element[player->index_nr];
5303
5304       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5305         explosion_type = EX_TYPE_CROSS;
5306       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5307         explosion_type = EX_TYPE_CENTER;
5308     }
5309   }
5310
5311   switch (element)
5312   {
5313     case EL_BUG:
5314     case EL_SPACESHIP:
5315     case EL_BD_BUTTERFLY:
5316     case EL_BD_FIREFLY:
5317     case EL_YAMYAM:
5318     case EL_DARK_YAMYAM:
5319     case EL_ROBOT:
5320     case EL_PACMAN:
5321     case EL_MOLE:
5322       RaiseScoreElement(element);
5323       break;
5324
5325     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5326     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5327     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5328     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5329     case EL_DYNABOMB_INCREASE_NUMBER:
5330     case EL_DYNABOMB_INCREASE_SIZE:
5331     case EL_DYNABOMB_INCREASE_POWER:
5332       explosion_type = EX_TYPE_DYNA;
5333       break;
5334
5335     case EL_DC_LANDMINE:
5336 #if 0
5337     case EL_EM_EXIT_OPEN:
5338     case EL_EM_STEEL_EXIT_OPEN:
5339 #endif
5340       explosion_type = EX_TYPE_CENTER;
5341       break;
5342
5343     case EL_PENGUIN:
5344     case EL_LAMP:
5345     case EL_LAMP_ACTIVE:
5346     case EL_AMOEBA_TO_DIAMOND:
5347       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5348         explosion_type = EX_TYPE_CENTER;
5349       break;
5350
5351     default:
5352       if (element_info[element].explosion_type == EXPLODES_CROSS)
5353         explosion_type = EX_TYPE_CROSS;
5354       else if (element_info[element].explosion_type == EXPLODES_1X1)
5355         explosion_type = EX_TYPE_CENTER;
5356       break;
5357   }
5358
5359   if (explosion_type == EX_TYPE_DYNA)
5360     DynaExplode(x, y);
5361   else
5362     Explode(x, y, EX_PHASE_START, explosion_type);
5363
5364   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5365 }
5366
5367 void SplashAcid(int x, int y)
5368 {
5369   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5370       (!IN_LEV_FIELD(x - 1, y - 2) ||
5371        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5372     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5373
5374   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5375       (!IN_LEV_FIELD(x + 1, y - 2) ||
5376        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5377     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5378
5379   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5380 }
5381
5382 static void InitBeltMovement()
5383 {
5384   static int belt_base_element[4] =
5385   {
5386     EL_CONVEYOR_BELT_1_LEFT,
5387     EL_CONVEYOR_BELT_2_LEFT,
5388     EL_CONVEYOR_BELT_3_LEFT,
5389     EL_CONVEYOR_BELT_4_LEFT
5390   };
5391   static int belt_base_active_element[4] =
5392   {
5393     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5394     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5395     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5396     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5397   };
5398
5399   int x, y, i, j;
5400
5401   /* set frame order for belt animation graphic according to belt direction */
5402   for (i = 0; i < NUM_BELTS; i++)
5403   {
5404     int belt_nr = i;
5405
5406     for (j = 0; j < NUM_BELT_PARTS; j++)
5407     {
5408       int element = belt_base_active_element[belt_nr] + j;
5409       int graphic = el2img(element);
5410
5411       if (game.belt_dir[i] == MV_LEFT)
5412         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5413       else
5414         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
5415     }
5416   }
5417
5418   SCAN_PLAYFIELD(x, y)
5419   {
5420     int element = Feld[x][y];
5421
5422     for (i = 0; i < NUM_BELTS; i++)
5423     {
5424       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5425       {
5426         int e_belt_nr = getBeltNrFromBeltElement(element);
5427         int belt_nr = i;
5428
5429         if (e_belt_nr == belt_nr)
5430         {
5431           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5432
5433           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5434         }
5435       }
5436     }
5437   }
5438 }
5439
5440 static void ToggleBeltSwitch(int x, int y)
5441 {
5442   static int belt_base_element[4] =
5443   {
5444     EL_CONVEYOR_BELT_1_LEFT,
5445     EL_CONVEYOR_BELT_2_LEFT,
5446     EL_CONVEYOR_BELT_3_LEFT,
5447     EL_CONVEYOR_BELT_4_LEFT
5448   };
5449   static int belt_base_active_element[4] =
5450   {
5451     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5452     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5453     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5454     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5455   };
5456   static int belt_base_switch_element[4] =
5457   {
5458     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5459     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5460     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5461     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5462   };
5463   static int belt_move_dir[4] =
5464   {
5465     MV_LEFT,
5466     MV_NONE,
5467     MV_RIGHT,
5468     MV_NONE,
5469   };
5470
5471   int element = Feld[x][y];
5472   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5473   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5474   int belt_dir = belt_move_dir[belt_dir_nr];
5475   int xx, yy, i;
5476
5477   if (!IS_BELT_SWITCH(element))
5478     return;
5479
5480   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5481   game.belt_dir[belt_nr] = belt_dir;
5482
5483   if (belt_dir_nr == 3)
5484     belt_dir_nr = 1;
5485
5486   /* set frame order for belt animation graphic according to belt direction */
5487   for (i = 0; i < NUM_BELT_PARTS; i++)
5488   {
5489     int element = belt_base_active_element[belt_nr] + i;
5490     int graphic = el2img(element);
5491
5492     if (belt_dir == MV_LEFT)
5493       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5494     else
5495       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
5496   }
5497
5498   SCAN_PLAYFIELD(xx, yy)
5499   {
5500     int element = Feld[xx][yy];
5501
5502     if (IS_BELT_SWITCH(element))
5503     {
5504       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5505
5506       if (e_belt_nr == belt_nr)
5507       {
5508         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5509         DrawLevelField(xx, yy);
5510       }
5511     }
5512     else if (IS_BELT(element) && belt_dir != MV_NONE)
5513     {
5514       int e_belt_nr = getBeltNrFromBeltElement(element);
5515
5516       if (e_belt_nr == belt_nr)
5517       {
5518         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5519
5520         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5521         DrawLevelField(xx, yy);
5522       }
5523     }
5524     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5525     {
5526       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5527
5528       if (e_belt_nr == belt_nr)
5529       {
5530         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5531
5532         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5533         DrawLevelField(xx, yy);
5534       }
5535     }
5536   }
5537 }
5538
5539 static void ToggleSwitchgateSwitch(int x, int y)
5540 {
5541   int xx, yy;
5542
5543   game.switchgate_pos = !game.switchgate_pos;
5544
5545   SCAN_PLAYFIELD(xx, yy)
5546   {
5547     int element = Feld[xx][yy];
5548
5549 #if !USE_BOTH_SWITCHGATE_SWITCHES
5550     if (element == EL_SWITCHGATE_SWITCH_UP ||
5551         element == EL_SWITCHGATE_SWITCH_DOWN)
5552     {
5553       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5554       DrawLevelField(xx, yy);
5555     }
5556     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5557              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5558     {
5559       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5560       DrawLevelField(xx, yy);
5561     }
5562 #else
5563     if (element == EL_SWITCHGATE_SWITCH_UP)
5564     {
5565       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5566       DrawLevelField(xx, yy);
5567     }
5568     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5569     {
5570       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5571       DrawLevelField(xx, yy);
5572     }
5573     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5574     {
5575       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5576       DrawLevelField(xx, yy);
5577     }
5578     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5579     {
5580       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5581       DrawLevelField(xx, yy);
5582     }
5583 #endif
5584     else if (element == EL_SWITCHGATE_OPEN ||
5585              element == EL_SWITCHGATE_OPENING)
5586     {
5587       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5588
5589       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5590     }
5591     else if (element == EL_SWITCHGATE_CLOSED ||
5592              element == EL_SWITCHGATE_CLOSING)
5593     {
5594       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5595
5596       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5597     }
5598   }
5599 }
5600
5601 static int getInvisibleActiveFromInvisibleElement(int element)
5602 {
5603   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5604           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5605           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5606           element);
5607 }
5608
5609 static int getInvisibleFromInvisibleActiveElement(int element)
5610 {
5611   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5612           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5613           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5614           element);
5615 }
5616
5617 static void RedrawAllLightSwitchesAndInvisibleElements()
5618 {
5619   int x, y;
5620
5621   SCAN_PLAYFIELD(x, y)
5622   {
5623     int element = Feld[x][y];
5624
5625     if (element == EL_LIGHT_SWITCH &&
5626         game.light_time_left > 0)
5627     {
5628       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5629       DrawLevelField(x, y);
5630     }
5631     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5632              game.light_time_left == 0)
5633     {
5634       Feld[x][y] = EL_LIGHT_SWITCH;
5635       DrawLevelField(x, y);
5636     }
5637     else if (element == EL_EMC_DRIPPER &&
5638              game.light_time_left > 0)
5639     {
5640       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5641       DrawLevelField(x, y);
5642     }
5643     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5644              game.light_time_left == 0)
5645     {
5646       Feld[x][y] = EL_EMC_DRIPPER;
5647       DrawLevelField(x, y);
5648     }
5649     else if (element == EL_INVISIBLE_STEELWALL ||
5650              element == EL_INVISIBLE_WALL ||
5651              element == EL_INVISIBLE_SAND)
5652     {
5653       if (game.light_time_left > 0)
5654         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5655
5656       DrawLevelField(x, y);
5657
5658       /* uncrumble neighbour fields, if needed */
5659       if (element == EL_INVISIBLE_SAND)
5660         DrawLevelFieldCrumbledSandNeighbours(x, y);
5661     }
5662     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5663              element == EL_INVISIBLE_WALL_ACTIVE ||
5664              element == EL_INVISIBLE_SAND_ACTIVE)
5665     {
5666       if (game.light_time_left == 0)
5667         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5668
5669       DrawLevelField(x, y);
5670
5671       /* re-crumble neighbour fields, if needed */
5672       if (element == EL_INVISIBLE_SAND)
5673         DrawLevelFieldCrumbledSandNeighbours(x, y);
5674     }
5675   }
5676 }
5677
5678 static void RedrawAllInvisibleElementsForLenses()
5679 {
5680   int x, y;
5681
5682   SCAN_PLAYFIELD(x, y)
5683   {
5684     int element = Feld[x][y];
5685
5686     if (element == EL_EMC_DRIPPER &&
5687         game.lenses_time_left > 0)
5688     {
5689       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5690       DrawLevelField(x, y);
5691     }
5692     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5693              game.lenses_time_left == 0)
5694     {
5695       Feld[x][y] = EL_EMC_DRIPPER;
5696       DrawLevelField(x, y);
5697     }
5698     else if (element == EL_INVISIBLE_STEELWALL ||
5699              element == EL_INVISIBLE_WALL ||
5700              element == EL_INVISIBLE_SAND)
5701     {
5702       if (game.lenses_time_left > 0)
5703         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5704
5705       DrawLevelField(x, y);
5706
5707       /* uncrumble neighbour fields, if needed */
5708       if (element == EL_INVISIBLE_SAND)
5709         DrawLevelFieldCrumbledSandNeighbours(x, y);
5710     }
5711     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5712              element == EL_INVISIBLE_WALL_ACTIVE ||
5713              element == EL_INVISIBLE_SAND_ACTIVE)
5714     {
5715       if (game.lenses_time_left == 0)
5716         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5717
5718       DrawLevelField(x, y);
5719
5720       /* re-crumble neighbour fields, if needed */
5721       if (element == EL_INVISIBLE_SAND)
5722         DrawLevelFieldCrumbledSandNeighbours(x, y);
5723     }
5724   }
5725 }
5726
5727 static void RedrawAllInvisibleElementsForMagnifier()
5728 {
5729   int x, y;
5730
5731   SCAN_PLAYFIELD(x, y)
5732   {
5733     int element = Feld[x][y];
5734
5735     if (element == EL_EMC_FAKE_GRASS &&
5736         game.magnify_time_left > 0)
5737     {
5738       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5739       DrawLevelField(x, y);
5740     }
5741     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5742              game.magnify_time_left == 0)
5743     {
5744       Feld[x][y] = EL_EMC_FAKE_GRASS;
5745       DrawLevelField(x, y);
5746     }
5747     else if (IS_GATE_GRAY(element) &&
5748              game.magnify_time_left > 0)
5749     {
5750       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5751                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5752                     IS_EM_GATE_GRAY(element) ?
5753                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5754                     IS_EMC_GATE_GRAY(element) ?
5755                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5756                     element);
5757       DrawLevelField(x, y);
5758     }
5759     else if (IS_GATE_GRAY_ACTIVE(element) &&
5760              game.magnify_time_left == 0)
5761     {
5762       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5763                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5764                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5765                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5766                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5767                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5768                     element);
5769       DrawLevelField(x, y);
5770     }
5771   }
5772 }
5773
5774 static void ToggleLightSwitch(int x, int y)
5775 {
5776   int element = Feld[x][y];
5777
5778   game.light_time_left =
5779     (element == EL_LIGHT_SWITCH ?
5780      level.time_light * FRAMES_PER_SECOND : 0);
5781
5782   RedrawAllLightSwitchesAndInvisibleElements();
5783 }
5784
5785 static void ActivateTimegateSwitch(int x, int y)
5786 {
5787   int xx, yy;
5788
5789   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5790
5791   SCAN_PLAYFIELD(xx, yy)
5792   {
5793     int element = Feld[xx][yy];
5794
5795     if (element == EL_TIMEGATE_CLOSED ||
5796         element == EL_TIMEGATE_CLOSING)
5797     {
5798       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5799       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5800     }
5801
5802     /*
5803     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5804     {
5805       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5806       DrawLevelField(xx, yy);
5807     }
5808     */
5809
5810   }
5811
5812 #if 1
5813   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5814                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5815 #else
5816   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5817 #endif
5818 }
5819
5820 void Impact(int x, int y)
5821 {
5822   boolean last_line = (y == lev_fieldy - 1);
5823   boolean object_hit = FALSE;
5824   boolean impact = (last_line || object_hit);
5825   int element = Feld[x][y];
5826   int smashed = EL_STEELWALL;
5827
5828   if (!last_line)       /* check if element below was hit */
5829   {
5830     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5831       return;
5832
5833     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5834                                          MovDir[x][y + 1] != MV_DOWN ||
5835                                          MovPos[x][y + 1] <= TILEY / 2));
5836
5837     /* do not smash moving elements that left the smashed field in time */
5838     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5839         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5840       object_hit = FALSE;
5841
5842 #if USE_QUICKSAND_IMPACT_BUGFIX
5843     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5844     {
5845       RemoveMovingField(x, y + 1);
5846       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5847       Feld[x][y + 2] = EL_ROCK;
5848       DrawLevelField(x, y + 2);
5849
5850       object_hit = TRUE;
5851     }
5852
5853     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5854     {
5855       RemoveMovingField(x, y + 1);
5856       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5857       Feld[x][y + 2] = EL_ROCK;
5858       DrawLevelField(x, y + 2);
5859
5860       object_hit = TRUE;
5861     }
5862 #endif
5863
5864     if (object_hit)
5865       smashed = MovingOrBlocked2Element(x, y + 1);
5866
5867     impact = (last_line || object_hit);
5868   }
5869
5870   if (!last_line && smashed == EL_ACID) /* element falls into acid */
5871   {
5872     SplashAcid(x, y + 1);
5873     return;
5874   }
5875
5876   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5877   /* only reset graphic animation if graphic really changes after impact */
5878   if (impact &&
5879       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5880   {
5881     ResetGfxAnimation(x, y);
5882     DrawLevelField(x, y);
5883   }
5884
5885   if (impact && CAN_EXPLODE_IMPACT(element))
5886   {
5887     Bang(x, y);
5888     return;
5889   }
5890   else if (impact && element == EL_PEARL &&
5891            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5892   {
5893     ResetGfxAnimation(x, y);
5894
5895     Feld[x][y] = EL_PEARL_BREAKING;
5896     PlayLevelSound(x, y, SND_PEARL_BREAKING);
5897     return;
5898   }
5899   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5900   {
5901     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5902
5903     return;
5904   }
5905
5906   if (impact && element == EL_AMOEBA_DROP)
5907   {
5908     if (object_hit && IS_PLAYER(x, y + 1))
5909       KillPlayerUnlessEnemyProtected(x, y + 1);
5910     else if (object_hit && smashed == EL_PENGUIN)
5911       Bang(x, y + 1);
5912     else
5913     {
5914       Feld[x][y] = EL_AMOEBA_GROWING;
5915       Store[x][y] = EL_AMOEBA_WET;
5916
5917       ResetRandomAnimationValue(x, y);
5918     }
5919     return;
5920   }
5921
5922   if (object_hit)               /* check which object was hit */
5923   {
5924     if ((CAN_PASS_MAGIC_WALL(element) && 
5925          (smashed == EL_MAGIC_WALL ||
5926           smashed == EL_BD_MAGIC_WALL)) ||
5927         (CAN_PASS_DC_MAGIC_WALL(element) &&
5928          smashed == EL_DC_MAGIC_WALL))
5929     {
5930       int xx, yy;
5931       int activated_magic_wall =
5932         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5933          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5934          EL_DC_MAGIC_WALL_ACTIVE);
5935
5936       /* activate magic wall / mill */
5937       SCAN_PLAYFIELD(xx, yy)
5938       {
5939         if (Feld[xx][yy] == smashed)
5940           Feld[xx][yy] = activated_magic_wall;
5941       }
5942
5943       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5944       game.magic_wall_active = TRUE;
5945
5946       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5947                             SND_MAGIC_WALL_ACTIVATING :
5948                             smashed == EL_BD_MAGIC_WALL ?
5949                             SND_BD_MAGIC_WALL_ACTIVATING :
5950                             SND_DC_MAGIC_WALL_ACTIVATING));
5951     }
5952
5953     if (IS_PLAYER(x, y + 1))
5954     {
5955       if (CAN_SMASH_PLAYER(element))
5956       {
5957         KillPlayerUnlessEnemyProtected(x, y + 1);
5958         return;
5959       }
5960     }
5961     else if (smashed == EL_PENGUIN)
5962     {
5963       if (CAN_SMASH_PLAYER(element))
5964       {
5965         Bang(x, y + 1);
5966         return;
5967       }
5968     }
5969     else if (element == EL_BD_DIAMOND)
5970     {
5971       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5972       {
5973         Bang(x, y + 1);
5974         return;
5975       }
5976     }
5977     else if (((element == EL_SP_INFOTRON ||
5978                element == EL_SP_ZONK) &&
5979               (smashed == EL_SP_SNIKSNAK ||
5980                smashed == EL_SP_ELECTRON ||
5981                smashed == EL_SP_DISK_ORANGE)) ||
5982              (element == EL_SP_INFOTRON &&
5983               smashed == EL_SP_DISK_YELLOW))
5984     {
5985       Bang(x, y + 1);
5986       return;
5987     }
5988     else if (CAN_SMASH_EVERYTHING(element))
5989     {
5990       if (IS_CLASSIC_ENEMY(smashed) ||
5991           CAN_EXPLODE_SMASHED(smashed))
5992       {
5993         Bang(x, y + 1);
5994         return;
5995       }
5996       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5997       {
5998         if (smashed == EL_LAMP ||
5999             smashed == EL_LAMP_ACTIVE)
6000         {
6001           Bang(x, y + 1);
6002           return;
6003         }
6004         else if (smashed == EL_NUT)
6005         {
6006           Feld[x][y + 1] = EL_NUT_BREAKING;
6007           PlayLevelSound(x, y, SND_NUT_BREAKING);
6008           RaiseScoreElement(EL_NUT);
6009           return;
6010         }
6011         else if (smashed == EL_PEARL)
6012         {
6013           ResetGfxAnimation(x, y);
6014
6015           Feld[x][y + 1] = EL_PEARL_BREAKING;
6016           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6017           return;
6018         }
6019         else if (smashed == EL_DIAMOND)
6020         {
6021           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6022           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6023           return;
6024         }
6025         else if (IS_BELT_SWITCH(smashed))
6026         {
6027           ToggleBeltSwitch(x, y + 1);
6028         }
6029         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6030                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6031                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6032                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6033         {
6034           ToggleSwitchgateSwitch(x, y + 1);
6035         }
6036         else if (smashed == EL_LIGHT_SWITCH ||
6037                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6038         {
6039           ToggleLightSwitch(x, y + 1);
6040         }
6041         else
6042         {
6043 #if 0
6044           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6045 #endif
6046
6047           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6048
6049           CheckElementChangeBySide(x, y + 1, smashed, element,
6050                                    CE_SWITCHED, CH_SIDE_TOP);
6051           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6052                                             CH_SIDE_TOP);
6053         }
6054       }
6055       else
6056       {
6057         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6058       }
6059     }
6060   }
6061
6062   /* play sound of magic wall / mill */
6063   if (!last_line &&
6064       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6065        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6066        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6067   {
6068     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6069       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6070     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6071       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6072     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6073       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6074
6075     return;
6076   }
6077
6078   /* play sound of object that hits the ground */
6079   if (last_line || object_hit)
6080     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6081 }
6082
6083 inline static void TurnRoundExt(int x, int y)
6084 {
6085   static struct
6086   {
6087     int dx, dy;
6088   } move_xy[] =
6089   {
6090     {  0,  0 },
6091     { -1,  0 },
6092     { +1,  0 },
6093     {  0,  0 },
6094     {  0, -1 },
6095     {  0,  0 }, { 0, 0 }, { 0, 0 },
6096     {  0, +1 }
6097   };
6098   static struct
6099   {
6100     int left, right, back;
6101   } turn[] =
6102   {
6103     { 0,        0,              0        },
6104     { MV_DOWN,  MV_UP,          MV_RIGHT },
6105     { MV_UP,    MV_DOWN,        MV_LEFT  },
6106     { 0,        0,              0        },
6107     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6108     { 0,        0,              0        },
6109     { 0,        0,              0        },
6110     { 0,        0,              0        },
6111     { MV_RIGHT, MV_LEFT,        MV_UP    }
6112   };
6113
6114   int element = Feld[x][y];
6115   int move_pattern = element_info[element].move_pattern;
6116
6117   int old_move_dir = MovDir[x][y];
6118   int left_dir  = turn[old_move_dir].left;
6119   int right_dir = turn[old_move_dir].right;
6120   int back_dir  = turn[old_move_dir].back;
6121
6122   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6123   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6124   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6125   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6126
6127   int left_x  = x + left_dx,  left_y  = y + left_dy;
6128   int right_x = x + right_dx, right_y = y + right_dy;
6129   int move_x  = x + move_dx,  move_y  = y + move_dy;
6130
6131   int xx, yy;
6132
6133   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6134   {
6135     TestIfBadThingTouchesOtherBadThing(x, y);
6136
6137     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6138       MovDir[x][y] = right_dir;
6139     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6140       MovDir[x][y] = left_dir;
6141
6142     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6143       MovDelay[x][y] = 9;
6144     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6145       MovDelay[x][y] = 1;
6146   }
6147   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6148   {
6149     TestIfBadThingTouchesOtherBadThing(x, y);
6150
6151     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6152       MovDir[x][y] = left_dir;
6153     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6154       MovDir[x][y] = right_dir;
6155
6156     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6157       MovDelay[x][y] = 9;
6158     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6159       MovDelay[x][y] = 1;
6160   }
6161   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6162   {
6163     TestIfBadThingTouchesOtherBadThing(x, y);
6164
6165     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6166       MovDir[x][y] = left_dir;
6167     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6168       MovDir[x][y] = right_dir;
6169
6170     if (MovDir[x][y] != old_move_dir)
6171       MovDelay[x][y] = 9;
6172   }
6173   else if (element == EL_YAMYAM)
6174   {
6175     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6176     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6177
6178     if (can_turn_left && can_turn_right)
6179       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6180     else if (can_turn_left)
6181       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6182     else if (can_turn_right)
6183       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6184     else
6185       MovDir[x][y] = back_dir;
6186
6187     MovDelay[x][y] = 16 + 16 * RND(3);
6188   }
6189   else if (element == EL_DARK_YAMYAM)
6190   {
6191     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6192                                                          left_x, left_y);
6193     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6194                                                          right_x, right_y);
6195
6196     if (can_turn_left && can_turn_right)
6197       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6198     else if (can_turn_left)
6199       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6200     else if (can_turn_right)
6201       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6202     else
6203       MovDir[x][y] = back_dir;
6204
6205     MovDelay[x][y] = 16 + 16 * RND(3);
6206   }
6207   else if (element == EL_PACMAN)
6208   {
6209     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6210     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6211
6212     if (can_turn_left && can_turn_right)
6213       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6214     else if (can_turn_left)
6215       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6216     else if (can_turn_right)
6217       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6218     else
6219       MovDir[x][y] = back_dir;
6220
6221     MovDelay[x][y] = 6 + RND(40);
6222   }
6223   else if (element == EL_PIG)
6224   {
6225     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6226     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6227     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6228     boolean should_turn_left, should_turn_right, should_move_on;
6229     int rnd_value = 24;
6230     int rnd = RND(rnd_value);
6231
6232     should_turn_left = (can_turn_left &&
6233                         (!can_move_on ||
6234                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6235                                                    y + back_dy + left_dy)));
6236     should_turn_right = (can_turn_right &&
6237                          (!can_move_on ||
6238                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6239                                                     y + back_dy + right_dy)));
6240     should_move_on = (can_move_on &&
6241                       (!can_turn_left ||
6242                        !can_turn_right ||
6243                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6244                                                  y + move_dy + left_dy) ||
6245                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6246                                                  y + move_dy + right_dy)));
6247
6248     if (should_turn_left || should_turn_right || should_move_on)
6249     {
6250       if (should_turn_left && should_turn_right && should_move_on)
6251         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6252                         rnd < 2 * rnd_value / 3 ? right_dir :
6253                         old_move_dir);
6254       else if (should_turn_left && should_turn_right)
6255         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6256       else if (should_turn_left && should_move_on)
6257         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6258       else if (should_turn_right && should_move_on)
6259         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6260       else if (should_turn_left)
6261         MovDir[x][y] = left_dir;
6262       else if (should_turn_right)
6263         MovDir[x][y] = right_dir;
6264       else if (should_move_on)
6265         MovDir[x][y] = old_move_dir;
6266     }
6267     else if (can_move_on && rnd > rnd_value / 8)
6268       MovDir[x][y] = old_move_dir;
6269     else if (can_turn_left && can_turn_right)
6270       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6271     else if (can_turn_left && rnd > rnd_value / 8)
6272       MovDir[x][y] = left_dir;
6273     else if (can_turn_right && rnd > rnd_value/8)
6274       MovDir[x][y] = right_dir;
6275     else
6276       MovDir[x][y] = back_dir;
6277
6278     xx = x + move_xy[MovDir[x][y]].dx;
6279     yy = y + move_xy[MovDir[x][y]].dy;
6280
6281     if (!IN_LEV_FIELD(xx, yy) ||
6282         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6283       MovDir[x][y] = old_move_dir;
6284
6285     MovDelay[x][y] = 0;
6286   }
6287   else if (element == EL_DRAGON)
6288   {
6289     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6290     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6291     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6292     int rnd_value = 24;
6293     int rnd = RND(rnd_value);
6294
6295     if (can_move_on && rnd > rnd_value / 8)
6296       MovDir[x][y] = old_move_dir;
6297     else if (can_turn_left && can_turn_right)
6298       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6299     else if (can_turn_left && rnd > rnd_value / 8)
6300       MovDir[x][y] = left_dir;
6301     else if (can_turn_right && rnd > rnd_value / 8)
6302       MovDir[x][y] = right_dir;
6303     else
6304       MovDir[x][y] = back_dir;
6305
6306     xx = x + move_xy[MovDir[x][y]].dx;
6307     yy = y + move_xy[MovDir[x][y]].dy;
6308
6309     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6310       MovDir[x][y] = old_move_dir;
6311
6312     MovDelay[x][y] = 0;
6313   }
6314   else if (element == EL_MOLE)
6315   {
6316     boolean can_move_on =
6317       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6318                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6319                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6320     if (!can_move_on)
6321     {
6322       boolean can_turn_left =
6323         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6324                               IS_AMOEBOID(Feld[left_x][left_y])));
6325
6326       boolean can_turn_right =
6327         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6328                               IS_AMOEBOID(Feld[right_x][right_y])));
6329
6330       if (can_turn_left && can_turn_right)
6331         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6332       else if (can_turn_left)
6333         MovDir[x][y] = left_dir;
6334       else
6335         MovDir[x][y] = right_dir;
6336     }
6337
6338     if (MovDir[x][y] != old_move_dir)
6339       MovDelay[x][y] = 9;
6340   }
6341   else if (element == EL_BALLOON)
6342   {
6343     MovDir[x][y] = game.wind_direction;
6344     MovDelay[x][y] = 0;
6345   }
6346   else if (element == EL_SPRING)
6347   {
6348 #if USE_NEW_SPRING_BUMPER
6349     if (MovDir[x][y] & MV_HORIZONTAL)
6350     {
6351       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6352           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6353       {
6354         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6355         ResetGfxAnimation(move_x, move_y);
6356         DrawLevelField(move_x, move_y);
6357
6358         MovDir[x][y] = back_dir;
6359       }
6360       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6361                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6362         MovDir[x][y] = MV_NONE;
6363     }
6364 #else
6365     if (MovDir[x][y] & MV_HORIZONTAL &&
6366         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6367          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6368       MovDir[x][y] = MV_NONE;
6369 #endif
6370
6371     MovDelay[x][y] = 0;
6372   }
6373   else if (element == EL_ROBOT ||
6374            element == EL_SATELLITE ||
6375            element == EL_PENGUIN ||
6376            element == EL_EMC_ANDROID)
6377   {
6378     int attr_x = -1, attr_y = -1;
6379
6380     if (AllPlayersGone)
6381     {
6382       attr_x = ExitX;
6383       attr_y = ExitY;
6384     }
6385     else
6386     {
6387       int i;
6388
6389       for (i = 0; i < MAX_PLAYERS; i++)
6390       {
6391         struct PlayerInfo *player = &stored_player[i];
6392         int jx = player->jx, jy = player->jy;
6393
6394         if (!player->active)
6395           continue;
6396
6397         if (attr_x == -1 ||
6398             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6399         {
6400           attr_x = jx;
6401           attr_y = jy;
6402         }
6403       }
6404     }
6405
6406     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6407         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6408          game.engine_version < VERSION_IDENT(3,1,0,0)))
6409     {
6410       attr_x = ZX;
6411       attr_y = ZY;
6412     }
6413
6414     if (element == EL_PENGUIN)
6415     {
6416       int i;
6417       static int xy[4][2] =
6418       {
6419         { 0, -1 },
6420         { -1, 0 },
6421         { +1, 0 },
6422         { 0, +1 }
6423       };
6424
6425       for (i = 0; i < NUM_DIRECTIONS; i++)
6426       {
6427         int ex = x + xy[i][0];
6428         int ey = y + xy[i][1];
6429
6430         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6431                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6432                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6433                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6434         {
6435           attr_x = ex;
6436           attr_y = ey;
6437           break;
6438         }
6439       }
6440     }
6441
6442     MovDir[x][y] = MV_NONE;
6443     if (attr_x < x)
6444       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6445     else if (attr_x > x)
6446       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6447     if (attr_y < y)
6448       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6449     else if (attr_y > y)
6450       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6451
6452     if (element == EL_ROBOT)
6453     {
6454       int newx, newy;
6455
6456       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6457         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6458       Moving2Blocked(x, y, &newx, &newy);
6459
6460       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6461         MovDelay[x][y] = 8 + 8 * !RND(3);
6462       else
6463         MovDelay[x][y] = 16;
6464     }
6465     else if (element == EL_PENGUIN)
6466     {
6467       int newx, newy;
6468
6469       MovDelay[x][y] = 1;
6470
6471       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6472       {
6473         boolean first_horiz = RND(2);
6474         int new_move_dir = MovDir[x][y];
6475
6476         MovDir[x][y] =
6477           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6478         Moving2Blocked(x, y, &newx, &newy);
6479
6480         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6481           return;
6482
6483         MovDir[x][y] =
6484           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6485         Moving2Blocked(x, y, &newx, &newy);
6486
6487         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6488           return;
6489
6490         MovDir[x][y] = old_move_dir;
6491         return;
6492       }
6493     }
6494     else if (element == EL_SATELLITE)
6495     {
6496       int newx, newy;
6497
6498       MovDelay[x][y] = 1;
6499
6500       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6501       {
6502         boolean first_horiz = RND(2);
6503         int new_move_dir = MovDir[x][y];
6504
6505         MovDir[x][y] =
6506           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6507         Moving2Blocked(x, y, &newx, &newy);
6508
6509         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6510           return;
6511
6512         MovDir[x][y] =
6513           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6514         Moving2Blocked(x, y, &newx, &newy);
6515
6516         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6517           return;
6518
6519         MovDir[x][y] = old_move_dir;
6520         return;
6521       }
6522     }
6523     else if (element == EL_EMC_ANDROID)
6524     {
6525       static int check_pos[16] =
6526       {
6527         -1,             /*  0 => (invalid)          */
6528         7,              /*  1 => MV_LEFT            */
6529         3,              /*  2 => MV_RIGHT           */
6530         -1,             /*  3 => (invalid)          */
6531         1,              /*  4 =>            MV_UP   */
6532         0,              /*  5 => MV_LEFT  | MV_UP   */
6533         2,              /*  6 => MV_RIGHT | MV_UP   */
6534         -1,             /*  7 => (invalid)          */
6535         5,              /*  8 =>            MV_DOWN */
6536         6,              /*  9 => MV_LEFT  | MV_DOWN */
6537         4,              /* 10 => MV_RIGHT | MV_DOWN */
6538         -1,             /* 11 => (invalid)          */
6539         -1,             /* 12 => (invalid)          */
6540         -1,             /* 13 => (invalid)          */
6541         -1,             /* 14 => (invalid)          */
6542         -1,             /* 15 => (invalid)          */
6543       };
6544       static struct
6545       {
6546         int dx, dy;
6547         int dir;
6548       } check_xy[8] =
6549       {
6550         { -1, -1,       MV_LEFT  | MV_UP   },
6551         {  0, -1,                  MV_UP   },
6552         { +1, -1,       MV_RIGHT | MV_UP   },
6553         { +1,  0,       MV_RIGHT           },
6554         { +1, +1,       MV_RIGHT | MV_DOWN },
6555         {  0, +1,                  MV_DOWN },
6556         { -1, +1,       MV_LEFT  | MV_DOWN },
6557         { -1,  0,       MV_LEFT            },
6558       };
6559       int start_pos, check_order;
6560       boolean can_clone = FALSE;
6561       int i;
6562
6563       /* check if there is any free field around current position */
6564       for (i = 0; i < 8; i++)
6565       {
6566         int newx = x + check_xy[i].dx;
6567         int newy = y + check_xy[i].dy;
6568
6569         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6570         {
6571           can_clone = TRUE;
6572
6573           break;
6574         }
6575       }
6576
6577       if (can_clone)            /* randomly find an element to clone */
6578       {
6579         can_clone = FALSE;
6580
6581         start_pos = check_pos[RND(8)];
6582         check_order = (RND(2) ? -1 : +1);
6583
6584         for (i = 0; i < 8; i++)
6585         {
6586           int pos_raw = start_pos + i * check_order;
6587           int pos = (pos_raw + 8) % 8;
6588           int newx = x + check_xy[pos].dx;
6589           int newy = y + check_xy[pos].dy;
6590
6591           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6592           {
6593             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6594             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6595
6596             Store[x][y] = Feld[newx][newy];
6597
6598             can_clone = TRUE;
6599
6600             break;
6601           }
6602         }
6603       }
6604
6605       if (can_clone)            /* randomly find a direction to move */
6606       {
6607         can_clone = FALSE;
6608
6609         start_pos = check_pos[RND(8)];
6610         check_order = (RND(2) ? -1 : +1);
6611
6612         for (i = 0; i < 8; i++)
6613         {
6614           int pos_raw = start_pos + i * check_order;
6615           int pos = (pos_raw + 8) % 8;
6616           int newx = x + check_xy[pos].dx;
6617           int newy = y + check_xy[pos].dy;
6618           int new_move_dir = check_xy[pos].dir;
6619
6620           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6621           {
6622             MovDir[x][y] = new_move_dir;
6623             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6624
6625             can_clone = TRUE;
6626
6627             break;
6628           }
6629         }
6630       }
6631
6632       if (can_clone)            /* cloning and moving successful */
6633         return;
6634
6635       /* cannot clone -- try to move towards player */
6636
6637       start_pos = check_pos[MovDir[x][y] & 0x0f];
6638       check_order = (RND(2) ? -1 : +1);
6639
6640       for (i = 0; i < 3; i++)
6641       {
6642         /* first check start_pos, then previous/next or (next/previous) pos */
6643         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6644         int pos = (pos_raw + 8) % 8;
6645         int newx = x + check_xy[pos].dx;
6646         int newy = y + check_xy[pos].dy;
6647         int new_move_dir = check_xy[pos].dir;
6648
6649         if (IS_PLAYER(newx, newy))
6650           break;
6651
6652         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6653         {
6654           MovDir[x][y] = new_move_dir;
6655           MovDelay[x][y] = level.android_move_time * 8 + 1;
6656
6657           break;
6658         }
6659       }
6660     }
6661   }
6662   else if (move_pattern == MV_TURNING_LEFT ||
6663            move_pattern == MV_TURNING_RIGHT ||
6664            move_pattern == MV_TURNING_LEFT_RIGHT ||
6665            move_pattern == MV_TURNING_RIGHT_LEFT ||
6666            move_pattern == MV_TURNING_RANDOM ||
6667            move_pattern == MV_ALL_DIRECTIONS)
6668   {
6669     boolean can_turn_left =
6670       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6671     boolean can_turn_right =
6672       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6673
6674     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6675       return;
6676
6677     if (move_pattern == MV_TURNING_LEFT)
6678       MovDir[x][y] = left_dir;
6679     else if (move_pattern == MV_TURNING_RIGHT)
6680       MovDir[x][y] = right_dir;
6681     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6682       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6683     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6684       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6685     else if (move_pattern == MV_TURNING_RANDOM)
6686       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6687                       can_turn_right && !can_turn_left ? right_dir :
6688                       RND(2) ? left_dir : right_dir);
6689     else if (can_turn_left && can_turn_right)
6690       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6691     else if (can_turn_left)
6692       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6693     else if (can_turn_right)
6694       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6695     else
6696       MovDir[x][y] = back_dir;
6697
6698     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6699   }
6700   else if (move_pattern == MV_HORIZONTAL ||
6701            move_pattern == MV_VERTICAL)
6702   {
6703     if (move_pattern & old_move_dir)
6704       MovDir[x][y] = back_dir;
6705     else if (move_pattern == MV_HORIZONTAL)
6706       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6707     else if (move_pattern == MV_VERTICAL)
6708       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6709
6710     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6711   }
6712   else if (move_pattern & MV_ANY_DIRECTION)
6713   {
6714     MovDir[x][y] = move_pattern;
6715     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6716   }
6717   else if (move_pattern & MV_WIND_DIRECTION)
6718   {
6719     MovDir[x][y] = game.wind_direction;
6720     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6721   }
6722   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6723   {
6724     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6725       MovDir[x][y] = left_dir;
6726     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6727       MovDir[x][y] = right_dir;
6728
6729     if (MovDir[x][y] != old_move_dir)
6730       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6731   }
6732   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6733   {
6734     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6735       MovDir[x][y] = right_dir;
6736     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6737       MovDir[x][y] = left_dir;
6738
6739     if (MovDir[x][y] != old_move_dir)
6740       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6741   }
6742   else if (move_pattern == MV_TOWARDS_PLAYER ||
6743            move_pattern == MV_AWAY_FROM_PLAYER)
6744   {
6745     int attr_x = -1, attr_y = -1;
6746     int newx, newy;
6747     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6748
6749     if (AllPlayersGone)
6750     {
6751       attr_x = ExitX;
6752       attr_y = ExitY;
6753     }
6754     else
6755     {
6756       int i;
6757
6758       for (i = 0; i < MAX_PLAYERS; i++)
6759       {
6760         struct PlayerInfo *player = &stored_player[i];
6761         int jx = player->jx, jy = player->jy;
6762
6763         if (!player->active)
6764           continue;
6765
6766         if (attr_x == -1 ||
6767             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6768         {
6769           attr_x = jx;
6770           attr_y = jy;
6771         }
6772       }
6773     }
6774
6775     MovDir[x][y] = MV_NONE;
6776     if (attr_x < x)
6777       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6778     else if (attr_x > x)
6779       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6780     if (attr_y < y)
6781       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6782     else if (attr_y > y)
6783       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6784
6785     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6786
6787     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6788     {
6789       boolean first_horiz = RND(2);
6790       int new_move_dir = MovDir[x][y];
6791
6792       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6793       {
6794         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6795         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6796
6797         return;
6798       }
6799
6800       MovDir[x][y] =
6801         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6802       Moving2Blocked(x, y, &newx, &newy);
6803
6804       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6805         return;
6806
6807       MovDir[x][y] =
6808         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6809       Moving2Blocked(x, y, &newx, &newy);
6810
6811       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6812         return;
6813
6814       MovDir[x][y] = old_move_dir;
6815     }
6816   }
6817   else if (move_pattern == MV_WHEN_PUSHED ||
6818            move_pattern == MV_WHEN_DROPPED)
6819   {
6820     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6821       MovDir[x][y] = MV_NONE;
6822
6823     MovDelay[x][y] = 0;
6824   }
6825   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6826   {
6827     static int test_xy[7][2] =
6828     {
6829       { 0, -1 },
6830       { -1, 0 },
6831       { +1, 0 },
6832       { 0, +1 },
6833       { 0, -1 },
6834       { -1, 0 },
6835       { +1, 0 },
6836     };
6837     static int test_dir[7] =
6838     {
6839       MV_UP,
6840       MV_LEFT,
6841       MV_RIGHT,
6842       MV_DOWN,
6843       MV_UP,
6844       MV_LEFT,
6845       MV_RIGHT,
6846     };
6847     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6848     int move_preference = -1000000;     /* start with very low preference */
6849     int new_move_dir = MV_NONE;
6850     int start_test = RND(4);
6851     int i;
6852
6853     for (i = 0; i < NUM_DIRECTIONS; i++)
6854     {
6855       int move_dir = test_dir[start_test + i];
6856       int move_dir_preference;
6857
6858       xx = x + test_xy[start_test + i][0];
6859       yy = y + test_xy[start_test + i][1];
6860
6861       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6862           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6863       {
6864         new_move_dir = move_dir;
6865
6866         break;
6867       }
6868
6869       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6870         continue;
6871
6872       move_dir_preference = -1 * RunnerVisit[xx][yy];
6873       if (hunter_mode && PlayerVisit[xx][yy] > 0)
6874         move_dir_preference = PlayerVisit[xx][yy];
6875
6876       if (move_dir_preference > move_preference)
6877       {
6878         /* prefer field that has not been visited for the longest time */
6879         move_preference = move_dir_preference;
6880         new_move_dir = move_dir;
6881       }
6882       else if (move_dir_preference == move_preference &&
6883                move_dir == old_move_dir)
6884       {
6885         /* prefer last direction when all directions are preferred equally */
6886         move_preference = move_dir_preference;
6887         new_move_dir = move_dir;
6888       }
6889     }
6890
6891     MovDir[x][y] = new_move_dir;
6892     if (old_move_dir != new_move_dir)
6893       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6894   }
6895 }
6896
6897 static void TurnRound(int x, int y)
6898 {
6899   int direction = MovDir[x][y];
6900
6901   TurnRoundExt(x, y);
6902
6903   GfxDir[x][y] = MovDir[x][y];
6904
6905   if (direction != MovDir[x][y])
6906     GfxFrame[x][y] = 0;
6907
6908   if (MovDelay[x][y])
6909     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6910
6911   ResetGfxFrame(x, y, FALSE);
6912 }
6913
6914 static boolean JustBeingPushed(int x, int y)
6915 {
6916   int i;
6917
6918   for (i = 0; i < MAX_PLAYERS; i++)
6919   {
6920     struct PlayerInfo *player = &stored_player[i];
6921
6922     if (player->active && player->is_pushing && player->MovPos)
6923     {
6924       int next_jx = player->jx + (player->jx - player->last_jx);
6925       int next_jy = player->jy + (player->jy - player->last_jy);
6926
6927       if (x == next_jx && y == next_jy)
6928         return TRUE;
6929     }
6930   }
6931
6932   return FALSE;
6933 }
6934
6935 void StartMoving(int x, int y)
6936 {
6937   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
6938   int element = Feld[x][y];
6939
6940   if (Stop[x][y])
6941     return;
6942
6943   if (MovDelay[x][y] == 0)
6944     GfxAction[x][y] = ACTION_DEFAULT;
6945
6946   if (CAN_FALL(element) && y < lev_fieldy - 1)
6947   {
6948     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
6949         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6950       if (JustBeingPushed(x, y))
6951         return;
6952
6953     if (element == EL_QUICKSAND_FULL)
6954     {
6955       if (IS_FREE(x, y + 1))
6956       {
6957         InitMovingField(x, y, MV_DOWN);
6958         started_moving = TRUE;
6959
6960         Feld[x][y] = EL_QUICKSAND_EMPTYING;
6961 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6962         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6963           Store[x][y] = EL_ROCK;
6964 #else
6965         Store[x][y] = EL_ROCK;
6966 #endif
6967
6968         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6969       }
6970       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6971       {
6972         if (!MovDelay[x][y])
6973           MovDelay[x][y] = TILEY + 1;
6974
6975         if (MovDelay[x][y])
6976         {
6977           MovDelay[x][y]--;
6978           if (MovDelay[x][y])
6979             return;
6980         }
6981
6982         Feld[x][y] = EL_QUICKSAND_EMPTY;
6983         Feld[x][y + 1] = EL_QUICKSAND_FULL;
6984         Store[x][y + 1] = Store[x][y];
6985         Store[x][y] = 0;
6986
6987         PlayLevelSoundAction(x, y, ACTION_FILLING);
6988       }
6989     }
6990     else if (element == EL_QUICKSAND_FAST_FULL)
6991     {
6992       if (IS_FREE(x, y + 1))
6993       {
6994         InitMovingField(x, y, MV_DOWN);
6995         started_moving = TRUE;
6996
6997         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
6998 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6999         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7000           Store[x][y] = EL_ROCK;
7001 #else
7002         Store[x][y] = EL_ROCK;
7003 #endif
7004
7005         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7006       }
7007       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7008       {
7009         if (!MovDelay[x][y])
7010           MovDelay[x][y] = TILEY + 1;
7011
7012         if (MovDelay[x][y])
7013         {
7014           MovDelay[x][y]--;
7015           if (MovDelay[x][y])
7016             return;
7017         }
7018
7019         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7020         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7021         Store[x][y + 1] = Store[x][y];
7022         Store[x][y] = 0;
7023
7024         PlayLevelSoundAction(x, y, ACTION_FILLING);
7025       }
7026     }
7027     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7028              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7029     {
7030       InitMovingField(x, y, MV_DOWN);
7031       started_moving = TRUE;
7032
7033       Feld[x][y] = EL_QUICKSAND_FILLING;
7034       Store[x][y] = element;
7035
7036       PlayLevelSoundAction(x, y, ACTION_FILLING);
7037     }
7038     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7039              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7040     {
7041       InitMovingField(x, y, MV_DOWN);
7042       started_moving = TRUE;
7043
7044       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7045       Store[x][y] = element;
7046
7047       PlayLevelSoundAction(x, y, ACTION_FILLING);
7048     }
7049     else if (element == EL_MAGIC_WALL_FULL)
7050     {
7051       if (IS_FREE(x, y + 1))
7052       {
7053         InitMovingField(x, y, MV_DOWN);
7054         started_moving = TRUE;
7055
7056         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7057         Store[x][y] = EL_CHANGED(Store[x][y]);
7058       }
7059       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7060       {
7061         if (!MovDelay[x][y])
7062           MovDelay[x][y] = TILEY/4 + 1;
7063
7064         if (MovDelay[x][y])
7065         {
7066           MovDelay[x][y]--;
7067           if (MovDelay[x][y])
7068             return;
7069         }
7070
7071         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7072         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7073         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7074         Store[x][y] = 0;
7075       }
7076     }
7077     else if (element == EL_BD_MAGIC_WALL_FULL)
7078     {
7079       if (IS_FREE(x, y + 1))
7080       {
7081         InitMovingField(x, y, MV_DOWN);
7082         started_moving = TRUE;
7083
7084         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7085         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7086       }
7087       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7088       {
7089         if (!MovDelay[x][y])
7090           MovDelay[x][y] = TILEY/4 + 1;
7091
7092         if (MovDelay[x][y])
7093         {
7094           MovDelay[x][y]--;
7095           if (MovDelay[x][y])
7096             return;
7097         }
7098
7099         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7100         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7101         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7102         Store[x][y] = 0;
7103       }
7104     }
7105     else if (element == EL_DC_MAGIC_WALL_FULL)
7106     {
7107       if (IS_FREE(x, y + 1))
7108       {
7109         InitMovingField(x, y, MV_DOWN);
7110         started_moving = TRUE;
7111
7112         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7113         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7114       }
7115       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7116       {
7117         if (!MovDelay[x][y])
7118           MovDelay[x][y] = TILEY/4 + 1;
7119
7120         if (MovDelay[x][y])
7121         {
7122           MovDelay[x][y]--;
7123           if (MovDelay[x][y])
7124             return;
7125         }
7126
7127         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7128         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7129         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7130         Store[x][y] = 0;
7131       }
7132     }
7133     else if ((CAN_PASS_MAGIC_WALL(element) &&
7134               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7135                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7136              (CAN_PASS_DC_MAGIC_WALL(element) &&
7137               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7138
7139     {
7140       InitMovingField(x, y, MV_DOWN);
7141       started_moving = TRUE;
7142
7143       Feld[x][y] =
7144         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7145          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7146          EL_DC_MAGIC_WALL_FILLING);
7147       Store[x][y] = element;
7148     }
7149     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7150     {
7151       SplashAcid(x, y + 1);
7152
7153       InitMovingField(x, y, MV_DOWN);
7154       started_moving = TRUE;
7155
7156       Store[x][y] = EL_ACID;
7157     }
7158     else if (
7159 #if USE_FIX_IMPACT_COLLISION
7160              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7161               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7162 #else
7163              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7164               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7165 #endif
7166              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7167               CAN_FALL(element) && WasJustFalling[x][y] &&
7168               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7169
7170              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7171               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7172               (Feld[x][y + 1] == EL_BLOCKED)))
7173     {
7174       /* this is needed for a special case not covered by calling "Impact()"
7175          from "ContinueMoving()": if an element moves to a tile directly below
7176          another element which was just falling on that tile (which was empty
7177          in the previous frame), the falling element above would just stop
7178          instead of smashing the element below (in previous version, the above
7179          element was just checked for "moving" instead of "falling", resulting
7180          in incorrect smashes caused by horizontal movement of the above
7181          element; also, the case of the player being the element to smash was
7182          simply not covered here... :-/ ) */
7183
7184       CheckCollision[x][y] = 0;
7185       CheckImpact[x][y] = 0;
7186
7187       Impact(x, y);
7188     }
7189     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7190     {
7191       if (MovDir[x][y] == MV_NONE)
7192       {
7193         InitMovingField(x, y, MV_DOWN);
7194         started_moving = TRUE;
7195       }
7196     }
7197     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7198     {
7199       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7200         MovDir[x][y] = MV_DOWN;
7201
7202       InitMovingField(x, y, MV_DOWN);
7203       started_moving = TRUE;
7204     }
7205     else if (element == EL_AMOEBA_DROP)
7206     {
7207       Feld[x][y] = EL_AMOEBA_GROWING;
7208       Store[x][y] = EL_AMOEBA_WET;
7209     }
7210     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7211               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7212              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7213              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7214     {
7215       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7216                                 (IS_FREE(x - 1, y + 1) ||
7217                                  Feld[x - 1][y + 1] == EL_ACID));
7218       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7219                                 (IS_FREE(x + 1, y + 1) ||
7220                                  Feld[x + 1][y + 1] == EL_ACID));
7221       boolean can_fall_any  = (can_fall_left || can_fall_right);
7222       boolean can_fall_both = (can_fall_left && can_fall_right);
7223       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7224
7225 #if USE_NEW_ALL_SLIPPERY
7226       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7227       {
7228         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7229           can_fall_right = FALSE;
7230         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7231           can_fall_left = FALSE;
7232         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7233           can_fall_right = FALSE;
7234         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7235           can_fall_left = FALSE;
7236
7237         can_fall_any  = (can_fall_left || can_fall_right);
7238         can_fall_both = FALSE;
7239       }
7240 #else
7241       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7242       {
7243         if (slippery_type == SLIPPERY_ONLY_LEFT)
7244           can_fall_right = FALSE;
7245         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7246           can_fall_left = FALSE;
7247         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7248           can_fall_right = FALSE;
7249         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7250           can_fall_left = FALSE;
7251
7252         can_fall_any  = (can_fall_left || can_fall_right);
7253         can_fall_both = (can_fall_left && can_fall_right);
7254       }
7255 #endif
7256
7257 #if USE_NEW_ALL_SLIPPERY
7258 #else
7259 #if USE_NEW_SP_SLIPPERY
7260       /* !!! better use the same properties as for custom elements here !!! */
7261       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7262                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7263       {
7264         can_fall_right = FALSE;         /* slip down on left side */
7265         can_fall_both = FALSE;
7266       }
7267 #endif
7268 #endif
7269
7270 #if USE_NEW_ALL_SLIPPERY
7271       if (can_fall_both)
7272       {
7273         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7274           can_fall_right = FALSE;       /* slip down on left side */
7275         else
7276           can_fall_left = !(can_fall_right = RND(2));
7277
7278         can_fall_both = FALSE;
7279       }
7280 #else
7281       if (can_fall_both)
7282       {
7283         if (game.emulation == EMU_BOULDERDASH ||
7284             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7285           can_fall_right = FALSE;       /* slip down on left side */
7286         else
7287           can_fall_left = !(can_fall_right = RND(2));
7288
7289         can_fall_both = FALSE;
7290       }
7291 #endif
7292
7293       if (can_fall_any)
7294       {
7295         /* if not determined otherwise, prefer left side for slipping down */
7296         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7297         started_moving = TRUE;
7298       }
7299     }
7300 #if 0
7301     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7302 #else
7303     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7304 #endif
7305     {
7306       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7307       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7308       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7309       int belt_dir = game.belt_dir[belt_nr];
7310
7311       if ((belt_dir == MV_LEFT  && left_is_free) ||
7312           (belt_dir == MV_RIGHT && right_is_free))
7313       {
7314         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7315
7316         InitMovingField(x, y, belt_dir);
7317         started_moving = TRUE;
7318
7319         Pushed[x][y] = TRUE;
7320         Pushed[nextx][y] = TRUE;
7321
7322         GfxAction[x][y] = ACTION_DEFAULT;
7323       }
7324       else
7325       {
7326         MovDir[x][y] = 0;       /* if element was moving, stop it */
7327       }
7328     }
7329   }
7330
7331   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7332 #if 0
7333   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7334 #else
7335   if (CAN_MOVE(element) && !started_moving)
7336 #endif
7337   {
7338     int move_pattern = element_info[element].move_pattern;
7339     int newx, newy;
7340
7341 #if 0
7342 #if DEBUG
7343     if (MovDir[x][y] == MV_NONE)
7344     {
7345       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7346              x, y, element, element_info[element].token_name);
7347       printf("StartMoving(): This should never happen!\n");
7348     }
7349 #endif
7350 #endif
7351
7352     Moving2Blocked(x, y, &newx, &newy);
7353
7354     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7355       return;
7356
7357     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7358         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7359     {
7360       WasJustMoving[x][y] = 0;
7361       CheckCollision[x][y] = 0;
7362
7363       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7364
7365       if (Feld[x][y] != element)        /* element has changed */
7366         return;
7367     }
7368
7369     if (!MovDelay[x][y])        /* start new movement phase */
7370     {
7371       /* all objects that can change their move direction after each step
7372          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7373
7374       if (element != EL_YAMYAM &&
7375           element != EL_DARK_YAMYAM &&
7376           element != EL_PACMAN &&
7377           !(move_pattern & MV_ANY_DIRECTION) &&
7378           move_pattern != MV_TURNING_LEFT &&
7379           move_pattern != MV_TURNING_RIGHT &&
7380           move_pattern != MV_TURNING_LEFT_RIGHT &&
7381           move_pattern != MV_TURNING_RIGHT_LEFT &&
7382           move_pattern != MV_TURNING_RANDOM)
7383       {
7384         TurnRound(x, y);
7385
7386         if (MovDelay[x][y] && (element == EL_BUG ||
7387                                element == EL_SPACESHIP ||
7388                                element == EL_SP_SNIKSNAK ||
7389                                element == EL_SP_ELECTRON ||
7390                                element == EL_MOLE))
7391           DrawLevelField(x, y);
7392       }
7393     }
7394
7395     if (MovDelay[x][y])         /* wait some time before next movement */
7396     {
7397       MovDelay[x][y]--;
7398
7399       if (element == EL_ROBOT ||
7400           element == EL_YAMYAM ||
7401           element == EL_DARK_YAMYAM)
7402       {
7403         DrawLevelElementAnimationIfNeeded(x, y, element);
7404         PlayLevelSoundAction(x, y, ACTION_WAITING);
7405       }
7406       else if (element == EL_SP_ELECTRON)
7407         DrawLevelElementAnimationIfNeeded(x, y, element);
7408       else if (element == EL_DRAGON)
7409       {
7410         int i;
7411         int dir = MovDir[x][y];
7412         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7413         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7414         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7415                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7416                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7417                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7418         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7419
7420         GfxAction[x][y] = ACTION_ATTACKING;
7421
7422         if (IS_PLAYER(x, y))
7423           DrawPlayerField(x, y);
7424         else
7425           DrawLevelField(x, y);
7426
7427         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7428
7429         for (i = 1; i <= 3; i++)
7430         {
7431           int xx = x + i * dx;
7432           int yy = y + i * dy;
7433           int sx = SCREENX(xx);
7434           int sy = SCREENY(yy);
7435           int flame_graphic = graphic + (i - 1);
7436
7437           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7438             break;
7439
7440           if (MovDelay[x][y])
7441           {
7442             int flamed = MovingOrBlocked2Element(xx, yy);
7443
7444             /* !!! */
7445 #if 0
7446             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7447               Bang(xx, yy);
7448             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
7449               RemoveMovingField(xx, yy);
7450             else
7451               RemoveField(xx, yy);
7452 #else
7453             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7454               Bang(xx, yy);
7455             else
7456               RemoveMovingField(xx, yy);
7457 #endif
7458
7459             ChangeDelay[xx][yy] = 0;
7460
7461             Feld[xx][yy] = EL_FLAMES;
7462
7463             if (IN_SCR_FIELD(sx, sy))
7464             {
7465               DrawLevelFieldCrumbledSand(xx, yy);
7466               DrawGraphic(sx, sy, flame_graphic, frame);
7467             }
7468           }
7469           else
7470           {
7471             if (Feld[xx][yy] == EL_FLAMES)
7472               Feld[xx][yy] = EL_EMPTY;
7473             DrawLevelField(xx, yy);
7474           }
7475         }
7476       }
7477
7478       if (MovDelay[x][y])       /* element still has to wait some time */
7479       {
7480         PlayLevelSoundAction(x, y, ACTION_WAITING);
7481
7482         return;
7483       }
7484     }
7485
7486     /* now make next step */
7487
7488     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7489
7490     if (DONT_COLLIDE_WITH(element) &&
7491         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7492         !PLAYER_ENEMY_PROTECTED(newx, newy))
7493     {
7494       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7495
7496       return;
7497     }
7498
7499     else if (CAN_MOVE_INTO_ACID(element) &&
7500              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7501              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7502              (MovDir[x][y] == MV_DOWN ||
7503               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7504     {
7505       SplashAcid(newx, newy);
7506       Store[x][y] = EL_ACID;
7507     }
7508     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7509     {
7510       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7511           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7512           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7513           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7514       {
7515         RemoveField(x, y);
7516         DrawLevelField(x, y);
7517
7518         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7519         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7520           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7521
7522         local_player->friends_still_needed--;
7523         if (!local_player->friends_still_needed &&
7524             !local_player->GameOver && AllPlayersGone)
7525           PlayerWins(local_player);
7526
7527         return;
7528       }
7529       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7530       {
7531         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7532           DrawLevelField(newx, newy);
7533         else
7534           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7535       }
7536       else if (!IS_FREE(newx, newy))
7537       {
7538         GfxAction[x][y] = ACTION_WAITING;
7539
7540         if (IS_PLAYER(x, y))
7541           DrawPlayerField(x, y);
7542         else
7543           DrawLevelField(x, y);
7544
7545         return;
7546       }
7547     }
7548     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7549     {
7550       if (IS_FOOD_PIG(Feld[newx][newy]))
7551       {
7552         if (IS_MOVING(newx, newy))
7553           RemoveMovingField(newx, newy);
7554         else
7555         {
7556           Feld[newx][newy] = EL_EMPTY;
7557           DrawLevelField(newx, newy);
7558         }
7559
7560         PlayLevelSound(x, y, SND_PIG_DIGGING);
7561       }
7562       else if (!IS_FREE(newx, newy))
7563       {
7564         if (IS_PLAYER(x, y))
7565           DrawPlayerField(x, y);
7566         else
7567           DrawLevelField(x, y);
7568
7569         return;
7570       }
7571     }
7572     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7573     {
7574       if (Store[x][y] != EL_EMPTY)
7575       {
7576         boolean can_clone = FALSE;
7577         int xx, yy;
7578
7579         /* check if element to clone is still there */
7580         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7581         {
7582           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7583           {
7584             can_clone = TRUE;
7585
7586             break;
7587           }
7588         }
7589
7590         /* cannot clone or target field not free anymore -- do not clone */
7591         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7592           Store[x][y] = EL_EMPTY;
7593       }
7594
7595       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7596       {
7597         if (IS_MV_DIAGONAL(MovDir[x][y]))
7598         {
7599           int diagonal_move_dir = MovDir[x][y];
7600           int stored = Store[x][y];
7601           int change_delay = 8;
7602           int graphic;
7603
7604           /* android is moving diagonally */
7605
7606           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7607
7608           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7609           GfxElement[x][y] = EL_EMC_ANDROID;
7610           GfxAction[x][y] = ACTION_SHRINKING;
7611           GfxDir[x][y] = diagonal_move_dir;
7612           ChangeDelay[x][y] = change_delay;
7613
7614           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7615                                    GfxDir[x][y]);
7616
7617           DrawLevelGraphicAnimation(x, y, graphic);
7618           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7619
7620           if (Feld[newx][newy] == EL_ACID)
7621           {
7622             SplashAcid(newx, newy);
7623
7624             return;
7625           }
7626
7627           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7628
7629           Store[newx][newy] = EL_EMC_ANDROID;
7630           GfxElement[newx][newy] = EL_EMC_ANDROID;
7631           GfxAction[newx][newy] = ACTION_GROWING;
7632           GfxDir[newx][newy] = diagonal_move_dir;
7633           ChangeDelay[newx][newy] = change_delay;
7634
7635           graphic = el_act_dir2img(GfxElement[newx][newy],
7636                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7637
7638           DrawLevelGraphicAnimation(newx, newy, graphic);
7639           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7640
7641           return;
7642         }
7643         else
7644         {
7645           Feld[newx][newy] = EL_EMPTY;
7646           DrawLevelField(newx, newy);
7647
7648           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7649         }
7650       }
7651       else if (!IS_FREE(newx, newy))
7652       {
7653 #if 0
7654         if (IS_PLAYER(x, y))
7655           DrawPlayerField(x, y);
7656         else
7657           DrawLevelField(x, y);
7658 #endif
7659
7660         return;
7661       }
7662     }
7663     else if (IS_CUSTOM_ELEMENT(element) &&
7664              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7665     {
7666       int new_element = Feld[newx][newy];
7667
7668       if (!IS_FREE(newx, newy))
7669       {
7670         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7671                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7672                       ACTION_BREAKING);
7673
7674         /* no element can dig solid indestructible elements */
7675         if (IS_INDESTRUCTIBLE(new_element) &&
7676             !IS_DIGGABLE(new_element) &&
7677             !IS_COLLECTIBLE(new_element))
7678           return;
7679
7680         if (AmoebaNr[newx][newy] &&
7681             (new_element == EL_AMOEBA_FULL ||
7682              new_element == EL_BD_AMOEBA ||
7683              new_element == EL_AMOEBA_GROWING))
7684         {
7685           AmoebaCnt[AmoebaNr[newx][newy]]--;
7686           AmoebaCnt2[AmoebaNr[newx][newy]]--;
7687         }
7688
7689         if (IS_MOVING(newx, newy))
7690           RemoveMovingField(newx, newy);
7691         else
7692         {
7693           RemoveField(newx, newy);
7694           DrawLevelField(newx, newy);
7695         }
7696
7697         /* if digged element was about to explode, prevent the explosion */
7698         ExplodeField[newx][newy] = EX_TYPE_NONE;
7699
7700         PlayLevelSoundAction(x, y, action);
7701       }
7702
7703       Store[newx][newy] = EL_EMPTY;
7704 #if 1
7705       /* this makes it possible to leave the removed element again */
7706       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7707         Store[newx][newy] = new_element;
7708 #else
7709       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7710       {
7711         int move_leave_element = element_info[element].move_leave_element;
7712
7713         /* this makes it possible to leave the removed element again */
7714         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7715                              new_element : move_leave_element);
7716       }
7717 #endif
7718
7719       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7720       {
7721         RunnerVisit[x][y] = FrameCounter;
7722         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7723       }
7724     }
7725     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7726     {
7727       if (!IS_FREE(newx, newy))
7728       {
7729         if (IS_PLAYER(x, y))
7730           DrawPlayerField(x, y);
7731         else
7732           DrawLevelField(x, y);
7733
7734         return;
7735       }
7736       else
7737       {
7738         boolean wanna_flame = !RND(10);
7739         int dx = newx - x, dy = newy - y;
7740         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7741         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7742         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7743                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7744         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7745                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7746
7747         if ((wanna_flame ||
7748              IS_CLASSIC_ENEMY(element1) ||
7749              IS_CLASSIC_ENEMY(element2)) &&
7750             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7751             element1 != EL_FLAMES && element2 != EL_FLAMES)
7752         {
7753           ResetGfxAnimation(x, y);
7754           GfxAction[x][y] = ACTION_ATTACKING;
7755
7756           if (IS_PLAYER(x, y))
7757             DrawPlayerField(x, y);
7758           else
7759             DrawLevelField(x, y);
7760
7761           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7762
7763           MovDelay[x][y] = 50;
7764
7765           /* !!! */
7766 #if 0
7767           RemoveField(newx, newy);
7768 #endif
7769           Feld[newx][newy] = EL_FLAMES;
7770           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7771           {
7772 #if 0
7773             RemoveField(newx1, newy1);
7774 #endif
7775             Feld[newx1][newy1] = EL_FLAMES;
7776           }
7777           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7778           {
7779 #if 0
7780             RemoveField(newx2, newy2);
7781 #endif
7782             Feld[newx2][newy2] = EL_FLAMES;
7783           }
7784
7785           return;
7786         }
7787       }
7788     }
7789     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7790              Feld[newx][newy] == EL_DIAMOND)
7791     {
7792       if (IS_MOVING(newx, newy))
7793         RemoveMovingField(newx, newy);
7794       else
7795       {
7796         Feld[newx][newy] = EL_EMPTY;
7797         DrawLevelField(newx, newy);
7798       }
7799
7800       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7801     }
7802     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7803              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7804     {
7805       if (AmoebaNr[newx][newy])
7806       {
7807         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7808         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7809             Feld[newx][newy] == EL_BD_AMOEBA)
7810           AmoebaCnt[AmoebaNr[newx][newy]]--;
7811       }
7812
7813 #if 0
7814       /* !!! test !!! */
7815       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7816       {
7817         RemoveMovingField(newx, newy);
7818       }
7819 #else
7820       if (IS_MOVING(newx, newy))
7821       {
7822         RemoveMovingField(newx, newy);
7823       }
7824 #endif
7825       else
7826       {
7827         Feld[newx][newy] = EL_EMPTY;
7828         DrawLevelField(newx, newy);
7829       }
7830
7831       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7832     }
7833     else if ((element == EL_PACMAN || element == EL_MOLE)
7834              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7835     {
7836       if (AmoebaNr[newx][newy])
7837       {
7838         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7839         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7840             Feld[newx][newy] == EL_BD_AMOEBA)
7841           AmoebaCnt[AmoebaNr[newx][newy]]--;
7842       }
7843
7844       if (element == EL_MOLE)
7845       {
7846         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7847         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7848
7849         ResetGfxAnimation(x, y);
7850         GfxAction[x][y] = ACTION_DIGGING;
7851         DrawLevelField(x, y);
7852
7853         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7854
7855         return;                         /* wait for shrinking amoeba */
7856       }
7857       else      /* element == EL_PACMAN */
7858       {
7859         Feld[newx][newy] = EL_EMPTY;
7860         DrawLevelField(newx, newy);
7861         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7862       }
7863     }
7864     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7865              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7866               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7867     {
7868       /* wait for shrinking amoeba to completely disappear */
7869       return;
7870     }
7871     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7872     {
7873       /* object was running against a wall */
7874
7875       TurnRound(x, y);
7876
7877 #if 0
7878       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7879       if (move_pattern & MV_ANY_DIRECTION &&
7880           move_pattern == MovDir[x][y])
7881       {
7882         int blocking_element =
7883           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7884
7885         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7886                                  MovDir[x][y]);
7887
7888         element = Feld[x][y];   /* element might have changed */
7889       }
7890 #endif
7891
7892       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7893         DrawLevelElementAnimation(x, y, element);
7894
7895       if (DONT_TOUCH(element))
7896         TestIfBadThingTouchesPlayer(x, y);
7897
7898       return;
7899     }
7900
7901     InitMovingField(x, y, MovDir[x][y]);
7902
7903     PlayLevelSoundAction(x, y, ACTION_MOVING);
7904   }
7905
7906   if (MovDir[x][y])
7907     ContinueMoving(x, y);
7908 }
7909
7910 void ContinueMoving(int x, int y)
7911 {
7912   int element = Feld[x][y];
7913   struct ElementInfo *ei = &element_info[element];
7914   int direction = MovDir[x][y];
7915   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7916   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7917   int newx = x + dx, newy = y + dy;
7918   int stored = Store[x][y];
7919   int stored_new = Store[newx][newy];
7920   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7921   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7922   boolean last_line = (newy == lev_fieldy - 1);
7923
7924   MovPos[x][y] += getElementMoveStepsize(x, y);
7925
7926   if (pushed_by_player) /* special case: moving object pushed by player */
7927     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7928
7929   if (ABS(MovPos[x][y]) < TILEX)
7930   {
7931 #if 0
7932     int ee = Feld[x][y];
7933     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7934     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7935
7936     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7937            x, y, ABS(MovPos[x][y]),
7938            ee, gg, ff,
7939            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
7940 #endif
7941
7942     DrawLevelField(x, y);
7943
7944     return;     /* element is still moving */
7945   }
7946
7947   /* element reached destination field */
7948
7949   Feld[x][y] = EL_EMPTY;
7950   Feld[newx][newy] = element;
7951   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
7952
7953   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
7954   {
7955     element = Feld[newx][newy] = EL_ACID;
7956   }
7957   else if (element == EL_MOLE)
7958   {
7959     Feld[x][y] = EL_SAND;
7960
7961     DrawLevelFieldCrumbledSandNeighbours(x, y);
7962   }
7963   else if (element == EL_QUICKSAND_FILLING)
7964   {
7965     element = Feld[newx][newy] = get_next_element(element);
7966     Store[newx][newy] = Store[x][y];
7967   }
7968   else if (element == EL_QUICKSAND_EMPTYING)
7969   {
7970     Feld[x][y] = get_next_element(element);
7971     element = Feld[newx][newy] = Store[x][y];
7972   }
7973   else if (element == EL_QUICKSAND_FAST_FILLING)
7974   {
7975     element = Feld[newx][newy] = get_next_element(element);
7976     Store[newx][newy] = Store[x][y];
7977   }
7978   else if (element == EL_QUICKSAND_FAST_EMPTYING)
7979   {
7980     Feld[x][y] = get_next_element(element);
7981     element = Feld[newx][newy] = Store[x][y];
7982   }
7983   else if (element == EL_MAGIC_WALL_FILLING)
7984   {
7985     element = Feld[newx][newy] = get_next_element(element);
7986     if (!game.magic_wall_active)
7987       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7988     Store[newx][newy] = Store[x][y];
7989   }
7990   else if (element == EL_MAGIC_WALL_EMPTYING)
7991   {
7992     Feld[x][y] = get_next_element(element);
7993     if (!game.magic_wall_active)
7994       Feld[x][y] = EL_MAGIC_WALL_DEAD;
7995     element = Feld[newx][newy] = Store[x][y];
7996
7997 #if USE_NEW_CUSTOM_VALUE
7998     InitField(newx, newy, FALSE);
7999 #endif
8000   }
8001   else if (element == EL_BD_MAGIC_WALL_FILLING)
8002   {
8003     element = Feld[newx][newy] = get_next_element(element);
8004     if (!game.magic_wall_active)
8005       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8006     Store[newx][newy] = Store[x][y];
8007   }
8008   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8009   {
8010     Feld[x][y] = get_next_element(element);
8011     if (!game.magic_wall_active)
8012       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8013     element = Feld[newx][newy] = Store[x][y];
8014
8015 #if USE_NEW_CUSTOM_VALUE
8016     InitField(newx, newy, FALSE);
8017 #endif
8018   }
8019   else if (element == EL_DC_MAGIC_WALL_FILLING)
8020   {
8021     element = Feld[newx][newy] = get_next_element(element);
8022     if (!game.magic_wall_active)
8023       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8024     Store[newx][newy] = Store[x][y];
8025   }
8026   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8027   {
8028     Feld[x][y] = get_next_element(element);
8029     if (!game.magic_wall_active)
8030       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8031     element = Feld[newx][newy] = Store[x][y];
8032
8033 #if USE_NEW_CUSTOM_VALUE
8034     InitField(newx, newy, FALSE);
8035 #endif
8036   }
8037   else if (element == EL_AMOEBA_DROPPING)
8038   {
8039     Feld[x][y] = get_next_element(element);
8040     element = Feld[newx][newy] = Store[x][y];
8041   }
8042   else if (element == EL_SOKOBAN_OBJECT)
8043   {
8044     if (Back[x][y])
8045       Feld[x][y] = Back[x][y];
8046
8047     if (Back[newx][newy])
8048       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8049
8050     Back[x][y] = Back[newx][newy] = 0;
8051   }
8052
8053   Store[x][y] = EL_EMPTY;
8054   MovPos[x][y] = 0;
8055   MovDir[x][y] = 0;
8056   MovDelay[x][y] = 0;
8057
8058   MovDelay[newx][newy] = 0;
8059
8060   if (CAN_CHANGE_OR_HAS_ACTION(element))
8061   {
8062     /* copy element change control values to new field */
8063     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8064     ChangePage[newx][newy]  = ChangePage[x][y];
8065     ChangeCount[newx][newy] = ChangeCount[x][y];
8066     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8067   }
8068
8069 #if USE_NEW_CUSTOM_VALUE
8070     CustomValue[newx][newy] = CustomValue[x][y];
8071 #endif
8072
8073   ChangeDelay[x][y] = 0;
8074   ChangePage[x][y] = -1;
8075   ChangeCount[x][y] = 0;
8076   ChangeEvent[x][y] = -1;
8077
8078 #if USE_NEW_CUSTOM_VALUE
8079   CustomValue[x][y] = 0;
8080 #endif
8081
8082   /* copy animation control values to new field */
8083   GfxFrame[newx][newy]  = GfxFrame[x][y];
8084   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8085   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8086   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8087
8088   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8089
8090   /* some elements can leave other elements behind after moving */
8091 #if 1
8092   if (ei->move_leave_element != EL_EMPTY &&
8093       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8094       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8095 #else
8096   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8097       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8098       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8099 #endif
8100   {
8101     int move_leave_element = ei->move_leave_element;
8102
8103 #if 1
8104 #if 1
8105     /* this makes it possible to leave the removed element again */
8106     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8107       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8108 #else
8109     /* this makes it possible to leave the removed element again */
8110     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8111       move_leave_element = stored;
8112 #endif
8113 #else
8114     /* this makes it possible to leave the removed element again */
8115     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8116         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8117       move_leave_element = stored;
8118 #endif
8119
8120     Feld[x][y] = move_leave_element;
8121
8122     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8123       MovDir[x][y] = direction;
8124
8125     InitField(x, y, FALSE);
8126
8127     if (GFX_CRUMBLED(Feld[x][y]))
8128       DrawLevelFieldCrumbledSandNeighbours(x, y);
8129
8130     if (ELEM_IS_PLAYER(move_leave_element))
8131       RelocatePlayer(x, y, move_leave_element);
8132   }
8133
8134   /* do this after checking for left-behind element */
8135   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8136
8137   if (!CAN_MOVE(element) ||
8138       (CAN_FALL(element) && direction == MV_DOWN &&
8139        (element == EL_SPRING ||
8140         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8141         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8142     GfxDir[x][y] = MovDir[newx][newy] = 0;
8143
8144   DrawLevelField(x, y);
8145   DrawLevelField(newx, newy);
8146
8147   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8148
8149   /* prevent pushed element from moving on in pushed direction */
8150   if (pushed_by_player && CAN_MOVE(element) &&
8151       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8152       !(element_info[element].move_pattern & direction))
8153     TurnRound(newx, newy);
8154
8155   /* prevent elements on conveyor belt from moving on in last direction */
8156   if (pushed_by_conveyor && CAN_FALL(element) &&
8157       direction & MV_HORIZONTAL)
8158     MovDir[newx][newy] = 0;
8159
8160   if (!pushed_by_player)
8161   {
8162     int nextx = newx + dx, nexty = newy + dy;
8163     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8164
8165     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8166
8167     if (CAN_FALL(element) && direction == MV_DOWN)
8168       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8169
8170     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8171       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8172
8173 #if USE_FIX_IMPACT_COLLISION
8174     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8175       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8176 #endif
8177   }
8178
8179   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8180   {
8181     TestIfBadThingTouchesPlayer(newx, newy);
8182     TestIfBadThingTouchesFriend(newx, newy);
8183
8184     if (!IS_CUSTOM_ELEMENT(element))
8185       TestIfBadThingTouchesOtherBadThing(newx, newy);
8186   }
8187   else if (element == EL_PENGUIN)
8188     TestIfFriendTouchesBadThing(newx, newy);
8189
8190   /* give the player one last chance (one more frame) to move away */
8191   if (CAN_FALL(element) && direction == MV_DOWN &&
8192       (last_line || (!IS_FREE(x, newy + 1) &&
8193                      (!IS_PLAYER(x, newy + 1) ||
8194                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8195     Impact(x, newy);
8196
8197   if (pushed_by_player && !game.use_change_when_pushing_bug)
8198   {
8199     int push_side = MV_DIR_OPPOSITE(direction);
8200     struct PlayerInfo *player = PLAYERINFO(x, y);
8201
8202     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8203                                player->index_bit, push_side);
8204     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8205                                         player->index_bit, push_side);
8206   }
8207
8208   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8209     MovDelay[newx][newy] = 1;
8210
8211   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8212
8213   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8214
8215 #if 0
8216   if (ChangePage[newx][newy] != -1)             /* delayed change */
8217   {
8218     int page = ChangePage[newx][newy];
8219     struct ElementChangeInfo *change = &ei->change_page[page];
8220
8221     ChangePage[newx][newy] = -1;
8222
8223     if (change->can_change)
8224     {
8225       if (ChangeElement(newx, newy, element, page))
8226       {
8227         if (change->post_change_function)
8228           change->post_change_function(newx, newy);
8229       }
8230     }
8231
8232     if (change->has_action)
8233       ExecuteCustomElementAction(newx, newy, element, page);
8234   }
8235 #endif
8236
8237   TestIfElementHitsCustomElement(newx, newy, direction);
8238   TestIfPlayerTouchesCustomElement(newx, newy);
8239   TestIfElementTouchesCustomElement(newx, newy);
8240
8241   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8242       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8243     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8244                              MV_DIR_OPPOSITE(direction));
8245 }
8246
8247 int AmoebeNachbarNr(int ax, int ay)
8248 {
8249   int i;
8250   int element = Feld[ax][ay];
8251   int group_nr = 0;
8252   static int xy[4][2] =
8253   {
8254     { 0, -1 },
8255     { -1, 0 },
8256     { +1, 0 },
8257     { 0, +1 }
8258   };
8259
8260   for (i = 0; i < NUM_DIRECTIONS; i++)
8261   {
8262     int x = ax + xy[i][0];
8263     int y = ay + xy[i][1];
8264
8265     if (!IN_LEV_FIELD(x, y))
8266       continue;
8267
8268     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8269       group_nr = AmoebaNr[x][y];
8270   }
8271
8272   return group_nr;
8273 }
8274
8275 void AmoebenVereinigen(int ax, int ay)
8276 {
8277   int i, x, y, xx, yy;
8278   int new_group_nr = AmoebaNr[ax][ay];
8279   static int xy[4][2] =
8280   {
8281     { 0, -1 },
8282     { -1, 0 },
8283     { +1, 0 },
8284     { 0, +1 }
8285   };
8286
8287   if (new_group_nr == 0)
8288     return;
8289
8290   for (i = 0; i < NUM_DIRECTIONS; i++)
8291   {
8292     x = ax + xy[i][0];
8293     y = ay + xy[i][1];
8294
8295     if (!IN_LEV_FIELD(x, y))
8296       continue;
8297
8298     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8299          Feld[x][y] == EL_BD_AMOEBA ||
8300          Feld[x][y] == EL_AMOEBA_DEAD) &&
8301         AmoebaNr[x][y] != new_group_nr)
8302     {
8303       int old_group_nr = AmoebaNr[x][y];
8304
8305       if (old_group_nr == 0)
8306         return;
8307
8308       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8309       AmoebaCnt[old_group_nr] = 0;
8310       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8311       AmoebaCnt2[old_group_nr] = 0;
8312
8313       SCAN_PLAYFIELD(xx, yy)
8314       {
8315         if (AmoebaNr[xx][yy] == old_group_nr)
8316           AmoebaNr[xx][yy] = new_group_nr;
8317       }
8318     }
8319   }
8320 }
8321
8322 void AmoebeUmwandeln(int ax, int ay)
8323 {
8324   int i, x, y;
8325
8326   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8327   {
8328     int group_nr = AmoebaNr[ax][ay];
8329
8330 #ifdef DEBUG
8331     if (group_nr == 0)
8332     {
8333       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8334       printf("AmoebeUmwandeln(): This should never happen!\n");
8335       return;
8336     }
8337 #endif
8338
8339     SCAN_PLAYFIELD(x, y)
8340     {
8341       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8342       {
8343         AmoebaNr[x][y] = 0;
8344         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8345       }
8346     }
8347
8348     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8349                             SND_AMOEBA_TURNING_TO_GEM :
8350                             SND_AMOEBA_TURNING_TO_ROCK));
8351     Bang(ax, ay);
8352   }
8353   else
8354   {
8355     static int xy[4][2] =
8356     {
8357       { 0, -1 },
8358       { -1, 0 },
8359       { +1, 0 },
8360       { 0, +1 }
8361     };
8362
8363     for (i = 0; i < NUM_DIRECTIONS; i++)
8364     {
8365       x = ax + xy[i][0];
8366       y = ay + xy[i][1];
8367
8368       if (!IN_LEV_FIELD(x, y))
8369         continue;
8370
8371       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8372       {
8373         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8374                               SND_AMOEBA_TURNING_TO_GEM :
8375                               SND_AMOEBA_TURNING_TO_ROCK));
8376         Bang(x, y);
8377       }
8378     }
8379   }
8380 }
8381
8382 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8383 {
8384   int x, y;
8385   int group_nr = AmoebaNr[ax][ay];
8386   boolean done = FALSE;
8387
8388 #ifdef DEBUG
8389   if (group_nr == 0)
8390   {
8391     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8392     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8393     return;
8394   }
8395 #endif
8396
8397   SCAN_PLAYFIELD(x, y)
8398   {
8399     if (AmoebaNr[x][y] == group_nr &&
8400         (Feld[x][y] == EL_AMOEBA_DEAD ||
8401          Feld[x][y] == EL_BD_AMOEBA ||
8402          Feld[x][y] == EL_AMOEBA_GROWING))
8403     {
8404       AmoebaNr[x][y] = 0;
8405       Feld[x][y] = new_element;
8406       InitField(x, y, FALSE);
8407       DrawLevelField(x, y);
8408       done = TRUE;
8409     }
8410   }
8411
8412   if (done)
8413     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8414                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8415                             SND_BD_AMOEBA_TURNING_TO_GEM));
8416 }
8417
8418 void AmoebeWaechst(int x, int y)
8419 {
8420   static unsigned long sound_delay = 0;
8421   static unsigned long sound_delay_value = 0;
8422
8423   if (!MovDelay[x][y])          /* start new growing cycle */
8424   {
8425     MovDelay[x][y] = 7;
8426
8427     if (DelayReached(&sound_delay, sound_delay_value))
8428     {
8429       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8430       sound_delay_value = 30;
8431     }
8432   }
8433
8434   if (MovDelay[x][y])           /* wait some time before growing bigger */
8435   {
8436     MovDelay[x][y]--;
8437     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8438     {
8439       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8440                                            6 - MovDelay[x][y]);
8441
8442       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8443     }
8444
8445     if (!MovDelay[x][y])
8446     {
8447       Feld[x][y] = Store[x][y];
8448       Store[x][y] = 0;
8449       DrawLevelField(x, y);
8450     }
8451   }
8452 }
8453
8454 void AmoebaDisappearing(int x, int y)
8455 {
8456   static unsigned long sound_delay = 0;
8457   static unsigned long sound_delay_value = 0;
8458
8459   if (!MovDelay[x][y])          /* start new shrinking cycle */
8460   {
8461     MovDelay[x][y] = 7;
8462
8463     if (DelayReached(&sound_delay, sound_delay_value))
8464       sound_delay_value = 30;
8465   }
8466
8467   if (MovDelay[x][y])           /* wait some time before shrinking */
8468   {
8469     MovDelay[x][y]--;
8470     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8471     {
8472       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8473                                            6 - MovDelay[x][y]);
8474
8475       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8476     }
8477
8478     if (!MovDelay[x][y])
8479     {
8480       Feld[x][y] = EL_EMPTY;
8481       DrawLevelField(x, y);
8482
8483       /* don't let mole enter this field in this cycle;
8484          (give priority to objects falling to this field from above) */
8485       Stop[x][y] = TRUE;
8486     }
8487   }
8488 }
8489
8490 void AmoebeAbleger(int ax, int ay)
8491 {
8492   int i;
8493   int element = Feld[ax][ay];
8494   int graphic = el2img(element);
8495   int newax = ax, neway = ay;
8496   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8497   static int xy[4][2] =
8498   {
8499     { 0, -1 },
8500     { -1, 0 },
8501     { +1, 0 },
8502     { 0, +1 }
8503   };
8504
8505   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8506   {
8507     Feld[ax][ay] = EL_AMOEBA_DEAD;
8508     DrawLevelField(ax, ay);
8509     return;
8510   }
8511
8512   if (IS_ANIMATED(graphic))
8513     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8514
8515   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8516     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8517
8518   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8519   {
8520     MovDelay[ax][ay]--;
8521     if (MovDelay[ax][ay])
8522       return;
8523   }
8524
8525   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8526   {
8527     int start = RND(4);
8528     int x = ax + xy[start][0];
8529     int y = ay + xy[start][1];
8530
8531     if (!IN_LEV_FIELD(x, y))
8532       return;
8533
8534     if (IS_FREE(x, y) ||
8535         CAN_GROW_INTO(Feld[x][y]) ||
8536         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8537         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8538     {
8539       newax = x;
8540       neway = y;
8541     }
8542
8543     if (newax == ax && neway == ay)
8544       return;
8545   }
8546   else                          /* normal or "filled" (BD style) amoeba */
8547   {
8548     int start = RND(4);
8549     boolean waiting_for_player = FALSE;
8550
8551     for (i = 0; i < NUM_DIRECTIONS; i++)
8552     {
8553       int j = (start + i) % 4;
8554       int x = ax + xy[j][0];
8555       int y = ay + xy[j][1];
8556
8557       if (!IN_LEV_FIELD(x, y))
8558         continue;
8559
8560       if (IS_FREE(x, y) ||
8561           CAN_GROW_INTO(Feld[x][y]) ||
8562           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8563           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8564       {
8565         newax = x;
8566         neway = y;
8567         break;
8568       }
8569       else if (IS_PLAYER(x, y))
8570         waiting_for_player = TRUE;
8571     }
8572
8573     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8574     {
8575       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8576       {
8577         Feld[ax][ay] = EL_AMOEBA_DEAD;
8578         DrawLevelField(ax, ay);
8579         AmoebaCnt[AmoebaNr[ax][ay]]--;
8580
8581         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8582         {
8583           if (element == EL_AMOEBA_FULL)
8584             AmoebeUmwandeln(ax, ay);
8585           else if (element == EL_BD_AMOEBA)
8586             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8587         }
8588       }
8589       return;
8590     }
8591     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8592     {
8593       /* amoeba gets larger by growing in some direction */
8594
8595       int new_group_nr = AmoebaNr[ax][ay];
8596
8597 #ifdef DEBUG
8598   if (new_group_nr == 0)
8599   {
8600     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8601     printf("AmoebeAbleger(): This should never happen!\n");
8602     return;
8603   }
8604 #endif
8605
8606       AmoebaNr[newax][neway] = new_group_nr;
8607       AmoebaCnt[new_group_nr]++;
8608       AmoebaCnt2[new_group_nr]++;
8609
8610       /* if amoeba touches other amoeba(s) after growing, unify them */
8611       AmoebenVereinigen(newax, neway);
8612
8613       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8614       {
8615         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8616         return;
8617       }
8618     }
8619   }
8620
8621   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8622       (neway == lev_fieldy - 1 && newax != ax))
8623   {
8624     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8625     Store[newax][neway] = element;
8626   }
8627   else if (neway == ay || element == EL_EMC_DRIPPER)
8628   {
8629     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8630
8631     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8632   }
8633   else
8634   {
8635     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8636     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8637     Store[ax][ay] = EL_AMOEBA_DROP;
8638     ContinueMoving(ax, ay);
8639     return;
8640   }
8641
8642   DrawLevelField(newax, neway);
8643 }
8644
8645 void Life(int ax, int ay)
8646 {
8647   int x1, y1, x2, y2;
8648   int life_time = 40;
8649   int element = Feld[ax][ay];
8650   int graphic = el2img(element);
8651   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8652                          level.biomaze);
8653   boolean changed = FALSE;
8654
8655   if (IS_ANIMATED(graphic))
8656     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8657
8658   if (Stop[ax][ay])
8659     return;
8660
8661   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8662     MovDelay[ax][ay] = life_time;
8663
8664   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8665   {
8666     MovDelay[ax][ay]--;
8667     if (MovDelay[ax][ay])
8668       return;
8669   }
8670
8671   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8672   {
8673     int xx = ax+x1, yy = ay+y1;
8674     int nachbarn = 0;
8675
8676     if (!IN_LEV_FIELD(xx, yy))
8677       continue;
8678
8679     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8680     {
8681       int x = xx+x2, y = yy+y2;
8682
8683       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8684         continue;
8685
8686       if (((Feld[x][y] == element ||
8687             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8688            !Stop[x][y]) ||
8689           (IS_FREE(x, y) && Stop[x][y]))
8690         nachbarn++;
8691     }
8692
8693     if (xx == ax && yy == ay)           /* field in the middle */
8694     {
8695       if (nachbarn < life_parameter[0] ||
8696           nachbarn > life_parameter[1])
8697       {
8698         Feld[xx][yy] = EL_EMPTY;
8699         if (!Stop[xx][yy])
8700           DrawLevelField(xx, yy);
8701         Stop[xx][yy] = TRUE;
8702         changed = TRUE;
8703       }
8704     }
8705     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8706     {                                   /* free border field */
8707       if (nachbarn >= life_parameter[2] &&
8708           nachbarn <= life_parameter[3])
8709       {
8710         Feld[xx][yy] = element;
8711         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8712         if (!Stop[xx][yy])
8713           DrawLevelField(xx, yy);
8714         Stop[xx][yy] = TRUE;
8715         changed = TRUE;
8716       }
8717     }
8718   }
8719
8720   if (changed)
8721     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8722                    SND_GAME_OF_LIFE_GROWING);
8723 }
8724
8725 static void InitRobotWheel(int x, int y)
8726 {
8727   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8728 }
8729
8730 static void RunRobotWheel(int x, int y)
8731 {
8732   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8733 }
8734
8735 static void StopRobotWheel(int x, int y)
8736 {
8737   if (ZX == x && ZY == y)
8738     ZX = ZY = -1;
8739 }
8740
8741 static void InitTimegateWheel(int x, int y)
8742 {
8743   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8744 }
8745
8746 static void RunTimegateWheel(int x, int y)
8747 {
8748   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8749 }
8750
8751 static void InitMagicBallDelay(int x, int y)
8752 {
8753 #if 1
8754   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8755 #else
8756   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8757 #endif
8758 }
8759
8760 static void ActivateMagicBall(int bx, int by)
8761 {
8762   int x, y;
8763
8764   if (level.ball_random)
8765   {
8766     int pos_border = RND(8);    /* select one of the eight border elements */
8767     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8768     int xx = pos_content % 3;
8769     int yy = pos_content / 3;
8770
8771     x = bx - 1 + xx;
8772     y = by - 1 + yy;
8773
8774     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8775       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8776   }
8777   else
8778   {
8779     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8780     {
8781       int xx = x - bx + 1;
8782       int yy = y - by + 1;
8783
8784       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8785         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8786     }
8787   }
8788
8789   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8790 }
8791
8792 void CheckExit(int x, int y)
8793 {
8794   if (local_player->gems_still_needed > 0 ||
8795       local_player->sokobanfields_still_needed > 0 ||
8796       local_player->lights_still_needed > 0)
8797   {
8798     int element = Feld[x][y];
8799     int graphic = el2img(element);
8800
8801     if (IS_ANIMATED(graphic))
8802       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8803
8804     return;
8805   }
8806
8807   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8808     return;
8809
8810   Feld[x][y] = EL_EXIT_OPENING;
8811
8812   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8813 }
8814
8815 void CheckExitEM(int x, int y)
8816 {
8817   if (local_player->gems_still_needed > 0 ||
8818       local_player->sokobanfields_still_needed > 0 ||
8819       local_player->lights_still_needed > 0)
8820   {
8821     int element = Feld[x][y];
8822     int graphic = el2img(element);
8823
8824     if (IS_ANIMATED(graphic))
8825       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8826
8827     return;
8828   }
8829
8830   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8831     return;
8832
8833   Feld[x][y] = EL_EM_EXIT_OPENING;
8834
8835   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8836 }
8837
8838 void CheckExitSteel(int x, int y)
8839 {
8840   if (local_player->gems_still_needed > 0 ||
8841       local_player->sokobanfields_still_needed > 0 ||
8842       local_player->lights_still_needed > 0)
8843   {
8844     int element = Feld[x][y];
8845     int graphic = el2img(element);
8846
8847     if (IS_ANIMATED(graphic))
8848       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8849
8850     return;
8851   }
8852
8853   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8854     return;
8855
8856   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8857
8858   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8859 }
8860
8861 void CheckExitSteelEM(int x, int y)
8862 {
8863   if (local_player->gems_still_needed > 0 ||
8864       local_player->sokobanfields_still_needed > 0 ||
8865       local_player->lights_still_needed > 0)
8866   {
8867     int element = Feld[x][y];
8868     int graphic = el2img(element);
8869
8870     if (IS_ANIMATED(graphic))
8871       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8872
8873     return;
8874   }
8875
8876   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8877     return;
8878
8879   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8880
8881   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8882 }
8883
8884 void CheckExitSP(int x, int y)
8885 {
8886   if (local_player->gems_still_needed > 0)
8887   {
8888     int element = Feld[x][y];
8889     int graphic = el2img(element);
8890
8891     if (IS_ANIMATED(graphic))
8892       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8893
8894     return;
8895   }
8896
8897   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8898     return;
8899
8900   Feld[x][y] = EL_SP_EXIT_OPENING;
8901
8902   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8903 }
8904
8905 static void CloseAllOpenTimegates()
8906 {
8907   int x, y;
8908
8909   SCAN_PLAYFIELD(x, y)
8910   {
8911     int element = Feld[x][y];
8912
8913     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8914     {
8915       Feld[x][y] = EL_TIMEGATE_CLOSING;
8916
8917       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8918     }
8919   }
8920 }
8921
8922 void DrawTwinkleOnField(int x, int y)
8923 {
8924   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8925     return;
8926
8927   if (Feld[x][y] == EL_BD_DIAMOND)
8928     return;
8929
8930   if (MovDelay[x][y] == 0)      /* next animation frame */
8931     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8932
8933   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8934   {
8935     MovDelay[x][y]--;
8936
8937     if (setup.direct_draw && MovDelay[x][y])
8938       SetDrawtoField(DRAW_BUFFERED);
8939
8940     DrawLevelElementAnimation(x, y, Feld[x][y]);
8941
8942     if (MovDelay[x][y] != 0)
8943     {
8944       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8945                                            10 - MovDelay[x][y]);
8946
8947       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8948
8949       if (setup.direct_draw)
8950       {
8951         int dest_x, dest_y;
8952
8953         dest_x = FX + SCREENX(x) * TILEX;
8954         dest_y = FY + SCREENY(y) * TILEY;
8955
8956         BlitBitmap(drawto_field, window,
8957                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8958         SetDrawtoField(DRAW_DIRECT);
8959       }
8960     }
8961   }
8962 }
8963
8964 void MauerWaechst(int x, int y)
8965 {
8966   int delay = 6;
8967
8968   if (!MovDelay[x][y])          /* next animation frame */
8969     MovDelay[x][y] = 3 * delay;
8970
8971   if (MovDelay[x][y])           /* wait some time before next frame */
8972   {
8973     MovDelay[x][y]--;
8974
8975     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8976     {
8977       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8978       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8979
8980       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8981     }
8982
8983     if (!MovDelay[x][y])
8984     {
8985       if (MovDir[x][y] == MV_LEFT)
8986       {
8987         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8988           DrawLevelField(x - 1, y);
8989       }
8990       else if (MovDir[x][y] == MV_RIGHT)
8991       {
8992         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8993           DrawLevelField(x + 1, y);
8994       }
8995       else if (MovDir[x][y] == MV_UP)
8996       {
8997         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8998           DrawLevelField(x, y - 1);
8999       }
9000       else
9001       {
9002         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9003           DrawLevelField(x, y + 1);
9004       }
9005
9006       Feld[x][y] = Store[x][y];
9007       Store[x][y] = 0;
9008       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9009       DrawLevelField(x, y);
9010     }
9011   }
9012 }
9013
9014 void MauerAbleger(int ax, int ay)
9015 {
9016   int element = Feld[ax][ay];
9017   int graphic = el2img(element);
9018   boolean oben_frei = FALSE, unten_frei = FALSE;
9019   boolean links_frei = FALSE, rechts_frei = FALSE;
9020   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9021   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9022   boolean new_wall = FALSE;
9023
9024   if (IS_ANIMATED(graphic))
9025     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9026
9027   if (!MovDelay[ax][ay])        /* start building new wall */
9028     MovDelay[ax][ay] = 6;
9029
9030   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9031   {
9032     MovDelay[ax][ay]--;
9033     if (MovDelay[ax][ay])
9034       return;
9035   }
9036
9037   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9038     oben_frei = TRUE;
9039   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9040     unten_frei = TRUE;
9041   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9042     links_frei = TRUE;
9043   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9044     rechts_frei = TRUE;
9045
9046   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9047       element == EL_EXPANDABLE_WALL_ANY)
9048   {
9049     if (oben_frei)
9050     {
9051       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9052       Store[ax][ay-1] = element;
9053       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9054       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9055         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9056                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9057       new_wall = TRUE;
9058     }
9059     if (unten_frei)
9060     {
9061       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9062       Store[ax][ay+1] = element;
9063       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9064       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9065         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9066                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9067       new_wall = TRUE;
9068     }
9069   }
9070
9071   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9072       element == EL_EXPANDABLE_WALL_ANY ||
9073       element == EL_EXPANDABLE_WALL ||
9074       element == EL_BD_EXPANDABLE_WALL)
9075   {
9076     if (links_frei)
9077     {
9078       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9079       Store[ax-1][ay] = element;
9080       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9081       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9082         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9083                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9084       new_wall = TRUE;
9085     }
9086
9087     if (rechts_frei)
9088     {
9089       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9090       Store[ax+1][ay] = element;
9091       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9092       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9093         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9094                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9095       new_wall = TRUE;
9096     }
9097   }
9098
9099   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9100     DrawLevelField(ax, ay);
9101
9102   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9103     oben_massiv = TRUE;
9104   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9105     unten_massiv = TRUE;
9106   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9107     links_massiv = TRUE;
9108   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9109     rechts_massiv = TRUE;
9110
9111   if (((oben_massiv && unten_massiv) ||
9112        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9113        element == EL_EXPANDABLE_WALL) &&
9114       ((links_massiv && rechts_massiv) ||
9115        element == EL_EXPANDABLE_WALL_VERTICAL))
9116     Feld[ax][ay] = EL_WALL;
9117
9118   if (new_wall)
9119     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9120 }
9121
9122 void MauerAblegerStahl(int ax, int ay)
9123 {
9124   int element = Feld[ax][ay];
9125   int graphic = el2img(element);
9126   boolean oben_frei = FALSE, unten_frei = FALSE;
9127   boolean links_frei = FALSE, rechts_frei = FALSE;
9128   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9129   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9130   boolean new_wall = FALSE;
9131
9132   if (IS_ANIMATED(graphic))
9133     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9134
9135   if (!MovDelay[ax][ay])        /* start building new wall */
9136     MovDelay[ax][ay] = 6;
9137
9138   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9139   {
9140     MovDelay[ax][ay]--;
9141     if (MovDelay[ax][ay])
9142       return;
9143   }
9144
9145   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9146     oben_frei = TRUE;
9147   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9148     unten_frei = TRUE;
9149   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9150     links_frei = TRUE;
9151   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9152     rechts_frei = TRUE;
9153
9154   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9155       element == EL_EXPANDABLE_STEELWALL_ANY)
9156   {
9157     if (oben_frei)
9158     {
9159       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9160       Store[ax][ay-1] = element;
9161       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9162       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9163         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9164                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9165       new_wall = TRUE;
9166     }
9167     if (unten_frei)
9168     {
9169       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9170       Store[ax][ay+1] = element;
9171       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9172       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9173         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9174                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9175       new_wall = TRUE;
9176     }
9177   }
9178
9179   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9180       element == EL_EXPANDABLE_STEELWALL_ANY)
9181   {
9182     if (links_frei)
9183     {
9184       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9185       Store[ax-1][ay] = element;
9186       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9187       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9188         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9189                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9190       new_wall = TRUE;
9191     }
9192
9193     if (rechts_frei)
9194     {
9195       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9196       Store[ax+1][ay] = element;
9197       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9198       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9199         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9200                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9201       new_wall = TRUE;
9202     }
9203   }
9204
9205   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9206     oben_massiv = TRUE;
9207   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9208     unten_massiv = TRUE;
9209   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9210     links_massiv = TRUE;
9211   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9212     rechts_massiv = TRUE;
9213
9214   if (((oben_massiv && unten_massiv) ||
9215        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9216       ((links_massiv && rechts_massiv) ||
9217        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9218     Feld[ax][ay] = EL_WALL;
9219
9220   if (new_wall)
9221     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9222 }
9223
9224 void CheckForDragon(int x, int y)
9225 {
9226   int i, j;
9227   boolean dragon_found = FALSE;
9228   static int xy[4][2] =
9229   {
9230     { 0, -1 },
9231     { -1, 0 },
9232     { +1, 0 },
9233     { 0, +1 }
9234   };
9235
9236   for (i = 0; i < NUM_DIRECTIONS; i++)
9237   {
9238     for (j = 0; j < 4; j++)
9239     {
9240       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9241
9242       if (IN_LEV_FIELD(xx, yy) &&
9243           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9244       {
9245         if (Feld[xx][yy] == EL_DRAGON)
9246           dragon_found = TRUE;
9247       }
9248       else
9249         break;
9250     }
9251   }
9252
9253   if (!dragon_found)
9254   {
9255     for (i = 0; i < NUM_DIRECTIONS; i++)
9256     {
9257       for (j = 0; j < 3; j++)
9258       {
9259         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9260   
9261         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9262         {
9263           Feld[xx][yy] = EL_EMPTY;
9264           DrawLevelField(xx, yy);
9265         }
9266         else
9267           break;
9268       }
9269     }
9270   }
9271 }
9272
9273 static void InitBuggyBase(int x, int y)
9274 {
9275   int element = Feld[x][y];
9276   int activating_delay = FRAMES_PER_SECOND / 4;
9277
9278   ChangeDelay[x][y] =
9279     (element == EL_SP_BUGGY_BASE ?
9280      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9281      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9282      activating_delay :
9283      element == EL_SP_BUGGY_BASE_ACTIVE ?
9284      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9285 }
9286
9287 static void WarnBuggyBase(int x, int y)
9288 {
9289   int i;
9290   static int xy[4][2] =
9291   {
9292     { 0, -1 },
9293     { -1, 0 },
9294     { +1, 0 },
9295     { 0, +1 }
9296   };
9297
9298   for (i = 0; i < NUM_DIRECTIONS; i++)
9299   {
9300     int xx = x + xy[i][0];
9301     int yy = y + xy[i][1];
9302
9303     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9304     {
9305       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9306
9307       break;
9308     }
9309   }
9310 }
9311
9312 static void InitTrap(int x, int y)
9313 {
9314   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9315 }
9316
9317 static void ActivateTrap(int x, int y)
9318 {
9319   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9320 }
9321
9322 static void ChangeActiveTrap(int x, int y)
9323 {
9324   int graphic = IMG_TRAP_ACTIVE;
9325
9326   /* if new animation frame was drawn, correct crumbled sand border */
9327   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9328     DrawLevelFieldCrumbledSand(x, y);
9329 }
9330
9331 static int getSpecialActionElement(int element, int number, int base_element)
9332 {
9333   return (element != EL_EMPTY ? element :
9334           number != -1 ? base_element + number - 1 :
9335           EL_EMPTY);
9336 }
9337
9338 static int getModifiedActionNumber(int value_old, int operator, int operand,
9339                                    int value_min, int value_max)
9340 {
9341   int value_new = (operator == CA_MODE_SET      ? operand :
9342                    operator == CA_MODE_ADD      ? value_old + operand :
9343                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9344                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9345                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9346                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9347                    value_old);
9348
9349   return (value_new < value_min ? value_min :
9350           value_new > value_max ? value_max :
9351           value_new);
9352 }
9353
9354 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9355 {
9356   struct ElementInfo *ei = &element_info[element];
9357   struct ElementChangeInfo *change = &ei->change_page[page];
9358   int target_element = change->target_element;
9359   int action_type = change->action_type;
9360   int action_mode = change->action_mode;
9361   int action_arg = change->action_arg;
9362   int i;
9363
9364   if (!change->has_action)
9365     return;
9366
9367   /* ---------- determine action paramater values -------------------------- */
9368
9369   int level_time_value =
9370     (level.time > 0 ? TimeLeft :
9371      TimePlayed);
9372
9373   int action_arg_element =
9374     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9375      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9376      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9377      EL_EMPTY);
9378
9379   int action_arg_direction =
9380     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9381      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9382      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9383      change->actual_trigger_side :
9384      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9385      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9386      MV_NONE);
9387
9388   int action_arg_number_min =
9389     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9390      CA_ARG_MIN);
9391
9392   int action_arg_number_max =
9393     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9394      action_type == CA_SET_LEVEL_GEMS ? 999 :
9395      action_type == CA_SET_LEVEL_TIME ? 9999 :
9396      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9397      action_type == CA_SET_CE_VALUE ? 9999 :
9398      action_type == CA_SET_CE_SCORE ? 9999 :
9399      CA_ARG_MAX);
9400
9401   int action_arg_number_reset =
9402     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9403      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9404      action_type == CA_SET_LEVEL_TIME ? level.time :
9405      action_type == CA_SET_LEVEL_SCORE ? 0 :
9406 #if USE_NEW_CUSTOM_VALUE
9407      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9408 #else
9409      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9410 #endif
9411      action_type == CA_SET_CE_SCORE ? 0 :
9412      0);
9413
9414   int action_arg_number =
9415     (action_arg <= CA_ARG_MAX ? action_arg :
9416      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9417      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9418      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9419      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9420      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9421      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9422 #if USE_NEW_CUSTOM_VALUE
9423      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9424 #else
9425      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9426 #endif
9427      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9428      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9429      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9430      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9431      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9432      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9433      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9434      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9435      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9436      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9437      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9438      -1);
9439
9440   int action_arg_number_old =
9441     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9442      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9443      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9444      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9445      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9446      0);
9447
9448   int action_arg_number_new =
9449     getModifiedActionNumber(action_arg_number_old,
9450                             action_mode, action_arg_number,
9451                             action_arg_number_min, action_arg_number_max);
9452
9453   int trigger_player_bits =
9454     (change->actual_trigger_player >= EL_PLAYER_1 &&
9455      change->actual_trigger_player <= EL_PLAYER_4 ?
9456      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9457      PLAYER_BITS_ANY);
9458
9459   int action_arg_player_bits =
9460     (action_arg >= CA_ARG_PLAYER_1 &&
9461      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9462      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9463      PLAYER_BITS_ANY);
9464
9465   /* ---------- execute action  -------------------------------------------- */
9466
9467   switch (action_type)
9468   {
9469     case CA_NO_ACTION:
9470     {
9471       return;
9472     }
9473
9474     /* ---------- level actions  ------------------------------------------- */
9475
9476     case CA_RESTART_LEVEL:
9477     {
9478       game.restart_level = TRUE;
9479
9480       break;
9481     }
9482
9483     case CA_SHOW_ENVELOPE:
9484     {
9485       int element = getSpecialActionElement(action_arg_element,
9486                                             action_arg_number, EL_ENVELOPE_1);
9487
9488       if (IS_ENVELOPE(element))
9489         local_player->show_envelope = element;
9490
9491       break;
9492     }
9493
9494     case CA_SET_LEVEL_TIME:
9495     {
9496       if (level.time > 0)       /* only modify limited time value */
9497       {
9498         TimeLeft = action_arg_number_new;
9499
9500 #if 1
9501         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
9502
9503         DisplayGameControlValues();
9504 #else
9505         DrawGameValue_Time(TimeLeft);
9506 #endif
9507
9508         if (!TimeLeft && setup.time_limit)
9509           for (i = 0; i < MAX_PLAYERS; i++)
9510             KillPlayer(&stored_player[i]);
9511       }
9512
9513       break;
9514     }
9515
9516     case CA_SET_LEVEL_SCORE:
9517     {
9518       local_player->score = action_arg_number_new;
9519
9520 #if 1
9521       game_control_value[GAME_CONTROL_SCORE] = local_player->score;
9522
9523       DisplayGameControlValues();
9524 #else
9525       DrawGameValue_Score(local_player->score);
9526 #endif
9527
9528       break;
9529     }
9530
9531     case CA_SET_LEVEL_GEMS:
9532     {
9533       local_player->gems_still_needed = action_arg_number_new;
9534
9535 #if 1
9536       game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
9537
9538       DisplayGameControlValues();
9539 #else
9540       DrawGameValue_Emeralds(local_player->gems_still_needed);
9541 #endif
9542
9543       break;
9544     }
9545
9546 #if !USE_PLAYER_GRAVITY
9547     case CA_SET_LEVEL_GRAVITY:
9548     {
9549       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
9550                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
9551                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9552                       game.gravity);
9553       break;
9554     }
9555 #endif
9556
9557     case CA_SET_LEVEL_WIND:
9558     {
9559       game.wind_direction = action_arg_direction;
9560
9561       break;
9562     }
9563
9564     /* ---------- player actions  ------------------------------------------ */
9565
9566     case CA_MOVE_PLAYER:
9567     {
9568       /* automatically move to the next field in specified direction */
9569       for (i = 0; i < MAX_PLAYERS; i++)
9570         if (trigger_player_bits & (1 << i))
9571           stored_player[i].programmed_action = action_arg_direction;
9572
9573       break;
9574     }
9575
9576     case CA_EXIT_PLAYER:
9577     {
9578       for (i = 0; i < MAX_PLAYERS; i++)
9579         if (action_arg_player_bits & (1 << i))
9580           PlayerWins(&stored_player[i]);
9581
9582       break;
9583     }
9584
9585     case CA_KILL_PLAYER:
9586     {
9587       for (i = 0; i < MAX_PLAYERS; i++)
9588         if (action_arg_player_bits & (1 << i))
9589           KillPlayer(&stored_player[i]);
9590
9591       break;
9592     }
9593
9594     case CA_SET_PLAYER_KEYS:
9595     {
9596       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9597       int element = getSpecialActionElement(action_arg_element,
9598                                             action_arg_number, EL_KEY_1);
9599
9600       if (IS_KEY(element))
9601       {
9602         for (i = 0; i < MAX_PLAYERS; i++)
9603         {
9604           if (trigger_player_bits & (1 << i))
9605           {
9606             stored_player[i].key[KEY_NR(element)] = key_state;
9607
9608             DrawGameDoorValues();
9609           }
9610         }
9611       }
9612
9613       break;
9614     }
9615
9616     case CA_SET_PLAYER_SPEED:
9617     {
9618       for (i = 0; i < MAX_PLAYERS; i++)
9619       {
9620         if (trigger_player_bits & (1 << i))
9621         {
9622           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9623
9624           if (action_arg == CA_ARG_SPEED_FASTER &&
9625               stored_player[i].cannot_move)
9626           {
9627             action_arg_number = STEPSIZE_VERY_SLOW;
9628           }
9629           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9630                    action_arg == CA_ARG_SPEED_FASTER)
9631           {
9632             action_arg_number = 2;
9633             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9634                            CA_MODE_MULTIPLY);
9635           }
9636           else if (action_arg == CA_ARG_NUMBER_RESET)
9637           {
9638             action_arg_number = level.initial_player_stepsize[i];
9639           }
9640
9641           move_stepsize =
9642             getModifiedActionNumber(move_stepsize,
9643                                     action_mode,
9644                                     action_arg_number,
9645                                     action_arg_number_min,
9646                                     action_arg_number_max);
9647
9648           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9649         }
9650       }
9651
9652       break;
9653     }
9654
9655     case CA_SET_PLAYER_SHIELD:
9656     {
9657       for (i = 0; i < MAX_PLAYERS; i++)
9658       {
9659         if (trigger_player_bits & (1 << i))
9660         {
9661           if (action_arg == CA_ARG_SHIELD_OFF)
9662           {
9663             stored_player[i].shield_normal_time_left = 0;
9664             stored_player[i].shield_deadly_time_left = 0;
9665           }
9666           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9667           {
9668             stored_player[i].shield_normal_time_left = 999999;
9669           }
9670           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9671           {
9672             stored_player[i].shield_normal_time_left = 999999;
9673             stored_player[i].shield_deadly_time_left = 999999;
9674           }
9675         }
9676       }
9677
9678       break;
9679     }
9680
9681 #if USE_PLAYER_GRAVITY
9682     case CA_SET_PLAYER_GRAVITY:
9683     {
9684       for (i = 0; i < MAX_PLAYERS; i++)
9685       {
9686         if (trigger_player_bits & (1 << i))
9687         {
9688           stored_player[i].gravity =
9689             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9690              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9691              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9692              stored_player[i].gravity);
9693         }
9694       }
9695
9696       break;
9697     }
9698 #endif
9699
9700     case CA_SET_PLAYER_ARTWORK:
9701     {
9702       for (i = 0; i < MAX_PLAYERS; i++)
9703       {
9704         if (trigger_player_bits & (1 << i))
9705         {
9706           int artwork_element = action_arg_element;
9707
9708           if (action_arg == CA_ARG_ELEMENT_RESET)
9709             artwork_element =
9710               (level.use_artwork_element[i] ? level.artwork_element[i] :
9711                stored_player[i].element_nr);
9712
9713 #if USE_GFX_RESET_PLAYER_ARTWORK
9714           if (stored_player[i].artwork_element != artwork_element)
9715             stored_player[i].Frame = 0;
9716 #endif
9717
9718           stored_player[i].artwork_element = artwork_element;
9719
9720           SetPlayerWaiting(&stored_player[i], FALSE);
9721
9722           /* set number of special actions for bored and sleeping animation */
9723           stored_player[i].num_special_action_bored =
9724             get_num_special_action(artwork_element,
9725                                    ACTION_BORING_1, ACTION_BORING_LAST);
9726           stored_player[i].num_special_action_sleeping =
9727             get_num_special_action(artwork_element,
9728                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9729         }
9730       }
9731
9732       break;
9733     }
9734
9735     /* ---------- CE actions  ---------------------------------------------- */
9736
9737     case CA_SET_CE_VALUE:
9738     {
9739 #if USE_NEW_CUSTOM_VALUE
9740       int last_ce_value = CustomValue[x][y];
9741
9742       CustomValue[x][y] = action_arg_number_new;
9743
9744       if (CustomValue[x][y] != last_ce_value)
9745       {
9746         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9747         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9748
9749         if (CustomValue[x][y] == 0)
9750         {
9751           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9752           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9753         }
9754       }
9755 #endif
9756
9757       break;
9758     }
9759
9760     case CA_SET_CE_SCORE:
9761     {
9762 #if USE_NEW_CUSTOM_VALUE
9763       int last_ce_score = ei->collect_score;
9764
9765       ei->collect_score = action_arg_number_new;
9766
9767       if (ei->collect_score != last_ce_score)
9768       {
9769         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9770         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9771
9772         if (ei->collect_score == 0)
9773         {
9774           int xx, yy;
9775
9776           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9777           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9778
9779           /*
9780             This is a very special case that seems to be a mixture between
9781             CheckElementChange() and CheckTriggeredElementChange(): while
9782             the first one only affects single elements that are triggered
9783             directly, the second one affects multiple elements in the playfield
9784             that are triggered indirectly by another element. This is a third
9785             case: Changing the CE score always affects multiple identical CEs,
9786             so every affected CE must be checked, not only the single CE for
9787             which the CE score was changed in the first place (as every instance
9788             of that CE shares the same CE score, and therefore also can change)!
9789           */
9790           SCAN_PLAYFIELD(xx, yy)
9791           {
9792             if (Feld[xx][yy] == element)
9793               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9794                                  CE_SCORE_GETS_ZERO);
9795           }
9796         }
9797       }
9798 #endif
9799
9800       break;
9801     }
9802
9803     /* ---------- engine actions  ------------------------------------------ */
9804
9805     case CA_SET_ENGINE_SCAN_MODE:
9806     {
9807       InitPlayfieldScanMode(action_arg);
9808
9809       break;
9810     }
9811
9812     default:
9813       break;
9814   }
9815 }
9816
9817 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9818 {
9819   int old_element = Feld[x][y];
9820   int new_element = GetElementFromGroupElement(element);
9821   int previous_move_direction = MovDir[x][y];
9822 #if USE_NEW_CUSTOM_VALUE
9823   int last_ce_value = CustomValue[x][y];
9824 #endif
9825   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9826   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9827   boolean add_player_onto_element = (new_element_is_player &&
9828 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9829                                      /* this breaks SnakeBite when a snake is
9830                                         halfway through a door that closes */
9831                                      /* NOW FIXED AT LEVEL INIT IN files.c */
9832                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9833 #endif
9834                                      IS_WALKABLE(old_element));
9835
9836 #if 0
9837   /* check if element under the player changes from accessible to unaccessible
9838      (needed for special case of dropping element which then changes) */
9839   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9840       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9841   {
9842     Bang(x, y);
9843
9844     return;
9845   }
9846 #endif
9847
9848   if (!add_player_onto_element)
9849   {
9850     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9851       RemoveMovingField(x, y);
9852     else
9853       RemoveField(x, y);
9854
9855     Feld[x][y] = new_element;
9856
9857 #if !USE_GFX_RESET_GFX_ANIMATION
9858     ResetGfxAnimation(x, y);
9859     ResetRandomAnimationValue(x, y);
9860 #endif
9861
9862     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9863       MovDir[x][y] = previous_move_direction;
9864
9865 #if USE_NEW_CUSTOM_VALUE
9866     if (element_info[new_element].use_last_ce_value)
9867       CustomValue[x][y] = last_ce_value;
9868 #endif
9869
9870     InitField_WithBug1(x, y, FALSE);
9871
9872     new_element = Feld[x][y];   /* element may have changed */
9873
9874 #if USE_GFX_RESET_GFX_ANIMATION
9875     ResetGfxAnimation(x, y);
9876     ResetRandomAnimationValue(x, y);
9877 #endif
9878
9879     DrawLevelField(x, y);
9880
9881     if (GFX_CRUMBLED(new_element))
9882       DrawLevelFieldCrumbledSandNeighbours(x, y);
9883   }
9884
9885 #if 1
9886   /* check if element under the player changes from accessible to unaccessible
9887      (needed for special case of dropping element which then changes) */
9888   /* (must be checked after creating new element for walkable group elements) */
9889 #if USE_FIX_KILLED_BY_NON_WALKABLE
9890   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9891       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9892   {
9893     Bang(x, y);
9894
9895     return;
9896   }
9897 #else
9898   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9899       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9900   {
9901     Bang(x, y);
9902
9903     return;
9904   }
9905 #endif
9906 #endif
9907
9908   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9909   if (new_element_is_player)
9910     RelocatePlayer(x, y, new_element);
9911
9912   if (is_change)
9913     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9914
9915   TestIfBadThingTouchesPlayer(x, y);
9916   TestIfPlayerTouchesCustomElement(x, y);
9917   TestIfElementTouchesCustomElement(x, y);
9918 }
9919
9920 static void CreateField(int x, int y, int element)
9921 {
9922   CreateFieldExt(x, y, element, FALSE);
9923 }
9924
9925 static void CreateElementFromChange(int x, int y, int element)
9926 {
9927   element = GET_VALID_RUNTIME_ELEMENT(element);
9928
9929 #if USE_STOP_CHANGED_ELEMENTS
9930   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9931   {
9932     int old_element = Feld[x][y];
9933
9934     /* prevent changed element from moving in same engine frame
9935        unless both old and new element can either fall or move */
9936     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9937         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9938       Stop[x][y] = TRUE;
9939   }
9940 #endif
9941
9942   CreateFieldExt(x, y, element, TRUE);
9943 }
9944
9945 static boolean ChangeElement(int x, int y, int element, int page)
9946 {
9947   struct ElementInfo *ei = &element_info[element];
9948   struct ElementChangeInfo *change = &ei->change_page[page];
9949   int ce_value = CustomValue[x][y];
9950   int ce_score = ei->collect_score;
9951   int target_element;
9952   int old_element = Feld[x][y];
9953
9954   /* always use default change event to prevent running into a loop */
9955   if (ChangeEvent[x][y] == -1)
9956     ChangeEvent[x][y] = CE_DELAY;
9957
9958   if (ChangeEvent[x][y] == CE_DELAY)
9959   {
9960     /* reset actual trigger element, trigger player and action element */
9961     change->actual_trigger_element = EL_EMPTY;
9962     change->actual_trigger_player = EL_PLAYER_1;
9963     change->actual_trigger_side = CH_SIDE_NONE;
9964     change->actual_trigger_ce_value = 0;
9965     change->actual_trigger_ce_score = 0;
9966   }
9967
9968   /* do not change elements more than a specified maximum number of changes */
9969   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
9970     return FALSE;
9971
9972   ChangeCount[x][y]++;          /* count number of changes in the same frame */
9973
9974   if (change->explode)
9975   {
9976     Bang(x, y);
9977
9978     return TRUE;
9979   }
9980
9981   if (change->use_target_content)
9982   {
9983     boolean complete_replace = TRUE;
9984     boolean can_replace[3][3];
9985     int xx, yy;
9986
9987     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9988     {
9989       boolean is_empty;
9990       boolean is_walkable;
9991       boolean is_diggable;
9992       boolean is_collectible;
9993       boolean is_removable;
9994       boolean is_destructible;
9995       int ex = x + xx - 1;
9996       int ey = y + yy - 1;
9997       int content_element = change->target_content.e[xx][yy];
9998       int e;
9999
10000       can_replace[xx][yy] = TRUE;
10001
10002       if (ex == x && ey == y)   /* do not check changing element itself */
10003         continue;
10004
10005       if (content_element == EL_EMPTY_SPACE)
10006       {
10007         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10008
10009         continue;
10010       }
10011
10012       if (!IN_LEV_FIELD(ex, ey))
10013       {
10014         can_replace[xx][yy] = FALSE;
10015         complete_replace = FALSE;
10016
10017         continue;
10018       }
10019
10020       e = Feld[ex][ey];
10021
10022       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10023         e = MovingOrBlocked2Element(ex, ey);
10024
10025       is_empty = (IS_FREE(ex, ey) ||
10026                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10027
10028       is_walkable     = (is_empty || IS_WALKABLE(e));
10029       is_diggable     = (is_empty || IS_DIGGABLE(e));
10030       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10031       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10032       is_removable    = (is_diggable || is_collectible);
10033
10034       can_replace[xx][yy] =
10035         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10036           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10037           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10038           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10039           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10040           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10041          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10042
10043       if (!can_replace[xx][yy])
10044         complete_replace = FALSE;
10045     }
10046
10047     if (!change->only_if_complete || complete_replace)
10048     {
10049       boolean something_has_changed = FALSE;
10050
10051       if (change->only_if_complete && change->use_random_replace &&
10052           RND(100) < change->random_percentage)
10053         return FALSE;
10054
10055       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10056       {
10057         int ex = x + xx - 1;
10058         int ey = y + yy - 1;
10059         int content_element;
10060
10061         if (can_replace[xx][yy] && (!change->use_random_replace ||
10062                                     RND(100) < change->random_percentage))
10063         {
10064           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10065             RemoveMovingField(ex, ey);
10066
10067           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10068
10069           content_element = change->target_content.e[xx][yy];
10070           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10071                                               ce_value, ce_score);
10072
10073           CreateElementFromChange(ex, ey, target_element);
10074
10075           something_has_changed = TRUE;
10076
10077           /* for symmetry reasons, freeze newly created border elements */
10078           if (ex != x || ey != y)
10079             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10080         }
10081       }
10082
10083       if (something_has_changed)
10084       {
10085         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10086         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10087       }
10088     }
10089   }
10090   else
10091   {
10092     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10093                                         ce_value, ce_score);
10094
10095     if (element == EL_DIAGONAL_GROWING ||
10096         element == EL_DIAGONAL_SHRINKING)
10097     {
10098       target_element = Store[x][y];
10099
10100       Store[x][y] = EL_EMPTY;
10101     }
10102
10103     CreateElementFromChange(x, y, target_element);
10104
10105     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10106     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10107   }
10108
10109   /* this uses direct change before indirect change */
10110   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10111
10112   return TRUE;
10113 }
10114
10115 #if USE_NEW_DELAYED_ACTION
10116
10117 static void HandleElementChange(int x, int y, int page)
10118 {
10119   int element = MovingOrBlocked2Element(x, y);
10120   struct ElementInfo *ei = &element_info[element];
10121   struct ElementChangeInfo *change = &ei->change_page[page];
10122
10123 #ifdef DEBUG
10124   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10125       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10126   {
10127     printf("\n\n");
10128     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10129            x, y, element, element_info[element].token_name);
10130     printf("HandleElementChange(): This should never happen!\n");
10131     printf("\n\n");
10132   }
10133 #endif
10134
10135   /* this can happen with classic bombs on walkable, changing elements */
10136   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10137   {
10138 #if 0
10139     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10140       ChangeDelay[x][y] = 0;
10141 #endif
10142
10143     return;
10144   }
10145
10146   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10147   {
10148     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10149
10150     if (change->can_change)
10151     {
10152 #if 1
10153       /* !!! not clear why graphic animation should be reset at all here !!! */
10154       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10155 #if USE_GFX_RESET_WHEN_NOT_MOVING
10156       /* when a custom element is about to change (for example by change delay),
10157          do not reset graphic animation when the custom element is moving */
10158       if (!IS_MOVING(x, y))
10159 #endif
10160       {
10161         ResetGfxAnimation(x, y);
10162         ResetRandomAnimationValue(x, y);
10163       }
10164 #endif
10165
10166       if (change->pre_change_function)
10167         change->pre_change_function(x, y);
10168     }
10169   }
10170
10171   ChangeDelay[x][y]--;
10172
10173   if (ChangeDelay[x][y] != 0)           /* continue element change */
10174   {
10175     if (change->can_change)
10176     {
10177       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10178
10179       if (IS_ANIMATED(graphic))
10180         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10181
10182       if (change->change_function)
10183         change->change_function(x, y);
10184     }
10185   }
10186   else                                  /* finish element change */
10187   {
10188     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10189     {
10190       page = ChangePage[x][y];
10191       ChangePage[x][y] = -1;
10192
10193       change = &ei->change_page[page];
10194     }
10195
10196     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10197     {
10198       ChangeDelay[x][y] = 1;            /* try change after next move step */
10199       ChangePage[x][y] = page;          /* remember page to use for change */
10200
10201       return;
10202     }
10203
10204     if (change->can_change)
10205     {
10206       if (ChangeElement(x, y, element, page))
10207       {
10208         if (change->post_change_function)
10209           change->post_change_function(x, y);
10210       }
10211     }
10212
10213     if (change->has_action)
10214       ExecuteCustomElementAction(x, y, element, page);
10215   }
10216 }
10217
10218 #else
10219
10220 static void HandleElementChange(int x, int y, int page)
10221 {
10222   int element = MovingOrBlocked2Element(x, y);
10223   struct ElementInfo *ei = &element_info[element];
10224   struct ElementChangeInfo *change = &ei->change_page[page];
10225
10226 #ifdef DEBUG
10227   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10228   {
10229     printf("\n\n");
10230     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10231            x, y, element, element_info[element].token_name);
10232     printf("HandleElementChange(): This should never happen!\n");
10233     printf("\n\n");
10234   }
10235 #endif
10236
10237   /* this can happen with classic bombs on walkable, changing elements */
10238   if (!CAN_CHANGE(element))
10239   {
10240 #if 0
10241     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10242       ChangeDelay[x][y] = 0;
10243 #endif
10244
10245     return;
10246   }
10247
10248   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10249   {
10250     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10251
10252     ResetGfxAnimation(x, y);
10253     ResetRandomAnimationValue(x, y);
10254
10255     if (change->pre_change_function)
10256       change->pre_change_function(x, y);
10257   }
10258
10259   ChangeDelay[x][y]--;
10260
10261   if (ChangeDelay[x][y] != 0)           /* continue element change */
10262   {
10263     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10264
10265     if (IS_ANIMATED(graphic))
10266       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10267
10268     if (change->change_function)
10269       change->change_function(x, y);
10270   }
10271   else                                  /* finish element change */
10272   {
10273     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10274     {
10275       page = ChangePage[x][y];
10276       ChangePage[x][y] = -1;
10277
10278       change = &ei->change_page[page];
10279     }
10280
10281     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10282     {
10283       ChangeDelay[x][y] = 1;            /* try change after next move step */
10284       ChangePage[x][y] = page;          /* remember page to use for change */
10285
10286       return;
10287     }
10288
10289     if (ChangeElement(x, y, element, page))
10290     {
10291       if (change->post_change_function)
10292         change->post_change_function(x, y);
10293     }
10294   }
10295 }
10296
10297 #endif
10298
10299 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10300                                               int trigger_element,
10301                                               int trigger_event,
10302                                               int trigger_player,
10303                                               int trigger_side,
10304                                               int trigger_page)
10305 {
10306   boolean change_done_any = FALSE;
10307   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10308   int i;
10309
10310   if (!(trigger_events[trigger_element][trigger_event]))
10311     return FALSE;
10312
10313 #if 0
10314   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10315          trigger_event, recursion_loop_depth, recursion_loop_detected,
10316          recursion_loop_element, EL_NAME(recursion_loop_element));
10317 #endif
10318
10319   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10320
10321   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10322   {
10323     int element = EL_CUSTOM_START + i;
10324     boolean change_done = FALSE;
10325     int p;
10326
10327     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10328         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10329       continue;
10330
10331     for (p = 0; p < element_info[element].num_change_pages; p++)
10332     {
10333       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10334
10335       if (change->can_change_or_has_action &&
10336           change->has_event[trigger_event] &&
10337           change->trigger_side & trigger_side &&
10338           change->trigger_player & trigger_player &&
10339           change->trigger_page & trigger_page_bits &&
10340           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10341       {
10342         change->actual_trigger_element = trigger_element;
10343         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10344         change->actual_trigger_side = trigger_side;
10345         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10346         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10347
10348         if ((change->can_change && !change_done) || change->has_action)
10349         {
10350           int x, y;
10351
10352           SCAN_PLAYFIELD(x, y)
10353           {
10354             if (Feld[x][y] == element)
10355             {
10356               if (change->can_change && !change_done)
10357               {
10358                 ChangeDelay[x][y] = 1;
10359                 ChangeEvent[x][y] = trigger_event;
10360
10361                 HandleElementChange(x, y, p);
10362               }
10363 #if USE_NEW_DELAYED_ACTION
10364               else if (change->has_action)
10365               {
10366                 ExecuteCustomElementAction(x, y, element, p);
10367                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10368               }
10369 #else
10370               if (change->has_action)
10371               {
10372                 ExecuteCustomElementAction(x, y, element, p);
10373                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10374               }
10375 #endif
10376             }
10377           }
10378
10379           if (change->can_change)
10380           {
10381             change_done = TRUE;
10382             change_done_any = TRUE;
10383           }
10384         }
10385       }
10386     }
10387   }
10388
10389   RECURSION_LOOP_DETECTION_END();
10390
10391   return change_done_any;
10392 }
10393
10394 static boolean CheckElementChangeExt(int x, int y,
10395                                      int element,
10396                                      int trigger_element,
10397                                      int trigger_event,
10398                                      int trigger_player,
10399                                      int trigger_side)
10400 {
10401   boolean change_done = FALSE;
10402   int p;
10403
10404   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10405       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10406     return FALSE;
10407
10408   if (Feld[x][y] == EL_BLOCKED)
10409   {
10410     Blocked2Moving(x, y, &x, &y);
10411     element = Feld[x][y];
10412   }
10413
10414 #if 0
10415   /* check if element has already changed */
10416   if (Feld[x][y] != element)
10417     return FALSE;
10418 #else
10419   /* check if element has already changed or is about to change after moving */
10420   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10421        Feld[x][y] != element) ||
10422
10423       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10424        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10425         ChangePage[x][y] != -1)))
10426     return FALSE;
10427 #endif
10428
10429 #if 0
10430   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10431          trigger_event, recursion_loop_depth, recursion_loop_detected,
10432          recursion_loop_element, EL_NAME(recursion_loop_element));
10433 #endif
10434
10435   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10436
10437   for (p = 0; p < element_info[element].num_change_pages; p++)
10438   {
10439     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10440
10441     /* check trigger element for all events where the element that is checked
10442        for changing interacts with a directly adjacent element -- this is
10443        different to element changes that affect other elements to change on the
10444        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10445     boolean check_trigger_element =
10446       (trigger_event == CE_TOUCHING_X ||
10447        trigger_event == CE_HITTING_X ||
10448        trigger_event == CE_HIT_BY_X ||
10449 #if 1
10450        /* this one was forgotten until 3.2.3 */
10451        trigger_event == CE_DIGGING_X);
10452 #endif
10453
10454     if (change->can_change_or_has_action &&
10455         change->has_event[trigger_event] &&
10456         change->trigger_side & trigger_side &&
10457         change->trigger_player & trigger_player &&
10458         (!check_trigger_element ||
10459          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10460     {
10461       change->actual_trigger_element = trigger_element;
10462       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10463       change->actual_trigger_side = trigger_side;
10464       change->actual_trigger_ce_value = CustomValue[x][y];
10465       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10466
10467       /* special case: trigger element not at (x,y) position for some events */
10468       if (check_trigger_element)
10469       {
10470         static struct
10471         {
10472           int dx, dy;
10473         } move_xy[] =
10474           {
10475             {  0,  0 },
10476             { -1,  0 },
10477             { +1,  0 },
10478             {  0,  0 },
10479             {  0, -1 },
10480             {  0,  0 }, { 0, 0 }, { 0, 0 },
10481             {  0, +1 }
10482           };
10483
10484         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10485         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10486
10487         change->actual_trigger_ce_value = CustomValue[xx][yy];
10488         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10489       }
10490
10491       if (change->can_change && !change_done)
10492       {
10493         ChangeDelay[x][y] = 1;
10494         ChangeEvent[x][y] = trigger_event;
10495
10496         HandleElementChange(x, y, p);
10497
10498         change_done = TRUE;
10499       }
10500 #if USE_NEW_DELAYED_ACTION
10501       else if (change->has_action)
10502       {
10503         ExecuteCustomElementAction(x, y, element, p);
10504         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10505       }
10506 #else
10507       if (change->has_action)
10508       {
10509         ExecuteCustomElementAction(x, y, element, p);
10510         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10511       }
10512 #endif
10513     }
10514   }
10515
10516   RECURSION_LOOP_DETECTION_END();
10517
10518   return change_done;
10519 }
10520
10521 static void PlayPlayerSound(struct PlayerInfo *player)
10522 {
10523   int jx = player->jx, jy = player->jy;
10524   int sound_element = player->artwork_element;
10525   int last_action = player->last_action_waiting;
10526   int action = player->action_waiting;
10527
10528   if (player->is_waiting)
10529   {
10530     if (action != last_action)
10531       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10532     else
10533       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10534   }
10535   else
10536   {
10537     if (action != last_action)
10538       StopSound(element_info[sound_element].sound[last_action]);
10539
10540     if (last_action == ACTION_SLEEPING)
10541       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10542   }
10543 }
10544
10545 static void PlayAllPlayersSound()
10546 {
10547   int i;
10548
10549   for (i = 0; i < MAX_PLAYERS; i++)
10550     if (stored_player[i].active)
10551       PlayPlayerSound(&stored_player[i]);
10552 }
10553
10554 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10555 {
10556   boolean last_waiting = player->is_waiting;
10557   int move_dir = player->MovDir;
10558
10559   player->dir_waiting = move_dir;
10560   player->last_action_waiting = player->action_waiting;
10561
10562   if (is_waiting)
10563   {
10564     if (!last_waiting)          /* not waiting -> waiting */
10565     {
10566       player->is_waiting = TRUE;
10567
10568       player->frame_counter_bored =
10569         FrameCounter +
10570         game.player_boring_delay_fixed +
10571         GetSimpleRandom(game.player_boring_delay_random);
10572       player->frame_counter_sleeping =
10573         FrameCounter +
10574         game.player_sleeping_delay_fixed +
10575         GetSimpleRandom(game.player_sleeping_delay_random);
10576
10577       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10578     }
10579
10580     if (game.player_sleeping_delay_fixed +
10581         game.player_sleeping_delay_random > 0 &&
10582         player->anim_delay_counter == 0 &&
10583         player->post_delay_counter == 0 &&
10584         FrameCounter >= player->frame_counter_sleeping)
10585       player->is_sleeping = TRUE;
10586     else if (game.player_boring_delay_fixed +
10587              game.player_boring_delay_random > 0 &&
10588              FrameCounter >= player->frame_counter_bored)
10589       player->is_bored = TRUE;
10590
10591     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10592                               player->is_bored ? ACTION_BORING :
10593                               ACTION_WAITING);
10594
10595     if (player->is_sleeping && player->use_murphy)
10596     {
10597       /* special case for sleeping Murphy when leaning against non-free tile */
10598
10599       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10600           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10601            !IS_MOVING(player->jx - 1, player->jy)))
10602         move_dir = MV_LEFT;
10603       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10604                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10605                 !IS_MOVING(player->jx + 1, player->jy)))
10606         move_dir = MV_RIGHT;
10607       else
10608         player->is_sleeping = FALSE;
10609
10610       player->dir_waiting = move_dir;
10611     }
10612
10613     if (player->is_sleeping)
10614     {
10615       if (player->num_special_action_sleeping > 0)
10616       {
10617         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10618         {
10619           int last_special_action = player->special_action_sleeping;
10620           int num_special_action = player->num_special_action_sleeping;
10621           int special_action =
10622             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10623              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10624              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10625              last_special_action + 1 : ACTION_SLEEPING);
10626           int special_graphic =
10627             el_act_dir2img(player->artwork_element, special_action, move_dir);
10628
10629           player->anim_delay_counter =
10630             graphic_info[special_graphic].anim_delay_fixed +
10631             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10632           player->post_delay_counter =
10633             graphic_info[special_graphic].post_delay_fixed +
10634             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10635
10636           player->special_action_sleeping = special_action;
10637         }
10638
10639         if (player->anim_delay_counter > 0)
10640         {
10641           player->action_waiting = player->special_action_sleeping;
10642           player->anim_delay_counter--;
10643         }
10644         else if (player->post_delay_counter > 0)
10645         {
10646           player->post_delay_counter--;
10647         }
10648       }
10649     }
10650     else if (player->is_bored)
10651     {
10652       if (player->num_special_action_bored > 0)
10653       {
10654         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10655         {
10656           int special_action =
10657             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10658           int special_graphic =
10659             el_act_dir2img(player->artwork_element, special_action, move_dir);
10660
10661           player->anim_delay_counter =
10662             graphic_info[special_graphic].anim_delay_fixed +
10663             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10664           player->post_delay_counter =
10665             graphic_info[special_graphic].post_delay_fixed +
10666             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10667
10668           player->special_action_bored = special_action;
10669         }
10670
10671         if (player->anim_delay_counter > 0)
10672         {
10673           player->action_waiting = player->special_action_bored;
10674           player->anim_delay_counter--;
10675         }
10676         else if (player->post_delay_counter > 0)
10677         {
10678           player->post_delay_counter--;
10679         }
10680       }
10681     }
10682   }
10683   else if (last_waiting)        /* waiting -> not waiting */
10684   {
10685     player->is_waiting = FALSE;
10686     player->is_bored = FALSE;
10687     player->is_sleeping = FALSE;
10688
10689     player->frame_counter_bored = -1;
10690     player->frame_counter_sleeping = -1;
10691
10692     player->anim_delay_counter = 0;
10693     player->post_delay_counter = 0;
10694
10695     player->dir_waiting = player->MovDir;
10696     player->action_waiting = ACTION_DEFAULT;
10697
10698     player->special_action_bored = ACTION_DEFAULT;
10699     player->special_action_sleeping = ACTION_DEFAULT;
10700   }
10701 }
10702
10703 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10704 {
10705   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10706   int left      = player_action & JOY_LEFT;
10707   int right     = player_action & JOY_RIGHT;
10708   int up        = player_action & JOY_UP;
10709   int down      = player_action & JOY_DOWN;
10710   int button1   = player_action & JOY_BUTTON_1;
10711   int button2   = player_action & JOY_BUTTON_2;
10712   int dx        = (left ? -1 : right ? 1 : 0);
10713   int dy        = (up   ? -1 : down  ? 1 : 0);
10714
10715   if (!player->active || tape.pausing)
10716     return 0;
10717
10718   if (player_action)
10719   {
10720     if (button1)
10721       snapped = SnapField(player, dx, dy);
10722     else
10723     {
10724       if (button2)
10725         dropped = DropElement(player);
10726
10727       moved = MovePlayer(player, dx, dy);
10728     }
10729
10730     if (tape.single_step && tape.recording && !tape.pausing)
10731     {
10732       if (button1 || (dropped && !moved))
10733       {
10734         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10735         SnapField(player, 0, 0);                /* stop snapping */
10736       }
10737     }
10738
10739     SetPlayerWaiting(player, FALSE);
10740
10741     return player_action;
10742   }
10743   else
10744   {
10745     /* no actions for this player (no input at player's configured device) */
10746
10747     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10748     SnapField(player, 0, 0);
10749     CheckGravityMovementWhenNotMoving(player);
10750
10751     if (player->MovPos == 0)
10752       SetPlayerWaiting(player, TRUE);
10753
10754     if (player->MovPos == 0)    /* needed for tape.playing */
10755       player->is_moving = FALSE;
10756
10757     player->is_dropping = FALSE;
10758     player->is_dropping_pressed = FALSE;
10759     player->drop_pressed_delay = 0;
10760
10761     return 0;
10762   }
10763 }
10764
10765 static void CheckLevelTime()
10766 {
10767   int i;
10768
10769   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10770   {
10771     if (level.native_em_level->lev->home == 0)  /* all players at home */
10772     {
10773       PlayerWins(local_player);
10774
10775       AllPlayersGone = TRUE;
10776
10777       level.native_em_level->lev->home = -1;
10778     }
10779
10780     if (level.native_em_level->ply[0]->alive == 0 &&
10781         level.native_em_level->ply[1]->alive == 0 &&
10782         level.native_em_level->ply[2]->alive == 0 &&
10783         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10784       AllPlayersGone = TRUE;
10785   }
10786
10787   if (TimeFrames >= FRAMES_PER_SECOND)
10788   {
10789     TimeFrames = 0;
10790     TapeTime++;
10791
10792     for (i = 0; i < MAX_PLAYERS; i++)
10793     {
10794       struct PlayerInfo *player = &stored_player[i];
10795
10796       if (SHIELD_ON(player))
10797       {
10798         player->shield_normal_time_left--;
10799
10800         if (player->shield_deadly_time_left > 0)
10801           player->shield_deadly_time_left--;
10802       }
10803     }
10804
10805     if (!local_player->LevelSolved && !level.use_step_counter)
10806     {
10807       TimePlayed++;
10808
10809       if (TimeLeft > 0)
10810       {
10811         TimeLeft--;
10812
10813         if (TimeLeft <= 10 && setup.time_limit)
10814           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10815
10816 #if 1
10817         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
10818
10819         DisplayGameControlValues();
10820 #else
10821         DrawGameValue_Time(TimeLeft);
10822 #endif
10823
10824         if (!TimeLeft && setup.time_limit)
10825         {
10826           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10827             level.native_em_level->lev->killed_out_of_time = TRUE;
10828           else
10829             for (i = 0; i < MAX_PLAYERS; i++)
10830               KillPlayer(&stored_player[i]);
10831         }
10832       }
10833 #if 1
10834       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10835       {
10836         game_control_value[GAME_CONTROL_TIME] = TimePlayed;
10837
10838         DisplayGameControlValues();
10839       }
10840 #else
10841       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10842         DrawGameValue_Time(TimePlayed);
10843 #endif
10844
10845       level.native_em_level->lev->time =
10846         (level.time == 0 ? TimePlayed : TimeLeft);
10847     }
10848
10849     if (tape.recording || tape.playing)
10850       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10851   }
10852
10853   DrawGameDoorValues();
10854 }
10855
10856 void AdvanceFrameAndPlayerCounters(int player_nr)
10857 {
10858   int i;
10859
10860   /* advance frame counters (global frame counter and time frame counter) */
10861   FrameCounter++;
10862   TimeFrames++;
10863
10864   /* advance player counters (counters for move delay, move animation etc.) */
10865   for (i = 0; i < MAX_PLAYERS; i++)
10866   {
10867     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10868     int move_delay_value = stored_player[i].move_delay_value;
10869     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10870
10871     if (!advance_player_counters)       /* not all players may be affected */
10872       continue;
10873
10874 #if USE_NEW_PLAYER_ANIM
10875     if (move_frames == 0)       /* less than one move per game frame */
10876     {
10877       int stepsize = TILEX / move_delay_value;
10878       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10879       int count = (stored_player[i].is_moving ?
10880                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10881
10882       if (count % delay == 0)
10883         move_frames = 1;
10884     }
10885 #endif
10886
10887     stored_player[i].Frame += move_frames;
10888
10889     if (stored_player[i].MovPos != 0)
10890       stored_player[i].StepFrame += move_frames;
10891
10892     if (stored_player[i].move_delay > 0)
10893       stored_player[i].move_delay--;
10894
10895     /* due to bugs in previous versions, counter must count up, not down */
10896     if (stored_player[i].push_delay != -1)
10897       stored_player[i].push_delay++;
10898
10899     if (stored_player[i].drop_delay > 0)
10900       stored_player[i].drop_delay--;
10901
10902     if (stored_player[i].is_dropping_pressed)
10903       stored_player[i].drop_pressed_delay++;
10904   }
10905 }
10906
10907 void StartGameActions(boolean init_network_game, boolean record_tape,
10908                       long random_seed)
10909 {
10910   unsigned long new_random_seed = InitRND(random_seed);
10911
10912   if (record_tape)
10913     TapeStartRecording(new_random_seed);
10914
10915 #if defined(NETWORK_AVALIABLE)
10916   if (init_network_game)
10917   {
10918     SendToServer_StartPlaying();
10919
10920     return;
10921   }
10922 #endif
10923
10924   InitGame();
10925 }
10926
10927 void GameActions()
10928 {
10929   static unsigned long game_frame_delay = 0;
10930   unsigned long game_frame_delay_value;
10931   byte *recorded_player_action;
10932   byte summarized_player_action = 0;
10933   byte tape_action[MAX_PLAYERS];
10934   int i;
10935
10936   /* detect endless loops, caused by custom element programming */
10937   if (recursion_loop_detected && recursion_loop_depth == 0)
10938   {
10939     char *message = getStringCat3("Internal Error ! Element ",
10940                                   EL_NAME(recursion_loop_element),
10941                                   " caused endless loop ! Quit the game ?");
10942
10943     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10944           EL_NAME(recursion_loop_element));
10945
10946     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10947
10948     recursion_loop_detected = FALSE;    /* if game should be continued */
10949
10950     free(message);
10951
10952     return;
10953   }
10954
10955   if (game.restart_level)
10956     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
10957
10958   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10959   {
10960     if (level.native_em_level->lev->home == 0)  /* all players at home */
10961     {
10962       PlayerWins(local_player);
10963
10964       AllPlayersGone = TRUE;
10965
10966       level.native_em_level->lev->home = -1;
10967     }
10968
10969     if (level.native_em_level->ply[0]->alive == 0 &&
10970         level.native_em_level->ply[1]->alive == 0 &&
10971         level.native_em_level->ply[2]->alive == 0 &&
10972         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10973       AllPlayersGone = TRUE;
10974   }
10975
10976   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10977     GameWon();
10978
10979   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10980     TapeStop();
10981
10982   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
10983     return;
10984
10985   game_frame_delay_value =
10986     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10987
10988   if (tape.playing && tape.warp_forward && !tape.pausing)
10989     game_frame_delay_value = 0;
10990
10991   /* ---------- main game synchronization point ---------- */
10992
10993   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10994
10995   if (network_playing && !network_player_action_received)
10996   {
10997     /* try to get network player actions in time */
10998
10999 #if defined(NETWORK_AVALIABLE)
11000     /* last chance to get network player actions without main loop delay */
11001     HandleNetworking();
11002 #endif
11003
11004     /* game was quit by network peer */
11005     if (game_status != GAME_MODE_PLAYING)
11006       return;
11007
11008     if (!network_player_action_received)
11009       return;           /* failed to get network player actions in time */
11010
11011     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11012   }
11013
11014   if (tape.pausing)
11015     return;
11016
11017   /* at this point we know that we really continue executing the game */
11018
11019   network_player_action_received = FALSE;
11020
11021   /* when playing tape, read previously recorded player input from tape data */
11022   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11023
11024 #if 1
11025   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11026   if (tape.pausing)
11027     return;
11028 #endif
11029
11030   if (tape.set_centered_player)
11031   {
11032     game.centered_player_nr_next = tape.centered_player_nr_next;
11033     game.set_centered_player = TRUE;
11034   }
11035
11036   for (i = 0; i < MAX_PLAYERS; i++)
11037   {
11038     summarized_player_action |= stored_player[i].action;
11039
11040     if (!network_playing)
11041       stored_player[i].effective_action = stored_player[i].action;
11042   }
11043
11044 #if defined(NETWORK_AVALIABLE)
11045   if (network_playing)
11046     SendToServer_MovePlayer(summarized_player_action);
11047 #endif
11048
11049   if (!options.network && !setup.team_mode)
11050     local_player->effective_action = summarized_player_action;
11051
11052   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11053   {
11054     for (i = 0; i < MAX_PLAYERS; i++)
11055       stored_player[i].effective_action =
11056         (i == game.centered_player_nr ? summarized_player_action : 0);
11057   }
11058
11059   if (recorded_player_action != NULL)
11060     for (i = 0; i < MAX_PLAYERS; i++)
11061       stored_player[i].effective_action = recorded_player_action[i];
11062
11063   for (i = 0; i < MAX_PLAYERS; i++)
11064   {
11065     tape_action[i] = stored_player[i].effective_action;
11066
11067     /* (this can only happen in the R'n'D game engine) */
11068     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11069       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11070   }
11071
11072   /* only record actions from input devices, but not programmed actions */
11073   if (tape.recording)
11074     TapeRecordAction(tape_action);
11075
11076   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11077   {
11078     GameActions_EM_Main();
11079   }
11080   else
11081   {
11082     GameActions_RND();
11083   }
11084 }
11085
11086 void GameActions_EM_Main()
11087 {
11088   byte effective_action[MAX_PLAYERS];
11089   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11090   int i;
11091
11092   for (i = 0; i < MAX_PLAYERS; i++)
11093     effective_action[i] = stored_player[i].effective_action;
11094
11095   GameActions_EM(effective_action, warp_mode);
11096
11097   CheckLevelTime();
11098
11099   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11100 }
11101
11102 void GameActions_RND()
11103 {
11104   int magic_wall_x = 0, magic_wall_y = 0;
11105   int i, x, y, element, graphic;
11106
11107   InitPlayfieldScanModeVars();
11108
11109 #if USE_ONE_MORE_CHANGE_PER_FRAME
11110   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11111   {
11112     SCAN_PLAYFIELD(x, y)
11113     {
11114       ChangeCount[x][y] = 0;
11115       ChangeEvent[x][y] = -1;
11116     }
11117   }
11118 #endif
11119
11120   if (game.set_centered_player)
11121   {
11122     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11123
11124     /* switching to "all players" only possible if all players fit to screen */
11125     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11126     {
11127       game.centered_player_nr_next = game.centered_player_nr;
11128       game.set_centered_player = FALSE;
11129     }
11130
11131     /* do not switch focus to non-existing (or non-active) player */
11132     if (game.centered_player_nr_next >= 0 &&
11133         !stored_player[game.centered_player_nr_next].active)
11134     {
11135       game.centered_player_nr_next = game.centered_player_nr;
11136       game.set_centered_player = FALSE;
11137     }
11138   }
11139
11140   if (game.set_centered_player &&
11141       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11142   {
11143     int sx, sy;
11144
11145     if (game.centered_player_nr_next == -1)
11146     {
11147       setScreenCenteredToAllPlayers(&sx, &sy);
11148     }
11149     else
11150     {
11151       sx = stored_player[game.centered_player_nr_next].jx;
11152       sy = stored_player[game.centered_player_nr_next].jy;
11153     }
11154
11155     game.centered_player_nr = game.centered_player_nr_next;
11156     game.set_centered_player = FALSE;
11157
11158     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11159     DrawGameDoorValues();
11160   }
11161
11162   for (i = 0; i < MAX_PLAYERS; i++)
11163   {
11164     int actual_player_action = stored_player[i].effective_action;
11165
11166 #if 1
11167     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11168        - rnd_equinox_tetrachloride 048
11169        - rnd_equinox_tetrachloride_ii 096
11170        - rnd_emanuel_schmieg 002
11171        - doctor_sloan_ww 001, 020
11172     */
11173     if (stored_player[i].MovPos == 0)
11174       CheckGravityMovement(&stored_player[i]);
11175 #endif
11176
11177     /* overwrite programmed action with tape action */
11178     if (stored_player[i].programmed_action)
11179       actual_player_action = stored_player[i].programmed_action;
11180
11181     PlayerActions(&stored_player[i], actual_player_action);
11182
11183     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11184   }
11185
11186   ScrollScreen(NULL, SCROLL_GO_ON);
11187
11188   /* for backwards compatibility, the following code emulates a fixed bug that
11189      occured when pushing elements (causing elements that just made their last
11190      pushing step to already (if possible) make their first falling step in the
11191      same game frame, which is bad); this code is also needed to use the famous
11192      "spring push bug" which is used in older levels and might be wanted to be
11193      used also in newer levels, but in this case the buggy pushing code is only
11194      affecting the "spring" element and no other elements */
11195
11196   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11197   {
11198     for (i = 0; i < MAX_PLAYERS; i++)
11199     {
11200       struct PlayerInfo *player = &stored_player[i];
11201       int x = player->jx;
11202       int y = player->jy;
11203
11204       if (player->active && player->is_pushing && player->is_moving &&
11205           IS_MOVING(x, y) &&
11206           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11207            Feld[x][y] == EL_SPRING))
11208       {
11209         ContinueMoving(x, y);
11210
11211         /* continue moving after pushing (this is actually a bug) */
11212         if (!IS_MOVING(x, y))
11213           Stop[x][y] = FALSE;
11214       }
11215     }
11216   }
11217
11218 #if 0
11219   debug_print_timestamp(0, "start main loop profiling");
11220 #endif
11221
11222   SCAN_PLAYFIELD(x, y)
11223   {
11224     ChangeCount[x][y] = 0;
11225     ChangeEvent[x][y] = -1;
11226
11227     /* this must be handled before main playfield loop */
11228     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11229     {
11230       MovDelay[x][y]--;
11231       if (MovDelay[x][y] <= 0)
11232         RemoveField(x, y);
11233     }
11234
11235 #if USE_NEW_SNAP_DELAY
11236     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11237     {
11238       MovDelay[x][y]--;
11239       if (MovDelay[x][y] <= 0)
11240       {
11241         RemoveField(x, y);
11242         DrawLevelField(x, y);
11243
11244         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11245       }
11246     }
11247 #endif
11248
11249 #if DEBUG
11250     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11251     {
11252       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11253       printf("GameActions(): This should never happen!\n");
11254
11255       ChangePage[x][y] = -1;
11256     }
11257 #endif
11258
11259     Stop[x][y] = FALSE;
11260     if (WasJustMoving[x][y] > 0)
11261       WasJustMoving[x][y]--;
11262     if (WasJustFalling[x][y] > 0)
11263       WasJustFalling[x][y]--;
11264     if (CheckCollision[x][y] > 0)
11265       CheckCollision[x][y]--;
11266     if (CheckImpact[x][y] > 0)
11267       CheckImpact[x][y]--;
11268
11269     GfxFrame[x][y]++;
11270
11271     /* reset finished pushing action (not done in ContinueMoving() to allow
11272        continuous pushing animation for elements with zero push delay) */
11273     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11274     {
11275       ResetGfxAnimation(x, y);
11276       DrawLevelField(x, y);
11277     }
11278
11279 #if DEBUG
11280     if (IS_BLOCKED(x, y))
11281     {
11282       int oldx, oldy;
11283
11284       Blocked2Moving(x, y, &oldx, &oldy);
11285       if (!IS_MOVING(oldx, oldy))
11286       {
11287         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11288         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11289         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11290         printf("GameActions(): This should never happen!\n");
11291       }
11292     }
11293 #endif
11294   }
11295
11296 #if 0
11297   debug_print_timestamp(0, "- time for pre-main loop:");
11298 #endif
11299
11300 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
11301   SCAN_PLAYFIELD(x, y)
11302   {
11303     element = Feld[x][y];
11304     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11305
11306 #if 1
11307     {
11308 #if 1
11309       int element2 = element;
11310       int graphic2 = graphic;
11311 #else
11312       int element2 = Feld[x][y];
11313       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11314 #endif
11315       int last_gfx_frame = GfxFrame[x][y];
11316
11317       if (graphic_info[graphic2].anim_global_sync)
11318         GfxFrame[x][y] = FrameCounter;
11319       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11320         GfxFrame[x][y] = CustomValue[x][y];
11321       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11322         GfxFrame[x][y] = element_info[element2].collect_score;
11323       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11324         GfxFrame[x][y] = ChangeDelay[x][y];
11325
11326       if (redraw && GfxFrame[x][y] != last_gfx_frame)
11327         DrawLevelGraphicAnimation(x, y, graphic2);
11328     }
11329 #else
11330     ResetGfxFrame(x, y, TRUE);
11331 #endif
11332
11333 #if 1
11334     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11335         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11336       ResetRandomAnimationValue(x, y);
11337 #endif
11338
11339 #if 1
11340     SetRandomAnimationValue(x, y);
11341 #endif
11342
11343 #if 1
11344     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11345 #endif
11346   }
11347 #endif  // -------------------- !!! TEST ONLY !!! --------------------
11348
11349 #if 0
11350   debug_print_timestamp(0, "- time for TEST loop:     -->");
11351 #endif
11352
11353   SCAN_PLAYFIELD(x, y)
11354   {
11355     element = Feld[x][y];
11356     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11357
11358     ResetGfxFrame(x, y, TRUE);
11359
11360     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11361         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11362       ResetRandomAnimationValue(x, y);
11363
11364     SetRandomAnimationValue(x, y);
11365
11366     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11367
11368     if (IS_INACTIVE(element))
11369     {
11370       if (IS_ANIMATED(graphic))
11371         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11372
11373       continue;
11374     }
11375
11376     /* this may take place after moving, so 'element' may have changed */
11377     if (IS_CHANGING(x, y) &&
11378         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11379     {
11380       int page = element_info[element].event_page_nr[CE_DELAY];
11381
11382 #if 1
11383       HandleElementChange(x, y, page);
11384 #else
11385       if (CAN_CHANGE(element))
11386         HandleElementChange(x, y, page);
11387
11388       if (HAS_ACTION(element))
11389         ExecuteCustomElementAction(x, y, element, page);
11390 #endif
11391
11392       element = Feld[x][y];
11393       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11394     }
11395
11396 #if 0   // ---------------------------------------------------------------------
11397
11398     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11399     {
11400       StartMoving(x, y);
11401
11402       element = Feld[x][y];
11403       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11404
11405       if (IS_ANIMATED(graphic) &&
11406           !IS_MOVING(x, y) &&
11407           !Stop[x][y])
11408         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11409
11410       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11411         DrawTwinkleOnField(x, y);
11412     }
11413     else if (IS_MOVING(x, y))
11414       ContinueMoving(x, y);
11415     else
11416     {
11417       switch (element)
11418       {
11419         case EL_ACID:
11420         case EL_EXIT_OPEN:
11421         case EL_EM_EXIT_OPEN:
11422         case EL_SP_EXIT_OPEN:
11423         case EL_STEEL_EXIT_OPEN:
11424         case EL_EM_STEEL_EXIT_OPEN:
11425         case EL_SP_TERMINAL:
11426         case EL_SP_TERMINAL_ACTIVE:
11427         case EL_EXTRA_TIME:
11428         case EL_SHIELD_NORMAL:
11429         case EL_SHIELD_DEADLY:
11430           if (IS_ANIMATED(graphic))
11431             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11432           break;
11433
11434         case EL_DYNAMITE_ACTIVE:
11435         case EL_EM_DYNAMITE_ACTIVE:
11436         case EL_DYNABOMB_PLAYER_1_ACTIVE:
11437         case EL_DYNABOMB_PLAYER_2_ACTIVE:
11438         case EL_DYNABOMB_PLAYER_3_ACTIVE:
11439         case EL_DYNABOMB_PLAYER_4_ACTIVE:
11440         case EL_SP_DISK_RED_ACTIVE:
11441           CheckDynamite(x, y);
11442           break;
11443
11444         case EL_AMOEBA_GROWING:
11445           AmoebeWaechst(x, y);
11446           break;
11447
11448         case EL_AMOEBA_SHRINKING:
11449           AmoebaDisappearing(x, y);
11450           break;
11451
11452 #if !USE_NEW_AMOEBA_CODE
11453         case EL_AMOEBA_WET:
11454         case EL_AMOEBA_DRY:
11455         case EL_AMOEBA_FULL:
11456         case EL_BD_AMOEBA:
11457         case EL_EMC_DRIPPER:
11458           AmoebeAbleger(x, y);
11459           break;
11460 #endif
11461
11462         case EL_GAME_OF_LIFE:
11463         case EL_BIOMAZE:
11464           Life(x, y);
11465           break;
11466
11467         case EL_EXIT_CLOSED:
11468           CheckExit(x, y);
11469           break;
11470
11471         case EL_EM_EXIT_CLOSED:
11472           CheckExitEM(x, y);
11473           break;
11474
11475         case EL_STEEL_EXIT_CLOSED:
11476           CheckExitSteel(x, y);
11477           break;
11478
11479         case EL_EM_STEEL_EXIT_CLOSED:
11480           CheckExitSteelEM(x, y);
11481           break;
11482
11483         case EL_SP_EXIT_CLOSED:
11484           CheckExitSP(x, y);
11485           break;
11486
11487         case EL_EXPANDABLE_WALL_GROWING:
11488         case EL_EXPANDABLE_STEELWALL_GROWING:
11489           MauerWaechst(x, y);
11490           break;
11491
11492         case EL_EXPANDABLE_WALL:
11493         case EL_EXPANDABLE_WALL_HORIZONTAL:
11494         case EL_EXPANDABLE_WALL_VERTICAL:
11495         case EL_EXPANDABLE_WALL_ANY:
11496         case EL_BD_EXPANDABLE_WALL:
11497           MauerAbleger(x, y);
11498           break;
11499
11500         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
11501         case EL_EXPANDABLE_STEELWALL_VERTICAL:
11502         case EL_EXPANDABLE_STEELWALL_ANY:
11503           MauerAblegerStahl(x, y);
11504           break;
11505
11506         case EL_FLAMES:
11507           CheckForDragon(x, y);
11508           break;
11509
11510         case EL_EXPLOSION:
11511           break;
11512
11513         case EL_ELEMENT_SNAPPING:
11514         case EL_DIAGONAL_SHRINKING:
11515         case EL_DIAGONAL_GROWING:
11516         {
11517           graphic =
11518             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11519
11520           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11521           break;
11522         }
11523
11524         default:
11525           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11526             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11527           break;
11528       }
11529     }
11530
11531 #else   // ---------------------------------------------------------------------
11532
11533     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11534     {
11535       StartMoving(x, y);
11536
11537       element = Feld[x][y];
11538       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11539
11540       if (IS_ANIMATED(graphic) &&
11541           !IS_MOVING(x, y) &&
11542           !Stop[x][y])
11543         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11544
11545       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11546         DrawTwinkleOnField(x, y);
11547     }
11548     else if ((element == EL_ACID ||
11549               element == EL_EXIT_OPEN ||
11550               element == EL_EM_EXIT_OPEN ||
11551               element == EL_SP_EXIT_OPEN ||
11552               element == EL_STEEL_EXIT_OPEN ||
11553               element == EL_EM_STEEL_EXIT_OPEN ||
11554               element == EL_SP_TERMINAL ||
11555               element == EL_SP_TERMINAL_ACTIVE ||
11556               element == EL_EXTRA_TIME ||
11557               element == EL_SHIELD_NORMAL ||
11558               element == EL_SHIELD_DEADLY) &&
11559              IS_ANIMATED(graphic))
11560       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11561     else if (IS_MOVING(x, y))
11562       ContinueMoving(x, y);
11563     else if (IS_ACTIVE_BOMB(element))
11564       CheckDynamite(x, y);
11565     else if (element == EL_AMOEBA_GROWING)
11566       AmoebeWaechst(x, y);
11567     else if (element == EL_AMOEBA_SHRINKING)
11568       AmoebaDisappearing(x, y);
11569
11570 #if !USE_NEW_AMOEBA_CODE
11571     else if (IS_AMOEBALIVE(element))
11572       AmoebeAbleger(x, y);
11573 #endif
11574
11575     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11576       Life(x, y);
11577     else if (element == EL_EXIT_CLOSED)
11578       CheckExit(x, y);
11579     else if (element == EL_EM_EXIT_CLOSED)
11580       CheckExitEM(x, y);
11581     else if (element == EL_STEEL_EXIT_CLOSED)
11582       CheckExitSteel(x, y);
11583     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11584       CheckExitSteelEM(x, y);
11585     else if (element == EL_SP_EXIT_CLOSED)
11586       CheckExitSP(x, y);
11587     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11588              element == EL_EXPANDABLE_STEELWALL_GROWING)
11589       MauerWaechst(x, y);
11590     else if (element == EL_EXPANDABLE_WALL ||
11591              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11592              element == EL_EXPANDABLE_WALL_VERTICAL ||
11593              element == EL_EXPANDABLE_WALL_ANY ||
11594              element == EL_BD_EXPANDABLE_WALL)
11595       MauerAbleger(x, y);
11596     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11597              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11598              element == EL_EXPANDABLE_STEELWALL_ANY)
11599       MauerAblegerStahl(x, y);
11600     else if (element == EL_FLAMES)
11601       CheckForDragon(x, y);
11602     else if (element == EL_EXPLOSION)
11603       ; /* drawing of correct explosion animation is handled separately */
11604     else if (element == EL_ELEMENT_SNAPPING ||
11605              element == EL_DIAGONAL_SHRINKING ||
11606              element == EL_DIAGONAL_GROWING)
11607     {
11608       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11609
11610       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11611     }
11612     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11613       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11614
11615 #endif  // ---------------------------------------------------------------------
11616
11617     if (IS_BELT_ACTIVE(element))
11618       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11619
11620     if (game.magic_wall_active)
11621     {
11622       int jx = local_player->jx, jy = local_player->jy;
11623
11624       /* play the element sound at the position nearest to the player */
11625       if ((element == EL_MAGIC_WALL_FULL ||
11626            element == EL_MAGIC_WALL_ACTIVE ||
11627            element == EL_MAGIC_WALL_EMPTYING ||
11628            element == EL_BD_MAGIC_WALL_FULL ||
11629            element == EL_BD_MAGIC_WALL_ACTIVE ||
11630            element == EL_BD_MAGIC_WALL_EMPTYING ||
11631            element == EL_DC_MAGIC_WALL_FULL ||
11632            element == EL_DC_MAGIC_WALL_ACTIVE ||
11633            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11634           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11635       {
11636         magic_wall_x = x;
11637         magic_wall_y = y;
11638       }
11639     }
11640   }
11641
11642 #if 0
11643   debug_print_timestamp(0, "- time for MAIN loop:     -->");
11644 #endif
11645
11646 #if USE_NEW_AMOEBA_CODE
11647   /* new experimental amoeba growth stuff */
11648   if (!(FrameCounter % 8))
11649   {
11650     static unsigned long random = 1684108901;
11651
11652     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11653     {
11654       x = RND(lev_fieldx);
11655       y = RND(lev_fieldy);
11656       element = Feld[x][y];
11657
11658       if (!IS_PLAYER(x,y) &&
11659           (element == EL_EMPTY ||
11660            CAN_GROW_INTO(element) ||
11661            element == EL_QUICKSAND_EMPTY ||
11662            element == EL_QUICKSAND_FAST_EMPTY ||
11663            element == EL_ACID_SPLASH_LEFT ||
11664            element == EL_ACID_SPLASH_RIGHT))
11665       {
11666         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11667             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11668             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11669             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11670           Feld[x][y] = EL_AMOEBA_DROP;
11671       }
11672
11673       random = random * 129 + 1;
11674     }
11675   }
11676 #endif
11677
11678 #if 0
11679   if (game.explosions_delayed)
11680 #endif
11681   {
11682     game.explosions_delayed = FALSE;
11683
11684     SCAN_PLAYFIELD(x, y)
11685     {
11686       element = Feld[x][y];
11687
11688       if (ExplodeField[x][y])
11689         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11690       else if (element == EL_EXPLOSION)
11691         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11692
11693       ExplodeField[x][y] = EX_TYPE_NONE;
11694     }
11695
11696     game.explosions_delayed = TRUE;
11697   }
11698
11699   if (game.magic_wall_active)
11700   {
11701     if (!(game.magic_wall_time_left % 4))
11702     {
11703       int element = Feld[magic_wall_x][magic_wall_y];
11704
11705       if (element == EL_BD_MAGIC_WALL_FULL ||
11706           element == EL_BD_MAGIC_WALL_ACTIVE ||
11707           element == EL_BD_MAGIC_WALL_EMPTYING)
11708         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11709       else if (element == EL_DC_MAGIC_WALL_FULL ||
11710                element == EL_DC_MAGIC_WALL_ACTIVE ||
11711                element == EL_DC_MAGIC_WALL_EMPTYING)
11712         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11713       else
11714         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11715     }
11716
11717     if (game.magic_wall_time_left > 0)
11718     {
11719       game.magic_wall_time_left--;
11720       if (!game.magic_wall_time_left)
11721       {
11722         SCAN_PLAYFIELD(x, y)
11723         {
11724           element = Feld[x][y];
11725
11726           if (element == EL_MAGIC_WALL_ACTIVE ||
11727               element == EL_MAGIC_WALL_FULL)
11728           {
11729             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11730             DrawLevelField(x, y);
11731           }
11732           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11733                    element == EL_BD_MAGIC_WALL_FULL)
11734           {
11735             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11736             DrawLevelField(x, y);
11737           }
11738           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11739                    element == EL_DC_MAGIC_WALL_FULL)
11740           {
11741             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11742             DrawLevelField(x, y);
11743           }
11744         }
11745
11746         game.magic_wall_active = FALSE;
11747       }
11748     }
11749   }
11750
11751   if (game.light_time_left > 0)
11752   {
11753     game.light_time_left--;
11754
11755     if (game.light_time_left == 0)
11756       RedrawAllLightSwitchesAndInvisibleElements();
11757   }
11758
11759   if (game.timegate_time_left > 0)
11760   {
11761     game.timegate_time_left--;
11762
11763     if (game.timegate_time_left == 0)
11764       CloseAllOpenTimegates();
11765   }
11766
11767   if (game.lenses_time_left > 0)
11768   {
11769     game.lenses_time_left--;
11770
11771     if (game.lenses_time_left == 0)
11772       RedrawAllInvisibleElementsForLenses();
11773   }
11774
11775   if (game.magnify_time_left > 0)
11776   {
11777     game.magnify_time_left--;
11778
11779     if (game.magnify_time_left == 0)
11780       RedrawAllInvisibleElementsForMagnifier();
11781   }
11782
11783   for (i = 0; i < MAX_PLAYERS; i++)
11784   {
11785     struct PlayerInfo *player = &stored_player[i];
11786
11787     if (SHIELD_ON(player))
11788     {
11789       if (player->shield_deadly_time_left)
11790         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11791       else if (player->shield_normal_time_left)
11792         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11793     }
11794   }
11795
11796   CheckLevelTime();
11797
11798   DrawAllPlayers();
11799   PlayAllPlayersSound();
11800
11801   if (options.debug)                    /* calculate frames per second */
11802   {
11803     static unsigned long fps_counter = 0;
11804     static int fps_frames = 0;
11805     unsigned long fps_delay_ms = Counter() - fps_counter;
11806
11807     fps_frames++;
11808
11809     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11810     {
11811       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11812
11813       fps_frames = 0;
11814       fps_counter = Counter();
11815     }
11816
11817     redraw_mask |= REDRAW_FPS;
11818   }
11819
11820   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11821
11822   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11823   {
11824     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11825
11826     local_player->show_envelope = 0;
11827   }
11828
11829 #if 0
11830   debug_print_timestamp(0, "stop main loop profiling ");
11831   printf("----------------------------------------------------------\n");
11832 #endif
11833
11834   /* use random number generator in every frame to make it less predictable */
11835   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11836     RND(1);
11837 }
11838
11839 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11840 {
11841   int min_x = x, min_y = y, max_x = x, max_y = y;
11842   int i;
11843
11844   for (i = 0; i < MAX_PLAYERS; i++)
11845   {
11846     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11847
11848     if (!stored_player[i].active || &stored_player[i] == player)
11849       continue;
11850
11851     min_x = MIN(min_x, jx);
11852     min_y = MIN(min_y, jy);
11853     max_x = MAX(max_x, jx);
11854     max_y = MAX(max_y, jy);
11855   }
11856
11857   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11858 }
11859
11860 static boolean AllPlayersInVisibleScreen()
11861 {
11862   int i;
11863
11864   for (i = 0; i < MAX_PLAYERS; i++)
11865   {
11866     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11867
11868     if (!stored_player[i].active)
11869       continue;
11870
11871     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11872       return FALSE;
11873   }
11874
11875   return TRUE;
11876 }
11877
11878 void ScrollLevel(int dx, int dy)
11879 {
11880 #if 1
11881   static Bitmap *bitmap_db_field2 = NULL;
11882   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11883   int x, y;
11884 #else
11885   int i, x, y;
11886 #endif
11887
11888 #if 0
11889   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11890   /* only horizontal XOR vertical scroll direction allowed */
11891   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11892     return;
11893 #endif
11894
11895 #if 1
11896   if (bitmap_db_field2 == NULL)
11897     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11898
11899   /* needed when blitting directly to same bitmap -- should not be needed with
11900      recent SDL libraries, but apparently does not work in 1.2.11 directly */
11901   BlitBitmap(drawto_field, bitmap_db_field2,
11902              FX + TILEX * (dx == -1) - softscroll_offset,
11903              FY + TILEY * (dy == -1) - softscroll_offset,
11904              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11905              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11906              FX + TILEX * (dx == 1) - softscroll_offset,
11907              FY + TILEY * (dy == 1) - softscroll_offset);
11908   BlitBitmap(bitmap_db_field2, drawto_field,
11909              FX + TILEX * (dx == 1) - softscroll_offset,
11910              FY + TILEY * (dy == 1) - softscroll_offset,
11911              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11912              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11913              FX + TILEX * (dx == 1) - softscroll_offset,
11914              FY + TILEY * (dy == 1) - softscroll_offset);
11915
11916 #else
11917
11918 #if 1
11919   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11920   int xsize = (BX2 - BX1 + 1);
11921   int ysize = (BY2 - BY1 + 1);
11922   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11923   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11924   int step  = (start < end ? +1 : -1);
11925
11926   for (i = start; i != end; i += step)
11927   {
11928     BlitBitmap(drawto_field, drawto_field,
11929                FX + TILEX * (dx != 0 ? i + step : 0),
11930                FY + TILEY * (dy != 0 ? i + step : 0),
11931                TILEX * (dx != 0 ? 1 : xsize),
11932                TILEY * (dy != 0 ? 1 : ysize),
11933                FX + TILEX * (dx != 0 ? i : 0),
11934                FY + TILEY * (dy != 0 ? i : 0));
11935   }
11936
11937 #else
11938
11939   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11940
11941   BlitBitmap(drawto_field, drawto_field,
11942              FX + TILEX * (dx == -1) - softscroll_offset,
11943              FY + TILEY * (dy == -1) - softscroll_offset,
11944              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11945              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11946              FX + TILEX * (dx == 1) - softscroll_offset,
11947              FY + TILEY * (dy == 1) - softscroll_offset);
11948 #endif
11949 #endif
11950
11951   if (dx != 0)
11952   {
11953     x = (dx == 1 ? BX1 : BX2);
11954     for (y = BY1; y <= BY2; y++)
11955       DrawScreenField(x, y);
11956   }
11957
11958   if (dy != 0)
11959   {
11960     y = (dy == 1 ? BY1 : BY2);
11961     for (x = BX1; x <= BX2; x++)
11962       DrawScreenField(x, y);
11963   }
11964
11965   redraw_mask |= REDRAW_FIELD;
11966 }
11967
11968 static boolean canFallDown(struct PlayerInfo *player)
11969 {
11970   int jx = player->jx, jy = player->jy;
11971
11972   return (IN_LEV_FIELD(jx, jy + 1) &&
11973           (IS_FREE(jx, jy + 1) ||
11974            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11975           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11976           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11977 }
11978
11979 static boolean canPassField(int x, int y, int move_dir)
11980 {
11981   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11982   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11983   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11984   int nextx = x + dx;
11985   int nexty = y + dy;
11986   int element = Feld[x][y];
11987
11988   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11989           !CAN_MOVE(element) &&
11990           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11991           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11992           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11993 }
11994
11995 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11996 {
11997   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11998   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11999   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12000   int newx = x + dx;
12001   int newy = y + dy;
12002
12003   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12004           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12005           (IS_DIGGABLE(Feld[newx][newy]) ||
12006            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12007            canPassField(newx, newy, move_dir)));
12008 }
12009
12010 static void CheckGravityMovement(struct PlayerInfo *player)
12011 {
12012 #if USE_PLAYER_GRAVITY
12013   if (player->gravity && !player->programmed_action)
12014 #else
12015   if (game.gravity && !player->programmed_action)
12016 #endif
12017   {
12018     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12019     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12020     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12021     int jx = player->jx, jy = player->jy;
12022     boolean player_is_moving_to_valid_field =
12023       (!player_is_snapping &&
12024        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12025         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12026     boolean player_can_fall_down = canFallDown(player);
12027
12028     if (player_can_fall_down &&
12029         !player_is_moving_to_valid_field)
12030       player->programmed_action = MV_DOWN;
12031   }
12032 }
12033
12034 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12035 {
12036   return CheckGravityMovement(player);
12037
12038 #if USE_PLAYER_GRAVITY
12039   if (player->gravity && !player->programmed_action)
12040 #else
12041   if (game.gravity && !player->programmed_action)
12042 #endif
12043   {
12044     int jx = player->jx, jy = player->jy;
12045     boolean field_under_player_is_free =
12046       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12047     boolean player_is_standing_on_valid_field =
12048       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12049        (IS_WALKABLE(Feld[jx][jy]) &&
12050         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12051
12052     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12053       player->programmed_action = MV_DOWN;
12054   }
12055 }
12056
12057 /*
12058   MovePlayerOneStep()
12059   -----------------------------------------------------------------------------
12060   dx, dy:               direction (non-diagonal) to try to move the player to
12061   real_dx, real_dy:     direction as read from input device (can be diagonal)
12062 */
12063
12064 boolean MovePlayerOneStep(struct PlayerInfo *player,
12065                           int dx, int dy, int real_dx, int real_dy)
12066 {
12067   int jx = player->jx, jy = player->jy;
12068   int new_jx = jx + dx, new_jy = jy + dy;
12069 #if !USE_FIXED_DONT_RUN_INTO
12070   int element;
12071 #endif
12072   int can_move;
12073   boolean player_can_move = !player->cannot_move;
12074
12075   if (!player->active || (!dx && !dy))
12076     return MP_NO_ACTION;
12077
12078   player->MovDir = (dx < 0 ? MV_LEFT :
12079                     dx > 0 ? MV_RIGHT :
12080                     dy < 0 ? MV_UP :
12081                     dy > 0 ? MV_DOWN :  MV_NONE);
12082
12083   if (!IN_LEV_FIELD(new_jx, new_jy))
12084     return MP_NO_ACTION;
12085
12086   if (!player_can_move)
12087   {
12088     if (player->MovPos == 0)
12089     {
12090       player->is_moving = FALSE;
12091       player->is_digging = FALSE;
12092       player->is_collecting = FALSE;
12093       player->is_snapping = FALSE;
12094       player->is_pushing = FALSE;
12095     }
12096   }
12097
12098 #if 1
12099   if (!options.network && game.centered_player_nr == -1 &&
12100       !AllPlayersInSight(player, new_jx, new_jy))
12101     return MP_NO_ACTION;
12102 #else
12103   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12104     return MP_NO_ACTION;
12105 #endif
12106
12107 #if !USE_FIXED_DONT_RUN_INTO
12108   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12109
12110   /* (moved to DigField()) */
12111   if (player_can_move && DONT_RUN_INTO(element))
12112   {
12113     if (element == EL_ACID && dx == 0 && dy == 1)
12114     {
12115       SplashAcid(new_jx, new_jy);
12116       Feld[jx][jy] = EL_PLAYER_1;
12117       InitMovingField(jx, jy, MV_DOWN);
12118       Store[jx][jy] = EL_ACID;
12119       ContinueMoving(jx, jy);
12120       BuryPlayer(player);
12121     }
12122     else
12123       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12124
12125     return MP_MOVING;
12126   }
12127 #endif
12128
12129   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12130   if (can_move != MP_MOVING)
12131     return can_move;
12132
12133   /* check if DigField() has caused relocation of the player */
12134   if (player->jx != jx || player->jy != jy)
12135     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12136
12137   StorePlayer[jx][jy] = 0;
12138   player->last_jx = jx;
12139   player->last_jy = jy;
12140   player->jx = new_jx;
12141   player->jy = new_jy;
12142   StorePlayer[new_jx][new_jy] = player->element_nr;
12143
12144   if (player->move_delay_value_next != -1)
12145   {
12146     player->move_delay_value = player->move_delay_value_next;
12147     player->move_delay_value_next = -1;
12148   }
12149
12150   player->MovPos =
12151     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12152
12153   player->step_counter++;
12154
12155   PlayerVisit[jx][jy] = FrameCounter;
12156
12157 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12158   player->is_moving = TRUE;
12159 #endif
12160
12161 #if 1
12162   /* should better be called in MovePlayer(), but this breaks some tapes */
12163   ScrollPlayer(player, SCROLL_INIT);
12164 #endif
12165
12166   return MP_MOVING;
12167 }
12168
12169 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12170 {
12171   int jx = player->jx, jy = player->jy;
12172   int old_jx = jx, old_jy = jy;
12173   int moved = MP_NO_ACTION;
12174
12175   if (!player->active)
12176     return FALSE;
12177
12178   if (!dx && !dy)
12179   {
12180     if (player->MovPos == 0)
12181     {
12182       player->is_moving = FALSE;
12183       player->is_digging = FALSE;
12184       player->is_collecting = FALSE;
12185       player->is_snapping = FALSE;
12186       player->is_pushing = FALSE;
12187     }
12188
12189     return FALSE;
12190   }
12191
12192   if (player->move_delay > 0)
12193     return FALSE;
12194
12195   player->move_delay = -1;              /* set to "uninitialized" value */
12196
12197   /* store if player is automatically moved to next field */
12198   player->is_auto_moving = (player->programmed_action != MV_NONE);
12199
12200   /* remove the last programmed player action */
12201   player->programmed_action = 0;
12202
12203   if (player->MovPos)
12204   {
12205     /* should only happen if pre-1.2 tape recordings are played */
12206     /* this is only for backward compatibility */
12207
12208     int original_move_delay_value = player->move_delay_value;
12209
12210 #if DEBUG
12211     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12212            tape.counter);
12213 #endif
12214
12215     /* scroll remaining steps with finest movement resolution */
12216     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12217
12218     while (player->MovPos)
12219     {
12220       ScrollPlayer(player, SCROLL_GO_ON);
12221       ScrollScreen(NULL, SCROLL_GO_ON);
12222
12223       AdvanceFrameAndPlayerCounters(player->index_nr);
12224
12225       DrawAllPlayers();
12226       BackToFront();
12227     }
12228
12229     player->move_delay_value = original_move_delay_value;
12230   }
12231
12232   player->is_active = FALSE;
12233
12234   if (player->last_move_dir & MV_HORIZONTAL)
12235   {
12236     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12237       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12238   }
12239   else
12240   {
12241     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12242       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12243   }
12244
12245 #if USE_FIXED_BORDER_RUNNING_GFX
12246   if (!moved && !player->is_active)
12247   {
12248     player->is_moving = FALSE;
12249     player->is_digging = FALSE;
12250     player->is_collecting = FALSE;
12251     player->is_snapping = FALSE;
12252     player->is_pushing = FALSE;
12253   }
12254 #endif
12255
12256   jx = player->jx;
12257   jy = player->jy;
12258
12259 #if 1
12260   if (moved & MP_MOVING && !ScreenMovPos &&
12261       (player->index_nr == game.centered_player_nr ||
12262        game.centered_player_nr == -1))
12263 #else
12264   if (moved & MP_MOVING && !ScreenMovPos &&
12265       (player == local_player || !options.network))
12266 #endif
12267   {
12268     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12269     int offset = game.scroll_delay_value;
12270
12271     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12272     {
12273       /* actual player has left the screen -- scroll in that direction */
12274       if (jx != old_jx)         /* player has moved horizontally */
12275         scroll_x += (jx - old_jx);
12276       else                      /* player has moved vertically */
12277         scroll_y += (jy - old_jy);
12278     }
12279     else
12280     {
12281       if (jx != old_jx)         /* player has moved horizontally */
12282       {
12283         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12284             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12285           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12286
12287         /* don't scroll over playfield boundaries */
12288         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12289           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12290
12291         /* don't scroll more than one field at a time */
12292         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12293
12294         /* don't scroll against the player's moving direction */
12295         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12296             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12297           scroll_x = old_scroll_x;
12298       }
12299       else                      /* player has moved vertically */
12300       {
12301         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12302             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12303           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12304
12305         /* don't scroll over playfield boundaries */
12306         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12307           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12308
12309         /* don't scroll more than one field at a time */
12310         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12311
12312         /* don't scroll against the player's moving direction */
12313         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12314             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12315           scroll_y = old_scroll_y;
12316       }
12317     }
12318
12319     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12320     {
12321 #if 1
12322       if (!options.network && game.centered_player_nr == -1 &&
12323           !AllPlayersInVisibleScreen())
12324       {
12325         scroll_x = old_scroll_x;
12326         scroll_y = old_scroll_y;
12327       }
12328       else
12329 #else
12330       if (!options.network && !AllPlayersInVisibleScreen())
12331       {
12332         scroll_x = old_scroll_x;
12333         scroll_y = old_scroll_y;
12334       }
12335       else
12336 #endif
12337       {
12338         ScrollScreen(player, SCROLL_INIT);
12339         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12340       }
12341     }
12342   }
12343
12344   player->StepFrame = 0;
12345
12346   if (moved & MP_MOVING)
12347   {
12348     if (old_jx != jx && old_jy == jy)
12349       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12350     else if (old_jx == jx && old_jy != jy)
12351       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12352
12353     DrawLevelField(jx, jy);     /* for "crumbled sand" */
12354
12355     player->last_move_dir = player->MovDir;
12356     player->is_moving = TRUE;
12357     player->is_snapping = FALSE;
12358     player->is_switching = FALSE;
12359     player->is_dropping = FALSE;
12360     player->is_dropping_pressed = FALSE;
12361     player->drop_pressed_delay = 0;
12362
12363 #if 0
12364     /* should better be called here than above, but this breaks some tapes */
12365     ScrollPlayer(player, SCROLL_INIT);
12366 #endif
12367   }
12368   else
12369   {
12370     CheckGravityMovementWhenNotMoving(player);
12371
12372     player->is_moving = FALSE;
12373
12374     /* at this point, the player is allowed to move, but cannot move right now
12375        (e.g. because of something blocking the way) -- ensure that the player
12376        is also allowed to move in the next frame (in old versions before 3.1.1,
12377        the player was forced to wait again for eight frames before next try) */
12378
12379     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12380       player->move_delay = 0;   /* allow direct movement in the next frame */
12381   }
12382
12383   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12384     player->move_delay = player->move_delay_value;
12385
12386   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12387   {
12388     TestIfPlayerTouchesBadThing(jx, jy);
12389     TestIfPlayerTouchesCustomElement(jx, jy);
12390   }
12391
12392   if (!player->active)
12393     RemovePlayer(player);
12394
12395   return moved;
12396 }
12397
12398 void ScrollPlayer(struct PlayerInfo *player, int mode)
12399 {
12400   int jx = player->jx, jy = player->jy;
12401   int last_jx = player->last_jx, last_jy = player->last_jy;
12402   int move_stepsize = TILEX / player->move_delay_value;
12403
12404 #if USE_NEW_PLAYER_SPEED
12405   if (!player->active)
12406     return;
12407
12408   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12409     return;
12410 #else
12411   if (!player->active || player->MovPos == 0)
12412     return;
12413 #endif
12414
12415   if (mode == SCROLL_INIT)
12416   {
12417     player->actual_frame_counter = FrameCounter;
12418     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12419
12420     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12421         Feld[last_jx][last_jy] == EL_EMPTY)
12422     {
12423       int last_field_block_delay = 0;   /* start with no blocking at all */
12424       int block_delay_adjustment = player->block_delay_adjustment;
12425
12426       /* if player blocks last field, add delay for exactly one move */
12427       if (player->block_last_field)
12428       {
12429         last_field_block_delay += player->move_delay_value;
12430
12431         /* when blocking enabled, prevent moving up despite gravity */
12432 #if USE_PLAYER_GRAVITY
12433         if (player->gravity && player->MovDir == MV_UP)
12434           block_delay_adjustment = -1;
12435 #else
12436         if (game.gravity && player->MovDir == MV_UP)
12437           block_delay_adjustment = -1;
12438 #endif
12439       }
12440
12441       /* add block delay adjustment (also possible when not blocking) */
12442       last_field_block_delay += block_delay_adjustment;
12443
12444       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12445       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12446     }
12447
12448 #if USE_NEW_PLAYER_SPEED
12449     if (player->MovPos != 0)    /* player has not yet reached destination */
12450       return;
12451 #else
12452     return;
12453 #endif
12454   }
12455   else if (!FrameReached(&player->actual_frame_counter, 1))
12456     return;
12457
12458 #if USE_NEW_PLAYER_SPEED
12459   if (player->MovPos != 0)
12460   {
12461     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12462     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12463
12464     /* before DrawPlayer() to draw correct player graphic for this case */
12465     if (player->MovPos == 0)
12466       CheckGravityMovement(player);
12467   }
12468 #else
12469   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12470   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12471
12472   /* before DrawPlayer() to draw correct player graphic for this case */
12473   if (player->MovPos == 0)
12474     CheckGravityMovement(player);
12475 #endif
12476
12477   if (player->MovPos == 0)      /* player reached destination field */
12478   {
12479     if (player->move_delay_reset_counter > 0)
12480     {
12481       player->move_delay_reset_counter--;
12482
12483       if (player->move_delay_reset_counter == 0)
12484       {
12485         /* continue with normal speed after quickly moving through gate */
12486         HALVE_PLAYER_SPEED(player);
12487
12488         /* be able to make the next move without delay */
12489         player->move_delay = 0;
12490       }
12491     }
12492
12493     player->last_jx = jx;
12494     player->last_jy = jy;
12495
12496     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12497         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12498         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12499         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12500         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12501         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12502     {
12503       DrawPlayer(player);       /* needed here only to cleanup last field */
12504       RemovePlayer(player);
12505
12506       if (local_player->friends_still_needed == 0 ||
12507           IS_SP_ELEMENT(Feld[jx][jy]))
12508         PlayerWins(player);
12509     }
12510
12511     /* this breaks one level: "machine", level 000 */
12512     {
12513       int move_direction = player->MovDir;
12514       int enter_side = MV_DIR_OPPOSITE(move_direction);
12515       int leave_side = move_direction;
12516       int old_jx = last_jx;
12517       int old_jy = last_jy;
12518       int old_element = Feld[old_jx][old_jy];
12519       int new_element = Feld[jx][jy];
12520
12521       if (IS_CUSTOM_ELEMENT(old_element))
12522         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12523                                    CE_LEFT_BY_PLAYER,
12524                                    player->index_bit, leave_side);
12525
12526       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12527                                           CE_PLAYER_LEAVES_X,
12528                                           player->index_bit, leave_side);
12529
12530       if (IS_CUSTOM_ELEMENT(new_element))
12531         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12532                                    player->index_bit, enter_side);
12533
12534       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12535                                           CE_PLAYER_ENTERS_X,
12536                                           player->index_bit, enter_side);
12537
12538       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12539                                         CE_MOVE_OF_X, move_direction);
12540     }
12541
12542     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12543     {
12544       TestIfPlayerTouchesBadThing(jx, jy);
12545       TestIfPlayerTouchesCustomElement(jx, jy);
12546
12547       /* needed because pushed element has not yet reached its destination,
12548          so it would trigger a change event at its previous field location */
12549       if (!player->is_pushing)
12550         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12551
12552       if (!player->active)
12553         RemovePlayer(player);
12554     }
12555
12556     if (!local_player->LevelSolved && level.use_step_counter)
12557     {
12558       int i;
12559
12560       TimePlayed++;
12561
12562       if (TimeLeft > 0)
12563       {
12564         TimeLeft--;
12565
12566         if (TimeLeft <= 10 && setup.time_limit)
12567           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12568
12569 #if 1
12570         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
12571
12572         DisplayGameControlValues();
12573 #else
12574         DrawGameValue_Time(TimeLeft);
12575 #endif
12576
12577         if (!TimeLeft && setup.time_limit)
12578           for (i = 0; i < MAX_PLAYERS; i++)
12579             KillPlayer(&stored_player[i]);
12580       }
12581 #if 1
12582       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12583       {
12584         game_control_value[GAME_CONTROL_TIME] = TimePlayed;
12585
12586         DisplayGameControlValues();
12587       }
12588 #else
12589       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12590         DrawGameValue_Time(TimePlayed);
12591 #endif
12592     }
12593
12594     if (tape.single_step && tape.recording && !tape.pausing &&
12595         !player->programmed_action)
12596       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12597   }
12598 }
12599
12600 void ScrollScreen(struct PlayerInfo *player, int mode)
12601 {
12602   static unsigned long screen_frame_counter = 0;
12603
12604   if (mode == SCROLL_INIT)
12605   {
12606     /* set scrolling step size according to actual player's moving speed */
12607     ScrollStepSize = TILEX / player->move_delay_value;
12608
12609     screen_frame_counter = FrameCounter;
12610     ScreenMovDir = player->MovDir;
12611     ScreenMovPos = player->MovPos;
12612     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12613     return;
12614   }
12615   else if (!FrameReached(&screen_frame_counter, 1))
12616     return;
12617
12618   if (ScreenMovPos)
12619   {
12620     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12621     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12622     redraw_mask |= REDRAW_FIELD;
12623   }
12624   else
12625     ScreenMovDir = MV_NONE;
12626 }
12627
12628 void TestIfPlayerTouchesCustomElement(int x, int y)
12629 {
12630   static int xy[4][2] =
12631   {
12632     { 0, -1 },
12633     { -1, 0 },
12634     { +1, 0 },
12635     { 0, +1 }
12636   };
12637   static int trigger_sides[4][2] =
12638   {
12639     /* center side       border side */
12640     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12641     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12642     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12643     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12644   };
12645   static int touch_dir[4] =
12646   {
12647     MV_LEFT | MV_RIGHT,
12648     MV_UP   | MV_DOWN,
12649     MV_UP   | MV_DOWN,
12650     MV_LEFT | MV_RIGHT
12651   };
12652   int center_element = Feld[x][y];      /* should always be non-moving! */
12653   int i;
12654
12655   for (i = 0; i < NUM_DIRECTIONS; i++)
12656   {
12657     int xx = x + xy[i][0];
12658     int yy = y + xy[i][1];
12659     int center_side = trigger_sides[i][0];
12660     int border_side = trigger_sides[i][1];
12661     int border_element;
12662
12663     if (!IN_LEV_FIELD(xx, yy))
12664       continue;
12665
12666     if (IS_PLAYER(x, y))
12667     {
12668       struct PlayerInfo *player = PLAYERINFO(x, y);
12669
12670       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12671         border_element = Feld[xx][yy];          /* may be moving! */
12672       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12673         border_element = Feld[xx][yy];
12674       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12675         border_element = MovingOrBlocked2Element(xx, yy);
12676       else
12677         continue;               /* center and border element do not touch */
12678
12679       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12680                                  player->index_bit, border_side);
12681       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12682                                           CE_PLAYER_TOUCHES_X,
12683                                           player->index_bit, border_side);
12684     }
12685     else if (IS_PLAYER(xx, yy))
12686     {
12687       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12688
12689       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12690       {
12691         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12692           continue;             /* center and border element do not touch */
12693       }
12694
12695       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12696                                  player->index_bit, center_side);
12697       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12698                                           CE_PLAYER_TOUCHES_X,
12699                                           player->index_bit, center_side);
12700       break;
12701     }
12702   }
12703 }
12704
12705 #if USE_ELEMENT_TOUCHING_BUGFIX
12706
12707 void TestIfElementTouchesCustomElement(int x, int y)
12708 {
12709   static int xy[4][2] =
12710   {
12711     { 0, -1 },
12712     { -1, 0 },
12713     { +1, 0 },
12714     { 0, +1 }
12715   };
12716   static int trigger_sides[4][2] =
12717   {
12718     /* center side      border side */
12719     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12720     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12721     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12722     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12723   };
12724   static int touch_dir[4] =
12725   {
12726     MV_LEFT | MV_RIGHT,
12727     MV_UP   | MV_DOWN,
12728     MV_UP   | MV_DOWN,
12729     MV_LEFT | MV_RIGHT
12730   };
12731   boolean change_center_element = FALSE;
12732   int center_element = Feld[x][y];      /* should always be non-moving! */
12733   int border_element_old[NUM_DIRECTIONS];
12734   int i;
12735
12736   for (i = 0; i < NUM_DIRECTIONS; i++)
12737   {
12738     int xx = x + xy[i][0];
12739     int yy = y + xy[i][1];
12740     int border_element;
12741
12742     border_element_old[i] = -1;
12743
12744     if (!IN_LEV_FIELD(xx, yy))
12745       continue;
12746
12747     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12748       border_element = Feld[xx][yy];    /* may be moving! */
12749     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12750       border_element = Feld[xx][yy];
12751     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12752       border_element = MovingOrBlocked2Element(xx, yy);
12753     else
12754       continue;                 /* center and border element do not touch */
12755
12756     border_element_old[i] = border_element;
12757   }
12758
12759   for (i = 0; i < NUM_DIRECTIONS; i++)
12760   {
12761     int xx = x + xy[i][0];
12762     int yy = y + xy[i][1];
12763     int center_side = trigger_sides[i][0];
12764     int border_element = border_element_old[i];
12765
12766     if (border_element == -1)
12767       continue;
12768
12769     /* check for change of border element */
12770     CheckElementChangeBySide(xx, yy, border_element, center_element,
12771                              CE_TOUCHING_X, center_side);
12772   }
12773
12774   for (i = 0; i < NUM_DIRECTIONS; i++)
12775   {
12776     int border_side = trigger_sides[i][1];
12777     int border_element = border_element_old[i];
12778
12779     if (border_element == -1)
12780       continue;
12781
12782     /* check for change of center element (but change it only once) */
12783     if (!change_center_element)
12784       change_center_element =
12785         CheckElementChangeBySide(x, y, center_element, border_element,
12786                                  CE_TOUCHING_X, border_side);
12787   }
12788 }
12789
12790 #else
12791
12792 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12793 {
12794   static int xy[4][2] =
12795   {
12796     { 0, -1 },
12797     { -1, 0 },
12798     { +1, 0 },
12799     { 0, +1 }
12800   };
12801   static int trigger_sides[4][2] =
12802   {
12803     /* center side      border side */
12804     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12805     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12806     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12807     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12808   };
12809   static int touch_dir[4] =
12810   {
12811     MV_LEFT | MV_RIGHT,
12812     MV_UP   | MV_DOWN,
12813     MV_UP   | MV_DOWN,
12814     MV_LEFT | MV_RIGHT
12815   };
12816   boolean change_center_element = FALSE;
12817   int center_element = Feld[x][y];      /* should always be non-moving! */
12818   int i;
12819
12820   for (i = 0; i < NUM_DIRECTIONS; i++)
12821   {
12822     int xx = x + xy[i][0];
12823     int yy = y + xy[i][1];
12824     int center_side = trigger_sides[i][0];
12825     int border_side = trigger_sides[i][1];
12826     int border_element;
12827
12828     if (!IN_LEV_FIELD(xx, yy))
12829       continue;
12830
12831     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12832       border_element = Feld[xx][yy];    /* may be moving! */
12833     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12834       border_element = Feld[xx][yy];
12835     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12836       border_element = MovingOrBlocked2Element(xx, yy);
12837     else
12838       continue;                 /* center and border element do not touch */
12839
12840     /* check for change of center element (but change it only once) */
12841     if (!change_center_element)
12842       change_center_element =
12843         CheckElementChangeBySide(x, y, center_element, border_element,
12844                                  CE_TOUCHING_X, border_side);
12845
12846     /* check for change of border element */
12847     CheckElementChangeBySide(xx, yy, border_element, center_element,
12848                              CE_TOUCHING_X, center_side);
12849   }
12850 }
12851
12852 #endif
12853
12854 void TestIfElementHitsCustomElement(int x, int y, int direction)
12855 {
12856   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12857   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12858   int hitx = x + dx, hity = y + dy;
12859   int hitting_element = Feld[x][y];
12860   int touched_element;
12861
12862   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12863     return;
12864
12865   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12866                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12867
12868   if (IN_LEV_FIELD(hitx, hity))
12869   {
12870     int opposite_direction = MV_DIR_OPPOSITE(direction);
12871     int hitting_side = direction;
12872     int touched_side = opposite_direction;
12873     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12874                           MovDir[hitx][hity] != direction ||
12875                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12876
12877     object_hit = TRUE;
12878
12879     if (object_hit)
12880     {
12881       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12882                                CE_HITTING_X, touched_side);
12883
12884       CheckElementChangeBySide(hitx, hity, touched_element,
12885                                hitting_element, CE_HIT_BY_X, hitting_side);
12886
12887       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12888                                CE_HIT_BY_SOMETHING, opposite_direction);
12889     }
12890   }
12891
12892   /* "hitting something" is also true when hitting the playfield border */
12893   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12894                            CE_HITTING_SOMETHING, direction);
12895 }
12896
12897 #if 0
12898 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12899 {
12900   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12901   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12902   int hitx = x + dx, hity = y + dy;
12903   int hitting_element = Feld[x][y];
12904   int touched_element;
12905 #if 0
12906   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12907                         !IS_FREE(hitx, hity) &&
12908                         (!IS_MOVING(hitx, hity) ||
12909                          MovDir[hitx][hity] != direction ||
12910                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
12911 #endif
12912
12913   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12914     return;
12915
12916 #if 0
12917   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12918     return;
12919 #endif
12920
12921   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12922                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12923
12924   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12925                            EP_CAN_SMASH_EVERYTHING, direction);
12926
12927   if (IN_LEV_FIELD(hitx, hity))
12928   {
12929     int opposite_direction = MV_DIR_OPPOSITE(direction);
12930     int hitting_side = direction;
12931     int touched_side = opposite_direction;
12932 #if 0
12933     int touched_element = MovingOrBlocked2Element(hitx, hity);
12934 #endif
12935 #if 1
12936     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12937                           MovDir[hitx][hity] != direction ||
12938                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12939
12940     object_hit = TRUE;
12941 #endif
12942
12943     if (object_hit)
12944     {
12945       int i;
12946
12947       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12948                                CE_SMASHED_BY_SOMETHING, opposite_direction);
12949
12950       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12951                                CE_OTHER_IS_SMASHING, touched_side);
12952
12953       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12954                                CE_OTHER_GETS_SMASHED, hitting_side);
12955     }
12956   }
12957 }
12958 #endif
12959
12960 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12961 {
12962   int i, kill_x = -1, kill_y = -1;
12963
12964   int bad_element = -1;
12965   static int test_xy[4][2] =
12966   {
12967     { 0, -1 },
12968     { -1, 0 },
12969     { +1, 0 },
12970     { 0, +1 }
12971   };
12972   static int test_dir[4] =
12973   {
12974     MV_UP,
12975     MV_LEFT,
12976     MV_RIGHT,
12977     MV_DOWN
12978   };
12979
12980   for (i = 0; i < NUM_DIRECTIONS; i++)
12981   {
12982     int test_x, test_y, test_move_dir, test_element;
12983
12984     test_x = good_x + test_xy[i][0];
12985     test_y = good_y + test_xy[i][1];
12986
12987     if (!IN_LEV_FIELD(test_x, test_y))
12988       continue;
12989
12990     test_move_dir =
12991       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12992
12993     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12994
12995     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12996        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12997     */
12998     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12999         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13000     {
13001       kill_x = test_x;
13002       kill_y = test_y;
13003       bad_element = test_element;
13004
13005       break;
13006     }
13007   }
13008
13009   if (kill_x != -1 || kill_y != -1)
13010   {
13011     if (IS_PLAYER(good_x, good_y))
13012     {
13013       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13014
13015       if (player->shield_deadly_time_left > 0 &&
13016           !IS_INDESTRUCTIBLE(bad_element))
13017         Bang(kill_x, kill_y);
13018       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13019         KillPlayer(player);
13020     }
13021     else
13022       Bang(good_x, good_y);
13023   }
13024 }
13025
13026 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13027 {
13028   int i, kill_x = -1, kill_y = -1;
13029   int bad_element = Feld[bad_x][bad_y];
13030   static int test_xy[4][2] =
13031   {
13032     { 0, -1 },
13033     { -1, 0 },
13034     { +1, 0 },
13035     { 0, +1 }
13036   };
13037   static int touch_dir[4] =
13038   {
13039     MV_LEFT | MV_RIGHT,
13040     MV_UP   | MV_DOWN,
13041     MV_UP   | MV_DOWN,
13042     MV_LEFT | MV_RIGHT
13043   };
13044   static int test_dir[4] =
13045   {
13046     MV_UP,
13047     MV_LEFT,
13048     MV_RIGHT,
13049     MV_DOWN
13050   };
13051
13052   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13053     return;
13054
13055   for (i = 0; i < NUM_DIRECTIONS; i++)
13056   {
13057     int test_x, test_y, test_move_dir, test_element;
13058
13059     test_x = bad_x + test_xy[i][0];
13060     test_y = bad_y + test_xy[i][1];
13061     if (!IN_LEV_FIELD(test_x, test_y))
13062       continue;
13063
13064     test_move_dir =
13065       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13066
13067     test_element = Feld[test_x][test_y];
13068
13069     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13070        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13071     */
13072     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13073         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13074     {
13075       /* good thing is player or penguin that does not move away */
13076       if (IS_PLAYER(test_x, test_y))
13077       {
13078         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13079
13080         if (bad_element == EL_ROBOT && player->is_moving)
13081           continue;     /* robot does not kill player if he is moving */
13082
13083         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13084         {
13085           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13086             continue;           /* center and border element do not touch */
13087         }
13088
13089         kill_x = test_x;
13090         kill_y = test_y;
13091         break;
13092       }
13093       else if (test_element == EL_PENGUIN)
13094       {
13095         kill_x = test_x;
13096         kill_y = test_y;
13097         break;
13098       }
13099     }
13100   }
13101
13102   if (kill_x != -1 || kill_y != -1)
13103   {
13104     if (IS_PLAYER(kill_x, kill_y))
13105     {
13106       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13107
13108       if (player->shield_deadly_time_left > 0 &&
13109           !IS_INDESTRUCTIBLE(bad_element))
13110         Bang(bad_x, bad_y);
13111       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13112         KillPlayer(player);
13113     }
13114     else
13115       Bang(kill_x, kill_y);
13116   }
13117 }
13118
13119 void TestIfPlayerTouchesBadThing(int x, int y)
13120 {
13121   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13122 }
13123
13124 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13125 {
13126   TestIfGoodThingHitsBadThing(x, y, move_dir);
13127 }
13128
13129 void TestIfBadThingTouchesPlayer(int x, int y)
13130 {
13131   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13132 }
13133
13134 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13135 {
13136   TestIfBadThingHitsGoodThing(x, y, move_dir);
13137 }
13138
13139 void TestIfFriendTouchesBadThing(int x, int y)
13140 {
13141   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13142 }
13143
13144 void TestIfBadThingTouchesFriend(int x, int y)
13145 {
13146   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13147 }
13148
13149 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13150 {
13151   int i, kill_x = bad_x, kill_y = bad_y;
13152   static int xy[4][2] =
13153   {
13154     { 0, -1 },
13155     { -1, 0 },
13156     { +1, 0 },
13157     { 0, +1 }
13158   };
13159
13160   for (i = 0; i < NUM_DIRECTIONS; i++)
13161   {
13162     int x, y, element;
13163
13164     x = bad_x + xy[i][0];
13165     y = bad_y + xy[i][1];
13166     if (!IN_LEV_FIELD(x, y))
13167       continue;
13168
13169     element = Feld[x][y];
13170     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13171         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13172     {
13173       kill_x = x;
13174       kill_y = y;
13175       break;
13176     }
13177   }
13178
13179   if (kill_x != bad_x || kill_y != bad_y)
13180     Bang(bad_x, bad_y);
13181 }
13182
13183 void KillPlayer(struct PlayerInfo *player)
13184 {
13185   int jx = player->jx, jy = player->jy;
13186
13187   if (!player->active)
13188     return;
13189
13190   /* the following code was introduced to prevent an infinite loop when calling
13191      -> Bang()
13192      -> CheckTriggeredElementChangeExt()
13193      -> ExecuteCustomElementAction()
13194      -> KillPlayer()
13195      -> (infinitely repeating the above sequence of function calls)
13196      which occurs when killing the player while having a CE with the setting
13197      "kill player X when explosion of <player X>"; the solution using a new
13198      field "player->killed" was chosen for backwards compatibility, although
13199      clever use of the fields "player->active" etc. would probably also work */
13200 #if 1
13201   if (player->killed)
13202     return;
13203 #endif
13204
13205   player->killed = TRUE;
13206
13207   /* remove accessible field at the player's position */
13208   Feld[jx][jy] = EL_EMPTY;
13209
13210   /* deactivate shield (else Bang()/Explode() would not work right) */
13211   player->shield_normal_time_left = 0;
13212   player->shield_deadly_time_left = 0;
13213
13214   Bang(jx, jy);
13215   BuryPlayer(player);
13216 }
13217
13218 static void KillPlayerUnlessEnemyProtected(int x, int y)
13219 {
13220   if (!PLAYER_ENEMY_PROTECTED(x, y))
13221     KillPlayer(PLAYERINFO(x, y));
13222 }
13223
13224 static void KillPlayerUnlessExplosionProtected(int x, int y)
13225 {
13226   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13227     KillPlayer(PLAYERINFO(x, y));
13228 }
13229
13230 void BuryPlayer(struct PlayerInfo *player)
13231 {
13232   int jx = player->jx, jy = player->jy;
13233
13234   if (!player->active)
13235     return;
13236
13237   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13238   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13239
13240   player->GameOver = TRUE;
13241   RemovePlayer(player);
13242 }
13243
13244 void RemovePlayer(struct PlayerInfo *player)
13245 {
13246   int jx = player->jx, jy = player->jy;
13247   int i, found = FALSE;
13248
13249   player->present = FALSE;
13250   player->active = FALSE;
13251
13252   if (!ExplodeField[jx][jy])
13253     StorePlayer[jx][jy] = 0;
13254
13255   if (player->is_moving)
13256     DrawLevelField(player->last_jx, player->last_jy);
13257
13258   for (i = 0; i < MAX_PLAYERS; i++)
13259     if (stored_player[i].active)
13260       found = TRUE;
13261
13262   if (!found)
13263     AllPlayersGone = TRUE;
13264
13265   ExitX = ZX = jx;
13266   ExitY = ZY = jy;
13267 }
13268
13269 #if USE_NEW_SNAP_DELAY
13270 static void setFieldForSnapping(int x, int y, int element, int direction)
13271 {
13272   struct ElementInfo *ei = &element_info[element];
13273   int direction_bit = MV_DIR_TO_BIT(direction);
13274   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13275   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13276                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13277
13278   Feld[x][y] = EL_ELEMENT_SNAPPING;
13279   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13280
13281   ResetGfxAnimation(x, y);
13282
13283   GfxElement[x][y] = element;
13284   GfxAction[x][y] = action;
13285   GfxDir[x][y] = direction;
13286   GfxFrame[x][y] = -1;
13287 }
13288 #endif
13289
13290 /*
13291   =============================================================================
13292   checkDiagonalPushing()
13293   -----------------------------------------------------------------------------
13294   check if diagonal input device direction results in pushing of object
13295   (by checking if the alternative direction is walkable, diggable, ...)
13296   =============================================================================
13297 */
13298
13299 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13300                                     int x, int y, int real_dx, int real_dy)
13301 {
13302   int jx, jy, dx, dy, xx, yy;
13303
13304   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13305     return TRUE;
13306
13307   /* diagonal direction: check alternative direction */
13308   jx = player->jx;
13309   jy = player->jy;
13310   dx = x - jx;
13311   dy = y - jy;
13312   xx = jx + (dx == 0 ? real_dx : 0);
13313   yy = jy + (dy == 0 ? real_dy : 0);
13314
13315   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13316 }
13317
13318 /*
13319   =============================================================================
13320   DigField()
13321   -----------------------------------------------------------------------------
13322   x, y:                 field next to player (non-diagonal) to try to dig to
13323   real_dx, real_dy:     direction as read from input device (can be diagonal)
13324   =============================================================================
13325 */
13326
13327 int DigField(struct PlayerInfo *player,
13328              int oldx, int oldy, int x, int y,
13329              int real_dx, int real_dy, int mode)
13330 {
13331   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13332   boolean player_was_pushing = player->is_pushing;
13333   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13334   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13335   int jx = oldx, jy = oldy;
13336   int dx = x - jx, dy = y - jy;
13337   int nextx = x + dx, nexty = y + dy;
13338   int move_direction = (dx == -1 ? MV_LEFT  :
13339                         dx == +1 ? MV_RIGHT :
13340                         dy == -1 ? MV_UP    :
13341                         dy == +1 ? MV_DOWN  : MV_NONE);
13342   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13343   int dig_side = MV_DIR_OPPOSITE(move_direction);
13344   int old_element = Feld[jx][jy];
13345 #if USE_FIXED_DONT_RUN_INTO
13346   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13347 #else
13348   int element;
13349 #endif
13350   int collect_count;
13351
13352   if (is_player)                /* function can also be called by EL_PENGUIN */
13353   {
13354     if (player->MovPos == 0)
13355     {
13356       player->is_digging = FALSE;
13357       player->is_collecting = FALSE;
13358     }
13359
13360     if (player->MovPos == 0)    /* last pushing move finished */
13361       player->is_pushing = FALSE;
13362
13363     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13364     {
13365       player->is_switching = FALSE;
13366       player->push_delay = -1;
13367
13368       return MP_NO_ACTION;
13369     }
13370   }
13371
13372 #if !USE_FIXED_DONT_RUN_INTO
13373   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13374     return MP_NO_ACTION;
13375 #endif
13376
13377   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13378     old_element = Back[jx][jy];
13379
13380   /* in case of element dropped at player position, check background */
13381   else if (Back[jx][jy] != EL_EMPTY &&
13382            game.engine_version >= VERSION_IDENT(2,2,0,0))
13383     old_element = Back[jx][jy];
13384
13385   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13386     return MP_NO_ACTION;        /* field has no opening in this direction */
13387
13388   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13389     return MP_NO_ACTION;        /* field has no opening in this direction */
13390
13391 #if USE_FIXED_DONT_RUN_INTO
13392   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13393   {
13394     SplashAcid(x, y);
13395
13396     Feld[jx][jy] = player->artwork_element;
13397     InitMovingField(jx, jy, MV_DOWN);
13398     Store[jx][jy] = EL_ACID;
13399     ContinueMoving(jx, jy);
13400     BuryPlayer(player);
13401
13402     return MP_DONT_RUN_INTO;
13403   }
13404 #endif
13405
13406 #if USE_FIXED_DONT_RUN_INTO
13407   if (player_can_move && DONT_RUN_INTO(element))
13408   {
13409     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13410
13411     return MP_DONT_RUN_INTO;
13412   }
13413 #endif
13414
13415 #if USE_FIXED_DONT_RUN_INTO
13416   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13417     return MP_NO_ACTION;
13418 #endif
13419
13420 #if !USE_FIXED_DONT_RUN_INTO
13421   element = Feld[x][y];
13422 #endif
13423
13424   collect_count = element_info[element].collect_count_initial;
13425
13426   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13427     return MP_NO_ACTION;
13428
13429   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13430     player_can_move = player_can_move_or_snap;
13431
13432   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13433       game.engine_version >= VERSION_IDENT(2,2,0,0))
13434   {
13435     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13436                                player->index_bit, dig_side);
13437     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13438                                         player->index_bit, dig_side);
13439
13440     if (element == EL_DC_LANDMINE)
13441       Bang(x, y);
13442
13443     if (Feld[x][y] != element)          /* field changed by snapping */
13444       return MP_ACTION;
13445
13446     return MP_NO_ACTION;
13447   }
13448
13449 #if USE_PLAYER_GRAVITY
13450   if (player->gravity && is_player && !player->is_auto_moving &&
13451       canFallDown(player) && move_direction != MV_DOWN &&
13452       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13453     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13454 #else
13455   if (game.gravity && is_player && !player->is_auto_moving &&
13456       canFallDown(player) && move_direction != MV_DOWN &&
13457       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13458     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13459 #endif
13460
13461   if (player_can_move &&
13462       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13463   {
13464     int sound_element = SND_ELEMENT(element);
13465     int sound_action = ACTION_WALKING;
13466
13467     if (IS_RND_GATE(element))
13468     {
13469       if (!player->key[RND_GATE_NR(element)])
13470         return MP_NO_ACTION;
13471     }
13472     else if (IS_RND_GATE_GRAY(element))
13473     {
13474       if (!player->key[RND_GATE_GRAY_NR(element)])
13475         return MP_NO_ACTION;
13476     }
13477     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13478     {
13479       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13480         return MP_NO_ACTION;
13481     }
13482     else if (element == EL_EXIT_OPEN ||
13483              element == EL_EM_EXIT_OPEN ||
13484              element == EL_STEEL_EXIT_OPEN ||
13485              element == EL_EM_STEEL_EXIT_OPEN ||
13486              element == EL_SP_EXIT_OPEN ||
13487              element == EL_SP_EXIT_OPENING)
13488     {
13489       sound_action = ACTION_PASSING;    /* player is passing exit */
13490     }
13491     else if (element == EL_EMPTY)
13492     {
13493       sound_action = ACTION_MOVING;             /* nothing to walk on */
13494     }
13495
13496     /* play sound from background or player, whatever is available */
13497     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13498       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13499     else
13500       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13501   }
13502   else if (player_can_move &&
13503            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13504   {
13505     if (!ACCESS_FROM(element, opposite_direction))
13506       return MP_NO_ACTION;      /* field not accessible from this direction */
13507
13508     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13509       return MP_NO_ACTION;
13510
13511     if (IS_EM_GATE(element))
13512     {
13513       if (!player->key[EM_GATE_NR(element)])
13514         return MP_NO_ACTION;
13515     }
13516     else if (IS_EM_GATE_GRAY(element))
13517     {
13518       if (!player->key[EM_GATE_GRAY_NR(element)])
13519         return MP_NO_ACTION;
13520     }
13521     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13522     {
13523       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13524         return MP_NO_ACTION;
13525     }
13526     else if (IS_EMC_GATE(element))
13527     {
13528       if (!player->key[EMC_GATE_NR(element)])
13529         return MP_NO_ACTION;
13530     }
13531     else if (IS_EMC_GATE_GRAY(element))
13532     {
13533       if (!player->key[EMC_GATE_GRAY_NR(element)])
13534         return MP_NO_ACTION;
13535     }
13536     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13537     {
13538       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13539         return MP_NO_ACTION;
13540     }
13541     else if (element == EL_DC_GATE_WHITE ||
13542              element == EL_DC_GATE_WHITE_GRAY ||
13543              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13544     {
13545       if (player->num_white_keys == 0)
13546         return MP_NO_ACTION;
13547
13548       player->num_white_keys--;
13549     }
13550     else if (IS_SP_PORT(element))
13551     {
13552       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13553           element == EL_SP_GRAVITY_PORT_RIGHT ||
13554           element == EL_SP_GRAVITY_PORT_UP ||
13555           element == EL_SP_GRAVITY_PORT_DOWN)
13556 #if USE_PLAYER_GRAVITY
13557         player->gravity = !player->gravity;
13558 #else
13559         game.gravity = !game.gravity;
13560 #endif
13561       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13562                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13563                element == EL_SP_GRAVITY_ON_PORT_UP ||
13564                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13565 #if USE_PLAYER_GRAVITY
13566         player->gravity = TRUE;
13567 #else
13568         game.gravity = TRUE;
13569 #endif
13570       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13571                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13572                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13573                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13574 #if USE_PLAYER_GRAVITY
13575         player->gravity = FALSE;
13576 #else
13577         game.gravity = FALSE;
13578 #endif
13579     }
13580
13581     /* automatically move to the next field with double speed */
13582     player->programmed_action = move_direction;
13583
13584     if (player->move_delay_reset_counter == 0)
13585     {
13586       player->move_delay_reset_counter = 2;     /* two double speed steps */
13587
13588       DOUBLE_PLAYER_SPEED(player);
13589     }
13590
13591     PlayLevelSoundAction(x, y, ACTION_PASSING);
13592   }
13593   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13594   {
13595     RemoveField(x, y);
13596
13597     if (mode != DF_SNAP)
13598     {
13599       GfxElement[x][y] = GFX_ELEMENT(element);
13600       player->is_digging = TRUE;
13601     }
13602
13603     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13604
13605     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13606                                         player->index_bit, dig_side);
13607
13608     if (mode == DF_SNAP)
13609     {
13610 #if USE_NEW_SNAP_DELAY
13611       if (level.block_snap_field)
13612         setFieldForSnapping(x, y, element, move_direction);
13613       else
13614         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13615 #else
13616       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13617 #endif
13618
13619       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13620                                           player->index_bit, dig_side);
13621     }
13622   }
13623   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13624   {
13625     RemoveField(x, y);
13626
13627     if (is_player && mode != DF_SNAP)
13628     {
13629       GfxElement[x][y] = element;
13630       player->is_collecting = TRUE;
13631     }
13632
13633     if (element == EL_SPEED_PILL)
13634     {
13635       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13636     }
13637     else if (element == EL_EXTRA_TIME && level.time > 0)
13638     {
13639       TimeLeft += level.extra_time;
13640
13641 #if 1
13642       game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13643
13644       DisplayGameControlValues();
13645 #else
13646       DrawGameValue_Time(TimeLeft);
13647 #endif
13648     }
13649     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13650     {
13651       player->shield_normal_time_left += level.shield_normal_time;
13652       if (element == EL_SHIELD_DEADLY)
13653         player->shield_deadly_time_left += level.shield_deadly_time;
13654     }
13655     else if (element == EL_DYNAMITE ||
13656              element == EL_EM_DYNAMITE ||
13657              element == EL_SP_DISK_RED)
13658     {
13659       if (player->inventory_size < MAX_INVENTORY_SIZE)
13660         player->inventory_element[player->inventory_size++] = element;
13661
13662       DrawGameDoorValues();
13663     }
13664     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13665     {
13666       player->dynabomb_count++;
13667       player->dynabombs_left++;
13668     }
13669     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13670     {
13671       player->dynabomb_size++;
13672     }
13673     else if (element == EL_DYNABOMB_INCREASE_POWER)
13674     {
13675       player->dynabomb_xl = TRUE;
13676     }
13677     else if (IS_KEY(element))
13678     {
13679       player->key[KEY_NR(element)] = TRUE;
13680
13681       DrawGameDoorValues();
13682     }
13683     else if (element == EL_DC_KEY_WHITE)
13684     {
13685       player->num_white_keys++;
13686
13687       /* display white keys? */
13688       /* DrawGameDoorValues(); */
13689     }
13690     else if (IS_ENVELOPE(element))
13691     {
13692       player->show_envelope = element;
13693     }
13694     else if (element == EL_EMC_LENSES)
13695     {
13696       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13697
13698       RedrawAllInvisibleElementsForLenses();
13699     }
13700     else if (element == EL_EMC_MAGNIFIER)
13701     {
13702       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13703
13704       RedrawAllInvisibleElementsForMagnifier();
13705     }
13706     else if (IS_DROPPABLE(element) ||
13707              IS_THROWABLE(element))     /* can be collected and dropped */
13708     {
13709       int i;
13710
13711       if (collect_count == 0)
13712         player->inventory_infinite_element = element;
13713       else
13714         for (i = 0; i < collect_count; i++)
13715           if (player->inventory_size < MAX_INVENTORY_SIZE)
13716             player->inventory_element[player->inventory_size++] = element;
13717
13718       DrawGameDoorValues();
13719     }
13720     else if (collect_count > 0)
13721     {
13722       local_player->gems_still_needed -= collect_count;
13723       if (local_player->gems_still_needed < 0)
13724         local_player->gems_still_needed = 0;
13725
13726 #if 1
13727       game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
13728
13729       DisplayGameControlValues();
13730 #else
13731       DrawGameValue_Emeralds(local_player->gems_still_needed);
13732 #endif
13733     }
13734
13735     RaiseScoreElement(element);
13736     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13737
13738     if (is_player)
13739       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13740                                           player->index_bit, dig_side);
13741
13742     if (mode == DF_SNAP)
13743     {
13744 #if USE_NEW_SNAP_DELAY
13745       if (level.block_snap_field)
13746         setFieldForSnapping(x, y, element, move_direction);
13747       else
13748         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13749 #else
13750       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13751 #endif
13752
13753       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13754                                           player->index_bit, dig_side);
13755     }
13756   }
13757   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13758   {
13759     if (mode == DF_SNAP && element != EL_BD_ROCK)
13760       return MP_NO_ACTION;
13761
13762     if (CAN_FALL(element) && dy)
13763       return MP_NO_ACTION;
13764
13765     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13766         !(element == EL_SPRING && level.use_spring_bug))
13767       return MP_NO_ACTION;
13768
13769     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13770         ((move_direction & MV_VERTICAL &&
13771           ((element_info[element].move_pattern & MV_LEFT &&
13772             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13773            (element_info[element].move_pattern & MV_RIGHT &&
13774             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13775          (move_direction & MV_HORIZONTAL &&
13776           ((element_info[element].move_pattern & MV_UP &&
13777             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13778            (element_info[element].move_pattern & MV_DOWN &&
13779             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13780       return MP_NO_ACTION;
13781
13782     /* do not push elements already moving away faster than player */
13783     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13784         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13785       return MP_NO_ACTION;
13786
13787     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13788     {
13789       if (player->push_delay_value == -1 || !player_was_pushing)
13790         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13791     }
13792     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13793     {
13794       if (player->push_delay_value == -1)
13795         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13796     }
13797     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13798     {
13799       if (!player->is_pushing)
13800         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13801     }
13802
13803     player->is_pushing = TRUE;
13804     player->is_active = TRUE;
13805
13806     if (!(IN_LEV_FIELD(nextx, nexty) &&
13807           (IS_FREE(nextx, nexty) ||
13808            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13809             IS_SB_ELEMENT(element)))))
13810       return MP_NO_ACTION;
13811
13812     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13813       return MP_NO_ACTION;
13814
13815     if (player->push_delay == -1)       /* new pushing; restart delay */
13816       player->push_delay = 0;
13817
13818     if (player->push_delay < player->push_delay_value &&
13819         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13820         element != EL_SPRING && element != EL_BALLOON)
13821     {
13822       /* make sure that there is no move delay before next try to push */
13823       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13824         player->move_delay = 0;
13825
13826       return MP_NO_ACTION;
13827     }
13828
13829     if (IS_SB_ELEMENT(element))
13830     {
13831       if (element == EL_SOKOBAN_FIELD_FULL)
13832       {
13833         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13834         local_player->sokobanfields_still_needed++;
13835       }
13836
13837       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13838       {
13839         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13840         local_player->sokobanfields_still_needed--;
13841       }
13842
13843       Feld[x][y] = EL_SOKOBAN_OBJECT;
13844
13845       if (Back[x][y] == Back[nextx][nexty])
13846         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13847       else if (Back[x][y] != 0)
13848         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13849                                     ACTION_EMPTYING);
13850       else
13851         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13852                                     ACTION_FILLING);
13853
13854       if (local_player->sokobanfields_still_needed == 0 &&
13855           game.emulation == EMU_SOKOBAN)
13856       {
13857         PlayerWins(player);
13858
13859         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13860       }
13861     }
13862     else
13863       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13864
13865     InitMovingField(x, y, move_direction);
13866     GfxAction[x][y] = ACTION_PUSHING;
13867
13868     if (mode == DF_SNAP)
13869       ContinueMoving(x, y);
13870     else
13871       MovPos[x][y] = (dx != 0 ? dx : dy);
13872
13873     Pushed[x][y] = TRUE;
13874     Pushed[nextx][nexty] = TRUE;
13875
13876     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13877       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13878     else
13879       player->push_delay_value = -1;    /* get new value later */
13880
13881     /* check for element change _after_ element has been pushed */
13882     if (game.use_change_when_pushing_bug)
13883     {
13884       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13885                                  player->index_bit, dig_side);
13886       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13887                                           player->index_bit, dig_side);
13888     }
13889   }
13890   else if (IS_SWITCHABLE(element))
13891   {
13892     if (PLAYER_SWITCHING(player, x, y))
13893     {
13894       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13895                                           player->index_bit, dig_side);
13896
13897       return MP_ACTION;
13898     }
13899
13900     player->is_switching = TRUE;
13901     player->switch_x = x;
13902     player->switch_y = y;
13903
13904     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13905
13906     if (element == EL_ROBOT_WHEEL)
13907     {
13908       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13909       ZX = x;
13910       ZY = y;
13911
13912       DrawLevelField(x, y);
13913     }
13914     else if (element == EL_SP_TERMINAL)
13915     {
13916       int xx, yy;
13917
13918       SCAN_PLAYFIELD(xx, yy)
13919       {
13920         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13921           Bang(xx, yy);
13922         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13923           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13924       }
13925     }
13926     else if (IS_BELT_SWITCH(element))
13927     {
13928       ToggleBeltSwitch(x, y);
13929     }
13930     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13931              element == EL_SWITCHGATE_SWITCH_DOWN ||
13932              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13933              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13934     {
13935       ToggleSwitchgateSwitch(x, y);
13936     }
13937     else if (element == EL_LIGHT_SWITCH ||
13938              element == EL_LIGHT_SWITCH_ACTIVE)
13939     {
13940       ToggleLightSwitch(x, y);
13941     }
13942     else if (element == EL_TIMEGATE_SWITCH ||
13943              element == EL_DC_TIMEGATE_SWITCH)
13944     {
13945       ActivateTimegateSwitch(x, y);
13946     }
13947     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13948              element == EL_BALLOON_SWITCH_RIGHT ||
13949              element == EL_BALLOON_SWITCH_UP    ||
13950              element == EL_BALLOON_SWITCH_DOWN  ||
13951              element == EL_BALLOON_SWITCH_NONE  ||
13952              element == EL_BALLOON_SWITCH_ANY)
13953     {
13954       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13955                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13956                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13957                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13958                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13959                              move_direction);
13960     }
13961     else if (element == EL_LAMP)
13962     {
13963       Feld[x][y] = EL_LAMP_ACTIVE;
13964       local_player->lights_still_needed--;
13965
13966       ResetGfxAnimation(x, y);
13967       DrawLevelField(x, y);
13968     }
13969     else if (element == EL_TIME_ORB_FULL)
13970     {
13971       Feld[x][y] = EL_TIME_ORB_EMPTY;
13972
13973       if (level.time > 0 || level.use_time_orb_bug)
13974       {
13975         TimeLeft += level.time_orb_time;
13976
13977 #if 1
13978         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13979
13980         DisplayGameControlValues();
13981 #else
13982         DrawGameValue_Time(TimeLeft);
13983 #endif
13984       }
13985
13986       ResetGfxAnimation(x, y);
13987       DrawLevelField(x, y);
13988     }
13989     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13990              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13991     {
13992       int xx, yy;
13993
13994       game.ball_state = !game.ball_state;
13995
13996       SCAN_PLAYFIELD(xx, yy)
13997       {
13998         int e = Feld[xx][yy];
13999
14000         if (game.ball_state)
14001         {
14002           if (e == EL_EMC_MAGIC_BALL)
14003             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14004           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14005             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14006         }
14007         else
14008         {
14009           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14010             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14011           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14012             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14013         }
14014       }
14015     }
14016
14017     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14018                                         player->index_bit, dig_side);
14019
14020     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14021                                         player->index_bit, dig_side);
14022
14023     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14024                                         player->index_bit, dig_side);
14025
14026     return MP_ACTION;
14027   }
14028   else
14029   {
14030     if (!PLAYER_SWITCHING(player, x, y))
14031     {
14032       player->is_switching = TRUE;
14033       player->switch_x = x;
14034       player->switch_y = y;
14035
14036       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14037                                  player->index_bit, dig_side);
14038       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14039                                           player->index_bit, dig_side);
14040
14041       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14042                                  player->index_bit, dig_side);
14043       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14044                                           player->index_bit, dig_side);
14045     }
14046
14047     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14048                                player->index_bit, dig_side);
14049     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14050                                         player->index_bit, dig_side);
14051
14052     return MP_NO_ACTION;
14053   }
14054
14055   player->push_delay = -1;
14056
14057   if (is_player)                /* function can also be called by EL_PENGUIN */
14058   {
14059     if (Feld[x][y] != element)          /* really digged/collected something */
14060     {
14061       player->is_collecting = !player->is_digging;
14062       player->is_active = TRUE;
14063     }
14064   }
14065
14066   return MP_MOVING;
14067 }
14068
14069 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14070 {
14071   int jx = player->jx, jy = player->jy;
14072   int x = jx + dx, y = jy + dy;
14073   int snap_direction = (dx == -1 ? MV_LEFT  :
14074                         dx == +1 ? MV_RIGHT :
14075                         dy == -1 ? MV_UP    :
14076                         dy == +1 ? MV_DOWN  : MV_NONE);
14077   boolean can_continue_snapping = (level.continuous_snapping &&
14078                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14079
14080   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14081     return FALSE;
14082
14083   if (!player->active || !IN_LEV_FIELD(x, y))
14084     return FALSE;
14085
14086   if (dx && dy)
14087     return FALSE;
14088
14089   if (!dx && !dy)
14090   {
14091     if (player->MovPos == 0)
14092       player->is_pushing = FALSE;
14093
14094     player->is_snapping = FALSE;
14095
14096     if (player->MovPos == 0)
14097     {
14098       player->is_moving = FALSE;
14099       player->is_digging = FALSE;
14100       player->is_collecting = FALSE;
14101     }
14102
14103     return FALSE;
14104   }
14105
14106 #if USE_NEW_CONTINUOUS_SNAPPING
14107   /* prevent snapping with already pressed snap key when not allowed */
14108   if (player->is_snapping && !can_continue_snapping)
14109     return FALSE;
14110 #else
14111   if (player->is_snapping)
14112     return FALSE;
14113 #endif
14114
14115   player->MovDir = snap_direction;
14116
14117   if (player->MovPos == 0)
14118   {
14119     player->is_moving = FALSE;
14120     player->is_digging = FALSE;
14121     player->is_collecting = FALSE;
14122   }
14123
14124   player->is_dropping = FALSE;
14125   player->is_dropping_pressed = FALSE;
14126   player->drop_pressed_delay = 0;
14127
14128   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14129     return FALSE;
14130
14131   player->is_snapping = TRUE;
14132   player->is_active = TRUE;
14133
14134   if (player->MovPos == 0)
14135   {
14136     player->is_moving = FALSE;
14137     player->is_digging = FALSE;
14138     player->is_collecting = FALSE;
14139   }
14140
14141   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14142     DrawLevelField(player->last_jx, player->last_jy);
14143
14144   DrawLevelField(x, y);
14145
14146   return TRUE;
14147 }
14148
14149 boolean DropElement(struct PlayerInfo *player)
14150 {
14151   int old_element, new_element;
14152   int dropx = player->jx, dropy = player->jy;
14153   int drop_direction = player->MovDir;
14154   int drop_side = drop_direction;
14155 #if 1
14156   int drop_element = get_next_drop_element(player);
14157 #else
14158   int drop_element = (player->inventory_size > 0 ?
14159                       player->inventory_element[player->inventory_size - 1] :
14160                       player->inventory_infinite_element != EL_UNDEFINED ?
14161                       player->inventory_infinite_element :
14162                       player->dynabombs_left > 0 ?
14163                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14164                       EL_UNDEFINED);
14165 #endif
14166
14167   player->is_dropping_pressed = TRUE;
14168
14169   /* do not drop an element on top of another element; when holding drop key
14170      pressed without moving, dropped element must move away before the next
14171      element can be dropped (this is especially important if the next element
14172      is dynamite, which can be placed on background for historical reasons) */
14173   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14174     return MP_ACTION;
14175
14176   if (IS_THROWABLE(drop_element))
14177   {
14178     dropx += GET_DX_FROM_DIR(drop_direction);
14179     dropy += GET_DY_FROM_DIR(drop_direction);
14180
14181     if (!IN_LEV_FIELD(dropx, dropy))
14182       return FALSE;
14183   }
14184
14185   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14186   new_element = drop_element;           /* default: no change when dropping */
14187
14188   /* check if player is active, not moving and ready to drop */
14189   if (!player->active || player->MovPos || player->drop_delay > 0)
14190     return FALSE;
14191
14192   /* check if player has anything that can be dropped */
14193   if (new_element == EL_UNDEFINED)
14194     return FALSE;
14195
14196   /* check if drop key was pressed long enough for EM style dynamite */
14197   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14198     return FALSE;
14199
14200   /* check if anything can be dropped at the current position */
14201   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14202     return FALSE;
14203
14204   /* collected custom elements can only be dropped on empty fields */
14205   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14206     return FALSE;
14207
14208   if (old_element != EL_EMPTY)
14209     Back[dropx][dropy] = old_element;   /* store old element on this field */
14210
14211   ResetGfxAnimation(dropx, dropy);
14212   ResetRandomAnimationValue(dropx, dropy);
14213
14214   if (player->inventory_size > 0 ||
14215       player->inventory_infinite_element != EL_UNDEFINED)
14216   {
14217     if (player->inventory_size > 0)
14218     {
14219       player->inventory_size--;
14220
14221       DrawGameDoorValues();
14222
14223       if (new_element == EL_DYNAMITE)
14224         new_element = EL_DYNAMITE_ACTIVE;
14225       else if (new_element == EL_EM_DYNAMITE)
14226         new_element = EL_EM_DYNAMITE_ACTIVE;
14227       else if (new_element == EL_SP_DISK_RED)
14228         new_element = EL_SP_DISK_RED_ACTIVE;
14229     }
14230
14231     Feld[dropx][dropy] = new_element;
14232
14233     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14234       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14235                           el2img(Feld[dropx][dropy]), 0);
14236
14237     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14238
14239     /* needed if previous element just changed to "empty" in the last frame */
14240     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14241
14242     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14243                                player->index_bit, drop_side);
14244     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14245                                         CE_PLAYER_DROPS_X,
14246                                         player->index_bit, drop_side);
14247
14248     TestIfElementTouchesCustomElement(dropx, dropy);
14249   }
14250   else          /* player is dropping a dyna bomb */
14251   {
14252     player->dynabombs_left--;
14253
14254     Feld[dropx][dropy] = new_element;
14255
14256     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14257       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14258                           el2img(Feld[dropx][dropy]), 0);
14259
14260     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14261   }
14262
14263   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14264     InitField_WithBug1(dropx, dropy, FALSE);
14265
14266   new_element = Feld[dropx][dropy];     /* element might have changed */
14267
14268   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14269       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14270   {
14271     int move_direction, nextx, nexty;
14272
14273     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14274       MovDir[dropx][dropy] = drop_direction;
14275
14276     move_direction = MovDir[dropx][dropy];
14277     nextx = dropx + GET_DX_FROM_DIR(move_direction);
14278     nexty = dropy + GET_DY_FROM_DIR(move_direction);
14279
14280     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14281
14282 #if USE_FIX_IMPACT_COLLISION
14283     /* do not cause impact style collision by dropping elements that can fall */
14284     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14285 #else
14286     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14287 #endif
14288   }
14289
14290   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14291   player->is_dropping = TRUE;
14292
14293   player->drop_pressed_delay = 0;
14294   player->is_dropping_pressed = FALSE;
14295
14296   player->drop_x = dropx;
14297   player->drop_y = dropy;
14298
14299   return TRUE;
14300 }
14301
14302 /* ------------------------------------------------------------------------- */
14303 /* game sound playing functions                                              */
14304 /* ------------------------------------------------------------------------- */
14305
14306 static int *loop_sound_frame = NULL;
14307 static int *loop_sound_volume = NULL;
14308
14309 void InitPlayLevelSound()
14310 {
14311   int num_sounds = getSoundListSize();
14312
14313   checked_free(loop_sound_frame);
14314   checked_free(loop_sound_volume);
14315
14316   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14317   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14318 }
14319
14320 static void PlayLevelSound(int x, int y, int nr)
14321 {
14322   int sx = SCREENX(x), sy = SCREENY(y);
14323   int volume, stereo_position;
14324   int max_distance = 8;
14325   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14326
14327   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14328       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14329     return;
14330
14331   if (!IN_LEV_FIELD(x, y) ||
14332       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14333       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14334     return;
14335
14336   volume = SOUND_MAX_VOLUME;
14337
14338   if (!IN_SCR_FIELD(sx, sy))
14339   {
14340     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14341     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14342
14343     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14344   }
14345
14346   stereo_position = (SOUND_MAX_LEFT +
14347                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14348                      (SCR_FIELDX + 2 * max_distance));
14349
14350   if (IS_LOOP_SOUND(nr))
14351   {
14352     /* This assures that quieter loop sounds do not overwrite louder ones,
14353        while restarting sound volume comparison with each new game frame. */
14354
14355     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14356       return;
14357
14358     loop_sound_volume[nr] = volume;
14359     loop_sound_frame[nr] = FrameCounter;
14360   }
14361
14362   PlaySoundExt(nr, volume, stereo_position, type);
14363 }
14364
14365 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14366 {
14367   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14368                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14369                  y < LEVELY(BY1) ? LEVELY(BY1) :
14370                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14371                  sound_action);
14372 }
14373
14374 static void PlayLevelSoundAction(int x, int y, int action)
14375 {
14376   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14377 }
14378
14379 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14380 {
14381   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14382
14383   if (sound_effect != SND_UNDEFINED)
14384     PlayLevelSound(x, y, sound_effect);
14385 }
14386
14387 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14388                                               int action)
14389 {
14390   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14391
14392   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14393     PlayLevelSound(x, y, sound_effect);
14394 }
14395
14396 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14397 {
14398   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14399
14400   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14401     PlayLevelSound(x, y, sound_effect);
14402 }
14403
14404 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14405 {
14406   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14407
14408   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14409     StopSound(sound_effect);
14410 }
14411
14412 static void PlayLevelMusic()
14413 {
14414   if (levelset.music[level_nr] != MUS_UNDEFINED)
14415     PlayMusic(levelset.music[level_nr]);        /* from config file */
14416   else
14417     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14418 }
14419
14420 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14421 {
14422   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14423   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14424   int x = xx - 1 - offset;
14425   int y = yy - 1 - offset;
14426
14427   switch (sample)
14428   {
14429     case SAMPLE_blank:
14430       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14431       break;
14432
14433     case SAMPLE_roll:
14434       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14435       break;
14436
14437     case SAMPLE_stone:
14438       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14439       break;
14440
14441     case SAMPLE_nut:
14442       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14443       break;
14444
14445     case SAMPLE_crack:
14446       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14447       break;
14448
14449     case SAMPLE_bug:
14450       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14451       break;
14452
14453     case SAMPLE_tank:
14454       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14455       break;
14456
14457     case SAMPLE_android_clone:
14458       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14459       break;
14460
14461     case SAMPLE_android_move:
14462       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14463       break;
14464
14465     case SAMPLE_spring:
14466       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14467       break;
14468
14469     case SAMPLE_slurp:
14470       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14471       break;
14472
14473     case SAMPLE_eater:
14474       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14475       break;
14476
14477     case SAMPLE_eater_eat:
14478       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14479       break;
14480
14481     case SAMPLE_alien:
14482       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14483       break;
14484
14485     case SAMPLE_collect:
14486       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14487       break;
14488
14489     case SAMPLE_diamond:
14490       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14491       break;
14492
14493     case SAMPLE_squash:
14494       /* !!! CHECK THIS !!! */
14495 #if 1
14496       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14497 #else
14498       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14499 #endif
14500       break;
14501
14502     case SAMPLE_wonderfall:
14503       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14504       break;
14505
14506     case SAMPLE_drip:
14507       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14508       break;
14509
14510     case SAMPLE_push:
14511       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14512       break;
14513
14514     case SAMPLE_dirt:
14515       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14516       break;
14517
14518     case SAMPLE_acid:
14519       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14520       break;
14521
14522     case SAMPLE_ball:
14523       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14524       break;
14525
14526     case SAMPLE_grow:
14527       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14528       break;
14529
14530     case SAMPLE_wonder:
14531       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14532       break;
14533
14534     case SAMPLE_door:
14535       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14536       break;
14537
14538     case SAMPLE_exit_open:
14539       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14540       break;
14541
14542     case SAMPLE_exit_leave:
14543       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14544       break;
14545
14546     case SAMPLE_dynamite:
14547       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14548       break;
14549
14550     case SAMPLE_tick:
14551       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14552       break;
14553
14554     case SAMPLE_press:
14555       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14556       break;
14557
14558     case SAMPLE_wheel:
14559       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14560       break;
14561
14562     case SAMPLE_boom:
14563       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14564       break;
14565
14566     case SAMPLE_die:
14567       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14568       break;
14569
14570     case SAMPLE_time:
14571       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14572       break;
14573
14574     default:
14575       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14576       break;
14577   }
14578 }
14579
14580 #if 0
14581 void ChangeTime(int value)
14582 {
14583   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
14584
14585   *time += value;
14586
14587   /* EMC game engine uses value from time counter of RND game engine */
14588   level.native_em_level->lev->time = *time;
14589
14590   DrawGameValue_Time(*time);
14591 }
14592
14593 void RaiseScore(int value)
14594 {
14595   /* EMC game engine and RND game engine have separate score counters */
14596   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
14597                 &level.native_em_level->lev->score : &local_player->score);
14598
14599   *score += value;
14600
14601   DrawGameValue_Score(*score);
14602 }
14603 #endif
14604
14605 void RaiseScore(int value)
14606 {
14607   local_player->score += value;
14608
14609 #if 1
14610   game_control_value[GAME_CONTROL_SCORE] = local_player->score;
14611
14612   DisplayGameControlValues();
14613 #else
14614   DrawGameValue_Score(local_player->score);
14615 #endif
14616 }
14617
14618 void RaiseScoreElement(int element)
14619 {
14620   switch (element)
14621   {
14622     case EL_EMERALD:
14623     case EL_BD_DIAMOND:
14624     case EL_EMERALD_YELLOW:
14625     case EL_EMERALD_RED:
14626     case EL_EMERALD_PURPLE:
14627     case EL_SP_INFOTRON:
14628       RaiseScore(level.score[SC_EMERALD]);
14629       break;
14630     case EL_DIAMOND:
14631       RaiseScore(level.score[SC_DIAMOND]);
14632       break;
14633     case EL_CRYSTAL:
14634       RaiseScore(level.score[SC_CRYSTAL]);
14635       break;
14636     case EL_PEARL:
14637       RaiseScore(level.score[SC_PEARL]);
14638       break;
14639     case EL_BUG:
14640     case EL_BD_BUTTERFLY:
14641     case EL_SP_ELECTRON:
14642       RaiseScore(level.score[SC_BUG]);
14643       break;
14644     case EL_SPACESHIP:
14645     case EL_BD_FIREFLY:
14646     case EL_SP_SNIKSNAK:
14647       RaiseScore(level.score[SC_SPACESHIP]);
14648       break;
14649     case EL_YAMYAM:
14650     case EL_DARK_YAMYAM:
14651       RaiseScore(level.score[SC_YAMYAM]);
14652       break;
14653     case EL_ROBOT:
14654       RaiseScore(level.score[SC_ROBOT]);
14655       break;
14656     case EL_PACMAN:
14657       RaiseScore(level.score[SC_PACMAN]);
14658       break;
14659     case EL_NUT:
14660       RaiseScore(level.score[SC_NUT]);
14661       break;
14662     case EL_DYNAMITE:
14663     case EL_EM_DYNAMITE:
14664     case EL_SP_DISK_RED:
14665     case EL_DYNABOMB_INCREASE_NUMBER:
14666     case EL_DYNABOMB_INCREASE_SIZE:
14667     case EL_DYNABOMB_INCREASE_POWER:
14668       RaiseScore(level.score[SC_DYNAMITE]);
14669       break;
14670     case EL_SHIELD_NORMAL:
14671     case EL_SHIELD_DEADLY:
14672       RaiseScore(level.score[SC_SHIELD]);
14673       break;
14674     case EL_EXTRA_TIME:
14675       RaiseScore(level.extra_time_score);
14676       break;
14677     case EL_KEY_1:
14678     case EL_KEY_2:
14679     case EL_KEY_3:
14680     case EL_KEY_4:
14681     case EL_EM_KEY_1:
14682     case EL_EM_KEY_2:
14683     case EL_EM_KEY_3:
14684     case EL_EM_KEY_4:
14685     case EL_EMC_KEY_5:
14686     case EL_EMC_KEY_6:
14687     case EL_EMC_KEY_7:
14688     case EL_EMC_KEY_8:
14689     case EL_DC_KEY_WHITE:
14690       RaiseScore(level.score[SC_KEY]);
14691       break;
14692     default:
14693       RaiseScore(element_info[element].collect_score);
14694       break;
14695   }
14696 }
14697
14698 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14699 {
14700   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14701   {
14702 #if defined(NETWORK_AVALIABLE)
14703     if (options.network)
14704       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14705     else
14706 #endif
14707     {
14708       if (quick_quit)
14709       {
14710 #if 1
14711
14712 #if 1
14713         FadeSkipNextFadeIn();
14714 #else
14715         fading = fading_none;
14716 #endif
14717
14718 #else
14719         OpenDoor(DOOR_CLOSE_1);
14720 #endif
14721
14722         game_status = GAME_MODE_MAIN;
14723
14724 #if 1
14725         DrawAndFadeInMainMenu(REDRAW_FIELD);
14726 #else
14727         DrawMainMenu();
14728 #endif
14729       }
14730       else
14731       {
14732 #if 0
14733         FadeOut(REDRAW_FIELD);
14734 #endif
14735
14736         game_status = GAME_MODE_MAIN;
14737
14738         DrawAndFadeInMainMenu(REDRAW_FIELD);
14739       }
14740     }
14741   }
14742   else          /* continue playing the game */
14743   {
14744     if (tape.playing && tape.deactivate_display)
14745       TapeDeactivateDisplayOff(TRUE);
14746
14747     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14748
14749     if (tape.playing && tape.deactivate_display)
14750       TapeDeactivateDisplayOn();
14751   }
14752 }
14753
14754 void RequestQuitGame(boolean ask_if_really_quit)
14755 {
14756   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14757   boolean skip_request = AllPlayersGone || quick_quit;
14758
14759   RequestQuitGameExt(skip_request, quick_quit,
14760                      "Do you really want to quit the game ?");
14761 }
14762
14763
14764 /* ------------------------------------------------------------------------- */
14765 /* random generator functions                                                */
14766 /* ------------------------------------------------------------------------- */
14767
14768 unsigned int InitEngineRandom_RND(long seed)
14769 {
14770   game.num_random_calls = 0;
14771
14772 #if 0
14773   unsigned int rnd_seed = InitEngineRandom(seed);
14774
14775   printf("::: START RND: %d\n", rnd_seed);
14776
14777   return rnd_seed;
14778 #else
14779
14780   return InitEngineRandom(seed);
14781
14782 #endif
14783
14784 }
14785
14786 unsigned int RND(int max)
14787 {
14788   if (max > 0)
14789   {
14790     game.num_random_calls++;
14791
14792     return GetEngineRandom(max);
14793   }
14794
14795   return 0;
14796 }
14797
14798
14799 /* ------------------------------------------------------------------------- */
14800 /* game engine snapshot handling functions                                   */
14801 /* ------------------------------------------------------------------------- */
14802
14803 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
14804
14805 struct EngineSnapshotInfo
14806 {
14807   /* runtime values for custom element collect score */
14808   int collect_score[NUM_CUSTOM_ELEMENTS];
14809
14810   /* runtime values for group element choice position */
14811   int choice_pos[NUM_GROUP_ELEMENTS];
14812
14813   /* runtime values for belt position animations */
14814   int belt_graphic[4 * NUM_BELT_PARTS];
14815   int belt_anim_mode[4 * NUM_BELT_PARTS];
14816 };
14817
14818 struct EngineSnapshotNodeInfo
14819 {
14820   void *buffer_orig;
14821   void *buffer_copy;
14822   int size;
14823 };
14824
14825 static struct EngineSnapshotInfo engine_snapshot_rnd;
14826 static ListNode *engine_snapshot_list = NULL;
14827 static char *snapshot_level_identifier = NULL;
14828 static int snapshot_level_nr = -1;
14829
14830 void FreeEngineSnapshot()
14831 {
14832   while (engine_snapshot_list != NULL)
14833     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14834                        checked_free);
14835
14836   setString(&snapshot_level_identifier, NULL);
14837   snapshot_level_nr = -1;
14838 }
14839
14840 static void SaveEngineSnapshotValues_RND()
14841 {
14842   static int belt_base_active_element[4] =
14843   {
14844     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14845     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14846     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14847     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14848   };
14849   int i, j;
14850
14851   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14852   {
14853     int element = EL_CUSTOM_START + i;
14854
14855     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14856   }
14857
14858   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14859   {
14860     int element = EL_GROUP_START + i;
14861
14862     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14863   }
14864
14865   for (i = 0; i < 4; i++)
14866   {
14867     for (j = 0; j < NUM_BELT_PARTS; j++)
14868     {
14869       int element = belt_base_active_element[i] + j;
14870       int graphic = el2img(element);
14871       int anim_mode = graphic_info[graphic].anim_mode;
14872
14873       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14874       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14875     }
14876   }
14877 }
14878
14879 static void LoadEngineSnapshotValues_RND()
14880 {
14881   unsigned long num_random_calls = game.num_random_calls;
14882   int i, j;
14883
14884   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14885   {
14886     int element = EL_CUSTOM_START + i;
14887
14888     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14889   }
14890
14891   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14892   {
14893     int element = EL_GROUP_START + i;
14894
14895     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14896   }
14897
14898   for (i = 0; i < 4; i++)
14899   {
14900     for (j = 0; j < NUM_BELT_PARTS; j++)
14901     {
14902       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14903       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14904
14905       graphic_info[graphic].anim_mode = anim_mode;
14906     }
14907   }
14908
14909   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14910   {
14911     InitRND(tape.random_seed);
14912     for (i = 0; i < num_random_calls; i++)
14913       RND(1);
14914   }
14915
14916   if (game.num_random_calls != num_random_calls)
14917   {
14918     Error(ERR_INFO, "number of random calls out of sync");
14919     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14920     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14921     Error(ERR_EXIT, "this should not happen -- please debug");
14922   }
14923 }
14924
14925 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14926 {
14927   struct EngineSnapshotNodeInfo *bi =
14928     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14929
14930   bi->buffer_orig = buffer;
14931   bi->buffer_copy = checked_malloc(size);
14932   bi->size = size;
14933
14934   memcpy(bi->buffer_copy, buffer, size);
14935
14936   addNodeToList(&engine_snapshot_list, NULL, bi);
14937 }
14938
14939 void SaveEngineSnapshot()
14940 {
14941   FreeEngineSnapshot();         /* free previous snapshot, if needed */
14942
14943   if (level_editor_test_game)   /* do not save snapshots from editor */
14944     return;
14945
14946   /* copy some special values to a structure better suited for the snapshot */
14947
14948   SaveEngineSnapshotValues_RND();
14949   SaveEngineSnapshotValues_EM();
14950
14951   /* save values stored in special snapshot structure */
14952
14953   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14954   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14955
14956   /* save further RND engine values */
14957
14958   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14959   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14960   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14961
14962   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14963   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14964   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14965   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14966
14967   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14968   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14969   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14970   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14971   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14972
14973   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14974   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14975   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14976
14977   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14978
14979   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14980
14981   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14982   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14983
14984   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14985   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14986   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14987   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14988   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14989   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14990   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14991   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14992   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14993   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14994   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14995   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14996   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14997   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14998   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14999   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15000   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15001   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15002
15003   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15004   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15005
15006   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15007   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15008   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15009
15010   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15011   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15012
15013   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15014   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15015   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15016   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15017   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15018
15019   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15020   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15021
15022   /* save level identification information */
15023
15024   setString(&snapshot_level_identifier, leveldir_current->identifier);
15025   snapshot_level_nr = level_nr;
15026
15027 #if 0
15028   ListNode *node = engine_snapshot_list;
15029   int num_bytes = 0;
15030
15031   while (node != NULL)
15032   {
15033     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15034
15035     node = node->next;
15036   }
15037
15038   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15039 #endif
15040 }
15041
15042 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15043 {
15044   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15045 }
15046
15047 void LoadEngineSnapshot()
15048 {
15049   ListNode *node = engine_snapshot_list;
15050
15051   if (engine_snapshot_list == NULL)
15052     return;
15053
15054   while (node != NULL)
15055   {
15056     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15057
15058     node = node->next;
15059   }
15060
15061   /* restore special values from snapshot structure */
15062
15063   LoadEngineSnapshotValues_RND();
15064   LoadEngineSnapshotValues_EM();
15065 }
15066
15067 boolean CheckEngineSnapshot()
15068 {
15069   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15070           snapshot_level_nr == level_nr);
15071 }
15072
15073
15074 /* ---------- new game button stuff ---------------------------------------- */
15075
15076 /* graphic position values for game buttons */
15077 #define GAME_BUTTON_XSIZE       30
15078 #define GAME_BUTTON_YSIZE       30
15079 #define GAME_BUTTON_XPOS        5
15080 #define GAME_BUTTON_YPOS        215
15081 #define SOUND_BUTTON_XPOS       5
15082 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15083
15084 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15085 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15086 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15087 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15088 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15089 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15090
15091 static struct
15092 {
15093   int *x, *y;
15094   int gd_x, gd_y;
15095   int gadget_id;
15096   char *infotext;
15097 } gamebutton_info[NUM_GAME_BUTTONS] =
15098 {
15099 #if 1
15100   {
15101     &game.button.stop.x,        &game.button.stop.y,
15102     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15103     GAME_CTRL_ID_STOP,
15104     "stop game"
15105   },
15106   {
15107     &game.button.pause.x,       &game.button.pause.y,
15108     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15109     GAME_CTRL_ID_PAUSE,
15110     "pause game"
15111   },
15112   {
15113     &game.button.play.x,        &game.button.play.y,
15114     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15115     GAME_CTRL_ID_PLAY,
15116     "play game"
15117   },
15118   {
15119     &game.button.sound_music.x, &game.button.sound_music.y,
15120     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15121     SOUND_CTRL_ID_MUSIC,
15122     "background music on/off"
15123   },
15124   {
15125     &game.button.sound_loops.x, &game.button.sound_loops.y,
15126     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15127     SOUND_CTRL_ID_LOOPS,
15128     "sound loops on/off"
15129   },
15130   {
15131     &game.button.sound_simple.x,&game.button.sound_simple.y,
15132     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15133     SOUND_CTRL_ID_SIMPLE,
15134     "normal sounds on/off"
15135   }
15136 #else
15137   {
15138     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15139     GAME_CTRL_ID_STOP,
15140     "stop game"
15141   },
15142   {
15143     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15144     GAME_CTRL_ID_PAUSE,
15145     "pause game"
15146   },
15147   {
15148     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15149     GAME_CTRL_ID_PLAY,
15150     "play game"
15151   },
15152   {
15153     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15154     SOUND_CTRL_ID_MUSIC,
15155     "background music on/off"
15156   },
15157   {
15158     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15159     SOUND_CTRL_ID_LOOPS,
15160     "sound loops on/off"
15161   },
15162   {
15163     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15164     SOUND_CTRL_ID_SIMPLE,
15165     "normal sounds on/off"
15166   }
15167 #endif
15168 };
15169
15170 void CreateGameButtons()
15171 {
15172   int i;
15173
15174   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15175   {
15176     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15177     struct GadgetInfo *gi;
15178     int button_type;
15179     boolean checked;
15180     unsigned long event_mask;
15181     int x, y;
15182     int gd_xoffset, gd_yoffset;
15183     int gd_x1, gd_x2, gd_y1, gd_y2;
15184     int id = i;
15185
15186     x = DX + *gamebutton_info[i].x;
15187     y = DY + *gamebutton_info[i].y;
15188     gd_xoffset = gamebutton_info[i].gd_x;
15189     gd_yoffset = gamebutton_info[i].gd_y;
15190     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15191     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15192
15193     if (id == GAME_CTRL_ID_STOP ||
15194         id == GAME_CTRL_ID_PAUSE ||
15195         id == GAME_CTRL_ID_PLAY)
15196     {
15197       button_type = GD_TYPE_NORMAL_BUTTON;
15198       checked = FALSE;
15199       event_mask = GD_EVENT_RELEASED;
15200       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15201       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15202     }
15203     else
15204     {
15205       button_type = GD_TYPE_CHECK_BUTTON;
15206       checked =
15207         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15208          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15209          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15210       event_mask = GD_EVENT_PRESSED;
15211       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
15212       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15213     }
15214
15215     gi = CreateGadget(GDI_CUSTOM_ID, id,
15216                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15217 #if 1
15218                       GDI_X, x,
15219                       GDI_Y, y,
15220 #else
15221                       GDI_X, DX + gd_xoffset,
15222                       GDI_Y, DY + gd_yoffset,
15223 #endif
15224                       GDI_WIDTH, GAME_BUTTON_XSIZE,
15225                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
15226                       GDI_TYPE, button_type,
15227                       GDI_STATE, GD_BUTTON_UNPRESSED,
15228                       GDI_CHECKED, checked,
15229                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15230                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15231                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15232                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15233                       GDI_EVENT_MASK, event_mask,
15234                       GDI_CALLBACK_ACTION, HandleGameButtons,
15235                       GDI_END);
15236
15237     if (gi == NULL)
15238       Error(ERR_EXIT, "cannot create gadget");
15239
15240     game_gadget[id] = gi;
15241   }
15242 }
15243
15244 void FreeGameButtons()
15245 {
15246   int i;
15247
15248   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15249     FreeGadget(game_gadget[i]);
15250 }
15251
15252 static void MapGameButtons()
15253 {
15254   int i;
15255
15256   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15257     MapGadget(game_gadget[i]);
15258 }
15259
15260 void UnmapGameButtons()
15261 {
15262   int i;
15263
15264   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15265     UnmapGadget(game_gadget[i]);
15266 }
15267
15268 static void HandleGameButtons(struct GadgetInfo *gi)
15269 {
15270   int id = gi->custom_id;
15271
15272   if (game_status != GAME_MODE_PLAYING)
15273     return;
15274
15275   switch (id)
15276   {
15277     case GAME_CTRL_ID_STOP:
15278       if (tape.playing)
15279         TapeStop();
15280       else
15281         RequestQuitGame(TRUE);
15282       break;
15283
15284     case GAME_CTRL_ID_PAUSE:
15285       if (options.network)
15286       {
15287 #if defined(NETWORK_AVALIABLE)
15288         if (tape.pausing)
15289           SendToServer_ContinuePlaying();
15290         else
15291           SendToServer_PausePlaying();
15292 #endif
15293       }
15294       else
15295         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15296       break;
15297
15298     case GAME_CTRL_ID_PLAY:
15299       if (tape.pausing)
15300       {
15301 #if defined(NETWORK_AVALIABLE)
15302         if (options.network)
15303           SendToServer_ContinuePlaying();
15304         else
15305 #endif
15306         {
15307           tape.pausing = FALSE;
15308           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15309         }
15310       }
15311       break;
15312
15313     case SOUND_CTRL_ID_MUSIC:
15314       if (setup.sound_music)
15315       { 
15316         setup.sound_music = FALSE;
15317         FadeMusic();
15318       }
15319       else if (audio.music_available)
15320       { 
15321         setup.sound = setup.sound_music = TRUE;
15322
15323         SetAudioMode(setup.sound);
15324
15325         PlayLevelMusic();
15326       }
15327       break;
15328
15329     case SOUND_CTRL_ID_LOOPS:
15330       if (setup.sound_loops)
15331         setup.sound_loops = FALSE;
15332       else if (audio.loops_available)
15333       {
15334         setup.sound = setup.sound_loops = TRUE;
15335         SetAudioMode(setup.sound);
15336       }
15337       break;
15338
15339     case SOUND_CTRL_ID_SIMPLE:
15340       if (setup.sound_simple)
15341         setup.sound_simple = FALSE;
15342       else if (audio.sound_available)
15343       {
15344         setup.sound = setup.sound_simple = TRUE;
15345         SetAudioMode(setup.sound);
15346       }
15347       break;
15348
15349     default:
15350       break;
15351   }
15352 }