rnd-20070323-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 + (i % 4) * MINI_TILEX;
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 (!level.shifted_relocation || center_screen)
4195       {
4196         /* quick relocation (without scrolling), with centering of screen */
4197
4198         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4199                     x > SBX_Right + MIDPOSX ? SBX_Right :
4200                     x - MIDPOSX);
4201
4202         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4203                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4204                     y - MIDPOSY);
4205       }
4206       else
4207       {
4208         /* quick relocation (without scrolling), but do not center screen */
4209
4210         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4211                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4212                                old_x - MIDPOSX);
4213
4214         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4215                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4216                                old_y - MIDPOSY);
4217
4218         int offset_x = x + (scroll_x - center_scroll_x);
4219         int offset_y = y + (scroll_y - center_scroll_y);
4220
4221         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4222                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4223                     offset_x - MIDPOSX);
4224
4225         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4226                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4227                     offset_y - MIDPOSY);
4228       }
4229     }
4230     else
4231     {
4232       /* quick relocation (without scrolling), inside visible screen area */
4233
4234       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
4235           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4236         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4237
4238       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
4239           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4240         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4241
4242       /* don't scroll over playfield boundaries */
4243       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4244         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4245
4246       /* don't scroll over playfield boundaries */
4247       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4248         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4249     }
4250
4251     RedrawPlayfield(TRUE, 0,0,0,0);
4252   }
4253   else
4254   {
4255 #if 1
4256     int scroll_xx, scroll_yy;
4257
4258     if (!level.shifted_relocation || center_screen)
4259     {
4260       /* visible relocation (with scrolling), with centering of screen */
4261
4262       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4263                    x > SBX_Right + MIDPOSX ? SBX_Right :
4264                    x - MIDPOSX);
4265
4266       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4267                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
4268                    y - MIDPOSY);
4269     }
4270     else
4271     {
4272       /* visible relocation (with scrolling), but do not center screen */
4273
4274       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4275                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
4276                              old_x - MIDPOSX);
4277
4278       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4279                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4280                              old_y - MIDPOSY);
4281
4282       int offset_x = x + (scroll_x - center_scroll_x);
4283       int offset_y = y + (scroll_y - center_scroll_y);
4284
4285       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4286                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4287                    offset_x - MIDPOSX);
4288
4289       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4290                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4291                    offset_y - MIDPOSY);
4292     }
4293
4294 #else
4295
4296     /* visible relocation (with scrolling), with centering of screen */
4297
4298     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4299                      x > SBX_Right + MIDPOSX ? SBX_Right :
4300                      x - MIDPOSX);
4301
4302     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4303                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
4304                      y - MIDPOSY);
4305 #endif
4306
4307     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
4308
4309     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4310     {
4311       int dx = 0, dy = 0;
4312       int fx = FX, fy = FY;
4313
4314       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4315       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4316
4317       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
4318         break;
4319
4320       scroll_x -= dx;
4321       scroll_y -= dy;
4322
4323       fx += dx * TILEX / 2;
4324       fy += dy * TILEY / 2;
4325
4326       ScrollLevel(dx, dy);
4327       DrawAllPlayers();
4328
4329       /* scroll in two steps of half tile size to make things smoother */
4330       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4331       FlushDisplay();
4332       Delay(wait_delay_value);
4333
4334       /* scroll second step to align at full tile size */
4335       BackToFront();
4336       Delay(wait_delay_value);
4337     }
4338
4339     DrawAllPlayers();
4340     BackToFront();
4341     Delay(wait_delay_value);
4342   }
4343 }
4344
4345 void RelocatePlayer(int jx, int jy, int el_player_raw)
4346 {
4347   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4348   int player_nr = GET_PLAYER_NR(el_player);
4349   struct PlayerInfo *player = &stored_player[player_nr];
4350   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4351   boolean no_delay = (tape.warp_forward);
4352   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4353   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4354   int old_jx = player->jx;
4355   int old_jy = player->jy;
4356   int old_element = Feld[old_jx][old_jy];
4357   int element = Feld[jx][jy];
4358   boolean player_relocated = (old_jx != jx || old_jy != jy);
4359
4360   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4361   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
4362   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4363   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
4364   int leave_side_horiz = move_dir_horiz;
4365   int leave_side_vert  = move_dir_vert;
4366   int enter_side = enter_side_horiz | enter_side_vert;
4367   int leave_side = leave_side_horiz | leave_side_vert;
4368
4369   if (player->GameOver)         /* do not reanimate dead player */
4370     return;
4371
4372   if (!player_relocated)        /* no need to relocate the player */
4373     return;
4374
4375   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
4376   {
4377     RemoveField(jx, jy);        /* temporarily remove newly placed player */
4378     DrawLevelField(jx, jy);
4379   }
4380
4381   if (player->present)
4382   {
4383     while (player->MovPos)
4384     {
4385       ScrollPlayer(player, SCROLL_GO_ON);
4386       ScrollScreen(NULL, SCROLL_GO_ON);
4387
4388       AdvanceFrameAndPlayerCounters(player->index_nr);
4389
4390       DrawPlayer(player);
4391
4392       BackToFront();
4393       Delay(wait_delay_value);
4394     }
4395
4396     DrawPlayer(player);         /* needed here only to cleanup last field */
4397     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
4398
4399     player->is_moving = FALSE;
4400   }
4401
4402   if (IS_CUSTOM_ELEMENT(old_element))
4403     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4404                                CE_LEFT_BY_PLAYER,
4405                                player->index_bit, leave_side);
4406
4407   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4408                                       CE_PLAYER_LEAVES_X,
4409                                       player->index_bit, leave_side);
4410
4411   Feld[jx][jy] = el_player;
4412   InitPlayerField(jx, jy, el_player, TRUE);
4413
4414   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4415   {
4416     Feld[jx][jy] = element;
4417     InitField(jx, jy, FALSE);
4418   }
4419
4420   /* only visually relocate centered player */
4421   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4422                      FALSE, level.instant_relocation);
4423
4424   TestIfPlayerTouchesBadThing(jx, jy);
4425   TestIfPlayerTouchesCustomElement(jx, jy);
4426
4427   if (IS_CUSTOM_ELEMENT(element))
4428     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4429                                player->index_bit, enter_side);
4430
4431   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4432                                       player->index_bit, enter_side);
4433 }
4434
4435 void Explode(int ex, int ey, int phase, int mode)
4436 {
4437   int x, y;
4438   int last_phase;
4439   int border_element;
4440
4441   /* !!! eliminate this variable !!! */
4442   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4443
4444   if (game.explosions_delayed)
4445   {
4446     ExplodeField[ex][ey] = mode;
4447     return;
4448   }
4449
4450   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
4451   {
4452     int center_element = Feld[ex][ey];
4453     int artwork_element, explosion_element;     /* set these values later */
4454
4455 #if 0
4456     /* --- This is only really needed (and now handled) in "Impact()". --- */
4457     /* do not explode moving elements that left the explode field in time */
4458     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4459         center_element == EL_EMPTY &&
4460         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4461       return;
4462 #endif
4463
4464 #if 0
4465     /* !!! at this place, the center element may be EL_BLOCKED !!! */
4466     if (mode == EX_TYPE_NORMAL ||
4467         mode == EX_TYPE_CENTER ||
4468         mode == EX_TYPE_CROSS)
4469       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4470 #endif
4471
4472     /* remove things displayed in background while burning dynamite */
4473     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4474       Back[ex][ey] = 0;
4475
4476     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4477     {
4478       /* put moving element to center field (and let it explode there) */
4479       center_element = MovingOrBlocked2Element(ex, ey);
4480       RemoveMovingField(ex, ey);
4481       Feld[ex][ey] = center_element;
4482     }
4483
4484     /* now "center_element" is finally determined -- set related values now */
4485     artwork_element = center_element;           /* for custom player artwork */
4486     explosion_element = center_element;         /* for custom player artwork */
4487
4488     if (IS_PLAYER(ex, ey))
4489     {
4490       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4491
4492       artwork_element = stored_player[player_nr].artwork_element;
4493
4494       if (level.use_explosion_element[player_nr])
4495       {
4496         explosion_element = level.explosion_element[player_nr];
4497         artwork_element = explosion_element;
4498       }
4499     }
4500
4501 #if 1
4502     if (mode == EX_TYPE_NORMAL ||
4503         mode == EX_TYPE_CENTER ||
4504         mode == EX_TYPE_CROSS)
4505       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4506 #endif
4507
4508     last_phase = element_info[explosion_element].explosion_delay + 1;
4509
4510     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4511     {
4512       int xx = x - ex + 1;
4513       int yy = y - ey + 1;
4514       int element;
4515
4516       if (!IN_LEV_FIELD(x, y) ||
4517           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4518           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
4519         continue;
4520
4521       element = Feld[x][y];
4522
4523       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4524       {
4525         element = MovingOrBlocked2Element(x, y);
4526
4527         if (!IS_EXPLOSION_PROOF(element))
4528           RemoveMovingField(x, y);
4529       }
4530
4531       /* indestructible elements can only explode in center (but not flames) */
4532       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4533                                            mode == EX_TYPE_BORDER)) ||
4534           element == EL_FLAMES)
4535         continue;
4536
4537       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4538          behaviour, for example when touching a yamyam that explodes to rocks
4539          with active deadly shield, a rock is created under the player !!! */
4540       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4541 #if 0
4542       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4543           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
4544            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
4545 #else
4546       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
4547 #endif
4548       {
4549         if (IS_ACTIVE_BOMB(element))
4550         {
4551           /* re-activate things under the bomb like gate or penguin */
4552           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
4553           Back[x][y] = 0;
4554         }
4555
4556         continue;
4557       }
4558
4559       /* save walkable background elements while explosion on same tile */
4560       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
4561           (x != ex || y != ey || mode == EX_TYPE_BORDER))
4562         Back[x][y] = element;
4563
4564       /* ignite explodable elements reached by other explosion */
4565       if (element == EL_EXPLOSION)
4566         element = Store2[x][y];
4567
4568       if (AmoebaNr[x][y] &&
4569           (element == EL_AMOEBA_FULL ||
4570            element == EL_BD_AMOEBA ||
4571            element == EL_AMOEBA_GROWING))
4572       {
4573         AmoebaCnt[AmoebaNr[x][y]]--;
4574         AmoebaCnt2[AmoebaNr[x][y]]--;
4575       }
4576
4577       RemoveField(x, y);
4578
4579       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
4580       {
4581         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
4582
4583         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
4584
4585         if (PLAYERINFO(ex, ey)->use_murphy)
4586           Store[x][y] = EL_EMPTY;
4587       }
4588
4589       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
4590          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
4591       else if (ELEM_IS_PLAYER(center_element))
4592         Store[x][y] = EL_EMPTY;
4593       else if (center_element == EL_YAMYAM)
4594         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4595       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4596         Store[x][y] = element_info[center_element].content.e[xx][yy];
4597 #if 1
4598       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4599          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4600          otherwise) -- FIX THIS !!! */
4601       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4602         Store[x][y] = element_info[element].content.e[1][1];
4603 #else
4604       else if (!CAN_EXPLODE(element))
4605         Store[x][y] = element_info[element].content.e[1][1];
4606 #endif
4607       else
4608         Store[x][y] = EL_EMPTY;
4609
4610       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4611           center_element == EL_AMOEBA_TO_DIAMOND)
4612         Store2[x][y] = element;
4613
4614       Feld[x][y] = EL_EXPLOSION;
4615       GfxElement[x][y] = artwork_element;
4616
4617       ExplodePhase[x][y] = 1;
4618       ExplodeDelay[x][y] = last_phase;
4619
4620       Stop[x][y] = TRUE;
4621     }
4622
4623     if (center_element == EL_YAMYAM)
4624       game.yamyam_content_nr =
4625         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4626
4627     return;
4628   }
4629
4630   if (Stop[ex][ey])
4631     return;
4632
4633   x = ex;
4634   y = ey;
4635
4636   if (phase == 1)
4637     GfxFrame[x][y] = 0;         /* restart explosion animation */
4638
4639   last_phase = ExplodeDelay[x][y];
4640
4641   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4642
4643 #ifdef DEBUG
4644
4645   /* activate this even in non-DEBUG version until cause for crash in
4646      getGraphicAnimationFrame() (see below) is found and eliminated */
4647
4648 #endif
4649 #if 1
4650
4651 #if 1
4652   /* this can happen if the player leaves an explosion just in time */
4653   if (GfxElement[x][y] == EL_UNDEFINED)
4654     GfxElement[x][y] = EL_EMPTY;
4655 #else
4656   if (GfxElement[x][y] == EL_UNDEFINED)
4657   {
4658     printf("\n\n");
4659     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4660     printf("Explode(): This should never happen!\n");
4661     printf("\n\n");
4662
4663     GfxElement[x][y] = EL_EMPTY;
4664   }
4665 #endif
4666
4667 #endif
4668
4669   border_element = Store2[x][y];
4670   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4671     border_element = StorePlayer[x][y];
4672
4673   if (phase == element_info[border_element].ignition_delay ||
4674       phase == last_phase)
4675   {
4676     boolean border_explosion = FALSE;
4677
4678     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4679         !PLAYER_EXPLOSION_PROTECTED(x, y))
4680     {
4681       KillPlayerUnlessExplosionProtected(x, y);
4682       border_explosion = TRUE;
4683     }
4684     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4685     {
4686       Feld[x][y] = Store2[x][y];
4687       Store2[x][y] = 0;
4688       Bang(x, y);
4689       border_explosion = TRUE;
4690     }
4691     else if (border_element == EL_AMOEBA_TO_DIAMOND)
4692     {
4693       AmoebeUmwandeln(x, y);
4694       Store2[x][y] = 0;
4695       border_explosion = TRUE;
4696     }
4697
4698     /* if an element just explodes due to another explosion (chain-reaction),
4699        do not immediately end the new explosion when it was the last frame of
4700        the explosion (as it would be done in the following "if"-statement!) */
4701     if (border_explosion && phase == last_phase)
4702       return;
4703   }
4704
4705   if (phase == last_phase)
4706   {
4707     int element;
4708
4709     element = Feld[x][y] = Store[x][y];
4710     Store[x][y] = Store2[x][y] = 0;
4711     GfxElement[x][y] = EL_UNDEFINED;
4712
4713     /* player can escape from explosions and might therefore be still alive */
4714     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4715         element <= EL_PLAYER_IS_EXPLODING_4)
4716     {
4717       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4718       int explosion_element = EL_PLAYER_1 + player_nr;
4719       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4720       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4721
4722       if (level.use_explosion_element[player_nr])
4723         explosion_element = level.explosion_element[player_nr];
4724
4725       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4726                     element_info[explosion_element].content.e[xx][yy]);
4727     }
4728
4729     /* restore probably existing indestructible background element */
4730     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4731       element = Feld[x][y] = Back[x][y];
4732     Back[x][y] = 0;
4733
4734     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4735     GfxDir[x][y] = MV_NONE;
4736     ChangeDelay[x][y] = 0;
4737     ChangePage[x][y] = -1;
4738
4739 #if USE_NEW_CUSTOM_VALUE
4740     CustomValue[x][y] = 0;
4741 #endif
4742
4743     InitField_WithBug2(x, y, FALSE);
4744
4745     DrawLevelField(x, y);
4746
4747     TestIfElementTouchesCustomElement(x, y);
4748
4749     if (GFX_CRUMBLED(element))
4750       DrawLevelFieldCrumbledSandNeighbours(x, y);
4751
4752     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4753       StorePlayer[x][y] = 0;
4754
4755     if (ELEM_IS_PLAYER(element))
4756       RelocatePlayer(x, y, element);
4757   }
4758   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4759   {
4760     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4761     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4762
4763     if (phase == delay)
4764       DrawLevelFieldCrumbledSand(x, y);
4765
4766     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4767     {
4768       DrawLevelElement(x, y, Back[x][y]);
4769       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4770     }
4771     else if (IS_WALKABLE_UNDER(Back[x][y]))
4772     {
4773       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4774       DrawLevelElementThruMask(x, y, Back[x][y]);
4775     }
4776     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4777       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4778   }
4779 }
4780
4781 void DynaExplode(int ex, int ey)
4782 {
4783   int i, j;
4784   int dynabomb_element = Feld[ex][ey];
4785   int dynabomb_size = 1;
4786   boolean dynabomb_xl = FALSE;
4787   struct PlayerInfo *player;
4788   static int xy[4][2] =
4789   {
4790     { 0, -1 },
4791     { -1, 0 },
4792     { +1, 0 },
4793     { 0, +1 }
4794   };
4795
4796   if (IS_ACTIVE_BOMB(dynabomb_element))
4797   {
4798     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4799     dynabomb_size = player->dynabomb_size;
4800     dynabomb_xl = player->dynabomb_xl;
4801     player->dynabombs_left++;
4802   }
4803
4804   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4805
4806   for (i = 0; i < NUM_DIRECTIONS; i++)
4807   {
4808     for (j = 1; j <= dynabomb_size; j++)
4809     {
4810       int x = ex + j * xy[i][0];
4811       int y = ey + j * xy[i][1];
4812       int element;
4813
4814       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4815         break;
4816
4817       element = Feld[x][y];
4818
4819       /* do not restart explosions of fields with active bombs */
4820       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4821         continue;
4822
4823       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4824
4825       if (element != EL_EMPTY && element != EL_EXPLOSION &&
4826           !IS_DIGGABLE(element) && !dynabomb_xl)
4827         break;
4828     }
4829   }
4830 }
4831
4832 void Bang(int x, int y)
4833 {
4834   int element = MovingOrBlocked2Element(x, y);
4835   int explosion_type = EX_TYPE_NORMAL;
4836
4837   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4838   {
4839     struct PlayerInfo *player = PLAYERINFO(x, y);
4840
4841     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4842                             player->element_nr);
4843
4844     if (level.use_explosion_element[player->index_nr])
4845     {
4846       int explosion_element = level.explosion_element[player->index_nr];
4847
4848       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4849         explosion_type = EX_TYPE_CROSS;
4850       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4851         explosion_type = EX_TYPE_CENTER;
4852     }
4853   }
4854
4855   switch (element)
4856   {
4857     case EL_BUG:
4858     case EL_SPACESHIP:
4859     case EL_BD_BUTTERFLY:
4860     case EL_BD_FIREFLY:
4861     case EL_YAMYAM:
4862     case EL_DARK_YAMYAM:
4863     case EL_ROBOT:
4864     case EL_PACMAN:
4865     case EL_MOLE:
4866       RaiseScoreElement(element);
4867       break;
4868
4869     case EL_DYNABOMB_PLAYER_1_ACTIVE:
4870     case EL_DYNABOMB_PLAYER_2_ACTIVE:
4871     case EL_DYNABOMB_PLAYER_3_ACTIVE:
4872     case EL_DYNABOMB_PLAYER_4_ACTIVE:
4873     case EL_DYNABOMB_INCREASE_NUMBER:
4874     case EL_DYNABOMB_INCREASE_SIZE:
4875     case EL_DYNABOMB_INCREASE_POWER:
4876       explosion_type = EX_TYPE_DYNA;
4877       break;
4878
4879     case EL_DC_LANDMINE:
4880 #if 0
4881     case EL_EM_EXIT_OPEN:
4882     case EL_EM_STEEL_EXIT_OPEN:
4883 #endif
4884       explosion_type = EX_TYPE_CENTER;
4885       break;
4886
4887     case EL_PENGUIN:
4888     case EL_LAMP:
4889     case EL_LAMP_ACTIVE:
4890     case EL_AMOEBA_TO_DIAMOND:
4891       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
4892         explosion_type = EX_TYPE_CENTER;
4893       break;
4894
4895     default:
4896       if (element_info[element].explosion_type == EXPLODES_CROSS)
4897         explosion_type = EX_TYPE_CROSS;
4898       else if (element_info[element].explosion_type == EXPLODES_1X1)
4899         explosion_type = EX_TYPE_CENTER;
4900       break;
4901   }
4902
4903   if (explosion_type == EX_TYPE_DYNA)
4904     DynaExplode(x, y);
4905   else
4906     Explode(x, y, EX_PHASE_START, explosion_type);
4907
4908   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4909 }
4910
4911 void SplashAcid(int x, int y)
4912 {
4913   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4914       (!IN_LEV_FIELD(x - 1, y - 2) ||
4915        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4916     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4917
4918   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4919       (!IN_LEV_FIELD(x + 1, y - 2) ||
4920        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4921     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4922
4923   PlayLevelSound(x, y, SND_ACID_SPLASHING);
4924 }
4925
4926 static void InitBeltMovement()
4927 {
4928   static int belt_base_element[4] =
4929   {
4930     EL_CONVEYOR_BELT_1_LEFT,
4931     EL_CONVEYOR_BELT_2_LEFT,
4932     EL_CONVEYOR_BELT_3_LEFT,
4933     EL_CONVEYOR_BELT_4_LEFT
4934   };
4935   static int belt_base_active_element[4] =
4936   {
4937     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4938     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4939     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4940     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4941   };
4942
4943   int x, y, i, j;
4944
4945   /* set frame order for belt animation graphic according to belt direction */
4946   for (i = 0; i < NUM_BELTS; i++)
4947   {
4948     int belt_nr = i;
4949
4950     for (j = 0; j < NUM_BELT_PARTS; j++)
4951     {
4952       int element = belt_base_active_element[belt_nr] + j;
4953       int graphic = el2img(element);
4954
4955       if (game.belt_dir[i] == MV_LEFT)
4956         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4957       else
4958         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4959     }
4960   }
4961
4962   SCAN_PLAYFIELD(x, y)
4963   {
4964     int element = Feld[x][y];
4965
4966     for (i = 0; i < NUM_BELTS; i++)
4967     {
4968       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4969       {
4970         int e_belt_nr = getBeltNrFromBeltElement(element);
4971         int belt_nr = i;
4972
4973         if (e_belt_nr == belt_nr)
4974         {
4975           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4976
4977           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4978         }
4979       }
4980     }
4981   }
4982 }
4983
4984 static void ToggleBeltSwitch(int x, int y)
4985 {
4986   static int belt_base_element[4] =
4987   {
4988     EL_CONVEYOR_BELT_1_LEFT,
4989     EL_CONVEYOR_BELT_2_LEFT,
4990     EL_CONVEYOR_BELT_3_LEFT,
4991     EL_CONVEYOR_BELT_4_LEFT
4992   };
4993   static int belt_base_active_element[4] =
4994   {
4995     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4996     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4997     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4998     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4999   };
5000   static int belt_base_switch_element[4] =
5001   {
5002     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5003     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5004     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5005     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5006   };
5007   static int belt_move_dir[4] =
5008   {
5009     MV_LEFT,
5010     MV_NONE,
5011     MV_RIGHT,
5012     MV_NONE,
5013   };
5014
5015   int element = Feld[x][y];
5016   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5017   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5018   int belt_dir = belt_move_dir[belt_dir_nr];
5019   int xx, yy, i;
5020
5021   if (!IS_BELT_SWITCH(element))
5022     return;
5023
5024   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5025   game.belt_dir[belt_nr] = belt_dir;
5026
5027   if (belt_dir_nr == 3)
5028     belt_dir_nr = 1;
5029
5030   /* set frame order for belt animation graphic according to belt direction */
5031   for (i = 0; i < NUM_BELT_PARTS; i++)
5032   {
5033     int element = belt_base_active_element[belt_nr] + i;
5034     int graphic = el2img(element);
5035
5036     if (belt_dir == MV_LEFT)
5037       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5038     else
5039       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
5040   }
5041
5042   SCAN_PLAYFIELD(xx, yy)
5043   {
5044     int element = Feld[xx][yy];
5045
5046     if (IS_BELT_SWITCH(element))
5047     {
5048       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5049
5050       if (e_belt_nr == belt_nr)
5051       {
5052         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5053         DrawLevelField(xx, yy);
5054       }
5055     }
5056     else if (IS_BELT(element) && belt_dir != MV_NONE)
5057     {
5058       int e_belt_nr = getBeltNrFromBeltElement(element);
5059
5060       if (e_belt_nr == belt_nr)
5061       {
5062         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5063
5064         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5065         DrawLevelField(xx, yy);
5066       }
5067     }
5068     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5069     {
5070       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5071
5072       if (e_belt_nr == belt_nr)
5073       {
5074         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5075
5076         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5077         DrawLevelField(xx, yy);
5078       }
5079     }
5080   }
5081 }
5082
5083 static void ToggleSwitchgateSwitch(int x, int y)
5084 {
5085   int xx, yy;
5086
5087   game.switchgate_pos = !game.switchgate_pos;
5088
5089   SCAN_PLAYFIELD(xx, yy)
5090   {
5091     int element = Feld[xx][yy];
5092
5093 #if !USE_BOTH_SWITCHGATE_SWITCHES
5094     if (element == EL_SWITCHGATE_SWITCH_UP ||
5095         element == EL_SWITCHGATE_SWITCH_DOWN)
5096     {
5097       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5098       DrawLevelField(xx, yy);
5099     }
5100     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5101              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5102     {
5103       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5104       DrawLevelField(xx, yy);
5105     }
5106 #else
5107     if (element == EL_SWITCHGATE_SWITCH_UP)
5108     {
5109       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5110       DrawLevelField(xx, yy);
5111     }
5112     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5113     {
5114       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5115       DrawLevelField(xx, yy);
5116     }
5117     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5118     {
5119       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5120       DrawLevelField(xx, yy);
5121     }
5122     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5123     {
5124       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5125       DrawLevelField(xx, yy);
5126     }
5127 #endif
5128     else if (element == EL_SWITCHGATE_OPEN ||
5129              element == EL_SWITCHGATE_OPENING)
5130     {
5131       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5132
5133       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5134     }
5135     else if (element == EL_SWITCHGATE_CLOSED ||
5136              element == EL_SWITCHGATE_CLOSING)
5137     {
5138       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5139
5140       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5141     }
5142   }
5143 }
5144
5145 static int getInvisibleActiveFromInvisibleElement(int element)
5146 {
5147   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5148           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5149           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5150           element);
5151 }
5152
5153 static int getInvisibleFromInvisibleActiveElement(int element)
5154 {
5155   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5156           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5157           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5158           element);
5159 }
5160
5161 static void RedrawAllLightSwitchesAndInvisibleElements()
5162 {
5163   int x, y;
5164
5165   SCAN_PLAYFIELD(x, y)
5166   {
5167     int element = Feld[x][y];
5168
5169     if (element == EL_LIGHT_SWITCH &&
5170         game.light_time_left > 0)
5171     {
5172       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5173       DrawLevelField(x, y);
5174     }
5175     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5176              game.light_time_left == 0)
5177     {
5178       Feld[x][y] = EL_LIGHT_SWITCH;
5179       DrawLevelField(x, y);
5180     }
5181     else if (element == EL_EMC_DRIPPER &&
5182              game.light_time_left > 0)
5183     {
5184       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5185       DrawLevelField(x, y);
5186     }
5187     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5188              game.light_time_left == 0)
5189     {
5190       Feld[x][y] = EL_EMC_DRIPPER;
5191       DrawLevelField(x, y);
5192     }
5193     else if (element == EL_INVISIBLE_STEELWALL ||
5194              element == EL_INVISIBLE_WALL ||
5195              element == EL_INVISIBLE_SAND)
5196     {
5197       if (game.light_time_left > 0)
5198         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5199
5200       DrawLevelField(x, y);
5201
5202       /* uncrumble neighbour fields, if needed */
5203       if (element == EL_INVISIBLE_SAND)
5204         DrawLevelFieldCrumbledSandNeighbours(x, y);
5205     }
5206     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5207              element == EL_INVISIBLE_WALL_ACTIVE ||
5208              element == EL_INVISIBLE_SAND_ACTIVE)
5209     {
5210       if (game.light_time_left == 0)
5211         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5212
5213       DrawLevelField(x, y);
5214
5215       /* re-crumble neighbour fields, if needed */
5216       if (element == EL_INVISIBLE_SAND)
5217         DrawLevelFieldCrumbledSandNeighbours(x, y);
5218     }
5219   }
5220 }
5221
5222 static void RedrawAllInvisibleElementsForLenses()
5223 {
5224   int x, y;
5225
5226   SCAN_PLAYFIELD(x, y)
5227   {
5228     int element = Feld[x][y];
5229
5230     if (element == EL_EMC_DRIPPER &&
5231         game.lenses_time_left > 0)
5232     {
5233       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5234       DrawLevelField(x, y);
5235     }
5236     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5237              game.lenses_time_left == 0)
5238     {
5239       Feld[x][y] = EL_EMC_DRIPPER;
5240       DrawLevelField(x, y);
5241     }
5242     else if (element == EL_INVISIBLE_STEELWALL ||
5243              element == EL_INVISIBLE_WALL ||
5244              element == EL_INVISIBLE_SAND)
5245     {
5246       if (game.lenses_time_left > 0)
5247         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5248
5249       DrawLevelField(x, y);
5250
5251       /* uncrumble neighbour fields, if needed */
5252       if (element == EL_INVISIBLE_SAND)
5253         DrawLevelFieldCrumbledSandNeighbours(x, y);
5254     }
5255     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5256              element == EL_INVISIBLE_WALL_ACTIVE ||
5257              element == EL_INVISIBLE_SAND_ACTIVE)
5258     {
5259       if (game.lenses_time_left == 0)
5260         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5261
5262       DrawLevelField(x, y);
5263
5264       /* re-crumble neighbour fields, if needed */
5265       if (element == EL_INVISIBLE_SAND)
5266         DrawLevelFieldCrumbledSandNeighbours(x, y);
5267     }
5268   }
5269 }
5270
5271 static void RedrawAllInvisibleElementsForMagnifier()
5272 {
5273   int x, y;
5274
5275   SCAN_PLAYFIELD(x, y)
5276   {
5277     int element = Feld[x][y];
5278
5279     if (element == EL_EMC_FAKE_GRASS &&
5280         game.magnify_time_left > 0)
5281     {
5282       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5283       DrawLevelField(x, y);
5284     }
5285     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5286              game.magnify_time_left == 0)
5287     {
5288       Feld[x][y] = EL_EMC_FAKE_GRASS;
5289       DrawLevelField(x, y);
5290     }
5291     else if (IS_GATE_GRAY(element) &&
5292              game.magnify_time_left > 0)
5293     {
5294       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5295                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5296                     IS_EM_GATE_GRAY(element) ?
5297                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5298                     IS_EMC_GATE_GRAY(element) ?
5299                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5300                     element);
5301       DrawLevelField(x, y);
5302     }
5303     else if (IS_GATE_GRAY_ACTIVE(element) &&
5304              game.magnify_time_left == 0)
5305     {
5306       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5307                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5308                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5309                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5310                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5311                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5312                     element);
5313       DrawLevelField(x, y);
5314     }
5315   }
5316 }
5317
5318 static void ToggleLightSwitch(int x, int y)
5319 {
5320   int element = Feld[x][y];
5321
5322   game.light_time_left =
5323     (element == EL_LIGHT_SWITCH ?
5324      level.time_light * FRAMES_PER_SECOND : 0);
5325
5326   RedrawAllLightSwitchesAndInvisibleElements();
5327 }
5328
5329 static void ActivateTimegateSwitch(int x, int y)
5330 {
5331   int xx, yy;
5332
5333   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5334
5335   SCAN_PLAYFIELD(xx, yy)
5336   {
5337     int element = Feld[xx][yy];
5338
5339     if (element == EL_TIMEGATE_CLOSED ||
5340         element == EL_TIMEGATE_CLOSING)
5341     {
5342       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5343       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5344     }
5345
5346     /*
5347     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5348     {
5349       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5350       DrawLevelField(xx, yy);
5351     }
5352     */
5353
5354   }
5355
5356 #if 1
5357   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5358                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5359 #else
5360   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5361 #endif
5362 }
5363
5364 void Impact(int x, int y)
5365 {
5366   boolean last_line = (y == lev_fieldy - 1);
5367   boolean object_hit = FALSE;
5368   boolean impact = (last_line || object_hit);
5369   int element = Feld[x][y];
5370   int smashed = EL_STEELWALL;
5371
5372   if (!last_line)       /* check if element below was hit */
5373   {
5374     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5375       return;
5376
5377     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5378                                          MovDir[x][y + 1] != MV_DOWN ||
5379                                          MovPos[x][y + 1] <= TILEY / 2));
5380
5381     /* do not smash moving elements that left the smashed field in time */
5382     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5383         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5384       object_hit = FALSE;
5385
5386 #if USE_QUICKSAND_IMPACT_BUGFIX
5387     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5388     {
5389       RemoveMovingField(x, y + 1);
5390       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5391       Feld[x][y + 2] = EL_ROCK;
5392       DrawLevelField(x, y + 2);
5393
5394       object_hit = TRUE;
5395     }
5396
5397     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5398     {
5399       RemoveMovingField(x, y + 1);
5400       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5401       Feld[x][y + 2] = EL_ROCK;
5402       DrawLevelField(x, y + 2);
5403
5404       object_hit = TRUE;
5405     }
5406 #endif
5407
5408     if (object_hit)
5409       smashed = MovingOrBlocked2Element(x, y + 1);
5410
5411     impact = (last_line || object_hit);
5412   }
5413
5414   if (!last_line && smashed == EL_ACID) /* element falls into acid */
5415   {
5416     SplashAcid(x, y + 1);
5417     return;
5418   }
5419
5420   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5421   /* only reset graphic animation if graphic really changes after impact */
5422   if (impact &&
5423       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5424   {
5425     ResetGfxAnimation(x, y);
5426     DrawLevelField(x, y);
5427   }
5428
5429   if (impact && CAN_EXPLODE_IMPACT(element))
5430   {
5431     Bang(x, y);
5432     return;
5433   }
5434   else if (impact && element == EL_PEARL &&
5435            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5436   {
5437     ResetGfxAnimation(x, y);
5438
5439     Feld[x][y] = EL_PEARL_BREAKING;
5440     PlayLevelSound(x, y, SND_PEARL_BREAKING);
5441     return;
5442   }
5443   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5444   {
5445     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5446
5447     return;
5448   }
5449
5450   if (impact && element == EL_AMOEBA_DROP)
5451   {
5452     if (object_hit && IS_PLAYER(x, y + 1))
5453       KillPlayerUnlessEnemyProtected(x, y + 1);
5454     else if (object_hit && smashed == EL_PENGUIN)
5455       Bang(x, y + 1);
5456     else
5457     {
5458       Feld[x][y] = EL_AMOEBA_GROWING;
5459       Store[x][y] = EL_AMOEBA_WET;
5460
5461       ResetRandomAnimationValue(x, y);
5462     }
5463     return;
5464   }
5465
5466   if (object_hit)               /* check which object was hit */
5467   {
5468     if ((CAN_PASS_MAGIC_WALL(element) && 
5469          (smashed == EL_MAGIC_WALL ||
5470           smashed == EL_BD_MAGIC_WALL)) ||
5471         (CAN_PASS_DC_MAGIC_WALL(element) &&
5472          smashed == EL_DC_MAGIC_WALL))
5473     {
5474       int xx, yy;
5475       int activated_magic_wall =
5476         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5477          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5478          EL_DC_MAGIC_WALL_ACTIVE);
5479
5480       /* activate magic wall / mill */
5481       SCAN_PLAYFIELD(xx, yy)
5482       {
5483         if (Feld[xx][yy] == smashed)
5484           Feld[xx][yy] = activated_magic_wall;
5485       }
5486
5487       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5488       game.magic_wall_active = TRUE;
5489
5490       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5491                             SND_MAGIC_WALL_ACTIVATING :
5492                             smashed == EL_BD_MAGIC_WALL ?
5493                             SND_BD_MAGIC_WALL_ACTIVATING :
5494                             SND_DC_MAGIC_WALL_ACTIVATING));
5495     }
5496
5497     if (IS_PLAYER(x, y + 1))
5498     {
5499       if (CAN_SMASH_PLAYER(element))
5500       {
5501         KillPlayerUnlessEnemyProtected(x, y + 1);
5502         return;
5503       }
5504     }
5505     else if (smashed == EL_PENGUIN)
5506     {
5507       if (CAN_SMASH_PLAYER(element))
5508       {
5509         Bang(x, y + 1);
5510         return;
5511       }
5512     }
5513     else if (element == EL_BD_DIAMOND)
5514     {
5515       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5516       {
5517         Bang(x, y + 1);
5518         return;
5519       }
5520     }
5521     else if (((element == EL_SP_INFOTRON ||
5522                element == EL_SP_ZONK) &&
5523               (smashed == EL_SP_SNIKSNAK ||
5524                smashed == EL_SP_ELECTRON ||
5525                smashed == EL_SP_DISK_ORANGE)) ||
5526              (element == EL_SP_INFOTRON &&
5527               smashed == EL_SP_DISK_YELLOW))
5528     {
5529       Bang(x, y + 1);
5530       return;
5531     }
5532     else if (CAN_SMASH_EVERYTHING(element))
5533     {
5534       if (IS_CLASSIC_ENEMY(smashed) ||
5535           CAN_EXPLODE_SMASHED(smashed))
5536       {
5537         Bang(x, y + 1);
5538         return;
5539       }
5540       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5541       {
5542         if (smashed == EL_LAMP ||
5543             smashed == EL_LAMP_ACTIVE)
5544         {
5545           Bang(x, y + 1);
5546           return;
5547         }
5548         else if (smashed == EL_NUT)
5549         {
5550           Feld[x][y + 1] = EL_NUT_BREAKING;
5551           PlayLevelSound(x, y, SND_NUT_BREAKING);
5552           RaiseScoreElement(EL_NUT);
5553           return;
5554         }
5555         else if (smashed == EL_PEARL)
5556         {
5557           ResetGfxAnimation(x, y);
5558
5559           Feld[x][y + 1] = EL_PEARL_BREAKING;
5560           PlayLevelSound(x, y, SND_PEARL_BREAKING);
5561           return;
5562         }
5563         else if (smashed == EL_DIAMOND)
5564         {
5565           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
5566           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
5567           return;
5568         }
5569         else if (IS_BELT_SWITCH(smashed))
5570         {
5571           ToggleBeltSwitch(x, y + 1);
5572         }
5573         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
5574                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
5575                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
5576                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
5577         {
5578           ToggleSwitchgateSwitch(x, y + 1);
5579         }
5580         else if (smashed == EL_LIGHT_SWITCH ||
5581                  smashed == EL_LIGHT_SWITCH_ACTIVE)
5582         {
5583           ToggleLightSwitch(x, y + 1);
5584         }
5585         else
5586         {
5587 #if 0
5588           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
5589 #endif
5590
5591           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5592
5593           CheckElementChangeBySide(x, y + 1, smashed, element,
5594                                    CE_SWITCHED, CH_SIDE_TOP);
5595           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5596                                             CH_SIDE_TOP);
5597         }
5598       }
5599       else
5600       {
5601         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5602       }
5603     }
5604   }
5605
5606   /* play sound of magic wall / mill */
5607   if (!last_line &&
5608       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5609        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
5610        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
5611   {
5612     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5613       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5614     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5615       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5616     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5617       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5618
5619     return;
5620   }
5621
5622   /* play sound of object that hits the ground */
5623   if (last_line || object_hit)
5624     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5625 }
5626
5627 inline static void TurnRoundExt(int x, int y)
5628 {
5629   static struct
5630   {
5631     int dx, dy;
5632   } move_xy[] =
5633   {
5634     {  0,  0 },
5635     { -1,  0 },
5636     { +1,  0 },
5637     {  0,  0 },
5638     {  0, -1 },
5639     {  0,  0 }, { 0, 0 }, { 0, 0 },
5640     {  0, +1 }
5641   };
5642   static struct
5643   {
5644     int left, right, back;
5645   } turn[] =
5646   {
5647     { 0,        0,              0        },
5648     { MV_DOWN,  MV_UP,          MV_RIGHT },
5649     { MV_UP,    MV_DOWN,        MV_LEFT  },
5650     { 0,        0,              0        },
5651     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
5652     { 0,        0,              0        },
5653     { 0,        0,              0        },
5654     { 0,        0,              0        },
5655     { MV_RIGHT, MV_LEFT,        MV_UP    }
5656   };
5657
5658   int element = Feld[x][y];
5659   int move_pattern = element_info[element].move_pattern;
5660
5661   int old_move_dir = MovDir[x][y];
5662   int left_dir  = turn[old_move_dir].left;
5663   int right_dir = turn[old_move_dir].right;
5664   int back_dir  = turn[old_move_dir].back;
5665
5666   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
5667   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
5668   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
5669   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
5670
5671   int left_x  = x + left_dx,  left_y  = y + left_dy;
5672   int right_x = x + right_dx, right_y = y + right_dy;
5673   int move_x  = x + move_dx,  move_y  = y + move_dy;
5674
5675   int xx, yy;
5676
5677   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5678   {
5679     TestIfBadThingTouchesOtherBadThing(x, y);
5680
5681     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5682       MovDir[x][y] = right_dir;
5683     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5684       MovDir[x][y] = left_dir;
5685
5686     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5687       MovDelay[x][y] = 9;
5688     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
5689       MovDelay[x][y] = 1;
5690   }
5691   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5692   {
5693     TestIfBadThingTouchesOtherBadThing(x, y);
5694
5695     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5696       MovDir[x][y] = left_dir;
5697     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5698       MovDir[x][y] = right_dir;
5699
5700     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5701       MovDelay[x][y] = 9;
5702     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
5703       MovDelay[x][y] = 1;
5704   }
5705   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5706   {
5707     TestIfBadThingTouchesOtherBadThing(x, y);
5708
5709     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5710       MovDir[x][y] = left_dir;
5711     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5712       MovDir[x][y] = right_dir;
5713
5714     if (MovDir[x][y] != old_move_dir)
5715       MovDelay[x][y] = 9;
5716   }
5717   else if (element == EL_YAMYAM)
5718   {
5719     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5720     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5721
5722     if (can_turn_left && can_turn_right)
5723       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5724     else if (can_turn_left)
5725       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5726     else if (can_turn_right)
5727       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5728     else
5729       MovDir[x][y] = back_dir;
5730
5731     MovDelay[x][y] = 16 + 16 * RND(3);
5732   }
5733   else if (element == EL_DARK_YAMYAM)
5734   {
5735     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5736                                                          left_x, left_y);
5737     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5738                                                          right_x, right_y);
5739
5740     if (can_turn_left && can_turn_right)
5741       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5742     else if (can_turn_left)
5743       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5744     else if (can_turn_right)
5745       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5746     else
5747       MovDir[x][y] = back_dir;
5748
5749     MovDelay[x][y] = 16 + 16 * RND(3);
5750   }
5751   else if (element == EL_PACMAN)
5752   {
5753     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5754     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5755
5756     if (can_turn_left && can_turn_right)
5757       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5758     else if (can_turn_left)
5759       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5760     else if (can_turn_right)
5761       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5762     else
5763       MovDir[x][y] = back_dir;
5764
5765     MovDelay[x][y] = 6 + RND(40);
5766   }
5767   else if (element == EL_PIG)
5768   {
5769     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5770     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5771     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5772     boolean should_turn_left, should_turn_right, should_move_on;
5773     int rnd_value = 24;
5774     int rnd = RND(rnd_value);
5775
5776     should_turn_left = (can_turn_left &&
5777                         (!can_move_on ||
5778                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5779                                                    y + back_dy + left_dy)));
5780     should_turn_right = (can_turn_right &&
5781                          (!can_move_on ||
5782                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5783                                                     y + back_dy + right_dy)));
5784     should_move_on = (can_move_on &&
5785                       (!can_turn_left ||
5786                        !can_turn_right ||
5787                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5788                                                  y + move_dy + left_dy) ||
5789                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5790                                                  y + move_dy + right_dy)));
5791
5792     if (should_turn_left || should_turn_right || should_move_on)
5793     {
5794       if (should_turn_left && should_turn_right && should_move_on)
5795         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
5796                         rnd < 2 * rnd_value / 3 ? right_dir :
5797                         old_move_dir);
5798       else if (should_turn_left && should_turn_right)
5799         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5800       else if (should_turn_left && should_move_on)
5801         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5802       else if (should_turn_right && should_move_on)
5803         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5804       else if (should_turn_left)
5805         MovDir[x][y] = left_dir;
5806       else if (should_turn_right)
5807         MovDir[x][y] = right_dir;
5808       else if (should_move_on)
5809         MovDir[x][y] = old_move_dir;
5810     }
5811     else if (can_move_on && rnd > rnd_value / 8)
5812       MovDir[x][y] = old_move_dir;
5813     else if (can_turn_left && can_turn_right)
5814       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5815     else if (can_turn_left && rnd > rnd_value / 8)
5816       MovDir[x][y] = left_dir;
5817     else if (can_turn_right && rnd > rnd_value/8)
5818       MovDir[x][y] = right_dir;
5819     else
5820       MovDir[x][y] = back_dir;
5821
5822     xx = x + move_xy[MovDir[x][y]].dx;
5823     yy = y + move_xy[MovDir[x][y]].dy;
5824
5825     if (!IN_LEV_FIELD(xx, yy) ||
5826         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5827       MovDir[x][y] = old_move_dir;
5828
5829     MovDelay[x][y] = 0;
5830   }
5831   else if (element == EL_DRAGON)
5832   {
5833     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5834     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5835     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5836     int rnd_value = 24;
5837     int rnd = RND(rnd_value);
5838
5839     if (can_move_on && rnd > rnd_value / 8)
5840       MovDir[x][y] = old_move_dir;
5841     else if (can_turn_left && can_turn_right)
5842       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5843     else if (can_turn_left && rnd > rnd_value / 8)
5844       MovDir[x][y] = left_dir;
5845     else if (can_turn_right && rnd > rnd_value / 8)
5846       MovDir[x][y] = right_dir;
5847     else
5848       MovDir[x][y] = back_dir;
5849
5850     xx = x + move_xy[MovDir[x][y]].dx;
5851     yy = y + move_xy[MovDir[x][y]].dy;
5852
5853     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5854       MovDir[x][y] = old_move_dir;
5855
5856     MovDelay[x][y] = 0;
5857   }
5858   else if (element == EL_MOLE)
5859   {
5860     boolean can_move_on =
5861       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5862                             IS_AMOEBOID(Feld[move_x][move_y]) ||
5863                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5864     if (!can_move_on)
5865     {
5866       boolean can_turn_left =
5867         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5868                               IS_AMOEBOID(Feld[left_x][left_y])));
5869
5870       boolean can_turn_right =
5871         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5872                               IS_AMOEBOID(Feld[right_x][right_y])));
5873
5874       if (can_turn_left && can_turn_right)
5875         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5876       else if (can_turn_left)
5877         MovDir[x][y] = left_dir;
5878       else
5879         MovDir[x][y] = right_dir;
5880     }
5881
5882     if (MovDir[x][y] != old_move_dir)
5883       MovDelay[x][y] = 9;
5884   }
5885   else if (element == EL_BALLOON)
5886   {
5887     MovDir[x][y] = game.wind_direction;
5888     MovDelay[x][y] = 0;
5889   }
5890   else if (element == EL_SPRING)
5891   {
5892 #if USE_NEW_SPRING_BUMPER
5893     if (MovDir[x][y] & MV_HORIZONTAL)
5894     {
5895       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5896           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5897       {
5898         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5899         ResetGfxAnimation(move_x, move_y);
5900         DrawLevelField(move_x, move_y);
5901
5902         MovDir[x][y] = back_dir;
5903       }
5904       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5905                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5906         MovDir[x][y] = MV_NONE;
5907     }
5908 #else
5909     if (MovDir[x][y] & MV_HORIZONTAL &&
5910         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5911          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5912       MovDir[x][y] = MV_NONE;
5913 #endif
5914
5915     MovDelay[x][y] = 0;
5916   }
5917   else if (element == EL_ROBOT ||
5918            element == EL_SATELLITE ||
5919            element == EL_PENGUIN ||
5920            element == EL_EMC_ANDROID)
5921   {
5922     int attr_x = -1, attr_y = -1;
5923
5924     if (AllPlayersGone)
5925     {
5926       attr_x = ExitX;
5927       attr_y = ExitY;
5928     }
5929     else
5930     {
5931       int i;
5932
5933       for (i = 0; i < MAX_PLAYERS; i++)
5934       {
5935         struct PlayerInfo *player = &stored_player[i];
5936         int jx = player->jx, jy = player->jy;
5937
5938         if (!player->active)
5939           continue;
5940
5941         if (attr_x == -1 ||
5942             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5943         {
5944           attr_x = jx;
5945           attr_y = jy;
5946         }
5947       }
5948     }
5949
5950     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5951         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5952          game.engine_version < VERSION_IDENT(3,1,0,0)))
5953     {
5954       attr_x = ZX;
5955       attr_y = ZY;
5956     }
5957
5958     if (element == EL_PENGUIN)
5959     {
5960       int i;
5961       static int xy[4][2] =
5962       {
5963         { 0, -1 },
5964         { -1, 0 },
5965         { +1, 0 },
5966         { 0, +1 }
5967       };
5968
5969       for (i = 0; i < NUM_DIRECTIONS; i++)
5970       {
5971         int ex = x + xy[i][0];
5972         int ey = y + xy[i][1];
5973
5974         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
5975                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
5976                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
5977                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
5978         {
5979           attr_x = ex;
5980           attr_y = ey;
5981           break;
5982         }
5983       }
5984     }
5985
5986     MovDir[x][y] = MV_NONE;
5987     if (attr_x < x)
5988       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5989     else if (attr_x > x)
5990       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5991     if (attr_y < y)
5992       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5993     else if (attr_y > y)
5994       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5995
5996     if (element == EL_ROBOT)
5997     {
5998       int newx, newy;
5999
6000       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6001         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6002       Moving2Blocked(x, y, &newx, &newy);
6003
6004       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6005         MovDelay[x][y] = 8 + 8 * !RND(3);
6006       else
6007         MovDelay[x][y] = 16;
6008     }
6009     else if (element == EL_PENGUIN)
6010     {
6011       int newx, newy;
6012
6013       MovDelay[x][y] = 1;
6014
6015       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6016       {
6017         boolean first_horiz = RND(2);
6018         int new_move_dir = MovDir[x][y];
6019
6020         MovDir[x][y] =
6021           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6022         Moving2Blocked(x, y, &newx, &newy);
6023
6024         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6025           return;
6026
6027         MovDir[x][y] =
6028           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6029         Moving2Blocked(x, y, &newx, &newy);
6030
6031         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6032           return;
6033
6034         MovDir[x][y] = old_move_dir;
6035         return;
6036       }
6037     }
6038     else if (element == EL_SATELLITE)
6039     {
6040       int newx, newy;
6041
6042       MovDelay[x][y] = 1;
6043
6044       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6045       {
6046         boolean first_horiz = RND(2);
6047         int new_move_dir = MovDir[x][y];
6048
6049         MovDir[x][y] =
6050           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6051         Moving2Blocked(x, y, &newx, &newy);
6052
6053         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6054           return;
6055
6056         MovDir[x][y] =
6057           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6058         Moving2Blocked(x, y, &newx, &newy);
6059
6060         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6061           return;
6062
6063         MovDir[x][y] = old_move_dir;
6064         return;
6065       }
6066     }
6067     else if (element == EL_EMC_ANDROID)
6068     {
6069       static int check_pos[16] =
6070       {
6071         -1,             /*  0 => (invalid)          */
6072         7,              /*  1 => MV_LEFT            */
6073         3,              /*  2 => MV_RIGHT           */
6074         -1,             /*  3 => (invalid)          */
6075         1,              /*  4 =>            MV_UP   */
6076         0,              /*  5 => MV_LEFT  | MV_UP   */
6077         2,              /*  6 => MV_RIGHT | MV_UP   */
6078         -1,             /*  7 => (invalid)          */
6079         5,              /*  8 =>            MV_DOWN */
6080         6,              /*  9 => MV_LEFT  | MV_DOWN */
6081         4,              /* 10 => MV_RIGHT | MV_DOWN */
6082         -1,             /* 11 => (invalid)          */
6083         -1,             /* 12 => (invalid)          */
6084         -1,             /* 13 => (invalid)          */
6085         -1,             /* 14 => (invalid)          */
6086         -1,             /* 15 => (invalid)          */
6087       };
6088       static struct
6089       {
6090         int dx, dy;
6091         int dir;
6092       } check_xy[8] =
6093       {
6094         { -1, -1,       MV_LEFT  | MV_UP   },
6095         {  0, -1,                  MV_UP   },
6096         { +1, -1,       MV_RIGHT | MV_UP   },
6097         { +1,  0,       MV_RIGHT           },
6098         { +1, +1,       MV_RIGHT | MV_DOWN },
6099         {  0, +1,                  MV_DOWN },
6100         { -1, +1,       MV_LEFT  | MV_DOWN },
6101         { -1,  0,       MV_LEFT            },
6102       };
6103       int start_pos, check_order;
6104       boolean can_clone = FALSE;
6105       int i;
6106
6107       /* check if there is any free field around current position */
6108       for (i = 0; i < 8; i++)
6109       {
6110         int newx = x + check_xy[i].dx;
6111         int newy = y + check_xy[i].dy;
6112
6113         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6114         {
6115           can_clone = TRUE;
6116
6117           break;
6118         }
6119       }
6120
6121       if (can_clone)            /* randomly find an element to clone */
6122       {
6123         can_clone = FALSE;
6124
6125         start_pos = check_pos[RND(8)];
6126         check_order = (RND(2) ? -1 : +1);
6127
6128         for (i = 0; i < 8; i++)
6129         {
6130           int pos_raw = start_pos + i * check_order;
6131           int pos = (pos_raw + 8) % 8;
6132           int newx = x + check_xy[pos].dx;
6133           int newy = y + check_xy[pos].dy;
6134
6135           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6136           {
6137             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6138             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6139
6140             Store[x][y] = Feld[newx][newy];
6141
6142             can_clone = TRUE;
6143
6144             break;
6145           }
6146         }
6147       }
6148
6149       if (can_clone)            /* randomly find a direction to move */
6150       {
6151         can_clone = FALSE;
6152
6153         start_pos = check_pos[RND(8)];
6154         check_order = (RND(2) ? -1 : +1);
6155
6156         for (i = 0; i < 8; i++)
6157         {
6158           int pos_raw = start_pos + i * check_order;
6159           int pos = (pos_raw + 8) % 8;
6160           int newx = x + check_xy[pos].dx;
6161           int newy = y + check_xy[pos].dy;
6162           int new_move_dir = check_xy[pos].dir;
6163
6164           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6165           {
6166             MovDir[x][y] = new_move_dir;
6167             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6168
6169             can_clone = TRUE;
6170
6171             break;
6172           }
6173         }
6174       }
6175
6176       if (can_clone)            /* cloning and moving successful */
6177         return;
6178
6179       /* cannot clone -- try to move towards player */
6180
6181       start_pos = check_pos[MovDir[x][y] & 0x0f];
6182       check_order = (RND(2) ? -1 : +1);
6183
6184       for (i = 0; i < 3; i++)
6185       {
6186         /* first check start_pos, then previous/next or (next/previous) pos */
6187         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6188         int pos = (pos_raw + 8) % 8;
6189         int newx = x + check_xy[pos].dx;
6190         int newy = y + check_xy[pos].dy;
6191         int new_move_dir = check_xy[pos].dir;
6192
6193         if (IS_PLAYER(newx, newy))
6194           break;
6195
6196         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6197         {
6198           MovDir[x][y] = new_move_dir;
6199           MovDelay[x][y] = level.android_move_time * 8 + 1;
6200
6201           break;
6202         }
6203       }
6204     }
6205   }
6206   else if (move_pattern == MV_TURNING_LEFT ||
6207            move_pattern == MV_TURNING_RIGHT ||
6208            move_pattern == MV_TURNING_LEFT_RIGHT ||
6209            move_pattern == MV_TURNING_RIGHT_LEFT ||
6210            move_pattern == MV_TURNING_RANDOM ||
6211            move_pattern == MV_ALL_DIRECTIONS)
6212   {
6213     boolean can_turn_left =
6214       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6215     boolean can_turn_right =
6216       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6217
6218     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6219       return;
6220
6221     if (move_pattern == MV_TURNING_LEFT)
6222       MovDir[x][y] = left_dir;
6223     else if (move_pattern == MV_TURNING_RIGHT)
6224       MovDir[x][y] = right_dir;
6225     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6226       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6227     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6228       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6229     else if (move_pattern == MV_TURNING_RANDOM)
6230       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6231                       can_turn_right && !can_turn_left ? right_dir :
6232                       RND(2) ? left_dir : right_dir);
6233     else if (can_turn_left && can_turn_right)
6234       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6235     else if (can_turn_left)
6236       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6237     else if (can_turn_right)
6238       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6239     else
6240       MovDir[x][y] = back_dir;
6241
6242     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6243   }
6244   else if (move_pattern == MV_HORIZONTAL ||
6245            move_pattern == MV_VERTICAL)
6246   {
6247     if (move_pattern & old_move_dir)
6248       MovDir[x][y] = back_dir;
6249     else if (move_pattern == MV_HORIZONTAL)
6250       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6251     else if (move_pattern == MV_VERTICAL)
6252       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6253
6254     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6255   }
6256   else if (move_pattern & MV_ANY_DIRECTION)
6257   {
6258     MovDir[x][y] = move_pattern;
6259     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6260   }
6261   else if (move_pattern & MV_WIND_DIRECTION)
6262   {
6263     MovDir[x][y] = game.wind_direction;
6264     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6265   }
6266   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6267   {
6268     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6269       MovDir[x][y] = left_dir;
6270     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6271       MovDir[x][y] = right_dir;
6272
6273     if (MovDir[x][y] != old_move_dir)
6274       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6275   }
6276   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6277   {
6278     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6279       MovDir[x][y] = right_dir;
6280     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6281       MovDir[x][y] = left_dir;
6282
6283     if (MovDir[x][y] != old_move_dir)
6284       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6285   }
6286   else if (move_pattern == MV_TOWARDS_PLAYER ||
6287            move_pattern == MV_AWAY_FROM_PLAYER)
6288   {
6289     int attr_x = -1, attr_y = -1;
6290     int newx, newy;
6291     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6292
6293     if (AllPlayersGone)
6294     {
6295       attr_x = ExitX;
6296       attr_y = ExitY;
6297     }
6298     else
6299     {
6300       int i;
6301
6302       for (i = 0; i < MAX_PLAYERS; i++)
6303       {
6304         struct PlayerInfo *player = &stored_player[i];
6305         int jx = player->jx, jy = player->jy;
6306
6307         if (!player->active)
6308           continue;
6309
6310         if (attr_x == -1 ||
6311             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6312         {
6313           attr_x = jx;
6314           attr_y = jy;
6315         }
6316       }
6317     }
6318
6319     MovDir[x][y] = MV_NONE;
6320     if (attr_x < x)
6321       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6322     else if (attr_x > x)
6323       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6324     if (attr_y < y)
6325       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6326     else if (attr_y > y)
6327       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6328
6329     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6330
6331     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6332     {
6333       boolean first_horiz = RND(2);
6334       int new_move_dir = MovDir[x][y];
6335
6336       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6337       {
6338         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6339         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6340
6341         return;
6342       }
6343
6344       MovDir[x][y] =
6345         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6346       Moving2Blocked(x, y, &newx, &newy);
6347
6348       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6349         return;
6350
6351       MovDir[x][y] =
6352         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6353       Moving2Blocked(x, y, &newx, &newy);
6354
6355       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6356         return;
6357
6358       MovDir[x][y] = old_move_dir;
6359     }
6360   }
6361   else if (move_pattern == MV_WHEN_PUSHED ||
6362            move_pattern == MV_WHEN_DROPPED)
6363   {
6364     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6365       MovDir[x][y] = MV_NONE;
6366
6367     MovDelay[x][y] = 0;
6368   }
6369   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6370   {
6371     static int test_xy[7][2] =
6372     {
6373       { 0, -1 },
6374       { -1, 0 },
6375       { +1, 0 },
6376       { 0, +1 },
6377       { 0, -1 },
6378       { -1, 0 },
6379       { +1, 0 },
6380     };
6381     static int test_dir[7] =
6382     {
6383       MV_UP,
6384       MV_LEFT,
6385       MV_RIGHT,
6386       MV_DOWN,
6387       MV_UP,
6388       MV_LEFT,
6389       MV_RIGHT,
6390     };
6391     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6392     int move_preference = -1000000;     /* start with very low preference */
6393     int new_move_dir = MV_NONE;
6394     int start_test = RND(4);
6395     int i;
6396
6397     for (i = 0; i < NUM_DIRECTIONS; i++)
6398     {
6399       int move_dir = test_dir[start_test + i];
6400       int move_dir_preference;
6401
6402       xx = x + test_xy[start_test + i][0];
6403       yy = y + test_xy[start_test + i][1];
6404
6405       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6406           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6407       {
6408         new_move_dir = move_dir;
6409
6410         break;
6411       }
6412
6413       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6414         continue;
6415
6416       move_dir_preference = -1 * RunnerVisit[xx][yy];
6417       if (hunter_mode && PlayerVisit[xx][yy] > 0)
6418         move_dir_preference = PlayerVisit[xx][yy];
6419
6420       if (move_dir_preference > move_preference)
6421       {
6422         /* prefer field that has not been visited for the longest time */
6423         move_preference = move_dir_preference;
6424         new_move_dir = move_dir;
6425       }
6426       else if (move_dir_preference == move_preference &&
6427                move_dir == old_move_dir)
6428       {
6429         /* prefer last direction when all directions are preferred equally */
6430         move_preference = move_dir_preference;
6431         new_move_dir = move_dir;
6432       }
6433     }
6434
6435     MovDir[x][y] = new_move_dir;
6436     if (old_move_dir != new_move_dir)
6437       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6438   }
6439 }
6440
6441 static void TurnRound(int x, int y)
6442 {
6443   int direction = MovDir[x][y];
6444
6445   TurnRoundExt(x, y);
6446
6447   GfxDir[x][y] = MovDir[x][y];
6448
6449   if (direction != MovDir[x][y])
6450     GfxFrame[x][y] = 0;
6451
6452   if (MovDelay[x][y])
6453     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6454
6455   ResetGfxFrame(x, y, FALSE);
6456 }
6457
6458 static boolean JustBeingPushed(int x, int y)
6459 {
6460   int i;
6461
6462   for (i = 0; i < MAX_PLAYERS; i++)
6463   {
6464     struct PlayerInfo *player = &stored_player[i];
6465
6466     if (player->active && player->is_pushing && player->MovPos)
6467     {
6468       int next_jx = player->jx + (player->jx - player->last_jx);
6469       int next_jy = player->jy + (player->jy - player->last_jy);
6470
6471       if (x == next_jx && y == next_jy)
6472         return TRUE;
6473     }
6474   }
6475
6476   return FALSE;
6477 }
6478
6479 void StartMoving(int x, int y)
6480 {
6481   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
6482   int element = Feld[x][y];
6483
6484   if (Stop[x][y])
6485     return;
6486
6487   if (MovDelay[x][y] == 0)
6488     GfxAction[x][y] = ACTION_DEFAULT;
6489
6490   if (CAN_FALL(element) && y < lev_fieldy - 1)
6491   {
6492     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
6493         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6494       if (JustBeingPushed(x, y))
6495         return;
6496
6497     if (element == EL_QUICKSAND_FULL)
6498     {
6499       if (IS_FREE(x, y + 1))
6500       {
6501         InitMovingField(x, y, MV_DOWN);
6502         started_moving = TRUE;
6503
6504         Feld[x][y] = EL_QUICKSAND_EMPTYING;
6505 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6506         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6507           Store[x][y] = EL_ROCK;
6508 #else
6509         Store[x][y] = EL_ROCK;
6510 #endif
6511
6512         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6513       }
6514       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6515       {
6516         if (!MovDelay[x][y])
6517           MovDelay[x][y] = TILEY + 1;
6518
6519         if (MovDelay[x][y])
6520         {
6521           MovDelay[x][y]--;
6522           if (MovDelay[x][y])
6523             return;
6524         }
6525
6526         Feld[x][y] = EL_QUICKSAND_EMPTY;
6527         Feld[x][y + 1] = EL_QUICKSAND_FULL;
6528         Store[x][y + 1] = Store[x][y];
6529         Store[x][y] = 0;
6530
6531         PlayLevelSoundAction(x, y, ACTION_FILLING);
6532       }
6533     }
6534     else if (element == EL_QUICKSAND_FAST_FULL)
6535     {
6536       if (IS_FREE(x, y + 1))
6537       {
6538         InitMovingField(x, y, MV_DOWN);
6539         started_moving = TRUE;
6540
6541         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
6542 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6543         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6544           Store[x][y] = EL_ROCK;
6545 #else
6546         Store[x][y] = EL_ROCK;
6547 #endif
6548
6549         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6550       }
6551       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6552       {
6553         if (!MovDelay[x][y])
6554           MovDelay[x][y] = TILEY + 1;
6555
6556         if (MovDelay[x][y])
6557         {
6558           MovDelay[x][y]--;
6559           if (MovDelay[x][y])
6560             return;
6561         }
6562
6563         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
6564         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
6565         Store[x][y + 1] = Store[x][y];
6566         Store[x][y] = 0;
6567
6568         PlayLevelSoundAction(x, y, ACTION_FILLING);
6569       }
6570     }
6571     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6572              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6573     {
6574       InitMovingField(x, y, MV_DOWN);
6575       started_moving = TRUE;
6576
6577       Feld[x][y] = EL_QUICKSAND_FILLING;
6578       Store[x][y] = element;
6579
6580       PlayLevelSoundAction(x, y, ACTION_FILLING);
6581     }
6582     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6583              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6584     {
6585       InitMovingField(x, y, MV_DOWN);
6586       started_moving = TRUE;
6587
6588       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
6589       Store[x][y] = element;
6590
6591       PlayLevelSoundAction(x, y, ACTION_FILLING);
6592     }
6593     else if (element == EL_MAGIC_WALL_FULL)
6594     {
6595       if (IS_FREE(x, y + 1))
6596       {
6597         InitMovingField(x, y, MV_DOWN);
6598         started_moving = TRUE;
6599
6600         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6601         Store[x][y] = EL_CHANGED(Store[x][y]);
6602       }
6603       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6604       {
6605         if (!MovDelay[x][y])
6606           MovDelay[x][y] = TILEY/4 + 1;
6607
6608         if (MovDelay[x][y])
6609         {
6610           MovDelay[x][y]--;
6611           if (MovDelay[x][y])
6612             return;
6613         }
6614
6615         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6616         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6617         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6618         Store[x][y] = 0;
6619       }
6620     }
6621     else if (element == EL_BD_MAGIC_WALL_FULL)
6622     {
6623       if (IS_FREE(x, y + 1))
6624       {
6625         InitMovingField(x, y, MV_DOWN);
6626         started_moving = TRUE;
6627
6628         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6629         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6630       }
6631       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6632       {
6633         if (!MovDelay[x][y])
6634           MovDelay[x][y] = TILEY/4 + 1;
6635
6636         if (MovDelay[x][y])
6637         {
6638           MovDelay[x][y]--;
6639           if (MovDelay[x][y])
6640             return;
6641         }
6642
6643         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6644         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6645         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6646         Store[x][y] = 0;
6647       }
6648     }
6649     else if (element == EL_DC_MAGIC_WALL_FULL)
6650     {
6651       if (IS_FREE(x, y + 1))
6652       {
6653         InitMovingField(x, y, MV_DOWN);
6654         started_moving = TRUE;
6655
6656         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
6657         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
6658       }
6659       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6660       {
6661         if (!MovDelay[x][y])
6662           MovDelay[x][y] = TILEY/4 + 1;
6663
6664         if (MovDelay[x][y])
6665         {
6666           MovDelay[x][y]--;
6667           if (MovDelay[x][y])
6668             return;
6669         }
6670
6671         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
6672         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
6673         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
6674         Store[x][y] = 0;
6675       }
6676     }
6677     else if ((CAN_PASS_MAGIC_WALL(element) &&
6678               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6679                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
6680              (CAN_PASS_DC_MAGIC_WALL(element) &&
6681               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
6682
6683     {
6684       InitMovingField(x, y, MV_DOWN);
6685       started_moving = TRUE;
6686
6687       Feld[x][y] =
6688         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6689          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
6690          EL_DC_MAGIC_WALL_FILLING);
6691       Store[x][y] = element;
6692     }
6693     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6694     {
6695       SplashAcid(x, y + 1);
6696
6697       InitMovingField(x, y, MV_DOWN);
6698       started_moving = TRUE;
6699
6700       Store[x][y] = EL_ACID;
6701     }
6702     else if (
6703 #if USE_FIX_IMPACT_COLLISION
6704              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6705               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
6706 #else
6707              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6708               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
6709 #endif
6710              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
6711               CAN_FALL(element) && WasJustFalling[x][y] &&
6712               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
6713
6714              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
6715               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
6716               (Feld[x][y + 1] == EL_BLOCKED)))
6717     {
6718       /* this is needed for a special case not covered by calling "Impact()"
6719          from "ContinueMoving()": if an element moves to a tile directly below
6720          another element which was just falling on that tile (which was empty
6721          in the previous frame), the falling element above would just stop
6722          instead of smashing the element below (in previous version, the above
6723          element was just checked for "moving" instead of "falling", resulting
6724          in incorrect smashes caused by horizontal movement of the above
6725          element; also, the case of the player being the element to smash was
6726          simply not covered here... :-/ ) */
6727
6728       CheckCollision[x][y] = 0;
6729       CheckImpact[x][y] = 0;
6730
6731       Impact(x, y);
6732     }
6733     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6734     {
6735       if (MovDir[x][y] == MV_NONE)
6736       {
6737         InitMovingField(x, y, MV_DOWN);
6738         started_moving = TRUE;
6739       }
6740     }
6741     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6742     {
6743       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6744         MovDir[x][y] = MV_DOWN;
6745
6746       InitMovingField(x, y, MV_DOWN);
6747       started_moving = TRUE;
6748     }
6749     else if (element == EL_AMOEBA_DROP)
6750     {
6751       Feld[x][y] = EL_AMOEBA_GROWING;
6752       Store[x][y] = EL_AMOEBA_WET;
6753     }
6754     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6755               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6756              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6757              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6758     {
6759       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
6760                                 (IS_FREE(x - 1, y + 1) ||
6761                                  Feld[x - 1][y + 1] == EL_ACID));
6762       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6763                                 (IS_FREE(x + 1, y + 1) ||
6764                                  Feld[x + 1][y + 1] == EL_ACID));
6765       boolean can_fall_any  = (can_fall_left || can_fall_right);
6766       boolean can_fall_both = (can_fall_left && can_fall_right);
6767       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6768
6769 #if USE_NEW_ALL_SLIPPERY
6770       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6771       {
6772         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6773           can_fall_right = FALSE;
6774         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6775           can_fall_left = FALSE;
6776         else if (slippery_type == SLIPPERY_ONLY_LEFT)
6777           can_fall_right = FALSE;
6778         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6779           can_fall_left = FALSE;
6780
6781         can_fall_any  = (can_fall_left || can_fall_right);
6782         can_fall_both = FALSE;
6783       }
6784 #else
6785       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6786       {
6787         if (slippery_type == SLIPPERY_ONLY_LEFT)
6788           can_fall_right = FALSE;
6789         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6790           can_fall_left = FALSE;
6791         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6792           can_fall_right = FALSE;
6793         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6794           can_fall_left = FALSE;
6795
6796         can_fall_any  = (can_fall_left || can_fall_right);
6797         can_fall_both = (can_fall_left && can_fall_right);
6798       }
6799 #endif
6800
6801 #if USE_NEW_ALL_SLIPPERY
6802 #else
6803 #if USE_NEW_SP_SLIPPERY
6804       /* !!! better use the same properties as for custom elements here !!! */
6805       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6806                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6807       {
6808         can_fall_right = FALSE;         /* slip down on left side */
6809         can_fall_both = FALSE;
6810       }
6811 #endif
6812 #endif
6813
6814 #if USE_NEW_ALL_SLIPPERY
6815       if (can_fall_both)
6816       {
6817         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6818           can_fall_right = FALSE;       /* slip down on left side */
6819         else
6820           can_fall_left = !(can_fall_right = RND(2));
6821
6822         can_fall_both = FALSE;
6823       }
6824 #else
6825       if (can_fall_both)
6826       {
6827         if (game.emulation == EMU_BOULDERDASH ||
6828             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6829           can_fall_right = FALSE;       /* slip down on left side */
6830         else
6831           can_fall_left = !(can_fall_right = RND(2));
6832
6833         can_fall_both = FALSE;
6834       }
6835 #endif
6836
6837       if (can_fall_any)
6838       {
6839         /* if not determined otherwise, prefer left side for slipping down */
6840         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6841         started_moving = TRUE;
6842       }
6843     }
6844 #if 0
6845     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6846 #else
6847     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6848 #endif
6849     {
6850       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
6851       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6852       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6853       int belt_dir = game.belt_dir[belt_nr];
6854
6855       if ((belt_dir == MV_LEFT  && left_is_free) ||
6856           (belt_dir == MV_RIGHT && right_is_free))
6857       {
6858         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6859
6860         InitMovingField(x, y, belt_dir);
6861         started_moving = TRUE;
6862
6863         Pushed[x][y] = TRUE;
6864         Pushed[nextx][y] = TRUE;
6865
6866         GfxAction[x][y] = ACTION_DEFAULT;
6867       }
6868       else
6869       {
6870         MovDir[x][y] = 0;       /* if element was moving, stop it */
6871       }
6872     }
6873   }
6874
6875   /* not "else if" because of elements that can fall and move (EL_SPRING) */
6876 #if 0
6877   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6878 #else
6879   if (CAN_MOVE(element) && !started_moving)
6880 #endif
6881   {
6882     int move_pattern = element_info[element].move_pattern;
6883     int newx, newy;
6884
6885 #if 0
6886 #if DEBUG
6887     if (MovDir[x][y] == MV_NONE)
6888     {
6889       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6890              x, y, element, element_info[element].token_name);
6891       printf("StartMoving(): This should never happen!\n");
6892     }
6893 #endif
6894 #endif
6895
6896     Moving2Blocked(x, y, &newx, &newy);
6897
6898     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6899       return;
6900
6901     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6902         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6903     {
6904       WasJustMoving[x][y] = 0;
6905       CheckCollision[x][y] = 0;
6906
6907       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6908
6909       if (Feld[x][y] != element)        /* element has changed */
6910         return;
6911     }
6912
6913     if (!MovDelay[x][y])        /* start new movement phase */
6914     {
6915       /* all objects that can change their move direction after each step
6916          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6917
6918       if (element != EL_YAMYAM &&
6919           element != EL_DARK_YAMYAM &&
6920           element != EL_PACMAN &&
6921           !(move_pattern & MV_ANY_DIRECTION) &&
6922           move_pattern != MV_TURNING_LEFT &&
6923           move_pattern != MV_TURNING_RIGHT &&
6924           move_pattern != MV_TURNING_LEFT_RIGHT &&
6925           move_pattern != MV_TURNING_RIGHT_LEFT &&
6926           move_pattern != MV_TURNING_RANDOM)
6927       {
6928         TurnRound(x, y);
6929
6930         if (MovDelay[x][y] && (element == EL_BUG ||
6931                                element == EL_SPACESHIP ||
6932                                element == EL_SP_SNIKSNAK ||
6933                                element == EL_SP_ELECTRON ||
6934                                element == EL_MOLE))
6935           DrawLevelField(x, y);
6936       }
6937     }
6938
6939     if (MovDelay[x][y])         /* wait some time before next movement */
6940     {
6941       MovDelay[x][y]--;
6942
6943       if (element == EL_ROBOT ||
6944           element == EL_YAMYAM ||
6945           element == EL_DARK_YAMYAM)
6946       {
6947         DrawLevelElementAnimationIfNeeded(x, y, element);
6948         PlayLevelSoundAction(x, y, ACTION_WAITING);
6949       }
6950       else if (element == EL_SP_ELECTRON)
6951         DrawLevelElementAnimationIfNeeded(x, y, element);
6952       else if (element == EL_DRAGON)
6953       {
6954         int i;
6955         int dir = MovDir[x][y];
6956         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6957         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
6958         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
6959                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
6960                        dir == MV_UP     ? IMG_FLAMES_1_UP :
6961                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6962         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6963
6964         GfxAction[x][y] = ACTION_ATTACKING;
6965
6966         if (IS_PLAYER(x, y))
6967           DrawPlayerField(x, y);
6968         else
6969           DrawLevelField(x, y);
6970
6971         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6972
6973         for (i = 1; i <= 3; i++)
6974         {
6975           int xx = x + i * dx;
6976           int yy = y + i * dy;
6977           int sx = SCREENX(xx);
6978           int sy = SCREENY(yy);
6979           int flame_graphic = graphic + (i - 1);
6980
6981           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6982             break;
6983
6984           if (MovDelay[x][y])
6985           {
6986             int flamed = MovingOrBlocked2Element(xx, yy);
6987
6988             /* !!! */
6989 #if 0
6990             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6991               Bang(xx, yy);
6992             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6993               RemoveMovingField(xx, yy);
6994             else
6995               RemoveField(xx, yy);
6996 #else
6997             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6998               Bang(xx, yy);
6999             else
7000               RemoveMovingField(xx, yy);
7001 #endif
7002
7003             ChangeDelay[xx][yy] = 0;
7004
7005             Feld[xx][yy] = EL_FLAMES;
7006
7007             if (IN_SCR_FIELD(sx, sy))
7008             {
7009               DrawLevelFieldCrumbledSand(xx, yy);
7010               DrawGraphic(sx, sy, flame_graphic, frame);
7011             }
7012           }
7013           else
7014           {
7015             if (Feld[xx][yy] == EL_FLAMES)
7016               Feld[xx][yy] = EL_EMPTY;
7017             DrawLevelField(xx, yy);
7018           }
7019         }
7020       }
7021
7022       if (MovDelay[x][y])       /* element still has to wait some time */
7023       {
7024         PlayLevelSoundAction(x, y, ACTION_WAITING);
7025
7026         return;
7027       }
7028     }
7029
7030     /* now make next step */
7031
7032     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7033
7034     if (DONT_COLLIDE_WITH(element) &&
7035         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7036         !PLAYER_ENEMY_PROTECTED(newx, newy))
7037     {
7038       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7039
7040       return;
7041     }
7042
7043     else if (CAN_MOVE_INTO_ACID(element) &&
7044              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7045              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7046              (MovDir[x][y] == MV_DOWN ||
7047               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7048     {
7049       SplashAcid(newx, newy);
7050       Store[x][y] = EL_ACID;
7051     }
7052     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7053     {
7054       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7055           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7056           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7057           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7058       {
7059         RemoveField(x, y);
7060         DrawLevelField(x, y);
7061
7062         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7063         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7064           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7065
7066         local_player->friends_still_needed--;
7067         if (!local_player->friends_still_needed &&
7068             !local_player->GameOver && AllPlayersGone)
7069           PlayerWins(local_player);
7070
7071         return;
7072       }
7073       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7074       {
7075         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7076           DrawLevelField(newx, newy);
7077         else
7078           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7079       }
7080       else if (!IS_FREE(newx, newy))
7081       {
7082         GfxAction[x][y] = ACTION_WAITING;
7083
7084         if (IS_PLAYER(x, y))
7085           DrawPlayerField(x, y);
7086         else
7087           DrawLevelField(x, y);
7088
7089         return;
7090       }
7091     }
7092     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7093     {
7094       if (IS_FOOD_PIG(Feld[newx][newy]))
7095       {
7096         if (IS_MOVING(newx, newy))
7097           RemoveMovingField(newx, newy);
7098         else
7099         {
7100           Feld[newx][newy] = EL_EMPTY;
7101           DrawLevelField(newx, newy);
7102         }
7103
7104         PlayLevelSound(x, y, SND_PIG_DIGGING);
7105       }
7106       else if (!IS_FREE(newx, newy))
7107       {
7108         if (IS_PLAYER(x, y))
7109           DrawPlayerField(x, y);
7110         else
7111           DrawLevelField(x, y);
7112
7113         return;
7114       }
7115     }
7116     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7117     {
7118       if (Store[x][y] != EL_EMPTY)
7119       {
7120         boolean can_clone = FALSE;
7121         int xx, yy;
7122
7123         /* check if element to clone is still there */
7124         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7125         {
7126           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7127           {
7128             can_clone = TRUE;
7129
7130             break;
7131           }
7132         }
7133
7134         /* cannot clone or target field not free anymore -- do not clone */
7135         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7136           Store[x][y] = EL_EMPTY;
7137       }
7138
7139       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7140       {
7141         if (IS_MV_DIAGONAL(MovDir[x][y]))
7142         {
7143           int diagonal_move_dir = MovDir[x][y];
7144           int stored = Store[x][y];
7145           int change_delay = 8;
7146           int graphic;
7147
7148           /* android is moving diagonally */
7149
7150           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7151
7152           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7153           GfxElement[x][y] = EL_EMC_ANDROID;
7154           GfxAction[x][y] = ACTION_SHRINKING;
7155           GfxDir[x][y] = diagonal_move_dir;
7156           ChangeDelay[x][y] = change_delay;
7157
7158           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7159                                    GfxDir[x][y]);
7160
7161           DrawLevelGraphicAnimation(x, y, graphic);
7162           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7163
7164           if (Feld[newx][newy] == EL_ACID)
7165           {
7166             SplashAcid(newx, newy);
7167
7168             return;
7169           }
7170
7171           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7172
7173           Store[newx][newy] = EL_EMC_ANDROID;
7174           GfxElement[newx][newy] = EL_EMC_ANDROID;
7175           GfxAction[newx][newy] = ACTION_GROWING;
7176           GfxDir[newx][newy] = diagonal_move_dir;
7177           ChangeDelay[newx][newy] = change_delay;
7178
7179           graphic = el_act_dir2img(GfxElement[newx][newy],
7180                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7181
7182           DrawLevelGraphicAnimation(newx, newy, graphic);
7183           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7184
7185           return;
7186         }
7187         else
7188         {
7189           Feld[newx][newy] = EL_EMPTY;
7190           DrawLevelField(newx, newy);
7191
7192           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7193         }
7194       }
7195       else if (!IS_FREE(newx, newy))
7196       {
7197 #if 0
7198         if (IS_PLAYER(x, y))
7199           DrawPlayerField(x, y);
7200         else
7201           DrawLevelField(x, y);
7202 #endif
7203
7204         return;
7205       }
7206     }
7207     else if (IS_CUSTOM_ELEMENT(element) &&
7208              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7209     {
7210       int new_element = Feld[newx][newy];
7211
7212       if (!IS_FREE(newx, newy))
7213       {
7214         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7215                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7216                       ACTION_BREAKING);
7217
7218         /* no element can dig solid indestructible elements */
7219         if (IS_INDESTRUCTIBLE(new_element) &&
7220             !IS_DIGGABLE(new_element) &&
7221             !IS_COLLECTIBLE(new_element))
7222           return;
7223
7224         if (AmoebaNr[newx][newy] &&
7225             (new_element == EL_AMOEBA_FULL ||
7226              new_element == EL_BD_AMOEBA ||
7227              new_element == EL_AMOEBA_GROWING))
7228         {
7229           AmoebaCnt[AmoebaNr[newx][newy]]--;
7230           AmoebaCnt2[AmoebaNr[newx][newy]]--;
7231         }
7232
7233         if (IS_MOVING(newx, newy))
7234           RemoveMovingField(newx, newy);
7235         else
7236         {
7237           RemoveField(newx, newy);
7238           DrawLevelField(newx, newy);
7239         }
7240
7241         /* if digged element was about to explode, prevent the explosion */
7242         ExplodeField[newx][newy] = EX_TYPE_NONE;
7243
7244         PlayLevelSoundAction(x, y, action);
7245       }
7246
7247       Store[newx][newy] = EL_EMPTY;
7248 #if 1
7249       /* this makes it possible to leave the removed element again */
7250       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7251         Store[newx][newy] = new_element;
7252 #else
7253       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7254       {
7255         int move_leave_element = element_info[element].move_leave_element;
7256
7257         /* this makes it possible to leave the removed element again */
7258         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7259                              new_element : move_leave_element);
7260       }
7261 #endif
7262
7263       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7264       {
7265         RunnerVisit[x][y] = FrameCounter;
7266         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7267       }
7268     }
7269     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7270     {
7271       if (!IS_FREE(newx, newy))
7272       {
7273         if (IS_PLAYER(x, y))
7274           DrawPlayerField(x, y);
7275         else
7276           DrawLevelField(x, y);
7277
7278         return;
7279       }
7280       else
7281       {
7282         boolean wanna_flame = !RND(10);
7283         int dx = newx - x, dy = newy - y;
7284         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7285         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7286         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7287                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7288         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7289                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7290
7291         if ((wanna_flame ||
7292              IS_CLASSIC_ENEMY(element1) ||
7293              IS_CLASSIC_ENEMY(element2)) &&
7294             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7295             element1 != EL_FLAMES && element2 != EL_FLAMES)
7296         {
7297           ResetGfxAnimation(x, y);
7298           GfxAction[x][y] = ACTION_ATTACKING;
7299
7300           if (IS_PLAYER(x, y))
7301             DrawPlayerField(x, y);
7302           else
7303             DrawLevelField(x, y);
7304
7305           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7306
7307           MovDelay[x][y] = 50;
7308
7309           /* !!! */
7310 #if 0
7311           RemoveField(newx, newy);
7312 #endif
7313           Feld[newx][newy] = EL_FLAMES;
7314           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7315           {
7316 #if 0
7317             RemoveField(newx1, newy1);
7318 #endif
7319             Feld[newx1][newy1] = EL_FLAMES;
7320           }
7321           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7322           {
7323 #if 0
7324             RemoveField(newx2, newy2);
7325 #endif
7326             Feld[newx2][newy2] = EL_FLAMES;
7327           }
7328
7329           return;
7330         }
7331       }
7332     }
7333     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7334              Feld[newx][newy] == EL_DIAMOND)
7335     {
7336       if (IS_MOVING(newx, newy))
7337         RemoveMovingField(newx, newy);
7338       else
7339       {
7340         Feld[newx][newy] = EL_EMPTY;
7341         DrawLevelField(newx, newy);
7342       }
7343
7344       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7345     }
7346     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7347              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7348     {
7349       if (AmoebaNr[newx][newy])
7350       {
7351         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7352         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7353             Feld[newx][newy] == EL_BD_AMOEBA)
7354           AmoebaCnt[AmoebaNr[newx][newy]]--;
7355       }
7356
7357 #if 0
7358       /* !!! test !!! */
7359       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7360       {
7361         RemoveMovingField(newx, newy);
7362       }
7363 #else
7364       if (IS_MOVING(newx, newy))
7365       {
7366         RemoveMovingField(newx, newy);
7367       }
7368 #endif
7369       else
7370       {
7371         Feld[newx][newy] = EL_EMPTY;
7372         DrawLevelField(newx, newy);
7373       }
7374
7375       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7376     }
7377     else if ((element == EL_PACMAN || element == EL_MOLE)
7378              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7379     {
7380       if (AmoebaNr[newx][newy])
7381       {
7382         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7383         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7384             Feld[newx][newy] == EL_BD_AMOEBA)
7385           AmoebaCnt[AmoebaNr[newx][newy]]--;
7386       }
7387
7388       if (element == EL_MOLE)
7389       {
7390         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7391         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7392
7393         ResetGfxAnimation(x, y);
7394         GfxAction[x][y] = ACTION_DIGGING;
7395         DrawLevelField(x, y);
7396
7397         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7398
7399         return;                         /* wait for shrinking amoeba */
7400       }
7401       else      /* element == EL_PACMAN */
7402       {
7403         Feld[newx][newy] = EL_EMPTY;
7404         DrawLevelField(newx, newy);
7405         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7406       }
7407     }
7408     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7409              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7410               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7411     {
7412       /* wait for shrinking amoeba to completely disappear */
7413       return;
7414     }
7415     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7416     {
7417       /* object was running against a wall */
7418
7419       TurnRound(x, y);
7420
7421 #if 0
7422       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7423       if (move_pattern & MV_ANY_DIRECTION &&
7424           move_pattern == MovDir[x][y])
7425       {
7426         int blocking_element =
7427           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7428
7429         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7430                                  MovDir[x][y]);
7431
7432         element = Feld[x][y];   /* element might have changed */
7433       }
7434 #endif
7435
7436       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7437         DrawLevelElementAnimation(x, y, element);
7438
7439       if (DONT_TOUCH(element))
7440         TestIfBadThingTouchesPlayer(x, y);
7441
7442       return;
7443     }
7444
7445     InitMovingField(x, y, MovDir[x][y]);
7446
7447     PlayLevelSoundAction(x, y, ACTION_MOVING);
7448   }
7449
7450   if (MovDir[x][y])
7451     ContinueMoving(x, y);
7452 }
7453
7454 void ContinueMoving(int x, int y)
7455 {
7456   int element = Feld[x][y];
7457   struct ElementInfo *ei = &element_info[element];
7458   int direction = MovDir[x][y];
7459   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7460   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7461   int newx = x + dx, newy = y + dy;
7462   int stored = Store[x][y];
7463   int stored_new = Store[newx][newy];
7464   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7465   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7466   boolean last_line = (newy == lev_fieldy - 1);
7467
7468   MovPos[x][y] += getElementMoveStepsize(x, y);
7469
7470   if (pushed_by_player) /* special case: moving object pushed by player */
7471     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7472
7473   if (ABS(MovPos[x][y]) < TILEX)
7474   {
7475 #if 0
7476     int ee = Feld[x][y];
7477     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7478     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7479
7480     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7481            x, y, ABS(MovPos[x][y]),
7482            ee, gg, ff,
7483            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
7484 #endif
7485
7486     DrawLevelField(x, y);
7487
7488     return;     /* element is still moving */
7489   }
7490
7491   /* element reached destination field */
7492
7493   Feld[x][y] = EL_EMPTY;
7494   Feld[newx][newy] = element;
7495   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
7496
7497   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
7498   {
7499     element = Feld[newx][newy] = EL_ACID;
7500   }
7501   else if (element == EL_MOLE)
7502   {
7503     Feld[x][y] = EL_SAND;
7504
7505     DrawLevelFieldCrumbledSandNeighbours(x, y);
7506   }
7507   else if (element == EL_QUICKSAND_FILLING)
7508   {
7509     element = Feld[newx][newy] = get_next_element(element);
7510     Store[newx][newy] = Store[x][y];
7511   }
7512   else if (element == EL_QUICKSAND_EMPTYING)
7513   {
7514     Feld[x][y] = get_next_element(element);
7515     element = Feld[newx][newy] = Store[x][y];
7516   }
7517   else if (element == EL_QUICKSAND_FAST_FILLING)
7518   {
7519     element = Feld[newx][newy] = get_next_element(element);
7520     Store[newx][newy] = Store[x][y];
7521   }
7522   else if (element == EL_QUICKSAND_FAST_EMPTYING)
7523   {
7524     Feld[x][y] = get_next_element(element);
7525     element = Feld[newx][newy] = Store[x][y];
7526   }
7527   else if (element == EL_MAGIC_WALL_FILLING)
7528   {
7529     element = Feld[newx][newy] = get_next_element(element);
7530     if (!game.magic_wall_active)
7531       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7532     Store[newx][newy] = Store[x][y];
7533   }
7534   else if (element == EL_MAGIC_WALL_EMPTYING)
7535   {
7536     Feld[x][y] = get_next_element(element);
7537     if (!game.magic_wall_active)
7538       Feld[x][y] = EL_MAGIC_WALL_DEAD;
7539     element = Feld[newx][newy] = Store[x][y];
7540
7541 #if USE_NEW_CUSTOM_VALUE
7542     InitField(newx, newy, FALSE);
7543 #endif
7544   }
7545   else if (element == EL_BD_MAGIC_WALL_FILLING)
7546   {
7547     element = Feld[newx][newy] = get_next_element(element);
7548     if (!game.magic_wall_active)
7549       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
7550     Store[newx][newy] = Store[x][y];
7551   }
7552   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
7553   {
7554     Feld[x][y] = get_next_element(element);
7555     if (!game.magic_wall_active)
7556       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7557     element = Feld[newx][newy] = Store[x][y];
7558
7559 #if USE_NEW_CUSTOM_VALUE
7560     InitField(newx, newy, FALSE);
7561 #endif
7562   }
7563   else if (element == EL_DC_MAGIC_WALL_FILLING)
7564   {
7565     element = Feld[newx][newy] = get_next_element(element);
7566     if (!game.magic_wall_active)
7567       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
7568     Store[newx][newy] = Store[x][y];
7569   }
7570   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
7571   {
7572     Feld[x][y] = get_next_element(element);
7573     if (!game.magic_wall_active)
7574       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
7575     element = Feld[newx][newy] = Store[x][y];
7576
7577 #if USE_NEW_CUSTOM_VALUE
7578     InitField(newx, newy, FALSE);
7579 #endif
7580   }
7581   else if (element == EL_AMOEBA_DROPPING)
7582   {
7583     Feld[x][y] = get_next_element(element);
7584     element = Feld[newx][newy] = Store[x][y];
7585   }
7586   else if (element == EL_SOKOBAN_OBJECT)
7587   {
7588     if (Back[x][y])
7589       Feld[x][y] = Back[x][y];
7590
7591     if (Back[newx][newy])
7592       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
7593
7594     Back[x][y] = Back[newx][newy] = 0;
7595   }
7596
7597   Store[x][y] = EL_EMPTY;
7598   MovPos[x][y] = 0;
7599   MovDir[x][y] = 0;
7600   MovDelay[x][y] = 0;
7601
7602   MovDelay[newx][newy] = 0;
7603
7604   if (CAN_CHANGE_OR_HAS_ACTION(element))
7605   {
7606     /* copy element change control values to new field */
7607     ChangeDelay[newx][newy] = ChangeDelay[x][y];
7608     ChangePage[newx][newy]  = ChangePage[x][y];
7609     ChangeCount[newx][newy] = ChangeCount[x][y];
7610     ChangeEvent[newx][newy] = ChangeEvent[x][y];
7611   }
7612
7613 #if USE_NEW_CUSTOM_VALUE
7614     CustomValue[newx][newy] = CustomValue[x][y];
7615 #endif
7616
7617   ChangeDelay[x][y] = 0;
7618   ChangePage[x][y] = -1;
7619   ChangeCount[x][y] = 0;
7620   ChangeEvent[x][y] = -1;
7621
7622 #if USE_NEW_CUSTOM_VALUE
7623   CustomValue[x][y] = 0;
7624 #endif
7625
7626   /* copy animation control values to new field */
7627   GfxFrame[newx][newy]  = GfxFrame[x][y];
7628   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
7629   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
7630   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
7631
7632   Pushed[x][y] = Pushed[newx][newy] = FALSE;
7633
7634   /* some elements can leave other elements behind after moving */
7635 #if 1
7636   if (ei->move_leave_element != EL_EMPTY &&
7637       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7638       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7639 #else
7640   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7641       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7642       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7643 #endif
7644   {
7645     int move_leave_element = ei->move_leave_element;
7646
7647 #if 1
7648 #if 1
7649     /* this makes it possible to leave the removed element again */
7650     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7651       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7652 #else
7653     /* this makes it possible to leave the removed element again */
7654     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7655       move_leave_element = stored;
7656 #endif
7657 #else
7658     /* this makes it possible to leave the removed element again */
7659     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7660         ei->move_leave_element == EL_TRIGGER_ELEMENT)
7661       move_leave_element = stored;
7662 #endif
7663
7664     Feld[x][y] = move_leave_element;
7665
7666     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7667       MovDir[x][y] = direction;
7668
7669     InitField(x, y, FALSE);
7670
7671     if (GFX_CRUMBLED(Feld[x][y]))
7672       DrawLevelFieldCrumbledSandNeighbours(x, y);
7673
7674     if (ELEM_IS_PLAYER(move_leave_element))
7675       RelocatePlayer(x, y, move_leave_element);
7676   }
7677
7678   /* do this after checking for left-behind element */
7679   ResetGfxAnimation(x, y);      /* reset animation values for old field */
7680
7681   if (!CAN_MOVE(element) ||
7682       (CAN_FALL(element) && direction == MV_DOWN &&
7683        (element == EL_SPRING ||
7684         element_info[element].move_pattern == MV_WHEN_PUSHED ||
7685         element_info[element].move_pattern == MV_WHEN_DROPPED)))
7686     GfxDir[x][y] = MovDir[newx][newy] = 0;
7687
7688   DrawLevelField(x, y);
7689   DrawLevelField(newx, newy);
7690
7691   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
7692
7693   /* prevent pushed element from moving on in pushed direction */
7694   if (pushed_by_player && CAN_MOVE(element) &&
7695       element_info[element].move_pattern & MV_ANY_DIRECTION &&
7696       !(element_info[element].move_pattern & direction))
7697     TurnRound(newx, newy);
7698
7699   /* prevent elements on conveyor belt from moving on in last direction */
7700   if (pushed_by_conveyor && CAN_FALL(element) &&
7701       direction & MV_HORIZONTAL)
7702     MovDir[newx][newy] = 0;
7703
7704   if (!pushed_by_player)
7705   {
7706     int nextx = newx + dx, nexty = newy + dy;
7707     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
7708
7709     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
7710
7711     if (CAN_FALL(element) && direction == MV_DOWN)
7712       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
7713
7714     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
7715       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
7716
7717 #if USE_FIX_IMPACT_COLLISION
7718     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
7719       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
7720 #endif
7721   }
7722
7723   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
7724   {
7725     TestIfBadThingTouchesPlayer(newx, newy);
7726     TestIfBadThingTouchesFriend(newx, newy);
7727
7728     if (!IS_CUSTOM_ELEMENT(element))
7729       TestIfBadThingTouchesOtherBadThing(newx, newy);
7730   }
7731   else if (element == EL_PENGUIN)
7732     TestIfFriendTouchesBadThing(newx, newy);
7733
7734   /* give the player one last chance (one more frame) to move away */
7735   if (CAN_FALL(element) && direction == MV_DOWN &&
7736       (last_line || (!IS_FREE(x, newy + 1) &&
7737                      (!IS_PLAYER(x, newy + 1) ||
7738                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
7739     Impact(x, newy);
7740
7741   if (pushed_by_player && !game.use_change_when_pushing_bug)
7742   {
7743     int push_side = MV_DIR_OPPOSITE(direction);
7744     struct PlayerInfo *player = PLAYERINFO(x, y);
7745
7746     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
7747                                player->index_bit, push_side);
7748     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
7749                                         player->index_bit, push_side);
7750   }
7751
7752   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
7753     MovDelay[newx][newy] = 1;
7754
7755   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
7756
7757   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
7758
7759 #if 0
7760   if (ChangePage[newx][newy] != -1)             /* delayed change */
7761   {
7762     int page = ChangePage[newx][newy];
7763     struct ElementChangeInfo *change = &ei->change_page[page];
7764
7765     ChangePage[newx][newy] = -1;
7766
7767     if (change->can_change)
7768     {
7769       if (ChangeElement(newx, newy, element, page))
7770       {
7771         if (change->post_change_function)
7772           change->post_change_function(newx, newy);
7773       }
7774     }
7775
7776     if (change->has_action)
7777       ExecuteCustomElementAction(newx, newy, element, page);
7778   }
7779 #endif
7780
7781   TestIfElementHitsCustomElement(newx, newy, direction);
7782   TestIfPlayerTouchesCustomElement(newx, newy);
7783   TestIfElementTouchesCustomElement(newx, newy);
7784
7785   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7786       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7787     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7788                              MV_DIR_OPPOSITE(direction));
7789 }
7790
7791 int AmoebeNachbarNr(int ax, int ay)
7792 {
7793   int i;
7794   int element = Feld[ax][ay];
7795   int group_nr = 0;
7796   static int xy[4][2] =
7797   {
7798     { 0, -1 },
7799     { -1, 0 },
7800     { +1, 0 },
7801     { 0, +1 }
7802   };
7803
7804   for (i = 0; i < NUM_DIRECTIONS; i++)
7805   {
7806     int x = ax + xy[i][0];
7807     int y = ay + xy[i][1];
7808
7809     if (!IN_LEV_FIELD(x, y))
7810       continue;
7811
7812     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7813       group_nr = AmoebaNr[x][y];
7814   }
7815
7816   return group_nr;
7817 }
7818
7819 void AmoebenVereinigen(int ax, int ay)
7820 {
7821   int i, x, y, xx, yy;
7822   int new_group_nr = AmoebaNr[ax][ay];
7823   static int xy[4][2] =
7824   {
7825     { 0, -1 },
7826     { -1, 0 },
7827     { +1, 0 },
7828     { 0, +1 }
7829   };
7830
7831   if (new_group_nr == 0)
7832     return;
7833
7834   for (i = 0; i < NUM_DIRECTIONS; i++)
7835   {
7836     x = ax + xy[i][0];
7837     y = ay + xy[i][1];
7838
7839     if (!IN_LEV_FIELD(x, y))
7840       continue;
7841
7842     if ((Feld[x][y] == EL_AMOEBA_FULL ||
7843          Feld[x][y] == EL_BD_AMOEBA ||
7844          Feld[x][y] == EL_AMOEBA_DEAD) &&
7845         AmoebaNr[x][y] != new_group_nr)
7846     {
7847       int old_group_nr = AmoebaNr[x][y];
7848
7849       if (old_group_nr == 0)
7850         return;
7851
7852       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7853       AmoebaCnt[old_group_nr] = 0;
7854       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7855       AmoebaCnt2[old_group_nr] = 0;
7856
7857       SCAN_PLAYFIELD(xx, yy)
7858       {
7859         if (AmoebaNr[xx][yy] == old_group_nr)
7860           AmoebaNr[xx][yy] = new_group_nr;
7861       }
7862     }
7863   }
7864 }
7865
7866 void AmoebeUmwandeln(int ax, int ay)
7867 {
7868   int i, x, y;
7869
7870   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7871   {
7872     int group_nr = AmoebaNr[ax][ay];
7873
7874 #ifdef DEBUG
7875     if (group_nr == 0)
7876     {
7877       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7878       printf("AmoebeUmwandeln(): This should never happen!\n");
7879       return;
7880     }
7881 #endif
7882
7883     SCAN_PLAYFIELD(x, y)
7884     {
7885       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7886       {
7887         AmoebaNr[x][y] = 0;
7888         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7889       }
7890     }
7891
7892     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7893                             SND_AMOEBA_TURNING_TO_GEM :
7894                             SND_AMOEBA_TURNING_TO_ROCK));
7895     Bang(ax, ay);
7896   }
7897   else
7898   {
7899     static int xy[4][2] =
7900     {
7901       { 0, -1 },
7902       { -1, 0 },
7903       { +1, 0 },
7904       { 0, +1 }
7905     };
7906
7907     for (i = 0; i < NUM_DIRECTIONS; i++)
7908     {
7909       x = ax + xy[i][0];
7910       y = ay + xy[i][1];
7911
7912       if (!IN_LEV_FIELD(x, y))
7913         continue;
7914
7915       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7916       {
7917         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7918                               SND_AMOEBA_TURNING_TO_GEM :
7919                               SND_AMOEBA_TURNING_TO_ROCK));
7920         Bang(x, y);
7921       }
7922     }
7923   }
7924 }
7925
7926 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7927 {
7928   int x, y;
7929   int group_nr = AmoebaNr[ax][ay];
7930   boolean done = FALSE;
7931
7932 #ifdef DEBUG
7933   if (group_nr == 0)
7934   {
7935     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7936     printf("AmoebeUmwandelnBD(): This should never happen!\n");
7937     return;
7938   }
7939 #endif
7940
7941   SCAN_PLAYFIELD(x, y)
7942   {
7943     if (AmoebaNr[x][y] == group_nr &&
7944         (Feld[x][y] == EL_AMOEBA_DEAD ||
7945          Feld[x][y] == EL_BD_AMOEBA ||
7946          Feld[x][y] == EL_AMOEBA_GROWING))
7947     {
7948       AmoebaNr[x][y] = 0;
7949       Feld[x][y] = new_element;
7950       InitField(x, y, FALSE);
7951       DrawLevelField(x, y);
7952       done = TRUE;
7953     }
7954   }
7955
7956   if (done)
7957     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7958                             SND_BD_AMOEBA_TURNING_TO_ROCK :
7959                             SND_BD_AMOEBA_TURNING_TO_GEM));
7960 }
7961
7962 void AmoebeWaechst(int x, int y)
7963 {
7964   static unsigned long sound_delay = 0;
7965   static unsigned long sound_delay_value = 0;
7966
7967   if (!MovDelay[x][y])          /* start new growing cycle */
7968   {
7969     MovDelay[x][y] = 7;
7970
7971     if (DelayReached(&sound_delay, sound_delay_value))
7972     {
7973       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7974       sound_delay_value = 30;
7975     }
7976   }
7977
7978   if (MovDelay[x][y])           /* wait some time before growing bigger */
7979   {
7980     MovDelay[x][y]--;
7981     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7982     {
7983       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7984                                            6 - MovDelay[x][y]);
7985
7986       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7987     }
7988
7989     if (!MovDelay[x][y])
7990     {
7991       Feld[x][y] = Store[x][y];
7992       Store[x][y] = 0;
7993       DrawLevelField(x, y);
7994     }
7995   }
7996 }
7997
7998 void AmoebaDisappearing(int x, int y)
7999 {
8000   static unsigned long sound_delay = 0;
8001   static unsigned long sound_delay_value = 0;
8002
8003   if (!MovDelay[x][y])          /* start new shrinking cycle */
8004   {
8005     MovDelay[x][y] = 7;
8006
8007     if (DelayReached(&sound_delay, sound_delay_value))
8008       sound_delay_value = 30;
8009   }
8010
8011   if (MovDelay[x][y])           /* wait some time before shrinking */
8012   {
8013     MovDelay[x][y]--;
8014     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8015     {
8016       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8017                                            6 - MovDelay[x][y]);
8018
8019       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8020     }
8021
8022     if (!MovDelay[x][y])
8023     {
8024       Feld[x][y] = EL_EMPTY;
8025       DrawLevelField(x, y);
8026
8027       /* don't let mole enter this field in this cycle;
8028          (give priority to objects falling to this field from above) */
8029       Stop[x][y] = TRUE;
8030     }
8031   }
8032 }
8033
8034 void AmoebeAbleger(int ax, int ay)
8035 {
8036   int i;
8037   int element = Feld[ax][ay];
8038   int graphic = el2img(element);
8039   int newax = ax, neway = ay;
8040   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8041   static int xy[4][2] =
8042   {
8043     { 0, -1 },
8044     { -1, 0 },
8045     { +1, 0 },
8046     { 0, +1 }
8047   };
8048
8049   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8050   {
8051     Feld[ax][ay] = EL_AMOEBA_DEAD;
8052     DrawLevelField(ax, ay);
8053     return;
8054   }
8055
8056   if (IS_ANIMATED(graphic))
8057     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8058
8059   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8060     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8061
8062   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8063   {
8064     MovDelay[ax][ay]--;
8065     if (MovDelay[ax][ay])
8066       return;
8067   }
8068
8069   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8070   {
8071     int start = RND(4);
8072     int x = ax + xy[start][0];
8073     int y = ay + xy[start][1];
8074
8075     if (!IN_LEV_FIELD(x, y))
8076       return;
8077
8078     if (IS_FREE(x, y) ||
8079         CAN_GROW_INTO(Feld[x][y]) ||
8080         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8081         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8082     {
8083       newax = x;
8084       neway = y;
8085     }
8086
8087     if (newax == ax && neway == ay)
8088       return;
8089   }
8090   else                          /* normal or "filled" (BD style) amoeba */
8091   {
8092     int start = RND(4);
8093     boolean waiting_for_player = FALSE;
8094
8095     for (i = 0; i < NUM_DIRECTIONS; i++)
8096     {
8097       int j = (start + i) % 4;
8098       int x = ax + xy[j][0];
8099       int y = ay + xy[j][1];
8100
8101       if (!IN_LEV_FIELD(x, y))
8102         continue;
8103
8104       if (IS_FREE(x, y) ||
8105           CAN_GROW_INTO(Feld[x][y]) ||
8106           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8107           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8108       {
8109         newax = x;
8110         neway = y;
8111         break;
8112       }
8113       else if (IS_PLAYER(x, y))
8114         waiting_for_player = TRUE;
8115     }
8116
8117     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8118     {
8119       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8120       {
8121         Feld[ax][ay] = EL_AMOEBA_DEAD;
8122         DrawLevelField(ax, ay);
8123         AmoebaCnt[AmoebaNr[ax][ay]]--;
8124
8125         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8126         {
8127           if (element == EL_AMOEBA_FULL)
8128             AmoebeUmwandeln(ax, ay);
8129           else if (element == EL_BD_AMOEBA)
8130             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8131         }
8132       }
8133       return;
8134     }
8135     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8136     {
8137       /* amoeba gets larger by growing in some direction */
8138
8139       int new_group_nr = AmoebaNr[ax][ay];
8140
8141 #ifdef DEBUG
8142   if (new_group_nr == 0)
8143   {
8144     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8145     printf("AmoebeAbleger(): This should never happen!\n");
8146     return;
8147   }
8148 #endif
8149
8150       AmoebaNr[newax][neway] = new_group_nr;
8151       AmoebaCnt[new_group_nr]++;
8152       AmoebaCnt2[new_group_nr]++;
8153
8154       /* if amoeba touches other amoeba(s) after growing, unify them */
8155       AmoebenVereinigen(newax, neway);
8156
8157       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8158       {
8159         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8160         return;
8161       }
8162     }
8163   }
8164
8165   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8166       (neway == lev_fieldy - 1 && newax != ax))
8167   {
8168     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8169     Store[newax][neway] = element;
8170   }
8171   else if (neway == ay || element == EL_EMC_DRIPPER)
8172   {
8173     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8174
8175     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8176   }
8177   else
8178   {
8179     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8180     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8181     Store[ax][ay] = EL_AMOEBA_DROP;
8182     ContinueMoving(ax, ay);
8183     return;
8184   }
8185
8186   DrawLevelField(newax, neway);
8187 }
8188
8189 void Life(int ax, int ay)
8190 {
8191   int x1, y1, x2, y2;
8192   int life_time = 40;
8193   int element = Feld[ax][ay];
8194   int graphic = el2img(element);
8195   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8196                          level.biomaze);
8197   boolean changed = FALSE;
8198
8199   if (IS_ANIMATED(graphic))
8200     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8201
8202   if (Stop[ax][ay])
8203     return;
8204
8205   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8206     MovDelay[ax][ay] = life_time;
8207
8208   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8209   {
8210     MovDelay[ax][ay]--;
8211     if (MovDelay[ax][ay])
8212       return;
8213   }
8214
8215   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8216   {
8217     int xx = ax+x1, yy = ay+y1;
8218     int nachbarn = 0;
8219
8220     if (!IN_LEV_FIELD(xx, yy))
8221       continue;
8222
8223     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8224     {
8225       int x = xx+x2, y = yy+y2;
8226
8227       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8228         continue;
8229
8230       if (((Feld[x][y] == element ||
8231             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8232            !Stop[x][y]) ||
8233           (IS_FREE(x, y) && Stop[x][y]))
8234         nachbarn++;
8235     }
8236
8237     if (xx == ax && yy == ay)           /* field in the middle */
8238     {
8239       if (nachbarn < life_parameter[0] ||
8240           nachbarn > life_parameter[1])
8241       {
8242         Feld[xx][yy] = EL_EMPTY;
8243         if (!Stop[xx][yy])
8244           DrawLevelField(xx, yy);
8245         Stop[xx][yy] = TRUE;
8246         changed = TRUE;
8247       }
8248     }
8249     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8250     {                                   /* free border field */
8251       if (nachbarn >= life_parameter[2] &&
8252           nachbarn <= life_parameter[3])
8253       {
8254         Feld[xx][yy] = element;
8255         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8256         if (!Stop[xx][yy])
8257           DrawLevelField(xx, yy);
8258         Stop[xx][yy] = TRUE;
8259         changed = TRUE;
8260       }
8261     }
8262   }
8263
8264   if (changed)
8265     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8266                    SND_GAME_OF_LIFE_GROWING);
8267 }
8268
8269 static void InitRobotWheel(int x, int y)
8270 {
8271   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8272 }
8273
8274 static void RunRobotWheel(int x, int y)
8275 {
8276   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8277 }
8278
8279 static void StopRobotWheel(int x, int y)
8280 {
8281   if (ZX == x && ZY == y)
8282     ZX = ZY = -1;
8283 }
8284
8285 static void InitTimegateWheel(int x, int y)
8286 {
8287   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8288 }
8289
8290 static void RunTimegateWheel(int x, int y)
8291 {
8292   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8293 }
8294
8295 static void InitMagicBallDelay(int x, int y)
8296 {
8297 #if 1
8298   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8299 #else
8300   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8301 #endif
8302 }
8303
8304 static void ActivateMagicBall(int bx, int by)
8305 {
8306   int x, y;
8307
8308   if (level.ball_random)
8309   {
8310     int pos_border = RND(8);    /* select one of the eight border elements */
8311     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8312     int xx = pos_content % 3;
8313     int yy = pos_content / 3;
8314
8315     x = bx - 1 + xx;
8316     y = by - 1 + yy;
8317
8318     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8319       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8320   }
8321   else
8322   {
8323     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8324     {
8325       int xx = x - bx + 1;
8326       int yy = y - by + 1;
8327
8328       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8329         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8330     }
8331   }
8332
8333   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8334 }
8335
8336 void CheckExit(int x, int y)
8337 {
8338   if (local_player->gems_still_needed > 0 ||
8339       local_player->sokobanfields_still_needed > 0 ||
8340       local_player->lights_still_needed > 0)
8341   {
8342     int element = Feld[x][y];
8343     int graphic = el2img(element);
8344
8345     if (IS_ANIMATED(graphic))
8346       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8347
8348     return;
8349   }
8350
8351   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8352     return;
8353
8354   Feld[x][y] = EL_EXIT_OPENING;
8355
8356   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8357 }
8358
8359 void CheckExitEM(int x, int y)
8360 {
8361   if (local_player->gems_still_needed > 0 ||
8362       local_player->sokobanfields_still_needed > 0 ||
8363       local_player->lights_still_needed > 0)
8364   {
8365     int element = Feld[x][y];
8366     int graphic = el2img(element);
8367
8368     if (IS_ANIMATED(graphic))
8369       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8370
8371     return;
8372   }
8373
8374   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8375     return;
8376
8377   Feld[x][y] = EL_EM_EXIT_OPENING;
8378
8379   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8380 }
8381
8382 void CheckExitSteel(int x, int y)
8383 {
8384   if (local_player->gems_still_needed > 0 ||
8385       local_player->sokobanfields_still_needed > 0 ||
8386       local_player->lights_still_needed > 0)
8387   {
8388     int element = Feld[x][y];
8389     int graphic = el2img(element);
8390
8391     if (IS_ANIMATED(graphic))
8392       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8393
8394     return;
8395   }
8396
8397   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8398     return;
8399
8400   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8401
8402   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8403 }
8404
8405 void CheckExitSteelEM(int x, int y)
8406 {
8407   if (local_player->gems_still_needed > 0 ||
8408       local_player->sokobanfields_still_needed > 0 ||
8409       local_player->lights_still_needed > 0)
8410   {
8411     int element = Feld[x][y];
8412     int graphic = el2img(element);
8413
8414     if (IS_ANIMATED(graphic))
8415       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8416
8417     return;
8418   }
8419
8420   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8421     return;
8422
8423   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8424
8425   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8426 }
8427
8428 void CheckExitSP(int x, int y)
8429 {
8430   if (local_player->gems_still_needed > 0)
8431   {
8432     int element = Feld[x][y];
8433     int graphic = el2img(element);
8434
8435     if (IS_ANIMATED(graphic))
8436       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8437
8438     return;
8439   }
8440
8441   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8442     return;
8443
8444   Feld[x][y] = EL_SP_EXIT_OPENING;
8445
8446   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8447 }
8448
8449 static void CloseAllOpenTimegates()
8450 {
8451   int x, y;
8452
8453   SCAN_PLAYFIELD(x, y)
8454   {
8455     int element = Feld[x][y];
8456
8457     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8458     {
8459       Feld[x][y] = EL_TIMEGATE_CLOSING;
8460
8461       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8462     }
8463   }
8464 }
8465
8466 void DrawTwinkleOnField(int x, int y)
8467 {
8468   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8469     return;
8470
8471   if (Feld[x][y] == EL_BD_DIAMOND)
8472     return;
8473
8474   if (MovDelay[x][y] == 0)      /* next animation frame */
8475     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8476
8477   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8478   {
8479     MovDelay[x][y]--;
8480
8481     if (setup.direct_draw && MovDelay[x][y])
8482       SetDrawtoField(DRAW_BUFFERED);
8483
8484     DrawLevelElementAnimation(x, y, Feld[x][y]);
8485
8486     if (MovDelay[x][y] != 0)
8487     {
8488       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8489                                            10 - MovDelay[x][y]);
8490
8491       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8492
8493       if (setup.direct_draw)
8494       {
8495         int dest_x, dest_y;
8496
8497         dest_x = FX + SCREENX(x) * TILEX;
8498         dest_y = FY + SCREENY(y) * TILEY;
8499
8500         BlitBitmap(drawto_field, window,
8501                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8502         SetDrawtoField(DRAW_DIRECT);
8503       }
8504     }
8505   }
8506 }
8507
8508 void MauerWaechst(int x, int y)
8509 {
8510   int delay = 6;
8511
8512   if (!MovDelay[x][y])          /* next animation frame */
8513     MovDelay[x][y] = 3 * delay;
8514
8515   if (MovDelay[x][y])           /* wait some time before next frame */
8516   {
8517     MovDelay[x][y]--;
8518
8519     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8520     {
8521       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8522       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8523
8524       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8525     }
8526
8527     if (!MovDelay[x][y])
8528     {
8529       if (MovDir[x][y] == MV_LEFT)
8530       {
8531         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8532           DrawLevelField(x - 1, y);
8533       }
8534       else if (MovDir[x][y] == MV_RIGHT)
8535       {
8536         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8537           DrawLevelField(x + 1, y);
8538       }
8539       else if (MovDir[x][y] == MV_UP)
8540       {
8541         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8542           DrawLevelField(x, y - 1);
8543       }
8544       else
8545       {
8546         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8547           DrawLevelField(x, y + 1);
8548       }
8549
8550       Feld[x][y] = Store[x][y];
8551       Store[x][y] = 0;
8552       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8553       DrawLevelField(x, y);
8554     }
8555   }
8556 }
8557
8558 void MauerAbleger(int ax, int ay)
8559 {
8560   int element = Feld[ax][ay];
8561   int graphic = el2img(element);
8562   boolean oben_frei = FALSE, unten_frei = FALSE;
8563   boolean links_frei = FALSE, rechts_frei = FALSE;
8564   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8565   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8566   boolean new_wall = FALSE;
8567
8568   if (IS_ANIMATED(graphic))
8569     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8570
8571   if (!MovDelay[ax][ay])        /* start building new wall */
8572     MovDelay[ax][ay] = 6;
8573
8574   if (MovDelay[ax][ay])         /* wait some time before building new wall */
8575   {
8576     MovDelay[ax][ay]--;
8577     if (MovDelay[ax][ay])
8578       return;
8579   }
8580
8581   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8582     oben_frei = TRUE;
8583   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8584     unten_frei = TRUE;
8585   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8586     links_frei = TRUE;
8587   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8588     rechts_frei = TRUE;
8589
8590   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
8591       element == EL_EXPANDABLE_WALL_ANY)
8592   {
8593     if (oben_frei)
8594     {
8595       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
8596       Store[ax][ay-1] = element;
8597       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8598       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8599         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8600                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
8601       new_wall = TRUE;
8602     }
8603     if (unten_frei)
8604     {
8605       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
8606       Store[ax][ay+1] = element;
8607       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8608       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8609         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8610                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8611       new_wall = TRUE;
8612     }
8613   }
8614
8615   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8616       element == EL_EXPANDABLE_WALL_ANY ||
8617       element == EL_EXPANDABLE_WALL ||
8618       element == EL_BD_EXPANDABLE_WALL)
8619   {
8620     if (links_frei)
8621     {
8622       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8623       Store[ax-1][ay] = element;
8624       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8625       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8626         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8627                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8628       new_wall = TRUE;
8629     }
8630
8631     if (rechts_frei)
8632     {
8633       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8634       Store[ax+1][ay] = element;
8635       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8636       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8637         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8638                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8639       new_wall = TRUE;
8640     }
8641   }
8642
8643   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8644     DrawLevelField(ax, ay);
8645
8646   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8647     oben_massiv = TRUE;
8648   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8649     unten_massiv = TRUE;
8650   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8651     links_massiv = TRUE;
8652   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8653     rechts_massiv = TRUE;
8654
8655   if (((oben_massiv && unten_massiv) ||
8656        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8657        element == EL_EXPANDABLE_WALL) &&
8658       ((links_massiv && rechts_massiv) ||
8659        element == EL_EXPANDABLE_WALL_VERTICAL))
8660     Feld[ax][ay] = EL_WALL;
8661
8662   if (new_wall)
8663     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8664 }
8665
8666 void MauerAblegerStahl(int ax, int ay)
8667 {
8668   int element = Feld[ax][ay];
8669   int graphic = el2img(element);
8670   boolean oben_frei = FALSE, unten_frei = FALSE;
8671   boolean links_frei = FALSE, rechts_frei = FALSE;
8672   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8673   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8674   boolean new_wall = FALSE;
8675
8676   if (IS_ANIMATED(graphic))
8677     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8678
8679   if (!MovDelay[ax][ay])        /* start building new wall */
8680     MovDelay[ax][ay] = 6;
8681
8682   if (MovDelay[ax][ay])         /* wait some time before building new wall */
8683   {
8684     MovDelay[ax][ay]--;
8685     if (MovDelay[ax][ay])
8686       return;
8687   }
8688
8689   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8690     oben_frei = TRUE;
8691   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8692     unten_frei = TRUE;
8693   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8694     links_frei = TRUE;
8695   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8696     rechts_frei = TRUE;
8697
8698   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
8699       element == EL_EXPANDABLE_STEELWALL_ANY)
8700   {
8701     if (oben_frei)
8702     {
8703       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
8704       Store[ax][ay-1] = element;
8705       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8706       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8707         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8708                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
8709       new_wall = TRUE;
8710     }
8711     if (unten_frei)
8712     {
8713       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
8714       Store[ax][ay+1] = element;
8715       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8716       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8717         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8718                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
8719       new_wall = TRUE;
8720     }
8721   }
8722
8723   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
8724       element == EL_EXPANDABLE_STEELWALL_ANY)
8725   {
8726     if (links_frei)
8727     {
8728       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8729       Store[ax-1][ay] = element;
8730       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8731       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8732         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8733                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
8734       new_wall = TRUE;
8735     }
8736
8737     if (rechts_frei)
8738     {
8739       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8740       Store[ax+1][ay] = element;
8741       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8742       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8743         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8744                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
8745       new_wall = TRUE;
8746     }
8747   }
8748
8749   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8750     oben_massiv = TRUE;
8751   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8752     unten_massiv = TRUE;
8753   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8754     links_massiv = TRUE;
8755   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8756     rechts_massiv = TRUE;
8757
8758   if (((oben_massiv && unten_massiv) ||
8759        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
8760       ((links_massiv && rechts_massiv) ||
8761        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
8762     Feld[ax][ay] = EL_WALL;
8763
8764   if (new_wall)
8765     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8766 }
8767
8768 void CheckForDragon(int x, int y)
8769 {
8770   int i, j;
8771   boolean dragon_found = FALSE;
8772   static int xy[4][2] =
8773   {
8774     { 0, -1 },
8775     { -1, 0 },
8776     { +1, 0 },
8777     { 0, +1 }
8778   };
8779
8780   for (i = 0; i < NUM_DIRECTIONS; i++)
8781   {
8782     for (j = 0; j < 4; j++)
8783     {
8784       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8785
8786       if (IN_LEV_FIELD(xx, yy) &&
8787           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
8788       {
8789         if (Feld[xx][yy] == EL_DRAGON)
8790           dragon_found = TRUE;
8791       }
8792       else
8793         break;
8794     }
8795   }
8796
8797   if (!dragon_found)
8798   {
8799     for (i = 0; i < NUM_DIRECTIONS; i++)
8800     {
8801       for (j = 0; j < 3; j++)
8802       {
8803         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8804   
8805         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
8806         {
8807           Feld[xx][yy] = EL_EMPTY;
8808           DrawLevelField(xx, yy);
8809         }
8810         else
8811           break;
8812       }
8813     }
8814   }
8815 }
8816
8817 static void InitBuggyBase(int x, int y)
8818 {
8819   int element = Feld[x][y];
8820   int activating_delay = FRAMES_PER_SECOND / 4;
8821
8822   ChangeDelay[x][y] =
8823     (element == EL_SP_BUGGY_BASE ?
8824      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
8825      element == EL_SP_BUGGY_BASE_ACTIVATING ?
8826      activating_delay :
8827      element == EL_SP_BUGGY_BASE_ACTIVE ?
8828      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
8829 }
8830
8831 static void WarnBuggyBase(int x, int y)
8832 {
8833   int i;
8834   static int xy[4][2] =
8835   {
8836     { 0, -1 },
8837     { -1, 0 },
8838     { +1, 0 },
8839     { 0, +1 }
8840   };
8841
8842   for (i = 0; i < NUM_DIRECTIONS; i++)
8843   {
8844     int xx = x + xy[i][0];
8845     int yy = y + xy[i][1];
8846
8847     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
8848     {
8849       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8850
8851       break;
8852     }
8853   }
8854 }
8855
8856 static void InitTrap(int x, int y)
8857 {
8858   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8859 }
8860
8861 static void ActivateTrap(int x, int y)
8862 {
8863   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8864 }
8865
8866 static void ChangeActiveTrap(int x, int y)
8867 {
8868   int graphic = IMG_TRAP_ACTIVE;
8869
8870   /* if new animation frame was drawn, correct crumbled sand border */
8871   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8872     DrawLevelFieldCrumbledSand(x, y);
8873 }
8874
8875 static int getSpecialActionElement(int element, int number, int base_element)
8876 {
8877   return (element != EL_EMPTY ? element :
8878           number != -1 ? base_element + number - 1 :
8879           EL_EMPTY);
8880 }
8881
8882 static int getModifiedActionNumber(int value_old, int operator, int operand,
8883                                    int value_min, int value_max)
8884 {
8885   int value_new = (operator == CA_MODE_SET      ? operand :
8886                    operator == CA_MODE_ADD      ? value_old + operand :
8887                    operator == CA_MODE_SUBTRACT ? value_old - operand :
8888                    operator == CA_MODE_MULTIPLY ? value_old * operand :
8889                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
8890                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
8891                    value_old);
8892
8893   return (value_new < value_min ? value_min :
8894           value_new > value_max ? value_max :
8895           value_new);
8896 }
8897
8898 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8899 {
8900   struct ElementInfo *ei = &element_info[element];
8901   struct ElementChangeInfo *change = &ei->change_page[page];
8902   int target_element = change->target_element;
8903   int action_type = change->action_type;
8904   int action_mode = change->action_mode;
8905   int action_arg = change->action_arg;
8906   int i;
8907
8908   if (!change->has_action)
8909     return;
8910
8911   /* ---------- determine action paramater values -------------------------- */
8912
8913   int level_time_value =
8914     (level.time > 0 ? TimeLeft :
8915      TimePlayed);
8916
8917   int action_arg_element =
8918     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
8919      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8920      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
8921      EL_EMPTY);
8922
8923   int action_arg_direction =
8924     (action_arg >= CA_ARG_DIRECTION_LEFT &&
8925      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8926      action_arg == CA_ARG_DIRECTION_TRIGGER ?
8927      change->actual_trigger_side :
8928      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8929      MV_DIR_OPPOSITE(change->actual_trigger_side) :
8930      MV_NONE);
8931
8932   int action_arg_number_min =
8933     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8934      CA_ARG_MIN);
8935
8936   int action_arg_number_max =
8937     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8938      action_type == CA_SET_LEVEL_GEMS ? 999 :
8939      action_type == CA_SET_LEVEL_TIME ? 9999 :
8940      action_type == CA_SET_LEVEL_SCORE ? 99999 :
8941      action_type == CA_SET_CE_VALUE ? 9999 :
8942      action_type == CA_SET_CE_SCORE ? 9999 :
8943      CA_ARG_MAX);
8944
8945   int action_arg_number_reset =
8946     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8947      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8948      action_type == CA_SET_LEVEL_TIME ? level.time :
8949      action_type == CA_SET_LEVEL_SCORE ? 0 :
8950 #if USE_NEW_CUSTOM_VALUE
8951      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8952 #else
8953      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8954 #endif
8955      action_type == CA_SET_CE_SCORE ? 0 :
8956      0);
8957
8958   int action_arg_number =
8959     (action_arg <= CA_ARG_MAX ? action_arg :
8960      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8961      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8962      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8963      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8964      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8965      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8966 #if USE_NEW_CUSTOM_VALUE
8967      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8968 #else
8969      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8970 #endif
8971      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8972      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8973      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8974      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8975      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8976      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8977      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8978      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8979      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8980      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
8981      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8982      -1);
8983
8984   int action_arg_number_old =
8985     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8986      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8987      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8988      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8989      action_type == CA_SET_CE_SCORE ? ei->collect_score :
8990      0);
8991
8992   int action_arg_number_new =
8993     getModifiedActionNumber(action_arg_number_old,
8994                             action_mode, action_arg_number,
8995                             action_arg_number_min, action_arg_number_max);
8996
8997   int trigger_player_bits =
8998     (change->actual_trigger_player >= EL_PLAYER_1 &&
8999      change->actual_trigger_player <= EL_PLAYER_4 ?
9000      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9001      PLAYER_BITS_ANY);
9002
9003   int action_arg_player_bits =
9004     (action_arg >= CA_ARG_PLAYER_1 &&
9005      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9006      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9007      PLAYER_BITS_ANY);
9008
9009   /* ---------- execute action  -------------------------------------------- */
9010
9011   switch (action_type)
9012   {
9013     case CA_NO_ACTION:
9014     {
9015       return;
9016     }
9017
9018     /* ---------- level actions  ------------------------------------------- */
9019
9020     case CA_RESTART_LEVEL:
9021     {
9022       game.restart_level = TRUE;
9023
9024       break;
9025     }
9026
9027     case CA_SHOW_ENVELOPE:
9028     {
9029       int element = getSpecialActionElement(action_arg_element,
9030                                             action_arg_number, EL_ENVELOPE_1);
9031
9032       if (IS_ENVELOPE(element))
9033         local_player->show_envelope = element;
9034
9035       break;
9036     }
9037
9038     case CA_SET_LEVEL_TIME:
9039     {
9040       if (level.time > 0)       /* only modify limited time value */
9041       {
9042         TimeLeft = action_arg_number_new;
9043
9044         DrawGameValue_Time(TimeLeft);
9045
9046         if (!TimeLeft && setup.time_limit)
9047           for (i = 0; i < MAX_PLAYERS; i++)
9048             KillPlayer(&stored_player[i]);
9049       }
9050
9051       break;
9052     }
9053
9054     case CA_SET_LEVEL_SCORE:
9055     {
9056       local_player->score = action_arg_number_new;
9057
9058       DrawGameValue_Score(local_player->score);
9059
9060       break;
9061     }
9062
9063     case CA_SET_LEVEL_GEMS:
9064     {
9065       local_player->gems_still_needed = action_arg_number_new;
9066
9067       DrawGameValue_Emeralds(local_player->gems_still_needed);
9068
9069       break;
9070     }
9071
9072 #if !USE_PLAYER_GRAVITY
9073     case CA_SET_LEVEL_GRAVITY:
9074     {
9075       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
9076                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
9077                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9078                       game.gravity);
9079       break;
9080     }
9081 #endif
9082
9083     case CA_SET_LEVEL_WIND:
9084     {
9085       game.wind_direction = action_arg_direction;
9086
9087       break;
9088     }
9089
9090     /* ---------- player actions  ------------------------------------------ */
9091
9092     case CA_MOVE_PLAYER:
9093     {
9094       /* automatically move to the next field in specified direction */
9095       for (i = 0; i < MAX_PLAYERS; i++)
9096         if (trigger_player_bits & (1 << i))
9097           stored_player[i].programmed_action = action_arg_direction;
9098
9099       break;
9100     }
9101
9102     case CA_EXIT_PLAYER:
9103     {
9104       for (i = 0; i < MAX_PLAYERS; i++)
9105         if (action_arg_player_bits & (1 << i))
9106           PlayerWins(&stored_player[i]);
9107
9108       break;
9109     }
9110
9111     case CA_KILL_PLAYER:
9112     {
9113       for (i = 0; i < MAX_PLAYERS; i++)
9114         if (action_arg_player_bits & (1 << i))
9115           KillPlayer(&stored_player[i]);
9116
9117       break;
9118     }
9119
9120     case CA_SET_PLAYER_KEYS:
9121     {
9122       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9123       int element = getSpecialActionElement(action_arg_element,
9124                                             action_arg_number, EL_KEY_1);
9125
9126       if (IS_KEY(element))
9127       {
9128         for (i = 0; i < MAX_PLAYERS; i++)
9129         {
9130           if (trigger_player_bits & (1 << i))
9131           {
9132             stored_player[i].key[KEY_NR(element)] = key_state;
9133
9134             DrawGameDoorValues();
9135           }
9136         }
9137       }
9138
9139       break;
9140     }
9141
9142     case CA_SET_PLAYER_SPEED:
9143     {
9144       for (i = 0; i < MAX_PLAYERS; i++)
9145       {
9146         if (trigger_player_bits & (1 << i))
9147         {
9148           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9149
9150           if (action_arg == CA_ARG_SPEED_FASTER &&
9151               stored_player[i].cannot_move)
9152           {
9153             action_arg_number = STEPSIZE_VERY_SLOW;
9154           }
9155           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9156                    action_arg == CA_ARG_SPEED_FASTER)
9157           {
9158             action_arg_number = 2;
9159             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9160                            CA_MODE_MULTIPLY);
9161           }
9162           else if (action_arg == CA_ARG_NUMBER_RESET)
9163           {
9164             action_arg_number = level.initial_player_stepsize[i];
9165           }
9166
9167           move_stepsize =
9168             getModifiedActionNumber(move_stepsize,
9169                                     action_mode,
9170                                     action_arg_number,
9171                                     action_arg_number_min,
9172                                     action_arg_number_max);
9173
9174           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9175         }
9176       }
9177
9178       break;
9179     }
9180
9181     case CA_SET_PLAYER_SHIELD:
9182     {
9183       for (i = 0; i < MAX_PLAYERS; i++)
9184       {
9185         if (trigger_player_bits & (1 << i))
9186         {
9187           if (action_arg == CA_ARG_SHIELD_OFF)
9188           {
9189             stored_player[i].shield_normal_time_left = 0;
9190             stored_player[i].shield_deadly_time_left = 0;
9191           }
9192           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9193           {
9194             stored_player[i].shield_normal_time_left = 999999;
9195           }
9196           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9197           {
9198             stored_player[i].shield_normal_time_left = 999999;
9199             stored_player[i].shield_deadly_time_left = 999999;
9200           }
9201         }
9202       }
9203
9204       break;
9205     }
9206
9207 #if USE_PLAYER_GRAVITY
9208     case CA_SET_PLAYER_GRAVITY:
9209     {
9210       for (i = 0; i < MAX_PLAYERS; i++)
9211       {
9212         if (trigger_player_bits & (1 << i))
9213         {
9214           stored_player[i].gravity =
9215             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9216              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9217              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9218              stored_player[i].gravity);
9219         }
9220       }
9221
9222       break;
9223     }
9224 #endif
9225
9226     case CA_SET_PLAYER_ARTWORK:
9227     {
9228       for (i = 0; i < MAX_PLAYERS; i++)
9229       {
9230         if (trigger_player_bits & (1 << i))
9231         {
9232           int artwork_element = action_arg_element;
9233
9234           if (action_arg == CA_ARG_ELEMENT_RESET)
9235             artwork_element =
9236               (level.use_artwork_element[i] ? level.artwork_element[i] :
9237                stored_player[i].element_nr);
9238
9239 #if USE_GFX_RESET_PLAYER_ARTWORK
9240           if (stored_player[i].artwork_element != artwork_element)
9241             stored_player[i].Frame = 0;
9242 #endif
9243
9244           stored_player[i].artwork_element = artwork_element;
9245
9246           SetPlayerWaiting(&stored_player[i], FALSE);
9247
9248           /* set number of special actions for bored and sleeping animation */
9249           stored_player[i].num_special_action_bored =
9250             get_num_special_action(artwork_element,
9251                                    ACTION_BORING_1, ACTION_BORING_LAST);
9252           stored_player[i].num_special_action_sleeping =
9253             get_num_special_action(artwork_element,
9254                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9255         }
9256       }
9257
9258       break;
9259     }
9260
9261     /* ---------- CE actions  ---------------------------------------------- */
9262
9263     case CA_SET_CE_VALUE:
9264     {
9265 #if USE_NEW_CUSTOM_VALUE
9266       int last_ce_value = CustomValue[x][y];
9267
9268       CustomValue[x][y] = action_arg_number_new;
9269
9270       if (CustomValue[x][y] != last_ce_value)
9271       {
9272         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9273         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9274
9275         if (CustomValue[x][y] == 0)
9276         {
9277           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9278           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9279         }
9280       }
9281 #endif
9282
9283       break;
9284     }
9285
9286     case CA_SET_CE_SCORE:
9287     {
9288 #if USE_NEW_CUSTOM_VALUE
9289       int last_ce_score = ei->collect_score;
9290
9291       ei->collect_score = action_arg_number_new;
9292
9293       if (ei->collect_score != last_ce_score)
9294       {
9295         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9296         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9297
9298         if (ei->collect_score == 0)
9299         {
9300           int xx, yy;
9301
9302           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9303           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9304
9305           /*
9306             This is a very special case that seems to be a mixture between
9307             CheckElementChange() and CheckTriggeredElementChange(): while
9308             the first one only affects single elements that are triggered
9309             directly, the second one affects multiple elements in the playfield
9310             that are triggered indirectly by another element. This is a third
9311             case: Changing the CE score always affects multiple identical CEs,
9312             so every affected CE must be checked, not only the single CE for
9313             which the CE score was changed in the first place (as every instance
9314             of that CE shares the same CE score, and therefore also can change)!
9315           */
9316           SCAN_PLAYFIELD(xx, yy)
9317           {
9318             if (Feld[xx][yy] == element)
9319               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9320                                  CE_SCORE_GETS_ZERO);
9321           }
9322         }
9323       }
9324 #endif
9325
9326       break;
9327     }
9328
9329     /* ---------- engine actions  ------------------------------------------ */
9330
9331     case CA_SET_ENGINE_SCAN_MODE:
9332     {
9333       InitPlayfieldScanMode(action_arg);
9334
9335       break;
9336     }
9337
9338     default:
9339       break;
9340   }
9341 }
9342
9343 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9344 {
9345   int old_element = Feld[x][y];
9346   int new_element = GetElementFromGroupElement(element);
9347   int previous_move_direction = MovDir[x][y];
9348 #if USE_NEW_CUSTOM_VALUE
9349   int last_ce_value = CustomValue[x][y];
9350 #endif
9351   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9352   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9353   boolean add_player_onto_element = (new_element_is_player &&
9354 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9355                                      /* this breaks SnakeBite when a snake is
9356                                         halfway through a door that closes */
9357                                      /* NOW FIXED AT LEVEL INIT IN files.c */
9358                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9359 #endif
9360                                      IS_WALKABLE(old_element));
9361
9362 #if 0
9363   /* check if element under the player changes from accessible to unaccessible
9364      (needed for special case of dropping element which then changes) */
9365   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9366       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9367   {
9368     Bang(x, y);
9369
9370     return;
9371   }
9372 #endif
9373
9374   if (!add_player_onto_element)
9375   {
9376     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9377       RemoveMovingField(x, y);
9378     else
9379       RemoveField(x, y);
9380
9381     Feld[x][y] = new_element;
9382
9383 #if !USE_GFX_RESET_GFX_ANIMATION
9384     ResetGfxAnimation(x, y);
9385     ResetRandomAnimationValue(x, y);
9386 #endif
9387
9388     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9389       MovDir[x][y] = previous_move_direction;
9390
9391 #if USE_NEW_CUSTOM_VALUE
9392     if (element_info[new_element].use_last_ce_value)
9393       CustomValue[x][y] = last_ce_value;
9394 #endif
9395
9396     InitField_WithBug1(x, y, FALSE);
9397
9398     new_element = Feld[x][y];   /* element may have changed */
9399
9400 #if USE_GFX_RESET_GFX_ANIMATION
9401     ResetGfxAnimation(x, y);
9402     ResetRandomAnimationValue(x, y);
9403 #endif
9404
9405     DrawLevelField(x, y);
9406
9407     if (GFX_CRUMBLED(new_element))
9408       DrawLevelFieldCrumbledSandNeighbours(x, y);
9409   }
9410
9411 #if 1
9412   /* check if element under the player changes from accessible to unaccessible
9413      (needed for special case of dropping element which then changes) */
9414   /* (must be checked after creating new element for walkable group elements) */
9415 #if USE_FIX_KILLED_BY_NON_WALKABLE
9416   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9417       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9418   {
9419     Bang(x, y);
9420
9421     return;
9422   }
9423 #else
9424   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9425       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9426   {
9427     Bang(x, y);
9428
9429     return;
9430   }
9431 #endif
9432 #endif
9433
9434   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9435   if (new_element_is_player)
9436     RelocatePlayer(x, y, new_element);
9437
9438   if (is_change)
9439     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9440
9441   TestIfBadThingTouchesPlayer(x, y);
9442   TestIfPlayerTouchesCustomElement(x, y);
9443   TestIfElementTouchesCustomElement(x, y);
9444 }
9445
9446 static void CreateField(int x, int y, int element)
9447 {
9448   CreateFieldExt(x, y, element, FALSE);
9449 }
9450
9451 static void CreateElementFromChange(int x, int y, int element)
9452 {
9453   element = GET_VALID_RUNTIME_ELEMENT(element);
9454
9455 #if USE_STOP_CHANGED_ELEMENTS
9456   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9457   {
9458     int old_element = Feld[x][y];
9459
9460     /* prevent changed element from moving in same engine frame
9461        unless both old and new element can either fall or move */
9462     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9463         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9464       Stop[x][y] = TRUE;
9465   }
9466 #endif
9467
9468   CreateFieldExt(x, y, element, TRUE);
9469 }
9470
9471 static boolean ChangeElement(int x, int y, int element, int page)
9472 {
9473   struct ElementInfo *ei = &element_info[element];
9474   struct ElementChangeInfo *change = &ei->change_page[page];
9475   int ce_value = CustomValue[x][y];
9476   int ce_score = ei->collect_score;
9477   int target_element;
9478   int old_element = Feld[x][y];
9479
9480   /* always use default change event to prevent running into a loop */
9481   if (ChangeEvent[x][y] == -1)
9482     ChangeEvent[x][y] = CE_DELAY;
9483
9484   if (ChangeEvent[x][y] == CE_DELAY)
9485   {
9486     /* reset actual trigger element, trigger player and action element */
9487     change->actual_trigger_element = EL_EMPTY;
9488     change->actual_trigger_player = EL_PLAYER_1;
9489     change->actual_trigger_side = CH_SIDE_NONE;
9490     change->actual_trigger_ce_value = 0;
9491     change->actual_trigger_ce_score = 0;
9492   }
9493
9494   /* do not change elements more than a specified maximum number of changes */
9495   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
9496     return FALSE;
9497
9498   ChangeCount[x][y]++;          /* count number of changes in the same frame */
9499
9500   if (change->explode)
9501   {
9502     Bang(x, y);
9503
9504     return TRUE;
9505   }
9506
9507   if (change->use_target_content)
9508   {
9509     boolean complete_replace = TRUE;
9510     boolean can_replace[3][3];
9511     int xx, yy;
9512
9513     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9514     {
9515       boolean is_empty;
9516       boolean is_walkable;
9517       boolean is_diggable;
9518       boolean is_collectible;
9519       boolean is_removable;
9520       boolean is_destructible;
9521       int ex = x + xx - 1;
9522       int ey = y + yy - 1;
9523       int content_element = change->target_content.e[xx][yy];
9524       int e;
9525
9526       can_replace[xx][yy] = TRUE;
9527
9528       if (ex == x && ey == y)   /* do not check changing element itself */
9529         continue;
9530
9531       if (content_element == EL_EMPTY_SPACE)
9532       {
9533         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
9534
9535         continue;
9536       }
9537
9538       if (!IN_LEV_FIELD(ex, ey))
9539       {
9540         can_replace[xx][yy] = FALSE;
9541         complete_replace = FALSE;
9542
9543         continue;
9544       }
9545
9546       e = Feld[ex][ey];
9547
9548       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9549         e = MovingOrBlocked2Element(ex, ey);
9550
9551       is_empty = (IS_FREE(ex, ey) ||
9552                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
9553
9554       is_walkable     = (is_empty || IS_WALKABLE(e));
9555       is_diggable     = (is_empty || IS_DIGGABLE(e));
9556       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
9557       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
9558       is_removable    = (is_diggable || is_collectible);
9559
9560       can_replace[xx][yy] =
9561         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
9562           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
9563           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
9564           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
9565           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
9566           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
9567          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
9568
9569       if (!can_replace[xx][yy])
9570         complete_replace = FALSE;
9571     }
9572
9573     if (!change->only_if_complete || complete_replace)
9574     {
9575       boolean something_has_changed = FALSE;
9576
9577       if (change->only_if_complete && change->use_random_replace &&
9578           RND(100) < change->random_percentage)
9579         return FALSE;
9580
9581       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9582       {
9583         int ex = x + xx - 1;
9584         int ey = y + yy - 1;
9585         int content_element;
9586
9587         if (can_replace[xx][yy] && (!change->use_random_replace ||
9588                                     RND(100) < change->random_percentage))
9589         {
9590           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9591             RemoveMovingField(ex, ey);
9592
9593           ChangeEvent[ex][ey] = ChangeEvent[x][y];
9594
9595           content_element = change->target_content.e[xx][yy];
9596           target_element = GET_TARGET_ELEMENT(element, content_element, change,
9597                                               ce_value, ce_score);
9598
9599           CreateElementFromChange(ex, ey, target_element);
9600
9601           something_has_changed = TRUE;
9602
9603           /* for symmetry reasons, freeze newly created border elements */
9604           if (ex != x || ey != y)
9605             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
9606         }
9607       }
9608
9609       if (something_has_changed)
9610       {
9611         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9612         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9613       }
9614     }
9615   }
9616   else
9617   {
9618     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9619                                         ce_value, ce_score);
9620
9621     if (element == EL_DIAGONAL_GROWING ||
9622         element == EL_DIAGONAL_SHRINKING)
9623     {
9624       target_element = Store[x][y];
9625
9626       Store[x][y] = EL_EMPTY;
9627     }
9628
9629     CreateElementFromChange(x, y, target_element);
9630
9631     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9632     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9633   }
9634
9635   /* this uses direct change before indirect change */
9636   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9637
9638   return TRUE;
9639 }
9640
9641 #if USE_NEW_DELAYED_ACTION
9642
9643 static void HandleElementChange(int x, int y, int page)
9644 {
9645   int element = MovingOrBlocked2Element(x, y);
9646   struct ElementInfo *ei = &element_info[element];
9647   struct ElementChangeInfo *change = &ei->change_page[page];
9648
9649 #ifdef DEBUG
9650   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
9651       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
9652   {
9653     printf("\n\n");
9654     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9655            x, y, element, element_info[element].token_name);
9656     printf("HandleElementChange(): This should never happen!\n");
9657     printf("\n\n");
9658   }
9659 #endif
9660
9661   /* this can happen with classic bombs on walkable, changing elements */
9662   if (!CAN_CHANGE_OR_HAS_ACTION(element))
9663   {
9664 #if 0
9665     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9666       ChangeDelay[x][y] = 0;
9667 #endif
9668
9669     return;
9670   }
9671
9672   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9673   {
9674     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9675
9676     if (change->can_change)
9677     {
9678 #if 1
9679       /* !!! not clear why graphic animation should be reset at all here !!! */
9680       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
9681 #if USE_GFX_RESET_WHEN_NOT_MOVING
9682       /* when a custom element is about to change (for example by change delay),
9683          do not reset graphic animation when the custom element is moving */
9684       if (!IS_MOVING(x, y))
9685 #endif
9686       {
9687         ResetGfxAnimation(x, y);
9688         ResetRandomAnimationValue(x, y);
9689       }
9690 #endif
9691
9692       if (change->pre_change_function)
9693         change->pre_change_function(x, y);
9694     }
9695   }
9696
9697   ChangeDelay[x][y]--;
9698
9699   if (ChangeDelay[x][y] != 0)           /* continue element change */
9700   {
9701     if (change->can_change)
9702     {
9703       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9704
9705       if (IS_ANIMATED(graphic))
9706         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9707
9708       if (change->change_function)
9709         change->change_function(x, y);
9710     }
9711   }
9712   else                                  /* finish element change */
9713   {
9714     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9715     {
9716       page = ChangePage[x][y];
9717       ChangePage[x][y] = -1;
9718
9719       change = &ei->change_page[page];
9720     }
9721
9722     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9723     {
9724       ChangeDelay[x][y] = 1;            /* try change after next move step */
9725       ChangePage[x][y] = page;          /* remember page to use for change */
9726
9727       return;
9728     }
9729
9730     if (change->can_change)
9731     {
9732       if (ChangeElement(x, y, element, page))
9733       {
9734         if (change->post_change_function)
9735           change->post_change_function(x, y);
9736       }
9737     }
9738
9739     if (change->has_action)
9740       ExecuteCustomElementAction(x, y, element, page);
9741   }
9742 }
9743
9744 #else
9745
9746 static void HandleElementChange(int x, int y, int page)
9747 {
9748   int element = MovingOrBlocked2Element(x, y);
9749   struct ElementInfo *ei = &element_info[element];
9750   struct ElementChangeInfo *change = &ei->change_page[page];
9751
9752 #ifdef DEBUG
9753   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
9754   {
9755     printf("\n\n");
9756     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9757            x, y, element, element_info[element].token_name);
9758     printf("HandleElementChange(): This should never happen!\n");
9759     printf("\n\n");
9760   }
9761 #endif
9762
9763   /* this can happen with classic bombs on walkable, changing elements */
9764   if (!CAN_CHANGE(element))
9765   {
9766 #if 0
9767     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9768       ChangeDelay[x][y] = 0;
9769 #endif
9770
9771     return;
9772   }
9773
9774   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9775   {
9776     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9777
9778     ResetGfxAnimation(x, y);
9779     ResetRandomAnimationValue(x, y);
9780
9781     if (change->pre_change_function)
9782       change->pre_change_function(x, y);
9783   }
9784
9785   ChangeDelay[x][y]--;
9786
9787   if (ChangeDelay[x][y] != 0)           /* continue element change */
9788   {
9789     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9790
9791     if (IS_ANIMATED(graphic))
9792       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9793
9794     if (change->change_function)
9795       change->change_function(x, y);
9796   }
9797   else                                  /* finish element change */
9798   {
9799     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9800     {
9801       page = ChangePage[x][y];
9802       ChangePage[x][y] = -1;
9803
9804       change = &ei->change_page[page];
9805     }
9806
9807     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9808     {
9809       ChangeDelay[x][y] = 1;            /* try change after next move step */
9810       ChangePage[x][y] = page;          /* remember page to use for change */
9811
9812       return;
9813     }
9814
9815     if (ChangeElement(x, y, element, page))
9816     {
9817       if (change->post_change_function)
9818         change->post_change_function(x, y);
9819     }
9820   }
9821 }
9822
9823 #endif
9824
9825 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
9826                                               int trigger_element,
9827                                               int trigger_event,
9828                                               int trigger_player,
9829                                               int trigger_side,
9830                                               int trigger_page)
9831 {
9832   boolean change_done_any = FALSE;
9833   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9834   int i;
9835
9836   if (!(trigger_events[trigger_element][trigger_event]))
9837     return FALSE;
9838
9839 #if 0
9840   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9841          trigger_event, recursion_loop_depth, recursion_loop_detected,
9842          recursion_loop_element, EL_NAME(recursion_loop_element));
9843 #endif
9844
9845   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9846
9847   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9848   {
9849     int element = EL_CUSTOM_START + i;
9850     boolean change_done = FALSE;
9851     int p;
9852
9853     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9854         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9855       continue;
9856
9857     for (p = 0; p < element_info[element].num_change_pages; p++)
9858     {
9859       struct ElementChangeInfo *change = &element_info[element].change_page[p];
9860
9861       if (change->can_change_or_has_action &&
9862           change->has_event[trigger_event] &&
9863           change->trigger_side & trigger_side &&
9864           change->trigger_player & trigger_player &&
9865           change->trigger_page & trigger_page_bits &&
9866           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9867       {
9868         change->actual_trigger_element = trigger_element;
9869         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9870         change->actual_trigger_side = trigger_side;
9871         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9872         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9873
9874         if ((change->can_change && !change_done) || change->has_action)
9875         {
9876           int x, y;
9877
9878           SCAN_PLAYFIELD(x, y)
9879           {
9880             if (Feld[x][y] == element)
9881             {
9882               if (change->can_change && !change_done)
9883               {
9884                 ChangeDelay[x][y] = 1;
9885                 ChangeEvent[x][y] = trigger_event;
9886
9887                 HandleElementChange(x, y, p);
9888               }
9889 #if USE_NEW_DELAYED_ACTION
9890               else if (change->has_action)
9891               {
9892                 ExecuteCustomElementAction(x, y, element, p);
9893                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9894               }
9895 #else
9896               if (change->has_action)
9897               {
9898                 ExecuteCustomElementAction(x, y, element, p);
9899                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9900               }
9901 #endif
9902             }
9903           }
9904
9905           if (change->can_change)
9906           {
9907             change_done = TRUE;
9908             change_done_any = TRUE;
9909           }
9910         }
9911       }
9912     }
9913   }
9914
9915   RECURSION_LOOP_DETECTION_END();
9916
9917   return change_done_any;
9918 }
9919
9920 static boolean CheckElementChangeExt(int x, int y,
9921                                      int element,
9922                                      int trigger_element,
9923                                      int trigger_event,
9924                                      int trigger_player,
9925                                      int trigger_side)
9926 {
9927   boolean change_done = FALSE;
9928   int p;
9929
9930   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9931       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9932     return FALSE;
9933
9934   if (Feld[x][y] == EL_BLOCKED)
9935   {
9936     Blocked2Moving(x, y, &x, &y);
9937     element = Feld[x][y];
9938   }
9939
9940 #if 0
9941   /* check if element has already changed */
9942   if (Feld[x][y] != element)
9943     return FALSE;
9944 #else
9945   /* check if element has already changed or is about to change after moving */
9946   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9947        Feld[x][y] != element) ||
9948
9949       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9950        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9951         ChangePage[x][y] != -1)))
9952     return FALSE;
9953 #endif
9954
9955 #if 0
9956   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9957          trigger_event, recursion_loop_depth, recursion_loop_detected,
9958          recursion_loop_element, EL_NAME(recursion_loop_element));
9959 #endif
9960
9961   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9962
9963   for (p = 0; p < element_info[element].num_change_pages; p++)
9964   {
9965     struct ElementChangeInfo *change = &element_info[element].change_page[p];
9966
9967     /* check trigger element for all events where the element that is checked
9968        for changing interacts with a directly adjacent element -- this is
9969        different to element changes that affect other elements to change on the
9970        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
9971     boolean check_trigger_element =
9972       (trigger_event == CE_TOUCHING_X ||
9973        trigger_event == CE_HITTING_X ||
9974        trigger_event == CE_HIT_BY_X ||
9975 #if 1
9976        /* this one was forgotten until 3.2.3 */
9977        trigger_event == CE_DIGGING_X);
9978 #endif
9979
9980     if (change->can_change_or_has_action &&
9981         change->has_event[trigger_event] &&
9982         change->trigger_side & trigger_side &&
9983         change->trigger_player & trigger_player &&
9984         (!check_trigger_element ||
9985          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9986     {
9987       change->actual_trigger_element = trigger_element;
9988       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9989       change->actual_trigger_side = trigger_side;
9990       change->actual_trigger_ce_value = CustomValue[x][y];
9991       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9992
9993       /* special case: trigger element not at (x,y) position for some events */
9994       if (check_trigger_element)
9995       {
9996         static struct
9997         {
9998           int dx, dy;
9999         } move_xy[] =
10000           {
10001             {  0,  0 },
10002             { -1,  0 },
10003             { +1,  0 },
10004             {  0,  0 },
10005             {  0, -1 },
10006             {  0,  0 }, { 0, 0 }, { 0, 0 },
10007             {  0, +1 }
10008           };
10009
10010         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10011         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10012
10013         change->actual_trigger_ce_value = CustomValue[xx][yy];
10014         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10015       }
10016
10017       if (change->can_change && !change_done)
10018       {
10019         ChangeDelay[x][y] = 1;
10020         ChangeEvent[x][y] = trigger_event;
10021
10022         HandleElementChange(x, y, p);
10023
10024         change_done = TRUE;
10025       }
10026 #if USE_NEW_DELAYED_ACTION
10027       else if (change->has_action)
10028       {
10029         ExecuteCustomElementAction(x, y, element, p);
10030         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10031       }
10032 #else
10033       if (change->has_action)
10034       {
10035         ExecuteCustomElementAction(x, y, element, p);
10036         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10037       }
10038 #endif
10039     }
10040   }
10041
10042   RECURSION_LOOP_DETECTION_END();
10043
10044   return change_done;
10045 }
10046
10047 static void PlayPlayerSound(struct PlayerInfo *player)
10048 {
10049   int jx = player->jx, jy = player->jy;
10050   int sound_element = player->artwork_element;
10051   int last_action = player->last_action_waiting;
10052   int action = player->action_waiting;
10053
10054   if (player->is_waiting)
10055   {
10056     if (action != last_action)
10057       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10058     else
10059       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10060   }
10061   else
10062   {
10063     if (action != last_action)
10064       StopSound(element_info[sound_element].sound[last_action]);
10065
10066     if (last_action == ACTION_SLEEPING)
10067       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10068   }
10069 }
10070
10071 static void PlayAllPlayersSound()
10072 {
10073   int i;
10074
10075   for (i = 0; i < MAX_PLAYERS; i++)
10076     if (stored_player[i].active)
10077       PlayPlayerSound(&stored_player[i]);
10078 }
10079
10080 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10081 {
10082   boolean last_waiting = player->is_waiting;
10083   int move_dir = player->MovDir;
10084
10085   player->dir_waiting = move_dir;
10086   player->last_action_waiting = player->action_waiting;
10087
10088   if (is_waiting)
10089   {
10090     if (!last_waiting)          /* not waiting -> waiting */
10091     {
10092       player->is_waiting = TRUE;
10093
10094       player->frame_counter_bored =
10095         FrameCounter +
10096         game.player_boring_delay_fixed +
10097         GetSimpleRandom(game.player_boring_delay_random);
10098       player->frame_counter_sleeping =
10099         FrameCounter +
10100         game.player_sleeping_delay_fixed +
10101         GetSimpleRandom(game.player_sleeping_delay_random);
10102
10103       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10104     }
10105
10106     if (game.player_sleeping_delay_fixed +
10107         game.player_sleeping_delay_random > 0 &&
10108         player->anim_delay_counter == 0 &&
10109         player->post_delay_counter == 0 &&
10110         FrameCounter >= player->frame_counter_sleeping)
10111       player->is_sleeping = TRUE;
10112     else if (game.player_boring_delay_fixed +
10113              game.player_boring_delay_random > 0 &&
10114              FrameCounter >= player->frame_counter_bored)
10115       player->is_bored = TRUE;
10116
10117     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10118                               player->is_bored ? ACTION_BORING :
10119                               ACTION_WAITING);
10120
10121     if (player->is_sleeping && player->use_murphy)
10122     {
10123       /* special case for sleeping Murphy when leaning against non-free tile */
10124
10125       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10126           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10127            !IS_MOVING(player->jx - 1, player->jy)))
10128         move_dir = MV_LEFT;
10129       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10130                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10131                 !IS_MOVING(player->jx + 1, player->jy)))
10132         move_dir = MV_RIGHT;
10133       else
10134         player->is_sleeping = FALSE;
10135
10136       player->dir_waiting = move_dir;
10137     }
10138
10139     if (player->is_sleeping)
10140     {
10141       if (player->num_special_action_sleeping > 0)
10142       {
10143         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10144         {
10145           int last_special_action = player->special_action_sleeping;
10146           int num_special_action = player->num_special_action_sleeping;
10147           int special_action =
10148             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10149              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10150              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10151              last_special_action + 1 : ACTION_SLEEPING);
10152           int special_graphic =
10153             el_act_dir2img(player->artwork_element, special_action, move_dir);
10154
10155           player->anim_delay_counter =
10156             graphic_info[special_graphic].anim_delay_fixed +
10157             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10158           player->post_delay_counter =
10159             graphic_info[special_graphic].post_delay_fixed +
10160             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10161
10162           player->special_action_sleeping = special_action;
10163         }
10164
10165         if (player->anim_delay_counter > 0)
10166         {
10167           player->action_waiting = player->special_action_sleeping;
10168           player->anim_delay_counter--;
10169         }
10170         else if (player->post_delay_counter > 0)
10171         {
10172           player->post_delay_counter--;
10173         }
10174       }
10175     }
10176     else if (player->is_bored)
10177     {
10178       if (player->num_special_action_bored > 0)
10179       {
10180         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10181         {
10182           int special_action =
10183             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10184           int special_graphic =
10185             el_act_dir2img(player->artwork_element, special_action, move_dir);
10186
10187           player->anim_delay_counter =
10188             graphic_info[special_graphic].anim_delay_fixed +
10189             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10190           player->post_delay_counter =
10191             graphic_info[special_graphic].post_delay_fixed +
10192             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10193
10194           player->special_action_bored = special_action;
10195         }
10196
10197         if (player->anim_delay_counter > 0)
10198         {
10199           player->action_waiting = player->special_action_bored;
10200           player->anim_delay_counter--;
10201         }
10202         else if (player->post_delay_counter > 0)
10203         {
10204           player->post_delay_counter--;
10205         }
10206       }
10207     }
10208   }
10209   else if (last_waiting)        /* waiting -> not waiting */
10210   {
10211     player->is_waiting = FALSE;
10212     player->is_bored = FALSE;
10213     player->is_sleeping = FALSE;
10214
10215     player->frame_counter_bored = -1;
10216     player->frame_counter_sleeping = -1;
10217
10218     player->anim_delay_counter = 0;
10219     player->post_delay_counter = 0;
10220
10221     player->dir_waiting = player->MovDir;
10222     player->action_waiting = ACTION_DEFAULT;
10223
10224     player->special_action_bored = ACTION_DEFAULT;
10225     player->special_action_sleeping = ACTION_DEFAULT;
10226   }
10227 }
10228
10229 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10230 {
10231   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10232   int left      = player_action & JOY_LEFT;
10233   int right     = player_action & JOY_RIGHT;
10234   int up        = player_action & JOY_UP;
10235   int down      = player_action & JOY_DOWN;
10236   int button1   = player_action & JOY_BUTTON_1;
10237   int button2   = player_action & JOY_BUTTON_2;
10238   int dx        = (left ? -1 : right ? 1 : 0);
10239   int dy        = (up   ? -1 : down  ? 1 : 0);
10240
10241   if (!player->active || tape.pausing)
10242     return 0;
10243
10244   if (player_action)
10245   {
10246     if (button1)
10247       snapped = SnapField(player, dx, dy);
10248     else
10249     {
10250       if (button2)
10251         dropped = DropElement(player);
10252
10253       moved = MovePlayer(player, dx, dy);
10254     }
10255
10256     if (tape.single_step && tape.recording && !tape.pausing)
10257     {
10258       if (button1 || (dropped && !moved))
10259       {
10260         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10261         SnapField(player, 0, 0);                /* stop snapping */
10262       }
10263     }
10264
10265     SetPlayerWaiting(player, FALSE);
10266
10267     return player_action;
10268   }
10269   else
10270   {
10271     /* no actions for this player (no input at player's configured device) */
10272
10273     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10274     SnapField(player, 0, 0);
10275     CheckGravityMovementWhenNotMoving(player);
10276
10277     if (player->MovPos == 0)
10278       SetPlayerWaiting(player, TRUE);
10279
10280     if (player->MovPos == 0)    /* needed for tape.playing */
10281       player->is_moving = FALSE;
10282
10283     player->is_dropping = FALSE;
10284     player->is_dropping_pressed = FALSE;
10285     player->drop_pressed_delay = 0;
10286
10287     return 0;
10288   }
10289 }
10290
10291 static void CheckLevelTime()
10292 {
10293   int i;
10294
10295   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10296   {
10297     if (level.native_em_level->lev->home == 0)  /* all players at home */
10298     {
10299       PlayerWins(local_player);
10300
10301       AllPlayersGone = TRUE;
10302
10303       level.native_em_level->lev->home = -1;
10304     }
10305
10306     if (level.native_em_level->ply[0]->alive == 0 &&
10307         level.native_em_level->ply[1]->alive == 0 &&
10308         level.native_em_level->ply[2]->alive == 0 &&
10309         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10310       AllPlayersGone = TRUE;
10311   }
10312
10313   if (TimeFrames >= FRAMES_PER_SECOND)
10314   {
10315     TimeFrames = 0;
10316     TapeTime++;
10317
10318     for (i = 0; i < MAX_PLAYERS; i++)
10319     {
10320       struct PlayerInfo *player = &stored_player[i];
10321
10322       if (SHIELD_ON(player))
10323       {
10324         player->shield_normal_time_left--;
10325
10326         if (player->shield_deadly_time_left > 0)
10327           player->shield_deadly_time_left--;
10328       }
10329     }
10330
10331     if (!local_player->LevelSolved && !level.use_step_counter)
10332     {
10333       TimePlayed++;
10334
10335       if (TimeLeft > 0)
10336       {
10337         TimeLeft--;
10338
10339         if (TimeLeft <= 10 && setup.time_limit)
10340           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10341
10342         DrawGameValue_Time(TimeLeft);
10343
10344         if (!TimeLeft && setup.time_limit)
10345         {
10346           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10347             level.native_em_level->lev->killed_out_of_time = TRUE;
10348           else
10349             for (i = 0; i < MAX_PLAYERS; i++)
10350               KillPlayer(&stored_player[i]);
10351         }
10352       }
10353       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10354         DrawGameValue_Time(TimePlayed);
10355
10356       level.native_em_level->lev->time =
10357         (level.time == 0 ? TimePlayed : TimeLeft);
10358     }
10359
10360     if (tape.recording || tape.playing)
10361       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10362   }
10363 }
10364
10365 void AdvanceFrameAndPlayerCounters(int player_nr)
10366 {
10367   int i;
10368
10369   /* advance frame counters (global frame counter and time frame counter) */
10370   FrameCounter++;
10371   TimeFrames++;
10372
10373   /* advance player counters (counters for move delay, move animation etc.) */
10374   for (i = 0; i < MAX_PLAYERS; i++)
10375   {
10376     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10377     int move_delay_value = stored_player[i].move_delay_value;
10378     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10379
10380     if (!advance_player_counters)       /* not all players may be affected */
10381       continue;
10382
10383 #if USE_NEW_PLAYER_ANIM
10384     if (move_frames == 0)       /* less than one move per game frame */
10385     {
10386       int stepsize = TILEX / move_delay_value;
10387       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10388       int count = (stored_player[i].is_moving ?
10389                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10390
10391       if (count % delay == 0)
10392         move_frames = 1;
10393     }
10394 #endif
10395
10396     stored_player[i].Frame += move_frames;
10397
10398     if (stored_player[i].MovPos != 0)
10399       stored_player[i].StepFrame += move_frames;
10400
10401     if (stored_player[i].move_delay > 0)
10402       stored_player[i].move_delay--;
10403
10404     /* due to bugs in previous versions, counter must count up, not down */
10405     if (stored_player[i].push_delay != -1)
10406       stored_player[i].push_delay++;
10407
10408     if (stored_player[i].drop_delay > 0)
10409       stored_player[i].drop_delay--;
10410
10411     if (stored_player[i].is_dropping_pressed)
10412       stored_player[i].drop_pressed_delay++;
10413   }
10414 }
10415
10416 void StartGameActions(boolean init_network_game, boolean record_tape,
10417                       long random_seed)
10418 {
10419   unsigned long new_random_seed = InitRND(random_seed);
10420
10421   if (record_tape)
10422     TapeStartRecording(new_random_seed);
10423
10424 #if defined(NETWORK_AVALIABLE)
10425   if (init_network_game)
10426   {
10427     SendToServer_StartPlaying();
10428
10429     return;
10430   }
10431 #endif
10432
10433   InitGame();
10434 }
10435
10436 void GameActions()
10437 {
10438   static unsigned long game_frame_delay = 0;
10439   unsigned long game_frame_delay_value;
10440   byte *recorded_player_action;
10441   byte summarized_player_action = 0;
10442   byte tape_action[MAX_PLAYERS];
10443   int i;
10444
10445   /* detect endless loops, caused by custom element programming */
10446   if (recursion_loop_detected && recursion_loop_depth == 0)
10447   {
10448     char *message = getStringCat3("Internal Error ! Element ",
10449                                   EL_NAME(recursion_loop_element),
10450                                   " caused endless loop ! Quit the game ?");
10451
10452     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10453           EL_NAME(recursion_loop_element));
10454
10455     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10456
10457     recursion_loop_detected = FALSE;    /* if game should be continued */
10458
10459     free(message);
10460
10461     return;
10462   }
10463
10464   if (game.restart_level)
10465     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
10466
10467   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10468   {
10469     if (level.native_em_level->lev->home == 0)  /* all players at home */
10470     {
10471       PlayerWins(local_player);
10472
10473       AllPlayersGone = TRUE;
10474
10475       level.native_em_level->lev->home = -1;
10476     }
10477
10478     if (level.native_em_level->ply[0]->alive == 0 &&
10479         level.native_em_level->ply[1]->alive == 0 &&
10480         level.native_em_level->ply[2]->alive == 0 &&
10481         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10482       AllPlayersGone = TRUE;
10483   }
10484
10485   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10486     GameWon();
10487
10488   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10489     TapeStop();
10490
10491   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
10492     return;
10493
10494   game_frame_delay_value =
10495     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10496
10497   if (tape.playing && tape.warp_forward && !tape.pausing)
10498     game_frame_delay_value = 0;
10499
10500   /* ---------- main game synchronization point ---------- */
10501
10502   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10503
10504   if (network_playing && !network_player_action_received)
10505   {
10506     /* try to get network player actions in time */
10507
10508 #if defined(NETWORK_AVALIABLE)
10509     /* last chance to get network player actions without main loop delay */
10510     HandleNetworking();
10511 #endif
10512
10513     /* game was quit by network peer */
10514     if (game_status != GAME_MODE_PLAYING)
10515       return;
10516
10517     if (!network_player_action_received)
10518       return;           /* failed to get network player actions in time */
10519
10520     /* do not yet reset "network_player_action_received" (for tape.pausing) */
10521   }
10522
10523   if (tape.pausing)
10524     return;
10525
10526   /* at this point we know that we really continue executing the game */
10527
10528   network_player_action_received = FALSE;
10529
10530   /* when playing tape, read previously recorded player input from tape data */
10531   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
10532
10533 #if 1
10534   /* TapePlayAction() may return NULL when toggling to "pause before death" */
10535   if (tape.pausing)
10536     return;
10537 #endif
10538
10539   if (tape.set_centered_player)
10540   {
10541     game.centered_player_nr_next = tape.centered_player_nr_next;
10542     game.set_centered_player = TRUE;
10543   }
10544
10545   for (i = 0; i < MAX_PLAYERS; i++)
10546   {
10547     summarized_player_action |= stored_player[i].action;
10548
10549     if (!network_playing)
10550       stored_player[i].effective_action = stored_player[i].action;
10551   }
10552
10553 #if defined(NETWORK_AVALIABLE)
10554   if (network_playing)
10555     SendToServer_MovePlayer(summarized_player_action);
10556 #endif
10557
10558   if (!options.network && !setup.team_mode)
10559     local_player->effective_action = summarized_player_action;
10560
10561   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
10562   {
10563     for (i = 0; i < MAX_PLAYERS; i++)
10564       stored_player[i].effective_action =
10565         (i == game.centered_player_nr ? summarized_player_action : 0);
10566   }
10567
10568   if (recorded_player_action != NULL)
10569     for (i = 0; i < MAX_PLAYERS; i++)
10570       stored_player[i].effective_action = recorded_player_action[i];
10571
10572   for (i = 0; i < MAX_PLAYERS; i++)
10573   {
10574     tape_action[i] = stored_player[i].effective_action;
10575
10576     /* (this can only happen in the R'n'D game engine) */
10577     if (tape.recording && tape_action[i] && !tape.player_participates[i])
10578       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
10579   }
10580
10581   /* only record actions from input devices, but not programmed actions */
10582   if (tape.recording)
10583     TapeRecordAction(tape_action);
10584
10585   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10586   {
10587     GameActions_EM_Main();
10588   }
10589   else
10590   {
10591     GameActions_RND();
10592   }
10593 }
10594
10595 void GameActions_EM_Main()
10596 {
10597   byte effective_action[MAX_PLAYERS];
10598   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
10599   int i;
10600
10601   for (i = 0; i < MAX_PLAYERS; i++)
10602     effective_action[i] = stored_player[i].effective_action;
10603
10604   GameActions_EM(effective_action, warp_mode);
10605
10606   CheckLevelTime();
10607
10608   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
10609 }
10610
10611 void GameActions_RND()
10612 {
10613   int magic_wall_x = 0, magic_wall_y = 0;
10614   int i, x, y, element, graphic;
10615
10616   InitPlayfieldScanModeVars();
10617
10618 #if USE_ONE_MORE_CHANGE_PER_FRAME
10619   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10620   {
10621     SCAN_PLAYFIELD(x, y)
10622     {
10623       ChangeCount[x][y] = 0;
10624       ChangeEvent[x][y] = -1;
10625     }
10626   }
10627 #endif
10628
10629   if (game.set_centered_player)
10630   {
10631     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
10632
10633     /* switching to "all players" only possible if all players fit to screen */
10634     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
10635     {
10636       game.centered_player_nr_next = game.centered_player_nr;
10637       game.set_centered_player = FALSE;
10638     }
10639
10640     /* do not switch focus to non-existing (or non-active) player */
10641     if (game.centered_player_nr_next >= 0 &&
10642         !stored_player[game.centered_player_nr_next].active)
10643     {
10644       game.centered_player_nr_next = game.centered_player_nr;
10645       game.set_centered_player = FALSE;
10646     }
10647   }
10648
10649   if (game.set_centered_player &&
10650       ScreenMovPos == 0)        /* screen currently aligned at tile position */
10651   {
10652     int sx, sy;
10653
10654     if (game.centered_player_nr_next == -1)
10655     {
10656       setScreenCenteredToAllPlayers(&sx, &sy);
10657     }
10658     else
10659     {
10660       sx = stored_player[game.centered_player_nr_next].jx;
10661       sy = stored_player[game.centered_player_nr_next].jy;
10662     }
10663
10664     game.centered_player_nr = game.centered_player_nr_next;
10665     game.set_centered_player = FALSE;
10666
10667     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
10668     DrawGameDoorValues();
10669   }
10670
10671   for (i = 0; i < MAX_PLAYERS; i++)
10672   {
10673     int actual_player_action = stored_player[i].effective_action;
10674
10675 #if 1
10676     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
10677        - rnd_equinox_tetrachloride 048
10678        - rnd_equinox_tetrachloride_ii 096
10679        - rnd_emanuel_schmieg 002
10680        - doctor_sloan_ww 001, 020
10681     */
10682     if (stored_player[i].MovPos == 0)
10683       CheckGravityMovement(&stored_player[i]);
10684 #endif
10685
10686     /* overwrite programmed action with tape action */
10687     if (stored_player[i].programmed_action)
10688       actual_player_action = stored_player[i].programmed_action;
10689
10690     PlayerActions(&stored_player[i], actual_player_action);
10691
10692     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
10693   }
10694
10695   ScrollScreen(NULL, SCROLL_GO_ON);
10696
10697   /* for backwards compatibility, the following code emulates a fixed bug that
10698      occured when pushing elements (causing elements that just made their last
10699      pushing step to already (if possible) make their first falling step in the
10700      same game frame, which is bad); this code is also needed to use the famous
10701      "spring push bug" which is used in older levels and might be wanted to be
10702      used also in newer levels, but in this case the buggy pushing code is only
10703      affecting the "spring" element and no other elements */
10704
10705   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
10706   {
10707     for (i = 0; i < MAX_PLAYERS; i++)
10708     {
10709       struct PlayerInfo *player = &stored_player[i];
10710       int x = player->jx;
10711       int y = player->jy;
10712
10713       if (player->active && player->is_pushing && player->is_moving &&
10714           IS_MOVING(x, y) &&
10715           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
10716            Feld[x][y] == EL_SPRING))
10717       {
10718         ContinueMoving(x, y);
10719
10720         /* continue moving after pushing (this is actually a bug) */
10721         if (!IS_MOVING(x, y))
10722           Stop[x][y] = FALSE;
10723       }
10724     }
10725   }
10726
10727 #if 0
10728   debug_print_timestamp(0, "start main loop profiling");
10729 #endif
10730
10731   SCAN_PLAYFIELD(x, y)
10732   {
10733     ChangeCount[x][y] = 0;
10734     ChangeEvent[x][y] = -1;
10735
10736     /* this must be handled before main playfield loop */
10737     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
10738     {
10739       MovDelay[x][y]--;
10740       if (MovDelay[x][y] <= 0)
10741         RemoveField(x, y);
10742     }
10743
10744 #if USE_NEW_SNAP_DELAY
10745     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
10746     {
10747       MovDelay[x][y]--;
10748       if (MovDelay[x][y] <= 0)
10749       {
10750         RemoveField(x, y);
10751         DrawLevelField(x, y);
10752
10753         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
10754       }
10755     }
10756 #endif
10757
10758 #if DEBUG
10759     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
10760     {
10761       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
10762       printf("GameActions(): This should never happen!\n");
10763
10764       ChangePage[x][y] = -1;
10765     }
10766 #endif
10767
10768     Stop[x][y] = FALSE;
10769     if (WasJustMoving[x][y] > 0)
10770       WasJustMoving[x][y]--;
10771     if (WasJustFalling[x][y] > 0)
10772       WasJustFalling[x][y]--;
10773     if (CheckCollision[x][y] > 0)
10774       CheckCollision[x][y]--;
10775     if (CheckImpact[x][y] > 0)
10776       CheckImpact[x][y]--;
10777
10778     GfxFrame[x][y]++;
10779
10780     /* reset finished pushing action (not done in ContinueMoving() to allow
10781        continuous pushing animation for elements with zero push delay) */
10782     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
10783     {
10784       ResetGfxAnimation(x, y);
10785       DrawLevelField(x, y);
10786     }
10787
10788 #if DEBUG
10789     if (IS_BLOCKED(x, y))
10790     {
10791       int oldx, oldy;
10792
10793       Blocked2Moving(x, y, &oldx, &oldy);
10794       if (!IS_MOVING(oldx, oldy))
10795       {
10796         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
10797         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
10798         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
10799         printf("GameActions(): This should never happen!\n");
10800       }
10801     }
10802 #endif
10803   }
10804
10805 #if 0
10806   debug_print_timestamp(0, "- time for pre-main loop:");
10807 #endif
10808
10809 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
10810   SCAN_PLAYFIELD(x, y)
10811   {
10812     element = Feld[x][y];
10813     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10814
10815 #if 1
10816     {
10817 #if 1
10818       int element2 = element;
10819       int graphic2 = graphic;
10820 #else
10821       int element2 = Feld[x][y];
10822       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
10823 #endif
10824       int last_gfx_frame = GfxFrame[x][y];
10825
10826       if (graphic_info[graphic2].anim_global_sync)
10827         GfxFrame[x][y] = FrameCounter;
10828       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
10829         GfxFrame[x][y] = CustomValue[x][y];
10830       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
10831         GfxFrame[x][y] = element_info[element2].collect_score;
10832       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
10833         GfxFrame[x][y] = ChangeDelay[x][y];
10834
10835       if (redraw && GfxFrame[x][y] != last_gfx_frame)
10836         DrawLevelGraphicAnimation(x, y, graphic2);
10837     }
10838 #else
10839     ResetGfxFrame(x, y, TRUE);
10840 #endif
10841
10842 #if 1
10843     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10844         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10845       ResetRandomAnimationValue(x, y);
10846 #endif
10847
10848 #if 1
10849     SetRandomAnimationValue(x, y);
10850 #endif
10851
10852 #if 1
10853     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10854 #endif
10855   }
10856 #endif  // -------------------- !!! TEST ONLY !!! --------------------
10857
10858 #if 0
10859   debug_print_timestamp(0, "- time for TEST loop:     -->");
10860 #endif
10861
10862   SCAN_PLAYFIELD(x, y)
10863   {
10864     element = Feld[x][y];
10865     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10866
10867     ResetGfxFrame(x, y, TRUE);
10868
10869     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10870         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10871       ResetRandomAnimationValue(x, y);
10872
10873     SetRandomAnimationValue(x, y);
10874
10875     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10876
10877     if (IS_INACTIVE(element))
10878     {
10879       if (IS_ANIMATED(graphic))
10880         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10881
10882       continue;
10883     }
10884
10885     /* this may take place after moving, so 'element' may have changed */
10886     if (IS_CHANGING(x, y) &&
10887         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10888     {
10889       int page = element_info[element].event_page_nr[CE_DELAY];
10890
10891 #if 1
10892       HandleElementChange(x, y, page);
10893 #else
10894       if (CAN_CHANGE(element))
10895         HandleElementChange(x, y, page);
10896
10897       if (HAS_ACTION(element))
10898         ExecuteCustomElementAction(x, y, element, page);
10899 #endif
10900
10901       element = Feld[x][y];
10902       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10903     }
10904
10905 #if 0   // ---------------------------------------------------------------------
10906
10907     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10908     {
10909       StartMoving(x, y);
10910
10911       element = Feld[x][y];
10912       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10913
10914       if (IS_ANIMATED(graphic) &&
10915           !IS_MOVING(x, y) &&
10916           !Stop[x][y])
10917         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10918
10919       if (IS_GEM(element) || element == EL_SP_INFOTRON)
10920         DrawTwinkleOnField(x, y);
10921     }
10922     else if (IS_MOVING(x, y))
10923       ContinueMoving(x, y);
10924     else
10925     {
10926       switch (element)
10927       {
10928         case EL_ACID:
10929         case EL_EXIT_OPEN:
10930         case EL_EM_EXIT_OPEN:
10931         case EL_SP_EXIT_OPEN:
10932         case EL_STEEL_EXIT_OPEN:
10933         case EL_EM_STEEL_EXIT_OPEN:
10934         case EL_SP_TERMINAL:
10935         case EL_SP_TERMINAL_ACTIVE:
10936         case EL_EXTRA_TIME:
10937         case EL_SHIELD_NORMAL:
10938         case EL_SHIELD_DEADLY:
10939           if (IS_ANIMATED(graphic))
10940             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10941           break;
10942
10943         case EL_DYNAMITE_ACTIVE:
10944         case EL_EM_DYNAMITE_ACTIVE:
10945         case EL_DYNABOMB_PLAYER_1_ACTIVE:
10946         case EL_DYNABOMB_PLAYER_2_ACTIVE:
10947         case EL_DYNABOMB_PLAYER_3_ACTIVE:
10948         case EL_DYNABOMB_PLAYER_4_ACTIVE:
10949         case EL_SP_DISK_RED_ACTIVE:
10950           CheckDynamite(x, y);
10951           break;
10952
10953         case EL_AMOEBA_GROWING:
10954           AmoebeWaechst(x, y);
10955           break;
10956
10957         case EL_AMOEBA_SHRINKING:
10958           AmoebaDisappearing(x, y);
10959           break;
10960
10961 #if !USE_NEW_AMOEBA_CODE
10962         case EL_AMOEBA_WET:
10963         case EL_AMOEBA_DRY:
10964         case EL_AMOEBA_FULL:
10965         case EL_BD_AMOEBA:
10966         case EL_EMC_DRIPPER:
10967           AmoebeAbleger(x, y);
10968           break;
10969 #endif
10970
10971         case EL_GAME_OF_LIFE:
10972         case EL_BIOMAZE:
10973           Life(x, y);
10974           break;
10975
10976         case EL_EXIT_CLOSED:
10977           CheckExit(x, y);
10978           break;
10979
10980         case EL_EM_EXIT_CLOSED:
10981           CheckExitEM(x, y);
10982           break;
10983
10984         case EL_STEEL_EXIT_CLOSED:
10985           CheckExitSteel(x, y);
10986           break;
10987
10988         case EL_EM_STEEL_EXIT_CLOSED:
10989           CheckExitSteelEM(x, y);
10990           break;
10991
10992         case EL_SP_EXIT_CLOSED:
10993           CheckExitSP(x, y);
10994           break;
10995
10996         case EL_EXPANDABLE_WALL_GROWING:
10997         case EL_EXPANDABLE_STEELWALL_GROWING:
10998           MauerWaechst(x, y);
10999           break;
11000
11001         case EL_EXPANDABLE_WALL:
11002         case EL_EXPANDABLE_WALL_HORIZONTAL:
11003         case EL_EXPANDABLE_WALL_VERTICAL:
11004         case EL_EXPANDABLE_WALL_ANY:
11005         case EL_BD_EXPANDABLE_WALL:
11006           MauerAbleger(x, y);
11007           break;
11008
11009         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
11010         case EL_EXPANDABLE_STEELWALL_VERTICAL:
11011         case EL_EXPANDABLE_STEELWALL_ANY:
11012           MauerAblegerStahl(x, y);
11013           break;
11014
11015         case EL_FLAMES:
11016           CheckForDragon(x, y);
11017           break;
11018
11019         case EL_EXPLOSION:
11020           break;
11021
11022         case EL_ELEMENT_SNAPPING:
11023         case EL_DIAGONAL_SHRINKING:
11024         case EL_DIAGONAL_GROWING:
11025         {
11026           graphic =
11027             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11028
11029           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11030           break;
11031         }
11032
11033         default:
11034           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11035             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11036           break;
11037       }
11038     }
11039
11040 #else   // ---------------------------------------------------------------------
11041
11042     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11043     {
11044       StartMoving(x, y);
11045
11046       element = Feld[x][y];
11047       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11048
11049       if (IS_ANIMATED(graphic) &&
11050           !IS_MOVING(x, y) &&
11051           !Stop[x][y])
11052         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11053
11054       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11055         DrawTwinkleOnField(x, y);
11056     }
11057     else if ((element == EL_ACID ||
11058               element == EL_EXIT_OPEN ||
11059               element == EL_EM_EXIT_OPEN ||
11060               element == EL_SP_EXIT_OPEN ||
11061               element == EL_STEEL_EXIT_OPEN ||
11062               element == EL_EM_STEEL_EXIT_OPEN ||
11063               element == EL_SP_TERMINAL ||
11064               element == EL_SP_TERMINAL_ACTIVE ||
11065               element == EL_EXTRA_TIME ||
11066               element == EL_SHIELD_NORMAL ||
11067               element == EL_SHIELD_DEADLY) &&
11068              IS_ANIMATED(graphic))
11069       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11070     else if (IS_MOVING(x, y))
11071       ContinueMoving(x, y);
11072     else if (IS_ACTIVE_BOMB(element))
11073       CheckDynamite(x, y);
11074     else if (element == EL_AMOEBA_GROWING)
11075       AmoebeWaechst(x, y);
11076     else if (element == EL_AMOEBA_SHRINKING)
11077       AmoebaDisappearing(x, y);
11078
11079 #if !USE_NEW_AMOEBA_CODE
11080     else if (IS_AMOEBALIVE(element))
11081       AmoebeAbleger(x, y);
11082 #endif
11083
11084     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11085       Life(x, y);
11086     else if (element == EL_EXIT_CLOSED)
11087       CheckExit(x, y);
11088     else if (element == EL_EM_EXIT_CLOSED)
11089       CheckExitEM(x, y);
11090     else if (element == EL_STEEL_EXIT_CLOSED)
11091       CheckExitSteel(x, y);
11092     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11093       CheckExitSteelEM(x, y);
11094     else if (element == EL_SP_EXIT_CLOSED)
11095       CheckExitSP(x, y);
11096     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11097              element == EL_EXPANDABLE_STEELWALL_GROWING)
11098       MauerWaechst(x, y);
11099     else if (element == EL_EXPANDABLE_WALL ||
11100              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11101              element == EL_EXPANDABLE_WALL_VERTICAL ||
11102              element == EL_EXPANDABLE_WALL_ANY ||
11103              element == EL_BD_EXPANDABLE_WALL)
11104       MauerAbleger(x, y);
11105     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11106              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11107              element == EL_EXPANDABLE_STEELWALL_ANY)
11108       MauerAblegerStahl(x, y);
11109     else if (element == EL_FLAMES)
11110       CheckForDragon(x, y);
11111     else if (element == EL_EXPLOSION)
11112       ; /* drawing of correct explosion animation is handled separately */
11113     else if (element == EL_ELEMENT_SNAPPING ||
11114              element == EL_DIAGONAL_SHRINKING ||
11115              element == EL_DIAGONAL_GROWING)
11116     {
11117       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11118
11119       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11120     }
11121     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11122       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11123
11124 #endif  // ---------------------------------------------------------------------
11125
11126     if (IS_BELT_ACTIVE(element))
11127       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11128
11129     if (game.magic_wall_active)
11130     {
11131       int jx = local_player->jx, jy = local_player->jy;
11132
11133       /* play the element sound at the position nearest to the player */
11134       if ((element == EL_MAGIC_WALL_FULL ||
11135            element == EL_MAGIC_WALL_ACTIVE ||
11136            element == EL_MAGIC_WALL_EMPTYING ||
11137            element == EL_BD_MAGIC_WALL_FULL ||
11138            element == EL_BD_MAGIC_WALL_ACTIVE ||
11139            element == EL_BD_MAGIC_WALL_EMPTYING ||
11140            element == EL_DC_MAGIC_WALL_FULL ||
11141            element == EL_DC_MAGIC_WALL_ACTIVE ||
11142            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11143           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11144       {
11145         magic_wall_x = x;
11146         magic_wall_y = y;
11147       }
11148     }
11149   }
11150
11151 #if 0
11152   debug_print_timestamp(0, "- time for MAIN loop:     -->");
11153 #endif
11154
11155 #if USE_NEW_AMOEBA_CODE
11156   /* new experimental amoeba growth stuff */
11157   if (!(FrameCounter % 8))
11158   {
11159     static unsigned long random = 1684108901;
11160
11161     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11162     {
11163       x = RND(lev_fieldx);
11164       y = RND(lev_fieldy);
11165       element = Feld[x][y];
11166
11167       if (!IS_PLAYER(x,y) &&
11168           (element == EL_EMPTY ||
11169            CAN_GROW_INTO(element) ||
11170            element == EL_QUICKSAND_EMPTY ||
11171            element == EL_QUICKSAND_FAST_EMPTY ||
11172            element == EL_ACID_SPLASH_LEFT ||
11173            element == EL_ACID_SPLASH_RIGHT))
11174       {
11175         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11176             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11177             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11178             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11179           Feld[x][y] = EL_AMOEBA_DROP;
11180       }
11181
11182       random = random * 129 + 1;
11183     }
11184   }
11185 #endif
11186
11187 #if 0
11188   if (game.explosions_delayed)
11189 #endif
11190   {
11191     game.explosions_delayed = FALSE;
11192
11193     SCAN_PLAYFIELD(x, y)
11194     {
11195       element = Feld[x][y];
11196
11197       if (ExplodeField[x][y])
11198         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11199       else if (element == EL_EXPLOSION)
11200         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11201
11202       ExplodeField[x][y] = EX_TYPE_NONE;
11203     }
11204
11205     game.explosions_delayed = TRUE;
11206   }
11207
11208   if (game.magic_wall_active)
11209   {
11210     if (!(game.magic_wall_time_left % 4))
11211     {
11212       int element = Feld[magic_wall_x][magic_wall_y];
11213
11214       if (element == EL_BD_MAGIC_WALL_FULL ||
11215           element == EL_BD_MAGIC_WALL_ACTIVE ||
11216           element == EL_BD_MAGIC_WALL_EMPTYING)
11217         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11218       else if (element == EL_DC_MAGIC_WALL_FULL ||
11219                element == EL_DC_MAGIC_WALL_ACTIVE ||
11220                element == EL_DC_MAGIC_WALL_EMPTYING)
11221         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11222       else
11223         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11224     }
11225
11226     if (game.magic_wall_time_left > 0)
11227     {
11228       game.magic_wall_time_left--;
11229       if (!game.magic_wall_time_left)
11230       {
11231         SCAN_PLAYFIELD(x, y)
11232         {
11233           element = Feld[x][y];
11234
11235           if (element == EL_MAGIC_WALL_ACTIVE ||
11236               element == EL_MAGIC_WALL_FULL)
11237           {
11238             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11239             DrawLevelField(x, y);
11240           }
11241           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11242                    element == EL_BD_MAGIC_WALL_FULL)
11243           {
11244             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11245             DrawLevelField(x, y);
11246           }
11247           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11248                    element == EL_DC_MAGIC_WALL_FULL)
11249           {
11250             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11251             DrawLevelField(x, y);
11252           }
11253         }
11254
11255         game.magic_wall_active = FALSE;
11256       }
11257     }
11258   }
11259
11260   if (game.light_time_left > 0)
11261   {
11262     game.light_time_left--;
11263
11264     if (game.light_time_left == 0)
11265       RedrawAllLightSwitchesAndInvisibleElements();
11266   }
11267
11268   if (game.timegate_time_left > 0)
11269   {
11270     game.timegate_time_left--;
11271
11272     if (game.timegate_time_left == 0)
11273       CloseAllOpenTimegates();
11274   }
11275
11276   if (game.lenses_time_left > 0)
11277   {
11278     game.lenses_time_left--;
11279
11280     if (game.lenses_time_left == 0)
11281       RedrawAllInvisibleElementsForLenses();
11282   }
11283
11284   if (game.magnify_time_left > 0)
11285   {
11286     game.magnify_time_left--;
11287
11288     if (game.magnify_time_left == 0)
11289       RedrawAllInvisibleElementsForMagnifier();
11290   }
11291
11292   for (i = 0; i < MAX_PLAYERS; i++)
11293   {
11294     struct PlayerInfo *player = &stored_player[i];
11295
11296     if (SHIELD_ON(player))
11297     {
11298       if (player->shield_deadly_time_left)
11299         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11300       else if (player->shield_normal_time_left)
11301         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11302     }
11303   }
11304
11305   CheckLevelTime();
11306
11307   DrawAllPlayers();
11308   PlayAllPlayersSound();
11309
11310   if (options.debug)                    /* calculate frames per second */
11311   {
11312     static unsigned long fps_counter = 0;
11313     static int fps_frames = 0;
11314     unsigned long fps_delay_ms = Counter() - fps_counter;
11315
11316     fps_frames++;
11317
11318     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11319     {
11320       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11321
11322       fps_frames = 0;
11323       fps_counter = Counter();
11324     }
11325
11326     redraw_mask |= REDRAW_FPS;
11327   }
11328
11329   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11330
11331   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11332   {
11333     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11334
11335     local_player->show_envelope = 0;
11336   }
11337
11338 #if 0
11339   debug_print_timestamp(0, "stop main loop profiling ");
11340   printf("----------------------------------------------------------\n");
11341 #endif
11342
11343   /* use random number generator in every frame to make it less predictable */
11344   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11345     RND(1);
11346 }
11347
11348 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11349 {
11350   int min_x = x, min_y = y, max_x = x, max_y = y;
11351   int i;
11352
11353   for (i = 0; i < MAX_PLAYERS; i++)
11354   {
11355     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11356
11357     if (!stored_player[i].active || &stored_player[i] == player)
11358       continue;
11359
11360     min_x = MIN(min_x, jx);
11361     min_y = MIN(min_y, jy);
11362     max_x = MAX(max_x, jx);
11363     max_y = MAX(max_y, jy);
11364   }
11365
11366   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11367 }
11368
11369 static boolean AllPlayersInVisibleScreen()
11370 {
11371   int i;
11372
11373   for (i = 0; i < MAX_PLAYERS; i++)
11374   {
11375     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11376
11377     if (!stored_player[i].active)
11378       continue;
11379
11380     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11381       return FALSE;
11382   }
11383
11384   return TRUE;
11385 }
11386
11387 void ScrollLevel(int dx, int dy)
11388 {
11389 #if 1
11390   static Bitmap *bitmap_db_field2 = NULL;
11391   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11392   int x, y;
11393 #else
11394   int i, x, y;
11395 #endif
11396
11397 #if 0
11398   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11399   /* only horizontal XOR vertical scroll direction allowed */
11400   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11401     return;
11402 #endif
11403
11404 #if 1
11405   if (bitmap_db_field2 == NULL)
11406     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11407
11408   /* needed when blitting directly to same bitmap -- should not be needed with
11409      recent SDL libraries, but apparently does not work in 1.2.11 directly */
11410   BlitBitmap(drawto_field, bitmap_db_field2,
11411              FX + TILEX * (dx == -1) - softscroll_offset,
11412              FY + TILEY * (dy == -1) - softscroll_offset,
11413              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11414              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11415              FX + TILEX * (dx == 1) - softscroll_offset,
11416              FY + TILEY * (dy == 1) - softscroll_offset);
11417   BlitBitmap(bitmap_db_field2, drawto_field,
11418              FX + TILEX * (dx == 1) - softscroll_offset,
11419              FY + TILEY * (dy == 1) - softscroll_offset,
11420              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11421              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11422              FX + TILEX * (dx == 1) - softscroll_offset,
11423              FY + TILEY * (dy == 1) - softscroll_offset);
11424
11425 #else
11426
11427 #if 1
11428   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11429   int xsize = (BX2 - BX1 + 1);
11430   int ysize = (BY2 - BY1 + 1);
11431   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11432   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11433   int step  = (start < end ? +1 : -1);
11434
11435   for (i = start; i != end; i += step)
11436   {
11437     BlitBitmap(drawto_field, drawto_field,
11438                FX + TILEX * (dx != 0 ? i + step : 0),
11439                FY + TILEY * (dy != 0 ? i + step : 0),
11440                TILEX * (dx != 0 ? 1 : xsize),
11441                TILEY * (dy != 0 ? 1 : ysize),
11442                FX + TILEX * (dx != 0 ? i : 0),
11443                FY + TILEY * (dy != 0 ? i : 0));
11444   }
11445
11446 #else
11447
11448   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11449
11450   BlitBitmap(drawto_field, drawto_field,
11451              FX + TILEX * (dx == -1) - softscroll_offset,
11452              FY + TILEY * (dy == -1) - softscroll_offset,
11453              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11454              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11455              FX + TILEX * (dx == 1) - softscroll_offset,
11456              FY + TILEY * (dy == 1) - softscroll_offset);
11457 #endif
11458 #endif
11459
11460   if (dx != 0)
11461   {
11462     x = (dx == 1 ? BX1 : BX2);
11463     for (y = BY1; y <= BY2; y++)
11464       DrawScreenField(x, y);
11465   }
11466
11467   if (dy != 0)
11468   {
11469     y = (dy == 1 ? BY1 : BY2);
11470     for (x = BX1; x <= BX2; x++)
11471       DrawScreenField(x, y);
11472   }
11473
11474   redraw_mask |= REDRAW_FIELD;
11475 }
11476
11477 static boolean canFallDown(struct PlayerInfo *player)
11478 {
11479   int jx = player->jx, jy = player->jy;
11480
11481   return (IN_LEV_FIELD(jx, jy + 1) &&
11482           (IS_FREE(jx, jy + 1) ||
11483            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11484           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11485           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11486 }
11487
11488 static boolean canPassField(int x, int y, int move_dir)
11489 {
11490   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11491   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11492   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11493   int nextx = x + dx;
11494   int nexty = y + dy;
11495   int element = Feld[x][y];
11496
11497   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11498           !CAN_MOVE(element) &&
11499           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11500           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11501           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11502 }
11503
11504 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11505 {
11506   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11507   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11508   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11509   int newx = x + dx;
11510   int newy = y + dy;
11511
11512   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11513           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11514           (IS_DIGGABLE(Feld[newx][newy]) ||
11515            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11516            canPassField(newx, newy, move_dir)));
11517 }
11518
11519 static void CheckGravityMovement(struct PlayerInfo *player)
11520 {
11521 #if USE_PLAYER_GRAVITY
11522   if (player->gravity && !player->programmed_action)
11523 #else
11524   if (game.gravity && !player->programmed_action)
11525 #endif
11526   {
11527     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11528     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11529     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11530     int jx = player->jx, jy = player->jy;
11531     boolean player_is_moving_to_valid_field =
11532       (!player_is_snapping &&
11533        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11534         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11535     boolean player_can_fall_down = canFallDown(player);
11536
11537     if (player_can_fall_down &&
11538         !player_is_moving_to_valid_field)
11539       player->programmed_action = MV_DOWN;
11540   }
11541 }
11542
11543 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11544 {
11545   return CheckGravityMovement(player);
11546
11547 #if USE_PLAYER_GRAVITY
11548   if (player->gravity && !player->programmed_action)
11549 #else
11550   if (game.gravity && !player->programmed_action)
11551 #endif
11552   {
11553     int jx = player->jx, jy = player->jy;
11554     boolean field_under_player_is_free =
11555       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11556     boolean player_is_standing_on_valid_field =
11557       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11558        (IS_WALKABLE(Feld[jx][jy]) &&
11559         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11560
11561     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11562       player->programmed_action = MV_DOWN;
11563   }
11564 }
11565
11566 /*
11567   MovePlayerOneStep()
11568   -----------------------------------------------------------------------------
11569   dx, dy:               direction (non-diagonal) to try to move the player to
11570   real_dx, real_dy:     direction as read from input device (can be diagonal)
11571 */
11572
11573 boolean MovePlayerOneStep(struct PlayerInfo *player,
11574                           int dx, int dy, int real_dx, int real_dy)
11575 {
11576   int jx = player->jx, jy = player->jy;
11577   int new_jx = jx + dx, new_jy = jy + dy;
11578 #if !USE_FIXED_DONT_RUN_INTO
11579   int element;
11580 #endif
11581   int can_move;
11582   boolean player_can_move = !player->cannot_move;
11583
11584   if (!player->active || (!dx && !dy))
11585     return MP_NO_ACTION;
11586
11587   player->MovDir = (dx < 0 ? MV_LEFT :
11588                     dx > 0 ? MV_RIGHT :
11589                     dy < 0 ? MV_UP :
11590                     dy > 0 ? MV_DOWN :  MV_NONE);
11591
11592   if (!IN_LEV_FIELD(new_jx, new_jy))
11593     return MP_NO_ACTION;
11594
11595   if (!player_can_move)
11596   {
11597     if (player->MovPos == 0)
11598     {
11599       player->is_moving = FALSE;
11600       player->is_digging = FALSE;
11601       player->is_collecting = FALSE;
11602       player->is_snapping = FALSE;
11603       player->is_pushing = FALSE;
11604     }
11605   }
11606
11607 #if 1
11608   if (!options.network && game.centered_player_nr == -1 &&
11609       !AllPlayersInSight(player, new_jx, new_jy))
11610     return MP_NO_ACTION;
11611 #else
11612   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
11613     return MP_NO_ACTION;
11614 #endif
11615
11616 #if !USE_FIXED_DONT_RUN_INTO
11617   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
11618
11619   /* (moved to DigField()) */
11620   if (player_can_move && DONT_RUN_INTO(element))
11621   {
11622     if (element == EL_ACID && dx == 0 && dy == 1)
11623     {
11624       SplashAcid(new_jx, new_jy);
11625       Feld[jx][jy] = EL_PLAYER_1;
11626       InitMovingField(jx, jy, MV_DOWN);
11627       Store[jx][jy] = EL_ACID;
11628       ContinueMoving(jx, jy);
11629       BuryPlayer(player);
11630     }
11631     else
11632       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11633
11634     return MP_MOVING;
11635   }
11636 #endif
11637
11638   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11639   if (can_move != MP_MOVING)
11640     return can_move;
11641
11642   /* check if DigField() has caused relocation of the player */
11643   if (player->jx != jx || player->jy != jy)
11644     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11645
11646   StorePlayer[jx][jy] = 0;
11647   player->last_jx = jx;
11648   player->last_jy = jy;
11649   player->jx = new_jx;
11650   player->jy = new_jy;
11651   StorePlayer[new_jx][new_jy] = player->element_nr;
11652
11653   if (player->move_delay_value_next != -1)
11654   {
11655     player->move_delay_value = player->move_delay_value_next;
11656     player->move_delay_value_next = -1;
11657   }
11658
11659   player->MovPos =
11660     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11661
11662   player->step_counter++;
11663
11664   PlayerVisit[jx][jy] = FrameCounter;
11665
11666 #if USE_UFAST_PLAYER_EXIT_BUGFIX
11667   player->is_moving = TRUE;
11668 #endif
11669
11670 #if 1
11671   /* should better be called in MovePlayer(), but this breaks some tapes */
11672   ScrollPlayer(player, SCROLL_INIT);
11673 #endif
11674
11675   return MP_MOVING;
11676 }
11677
11678 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
11679 {
11680   int jx = player->jx, jy = player->jy;
11681   int old_jx = jx, old_jy = jy;
11682   int moved = MP_NO_ACTION;
11683
11684   if (!player->active)
11685     return FALSE;
11686
11687   if (!dx && !dy)
11688   {
11689     if (player->MovPos == 0)
11690     {
11691       player->is_moving = FALSE;
11692       player->is_digging = FALSE;
11693       player->is_collecting = FALSE;
11694       player->is_snapping = FALSE;
11695       player->is_pushing = FALSE;
11696     }
11697
11698     return FALSE;
11699   }
11700
11701   if (player->move_delay > 0)
11702     return FALSE;
11703
11704   player->move_delay = -1;              /* set to "uninitialized" value */
11705
11706   /* store if player is automatically moved to next field */
11707   player->is_auto_moving = (player->programmed_action != MV_NONE);
11708
11709   /* remove the last programmed player action */
11710   player->programmed_action = 0;
11711
11712   if (player->MovPos)
11713   {
11714     /* should only happen if pre-1.2 tape recordings are played */
11715     /* this is only for backward compatibility */
11716
11717     int original_move_delay_value = player->move_delay_value;
11718
11719 #if DEBUG
11720     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
11721            tape.counter);
11722 #endif
11723
11724     /* scroll remaining steps with finest movement resolution */
11725     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
11726
11727     while (player->MovPos)
11728     {
11729       ScrollPlayer(player, SCROLL_GO_ON);
11730       ScrollScreen(NULL, SCROLL_GO_ON);
11731
11732       AdvanceFrameAndPlayerCounters(player->index_nr);
11733
11734       DrawAllPlayers();
11735       BackToFront();
11736     }
11737
11738     player->move_delay_value = original_move_delay_value;
11739   }
11740
11741   player->is_active = FALSE;
11742
11743   if (player->last_move_dir & MV_HORIZONTAL)
11744   {
11745     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
11746       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
11747   }
11748   else
11749   {
11750     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
11751       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
11752   }
11753
11754 #if USE_FIXED_BORDER_RUNNING_GFX
11755   if (!moved && !player->is_active)
11756   {
11757     player->is_moving = FALSE;
11758     player->is_digging = FALSE;
11759     player->is_collecting = FALSE;
11760     player->is_snapping = FALSE;
11761     player->is_pushing = FALSE;
11762   }
11763 #endif
11764
11765   jx = player->jx;
11766   jy = player->jy;
11767
11768 #if 1
11769   if (moved & MP_MOVING && !ScreenMovPos &&
11770       (player->index_nr == game.centered_player_nr ||
11771        game.centered_player_nr == -1))
11772 #else
11773   if (moved & MP_MOVING && !ScreenMovPos &&
11774       (player == local_player || !options.network))
11775 #endif
11776   {
11777     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
11778     int offset = (setup.scroll_delay ? 3 : 0);
11779
11780     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11781     {
11782       /* actual player has left the screen -- scroll in that direction */
11783       if (jx != old_jx)         /* player has moved horizontally */
11784         scroll_x += (jx - old_jx);
11785       else                      /* player has moved vertically */
11786         scroll_y += (jy - old_jy);
11787     }
11788     else
11789     {
11790       if (jx != old_jx)         /* player has moved horizontally */
11791       {
11792         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
11793             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
11794           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
11795
11796         /* don't scroll over playfield boundaries */
11797         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
11798           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
11799
11800         /* don't scroll more than one field at a time */
11801         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
11802
11803         /* don't scroll against the player's moving direction */
11804         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
11805             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
11806           scroll_x = old_scroll_x;
11807       }
11808       else                      /* player has moved vertically */
11809       {
11810         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
11811             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
11812           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
11813
11814         /* don't scroll over playfield boundaries */
11815         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
11816           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
11817
11818         /* don't scroll more than one field at a time */
11819         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
11820
11821         /* don't scroll against the player's moving direction */
11822         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
11823             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
11824           scroll_y = old_scroll_y;
11825       }
11826     }
11827
11828     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
11829     {
11830 #if 1
11831       if (!options.network && game.centered_player_nr == -1 &&
11832           !AllPlayersInVisibleScreen())
11833       {
11834         scroll_x = old_scroll_x;
11835         scroll_y = old_scroll_y;
11836       }
11837       else
11838 #else
11839       if (!options.network && !AllPlayersInVisibleScreen())
11840       {
11841         scroll_x = old_scroll_x;
11842         scroll_y = old_scroll_y;
11843       }
11844       else
11845 #endif
11846       {
11847         ScrollScreen(player, SCROLL_INIT);
11848         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
11849       }
11850     }
11851   }
11852
11853   player->StepFrame = 0;
11854
11855   if (moved & MP_MOVING)
11856   {
11857     if (old_jx != jx && old_jy == jy)
11858       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
11859     else if (old_jx == jx && old_jy != jy)
11860       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
11861
11862     DrawLevelField(jx, jy);     /* for "crumbled sand" */
11863
11864     player->last_move_dir = player->MovDir;
11865     player->is_moving = TRUE;
11866     player->is_snapping = FALSE;
11867     player->is_switching = FALSE;
11868     player->is_dropping = FALSE;
11869     player->is_dropping_pressed = FALSE;
11870     player->drop_pressed_delay = 0;
11871
11872 #if 0
11873     /* should better be called here than above, but this breaks some tapes */
11874     ScrollPlayer(player, SCROLL_INIT);
11875 #endif
11876   }
11877   else
11878   {
11879     CheckGravityMovementWhenNotMoving(player);
11880
11881     player->is_moving = FALSE;
11882
11883     /* at this point, the player is allowed to move, but cannot move right now
11884        (e.g. because of something blocking the way) -- ensure that the player
11885        is also allowed to move in the next frame (in old versions before 3.1.1,
11886        the player was forced to wait again for eight frames before next try) */
11887
11888     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11889       player->move_delay = 0;   /* allow direct movement in the next frame */
11890   }
11891
11892   if (player->move_delay == -1)         /* not yet initialized by DigField() */
11893     player->move_delay = player->move_delay_value;
11894
11895   if (game.engine_version < VERSION_IDENT(3,0,7,0))
11896   {
11897     TestIfPlayerTouchesBadThing(jx, jy);
11898     TestIfPlayerTouchesCustomElement(jx, jy);
11899   }
11900
11901   if (!player->active)
11902     RemovePlayer(player);
11903
11904   return moved;
11905 }
11906
11907 void ScrollPlayer(struct PlayerInfo *player, int mode)
11908 {
11909   int jx = player->jx, jy = player->jy;
11910   int last_jx = player->last_jx, last_jy = player->last_jy;
11911   int move_stepsize = TILEX / player->move_delay_value;
11912
11913 #if USE_NEW_PLAYER_SPEED
11914   if (!player->active)
11915     return;
11916
11917   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
11918     return;
11919 #else
11920   if (!player->active || player->MovPos == 0)
11921     return;
11922 #endif
11923
11924   if (mode == SCROLL_INIT)
11925   {
11926     player->actual_frame_counter = FrameCounter;
11927     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11928
11929     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
11930         Feld[last_jx][last_jy] == EL_EMPTY)
11931     {
11932       int last_field_block_delay = 0;   /* start with no blocking at all */
11933       int block_delay_adjustment = player->block_delay_adjustment;
11934
11935       /* if player blocks last field, add delay for exactly one move */
11936       if (player->block_last_field)
11937       {
11938         last_field_block_delay += player->move_delay_value;
11939
11940         /* when blocking enabled, prevent moving up despite gravity */
11941 #if USE_PLAYER_GRAVITY
11942         if (player->gravity && player->MovDir == MV_UP)
11943           block_delay_adjustment = -1;
11944 #else
11945         if (game.gravity && player->MovDir == MV_UP)
11946           block_delay_adjustment = -1;
11947 #endif
11948       }
11949
11950       /* add block delay adjustment (also possible when not blocking) */
11951       last_field_block_delay += block_delay_adjustment;
11952
11953       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
11954       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
11955     }
11956
11957 #if USE_NEW_PLAYER_SPEED
11958     if (player->MovPos != 0)    /* player has not yet reached destination */
11959       return;
11960 #else
11961     return;
11962 #endif
11963   }
11964   else if (!FrameReached(&player->actual_frame_counter, 1))
11965     return;
11966
11967 #if USE_NEW_PLAYER_SPEED
11968   if (player->MovPos != 0)
11969   {
11970     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11971     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11972
11973     /* before DrawPlayer() to draw correct player graphic for this case */
11974     if (player->MovPos == 0)
11975       CheckGravityMovement(player);
11976   }
11977 #else
11978   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11979   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11980
11981   /* before DrawPlayer() to draw correct player graphic for this case */
11982   if (player->MovPos == 0)
11983     CheckGravityMovement(player);
11984 #endif
11985
11986   if (player->MovPos == 0)      /* player reached destination field */
11987   {
11988     if (player->move_delay_reset_counter > 0)
11989     {
11990       player->move_delay_reset_counter--;
11991
11992       if (player->move_delay_reset_counter == 0)
11993       {
11994         /* continue with normal speed after quickly moving through gate */
11995         HALVE_PLAYER_SPEED(player);
11996
11997         /* be able to make the next move without delay */
11998         player->move_delay = 0;
11999       }
12000     }
12001
12002     player->last_jx = jx;
12003     player->last_jy = jy;
12004
12005     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12006         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12007         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12008         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12009         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12010         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12011     {
12012       DrawPlayer(player);       /* needed here only to cleanup last field */
12013       RemovePlayer(player);
12014
12015       if (local_player->friends_still_needed == 0 ||
12016           IS_SP_ELEMENT(Feld[jx][jy]))
12017         PlayerWins(player);
12018     }
12019
12020     /* this breaks one level: "machine", level 000 */
12021     {
12022       int move_direction = player->MovDir;
12023       int enter_side = MV_DIR_OPPOSITE(move_direction);
12024       int leave_side = move_direction;
12025       int old_jx = last_jx;
12026       int old_jy = last_jy;
12027       int old_element = Feld[old_jx][old_jy];
12028       int new_element = Feld[jx][jy];
12029
12030       if (IS_CUSTOM_ELEMENT(old_element))
12031         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12032                                    CE_LEFT_BY_PLAYER,
12033                                    player->index_bit, leave_side);
12034
12035       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12036                                           CE_PLAYER_LEAVES_X,
12037                                           player->index_bit, leave_side);
12038
12039       if (IS_CUSTOM_ELEMENT(new_element))
12040         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12041                                    player->index_bit, enter_side);
12042
12043       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12044                                           CE_PLAYER_ENTERS_X,
12045                                           player->index_bit, enter_side);
12046
12047       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12048                                         CE_MOVE_OF_X, move_direction);
12049     }
12050
12051     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12052     {
12053       TestIfPlayerTouchesBadThing(jx, jy);
12054       TestIfPlayerTouchesCustomElement(jx, jy);
12055
12056       /* needed because pushed element has not yet reached its destination,
12057          so it would trigger a change event at its previous field location */
12058       if (!player->is_pushing)
12059         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12060
12061       if (!player->active)
12062         RemovePlayer(player);
12063     }
12064
12065     if (!local_player->LevelSolved && level.use_step_counter)
12066     {
12067       int i;
12068
12069       TimePlayed++;
12070
12071       if (TimeLeft > 0)
12072       {
12073         TimeLeft--;
12074
12075         if (TimeLeft <= 10 && setup.time_limit)
12076           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12077
12078         DrawGameValue_Time(TimeLeft);
12079
12080         if (!TimeLeft && setup.time_limit)
12081           for (i = 0; i < MAX_PLAYERS; i++)
12082             KillPlayer(&stored_player[i]);
12083       }
12084       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12085         DrawGameValue_Time(TimePlayed);
12086     }
12087
12088     if (tape.single_step && tape.recording && !tape.pausing &&
12089         !player->programmed_action)
12090       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12091   }
12092 }
12093
12094 void ScrollScreen(struct PlayerInfo *player, int mode)
12095 {
12096   static unsigned long screen_frame_counter = 0;
12097
12098   if (mode == SCROLL_INIT)
12099   {
12100     /* set scrolling step size according to actual player's moving speed */
12101     ScrollStepSize = TILEX / player->move_delay_value;
12102
12103     screen_frame_counter = FrameCounter;
12104     ScreenMovDir = player->MovDir;
12105     ScreenMovPos = player->MovPos;
12106     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12107     return;
12108   }
12109   else if (!FrameReached(&screen_frame_counter, 1))
12110     return;
12111
12112   if (ScreenMovPos)
12113   {
12114     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12115     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12116     redraw_mask |= REDRAW_FIELD;
12117   }
12118   else
12119     ScreenMovDir = MV_NONE;
12120 }
12121
12122 void TestIfPlayerTouchesCustomElement(int x, int y)
12123 {
12124   static int xy[4][2] =
12125   {
12126     { 0, -1 },
12127     { -1, 0 },
12128     { +1, 0 },
12129     { 0, +1 }
12130   };
12131   static int trigger_sides[4][2] =
12132   {
12133     /* center side       border side */
12134     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12135     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12136     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12137     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12138   };
12139   static int touch_dir[4] =
12140   {
12141     MV_LEFT | MV_RIGHT,
12142     MV_UP   | MV_DOWN,
12143     MV_UP   | MV_DOWN,
12144     MV_LEFT | MV_RIGHT
12145   };
12146   int center_element = Feld[x][y];      /* should always be non-moving! */
12147   int i;
12148
12149   for (i = 0; i < NUM_DIRECTIONS; i++)
12150   {
12151     int xx = x + xy[i][0];
12152     int yy = y + xy[i][1];
12153     int center_side = trigger_sides[i][0];
12154     int border_side = trigger_sides[i][1];
12155     int border_element;
12156
12157     if (!IN_LEV_FIELD(xx, yy))
12158       continue;
12159
12160     if (IS_PLAYER(x, y))
12161     {
12162       struct PlayerInfo *player = PLAYERINFO(x, y);
12163
12164       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12165         border_element = Feld[xx][yy];          /* may be moving! */
12166       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12167         border_element = Feld[xx][yy];
12168       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12169         border_element = MovingOrBlocked2Element(xx, yy);
12170       else
12171         continue;               /* center and border element do not touch */
12172
12173       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12174                                  player->index_bit, border_side);
12175       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12176                                           CE_PLAYER_TOUCHES_X,
12177                                           player->index_bit, border_side);
12178     }
12179     else if (IS_PLAYER(xx, yy))
12180     {
12181       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12182
12183       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12184       {
12185         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12186           continue;             /* center and border element do not touch */
12187       }
12188
12189       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12190                                  player->index_bit, center_side);
12191       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12192                                           CE_PLAYER_TOUCHES_X,
12193                                           player->index_bit, center_side);
12194       break;
12195     }
12196   }
12197 }
12198
12199 #if USE_ELEMENT_TOUCHING_BUGFIX
12200
12201 void TestIfElementTouchesCustomElement(int x, int y)
12202 {
12203   static int xy[4][2] =
12204   {
12205     { 0, -1 },
12206     { -1, 0 },
12207     { +1, 0 },
12208     { 0, +1 }
12209   };
12210   static int trigger_sides[4][2] =
12211   {
12212     /* center side      border side */
12213     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12214     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12215     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12216     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12217   };
12218   static int touch_dir[4] =
12219   {
12220     MV_LEFT | MV_RIGHT,
12221     MV_UP   | MV_DOWN,
12222     MV_UP   | MV_DOWN,
12223     MV_LEFT | MV_RIGHT
12224   };
12225   boolean change_center_element = FALSE;
12226   int center_element = Feld[x][y];      /* should always be non-moving! */
12227   int border_element_old[NUM_DIRECTIONS];
12228   int i;
12229
12230   for (i = 0; i < NUM_DIRECTIONS; i++)
12231   {
12232     int xx = x + xy[i][0];
12233     int yy = y + xy[i][1];
12234     int border_element;
12235
12236     border_element_old[i] = -1;
12237
12238     if (!IN_LEV_FIELD(xx, yy))
12239       continue;
12240
12241     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12242       border_element = Feld[xx][yy];    /* may be moving! */
12243     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12244       border_element = Feld[xx][yy];
12245     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12246       border_element = MovingOrBlocked2Element(xx, yy);
12247     else
12248       continue;                 /* center and border element do not touch */
12249
12250     border_element_old[i] = border_element;
12251   }
12252
12253   for (i = 0; i < NUM_DIRECTIONS; i++)
12254   {
12255     int xx = x + xy[i][0];
12256     int yy = y + xy[i][1];
12257     int center_side = trigger_sides[i][0];
12258     int border_element = border_element_old[i];
12259
12260     if (border_element == -1)
12261       continue;
12262
12263     /* check for change of border element */
12264     CheckElementChangeBySide(xx, yy, border_element, center_element,
12265                              CE_TOUCHING_X, center_side);
12266   }
12267
12268   for (i = 0; i < NUM_DIRECTIONS; i++)
12269   {
12270     int border_side = trigger_sides[i][1];
12271     int border_element = border_element_old[i];
12272
12273     if (border_element == -1)
12274       continue;
12275
12276     /* check for change of center element (but change it only once) */
12277     if (!change_center_element)
12278       change_center_element =
12279         CheckElementChangeBySide(x, y, center_element, border_element,
12280                                  CE_TOUCHING_X, border_side);
12281   }
12282 }
12283
12284 #else
12285
12286 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12287 {
12288   static int xy[4][2] =
12289   {
12290     { 0, -1 },
12291     { -1, 0 },
12292     { +1, 0 },
12293     { 0, +1 }
12294   };
12295   static int trigger_sides[4][2] =
12296   {
12297     /* center side      border side */
12298     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12299     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12300     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12301     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12302   };
12303   static int touch_dir[4] =
12304   {
12305     MV_LEFT | MV_RIGHT,
12306     MV_UP   | MV_DOWN,
12307     MV_UP   | MV_DOWN,
12308     MV_LEFT | MV_RIGHT
12309   };
12310   boolean change_center_element = FALSE;
12311   int center_element = Feld[x][y];      /* should always be non-moving! */
12312   int i;
12313
12314   for (i = 0; i < NUM_DIRECTIONS; i++)
12315   {
12316     int xx = x + xy[i][0];
12317     int yy = y + xy[i][1];
12318     int center_side = trigger_sides[i][0];
12319     int border_side = trigger_sides[i][1];
12320     int border_element;
12321
12322     if (!IN_LEV_FIELD(xx, yy))
12323       continue;
12324
12325     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12326       border_element = Feld[xx][yy];    /* may be moving! */
12327     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12328       border_element = Feld[xx][yy];
12329     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12330       border_element = MovingOrBlocked2Element(xx, yy);
12331     else
12332       continue;                 /* center and border element do not touch */
12333
12334     /* check for change of center element (but change it only once) */
12335     if (!change_center_element)
12336       change_center_element =
12337         CheckElementChangeBySide(x, y, center_element, border_element,
12338                                  CE_TOUCHING_X, border_side);
12339
12340     /* check for change of border element */
12341     CheckElementChangeBySide(xx, yy, border_element, center_element,
12342                              CE_TOUCHING_X, center_side);
12343   }
12344 }
12345
12346 #endif
12347
12348 void TestIfElementHitsCustomElement(int x, int y, int direction)
12349 {
12350   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12351   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12352   int hitx = x + dx, hity = y + dy;
12353   int hitting_element = Feld[x][y];
12354   int touched_element;
12355
12356   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12357     return;
12358
12359   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12360                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12361
12362   if (IN_LEV_FIELD(hitx, hity))
12363   {
12364     int opposite_direction = MV_DIR_OPPOSITE(direction);
12365     int hitting_side = direction;
12366     int touched_side = opposite_direction;
12367     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12368                           MovDir[hitx][hity] != direction ||
12369                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12370
12371     object_hit = TRUE;
12372
12373     if (object_hit)
12374     {
12375       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12376                                CE_HITTING_X, touched_side);
12377
12378       CheckElementChangeBySide(hitx, hity, touched_element,
12379                                hitting_element, CE_HIT_BY_X, hitting_side);
12380
12381       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12382                                CE_HIT_BY_SOMETHING, opposite_direction);
12383     }
12384   }
12385
12386   /* "hitting something" is also true when hitting the playfield border */
12387   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12388                            CE_HITTING_SOMETHING, direction);
12389 }
12390
12391 #if 0
12392 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12393 {
12394   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12395   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12396   int hitx = x + dx, hity = y + dy;
12397   int hitting_element = Feld[x][y];
12398   int touched_element;
12399 #if 0
12400   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12401                         !IS_FREE(hitx, hity) &&
12402                         (!IS_MOVING(hitx, hity) ||
12403                          MovDir[hitx][hity] != direction ||
12404                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
12405 #endif
12406
12407   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12408     return;
12409
12410 #if 0
12411   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12412     return;
12413 #endif
12414
12415   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12416                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12417
12418   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12419                            EP_CAN_SMASH_EVERYTHING, direction);
12420
12421   if (IN_LEV_FIELD(hitx, hity))
12422   {
12423     int opposite_direction = MV_DIR_OPPOSITE(direction);
12424     int hitting_side = direction;
12425     int touched_side = opposite_direction;
12426 #if 0
12427     int touched_element = MovingOrBlocked2Element(hitx, hity);
12428 #endif
12429 #if 1
12430     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12431                           MovDir[hitx][hity] != direction ||
12432                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12433
12434     object_hit = TRUE;
12435 #endif
12436
12437     if (object_hit)
12438     {
12439       int i;
12440
12441       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12442                                CE_SMASHED_BY_SOMETHING, opposite_direction);
12443
12444       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12445                                CE_OTHER_IS_SMASHING, touched_side);
12446
12447       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12448                                CE_OTHER_GETS_SMASHED, hitting_side);
12449     }
12450   }
12451 }
12452 #endif
12453
12454 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12455 {
12456   int i, kill_x = -1, kill_y = -1;
12457
12458   int bad_element = -1;
12459   static int test_xy[4][2] =
12460   {
12461     { 0, -1 },
12462     { -1, 0 },
12463     { +1, 0 },
12464     { 0, +1 }
12465   };
12466   static int test_dir[4] =
12467   {
12468     MV_UP,
12469     MV_LEFT,
12470     MV_RIGHT,
12471     MV_DOWN
12472   };
12473
12474   for (i = 0; i < NUM_DIRECTIONS; i++)
12475   {
12476     int test_x, test_y, test_move_dir, test_element;
12477
12478     test_x = good_x + test_xy[i][0];
12479     test_y = good_y + test_xy[i][1];
12480
12481     if (!IN_LEV_FIELD(test_x, test_y))
12482       continue;
12483
12484     test_move_dir =
12485       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12486
12487     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12488
12489     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12490        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12491     */
12492     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12493         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12494     {
12495       kill_x = test_x;
12496       kill_y = test_y;
12497       bad_element = test_element;
12498
12499       break;
12500     }
12501   }
12502
12503   if (kill_x != -1 || kill_y != -1)
12504   {
12505     if (IS_PLAYER(good_x, good_y))
12506     {
12507       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12508
12509       if (player->shield_deadly_time_left > 0 &&
12510           !IS_INDESTRUCTIBLE(bad_element))
12511         Bang(kill_x, kill_y);
12512       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12513         KillPlayer(player);
12514     }
12515     else
12516       Bang(good_x, good_y);
12517   }
12518 }
12519
12520 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12521 {
12522   int i, kill_x = -1, kill_y = -1;
12523   int bad_element = Feld[bad_x][bad_y];
12524   static int test_xy[4][2] =
12525   {
12526     { 0, -1 },
12527     { -1, 0 },
12528     { +1, 0 },
12529     { 0, +1 }
12530   };
12531   static int touch_dir[4] =
12532   {
12533     MV_LEFT | MV_RIGHT,
12534     MV_UP   | MV_DOWN,
12535     MV_UP   | MV_DOWN,
12536     MV_LEFT | MV_RIGHT
12537   };
12538   static int test_dir[4] =
12539   {
12540     MV_UP,
12541     MV_LEFT,
12542     MV_RIGHT,
12543     MV_DOWN
12544   };
12545
12546   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12547     return;
12548
12549   for (i = 0; i < NUM_DIRECTIONS; i++)
12550   {
12551     int test_x, test_y, test_move_dir, test_element;
12552
12553     test_x = bad_x + test_xy[i][0];
12554     test_y = bad_y + test_xy[i][1];
12555     if (!IN_LEV_FIELD(test_x, test_y))
12556       continue;
12557
12558     test_move_dir =
12559       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12560
12561     test_element = Feld[test_x][test_y];
12562
12563     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12564        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12565     */
12566     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12567         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12568     {
12569       /* good thing is player or penguin that does not move away */
12570       if (IS_PLAYER(test_x, test_y))
12571       {
12572         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12573
12574         if (bad_element == EL_ROBOT && player->is_moving)
12575           continue;     /* robot does not kill player if he is moving */
12576
12577         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12578         {
12579           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12580             continue;           /* center and border element do not touch */
12581         }
12582
12583         kill_x = test_x;
12584         kill_y = test_y;
12585         break;
12586       }
12587       else if (test_element == EL_PENGUIN)
12588       {
12589         kill_x = test_x;
12590         kill_y = test_y;
12591         break;
12592       }
12593     }
12594   }
12595
12596   if (kill_x != -1 || kill_y != -1)
12597   {
12598     if (IS_PLAYER(kill_x, kill_y))
12599     {
12600       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12601
12602       if (player->shield_deadly_time_left > 0 &&
12603           !IS_INDESTRUCTIBLE(bad_element))
12604         Bang(bad_x, bad_y);
12605       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12606         KillPlayer(player);
12607     }
12608     else
12609       Bang(kill_x, kill_y);
12610   }
12611 }
12612
12613 void TestIfPlayerTouchesBadThing(int x, int y)
12614 {
12615   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12616 }
12617
12618 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12619 {
12620   TestIfGoodThingHitsBadThing(x, y, move_dir);
12621 }
12622
12623 void TestIfBadThingTouchesPlayer(int x, int y)
12624 {
12625   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12626 }
12627
12628 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12629 {
12630   TestIfBadThingHitsGoodThing(x, y, move_dir);
12631 }
12632
12633 void TestIfFriendTouchesBadThing(int x, int y)
12634 {
12635   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12636 }
12637
12638 void TestIfBadThingTouchesFriend(int x, int y)
12639 {
12640   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12641 }
12642
12643 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12644 {
12645   int i, kill_x = bad_x, kill_y = bad_y;
12646   static int xy[4][2] =
12647   {
12648     { 0, -1 },
12649     { -1, 0 },
12650     { +1, 0 },
12651     { 0, +1 }
12652   };
12653
12654   for (i = 0; i < NUM_DIRECTIONS; i++)
12655   {
12656     int x, y, element;
12657
12658     x = bad_x + xy[i][0];
12659     y = bad_y + xy[i][1];
12660     if (!IN_LEV_FIELD(x, y))
12661       continue;
12662
12663     element = Feld[x][y];
12664     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12665         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12666     {
12667       kill_x = x;
12668       kill_y = y;
12669       break;
12670     }
12671   }
12672
12673   if (kill_x != bad_x || kill_y != bad_y)
12674     Bang(bad_x, bad_y);
12675 }
12676
12677 void KillPlayer(struct PlayerInfo *player)
12678 {
12679   int jx = player->jx, jy = player->jy;
12680
12681   if (!player->active)
12682     return;
12683
12684   /* the following code was introduced to prevent an infinite loop when calling
12685      -> Bang()
12686      -> CheckTriggeredElementChangeExt()
12687      -> ExecuteCustomElementAction()
12688      -> KillPlayer()
12689      -> (infinitely repeating the above sequence of function calls)
12690      which occurs when killing the player while having a CE with the setting
12691      "kill player X when explosion of <player X>"; the solution using a new
12692      field "player->killed" was chosen for backwards compatibility, although
12693      clever use of the fields "player->active" etc. would probably also work */
12694 #if 1
12695   if (player->killed)
12696     return;
12697 #endif
12698
12699   player->killed = TRUE;
12700
12701   /* remove accessible field at the player's position */
12702   Feld[jx][jy] = EL_EMPTY;
12703
12704   /* deactivate shield (else Bang()/Explode() would not work right) */
12705   player->shield_normal_time_left = 0;
12706   player->shield_deadly_time_left = 0;
12707
12708   Bang(jx, jy);
12709   BuryPlayer(player);
12710 }
12711
12712 static void KillPlayerUnlessEnemyProtected(int x, int y)
12713 {
12714   if (!PLAYER_ENEMY_PROTECTED(x, y))
12715     KillPlayer(PLAYERINFO(x, y));
12716 }
12717
12718 static void KillPlayerUnlessExplosionProtected(int x, int y)
12719 {
12720   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
12721     KillPlayer(PLAYERINFO(x, y));
12722 }
12723
12724 void BuryPlayer(struct PlayerInfo *player)
12725 {
12726   int jx = player->jx, jy = player->jy;
12727
12728   if (!player->active)
12729     return;
12730
12731   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
12732   PlayLevelSound(jx, jy, SND_GAME_LOSING);
12733
12734   player->GameOver = TRUE;
12735   RemovePlayer(player);
12736 }
12737
12738 void RemovePlayer(struct PlayerInfo *player)
12739 {
12740   int jx = player->jx, jy = player->jy;
12741   int i, found = FALSE;
12742
12743   player->present = FALSE;
12744   player->active = FALSE;
12745
12746   if (!ExplodeField[jx][jy])
12747     StorePlayer[jx][jy] = 0;
12748
12749   if (player->is_moving)
12750     DrawLevelField(player->last_jx, player->last_jy);
12751
12752   for (i = 0; i < MAX_PLAYERS; i++)
12753     if (stored_player[i].active)
12754       found = TRUE;
12755
12756   if (!found)
12757     AllPlayersGone = TRUE;
12758
12759   ExitX = ZX = jx;
12760   ExitY = ZY = jy;
12761 }
12762
12763 #if USE_NEW_SNAP_DELAY
12764 static void setFieldForSnapping(int x, int y, int element, int direction)
12765 {
12766   struct ElementInfo *ei = &element_info[element];
12767   int direction_bit = MV_DIR_TO_BIT(direction);
12768   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
12769   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
12770                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
12771
12772   Feld[x][y] = EL_ELEMENT_SNAPPING;
12773   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
12774
12775   ResetGfxAnimation(x, y);
12776
12777   GfxElement[x][y] = element;
12778   GfxAction[x][y] = action;
12779   GfxDir[x][y] = direction;
12780   GfxFrame[x][y] = -1;
12781 }
12782 #endif
12783
12784 /*
12785   =============================================================================
12786   checkDiagonalPushing()
12787   -----------------------------------------------------------------------------
12788   check if diagonal input device direction results in pushing of object
12789   (by checking if the alternative direction is walkable, diggable, ...)
12790   =============================================================================
12791 */
12792
12793 static boolean checkDiagonalPushing(struct PlayerInfo *player,
12794                                     int x, int y, int real_dx, int real_dy)
12795 {
12796   int jx, jy, dx, dy, xx, yy;
12797
12798   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
12799     return TRUE;
12800
12801   /* diagonal direction: check alternative direction */
12802   jx = player->jx;
12803   jy = player->jy;
12804   dx = x - jx;
12805   dy = y - jy;
12806   xx = jx + (dx == 0 ? real_dx : 0);
12807   yy = jy + (dy == 0 ? real_dy : 0);
12808
12809   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
12810 }
12811
12812 /*
12813   =============================================================================
12814   DigField()
12815   -----------------------------------------------------------------------------
12816   x, y:                 field next to player (non-diagonal) to try to dig to
12817   real_dx, real_dy:     direction as read from input device (can be diagonal)
12818   =============================================================================
12819 */
12820
12821 int DigField(struct PlayerInfo *player,
12822              int oldx, int oldy, int x, int y,
12823              int real_dx, int real_dy, int mode)
12824 {
12825   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
12826   boolean player_was_pushing = player->is_pushing;
12827   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
12828   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
12829   int jx = oldx, jy = oldy;
12830   int dx = x - jx, dy = y - jy;
12831   int nextx = x + dx, nexty = y + dy;
12832   int move_direction = (dx == -1 ? MV_LEFT  :
12833                         dx == +1 ? MV_RIGHT :
12834                         dy == -1 ? MV_UP    :
12835                         dy == +1 ? MV_DOWN  : MV_NONE);
12836   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
12837   int dig_side = MV_DIR_OPPOSITE(move_direction);
12838   int old_element = Feld[jx][jy];
12839 #if USE_FIXED_DONT_RUN_INTO
12840   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
12841 #else
12842   int element;
12843 #endif
12844   int collect_count;
12845
12846   if (is_player)                /* function can also be called by EL_PENGUIN */
12847   {
12848     if (player->MovPos == 0)
12849     {
12850       player->is_digging = FALSE;
12851       player->is_collecting = FALSE;
12852     }
12853
12854     if (player->MovPos == 0)    /* last pushing move finished */
12855       player->is_pushing = FALSE;
12856
12857     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
12858     {
12859       player->is_switching = FALSE;
12860       player->push_delay = -1;
12861
12862       return MP_NO_ACTION;
12863     }
12864   }
12865
12866 #if !USE_FIXED_DONT_RUN_INTO
12867   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12868     return MP_NO_ACTION;
12869 #endif
12870
12871   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
12872     old_element = Back[jx][jy];
12873
12874   /* in case of element dropped at player position, check background */
12875   else if (Back[jx][jy] != EL_EMPTY &&
12876            game.engine_version >= VERSION_IDENT(2,2,0,0))
12877     old_element = Back[jx][jy];
12878
12879   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12880     return MP_NO_ACTION;        /* field has no opening in this direction */
12881
12882   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12883     return MP_NO_ACTION;        /* field has no opening in this direction */
12884
12885 #if USE_FIXED_DONT_RUN_INTO
12886   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12887   {
12888     SplashAcid(x, y);
12889
12890     Feld[jx][jy] = player->artwork_element;
12891     InitMovingField(jx, jy, MV_DOWN);
12892     Store[jx][jy] = EL_ACID;
12893     ContinueMoving(jx, jy);
12894     BuryPlayer(player);
12895
12896     return MP_DONT_RUN_INTO;
12897   }
12898 #endif
12899
12900 #if USE_FIXED_DONT_RUN_INTO
12901   if (player_can_move && DONT_RUN_INTO(element))
12902   {
12903     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12904
12905     return MP_DONT_RUN_INTO;
12906   }
12907 #endif
12908
12909 #if USE_FIXED_DONT_RUN_INTO
12910   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12911     return MP_NO_ACTION;
12912 #endif
12913
12914 #if !USE_FIXED_DONT_RUN_INTO
12915   element = Feld[x][y];
12916 #endif
12917
12918   collect_count = element_info[element].collect_count_initial;
12919
12920   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
12921     return MP_NO_ACTION;
12922
12923   if (game.engine_version < VERSION_IDENT(2,2,0,0))
12924     player_can_move = player_can_move_or_snap;
12925
12926   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
12927       game.engine_version >= VERSION_IDENT(2,2,0,0))
12928   {
12929     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
12930                                player->index_bit, dig_side);
12931     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12932                                         player->index_bit, dig_side);
12933
12934     if (element == EL_DC_LANDMINE)
12935       Bang(x, y);
12936
12937     if (Feld[x][y] != element)          /* field changed by snapping */
12938       return MP_ACTION;
12939
12940     return MP_NO_ACTION;
12941   }
12942
12943 #if USE_PLAYER_GRAVITY
12944   if (player->gravity && is_player && !player->is_auto_moving &&
12945       canFallDown(player) && move_direction != MV_DOWN &&
12946       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12947     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12948 #else
12949   if (game.gravity && is_player && !player->is_auto_moving &&
12950       canFallDown(player) && move_direction != MV_DOWN &&
12951       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12952     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12953 #endif
12954
12955   if (player_can_move &&
12956       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12957   {
12958     int sound_element = SND_ELEMENT(element);
12959     int sound_action = ACTION_WALKING;
12960
12961     if (IS_RND_GATE(element))
12962     {
12963       if (!player->key[RND_GATE_NR(element)])
12964         return MP_NO_ACTION;
12965     }
12966     else if (IS_RND_GATE_GRAY(element))
12967     {
12968       if (!player->key[RND_GATE_GRAY_NR(element)])
12969         return MP_NO_ACTION;
12970     }
12971     else if (IS_RND_GATE_GRAY_ACTIVE(element))
12972     {
12973       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
12974         return MP_NO_ACTION;
12975     }
12976     else if (element == EL_EXIT_OPEN ||
12977              element == EL_EM_EXIT_OPEN ||
12978              element == EL_STEEL_EXIT_OPEN ||
12979              element == EL_EM_STEEL_EXIT_OPEN ||
12980              element == EL_SP_EXIT_OPEN ||
12981              element == EL_SP_EXIT_OPENING)
12982     {
12983       sound_action = ACTION_PASSING;    /* player is passing exit */
12984     }
12985     else if (element == EL_EMPTY)
12986     {
12987       sound_action = ACTION_MOVING;             /* nothing to walk on */
12988     }
12989
12990     /* play sound from background or player, whatever is available */
12991     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12992       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12993     else
12994       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
12995   }
12996   else if (player_can_move &&
12997            IS_PASSABLE(element) && canPassField(x, y, move_direction))
12998   {
12999     if (!ACCESS_FROM(element, opposite_direction))
13000       return MP_NO_ACTION;      /* field not accessible from this direction */
13001
13002     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13003       return MP_NO_ACTION;
13004
13005     if (IS_EM_GATE(element))
13006     {
13007       if (!player->key[EM_GATE_NR(element)])
13008         return MP_NO_ACTION;
13009     }
13010     else if (IS_EM_GATE_GRAY(element))
13011     {
13012       if (!player->key[EM_GATE_GRAY_NR(element)])
13013         return MP_NO_ACTION;
13014     }
13015     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13016     {
13017       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13018         return MP_NO_ACTION;
13019     }
13020     else if (IS_EMC_GATE(element))
13021     {
13022       if (!player->key[EMC_GATE_NR(element)])
13023         return MP_NO_ACTION;
13024     }
13025     else if (IS_EMC_GATE_GRAY(element))
13026     {
13027       if (!player->key[EMC_GATE_GRAY_NR(element)])
13028         return MP_NO_ACTION;
13029     }
13030     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13031     {
13032       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13033         return MP_NO_ACTION;
13034     }
13035     else if (element == EL_DC_GATE_WHITE ||
13036              element == EL_DC_GATE_WHITE_GRAY ||
13037              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13038     {
13039       if (player->num_white_keys == 0)
13040         return MP_NO_ACTION;
13041
13042       player->num_white_keys--;
13043     }
13044     else if (IS_SP_PORT(element))
13045     {
13046       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13047           element == EL_SP_GRAVITY_PORT_RIGHT ||
13048           element == EL_SP_GRAVITY_PORT_UP ||
13049           element == EL_SP_GRAVITY_PORT_DOWN)
13050 #if USE_PLAYER_GRAVITY
13051         player->gravity = !player->gravity;
13052 #else
13053         game.gravity = !game.gravity;
13054 #endif
13055       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13056                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13057                element == EL_SP_GRAVITY_ON_PORT_UP ||
13058                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13059 #if USE_PLAYER_GRAVITY
13060         player->gravity = TRUE;
13061 #else
13062         game.gravity = TRUE;
13063 #endif
13064       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13065                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13066                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13067                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13068 #if USE_PLAYER_GRAVITY
13069         player->gravity = FALSE;
13070 #else
13071         game.gravity = FALSE;
13072 #endif
13073     }
13074
13075     /* automatically move to the next field with double speed */
13076     player->programmed_action = move_direction;
13077
13078     if (player->move_delay_reset_counter == 0)
13079     {
13080       player->move_delay_reset_counter = 2;     /* two double speed steps */
13081
13082       DOUBLE_PLAYER_SPEED(player);
13083     }
13084
13085     PlayLevelSoundAction(x, y, ACTION_PASSING);
13086   }
13087   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13088   {
13089     RemoveField(x, y);
13090
13091     if (mode != DF_SNAP)
13092     {
13093       GfxElement[x][y] = GFX_ELEMENT(element);
13094       player->is_digging = TRUE;
13095     }
13096
13097     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13098
13099     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13100                                         player->index_bit, dig_side);
13101
13102     if (mode == DF_SNAP)
13103     {
13104 #if USE_NEW_SNAP_DELAY
13105       if (level.block_snap_field)
13106         setFieldForSnapping(x, y, element, move_direction);
13107       else
13108         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13109 #else
13110       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13111 #endif
13112
13113       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13114                                           player->index_bit, dig_side);
13115     }
13116   }
13117   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13118   {
13119     RemoveField(x, y);
13120
13121     if (is_player && mode != DF_SNAP)
13122     {
13123       GfxElement[x][y] = element;
13124       player->is_collecting = TRUE;
13125     }
13126
13127     if (element == EL_SPEED_PILL)
13128     {
13129       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13130     }
13131     else if (element == EL_EXTRA_TIME && level.time > 0)
13132     {
13133       TimeLeft += level.extra_time;
13134       DrawGameValue_Time(TimeLeft);
13135     }
13136     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13137     {
13138       player->shield_normal_time_left += level.shield_normal_time;
13139       if (element == EL_SHIELD_DEADLY)
13140         player->shield_deadly_time_left += level.shield_deadly_time;
13141     }
13142     else if (element == EL_DYNAMITE ||
13143              element == EL_EM_DYNAMITE ||
13144              element == EL_SP_DISK_RED)
13145     {
13146       if (player->inventory_size < MAX_INVENTORY_SIZE)
13147         player->inventory_element[player->inventory_size++] = element;
13148
13149       DrawGameDoorValues();
13150     }
13151     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13152     {
13153       player->dynabomb_count++;
13154       player->dynabombs_left++;
13155     }
13156     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13157     {
13158       player->dynabomb_size++;
13159     }
13160     else if (element == EL_DYNABOMB_INCREASE_POWER)
13161     {
13162       player->dynabomb_xl = TRUE;
13163     }
13164     else if (IS_KEY(element))
13165     {
13166       player->key[KEY_NR(element)] = TRUE;
13167
13168       DrawGameDoorValues();
13169     }
13170     else if (element == EL_DC_KEY_WHITE)
13171     {
13172       player->num_white_keys++;
13173
13174       /* display white keys? */
13175       /* DrawGameDoorValues(); */
13176     }
13177     else if (IS_ENVELOPE(element))
13178     {
13179       player->show_envelope = element;
13180     }
13181     else if (element == EL_EMC_LENSES)
13182     {
13183       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13184
13185       RedrawAllInvisibleElementsForLenses();
13186     }
13187     else if (element == EL_EMC_MAGNIFIER)
13188     {
13189       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13190
13191       RedrawAllInvisibleElementsForMagnifier();
13192     }
13193     else if (IS_DROPPABLE(element) ||
13194              IS_THROWABLE(element))     /* can be collected and dropped */
13195     {
13196       int i;
13197
13198       if (collect_count == 0)
13199         player->inventory_infinite_element = element;
13200       else
13201         for (i = 0; i < collect_count; i++)
13202           if (player->inventory_size < MAX_INVENTORY_SIZE)
13203             player->inventory_element[player->inventory_size++] = element;
13204
13205       DrawGameDoorValues();
13206     }
13207     else if (collect_count > 0)
13208     {
13209       local_player->gems_still_needed -= collect_count;
13210       if (local_player->gems_still_needed < 0)
13211         local_player->gems_still_needed = 0;
13212
13213       DrawGameValue_Emeralds(local_player->gems_still_needed);
13214     }
13215
13216     RaiseScoreElement(element);
13217     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13218
13219     if (is_player)
13220       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13221                                           player->index_bit, dig_side);
13222
13223     if (mode == DF_SNAP)
13224     {
13225 #if USE_NEW_SNAP_DELAY
13226       if (level.block_snap_field)
13227         setFieldForSnapping(x, y, element, move_direction);
13228       else
13229         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13230 #else
13231       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13232 #endif
13233
13234       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13235                                           player->index_bit, dig_side);
13236     }
13237   }
13238   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13239   {
13240     if (mode == DF_SNAP && element != EL_BD_ROCK)
13241       return MP_NO_ACTION;
13242
13243     if (CAN_FALL(element) && dy)
13244       return MP_NO_ACTION;
13245
13246     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13247         !(element == EL_SPRING && level.use_spring_bug))
13248       return MP_NO_ACTION;
13249
13250     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13251         ((move_direction & MV_VERTICAL &&
13252           ((element_info[element].move_pattern & MV_LEFT &&
13253             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13254            (element_info[element].move_pattern & MV_RIGHT &&
13255             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13256          (move_direction & MV_HORIZONTAL &&
13257           ((element_info[element].move_pattern & MV_UP &&
13258             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13259            (element_info[element].move_pattern & MV_DOWN &&
13260             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13261       return MP_NO_ACTION;
13262
13263     /* do not push elements already moving away faster than player */
13264     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13265         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13266       return MP_NO_ACTION;
13267
13268     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13269     {
13270       if (player->push_delay_value == -1 || !player_was_pushing)
13271         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13272     }
13273     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13274     {
13275       if (player->push_delay_value == -1)
13276         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13277     }
13278     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13279     {
13280       if (!player->is_pushing)
13281         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13282     }
13283
13284     player->is_pushing = TRUE;
13285     player->is_active = TRUE;
13286
13287     if (!(IN_LEV_FIELD(nextx, nexty) &&
13288           (IS_FREE(nextx, nexty) ||
13289            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13290             IS_SB_ELEMENT(element)))))
13291       return MP_NO_ACTION;
13292
13293     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13294       return MP_NO_ACTION;
13295
13296     if (player->push_delay == -1)       /* new pushing; restart delay */
13297       player->push_delay = 0;
13298
13299     if (player->push_delay < player->push_delay_value &&
13300         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13301         element != EL_SPRING && element != EL_BALLOON)
13302     {
13303       /* make sure that there is no move delay before next try to push */
13304       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13305         player->move_delay = 0;
13306
13307       return MP_NO_ACTION;
13308     }
13309
13310     if (IS_SB_ELEMENT(element))
13311     {
13312       if (element == EL_SOKOBAN_FIELD_FULL)
13313       {
13314         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13315         local_player->sokobanfields_still_needed++;
13316       }
13317
13318       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13319       {
13320         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13321         local_player->sokobanfields_still_needed--;
13322       }
13323
13324       Feld[x][y] = EL_SOKOBAN_OBJECT;
13325
13326       if (Back[x][y] == Back[nextx][nexty])
13327         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13328       else if (Back[x][y] != 0)
13329         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13330                                     ACTION_EMPTYING);
13331       else
13332         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13333                                     ACTION_FILLING);
13334
13335       if (local_player->sokobanfields_still_needed == 0 &&
13336           game.emulation == EMU_SOKOBAN)
13337       {
13338         PlayerWins(player);
13339
13340         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13341       }
13342     }
13343     else
13344       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13345
13346     InitMovingField(x, y, move_direction);
13347     GfxAction[x][y] = ACTION_PUSHING;
13348
13349     if (mode == DF_SNAP)
13350       ContinueMoving(x, y);
13351     else
13352       MovPos[x][y] = (dx != 0 ? dx : dy);
13353
13354     Pushed[x][y] = TRUE;
13355     Pushed[nextx][nexty] = TRUE;
13356
13357     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13358       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13359     else
13360       player->push_delay_value = -1;    /* get new value later */
13361
13362     /* check for element change _after_ element has been pushed */
13363     if (game.use_change_when_pushing_bug)
13364     {
13365       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13366                                  player->index_bit, dig_side);
13367       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13368                                           player->index_bit, dig_side);
13369     }
13370   }
13371   else if (IS_SWITCHABLE(element))
13372   {
13373     if (PLAYER_SWITCHING(player, x, y))
13374     {
13375       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13376                                           player->index_bit, dig_side);
13377
13378       return MP_ACTION;
13379     }
13380
13381     player->is_switching = TRUE;
13382     player->switch_x = x;
13383     player->switch_y = y;
13384
13385     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13386
13387     if (element == EL_ROBOT_WHEEL)
13388     {
13389       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13390       ZX = x;
13391       ZY = y;
13392
13393       DrawLevelField(x, y);
13394     }
13395     else if (element == EL_SP_TERMINAL)
13396     {
13397       int xx, yy;
13398
13399       SCAN_PLAYFIELD(xx, yy)
13400       {
13401         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13402           Bang(xx, yy);
13403         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13404           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13405       }
13406     }
13407     else if (IS_BELT_SWITCH(element))
13408     {
13409       ToggleBeltSwitch(x, y);
13410     }
13411     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13412              element == EL_SWITCHGATE_SWITCH_DOWN ||
13413              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13414              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13415     {
13416       ToggleSwitchgateSwitch(x, y);
13417     }
13418     else if (element == EL_LIGHT_SWITCH ||
13419              element == EL_LIGHT_SWITCH_ACTIVE)
13420     {
13421       ToggleLightSwitch(x, y);
13422     }
13423     else if (element == EL_TIMEGATE_SWITCH ||
13424              element == EL_DC_TIMEGATE_SWITCH)
13425     {
13426       ActivateTimegateSwitch(x, y);
13427     }
13428     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13429              element == EL_BALLOON_SWITCH_RIGHT ||
13430              element == EL_BALLOON_SWITCH_UP    ||
13431              element == EL_BALLOON_SWITCH_DOWN  ||
13432              element == EL_BALLOON_SWITCH_NONE  ||
13433              element == EL_BALLOON_SWITCH_ANY)
13434     {
13435       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13436                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13437                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13438                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13439                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13440                              move_direction);
13441     }
13442     else if (element == EL_LAMP)
13443     {
13444       Feld[x][y] = EL_LAMP_ACTIVE;
13445       local_player->lights_still_needed--;
13446
13447       ResetGfxAnimation(x, y);
13448       DrawLevelField(x, y);
13449     }
13450     else if (element == EL_TIME_ORB_FULL)
13451     {
13452       Feld[x][y] = EL_TIME_ORB_EMPTY;
13453
13454       if (level.time > 0 || level.use_time_orb_bug)
13455       {
13456         TimeLeft += level.time_orb_time;
13457         DrawGameValue_Time(TimeLeft);
13458       }
13459
13460       ResetGfxAnimation(x, y);
13461       DrawLevelField(x, y);
13462     }
13463     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13464              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13465     {
13466       int xx, yy;
13467
13468       game.ball_state = !game.ball_state;
13469
13470       SCAN_PLAYFIELD(xx, yy)
13471       {
13472         int e = Feld[xx][yy];
13473
13474         if (game.ball_state)
13475         {
13476           if (e == EL_EMC_MAGIC_BALL)
13477             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13478           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13479             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13480         }
13481         else
13482         {
13483           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13484             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13485           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13486             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13487         }
13488       }
13489     }
13490
13491     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13492                                         player->index_bit, dig_side);
13493
13494     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13495                                         player->index_bit, dig_side);
13496
13497     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13498                                         player->index_bit, dig_side);
13499
13500     return MP_ACTION;
13501   }
13502   else
13503   {
13504     if (!PLAYER_SWITCHING(player, x, y))
13505     {
13506       player->is_switching = TRUE;
13507       player->switch_x = x;
13508       player->switch_y = y;
13509
13510       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13511                                  player->index_bit, dig_side);
13512       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13513                                           player->index_bit, dig_side);
13514
13515       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13516                                  player->index_bit, dig_side);
13517       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13518                                           player->index_bit, dig_side);
13519     }
13520
13521     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13522                                player->index_bit, dig_side);
13523     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13524                                         player->index_bit, dig_side);
13525
13526     return MP_NO_ACTION;
13527   }
13528
13529   player->push_delay = -1;
13530
13531   if (is_player)                /* function can also be called by EL_PENGUIN */
13532   {
13533     if (Feld[x][y] != element)          /* really digged/collected something */
13534     {
13535       player->is_collecting = !player->is_digging;
13536       player->is_active = TRUE;
13537     }
13538   }
13539
13540   return MP_MOVING;
13541 }
13542
13543 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13544 {
13545   int jx = player->jx, jy = player->jy;
13546   int x = jx + dx, y = jy + dy;
13547   int snap_direction = (dx == -1 ? MV_LEFT  :
13548                         dx == +1 ? MV_RIGHT :
13549                         dy == -1 ? MV_UP    :
13550                         dy == +1 ? MV_DOWN  : MV_NONE);
13551   boolean can_continue_snapping = (level.continuous_snapping &&
13552                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13553
13554   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13555     return FALSE;
13556
13557   if (!player->active || !IN_LEV_FIELD(x, y))
13558     return FALSE;
13559
13560   if (dx && dy)
13561     return FALSE;
13562
13563   if (!dx && !dy)
13564   {
13565     if (player->MovPos == 0)
13566       player->is_pushing = FALSE;
13567
13568     player->is_snapping = FALSE;
13569
13570     if (player->MovPos == 0)
13571     {
13572       player->is_moving = FALSE;
13573       player->is_digging = FALSE;
13574       player->is_collecting = FALSE;
13575     }
13576
13577     return FALSE;
13578   }
13579
13580 #if USE_NEW_CONTINUOUS_SNAPPING
13581   /* prevent snapping with already pressed snap key when not allowed */
13582   if (player->is_snapping && !can_continue_snapping)
13583     return FALSE;
13584 #else
13585   if (player->is_snapping)
13586     return FALSE;
13587 #endif
13588
13589   player->MovDir = snap_direction;
13590
13591   if (player->MovPos == 0)
13592   {
13593     player->is_moving = FALSE;
13594     player->is_digging = FALSE;
13595     player->is_collecting = FALSE;
13596   }
13597
13598   player->is_dropping = FALSE;
13599   player->is_dropping_pressed = FALSE;
13600   player->drop_pressed_delay = 0;
13601
13602   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13603     return FALSE;
13604
13605   player->is_snapping = TRUE;
13606   player->is_active = TRUE;
13607
13608   if (player->MovPos == 0)
13609   {
13610     player->is_moving = FALSE;
13611     player->is_digging = FALSE;
13612     player->is_collecting = FALSE;
13613   }
13614
13615   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13616     DrawLevelField(player->last_jx, player->last_jy);
13617
13618   DrawLevelField(x, y);
13619
13620   return TRUE;
13621 }
13622
13623 boolean DropElement(struct PlayerInfo *player)
13624 {
13625   int old_element, new_element;
13626   int dropx = player->jx, dropy = player->jy;
13627   int drop_direction = player->MovDir;
13628   int drop_side = drop_direction;
13629   int drop_element = (player->inventory_size > 0 ?
13630                       player->inventory_element[player->inventory_size - 1] :
13631                       player->inventory_infinite_element != EL_UNDEFINED ?
13632                       player->inventory_infinite_element :
13633                       player->dynabombs_left > 0 ?
13634                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
13635                       EL_UNDEFINED);
13636
13637   player->is_dropping_pressed = TRUE;
13638
13639   /* do not drop an element on top of another element; when holding drop key
13640      pressed without moving, dropped element must move away before the next
13641      element can be dropped (this is especially important if the next element
13642      is dynamite, which can be placed on background for historical reasons) */
13643   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13644     return MP_ACTION;
13645
13646   if (IS_THROWABLE(drop_element))
13647   {
13648     dropx += GET_DX_FROM_DIR(drop_direction);
13649     dropy += GET_DY_FROM_DIR(drop_direction);
13650
13651     if (!IN_LEV_FIELD(dropx, dropy))
13652       return FALSE;
13653   }
13654
13655   old_element = Feld[dropx][dropy];     /* old element at dropping position */
13656   new_element = drop_element;           /* default: no change when dropping */
13657
13658   /* check if player is active, not moving and ready to drop */
13659   if (!player->active || player->MovPos || player->drop_delay > 0)
13660     return FALSE;
13661
13662   /* check if player has anything that can be dropped */
13663   if (new_element == EL_UNDEFINED)
13664     return FALSE;
13665
13666   /* check if drop key was pressed long enough for EM style dynamite */
13667   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13668     return FALSE;
13669
13670   /* check if anything can be dropped at the current position */
13671   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13672     return FALSE;
13673
13674   /* collected custom elements can only be dropped on empty fields */
13675   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13676     return FALSE;
13677
13678   if (old_element != EL_EMPTY)
13679     Back[dropx][dropy] = old_element;   /* store old element on this field */
13680
13681   ResetGfxAnimation(dropx, dropy);
13682   ResetRandomAnimationValue(dropx, dropy);
13683
13684   if (player->inventory_size > 0 ||
13685       player->inventory_infinite_element != EL_UNDEFINED)
13686   {
13687     if (player->inventory_size > 0)
13688     {
13689       player->inventory_size--;
13690
13691       DrawGameDoorValues();
13692
13693       if (new_element == EL_DYNAMITE)
13694         new_element = EL_DYNAMITE_ACTIVE;
13695       else if (new_element == EL_EM_DYNAMITE)
13696         new_element = EL_EM_DYNAMITE_ACTIVE;
13697       else if (new_element == EL_SP_DISK_RED)
13698         new_element = EL_SP_DISK_RED_ACTIVE;
13699     }
13700
13701     Feld[dropx][dropy] = new_element;
13702
13703     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13704       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13705                           el2img(Feld[dropx][dropy]), 0);
13706
13707     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13708
13709     /* needed if previous element just changed to "empty" in the last frame */
13710     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
13711
13712     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13713                                player->index_bit, drop_side);
13714     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13715                                         CE_PLAYER_DROPS_X,
13716                                         player->index_bit, drop_side);
13717
13718     TestIfElementTouchesCustomElement(dropx, dropy);
13719   }
13720   else          /* player is dropping a dyna bomb */
13721   {
13722     player->dynabombs_left--;
13723
13724     Feld[dropx][dropy] = new_element;
13725
13726     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13727       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13728                           el2img(Feld[dropx][dropy]), 0);
13729
13730     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13731   }
13732
13733   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
13734     InitField_WithBug1(dropx, dropy, FALSE);
13735
13736   new_element = Feld[dropx][dropy];     /* element might have changed */
13737
13738   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
13739       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
13740   {
13741     int move_direction, nextx, nexty;
13742
13743     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
13744       MovDir[dropx][dropy] = drop_direction;
13745
13746     move_direction = MovDir[dropx][dropy];
13747     nextx = dropx + GET_DX_FROM_DIR(move_direction);
13748     nexty = dropy + GET_DY_FROM_DIR(move_direction);
13749
13750     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
13751
13752 #if USE_FIX_IMPACT_COLLISION
13753     /* do not cause impact style collision by dropping elements that can fall */
13754     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13755 #else
13756     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13757 #endif
13758   }
13759
13760   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
13761   player->is_dropping = TRUE;
13762
13763   player->drop_pressed_delay = 0;
13764   player->is_dropping_pressed = FALSE;
13765
13766   player->drop_x = dropx;
13767   player->drop_y = dropy;
13768
13769   return TRUE;
13770 }
13771
13772 /* ------------------------------------------------------------------------- */
13773 /* game sound playing functions                                              */
13774 /* ------------------------------------------------------------------------- */
13775
13776 static int *loop_sound_frame = NULL;
13777 static int *loop_sound_volume = NULL;
13778
13779 void InitPlayLevelSound()
13780 {
13781   int num_sounds = getSoundListSize();
13782
13783   checked_free(loop_sound_frame);
13784   checked_free(loop_sound_volume);
13785
13786   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
13787   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
13788 }
13789
13790 static void PlayLevelSound(int x, int y, int nr)
13791 {
13792   int sx = SCREENX(x), sy = SCREENY(y);
13793   int volume, stereo_position;
13794   int max_distance = 8;
13795   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
13796
13797   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
13798       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
13799     return;
13800
13801   if (!IN_LEV_FIELD(x, y) ||
13802       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
13803       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
13804     return;
13805
13806   volume = SOUND_MAX_VOLUME;
13807
13808   if (!IN_SCR_FIELD(sx, sy))
13809   {
13810     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
13811     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
13812
13813     volume -= volume * (dx > dy ? dx : dy) / max_distance;
13814   }
13815
13816   stereo_position = (SOUND_MAX_LEFT +
13817                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
13818                      (SCR_FIELDX + 2 * max_distance));
13819
13820   if (IS_LOOP_SOUND(nr))
13821   {
13822     /* This assures that quieter loop sounds do not overwrite louder ones,
13823        while restarting sound volume comparison with each new game frame. */
13824
13825     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
13826       return;
13827
13828     loop_sound_volume[nr] = volume;
13829     loop_sound_frame[nr] = FrameCounter;
13830   }
13831
13832   PlaySoundExt(nr, volume, stereo_position, type);
13833 }
13834
13835 static void PlayLevelSoundNearest(int x, int y, int sound_action)
13836 {
13837   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
13838                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
13839                  y < LEVELY(BY1) ? LEVELY(BY1) :
13840                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
13841                  sound_action);
13842 }
13843
13844 static void PlayLevelSoundAction(int x, int y, int action)
13845 {
13846   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
13847 }
13848
13849 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
13850 {
13851   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13852
13853   if (sound_effect != SND_UNDEFINED)
13854     PlayLevelSound(x, y, sound_effect);
13855 }
13856
13857 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
13858                                               int action)
13859 {
13860   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13861
13862   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13863     PlayLevelSound(x, y, sound_effect);
13864 }
13865
13866 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
13867 {
13868   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13869
13870   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13871     PlayLevelSound(x, y, sound_effect);
13872 }
13873
13874 static void StopLevelSoundActionIfLoop(int x, int y, int action)
13875 {
13876   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13877
13878   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13879     StopSound(sound_effect);
13880 }
13881
13882 static void PlayLevelMusic()
13883 {
13884   if (levelset.music[level_nr] != MUS_UNDEFINED)
13885     PlayMusic(levelset.music[level_nr]);        /* from config file */
13886   else
13887     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
13888 }
13889
13890 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
13891 {
13892   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13893   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
13894   int x = xx - 1 - offset;
13895   int y = yy - 1 - offset;
13896
13897   switch (sample)
13898   {
13899     case SAMPLE_blank:
13900       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13901       break;
13902
13903     case SAMPLE_roll:
13904       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13905       break;
13906
13907     case SAMPLE_stone:
13908       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13909       break;
13910
13911     case SAMPLE_nut:
13912       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13913       break;
13914
13915     case SAMPLE_crack:
13916       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13917       break;
13918
13919     case SAMPLE_bug:
13920       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13921       break;
13922
13923     case SAMPLE_tank:
13924       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13925       break;
13926
13927     case SAMPLE_android_clone:
13928       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13929       break;
13930
13931     case SAMPLE_android_move:
13932       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13933       break;
13934
13935     case SAMPLE_spring:
13936       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13937       break;
13938
13939     case SAMPLE_slurp:
13940       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
13941       break;
13942
13943     case SAMPLE_eater:
13944       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13945       break;
13946
13947     case SAMPLE_eater_eat:
13948       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13949       break;
13950
13951     case SAMPLE_alien:
13952       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13953       break;
13954
13955     case SAMPLE_collect:
13956       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13957       break;
13958
13959     case SAMPLE_diamond:
13960       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13961       break;
13962
13963     case SAMPLE_squash:
13964       /* !!! CHECK THIS !!! */
13965 #if 1
13966       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13967 #else
13968       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13969 #endif
13970       break;
13971
13972     case SAMPLE_wonderfall:
13973       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13974       break;
13975
13976     case SAMPLE_drip:
13977       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13978       break;
13979
13980     case SAMPLE_push:
13981       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13982       break;
13983
13984     case SAMPLE_dirt:
13985       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13986       break;
13987
13988     case SAMPLE_acid:
13989       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13990       break;
13991
13992     case SAMPLE_ball:
13993       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13994       break;
13995
13996     case SAMPLE_grow:
13997       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13998       break;
13999
14000     case SAMPLE_wonder:
14001       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14002       break;
14003
14004     case SAMPLE_door:
14005       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14006       break;
14007
14008     case SAMPLE_exit_open:
14009       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14010       break;
14011
14012     case SAMPLE_exit_leave:
14013       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14014       break;
14015
14016     case SAMPLE_dynamite:
14017       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14018       break;
14019
14020     case SAMPLE_tick:
14021       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14022       break;
14023
14024     case SAMPLE_press:
14025       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14026       break;
14027
14028     case SAMPLE_wheel:
14029       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14030       break;
14031
14032     case SAMPLE_boom:
14033       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14034       break;
14035
14036     case SAMPLE_die:
14037       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14038       break;
14039
14040     case SAMPLE_time:
14041       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14042       break;
14043
14044     default:
14045       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14046       break;
14047   }
14048 }
14049
14050 #if 0
14051 void ChangeTime(int value)
14052 {
14053   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
14054
14055   *time += value;
14056
14057   /* EMC game engine uses value from time counter of RND game engine */
14058   level.native_em_level->lev->time = *time;
14059
14060   DrawGameValue_Time(*time);
14061 }
14062
14063 void RaiseScore(int value)
14064 {
14065   /* EMC game engine and RND game engine have separate score counters */
14066   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
14067                 &level.native_em_level->lev->score : &local_player->score);
14068
14069   *score += value;
14070
14071   DrawGameValue_Score(*score);
14072 }
14073 #endif
14074
14075 void RaiseScore(int value)
14076 {
14077   local_player->score += value;
14078
14079   DrawGameValue_Score(local_player->score);
14080 }
14081
14082 void RaiseScoreElement(int element)
14083 {
14084   switch (element)
14085   {
14086     case EL_EMERALD:
14087     case EL_BD_DIAMOND:
14088     case EL_EMERALD_YELLOW:
14089     case EL_EMERALD_RED:
14090     case EL_EMERALD_PURPLE:
14091     case EL_SP_INFOTRON:
14092       RaiseScore(level.score[SC_EMERALD]);
14093       break;
14094     case EL_DIAMOND:
14095       RaiseScore(level.score[SC_DIAMOND]);
14096       break;
14097     case EL_CRYSTAL:
14098       RaiseScore(level.score[SC_CRYSTAL]);
14099       break;
14100     case EL_PEARL:
14101       RaiseScore(level.score[SC_PEARL]);
14102       break;
14103     case EL_BUG:
14104     case EL_BD_BUTTERFLY:
14105     case EL_SP_ELECTRON:
14106       RaiseScore(level.score[SC_BUG]);
14107       break;
14108     case EL_SPACESHIP:
14109     case EL_BD_FIREFLY:
14110     case EL_SP_SNIKSNAK:
14111       RaiseScore(level.score[SC_SPACESHIP]);
14112       break;
14113     case EL_YAMYAM:
14114     case EL_DARK_YAMYAM:
14115       RaiseScore(level.score[SC_YAMYAM]);
14116       break;
14117     case EL_ROBOT:
14118       RaiseScore(level.score[SC_ROBOT]);
14119       break;
14120     case EL_PACMAN:
14121       RaiseScore(level.score[SC_PACMAN]);
14122       break;
14123     case EL_NUT:
14124       RaiseScore(level.score[SC_NUT]);
14125       break;
14126     case EL_DYNAMITE:
14127     case EL_EM_DYNAMITE:
14128     case EL_SP_DISK_RED:
14129     case EL_DYNABOMB_INCREASE_NUMBER:
14130     case EL_DYNABOMB_INCREASE_SIZE:
14131     case EL_DYNABOMB_INCREASE_POWER:
14132       RaiseScore(level.score[SC_DYNAMITE]);
14133       break;
14134     case EL_SHIELD_NORMAL:
14135     case EL_SHIELD_DEADLY:
14136       RaiseScore(level.score[SC_SHIELD]);
14137       break;
14138     case EL_EXTRA_TIME:
14139       RaiseScore(level.extra_time_score);
14140       break;
14141     case EL_KEY_1:
14142     case EL_KEY_2:
14143     case EL_KEY_3:
14144     case EL_KEY_4:
14145     case EL_EM_KEY_1:
14146     case EL_EM_KEY_2:
14147     case EL_EM_KEY_3:
14148     case EL_EM_KEY_4:
14149     case EL_EMC_KEY_5:
14150     case EL_EMC_KEY_6:
14151     case EL_EMC_KEY_7:
14152     case EL_EMC_KEY_8:
14153     case EL_DC_KEY_WHITE:
14154       RaiseScore(level.score[SC_KEY]);
14155       break;
14156     default:
14157       RaiseScore(element_info[element].collect_score);
14158       break;
14159   }
14160 }
14161
14162 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14163 {
14164   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14165   {
14166 #if defined(NETWORK_AVALIABLE)
14167     if (options.network)
14168       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14169     else
14170 #endif
14171     {
14172       if (quick_quit)
14173       {
14174 #if 1
14175
14176 #if 1
14177         FadeSkipNextFadeIn();
14178 #else
14179         fading = fading_none;
14180 #endif
14181
14182 #else
14183         OpenDoor(DOOR_CLOSE_1);
14184 #endif
14185
14186         game_status = GAME_MODE_MAIN;
14187
14188 #if 1
14189         DrawAndFadeInMainMenu(REDRAW_FIELD);
14190 #else
14191         DrawMainMenu();
14192 #endif
14193       }
14194       else
14195       {
14196 #if 0
14197         FadeOut(REDRAW_FIELD);
14198 #endif
14199
14200         game_status = GAME_MODE_MAIN;
14201
14202         DrawAndFadeInMainMenu(REDRAW_FIELD);
14203       }
14204     }
14205   }
14206   else          /* continue playing the game */
14207   {
14208     if (tape.playing && tape.deactivate_display)
14209       TapeDeactivateDisplayOff(TRUE);
14210
14211     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14212
14213     if (tape.playing && tape.deactivate_display)
14214       TapeDeactivateDisplayOn();
14215   }
14216 }
14217
14218 void RequestQuitGame(boolean ask_if_really_quit)
14219 {
14220   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14221   boolean skip_request = AllPlayersGone || quick_quit;
14222
14223   RequestQuitGameExt(skip_request, quick_quit,
14224                      "Do you really want to quit the game ?");
14225 }
14226
14227
14228 /* ------------------------------------------------------------------------- */
14229 /* random generator functions                                                */
14230 /* ------------------------------------------------------------------------- */
14231
14232 unsigned int InitEngineRandom_RND(long seed)
14233 {
14234   game.num_random_calls = 0;
14235
14236 #if 0
14237   unsigned int rnd_seed = InitEngineRandom(seed);
14238
14239   printf("::: START RND: %d\n", rnd_seed);
14240
14241   return rnd_seed;
14242 #else
14243
14244   return InitEngineRandom(seed);
14245
14246 #endif
14247
14248 }
14249
14250 unsigned int RND(int max)
14251 {
14252   if (max > 0)
14253   {
14254     game.num_random_calls++;
14255
14256     return GetEngineRandom(max);
14257   }
14258
14259   return 0;
14260 }
14261
14262
14263 /* ------------------------------------------------------------------------- */
14264 /* game engine snapshot handling functions                                   */
14265 /* ------------------------------------------------------------------------- */
14266
14267 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
14268
14269 struct EngineSnapshotInfo
14270 {
14271   /* runtime values for custom element collect score */
14272   int collect_score[NUM_CUSTOM_ELEMENTS];
14273
14274   /* runtime values for group element choice position */
14275   int choice_pos[NUM_GROUP_ELEMENTS];
14276
14277   /* runtime values for belt position animations */
14278   int belt_graphic[4 * NUM_BELT_PARTS];
14279   int belt_anim_mode[4 * NUM_BELT_PARTS];
14280 };
14281
14282 struct EngineSnapshotNodeInfo
14283 {
14284   void *buffer_orig;
14285   void *buffer_copy;
14286   int size;
14287 };
14288
14289 static struct EngineSnapshotInfo engine_snapshot_rnd;
14290 static ListNode *engine_snapshot_list = NULL;
14291 static char *snapshot_level_identifier = NULL;
14292 static int snapshot_level_nr = -1;
14293
14294 void FreeEngineSnapshot()
14295 {
14296   while (engine_snapshot_list != NULL)
14297     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14298                        checked_free);
14299
14300   setString(&snapshot_level_identifier, NULL);
14301   snapshot_level_nr = -1;
14302 }
14303
14304 static void SaveEngineSnapshotValues_RND()
14305 {
14306   static int belt_base_active_element[4] =
14307   {
14308     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14309     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14310     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14311     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14312   };
14313   int i, j;
14314
14315   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14316   {
14317     int element = EL_CUSTOM_START + i;
14318
14319     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14320   }
14321
14322   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14323   {
14324     int element = EL_GROUP_START + i;
14325
14326     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14327   }
14328
14329   for (i = 0; i < 4; i++)
14330   {
14331     for (j = 0; j < NUM_BELT_PARTS; j++)
14332     {
14333       int element = belt_base_active_element[i] + j;
14334       int graphic = el2img(element);
14335       int anim_mode = graphic_info[graphic].anim_mode;
14336
14337       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14338       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14339     }
14340   }
14341 }
14342
14343 static void LoadEngineSnapshotValues_RND()
14344 {
14345   unsigned long num_random_calls = game.num_random_calls;
14346   int i, j;
14347
14348   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14349   {
14350     int element = EL_CUSTOM_START + i;
14351
14352     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14353   }
14354
14355   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14356   {
14357     int element = EL_GROUP_START + i;
14358
14359     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14360   }
14361
14362   for (i = 0; i < 4; i++)
14363   {
14364     for (j = 0; j < NUM_BELT_PARTS; j++)
14365     {
14366       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14367       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14368
14369       graphic_info[graphic].anim_mode = anim_mode;
14370     }
14371   }
14372
14373   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14374   {
14375     InitRND(tape.random_seed);
14376     for (i = 0; i < num_random_calls; i++)
14377       RND(1);
14378   }
14379
14380   if (game.num_random_calls != num_random_calls)
14381   {
14382     Error(ERR_INFO, "number of random calls out of sync");
14383     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14384     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14385     Error(ERR_EXIT, "this should not happen -- please debug");
14386   }
14387 }
14388
14389 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14390 {
14391   struct EngineSnapshotNodeInfo *bi =
14392     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14393
14394   bi->buffer_orig = buffer;
14395   bi->buffer_copy = checked_malloc(size);
14396   bi->size = size;
14397
14398   memcpy(bi->buffer_copy, buffer, size);
14399
14400   addNodeToList(&engine_snapshot_list, NULL, bi);
14401 }
14402
14403 void SaveEngineSnapshot()
14404 {
14405   FreeEngineSnapshot();         /* free previous snapshot, if needed */
14406
14407   if (level_editor_test_game)   /* do not save snapshots from editor */
14408     return;
14409
14410   /* copy some special values to a structure better suited for the snapshot */
14411
14412   SaveEngineSnapshotValues_RND();
14413   SaveEngineSnapshotValues_EM();
14414
14415   /* save values stored in special snapshot structure */
14416
14417   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14418   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14419
14420   /* save further RND engine values */
14421
14422   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14423   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14424   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14425
14426   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14427   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14428   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14429   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14430
14431   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14432   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14433   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14434   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14435   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14436
14437   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14438   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14439   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14440
14441   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14442
14443   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14444
14445   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14446   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14447
14448   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14449   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14450   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14451   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14452   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14453   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14454   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14455   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14456   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14457   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14458   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14459   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14460   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14461   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14462   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14463   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14464   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
14465   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
14466
14467   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14468   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14469
14470   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14471   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14472   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14473
14474   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14475   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14476
14477   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14478   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14479   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14480   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14481   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14482
14483   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14484   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14485
14486   /* save level identification information */
14487
14488   setString(&snapshot_level_identifier, leveldir_current->identifier);
14489   snapshot_level_nr = level_nr;
14490
14491 #if 0
14492   ListNode *node = engine_snapshot_list;
14493   int num_bytes = 0;
14494
14495   while (node != NULL)
14496   {
14497     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14498
14499     node = node->next;
14500   }
14501
14502   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14503 #endif
14504 }
14505
14506 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
14507 {
14508   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
14509 }
14510
14511 void LoadEngineSnapshot()
14512 {
14513   ListNode *node = engine_snapshot_list;
14514
14515   if (engine_snapshot_list == NULL)
14516     return;
14517
14518   while (node != NULL)
14519   {
14520     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
14521
14522     node = node->next;
14523   }
14524
14525   /* restore special values from snapshot structure */
14526
14527   LoadEngineSnapshotValues_RND();
14528   LoadEngineSnapshotValues_EM();
14529 }
14530
14531 boolean CheckEngineSnapshot()
14532 {
14533   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14534           snapshot_level_nr == level_nr);
14535 }
14536
14537
14538 /* ---------- new game button stuff ---------------------------------------- */
14539
14540 /* graphic position values for game buttons */
14541 #define GAME_BUTTON_XSIZE       30
14542 #define GAME_BUTTON_YSIZE       30
14543 #define GAME_BUTTON_XPOS        5
14544 #define GAME_BUTTON_YPOS        215
14545 #define SOUND_BUTTON_XPOS       5
14546 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
14547
14548 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14549 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14550 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14551 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14552 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14553 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14554
14555 static struct
14556 {
14557   int *x, *y;
14558   int gd_x, gd_y;
14559   int gadget_id;
14560   char *infotext;
14561 } gamebutton_info[NUM_GAME_BUTTONS] =
14562 {
14563 #if 1
14564   {
14565     &game.button.stop.x,        &game.button.stop.y,
14566     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
14567     GAME_CTRL_ID_STOP,
14568     "stop game"
14569   },
14570   {
14571     &game.button.pause.x,       &game.button.pause.y,
14572     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
14573     GAME_CTRL_ID_PAUSE,
14574     "pause game"
14575   },
14576   {
14577     &game.button.play.x,        &game.button.play.y,
14578     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
14579     GAME_CTRL_ID_PLAY,
14580     "play game"
14581   },
14582   {
14583     &game.button.sound_music.x, &game.button.sound_music.y,
14584     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
14585     SOUND_CTRL_ID_MUSIC,
14586     "background music on/off"
14587   },
14588   {
14589     &game.button.sound_loops.x, &game.button.sound_loops.y,
14590     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
14591     SOUND_CTRL_ID_LOOPS,
14592     "sound loops on/off"
14593   },
14594   {
14595     &game.button.sound_simple.x,&game.button.sound_simple.y,
14596     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
14597     SOUND_CTRL_ID_SIMPLE,
14598     "normal sounds on/off"
14599   }
14600 #else
14601   {
14602     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
14603     GAME_CTRL_ID_STOP,
14604     "stop game"
14605   },
14606   {
14607     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
14608     GAME_CTRL_ID_PAUSE,
14609     "pause game"
14610   },
14611   {
14612     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
14613     GAME_CTRL_ID_PLAY,
14614     "play game"
14615   },
14616   {
14617     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
14618     SOUND_CTRL_ID_MUSIC,
14619     "background music on/off"
14620   },
14621   {
14622     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
14623     SOUND_CTRL_ID_LOOPS,
14624     "sound loops on/off"
14625   },
14626   {
14627     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
14628     SOUND_CTRL_ID_SIMPLE,
14629     "normal sounds on/off"
14630   }
14631 #endif
14632 };
14633
14634 void CreateGameButtons()
14635 {
14636   int i;
14637
14638   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14639   {
14640     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
14641     struct GadgetInfo *gi;
14642     int button_type;
14643     boolean checked;
14644     unsigned long event_mask;
14645     int x, y;
14646     int gd_xoffset, gd_yoffset;
14647     int gd_x1, gd_x2, gd_y1, gd_y2;
14648     int id = i;
14649
14650     x = DX + *gamebutton_info[i].x;
14651     y = DY + *gamebutton_info[i].y;
14652     gd_xoffset = gamebutton_info[i].gd_x;
14653     gd_yoffset = gamebutton_info[i].gd_y;
14654     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
14655     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
14656
14657     if (id == GAME_CTRL_ID_STOP ||
14658         id == GAME_CTRL_ID_PAUSE ||
14659         id == GAME_CTRL_ID_PLAY)
14660     {
14661       button_type = GD_TYPE_NORMAL_BUTTON;
14662       checked = FALSE;
14663       event_mask = GD_EVENT_RELEASED;
14664       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14665       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14666     }
14667     else
14668     {
14669       button_type = GD_TYPE_CHECK_BUTTON;
14670       checked =
14671         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14672          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14673          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14674       event_mask = GD_EVENT_PRESSED;
14675       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
14676       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14677     }
14678
14679     gi = CreateGadget(GDI_CUSTOM_ID, id,
14680                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14681 #if 1
14682                       GDI_X, x,
14683                       GDI_Y, y,
14684 #else
14685                       GDI_X, DX + gd_xoffset,
14686                       GDI_Y, DY + gd_yoffset,
14687 #endif
14688                       GDI_WIDTH, GAME_BUTTON_XSIZE,
14689                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
14690                       GDI_TYPE, button_type,
14691                       GDI_STATE, GD_BUTTON_UNPRESSED,
14692                       GDI_CHECKED, checked,
14693                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
14694                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
14695                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
14696                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
14697                       GDI_EVENT_MASK, event_mask,
14698                       GDI_CALLBACK_ACTION, HandleGameButtons,
14699                       GDI_END);
14700
14701     if (gi == NULL)
14702       Error(ERR_EXIT, "cannot create gadget");
14703
14704     game_gadget[id] = gi;
14705   }
14706 }
14707
14708 void FreeGameButtons()
14709 {
14710   int i;
14711
14712   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14713     FreeGadget(game_gadget[i]);
14714 }
14715
14716 static void MapGameButtons()
14717 {
14718   int i;
14719
14720   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14721     MapGadget(game_gadget[i]);
14722 }
14723
14724 void UnmapGameButtons()
14725 {
14726   int i;
14727
14728   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14729     UnmapGadget(game_gadget[i]);
14730 }
14731
14732 static void HandleGameButtons(struct GadgetInfo *gi)
14733 {
14734   int id = gi->custom_id;
14735
14736   if (game_status != GAME_MODE_PLAYING)
14737     return;
14738
14739   switch (id)
14740   {
14741     case GAME_CTRL_ID_STOP:
14742       if (tape.playing)
14743         TapeStop();
14744       else
14745         RequestQuitGame(TRUE);
14746       break;
14747
14748     case GAME_CTRL_ID_PAUSE:
14749       if (options.network)
14750       {
14751 #if defined(NETWORK_AVALIABLE)
14752         if (tape.pausing)
14753           SendToServer_ContinuePlaying();
14754         else
14755           SendToServer_PausePlaying();
14756 #endif
14757       }
14758       else
14759         TapeTogglePause(TAPE_TOGGLE_MANUAL);
14760       break;
14761
14762     case GAME_CTRL_ID_PLAY:
14763       if (tape.pausing)
14764       {
14765 #if defined(NETWORK_AVALIABLE)
14766         if (options.network)
14767           SendToServer_ContinuePlaying();
14768         else
14769 #endif
14770         {
14771           tape.pausing = FALSE;
14772           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
14773         }
14774       }
14775       break;
14776
14777     case SOUND_CTRL_ID_MUSIC:
14778       if (setup.sound_music)
14779       { 
14780         setup.sound_music = FALSE;
14781         FadeMusic();
14782       }
14783       else if (audio.music_available)
14784       { 
14785         setup.sound = setup.sound_music = TRUE;
14786
14787         SetAudioMode(setup.sound);
14788
14789         PlayLevelMusic();
14790       }
14791       break;
14792
14793     case SOUND_CTRL_ID_LOOPS:
14794       if (setup.sound_loops)
14795         setup.sound_loops = FALSE;
14796       else if (audio.loops_available)
14797       {
14798         setup.sound = setup.sound_loops = TRUE;
14799         SetAudioMode(setup.sound);
14800       }
14801       break;
14802
14803     case SOUND_CTRL_ID_SIMPLE:
14804       if (setup.sound_simple)
14805         setup.sound_simple = FALSE;
14806       else if (audio.sound_available)
14807       {
14808         setup.sound = setup.sound_simple = TRUE;
14809         SetAudioMode(setup.sound);
14810       }
14811       break;
14812
14813     default:
14814       break;
14815   }
14816 }