rnd-20070321-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61
62 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
63
64
65 /* for DigField() */
66 #define DF_NO_PUSH              0
67 #define DF_DIG                  1
68 #define DF_SNAP                 2
69
70 /* for MovePlayer() */
71 #define MP_NO_ACTION            0
72 #define MP_MOVING               1
73 #define MP_ACTION               2
74 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
75
76 /* for ScrollPlayer() */
77 #define SCROLL_INIT             0
78 #define SCROLL_GO_ON            1
79
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START          0
82 #define EX_TYPE_NONE            0
83 #define EX_TYPE_NORMAL          (1 << 0)
84 #define EX_TYPE_CENTER          (1 << 1)
85 #define EX_TYPE_BORDER          (1 << 2)
86 #define EX_TYPE_CROSS           (1 << 3)
87 #define EX_TYPE_DYNA            (1 << 4)
88 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
89
90 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
91 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
92 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
93 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
94
95 /* special positions in the game control window (relative to control window) */
96 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
97 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
98 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
99 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
100 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
101 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
102 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
103 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
104 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
105 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
106 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
107 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
108 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
109 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
110 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
111 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
112
113 /* special positions in the game control window (relative to main window) */
114 #define DX_LEVEL1               (DX + XX_LEVEL1)
115 #define DX_LEVEL2               (DX + XX_LEVEL2)
116 #define DX_LEVEL                (DX + XX_LEVEL)
117 #define DY_LEVEL                (DY + YY_LEVEL)
118 #define DX_EMERALDS             (DX + XX_EMERALDS)
119 #define DY_EMERALDS             (DY + YY_EMERALDS)
120 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
121 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
122 #define DX_KEYS                 (DX + XX_KEYS)
123 #define DY_KEYS                 (DY + YY_KEYS)
124 #define DX_SCORE                (DX + XX_SCORE)
125 #define DY_SCORE                (DY + YY_SCORE)
126 #define DX_TIME1                (DX + XX_TIME1)
127 #define DX_TIME2                (DX + XX_TIME2)
128 #define DX_TIME                 (DX + XX_TIME)
129 #define DY_TIME                 (DY + YY_TIME)
130
131 #if 0
132 /* game panel display and control definitions */
133
134 #define GAME_CONTROL_LEVEL                      0
135 #define GAME_CONTROL_GEMS                       1
136 #define GAME_CONTROL_INVENTORY                  2
137 #define GAME_CONTROL_KEY_1                      3
138 #define GAME_CONTROL_KEY_2                      4
139 #define GAME_CONTROL_KEY_3                      5
140 #define GAME_CONTROL_KEY_4                      6
141 #define GAME_CONTROL_KEY_5                      7
142 #define GAME_CONTROL_KEY_6                      8
143 #define GAME_CONTROL_KEY_7                      9
144 #define GAME_CONTROL_KEY_8                      10
145 #define GAME_CONTROL_KEY_WHITE                  11
146 #define GAME_CONTROL_KEY_WHITE_COUNT            12
147 #define GAME_CONTROL_SCORE                      13
148 #define GAME_CONTROL_TIME                       14
149 #define GAME_CONTROL_TIME_HH                    15
150 #define GAME_CONTROL_TIME_MM                    16
151 #define GAME_CONTROL_TIME_SS                    17
152 #define GAME_CONTROL_DROP_NEXT_1                18
153 #define GAME_CONTROL_DROP_NEXT_2                19
154 #define GAME_CONTROL_DROP_NEXT_3                20
155 #define GAME_CONTROL_DROP_NEXT_4                21
156 #define GAME_CONTROL_DROP_NEXT_5                22
157 #define GAME_CONTROL_DROP_NEXT_6                23
158 #define GAME_CONTROL_DROP_NEXT_7                24
159 #define GAME_CONTROL_DROP_NEXT_8                25
160 #define GAME_CONTROL_SHIELD_NORMAL              26
161 #define GAME_CONTROL_SHIELD_NORMAL_TIME         27
162 #define GAME_CONTROL_SHIELD_DEADLY              28
163 #define GAME_CONTROL_SHIELD_DEADLY_TIME         29
164 #define GAME_CONTROL_EXIT                       30
165 #define GAME_CONTROL_EM_EXIT                    31
166 #define GAME_CONTROL_SP_EXIT                    32
167 #define GAME_CONTROL_STEEL_EXIT                 33
168 #define GAME_CONTROL_EM_STEEL_EXIT              34
169 #define GAME_CONTROL_EMC_MAGIC_BALL             35
170 #define GAME_CONTROL_EMC_MAGIC_BALL_TIME        36
171 #define GAME_CONTROL_LIGHT_SWITCH               37
172 #define GAME_CONTROL_LIGHT_SWITCH_TIME          38
173 #define GAME_CONTROL_TIMEGATE_SWITCH            39
174 #define GAME_CONTROL_TIMEGATE_SWITCH_TIME       40
175 #define GAME_CONTROL_SWITCHGATE_SWITCH          41
176 #define GAME_CONTROL_EMC_LENSES                 42
177 #define GAME_CONTROL_EMC_LENSES_TIME            43
178 #define GAME_CONTROL_EMC_MAGNIFIER              44
179 #define GAME_CONTROL_EMC_MAGNIFIER_TIME         45
180 #define GAME_CONTROL_BALLOON_SWITCH             46
181 #define GAME_CONTROL_DYNABOMB_NUMBER            47
182 #define GAME_CONTROL_DYNABOMB_SIZE              48
183 #define GAME_CONTROL_DYNABOMB_POWER             49
184 #define GAME_CONTROL_PENGUINS                   50
185 #define GAME_CONTROL_SOKOBAN_OBJECTS            51
186 #define GAME_CONTROL_SOKOBAN_FIELDS             52
187 #define GAME_CONTROL_ROBOT_WHEEL                53
188 #define GAME_CONTROL_CONVEYOR_BELT_1            54
189 #define GAME_CONTROL_CONVEYOR_BELT_1_SWITCH     55
190 #define GAME_CONTROL_CONVEYOR_BELT_2            56
191 #define GAME_CONTROL_CONVEYOR_BELT_2_SWITCH     57
192 #define GAME_CONTROL_CONVEYOR_BELT_3            58
193 #define GAME_CONTROL_CONVEYOR_BELT_3_SWITCH     59
194 #define GAME_CONTROL_CONVEYOR_BELT_4            60
195 #define GAME_CONTROL_CONVEYOR_BELT_4_SWITCH     61
196 #define GAME_CONTROL_MAGIC_WALL                 62
197 #define GAME_CONTROL_MAGIC_WALL_TIME            63
198 #define GAME_CONTROL_BD_MAGIC_WALL              64
199 #define GAME_CONTROL_DC_MAGIC_WALL              65
200 #define GAME_CONTROL_PLAYER_NAME                66
201 #define GAME_CONTROL_LEVEL_NAME                 67
202 #define GAME_CONTROL_LEVEL_AUTHOR               68
203
204 struct GameControlInfo
205 {
206   int nr;
207
208   struct TextPosInfo *pos_text;
209   int type;
210   void *ptr;
211 };
212
213 static struct GameControlInfo game_controls[] =
214 {
215   {
216     GAME_CONTROL_LEVEL,
217     &game.panel.level,
218     TYPE_INTEGER,
219   },
220   {
221     GAME_CONTROL_GEMS,
222     &game.panel.gems,
223     TYPE_INTEGER,
224   },
225   {
226     GAME_CONTROL_INVENTORY,
227     &game.panel.inventory,
228     TYPE_INTEGER,
229   },
230   {
231     GAME_CONTROL_KEYS,
232     &game.panel.keys,
233     TYPE_INTEGER,
234   },
235   {
236     GAME_CONTROL_SCORE,
237     &game.panel.score,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_CONTROL_TIME,
242     &game.panel.time,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_CONTROL_TIME_HH,
247     &game.panel.time_hh,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_CONTROL_TIME_MM,
252     &game.panel.time_mm,
253     TYPE_INTEGER,
254   },
255   {
256     GAME_CONTROL_TIME_SS,
257     &game.panel.time_ss,
258     TYPE_INTEGER,
259   },
260   {
261     GAME_CONTROL_DROP_NEXT_1,
262     &game.panel.drop_next_1,
263     TYPE_INTEGER,
264   },
265   {
266     GAME_CONTROL_DROP_NEXT_2,
267     &game.panel.drop_next_2,
268     TYPE_INTEGER,
269   },
270   {
271     GAME_CONTROL_DROP_NEXT_3,
272     &game.panel.drop_next_3,
273     TYPE_INTEGER,
274   },
275   {
276     GAME_CONTROL_DROP_NEXT_4,
277     &game.panel.drop_next_4,
278     TYPE_INTEGER,
279   },
280   {
281     GAME_CONTROL_DROP_NEXT_5,
282     &game.panel.drop_next_5,
283     TYPE_INTEGER,
284   },
285   {
286     GAME_CONTROL_DROP_NEXT_6,
287     &game.panel.drop_next_6,
288     TYPE_INTEGER,
289   },
290   {
291     GAME_CONTROL_DROP_NEXT_7,
292     &game.panel.drop_next_7,
293     TYPE_INTEGER,
294   },
295   {
296     GAME_CONTROL_DROP_NEXT_8,
297     &game.panel.drop_next_8,
298     TYPE_INTEGER,
299   },
300   {
301     GAME_CONTROL_EMC_KEYS,
302     &game.panel.emc_keys,
303     TYPE_INTEGER,
304   },
305   {
306     GAME_CONTROL_KEY_1,
307     &game.panel.key_1,
308     TYPE_INTEGER,
309   },
310   {
311     GAME_CONTROL_KEY_2,
312     &game.panel.key_2,
313     TYPE_INTEGER,
314   },
315   {
316     GAME_CONTROL_KEY_3,
317     &game.panel.key_3,
318     TYPE_INTEGER,
319   },
320   {
321     GAME_CONTROL_KEY_4,
322     &game.panel.key_4,
323     TYPE_INTEGER,
324   },
325   {
326     GAME_CONTROL_KEY_5,
327     &game.panel.key_5,
328     TYPE_INTEGER,
329   },
330   {
331     GAME_CONTROL_KEY_6,
332     &game.panel.key_6,
333     TYPE_INTEGER,
334   },
335   {
336     GAME_CONTROL_KEY_7,
337     &game.panel.key_7,
338     TYPE_INTEGER,
339   },
340   {
341     GAME_CONTROL_KEY_8,
342     &game.panel.key_8,
343     TYPE_INTEGER,
344   },
345   {
346     GAME_CONTROL_KEY_WHITE,
347     &game.panel.key_white,
348     TYPE_INTEGER,
349   },
350   {
351     GAME_CONTROL_KEY_WHITE_COUNT,
352     &game.panel.key_white_count,
353     TYPE_INTEGER,
354   },
355   {
356     GAME_CONTROL_SHIELD_NORMAL,
357     &game.panel.shield_normal,
358     TYPE_INTEGER,
359   },
360   {
361     GAME_CONTROL_SHIELD_NORMAL_TIME,
362     &game.panel.shield_normal_time,
363     TYPE_INTEGER,
364   },
365   {
366     GAME_CONTROL_SHIELD_DEADLY,
367     &game.panel.shield_deadly,
368     TYPE_INTEGER,
369   },
370   {
371     GAME_CONTROL_SHIELD_DEADLY_TIME,
372     &game.panel.shield_deadly_time,
373     TYPE_INTEGER,
374   },
375   {
376     GAME_CONTROL_EXIT,
377     &game.panel.exit,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_CONTROL_EM_EXIT,
382     &game.panel.em_exit,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_CONTROL_SP_EXIT,
387     &game.panel.sp_exit,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_CONTROL_STEEL_EXIT,
392     &game.panel.steel_exit,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_CONTROL_EM_STEEL_EXIT,
397     &game.panel.em_steel_exit,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_CONTROL_EMC_MAGIC_BALL,
402     &game.panel.emc_magic_ball,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_CONTROL_EMC_MAGIC_BALL_TIME,
407     &game.panel.emc_magic_ball_time,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_CONTROL_LIGHT_SWITCH,
412     &game.panel.light_switch,
413     TYPE_INTEGER,
414   },
415   {
416     GAME_CONTROL_LIGHT_SWITCH_TIME,
417     &game.panel.light_switch_time,
418     TYPE_INTEGER,
419   },
420   {
421     GAME_CONTROL_TIMEGATE_SWITCH,
422     &game.panel.timegate_switch,
423     TYPE_INTEGER,
424   },
425   {
426     GAME_CONTROL_TIMEGATE_SWITCH_TIME,
427     &game.panel.timegate_switch_time,
428     TYPE_INTEGER,
429   },
430   {
431     GAME_CONTROL_SWITCHGATE_SWITCH,
432     &game.panel.switchgate_switch,
433     TYPE_INTEGER,
434   },
435   {
436     GAME_CONTROL_EMC_LENSES,
437     &game.panel.emc_lenses,
438     TYPE_INTEGER,
439   },
440   {
441     GAME_CONTROL_EMC_LENSES_TIME,
442     &game.panel.emc_lenses_time,
443     TYPE_INTEGER,
444   },
445   {
446     GAME_CONTROL_EMC_MAGNIFIER,
447     &game.panel.emc_magnifier,
448     TYPE_INTEGER,
449   },
450   {
451     GAME_CONTROL_EMC_MAGNIFIER_TIME,
452     &game.panel.emc_magnifier_time,
453     TYPE_INTEGER,
454   },
455   {
456     GAME_CONTROL_BALLOON_SWITCH,
457     &game.panel.balloon_switch,
458     TYPE_INTEGER,
459   },
460   {
461     GAME_CONTROL_DYNABOMB_NUMBER,
462     &game.panel.dynabomb_number,
463     TYPE_INTEGER,
464   },
465   {
466     GAME_CONTROL_DYNABOMB_SIZE,
467     &game.panel.dynabomb_size,
468     TYPE_INTEGER,
469   },
470   {
471     GAME_CONTROL_DYNABOMB_POWER,
472     &game.panel.dynabomb_power,
473     TYPE_INTEGER,
474   },
475   {
476     GAME_CONTROL_PENGUINS,
477     &game.panel.penguins,
478     TYPE_INTEGER,
479   },
480   {
481     GAME_CONTROL_SOKOBAN_OBJECTS,
482     &game.panel.sokoban_objects,
483     TYPE_INTEGER,
484   },
485   {
486     GAME_CONTROL_SOKOBAN_FIELDS,
487     &game.panel.sokoban_fields,
488     TYPE_INTEGER,
489   },
490   {
491     GAME_CONTROL_ROBOT_WHEEL,
492     &game.panel.robot_wheel,
493     TYPE_INTEGER,
494   },
495   {
496     GAME_CONTROL_CONVEYOR_BELT_1,
497     &game.panel.conveyor_belt_1,
498     TYPE_INTEGER,
499   },
500   {
501     GAME_CONTROL_CONVEYOR_BELT_1_SWITCH,
502     &game.panel.conveyor_belt_1_switch,
503     TYPE_INTEGER,
504   },
505   {
506     GAME_CONTROL_CONVEYOR_BELT_2,
507     &game.panel.conveyor_belt_2,
508     TYPE_INTEGER,
509   },
510   {
511     GAME_CONTROL_CONVEYOR_BELT_2_SWITCH,
512     &game.panel.conveyor_belt_2_switch,
513     TYPE_INTEGER,
514   },
515   {
516     GAME_CONTROL_CONVEYOR_BELT_3,
517     &game.panel.conveyor_belt_3,
518     TYPE_INTEGER,
519   },
520   {
521     GAME_CONTROL_CONVEYOR_BELT_3_SWITCH,
522     &game.panel.conveyor_belt_3_switch,
523     TYPE_INTEGER,
524   },
525   {
526     GAME_CONTROL_CONVEYOR_BELT_4,
527     &game.panel.conveyor_belt_4,
528     TYPE_INTEGER,
529   },
530   {
531     GAME_CONTROL_CONVEYOR_BELT_4_SWITCH,
532     &game.panel.conveyor_belt_4_switch,
533     TYPE_INTEGER,
534   },
535   {
536     GAME_CONTROL_MAGIC_WALL,
537     &game.panel.magic_wall,
538     TYPE_INTEGER,
539   },
540   {
541     GAME_CONTROL_MAGIC_WALL_TIME,
542     &game.panel.magic_wall_time,
543     TYPE_INTEGER,
544   },
545   {
546     GAME_CONTROL_BD_MAGIC_WALL,
547     &game.panel.bd_magic_wall,
548     TYPE_INTEGER,
549   },
550   {
551     GAME_CONTROL_DC_MAGIC_WALL,
552     &game.panel.dc_magic_wall,
553     TYPE_INTEGER,
554   },
555   {
556     GAME_CONTROL_PLAYER_NAME,
557     &game.panel.player_name,
558     TYPE_INTEGER,
559   },
560   {
561     GAME_CONTROL_LEVEL_NAME,
562     &game.panel.level_name,
563     TYPE_INTEGER,
564   },
565   {
566     GAME_CONTROL_LEVEL_AUTHOR,
567     &game.panel.level_author,
568     TYPE_INTEGER,
569   },
570
571   {
572     -1,
573     NULL,
574     -1,
575     NULL
576   }
577 };
578 #endif
579
580
581 /* values for delayed check of falling and moving elements and for collision */
582 #define CHECK_DELAY_MOVING      3
583 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
584 #define CHECK_DELAY_COLLISION   2
585 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
586
587 /* values for initial player move delay (initial delay counter value) */
588 #define INITIAL_MOVE_DELAY_OFF  -1
589 #define INITIAL_MOVE_DELAY_ON   0
590
591 /* values for player movement speed (which is in fact a delay value) */
592 #define MOVE_DELAY_MIN_SPEED    32
593 #define MOVE_DELAY_NORMAL_SPEED 8
594 #define MOVE_DELAY_HIGH_SPEED   4
595 #define MOVE_DELAY_MAX_SPEED    1
596
597 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
598 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
599
600 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
601 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
602
603 /* values for other actions */
604 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
605 #define MOVE_STEPSIZE_MIN       (1)
606 #define MOVE_STEPSIZE_MAX       (TILEX)
607
608 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
609 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
610
611 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
612
613 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
614                                  RND(element_info[e].push_delay_random))
615 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
616                                  RND(element_info[e].drop_delay_random))
617 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
618                                  RND(element_info[e].move_delay_random))
619 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
620                                     (element_info[e].move_delay_random))
621 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
622                                  RND(element_info[e].ce_value_random_initial))
623 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
624 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
625                                  RND((c)->delay_random * (c)->delay_frames))
626 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
627                                  RND((c)->delay_random))
628
629
630 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
631          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
632
633 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
634         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
635          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
636          (be) + (e) - EL_SELF)
637
638 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
639         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
640          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
641          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
642          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
643          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
644          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
645          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
646          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
647          (e))
648
649 #define CAN_GROW_INTO(e)                                                \
650         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
651
652 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
653                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
654                                         (condition)))
655
656 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
657                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
658                                         (CAN_MOVE_INTO_ACID(e) &&       \
659                                          Feld[x][y] == EL_ACID) ||      \
660                                         (condition)))
661
662 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
663                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
664                                         (CAN_MOVE_INTO_ACID(e) &&       \
665                                          Feld[x][y] == EL_ACID) ||      \
666                                         (condition)))
667
668 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
669                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
670                                         (condition) ||                  \
671                                         (CAN_MOVE_INTO_ACID(e) &&       \
672                                          Feld[x][y] == EL_ACID) ||      \
673                                         (DONT_COLLIDE_WITH(e) &&        \
674                                          IS_PLAYER(x, y) &&             \
675                                          !PLAYER_ENEMY_PROTECTED(x, y))))
676
677 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
678         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
679
680 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
681         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
682
683 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
684         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
685
686 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
687         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
688                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
689
690 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
691         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
692
693 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
694         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
695
696 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
697         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
698
699 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
700         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
701
702 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
703         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
704
705 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
706         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
707                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
708                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
709                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
710                                                  IS_FOOD_PENGUIN(Feld[x][y])))
711 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
712         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
713
714 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
715         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
716
717 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
718         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
719
720 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
721         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
722                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
723
724 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
725
726 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
727                 (!IS_PLAYER(x, y) &&                                    \
728                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
729
730 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
731         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
732
733 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
734 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
735
736 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
737 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
738 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
739 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
740
741 /* game button identifiers */
742 #define GAME_CTRL_ID_STOP               0
743 #define GAME_CTRL_ID_PAUSE              1
744 #define GAME_CTRL_ID_PLAY               2
745 #define SOUND_CTRL_ID_MUSIC             3
746 #define SOUND_CTRL_ID_LOOPS             4
747 #define SOUND_CTRL_ID_SIMPLE            5
748
749 #define NUM_GAME_BUTTONS                6
750
751
752 /* forward declaration for internal use */
753
754 static void CreateField(int, int, int);
755
756 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
757 static void AdvanceFrameAndPlayerCounters(int);
758
759 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
760 static boolean MovePlayer(struct PlayerInfo *, int, int);
761 static void ScrollPlayer(struct PlayerInfo *, int);
762 static void ScrollScreen(struct PlayerInfo *, int);
763
764 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
765
766 static void InitBeltMovement(void);
767 static void CloseAllOpenTimegates(void);
768 static void CheckGravityMovement(struct PlayerInfo *);
769 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
770 static void KillPlayerUnlessEnemyProtected(int, int);
771 static void KillPlayerUnlessExplosionProtected(int, int);
772
773 static void TestIfPlayerTouchesCustomElement(int, int);
774 static void TestIfElementTouchesCustomElement(int, int);
775 static void TestIfElementHitsCustomElement(int, int, int);
776 #if 0
777 static void TestIfElementSmashesCustomElement(int, int, int);
778 #endif
779
780 static void HandleElementChange(int, int, int);
781 static void ExecuteCustomElementAction(int, int, int, int);
782 static boolean ChangeElement(int, int, int, int);
783
784 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
785 #define CheckTriggeredElementChange(x, y, e, ev)                        \
786         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
787 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
788         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
789 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
790         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
791 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
792         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
793
794 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
795 #define CheckElementChange(x, y, e, te, ev)                             \
796         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
797 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
798         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
799 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
800         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
801
802 static void PlayLevelSound(int, int, int);
803 static void PlayLevelSoundNearest(int, int, int);
804 static void PlayLevelSoundAction(int, int, int);
805 static void PlayLevelSoundElementAction(int, int, int, int);
806 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
807 static void PlayLevelSoundActionIfLoop(int, int, int);
808 static void StopLevelSoundActionIfLoop(int, int, int);
809 static void PlayLevelMusic();
810
811 static void MapGameButtons();
812 static void HandleGameButtons(struct GadgetInfo *);
813
814 int AmoebeNachbarNr(int, int);
815 void AmoebeUmwandeln(int, int);
816 void ContinueMoving(int, int);
817 void Bang(int, int);
818 void InitMovDir(int, int);
819 void InitAmoebaNr(int, int);
820 int NewHiScore(void);
821
822 void TestIfGoodThingHitsBadThing(int, int, int);
823 void TestIfBadThingHitsGoodThing(int, int, int);
824 void TestIfPlayerTouchesBadThing(int, int);
825 void TestIfPlayerRunsIntoBadThing(int, int, int);
826 void TestIfBadThingTouchesPlayer(int, int);
827 void TestIfBadThingRunsIntoPlayer(int, int, int);
828 void TestIfFriendTouchesBadThing(int, int);
829 void TestIfBadThingTouchesFriend(int, int);
830 void TestIfBadThingTouchesOtherBadThing(int, int);
831
832 void KillPlayer(struct PlayerInfo *);
833 void BuryPlayer(struct PlayerInfo *);
834 void RemovePlayer(struct PlayerInfo *);
835
836 boolean SnapField(struct PlayerInfo *, int, int);
837 boolean DropElement(struct PlayerInfo *);
838
839 static int getInvisibleActiveFromInvisibleElement(int);
840 static int getInvisibleFromInvisibleActiveElement(int);
841
842 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
843
844 /* for detection of endless loops, caused by custom element programming */
845 /* (using maximal playfield width x 10 is just a rough approximation) */
846 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
847
848 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
849 {                                                                       \
850   if (recursion_loop_detected)                                          \
851     return (rc);                                                        \
852                                                                         \
853   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
854   {                                                                     \
855     recursion_loop_detected = TRUE;                                     \
856     recursion_loop_element = (e);                                       \
857   }                                                                     \
858                                                                         \
859   recursion_loop_depth++;                                               \
860 }
861
862 #define RECURSION_LOOP_DETECTION_END()                                  \
863 {                                                                       \
864   recursion_loop_depth--;                                               \
865 }
866
867 static int recursion_loop_depth;
868 static boolean recursion_loop_detected;
869 static boolean recursion_loop_element;
870
871
872 /* ------------------------------------------------------------------------- */
873 /* definition of elements that automatically change to other elements after  */
874 /* a specified time, eventually calling a function when changing             */
875 /* ------------------------------------------------------------------------- */
876
877 /* forward declaration for changer functions */
878 static void InitBuggyBase(int, int);
879 static void WarnBuggyBase(int, int);
880
881 static void InitTrap(int, int);
882 static void ActivateTrap(int, int);
883 static void ChangeActiveTrap(int, int);
884
885 static void InitRobotWheel(int, int);
886 static void RunRobotWheel(int, int);
887 static void StopRobotWheel(int, int);
888
889 static void InitTimegateWheel(int, int);
890 static void RunTimegateWheel(int, int);
891
892 static void InitMagicBallDelay(int, int);
893 static void ActivateMagicBall(int, int);
894
895 struct ChangingElementInfo
896 {
897   int element;
898   int target_element;
899   int change_delay;
900   void (*pre_change_function)(int x, int y);
901   void (*change_function)(int x, int y);
902   void (*post_change_function)(int x, int y);
903 };
904
905 static struct ChangingElementInfo change_delay_list[] =
906 {
907   {
908     EL_NUT_BREAKING,
909     EL_EMERALD,
910     6,
911     NULL,
912     NULL,
913     NULL
914   },
915   {
916     EL_PEARL_BREAKING,
917     EL_EMPTY,
918     8,
919     NULL,
920     NULL,
921     NULL
922   },
923   {
924     EL_EXIT_OPENING,
925     EL_EXIT_OPEN,
926     29,
927     NULL,
928     NULL,
929     NULL
930   },
931   {
932     EL_EXIT_CLOSING,
933     EL_EXIT_CLOSED,
934     29,
935     NULL,
936     NULL,
937     NULL
938   },
939   {
940     EL_STEEL_EXIT_OPENING,
941     EL_STEEL_EXIT_OPEN,
942     29,
943     NULL,
944     NULL,
945     NULL
946   },
947   {
948     EL_STEEL_EXIT_CLOSING,
949     EL_STEEL_EXIT_CLOSED,
950     29,
951     NULL,
952     NULL,
953     NULL
954   },
955   {
956     EL_EM_EXIT_OPENING,
957     EL_EM_EXIT_OPEN,
958     29,
959     NULL,
960     NULL,
961     NULL
962   },
963   {
964     EL_EM_EXIT_CLOSING,
965 #if 1
966     EL_EMPTY,
967 #else
968     EL_EM_EXIT_CLOSED,
969 #endif
970     29,
971     NULL,
972     NULL,
973     NULL
974   },
975   {
976     EL_EM_STEEL_EXIT_OPENING,
977     EL_EM_STEEL_EXIT_OPEN,
978     29,
979     NULL,
980     NULL,
981     NULL
982   },
983   {
984     EL_EM_STEEL_EXIT_CLOSING,
985 #if 1
986     EL_STEELWALL,
987 #else
988     EL_EM_STEEL_EXIT_CLOSED,
989 #endif
990     29,
991     NULL,
992     NULL,
993     NULL
994   },
995   {
996     EL_SP_EXIT_OPENING,
997     EL_SP_EXIT_OPEN,
998     29,
999     NULL,
1000     NULL,
1001     NULL
1002   },
1003   {
1004     EL_SP_EXIT_CLOSING,
1005     EL_SP_EXIT_CLOSED,
1006     29,
1007     NULL,
1008     NULL,
1009     NULL
1010   },
1011   {
1012     EL_SWITCHGATE_OPENING,
1013     EL_SWITCHGATE_OPEN,
1014     29,
1015     NULL,
1016     NULL,
1017     NULL
1018   },
1019   {
1020     EL_SWITCHGATE_CLOSING,
1021     EL_SWITCHGATE_CLOSED,
1022     29,
1023     NULL,
1024     NULL,
1025     NULL
1026   },
1027   {
1028     EL_TIMEGATE_OPENING,
1029     EL_TIMEGATE_OPEN,
1030     29,
1031     NULL,
1032     NULL,
1033     NULL
1034   },
1035   {
1036     EL_TIMEGATE_CLOSING,
1037     EL_TIMEGATE_CLOSED,
1038     29,
1039     NULL,
1040     NULL,
1041     NULL
1042   },
1043
1044   {
1045     EL_ACID_SPLASH_LEFT,
1046     EL_EMPTY,
1047     8,
1048     NULL,
1049     NULL,
1050     NULL
1051   },
1052   {
1053     EL_ACID_SPLASH_RIGHT,
1054     EL_EMPTY,
1055     8,
1056     NULL,
1057     NULL,
1058     NULL
1059   },
1060   {
1061     EL_SP_BUGGY_BASE,
1062     EL_SP_BUGGY_BASE_ACTIVATING,
1063     0,
1064     InitBuggyBase,
1065     NULL,
1066     NULL
1067   },
1068   {
1069     EL_SP_BUGGY_BASE_ACTIVATING,
1070     EL_SP_BUGGY_BASE_ACTIVE,
1071     0,
1072     InitBuggyBase,
1073     NULL,
1074     NULL
1075   },
1076   {
1077     EL_SP_BUGGY_BASE_ACTIVE,
1078     EL_SP_BUGGY_BASE,
1079     0,
1080     InitBuggyBase,
1081     WarnBuggyBase,
1082     NULL
1083   },
1084   {
1085     EL_TRAP,
1086     EL_TRAP_ACTIVE,
1087     0,
1088     InitTrap,
1089     NULL,
1090     ActivateTrap
1091   },
1092   {
1093     EL_TRAP_ACTIVE,
1094     EL_TRAP,
1095     31,
1096     NULL,
1097     ChangeActiveTrap,
1098     NULL
1099   },
1100   {
1101     EL_ROBOT_WHEEL_ACTIVE,
1102     EL_ROBOT_WHEEL,
1103     0,
1104     InitRobotWheel,
1105     RunRobotWheel,
1106     StopRobotWheel
1107   },
1108   {
1109     EL_TIMEGATE_SWITCH_ACTIVE,
1110     EL_TIMEGATE_SWITCH,
1111     0,
1112     InitTimegateWheel,
1113     RunTimegateWheel,
1114     NULL
1115   },
1116   {
1117     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1118     EL_DC_TIMEGATE_SWITCH,
1119     0,
1120     InitTimegateWheel,
1121     RunTimegateWheel,
1122     NULL
1123   },
1124   {
1125     EL_EMC_MAGIC_BALL_ACTIVE,
1126     EL_EMC_MAGIC_BALL_ACTIVE,
1127     0,
1128     InitMagicBallDelay,
1129     NULL,
1130     ActivateMagicBall
1131   },
1132   {
1133     EL_EMC_SPRING_BUMPER_ACTIVE,
1134     EL_EMC_SPRING_BUMPER,
1135     8,
1136     NULL,
1137     NULL,
1138     NULL
1139   },
1140   {
1141     EL_DIAGONAL_SHRINKING,
1142     EL_UNDEFINED,
1143     0,
1144     NULL,
1145     NULL,
1146     NULL
1147   },
1148   {
1149     EL_DIAGONAL_GROWING,
1150     EL_UNDEFINED,
1151     0,
1152     NULL,
1153     NULL,
1154     NULL,
1155   },
1156
1157   {
1158     EL_UNDEFINED,
1159     EL_UNDEFINED,
1160     -1,
1161     NULL,
1162     NULL,
1163     NULL
1164   }
1165 };
1166
1167 struct
1168 {
1169   int element;
1170   int push_delay_fixed, push_delay_random;
1171 }
1172 push_delay_list[] =
1173 {
1174   { EL_SPRING,                  0, 0 },
1175   { EL_BALLOON,                 0, 0 },
1176
1177   { EL_SOKOBAN_OBJECT,          2, 0 },
1178   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1179   { EL_SATELLITE,               2, 0 },
1180   { EL_SP_DISK_YELLOW,          2, 0 },
1181
1182   { EL_UNDEFINED,               0, 0 },
1183 };
1184
1185 struct
1186 {
1187   int element;
1188   int move_stepsize;
1189 }
1190 move_stepsize_list[] =
1191 {
1192   { EL_AMOEBA_DROP,             2 },
1193   { EL_AMOEBA_DROPPING,         2 },
1194   { EL_QUICKSAND_FILLING,       1 },
1195   { EL_QUICKSAND_EMPTYING,      1 },
1196   { EL_QUICKSAND_FAST_FILLING,  2 },
1197   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1198   { EL_MAGIC_WALL_FILLING,      2 },
1199   { EL_MAGIC_WALL_EMPTYING,     2 },
1200   { EL_BD_MAGIC_WALL_FILLING,   2 },
1201   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1202   { EL_DC_MAGIC_WALL_FILLING,   2 },
1203   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1204
1205   { EL_UNDEFINED,               0 },
1206 };
1207
1208 struct
1209 {
1210   int element;
1211   int count;
1212 }
1213 collect_count_list[] =
1214 {
1215   { EL_EMERALD,                 1 },
1216   { EL_BD_DIAMOND,              1 },
1217   { EL_EMERALD_YELLOW,          1 },
1218   { EL_EMERALD_RED,             1 },
1219   { EL_EMERALD_PURPLE,          1 },
1220   { EL_DIAMOND,                 3 },
1221   { EL_SP_INFOTRON,             1 },
1222   { EL_PEARL,                   5 },
1223   { EL_CRYSTAL,                 8 },
1224
1225   { EL_UNDEFINED,               0 },
1226 };
1227
1228 struct
1229 {
1230   int element;
1231   int direction;
1232 }
1233 access_direction_list[] =
1234 {
1235   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1236   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1237   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1238   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1239   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1240   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1241   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1242   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1243   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1244   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1245   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1246
1247   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1248   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1249   { EL_SP_PORT_UP,                                                   MV_DOWN },
1250   { EL_SP_PORT_DOWN,                                         MV_UP           },
1251   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1252   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1253   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1254   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1255   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1256   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1257   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1258   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1259   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1260   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1261   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1262   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1263   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1264   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1265   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1266
1267   { EL_UNDEFINED,                       MV_NONE                              }
1268 };
1269
1270 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1271
1272 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1273 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1274 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1275                                  IS_JUST_CHANGING(x, y))
1276
1277 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1278
1279 /* static variables for playfield scan mode (scanning forward or backward) */
1280 static int playfield_scan_start_x = 0;
1281 static int playfield_scan_start_y = 0;
1282 static int playfield_scan_delta_x = 1;
1283 static int playfield_scan_delta_y = 1;
1284
1285 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1286                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1287                                      (y) += playfield_scan_delta_y)     \
1288                                 for ((x) = playfield_scan_start_x;      \
1289                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1290                                      (x) += playfield_scan_delta_x)
1291
1292 #ifdef DEBUG
1293 void DEBUG_SetMaximumDynamite()
1294 {
1295   int i;
1296
1297   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1298     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1299       local_player->inventory_element[local_player->inventory_size++] =
1300         EL_DYNAMITE;
1301 }
1302 #endif
1303
1304 static void InitPlayfieldScanModeVars()
1305 {
1306   if (game.use_reverse_scan_direction)
1307   {
1308     playfield_scan_start_x = lev_fieldx - 1;
1309     playfield_scan_start_y = lev_fieldy - 1;
1310
1311     playfield_scan_delta_x = -1;
1312     playfield_scan_delta_y = -1;
1313   }
1314   else
1315   {
1316     playfield_scan_start_x = 0;
1317     playfield_scan_start_y = 0;
1318
1319     playfield_scan_delta_x = 1;
1320     playfield_scan_delta_y = 1;
1321   }
1322 }
1323
1324 static void InitPlayfieldScanMode(int mode)
1325 {
1326   game.use_reverse_scan_direction =
1327     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1328
1329   InitPlayfieldScanModeVars();
1330 }
1331
1332 static int get_move_delay_from_stepsize(int move_stepsize)
1333 {
1334   move_stepsize =
1335     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1336
1337   /* make sure that stepsize value is always a power of 2 */
1338   move_stepsize = (1 << log_2(move_stepsize));
1339
1340   return TILEX / move_stepsize;
1341 }
1342
1343 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1344                                boolean init_game)
1345 {
1346   int player_nr = player->index_nr;
1347   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1348   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1349
1350   /* do no immediately change move delay -- the player might just be moving */
1351   player->move_delay_value_next = move_delay;
1352
1353   /* information if player can move must be set separately */
1354   player->cannot_move = cannot_move;
1355
1356   if (init_game)
1357   {
1358     player->move_delay       = game.initial_move_delay[player_nr];
1359     player->move_delay_value = game.initial_move_delay_value[player_nr];
1360
1361     player->move_delay_value_next = -1;
1362
1363     player->move_delay_reset_counter = 0;
1364   }
1365 }
1366
1367 void GetPlayerConfig()
1368 {
1369   GameFrameDelay = setup.game_frame_delay;
1370
1371   if (!audio.sound_available)
1372     setup.sound_simple = FALSE;
1373
1374   if (!audio.loops_available)
1375     setup.sound_loops = FALSE;
1376
1377   if (!audio.music_available)
1378     setup.sound_music = FALSE;
1379
1380   if (!video.fullscreen_available)
1381     setup.fullscreen = FALSE;
1382
1383   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1384
1385   SetAudioMode(setup.sound);
1386   InitJoysticks();
1387 }
1388
1389 int GetElementFromGroupElement(int element)
1390 {
1391   if (IS_GROUP_ELEMENT(element))
1392   {
1393     struct ElementGroupInfo *group = element_info[element].group;
1394     int last_anim_random_frame = gfx.anim_random_frame;
1395     int element_pos;
1396
1397     if (group->choice_mode == ANIM_RANDOM)
1398       gfx.anim_random_frame = RND(group->num_elements_resolved);
1399
1400     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1401                                     group->choice_mode, 0,
1402                                     group->choice_pos);
1403
1404     if (group->choice_mode == ANIM_RANDOM)
1405       gfx.anim_random_frame = last_anim_random_frame;
1406
1407     group->choice_pos++;
1408
1409     element = group->element_resolved[element_pos];
1410   }
1411
1412   return element;
1413 }
1414
1415 static void InitPlayerField(int x, int y, int element, boolean init_game)
1416 {
1417   if (element == EL_SP_MURPHY)
1418   {
1419     if (init_game)
1420     {
1421       if (stored_player[0].present)
1422       {
1423         Feld[x][y] = EL_SP_MURPHY_CLONE;
1424
1425         return;
1426       }
1427       else
1428       {
1429         stored_player[0].use_murphy = TRUE;
1430
1431         if (!level.use_artwork_element[0])
1432           stored_player[0].artwork_element = EL_SP_MURPHY;
1433       }
1434
1435       Feld[x][y] = EL_PLAYER_1;
1436     }
1437   }
1438
1439   if (init_game)
1440   {
1441     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1442     int jx = player->jx, jy = player->jy;
1443
1444     player->present = TRUE;
1445
1446     player->block_last_field = (element == EL_SP_MURPHY ?
1447                                 level.sp_block_last_field :
1448                                 level.block_last_field);
1449
1450     /* ---------- initialize player's last field block delay --------------- */
1451
1452     /* always start with reliable default value (no adjustment needed) */
1453     player->block_delay_adjustment = 0;
1454
1455     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1456     if (player->block_last_field && element == EL_SP_MURPHY)
1457       player->block_delay_adjustment = 1;
1458
1459     /* special case 2: in game engines before 3.1.1, blocking was different */
1460     if (game.use_block_last_field_bug)
1461       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1462
1463     if (!options.network || player->connected)
1464     {
1465       player->active = TRUE;
1466
1467       /* remove potentially duplicate players */
1468       if (StorePlayer[jx][jy] == Feld[x][y])
1469         StorePlayer[jx][jy] = 0;
1470
1471       StorePlayer[x][y] = Feld[x][y];
1472
1473       if (options.debug)
1474       {
1475         printf("Player %d activated.\n", player->element_nr);
1476         printf("[Local player is %d and currently %s.]\n",
1477                local_player->element_nr,
1478                local_player->active ? "active" : "not active");
1479       }
1480     }
1481
1482     Feld[x][y] = EL_EMPTY;
1483
1484     player->jx = player->last_jx = x;
1485     player->jy = player->last_jy = y;
1486   }
1487 }
1488
1489 static void InitField(int x, int y, boolean init_game)
1490 {
1491   int element = Feld[x][y];
1492
1493   switch (element)
1494   {
1495     case EL_SP_MURPHY:
1496     case EL_PLAYER_1:
1497     case EL_PLAYER_2:
1498     case EL_PLAYER_3:
1499     case EL_PLAYER_4:
1500       InitPlayerField(x, y, element, init_game);
1501       break;
1502
1503     case EL_SOKOBAN_FIELD_PLAYER:
1504       element = Feld[x][y] = EL_PLAYER_1;
1505       InitField(x, y, init_game);
1506
1507       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1508       InitField(x, y, init_game);
1509       break;
1510
1511     case EL_SOKOBAN_FIELD_EMPTY:
1512       local_player->sokobanfields_still_needed++;
1513       break;
1514
1515     case EL_STONEBLOCK:
1516       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1517         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1518       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1519         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1520       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1521         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1522       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1523         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1524       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1525         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1526       break;
1527
1528     case EL_BUG:
1529     case EL_BUG_RIGHT:
1530     case EL_BUG_UP:
1531     case EL_BUG_LEFT:
1532     case EL_BUG_DOWN:
1533     case EL_SPACESHIP:
1534     case EL_SPACESHIP_RIGHT:
1535     case EL_SPACESHIP_UP:
1536     case EL_SPACESHIP_LEFT:
1537     case EL_SPACESHIP_DOWN:
1538     case EL_BD_BUTTERFLY:
1539     case EL_BD_BUTTERFLY_RIGHT:
1540     case EL_BD_BUTTERFLY_UP:
1541     case EL_BD_BUTTERFLY_LEFT:
1542     case EL_BD_BUTTERFLY_DOWN:
1543     case EL_BD_FIREFLY:
1544     case EL_BD_FIREFLY_RIGHT:
1545     case EL_BD_FIREFLY_UP:
1546     case EL_BD_FIREFLY_LEFT:
1547     case EL_BD_FIREFLY_DOWN:
1548     case EL_PACMAN_RIGHT:
1549     case EL_PACMAN_UP:
1550     case EL_PACMAN_LEFT:
1551     case EL_PACMAN_DOWN:
1552     case EL_YAMYAM:
1553     case EL_YAMYAM_LEFT:
1554     case EL_YAMYAM_RIGHT:
1555     case EL_YAMYAM_UP:
1556     case EL_YAMYAM_DOWN:
1557     case EL_DARK_YAMYAM:
1558     case EL_ROBOT:
1559     case EL_PACMAN:
1560     case EL_SP_SNIKSNAK:
1561     case EL_SP_ELECTRON:
1562     case EL_MOLE:
1563     case EL_MOLE_LEFT:
1564     case EL_MOLE_RIGHT:
1565     case EL_MOLE_UP:
1566     case EL_MOLE_DOWN:
1567       InitMovDir(x, y);
1568       break;
1569
1570     case EL_AMOEBA_FULL:
1571     case EL_BD_AMOEBA:
1572       InitAmoebaNr(x, y);
1573       break;
1574
1575     case EL_AMOEBA_DROP:
1576       if (y == lev_fieldy - 1)
1577       {
1578         Feld[x][y] = EL_AMOEBA_GROWING;
1579         Store[x][y] = EL_AMOEBA_WET;
1580       }
1581       break;
1582
1583     case EL_DYNAMITE_ACTIVE:
1584     case EL_SP_DISK_RED_ACTIVE:
1585     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1586     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1587     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1588     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1589       MovDelay[x][y] = 96;
1590       break;
1591
1592     case EL_EM_DYNAMITE_ACTIVE:
1593       MovDelay[x][y] = 32;
1594       break;
1595
1596     case EL_LAMP:
1597       local_player->lights_still_needed++;
1598       break;
1599
1600     case EL_PENGUIN:
1601       local_player->friends_still_needed++;
1602       break;
1603
1604     case EL_PIG:
1605     case EL_DRAGON:
1606       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1607       break;
1608
1609     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1610     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1611     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1612     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1613     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1614     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1615     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1616     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1617     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1618     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1619     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1620     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1621       if (init_game)
1622       {
1623         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1624         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1625         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1626
1627         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1628         {
1629           game.belt_dir[belt_nr] = belt_dir;
1630           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1631         }
1632         else    /* more than one switch -- set it like the first switch */
1633         {
1634           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1635         }
1636       }
1637       break;
1638
1639 #if !USE_BOTH_SWITCHGATE_SWITCHES
1640     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1641       if (init_game)
1642         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1643       break;
1644
1645     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1646       if (init_game)
1647         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1648       break;
1649 #endif
1650
1651     case EL_LIGHT_SWITCH_ACTIVE:
1652       if (init_game)
1653         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1654       break;
1655
1656     case EL_INVISIBLE_STEELWALL:
1657     case EL_INVISIBLE_WALL:
1658     case EL_INVISIBLE_SAND:
1659       if (game.light_time_left > 0 ||
1660           game.lenses_time_left > 0)
1661         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1662       break;
1663
1664     case EL_EMC_MAGIC_BALL:
1665       if (game.ball_state)
1666         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1667       break;
1668
1669     case EL_EMC_MAGIC_BALL_SWITCH:
1670       if (game.ball_state)
1671         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1672       break;
1673
1674     default:
1675       if (IS_CUSTOM_ELEMENT(element))
1676       {
1677         if (CAN_MOVE(element))
1678           InitMovDir(x, y);
1679
1680 #if USE_NEW_CUSTOM_VALUE
1681         if (!element_info[element].use_last_ce_value || init_game)
1682           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1683 #endif
1684       }
1685       else if (IS_GROUP_ELEMENT(element))
1686       {
1687         Feld[x][y] = GetElementFromGroupElement(element);
1688
1689         InitField(x, y, init_game);
1690       }
1691
1692       break;
1693   }
1694
1695   if (!init_game)
1696     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1697 }
1698
1699 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1700 {
1701   InitField(x, y, init_game);
1702
1703   /* not needed to call InitMovDir() -- already done by InitField()! */
1704   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1705       CAN_MOVE(Feld[x][y]))
1706     InitMovDir(x, y);
1707 }
1708
1709 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1710 {
1711   int old_element = Feld[x][y];
1712
1713   InitField(x, y, init_game);
1714
1715   /* not needed to call InitMovDir() -- already done by InitField()! */
1716   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1717       CAN_MOVE(old_element) &&
1718       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1719     InitMovDir(x, y);
1720
1721   /* this case is in fact a combination of not less than three bugs:
1722      first, it calls InitMovDir() for elements that can move, although this is
1723      already done by InitField(); then, it checks the element that was at this
1724      field _before_ the call to InitField() (which can change it); lastly, it
1725      was not called for "mole with direction" elements, which were treated as
1726      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1727   */
1728 }
1729
1730 #if 1
1731
1732 void DrawGameValue_Emeralds(int value)
1733 {
1734   struct TextPosInfo *pos = &game.panel.gems;
1735 #if 1
1736   int font_nr = pos->font;
1737 #else
1738   int font_nr = FONT_TEXT_2;
1739 #endif
1740   int font_width = getFontWidth(font_nr);
1741   int chars = pos->chars;
1742
1743   if (PANEL_DEACTIVATED(pos))
1744     return;
1745
1746   pos->width = chars * font_width;
1747
1748   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1749 }
1750
1751 void DrawGameValue_Dynamite(int value)
1752 {
1753   struct TextPosInfo *pos = &game.panel.inventory;
1754 #if 1
1755   int font_nr = pos->font;
1756 #else
1757   int font_nr = FONT_TEXT_2;
1758 #endif
1759   int font_width = getFontWidth(font_nr);
1760   int chars = pos->chars;
1761
1762   if (PANEL_DEACTIVATED(pos))
1763     return;
1764
1765   pos->width = chars * font_width;
1766
1767   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1768 }
1769
1770 void DrawGameValue_Score(int value)
1771 {
1772   struct TextPosInfo *pos = &game.panel.score;
1773 #if 1
1774   int font_nr = pos->font;
1775 #else
1776   int font_nr = FONT_TEXT_2;
1777 #endif
1778   int font_width = getFontWidth(font_nr);
1779   int chars = pos->chars;
1780
1781   if (PANEL_DEACTIVATED(pos))
1782     return;
1783
1784   pos->width = chars * font_width;
1785
1786   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1787 }
1788
1789 void DrawGameValue_Time(int value)
1790 {
1791   struct TextPosInfo *pos = &game.panel.time;
1792   static int last_value = -1;
1793   int chars1 = 3;
1794   int chars2 = 4;
1795   int chars = pos->chars;
1796 #if 1
1797   int font1_nr = pos->font;
1798   int font2_nr = pos->font_alt;
1799 #else
1800   int font1_nr = FONT_TEXT_2;
1801   int font2_nr = FONT_TEXT_1;
1802 #endif
1803   int font_nr = font1_nr;
1804   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
1805
1806   if (PANEL_DEACTIVATED(pos))
1807     return;
1808
1809   if (use_dynamic_chars)                /* use dynamic number of chars */
1810   {
1811     chars   = (value < 1000 ? chars1   : chars2);
1812     font_nr = (value < 1000 ? font1_nr : font2_nr);
1813   }
1814
1815   /* clear background if value just changed its size (dynamic chars only) */
1816   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
1817   {
1818     int width1 = chars1 * getFontWidth(font1_nr);
1819     int width2 = chars2 * getFontWidth(font2_nr);
1820     int max_width = MAX(width1, width2);
1821     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
1822
1823     pos->width = max_width;
1824
1825     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
1826                                max_width, max_height);
1827   }
1828
1829   pos->width = chars * getFontWidth(font_nr);
1830
1831   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1832
1833   last_value = value;
1834 }
1835
1836 void DrawGameValue_Level(int value)
1837 {
1838   struct TextPosInfo *pos = &game.panel.level_number;
1839   int chars1 = 2;
1840   int chars2 = 3;
1841   int chars = pos->chars;
1842 #if 1
1843   int font1_nr = pos->font;
1844   int font2_nr = pos->font_alt;
1845 #else
1846   int font1_nr = FONT_TEXT_2;
1847   int font2_nr = FONT_TEXT_1;
1848 #endif
1849   int font_nr = font1_nr;
1850   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
1851
1852   if (PANEL_DEACTIVATED(pos))
1853     return;
1854
1855   if (use_dynamic_chars)                /* use dynamic number of chars */
1856   {
1857     chars   = (level_nr < 100 ? chars1   : chars2);
1858     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
1859   }
1860
1861   pos->width = chars * getFontWidth(font_nr);
1862
1863   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1864 }
1865
1866 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1867 {
1868 #if 0
1869   struct TextPosInfo *pos = &game.panel.keys;
1870 #endif
1871 #if 0
1872   int base_key_graphic = EL_KEY_1;
1873 #endif
1874   int i;
1875
1876 #if 0
1877   if (PANEL_DEACTIVATED(pos))
1878     return;
1879 #endif
1880
1881 #if 0
1882   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1883     base_key_graphic = EL_EM_KEY_1;
1884 #endif
1885
1886 #if 0
1887   pos->width = 4 * MINI_TILEX;
1888 #endif
1889
1890 #if 1
1891   for (i = 0; i < MAX_NUM_KEYS; i++)
1892 #else
1893   /* currently only 4 of 8 possible keys are displayed */
1894   for (i = 0; i < STD_NUM_KEYS; i++)
1895 #endif
1896   {
1897 #if 1
1898     struct TextPosInfo *pos = &game.panel.key[i];
1899 #endif
1900     int src_x = DOOR_GFX_PAGEX5 + 18;
1901     int src_y = DOOR_GFX_PAGEY1 + 123;
1902 #if 1
1903     int dst_x = PANEL_XPOS(pos);
1904     int dst_y = PANEL_YPOS(pos);
1905 #else
1906     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
1907     int dst_y = PANEL_YPOS(pos);
1908 #endif
1909
1910 #if 1
1911     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
1912                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
1913                    EL_KEY_1) + i;
1914     int graphic = el2edimg(element);
1915 #endif
1916
1917 #if 1
1918     if (PANEL_DEACTIVATED(pos))
1919       continue;
1920 #endif
1921
1922 #if 0
1923     /* masked blit with tiles from half-size scaled bitmap does not work yet
1924        (no mask bitmap created for these sizes after loading and scaling) --
1925        solution: load without creating mask, scale, then create final mask */
1926
1927     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1928                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1929
1930     if (key[i])
1931     {
1932 #if 0
1933       int graphic = el2edimg(base_key_graphic + i);
1934 #endif
1935       Bitmap *src_bitmap;
1936       int src_x, src_y;
1937
1938       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1939
1940       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
1941                     dst_x - src_x, dst_y - src_y);
1942       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
1943                        dst_x, dst_y);
1944     }
1945 #else
1946 #if 1
1947     if (key[i])
1948       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
1949     else
1950       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1951                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1952 #else
1953     if (key[i])
1954       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
1955     else
1956       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1957                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1958 #endif
1959 #endif
1960   }
1961 }
1962
1963 #else
1964
1965 void DrawGameValue_Emeralds(int value)
1966 {
1967   int font_nr = FONT_TEXT_2;
1968   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1969
1970   if (PANEL_DEACTIVATED(game.panel.gems))
1971     return;
1972
1973   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
1974 }
1975
1976 void DrawGameValue_Dynamite(int value)
1977 {
1978   int font_nr = FONT_TEXT_2;
1979   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1980
1981   if (PANEL_DEACTIVATED(game.panel.inventory))
1982     return;
1983
1984   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
1985 }
1986
1987 void DrawGameValue_Score(int value)
1988 {
1989   int font_nr = FONT_TEXT_2;
1990   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
1991
1992   if (PANEL_DEACTIVATED(game.panel.score))
1993     return;
1994
1995   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
1996 }
1997
1998 void DrawGameValue_Time(int value)
1999 {
2000   int font1_nr = FONT_TEXT_2;
2001 #if 1
2002   int font2_nr = FONT_TEXT_1;
2003 #else
2004   int font2_nr = FONT_LEVEL_NUMBER;
2005 #endif
2006   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2007   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2008
2009   if (PANEL_DEACTIVATED(game.panel.time))
2010     return;
2011
2012   /* clear background if value just changed its size */
2013   if (value == 999 || value == 1000)
2014     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2015
2016   if (value < 1000)
2017     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2018   else
2019     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2020 }
2021
2022 void DrawGameValue_Level(int value)
2023 {
2024   int font1_nr = FONT_TEXT_2;
2025 #if 1
2026   int font2_nr = FONT_TEXT_1;
2027 #else
2028   int font2_nr = FONT_LEVEL_NUMBER;
2029 #endif
2030
2031   if (PANEL_DEACTIVATED(game.panel.level))
2032     return;
2033
2034   if (level_nr < 100)
2035     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2036   else
2037     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2038 }
2039
2040 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2041 {
2042   int base_key_graphic = EL_KEY_1;
2043   int i;
2044
2045   if (PANEL_DEACTIVATED(game.panel.keys))
2046     return;
2047
2048   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2049     base_key_graphic = EL_EM_KEY_1;
2050
2051   /* currently only 4 of 8 possible keys are displayed */
2052   for (i = 0; i < STD_NUM_KEYS; i++)
2053   {
2054     int x = XX_KEYS + i * MINI_TILEX;
2055     int y = YY_KEYS;
2056
2057     if (key[i])
2058       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2059     else
2060       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2061                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2062   }
2063 }
2064
2065 #endif
2066
2067 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2068                        int key_bits)
2069 {
2070   int key[MAX_NUM_KEYS];
2071   int i;
2072
2073   /* prevent EM engine from updating time/score values parallel to GameWon() */
2074   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2075       local_player->LevelSolved)
2076     return;
2077
2078   for (i = 0; i < MAX_NUM_KEYS; i++)
2079     key[i] = key_bits & (1 << i);
2080
2081   DrawGameValue_Level(level_nr);
2082
2083   DrawGameValue_Emeralds(emeralds);
2084   DrawGameValue_Dynamite(dynamite);
2085   DrawGameValue_Score(score);
2086   DrawGameValue_Time(time);
2087
2088   DrawGameValue_Keys(key);
2089 }
2090
2091 void DrawGameDoorValues()
2092 {
2093   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2094   int dynamite_value = 0;
2095   int score_value = (local_player->LevelSolved ? local_player->score_final :
2096                      local_player->score);
2097   int gems_value = local_player->gems_still_needed;
2098   int key_bits = 0;
2099   int i, j;
2100
2101   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2102   {
2103     DrawGameDoorValues_EM();
2104
2105     return;
2106   }
2107
2108   if (game.centered_player_nr == -1)
2109   {
2110     for (i = 0; i < MAX_PLAYERS; i++)
2111     {
2112       for (j = 0; j < MAX_NUM_KEYS; j++)
2113         if (stored_player[i].key[j])
2114           key_bits |= (1 << j);
2115
2116       dynamite_value += stored_player[i].inventory_size;
2117     }
2118   }
2119   else
2120   {
2121     int player_nr = game.centered_player_nr;
2122
2123     for (i = 0; i < MAX_NUM_KEYS; i++)
2124       if (stored_player[player_nr].key[i])
2125         key_bits |= (1 << i);
2126
2127     dynamite_value = stored_player[player_nr].inventory_size;
2128   }
2129
2130   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2131                     key_bits);
2132 }
2133
2134
2135 /*
2136   =============================================================================
2137   InitGameEngine()
2138   -----------------------------------------------------------------------------
2139   initialize game engine due to level / tape version number
2140   =============================================================================
2141 */
2142
2143 static void InitGameEngine()
2144 {
2145   int i, j, k, l, x, y;
2146
2147   /* set game engine from tape file when re-playing, else from level file */
2148   game.engine_version = (tape.playing ? tape.engine_version :
2149                          level.game_version);
2150
2151   /* ---------------------------------------------------------------------- */
2152   /* set flags for bugs and changes according to active game engine version */
2153   /* ---------------------------------------------------------------------- */
2154
2155   /*
2156     Summary of bugfix/change:
2157     Fixed handling for custom elements that change when pushed by the player.
2158
2159     Fixed/changed in version:
2160     3.1.0
2161
2162     Description:
2163     Before 3.1.0, custom elements that "change when pushing" changed directly
2164     after the player started pushing them (until then handled in "DigField()").
2165     Since 3.1.0, these custom elements are not changed until the "pushing"
2166     move of the element is finished (now handled in "ContinueMoving()").
2167
2168     Affected levels/tapes:
2169     The first condition is generally needed for all levels/tapes before version
2170     3.1.0, which might use the old behaviour before it was changed; known tapes
2171     that are affected are some tapes from the level set "Walpurgis Gardens" by
2172     Jamie Cullen.
2173     The second condition is an exception from the above case and is needed for
2174     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2175     above (including some development versions of 3.1.0), but before it was
2176     known that this change would break tapes like the above and was fixed in
2177     3.1.1, so that the changed behaviour was active although the engine version
2178     while recording maybe was before 3.1.0. There is at least one tape that is
2179     affected by this exception, which is the tape for the one-level set "Bug
2180     Machine" by Juergen Bonhagen.
2181   */
2182
2183   game.use_change_when_pushing_bug =
2184     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2185      !(tape.playing &&
2186        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2187        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2188
2189   /*
2190     Summary of bugfix/change:
2191     Fixed handling for blocking the field the player leaves when moving.
2192
2193     Fixed/changed in version:
2194     3.1.1
2195
2196     Description:
2197     Before 3.1.1, when "block last field when moving" was enabled, the field
2198     the player is leaving when moving was blocked for the time of the move,
2199     and was directly unblocked afterwards. This resulted in the last field
2200     being blocked for exactly one less than the number of frames of one player
2201     move. Additionally, even when blocking was disabled, the last field was
2202     blocked for exactly one frame.
2203     Since 3.1.1, due to changes in player movement handling, the last field
2204     is not blocked at all when blocking is disabled. When blocking is enabled,
2205     the last field is blocked for exactly the number of frames of one player
2206     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2207     last field is blocked for exactly one more than the number of frames of
2208     one player move.
2209
2210     Affected levels/tapes:
2211     (!!! yet to be determined -- probably many !!!)
2212   */
2213
2214   game.use_block_last_field_bug =
2215     (game.engine_version < VERSION_IDENT(3,1,1,0));
2216
2217   /*
2218     Summary of bugfix/change:
2219     Changed behaviour of CE changes with multiple changes per single frame.
2220
2221     Fixed/changed in version:
2222     3.2.0-6
2223
2224     Description:
2225     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
2226     This resulted in race conditions where CEs seem to behave strange in some
2227     situations (where triggered CE changes were just skipped because there was
2228     already a CE change on that tile in the playfield in that engine frame).
2229     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
2230     (The number of changes per frame must be limited in any case, because else
2231     it is easily possible to define CE changes that would result in an infinite
2232     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
2233     should be set large enough so that it would only be reached in cases where
2234     the corresponding CE change conditions run into a loop. Therefore, it seems
2235     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
2236     maximal number of change pages for custom elements.)
2237
2238     Affected levels/tapes:
2239     Probably many.
2240   */
2241
2242 #if USE_ONLY_ONE_CHANGE_PER_FRAME
2243   game.max_num_changes_per_frame = 1;
2244 #else
2245   game.max_num_changes_per_frame =
2246     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
2247 #endif
2248
2249   /* ---------------------------------------------------------------------- */
2250
2251   /* default scan direction: scan playfield from top/left to bottom/right */
2252   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2253
2254   /* dynamically adjust element properties according to game engine version */
2255   InitElementPropertiesEngine(game.engine_version);
2256
2257 #if 0
2258   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2259   printf("          tape version == %06d [%s] [file: %06d]\n",
2260          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2261          tape.file_version);
2262   printf("       => game.engine_version == %06d\n", game.engine_version);
2263 #endif
2264
2265   /* ---------- initialize player's initial move delay --------------------- */
2266
2267   /* dynamically adjust player properties according to level information */
2268   for (i = 0; i < MAX_PLAYERS; i++)
2269     game.initial_move_delay_value[i] =
2270       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2271
2272   /* dynamically adjust player properties according to game engine version */
2273   for (i = 0; i < MAX_PLAYERS; i++)
2274     game.initial_move_delay[i] =
2275       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2276        game.initial_move_delay_value[i] : 0);
2277
2278   /* ---------- initialize player's initial push delay --------------------- */
2279
2280   /* dynamically adjust player properties according to game engine version */
2281   game.initial_push_delay_value =
2282     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2283
2284   /* ---------- initialize changing elements ------------------------------- */
2285
2286   /* initialize changing elements information */
2287   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2288   {
2289     struct ElementInfo *ei = &element_info[i];
2290
2291     /* this pointer might have been changed in the level editor */
2292     ei->change = &ei->change_page[0];
2293
2294     if (!IS_CUSTOM_ELEMENT(i))
2295     {
2296       ei->change->target_element = EL_EMPTY_SPACE;
2297       ei->change->delay_fixed = 0;
2298       ei->change->delay_random = 0;
2299       ei->change->delay_frames = 1;
2300     }
2301
2302     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2303     {
2304       ei->has_change_event[j] = FALSE;
2305
2306       ei->event_page_nr[j] = 0;
2307       ei->event_page[j] = &ei->change_page[0];
2308     }
2309   }
2310
2311   /* add changing elements from pre-defined list */
2312   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2313   {
2314     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2315     struct ElementInfo *ei = &element_info[ch_delay->element];
2316
2317     ei->change->target_element       = ch_delay->target_element;
2318     ei->change->delay_fixed          = ch_delay->change_delay;
2319
2320     ei->change->pre_change_function  = ch_delay->pre_change_function;
2321     ei->change->change_function      = ch_delay->change_function;
2322     ei->change->post_change_function = ch_delay->post_change_function;
2323
2324     ei->change->can_change = TRUE;
2325     ei->change->can_change_or_has_action = TRUE;
2326
2327     ei->has_change_event[CE_DELAY] = TRUE;
2328
2329     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2330     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2331   }
2332
2333   /* ---------- initialize internal run-time variables ------------- */
2334
2335   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2336   {
2337     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2338
2339     for (j = 0; j < ei->num_change_pages; j++)
2340     {
2341       ei->change_page[j].can_change_or_has_action =
2342         (ei->change_page[j].can_change |
2343          ei->change_page[j].has_action);
2344     }
2345   }
2346
2347   /* add change events from custom element configuration */
2348   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2349   {
2350     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2351
2352     for (j = 0; j < ei->num_change_pages; j++)
2353     {
2354       if (!ei->change_page[j].can_change_or_has_action)
2355         continue;
2356
2357       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2358       {
2359         /* only add event page for the first page found with this event */
2360         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2361         {
2362           ei->has_change_event[k] = TRUE;
2363
2364           ei->event_page_nr[k] = j;
2365           ei->event_page[k] = &ei->change_page[j];
2366         }
2367       }
2368     }
2369   }
2370
2371   /* ---------- initialize run-time trigger player and element ------------- */
2372
2373   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2374   {
2375     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2376
2377     for (j = 0; j < ei->num_change_pages; j++)
2378     {
2379       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2380       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
2381       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2382       ei->change_page[j].actual_trigger_ce_value = 0;
2383       ei->change_page[j].actual_trigger_ce_score = 0;
2384     }
2385   }
2386
2387   /* ---------- initialize trigger events ---------------------------------- */
2388
2389   /* initialize trigger events information */
2390   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2391     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2392       trigger_events[i][j] = FALSE;
2393
2394   /* add trigger events from element change event properties */
2395   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2396   {
2397     struct ElementInfo *ei = &element_info[i];
2398
2399     for (j = 0; j < ei->num_change_pages; j++)
2400     {
2401       if (!ei->change_page[j].can_change_or_has_action)
2402         continue;
2403
2404       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2405       {
2406         int trigger_element = ei->change_page[j].trigger_element;
2407
2408         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2409         {
2410           if (ei->change_page[j].has_event[k])
2411           {
2412             if (IS_GROUP_ELEMENT(trigger_element))
2413             {
2414               struct ElementGroupInfo *group =
2415                 element_info[trigger_element].group;
2416
2417               for (l = 0; l < group->num_elements_resolved; l++)
2418                 trigger_events[group->element_resolved[l]][k] = TRUE;
2419             }
2420             else if (trigger_element == EL_ANY_ELEMENT)
2421               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2422                 trigger_events[l][k] = TRUE;
2423             else
2424               trigger_events[trigger_element][k] = TRUE;
2425           }
2426         }
2427       }
2428     }
2429   }
2430
2431   /* ---------- initialize push delay -------------------------------------- */
2432
2433   /* initialize push delay values to default */
2434   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2435   {
2436     if (!IS_CUSTOM_ELEMENT(i))
2437     {
2438       /* set default push delay values (corrected since version 3.0.7-1) */
2439       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2440       {
2441         element_info[i].push_delay_fixed = 2;
2442         element_info[i].push_delay_random = 8;
2443       }
2444       else
2445       {
2446         element_info[i].push_delay_fixed = 8;
2447         element_info[i].push_delay_random = 8;
2448       }
2449     }
2450   }
2451
2452   /* set push delay value for certain elements from pre-defined list */
2453   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2454   {
2455     int e = push_delay_list[i].element;
2456
2457     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2458     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2459   }
2460
2461   /* set push delay value for Supaplex elements for newer engine versions */
2462   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2463   {
2464     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2465     {
2466       if (IS_SP_ELEMENT(i))
2467       {
2468         /* set SP push delay to just enough to push under a falling zonk */
2469         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2470
2471         element_info[i].push_delay_fixed  = delay;
2472         element_info[i].push_delay_random = 0;
2473       }
2474     }
2475   }
2476
2477   /* ---------- initialize move stepsize ----------------------------------- */
2478
2479   /* initialize move stepsize values to default */
2480   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2481     if (!IS_CUSTOM_ELEMENT(i))
2482       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2483
2484   /* set move stepsize value for certain elements from pre-defined list */
2485   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2486   {
2487     int e = move_stepsize_list[i].element;
2488
2489     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2490   }
2491
2492   /* ---------- initialize collect score ----------------------------------- */
2493
2494   /* initialize collect score values for custom elements from initial value */
2495   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2496     if (IS_CUSTOM_ELEMENT(i))
2497       element_info[i].collect_score = element_info[i].collect_score_initial;
2498
2499   /* ---------- initialize collect count ----------------------------------- */
2500
2501   /* initialize collect count values for non-custom elements */
2502   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2503     if (!IS_CUSTOM_ELEMENT(i))
2504       element_info[i].collect_count_initial = 0;
2505
2506   /* add collect count values for all elements from pre-defined list */
2507   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2508     element_info[collect_count_list[i].element].collect_count_initial =
2509       collect_count_list[i].count;
2510
2511   /* ---------- initialize access direction -------------------------------- */
2512
2513   /* initialize access direction values to default (access from every side) */
2514   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2515     if (!IS_CUSTOM_ELEMENT(i))
2516       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2517
2518   /* set access direction value for certain elements from pre-defined list */
2519   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2520     element_info[access_direction_list[i].element].access_direction =
2521       access_direction_list[i].direction;
2522
2523   /* ---------- initialize explosion content ------------------------------- */
2524   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2525   {
2526     if (IS_CUSTOM_ELEMENT(i))
2527       continue;
2528
2529     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2530     {
2531       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2532
2533       element_info[i].content.e[x][y] =
2534         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2535          i == EL_PLAYER_2 ? EL_EMERALD_RED :
2536          i == EL_PLAYER_3 ? EL_EMERALD :
2537          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2538          i == EL_MOLE ? EL_EMERALD_RED :
2539          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2540          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2541          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2542          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2543          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2544          i == EL_WALL_EMERALD ? EL_EMERALD :
2545          i == EL_WALL_DIAMOND ? EL_DIAMOND :
2546          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2547          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2548          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2549          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2550          i == EL_WALL_PEARL ? EL_PEARL :
2551          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2552          EL_EMPTY);
2553     }
2554   }
2555
2556   /* ---------- initialize recursion detection ------------------------------ */
2557   recursion_loop_depth = 0;
2558   recursion_loop_detected = FALSE;
2559   recursion_loop_element = EL_UNDEFINED;
2560 }
2561
2562 int get_num_special_action(int element, int action_first, int action_last)
2563 {
2564   int num_special_action = 0;
2565   int i, j;
2566
2567   for (i = action_first; i <= action_last; i++)
2568   {
2569     boolean found = FALSE;
2570
2571     for (j = 0; j < NUM_DIRECTIONS; j++)
2572       if (el_act_dir2img(element, i, j) !=
2573           el_act_dir2img(element, ACTION_DEFAULT, j))
2574         found = TRUE;
2575
2576     if (found)
2577       num_special_action++;
2578     else
2579       break;
2580   }
2581
2582   return num_special_action;
2583 }
2584
2585
2586 /*
2587   =============================================================================
2588   InitGame()
2589   -----------------------------------------------------------------------------
2590   initialize and start new game
2591   =============================================================================
2592 */
2593
2594 void InitGame()
2595 {
2596   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
2597   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
2598   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
2599 #if 0
2600   boolean do_fading = (game_status == GAME_MODE_MAIN);
2601 #endif
2602   int i, j, x, y;
2603
2604   game_status = GAME_MODE_PLAYING;
2605
2606   InitGameEngine();
2607
2608   /* don't play tapes over network */
2609   network_playing = (options.network && !tape.playing);
2610
2611   for (i = 0; i < MAX_PLAYERS; i++)
2612   {
2613     struct PlayerInfo *player = &stored_player[i];
2614
2615     player->index_nr = i;
2616     player->index_bit = (1 << i);
2617     player->element_nr = EL_PLAYER_1 + i;
2618
2619     player->present = FALSE;
2620     player->active = FALSE;
2621     player->killed = FALSE;
2622
2623     player->action = 0;
2624     player->effective_action = 0;
2625     player->programmed_action = 0;
2626
2627     player->score = 0;
2628     player->score_final = 0;
2629
2630     player->gems_still_needed = level.gems_needed;
2631     player->sokobanfields_still_needed = 0;
2632     player->lights_still_needed = 0;
2633     player->friends_still_needed = 0;
2634
2635     for (j = 0; j < MAX_NUM_KEYS; j++)
2636       player->key[j] = FALSE;
2637
2638     player->num_white_keys = 0;
2639
2640     player->dynabomb_count = 0;
2641     player->dynabomb_size = 1;
2642     player->dynabombs_left = 0;
2643     player->dynabomb_xl = FALSE;
2644
2645     player->MovDir = MV_NONE;
2646     player->MovPos = 0;
2647     player->GfxPos = 0;
2648     player->GfxDir = MV_NONE;
2649     player->GfxAction = ACTION_DEFAULT;
2650     player->Frame = 0;
2651     player->StepFrame = 0;
2652
2653     player->use_murphy = FALSE;
2654     player->artwork_element =
2655       (level.use_artwork_element[i] ? level.artwork_element[i] :
2656        player->element_nr);
2657
2658     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
2659     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2660
2661     player->gravity = level.initial_player_gravity[i];
2662
2663     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2664
2665     player->actual_frame_counter = 0;
2666
2667     player->step_counter = 0;
2668
2669     player->last_move_dir = MV_NONE;
2670
2671     player->is_active = FALSE;
2672
2673     player->is_waiting = FALSE;
2674     player->is_moving = FALSE;
2675     player->is_auto_moving = FALSE;
2676     player->is_digging = FALSE;
2677     player->is_snapping = FALSE;
2678     player->is_collecting = FALSE;
2679     player->is_pushing = FALSE;
2680     player->is_switching = FALSE;
2681     player->is_dropping = FALSE;
2682     player->is_dropping_pressed = FALSE;
2683
2684     player->is_bored = FALSE;
2685     player->is_sleeping = FALSE;
2686
2687     player->frame_counter_bored = -1;
2688     player->frame_counter_sleeping = -1;
2689
2690     player->anim_delay_counter = 0;
2691     player->post_delay_counter = 0;
2692
2693     player->dir_waiting = MV_NONE;
2694     player->action_waiting = ACTION_DEFAULT;
2695     player->last_action_waiting = ACTION_DEFAULT;
2696     player->special_action_bored = ACTION_DEFAULT;
2697     player->special_action_sleeping = ACTION_DEFAULT;
2698
2699     player->switch_x = -1;
2700     player->switch_y = -1;
2701
2702     player->drop_x = -1;
2703     player->drop_y = -1;
2704
2705     player->show_envelope = 0;
2706
2707     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2708
2709     player->push_delay       = -1;      /* initialized when pushing starts */
2710     player->push_delay_value = game.initial_push_delay_value;
2711
2712     player->drop_delay = 0;
2713     player->drop_pressed_delay = 0;
2714
2715     player->last_jx = -1;
2716     player->last_jy = -1;
2717     player->jx = -1;
2718     player->jy = -1;
2719
2720     player->shield_normal_time_left = 0;
2721     player->shield_deadly_time_left = 0;
2722
2723     player->inventory_infinite_element = EL_UNDEFINED;
2724     player->inventory_size = 0;
2725
2726     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2727     SnapField(player, 0, 0);
2728
2729     player->LevelSolved = FALSE;
2730     player->GameOver = FALSE;
2731
2732     player->LevelSolved_GameWon = FALSE;
2733     player->LevelSolved_GameEnd = FALSE;
2734     player->LevelSolved_PanelOff = FALSE;
2735     player->LevelSolved_SaveTape = FALSE;
2736     player->LevelSolved_SaveScore = FALSE;
2737   }
2738
2739   network_player_action_received = FALSE;
2740
2741 #if defined(NETWORK_AVALIABLE)
2742   /* initial null action */
2743   if (network_playing)
2744     SendToServer_MovePlayer(MV_NONE);
2745 #endif
2746
2747   ZX = ZY = -1;
2748   ExitX = ExitY = -1;
2749
2750   FrameCounter = 0;
2751   TimeFrames = 0;
2752   TimePlayed = 0;
2753   TimeLeft = level.time;
2754   TapeTime = 0;
2755
2756   ScreenMovDir = MV_NONE;
2757   ScreenMovPos = 0;
2758   ScreenGfxPos = 0;
2759
2760   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
2761
2762   AllPlayersGone = FALSE;
2763
2764   game.yamyam_content_nr = 0;
2765   game.magic_wall_active = FALSE;
2766   game.magic_wall_time_left = 0;
2767   game.light_time_left = 0;
2768   game.timegate_time_left = 0;
2769   game.switchgate_pos = 0;
2770   game.wind_direction = level.wind_direction_initial;
2771
2772 #if !USE_PLAYER_GRAVITY
2773   game.gravity = FALSE;
2774   game.explosions_delayed = TRUE;
2775 #endif
2776
2777   game.lenses_time_left = 0;
2778   game.magnify_time_left = 0;
2779
2780   game.ball_state = level.ball_state_initial;
2781   game.ball_content_nr = 0;
2782
2783   game.envelope_active = FALSE;
2784
2785   /* set focus to local player for network games, else to all players */
2786   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2787   game.centered_player_nr_next = game.centered_player_nr;
2788   game.set_centered_player = FALSE;
2789
2790   if (network_playing && tape.recording)
2791   {
2792     /* store client dependent player focus when recording network games */
2793     tape.centered_player_nr_next = game.centered_player_nr_next;
2794     tape.set_centered_player = TRUE;
2795   }
2796
2797   for (i = 0; i < NUM_BELTS; i++)
2798   {
2799     game.belt_dir[i] = MV_NONE;
2800     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
2801   }
2802
2803   for (i = 0; i < MAX_NUM_AMOEBA; i++)
2804     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2805
2806   SCAN_PLAYFIELD(x, y)
2807   {
2808     Feld[x][y] = level.field[x][y];
2809     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2810     ChangeDelay[x][y] = 0;
2811     ChangePage[x][y] = -1;
2812 #if USE_NEW_CUSTOM_VALUE
2813     CustomValue[x][y] = 0;              /* initialized in InitField() */
2814 #endif
2815     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2816     AmoebaNr[x][y] = 0;
2817     WasJustMoving[x][y] = 0;
2818     WasJustFalling[x][y] = 0;
2819     CheckCollision[x][y] = 0;
2820     CheckImpact[x][y] = 0;
2821     Stop[x][y] = FALSE;
2822     Pushed[x][y] = FALSE;
2823
2824     ChangeCount[x][y] = 0;
2825     ChangeEvent[x][y] = -1;
2826
2827     ExplodePhase[x][y] = 0;
2828     ExplodeDelay[x][y] = 0;
2829     ExplodeField[x][y] = EX_TYPE_NONE;
2830
2831     RunnerVisit[x][y] = 0;
2832     PlayerVisit[x][y] = 0;
2833
2834     GfxFrame[x][y] = 0;
2835     GfxRandom[x][y] = INIT_GFX_RANDOM();
2836     GfxElement[x][y] = EL_UNDEFINED;
2837     GfxAction[x][y] = ACTION_DEFAULT;
2838     GfxDir[x][y] = MV_NONE;
2839   }
2840
2841   SCAN_PLAYFIELD(x, y)
2842   {
2843     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2844       emulate_bd = FALSE;
2845     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2846       emulate_sb = FALSE;
2847     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2848       emulate_sp = FALSE;
2849
2850     InitField(x, y, TRUE);
2851   }
2852
2853   InitBeltMovement();
2854
2855   for (i = 0; i < MAX_PLAYERS; i++)
2856   {
2857     struct PlayerInfo *player = &stored_player[i];
2858
2859     /* set number of special actions for bored and sleeping animation */
2860     player->num_special_action_bored =
2861       get_num_special_action(player->artwork_element,
2862                              ACTION_BORING_1, ACTION_BORING_LAST);
2863     player->num_special_action_sleeping =
2864       get_num_special_action(player->artwork_element,
2865                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2866   }
2867
2868   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2869                     emulate_sb ? EMU_SOKOBAN :
2870                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2871
2872 #if USE_NEW_ALL_SLIPPERY
2873   /* initialize type of slippery elements */
2874   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2875   {
2876     if (!IS_CUSTOM_ELEMENT(i))
2877     {
2878       /* default: elements slip down either to the left or right randomly */
2879       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2880
2881       /* SP style elements prefer to slip down on the left side */
2882       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2883         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2884
2885       /* BD style elements prefer to slip down on the left side */
2886       if (game.emulation == EMU_BOULDERDASH)
2887         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2888     }
2889   }
2890 #endif
2891
2892   /* initialize explosion and ignition delay */
2893   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2894   {
2895     if (!IS_CUSTOM_ELEMENT(i))
2896     {
2897       int num_phase = 8;
2898       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2899                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2900                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
2901       int last_phase = (num_phase + 1) * delay;
2902       int half_phase = (num_phase / 2) * delay;
2903
2904       element_info[i].explosion_delay = last_phase - 1;
2905       element_info[i].ignition_delay = half_phase;
2906
2907       if (i == EL_BLACK_ORB)
2908         element_info[i].ignition_delay = 1;
2909     }
2910
2911 #if 0
2912     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
2913       element_info[i].explosion_delay = 1;
2914
2915     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
2916       element_info[i].ignition_delay = 1;
2917 #endif
2918   }
2919
2920   /* correct non-moving belts to start moving left */
2921   for (i = 0; i < NUM_BELTS; i++)
2922     if (game.belt_dir[i] == MV_NONE)
2923       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
2924
2925   /* check if any connected player was not found in playfield */
2926   for (i = 0; i < MAX_PLAYERS; i++)
2927   {
2928     struct PlayerInfo *player = &stored_player[i];
2929
2930     if (player->connected && !player->present)
2931     {
2932       for (j = 0; j < MAX_PLAYERS; j++)
2933       {
2934         struct PlayerInfo *some_player = &stored_player[j];
2935         int jx = some_player->jx, jy = some_player->jy;
2936
2937         /* assign first free player found that is present in the playfield */
2938         if (some_player->present && !some_player->connected)
2939         {
2940           player->present = TRUE;
2941           player->active = TRUE;
2942
2943           some_player->present = FALSE;
2944           some_player->active = FALSE;
2945
2946           player->artwork_element = some_player->artwork_element;
2947
2948           player->block_last_field       = some_player->block_last_field;
2949           player->block_delay_adjustment = some_player->block_delay_adjustment;
2950
2951           StorePlayer[jx][jy] = player->element_nr;
2952           player->jx = player->last_jx = jx;
2953           player->jy = player->last_jy = jy;
2954
2955           break;
2956         }
2957       }
2958     }
2959   }
2960
2961   if (tape.playing)
2962   {
2963     /* when playing a tape, eliminate all players who do not participate */
2964
2965     for (i = 0; i < MAX_PLAYERS; i++)
2966     {
2967       if (stored_player[i].active && !tape.player_participates[i])
2968       {
2969         struct PlayerInfo *player = &stored_player[i];
2970         int jx = player->jx, jy = player->jy;
2971
2972         player->active = FALSE;
2973         StorePlayer[jx][jy] = 0;
2974         Feld[jx][jy] = EL_EMPTY;
2975       }
2976     }
2977   }
2978   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
2979   {
2980     /* when in single player mode, eliminate all but the first active player */
2981
2982     for (i = 0; i < MAX_PLAYERS; i++)
2983     {
2984       if (stored_player[i].active)
2985       {
2986         for (j = i + 1; j < MAX_PLAYERS; j++)
2987         {
2988           if (stored_player[j].active)
2989           {
2990             struct PlayerInfo *player = &stored_player[j];
2991             int jx = player->jx, jy = player->jy;
2992
2993             player->active = FALSE;
2994             player->present = FALSE;
2995
2996             StorePlayer[jx][jy] = 0;
2997             Feld[jx][jy] = EL_EMPTY;
2998           }
2999         }
3000       }
3001     }
3002   }
3003
3004   /* when recording the game, store which players take part in the game */
3005   if (tape.recording)
3006   {
3007     for (i = 0; i < MAX_PLAYERS; i++)
3008       if (stored_player[i].active)
3009         tape.player_participates[i] = TRUE;
3010   }
3011
3012   if (options.debug)
3013   {
3014     for (i = 0; i < MAX_PLAYERS; i++)
3015     {
3016       struct PlayerInfo *player = &stored_player[i];
3017
3018       printf("Player %d: present == %d, connected == %d, active == %d.\n",
3019              i+1,
3020              player->present,
3021              player->connected,
3022              player->active);
3023       if (local_player == player)
3024         printf("Player  %d is local player.\n", i+1);
3025     }
3026   }
3027
3028   if (BorderElement == EL_EMPTY)
3029   {
3030     SBX_Left = 0;
3031     SBX_Right = lev_fieldx - SCR_FIELDX;
3032     SBY_Upper = 0;
3033     SBY_Lower = lev_fieldy - SCR_FIELDY;
3034   }
3035   else
3036   {
3037     SBX_Left = -1;
3038     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3039     SBY_Upper = -1;
3040     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3041   }
3042
3043   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3044     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3045
3046   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3047     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3048
3049   /* if local player not found, look for custom element that might create
3050      the player (make some assumptions about the right custom element) */
3051   if (!local_player->present)
3052   {
3053     int start_x = 0, start_y = 0;
3054     int found_rating = 0;
3055     int found_element = EL_UNDEFINED;
3056     int player_nr = local_player->index_nr;
3057
3058     SCAN_PLAYFIELD(x, y)
3059     {
3060       int element = Feld[x][y];
3061       int content;
3062       int xx, yy;
3063       boolean is_player;
3064
3065       if (level.use_start_element[player_nr] &&
3066           level.start_element[player_nr] == element &&
3067           found_rating < 4)
3068       {
3069         start_x = x;
3070         start_y = y;
3071
3072         found_rating = 4;
3073         found_element = element;
3074       }
3075
3076       if (!IS_CUSTOM_ELEMENT(element))
3077         continue;
3078
3079       if (CAN_CHANGE(element))
3080       {
3081         for (i = 0; i < element_info[element].num_change_pages; i++)
3082         {
3083           /* check for player created from custom element as single target */
3084           content = element_info[element].change_page[i].target_element;
3085           is_player = ELEM_IS_PLAYER(content);
3086
3087           if (is_player && (found_rating < 3 || element < found_element))
3088           {
3089             start_x = x;
3090             start_y = y;
3091
3092             found_rating = 3;
3093             found_element = element;
3094           }
3095         }
3096       }
3097
3098       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3099       {
3100         /* check for player created from custom element as explosion content */
3101         content = element_info[element].content.e[xx][yy];
3102         is_player = ELEM_IS_PLAYER(content);
3103
3104         if (is_player && (found_rating < 2 || element < found_element))
3105         {
3106           start_x = x + xx - 1;
3107           start_y = y + yy - 1;
3108
3109           found_rating = 2;
3110           found_element = element;
3111         }
3112
3113         if (!CAN_CHANGE(element))
3114           continue;
3115
3116         for (i = 0; i < element_info[element].num_change_pages; i++)
3117         {
3118           /* check for player created from custom element as extended target */
3119           content =
3120             element_info[element].change_page[i].target_content.e[xx][yy];
3121
3122           is_player = ELEM_IS_PLAYER(content);
3123
3124           if (is_player && (found_rating < 1 || element < found_element))
3125           {
3126             start_x = x + xx - 1;
3127             start_y = y + yy - 1;
3128
3129             found_rating = 1;
3130             found_element = element;
3131           }
3132         }
3133       }
3134     }
3135
3136     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3137                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3138                 start_x - MIDPOSX);
3139
3140     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3141                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3142                 start_y - MIDPOSY);
3143   }
3144   else
3145   {
3146     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3147                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3148                 local_player->jx - MIDPOSX);
3149
3150     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3151                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3152                 local_player->jy - MIDPOSY);
3153   }
3154
3155   StopAnimation();
3156
3157   if (!game.restart_level)
3158     CloseDoor(DOOR_CLOSE_1);
3159
3160 #if 1
3161   if (level_editor_test_game)
3162     FadeSkipNextFadeIn();
3163   else
3164     FadeSetStartItem();
3165 #else
3166   if (level_editor_test_game)
3167     fading = fading_none;
3168   else
3169     fading = menu.destination;
3170 #endif
3171
3172 #if 1
3173   FadeOut(REDRAW_FIELD);
3174 #else
3175   if (do_fading)
3176     FadeOut(REDRAW_FIELD);
3177 #endif
3178
3179   /* !!! FIX THIS (START) !!! */
3180   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3181   {
3182     InitGameEngine_EM();
3183
3184     /* blit playfield from scroll buffer to normal back buffer for fading in */
3185     BlitScreenToBitmap_EM(backbuffer);
3186   }
3187   else
3188   {
3189     DrawLevel();
3190     DrawAllPlayers();
3191
3192     /* after drawing the level, correct some elements */
3193     if (game.timegate_time_left == 0)
3194       CloseAllOpenTimegates();
3195
3196     /* blit playfield from scroll buffer to normal back buffer for fading in */
3197     if (setup.soft_scrolling)
3198       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3199
3200     redraw_mask |= REDRAW_FROM_BACKBUFFER;
3201   }
3202   /* !!! FIX THIS (END) !!! */
3203
3204 #if 1
3205   FadeIn(REDRAW_FIELD);
3206 #else
3207   if (do_fading)
3208     FadeIn(REDRAW_FIELD);
3209
3210   BackToFront();
3211 #endif
3212
3213   if (!game.restart_level)
3214   {
3215     /* copy default game door content to main double buffer */
3216     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3217                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3218   }
3219
3220   SetPanelBackground();
3221   SetDrawBackgroundMask(REDRAW_DOOR_1);
3222
3223   DrawGameDoorValues();
3224
3225   if (!game.restart_level)
3226   {
3227     UnmapGameButtons();
3228     UnmapTapeButtons();
3229     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3230     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3231     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3232     MapGameButtons();
3233     MapTapeButtons();
3234
3235     /* copy actual game door content to door double buffer for OpenDoor() */
3236     BlitBitmap(drawto, bitmap_db_door,
3237                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3238
3239     OpenDoor(DOOR_OPEN_ALL);
3240
3241     PlaySound(SND_GAME_STARTING);
3242
3243     if (setup.sound_music)
3244       PlayLevelMusic();
3245
3246     KeyboardAutoRepeatOffUnlessAutoplay();
3247
3248     if (options.debug)
3249     {
3250       for (i = 0; i < MAX_PLAYERS; i++)
3251         printf("Player %d %sactive.\n",
3252                i + 1, (stored_player[i].active ? "" : "not "));
3253     }
3254   }
3255
3256 #if 1
3257   UnmapAllGadgets();
3258
3259   MapGameButtons();
3260   MapTapeButtons();
3261 #endif
3262
3263   game.restart_level = FALSE;
3264 }
3265
3266 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3267 {
3268   /* this is used for non-R'n'D game engines to update certain engine values */
3269
3270   /* needed to determine if sounds are played within the visible screen area */
3271   scroll_x = actual_scroll_x;
3272   scroll_y = actual_scroll_y;
3273 }
3274
3275 void InitMovDir(int x, int y)
3276 {
3277   int i, element = Feld[x][y];
3278   static int xy[4][2] =
3279   {
3280     {  0, +1 },
3281     { +1,  0 },
3282     {  0, -1 },
3283     { -1,  0 }
3284   };
3285   static int direction[3][4] =
3286   {
3287     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
3288     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
3289     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
3290   };
3291
3292   switch (element)
3293   {
3294     case EL_BUG_RIGHT:
3295     case EL_BUG_UP:
3296     case EL_BUG_LEFT:
3297     case EL_BUG_DOWN:
3298       Feld[x][y] = EL_BUG;
3299       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3300       break;
3301
3302     case EL_SPACESHIP_RIGHT:
3303     case EL_SPACESHIP_UP:
3304     case EL_SPACESHIP_LEFT:
3305     case EL_SPACESHIP_DOWN:
3306       Feld[x][y] = EL_SPACESHIP;
3307       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3308       break;
3309
3310     case EL_BD_BUTTERFLY_RIGHT:
3311     case EL_BD_BUTTERFLY_UP:
3312     case EL_BD_BUTTERFLY_LEFT:
3313     case EL_BD_BUTTERFLY_DOWN:
3314       Feld[x][y] = EL_BD_BUTTERFLY;
3315       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3316       break;
3317
3318     case EL_BD_FIREFLY_RIGHT:
3319     case EL_BD_FIREFLY_UP:
3320     case EL_BD_FIREFLY_LEFT:
3321     case EL_BD_FIREFLY_DOWN:
3322       Feld[x][y] = EL_BD_FIREFLY;
3323       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3324       break;
3325
3326     case EL_PACMAN_RIGHT:
3327     case EL_PACMAN_UP:
3328     case EL_PACMAN_LEFT:
3329     case EL_PACMAN_DOWN:
3330       Feld[x][y] = EL_PACMAN;
3331       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3332       break;
3333
3334     case EL_YAMYAM_LEFT:
3335     case EL_YAMYAM_RIGHT:
3336     case EL_YAMYAM_UP:
3337     case EL_YAMYAM_DOWN:
3338       Feld[x][y] = EL_YAMYAM;
3339       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3340       break;
3341
3342     case EL_SP_SNIKSNAK:
3343       MovDir[x][y] = MV_UP;
3344       break;
3345
3346     case EL_SP_ELECTRON:
3347       MovDir[x][y] = MV_LEFT;
3348       break;
3349
3350     case EL_MOLE_LEFT:
3351     case EL_MOLE_RIGHT:
3352     case EL_MOLE_UP:
3353     case EL_MOLE_DOWN:
3354       Feld[x][y] = EL_MOLE;
3355       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3356       break;
3357
3358     default:
3359       if (IS_CUSTOM_ELEMENT(element))
3360       {
3361         struct ElementInfo *ei = &element_info[element];
3362         int move_direction_initial = ei->move_direction_initial;
3363         int move_pattern = ei->move_pattern;
3364
3365         if (move_direction_initial == MV_START_PREVIOUS)
3366         {
3367           if (MovDir[x][y] != MV_NONE)
3368             return;
3369
3370           move_direction_initial = MV_START_AUTOMATIC;
3371         }
3372
3373         if (move_direction_initial == MV_START_RANDOM)
3374           MovDir[x][y] = 1 << RND(4);
3375         else if (move_direction_initial & MV_ANY_DIRECTION)
3376           MovDir[x][y] = move_direction_initial;
3377         else if (move_pattern == MV_ALL_DIRECTIONS ||
3378                  move_pattern == MV_TURNING_LEFT ||
3379                  move_pattern == MV_TURNING_RIGHT ||
3380                  move_pattern == MV_TURNING_LEFT_RIGHT ||
3381                  move_pattern == MV_TURNING_RIGHT_LEFT ||
3382                  move_pattern == MV_TURNING_RANDOM)
3383           MovDir[x][y] = 1 << RND(4);
3384         else if (move_pattern == MV_HORIZONTAL)
3385           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3386         else if (move_pattern == MV_VERTICAL)
3387           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3388         else if (move_pattern & MV_ANY_DIRECTION)
3389           MovDir[x][y] = element_info[element].move_pattern;
3390         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
3391                  move_pattern == MV_ALONG_RIGHT_SIDE)
3392         {
3393           /* use random direction as default start direction */
3394           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3395             MovDir[x][y] = 1 << RND(4);
3396
3397           for (i = 0; i < NUM_DIRECTIONS; i++)
3398           {
3399             int x1 = x + xy[i][0];
3400             int y1 = y + xy[i][1];
3401
3402             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3403             {
3404               if (move_pattern == MV_ALONG_RIGHT_SIDE)
3405                 MovDir[x][y] = direction[0][i];
3406               else
3407                 MovDir[x][y] = direction[1][i];
3408
3409               break;
3410             }
3411           }
3412         }                
3413       }
3414       else
3415       {
3416         MovDir[x][y] = 1 << RND(4);
3417
3418         if (element != EL_BUG &&
3419             element != EL_SPACESHIP &&
3420             element != EL_BD_BUTTERFLY &&
3421             element != EL_BD_FIREFLY)
3422           break;
3423
3424         for (i = 0; i < NUM_DIRECTIONS; i++)
3425         {
3426           int x1 = x + xy[i][0];
3427           int y1 = y + xy[i][1];
3428
3429           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3430           {
3431             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3432             {
3433               MovDir[x][y] = direction[0][i];
3434               break;
3435             }
3436             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3437                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3438             {
3439               MovDir[x][y] = direction[1][i];
3440               break;
3441             }
3442           }
3443         }
3444       }
3445       break;
3446   }
3447
3448   GfxDir[x][y] = MovDir[x][y];
3449 }
3450
3451 void InitAmoebaNr(int x, int y)
3452 {
3453   int i;
3454   int group_nr = AmoebeNachbarNr(x, y);
3455
3456   if (group_nr == 0)
3457   {
3458     for (i = 1; i < MAX_NUM_AMOEBA; i++)
3459     {
3460       if (AmoebaCnt[i] == 0)
3461       {
3462         group_nr = i;
3463         break;
3464       }
3465     }
3466   }
3467
3468   AmoebaNr[x][y] = group_nr;
3469   AmoebaCnt[group_nr]++;
3470   AmoebaCnt2[group_nr]++;
3471 }
3472
3473 static void PlayerWins(struct PlayerInfo *player)
3474 {
3475   player->LevelSolved = TRUE;
3476   player->GameOver = TRUE;
3477
3478   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
3479                          level.native_em_level->lev->score : player->score);
3480 }
3481
3482 void GameWon()
3483 {
3484   static int time, time_final;
3485   static int score, score_final;
3486   static int game_over_delay_1 = 0;
3487   static int game_over_delay_2 = 0;
3488   int game_over_delay_value_1 = 50;
3489   int game_over_delay_value_2 = 50;
3490
3491   if (!local_player->LevelSolved_GameWon)
3492   {
3493     int i;
3494
3495     /* do not start end game actions before the player stops moving (to exit) */
3496     if (local_player->MovPos)
3497       return;
3498
3499     local_player->LevelSolved_GameWon = TRUE;
3500     local_player->LevelSolved_SaveTape = tape.recording;
3501     local_player->LevelSolved_SaveScore = !tape.playing;
3502
3503     if (tape.auto_play)         /* tape might already be stopped here */
3504       tape.auto_play_level_solved = TRUE;
3505
3506 #if 1
3507     TapeStop();
3508 #endif
3509
3510     game_over_delay_1 = game_over_delay_value_1;
3511     game_over_delay_2 = game_over_delay_value_2;
3512
3513     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3514     score = score_final = local_player->score_final;
3515
3516     if (TimeLeft > 0)
3517     {
3518       time_final = 0;
3519       score_final += TimeLeft * level.score[SC_TIME_BONUS];
3520     }
3521     else if (level.time == 0 && TimePlayed < 999)
3522     {
3523       time_final = 999;
3524       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3525     }
3526
3527     local_player->score_final = score_final;
3528
3529     if (level_editor_test_game)
3530     {
3531       time = time_final;
3532       score = score_final;
3533
3534       DrawGameValue_Time(time);
3535       DrawGameValue_Score(score);
3536     }
3537
3538     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3539     {
3540       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
3541       {
3542         /* close exit door after last player */
3543         if ((AllPlayersGone &&
3544              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3545               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3546               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3547             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3548             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3549         {
3550           int element = Feld[ExitX][ExitY];
3551
3552 #if 0
3553           if (element == EL_EM_EXIT_OPEN ||
3554               element == EL_EM_STEEL_EXIT_OPEN)
3555           {
3556             Bang(ExitX, ExitY);
3557           }
3558           else
3559 #endif
3560           {
3561             Feld[ExitX][ExitY] =
3562               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
3563                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
3564                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
3565                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
3566                EL_EM_STEEL_EXIT_CLOSING);
3567
3568             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3569           }
3570         }
3571
3572         /* player disappears */
3573         DrawLevelField(ExitX, ExitY);
3574       }
3575
3576       for (i = 0; i < MAX_PLAYERS; i++)
3577       {
3578         struct PlayerInfo *player = &stored_player[i];
3579
3580         if (player->present)
3581         {
3582           RemovePlayer(player);
3583
3584           /* player disappears */
3585           DrawLevelField(player->jx, player->jy);
3586         }
3587       }
3588     }
3589
3590     PlaySound(SND_GAME_WINNING);
3591   }
3592
3593   if (game_over_delay_1 > 0)
3594   {
3595     game_over_delay_1--;
3596
3597     return;
3598   }
3599
3600   if (time != time_final)
3601   {
3602     int time_to_go = ABS(time_final - time);
3603     int time_count_dir = (time < time_final ? +1 : -1);
3604     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
3605
3606     time  += time_count_steps * time_count_dir;
3607     score += time_count_steps * level.score[SC_TIME_BONUS];
3608
3609     DrawGameValue_Time(time);
3610     DrawGameValue_Score(score);
3611
3612     if (time == time_final)
3613       StopSound(SND_GAME_LEVELTIME_BONUS);
3614     else if (setup.sound_loops)
3615       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
3616     else
3617       PlaySound(SND_GAME_LEVELTIME_BONUS);
3618
3619     return;
3620   }
3621
3622   local_player->LevelSolved_PanelOff = TRUE;
3623
3624   if (game_over_delay_2 > 0)
3625   {
3626     game_over_delay_2--;
3627
3628     return;
3629   }
3630
3631 #if 1
3632   GameEnd();
3633 #endif
3634 }
3635
3636 void GameEnd()
3637 {
3638   int hi_pos;
3639   boolean raise_level = FALSE;
3640
3641   local_player->LevelSolved_GameEnd = TRUE;
3642
3643   CloseDoor(DOOR_CLOSE_1);
3644
3645   if (local_player->LevelSolved_SaveTape)
3646   {
3647 #if 0
3648     TapeStop();
3649 #endif
3650
3651 #if 1
3652     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
3653 #else
3654     SaveTape(tape.level_nr);            /* ask to save tape */
3655 #endif
3656   }
3657
3658   if (level_editor_test_game)
3659   {
3660     game_status = GAME_MODE_MAIN;
3661
3662 #if 1
3663     DrawAndFadeInMainMenu(REDRAW_FIELD);
3664 #else
3665     DrawMainMenu();
3666 #endif
3667
3668     return;
3669   }
3670
3671   if (!local_player->LevelSolved_SaveScore)
3672   {
3673 #if 1
3674     FadeOut(REDRAW_FIELD);
3675 #endif
3676
3677     game_status = GAME_MODE_MAIN;
3678
3679     DrawAndFadeInMainMenu(REDRAW_FIELD);
3680
3681     return;
3682   }
3683
3684   if (level_nr == leveldir_current->handicap_level)
3685   {
3686     leveldir_current->handicap_level++;
3687     SaveLevelSetup_SeriesInfo();
3688   }
3689
3690   if (level_nr < leveldir_current->last_level)
3691     raise_level = TRUE;                 /* advance to next level */
3692
3693   if ((hi_pos = NewHiScore()) >= 0) 
3694   {
3695     game_status = GAME_MODE_SCORES;
3696
3697     DrawHallOfFame(hi_pos);
3698
3699     if (raise_level)
3700     {
3701       level_nr++;
3702       TapeErase();
3703     }
3704   }
3705   else
3706   {
3707 #if 1
3708     FadeOut(REDRAW_FIELD);
3709 #endif
3710
3711     game_status = GAME_MODE_MAIN;
3712
3713     if (raise_level)
3714     {
3715       level_nr++;
3716       TapeErase();
3717     }
3718
3719     DrawAndFadeInMainMenu(REDRAW_FIELD);
3720   }
3721 }
3722
3723 int NewHiScore()
3724 {
3725   int k, l;
3726   int position = -1;
3727
3728   LoadScore(level_nr);
3729
3730   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3731       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
3732     return -1;
3733
3734   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
3735   {
3736     if (local_player->score_final > highscore[k].Score)
3737     {
3738       /* player has made it to the hall of fame */
3739
3740       if (k < MAX_SCORE_ENTRIES - 1)
3741       {
3742         int m = MAX_SCORE_ENTRIES - 1;
3743
3744 #ifdef ONE_PER_NAME
3745         for (l = k; l < MAX_SCORE_ENTRIES; l++)
3746           if (strEqual(setup.player_name, highscore[l].Name))
3747             m = l;
3748         if (m == k)     /* player's new highscore overwrites his old one */
3749           goto put_into_list;
3750 #endif
3751
3752         for (l = m; l > k; l--)
3753         {
3754           strcpy(highscore[l].Name, highscore[l - 1].Name);
3755           highscore[l].Score = highscore[l - 1].Score;
3756         }
3757       }
3758
3759 #ifdef ONE_PER_NAME
3760       put_into_list:
3761 #endif
3762       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3763       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3764       highscore[k].Score = local_player->score_final; 
3765       position = k;
3766       break;
3767     }
3768
3769 #ifdef ONE_PER_NAME
3770     else if (!strncmp(setup.player_name, highscore[k].Name,
3771                       MAX_PLAYER_NAME_LEN))
3772       break;    /* player already there with a higher score */
3773 #endif
3774
3775   }
3776
3777   if (position >= 0) 
3778     SaveScore(level_nr);
3779
3780   return position;
3781 }
3782
3783 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
3784 {
3785   int element = Feld[x][y];
3786   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3787   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3788   int horiz_move = (dx != 0);
3789   int sign = (horiz_move ? dx : dy);
3790   int step = sign * element_info[element].move_stepsize;
3791
3792   /* special values for move stepsize for spring and things on conveyor belt */
3793   if (horiz_move)
3794   {
3795     if (CAN_FALL(element) &&
3796         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3797       step = sign * MOVE_STEPSIZE_NORMAL / 2;
3798     else if (element == EL_SPRING)
3799       step = sign * MOVE_STEPSIZE_NORMAL * 2;
3800   }
3801
3802   return step;
3803 }
3804
3805 inline static int getElementMoveStepsize(int x, int y)
3806 {
3807   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
3808 }
3809
3810 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3811 {
3812   if (player->GfxAction != action || player->GfxDir != dir)
3813   {
3814 #if 0
3815     printf("Player frame reset! (%d => %d, %d => %d)\n",
3816            player->GfxAction, action, player->GfxDir, dir);
3817 #endif
3818
3819     player->GfxAction = action;
3820     player->GfxDir = dir;
3821     player->Frame = 0;
3822     player->StepFrame = 0;
3823   }
3824 }
3825
3826 #if USE_GFX_RESET_GFX_ANIMATION
3827 static void ResetGfxFrame(int x, int y, boolean redraw)
3828 {
3829   int element = Feld[x][y];
3830   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3831   int last_gfx_frame = GfxFrame[x][y];
3832
3833   if (graphic_info[graphic].anim_global_sync)
3834     GfxFrame[x][y] = FrameCounter;
3835   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3836     GfxFrame[x][y] = CustomValue[x][y];
3837   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3838     GfxFrame[x][y] = element_info[element].collect_score;
3839   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3840     GfxFrame[x][y] = ChangeDelay[x][y];
3841
3842   if (redraw && GfxFrame[x][y] != last_gfx_frame)
3843     DrawLevelGraphicAnimation(x, y, graphic);
3844 }
3845 #endif
3846
3847 static void ResetGfxAnimation(int x, int y)
3848 {
3849   GfxAction[x][y] = ACTION_DEFAULT;
3850   GfxDir[x][y] = MovDir[x][y];
3851   GfxFrame[x][y] = 0;
3852
3853 #if USE_GFX_RESET_GFX_ANIMATION
3854   ResetGfxFrame(x, y, FALSE);
3855 #endif
3856 }
3857
3858 static void ResetRandomAnimationValue(int x, int y)
3859 {
3860   GfxRandom[x][y] = INIT_GFX_RANDOM();
3861 }
3862
3863 void InitMovingField(int x, int y, int direction)
3864 {
3865   int element = Feld[x][y];
3866   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3867   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3868   int newx = x + dx;
3869   int newy = y + dy;
3870   boolean is_moving_before, is_moving_after;
3871 #if 0
3872   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3873 #endif
3874
3875   /* check if element was/is moving or being moved before/after mode change */
3876 #if 1
3877 #if 1
3878   is_moving_before = (WasJustMoving[x][y] != 0);
3879 #else
3880   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
3881   is_moving_before = WasJustMoving[x][y];
3882 #endif
3883 #else
3884   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3885 #endif
3886   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
3887
3888   /* reset animation only for moving elements which change direction of moving
3889      or which just started or stopped moving
3890      (else CEs with property "can move" / "not moving" are reset each frame) */
3891 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3892 #if 1
3893   if (is_moving_before != is_moving_after ||
3894       direction != MovDir[x][y])
3895     ResetGfxAnimation(x, y);
3896 #else
3897   if ((is_moving_before || is_moving_after) && !continues_moving)
3898     ResetGfxAnimation(x, y);
3899 #endif
3900 #else
3901   if (!continues_moving)
3902     ResetGfxAnimation(x, y);
3903 #endif
3904
3905   MovDir[x][y] = direction;
3906   GfxDir[x][y] = direction;
3907
3908 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3909   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3910                      direction == MV_DOWN && CAN_FALL(element) ?
3911                      ACTION_FALLING : ACTION_MOVING);
3912 #else
3913   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3914                      ACTION_FALLING : ACTION_MOVING);
3915 #endif
3916
3917   /* this is needed for CEs with property "can move" / "not moving" */
3918
3919   if (is_moving_after)
3920   {
3921     if (Feld[newx][newy] == EL_EMPTY)
3922       Feld[newx][newy] = EL_BLOCKED;
3923
3924     MovDir[newx][newy] = MovDir[x][y];
3925
3926 #if USE_NEW_CUSTOM_VALUE
3927     CustomValue[newx][newy] = CustomValue[x][y];
3928 #endif
3929
3930     GfxFrame[newx][newy] = GfxFrame[x][y];
3931     GfxRandom[newx][newy] = GfxRandom[x][y];
3932     GfxAction[newx][newy] = GfxAction[x][y];
3933     GfxDir[newx][newy] = GfxDir[x][y];
3934   }
3935 }
3936
3937 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3938 {
3939   int direction = MovDir[x][y];
3940   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3941   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
3942
3943   *goes_to_x = newx;
3944   *goes_to_y = newy;
3945 }
3946
3947 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3948 {
3949   int oldx = x, oldy = y;
3950   int direction = MovDir[x][y];
3951
3952   if (direction == MV_LEFT)
3953     oldx++;
3954   else if (direction == MV_RIGHT)
3955     oldx--;
3956   else if (direction == MV_UP)
3957     oldy++;
3958   else if (direction == MV_DOWN)
3959     oldy--;
3960
3961   *comes_from_x = oldx;
3962   *comes_from_y = oldy;
3963 }
3964
3965 int MovingOrBlocked2Element(int x, int y)
3966 {
3967   int element = Feld[x][y];
3968
3969   if (element == EL_BLOCKED)
3970   {
3971     int oldx, oldy;
3972
3973     Blocked2Moving(x, y, &oldx, &oldy);
3974     return Feld[oldx][oldy];
3975   }
3976   else
3977     return element;
3978 }
3979
3980 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3981 {
3982   /* like MovingOrBlocked2Element(), but if element is moving
3983      and (x,y) is the field the moving element is just leaving,
3984      return EL_BLOCKED instead of the element value */
3985   int element = Feld[x][y];
3986
3987   if (IS_MOVING(x, y))
3988   {
3989     if (element == EL_BLOCKED)
3990     {
3991       int oldx, oldy;
3992
3993       Blocked2Moving(x, y, &oldx, &oldy);
3994       return Feld[oldx][oldy];
3995     }
3996     else
3997       return EL_BLOCKED;
3998   }
3999   else
4000     return element;
4001 }
4002
4003 static void RemoveField(int x, int y)
4004 {
4005   Feld[x][y] = EL_EMPTY;
4006
4007   MovPos[x][y] = 0;
4008   MovDir[x][y] = 0;
4009   MovDelay[x][y] = 0;
4010
4011 #if USE_NEW_CUSTOM_VALUE
4012   CustomValue[x][y] = 0;
4013 #endif
4014
4015   AmoebaNr[x][y] = 0;
4016   ChangeDelay[x][y] = 0;
4017   ChangePage[x][y] = -1;
4018   Pushed[x][y] = FALSE;
4019
4020 #if 0
4021   ExplodeField[x][y] = EX_TYPE_NONE;
4022 #endif
4023
4024   GfxElement[x][y] = EL_UNDEFINED;
4025   GfxAction[x][y] = ACTION_DEFAULT;
4026   GfxDir[x][y] = MV_NONE;
4027 }
4028
4029 void RemoveMovingField(int x, int y)
4030 {
4031   int oldx = x, oldy = y, newx = x, newy = y;
4032   int element = Feld[x][y];
4033   int next_element = EL_UNDEFINED;
4034
4035   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4036     return;
4037
4038   if (IS_MOVING(x, y))
4039   {
4040     Moving2Blocked(x, y, &newx, &newy);
4041
4042     if (Feld[newx][newy] != EL_BLOCKED)
4043     {
4044       /* element is moving, but target field is not free (blocked), but
4045          already occupied by something different (example: acid pool);
4046          in this case, only remove the moving field, but not the target */
4047
4048       RemoveField(oldx, oldy);
4049
4050       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4051
4052       DrawLevelField(oldx, oldy);
4053
4054       return;
4055     }
4056   }
4057   else if (element == EL_BLOCKED)
4058   {
4059     Blocked2Moving(x, y, &oldx, &oldy);
4060     if (!IS_MOVING(oldx, oldy))
4061       return;
4062   }
4063
4064   if (element == EL_BLOCKED &&
4065       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4066        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4067        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4068        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4069        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4070        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4071     next_element = get_next_element(Feld[oldx][oldy]);
4072
4073   RemoveField(oldx, oldy);
4074   RemoveField(newx, newy);
4075
4076   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4077
4078   if (next_element != EL_UNDEFINED)
4079     Feld[oldx][oldy] = next_element;
4080
4081   DrawLevelField(oldx, oldy);
4082   DrawLevelField(newx, newy);
4083 }
4084
4085 void DrawDynamite(int x, int y)
4086 {
4087   int sx = SCREENX(x), sy = SCREENY(y);
4088   int graphic = el2img(Feld[x][y]);
4089   int frame;
4090
4091   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4092     return;
4093
4094   if (IS_WALKABLE_INSIDE(Back[x][y]))
4095     return;
4096
4097   if (Back[x][y])
4098     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4099   else if (Store[x][y])
4100     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4101
4102   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4103
4104   if (Back[x][y] || Store[x][y])
4105     DrawGraphicThruMask(sx, sy, graphic, frame);
4106   else
4107     DrawGraphic(sx, sy, graphic, frame);
4108 }
4109
4110 void CheckDynamite(int x, int y)
4111 {
4112   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4113   {
4114     MovDelay[x][y]--;
4115
4116     if (MovDelay[x][y] != 0)
4117     {
4118       DrawDynamite(x, y);
4119       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4120
4121       return;
4122     }
4123   }
4124
4125   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4126
4127   Bang(x, y);
4128 }
4129
4130 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4131 {
4132   boolean num_checked_players = 0;
4133   int i;
4134
4135   for (i = 0; i < MAX_PLAYERS; i++)
4136   {
4137     if (stored_player[i].active)
4138     {
4139       int sx = stored_player[i].jx;
4140       int sy = stored_player[i].jy;
4141
4142       if (num_checked_players == 0)
4143       {
4144         *sx1 = *sx2 = sx;
4145         *sy1 = *sy2 = sy;
4146       }
4147       else
4148       {
4149         *sx1 = MIN(*sx1, sx);
4150         *sy1 = MIN(*sy1, sy);
4151         *sx2 = MAX(*sx2, sx);
4152         *sy2 = MAX(*sy2, sy);
4153       }
4154
4155       num_checked_players++;
4156     }
4157   }
4158 }
4159
4160 static boolean checkIfAllPlayersFitToScreen_RND()
4161 {
4162   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4163
4164   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4165
4166   return (sx2 - sx1 < SCR_FIELDX &&
4167           sy2 - sy1 < SCR_FIELDY);
4168 }
4169
4170 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4171 {
4172   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4173
4174   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4175
4176   *sx = (sx1 + sx2) / 2;
4177   *sy = (sy1 + sy2) / 2;
4178 }
4179
4180 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4181                         boolean center_screen, boolean quick_relocation)
4182 {
4183   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4184   boolean no_delay = (tape.warp_forward);
4185   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4186   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4187
4188   if (quick_relocation)
4189   {
4190     int offset = (setup.scroll_delay ? 3 : 0);
4191
4192     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4193     {
4194       if (center_screen)
4195       {
4196         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4197                     x > SBX_Right + MIDPOSX ? SBX_Right :
4198                     x - MIDPOSX);
4199
4200         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4201                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4202                     y - MIDPOSY);
4203       }
4204       else
4205       {
4206         /* quick relocation (without scrolling), but do not center screen */
4207
4208         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4209                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4210                                old_x - MIDPOSX);
4211
4212         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4213                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4214                                old_y - MIDPOSY);
4215
4216         int offset_x = x + (scroll_x - center_scroll_x);
4217         int offset_y = y + (scroll_y - center_scroll_y);
4218
4219         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4220                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4221                     offset_x - MIDPOSX);
4222
4223         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4224                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4225                     offset_y - MIDPOSY);
4226       }
4227     }
4228     else
4229     {
4230       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
4231           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4232         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4233
4234       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
4235           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4236         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4237
4238       /* don't scroll over playfield boundaries */
4239       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4240         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4241
4242       /* don't scroll over playfield boundaries */
4243       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4244         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4245     }
4246
4247     RedrawPlayfield(TRUE, 0,0,0,0);
4248   }
4249   else
4250   {
4251     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4252                      x > SBX_Right + MIDPOSX ? SBX_Right :
4253                      x - MIDPOSX);
4254
4255     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4256                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
4257                      y - MIDPOSY);
4258
4259     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
4260
4261     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4262     {
4263       int dx = 0, dy = 0;
4264       int fx = FX, fy = FY;
4265
4266       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4267       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4268
4269       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
4270         break;
4271
4272       scroll_x -= dx;
4273       scroll_y -= dy;
4274
4275       fx += dx * TILEX / 2;
4276       fy += dy * TILEY / 2;
4277
4278       ScrollLevel(dx, dy);
4279       DrawAllPlayers();
4280
4281       /* scroll in two steps of half tile size to make things smoother */
4282       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4283       FlushDisplay();
4284       Delay(wait_delay_value);
4285
4286       /* scroll second step to align at full tile size */
4287       BackToFront();
4288       Delay(wait_delay_value);
4289     }
4290
4291     DrawAllPlayers();
4292     BackToFront();
4293     Delay(wait_delay_value);
4294   }
4295 }
4296
4297 void RelocatePlayer(int jx, int jy, int el_player_raw)
4298 {
4299   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4300   int player_nr = GET_PLAYER_NR(el_player);
4301   struct PlayerInfo *player = &stored_player[player_nr];
4302   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4303   boolean no_delay = (tape.warp_forward);
4304   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4305   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4306   int old_jx = player->jx;
4307   int old_jy = player->jy;
4308   int old_element = Feld[old_jx][old_jy];
4309   int element = Feld[jx][jy];
4310   boolean player_relocated = (old_jx != jx || old_jy != jy);
4311
4312   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4313   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
4314   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4315   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
4316   int leave_side_horiz = move_dir_horiz;
4317   int leave_side_vert  = move_dir_vert;
4318   int enter_side = enter_side_horiz | enter_side_vert;
4319   int leave_side = leave_side_horiz | leave_side_vert;
4320
4321   if (player->GameOver)         /* do not reanimate dead player */
4322     return;
4323
4324   if (!player_relocated)        /* no need to relocate the player */
4325     return;
4326
4327   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
4328   {
4329     RemoveField(jx, jy);        /* temporarily remove newly placed player */
4330     DrawLevelField(jx, jy);
4331   }
4332
4333   if (player->present)
4334   {
4335     while (player->MovPos)
4336     {
4337       ScrollPlayer(player, SCROLL_GO_ON);
4338       ScrollScreen(NULL, SCROLL_GO_ON);
4339
4340       AdvanceFrameAndPlayerCounters(player->index_nr);
4341
4342       DrawPlayer(player);
4343
4344       BackToFront();
4345       Delay(wait_delay_value);
4346     }
4347
4348     DrawPlayer(player);         /* needed here only to cleanup last field */
4349     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
4350
4351     player->is_moving = FALSE;
4352   }
4353
4354   if (IS_CUSTOM_ELEMENT(old_element))
4355     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4356                                CE_LEFT_BY_PLAYER,
4357                                player->index_bit, leave_side);
4358
4359   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4360                                       CE_PLAYER_LEAVES_X,
4361                                       player->index_bit, leave_side);
4362
4363   Feld[jx][jy] = el_player;
4364   InitPlayerField(jx, jy, el_player, TRUE);
4365
4366   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4367   {
4368     Feld[jx][jy] = element;
4369     InitField(jx, jy, FALSE);
4370   }
4371
4372   /* only visually relocate centered player */
4373   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4374                      FALSE, level.instant_relocation);
4375
4376   TestIfPlayerTouchesBadThing(jx, jy);
4377   TestIfPlayerTouchesCustomElement(jx, jy);
4378
4379   if (IS_CUSTOM_ELEMENT(element))
4380     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4381                                player->index_bit, enter_side);
4382
4383   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4384                                       player->index_bit, enter_side);
4385 }
4386
4387 void Explode(int ex, int ey, int phase, int mode)
4388 {
4389   int x, y;
4390   int last_phase;
4391   int border_element;
4392
4393   /* !!! eliminate this variable !!! */
4394   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4395
4396   if (game.explosions_delayed)
4397   {
4398     ExplodeField[ex][ey] = mode;
4399     return;
4400   }
4401
4402   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
4403   {
4404     int center_element = Feld[ex][ey];
4405     int artwork_element, explosion_element;     /* set these values later */
4406
4407 #if 0
4408     /* --- This is only really needed (and now handled) in "Impact()". --- */
4409     /* do not explode moving elements that left the explode field in time */
4410     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4411         center_element == EL_EMPTY &&
4412         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4413       return;
4414 #endif
4415
4416 #if 0
4417     /* !!! at this place, the center element may be EL_BLOCKED !!! */
4418     if (mode == EX_TYPE_NORMAL ||
4419         mode == EX_TYPE_CENTER ||
4420         mode == EX_TYPE_CROSS)
4421       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4422 #endif
4423
4424     /* remove things displayed in background while burning dynamite */
4425     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4426       Back[ex][ey] = 0;
4427
4428     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4429     {
4430       /* put moving element to center field (and let it explode there) */
4431       center_element = MovingOrBlocked2Element(ex, ey);
4432       RemoveMovingField(ex, ey);
4433       Feld[ex][ey] = center_element;
4434     }
4435
4436     /* now "center_element" is finally determined -- set related values now */
4437     artwork_element = center_element;           /* for custom player artwork */
4438     explosion_element = center_element;         /* for custom player artwork */
4439
4440     if (IS_PLAYER(ex, ey))
4441     {
4442       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4443
4444       artwork_element = stored_player[player_nr].artwork_element;
4445
4446       if (level.use_explosion_element[player_nr])
4447       {
4448         explosion_element = level.explosion_element[player_nr];
4449         artwork_element = explosion_element;
4450       }
4451     }
4452
4453 #if 1
4454     if (mode == EX_TYPE_NORMAL ||
4455         mode == EX_TYPE_CENTER ||
4456         mode == EX_TYPE_CROSS)
4457       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4458 #endif
4459
4460     last_phase = element_info[explosion_element].explosion_delay + 1;
4461
4462     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4463     {
4464       int xx = x - ex + 1;
4465       int yy = y - ey + 1;
4466       int element;
4467
4468       if (!IN_LEV_FIELD(x, y) ||
4469           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4470           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
4471         continue;
4472
4473       element = Feld[x][y];
4474
4475       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4476       {
4477         element = MovingOrBlocked2Element(x, y);
4478
4479         if (!IS_EXPLOSION_PROOF(element))
4480           RemoveMovingField(x, y);
4481       }
4482
4483       /* indestructible elements can only explode in center (but not flames) */
4484       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4485                                            mode == EX_TYPE_BORDER)) ||
4486           element == EL_FLAMES)
4487         continue;
4488
4489       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4490          behaviour, for example when touching a yamyam that explodes to rocks
4491          with active deadly shield, a rock is created under the player !!! */
4492       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4493 #if 0
4494       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4495           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
4496            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
4497 #else
4498       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
4499 #endif
4500       {
4501         if (IS_ACTIVE_BOMB(element))
4502         {
4503           /* re-activate things under the bomb like gate or penguin */
4504           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
4505           Back[x][y] = 0;
4506         }
4507
4508         continue;
4509       }
4510
4511       /* save walkable background elements while explosion on same tile */
4512       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
4513           (x != ex || y != ey || mode == EX_TYPE_BORDER))
4514         Back[x][y] = element;
4515
4516       /* ignite explodable elements reached by other explosion */
4517       if (element == EL_EXPLOSION)
4518         element = Store2[x][y];
4519
4520       if (AmoebaNr[x][y] &&
4521           (element == EL_AMOEBA_FULL ||
4522            element == EL_BD_AMOEBA ||
4523            element == EL_AMOEBA_GROWING))
4524       {
4525         AmoebaCnt[AmoebaNr[x][y]]--;
4526         AmoebaCnt2[AmoebaNr[x][y]]--;
4527       }
4528
4529       RemoveField(x, y);
4530
4531       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
4532       {
4533         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
4534
4535         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
4536
4537         if (PLAYERINFO(ex, ey)->use_murphy)
4538           Store[x][y] = EL_EMPTY;
4539       }
4540
4541       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
4542          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
4543       else if (ELEM_IS_PLAYER(center_element))
4544         Store[x][y] = EL_EMPTY;
4545       else if (center_element == EL_YAMYAM)
4546         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4547       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4548         Store[x][y] = element_info[center_element].content.e[xx][yy];
4549 #if 1
4550       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4551          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4552          otherwise) -- FIX THIS !!! */
4553       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4554         Store[x][y] = element_info[element].content.e[1][1];
4555 #else
4556       else if (!CAN_EXPLODE(element))
4557         Store[x][y] = element_info[element].content.e[1][1];
4558 #endif
4559       else
4560         Store[x][y] = EL_EMPTY;
4561
4562       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4563           center_element == EL_AMOEBA_TO_DIAMOND)
4564         Store2[x][y] = element;
4565
4566       Feld[x][y] = EL_EXPLOSION;
4567       GfxElement[x][y] = artwork_element;
4568
4569       ExplodePhase[x][y] = 1;
4570       ExplodeDelay[x][y] = last_phase;
4571
4572       Stop[x][y] = TRUE;
4573     }
4574
4575     if (center_element == EL_YAMYAM)
4576       game.yamyam_content_nr =
4577         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4578
4579     return;
4580   }
4581
4582   if (Stop[ex][ey])
4583     return;
4584
4585   x = ex;
4586   y = ey;
4587
4588   if (phase == 1)
4589     GfxFrame[x][y] = 0;         /* restart explosion animation */
4590
4591   last_phase = ExplodeDelay[x][y];
4592
4593   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4594
4595 #ifdef DEBUG
4596
4597   /* activate this even in non-DEBUG version until cause for crash in
4598      getGraphicAnimationFrame() (see below) is found and eliminated */
4599
4600 #endif
4601 #if 1
4602
4603 #if 1
4604   /* this can happen if the player leaves an explosion just in time */
4605   if (GfxElement[x][y] == EL_UNDEFINED)
4606     GfxElement[x][y] = EL_EMPTY;
4607 #else
4608   if (GfxElement[x][y] == EL_UNDEFINED)
4609   {
4610     printf("\n\n");
4611     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4612     printf("Explode(): This should never happen!\n");
4613     printf("\n\n");
4614
4615     GfxElement[x][y] = EL_EMPTY;
4616   }
4617 #endif
4618
4619 #endif
4620
4621   border_element = Store2[x][y];
4622   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4623     border_element = StorePlayer[x][y];
4624
4625   if (phase == element_info[border_element].ignition_delay ||
4626       phase == last_phase)
4627   {
4628     boolean border_explosion = FALSE;
4629
4630     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4631         !PLAYER_EXPLOSION_PROTECTED(x, y))
4632     {
4633       KillPlayerUnlessExplosionProtected(x, y);
4634       border_explosion = TRUE;
4635     }
4636     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4637     {
4638       Feld[x][y] = Store2[x][y];
4639       Store2[x][y] = 0;
4640       Bang(x, y);
4641       border_explosion = TRUE;
4642     }
4643     else if (border_element == EL_AMOEBA_TO_DIAMOND)
4644     {
4645       AmoebeUmwandeln(x, y);
4646       Store2[x][y] = 0;
4647       border_explosion = TRUE;
4648     }
4649
4650     /* if an element just explodes due to another explosion (chain-reaction),
4651        do not immediately end the new explosion when it was the last frame of
4652        the explosion (as it would be done in the following "if"-statement!) */
4653     if (border_explosion && phase == last_phase)
4654       return;
4655   }
4656
4657   if (phase == last_phase)
4658   {
4659     int element;
4660
4661     element = Feld[x][y] = Store[x][y];
4662     Store[x][y] = Store2[x][y] = 0;
4663     GfxElement[x][y] = EL_UNDEFINED;
4664
4665     /* player can escape from explosions and might therefore be still alive */
4666     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4667         element <= EL_PLAYER_IS_EXPLODING_4)
4668     {
4669       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4670       int explosion_element = EL_PLAYER_1 + player_nr;
4671       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4672       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4673
4674       if (level.use_explosion_element[player_nr])
4675         explosion_element = level.explosion_element[player_nr];
4676
4677       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4678                     element_info[explosion_element].content.e[xx][yy]);
4679     }
4680
4681     /* restore probably existing indestructible background element */
4682     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4683       element = Feld[x][y] = Back[x][y];
4684     Back[x][y] = 0;
4685
4686     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4687     GfxDir[x][y] = MV_NONE;
4688     ChangeDelay[x][y] = 0;
4689     ChangePage[x][y] = -1;
4690
4691 #if USE_NEW_CUSTOM_VALUE
4692     CustomValue[x][y] = 0;
4693 #endif
4694
4695     InitField_WithBug2(x, y, FALSE);
4696
4697     DrawLevelField(x, y);
4698
4699     TestIfElementTouchesCustomElement(x, y);
4700
4701     if (GFX_CRUMBLED(element))
4702       DrawLevelFieldCrumbledSandNeighbours(x, y);
4703
4704     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4705       StorePlayer[x][y] = 0;
4706
4707     if (ELEM_IS_PLAYER(element))
4708       RelocatePlayer(x, y, element);
4709   }
4710   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4711   {
4712     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4713     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4714
4715     if (phase == delay)
4716       DrawLevelFieldCrumbledSand(x, y);
4717
4718     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4719     {
4720       DrawLevelElement(x, y, Back[x][y]);
4721       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4722     }
4723     else if (IS_WALKABLE_UNDER(Back[x][y]))
4724     {
4725       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4726       DrawLevelElementThruMask(x, y, Back[x][y]);
4727     }
4728     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4729       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4730   }
4731 }
4732
4733 void DynaExplode(int ex, int ey)
4734 {
4735   int i, j;
4736   int dynabomb_element = Feld[ex][ey];
4737   int dynabomb_size = 1;
4738   boolean dynabomb_xl = FALSE;
4739   struct PlayerInfo *player;
4740   static int xy[4][2] =
4741   {
4742     { 0, -1 },
4743     { -1, 0 },
4744     { +1, 0 },
4745     { 0, +1 }
4746   };
4747
4748   if (IS_ACTIVE_BOMB(dynabomb_element))
4749   {
4750     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4751     dynabomb_size = player->dynabomb_size;
4752     dynabomb_xl = player->dynabomb_xl;
4753     player->dynabombs_left++;
4754   }
4755
4756   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4757
4758   for (i = 0; i < NUM_DIRECTIONS; i++)
4759   {
4760     for (j = 1; j <= dynabomb_size; j++)
4761     {
4762       int x = ex + j * xy[i][0];
4763       int y = ey + j * xy[i][1];
4764       int element;
4765
4766       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4767         break;
4768
4769       element = Feld[x][y];
4770
4771       /* do not restart explosions of fields with active bombs */
4772       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4773         continue;
4774
4775       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4776
4777       if (element != EL_EMPTY && element != EL_EXPLOSION &&
4778           !IS_DIGGABLE(element) && !dynabomb_xl)
4779         break;
4780     }
4781   }
4782 }
4783
4784 void Bang(int x, int y)
4785 {
4786   int element = MovingOrBlocked2Element(x, y);
4787   int explosion_type = EX_TYPE_NORMAL;
4788
4789   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4790   {
4791     struct PlayerInfo *player = PLAYERINFO(x, y);
4792
4793     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4794                             player->element_nr);
4795
4796     if (level.use_explosion_element[player->index_nr])
4797     {
4798       int explosion_element = level.explosion_element[player->index_nr];
4799
4800       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4801         explosion_type = EX_TYPE_CROSS;
4802       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4803         explosion_type = EX_TYPE_CENTER;
4804     }
4805   }
4806
4807   switch (element)
4808   {
4809     case EL_BUG:
4810     case EL_SPACESHIP:
4811     case EL_BD_BUTTERFLY:
4812     case EL_BD_FIREFLY:
4813     case EL_YAMYAM:
4814     case EL_DARK_YAMYAM:
4815     case EL_ROBOT:
4816     case EL_PACMAN:
4817     case EL_MOLE:
4818       RaiseScoreElement(element);
4819       break;
4820
4821     case EL_DYNABOMB_PLAYER_1_ACTIVE:
4822     case EL_DYNABOMB_PLAYER_2_ACTIVE:
4823     case EL_DYNABOMB_PLAYER_3_ACTIVE:
4824     case EL_DYNABOMB_PLAYER_4_ACTIVE:
4825     case EL_DYNABOMB_INCREASE_NUMBER:
4826     case EL_DYNABOMB_INCREASE_SIZE:
4827     case EL_DYNABOMB_INCREASE_POWER:
4828       explosion_type = EX_TYPE_DYNA;
4829       break;
4830
4831     case EL_DC_LANDMINE:
4832 #if 0
4833     case EL_EM_EXIT_OPEN:
4834     case EL_EM_STEEL_EXIT_OPEN:
4835 #endif
4836       explosion_type = EX_TYPE_CENTER;
4837       break;
4838
4839     case EL_PENGUIN:
4840     case EL_LAMP:
4841     case EL_LAMP_ACTIVE:
4842     case EL_AMOEBA_TO_DIAMOND:
4843       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
4844         explosion_type = EX_TYPE_CENTER;
4845       break;
4846
4847     default:
4848       if (element_info[element].explosion_type == EXPLODES_CROSS)
4849         explosion_type = EX_TYPE_CROSS;
4850       else if (element_info[element].explosion_type == EXPLODES_1X1)
4851         explosion_type = EX_TYPE_CENTER;
4852       break;
4853   }
4854
4855   if (explosion_type == EX_TYPE_DYNA)
4856     DynaExplode(x, y);
4857   else
4858     Explode(x, y, EX_PHASE_START, explosion_type);
4859
4860   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4861 }
4862
4863 void SplashAcid(int x, int y)
4864 {
4865   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4866       (!IN_LEV_FIELD(x - 1, y - 2) ||
4867        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4868     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4869
4870   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4871       (!IN_LEV_FIELD(x + 1, y - 2) ||
4872        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4873     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4874
4875   PlayLevelSound(x, y, SND_ACID_SPLASHING);
4876 }
4877
4878 static void InitBeltMovement()
4879 {
4880   static int belt_base_element[4] =
4881   {
4882     EL_CONVEYOR_BELT_1_LEFT,
4883     EL_CONVEYOR_BELT_2_LEFT,
4884     EL_CONVEYOR_BELT_3_LEFT,
4885     EL_CONVEYOR_BELT_4_LEFT
4886   };
4887   static int belt_base_active_element[4] =
4888   {
4889     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4890     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4891     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4892     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4893   };
4894
4895   int x, y, i, j;
4896
4897   /* set frame order for belt animation graphic according to belt direction */
4898   for (i = 0; i < NUM_BELTS; i++)
4899   {
4900     int belt_nr = i;
4901
4902     for (j = 0; j < NUM_BELT_PARTS; j++)
4903     {
4904       int element = belt_base_active_element[belt_nr] + j;
4905       int graphic = el2img(element);
4906
4907       if (game.belt_dir[i] == MV_LEFT)
4908         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4909       else
4910         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4911     }
4912   }
4913
4914   SCAN_PLAYFIELD(x, y)
4915   {
4916     int element = Feld[x][y];
4917
4918     for (i = 0; i < NUM_BELTS; i++)
4919     {
4920       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4921       {
4922         int e_belt_nr = getBeltNrFromBeltElement(element);
4923         int belt_nr = i;
4924
4925         if (e_belt_nr == belt_nr)
4926         {
4927           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4928
4929           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4930         }
4931       }
4932     }
4933   }
4934 }
4935
4936 static void ToggleBeltSwitch(int x, int y)
4937 {
4938   static int belt_base_element[4] =
4939   {
4940     EL_CONVEYOR_BELT_1_LEFT,
4941     EL_CONVEYOR_BELT_2_LEFT,
4942     EL_CONVEYOR_BELT_3_LEFT,
4943     EL_CONVEYOR_BELT_4_LEFT
4944   };
4945   static int belt_base_active_element[4] =
4946   {
4947     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4948     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4949     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4950     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4951   };
4952   static int belt_base_switch_element[4] =
4953   {
4954     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4955     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4956     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4957     EL_CONVEYOR_BELT_4_SWITCH_LEFT
4958   };
4959   static int belt_move_dir[4] =
4960   {
4961     MV_LEFT,
4962     MV_NONE,
4963     MV_RIGHT,
4964     MV_NONE,
4965   };
4966
4967   int element = Feld[x][y];
4968   int belt_nr = getBeltNrFromBeltSwitchElement(element);
4969   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4970   int belt_dir = belt_move_dir[belt_dir_nr];
4971   int xx, yy, i;
4972
4973   if (!IS_BELT_SWITCH(element))
4974     return;
4975
4976   game.belt_dir_nr[belt_nr] = belt_dir_nr;
4977   game.belt_dir[belt_nr] = belt_dir;
4978
4979   if (belt_dir_nr == 3)
4980     belt_dir_nr = 1;
4981
4982   /* set frame order for belt animation graphic according to belt direction */
4983   for (i = 0; i < NUM_BELT_PARTS; i++)
4984   {
4985     int element = belt_base_active_element[belt_nr] + i;
4986     int graphic = el2img(element);
4987
4988     if (belt_dir == MV_LEFT)
4989       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4990     else
4991       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4992   }
4993
4994   SCAN_PLAYFIELD(xx, yy)
4995   {
4996     int element = Feld[xx][yy];
4997
4998     if (IS_BELT_SWITCH(element))
4999     {
5000       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5001
5002       if (e_belt_nr == belt_nr)
5003       {
5004         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5005         DrawLevelField(xx, yy);
5006       }
5007     }
5008     else if (IS_BELT(element) && belt_dir != MV_NONE)
5009     {
5010       int e_belt_nr = getBeltNrFromBeltElement(element);
5011
5012       if (e_belt_nr == belt_nr)
5013       {
5014         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5015
5016         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5017         DrawLevelField(xx, yy);
5018       }
5019     }
5020     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5021     {
5022       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5023
5024       if (e_belt_nr == belt_nr)
5025       {
5026         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5027
5028         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5029         DrawLevelField(xx, yy);
5030       }
5031     }
5032   }
5033 }
5034
5035 static void ToggleSwitchgateSwitch(int x, int y)
5036 {
5037   int xx, yy;
5038
5039   game.switchgate_pos = !game.switchgate_pos;
5040
5041   SCAN_PLAYFIELD(xx, yy)
5042   {
5043     int element = Feld[xx][yy];
5044
5045 #if !USE_BOTH_SWITCHGATE_SWITCHES
5046     if (element == EL_SWITCHGATE_SWITCH_UP ||
5047         element == EL_SWITCHGATE_SWITCH_DOWN)
5048     {
5049       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5050       DrawLevelField(xx, yy);
5051     }
5052     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5053              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5054     {
5055       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5056       DrawLevelField(xx, yy);
5057     }
5058 #else
5059     if (element == EL_SWITCHGATE_SWITCH_UP)
5060     {
5061       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5062       DrawLevelField(xx, yy);
5063     }
5064     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5065     {
5066       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5067       DrawLevelField(xx, yy);
5068     }
5069     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5070     {
5071       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5072       DrawLevelField(xx, yy);
5073     }
5074     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5075     {
5076       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5077       DrawLevelField(xx, yy);
5078     }
5079 #endif
5080     else if (element == EL_SWITCHGATE_OPEN ||
5081              element == EL_SWITCHGATE_OPENING)
5082     {
5083       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5084
5085       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5086     }
5087     else if (element == EL_SWITCHGATE_CLOSED ||
5088              element == EL_SWITCHGATE_CLOSING)
5089     {
5090       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5091
5092       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5093     }
5094   }
5095 }
5096
5097 static int getInvisibleActiveFromInvisibleElement(int element)
5098 {
5099   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5100           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5101           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5102           element);
5103 }
5104
5105 static int getInvisibleFromInvisibleActiveElement(int element)
5106 {
5107   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5108           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5109           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5110           element);
5111 }
5112
5113 static void RedrawAllLightSwitchesAndInvisibleElements()
5114 {
5115   int x, y;
5116
5117   SCAN_PLAYFIELD(x, y)
5118   {
5119     int element = Feld[x][y];
5120
5121     if (element == EL_LIGHT_SWITCH &&
5122         game.light_time_left > 0)
5123     {
5124       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5125       DrawLevelField(x, y);
5126     }
5127     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5128              game.light_time_left == 0)
5129     {
5130       Feld[x][y] = EL_LIGHT_SWITCH;
5131       DrawLevelField(x, y);
5132     }
5133     else if (element == EL_EMC_DRIPPER &&
5134              game.light_time_left > 0)
5135     {
5136       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5137       DrawLevelField(x, y);
5138     }
5139     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5140              game.light_time_left == 0)
5141     {
5142       Feld[x][y] = EL_EMC_DRIPPER;
5143       DrawLevelField(x, y);
5144     }
5145     else if (element == EL_INVISIBLE_STEELWALL ||
5146              element == EL_INVISIBLE_WALL ||
5147              element == EL_INVISIBLE_SAND)
5148     {
5149       if (game.light_time_left > 0)
5150         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5151
5152       DrawLevelField(x, y);
5153
5154       /* uncrumble neighbour fields, if needed */
5155       if (element == EL_INVISIBLE_SAND)
5156         DrawLevelFieldCrumbledSandNeighbours(x, y);
5157     }
5158     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5159              element == EL_INVISIBLE_WALL_ACTIVE ||
5160              element == EL_INVISIBLE_SAND_ACTIVE)
5161     {
5162       if (game.light_time_left == 0)
5163         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5164
5165       DrawLevelField(x, y);
5166
5167       /* re-crumble neighbour fields, if needed */
5168       if (element == EL_INVISIBLE_SAND)
5169         DrawLevelFieldCrumbledSandNeighbours(x, y);
5170     }
5171   }
5172 }
5173
5174 static void RedrawAllInvisibleElementsForLenses()
5175 {
5176   int x, y;
5177
5178   SCAN_PLAYFIELD(x, y)
5179   {
5180     int element = Feld[x][y];
5181
5182     if (element == EL_EMC_DRIPPER &&
5183         game.lenses_time_left > 0)
5184     {
5185       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5186       DrawLevelField(x, y);
5187     }
5188     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5189              game.lenses_time_left == 0)
5190     {
5191       Feld[x][y] = EL_EMC_DRIPPER;
5192       DrawLevelField(x, y);
5193     }
5194     else if (element == EL_INVISIBLE_STEELWALL ||
5195              element == EL_INVISIBLE_WALL ||
5196              element == EL_INVISIBLE_SAND)
5197     {
5198       if (game.lenses_time_left > 0)
5199         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5200
5201       DrawLevelField(x, y);
5202
5203       /* uncrumble neighbour fields, if needed */
5204       if (element == EL_INVISIBLE_SAND)
5205         DrawLevelFieldCrumbledSandNeighbours(x, y);
5206     }
5207     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5208              element == EL_INVISIBLE_WALL_ACTIVE ||
5209              element == EL_INVISIBLE_SAND_ACTIVE)
5210     {
5211       if (game.lenses_time_left == 0)
5212         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5213
5214       DrawLevelField(x, y);
5215
5216       /* re-crumble neighbour fields, if needed */
5217       if (element == EL_INVISIBLE_SAND)
5218         DrawLevelFieldCrumbledSandNeighbours(x, y);
5219     }
5220   }
5221 }
5222
5223 static void RedrawAllInvisibleElementsForMagnifier()
5224 {
5225   int x, y;
5226
5227   SCAN_PLAYFIELD(x, y)
5228   {
5229     int element = Feld[x][y];
5230
5231     if (element == EL_EMC_FAKE_GRASS &&
5232         game.magnify_time_left > 0)
5233     {
5234       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5235       DrawLevelField(x, y);
5236     }
5237     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5238              game.magnify_time_left == 0)
5239     {
5240       Feld[x][y] = EL_EMC_FAKE_GRASS;
5241       DrawLevelField(x, y);
5242     }
5243     else if (IS_GATE_GRAY(element) &&
5244              game.magnify_time_left > 0)
5245     {
5246       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5247                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5248                     IS_EM_GATE_GRAY(element) ?
5249                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5250                     IS_EMC_GATE_GRAY(element) ?
5251                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5252                     element);
5253       DrawLevelField(x, y);
5254     }
5255     else if (IS_GATE_GRAY_ACTIVE(element) &&
5256              game.magnify_time_left == 0)
5257     {
5258       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5259                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5260                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5261                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5262                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5263                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5264                     element);
5265       DrawLevelField(x, y);
5266     }
5267   }
5268 }
5269
5270 static void ToggleLightSwitch(int x, int y)
5271 {
5272   int element = Feld[x][y];
5273
5274   game.light_time_left =
5275     (element == EL_LIGHT_SWITCH ?
5276      level.time_light * FRAMES_PER_SECOND : 0);
5277
5278   RedrawAllLightSwitchesAndInvisibleElements();
5279 }
5280
5281 static void ActivateTimegateSwitch(int x, int y)
5282 {
5283   int xx, yy;
5284
5285   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5286
5287   SCAN_PLAYFIELD(xx, yy)
5288   {
5289     int element = Feld[xx][yy];
5290
5291     if (element == EL_TIMEGATE_CLOSED ||
5292         element == EL_TIMEGATE_CLOSING)
5293     {
5294       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5295       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5296     }
5297
5298     /*
5299     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5300     {
5301       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5302       DrawLevelField(xx, yy);
5303     }
5304     */
5305
5306   }
5307
5308 #if 1
5309   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5310                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5311 #else
5312   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5313 #endif
5314 }
5315
5316 void Impact(int x, int y)
5317 {
5318   boolean last_line = (y == lev_fieldy - 1);
5319   boolean object_hit = FALSE;
5320   boolean impact = (last_line || object_hit);
5321   int element = Feld[x][y];
5322   int smashed = EL_STEELWALL;
5323
5324   if (!last_line)       /* check if element below was hit */
5325   {
5326     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5327       return;
5328
5329     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5330                                          MovDir[x][y + 1] != MV_DOWN ||
5331                                          MovPos[x][y + 1] <= TILEY / 2));
5332
5333     /* do not smash moving elements that left the smashed field in time */
5334     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5335         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5336       object_hit = FALSE;
5337
5338 #if USE_QUICKSAND_IMPACT_BUGFIX
5339     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5340     {
5341       RemoveMovingField(x, y + 1);
5342       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5343       Feld[x][y + 2] = EL_ROCK;
5344       DrawLevelField(x, y + 2);
5345
5346       object_hit = TRUE;
5347     }
5348
5349     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5350     {
5351       RemoveMovingField(x, y + 1);
5352       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5353       Feld[x][y + 2] = EL_ROCK;
5354       DrawLevelField(x, y + 2);
5355
5356       object_hit = TRUE;
5357     }
5358 #endif
5359
5360     if (object_hit)
5361       smashed = MovingOrBlocked2Element(x, y + 1);
5362
5363     impact = (last_line || object_hit);
5364   }
5365
5366   if (!last_line && smashed == EL_ACID) /* element falls into acid */
5367   {
5368     SplashAcid(x, y + 1);
5369     return;
5370   }
5371
5372   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5373   /* only reset graphic animation if graphic really changes after impact */
5374   if (impact &&
5375       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5376   {
5377     ResetGfxAnimation(x, y);
5378     DrawLevelField(x, y);
5379   }
5380
5381   if (impact && CAN_EXPLODE_IMPACT(element))
5382   {
5383     Bang(x, y);
5384     return;
5385   }
5386   else if (impact && element == EL_PEARL &&
5387            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5388   {
5389     ResetGfxAnimation(x, y);
5390
5391     Feld[x][y] = EL_PEARL_BREAKING;
5392     PlayLevelSound(x, y, SND_PEARL_BREAKING);
5393     return;
5394   }
5395   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5396   {
5397     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5398
5399     return;
5400   }
5401
5402   if (impact && element == EL_AMOEBA_DROP)
5403   {
5404     if (object_hit && IS_PLAYER(x, y + 1))
5405       KillPlayerUnlessEnemyProtected(x, y + 1);
5406     else if (object_hit && smashed == EL_PENGUIN)
5407       Bang(x, y + 1);
5408     else
5409     {
5410       Feld[x][y] = EL_AMOEBA_GROWING;
5411       Store[x][y] = EL_AMOEBA_WET;
5412
5413       ResetRandomAnimationValue(x, y);
5414     }
5415     return;
5416   }
5417
5418   if (object_hit)               /* check which object was hit */
5419   {
5420     if ((CAN_PASS_MAGIC_WALL(element) && 
5421          (smashed == EL_MAGIC_WALL ||
5422           smashed == EL_BD_MAGIC_WALL)) ||
5423         (CAN_PASS_DC_MAGIC_WALL(element) &&
5424          smashed == EL_DC_MAGIC_WALL))
5425     {
5426       int xx, yy;
5427       int activated_magic_wall =
5428         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5429          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5430          EL_DC_MAGIC_WALL_ACTIVE);
5431
5432       /* activate magic wall / mill */
5433       SCAN_PLAYFIELD(xx, yy)
5434       {
5435         if (Feld[xx][yy] == smashed)
5436           Feld[xx][yy] = activated_magic_wall;
5437       }
5438
5439       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5440       game.magic_wall_active = TRUE;
5441
5442       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5443                             SND_MAGIC_WALL_ACTIVATING :
5444                             smashed == EL_BD_MAGIC_WALL ?
5445                             SND_BD_MAGIC_WALL_ACTIVATING :
5446                             SND_DC_MAGIC_WALL_ACTIVATING));
5447     }
5448
5449     if (IS_PLAYER(x, y + 1))
5450     {
5451       if (CAN_SMASH_PLAYER(element))
5452       {
5453         KillPlayerUnlessEnemyProtected(x, y + 1);
5454         return;
5455       }
5456     }
5457     else if (smashed == EL_PENGUIN)
5458     {
5459       if (CAN_SMASH_PLAYER(element))
5460       {
5461         Bang(x, y + 1);
5462         return;
5463       }
5464     }
5465     else if (element == EL_BD_DIAMOND)
5466     {
5467       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5468       {
5469         Bang(x, y + 1);
5470         return;
5471       }
5472     }
5473     else if (((element == EL_SP_INFOTRON ||
5474                element == EL_SP_ZONK) &&
5475               (smashed == EL_SP_SNIKSNAK ||
5476                smashed == EL_SP_ELECTRON ||
5477                smashed == EL_SP_DISK_ORANGE)) ||
5478              (element == EL_SP_INFOTRON &&
5479               smashed == EL_SP_DISK_YELLOW))
5480     {
5481       Bang(x, y + 1);
5482       return;
5483     }
5484     else if (CAN_SMASH_EVERYTHING(element))
5485     {
5486       if (IS_CLASSIC_ENEMY(smashed) ||
5487           CAN_EXPLODE_SMASHED(smashed))
5488       {
5489         Bang(x, y + 1);
5490         return;
5491       }
5492       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5493       {
5494         if (smashed == EL_LAMP ||
5495             smashed == EL_LAMP_ACTIVE)
5496         {
5497           Bang(x, y + 1);
5498           return;
5499         }
5500         else if (smashed == EL_NUT)
5501         {
5502           Feld[x][y + 1] = EL_NUT_BREAKING;
5503           PlayLevelSound(x, y, SND_NUT_BREAKING);
5504           RaiseScoreElement(EL_NUT);
5505           return;
5506         }
5507         else if (smashed == EL_PEARL)
5508         {
5509           ResetGfxAnimation(x, y);
5510
5511           Feld[x][y + 1] = EL_PEARL_BREAKING;
5512           PlayLevelSound(x, y, SND_PEARL_BREAKING);
5513           return;
5514         }
5515         else if (smashed == EL_DIAMOND)
5516         {
5517           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
5518           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
5519           return;
5520         }
5521         else if (IS_BELT_SWITCH(smashed))
5522         {
5523           ToggleBeltSwitch(x, y + 1);
5524         }
5525         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
5526                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
5527                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
5528                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
5529         {
5530           ToggleSwitchgateSwitch(x, y + 1);
5531         }
5532         else if (smashed == EL_LIGHT_SWITCH ||
5533                  smashed == EL_LIGHT_SWITCH_ACTIVE)
5534         {
5535           ToggleLightSwitch(x, y + 1);
5536         }
5537         else
5538         {
5539 #if 0
5540           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
5541 #endif
5542
5543           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5544
5545           CheckElementChangeBySide(x, y + 1, smashed, element,
5546                                    CE_SWITCHED, CH_SIDE_TOP);
5547           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5548                                             CH_SIDE_TOP);
5549         }
5550       }
5551       else
5552       {
5553         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5554       }
5555     }
5556   }
5557
5558   /* play sound of magic wall / mill */
5559   if (!last_line &&
5560       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5561        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
5562        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
5563   {
5564     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5565       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5566     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5567       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5568     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5569       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5570
5571     return;
5572   }
5573
5574   /* play sound of object that hits the ground */
5575   if (last_line || object_hit)
5576     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5577 }
5578
5579 inline static void TurnRoundExt(int x, int y)
5580 {
5581   static struct
5582   {
5583     int dx, dy;
5584   } move_xy[] =
5585   {
5586     {  0,  0 },
5587     { -1,  0 },
5588     { +1,  0 },
5589     {  0,  0 },
5590     {  0, -1 },
5591     {  0,  0 }, { 0, 0 }, { 0, 0 },
5592     {  0, +1 }
5593   };
5594   static struct
5595   {
5596     int left, right, back;
5597   } turn[] =
5598   {
5599     { 0,        0,              0        },
5600     { MV_DOWN,  MV_UP,          MV_RIGHT },
5601     { MV_UP,    MV_DOWN,        MV_LEFT  },
5602     { 0,        0,              0        },
5603     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
5604     { 0,        0,              0        },
5605     { 0,        0,              0        },
5606     { 0,        0,              0        },
5607     { MV_RIGHT, MV_LEFT,        MV_UP    }
5608   };
5609
5610   int element = Feld[x][y];
5611   int move_pattern = element_info[element].move_pattern;
5612
5613   int old_move_dir = MovDir[x][y];
5614   int left_dir  = turn[old_move_dir].left;
5615   int right_dir = turn[old_move_dir].right;
5616   int back_dir  = turn[old_move_dir].back;
5617
5618   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
5619   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
5620   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
5621   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
5622
5623   int left_x  = x + left_dx,  left_y  = y + left_dy;
5624   int right_x = x + right_dx, right_y = y + right_dy;
5625   int move_x  = x + move_dx,  move_y  = y + move_dy;
5626
5627   int xx, yy;
5628
5629   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5630   {
5631     TestIfBadThingTouchesOtherBadThing(x, y);
5632
5633     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5634       MovDir[x][y] = right_dir;
5635     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5636       MovDir[x][y] = left_dir;
5637
5638     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5639       MovDelay[x][y] = 9;
5640     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
5641       MovDelay[x][y] = 1;
5642   }
5643   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5644   {
5645     TestIfBadThingTouchesOtherBadThing(x, y);
5646
5647     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5648       MovDir[x][y] = left_dir;
5649     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5650       MovDir[x][y] = right_dir;
5651
5652     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5653       MovDelay[x][y] = 9;
5654     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
5655       MovDelay[x][y] = 1;
5656   }
5657   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5658   {
5659     TestIfBadThingTouchesOtherBadThing(x, y);
5660
5661     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5662       MovDir[x][y] = left_dir;
5663     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5664       MovDir[x][y] = right_dir;
5665
5666     if (MovDir[x][y] != old_move_dir)
5667       MovDelay[x][y] = 9;
5668   }
5669   else if (element == EL_YAMYAM)
5670   {
5671     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5672     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5673
5674     if (can_turn_left && can_turn_right)
5675       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5676     else if (can_turn_left)
5677       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5678     else if (can_turn_right)
5679       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5680     else
5681       MovDir[x][y] = back_dir;
5682
5683     MovDelay[x][y] = 16 + 16 * RND(3);
5684   }
5685   else if (element == EL_DARK_YAMYAM)
5686   {
5687     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5688                                                          left_x, left_y);
5689     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5690                                                          right_x, right_y);
5691
5692     if (can_turn_left && can_turn_right)
5693       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5694     else if (can_turn_left)
5695       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5696     else if (can_turn_right)
5697       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5698     else
5699       MovDir[x][y] = back_dir;
5700
5701     MovDelay[x][y] = 16 + 16 * RND(3);
5702   }
5703   else if (element == EL_PACMAN)
5704   {
5705     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5706     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5707
5708     if (can_turn_left && can_turn_right)
5709       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5710     else if (can_turn_left)
5711       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5712     else if (can_turn_right)
5713       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5714     else
5715       MovDir[x][y] = back_dir;
5716
5717     MovDelay[x][y] = 6 + RND(40);
5718   }
5719   else if (element == EL_PIG)
5720   {
5721     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5722     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5723     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5724     boolean should_turn_left, should_turn_right, should_move_on;
5725     int rnd_value = 24;
5726     int rnd = RND(rnd_value);
5727
5728     should_turn_left = (can_turn_left &&
5729                         (!can_move_on ||
5730                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5731                                                    y + back_dy + left_dy)));
5732     should_turn_right = (can_turn_right &&
5733                          (!can_move_on ||
5734                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5735                                                     y + back_dy + right_dy)));
5736     should_move_on = (can_move_on &&
5737                       (!can_turn_left ||
5738                        !can_turn_right ||
5739                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5740                                                  y + move_dy + left_dy) ||
5741                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5742                                                  y + move_dy + right_dy)));
5743
5744     if (should_turn_left || should_turn_right || should_move_on)
5745     {
5746       if (should_turn_left && should_turn_right && should_move_on)
5747         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
5748                         rnd < 2 * rnd_value / 3 ? right_dir :
5749                         old_move_dir);
5750       else if (should_turn_left && should_turn_right)
5751         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5752       else if (should_turn_left && should_move_on)
5753         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5754       else if (should_turn_right && should_move_on)
5755         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5756       else if (should_turn_left)
5757         MovDir[x][y] = left_dir;
5758       else if (should_turn_right)
5759         MovDir[x][y] = right_dir;
5760       else if (should_move_on)
5761         MovDir[x][y] = old_move_dir;
5762     }
5763     else if (can_move_on && rnd > rnd_value / 8)
5764       MovDir[x][y] = old_move_dir;
5765     else if (can_turn_left && can_turn_right)
5766       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5767     else if (can_turn_left && rnd > rnd_value / 8)
5768       MovDir[x][y] = left_dir;
5769     else if (can_turn_right && rnd > rnd_value/8)
5770       MovDir[x][y] = right_dir;
5771     else
5772       MovDir[x][y] = back_dir;
5773
5774     xx = x + move_xy[MovDir[x][y]].dx;
5775     yy = y + move_xy[MovDir[x][y]].dy;
5776
5777     if (!IN_LEV_FIELD(xx, yy) ||
5778         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5779       MovDir[x][y] = old_move_dir;
5780
5781     MovDelay[x][y] = 0;
5782   }
5783   else if (element == EL_DRAGON)
5784   {
5785     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5786     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5787     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5788     int rnd_value = 24;
5789     int rnd = RND(rnd_value);
5790
5791     if (can_move_on && rnd > rnd_value / 8)
5792       MovDir[x][y] = old_move_dir;
5793     else if (can_turn_left && can_turn_right)
5794       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5795     else if (can_turn_left && rnd > rnd_value / 8)
5796       MovDir[x][y] = left_dir;
5797     else if (can_turn_right && rnd > rnd_value / 8)
5798       MovDir[x][y] = right_dir;
5799     else
5800       MovDir[x][y] = back_dir;
5801
5802     xx = x + move_xy[MovDir[x][y]].dx;
5803     yy = y + move_xy[MovDir[x][y]].dy;
5804
5805     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5806       MovDir[x][y] = old_move_dir;
5807
5808     MovDelay[x][y] = 0;
5809   }
5810   else if (element == EL_MOLE)
5811   {
5812     boolean can_move_on =
5813       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5814                             IS_AMOEBOID(Feld[move_x][move_y]) ||
5815                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5816     if (!can_move_on)
5817     {
5818       boolean can_turn_left =
5819         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5820                               IS_AMOEBOID(Feld[left_x][left_y])));
5821
5822       boolean can_turn_right =
5823         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5824                               IS_AMOEBOID(Feld[right_x][right_y])));
5825
5826       if (can_turn_left && can_turn_right)
5827         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5828       else if (can_turn_left)
5829         MovDir[x][y] = left_dir;
5830       else
5831         MovDir[x][y] = right_dir;
5832     }
5833
5834     if (MovDir[x][y] != old_move_dir)
5835       MovDelay[x][y] = 9;
5836   }
5837   else if (element == EL_BALLOON)
5838   {
5839     MovDir[x][y] = game.wind_direction;
5840     MovDelay[x][y] = 0;
5841   }
5842   else if (element == EL_SPRING)
5843   {
5844 #if USE_NEW_SPRING_BUMPER
5845     if (MovDir[x][y] & MV_HORIZONTAL)
5846     {
5847       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5848           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5849       {
5850         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5851         ResetGfxAnimation(move_x, move_y);
5852         DrawLevelField(move_x, move_y);
5853
5854         MovDir[x][y] = back_dir;
5855       }
5856       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5857                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5858         MovDir[x][y] = MV_NONE;
5859     }
5860 #else
5861     if (MovDir[x][y] & MV_HORIZONTAL &&
5862         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5863          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5864       MovDir[x][y] = MV_NONE;
5865 #endif
5866
5867     MovDelay[x][y] = 0;
5868   }
5869   else if (element == EL_ROBOT ||
5870            element == EL_SATELLITE ||
5871            element == EL_PENGUIN ||
5872            element == EL_EMC_ANDROID)
5873   {
5874     int attr_x = -1, attr_y = -1;
5875
5876     if (AllPlayersGone)
5877     {
5878       attr_x = ExitX;
5879       attr_y = ExitY;
5880     }
5881     else
5882     {
5883       int i;
5884
5885       for (i = 0; i < MAX_PLAYERS; i++)
5886       {
5887         struct PlayerInfo *player = &stored_player[i];
5888         int jx = player->jx, jy = player->jy;
5889
5890         if (!player->active)
5891           continue;
5892
5893         if (attr_x == -1 ||
5894             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5895         {
5896           attr_x = jx;
5897           attr_y = jy;
5898         }
5899       }
5900     }
5901
5902     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5903         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5904          game.engine_version < VERSION_IDENT(3,1,0,0)))
5905     {
5906       attr_x = ZX;
5907       attr_y = ZY;
5908     }
5909
5910     if (element == EL_PENGUIN)
5911     {
5912       int i;
5913       static int xy[4][2] =
5914       {
5915         { 0, -1 },
5916         { -1, 0 },
5917         { +1, 0 },
5918         { 0, +1 }
5919       };
5920
5921       for (i = 0; i < NUM_DIRECTIONS; i++)
5922       {
5923         int ex = x + xy[i][0];
5924         int ey = y + xy[i][1];
5925
5926         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
5927                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
5928                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
5929                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
5930         {
5931           attr_x = ex;
5932           attr_y = ey;
5933           break;
5934         }
5935       }
5936     }
5937
5938     MovDir[x][y] = MV_NONE;
5939     if (attr_x < x)
5940       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5941     else if (attr_x > x)
5942       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5943     if (attr_y < y)
5944       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5945     else if (attr_y > y)
5946       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5947
5948     if (element == EL_ROBOT)
5949     {
5950       int newx, newy;
5951
5952       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5953         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5954       Moving2Blocked(x, y, &newx, &newy);
5955
5956       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5957         MovDelay[x][y] = 8 + 8 * !RND(3);
5958       else
5959         MovDelay[x][y] = 16;
5960     }
5961     else if (element == EL_PENGUIN)
5962     {
5963       int newx, newy;
5964
5965       MovDelay[x][y] = 1;
5966
5967       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5968       {
5969         boolean first_horiz = RND(2);
5970         int new_move_dir = MovDir[x][y];
5971
5972         MovDir[x][y] =
5973           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5974         Moving2Blocked(x, y, &newx, &newy);
5975
5976         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5977           return;
5978
5979         MovDir[x][y] =
5980           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5981         Moving2Blocked(x, y, &newx, &newy);
5982
5983         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5984           return;
5985
5986         MovDir[x][y] = old_move_dir;
5987         return;
5988       }
5989     }
5990     else if (element == EL_SATELLITE)
5991     {
5992       int newx, newy;
5993
5994       MovDelay[x][y] = 1;
5995
5996       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5997       {
5998         boolean first_horiz = RND(2);
5999         int new_move_dir = MovDir[x][y];
6000
6001         MovDir[x][y] =
6002           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6003         Moving2Blocked(x, y, &newx, &newy);
6004
6005         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6006           return;
6007
6008         MovDir[x][y] =
6009           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6010         Moving2Blocked(x, y, &newx, &newy);
6011
6012         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6013           return;
6014
6015         MovDir[x][y] = old_move_dir;
6016         return;
6017       }
6018     }
6019     else if (element == EL_EMC_ANDROID)
6020     {
6021       static int check_pos[16] =
6022       {
6023         -1,             /*  0 => (invalid)          */
6024         7,              /*  1 => MV_LEFT            */
6025         3,              /*  2 => MV_RIGHT           */
6026         -1,             /*  3 => (invalid)          */
6027         1,              /*  4 =>            MV_UP   */
6028         0,              /*  5 => MV_LEFT  | MV_UP   */
6029         2,              /*  6 => MV_RIGHT | MV_UP   */
6030         -1,             /*  7 => (invalid)          */
6031         5,              /*  8 =>            MV_DOWN */
6032         6,              /*  9 => MV_LEFT  | MV_DOWN */
6033         4,              /* 10 => MV_RIGHT | MV_DOWN */
6034         -1,             /* 11 => (invalid)          */
6035         -1,             /* 12 => (invalid)          */
6036         -1,             /* 13 => (invalid)          */
6037         -1,             /* 14 => (invalid)          */
6038         -1,             /* 15 => (invalid)          */
6039       };
6040       static struct
6041       {
6042         int dx, dy;
6043         int dir;
6044       } check_xy[8] =
6045       {
6046         { -1, -1,       MV_LEFT  | MV_UP   },
6047         {  0, -1,                  MV_UP   },
6048         { +1, -1,       MV_RIGHT | MV_UP   },
6049         { +1,  0,       MV_RIGHT           },
6050         { +1, +1,       MV_RIGHT | MV_DOWN },
6051         {  0, +1,                  MV_DOWN },
6052         { -1, +1,       MV_LEFT  | MV_DOWN },
6053         { -1,  0,       MV_LEFT            },
6054       };
6055       int start_pos, check_order;
6056       boolean can_clone = FALSE;
6057       int i;
6058
6059       /* check if there is any free field around current position */
6060       for (i = 0; i < 8; i++)
6061       {
6062         int newx = x + check_xy[i].dx;
6063         int newy = y + check_xy[i].dy;
6064
6065         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6066         {
6067           can_clone = TRUE;
6068
6069           break;
6070         }
6071       }
6072
6073       if (can_clone)            /* randomly find an element to clone */
6074       {
6075         can_clone = FALSE;
6076
6077         start_pos = check_pos[RND(8)];
6078         check_order = (RND(2) ? -1 : +1);
6079
6080         for (i = 0; i < 8; i++)
6081         {
6082           int pos_raw = start_pos + i * check_order;
6083           int pos = (pos_raw + 8) % 8;
6084           int newx = x + check_xy[pos].dx;
6085           int newy = y + check_xy[pos].dy;
6086
6087           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6088           {
6089             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6090             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6091
6092             Store[x][y] = Feld[newx][newy];
6093
6094             can_clone = TRUE;
6095
6096             break;
6097           }
6098         }
6099       }
6100
6101       if (can_clone)            /* randomly find a direction to move */
6102       {
6103         can_clone = FALSE;
6104
6105         start_pos = check_pos[RND(8)];
6106         check_order = (RND(2) ? -1 : +1);
6107
6108         for (i = 0; i < 8; i++)
6109         {
6110           int pos_raw = start_pos + i * check_order;
6111           int pos = (pos_raw + 8) % 8;
6112           int newx = x + check_xy[pos].dx;
6113           int newy = y + check_xy[pos].dy;
6114           int new_move_dir = check_xy[pos].dir;
6115
6116           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6117           {
6118             MovDir[x][y] = new_move_dir;
6119             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6120
6121             can_clone = TRUE;
6122
6123             break;
6124           }
6125         }
6126       }
6127
6128       if (can_clone)            /* cloning and moving successful */
6129         return;
6130
6131       /* cannot clone -- try to move towards player */
6132
6133       start_pos = check_pos[MovDir[x][y] & 0x0f];
6134       check_order = (RND(2) ? -1 : +1);
6135
6136       for (i = 0; i < 3; i++)
6137       {
6138         /* first check start_pos, then previous/next or (next/previous) pos */
6139         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6140         int pos = (pos_raw + 8) % 8;
6141         int newx = x + check_xy[pos].dx;
6142         int newy = y + check_xy[pos].dy;
6143         int new_move_dir = check_xy[pos].dir;
6144
6145         if (IS_PLAYER(newx, newy))
6146           break;
6147
6148         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6149         {
6150           MovDir[x][y] = new_move_dir;
6151           MovDelay[x][y] = level.android_move_time * 8 + 1;
6152
6153           break;
6154         }
6155       }
6156     }
6157   }
6158   else if (move_pattern == MV_TURNING_LEFT ||
6159            move_pattern == MV_TURNING_RIGHT ||
6160            move_pattern == MV_TURNING_LEFT_RIGHT ||
6161            move_pattern == MV_TURNING_RIGHT_LEFT ||
6162            move_pattern == MV_TURNING_RANDOM ||
6163            move_pattern == MV_ALL_DIRECTIONS)
6164   {
6165     boolean can_turn_left =
6166       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6167     boolean can_turn_right =
6168       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6169
6170     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6171       return;
6172
6173     if (move_pattern == MV_TURNING_LEFT)
6174       MovDir[x][y] = left_dir;
6175     else if (move_pattern == MV_TURNING_RIGHT)
6176       MovDir[x][y] = right_dir;
6177     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6178       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6179     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6180       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6181     else if (move_pattern == MV_TURNING_RANDOM)
6182       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6183                       can_turn_right && !can_turn_left ? right_dir :
6184                       RND(2) ? left_dir : right_dir);
6185     else if (can_turn_left && can_turn_right)
6186       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6187     else if (can_turn_left)
6188       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6189     else if (can_turn_right)
6190       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6191     else
6192       MovDir[x][y] = back_dir;
6193
6194     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6195   }
6196   else if (move_pattern == MV_HORIZONTAL ||
6197            move_pattern == MV_VERTICAL)
6198   {
6199     if (move_pattern & old_move_dir)
6200       MovDir[x][y] = back_dir;
6201     else if (move_pattern == MV_HORIZONTAL)
6202       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6203     else if (move_pattern == MV_VERTICAL)
6204       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6205
6206     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6207   }
6208   else if (move_pattern & MV_ANY_DIRECTION)
6209   {
6210     MovDir[x][y] = move_pattern;
6211     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6212   }
6213   else if (move_pattern & MV_WIND_DIRECTION)
6214   {
6215     MovDir[x][y] = game.wind_direction;
6216     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6217   }
6218   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6219   {
6220     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6221       MovDir[x][y] = left_dir;
6222     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6223       MovDir[x][y] = right_dir;
6224
6225     if (MovDir[x][y] != old_move_dir)
6226       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6227   }
6228   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6229   {
6230     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6231       MovDir[x][y] = right_dir;
6232     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6233       MovDir[x][y] = left_dir;
6234
6235     if (MovDir[x][y] != old_move_dir)
6236       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6237   }
6238   else if (move_pattern == MV_TOWARDS_PLAYER ||
6239            move_pattern == MV_AWAY_FROM_PLAYER)
6240   {
6241     int attr_x = -1, attr_y = -1;
6242     int newx, newy;
6243     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6244
6245     if (AllPlayersGone)
6246     {
6247       attr_x = ExitX;
6248       attr_y = ExitY;
6249     }
6250     else
6251     {
6252       int i;
6253
6254       for (i = 0; i < MAX_PLAYERS; i++)
6255       {
6256         struct PlayerInfo *player = &stored_player[i];
6257         int jx = player->jx, jy = player->jy;
6258
6259         if (!player->active)
6260           continue;
6261
6262         if (attr_x == -1 ||
6263             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6264         {
6265           attr_x = jx;
6266           attr_y = jy;
6267         }
6268       }
6269     }
6270
6271     MovDir[x][y] = MV_NONE;
6272     if (attr_x < x)
6273       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6274     else if (attr_x > x)
6275       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6276     if (attr_y < y)
6277       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6278     else if (attr_y > y)
6279       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6280
6281     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6282
6283     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6284     {
6285       boolean first_horiz = RND(2);
6286       int new_move_dir = MovDir[x][y];
6287
6288       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6289       {
6290         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6291         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6292
6293         return;
6294       }
6295
6296       MovDir[x][y] =
6297         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6298       Moving2Blocked(x, y, &newx, &newy);
6299
6300       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6301         return;
6302
6303       MovDir[x][y] =
6304         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6305       Moving2Blocked(x, y, &newx, &newy);
6306
6307       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6308         return;
6309
6310       MovDir[x][y] = old_move_dir;
6311     }
6312   }
6313   else if (move_pattern == MV_WHEN_PUSHED ||
6314            move_pattern == MV_WHEN_DROPPED)
6315   {
6316     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6317       MovDir[x][y] = MV_NONE;
6318
6319     MovDelay[x][y] = 0;
6320   }
6321   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6322   {
6323     static int test_xy[7][2] =
6324     {
6325       { 0, -1 },
6326       { -1, 0 },
6327       { +1, 0 },
6328       { 0, +1 },
6329       { 0, -1 },
6330       { -1, 0 },
6331       { +1, 0 },
6332     };
6333     static int test_dir[7] =
6334     {
6335       MV_UP,
6336       MV_LEFT,
6337       MV_RIGHT,
6338       MV_DOWN,
6339       MV_UP,
6340       MV_LEFT,
6341       MV_RIGHT,
6342     };
6343     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6344     int move_preference = -1000000;     /* start with very low preference */
6345     int new_move_dir = MV_NONE;
6346     int start_test = RND(4);
6347     int i;
6348
6349     for (i = 0; i < NUM_DIRECTIONS; i++)
6350     {
6351       int move_dir = test_dir[start_test + i];
6352       int move_dir_preference;
6353
6354       xx = x + test_xy[start_test + i][0];
6355       yy = y + test_xy[start_test + i][1];
6356
6357       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6358           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6359       {
6360         new_move_dir = move_dir;
6361
6362         break;
6363       }
6364
6365       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6366         continue;
6367
6368       move_dir_preference = -1 * RunnerVisit[xx][yy];
6369       if (hunter_mode && PlayerVisit[xx][yy] > 0)
6370         move_dir_preference = PlayerVisit[xx][yy];
6371
6372       if (move_dir_preference > move_preference)
6373       {
6374         /* prefer field that has not been visited for the longest time */
6375         move_preference = move_dir_preference;
6376         new_move_dir = move_dir;
6377       }
6378       else if (move_dir_preference == move_preference &&
6379                move_dir == old_move_dir)
6380       {
6381         /* prefer last direction when all directions are preferred equally */
6382         move_preference = move_dir_preference;
6383         new_move_dir = move_dir;
6384       }
6385     }
6386
6387     MovDir[x][y] = new_move_dir;
6388     if (old_move_dir != new_move_dir)
6389       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6390   }
6391 }
6392
6393 static void TurnRound(int x, int y)
6394 {
6395   int direction = MovDir[x][y];
6396
6397   TurnRoundExt(x, y);
6398
6399   GfxDir[x][y] = MovDir[x][y];
6400
6401   if (direction != MovDir[x][y])
6402     GfxFrame[x][y] = 0;
6403
6404   if (MovDelay[x][y])
6405     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6406
6407   ResetGfxFrame(x, y, FALSE);
6408 }
6409
6410 static boolean JustBeingPushed(int x, int y)
6411 {
6412   int i;
6413
6414   for (i = 0; i < MAX_PLAYERS; i++)
6415   {
6416     struct PlayerInfo *player = &stored_player[i];
6417
6418     if (player->active && player->is_pushing && player->MovPos)
6419     {
6420       int next_jx = player->jx + (player->jx - player->last_jx);
6421       int next_jy = player->jy + (player->jy - player->last_jy);
6422
6423       if (x == next_jx && y == next_jy)
6424         return TRUE;
6425     }
6426   }
6427
6428   return FALSE;
6429 }
6430
6431 void StartMoving(int x, int y)
6432 {
6433   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
6434   int element = Feld[x][y];
6435
6436   if (Stop[x][y])
6437     return;
6438
6439   if (MovDelay[x][y] == 0)
6440     GfxAction[x][y] = ACTION_DEFAULT;
6441
6442   if (CAN_FALL(element) && y < lev_fieldy - 1)
6443   {
6444     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
6445         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6446       if (JustBeingPushed(x, y))
6447         return;
6448
6449     if (element == EL_QUICKSAND_FULL)
6450     {
6451       if (IS_FREE(x, y + 1))
6452       {
6453         InitMovingField(x, y, MV_DOWN);
6454         started_moving = TRUE;
6455
6456         Feld[x][y] = EL_QUICKSAND_EMPTYING;
6457 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6458         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6459           Store[x][y] = EL_ROCK;
6460 #else
6461         Store[x][y] = EL_ROCK;
6462 #endif
6463
6464         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6465       }
6466       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6467       {
6468         if (!MovDelay[x][y])
6469           MovDelay[x][y] = TILEY + 1;
6470
6471         if (MovDelay[x][y])
6472         {
6473           MovDelay[x][y]--;
6474           if (MovDelay[x][y])
6475             return;
6476         }
6477
6478         Feld[x][y] = EL_QUICKSAND_EMPTY;
6479         Feld[x][y + 1] = EL_QUICKSAND_FULL;
6480         Store[x][y + 1] = Store[x][y];
6481         Store[x][y] = 0;
6482
6483         PlayLevelSoundAction(x, y, ACTION_FILLING);
6484       }
6485     }
6486     else if (element == EL_QUICKSAND_FAST_FULL)
6487     {
6488       if (IS_FREE(x, y + 1))
6489       {
6490         InitMovingField(x, y, MV_DOWN);
6491         started_moving = TRUE;
6492
6493         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
6494 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6495         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6496           Store[x][y] = EL_ROCK;
6497 #else
6498         Store[x][y] = EL_ROCK;
6499 #endif
6500
6501         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6502       }
6503       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6504       {
6505         if (!MovDelay[x][y])
6506           MovDelay[x][y] = TILEY + 1;
6507
6508         if (MovDelay[x][y])
6509         {
6510           MovDelay[x][y]--;
6511           if (MovDelay[x][y])
6512             return;
6513         }
6514
6515         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
6516         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
6517         Store[x][y + 1] = Store[x][y];
6518         Store[x][y] = 0;
6519
6520         PlayLevelSoundAction(x, y, ACTION_FILLING);
6521       }
6522     }
6523     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6524              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6525     {
6526       InitMovingField(x, y, MV_DOWN);
6527       started_moving = TRUE;
6528
6529       Feld[x][y] = EL_QUICKSAND_FILLING;
6530       Store[x][y] = element;
6531
6532       PlayLevelSoundAction(x, y, ACTION_FILLING);
6533     }
6534     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6535              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6536     {
6537       InitMovingField(x, y, MV_DOWN);
6538       started_moving = TRUE;
6539
6540       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
6541       Store[x][y] = element;
6542
6543       PlayLevelSoundAction(x, y, ACTION_FILLING);
6544     }
6545     else if (element == EL_MAGIC_WALL_FULL)
6546     {
6547       if (IS_FREE(x, y + 1))
6548       {
6549         InitMovingField(x, y, MV_DOWN);
6550         started_moving = TRUE;
6551
6552         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6553         Store[x][y] = EL_CHANGED(Store[x][y]);
6554       }
6555       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6556       {
6557         if (!MovDelay[x][y])
6558           MovDelay[x][y] = TILEY/4 + 1;
6559
6560         if (MovDelay[x][y])
6561         {
6562           MovDelay[x][y]--;
6563           if (MovDelay[x][y])
6564             return;
6565         }
6566
6567         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6568         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6569         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6570         Store[x][y] = 0;
6571       }
6572     }
6573     else if (element == EL_BD_MAGIC_WALL_FULL)
6574     {
6575       if (IS_FREE(x, y + 1))
6576       {
6577         InitMovingField(x, y, MV_DOWN);
6578         started_moving = TRUE;
6579
6580         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6581         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6582       }
6583       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6584       {
6585         if (!MovDelay[x][y])
6586           MovDelay[x][y] = TILEY/4 + 1;
6587
6588         if (MovDelay[x][y])
6589         {
6590           MovDelay[x][y]--;
6591           if (MovDelay[x][y])
6592             return;
6593         }
6594
6595         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6596         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6597         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6598         Store[x][y] = 0;
6599       }
6600     }
6601     else if (element == EL_DC_MAGIC_WALL_FULL)
6602     {
6603       if (IS_FREE(x, y + 1))
6604       {
6605         InitMovingField(x, y, MV_DOWN);
6606         started_moving = TRUE;
6607
6608         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
6609         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
6610       }
6611       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6612       {
6613         if (!MovDelay[x][y])
6614           MovDelay[x][y] = TILEY/4 + 1;
6615
6616         if (MovDelay[x][y])
6617         {
6618           MovDelay[x][y]--;
6619           if (MovDelay[x][y])
6620             return;
6621         }
6622
6623         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
6624         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
6625         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
6626         Store[x][y] = 0;
6627       }
6628     }
6629     else if ((CAN_PASS_MAGIC_WALL(element) &&
6630               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6631                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
6632              (CAN_PASS_DC_MAGIC_WALL(element) &&
6633               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
6634
6635     {
6636       InitMovingField(x, y, MV_DOWN);
6637       started_moving = TRUE;
6638
6639       Feld[x][y] =
6640         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6641          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
6642          EL_DC_MAGIC_WALL_FILLING);
6643       Store[x][y] = element;
6644     }
6645     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6646     {
6647       SplashAcid(x, y + 1);
6648
6649       InitMovingField(x, y, MV_DOWN);
6650       started_moving = TRUE;
6651
6652       Store[x][y] = EL_ACID;
6653     }
6654     else if (
6655 #if USE_FIX_IMPACT_COLLISION
6656              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6657               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
6658 #else
6659              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6660               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
6661 #endif
6662              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
6663               CAN_FALL(element) && WasJustFalling[x][y] &&
6664               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
6665
6666              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
6667               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
6668               (Feld[x][y + 1] == EL_BLOCKED)))
6669     {
6670       /* this is needed for a special case not covered by calling "Impact()"
6671          from "ContinueMoving()": if an element moves to a tile directly below
6672          another element which was just falling on that tile (which was empty
6673          in the previous frame), the falling element above would just stop
6674          instead of smashing the element below (in previous version, the above
6675          element was just checked for "moving" instead of "falling", resulting
6676          in incorrect smashes caused by horizontal movement of the above
6677          element; also, the case of the player being the element to smash was
6678          simply not covered here... :-/ ) */
6679
6680       CheckCollision[x][y] = 0;
6681       CheckImpact[x][y] = 0;
6682
6683       Impact(x, y);
6684     }
6685     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6686     {
6687       if (MovDir[x][y] == MV_NONE)
6688       {
6689         InitMovingField(x, y, MV_DOWN);
6690         started_moving = TRUE;
6691       }
6692     }
6693     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6694     {
6695       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6696         MovDir[x][y] = MV_DOWN;
6697
6698       InitMovingField(x, y, MV_DOWN);
6699       started_moving = TRUE;
6700     }
6701     else if (element == EL_AMOEBA_DROP)
6702     {
6703       Feld[x][y] = EL_AMOEBA_GROWING;
6704       Store[x][y] = EL_AMOEBA_WET;
6705     }
6706     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6707               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6708              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6709              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6710     {
6711       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
6712                                 (IS_FREE(x - 1, y + 1) ||
6713                                  Feld[x - 1][y + 1] == EL_ACID));
6714       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6715                                 (IS_FREE(x + 1, y + 1) ||
6716                                  Feld[x + 1][y + 1] == EL_ACID));
6717       boolean can_fall_any  = (can_fall_left || can_fall_right);
6718       boolean can_fall_both = (can_fall_left && can_fall_right);
6719       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6720
6721 #if USE_NEW_ALL_SLIPPERY
6722       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6723       {
6724         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6725           can_fall_right = FALSE;
6726         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6727           can_fall_left = FALSE;
6728         else if (slippery_type == SLIPPERY_ONLY_LEFT)
6729           can_fall_right = FALSE;
6730         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6731           can_fall_left = FALSE;
6732
6733         can_fall_any  = (can_fall_left || can_fall_right);
6734         can_fall_both = FALSE;
6735       }
6736 #else
6737       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6738       {
6739         if (slippery_type == SLIPPERY_ONLY_LEFT)
6740           can_fall_right = FALSE;
6741         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6742           can_fall_left = FALSE;
6743         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6744           can_fall_right = FALSE;
6745         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6746           can_fall_left = FALSE;
6747
6748         can_fall_any  = (can_fall_left || can_fall_right);
6749         can_fall_both = (can_fall_left && can_fall_right);
6750       }
6751 #endif
6752
6753 #if USE_NEW_ALL_SLIPPERY
6754 #else
6755 #if USE_NEW_SP_SLIPPERY
6756       /* !!! better use the same properties as for custom elements here !!! */
6757       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6758                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6759       {
6760         can_fall_right = FALSE;         /* slip down on left side */
6761         can_fall_both = FALSE;
6762       }
6763 #endif
6764 #endif
6765
6766 #if USE_NEW_ALL_SLIPPERY
6767       if (can_fall_both)
6768       {
6769         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6770           can_fall_right = FALSE;       /* slip down on left side */
6771         else
6772           can_fall_left = !(can_fall_right = RND(2));
6773
6774         can_fall_both = FALSE;
6775       }
6776 #else
6777       if (can_fall_both)
6778       {
6779         if (game.emulation == EMU_BOULDERDASH ||
6780             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6781           can_fall_right = FALSE;       /* slip down on left side */
6782         else
6783           can_fall_left = !(can_fall_right = RND(2));
6784
6785         can_fall_both = FALSE;
6786       }
6787 #endif
6788
6789       if (can_fall_any)
6790       {
6791         /* if not determined otherwise, prefer left side for slipping down */
6792         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6793         started_moving = TRUE;
6794       }
6795     }
6796 #if 0
6797     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6798 #else
6799     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6800 #endif
6801     {
6802       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
6803       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6804       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6805       int belt_dir = game.belt_dir[belt_nr];
6806
6807       if ((belt_dir == MV_LEFT  && left_is_free) ||
6808           (belt_dir == MV_RIGHT && right_is_free))
6809       {
6810         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6811
6812         InitMovingField(x, y, belt_dir);
6813         started_moving = TRUE;
6814
6815         Pushed[x][y] = TRUE;
6816         Pushed[nextx][y] = TRUE;
6817
6818         GfxAction[x][y] = ACTION_DEFAULT;
6819       }
6820       else
6821       {
6822         MovDir[x][y] = 0;       /* if element was moving, stop it */
6823       }
6824     }
6825   }
6826
6827   /* not "else if" because of elements that can fall and move (EL_SPRING) */
6828 #if 0
6829   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6830 #else
6831   if (CAN_MOVE(element) && !started_moving)
6832 #endif
6833   {
6834     int move_pattern = element_info[element].move_pattern;
6835     int newx, newy;
6836
6837 #if 0
6838 #if DEBUG
6839     if (MovDir[x][y] == MV_NONE)
6840     {
6841       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6842              x, y, element, element_info[element].token_name);
6843       printf("StartMoving(): This should never happen!\n");
6844     }
6845 #endif
6846 #endif
6847
6848     Moving2Blocked(x, y, &newx, &newy);
6849
6850     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6851       return;
6852
6853     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6854         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6855     {
6856       WasJustMoving[x][y] = 0;
6857       CheckCollision[x][y] = 0;
6858
6859       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6860
6861       if (Feld[x][y] != element)        /* element has changed */
6862         return;
6863     }
6864
6865     if (!MovDelay[x][y])        /* start new movement phase */
6866     {
6867       /* all objects that can change their move direction after each step
6868          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6869
6870       if (element != EL_YAMYAM &&
6871           element != EL_DARK_YAMYAM &&
6872           element != EL_PACMAN &&
6873           !(move_pattern & MV_ANY_DIRECTION) &&
6874           move_pattern != MV_TURNING_LEFT &&
6875           move_pattern != MV_TURNING_RIGHT &&
6876           move_pattern != MV_TURNING_LEFT_RIGHT &&
6877           move_pattern != MV_TURNING_RIGHT_LEFT &&
6878           move_pattern != MV_TURNING_RANDOM)
6879       {
6880         TurnRound(x, y);
6881
6882         if (MovDelay[x][y] && (element == EL_BUG ||
6883                                element == EL_SPACESHIP ||
6884                                element == EL_SP_SNIKSNAK ||
6885                                element == EL_SP_ELECTRON ||
6886                                element == EL_MOLE))
6887           DrawLevelField(x, y);
6888       }
6889     }
6890
6891     if (MovDelay[x][y])         /* wait some time before next movement */
6892     {
6893       MovDelay[x][y]--;
6894
6895       if (element == EL_ROBOT ||
6896           element == EL_YAMYAM ||
6897           element == EL_DARK_YAMYAM)
6898       {
6899         DrawLevelElementAnimationIfNeeded(x, y, element);
6900         PlayLevelSoundAction(x, y, ACTION_WAITING);
6901       }
6902       else if (element == EL_SP_ELECTRON)
6903         DrawLevelElementAnimationIfNeeded(x, y, element);
6904       else if (element == EL_DRAGON)
6905       {
6906         int i;
6907         int dir = MovDir[x][y];
6908         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6909         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
6910         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
6911                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
6912                        dir == MV_UP     ? IMG_FLAMES_1_UP :
6913                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6914         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6915
6916         GfxAction[x][y] = ACTION_ATTACKING;
6917
6918         if (IS_PLAYER(x, y))
6919           DrawPlayerField(x, y);
6920         else
6921           DrawLevelField(x, y);
6922
6923         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6924
6925         for (i = 1; i <= 3; i++)
6926         {
6927           int xx = x + i * dx;
6928           int yy = y + i * dy;
6929           int sx = SCREENX(xx);
6930           int sy = SCREENY(yy);
6931           int flame_graphic = graphic + (i - 1);
6932
6933           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6934             break;
6935
6936           if (MovDelay[x][y])
6937           {
6938             int flamed = MovingOrBlocked2Element(xx, yy);
6939
6940             /* !!! */
6941 #if 0
6942             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6943               Bang(xx, yy);
6944             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6945               RemoveMovingField(xx, yy);
6946             else
6947               RemoveField(xx, yy);
6948 #else
6949             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6950               Bang(xx, yy);
6951             else
6952               RemoveMovingField(xx, yy);
6953 #endif
6954
6955             ChangeDelay[xx][yy] = 0;
6956
6957             Feld[xx][yy] = EL_FLAMES;
6958
6959             if (IN_SCR_FIELD(sx, sy))
6960             {
6961               DrawLevelFieldCrumbledSand(xx, yy);
6962               DrawGraphic(sx, sy, flame_graphic, frame);
6963             }
6964           }
6965           else
6966           {
6967             if (Feld[xx][yy] == EL_FLAMES)
6968               Feld[xx][yy] = EL_EMPTY;
6969             DrawLevelField(xx, yy);
6970           }
6971         }
6972       }
6973
6974       if (MovDelay[x][y])       /* element still has to wait some time */
6975       {
6976         PlayLevelSoundAction(x, y, ACTION_WAITING);
6977
6978         return;
6979       }
6980     }
6981
6982     /* now make next step */
6983
6984     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6985
6986     if (DONT_COLLIDE_WITH(element) &&
6987         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6988         !PLAYER_ENEMY_PROTECTED(newx, newy))
6989     {
6990       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6991
6992       return;
6993     }
6994
6995     else if (CAN_MOVE_INTO_ACID(element) &&
6996              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6997              !IS_MV_DIAGONAL(MovDir[x][y]) &&
6998              (MovDir[x][y] == MV_DOWN ||
6999               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7000     {
7001       SplashAcid(newx, newy);
7002       Store[x][y] = EL_ACID;
7003     }
7004     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7005     {
7006       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7007           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7008           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7009           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7010       {
7011         RemoveField(x, y);
7012         DrawLevelField(x, y);
7013
7014         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7015         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7016           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7017
7018         local_player->friends_still_needed--;
7019         if (!local_player->friends_still_needed &&
7020             !local_player->GameOver && AllPlayersGone)
7021           PlayerWins(local_player);
7022
7023         return;
7024       }
7025       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7026       {
7027         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7028           DrawLevelField(newx, newy);
7029         else
7030           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7031       }
7032       else if (!IS_FREE(newx, newy))
7033       {
7034         GfxAction[x][y] = ACTION_WAITING;
7035
7036         if (IS_PLAYER(x, y))
7037           DrawPlayerField(x, y);
7038         else
7039           DrawLevelField(x, y);
7040
7041         return;
7042       }
7043     }
7044     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7045     {
7046       if (IS_FOOD_PIG(Feld[newx][newy]))
7047       {
7048         if (IS_MOVING(newx, newy))
7049           RemoveMovingField(newx, newy);
7050         else
7051         {
7052           Feld[newx][newy] = EL_EMPTY;
7053           DrawLevelField(newx, newy);
7054         }
7055
7056         PlayLevelSound(x, y, SND_PIG_DIGGING);
7057       }
7058       else if (!IS_FREE(newx, newy))
7059       {
7060         if (IS_PLAYER(x, y))
7061           DrawPlayerField(x, y);
7062         else
7063           DrawLevelField(x, y);
7064
7065         return;
7066       }
7067     }
7068     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7069     {
7070       if (Store[x][y] != EL_EMPTY)
7071       {
7072         boolean can_clone = FALSE;
7073         int xx, yy;
7074
7075         /* check if element to clone is still there */
7076         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7077         {
7078           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7079           {
7080             can_clone = TRUE;
7081
7082             break;
7083           }
7084         }
7085
7086         /* cannot clone or target field not free anymore -- do not clone */
7087         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7088           Store[x][y] = EL_EMPTY;
7089       }
7090
7091       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7092       {
7093         if (IS_MV_DIAGONAL(MovDir[x][y]))
7094         {
7095           int diagonal_move_dir = MovDir[x][y];
7096           int stored = Store[x][y];
7097           int change_delay = 8;
7098           int graphic;
7099
7100           /* android is moving diagonally */
7101
7102           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7103
7104           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7105           GfxElement[x][y] = EL_EMC_ANDROID;
7106           GfxAction[x][y] = ACTION_SHRINKING;
7107           GfxDir[x][y] = diagonal_move_dir;
7108           ChangeDelay[x][y] = change_delay;
7109
7110           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7111                                    GfxDir[x][y]);
7112
7113           DrawLevelGraphicAnimation(x, y, graphic);
7114           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7115
7116           if (Feld[newx][newy] == EL_ACID)
7117           {
7118             SplashAcid(newx, newy);
7119
7120             return;
7121           }
7122
7123           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7124
7125           Store[newx][newy] = EL_EMC_ANDROID;
7126           GfxElement[newx][newy] = EL_EMC_ANDROID;
7127           GfxAction[newx][newy] = ACTION_GROWING;
7128           GfxDir[newx][newy] = diagonal_move_dir;
7129           ChangeDelay[newx][newy] = change_delay;
7130
7131           graphic = el_act_dir2img(GfxElement[newx][newy],
7132                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7133
7134           DrawLevelGraphicAnimation(newx, newy, graphic);
7135           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7136
7137           return;
7138         }
7139         else
7140         {
7141           Feld[newx][newy] = EL_EMPTY;
7142           DrawLevelField(newx, newy);
7143
7144           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7145         }
7146       }
7147       else if (!IS_FREE(newx, newy))
7148       {
7149 #if 0
7150         if (IS_PLAYER(x, y))
7151           DrawPlayerField(x, y);
7152         else
7153           DrawLevelField(x, y);
7154 #endif
7155
7156         return;
7157       }
7158     }
7159     else if (IS_CUSTOM_ELEMENT(element) &&
7160              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7161     {
7162       int new_element = Feld[newx][newy];
7163
7164       if (!IS_FREE(newx, newy))
7165       {
7166         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7167                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7168                       ACTION_BREAKING);
7169
7170         /* no element can dig solid indestructible elements */
7171         if (IS_INDESTRUCTIBLE(new_element) &&
7172             !IS_DIGGABLE(new_element) &&
7173             !IS_COLLECTIBLE(new_element))
7174           return;
7175
7176         if (AmoebaNr[newx][newy] &&
7177             (new_element == EL_AMOEBA_FULL ||
7178              new_element == EL_BD_AMOEBA ||
7179              new_element == EL_AMOEBA_GROWING))
7180         {
7181           AmoebaCnt[AmoebaNr[newx][newy]]--;
7182           AmoebaCnt2[AmoebaNr[newx][newy]]--;
7183         }
7184
7185         if (IS_MOVING(newx, newy))
7186           RemoveMovingField(newx, newy);
7187         else
7188         {
7189           RemoveField(newx, newy);
7190           DrawLevelField(newx, newy);
7191         }
7192
7193         /* if digged element was about to explode, prevent the explosion */
7194         ExplodeField[newx][newy] = EX_TYPE_NONE;
7195
7196         PlayLevelSoundAction(x, y, action);
7197       }
7198
7199       Store[newx][newy] = EL_EMPTY;
7200 #if 1
7201       /* this makes it possible to leave the removed element again */
7202       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7203         Store[newx][newy] = new_element;
7204 #else
7205       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7206       {
7207         int move_leave_element = element_info[element].move_leave_element;
7208
7209         /* this makes it possible to leave the removed element again */
7210         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7211                              new_element : move_leave_element);
7212       }
7213 #endif
7214
7215       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7216       {
7217         RunnerVisit[x][y] = FrameCounter;
7218         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7219       }
7220     }
7221     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7222     {
7223       if (!IS_FREE(newx, newy))
7224       {
7225         if (IS_PLAYER(x, y))
7226           DrawPlayerField(x, y);
7227         else
7228           DrawLevelField(x, y);
7229
7230         return;
7231       }
7232       else
7233       {
7234         boolean wanna_flame = !RND(10);
7235         int dx = newx - x, dy = newy - y;
7236         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7237         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7238         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7239                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7240         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7241                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7242
7243         if ((wanna_flame ||
7244              IS_CLASSIC_ENEMY(element1) ||
7245              IS_CLASSIC_ENEMY(element2)) &&
7246             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7247             element1 != EL_FLAMES && element2 != EL_FLAMES)
7248         {
7249           ResetGfxAnimation(x, y);
7250           GfxAction[x][y] = ACTION_ATTACKING;
7251
7252           if (IS_PLAYER(x, y))
7253             DrawPlayerField(x, y);
7254           else
7255             DrawLevelField(x, y);
7256
7257           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7258
7259           MovDelay[x][y] = 50;
7260
7261           /* !!! */
7262 #if 0
7263           RemoveField(newx, newy);
7264 #endif
7265           Feld[newx][newy] = EL_FLAMES;
7266           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7267           {
7268 #if 0
7269             RemoveField(newx1, newy1);
7270 #endif
7271             Feld[newx1][newy1] = EL_FLAMES;
7272           }
7273           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7274           {
7275 #if 0
7276             RemoveField(newx2, newy2);
7277 #endif
7278             Feld[newx2][newy2] = EL_FLAMES;
7279           }
7280
7281           return;
7282         }
7283       }
7284     }
7285     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7286              Feld[newx][newy] == EL_DIAMOND)
7287     {
7288       if (IS_MOVING(newx, newy))
7289         RemoveMovingField(newx, newy);
7290       else
7291       {
7292         Feld[newx][newy] = EL_EMPTY;
7293         DrawLevelField(newx, newy);
7294       }
7295
7296       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7297     }
7298     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7299              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7300     {
7301       if (AmoebaNr[newx][newy])
7302       {
7303         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7304         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7305             Feld[newx][newy] == EL_BD_AMOEBA)
7306           AmoebaCnt[AmoebaNr[newx][newy]]--;
7307       }
7308
7309 #if 0
7310       /* !!! test !!! */
7311       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7312       {
7313         RemoveMovingField(newx, newy);
7314       }
7315 #else
7316       if (IS_MOVING(newx, newy))
7317       {
7318         RemoveMovingField(newx, newy);
7319       }
7320 #endif
7321       else
7322       {
7323         Feld[newx][newy] = EL_EMPTY;
7324         DrawLevelField(newx, newy);
7325       }
7326
7327       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7328     }
7329     else if ((element == EL_PACMAN || element == EL_MOLE)
7330              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7331     {
7332       if (AmoebaNr[newx][newy])
7333       {
7334         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7335         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7336             Feld[newx][newy] == EL_BD_AMOEBA)
7337           AmoebaCnt[AmoebaNr[newx][newy]]--;
7338       }
7339
7340       if (element == EL_MOLE)
7341       {
7342         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7343         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7344
7345         ResetGfxAnimation(x, y);
7346         GfxAction[x][y] = ACTION_DIGGING;
7347         DrawLevelField(x, y);
7348
7349         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7350
7351         return;                         /* wait for shrinking amoeba */
7352       }
7353       else      /* element == EL_PACMAN */
7354       {
7355         Feld[newx][newy] = EL_EMPTY;
7356         DrawLevelField(newx, newy);
7357         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7358       }
7359     }
7360     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7361              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7362               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7363     {
7364       /* wait for shrinking amoeba to completely disappear */
7365       return;
7366     }
7367     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7368     {
7369       /* object was running against a wall */
7370
7371       TurnRound(x, y);
7372
7373 #if 0
7374       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7375       if (move_pattern & MV_ANY_DIRECTION &&
7376           move_pattern == MovDir[x][y])
7377       {
7378         int blocking_element =
7379           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7380
7381         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7382                                  MovDir[x][y]);
7383
7384         element = Feld[x][y];   /* element might have changed */
7385       }
7386 #endif
7387
7388       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7389         DrawLevelElementAnimation(x, y, element);
7390
7391       if (DONT_TOUCH(element))
7392         TestIfBadThingTouchesPlayer(x, y);
7393
7394       return;
7395     }
7396
7397     InitMovingField(x, y, MovDir[x][y]);
7398
7399     PlayLevelSoundAction(x, y, ACTION_MOVING);
7400   }
7401
7402   if (MovDir[x][y])
7403     ContinueMoving(x, y);
7404 }
7405
7406 void ContinueMoving(int x, int y)
7407 {
7408   int element = Feld[x][y];
7409   struct ElementInfo *ei = &element_info[element];
7410   int direction = MovDir[x][y];
7411   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7412   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7413   int newx = x + dx, newy = y + dy;
7414   int stored = Store[x][y];
7415   int stored_new = Store[newx][newy];
7416   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7417   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7418   boolean last_line = (newy == lev_fieldy - 1);
7419
7420   MovPos[x][y] += getElementMoveStepsize(x, y);
7421
7422   if (pushed_by_player) /* special case: moving object pushed by player */
7423     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7424
7425   if (ABS(MovPos[x][y]) < TILEX)
7426   {
7427 #if 0
7428     int ee = Feld[x][y];
7429     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7430     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7431
7432     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7433            x, y, ABS(MovPos[x][y]),
7434            ee, gg, ff,
7435            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
7436 #endif
7437
7438     DrawLevelField(x, y);
7439
7440     return;     /* element is still moving */
7441   }
7442
7443   /* element reached destination field */
7444
7445   Feld[x][y] = EL_EMPTY;
7446   Feld[newx][newy] = element;
7447   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
7448
7449   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
7450   {
7451     element = Feld[newx][newy] = EL_ACID;
7452   }
7453   else if (element == EL_MOLE)
7454   {
7455     Feld[x][y] = EL_SAND;
7456
7457     DrawLevelFieldCrumbledSandNeighbours(x, y);
7458   }
7459   else if (element == EL_QUICKSAND_FILLING)
7460   {
7461     element = Feld[newx][newy] = get_next_element(element);
7462     Store[newx][newy] = Store[x][y];
7463   }
7464   else if (element == EL_QUICKSAND_EMPTYING)
7465   {
7466     Feld[x][y] = get_next_element(element);
7467     element = Feld[newx][newy] = Store[x][y];
7468   }
7469   else if (element == EL_QUICKSAND_FAST_FILLING)
7470   {
7471     element = Feld[newx][newy] = get_next_element(element);
7472     Store[newx][newy] = Store[x][y];
7473   }
7474   else if (element == EL_QUICKSAND_FAST_EMPTYING)
7475   {
7476     Feld[x][y] = get_next_element(element);
7477     element = Feld[newx][newy] = Store[x][y];
7478   }
7479   else if (element == EL_MAGIC_WALL_FILLING)
7480   {
7481     element = Feld[newx][newy] = get_next_element(element);
7482     if (!game.magic_wall_active)
7483       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7484     Store[newx][newy] = Store[x][y];
7485   }
7486   else if (element == EL_MAGIC_WALL_EMPTYING)
7487   {
7488     Feld[x][y] = get_next_element(element);
7489     if (!game.magic_wall_active)
7490       Feld[x][y] = EL_MAGIC_WALL_DEAD;
7491     element = Feld[newx][newy] = Store[x][y];
7492
7493 #if USE_NEW_CUSTOM_VALUE
7494     InitField(newx, newy, FALSE);
7495 #endif
7496   }
7497   else if (element == EL_BD_MAGIC_WALL_FILLING)
7498   {
7499     element = Feld[newx][newy] = get_next_element(element);
7500     if (!game.magic_wall_active)
7501       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
7502     Store[newx][newy] = Store[x][y];
7503   }
7504   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
7505   {
7506     Feld[x][y] = get_next_element(element);
7507     if (!game.magic_wall_active)
7508       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7509     element = Feld[newx][newy] = Store[x][y];
7510
7511 #if USE_NEW_CUSTOM_VALUE
7512     InitField(newx, newy, FALSE);
7513 #endif
7514   }
7515   else if (element == EL_DC_MAGIC_WALL_FILLING)
7516   {
7517     element = Feld[newx][newy] = get_next_element(element);
7518     if (!game.magic_wall_active)
7519       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
7520     Store[newx][newy] = Store[x][y];
7521   }
7522   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
7523   {
7524     Feld[x][y] = get_next_element(element);
7525     if (!game.magic_wall_active)
7526       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
7527     element = Feld[newx][newy] = Store[x][y];
7528
7529 #if USE_NEW_CUSTOM_VALUE
7530     InitField(newx, newy, FALSE);
7531 #endif
7532   }
7533   else if (element == EL_AMOEBA_DROPPING)
7534   {
7535     Feld[x][y] = get_next_element(element);
7536     element = Feld[newx][newy] = Store[x][y];
7537   }
7538   else if (element == EL_SOKOBAN_OBJECT)
7539   {
7540     if (Back[x][y])
7541       Feld[x][y] = Back[x][y];
7542
7543     if (Back[newx][newy])
7544       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
7545
7546     Back[x][y] = Back[newx][newy] = 0;
7547   }
7548
7549   Store[x][y] = EL_EMPTY;
7550   MovPos[x][y] = 0;
7551   MovDir[x][y] = 0;
7552   MovDelay[x][y] = 0;
7553
7554   MovDelay[newx][newy] = 0;
7555
7556   if (CAN_CHANGE_OR_HAS_ACTION(element))
7557   {
7558     /* copy element change control values to new field */
7559     ChangeDelay[newx][newy] = ChangeDelay[x][y];
7560     ChangePage[newx][newy]  = ChangePage[x][y];
7561     ChangeCount[newx][newy] = ChangeCount[x][y];
7562     ChangeEvent[newx][newy] = ChangeEvent[x][y];
7563   }
7564
7565 #if USE_NEW_CUSTOM_VALUE
7566     CustomValue[newx][newy] = CustomValue[x][y];
7567 #endif
7568
7569   ChangeDelay[x][y] = 0;
7570   ChangePage[x][y] = -1;
7571   ChangeCount[x][y] = 0;
7572   ChangeEvent[x][y] = -1;
7573
7574 #if USE_NEW_CUSTOM_VALUE
7575   CustomValue[x][y] = 0;
7576 #endif
7577
7578   /* copy animation control values to new field */
7579   GfxFrame[newx][newy]  = GfxFrame[x][y];
7580   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
7581   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
7582   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
7583
7584   Pushed[x][y] = Pushed[newx][newy] = FALSE;
7585
7586   /* some elements can leave other elements behind after moving */
7587 #if 1
7588   if (ei->move_leave_element != EL_EMPTY &&
7589       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7590       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7591 #else
7592   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7593       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7594       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7595 #endif
7596   {
7597     int move_leave_element = ei->move_leave_element;
7598
7599 #if 1
7600 #if 1
7601     /* this makes it possible to leave the removed element again */
7602     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7603       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7604 #else
7605     /* this makes it possible to leave the removed element again */
7606     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7607       move_leave_element = stored;
7608 #endif
7609 #else
7610     /* this makes it possible to leave the removed element again */
7611     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7612         ei->move_leave_element == EL_TRIGGER_ELEMENT)
7613       move_leave_element = stored;
7614 #endif
7615
7616     Feld[x][y] = move_leave_element;
7617
7618     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7619       MovDir[x][y] = direction;
7620
7621     InitField(x, y, FALSE);
7622
7623     if (GFX_CRUMBLED(Feld[x][y]))
7624       DrawLevelFieldCrumbledSandNeighbours(x, y);
7625
7626     if (ELEM_IS_PLAYER(move_leave_element))
7627       RelocatePlayer(x, y, move_leave_element);
7628   }
7629
7630   /* do this after checking for left-behind element */
7631   ResetGfxAnimation(x, y);      /* reset animation values for old field */
7632
7633   if (!CAN_MOVE(element) ||
7634       (CAN_FALL(element) && direction == MV_DOWN &&
7635        (element == EL_SPRING ||
7636         element_info[element].move_pattern == MV_WHEN_PUSHED ||
7637         element_info[element].move_pattern == MV_WHEN_DROPPED)))
7638     GfxDir[x][y] = MovDir[newx][newy] = 0;
7639
7640   DrawLevelField(x, y);
7641   DrawLevelField(newx, newy);
7642
7643   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
7644
7645   /* prevent pushed element from moving on in pushed direction */
7646   if (pushed_by_player && CAN_MOVE(element) &&
7647       element_info[element].move_pattern & MV_ANY_DIRECTION &&
7648       !(element_info[element].move_pattern & direction))
7649     TurnRound(newx, newy);
7650
7651   /* prevent elements on conveyor belt from moving on in last direction */
7652   if (pushed_by_conveyor && CAN_FALL(element) &&
7653       direction & MV_HORIZONTAL)
7654     MovDir[newx][newy] = 0;
7655
7656   if (!pushed_by_player)
7657   {
7658     int nextx = newx + dx, nexty = newy + dy;
7659     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
7660
7661     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
7662
7663     if (CAN_FALL(element) && direction == MV_DOWN)
7664       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
7665
7666     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
7667       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
7668
7669 #if USE_FIX_IMPACT_COLLISION
7670     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
7671       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
7672 #endif
7673   }
7674
7675   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
7676   {
7677     TestIfBadThingTouchesPlayer(newx, newy);
7678     TestIfBadThingTouchesFriend(newx, newy);
7679
7680     if (!IS_CUSTOM_ELEMENT(element))
7681       TestIfBadThingTouchesOtherBadThing(newx, newy);
7682   }
7683   else if (element == EL_PENGUIN)
7684     TestIfFriendTouchesBadThing(newx, newy);
7685
7686   /* give the player one last chance (one more frame) to move away */
7687   if (CAN_FALL(element) && direction == MV_DOWN &&
7688       (last_line || (!IS_FREE(x, newy + 1) &&
7689                      (!IS_PLAYER(x, newy + 1) ||
7690                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
7691     Impact(x, newy);
7692
7693   if (pushed_by_player && !game.use_change_when_pushing_bug)
7694   {
7695     int push_side = MV_DIR_OPPOSITE(direction);
7696     struct PlayerInfo *player = PLAYERINFO(x, y);
7697
7698     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
7699                                player->index_bit, push_side);
7700     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
7701                                         player->index_bit, push_side);
7702   }
7703
7704   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
7705     MovDelay[newx][newy] = 1;
7706
7707   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
7708
7709   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
7710
7711 #if 0
7712   if (ChangePage[newx][newy] != -1)             /* delayed change */
7713   {
7714     int page = ChangePage[newx][newy];
7715     struct ElementChangeInfo *change = &ei->change_page[page];
7716
7717     ChangePage[newx][newy] = -1;
7718
7719     if (change->can_change)
7720     {
7721       if (ChangeElement(newx, newy, element, page))
7722       {
7723         if (change->post_change_function)
7724           change->post_change_function(newx, newy);
7725       }
7726     }
7727
7728     if (change->has_action)
7729       ExecuteCustomElementAction(newx, newy, element, page);
7730   }
7731 #endif
7732
7733   TestIfElementHitsCustomElement(newx, newy, direction);
7734   TestIfPlayerTouchesCustomElement(newx, newy);
7735   TestIfElementTouchesCustomElement(newx, newy);
7736
7737   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7738       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7739     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7740                              MV_DIR_OPPOSITE(direction));
7741 }
7742
7743 int AmoebeNachbarNr(int ax, int ay)
7744 {
7745   int i;
7746   int element = Feld[ax][ay];
7747   int group_nr = 0;
7748   static int xy[4][2] =
7749   {
7750     { 0, -1 },
7751     { -1, 0 },
7752     { +1, 0 },
7753     { 0, +1 }
7754   };
7755
7756   for (i = 0; i < NUM_DIRECTIONS; i++)
7757   {
7758     int x = ax + xy[i][0];
7759     int y = ay + xy[i][1];
7760
7761     if (!IN_LEV_FIELD(x, y))
7762       continue;
7763
7764     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7765       group_nr = AmoebaNr[x][y];
7766   }
7767
7768   return group_nr;
7769 }
7770
7771 void AmoebenVereinigen(int ax, int ay)
7772 {
7773   int i, x, y, xx, yy;
7774   int new_group_nr = AmoebaNr[ax][ay];
7775   static int xy[4][2] =
7776   {
7777     { 0, -1 },
7778     { -1, 0 },
7779     { +1, 0 },
7780     { 0, +1 }
7781   };
7782
7783   if (new_group_nr == 0)
7784     return;
7785
7786   for (i = 0; i < NUM_DIRECTIONS; i++)
7787   {
7788     x = ax + xy[i][0];
7789     y = ay + xy[i][1];
7790
7791     if (!IN_LEV_FIELD(x, y))
7792       continue;
7793
7794     if ((Feld[x][y] == EL_AMOEBA_FULL ||
7795          Feld[x][y] == EL_BD_AMOEBA ||
7796          Feld[x][y] == EL_AMOEBA_DEAD) &&
7797         AmoebaNr[x][y] != new_group_nr)
7798     {
7799       int old_group_nr = AmoebaNr[x][y];
7800
7801       if (old_group_nr == 0)
7802         return;
7803
7804       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7805       AmoebaCnt[old_group_nr] = 0;
7806       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7807       AmoebaCnt2[old_group_nr] = 0;
7808
7809       SCAN_PLAYFIELD(xx, yy)
7810       {
7811         if (AmoebaNr[xx][yy] == old_group_nr)
7812           AmoebaNr[xx][yy] = new_group_nr;
7813       }
7814     }
7815   }
7816 }
7817
7818 void AmoebeUmwandeln(int ax, int ay)
7819 {
7820   int i, x, y;
7821
7822   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7823   {
7824     int group_nr = AmoebaNr[ax][ay];
7825
7826 #ifdef DEBUG
7827     if (group_nr == 0)
7828     {
7829       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7830       printf("AmoebeUmwandeln(): This should never happen!\n");
7831       return;
7832     }
7833 #endif
7834
7835     SCAN_PLAYFIELD(x, y)
7836     {
7837       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7838       {
7839         AmoebaNr[x][y] = 0;
7840         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7841       }
7842     }
7843
7844     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7845                             SND_AMOEBA_TURNING_TO_GEM :
7846                             SND_AMOEBA_TURNING_TO_ROCK));
7847     Bang(ax, ay);
7848   }
7849   else
7850   {
7851     static int xy[4][2] =
7852     {
7853       { 0, -1 },
7854       { -1, 0 },
7855       { +1, 0 },
7856       { 0, +1 }
7857     };
7858
7859     for (i = 0; i < NUM_DIRECTIONS; i++)
7860     {
7861       x = ax + xy[i][0];
7862       y = ay + xy[i][1];
7863
7864       if (!IN_LEV_FIELD(x, y))
7865         continue;
7866
7867       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7868       {
7869         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7870                               SND_AMOEBA_TURNING_TO_GEM :
7871                               SND_AMOEBA_TURNING_TO_ROCK));
7872         Bang(x, y);
7873       }
7874     }
7875   }
7876 }
7877
7878 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7879 {
7880   int x, y;
7881   int group_nr = AmoebaNr[ax][ay];
7882   boolean done = FALSE;
7883
7884 #ifdef DEBUG
7885   if (group_nr == 0)
7886   {
7887     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7888     printf("AmoebeUmwandelnBD(): This should never happen!\n");
7889     return;
7890   }
7891 #endif
7892
7893   SCAN_PLAYFIELD(x, y)
7894   {
7895     if (AmoebaNr[x][y] == group_nr &&
7896         (Feld[x][y] == EL_AMOEBA_DEAD ||
7897          Feld[x][y] == EL_BD_AMOEBA ||
7898          Feld[x][y] == EL_AMOEBA_GROWING))
7899     {
7900       AmoebaNr[x][y] = 0;
7901       Feld[x][y] = new_element;
7902       InitField(x, y, FALSE);
7903       DrawLevelField(x, y);
7904       done = TRUE;
7905     }
7906   }
7907
7908   if (done)
7909     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7910                             SND_BD_AMOEBA_TURNING_TO_ROCK :
7911                             SND_BD_AMOEBA_TURNING_TO_GEM));
7912 }
7913
7914 void AmoebeWaechst(int x, int y)
7915 {
7916   static unsigned long sound_delay = 0;
7917   static unsigned long sound_delay_value = 0;
7918
7919   if (!MovDelay[x][y])          /* start new growing cycle */
7920   {
7921     MovDelay[x][y] = 7;
7922
7923     if (DelayReached(&sound_delay, sound_delay_value))
7924     {
7925       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7926       sound_delay_value = 30;
7927     }
7928   }
7929
7930   if (MovDelay[x][y])           /* wait some time before growing bigger */
7931   {
7932     MovDelay[x][y]--;
7933     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7934     {
7935       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7936                                            6 - MovDelay[x][y]);
7937
7938       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7939     }
7940
7941     if (!MovDelay[x][y])
7942     {
7943       Feld[x][y] = Store[x][y];
7944       Store[x][y] = 0;
7945       DrawLevelField(x, y);
7946     }
7947   }
7948 }
7949
7950 void AmoebaDisappearing(int x, int y)
7951 {
7952   static unsigned long sound_delay = 0;
7953   static unsigned long sound_delay_value = 0;
7954
7955   if (!MovDelay[x][y])          /* start new shrinking cycle */
7956   {
7957     MovDelay[x][y] = 7;
7958
7959     if (DelayReached(&sound_delay, sound_delay_value))
7960       sound_delay_value = 30;
7961   }
7962
7963   if (MovDelay[x][y])           /* wait some time before shrinking */
7964   {
7965     MovDelay[x][y]--;
7966     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7967     {
7968       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7969                                            6 - MovDelay[x][y]);
7970
7971       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7972     }
7973
7974     if (!MovDelay[x][y])
7975     {
7976       Feld[x][y] = EL_EMPTY;
7977       DrawLevelField(x, y);
7978
7979       /* don't let mole enter this field in this cycle;
7980          (give priority to objects falling to this field from above) */
7981       Stop[x][y] = TRUE;
7982     }
7983   }
7984 }
7985
7986 void AmoebeAbleger(int ax, int ay)
7987 {
7988   int i;
7989   int element = Feld[ax][ay];
7990   int graphic = el2img(element);
7991   int newax = ax, neway = ay;
7992   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7993   static int xy[4][2] =
7994   {
7995     { 0, -1 },
7996     { -1, 0 },
7997     { +1, 0 },
7998     { 0, +1 }
7999   };
8000
8001   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8002   {
8003     Feld[ax][ay] = EL_AMOEBA_DEAD;
8004     DrawLevelField(ax, ay);
8005     return;
8006   }
8007
8008   if (IS_ANIMATED(graphic))
8009     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8010
8011   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8012     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8013
8014   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8015   {
8016     MovDelay[ax][ay]--;
8017     if (MovDelay[ax][ay])
8018       return;
8019   }
8020
8021   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8022   {
8023     int start = RND(4);
8024     int x = ax + xy[start][0];
8025     int y = ay + xy[start][1];
8026
8027     if (!IN_LEV_FIELD(x, y))
8028       return;
8029
8030     if (IS_FREE(x, y) ||
8031         CAN_GROW_INTO(Feld[x][y]) ||
8032         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8033         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8034     {
8035       newax = x;
8036       neway = y;
8037     }
8038
8039     if (newax == ax && neway == ay)
8040       return;
8041   }
8042   else                          /* normal or "filled" (BD style) amoeba */
8043   {
8044     int start = RND(4);
8045     boolean waiting_for_player = FALSE;
8046
8047     for (i = 0; i < NUM_DIRECTIONS; i++)
8048     {
8049       int j = (start + i) % 4;
8050       int x = ax + xy[j][0];
8051       int y = ay + xy[j][1];
8052
8053       if (!IN_LEV_FIELD(x, y))
8054         continue;
8055
8056       if (IS_FREE(x, y) ||
8057           CAN_GROW_INTO(Feld[x][y]) ||
8058           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8059           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8060       {
8061         newax = x;
8062         neway = y;
8063         break;
8064       }
8065       else if (IS_PLAYER(x, y))
8066         waiting_for_player = TRUE;
8067     }
8068
8069     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8070     {
8071       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8072       {
8073         Feld[ax][ay] = EL_AMOEBA_DEAD;
8074         DrawLevelField(ax, ay);
8075         AmoebaCnt[AmoebaNr[ax][ay]]--;
8076
8077         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8078         {
8079           if (element == EL_AMOEBA_FULL)
8080             AmoebeUmwandeln(ax, ay);
8081           else if (element == EL_BD_AMOEBA)
8082             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8083         }
8084       }
8085       return;
8086     }
8087     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8088     {
8089       /* amoeba gets larger by growing in some direction */
8090
8091       int new_group_nr = AmoebaNr[ax][ay];
8092
8093 #ifdef DEBUG
8094   if (new_group_nr == 0)
8095   {
8096     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8097     printf("AmoebeAbleger(): This should never happen!\n");
8098     return;
8099   }
8100 #endif
8101
8102       AmoebaNr[newax][neway] = new_group_nr;
8103       AmoebaCnt[new_group_nr]++;
8104       AmoebaCnt2[new_group_nr]++;
8105
8106       /* if amoeba touches other amoeba(s) after growing, unify them */
8107       AmoebenVereinigen(newax, neway);
8108
8109       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8110       {
8111         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8112         return;
8113       }
8114     }
8115   }
8116
8117   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8118       (neway == lev_fieldy - 1 && newax != ax))
8119   {
8120     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8121     Store[newax][neway] = element;
8122   }
8123   else if (neway == ay || element == EL_EMC_DRIPPER)
8124   {
8125     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8126
8127     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8128   }
8129   else
8130   {
8131     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8132     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8133     Store[ax][ay] = EL_AMOEBA_DROP;
8134     ContinueMoving(ax, ay);
8135     return;
8136   }
8137
8138   DrawLevelField(newax, neway);
8139 }
8140
8141 void Life(int ax, int ay)
8142 {
8143   int x1, y1, x2, y2;
8144   int life_time = 40;
8145   int element = Feld[ax][ay];
8146   int graphic = el2img(element);
8147   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8148                          level.biomaze);
8149   boolean changed = FALSE;
8150
8151   if (IS_ANIMATED(graphic))
8152     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8153
8154   if (Stop[ax][ay])
8155     return;
8156
8157   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8158     MovDelay[ax][ay] = life_time;
8159
8160   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8161   {
8162     MovDelay[ax][ay]--;
8163     if (MovDelay[ax][ay])
8164       return;
8165   }
8166
8167   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8168   {
8169     int xx = ax+x1, yy = ay+y1;
8170     int nachbarn = 0;
8171
8172     if (!IN_LEV_FIELD(xx, yy))
8173       continue;
8174
8175     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8176     {
8177       int x = xx+x2, y = yy+y2;
8178
8179       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8180         continue;
8181
8182       if (((Feld[x][y] == element ||
8183             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8184            !Stop[x][y]) ||
8185           (IS_FREE(x, y) && Stop[x][y]))
8186         nachbarn++;
8187     }
8188
8189     if (xx == ax && yy == ay)           /* field in the middle */
8190     {
8191       if (nachbarn < life_parameter[0] ||
8192           nachbarn > life_parameter[1])
8193       {
8194         Feld[xx][yy] = EL_EMPTY;
8195         if (!Stop[xx][yy])
8196           DrawLevelField(xx, yy);
8197         Stop[xx][yy] = TRUE;
8198         changed = TRUE;
8199       }
8200     }
8201     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8202     {                                   /* free border field */
8203       if (nachbarn >= life_parameter[2] &&
8204           nachbarn <= life_parameter[3])
8205       {
8206         Feld[xx][yy] = element;
8207         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8208         if (!Stop[xx][yy])
8209           DrawLevelField(xx, yy);
8210         Stop[xx][yy] = TRUE;
8211         changed = TRUE;
8212       }
8213     }
8214   }
8215
8216   if (changed)
8217     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8218                    SND_GAME_OF_LIFE_GROWING);
8219 }
8220
8221 static void InitRobotWheel(int x, int y)
8222 {
8223   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8224 }
8225
8226 static void RunRobotWheel(int x, int y)
8227 {
8228   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8229 }
8230
8231 static void StopRobotWheel(int x, int y)
8232 {
8233   if (ZX == x && ZY == y)
8234     ZX = ZY = -1;
8235 }
8236
8237 static void InitTimegateWheel(int x, int y)
8238 {
8239   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8240 }
8241
8242 static void RunTimegateWheel(int x, int y)
8243 {
8244   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8245 }
8246
8247 static void InitMagicBallDelay(int x, int y)
8248 {
8249 #if 1
8250   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8251 #else
8252   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8253 #endif
8254 }
8255
8256 static void ActivateMagicBall(int bx, int by)
8257 {
8258   int x, y;
8259
8260   if (level.ball_random)
8261   {
8262     int pos_border = RND(8);    /* select one of the eight border elements */
8263     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8264     int xx = pos_content % 3;
8265     int yy = pos_content / 3;
8266
8267     x = bx - 1 + xx;
8268     y = by - 1 + yy;
8269
8270     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8271       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8272   }
8273   else
8274   {
8275     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8276     {
8277       int xx = x - bx + 1;
8278       int yy = y - by + 1;
8279
8280       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8281         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8282     }
8283   }
8284
8285   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8286 }
8287
8288 void CheckExit(int x, int y)
8289 {
8290   if (local_player->gems_still_needed > 0 ||
8291       local_player->sokobanfields_still_needed > 0 ||
8292       local_player->lights_still_needed > 0)
8293   {
8294     int element = Feld[x][y];
8295     int graphic = el2img(element);
8296
8297     if (IS_ANIMATED(graphic))
8298       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8299
8300     return;
8301   }
8302
8303   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8304     return;
8305
8306   Feld[x][y] = EL_EXIT_OPENING;
8307
8308   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8309 }
8310
8311 void CheckExitEM(int x, int y)
8312 {
8313   if (local_player->gems_still_needed > 0 ||
8314       local_player->sokobanfields_still_needed > 0 ||
8315       local_player->lights_still_needed > 0)
8316   {
8317     int element = Feld[x][y];
8318     int graphic = el2img(element);
8319
8320     if (IS_ANIMATED(graphic))
8321       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8322
8323     return;
8324   }
8325
8326   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8327     return;
8328
8329   Feld[x][y] = EL_EM_EXIT_OPENING;
8330
8331   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8332 }
8333
8334 void CheckExitSteel(int x, int y)
8335 {
8336   if (local_player->gems_still_needed > 0 ||
8337       local_player->sokobanfields_still_needed > 0 ||
8338       local_player->lights_still_needed > 0)
8339   {
8340     int element = Feld[x][y];
8341     int graphic = el2img(element);
8342
8343     if (IS_ANIMATED(graphic))
8344       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8345
8346     return;
8347   }
8348
8349   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8350     return;
8351
8352   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8353
8354   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8355 }
8356
8357 void CheckExitSteelEM(int x, int y)
8358 {
8359   if (local_player->gems_still_needed > 0 ||
8360       local_player->sokobanfields_still_needed > 0 ||
8361       local_player->lights_still_needed > 0)
8362   {
8363     int element = Feld[x][y];
8364     int graphic = el2img(element);
8365
8366     if (IS_ANIMATED(graphic))
8367       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8368
8369     return;
8370   }
8371
8372   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8373     return;
8374
8375   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8376
8377   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8378 }
8379
8380 void CheckExitSP(int x, int y)
8381 {
8382   if (local_player->gems_still_needed > 0)
8383   {
8384     int element = Feld[x][y];
8385     int graphic = el2img(element);
8386
8387     if (IS_ANIMATED(graphic))
8388       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8389
8390     return;
8391   }
8392
8393   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8394     return;
8395
8396   Feld[x][y] = EL_SP_EXIT_OPENING;
8397
8398   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8399 }
8400
8401 static void CloseAllOpenTimegates()
8402 {
8403   int x, y;
8404
8405   SCAN_PLAYFIELD(x, y)
8406   {
8407     int element = Feld[x][y];
8408
8409     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8410     {
8411       Feld[x][y] = EL_TIMEGATE_CLOSING;
8412
8413       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8414     }
8415   }
8416 }
8417
8418 void DrawTwinkleOnField(int x, int y)
8419 {
8420   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8421     return;
8422
8423   if (Feld[x][y] == EL_BD_DIAMOND)
8424     return;
8425
8426   if (MovDelay[x][y] == 0)      /* next animation frame */
8427     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8428
8429   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8430   {
8431     MovDelay[x][y]--;
8432
8433     if (setup.direct_draw && MovDelay[x][y])
8434       SetDrawtoField(DRAW_BUFFERED);
8435
8436     DrawLevelElementAnimation(x, y, Feld[x][y]);
8437
8438     if (MovDelay[x][y] != 0)
8439     {
8440       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8441                                            10 - MovDelay[x][y]);
8442
8443       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8444
8445       if (setup.direct_draw)
8446       {
8447         int dest_x, dest_y;
8448
8449         dest_x = FX + SCREENX(x) * TILEX;
8450         dest_y = FY + SCREENY(y) * TILEY;
8451
8452         BlitBitmap(drawto_field, window,
8453                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8454         SetDrawtoField(DRAW_DIRECT);
8455       }
8456     }
8457   }
8458 }
8459
8460 void MauerWaechst(int x, int y)
8461 {
8462   int delay = 6;
8463
8464   if (!MovDelay[x][y])          /* next animation frame */
8465     MovDelay[x][y] = 3 * delay;
8466
8467   if (MovDelay[x][y])           /* wait some time before next frame */
8468   {
8469     MovDelay[x][y]--;
8470
8471     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8472     {
8473       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8474       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8475
8476       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8477     }
8478
8479     if (!MovDelay[x][y])
8480     {
8481       if (MovDir[x][y] == MV_LEFT)
8482       {
8483         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8484           DrawLevelField(x - 1, y);
8485       }
8486       else if (MovDir[x][y] == MV_RIGHT)
8487       {
8488         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8489           DrawLevelField(x + 1, y);
8490       }
8491       else if (MovDir[x][y] == MV_UP)
8492       {
8493         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8494           DrawLevelField(x, y - 1);
8495       }
8496       else
8497       {
8498         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8499           DrawLevelField(x, y + 1);
8500       }
8501
8502       Feld[x][y] = Store[x][y];
8503       Store[x][y] = 0;
8504       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8505       DrawLevelField(x, y);
8506     }
8507   }
8508 }
8509
8510 void MauerAbleger(int ax, int ay)
8511 {
8512   int element = Feld[ax][ay];
8513   int graphic = el2img(element);
8514   boolean oben_frei = FALSE, unten_frei = FALSE;
8515   boolean links_frei = FALSE, rechts_frei = FALSE;
8516   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8517   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8518   boolean new_wall = FALSE;
8519
8520   if (IS_ANIMATED(graphic))
8521     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8522
8523   if (!MovDelay[ax][ay])        /* start building new wall */
8524     MovDelay[ax][ay] = 6;
8525
8526   if (MovDelay[ax][ay])         /* wait some time before building new wall */
8527   {
8528     MovDelay[ax][ay]--;
8529     if (MovDelay[ax][ay])
8530       return;
8531   }
8532
8533   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8534     oben_frei = TRUE;
8535   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8536     unten_frei = TRUE;
8537   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8538     links_frei = TRUE;
8539   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8540     rechts_frei = TRUE;
8541
8542   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
8543       element == EL_EXPANDABLE_WALL_ANY)
8544   {
8545     if (oben_frei)
8546     {
8547       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
8548       Store[ax][ay-1] = element;
8549       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8550       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8551         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8552                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
8553       new_wall = TRUE;
8554     }
8555     if (unten_frei)
8556     {
8557       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
8558       Store[ax][ay+1] = element;
8559       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8560       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8561         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8562                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8563       new_wall = TRUE;
8564     }
8565   }
8566
8567   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8568       element == EL_EXPANDABLE_WALL_ANY ||
8569       element == EL_EXPANDABLE_WALL ||
8570       element == EL_BD_EXPANDABLE_WALL)
8571   {
8572     if (links_frei)
8573     {
8574       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8575       Store[ax-1][ay] = element;
8576       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8577       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8578         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8579                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8580       new_wall = TRUE;
8581     }
8582
8583     if (rechts_frei)
8584     {
8585       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8586       Store[ax+1][ay] = element;
8587       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8588       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8589         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8590                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8591       new_wall = TRUE;
8592     }
8593   }
8594
8595   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8596     DrawLevelField(ax, ay);
8597
8598   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8599     oben_massiv = TRUE;
8600   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8601     unten_massiv = TRUE;
8602   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8603     links_massiv = TRUE;
8604   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8605     rechts_massiv = TRUE;
8606
8607   if (((oben_massiv && unten_massiv) ||
8608        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8609        element == EL_EXPANDABLE_WALL) &&
8610       ((links_massiv && rechts_massiv) ||
8611        element == EL_EXPANDABLE_WALL_VERTICAL))
8612     Feld[ax][ay] = EL_WALL;
8613
8614   if (new_wall)
8615     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8616 }
8617
8618 void MauerAblegerStahl(int ax, int ay)
8619 {
8620   int element = Feld[ax][ay];
8621   int graphic = el2img(element);
8622   boolean oben_frei = FALSE, unten_frei = FALSE;
8623   boolean links_frei = FALSE, rechts_frei = FALSE;
8624   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8625   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8626   boolean new_wall = FALSE;
8627
8628   if (IS_ANIMATED(graphic))
8629     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8630
8631   if (!MovDelay[ax][ay])        /* start building new wall */
8632     MovDelay[ax][ay] = 6;
8633
8634   if (MovDelay[ax][ay])         /* wait some time before building new wall */
8635   {
8636     MovDelay[ax][ay]--;
8637     if (MovDelay[ax][ay])
8638       return;
8639   }
8640
8641   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8642     oben_frei = TRUE;
8643   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8644     unten_frei = TRUE;
8645   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8646     links_frei = TRUE;
8647   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8648     rechts_frei = TRUE;
8649
8650   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
8651       element == EL_EXPANDABLE_STEELWALL_ANY)
8652   {
8653     if (oben_frei)
8654     {
8655       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
8656       Store[ax][ay-1] = element;
8657       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8658       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8659         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8660                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
8661       new_wall = TRUE;
8662     }
8663     if (unten_frei)
8664     {
8665       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
8666       Store[ax][ay+1] = element;
8667       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8668       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8669         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8670                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
8671       new_wall = TRUE;
8672     }
8673   }
8674
8675   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
8676       element == EL_EXPANDABLE_STEELWALL_ANY)
8677   {
8678     if (links_frei)
8679     {
8680       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8681       Store[ax-1][ay] = element;
8682       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8683       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8684         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8685                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
8686       new_wall = TRUE;
8687     }
8688
8689     if (rechts_frei)
8690     {
8691       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8692       Store[ax+1][ay] = element;
8693       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8694       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8695         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8696                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
8697       new_wall = TRUE;
8698     }
8699   }
8700
8701   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8702     oben_massiv = TRUE;
8703   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8704     unten_massiv = TRUE;
8705   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8706     links_massiv = TRUE;
8707   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8708     rechts_massiv = TRUE;
8709
8710   if (((oben_massiv && unten_massiv) ||
8711        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
8712       ((links_massiv && rechts_massiv) ||
8713        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
8714     Feld[ax][ay] = EL_WALL;
8715
8716   if (new_wall)
8717     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8718 }
8719
8720 void CheckForDragon(int x, int y)
8721 {
8722   int i, j;
8723   boolean dragon_found = FALSE;
8724   static int xy[4][2] =
8725   {
8726     { 0, -1 },
8727     { -1, 0 },
8728     { +1, 0 },
8729     { 0, +1 }
8730   };
8731
8732   for (i = 0; i < NUM_DIRECTIONS; i++)
8733   {
8734     for (j = 0; j < 4; j++)
8735     {
8736       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8737
8738       if (IN_LEV_FIELD(xx, yy) &&
8739           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
8740       {
8741         if (Feld[xx][yy] == EL_DRAGON)
8742           dragon_found = TRUE;
8743       }
8744       else
8745         break;
8746     }
8747   }
8748
8749   if (!dragon_found)
8750   {
8751     for (i = 0; i < NUM_DIRECTIONS; i++)
8752     {
8753       for (j = 0; j < 3; j++)
8754       {
8755         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8756   
8757         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
8758         {
8759           Feld[xx][yy] = EL_EMPTY;
8760           DrawLevelField(xx, yy);
8761         }
8762         else
8763           break;
8764       }
8765     }
8766   }
8767 }
8768
8769 static void InitBuggyBase(int x, int y)
8770 {
8771   int element = Feld[x][y];
8772   int activating_delay = FRAMES_PER_SECOND / 4;
8773
8774   ChangeDelay[x][y] =
8775     (element == EL_SP_BUGGY_BASE ?
8776      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
8777      element == EL_SP_BUGGY_BASE_ACTIVATING ?
8778      activating_delay :
8779      element == EL_SP_BUGGY_BASE_ACTIVE ?
8780      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
8781 }
8782
8783 static void WarnBuggyBase(int x, int y)
8784 {
8785   int i;
8786   static int xy[4][2] =
8787   {
8788     { 0, -1 },
8789     { -1, 0 },
8790     { +1, 0 },
8791     { 0, +1 }
8792   };
8793
8794   for (i = 0; i < NUM_DIRECTIONS; i++)
8795   {
8796     int xx = x + xy[i][0];
8797     int yy = y + xy[i][1];
8798
8799     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
8800     {
8801       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8802
8803       break;
8804     }
8805   }
8806 }
8807
8808 static void InitTrap(int x, int y)
8809 {
8810   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8811 }
8812
8813 static void ActivateTrap(int x, int y)
8814 {
8815   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8816 }
8817
8818 static void ChangeActiveTrap(int x, int y)
8819 {
8820   int graphic = IMG_TRAP_ACTIVE;
8821
8822   /* if new animation frame was drawn, correct crumbled sand border */
8823   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8824     DrawLevelFieldCrumbledSand(x, y);
8825 }
8826
8827 static int getSpecialActionElement(int element, int number, int base_element)
8828 {
8829   return (element != EL_EMPTY ? element :
8830           number != -1 ? base_element + number - 1 :
8831           EL_EMPTY);
8832 }
8833
8834 static int getModifiedActionNumber(int value_old, int operator, int operand,
8835                                    int value_min, int value_max)
8836 {
8837   int value_new = (operator == CA_MODE_SET      ? operand :
8838                    operator == CA_MODE_ADD      ? value_old + operand :
8839                    operator == CA_MODE_SUBTRACT ? value_old - operand :
8840                    operator == CA_MODE_MULTIPLY ? value_old * operand :
8841                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
8842                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
8843                    value_old);
8844
8845   return (value_new < value_min ? value_min :
8846           value_new > value_max ? value_max :
8847           value_new);
8848 }
8849
8850 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8851 {
8852   struct ElementInfo *ei = &element_info[element];
8853   struct ElementChangeInfo *change = &ei->change_page[page];
8854   int target_element = change->target_element;
8855   int action_type = change->action_type;
8856   int action_mode = change->action_mode;
8857   int action_arg = change->action_arg;
8858   int i;
8859
8860   if (!change->has_action)
8861     return;
8862
8863   /* ---------- determine action paramater values -------------------------- */
8864
8865   int level_time_value =
8866     (level.time > 0 ? TimeLeft :
8867      TimePlayed);
8868
8869   int action_arg_element =
8870     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
8871      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8872      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
8873      EL_EMPTY);
8874
8875   int action_arg_direction =
8876     (action_arg >= CA_ARG_DIRECTION_LEFT &&
8877      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8878      action_arg == CA_ARG_DIRECTION_TRIGGER ?
8879      change->actual_trigger_side :
8880      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8881      MV_DIR_OPPOSITE(change->actual_trigger_side) :
8882      MV_NONE);
8883
8884   int action_arg_number_min =
8885     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8886      CA_ARG_MIN);
8887
8888   int action_arg_number_max =
8889     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8890      action_type == CA_SET_LEVEL_GEMS ? 999 :
8891      action_type == CA_SET_LEVEL_TIME ? 9999 :
8892      action_type == CA_SET_LEVEL_SCORE ? 99999 :
8893      action_type == CA_SET_CE_VALUE ? 9999 :
8894      action_type == CA_SET_CE_SCORE ? 9999 :
8895      CA_ARG_MAX);
8896
8897   int action_arg_number_reset =
8898     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8899      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8900      action_type == CA_SET_LEVEL_TIME ? level.time :
8901      action_type == CA_SET_LEVEL_SCORE ? 0 :
8902 #if USE_NEW_CUSTOM_VALUE
8903      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8904 #else
8905      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8906 #endif
8907      action_type == CA_SET_CE_SCORE ? 0 :
8908      0);
8909
8910   int action_arg_number =
8911     (action_arg <= CA_ARG_MAX ? action_arg :
8912      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8913      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8914      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8915      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8916      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8917      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8918 #if USE_NEW_CUSTOM_VALUE
8919      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8920 #else
8921      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8922 #endif
8923      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8924      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8925      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8926      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8927      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8928      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8929      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8930      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8931      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8932      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
8933      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8934      -1);
8935
8936   int action_arg_number_old =
8937     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8938      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8939      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8940      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8941      action_type == CA_SET_CE_SCORE ? ei->collect_score :
8942      0);
8943
8944   int action_arg_number_new =
8945     getModifiedActionNumber(action_arg_number_old,
8946                             action_mode, action_arg_number,
8947                             action_arg_number_min, action_arg_number_max);
8948
8949   int trigger_player_bits =
8950     (change->actual_trigger_player >= EL_PLAYER_1 &&
8951      change->actual_trigger_player <= EL_PLAYER_4 ?
8952      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8953      PLAYER_BITS_ANY);
8954
8955   int action_arg_player_bits =
8956     (action_arg >= CA_ARG_PLAYER_1 &&
8957      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8958      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8959      PLAYER_BITS_ANY);
8960
8961   /* ---------- execute action  -------------------------------------------- */
8962
8963   switch (action_type)
8964   {
8965     case CA_NO_ACTION:
8966     {
8967       return;
8968     }
8969
8970     /* ---------- level actions  ------------------------------------------- */
8971
8972     case CA_RESTART_LEVEL:
8973     {
8974       game.restart_level = TRUE;
8975
8976       break;
8977     }
8978
8979     case CA_SHOW_ENVELOPE:
8980     {
8981       int element = getSpecialActionElement(action_arg_element,
8982                                             action_arg_number, EL_ENVELOPE_1);
8983
8984       if (IS_ENVELOPE(element))
8985         local_player->show_envelope = element;
8986
8987       break;
8988     }
8989
8990     case CA_SET_LEVEL_TIME:
8991     {
8992       if (level.time > 0)       /* only modify limited time value */
8993       {
8994         TimeLeft = action_arg_number_new;
8995
8996         DrawGameValue_Time(TimeLeft);
8997
8998         if (!TimeLeft && setup.time_limit)
8999           for (i = 0; i < MAX_PLAYERS; i++)
9000             KillPlayer(&stored_player[i]);
9001       }
9002
9003       break;
9004     }
9005
9006     case CA_SET_LEVEL_SCORE:
9007     {
9008       local_player->score = action_arg_number_new;
9009
9010       DrawGameValue_Score(local_player->score);
9011
9012       break;
9013     }
9014
9015     case CA_SET_LEVEL_GEMS:
9016     {
9017       local_player->gems_still_needed = action_arg_number_new;
9018
9019       DrawGameValue_Emeralds(local_player->gems_still_needed);
9020
9021       break;
9022     }
9023
9024 #if !USE_PLAYER_GRAVITY
9025     case CA_SET_LEVEL_GRAVITY:
9026     {
9027       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
9028                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
9029                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9030                       game.gravity);
9031       break;
9032     }
9033 #endif
9034
9035     case CA_SET_LEVEL_WIND:
9036     {
9037       game.wind_direction = action_arg_direction;
9038
9039       break;
9040     }
9041
9042     /* ---------- player actions  ------------------------------------------ */
9043
9044     case CA_MOVE_PLAYER:
9045     {
9046       /* automatically move to the next field in specified direction */
9047       for (i = 0; i < MAX_PLAYERS; i++)
9048         if (trigger_player_bits & (1 << i))
9049           stored_player[i].programmed_action = action_arg_direction;
9050
9051       break;
9052     }
9053
9054     case CA_EXIT_PLAYER:
9055     {
9056       for (i = 0; i < MAX_PLAYERS; i++)
9057         if (action_arg_player_bits & (1 << i))
9058           PlayerWins(&stored_player[i]);
9059
9060       break;
9061     }
9062
9063     case CA_KILL_PLAYER:
9064     {
9065       for (i = 0; i < MAX_PLAYERS; i++)
9066         if (action_arg_player_bits & (1 << i))
9067           KillPlayer(&stored_player[i]);
9068
9069       break;
9070     }
9071
9072     case CA_SET_PLAYER_KEYS:
9073     {
9074       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9075       int element = getSpecialActionElement(action_arg_element,
9076                                             action_arg_number, EL_KEY_1);
9077
9078       if (IS_KEY(element))
9079       {
9080         for (i = 0; i < MAX_PLAYERS; i++)
9081         {
9082           if (trigger_player_bits & (1 << i))
9083           {
9084             stored_player[i].key[KEY_NR(element)] = key_state;
9085
9086             DrawGameDoorValues();
9087           }
9088         }
9089       }
9090
9091       break;
9092     }
9093
9094     case CA_SET_PLAYER_SPEED:
9095     {
9096       for (i = 0; i < MAX_PLAYERS; i++)
9097       {
9098         if (trigger_player_bits & (1 << i))
9099         {
9100           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9101
9102           if (action_arg == CA_ARG_SPEED_FASTER &&
9103               stored_player[i].cannot_move)
9104           {
9105             action_arg_number = STEPSIZE_VERY_SLOW;
9106           }
9107           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9108                    action_arg == CA_ARG_SPEED_FASTER)
9109           {
9110             action_arg_number = 2;
9111             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9112                            CA_MODE_MULTIPLY);
9113           }
9114           else if (action_arg == CA_ARG_NUMBER_RESET)
9115           {
9116             action_arg_number = level.initial_player_stepsize[i];
9117           }
9118
9119           move_stepsize =
9120             getModifiedActionNumber(move_stepsize,
9121                                     action_mode,
9122                                     action_arg_number,
9123                                     action_arg_number_min,
9124                                     action_arg_number_max);
9125
9126           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9127         }
9128       }
9129
9130       break;
9131     }
9132
9133     case CA_SET_PLAYER_SHIELD:
9134     {
9135       for (i = 0; i < MAX_PLAYERS; i++)
9136       {
9137         if (trigger_player_bits & (1 << i))
9138         {
9139           if (action_arg == CA_ARG_SHIELD_OFF)
9140           {
9141             stored_player[i].shield_normal_time_left = 0;
9142             stored_player[i].shield_deadly_time_left = 0;
9143           }
9144           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9145           {
9146             stored_player[i].shield_normal_time_left = 999999;
9147           }
9148           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9149           {
9150             stored_player[i].shield_normal_time_left = 999999;
9151             stored_player[i].shield_deadly_time_left = 999999;
9152           }
9153         }
9154       }
9155
9156       break;
9157     }
9158
9159 #if USE_PLAYER_GRAVITY
9160     case CA_SET_PLAYER_GRAVITY:
9161     {
9162       for (i = 0; i < MAX_PLAYERS; i++)
9163       {
9164         if (trigger_player_bits & (1 << i))
9165         {
9166           stored_player[i].gravity =
9167             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9168              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9169              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9170              stored_player[i].gravity);
9171         }
9172       }
9173
9174       break;
9175     }
9176 #endif
9177
9178     case CA_SET_PLAYER_ARTWORK:
9179     {
9180       for (i = 0; i < MAX_PLAYERS; i++)
9181       {
9182         if (trigger_player_bits & (1 << i))
9183         {
9184           int artwork_element = action_arg_element;
9185
9186           if (action_arg == CA_ARG_ELEMENT_RESET)
9187             artwork_element =
9188               (level.use_artwork_element[i] ? level.artwork_element[i] :
9189                stored_player[i].element_nr);
9190
9191 #if USE_GFX_RESET_PLAYER_ARTWORK
9192           if (stored_player[i].artwork_element != artwork_element)
9193             stored_player[i].Frame = 0;
9194 #endif
9195
9196           stored_player[i].artwork_element = artwork_element;
9197
9198           SetPlayerWaiting(&stored_player[i], FALSE);
9199
9200           /* set number of special actions for bored and sleeping animation */
9201           stored_player[i].num_special_action_bored =
9202             get_num_special_action(artwork_element,
9203                                    ACTION_BORING_1, ACTION_BORING_LAST);
9204           stored_player[i].num_special_action_sleeping =
9205             get_num_special_action(artwork_element,
9206                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9207         }
9208       }
9209
9210       break;
9211     }
9212
9213     /* ---------- CE actions  ---------------------------------------------- */
9214
9215     case CA_SET_CE_VALUE:
9216     {
9217 #if USE_NEW_CUSTOM_VALUE
9218       int last_ce_value = CustomValue[x][y];
9219
9220       CustomValue[x][y] = action_arg_number_new;
9221
9222       if (CustomValue[x][y] != last_ce_value)
9223       {
9224         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9225         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9226
9227         if (CustomValue[x][y] == 0)
9228         {
9229           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9230           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9231         }
9232       }
9233 #endif
9234
9235       break;
9236     }
9237
9238     case CA_SET_CE_SCORE:
9239     {
9240 #if USE_NEW_CUSTOM_VALUE
9241       int last_ce_score = ei->collect_score;
9242
9243       ei->collect_score = action_arg_number_new;
9244
9245       if (ei->collect_score != last_ce_score)
9246       {
9247         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9248         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9249
9250         if (ei->collect_score == 0)
9251         {
9252           int xx, yy;
9253
9254           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9255           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9256
9257           /*
9258             This is a very special case that seems to be a mixture between
9259             CheckElementChange() and CheckTriggeredElementChange(): while
9260             the first one only affects single elements that are triggered
9261             directly, the second one affects multiple elements in the playfield
9262             that are triggered indirectly by another element. This is a third
9263             case: Changing the CE score always affects multiple identical CEs,
9264             so every affected CE must be checked, not only the single CE for
9265             which the CE score was changed in the first place (as every instance
9266             of that CE shares the same CE score, and therefore also can change)!
9267           */
9268           SCAN_PLAYFIELD(xx, yy)
9269           {
9270             if (Feld[xx][yy] == element)
9271               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9272                                  CE_SCORE_GETS_ZERO);
9273           }
9274         }
9275       }
9276 #endif
9277
9278       break;
9279     }
9280
9281     /* ---------- engine actions  ------------------------------------------ */
9282
9283     case CA_SET_ENGINE_SCAN_MODE:
9284     {
9285       InitPlayfieldScanMode(action_arg);
9286
9287       break;
9288     }
9289
9290     default:
9291       break;
9292   }
9293 }
9294
9295 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9296 {
9297   int old_element = Feld[x][y];
9298   int new_element = GetElementFromGroupElement(element);
9299   int previous_move_direction = MovDir[x][y];
9300 #if USE_NEW_CUSTOM_VALUE
9301   int last_ce_value = CustomValue[x][y];
9302 #endif
9303   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9304   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9305   boolean add_player_onto_element = (new_element_is_player &&
9306 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9307                                      /* this breaks SnakeBite when a snake is
9308                                         halfway through a door that closes */
9309                                      /* NOW FIXED AT LEVEL INIT IN files.c */
9310                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9311 #endif
9312                                      IS_WALKABLE(old_element));
9313
9314 #if 0
9315   /* check if element under the player changes from accessible to unaccessible
9316      (needed for special case of dropping element which then changes) */
9317   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9318       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9319   {
9320     Bang(x, y);
9321
9322     return;
9323   }
9324 #endif
9325
9326   if (!add_player_onto_element)
9327   {
9328     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9329       RemoveMovingField(x, y);
9330     else
9331       RemoveField(x, y);
9332
9333     Feld[x][y] = new_element;
9334
9335 #if !USE_GFX_RESET_GFX_ANIMATION
9336     ResetGfxAnimation(x, y);
9337     ResetRandomAnimationValue(x, y);
9338 #endif
9339
9340     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9341       MovDir[x][y] = previous_move_direction;
9342
9343 #if USE_NEW_CUSTOM_VALUE
9344     if (element_info[new_element].use_last_ce_value)
9345       CustomValue[x][y] = last_ce_value;
9346 #endif
9347
9348     InitField_WithBug1(x, y, FALSE);
9349
9350     new_element = Feld[x][y];   /* element may have changed */
9351
9352 #if USE_GFX_RESET_GFX_ANIMATION
9353     ResetGfxAnimation(x, y);
9354     ResetRandomAnimationValue(x, y);
9355 #endif
9356
9357     DrawLevelField(x, y);
9358
9359     if (GFX_CRUMBLED(new_element))
9360       DrawLevelFieldCrumbledSandNeighbours(x, y);
9361   }
9362
9363 #if 1
9364   /* check if element under the player changes from accessible to unaccessible
9365      (needed for special case of dropping element which then changes) */
9366   /* (must be checked after creating new element for walkable group elements) */
9367 #if USE_FIX_KILLED_BY_NON_WALKABLE
9368   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9369       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9370   {
9371     Bang(x, y);
9372
9373     return;
9374   }
9375 #else
9376   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9377       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9378   {
9379     Bang(x, y);
9380
9381     return;
9382   }
9383 #endif
9384 #endif
9385
9386   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9387   if (new_element_is_player)
9388     RelocatePlayer(x, y, new_element);
9389
9390   if (is_change)
9391     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9392
9393   TestIfBadThingTouchesPlayer(x, y);
9394   TestIfPlayerTouchesCustomElement(x, y);
9395   TestIfElementTouchesCustomElement(x, y);
9396 }
9397
9398 static void CreateField(int x, int y, int element)
9399 {
9400   CreateFieldExt(x, y, element, FALSE);
9401 }
9402
9403 static void CreateElementFromChange(int x, int y, int element)
9404 {
9405   element = GET_VALID_RUNTIME_ELEMENT(element);
9406
9407 #if USE_STOP_CHANGED_ELEMENTS
9408   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9409   {
9410     int old_element = Feld[x][y];
9411
9412     /* prevent changed element from moving in same engine frame
9413        unless both old and new element can either fall or move */
9414     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9415         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9416       Stop[x][y] = TRUE;
9417   }
9418 #endif
9419
9420   CreateFieldExt(x, y, element, TRUE);
9421 }
9422
9423 static boolean ChangeElement(int x, int y, int element, int page)
9424 {
9425   struct ElementInfo *ei = &element_info[element];
9426   struct ElementChangeInfo *change = &ei->change_page[page];
9427   int ce_value = CustomValue[x][y];
9428   int ce_score = ei->collect_score;
9429   int target_element;
9430   int old_element = Feld[x][y];
9431
9432   /* always use default change event to prevent running into a loop */
9433   if (ChangeEvent[x][y] == -1)
9434     ChangeEvent[x][y] = CE_DELAY;
9435
9436   if (ChangeEvent[x][y] == CE_DELAY)
9437   {
9438     /* reset actual trigger element, trigger player and action element */
9439     change->actual_trigger_element = EL_EMPTY;
9440     change->actual_trigger_player = EL_PLAYER_1;
9441     change->actual_trigger_side = CH_SIDE_NONE;
9442     change->actual_trigger_ce_value = 0;
9443     change->actual_trigger_ce_score = 0;
9444   }
9445
9446   /* do not change elements more than a specified maximum number of changes */
9447   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
9448     return FALSE;
9449
9450   ChangeCount[x][y]++;          /* count number of changes in the same frame */
9451
9452   if (change->explode)
9453   {
9454     Bang(x, y);
9455
9456     return TRUE;
9457   }
9458
9459   if (change->use_target_content)
9460   {
9461     boolean complete_replace = TRUE;
9462     boolean can_replace[3][3];
9463     int xx, yy;
9464
9465     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9466     {
9467       boolean is_empty;
9468       boolean is_walkable;
9469       boolean is_diggable;
9470       boolean is_collectible;
9471       boolean is_removable;
9472       boolean is_destructible;
9473       int ex = x + xx - 1;
9474       int ey = y + yy - 1;
9475       int content_element = change->target_content.e[xx][yy];
9476       int e;
9477
9478       can_replace[xx][yy] = TRUE;
9479
9480       if (ex == x && ey == y)   /* do not check changing element itself */
9481         continue;
9482
9483       if (content_element == EL_EMPTY_SPACE)
9484       {
9485         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
9486
9487         continue;
9488       }
9489
9490       if (!IN_LEV_FIELD(ex, ey))
9491       {
9492         can_replace[xx][yy] = FALSE;
9493         complete_replace = FALSE;
9494
9495         continue;
9496       }
9497
9498       e = Feld[ex][ey];
9499
9500       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9501         e = MovingOrBlocked2Element(ex, ey);
9502
9503       is_empty = (IS_FREE(ex, ey) ||
9504                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
9505
9506       is_walkable     = (is_empty || IS_WALKABLE(e));
9507       is_diggable     = (is_empty || IS_DIGGABLE(e));
9508       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
9509       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
9510       is_removable    = (is_diggable || is_collectible);
9511
9512       can_replace[xx][yy] =
9513         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
9514           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
9515           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
9516           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
9517           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
9518           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
9519          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
9520
9521       if (!can_replace[xx][yy])
9522         complete_replace = FALSE;
9523     }
9524
9525     if (!change->only_if_complete || complete_replace)
9526     {
9527       boolean something_has_changed = FALSE;
9528
9529       if (change->only_if_complete && change->use_random_replace &&
9530           RND(100) < change->random_percentage)
9531         return FALSE;
9532
9533       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9534       {
9535         int ex = x + xx - 1;
9536         int ey = y + yy - 1;
9537         int content_element;
9538
9539         if (can_replace[xx][yy] && (!change->use_random_replace ||
9540                                     RND(100) < change->random_percentage))
9541         {
9542           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9543             RemoveMovingField(ex, ey);
9544
9545           ChangeEvent[ex][ey] = ChangeEvent[x][y];
9546
9547           content_element = change->target_content.e[xx][yy];
9548           target_element = GET_TARGET_ELEMENT(element, content_element, change,
9549                                               ce_value, ce_score);
9550
9551           CreateElementFromChange(ex, ey, target_element);
9552
9553           something_has_changed = TRUE;
9554
9555           /* for symmetry reasons, freeze newly created border elements */
9556           if (ex != x || ey != y)
9557             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
9558         }
9559       }
9560
9561       if (something_has_changed)
9562       {
9563         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9564         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9565       }
9566     }
9567   }
9568   else
9569   {
9570     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9571                                         ce_value, ce_score);
9572
9573     if (element == EL_DIAGONAL_GROWING ||
9574         element == EL_DIAGONAL_SHRINKING)
9575     {
9576       target_element = Store[x][y];
9577
9578       Store[x][y] = EL_EMPTY;
9579     }
9580
9581     CreateElementFromChange(x, y, target_element);
9582
9583     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9584     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9585   }
9586
9587   /* this uses direct change before indirect change */
9588   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9589
9590   return TRUE;
9591 }
9592
9593 #if USE_NEW_DELAYED_ACTION
9594
9595 static void HandleElementChange(int x, int y, int page)
9596 {
9597   int element = MovingOrBlocked2Element(x, y);
9598   struct ElementInfo *ei = &element_info[element];
9599   struct ElementChangeInfo *change = &ei->change_page[page];
9600
9601 #ifdef DEBUG
9602   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
9603       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
9604   {
9605     printf("\n\n");
9606     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9607            x, y, element, element_info[element].token_name);
9608     printf("HandleElementChange(): This should never happen!\n");
9609     printf("\n\n");
9610   }
9611 #endif
9612
9613   /* this can happen with classic bombs on walkable, changing elements */
9614   if (!CAN_CHANGE_OR_HAS_ACTION(element))
9615   {
9616 #if 0
9617     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9618       ChangeDelay[x][y] = 0;
9619 #endif
9620
9621     return;
9622   }
9623
9624   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9625   {
9626     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9627
9628     if (change->can_change)
9629     {
9630 #if 1
9631       /* !!! not clear why graphic animation should be reset at all here !!! */
9632       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
9633 #if USE_GFX_RESET_WHEN_NOT_MOVING
9634       /* when a custom element is about to change (for example by change delay),
9635          do not reset graphic animation when the custom element is moving */
9636       if (!IS_MOVING(x, y))
9637 #endif
9638       {
9639         ResetGfxAnimation(x, y);
9640         ResetRandomAnimationValue(x, y);
9641       }
9642 #endif
9643
9644       if (change->pre_change_function)
9645         change->pre_change_function(x, y);
9646     }
9647   }
9648
9649   ChangeDelay[x][y]--;
9650
9651   if (ChangeDelay[x][y] != 0)           /* continue element change */
9652   {
9653     if (change->can_change)
9654     {
9655       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9656
9657       if (IS_ANIMATED(graphic))
9658         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9659
9660       if (change->change_function)
9661         change->change_function(x, y);
9662     }
9663   }
9664   else                                  /* finish element change */
9665   {
9666     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9667     {
9668       page = ChangePage[x][y];
9669       ChangePage[x][y] = -1;
9670
9671       change = &ei->change_page[page];
9672     }
9673
9674     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9675     {
9676       ChangeDelay[x][y] = 1;            /* try change after next move step */
9677       ChangePage[x][y] = page;          /* remember page to use for change */
9678
9679       return;
9680     }
9681
9682     if (change->can_change)
9683     {
9684       if (ChangeElement(x, y, element, page))
9685       {
9686         if (change->post_change_function)
9687           change->post_change_function(x, y);
9688       }
9689     }
9690
9691     if (change->has_action)
9692       ExecuteCustomElementAction(x, y, element, page);
9693   }
9694 }
9695
9696 #else
9697
9698 static void HandleElementChange(int x, int y, int page)
9699 {
9700   int element = MovingOrBlocked2Element(x, y);
9701   struct ElementInfo *ei = &element_info[element];
9702   struct ElementChangeInfo *change = &ei->change_page[page];
9703
9704 #ifdef DEBUG
9705   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
9706   {
9707     printf("\n\n");
9708     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9709            x, y, element, element_info[element].token_name);
9710     printf("HandleElementChange(): This should never happen!\n");
9711     printf("\n\n");
9712   }
9713 #endif
9714
9715   /* this can happen with classic bombs on walkable, changing elements */
9716   if (!CAN_CHANGE(element))
9717   {
9718 #if 0
9719     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9720       ChangeDelay[x][y] = 0;
9721 #endif
9722
9723     return;
9724   }
9725
9726   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9727   {
9728     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9729
9730     ResetGfxAnimation(x, y);
9731     ResetRandomAnimationValue(x, y);
9732
9733     if (change->pre_change_function)
9734       change->pre_change_function(x, y);
9735   }
9736
9737   ChangeDelay[x][y]--;
9738
9739   if (ChangeDelay[x][y] != 0)           /* continue element change */
9740   {
9741     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9742
9743     if (IS_ANIMATED(graphic))
9744       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9745
9746     if (change->change_function)
9747       change->change_function(x, y);
9748   }
9749   else                                  /* finish element change */
9750   {
9751     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9752     {
9753       page = ChangePage[x][y];
9754       ChangePage[x][y] = -1;
9755
9756       change = &ei->change_page[page];
9757     }
9758
9759     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9760     {
9761       ChangeDelay[x][y] = 1;            /* try change after next move step */
9762       ChangePage[x][y] = page;          /* remember page to use for change */
9763
9764       return;
9765     }
9766
9767     if (ChangeElement(x, y, element, page))
9768     {
9769       if (change->post_change_function)
9770         change->post_change_function(x, y);
9771     }
9772   }
9773 }
9774
9775 #endif
9776
9777 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
9778                                               int trigger_element,
9779                                               int trigger_event,
9780                                               int trigger_player,
9781                                               int trigger_side,
9782                                               int trigger_page)
9783 {
9784   boolean change_done_any = FALSE;
9785   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9786   int i;
9787
9788   if (!(trigger_events[trigger_element][trigger_event]))
9789     return FALSE;
9790
9791 #if 0
9792   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9793          trigger_event, recursion_loop_depth, recursion_loop_detected,
9794          recursion_loop_element, EL_NAME(recursion_loop_element));
9795 #endif
9796
9797   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9798
9799   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9800   {
9801     int element = EL_CUSTOM_START + i;
9802     boolean change_done = FALSE;
9803     int p;
9804
9805     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9806         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9807       continue;
9808
9809     for (p = 0; p < element_info[element].num_change_pages; p++)
9810     {
9811       struct ElementChangeInfo *change = &element_info[element].change_page[p];
9812
9813       if (change->can_change_or_has_action &&
9814           change->has_event[trigger_event] &&
9815           change->trigger_side & trigger_side &&
9816           change->trigger_player & trigger_player &&
9817           change->trigger_page & trigger_page_bits &&
9818           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9819       {
9820         change->actual_trigger_element = trigger_element;
9821         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9822         change->actual_trigger_side = trigger_side;
9823         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9824         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9825
9826         if ((change->can_change && !change_done) || change->has_action)
9827         {
9828           int x, y;
9829
9830           SCAN_PLAYFIELD(x, y)
9831           {
9832             if (Feld[x][y] == element)
9833             {
9834               if (change->can_change && !change_done)
9835               {
9836                 ChangeDelay[x][y] = 1;
9837                 ChangeEvent[x][y] = trigger_event;
9838
9839                 HandleElementChange(x, y, p);
9840               }
9841 #if USE_NEW_DELAYED_ACTION
9842               else if (change->has_action)
9843               {
9844                 ExecuteCustomElementAction(x, y, element, p);
9845                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9846               }
9847 #else
9848               if (change->has_action)
9849               {
9850                 ExecuteCustomElementAction(x, y, element, p);
9851                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9852               }
9853 #endif
9854             }
9855           }
9856
9857           if (change->can_change)
9858           {
9859             change_done = TRUE;
9860             change_done_any = TRUE;
9861           }
9862         }
9863       }
9864     }
9865   }
9866
9867   RECURSION_LOOP_DETECTION_END();
9868
9869   return change_done_any;
9870 }
9871
9872 static boolean CheckElementChangeExt(int x, int y,
9873                                      int element,
9874                                      int trigger_element,
9875                                      int trigger_event,
9876                                      int trigger_player,
9877                                      int trigger_side)
9878 {
9879   boolean change_done = FALSE;
9880   int p;
9881
9882   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9883       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9884     return FALSE;
9885
9886   if (Feld[x][y] == EL_BLOCKED)
9887   {
9888     Blocked2Moving(x, y, &x, &y);
9889     element = Feld[x][y];
9890   }
9891
9892 #if 0
9893   /* check if element has already changed */
9894   if (Feld[x][y] != element)
9895     return FALSE;
9896 #else
9897   /* check if element has already changed or is about to change after moving */
9898   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9899        Feld[x][y] != element) ||
9900
9901       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9902        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9903         ChangePage[x][y] != -1)))
9904     return FALSE;
9905 #endif
9906
9907 #if 0
9908   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9909          trigger_event, recursion_loop_depth, recursion_loop_detected,
9910          recursion_loop_element, EL_NAME(recursion_loop_element));
9911 #endif
9912
9913   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9914
9915   for (p = 0; p < element_info[element].num_change_pages; p++)
9916   {
9917     struct ElementChangeInfo *change = &element_info[element].change_page[p];
9918
9919     /* check trigger element for all events where the element that is checked
9920        for changing interacts with a directly adjacent element -- this is
9921        different to element changes that affect other elements to change on the
9922        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
9923     boolean check_trigger_element =
9924       (trigger_event == CE_TOUCHING_X ||
9925        trigger_event == CE_HITTING_X ||
9926        trigger_event == CE_HIT_BY_X ||
9927 #if 1
9928        /* this one was forgotten until 3.2.3 */
9929        trigger_event == CE_DIGGING_X);
9930 #endif
9931
9932     if (change->can_change_or_has_action &&
9933         change->has_event[trigger_event] &&
9934         change->trigger_side & trigger_side &&
9935         change->trigger_player & trigger_player &&
9936         (!check_trigger_element ||
9937          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9938     {
9939       change->actual_trigger_element = trigger_element;
9940       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9941       change->actual_trigger_side = trigger_side;
9942       change->actual_trigger_ce_value = CustomValue[x][y];
9943       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9944
9945       /* special case: trigger element not at (x,y) position for some events */
9946       if (check_trigger_element)
9947       {
9948         static struct
9949         {
9950           int dx, dy;
9951         } move_xy[] =
9952           {
9953             {  0,  0 },
9954             { -1,  0 },
9955             { +1,  0 },
9956             {  0,  0 },
9957             {  0, -1 },
9958             {  0,  0 }, { 0, 0 }, { 0, 0 },
9959             {  0, +1 }
9960           };
9961
9962         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9963         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9964
9965         change->actual_trigger_ce_value = CustomValue[xx][yy];
9966         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9967       }
9968
9969       if (change->can_change && !change_done)
9970       {
9971         ChangeDelay[x][y] = 1;
9972         ChangeEvent[x][y] = trigger_event;
9973
9974         HandleElementChange(x, y, p);
9975
9976         change_done = TRUE;
9977       }
9978 #if USE_NEW_DELAYED_ACTION
9979       else if (change->has_action)
9980       {
9981         ExecuteCustomElementAction(x, y, element, p);
9982         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9983       }
9984 #else
9985       if (change->has_action)
9986       {
9987         ExecuteCustomElementAction(x, y, element, p);
9988         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9989       }
9990 #endif
9991     }
9992   }
9993
9994   RECURSION_LOOP_DETECTION_END();
9995
9996   return change_done;
9997 }
9998
9999 static void PlayPlayerSound(struct PlayerInfo *player)
10000 {
10001   int jx = player->jx, jy = player->jy;
10002   int sound_element = player->artwork_element;
10003   int last_action = player->last_action_waiting;
10004   int action = player->action_waiting;
10005
10006   if (player->is_waiting)
10007   {
10008     if (action != last_action)
10009       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10010     else
10011       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10012   }
10013   else
10014   {
10015     if (action != last_action)
10016       StopSound(element_info[sound_element].sound[last_action]);
10017
10018     if (last_action == ACTION_SLEEPING)
10019       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10020   }
10021 }
10022
10023 static void PlayAllPlayersSound()
10024 {
10025   int i;
10026
10027   for (i = 0; i < MAX_PLAYERS; i++)
10028     if (stored_player[i].active)
10029       PlayPlayerSound(&stored_player[i]);
10030 }
10031
10032 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10033 {
10034   boolean last_waiting = player->is_waiting;
10035   int move_dir = player->MovDir;
10036
10037   player->dir_waiting = move_dir;
10038   player->last_action_waiting = player->action_waiting;
10039
10040   if (is_waiting)
10041   {
10042     if (!last_waiting)          /* not waiting -> waiting */
10043     {
10044       player->is_waiting = TRUE;
10045
10046       player->frame_counter_bored =
10047         FrameCounter +
10048         game.player_boring_delay_fixed +
10049         GetSimpleRandom(game.player_boring_delay_random);
10050       player->frame_counter_sleeping =
10051         FrameCounter +
10052         game.player_sleeping_delay_fixed +
10053         GetSimpleRandom(game.player_sleeping_delay_random);
10054
10055       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10056     }
10057
10058     if (game.player_sleeping_delay_fixed +
10059         game.player_sleeping_delay_random > 0 &&
10060         player->anim_delay_counter == 0 &&
10061         player->post_delay_counter == 0 &&
10062         FrameCounter >= player->frame_counter_sleeping)
10063       player->is_sleeping = TRUE;
10064     else if (game.player_boring_delay_fixed +
10065              game.player_boring_delay_random > 0 &&
10066              FrameCounter >= player->frame_counter_bored)
10067       player->is_bored = TRUE;
10068
10069     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10070                               player->is_bored ? ACTION_BORING :
10071                               ACTION_WAITING);
10072
10073     if (player->is_sleeping && player->use_murphy)
10074     {
10075       /* special case for sleeping Murphy when leaning against non-free tile */
10076
10077       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10078           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10079            !IS_MOVING(player->jx - 1, player->jy)))
10080         move_dir = MV_LEFT;
10081       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10082                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10083                 !IS_MOVING(player->jx + 1, player->jy)))
10084         move_dir = MV_RIGHT;
10085       else
10086         player->is_sleeping = FALSE;
10087
10088       player->dir_waiting = move_dir;
10089     }
10090
10091     if (player->is_sleeping)
10092     {
10093       if (player->num_special_action_sleeping > 0)
10094       {
10095         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10096         {
10097           int last_special_action = player->special_action_sleeping;
10098           int num_special_action = player->num_special_action_sleeping;
10099           int special_action =
10100             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10101              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10102              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10103              last_special_action + 1 : ACTION_SLEEPING);
10104           int special_graphic =
10105             el_act_dir2img(player->artwork_element, special_action, move_dir);
10106
10107           player->anim_delay_counter =
10108             graphic_info[special_graphic].anim_delay_fixed +
10109             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10110           player->post_delay_counter =
10111             graphic_info[special_graphic].post_delay_fixed +
10112             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10113
10114           player->special_action_sleeping = special_action;
10115         }
10116
10117         if (player->anim_delay_counter > 0)
10118         {
10119           player->action_waiting = player->special_action_sleeping;
10120           player->anim_delay_counter--;
10121         }
10122         else if (player->post_delay_counter > 0)
10123         {
10124           player->post_delay_counter--;
10125         }
10126       }
10127     }
10128     else if (player->is_bored)
10129     {
10130       if (player->num_special_action_bored > 0)
10131       {
10132         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10133         {
10134           int special_action =
10135             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10136           int special_graphic =
10137             el_act_dir2img(player->artwork_element, special_action, move_dir);
10138
10139           player->anim_delay_counter =
10140             graphic_info[special_graphic].anim_delay_fixed +
10141             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10142           player->post_delay_counter =
10143             graphic_info[special_graphic].post_delay_fixed +
10144             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10145
10146           player->special_action_bored = special_action;
10147         }
10148
10149         if (player->anim_delay_counter > 0)
10150         {
10151           player->action_waiting = player->special_action_bored;
10152           player->anim_delay_counter--;
10153         }
10154         else if (player->post_delay_counter > 0)
10155         {
10156           player->post_delay_counter--;
10157         }
10158       }
10159     }
10160   }
10161   else if (last_waiting)        /* waiting -> not waiting */
10162   {
10163     player->is_waiting = FALSE;
10164     player->is_bored = FALSE;
10165     player->is_sleeping = FALSE;
10166
10167     player->frame_counter_bored = -1;
10168     player->frame_counter_sleeping = -1;
10169
10170     player->anim_delay_counter = 0;
10171     player->post_delay_counter = 0;
10172
10173     player->dir_waiting = player->MovDir;
10174     player->action_waiting = ACTION_DEFAULT;
10175
10176     player->special_action_bored = ACTION_DEFAULT;
10177     player->special_action_sleeping = ACTION_DEFAULT;
10178   }
10179 }
10180
10181 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10182 {
10183   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10184   int left      = player_action & JOY_LEFT;
10185   int right     = player_action & JOY_RIGHT;
10186   int up        = player_action & JOY_UP;
10187   int down      = player_action & JOY_DOWN;
10188   int button1   = player_action & JOY_BUTTON_1;
10189   int button2   = player_action & JOY_BUTTON_2;
10190   int dx        = (left ? -1 : right ? 1 : 0);
10191   int dy        = (up   ? -1 : down  ? 1 : 0);
10192
10193   if (!player->active || tape.pausing)
10194     return 0;
10195
10196   if (player_action)
10197   {
10198     if (button1)
10199       snapped = SnapField(player, dx, dy);
10200     else
10201     {
10202       if (button2)
10203         dropped = DropElement(player);
10204
10205       moved = MovePlayer(player, dx, dy);
10206     }
10207
10208     if (tape.single_step && tape.recording && !tape.pausing)
10209     {
10210       if (button1 || (dropped && !moved))
10211       {
10212         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10213         SnapField(player, 0, 0);                /* stop snapping */
10214       }
10215     }
10216
10217     SetPlayerWaiting(player, FALSE);
10218
10219     return player_action;
10220   }
10221   else
10222   {
10223     /* no actions for this player (no input at player's configured device) */
10224
10225     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10226     SnapField(player, 0, 0);
10227     CheckGravityMovementWhenNotMoving(player);
10228
10229     if (player->MovPos == 0)
10230       SetPlayerWaiting(player, TRUE);
10231
10232     if (player->MovPos == 0)    /* needed for tape.playing */
10233       player->is_moving = FALSE;
10234
10235     player->is_dropping = FALSE;
10236     player->is_dropping_pressed = FALSE;
10237     player->drop_pressed_delay = 0;
10238
10239     return 0;
10240   }
10241 }
10242
10243 static void CheckLevelTime()
10244 {
10245   int i;
10246
10247   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10248   {
10249     if (level.native_em_level->lev->home == 0)  /* all players at home */
10250     {
10251       PlayerWins(local_player);
10252
10253       AllPlayersGone = TRUE;
10254
10255       level.native_em_level->lev->home = -1;
10256     }
10257
10258     if (level.native_em_level->ply[0]->alive == 0 &&
10259         level.native_em_level->ply[1]->alive == 0 &&
10260         level.native_em_level->ply[2]->alive == 0 &&
10261         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10262       AllPlayersGone = TRUE;
10263   }
10264
10265   if (TimeFrames >= FRAMES_PER_SECOND)
10266   {
10267     TimeFrames = 0;
10268     TapeTime++;
10269
10270     for (i = 0; i < MAX_PLAYERS; i++)
10271     {
10272       struct PlayerInfo *player = &stored_player[i];
10273
10274       if (SHIELD_ON(player))
10275       {
10276         player->shield_normal_time_left--;
10277
10278         if (player->shield_deadly_time_left > 0)
10279           player->shield_deadly_time_left--;
10280       }
10281     }
10282
10283     if (!local_player->LevelSolved && !level.use_step_counter)
10284     {
10285       TimePlayed++;
10286
10287       if (TimeLeft > 0)
10288       {
10289         TimeLeft--;
10290
10291         if (TimeLeft <= 10 && setup.time_limit)
10292           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10293
10294         DrawGameValue_Time(TimeLeft);
10295
10296         if (!TimeLeft && setup.time_limit)
10297         {
10298           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10299             level.native_em_level->lev->killed_out_of_time = TRUE;
10300           else
10301             for (i = 0; i < MAX_PLAYERS; i++)
10302               KillPlayer(&stored_player[i]);
10303         }
10304       }
10305       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10306         DrawGameValue_Time(TimePlayed);
10307
10308       level.native_em_level->lev->time =
10309         (level.time == 0 ? TimePlayed : TimeLeft);
10310     }
10311
10312     if (tape.recording || tape.playing)
10313       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10314   }
10315 }
10316
10317 void AdvanceFrameAndPlayerCounters(int player_nr)
10318 {
10319   int i;
10320
10321   /* advance frame counters (global frame counter and time frame counter) */
10322   FrameCounter++;
10323   TimeFrames++;
10324
10325   /* advance player counters (counters for move delay, move animation etc.) */
10326   for (i = 0; i < MAX_PLAYERS; i++)
10327   {
10328     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10329     int move_delay_value = stored_player[i].move_delay_value;
10330     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10331
10332     if (!advance_player_counters)       /* not all players may be affected */
10333       continue;
10334
10335 #if USE_NEW_PLAYER_ANIM
10336     if (move_frames == 0)       /* less than one move per game frame */
10337     {
10338       int stepsize = TILEX / move_delay_value;
10339       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10340       int count = (stored_player[i].is_moving ?
10341                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10342
10343       if (count % delay == 0)
10344         move_frames = 1;
10345     }
10346 #endif
10347
10348     stored_player[i].Frame += move_frames;
10349
10350     if (stored_player[i].MovPos != 0)
10351       stored_player[i].StepFrame += move_frames;
10352
10353     if (stored_player[i].move_delay > 0)
10354       stored_player[i].move_delay--;
10355
10356     /* due to bugs in previous versions, counter must count up, not down */
10357     if (stored_player[i].push_delay != -1)
10358       stored_player[i].push_delay++;
10359
10360     if (stored_player[i].drop_delay > 0)
10361       stored_player[i].drop_delay--;
10362
10363     if (stored_player[i].is_dropping_pressed)
10364       stored_player[i].drop_pressed_delay++;
10365   }
10366 }
10367
10368 void StartGameActions(boolean init_network_game, boolean record_tape,
10369                       long random_seed)
10370 {
10371   unsigned long new_random_seed = InitRND(random_seed);
10372
10373   if (record_tape)
10374     TapeStartRecording(new_random_seed);
10375
10376 #if defined(NETWORK_AVALIABLE)
10377   if (init_network_game)
10378   {
10379     SendToServer_StartPlaying();
10380
10381     return;
10382   }
10383 #endif
10384
10385   InitGame();
10386 }
10387
10388 void GameActions()
10389 {
10390   static unsigned long game_frame_delay = 0;
10391   unsigned long game_frame_delay_value;
10392   byte *recorded_player_action;
10393   byte summarized_player_action = 0;
10394   byte tape_action[MAX_PLAYERS];
10395   int i;
10396
10397   /* detect endless loops, caused by custom element programming */
10398   if (recursion_loop_detected && recursion_loop_depth == 0)
10399   {
10400     char *message = getStringCat3("Internal Error ! Element ",
10401                                   EL_NAME(recursion_loop_element),
10402                                   " caused endless loop ! Quit the game ?");
10403
10404     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10405           EL_NAME(recursion_loop_element));
10406
10407     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10408
10409     recursion_loop_detected = FALSE;    /* if game should be continued */
10410
10411     free(message);
10412
10413     return;
10414   }
10415
10416   if (game.restart_level)
10417     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
10418
10419   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10420   {
10421     if (level.native_em_level->lev->home == 0)  /* all players at home */
10422     {
10423       PlayerWins(local_player);
10424
10425       AllPlayersGone = TRUE;
10426
10427       level.native_em_level->lev->home = -1;
10428     }
10429
10430     if (level.native_em_level->ply[0]->alive == 0 &&
10431         level.native_em_level->ply[1]->alive == 0 &&
10432         level.native_em_level->ply[2]->alive == 0 &&
10433         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10434       AllPlayersGone = TRUE;
10435   }
10436
10437   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10438     GameWon();
10439
10440   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10441     TapeStop();
10442
10443   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
10444     return;
10445
10446   game_frame_delay_value =
10447     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10448
10449   if (tape.playing && tape.warp_forward && !tape.pausing)
10450     game_frame_delay_value = 0;
10451
10452   /* ---------- main game synchronization point ---------- */
10453
10454   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10455
10456   if (network_playing && !network_player_action_received)
10457   {
10458     /* try to get network player actions in time */
10459
10460 #if defined(NETWORK_AVALIABLE)
10461     /* last chance to get network player actions without main loop delay */
10462     HandleNetworking();
10463 #endif
10464
10465     /* game was quit by network peer */
10466     if (game_status != GAME_MODE_PLAYING)
10467       return;
10468
10469     if (!network_player_action_received)
10470       return;           /* failed to get network player actions in time */
10471
10472     /* do not yet reset "network_player_action_received" (for tape.pausing) */
10473   }
10474
10475   if (tape.pausing)
10476     return;
10477
10478   /* at this point we know that we really continue executing the game */
10479
10480   network_player_action_received = FALSE;
10481
10482   /* when playing tape, read previously recorded player input from tape data */
10483   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
10484
10485 #if 1
10486   /* TapePlayAction() may return NULL when toggling to "pause before death" */
10487   if (tape.pausing)
10488     return;
10489 #endif
10490
10491   if (tape.set_centered_player)
10492   {
10493     game.centered_player_nr_next = tape.centered_player_nr_next;
10494     game.set_centered_player = TRUE;
10495   }
10496
10497   for (i = 0; i < MAX_PLAYERS; i++)
10498   {
10499     summarized_player_action |= stored_player[i].action;
10500
10501     if (!network_playing)
10502       stored_player[i].effective_action = stored_player[i].action;
10503   }
10504
10505 #if defined(NETWORK_AVALIABLE)
10506   if (network_playing)
10507     SendToServer_MovePlayer(summarized_player_action);
10508 #endif
10509
10510   if (!options.network && !setup.team_mode)
10511     local_player->effective_action = summarized_player_action;
10512
10513   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
10514   {
10515     for (i = 0; i < MAX_PLAYERS; i++)
10516       stored_player[i].effective_action =
10517         (i == game.centered_player_nr ? summarized_player_action : 0);
10518   }
10519
10520   if (recorded_player_action != NULL)
10521     for (i = 0; i < MAX_PLAYERS; i++)
10522       stored_player[i].effective_action = recorded_player_action[i];
10523
10524   for (i = 0; i < MAX_PLAYERS; i++)
10525   {
10526     tape_action[i] = stored_player[i].effective_action;
10527
10528     /* (this can only happen in the R'n'D game engine) */
10529     if (tape.recording && tape_action[i] && !tape.player_participates[i])
10530       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
10531   }
10532
10533   /* only record actions from input devices, but not programmed actions */
10534   if (tape.recording)
10535     TapeRecordAction(tape_action);
10536
10537   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10538   {
10539     GameActions_EM_Main();
10540   }
10541   else
10542   {
10543     GameActions_RND();
10544   }
10545 }
10546
10547 void GameActions_EM_Main()
10548 {
10549   byte effective_action[MAX_PLAYERS];
10550   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
10551   int i;
10552
10553   for (i = 0; i < MAX_PLAYERS; i++)
10554     effective_action[i] = stored_player[i].effective_action;
10555
10556   GameActions_EM(effective_action, warp_mode);
10557
10558   CheckLevelTime();
10559
10560   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
10561 }
10562
10563 void GameActions_RND()
10564 {
10565   int magic_wall_x = 0, magic_wall_y = 0;
10566   int i, x, y, element, graphic;
10567
10568   InitPlayfieldScanModeVars();
10569
10570 #if USE_ONE_MORE_CHANGE_PER_FRAME
10571   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10572   {
10573     SCAN_PLAYFIELD(x, y)
10574     {
10575       ChangeCount[x][y] = 0;
10576       ChangeEvent[x][y] = -1;
10577     }
10578   }
10579 #endif
10580
10581   if (game.set_centered_player)
10582   {
10583     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
10584
10585     /* switching to "all players" only possible if all players fit to screen */
10586     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
10587     {
10588       game.centered_player_nr_next = game.centered_player_nr;
10589       game.set_centered_player = FALSE;
10590     }
10591
10592     /* do not switch focus to non-existing (or non-active) player */
10593     if (game.centered_player_nr_next >= 0 &&
10594         !stored_player[game.centered_player_nr_next].active)
10595     {
10596       game.centered_player_nr_next = game.centered_player_nr;
10597       game.set_centered_player = FALSE;
10598     }
10599   }
10600
10601   if (game.set_centered_player &&
10602       ScreenMovPos == 0)        /* screen currently aligned at tile position */
10603   {
10604     int sx, sy;
10605
10606     if (game.centered_player_nr_next == -1)
10607     {
10608       setScreenCenteredToAllPlayers(&sx, &sy);
10609     }
10610     else
10611     {
10612       sx = stored_player[game.centered_player_nr_next].jx;
10613       sy = stored_player[game.centered_player_nr_next].jy;
10614     }
10615
10616     game.centered_player_nr = game.centered_player_nr_next;
10617     game.set_centered_player = FALSE;
10618
10619     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
10620     DrawGameDoorValues();
10621   }
10622
10623   for (i = 0; i < MAX_PLAYERS; i++)
10624   {
10625     int actual_player_action = stored_player[i].effective_action;
10626
10627 #if 1
10628     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
10629        - rnd_equinox_tetrachloride 048
10630        - rnd_equinox_tetrachloride_ii 096
10631        - rnd_emanuel_schmieg 002
10632        - doctor_sloan_ww 001, 020
10633     */
10634     if (stored_player[i].MovPos == 0)
10635       CheckGravityMovement(&stored_player[i]);
10636 #endif
10637
10638     /* overwrite programmed action with tape action */
10639     if (stored_player[i].programmed_action)
10640       actual_player_action = stored_player[i].programmed_action;
10641
10642     PlayerActions(&stored_player[i], actual_player_action);
10643
10644     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
10645   }
10646
10647   ScrollScreen(NULL, SCROLL_GO_ON);
10648
10649   /* for backwards compatibility, the following code emulates a fixed bug that
10650      occured when pushing elements (causing elements that just made their last
10651      pushing step to already (if possible) make their first falling step in the
10652      same game frame, which is bad); this code is also needed to use the famous
10653      "spring push bug" which is used in older levels and might be wanted to be
10654      used also in newer levels, but in this case the buggy pushing code is only
10655      affecting the "spring" element and no other elements */
10656
10657   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
10658   {
10659     for (i = 0; i < MAX_PLAYERS; i++)
10660     {
10661       struct PlayerInfo *player = &stored_player[i];
10662       int x = player->jx;
10663       int y = player->jy;
10664
10665       if (player->active && player->is_pushing && player->is_moving &&
10666           IS_MOVING(x, y) &&
10667           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
10668            Feld[x][y] == EL_SPRING))
10669       {
10670         ContinueMoving(x, y);
10671
10672         /* continue moving after pushing (this is actually a bug) */
10673         if (!IS_MOVING(x, y))
10674           Stop[x][y] = FALSE;
10675       }
10676     }
10677   }
10678
10679 #if 0
10680   debug_print_timestamp(0, "start main loop profiling");
10681 #endif
10682
10683   SCAN_PLAYFIELD(x, y)
10684   {
10685     ChangeCount[x][y] = 0;
10686     ChangeEvent[x][y] = -1;
10687
10688     /* this must be handled before main playfield loop */
10689     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
10690     {
10691       MovDelay[x][y]--;
10692       if (MovDelay[x][y] <= 0)
10693         RemoveField(x, y);
10694     }
10695
10696 #if USE_NEW_SNAP_DELAY
10697     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
10698     {
10699       MovDelay[x][y]--;
10700       if (MovDelay[x][y] <= 0)
10701       {
10702         RemoveField(x, y);
10703         DrawLevelField(x, y);
10704
10705         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
10706       }
10707     }
10708 #endif
10709
10710 #if DEBUG
10711     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
10712     {
10713       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
10714       printf("GameActions(): This should never happen!\n");
10715
10716       ChangePage[x][y] = -1;
10717     }
10718 #endif
10719
10720     Stop[x][y] = FALSE;
10721     if (WasJustMoving[x][y] > 0)
10722       WasJustMoving[x][y]--;
10723     if (WasJustFalling[x][y] > 0)
10724       WasJustFalling[x][y]--;
10725     if (CheckCollision[x][y] > 0)
10726       CheckCollision[x][y]--;
10727     if (CheckImpact[x][y] > 0)
10728       CheckImpact[x][y]--;
10729
10730     GfxFrame[x][y]++;
10731
10732     /* reset finished pushing action (not done in ContinueMoving() to allow
10733        continuous pushing animation for elements with zero push delay) */
10734     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
10735     {
10736       ResetGfxAnimation(x, y);
10737       DrawLevelField(x, y);
10738     }
10739
10740 #if DEBUG
10741     if (IS_BLOCKED(x, y))
10742     {
10743       int oldx, oldy;
10744
10745       Blocked2Moving(x, y, &oldx, &oldy);
10746       if (!IS_MOVING(oldx, oldy))
10747       {
10748         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
10749         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
10750         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
10751         printf("GameActions(): This should never happen!\n");
10752       }
10753     }
10754 #endif
10755   }
10756
10757 #if 0
10758   debug_print_timestamp(0, "- time for pre-main loop:");
10759 #endif
10760
10761 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
10762   SCAN_PLAYFIELD(x, y)
10763   {
10764     element = Feld[x][y];
10765     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10766
10767 #if 1
10768     {
10769 #if 1
10770       int element2 = element;
10771       int graphic2 = graphic;
10772 #else
10773       int element2 = Feld[x][y];
10774       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
10775 #endif
10776       int last_gfx_frame = GfxFrame[x][y];
10777
10778       if (graphic_info[graphic2].anim_global_sync)
10779         GfxFrame[x][y] = FrameCounter;
10780       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
10781         GfxFrame[x][y] = CustomValue[x][y];
10782       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
10783         GfxFrame[x][y] = element_info[element2].collect_score;
10784       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
10785         GfxFrame[x][y] = ChangeDelay[x][y];
10786
10787       if (redraw && GfxFrame[x][y] != last_gfx_frame)
10788         DrawLevelGraphicAnimation(x, y, graphic2);
10789     }
10790 #else
10791     ResetGfxFrame(x, y, TRUE);
10792 #endif
10793
10794 #if 1
10795     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10796         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10797       ResetRandomAnimationValue(x, y);
10798 #endif
10799
10800 #if 1
10801     SetRandomAnimationValue(x, y);
10802 #endif
10803
10804 #if 1
10805     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10806 #endif
10807   }
10808 #endif  // -------------------- !!! TEST ONLY !!! --------------------
10809
10810 #if 0
10811   debug_print_timestamp(0, "- time for TEST loop:     -->");
10812 #endif
10813
10814   SCAN_PLAYFIELD(x, y)
10815   {
10816     element = Feld[x][y];
10817     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10818
10819     ResetGfxFrame(x, y, TRUE);
10820
10821     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10822         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10823       ResetRandomAnimationValue(x, y);
10824
10825     SetRandomAnimationValue(x, y);
10826
10827     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10828
10829     if (IS_INACTIVE(element))
10830     {
10831       if (IS_ANIMATED(graphic))
10832         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10833
10834       continue;
10835     }
10836
10837     /* this may take place after moving, so 'element' may have changed */
10838     if (IS_CHANGING(x, y) &&
10839         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10840     {
10841       int page = element_info[element].event_page_nr[CE_DELAY];
10842
10843 #if 1
10844       HandleElementChange(x, y, page);
10845 #else
10846       if (CAN_CHANGE(element))
10847         HandleElementChange(x, y, page);
10848
10849       if (HAS_ACTION(element))
10850         ExecuteCustomElementAction(x, y, element, page);
10851 #endif
10852
10853       element = Feld[x][y];
10854       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10855     }
10856
10857 #if 0   // ---------------------------------------------------------------------
10858
10859     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10860     {
10861       StartMoving(x, y);
10862
10863       element = Feld[x][y];
10864       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10865
10866       if (IS_ANIMATED(graphic) &&
10867           !IS_MOVING(x, y) &&
10868           !Stop[x][y])
10869         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10870
10871       if (IS_GEM(element) || element == EL_SP_INFOTRON)
10872         DrawTwinkleOnField(x, y);
10873     }
10874     else if (IS_MOVING(x, y))
10875       ContinueMoving(x, y);
10876     else
10877     {
10878       switch (element)
10879       {
10880         case EL_ACID:
10881         case EL_EXIT_OPEN:
10882         case EL_EM_EXIT_OPEN:
10883         case EL_SP_EXIT_OPEN:
10884         case EL_STEEL_EXIT_OPEN:
10885         case EL_EM_STEEL_EXIT_OPEN:
10886         case EL_SP_TERMINAL:
10887         case EL_SP_TERMINAL_ACTIVE:
10888         case EL_EXTRA_TIME:
10889         case EL_SHIELD_NORMAL:
10890         case EL_SHIELD_DEADLY:
10891           if (IS_ANIMATED(graphic))
10892             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10893           break;
10894
10895         case EL_DYNAMITE_ACTIVE:
10896         case EL_EM_DYNAMITE_ACTIVE:
10897         case EL_DYNABOMB_PLAYER_1_ACTIVE:
10898         case EL_DYNABOMB_PLAYER_2_ACTIVE:
10899         case EL_DYNABOMB_PLAYER_3_ACTIVE:
10900         case EL_DYNABOMB_PLAYER_4_ACTIVE:
10901         case EL_SP_DISK_RED_ACTIVE:
10902           CheckDynamite(x, y);
10903           break;
10904
10905         case EL_AMOEBA_GROWING:
10906           AmoebeWaechst(x, y);
10907           break;
10908
10909         case EL_AMOEBA_SHRINKING:
10910           AmoebaDisappearing(x, y);
10911           break;
10912
10913 #if !USE_NEW_AMOEBA_CODE
10914         case EL_AMOEBA_WET:
10915         case EL_AMOEBA_DRY:
10916         case EL_AMOEBA_FULL:
10917         case EL_BD_AMOEBA:
10918         case EL_EMC_DRIPPER:
10919           AmoebeAbleger(x, y);
10920           break;
10921 #endif
10922
10923         case EL_GAME_OF_LIFE:
10924         case EL_BIOMAZE:
10925           Life(x, y);
10926           break;
10927
10928         case EL_EXIT_CLOSED:
10929           CheckExit(x, y);
10930           break;
10931
10932         case EL_EM_EXIT_CLOSED:
10933           CheckExitEM(x, y);
10934           break;
10935
10936         case EL_STEEL_EXIT_CLOSED:
10937           CheckExitSteel(x, y);
10938           break;
10939
10940         case EL_EM_STEEL_EXIT_CLOSED:
10941           CheckExitSteelEM(x, y);
10942           break;
10943
10944         case EL_SP_EXIT_CLOSED:
10945           CheckExitSP(x, y);
10946           break;
10947
10948         case EL_EXPANDABLE_WALL_GROWING:
10949         case EL_EXPANDABLE_STEELWALL_GROWING:
10950           MauerWaechst(x, y);
10951           break;
10952
10953         case EL_EXPANDABLE_WALL:
10954         case EL_EXPANDABLE_WALL_HORIZONTAL:
10955         case EL_EXPANDABLE_WALL_VERTICAL:
10956         case EL_EXPANDABLE_WALL_ANY:
10957         case EL_BD_EXPANDABLE_WALL:
10958           MauerAbleger(x, y);
10959           break;
10960
10961         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
10962         case EL_EXPANDABLE_STEELWALL_VERTICAL:
10963         case EL_EXPANDABLE_STEELWALL_ANY:
10964           MauerAblegerStahl(x, y);
10965           break;
10966
10967         case EL_FLAMES:
10968           CheckForDragon(x, y);
10969           break;
10970
10971         case EL_EXPLOSION:
10972           break;
10973
10974         case EL_ELEMENT_SNAPPING:
10975         case EL_DIAGONAL_SHRINKING:
10976         case EL_DIAGONAL_GROWING:
10977         {
10978           graphic =
10979             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10980
10981           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10982           break;
10983         }
10984
10985         default:
10986           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10987             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10988           break;
10989       }
10990     }
10991
10992 #else   // ---------------------------------------------------------------------
10993
10994     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10995     {
10996       StartMoving(x, y);
10997
10998       element = Feld[x][y];
10999       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11000
11001       if (IS_ANIMATED(graphic) &&
11002           !IS_MOVING(x, y) &&
11003           !Stop[x][y])
11004         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11005
11006       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11007         DrawTwinkleOnField(x, y);
11008     }
11009     else if ((element == EL_ACID ||
11010               element == EL_EXIT_OPEN ||
11011               element == EL_EM_EXIT_OPEN ||
11012               element == EL_SP_EXIT_OPEN ||
11013               element == EL_STEEL_EXIT_OPEN ||
11014               element == EL_EM_STEEL_EXIT_OPEN ||
11015               element == EL_SP_TERMINAL ||
11016               element == EL_SP_TERMINAL_ACTIVE ||
11017               element == EL_EXTRA_TIME ||
11018               element == EL_SHIELD_NORMAL ||
11019               element == EL_SHIELD_DEADLY) &&
11020              IS_ANIMATED(graphic))
11021       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11022     else if (IS_MOVING(x, y))
11023       ContinueMoving(x, y);
11024     else if (IS_ACTIVE_BOMB(element))
11025       CheckDynamite(x, y);
11026     else if (element == EL_AMOEBA_GROWING)
11027       AmoebeWaechst(x, y);
11028     else if (element == EL_AMOEBA_SHRINKING)
11029       AmoebaDisappearing(x, y);
11030
11031 #if !USE_NEW_AMOEBA_CODE
11032     else if (IS_AMOEBALIVE(element))
11033       AmoebeAbleger(x, y);
11034 #endif
11035
11036     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11037       Life(x, y);
11038     else if (element == EL_EXIT_CLOSED)
11039       CheckExit(x, y);
11040     else if (element == EL_EM_EXIT_CLOSED)
11041       CheckExitEM(x, y);
11042     else if (element == EL_STEEL_EXIT_CLOSED)
11043       CheckExitSteel(x, y);
11044     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11045       CheckExitSteelEM(x, y);
11046     else if (element == EL_SP_EXIT_CLOSED)
11047       CheckExitSP(x, y);
11048     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11049              element == EL_EXPANDABLE_STEELWALL_GROWING)
11050       MauerWaechst(x, y);
11051     else if (element == EL_EXPANDABLE_WALL ||
11052              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11053              element == EL_EXPANDABLE_WALL_VERTICAL ||
11054              element == EL_EXPANDABLE_WALL_ANY ||
11055              element == EL_BD_EXPANDABLE_WALL)
11056       MauerAbleger(x, y);
11057     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11058              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11059              element == EL_EXPANDABLE_STEELWALL_ANY)
11060       MauerAblegerStahl(x, y);
11061     else if (element == EL_FLAMES)
11062       CheckForDragon(x, y);
11063     else if (element == EL_EXPLOSION)
11064       ; /* drawing of correct explosion animation is handled separately */
11065     else if (element == EL_ELEMENT_SNAPPING ||
11066              element == EL_DIAGONAL_SHRINKING ||
11067              element == EL_DIAGONAL_GROWING)
11068     {
11069       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11070
11071       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11072     }
11073     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11074       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11075
11076 #endif  // ---------------------------------------------------------------------
11077
11078     if (IS_BELT_ACTIVE(element))
11079       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11080
11081     if (game.magic_wall_active)
11082     {
11083       int jx = local_player->jx, jy = local_player->jy;
11084
11085       /* play the element sound at the position nearest to the player */
11086       if ((element == EL_MAGIC_WALL_FULL ||
11087            element == EL_MAGIC_WALL_ACTIVE ||
11088            element == EL_MAGIC_WALL_EMPTYING ||
11089            element == EL_BD_MAGIC_WALL_FULL ||
11090            element == EL_BD_MAGIC_WALL_ACTIVE ||
11091            element == EL_BD_MAGIC_WALL_EMPTYING ||
11092            element == EL_DC_MAGIC_WALL_FULL ||
11093            element == EL_DC_MAGIC_WALL_ACTIVE ||
11094            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11095           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11096       {
11097         magic_wall_x = x;
11098         magic_wall_y = y;
11099       }
11100     }
11101   }
11102
11103 #if 0
11104   debug_print_timestamp(0, "- time for MAIN loop:     -->");
11105 #endif
11106
11107 #if USE_NEW_AMOEBA_CODE
11108   /* new experimental amoeba growth stuff */
11109   if (!(FrameCounter % 8))
11110   {
11111     static unsigned long random = 1684108901;
11112
11113     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11114     {
11115       x = RND(lev_fieldx);
11116       y = RND(lev_fieldy);
11117       element = Feld[x][y];
11118
11119       if (!IS_PLAYER(x,y) &&
11120           (element == EL_EMPTY ||
11121            CAN_GROW_INTO(element) ||
11122            element == EL_QUICKSAND_EMPTY ||
11123            element == EL_QUICKSAND_FAST_EMPTY ||
11124            element == EL_ACID_SPLASH_LEFT ||
11125            element == EL_ACID_SPLASH_RIGHT))
11126       {
11127         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11128             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11129             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11130             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11131           Feld[x][y] = EL_AMOEBA_DROP;
11132       }
11133
11134       random = random * 129 + 1;
11135     }
11136   }
11137 #endif
11138
11139 #if 0
11140   if (game.explosions_delayed)
11141 #endif
11142   {
11143     game.explosions_delayed = FALSE;
11144
11145     SCAN_PLAYFIELD(x, y)
11146     {
11147       element = Feld[x][y];
11148
11149       if (ExplodeField[x][y])
11150         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11151       else if (element == EL_EXPLOSION)
11152         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11153
11154       ExplodeField[x][y] = EX_TYPE_NONE;
11155     }
11156
11157     game.explosions_delayed = TRUE;
11158   }
11159
11160   if (game.magic_wall_active)
11161   {
11162     if (!(game.magic_wall_time_left % 4))
11163     {
11164       int element = Feld[magic_wall_x][magic_wall_y];
11165
11166       if (element == EL_BD_MAGIC_WALL_FULL ||
11167           element == EL_BD_MAGIC_WALL_ACTIVE ||
11168           element == EL_BD_MAGIC_WALL_EMPTYING)
11169         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11170       else if (element == EL_DC_MAGIC_WALL_FULL ||
11171                element == EL_DC_MAGIC_WALL_ACTIVE ||
11172                element == EL_DC_MAGIC_WALL_EMPTYING)
11173         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11174       else
11175         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11176     }
11177
11178     if (game.magic_wall_time_left > 0)
11179     {
11180       game.magic_wall_time_left--;
11181       if (!game.magic_wall_time_left)
11182       {
11183         SCAN_PLAYFIELD(x, y)
11184         {
11185           element = Feld[x][y];
11186
11187           if (element == EL_MAGIC_WALL_ACTIVE ||
11188               element == EL_MAGIC_WALL_FULL)
11189           {
11190             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11191             DrawLevelField(x, y);
11192           }
11193           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11194                    element == EL_BD_MAGIC_WALL_FULL)
11195           {
11196             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11197             DrawLevelField(x, y);
11198           }
11199           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11200                    element == EL_DC_MAGIC_WALL_FULL)
11201           {
11202             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11203             DrawLevelField(x, y);
11204           }
11205         }
11206
11207         game.magic_wall_active = FALSE;
11208       }
11209     }
11210   }
11211
11212   if (game.light_time_left > 0)
11213   {
11214     game.light_time_left--;
11215
11216     if (game.light_time_left == 0)
11217       RedrawAllLightSwitchesAndInvisibleElements();
11218   }
11219
11220   if (game.timegate_time_left > 0)
11221   {
11222     game.timegate_time_left--;
11223
11224     if (game.timegate_time_left == 0)
11225       CloseAllOpenTimegates();
11226   }
11227
11228   if (game.lenses_time_left > 0)
11229   {
11230     game.lenses_time_left--;
11231
11232     if (game.lenses_time_left == 0)
11233       RedrawAllInvisibleElementsForLenses();
11234   }
11235
11236   if (game.magnify_time_left > 0)
11237   {
11238     game.magnify_time_left--;
11239
11240     if (game.magnify_time_left == 0)
11241       RedrawAllInvisibleElementsForMagnifier();
11242   }
11243
11244   for (i = 0; i < MAX_PLAYERS; i++)
11245   {
11246     struct PlayerInfo *player = &stored_player[i];
11247
11248     if (SHIELD_ON(player))
11249     {
11250       if (player->shield_deadly_time_left)
11251         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11252       else if (player->shield_normal_time_left)
11253         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11254     }
11255   }
11256
11257   CheckLevelTime();
11258
11259   DrawAllPlayers();
11260   PlayAllPlayersSound();
11261
11262   if (options.debug)                    /* calculate frames per second */
11263   {
11264     static unsigned long fps_counter = 0;
11265     static int fps_frames = 0;
11266     unsigned long fps_delay_ms = Counter() - fps_counter;
11267
11268     fps_frames++;
11269
11270     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11271     {
11272       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11273
11274       fps_frames = 0;
11275       fps_counter = Counter();
11276     }
11277
11278     redraw_mask |= REDRAW_FPS;
11279   }
11280
11281   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11282
11283   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11284   {
11285     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11286
11287     local_player->show_envelope = 0;
11288   }
11289
11290 #if 0
11291   debug_print_timestamp(0, "stop main loop profiling ");
11292   printf("----------------------------------------------------------\n");
11293 #endif
11294
11295   /* use random number generator in every frame to make it less predictable */
11296   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11297     RND(1);
11298 }
11299
11300 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11301 {
11302   int min_x = x, min_y = y, max_x = x, max_y = y;
11303   int i;
11304
11305   for (i = 0; i < MAX_PLAYERS; i++)
11306   {
11307     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11308
11309     if (!stored_player[i].active || &stored_player[i] == player)
11310       continue;
11311
11312     min_x = MIN(min_x, jx);
11313     min_y = MIN(min_y, jy);
11314     max_x = MAX(max_x, jx);
11315     max_y = MAX(max_y, jy);
11316   }
11317
11318   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11319 }
11320
11321 static boolean AllPlayersInVisibleScreen()
11322 {
11323   int i;
11324
11325   for (i = 0; i < MAX_PLAYERS; i++)
11326   {
11327     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11328
11329     if (!stored_player[i].active)
11330       continue;
11331
11332     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11333       return FALSE;
11334   }
11335
11336   return TRUE;
11337 }
11338
11339 void ScrollLevel(int dx, int dy)
11340 {
11341 #if 1
11342   static Bitmap *bitmap_db_field2 = NULL;
11343   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11344   int x, y;
11345 #else
11346   int i, x, y;
11347 #endif
11348
11349 #if 0
11350   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11351   /* only horizontal XOR vertical scroll direction allowed */
11352   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11353     return;
11354 #endif
11355
11356 #if 1
11357   if (bitmap_db_field2 == NULL)
11358     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11359
11360   /* needed when blitting directly to same bitmap -- should not be needed with
11361      recent SDL libraries, but apparently does not work in 1.2.11 directly */
11362   BlitBitmap(drawto_field, bitmap_db_field2,
11363              FX + TILEX * (dx == -1) - softscroll_offset,
11364              FY + TILEY * (dy == -1) - softscroll_offset,
11365              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11366              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11367              FX + TILEX * (dx == 1) - softscroll_offset,
11368              FY + TILEY * (dy == 1) - softscroll_offset);
11369   BlitBitmap(bitmap_db_field2, drawto_field,
11370              FX + TILEX * (dx == 1) - softscroll_offset,
11371              FY + TILEY * (dy == 1) - softscroll_offset,
11372              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11373              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11374              FX + TILEX * (dx == 1) - softscroll_offset,
11375              FY + TILEY * (dy == 1) - softscroll_offset);
11376
11377 #else
11378
11379 #if 1
11380   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11381   int xsize = (BX2 - BX1 + 1);
11382   int ysize = (BY2 - BY1 + 1);
11383   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11384   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11385   int step  = (start < end ? +1 : -1);
11386
11387   for (i = start; i != end; i += step)
11388   {
11389     BlitBitmap(drawto_field, drawto_field,
11390                FX + TILEX * (dx != 0 ? i + step : 0),
11391                FY + TILEY * (dy != 0 ? i + step : 0),
11392                TILEX * (dx != 0 ? 1 : xsize),
11393                TILEY * (dy != 0 ? 1 : ysize),
11394                FX + TILEX * (dx != 0 ? i : 0),
11395                FY + TILEY * (dy != 0 ? i : 0));
11396   }
11397
11398 #else
11399
11400   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11401
11402   BlitBitmap(drawto_field, drawto_field,
11403              FX + TILEX * (dx == -1) - softscroll_offset,
11404              FY + TILEY * (dy == -1) - softscroll_offset,
11405              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11406              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11407              FX + TILEX * (dx == 1) - softscroll_offset,
11408              FY + TILEY * (dy == 1) - softscroll_offset);
11409 #endif
11410 #endif
11411
11412   if (dx != 0)
11413   {
11414     x = (dx == 1 ? BX1 : BX2);
11415     for (y = BY1; y <= BY2; y++)
11416       DrawScreenField(x, y);
11417   }
11418
11419   if (dy != 0)
11420   {
11421     y = (dy == 1 ? BY1 : BY2);
11422     for (x = BX1; x <= BX2; x++)
11423       DrawScreenField(x, y);
11424   }
11425
11426   redraw_mask |= REDRAW_FIELD;
11427 }
11428
11429 static boolean canFallDown(struct PlayerInfo *player)
11430 {
11431   int jx = player->jx, jy = player->jy;
11432
11433   return (IN_LEV_FIELD(jx, jy + 1) &&
11434           (IS_FREE(jx, jy + 1) ||
11435            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11436           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11437           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11438 }
11439
11440 static boolean canPassField(int x, int y, int move_dir)
11441 {
11442   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11443   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11444   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11445   int nextx = x + dx;
11446   int nexty = y + dy;
11447   int element = Feld[x][y];
11448
11449   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11450           !CAN_MOVE(element) &&
11451           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11452           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11453           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11454 }
11455
11456 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11457 {
11458   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11459   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11460   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11461   int newx = x + dx;
11462   int newy = y + dy;
11463
11464   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11465           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11466           (IS_DIGGABLE(Feld[newx][newy]) ||
11467            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11468            canPassField(newx, newy, move_dir)));
11469 }
11470
11471 static void CheckGravityMovement(struct PlayerInfo *player)
11472 {
11473 #if USE_PLAYER_GRAVITY
11474   if (player->gravity && !player->programmed_action)
11475 #else
11476   if (game.gravity && !player->programmed_action)
11477 #endif
11478   {
11479     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11480     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11481     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11482     int jx = player->jx, jy = player->jy;
11483     boolean player_is_moving_to_valid_field =
11484       (!player_is_snapping &&
11485        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11486         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11487     boolean player_can_fall_down = canFallDown(player);
11488
11489     if (player_can_fall_down &&
11490         !player_is_moving_to_valid_field)
11491       player->programmed_action = MV_DOWN;
11492   }
11493 }
11494
11495 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11496 {
11497   return CheckGravityMovement(player);
11498
11499 #if USE_PLAYER_GRAVITY
11500   if (player->gravity && !player->programmed_action)
11501 #else
11502   if (game.gravity && !player->programmed_action)
11503 #endif
11504   {
11505     int jx = player->jx, jy = player->jy;
11506     boolean field_under_player_is_free =
11507       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11508     boolean player_is_standing_on_valid_field =
11509       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11510        (IS_WALKABLE(Feld[jx][jy]) &&
11511         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11512
11513     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11514       player->programmed_action = MV_DOWN;
11515   }
11516 }
11517
11518 /*
11519   MovePlayerOneStep()
11520   -----------------------------------------------------------------------------
11521   dx, dy:               direction (non-diagonal) to try to move the player to
11522   real_dx, real_dy:     direction as read from input device (can be diagonal)
11523 */
11524
11525 boolean MovePlayerOneStep(struct PlayerInfo *player,
11526                           int dx, int dy, int real_dx, int real_dy)
11527 {
11528   int jx = player->jx, jy = player->jy;
11529   int new_jx = jx + dx, new_jy = jy + dy;
11530 #if !USE_FIXED_DONT_RUN_INTO
11531   int element;
11532 #endif
11533   int can_move;
11534   boolean player_can_move = !player->cannot_move;
11535
11536   if (!player->active || (!dx && !dy))
11537     return MP_NO_ACTION;
11538
11539   player->MovDir = (dx < 0 ? MV_LEFT :
11540                     dx > 0 ? MV_RIGHT :
11541                     dy < 0 ? MV_UP :
11542                     dy > 0 ? MV_DOWN :  MV_NONE);
11543
11544   if (!IN_LEV_FIELD(new_jx, new_jy))
11545     return MP_NO_ACTION;
11546
11547   if (!player_can_move)
11548   {
11549     if (player->MovPos == 0)
11550     {
11551       player->is_moving = FALSE;
11552       player->is_digging = FALSE;
11553       player->is_collecting = FALSE;
11554       player->is_snapping = FALSE;
11555       player->is_pushing = FALSE;
11556     }
11557   }
11558
11559 #if 1
11560   if (!options.network && game.centered_player_nr == -1 &&
11561       !AllPlayersInSight(player, new_jx, new_jy))
11562     return MP_NO_ACTION;
11563 #else
11564   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
11565     return MP_NO_ACTION;
11566 #endif
11567
11568 #if !USE_FIXED_DONT_RUN_INTO
11569   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
11570
11571   /* (moved to DigField()) */
11572   if (player_can_move && DONT_RUN_INTO(element))
11573   {
11574     if (element == EL_ACID && dx == 0 && dy == 1)
11575     {
11576       SplashAcid(new_jx, new_jy);
11577       Feld[jx][jy] = EL_PLAYER_1;
11578       InitMovingField(jx, jy, MV_DOWN);
11579       Store[jx][jy] = EL_ACID;
11580       ContinueMoving(jx, jy);
11581       BuryPlayer(player);
11582     }
11583     else
11584       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11585
11586     return MP_MOVING;
11587   }
11588 #endif
11589
11590   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11591   if (can_move != MP_MOVING)
11592     return can_move;
11593
11594   /* check if DigField() has caused relocation of the player */
11595   if (player->jx != jx || player->jy != jy)
11596     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11597
11598   StorePlayer[jx][jy] = 0;
11599   player->last_jx = jx;
11600   player->last_jy = jy;
11601   player->jx = new_jx;
11602   player->jy = new_jy;
11603   StorePlayer[new_jx][new_jy] = player->element_nr;
11604
11605   if (player->move_delay_value_next != -1)
11606   {
11607     player->move_delay_value = player->move_delay_value_next;
11608     player->move_delay_value_next = -1;
11609   }
11610
11611   player->MovPos =
11612     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11613
11614   player->step_counter++;
11615
11616   PlayerVisit[jx][jy] = FrameCounter;
11617
11618 #if USE_UFAST_PLAYER_EXIT_BUGFIX
11619   player->is_moving = TRUE;
11620 #endif
11621
11622 #if 1
11623   /* should better be called in MovePlayer(), but this breaks some tapes */
11624   ScrollPlayer(player, SCROLL_INIT);
11625 #endif
11626
11627   return MP_MOVING;
11628 }
11629
11630 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
11631 {
11632   int jx = player->jx, jy = player->jy;
11633   int old_jx = jx, old_jy = jy;
11634   int moved = MP_NO_ACTION;
11635
11636   if (!player->active)
11637     return FALSE;
11638
11639   if (!dx && !dy)
11640   {
11641     if (player->MovPos == 0)
11642     {
11643       player->is_moving = FALSE;
11644       player->is_digging = FALSE;
11645       player->is_collecting = FALSE;
11646       player->is_snapping = FALSE;
11647       player->is_pushing = FALSE;
11648     }
11649
11650     return FALSE;
11651   }
11652
11653   if (player->move_delay > 0)
11654     return FALSE;
11655
11656   player->move_delay = -1;              /* set to "uninitialized" value */
11657
11658   /* store if player is automatically moved to next field */
11659   player->is_auto_moving = (player->programmed_action != MV_NONE);
11660
11661   /* remove the last programmed player action */
11662   player->programmed_action = 0;
11663
11664   if (player->MovPos)
11665   {
11666     /* should only happen if pre-1.2 tape recordings are played */
11667     /* this is only for backward compatibility */
11668
11669     int original_move_delay_value = player->move_delay_value;
11670
11671 #if DEBUG
11672     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
11673            tape.counter);
11674 #endif
11675
11676     /* scroll remaining steps with finest movement resolution */
11677     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
11678
11679     while (player->MovPos)
11680     {
11681       ScrollPlayer(player, SCROLL_GO_ON);
11682       ScrollScreen(NULL, SCROLL_GO_ON);
11683
11684       AdvanceFrameAndPlayerCounters(player->index_nr);
11685
11686       DrawAllPlayers();
11687       BackToFront();
11688     }
11689
11690     player->move_delay_value = original_move_delay_value;
11691   }
11692
11693   player->is_active = FALSE;
11694
11695   if (player->last_move_dir & MV_HORIZONTAL)
11696   {
11697     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
11698       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
11699   }
11700   else
11701   {
11702     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
11703       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
11704   }
11705
11706 #if USE_FIXED_BORDER_RUNNING_GFX
11707   if (!moved && !player->is_active)
11708   {
11709     player->is_moving = FALSE;
11710     player->is_digging = FALSE;
11711     player->is_collecting = FALSE;
11712     player->is_snapping = FALSE;
11713     player->is_pushing = FALSE;
11714   }
11715 #endif
11716
11717   jx = player->jx;
11718   jy = player->jy;
11719
11720 #if 1
11721   if (moved & MP_MOVING && !ScreenMovPos &&
11722       (player->index_nr == game.centered_player_nr ||
11723        game.centered_player_nr == -1))
11724 #else
11725   if (moved & MP_MOVING && !ScreenMovPos &&
11726       (player == local_player || !options.network))
11727 #endif
11728   {
11729     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
11730     int offset = (setup.scroll_delay ? 3 : 0);
11731
11732     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11733     {
11734       /* actual player has left the screen -- scroll in that direction */
11735       if (jx != old_jx)         /* player has moved horizontally */
11736         scroll_x += (jx - old_jx);
11737       else                      /* player has moved vertically */
11738         scroll_y += (jy - old_jy);
11739     }
11740     else
11741     {
11742       if (jx != old_jx)         /* player has moved horizontally */
11743       {
11744         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
11745             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
11746           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
11747
11748         /* don't scroll over playfield boundaries */
11749         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
11750           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
11751
11752         /* don't scroll more than one field at a time */
11753         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
11754
11755         /* don't scroll against the player's moving direction */
11756         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
11757             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
11758           scroll_x = old_scroll_x;
11759       }
11760       else                      /* player has moved vertically */
11761       {
11762         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
11763             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
11764           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
11765
11766         /* don't scroll over playfield boundaries */
11767         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
11768           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
11769
11770         /* don't scroll more than one field at a time */
11771         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
11772
11773         /* don't scroll against the player's moving direction */
11774         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
11775             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
11776           scroll_y = old_scroll_y;
11777       }
11778     }
11779
11780     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
11781     {
11782 #if 1
11783       if (!options.network && game.centered_player_nr == -1 &&
11784           !AllPlayersInVisibleScreen())
11785       {
11786         scroll_x = old_scroll_x;
11787         scroll_y = old_scroll_y;
11788       }
11789       else
11790 #else
11791       if (!options.network && !AllPlayersInVisibleScreen())
11792       {
11793         scroll_x = old_scroll_x;
11794         scroll_y = old_scroll_y;
11795       }
11796       else
11797 #endif
11798       {
11799         ScrollScreen(player, SCROLL_INIT);
11800         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
11801       }
11802     }
11803   }
11804
11805   player->StepFrame = 0;
11806
11807   if (moved & MP_MOVING)
11808   {
11809     if (old_jx != jx && old_jy == jy)
11810       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
11811     else if (old_jx == jx && old_jy != jy)
11812       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
11813
11814     DrawLevelField(jx, jy);     /* for "crumbled sand" */
11815
11816     player->last_move_dir = player->MovDir;
11817     player->is_moving = TRUE;
11818     player->is_snapping = FALSE;
11819     player->is_switching = FALSE;
11820     player->is_dropping = FALSE;
11821     player->is_dropping_pressed = FALSE;
11822     player->drop_pressed_delay = 0;
11823
11824 #if 0
11825     /* should better be called here than above, but this breaks some tapes */
11826     ScrollPlayer(player, SCROLL_INIT);
11827 #endif
11828   }
11829   else
11830   {
11831     CheckGravityMovementWhenNotMoving(player);
11832
11833     player->is_moving = FALSE;
11834
11835     /* at this point, the player is allowed to move, but cannot move right now
11836        (e.g. because of something blocking the way) -- ensure that the player
11837        is also allowed to move in the next frame (in old versions before 3.1.1,
11838        the player was forced to wait again for eight frames before next try) */
11839
11840     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11841       player->move_delay = 0;   /* allow direct movement in the next frame */
11842   }
11843
11844   if (player->move_delay == -1)         /* not yet initialized by DigField() */
11845     player->move_delay = player->move_delay_value;
11846
11847   if (game.engine_version < VERSION_IDENT(3,0,7,0))
11848   {
11849     TestIfPlayerTouchesBadThing(jx, jy);
11850     TestIfPlayerTouchesCustomElement(jx, jy);
11851   }
11852
11853   if (!player->active)
11854     RemovePlayer(player);
11855
11856   return moved;
11857 }
11858
11859 void ScrollPlayer(struct PlayerInfo *player, int mode)
11860 {
11861   int jx = player->jx, jy = player->jy;
11862   int last_jx = player->last_jx, last_jy = player->last_jy;
11863   int move_stepsize = TILEX / player->move_delay_value;
11864
11865 #if USE_NEW_PLAYER_SPEED
11866   if (!player->active)
11867     return;
11868
11869   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
11870     return;
11871 #else
11872   if (!player->active || player->MovPos == 0)
11873     return;
11874 #endif
11875
11876   if (mode == SCROLL_INIT)
11877   {
11878     player->actual_frame_counter = FrameCounter;
11879     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11880
11881     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
11882         Feld[last_jx][last_jy] == EL_EMPTY)
11883     {
11884       int last_field_block_delay = 0;   /* start with no blocking at all */
11885       int block_delay_adjustment = player->block_delay_adjustment;
11886
11887       /* if player blocks last field, add delay for exactly one move */
11888       if (player->block_last_field)
11889       {
11890         last_field_block_delay += player->move_delay_value;
11891
11892         /* when blocking enabled, prevent moving up despite gravity */
11893 #if USE_PLAYER_GRAVITY
11894         if (player->gravity && player->MovDir == MV_UP)
11895           block_delay_adjustment = -1;
11896 #else
11897         if (game.gravity && player->MovDir == MV_UP)
11898           block_delay_adjustment = -1;
11899 #endif
11900       }
11901
11902       /* add block delay adjustment (also possible when not blocking) */
11903       last_field_block_delay += block_delay_adjustment;
11904
11905       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
11906       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
11907     }
11908
11909 #if USE_NEW_PLAYER_SPEED
11910     if (player->MovPos != 0)    /* player has not yet reached destination */
11911       return;
11912 #else
11913     return;
11914 #endif
11915   }
11916   else if (!FrameReached(&player->actual_frame_counter, 1))
11917     return;
11918
11919 #if USE_NEW_PLAYER_SPEED
11920   if (player->MovPos != 0)
11921   {
11922     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11923     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11924
11925     /* before DrawPlayer() to draw correct player graphic for this case */
11926     if (player->MovPos == 0)
11927       CheckGravityMovement(player);
11928   }
11929 #else
11930   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11931   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11932
11933   /* before DrawPlayer() to draw correct player graphic for this case */
11934   if (player->MovPos == 0)
11935     CheckGravityMovement(player);
11936 #endif
11937
11938   if (player->MovPos == 0)      /* player reached destination field */
11939   {
11940     if (player->move_delay_reset_counter > 0)
11941     {
11942       player->move_delay_reset_counter--;
11943
11944       if (player->move_delay_reset_counter == 0)
11945       {
11946         /* continue with normal speed after quickly moving through gate */
11947         HALVE_PLAYER_SPEED(player);
11948
11949         /* be able to make the next move without delay */
11950         player->move_delay = 0;
11951       }
11952     }
11953
11954     player->last_jx = jx;
11955     player->last_jy = jy;
11956
11957     if (Feld[jx][jy] == EL_EXIT_OPEN ||
11958         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
11959         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
11960         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
11961         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
11962         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
11963     {
11964       DrawPlayer(player);       /* needed here only to cleanup last field */
11965       RemovePlayer(player);
11966
11967       if (local_player->friends_still_needed == 0 ||
11968           IS_SP_ELEMENT(Feld[jx][jy]))
11969         PlayerWins(player);
11970     }
11971
11972     /* this breaks one level: "machine", level 000 */
11973     {
11974       int move_direction = player->MovDir;
11975       int enter_side = MV_DIR_OPPOSITE(move_direction);
11976       int leave_side = move_direction;
11977       int old_jx = last_jx;
11978       int old_jy = last_jy;
11979       int old_element = Feld[old_jx][old_jy];
11980       int new_element = Feld[jx][jy];
11981
11982       if (IS_CUSTOM_ELEMENT(old_element))
11983         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
11984                                    CE_LEFT_BY_PLAYER,
11985                                    player->index_bit, leave_side);
11986
11987       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
11988                                           CE_PLAYER_LEAVES_X,
11989                                           player->index_bit, leave_side);
11990
11991       if (IS_CUSTOM_ELEMENT(new_element))
11992         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
11993                                    player->index_bit, enter_side);
11994
11995       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
11996                                           CE_PLAYER_ENTERS_X,
11997                                           player->index_bit, enter_side);
11998
11999       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12000                                         CE_MOVE_OF_X, move_direction);
12001     }
12002
12003     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12004     {
12005       TestIfPlayerTouchesBadThing(jx, jy);
12006       TestIfPlayerTouchesCustomElement(jx, jy);
12007
12008       /* needed because pushed element has not yet reached its destination,
12009          so it would trigger a change event at its previous field location */
12010       if (!player->is_pushing)
12011         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12012
12013       if (!player->active)
12014         RemovePlayer(player);
12015     }
12016
12017     if (!local_player->LevelSolved && level.use_step_counter)
12018     {
12019       int i;
12020
12021       TimePlayed++;
12022
12023       if (TimeLeft > 0)
12024       {
12025         TimeLeft--;
12026
12027         if (TimeLeft <= 10 && setup.time_limit)
12028           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12029
12030         DrawGameValue_Time(TimeLeft);
12031
12032         if (!TimeLeft && setup.time_limit)
12033           for (i = 0; i < MAX_PLAYERS; i++)
12034             KillPlayer(&stored_player[i]);
12035       }
12036       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12037         DrawGameValue_Time(TimePlayed);
12038     }
12039
12040     if (tape.single_step && tape.recording && !tape.pausing &&
12041         !player->programmed_action)
12042       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12043   }
12044 }
12045
12046 void ScrollScreen(struct PlayerInfo *player, int mode)
12047 {
12048   static unsigned long screen_frame_counter = 0;
12049
12050   if (mode == SCROLL_INIT)
12051   {
12052     /* set scrolling step size according to actual player's moving speed */
12053     ScrollStepSize = TILEX / player->move_delay_value;
12054
12055     screen_frame_counter = FrameCounter;
12056     ScreenMovDir = player->MovDir;
12057     ScreenMovPos = player->MovPos;
12058     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12059     return;
12060   }
12061   else if (!FrameReached(&screen_frame_counter, 1))
12062     return;
12063
12064   if (ScreenMovPos)
12065   {
12066     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12067     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12068     redraw_mask |= REDRAW_FIELD;
12069   }
12070   else
12071     ScreenMovDir = MV_NONE;
12072 }
12073
12074 void TestIfPlayerTouchesCustomElement(int x, int y)
12075 {
12076   static int xy[4][2] =
12077   {
12078     { 0, -1 },
12079     { -1, 0 },
12080     { +1, 0 },
12081     { 0, +1 }
12082   };
12083   static int trigger_sides[4][2] =
12084   {
12085     /* center side       border side */
12086     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12087     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12088     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12089     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12090   };
12091   static int touch_dir[4] =
12092   {
12093     MV_LEFT | MV_RIGHT,
12094     MV_UP   | MV_DOWN,
12095     MV_UP   | MV_DOWN,
12096     MV_LEFT | MV_RIGHT
12097   };
12098   int center_element = Feld[x][y];      /* should always be non-moving! */
12099   int i;
12100
12101   for (i = 0; i < NUM_DIRECTIONS; i++)
12102   {
12103     int xx = x + xy[i][0];
12104     int yy = y + xy[i][1];
12105     int center_side = trigger_sides[i][0];
12106     int border_side = trigger_sides[i][1];
12107     int border_element;
12108
12109     if (!IN_LEV_FIELD(xx, yy))
12110       continue;
12111
12112     if (IS_PLAYER(x, y))
12113     {
12114       struct PlayerInfo *player = PLAYERINFO(x, y);
12115
12116       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12117         border_element = Feld[xx][yy];          /* may be moving! */
12118       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12119         border_element = Feld[xx][yy];
12120       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12121         border_element = MovingOrBlocked2Element(xx, yy);
12122       else
12123         continue;               /* center and border element do not touch */
12124
12125       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12126                                  player->index_bit, border_side);
12127       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12128                                           CE_PLAYER_TOUCHES_X,
12129                                           player->index_bit, border_side);
12130     }
12131     else if (IS_PLAYER(xx, yy))
12132     {
12133       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12134
12135       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12136       {
12137         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12138           continue;             /* center and border element do not touch */
12139       }
12140
12141       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12142                                  player->index_bit, center_side);
12143       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12144                                           CE_PLAYER_TOUCHES_X,
12145                                           player->index_bit, center_side);
12146       break;
12147     }
12148   }
12149 }
12150
12151 #if USE_ELEMENT_TOUCHING_BUGFIX
12152
12153 void TestIfElementTouchesCustomElement(int x, int y)
12154 {
12155   static int xy[4][2] =
12156   {
12157     { 0, -1 },
12158     { -1, 0 },
12159     { +1, 0 },
12160     { 0, +1 }
12161   };
12162   static int trigger_sides[4][2] =
12163   {
12164     /* center side      border side */
12165     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12166     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12167     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12168     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12169   };
12170   static int touch_dir[4] =
12171   {
12172     MV_LEFT | MV_RIGHT,
12173     MV_UP   | MV_DOWN,
12174     MV_UP   | MV_DOWN,
12175     MV_LEFT | MV_RIGHT
12176   };
12177   boolean change_center_element = FALSE;
12178   int center_element = Feld[x][y];      /* should always be non-moving! */
12179   int border_element_old[NUM_DIRECTIONS];
12180   int i;
12181
12182   for (i = 0; i < NUM_DIRECTIONS; i++)
12183   {
12184     int xx = x + xy[i][0];
12185     int yy = y + xy[i][1];
12186     int border_element;
12187
12188     border_element_old[i] = -1;
12189
12190     if (!IN_LEV_FIELD(xx, yy))
12191       continue;
12192
12193     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12194       border_element = Feld[xx][yy];    /* may be moving! */
12195     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12196       border_element = Feld[xx][yy];
12197     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12198       border_element = MovingOrBlocked2Element(xx, yy);
12199     else
12200       continue;                 /* center and border element do not touch */
12201
12202     border_element_old[i] = border_element;
12203   }
12204
12205   for (i = 0; i < NUM_DIRECTIONS; i++)
12206   {
12207     int xx = x + xy[i][0];
12208     int yy = y + xy[i][1];
12209     int center_side = trigger_sides[i][0];
12210     int border_element = border_element_old[i];
12211
12212     if (border_element == -1)
12213       continue;
12214
12215     /* check for change of border element */
12216     CheckElementChangeBySide(xx, yy, border_element, center_element,
12217                              CE_TOUCHING_X, center_side);
12218   }
12219
12220   for (i = 0; i < NUM_DIRECTIONS; i++)
12221   {
12222     int border_side = trigger_sides[i][1];
12223     int border_element = border_element_old[i];
12224
12225     if (border_element == -1)
12226       continue;
12227
12228     /* check for change of center element (but change it only once) */
12229     if (!change_center_element)
12230       change_center_element =
12231         CheckElementChangeBySide(x, y, center_element, border_element,
12232                                  CE_TOUCHING_X, border_side);
12233   }
12234 }
12235
12236 #else
12237
12238 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12239 {
12240   static int xy[4][2] =
12241   {
12242     { 0, -1 },
12243     { -1, 0 },
12244     { +1, 0 },
12245     { 0, +1 }
12246   };
12247   static int trigger_sides[4][2] =
12248   {
12249     /* center side      border side */
12250     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12251     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12252     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12253     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12254   };
12255   static int touch_dir[4] =
12256   {
12257     MV_LEFT | MV_RIGHT,
12258     MV_UP   | MV_DOWN,
12259     MV_UP   | MV_DOWN,
12260     MV_LEFT | MV_RIGHT
12261   };
12262   boolean change_center_element = FALSE;
12263   int center_element = Feld[x][y];      /* should always be non-moving! */
12264   int i;
12265
12266   for (i = 0; i < NUM_DIRECTIONS; i++)
12267   {
12268     int xx = x + xy[i][0];
12269     int yy = y + xy[i][1];
12270     int center_side = trigger_sides[i][0];
12271     int border_side = trigger_sides[i][1];
12272     int border_element;
12273
12274     if (!IN_LEV_FIELD(xx, yy))
12275       continue;
12276
12277     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12278       border_element = Feld[xx][yy];    /* may be moving! */
12279     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12280       border_element = Feld[xx][yy];
12281     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12282       border_element = MovingOrBlocked2Element(xx, yy);
12283     else
12284       continue;                 /* center and border element do not touch */
12285
12286     /* check for change of center element (but change it only once) */
12287     if (!change_center_element)
12288       change_center_element =
12289         CheckElementChangeBySide(x, y, center_element, border_element,
12290                                  CE_TOUCHING_X, border_side);
12291
12292     /* check for change of border element */
12293     CheckElementChangeBySide(xx, yy, border_element, center_element,
12294                              CE_TOUCHING_X, center_side);
12295   }
12296 }
12297
12298 #endif
12299
12300 void TestIfElementHitsCustomElement(int x, int y, int direction)
12301 {
12302   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12303   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12304   int hitx = x + dx, hity = y + dy;
12305   int hitting_element = Feld[x][y];
12306   int touched_element;
12307
12308   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12309     return;
12310
12311   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12312                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12313
12314   if (IN_LEV_FIELD(hitx, hity))
12315   {
12316     int opposite_direction = MV_DIR_OPPOSITE(direction);
12317     int hitting_side = direction;
12318     int touched_side = opposite_direction;
12319     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12320                           MovDir[hitx][hity] != direction ||
12321                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12322
12323     object_hit = TRUE;
12324
12325     if (object_hit)
12326     {
12327       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12328                                CE_HITTING_X, touched_side);
12329
12330       CheckElementChangeBySide(hitx, hity, touched_element,
12331                                hitting_element, CE_HIT_BY_X, hitting_side);
12332
12333       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12334                                CE_HIT_BY_SOMETHING, opposite_direction);
12335     }
12336   }
12337
12338   /* "hitting something" is also true when hitting the playfield border */
12339   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12340                            CE_HITTING_SOMETHING, direction);
12341 }
12342
12343 #if 0
12344 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12345 {
12346   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12347   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12348   int hitx = x + dx, hity = y + dy;
12349   int hitting_element = Feld[x][y];
12350   int touched_element;
12351 #if 0
12352   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12353                         !IS_FREE(hitx, hity) &&
12354                         (!IS_MOVING(hitx, hity) ||
12355                          MovDir[hitx][hity] != direction ||
12356                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
12357 #endif
12358
12359   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12360     return;
12361
12362 #if 0
12363   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12364     return;
12365 #endif
12366
12367   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12368                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12369
12370   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12371                            EP_CAN_SMASH_EVERYTHING, direction);
12372
12373   if (IN_LEV_FIELD(hitx, hity))
12374   {
12375     int opposite_direction = MV_DIR_OPPOSITE(direction);
12376     int hitting_side = direction;
12377     int touched_side = opposite_direction;
12378 #if 0
12379     int touched_element = MovingOrBlocked2Element(hitx, hity);
12380 #endif
12381 #if 1
12382     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12383                           MovDir[hitx][hity] != direction ||
12384                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12385
12386     object_hit = TRUE;
12387 #endif
12388
12389     if (object_hit)
12390     {
12391       int i;
12392
12393       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12394                                CE_SMASHED_BY_SOMETHING, opposite_direction);
12395
12396       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12397                                CE_OTHER_IS_SMASHING, touched_side);
12398
12399       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12400                                CE_OTHER_GETS_SMASHED, hitting_side);
12401     }
12402   }
12403 }
12404 #endif
12405
12406 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12407 {
12408   int i, kill_x = -1, kill_y = -1;
12409
12410   int bad_element = -1;
12411   static int test_xy[4][2] =
12412   {
12413     { 0, -1 },
12414     { -1, 0 },
12415     { +1, 0 },
12416     { 0, +1 }
12417   };
12418   static int test_dir[4] =
12419   {
12420     MV_UP,
12421     MV_LEFT,
12422     MV_RIGHT,
12423     MV_DOWN
12424   };
12425
12426   for (i = 0; i < NUM_DIRECTIONS; i++)
12427   {
12428     int test_x, test_y, test_move_dir, test_element;
12429
12430     test_x = good_x + test_xy[i][0];
12431     test_y = good_y + test_xy[i][1];
12432
12433     if (!IN_LEV_FIELD(test_x, test_y))
12434       continue;
12435
12436     test_move_dir =
12437       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12438
12439     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12440
12441     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12442        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12443     */
12444     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12445         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12446     {
12447       kill_x = test_x;
12448       kill_y = test_y;
12449       bad_element = test_element;
12450
12451       break;
12452     }
12453   }
12454
12455   if (kill_x != -1 || kill_y != -1)
12456   {
12457     if (IS_PLAYER(good_x, good_y))
12458     {
12459       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12460
12461       if (player->shield_deadly_time_left > 0 &&
12462           !IS_INDESTRUCTIBLE(bad_element))
12463         Bang(kill_x, kill_y);
12464       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12465         KillPlayer(player);
12466     }
12467     else
12468       Bang(good_x, good_y);
12469   }
12470 }
12471
12472 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12473 {
12474   int i, kill_x = -1, kill_y = -1;
12475   int bad_element = Feld[bad_x][bad_y];
12476   static int test_xy[4][2] =
12477   {
12478     { 0, -1 },
12479     { -1, 0 },
12480     { +1, 0 },
12481     { 0, +1 }
12482   };
12483   static int touch_dir[4] =
12484   {
12485     MV_LEFT | MV_RIGHT,
12486     MV_UP   | MV_DOWN,
12487     MV_UP   | MV_DOWN,
12488     MV_LEFT | MV_RIGHT
12489   };
12490   static int test_dir[4] =
12491   {
12492     MV_UP,
12493     MV_LEFT,
12494     MV_RIGHT,
12495     MV_DOWN
12496   };
12497
12498   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12499     return;
12500
12501   for (i = 0; i < NUM_DIRECTIONS; i++)
12502   {
12503     int test_x, test_y, test_move_dir, test_element;
12504
12505     test_x = bad_x + test_xy[i][0];
12506     test_y = bad_y + test_xy[i][1];
12507     if (!IN_LEV_FIELD(test_x, test_y))
12508       continue;
12509
12510     test_move_dir =
12511       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12512
12513     test_element = Feld[test_x][test_y];
12514
12515     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12516        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12517     */
12518     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12519         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12520     {
12521       /* good thing is player or penguin that does not move away */
12522       if (IS_PLAYER(test_x, test_y))
12523       {
12524         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12525
12526         if (bad_element == EL_ROBOT && player->is_moving)
12527           continue;     /* robot does not kill player if he is moving */
12528
12529         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12530         {
12531           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12532             continue;           /* center and border element do not touch */
12533         }
12534
12535         kill_x = test_x;
12536         kill_y = test_y;
12537         break;
12538       }
12539       else if (test_element == EL_PENGUIN)
12540       {
12541         kill_x = test_x;
12542         kill_y = test_y;
12543         break;
12544       }
12545     }
12546   }
12547
12548   if (kill_x != -1 || kill_y != -1)
12549   {
12550     if (IS_PLAYER(kill_x, kill_y))
12551     {
12552       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12553
12554       if (player->shield_deadly_time_left > 0 &&
12555           !IS_INDESTRUCTIBLE(bad_element))
12556         Bang(bad_x, bad_y);
12557       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12558         KillPlayer(player);
12559     }
12560     else
12561       Bang(kill_x, kill_y);
12562   }
12563 }
12564
12565 void TestIfPlayerTouchesBadThing(int x, int y)
12566 {
12567   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12568 }
12569
12570 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12571 {
12572   TestIfGoodThingHitsBadThing(x, y, move_dir);
12573 }
12574
12575 void TestIfBadThingTouchesPlayer(int x, int y)
12576 {
12577   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12578 }
12579
12580 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12581 {
12582   TestIfBadThingHitsGoodThing(x, y, move_dir);
12583 }
12584
12585 void TestIfFriendTouchesBadThing(int x, int y)
12586 {
12587   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12588 }
12589
12590 void TestIfBadThingTouchesFriend(int x, int y)
12591 {
12592   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12593 }
12594
12595 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12596 {
12597   int i, kill_x = bad_x, kill_y = bad_y;
12598   static int xy[4][2] =
12599   {
12600     { 0, -1 },
12601     { -1, 0 },
12602     { +1, 0 },
12603     { 0, +1 }
12604   };
12605
12606   for (i = 0; i < NUM_DIRECTIONS; i++)
12607   {
12608     int x, y, element;
12609
12610     x = bad_x + xy[i][0];
12611     y = bad_y + xy[i][1];
12612     if (!IN_LEV_FIELD(x, y))
12613       continue;
12614
12615     element = Feld[x][y];
12616     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12617         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12618     {
12619       kill_x = x;
12620       kill_y = y;
12621       break;
12622     }
12623   }
12624
12625   if (kill_x != bad_x || kill_y != bad_y)
12626     Bang(bad_x, bad_y);
12627 }
12628
12629 void KillPlayer(struct PlayerInfo *player)
12630 {
12631   int jx = player->jx, jy = player->jy;
12632
12633   if (!player->active)
12634     return;
12635
12636   /* the following code was introduced to prevent an infinite loop when calling
12637      -> Bang()
12638      -> CheckTriggeredElementChangeExt()
12639      -> ExecuteCustomElementAction()
12640      -> KillPlayer()
12641      -> (infinitely repeating the above sequence of function calls)
12642      which occurs when killing the player while having a CE with the setting
12643      "kill player X when explosion of <player X>"; the solution using a new
12644      field "player->killed" was chosen for backwards compatibility, although
12645      clever use of the fields "player->active" etc. would probably also work */
12646 #if 1
12647   if (player->killed)
12648     return;
12649 #endif
12650
12651   player->killed = TRUE;
12652
12653   /* remove accessible field at the player's position */
12654   Feld[jx][jy] = EL_EMPTY;
12655
12656   /* deactivate shield (else Bang()/Explode() would not work right) */
12657   player->shield_normal_time_left = 0;
12658   player->shield_deadly_time_left = 0;
12659
12660   Bang(jx, jy);
12661   BuryPlayer(player);
12662 }
12663
12664 static void KillPlayerUnlessEnemyProtected(int x, int y)
12665 {
12666   if (!PLAYER_ENEMY_PROTECTED(x, y))
12667     KillPlayer(PLAYERINFO(x, y));
12668 }
12669
12670 static void KillPlayerUnlessExplosionProtected(int x, int y)
12671 {
12672   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
12673     KillPlayer(PLAYERINFO(x, y));
12674 }
12675
12676 void BuryPlayer(struct PlayerInfo *player)
12677 {
12678   int jx = player->jx, jy = player->jy;
12679
12680   if (!player->active)
12681     return;
12682
12683   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
12684   PlayLevelSound(jx, jy, SND_GAME_LOSING);
12685
12686   player->GameOver = TRUE;
12687   RemovePlayer(player);
12688 }
12689
12690 void RemovePlayer(struct PlayerInfo *player)
12691 {
12692   int jx = player->jx, jy = player->jy;
12693   int i, found = FALSE;
12694
12695   player->present = FALSE;
12696   player->active = FALSE;
12697
12698   if (!ExplodeField[jx][jy])
12699     StorePlayer[jx][jy] = 0;
12700
12701   if (player->is_moving)
12702     DrawLevelField(player->last_jx, player->last_jy);
12703
12704   for (i = 0; i < MAX_PLAYERS; i++)
12705     if (stored_player[i].active)
12706       found = TRUE;
12707
12708   if (!found)
12709     AllPlayersGone = TRUE;
12710
12711   ExitX = ZX = jx;
12712   ExitY = ZY = jy;
12713 }
12714
12715 #if USE_NEW_SNAP_DELAY
12716 static void setFieldForSnapping(int x, int y, int element, int direction)
12717 {
12718   struct ElementInfo *ei = &element_info[element];
12719   int direction_bit = MV_DIR_TO_BIT(direction);
12720   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
12721   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
12722                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
12723
12724   Feld[x][y] = EL_ELEMENT_SNAPPING;
12725   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
12726
12727   ResetGfxAnimation(x, y);
12728
12729   GfxElement[x][y] = element;
12730   GfxAction[x][y] = action;
12731   GfxDir[x][y] = direction;
12732   GfxFrame[x][y] = -1;
12733 }
12734 #endif
12735
12736 /*
12737   =============================================================================
12738   checkDiagonalPushing()
12739   -----------------------------------------------------------------------------
12740   check if diagonal input device direction results in pushing of object
12741   (by checking if the alternative direction is walkable, diggable, ...)
12742   =============================================================================
12743 */
12744
12745 static boolean checkDiagonalPushing(struct PlayerInfo *player,
12746                                     int x, int y, int real_dx, int real_dy)
12747 {
12748   int jx, jy, dx, dy, xx, yy;
12749
12750   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
12751     return TRUE;
12752
12753   /* diagonal direction: check alternative direction */
12754   jx = player->jx;
12755   jy = player->jy;
12756   dx = x - jx;
12757   dy = y - jy;
12758   xx = jx + (dx == 0 ? real_dx : 0);
12759   yy = jy + (dy == 0 ? real_dy : 0);
12760
12761   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
12762 }
12763
12764 /*
12765   =============================================================================
12766   DigField()
12767   -----------------------------------------------------------------------------
12768   x, y:                 field next to player (non-diagonal) to try to dig to
12769   real_dx, real_dy:     direction as read from input device (can be diagonal)
12770   =============================================================================
12771 */
12772
12773 int DigField(struct PlayerInfo *player,
12774              int oldx, int oldy, int x, int y,
12775              int real_dx, int real_dy, int mode)
12776 {
12777   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
12778   boolean player_was_pushing = player->is_pushing;
12779   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
12780   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
12781   int jx = oldx, jy = oldy;
12782   int dx = x - jx, dy = y - jy;
12783   int nextx = x + dx, nexty = y + dy;
12784   int move_direction = (dx == -1 ? MV_LEFT  :
12785                         dx == +1 ? MV_RIGHT :
12786                         dy == -1 ? MV_UP    :
12787                         dy == +1 ? MV_DOWN  : MV_NONE);
12788   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
12789   int dig_side = MV_DIR_OPPOSITE(move_direction);
12790   int old_element = Feld[jx][jy];
12791 #if USE_FIXED_DONT_RUN_INTO
12792   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
12793 #else
12794   int element;
12795 #endif
12796   int collect_count;
12797
12798   if (is_player)                /* function can also be called by EL_PENGUIN */
12799   {
12800     if (player->MovPos == 0)
12801     {
12802       player->is_digging = FALSE;
12803       player->is_collecting = FALSE;
12804     }
12805
12806     if (player->MovPos == 0)    /* last pushing move finished */
12807       player->is_pushing = FALSE;
12808
12809     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
12810     {
12811       player->is_switching = FALSE;
12812       player->push_delay = -1;
12813
12814       return MP_NO_ACTION;
12815     }
12816   }
12817
12818 #if !USE_FIXED_DONT_RUN_INTO
12819   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12820     return MP_NO_ACTION;
12821 #endif
12822
12823   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
12824     old_element = Back[jx][jy];
12825
12826   /* in case of element dropped at player position, check background */
12827   else if (Back[jx][jy] != EL_EMPTY &&
12828            game.engine_version >= VERSION_IDENT(2,2,0,0))
12829     old_element = Back[jx][jy];
12830
12831   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12832     return MP_NO_ACTION;        /* field has no opening in this direction */
12833
12834   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12835     return MP_NO_ACTION;        /* field has no opening in this direction */
12836
12837 #if USE_FIXED_DONT_RUN_INTO
12838   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12839   {
12840     SplashAcid(x, y);
12841
12842     Feld[jx][jy] = player->artwork_element;
12843     InitMovingField(jx, jy, MV_DOWN);
12844     Store[jx][jy] = EL_ACID;
12845     ContinueMoving(jx, jy);
12846     BuryPlayer(player);
12847
12848     return MP_DONT_RUN_INTO;
12849   }
12850 #endif
12851
12852 #if USE_FIXED_DONT_RUN_INTO
12853   if (player_can_move && DONT_RUN_INTO(element))
12854   {
12855     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12856
12857     return MP_DONT_RUN_INTO;
12858   }
12859 #endif
12860
12861 #if USE_FIXED_DONT_RUN_INTO
12862   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12863     return MP_NO_ACTION;
12864 #endif
12865
12866 #if !USE_FIXED_DONT_RUN_INTO
12867   element = Feld[x][y];
12868 #endif
12869
12870   collect_count = element_info[element].collect_count_initial;
12871
12872   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
12873     return MP_NO_ACTION;
12874
12875   if (game.engine_version < VERSION_IDENT(2,2,0,0))
12876     player_can_move = player_can_move_or_snap;
12877
12878   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
12879       game.engine_version >= VERSION_IDENT(2,2,0,0))
12880   {
12881     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
12882                                player->index_bit, dig_side);
12883     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12884                                         player->index_bit, dig_side);
12885
12886     if (element == EL_DC_LANDMINE)
12887       Bang(x, y);
12888
12889     if (Feld[x][y] != element)          /* field changed by snapping */
12890       return MP_ACTION;
12891
12892     return MP_NO_ACTION;
12893   }
12894
12895 #if USE_PLAYER_GRAVITY
12896   if (player->gravity && is_player && !player->is_auto_moving &&
12897       canFallDown(player) && move_direction != MV_DOWN &&
12898       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12899     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12900 #else
12901   if (game.gravity && is_player && !player->is_auto_moving &&
12902       canFallDown(player) && move_direction != MV_DOWN &&
12903       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12904     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12905 #endif
12906
12907   if (player_can_move &&
12908       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12909   {
12910     int sound_element = SND_ELEMENT(element);
12911     int sound_action = ACTION_WALKING;
12912
12913     if (IS_RND_GATE(element))
12914     {
12915       if (!player->key[RND_GATE_NR(element)])
12916         return MP_NO_ACTION;
12917     }
12918     else if (IS_RND_GATE_GRAY(element))
12919     {
12920       if (!player->key[RND_GATE_GRAY_NR(element)])
12921         return MP_NO_ACTION;
12922     }
12923     else if (IS_RND_GATE_GRAY_ACTIVE(element))
12924     {
12925       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
12926         return MP_NO_ACTION;
12927     }
12928     else if (element == EL_EXIT_OPEN ||
12929              element == EL_EM_EXIT_OPEN ||
12930              element == EL_STEEL_EXIT_OPEN ||
12931              element == EL_EM_STEEL_EXIT_OPEN ||
12932              element == EL_SP_EXIT_OPEN ||
12933              element == EL_SP_EXIT_OPENING)
12934     {
12935       sound_action = ACTION_PASSING;    /* player is passing exit */
12936     }
12937     else if (element == EL_EMPTY)
12938     {
12939       sound_action = ACTION_MOVING;             /* nothing to walk on */
12940     }
12941
12942     /* play sound from background or player, whatever is available */
12943     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12944       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12945     else
12946       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
12947   }
12948   else if (player_can_move &&
12949            IS_PASSABLE(element) && canPassField(x, y, move_direction))
12950   {
12951     if (!ACCESS_FROM(element, opposite_direction))
12952       return MP_NO_ACTION;      /* field not accessible from this direction */
12953
12954     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
12955       return MP_NO_ACTION;
12956
12957     if (IS_EM_GATE(element))
12958     {
12959       if (!player->key[EM_GATE_NR(element)])
12960         return MP_NO_ACTION;
12961     }
12962     else if (IS_EM_GATE_GRAY(element))
12963     {
12964       if (!player->key[EM_GATE_GRAY_NR(element)])
12965         return MP_NO_ACTION;
12966     }
12967     else if (IS_EM_GATE_GRAY_ACTIVE(element))
12968     {
12969       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
12970         return MP_NO_ACTION;
12971     }
12972     else if (IS_EMC_GATE(element))
12973     {
12974       if (!player->key[EMC_GATE_NR(element)])
12975         return MP_NO_ACTION;
12976     }
12977     else if (IS_EMC_GATE_GRAY(element))
12978     {
12979       if (!player->key[EMC_GATE_GRAY_NR(element)])
12980         return MP_NO_ACTION;
12981     }
12982     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
12983     {
12984       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
12985         return MP_NO_ACTION;
12986     }
12987     else if (element == EL_DC_GATE_WHITE ||
12988              element == EL_DC_GATE_WHITE_GRAY ||
12989              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
12990     {
12991       if (player->num_white_keys == 0)
12992         return MP_NO_ACTION;
12993
12994       player->num_white_keys--;
12995     }
12996     else if (IS_SP_PORT(element))
12997     {
12998       if (element == EL_SP_GRAVITY_PORT_LEFT ||
12999           element == EL_SP_GRAVITY_PORT_RIGHT ||
13000           element == EL_SP_GRAVITY_PORT_UP ||
13001           element == EL_SP_GRAVITY_PORT_DOWN)
13002 #if USE_PLAYER_GRAVITY
13003         player->gravity = !player->gravity;
13004 #else
13005         game.gravity = !game.gravity;
13006 #endif
13007       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13008                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13009                element == EL_SP_GRAVITY_ON_PORT_UP ||
13010                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13011 #if USE_PLAYER_GRAVITY
13012         player->gravity = TRUE;
13013 #else
13014         game.gravity = TRUE;
13015 #endif
13016       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13017                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13018                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13019                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13020 #if USE_PLAYER_GRAVITY
13021         player->gravity = FALSE;
13022 #else
13023         game.gravity = FALSE;
13024 #endif
13025     }
13026
13027     /* automatically move to the next field with double speed */
13028     player->programmed_action = move_direction;
13029
13030     if (player->move_delay_reset_counter == 0)
13031     {
13032       player->move_delay_reset_counter = 2;     /* two double speed steps */
13033
13034       DOUBLE_PLAYER_SPEED(player);
13035     }
13036
13037     PlayLevelSoundAction(x, y, ACTION_PASSING);
13038   }
13039   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13040   {
13041     RemoveField(x, y);
13042
13043     if (mode != DF_SNAP)
13044     {
13045       GfxElement[x][y] = GFX_ELEMENT(element);
13046       player->is_digging = TRUE;
13047     }
13048
13049     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13050
13051     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13052                                         player->index_bit, dig_side);
13053
13054     if (mode == DF_SNAP)
13055     {
13056 #if USE_NEW_SNAP_DELAY
13057       if (level.block_snap_field)
13058         setFieldForSnapping(x, y, element, move_direction);
13059       else
13060         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13061 #else
13062       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13063 #endif
13064
13065       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13066                                           player->index_bit, dig_side);
13067     }
13068   }
13069   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13070   {
13071     RemoveField(x, y);
13072
13073     if (is_player && mode != DF_SNAP)
13074     {
13075       GfxElement[x][y] = element;
13076       player->is_collecting = TRUE;
13077     }
13078
13079     if (element == EL_SPEED_PILL)
13080     {
13081       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13082     }
13083     else if (element == EL_EXTRA_TIME && level.time > 0)
13084     {
13085       TimeLeft += level.extra_time;
13086       DrawGameValue_Time(TimeLeft);
13087     }
13088     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13089     {
13090       player->shield_normal_time_left += level.shield_normal_time;
13091       if (element == EL_SHIELD_DEADLY)
13092         player->shield_deadly_time_left += level.shield_deadly_time;
13093     }
13094     else if (element == EL_DYNAMITE ||
13095              element == EL_EM_DYNAMITE ||
13096              element == EL_SP_DISK_RED)
13097     {
13098       if (player->inventory_size < MAX_INVENTORY_SIZE)
13099         player->inventory_element[player->inventory_size++] = element;
13100
13101       DrawGameDoorValues();
13102     }
13103     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13104     {
13105       player->dynabomb_count++;
13106       player->dynabombs_left++;
13107     }
13108     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13109     {
13110       player->dynabomb_size++;
13111     }
13112     else if (element == EL_DYNABOMB_INCREASE_POWER)
13113     {
13114       player->dynabomb_xl = TRUE;
13115     }
13116     else if (IS_KEY(element))
13117     {
13118       player->key[KEY_NR(element)] = TRUE;
13119
13120       DrawGameDoorValues();
13121     }
13122     else if (element == EL_DC_KEY_WHITE)
13123     {
13124       player->num_white_keys++;
13125
13126       /* display white keys? */
13127       /* DrawGameDoorValues(); */
13128     }
13129     else if (IS_ENVELOPE(element))
13130     {
13131       player->show_envelope = element;
13132     }
13133     else if (element == EL_EMC_LENSES)
13134     {
13135       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13136
13137       RedrawAllInvisibleElementsForLenses();
13138     }
13139     else if (element == EL_EMC_MAGNIFIER)
13140     {
13141       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13142
13143       RedrawAllInvisibleElementsForMagnifier();
13144     }
13145     else if (IS_DROPPABLE(element) ||
13146              IS_THROWABLE(element))     /* can be collected and dropped */
13147     {
13148       int i;
13149
13150       if (collect_count == 0)
13151         player->inventory_infinite_element = element;
13152       else
13153         for (i = 0; i < collect_count; i++)
13154           if (player->inventory_size < MAX_INVENTORY_SIZE)
13155             player->inventory_element[player->inventory_size++] = element;
13156
13157       DrawGameDoorValues();
13158     }
13159     else if (collect_count > 0)
13160     {
13161       local_player->gems_still_needed -= collect_count;
13162       if (local_player->gems_still_needed < 0)
13163         local_player->gems_still_needed = 0;
13164
13165       DrawGameValue_Emeralds(local_player->gems_still_needed);
13166     }
13167
13168     RaiseScoreElement(element);
13169     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13170
13171     if (is_player)
13172       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13173                                           player->index_bit, dig_side);
13174
13175     if (mode == DF_SNAP)
13176     {
13177 #if USE_NEW_SNAP_DELAY
13178       if (level.block_snap_field)
13179         setFieldForSnapping(x, y, element, move_direction);
13180       else
13181         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13182 #else
13183       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13184 #endif
13185
13186       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13187                                           player->index_bit, dig_side);
13188     }
13189   }
13190   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13191   {
13192     if (mode == DF_SNAP && element != EL_BD_ROCK)
13193       return MP_NO_ACTION;
13194
13195     if (CAN_FALL(element) && dy)
13196       return MP_NO_ACTION;
13197
13198     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13199         !(element == EL_SPRING && level.use_spring_bug))
13200       return MP_NO_ACTION;
13201
13202     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13203         ((move_direction & MV_VERTICAL &&
13204           ((element_info[element].move_pattern & MV_LEFT &&
13205             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13206            (element_info[element].move_pattern & MV_RIGHT &&
13207             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13208          (move_direction & MV_HORIZONTAL &&
13209           ((element_info[element].move_pattern & MV_UP &&
13210             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13211            (element_info[element].move_pattern & MV_DOWN &&
13212             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13213       return MP_NO_ACTION;
13214
13215     /* do not push elements already moving away faster than player */
13216     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13217         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13218       return MP_NO_ACTION;
13219
13220     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13221     {
13222       if (player->push_delay_value == -1 || !player_was_pushing)
13223         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13224     }
13225     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13226     {
13227       if (player->push_delay_value == -1)
13228         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13229     }
13230     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13231     {
13232       if (!player->is_pushing)
13233         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13234     }
13235
13236     player->is_pushing = TRUE;
13237     player->is_active = TRUE;
13238
13239     if (!(IN_LEV_FIELD(nextx, nexty) &&
13240           (IS_FREE(nextx, nexty) ||
13241            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13242             IS_SB_ELEMENT(element)))))
13243       return MP_NO_ACTION;
13244
13245     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13246       return MP_NO_ACTION;
13247
13248     if (player->push_delay == -1)       /* new pushing; restart delay */
13249       player->push_delay = 0;
13250
13251     if (player->push_delay < player->push_delay_value &&
13252         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13253         element != EL_SPRING && element != EL_BALLOON)
13254     {
13255       /* make sure that there is no move delay before next try to push */
13256       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13257         player->move_delay = 0;
13258
13259       return MP_NO_ACTION;
13260     }
13261
13262     if (IS_SB_ELEMENT(element))
13263     {
13264       if (element == EL_SOKOBAN_FIELD_FULL)
13265       {
13266         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13267         local_player->sokobanfields_still_needed++;
13268       }
13269
13270       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13271       {
13272         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13273         local_player->sokobanfields_still_needed--;
13274       }
13275
13276       Feld[x][y] = EL_SOKOBAN_OBJECT;
13277
13278       if (Back[x][y] == Back[nextx][nexty])
13279         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13280       else if (Back[x][y] != 0)
13281         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13282                                     ACTION_EMPTYING);
13283       else
13284         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13285                                     ACTION_FILLING);
13286
13287       if (local_player->sokobanfields_still_needed == 0 &&
13288           game.emulation == EMU_SOKOBAN)
13289       {
13290         PlayerWins(player);
13291
13292         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13293       }
13294     }
13295     else
13296       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13297
13298     InitMovingField(x, y, move_direction);
13299     GfxAction[x][y] = ACTION_PUSHING;
13300
13301     if (mode == DF_SNAP)
13302       ContinueMoving(x, y);
13303     else
13304       MovPos[x][y] = (dx != 0 ? dx : dy);
13305
13306     Pushed[x][y] = TRUE;
13307     Pushed[nextx][nexty] = TRUE;
13308
13309     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13310       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13311     else
13312       player->push_delay_value = -1;    /* get new value later */
13313
13314     /* check for element change _after_ element has been pushed */
13315     if (game.use_change_when_pushing_bug)
13316     {
13317       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13318                                  player->index_bit, dig_side);
13319       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13320                                           player->index_bit, dig_side);
13321     }
13322   }
13323   else if (IS_SWITCHABLE(element))
13324   {
13325     if (PLAYER_SWITCHING(player, x, y))
13326     {
13327       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13328                                           player->index_bit, dig_side);
13329
13330       return MP_ACTION;
13331     }
13332
13333     player->is_switching = TRUE;
13334     player->switch_x = x;
13335     player->switch_y = y;
13336
13337     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13338
13339     if (element == EL_ROBOT_WHEEL)
13340     {
13341       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13342       ZX = x;
13343       ZY = y;
13344
13345       DrawLevelField(x, y);
13346     }
13347     else if (element == EL_SP_TERMINAL)
13348     {
13349       int xx, yy;
13350
13351       SCAN_PLAYFIELD(xx, yy)
13352       {
13353         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13354           Bang(xx, yy);
13355         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13356           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13357       }
13358     }
13359     else if (IS_BELT_SWITCH(element))
13360     {
13361       ToggleBeltSwitch(x, y);
13362     }
13363     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13364              element == EL_SWITCHGATE_SWITCH_DOWN ||
13365              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13366              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13367     {
13368       ToggleSwitchgateSwitch(x, y);
13369     }
13370     else if (element == EL_LIGHT_SWITCH ||
13371              element == EL_LIGHT_SWITCH_ACTIVE)
13372     {
13373       ToggleLightSwitch(x, y);
13374     }
13375     else if (element == EL_TIMEGATE_SWITCH ||
13376              element == EL_DC_TIMEGATE_SWITCH)
13377     {
13378       ActivateTimegateSwitch(x, y);
13379     }
13380     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13381              element == EL_BALLOON_SWITCH_RIGHT ||
13382              element == EL_BALLOON_SWITCH_UP    ||
13383              element == EL_BALLOON_SWITCH_DOWN  ||
13384              element == EL_BALLOON_SWITCH_NONE  ||
13385              element == EL_BALLOON_SWITCH_ANY)
13386     {
13387       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13388                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13389                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13390                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13391                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13392                              move_direction);
13393     }
13394     else if (element == EL_LAMP)
13395     {
13396       Feld[x][y] = EL_LAMP_ACTIVE;
13397       local_player->lights_still_needed--;
13398
13399       ResetGfxAnimation(x, y);
13400       DrawLevelField(x, y);
13401     }
13402     else if (element == EL_TIME_ORB_FULL)
13403     {
13404       Feld[x][y] = EL_TIME_ORB_EMPTY;
13405
13406       if (level.time > 0 || level.use_time_orb_bug)
13407       {
13408         TimeLeft += level.time_orb_time;
13409         DrawGameValue_Time(TimeLeft);
13410       }
13411
13412       ResetGfxAnimation(x, y);
13413       DrawLevelField(x, y);
13414     }
13415     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13416              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13417     {
13418       int xx, yy;
13419
13420       game.ball_state = !game.ball_state;
13421
13422       SCAN_PLAYFIELD(xx, yy)
13423       {
13424         int e = Feld[xx][yy];
13425
13426         if (game.ball_state)
13427         {
13428           if (e == EL_EMC_MAGIC_BALL)
13429             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13430           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13431             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13432         }
13433         else
13434         {
13435           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13436             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13437           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13438             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13439         }
13440       }
13441     }
13442
13443     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13444                                         player->index_bit, dig_side);
13445
13446     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13447                                         player->index_bit, dig_side);
13448
13449     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13450                                         player->index_bit, dig_side);
13451
13452     return MP_ACTION;
13453   }
13454   else
13455   {
13456     if (!PLAYER_SWITCHING(player, x, y))
13457     {
13458       player->is_switching = TRUE;
13459       player->switch_x = x;
13460       player->switch_y = y;
13461
13462       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13463                                  player->index_bit, dig_side);
13464       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13465                                           player->index_bit, dig_side);
13466
13467       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13468                                  player->index_bit, dig_side);
13469       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13470                                           player->index_bit, dig_side);
13471     }
13472
13473     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13474                                player->index_bit, dig_side);
13475     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13476                                         player->index_bit, dig_side);
13477
13478     return MP_NO_ACTION;
13479   }
13480
13481   player->push_delay = -1;
13482
13483   if (is_player)                /* function can also be called by EL_PENGUIN */
13484   {
13485     if (Feld[x][y] != element)          /* really digged/collected something */
13486     {
13487       player->is_collecting = !player->is_digging;
13488       player->is_active = TRUE;
13489     }
13490   }
13491
13492   return MP_MOVING;
13493 }
13494
13495 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13496 {
13497   int jx = player->jx, jy = player->jy;
13498   int x = jx + dx, y = jy + dy;
13499   int snap_direction = (dx == -1 ? MV_LEFT  :
13500                         dx == +1 ? MV_RIGHT :
13501                         dy == -1 ? MV_UP    :
13502                         dy == +1 ? MV_DOWN  : MV_NONE);
13503   boolean can_continue_snapping = (level.continuous_snapping &&
13504                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13505
13506   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13507     return FALSE;
13508
13509   if (!player->active || !IN_LEV_FIELD(x, y))
13510     return FALSE;
13511
13512   if (dx && dy)
13513     return FALSE;
13514
13515   if (!dx && !dy)
13516   {
13517     if (player->MovPos == 0)
13518       player->is_pushing = FALSE;
13519
13520     player->is_snapping = FALSE;
13521
13522     if (player->MovPos == 0)
13523     {
13524       player->is_moving = FALSE;
13525       player->is_digging = FALSE;
13526       player->is_collecting = FALSE;
13527     }
13528
13529     return FALSE;
13530   }
13531
13532 #if USE_NEW_CONTINUOUS_SNAPPING
13533   /* prevent snapping with already pressed snap key when not allowed */
13534   if (player->is_snapping && !can_continue_snapping)
13535     return FALSE;
13536 #else
13537   if (player->is_snapping)
13538     return FALSE;
13539 #endif
13540
13541   player->MovDir = snap_direction;
13542
13543   if (player->MovPos == 0)
13544   {
13545     player->is_moving = FALSE;
13546     player->is_digging = FALSE;
13547     player->is_collecting = FALSE;
13548   }
13549
13550   player->is_dropping = FALSE;
13551   player->is_dropping_pressed = FALSE;
13552   player->drop_pressed_delay = 0;
13553
13554   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13555     return FALSE;
13556
13557   player->is_snapping = TRUE;
13558   player->is_active = TRUE;
13559
13560   if (player->MovPos == 0)
13561   {
13562     player->is_moving = FALSE;
13563     player->is_digging = FALSE;
13564     player->is_collecting = FALSE;
13565   }
13566
13567   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13568     DrawLevelField(player->last_jx, player->last_jy);
13569
13570   DrawLevelField(x, y);
13571
13572   return TRUE;
13573 }
13574
13575 boolean DropElement(struct PlayerInfo *player)
13576 {
13577   int old_element, new_element;
13578   int dropx = player->jx, dropy = player->jy;
13579   int drop_direction = player->MovDir;
13580   int drop_side = drop_direction;
13581   int drop_element = (player->inventory_size > 0 ?
13582                       player->inventory_element[player->inventory_size - 1] :
13583                       player->inventory_infinite_element != EL_UNDEFINED ?
13584                       player->inventory_infinite_element :
13585                       player->dynabombs_left > 0 ?
13586                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
13587                       EL_UNDEFINED);
13588
13589   player->is_dropping_pressed = TRUE;
13590
13591   /* do not drop an element on top of another element; when holding drop key
13592      pressed without moving, dropped element must move away before the next
13593      element can be dropped (this is especially important if the next element
13594      is dynamite, which can be placed on background for historical reasons) */
13595   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13596     return MP_ACTION;
13597
13598   if (IS_THROWABLE(drop_element))
13599   {
13600     dropx += GET_DX_FROM_DIR(drop_direction);
13601     dropy += GET_DY_FROM_DIR(drop_direction);
13602
13603     if (!IN_LEV_FIELD(dropx, dropy))
13604       return FALSE;
13605   }
13606
13607   old_element = Feld[dropx][dropy];     /* old element at dropping position */
13608   new_element = drop_element;           /* default: no change when dropping */
13609
13610   /* check if player is active, not moving and ready to drop */
13611   if (!player->active || player->MovPos || player->drop_delay > 0)
13612     return FALSE;
13613
13614   /* check if player has anything that can be dropped */
13615   if (new_element == EL_UNDEFINED)
13616     return FALSE;
13617
13618   /* check if drop key was pressed long enough for EM style dynamite */
13619   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13620     return FALSE;
13621
13622   /* check if anything can be dropped at the current position */
13623   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13624     return FALSE;
13625
13626   /* collected custom elements can only be dropped on empty fields */
13627   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13628     return FALSE;
13629
13630   if (old_element != EL_EMPTY)
13631     Back[dropx][dropy] = old_element;   /* store old element on this field */
13632
13633   ResetGfxAnimation(dropx, dropy);
13634   ResetRandomAnimationValue(dropx, dropy);
13635
13636   if (player->inventory_size > 0 ||
13637       player->inventory_infinite_element != EL_UNDEFINED)
13638   {
13639     if (player->inventory_size > 0)
13640     {
13641       player->inventory_size--;
13642
13643       DrawGameDoorValues();
13644
13645       if (new_element == EL_DYNAMITE)
13646         new_element = EL_DYNAMITE_ACTIVE;
13647       else if (new_element == EL_EM_DYNAMITE)
13648         new_element = EL_EM_DYNAMITE_ACTIVE;
13649       else if (new_element == EL_SP_DISK_RED)
13650         new_element = EL_SP_DISK_RED_ACTIVE;
13651     }
13652
13653     Feld[dropx][dropy] = new_element;
13654
13655     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13656       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13657                           el2img(Feld[dropx][dropy]), 0);
13658
13659     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13660
13661     /* needed if previous element just changed to "empty" in the last frame */
13662     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
13663
13664     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13665                                player->index_bit, drop_side);
13666     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13667                                         CE_PLAYER_DROPS_X,
13668                                         player->index_bit, drop_side);
13669
13670     TestIfElementTouchesCustomElement(dropx, dropy);
13671   }
13672   else          /* player is dropping a dyna bomb */
13673   {
13674     player->dynabombs_left--;
13675
13676     Feld[dropx][dropy] = new_element;
13677
13678     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13679       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13680                           el2img(Feld[dropx][dropy]), 0);
13681
13682     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13683   }
13684
13685   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
13686     InitField_WithBug1(dropx, dropy, FALSE);
13687
13688   new_element = Feld[dropx][dropy];     /* element might have changed */
13689
13690   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
13691       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
13692   {
13693     int move_direction, nextx, nexty;
13694
13695     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
13696       MovDir[dropx][dropy] = drop_direction;
13697
13698     move_direction = MovDir[dropx][dropy];
13699     nextx = dropx + GET_DX_FROM_DIR(move_direction);
13700     nexty = dropy + GET_DY_FROM_DIR(move_direction);
13701
13702     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
13703
13704 #if USE_FIX_IMPACT_COLLISION
13705     /* do not cause impact style collision by dropping elements that can fall */
13706     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13707 #else
13708     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13709 #endif
13710   }
13711
13712   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
13713   player->is_dropping = TRUE;
13714
13715   player->drop_pressed_delay = 0;
13716   player->is_dropping_pressed = FALSE;
13717
13718   player->drop_x = dropx;
13719   player->drop_y = dropy;
13720
13721   return TRUE;
13722 }
13723
13724 /* ------------------------------------------------------------------------- */
13725 /* game sound playing functions                                              */
13726 /* ------------------------------------------------------------------------- */
13727
13728 static int *loop_sound_frame = NULL;
13729 static int *loop_sound_volume = NULL;
13730
13731 void InitPlayLevelSound()
13732 {
13733   int num_sounds = getSoundListSize();
13734
13735   checked_free(loop_sound_frame);
13736   checked_free(loop_sound_volume);
13737
13738   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
13739   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
13740 }
13741
13742 static void PlayLevelSound(int x, int y, int nr)
13743 {
13744   int sx = SCREENX(x), sy = SCREENY(y);
13745   int volume, stereo_position;
13746   int max_distance = 8;
13747   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
13748
13749   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
13750       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
13751     return;
13752
13753   if (!IN_LEV_FIELD(x, y) ||
13754       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
13755       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
13756     return;
13757
13758   volume = SOUND_MAX_VOLUME;
13759
13760   if (!IN_SCR_FIELD(sx, sy))
13761   {
13762     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
13763     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
13764
13765     volume -= volume * (dx > dy ? dx : dy) / max_distance;
13766   }
13767
13768   stereo_position = (SOUND_MAX_LEFT +
13769                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
13770                      (SCR_FIELDX + 2 * max_distance));
13771
13772   if (IS_LOOP_SOUND(nr))
13773   {
13774     /* This assures that quieter loop sounds do not overwrite louder ones,
13775        while restarting sound volume comparison with each new game frame. */
13776
13777     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
13778       return;
13779
13780     loop_sound_volume[nr] = volume;
13781     loop_sound_frame[nr] = FrameCounter;
13782   }
13783
13784   PlaySoundExt(nr, volume, stereo_position, type);
13785 }
13786
13787 static void PlayLevelSoundNearest(int x, int y, int sound_action)
13788 {
13789   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
13790                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
13791                  y < LEVELY(BY1) ? LEVELY(BY1) :
13792                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
13793                  sound_action);
13794 }
13795
13796 static void PlayLevelSoundAction(int x, int y, int action)
13797 {
13798   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
13799 }
13800
13801 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
13802 {
13803   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13804
13805   if (sound_effect != SND_UNDEFINED)
13806     PlayLevelSound(x, y, sound_effect);
13807 }
13808
13809 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
13810                                               int action)
13811 {
13812   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13813
13814   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13815     PlayLevelSound(x, y, sound_effect);
13816 }
13817
13818 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
13819 {
13820   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13821
13822   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13823     PlayLevelSound(x, y, sound_effect);
13824 }
13825
13826 static void StopLevelSoundActionIfLoop(int x, int y, int action)
13827 {
13828   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13829
13830   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13831     StopSound(sound_effect);
13832 }
13833
13834 static void PlayLevelMusic()
13835 {
13836   if (levelset.music[level_nr] != MUS_UNDEFINED)
13837     PlayMusic(levelset.music[level_nr]);        /* from config file */
13838   else
13839     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
13840 }
13841
13842 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
13843 {
13844   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13845   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
13846   int x = xx - 1 - offset;
13847   int y = yy - 1 - offset;
13848
13849   switch (sample)
13850   {
13851     case SAMPLE_blank:
13852       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13853       break;
13854
13855     case SAMPLE_roll:
13856       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13857       break;
13858
13859     case SAMPLE_stone:
13860       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13861       break;
13862
13863     case SAMPLE_nut:
13864       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13865       break;
13866
13867     case SAMPLE_crack:
13868       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13869       break;
13870
13871     case SAMPLE_bug:
13872       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13873       break;
13874
13875     case SAMPLE_tank:
13876       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13877       break;
13878
13879     case SAMPLE_android_clone:
13880       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13881       break;
13882
13883     case SAMPLE_android_move:
13884       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13885       break;
13886
13887     case SAMPLE_spring:
13888       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13889       break;
13890
13891     case SAMPLE_slurp:
13892       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
13893       break;
13894
13895     case SAMPLE_eater:
13896       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13897       break;
13898
13899     case SAMPLE_eater_eat:
13900       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13901       break;
13902
13903     case SAMPLE_alien:
13904       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13905       break;
13906
13907     case SAMPLE_collect:
13908       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13909       break;
13910
13911     case SAMPLE_diamond:
13912       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13913       break;
13914
13915     case SAMPLE_squash:
13916       /* !!! CHECK THIS !!! */
13917 #if 1
13918       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13919 #else
13920       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13921 #endif
13922       break;
13923
13924     case SAMPLE_wonderfall:
13925       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13926       break;
13927
13928     case SAMPLE_drip:
13929       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13930       break;
13931
13932     case SAMPLE_push:
13933       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13934       break;
13935
13936     case SAMPLE_dirt:
13937       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13938       break;
13939
13940     case SAMPLE_acid:
13941       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13942       break;
13943
13944     case SAMPLE_ball:
13945       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13946       break;
13947
13948     case SAMPLE_grow:
13949       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13950       break;
13951
13952     case SAMPLE_wonder:
13953       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13954       break;
13955
13956     case SAMPLE_door:
13957       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13958       break;
13959
13960     case SAMPLE_exit_open:
13961       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13962       break;
13963
13964     case SAMPLE_exit_leave:
13965       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13966       break;
13967
13968     case SAMPLE_dynamite:
13969       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13970       break;
13971
13972     case SAMPLE_tick:
13973       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13974       break;
13975
13976     case SAMPLE_press:
13977       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13978       break;
13979
13980     case SAMPLE_wheel:
13981       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13982       break;
13983
13984     case SAMPLE_boom:
13985       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13986       break;
13987
13988     case SAMPLE_die:
13989       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13990       break;
13991
13992     case SAMPLE_time:
13993       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13994       break;
13995
13996     default:
13997       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13998       break;
13999   }
14000 }
14001
14002 #if 0
14003 void ChangeTime(int value)
14004 {
14005   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
14006
14007   *time += value;
14008
14009   /* EMC game engine uses value from time counter of RND game engine */
14010   level.native_em_level->lev->time = *time;
14011
14012   DrawGameValue_Time(*time);
14013 }
14014
14015 void RaiseScore(int value)
14016 {
14017   /* EMC game engine and RND game engine have separate score counters */
14018   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
14019                 &level.native_em_level->lev->score : &local_player->score);
14020
14021   *score += value;
14022
14023   DrawGameValue_Score(*score);
14024 }
14025 #endif
14026
14027 void RaiseScore(int value)
14028 {
14029   local_player->score += value;
14030
14031   DrawGameValue_Score(local_player->score);
14032 }
14033
14034 void RaiseScoreElement(int element)
14035 {
14036   switch (element)
14037   {
14038     case EL_EMERALD:
14039     case EL_BD_DIAMOND:
14040     case EL_EMERALD_YELLOW:
14041     case EL_EMERALD_RED:
14042     case EL_EMERALD_PURPLE:
14043     case EL_SP_INFOTRON:
14044       RaiseScore(level.score[SC_EMERALD]);
14045       break;
14046     case EL_DIAMOND:
14047       RaiseScore(level.score[SC_DIAMOND]);
14048       break;
14049     case EL_CRYSTAL:
14050       RaiseScore(level.score[SC_CRYSTAL]);
14051       break;
14052     case EL_PEARL:
14053       RaiseScore(level.score[SC_PEARL]);
14054       break;
14055     case EL_BUG:
14056     case EL_BD_BUTTERFLY:
14057     case EL_SP_ELECTRON:
14058       RaiseScore(level.score[SC_BUG]);
14059       break;
14060     case EL_SPACESHIP:
14061     case EL_BD_FIREFLY:
14062     case EL_SP_SNIKSNAK:
14063       RaiseScore(level.score[SC_SPACESHIP]);
14064       break;
14065     case EL_YAMYAM:
14066     case EL_DARK_YAMYAM:
14067       RaiseScore(level.score[SC_YAMYAM]);
14068       break;
14069     case EL_ROBOT:
14070       RaiseScore(level.score[SC_ROBOT]);
14071       break;
14072     case EL_PACMAN:
14073       RaiseScore(level.score[SC_PACMAN]);
14074       break;
14075     case EL_NUT:
14076       RaiseScore(level.score[SC_NUT]);
14077       break;
14078     case EL_DYNAMITE:
14079     case EL_EM_DYNAMITE:
14080     case EL_SP_DISK_RED:
14081     case EL_DYNABOMB_INCREASE_NUMBER:
14082     case EL_DYNABOMB_INCREASE_SIZE:
14083     case EL_DYNABOMB_INCREASE_POWER:
14084       RaiseScore(level.score[SC_DYNAMITE]);
14085       break;
14086     case EL_SHIELD_NORMAL:
14087     case EL_SHIELD_DEADLY:
14088       RaiseScore(level.score[SC_SHIELD]);
14089       break;
14090     case EL_EXTRA_TIME:
14091       RaiseScore(level.extra_time_score);
14092       break;
14093     case EL_KEY_1:
14094     case EL_KEY_2:
14095     case EL_KEY_3:
14096     case EL_KEY_4:
14097     case EL_EM_KEY_1:
14098     case EL_EM_KEY_2:
14099     case EL_EM_KEY_3:
14100     case EL_EM_KEY_4:
14101     case EL_EMC_KEY_5:
14102     case EL_EMC_KEY_6:
14103     case EL_EMC_KEY_7:
14104     case EL_EMC_KEY_8:
14105     case EL_DC_KEY_WHITE:
14106       RaiseScore(level.score[SC_KEY]);
14107       break;
14108     default:
14109       RaiseScore(element_info[element].collect_score);
14110       break;
14111   }
14112 }
14113
14114 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14115 {
14116   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14117   {
14118 #if defined(NETWORK_AVALIABLE)
14119     if (options.network)
14120       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14121     else
14122 #endif
14123     {
14124       if (quick_quit)
14125       {
14126 #if 1
14127
14128 #if 1
14129         FadeSkipNextFadeIn();
14130 #else
14131         fading = fading_none;
14132 #endif
14133
14134 #else
14135         OpenDoor(DOOR_CLOSE_1);
14136 #endif
14137
14138         game_status = GAME_MODE_MAIN;
14139
14140 #if 1
14141         DrawAndFadeInMainMenu(REDRAW_FIELD);
14142 #else
14143         DrawMainMenu();
14144 #endif
14145       }
14146       else
14147       {
14148 #if 0
14149         FadeOut(REDRAW_FIELD);
14150 #endif
14151
14152         game_status = GAME_MODE_MAIN;
14153
14154         DrawAndFadeInMainMenu(REDRAW_FIELD);
14155       }
14156     }
14157   }
14158   else          /* continue playing the game */
14159   {
14160     if (tape.playing && tape.deactivate_display)
14161       TapeDeactivateDisplayOff(TRUE);
14162
14163     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14164
14165     if (tape.playing && tape.deactivate_display)
14166       TapeDeactivateDisplayOn();
14167   }
14168 }
14169
14170 void RequestQuitGame(boolean ask_if_really_quit)
14171 {
14172   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14173   boolean skip_request = AllPlayersGone || quick_quit;
14174
14175   RequestQuitGameExt(skip_request, quick_quit,
14176                      "Do you really want to quit the game ?");
14177 }
14178
14179
14180 /* ------------------------------------------------------------------------- */
14181 /* random generator functions                                                */
14182 /* ------------------------------------------------------------------------- */
14183
14184 unsigned int InitEngineRandom_RND(long seed)
14185 {
14186   game.num_random_calls = 0;
14187
14188 #if 0
14189   unsigned int rnd_seed = InitEngineRandom(seed);
14190
14191   printf("::: START RND: %d\n", rnd_seed);
14192
14193   return rnd_seed;
14194 #else
14195
14196   return InitEngineRandom(seed);
14197
14198 #endif
14199
14200 }
14201
14202 unsigned int RND(int max)
14203 {
14204   if (max > 0)
14205   {
14206     game.num_random_calls++;
14207
14208     return GetEngineRandom(max);
14209   }
14210
14211   return 0;
14212 }
14213
14214
14215 /* ------------------------------------------------------------------------- */
14216 /* game engine snapshot handling functions                                   */
14217 /* ------------------------------------------------------------------------- */
14218
14219 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
14220
14221 struct EngineSnapshotInfo
14222 {
14223   /* runtime values for custom element collect score */
14224   int collect_score[NUM_CUSTOM_ELEMENTS];
14225
14226   /* runtime values for group element choice position */
14227   int choice_pos[NUM_GROUP_ELEMENTS];
14228
14229   /* runtime values for belt position animations */
14230   int belt_graphic[4 * NUM_BELT_PARTS];
14231   int belt_anim_mode[4 * NUM_BELT_PARTS];
14232 };
14233
14234 struct EngineSnapshotNodeInfo
14235 {
14236   void *buffer_orig;
14237   void *buffer_copy;
14238   int size;
14239 };
14240
14241 static struct EngineSnapshotInfo engine_snapshot_rnd;
14242 static ListNode *engine_snapshot_list = NULL;
14243 static char *snapshot_level_identifier = NULL;
14244 static int snapshot_level_nr = -1;
14245
14246 void FreeEngineSnapshot()
14247 {
14248   while (engine_snapshot_list != NULL)
14249     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14250                        checked_free);
14251
14252   setString(&snapshot_level_identifier, NULL);
14253   snapshot_level_nr = -1;
14254 }
14255
14256 static void SaveEngineSnapshotValues_RND()
14257 {
14258   static int belt_base_active_element[4] =
14259   {
14260     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14261     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14262     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14263     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14264   };
14265   int i, j;
14266
14267   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14268   {
14269     int element = EL_CUSTOM_START + i;
14270
14271     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14272   }
14273
14274   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14275   {
14276     int element = EL_GROUP_START + i;
14277
14278     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14279   }
14280
14281   for (i = 0; i < 4; i++)
14282   {
14283     for (j = 0; j < NUM_BELT_PARTS; j++)
14284     {
14285       int element = belt_base_active_element[i] + j;
14286       int graphic = el2img(element);
14287       int anim_mode = graphic_info[graphic].anim_mode;
14288
14289       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14290       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14291     }
14292   }
14293 }
14294
14295 static void LoadEngineSnapshotValues_RND()
14296 {
14297   unsigned long num_random_calls = game.num_random_calls;
14298   int i, j;
14299
14300   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14301   {
14302     int element = EL_CUSTOM_START + i;
14303
14304     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14305   }
14306
14307   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14308   {
14309     int element = EL_GROUP_START + i;
14310
14311     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14312   }
14313
14314   for (i = 0; i < 4; i++)
14315   {
14316     for (j = 0; j < NUM_BELT_PARTS; j++)
14317     {
14318       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14319       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14320
14321       graphic_info[graphic].anim_mode = anim_mode;
14322     }
14323   }
14324
14325   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14326   {
14327     InitRND(tape.random_seed);
14328     for (i = 0; i < num_random_calls; i++)
14329       RND(1);
14330   }
14331
14332   if (game.num_random_calls != num_random_calls)
14333   {
14334     Error(ERR_INFO, "number of random calls out of sync");
14335     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14336     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14337     Error(ERR_EXIT, "this should not happen -- please debug");
14338   }
14339 }
14340
14341 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14342 {
14343   struct EngineSnapshotNodeInfo *bi =
14344     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14345
14346   bi->buffer_orig = buffer;
14347   bi->buffer_copy = checked_malloc(size);
14348   bi->size = size;
14349
14350   memcpy(bi->buffer_copy, buffer, size);
14351
14352   addNodeToList(&engine_snapshot_list, NULL, bi);
14353 }
14354
14355 void SaveEngineSnapshot()
14356 {
14357   FreeEngineSnapshot();         /* free previous snapshot, if needed */
14358
14359   if (level_editor_test_game)   /* do not save snapshots from editor */
14360     return;
14361
14362   /* copy some special values to a structure better suited for the snapshot */
14363
14364   SaveEngineSnapshotValues_RND();
14365   SaveEngineSnapshotValues_EM();
14366
14367   /* save values stored in special snapshot structure */
14368
14369   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14370   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14371
14372   /* save further RND engine values */
14373
14374   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14375   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14376   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14377
14378   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14379   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14380   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14381   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14382
14383   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14384   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14385   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14386   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14387   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14388
14389   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14390   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14391   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14392
14393   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14394
14395   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14396
14397   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14398   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14399
14400   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14401   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14402   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14403   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14404   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14405   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14406   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14407   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14408   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14409   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14410   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14411   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14412   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14413   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14414   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14415   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14416   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
14417   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
14418
14419   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14420   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14421
14422   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14423   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14424   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14425
14426   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14427   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14428
14429   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14430   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14431   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14432   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14433   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14434
14435   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14436   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14437
14438   /* save level identification information */
14439
14440   setString(&snapshot_level_identifier, leveldir_current->identifier);
14441   snapshot_level_nr = level_nr;
14442
14443 #if 0
14444   ListNode *node = engine_snapshot_list;
14445   int num_bytes = 0;
14446
14447   while (node != NULL)
14448   {
14449     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14450
14451     node = node->next;
14452   }
14453
14454   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14455 #endif
14456 }
14457
14458 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
14459 {
14460   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
14461 }
14462
14463 void LoadEngineSnapshot()
14464 {
14465   ListNode *node = engine_snapshot_list;
14466
14467   if (engine_snapshot_list == NULL)
14468     return;
14469
14470   while (node != NULL)
14471   {
14472     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
14473
14474     node = node->next;
14475   }
14476
14477   /* restore special values from snapshot structure */
14478
14479   LoadEngineSnapshotValues_RND();
14480   LoadEngineSnapshotValues_EM();
14481 }
14482
14483 boolean CheckEngineSnapshot()
14484 {
14485   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14486           snapshot_level_nr == level_nr);
14487 }
14488
14489
14490 /* ---------- new game button stuff ---------------------------------------- */
14491
14492 /* graphic position values for game buttons */
14493 #define GAME_BUTTON_XSIZE       30
14494 #define GAME_BUTTON_YSIZE       30
14495 #define GAME_BUTTON_XPOS        5
14496 #define GAME_BUTTON_YPOS        215
14497 #define SOUND_BUTTON_XPOS       5
14498 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
14499
14500 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14501 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14502 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14503 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14504 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14505 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14506
14507 static struct
14508 {
14509   int *x, *y;
14510   int gd_x, gd_y;
14511   int gadget_id;
14512   char *infotext;
14513 } gamebutton_info[NUM_GAME_BUTTONS] =
14514 {
14515 #if 1
14516   {
14517     &game.button.stop.x,        &game.button.stop.y,
14518     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
14519     GAME_CTRL_ID_STOP,
14520     "stop game"
14521   },
14522   {
14523     &game.button.pause.x,       &game.button.pause.y,
14524     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
14525     GAME_CTRL_ID_PAUSE,
14526     "pause game"
14527   },
14528   {
14529     &game.button.play.x,        &game.button.play.y,
14530     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
14531     GAME_CTRL_ID_PLAY,
14532     "play game"
14533   },
14534   {
14535     &game.button.sound_music.x, &game.button.sound_music.y,
14536     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
14537     SOUND_CTRL_ID_MUSIC,
14538     "background music on/off"
14539   },
14540   {
14541     &game.button.sound_loops.x, &game.button.sound_loops.y,
14542     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
14543     SOUND_CTRL_ID_LOOPS,
14544     "sound loops on/off"
14545   },
14546   {
14547     &game.button.sound_simple.x,&game.button.sound_simple.y,
14548     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
14549     SOUND_CTRL_ID_SIMPLE,
14550     "normal sounds on/off"
14551   }
14552 #else
14553   {
14554     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
14555     GAME_CTRL_ID_STOP,
14556     "stop game"
14557   },
14558   {
14559     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
14560     GAME_CTRL_ID_PAUSE,
14561     "pause game"
14562   },
14563   {
14564     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
14565     GAME_CTRL_ID_PLAY,
14566     "play game"
14567   },
14568   {
14569     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
14570     SOUND_CTRL_ID_MUSIC,
14571     "background music on/off"
14572   },
14573   {
14574     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
14575     SOUND_CTRL_ID_LOOPS,
14576     "sound loops on/off"
14577   },
14578   {
14579     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
14580     SOUND_CTRL_ID_SIMPLE,
14581     "normal sounds on/off"
14582   }
14583 #endif
14584 };
14585
14586 void CreateGameButtons()
14587 {
14588   int i;
14589
14590   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14591   {
14592     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
14593     struct GadgetInfo *gi;
14594     int button_type;
14595     boolean checked;
14596     unsigned long event_mask;
14597     int x, y;
14598     int gd_xoffset, gd_yoffset;
14599     int gd_x1, gd_x2, gd_y1, gd_y2;
14600     int id = i;
14601
14602     x = DX + *gamebutton_info[i].x;
14603     y = DY + *gamebutton_info[i].y;
14604     gd_xoffset = gamebutton_info[i].gd_x;
14605     gd_yoffset = gamebutton_info[i].gd_y;
14606     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
14607     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
14608
14609     if (id == GAME_CTRL_ID_STOP ||
14610         id == GAME_CTRL_ID_PAUSE ||
14611         id == GAME_CTRL_ID_PLAY)
14612     {
14613       button_type = GD_TYPE_NORMAL_BUTTON;
14614       checked = FALSE;
14615       event_mask = GD_EVENT_RELEASED;
14616       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14617       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14618     }
14619     else
14620     {
14621       button_type = GD_TYPE_CHECK_BUTTON;
14622       checked =
14623         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14624          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14625          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14626       event_mask = GD_EVENT_PRESSED;
14627       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
14628       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14629     }
14630
14631     gi = CreateGadget(GDI_CUSTOM_ID, id,
14632                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14633 #if 1
14634                       GDI_X, x,
14635                       GDI_Y, y,
14636 #else
14637                       GDI_X, DX + gd_xoffset,
14638                       GDI_Y, DY + gd_yoffset,
14639 #endif
14640                       GDI_WIDTH, GAME_BUTTON_XSIZE,
14641                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
14642                       GDI_TYPE, button_type,
14643                       GDI_STATE, GD_BUTTON_UNPRESSED,
14644                       GDI_CHECKED, checked,
14645                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
14646                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
14647                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
14648                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
14649                       GDI_EVENT_MASK, event_mask,
14650                       GDI_CALLBACK_ACTION, HandleGameButtons,
14651                       GDI_END);
14652
14653     if (gi == NULL)
14654       Error(ERR_EXIT, "cannot create gadget");
14655
14656     game_gadget[id] = gi;
14657   }
14658 }
14659
14660 void FreeGameButtons()
14661 {
14662   int i;
14663
14664   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14665     FreeGadget(game_gadget[i]);
14666 }
14667
14668 static void MapGameButtons()
14669 {
14670   int i;
14671
14672   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14673     MapGadget(game_gadget[i]);
14674 }
14675
14676 void UnmapGameButtons()
14677 {
14678   int i;
14679
14680   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14681     UnmapGadget(game_gadget[i]);
14682 }
14683
14684 static void HandleGameButtons(struct GadgetInfo *gi)
14685 {
14686   int id = gi->custom_id;
14687
14688   if (game_status != GAME_MODE_PLAYING)
14689     return;
14690
14691   switch (id)
14692   {
14693     case GAME_CTRL_ID_STOP:
14694       if (tape.playing)
14695         TapeStop();
14696       else
14697         RequestQuitGame(TRUE);
14698       break;
14699
14700     case GAME_CTRL_ID_PAUSE:
14701       if (options.network)
14702       {
14703 #if defined(NETWORK_AVALIABLE)
14704         if (tape.pausing)
14705           SendToServer_ContinuePlaying();
14706         else
14707           SendToServer_PausePlaying();
14708 #endif
14709       }
14710       else
14711         TapeTogglePause(TAPE_TOGGLE_MANUAL);
14712       break;
14713
14714     case GAME_CTRL_ID_PLAY:
14715       if (tape.pausing)
14716       {
14717 #if defined(NETWORK_AVALIABLE)
14718         if (options.network)
14719           SendToServer_ContinuePlaying();
14720         else
14721 #endif
14722         {
14723           tape.pausing = FALSE;
14724           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
14725         }
14726       }
14727       break;
14728
14729     case SOUND_CTRL_ID_MUSIC:
14730       if (setup.sound_music)
14731       { 
14732         setup.sound_music = FALSE;
14733         FadeMusic();
14734       }
14735       else if (audio.music_available)
14736       { 
14737         setup.sound = setup.sound_music = TRUE;
14738
14739         SetAudioMode(setup.sound);
14740
14741         PlayLevelMusic();
14742       }
14743       break;
14744
14745     case SOUND_CTRL_ID_LOOPS:
14746       if (setup.sound_loops)
14747         setup.sound_loops = FALSE;
14748       else if (audio.loops_available)
14749       {
14750         setup.sound = setup.sound_loops = TRUE;
14751         SetAudioMode(setup.sound);
14752       }
14753       break;
14754
14755     case SOUND_CTRL_ID_SIMPLE:
14756       if (setup.sound_simple)
14757         setup.sound_simple = FALSE;
14758       else if (audio.sound_available)
14759       {
14760         setup.sound = setup.sound_simple = TRUE;
14761         SetAudioMode(setup.sound);
14762       }
14763       break;
14764
14765     default:
14766       break;
14767   }
14768 }