rnd-20070320-3-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61
62 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
63
64
65 /* for DigField() */
66 #define DF_NO_PUSH              0
67 #define DF_DIG                  1
68 #define DF_SNAP                 2
69
70 /* for MovePlayer() */
71 #define MP_NO_ACTION            0
72 #define MP_MOVING               1
73 #define MP_ACTION               2
74 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
75
76 /* for ScrollPlayer() */
77 #define SCROLL_INIT             0
78 #define SCROLL_GO_ON            1
79
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START          0
82 #define EX_TYPE_NONE            0
83 #define EX_TYPE_NORMAL          (1 << 0)
84 #define EX_TYPE_CENTER          (1 << 1)
85 #define EX_TYPE_BORDER          (1 << 2)
86 #define EX_TYPE_CROSS           (1 << 3)
87 #define EX_TYPE_DYNA            (1 << 4)
88 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
89
90 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
91 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
92 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
93 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
94
95 /* special positions in the game control window (relative to control window) */
96 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
97 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
98 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
99 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
100 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
101 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
102 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
103 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
104 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
105 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
106 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
107 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
108 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
109 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
110 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
111 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
112
113 /* special positions in the game control window (relative to main window) */
114 #define DX_LEVEL1               (DX + XX_LEVEL1)
115 #define DX_LEVEL2               (DX + XX_LEVEL2)
116 #define DX_LEVEL                (DX + XX_LEVEL)
117 #define DY_LEVEL                (DY + YY_LEVEL)
118 #define DX_EMERALDS             (DX + XX_EMERALDS)
119 #define DY_EMERALDS             (DY + YY_EMERALDS)
120 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
121 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
122 #define DX_KEYS                 (DX + XX_KEYS)
123 #define DY_KEYS                 (DY + YY_KEYS)
124 #define DX_SCORE                (DX + XX_SCORE)
125 #define DY_SCORE                (DY + YY_SCORE)
126 #define DX_TIME1                (DX + XX_TIME1)
127 #define DX_TIME2                (DX + XX_TIME2)
128 #define DX_TIME                 (DX + XX_TIME)
129 #define DY_TIME                 (DY + YY_TIME)
130
131 #if 0
132 /* game panel display and control definitions */
133
134 #define GAME_CONTROL_LEVEL                      0
135 #define GAME_CONTROL_GEMS                       1
136 #define GAME_CONTROL_INVENTORY                  2
137 #define GAME_CONTROL_KEY_1                      3
138 #define GAME_CONTROL_KEY_2                      4
139 #define GAME_CONTROL_KEY_3                      5
140 #define GAME_CONTROL_KEY_4                      6
141 #define GAME_CONTROL_KEY_5                      7
142 #define GAME_CONTROL_KEY_6                      8
143 #define GAME_CONTROL_KEY_7                      9
144 #define GAME_CONTROL_KEY_8                      10
145 #define GAME_CONTROL_KEY_WHITE                  11
146 #define GAME_CONTROL_KEY_WHITE_COUNT            12
147 #define GAME_CONTROL_SCORE                      13
148 #define GAME_CONTROL_TIME                       14
149 #define GAME_CONTROL_TIME_HH                    15
150 #define GAME_CONTROL_TIME_MM                    16
151 #define GAME_CONTROL_TIME_SS                    17
152 #define GAME_CONTROL_DROP_NEXT_1                18
153 #define GAME_CONTROL_DROP_NEXT_2                19
154 #define GAME_CONTROL_DROP_NEXT_3                20
155 #define GAME_CONTROL_DROP_NEXT_4                21
156 #define GAME_CONTROL_DROP_NEXT_5                22
157 #define GAME_CONTROL_DROP_NEXT_6                23
158 #define GAME_CONTROL_DROP_NEXT_7                24
159 #define GAME_CONTROL_DROP_NEXT_8                25
160 #define GAME_CONTROL_SHIELD_NORMAL              26
161 #define GAME_CONTROL_SHIELD_NORMAL_TIME         27
162 #define GAME_CONTROL_SHIELD_DEADLY              28
163 #define GAME_CONTROL_SHIELD_DEADLY_TIME         29
164 #define GAME_CONTROL_EXIT                       30
165 #define GAME_CONTROL_EM_EXIT                    31
166 #define GAME_CONTROL_SP_EXIT                    32
167 #define GAME_CONTROL_STEEL_EXIT                 33
168 #define GAME_CONTROL_EM_STEEL_EXIT              34
169 #define GAME_CONTROL_EMC_MAGIC_BALL             35
170 #define GAME_CONTROL_EMC_MAGIC_BALL_TIME        36
171 #define GAME_CONTROL_LIGHT_SWITCH               37
172 #define GAME_CONTROL_LIGHT_SWITCH_TIME          38
173 #define GAME_CONTROL_TIMEGATE_SWITCH            39
174 #define GAME_CONTROL_TIMEGATE_SWITCH_TIME       40
175 #define GAME_CONTROL_SWITCHGATE_SWITCH          41
176 #define GAME_CONTROL_EMC_LENSES                 42
177 #define GAME_CONTROL_EMC_LENSES_TIME            43
178 #define GAME_CONTROL_EMC_MAGNIFIER              44
179 #define GAME_CONTROL_EMC_MAGNIFIER_TIME         45
180 #define GAME_CONTROL_BALLOON_SWITCH             46
181 #define GAME_CONTROL_DYNABOMB_NUMBER            47
182 #define GAME_CONTROL_DYNABOMB_SIZE              48
183 #define GAME_CONTROL_DYNABOMB_POWER             49
184 #define GAME_CONTROL_PENGUINS                   50
185 #define GAME_CONTROL_SOKOBAN_OBJECTS            51
186 #define GAME_CONTROL_SOKOBAN_FIELDS             52
187 #define GAME_CONTROL_ROBOT_WHEEL                53
188 #define GAME_CONTROL_CONVEYOR_BELT_1            54
189 #define GAME_CONTROL_CONVEYOR_BELT_1_SWITCH     55
190 #define GAME_CONTROL_CONVEYOR_BELT_2            56
191 #define GAME_CONTROL_CONVEYOR_BELT_2_SWITCH     57
192 #define GAME_CONTROL_CONVEYOR_BELT_3            58
193 #define GAME_CONTROL_CONVEYOR_BELT_3_SWITCH     59
194 #define GAME_CONTROL_CONVEYOR_BELT_4            60
195 #define GAME_CONTROL_CONVEYOR_BELT_4_SWITCH     61
196 #define GAME_CONTROL_MAGIC_WALL                 62
197 #define GAME_CONTROL_MAGIC_WALL_TIME            63
198 #define GAME_CONTROL_BD_MAGIC_WALL              64
199 #define GAME_CONTROL_DC_MAGIC_WALL              65
200 #define GAME_CONTROL_PLAYER_NAME                66
201 #define GAME_CONTROL_LEVEL_NAME                 67
202 #define GAME_CONTROL_LEVEL_AUTHOR               68
203
204 struct GameControlInfo
205 {
206   int nr;
207
208   struct TextPosInfo *pos_text;
209   int type;
210   void *ptr;
211 };
212
213 static struct GameControlInfo game_controls[] =
214 {
215   {
216     GAME_CONTROL_LEVEL,
217     &game.panel.level,
218     TYPE_INTEGER,
219   },
220   {
221     GAME_CONTROL_GEMS,
222     &game.panel.gems,
223     TYPE_INTEGER,
224   },
225   {
226     GAME_CONTROL_INVENTORY,
227     &game.panel.inventory,
228     TYPE_INTEGER,
229   },
230   {
231     GAME_CONTROL_KEYS,
232     &game.panel.keys,
233     TYPE_INTEGER,
234   },
235   {
236     GAME_CONTROL_SCORE,
237     &game.panel.score,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_CONTROL_TIME,
242     &game.panel.time,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_CONTROL_TIME_HH,
247     &game.panel.time_hh,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_CONTROL_TIME_MM,
252     &game.panel.time_mm,
253     TYPE_INTEGER,
254   },
255   {
256     GAME_CONTROL_TIME_SS,
257     &game.panel.time_ss,
258     TYPE_INTEGER,
259   },
260   {
261     GAME_CONTROL_DROP_NEXT_1,
262     &game.panel.drop_next_1,
263     TYPE_INTEGER,
264   },
265   {
266     GAME_CONTROL_DROP_NEXT_2,
267     &game.panel.drop_next_2,
268     TYPE_INTEGER,
269   },
270   {
271     GAME_CONTROL_DROP_NEXT_3,
272     &game.panel.drop_next_3,
273     TYPE_INTEGER,
274   },
275   {
276     GAME_CONTROL_DROP_NEXT_4,
277     &game.panel.drop_next_4,
278     TYPE_INTEGER,
279   },
280   {
281     GAME_CONTROL_DROP_NEXT_5,
282     &game.panel.drop_next_5,
283     TYPE_INTEGER,
284   },
285   {
286     GAME_CONTROL_DROP_NEXT_6,
287     &game.panel.drop_next_6,
288     TYPE_INTEGER,
289   },
290   {
291     GAME_CONTROL_DROP_NEXT_7,
292     &game.panel.drop_next_7,
293     TYPE_INTEGER,
294   },
295   {
296     GAME_CONTROL_DROP_NEXT_8,
297     &game.panel.drop_next_8,
298     TYPE_INTEGER,
299   },
300   {
301     GAME_CONTROL_EMC_KEYS,
302     &game.panel.emc_keys,
303     TYPE_INTEGER,
304   },
305   {
306     GAME_CONTROL_KEY_1,
307     &game.panel.key_1,
308     TYPE_INTEGER,
309   },
310   {
311     GAME_CONTROL_KEY_2,
312     &game.panel.key_2,
313     TYPE_INTEGER,
314   },
315   {
316     GAME_CONTROL_KEY_3,
317     &game.panel.key_3,
318     TYPE_INTEGER,
319   },
320   {
321     GAME_CONTROL_KEY_4,
322     &game.panel.key_4,
323     TYPE_INTEGER,
324   },
325   {
326     GAME_CONTROL_KEY_5,
327     &game.panel.key_5,
328     TYPE_INTEGER,
329   },
330   {
331     GAME_CONTROL_KEY_6,
332     &game.panel.key_6,
333     TYPE_INTEGER,
334   },
335   {
336     GAME_CONTROL_KEY_7,
337     &game.panel.key_7,
338     TYPE_INTEGER,
339   },
340   {
341     GAME_CONTROL_KEY_8,
342     &game.panel.key_8,
343     TYPE_INTEGER,
344   },
345   {
346     GAME_CONTROL_KEY_WHITE,
347     &game.panel.key_white,
348     TYPE_INTEGER,
349   },
350   {
351     GAME_CONTROL_KEY_WHITE_COUNT,
352     &game.panel.key_white_count,
353     TYPE_INTEGER,
354   },
355   {
356     GAME_CONTROL_SHIELD_NORMAL,
357     &game.panel.shield_normal,
358     TYPE_INTEGER,
359   },
360   {
361     GAME_CONTROL_SHIELD_NORMAL_TIME,
362     &game.panel.shield_normal_time,
363     TYPE_INTEGER,
364   },
365   {
366     GAME_CONTROL_SHIELD_DEADLY,
367     &game.panel.shield_deadly,
368     TYPE_INTEGER,
369   },
370   {
371     GAME_CONTROL_SHIELD_DEADLY_TIME,
372     &game.panel.shield_deadly_time,
373     TYPE_INTEGER,
374   },
375   {
376     GAME_CONTROL_EXIT,
377     &game.panel.exit,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_CONTROL_EM_EXIT,
382     &game.panel.em_exit,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_CONTROL_SP_EXIT,
387     &game.panel.sp_exit,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_CONTROL_STEEL_EXIT,
392     &game.panel.steel_exit,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_CONTROL_EM_STEEL_EXIT,
397     &game.panel.em_steel_exit,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_CONTROL_EMC_MAGIC_BALL,
402     &game.panel.emc_magic_ball,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_CONTROL_EMC_MAGIC_BALL_TIME,
407     &game.panel.emc_magic_ball_time,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_CONTROL_LIGHT_SWITCH,
412     &game.panel.light_switch,
413     TYPE_INTEGER,
414   },
415   {
416     GAME_CONTROL_LIGHT_SWITCH_TIME,
417     &game.panel.light_switch_time,
418     TYPE_INTEGER,
419   },
420   {
421     GAME_CONTROL_TIMEGATE_SWITCH,
422     &game.panel.timegate_switch,
423     TYPE_INTEGER,
424   },
425   {
426     GAME_CONTROL_TIMEGATE_SWITCH_TIME,
427     &game.panel.timegate_switch_time,
428     TYPE_INTEGER,
429   },
430   {
431     GAME_CONTROL_SWITCHGATE_SWITCH,
432     &game.panel.switchgate_switch,
433     TYPE_INTEGER,
434   },
435   {
436     GAME_CONTROL_EMC_LENSES,
437     &game.panel.emc_lenses,
438     TYPE_INTEGER,
439   },
440   {
441     GAME_CONTROL_EMC_LENSES_TIME,
442     &game.panel.emc_lenses_time,
443     TYPE_INTEGER,
444   },
445   {
446     GAME_CONTROL_EMC_MAGNIFIER,
447     &game.panel.emc_magnifier,
448     TYPE_INTEGER,
449   },
450   {
451     GAME_CONTROL_EMC_MAGNIFIER_TIME,
452     &game.panel.emc_magnifier_time,
453     TYPE_INTEGER,
454   },
455   {
456     GAME_CONTROL_BALLOON_SWITCH,
457     &game.panel.balloon_switch,
458     TYPE_INTEGER,
459   },
460   {
461     GAME_CONTROL_DYNABOMB_NUMBER,
462     &game.panel.dynabomb_number,
463     TYPE_INTEGER,
464   },
465   {
466     GAME_CONTROL_DYNABOMB_SIZE,
467     &game.panel.dynabomb_size,
468     TYPE_INTEGER,
469   },
470   {
471     GAME_CONTROL_DYNABOMB_POWER,
472     &game.panel.dynabomb_power,
473     TYPE_INTEGER,
474   },
475   {
476     GAME_CONTROL_PENGUINS,
477     &game.panel.penguins,
478     TYPE_INTEGER,
479   },
480   {
481     GAME_CONTROL_SOKOBAN_OBJECTS,
482     &game.panel.sokoban_objects,
483     TYPE_INTEGER,
484   },
485   {
486     GAME_CONTROL_SOKOBAN_FIELDS,
487     &game.panel.sokoban_fields,
488     TYPE_INTEGER,
489   },
490   {
491     GAME_CONTROL_ROBOT_WHEEL,
492     &game.panel.robot_wheel,
493     TYPE_INTEGER,
494   },
495   {
496     GAME_CONTROL_CONVEYOR_BELT_1,
497     &game.panel.conveyor_belt_1,
498     TYPE_INTEGER,
499   },
500   {
501     GAME_CONTROL_CONVEYOR_BELT_1_SWITCH,
502     &game.panel.conveyor_belt_1_switch,
503     TYPE_INTEGER,
504   },
505   {
506     GAME_CONTROL_CONVEYOR_BELT_2,
507     &game.panel.conveyor_belt_2,
508     TYPE_INTEGER,
509   },
510   {
511     GAME_CONTROL_CONVEYOR_BELT_2_SWITCH,
512     &game.panel.conveyor_belt_2_switch,
513     TYPE_INTEGER,
514   },
515   {
516     GAME_CONTROL_CONVEYOR_BELT_3,
517     &game.panel.conveyor_belt_3,
518     TYPE_INTEGER,
519   },
520   {
521     GAME_CONTROL_CONVEYOR_BELT_3_SWITCH,
522     &game.panel.conveyor_belt_3_switch,
523     TYPE_INTEGER,
524   },
525   {
526     GAME_CONTROL_CONVEYOR_BELT_4,
527     &game.panel.conveyor_belt_4,
528     TYPE_INTEGER,
529   },
530   {
531     GAME_CONTROL_CONVEYOR_BELT_4_SWITCH,
532     &game.panel.conveyor_belt_4_switch,
533     TYPE_INTEGER,
534   },
535   {
536     GAME_CONTROL_MAGIC_WALL,
537     &game.panel.magic_wall,
538     TYPE_INTEGER,
539   },
540   {
541     GAME_CONTROL_MAGIC_WALL_TIME,
542     &game.panel.magic_wall_time,
543     TYPE_INTEGER,
544   },
545   {
546     GAME_CONTROL_BD_MAGIC_WALL,
547     &game.panel.bd_magic_wall,
548     TYPE_INTEGER,
549   },
550   {
551     GAME_CONTROL_DC_MAGIC_WALL,
552     &game.panel.dc_magic_wall,
553     TYPE_INTEGER,
554   },
555   {
556     GAME_CONTROL_PLAYER_NAME,
557     &game.panel.player_name,
558     TYPE_INTEGER,
559   },
560   {
561     GAME_CONTROL_LEVEL_NAME,
562     &game.panel.level_name,
563     TYPE_INTEGER,
564   },
565   {
566     GAME_CONTROL_LEVEL_AUTHOR,
567     &game.panel.level_author,
568     TYPE_INTEGER,
569   },
570
571   {
572     -1,
573     NULL,
574     -1,
575     NULL
576   }
577 };
578 #endif
579
580
581 /* values for delayed check of falling and moving elements and for collision */
582 #define CHECK_DELAY_MOVING      3
583 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
584 #define CHECK_DELAY_COLLISION   2
585 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
586
587 /* values for initial player move delay (initial delay counter value) */
588 #define INITIAL_MOVE_DELAY_OFF  -1
589 #define INITIAL_MOVE_DELAY_ON   0
590
591 /* values for player movement speed (which is in fact a delay value) */
592 #define MOVE_DELAY_MIN_SPEED    32
593 #define MOVE_DELAY_NORMAL_SPEED 8
594 #define MOVE_DELAY_HIGH_SPEED   4
595 #define MOVE_DELAY_MAX_SPEED    1
596
597 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
598 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
599
600 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
601 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
602
603 /* values for other actions */
604 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
605 #define MOVE_STEPSIZE_MIN       (1)
606 #define MOVE_STEPSIZE_MAX       (TILEX)
607
608 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
609 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
610
611 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
612
613 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
614                                  RND(element_info[e].push_delay_random))
615 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
616                                  RND(element_info[e].drop_delay_random))
617 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
618                                  RND(element_info[e].move_delay_random))
619 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
620                                     (element_info[e].move_delay_random))
621 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
622                                  RND(element_info[e].ce_value_random_initial))
623 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
624 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
625                                  RND((c)->delay_random * (c)->delay_frames))
626 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
627                                  RND((c)->delay_random))
628
629
630 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
631          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
632
633 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
634         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
635          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
636          (be) + (e) - EL_SELF)
637
638 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
639         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
640          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
641          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
642          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
643          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
644          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
645          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
646          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
647          (e))
648
649 #define CAN_GROW_INTO(e)                                                \
650         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
651
652 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
653                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
654                                         (condition)))
655
656 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
657                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
658                                         (CAN_MOVE_INTO_ACID(e) &&       \
659                                          Feld[x][y] == EL_ACID) ||      \
660                                         (condition)))
661
662 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
663                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
664                                         (CAN_MOVE_INTO_ACID(e) &&       \
665                                          Feld[x][y] == EL_ACID) ||      \
666                                         (condition)))
667
668 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
669                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
670                                         (condition) ||                  \
671                                         (CAN_MOVE_INTO_ACID(e) &&       \
672                                          Feld[x][y] == EL_ACID) ||      \
673                                         (DONT_COLLIDE_WITH(e) &&        \
674                                          IS_PLAYER(x, y) &&             \
675                                          !PLAYER_ENEMY_PROTECTED(x, y))))
676
677 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
678         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
679
680 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
681         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
682
683 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
684         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
685
686 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
687         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
688                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
689
690 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
691         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
692
693 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
694         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
695
696 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
697         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
698
699 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
700         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
701
702 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
703         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
704
705 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
706         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
707                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
708                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
709                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
710                                                  IS_FOOD_PENGUIN(Feld[x][y])))
711 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
712         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
713
714 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
715         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
716
717 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
718         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
719
720 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
721         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
722                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
723
724 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
725
726 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
727                 (!IS_PLAYER(x, y) &&                                    \
728                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
729
730 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
731         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
732
733 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
734 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
735
736 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
737 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
738 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
739 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
740
741 /* game button identifiers */
742 #define GAME_CTRL_ID_STOP               0
743 #define GAME_CTRL_ID_PAUSE              1
744 #define GAME_CTRL_ID_PLAY               2
745 #define SOUND_CTRL_ID_MUSIC             3
746 #define SOUND_CTRL_ID_LOOPS             4
747 #define SOUND_CTRL_ID_SIMPLE            5
748
749 #define NUM_GAME_BUTTONS                6
750
751
752 /* forward declaration for internal use */
753
754 static void CreateField(int, int, int);
755
756 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
757 static void AdvanceFrameAndPlayerCounters(int);
758
759 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
760 static boolean MovePlayer(struct PlayerInfo *, int, int);
761 static void ScrollPlayer(struct PlayerInfo *, int);
762 static void ScrollScreen(struct PlayerInfo *, int);
763
764 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
765
766 static void InitBeltMovement(void);
767 static void CloseAllOpenTimegates(void);
768 static void CheckGravityMovement(struct PlayerInfo *);
769 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
770 static void KillPlayerUnlessEnemyProtected(int, int);
771 static void KillPlayerUnlessExplosionProtected(int, int);
772
773 static void TestIfPlayerTouchesCustomElement(int, int);
774 static void TestIfElementTouchesCustomElement(int, int);
775 static void TestIfElementHitsCustomElement(int, int, int);
776 #if 0
777 static void TestIfElementSmashesCustomElement(int, int, int);
778 #endif
779
780 static void HandleElementChange(int, int, int);
781 static void ExecuteCustomElementAction(int, int, int, int);
782 static boolean ChangeElement(int, int, int, int);
783
784 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
785 #define CheckTriggeredElementChange(x, y, e, ev)                        \
786         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
787 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
788         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
789 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
790         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
791 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
792         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
793
794 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
795 #define CheckElementChange(x, y, e, te, ev)                             \
796         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
797 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
798         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
799 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
800         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
801
802 static void PlayLevelSound(int, int, int);
803 static void PlayLevelSoundNearest(int, int, int);
804 static void PlayLevelSoundAction(int, int, int);
805 static void PlayLevelSoundElementAction(int, int, int, int);
806 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
807 static void PlayLevelSoundActionIfLoop(int, int, int);
808 static void StopLevelSoundActionIfLoop(int, int, int);
809 static void PlayLevelMusic();
810
811 static void MapGameButtons();
812 static void HandleGameButtons(struct GadgetInfo *);
813
814 int AmoebeNachbarNr(int, int);
815 void AmoebeUmwandeln(int, int);
816 void ContinueMoving(int, int);
817 void Bang(int, int);
818 void InitMovDir(int, int);
819 void InitAmoebaNr(int, int);
820 int NewHiScore(void);
821
822 void TestIfGoodThingHitsBadThing(int, int, int);
823 void TestIfBadThingHitsGoodThing(int, int, int);
824 void TestIfPlayerTouchesBadThing(int, int);
825 void TestIfPlayerRunsIntoBadThing(int, int, int);
826 void TestIfBadThingTouchesPlayer(int, int);
827 void TestIfBadThingRunsIntoPlayer(int, int, int);
828 void TestIfFriendTouchesBadThing(int, int);
829 void TestIfBadThingTouchesFriend(int, int);
830 void TestIfBadThingTouchesOtherBadThing(int, int);
831
832 void KillPlayer(struct PlayerInfo *);
833 void BuryPlayer(struct PlayerInfo *);
834 void RemovePlayer(struct PlayerInfo *);
835
836 boolean SnapField(struct PlayerInfo *, int, int);
837 boolean DropElement(struct PlayerInfo *);
838
839 static int getInvisibleActiveFromInvisibleElement(int);
840 static int getInvisibleFromInvisibleActiveElement(int);
841
842 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
843
844 /* for detection of endless loops, caused by custom element programming */
845 /* (using maximal playfield width x 10 is just a rough approximation) */
846 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
847
848 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
849 {                                                                       \
850   if (recursion_loop_detected)                                          \
851     return (rc);                                                        \
852                                                                         \
853   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
854   {                                                                     \
855     recursion_loop_detected = TRUE;                                     \
856     recursion_loop_element = (e);                                       \
857   }                                                                     \
858                                                                         \
859   recursion_loop_depth++;                                               \
860 }
861
862 #define RECURSION_LOOP_DETECTION_END()                                  \
863 {                                                                       \
864   recursion_loop_depth--;                                               \
865 }
866
867 static int recursion_loop_depth;
868 static boolean recursion_loop_detected;
869 static boolean recursion_loop_element;
870
871
872 /* ------------------------------------------------------------------------- */
873 /* definition of elements that automatically change to other elements after  */
874 /* a specified time, eventually calling a function when changing             */
875 /* ------------------------------------------------------------------------- */
876
877 /* forward declaration for changer functions */
878 static void InitBuggyBase(int, int);
879 static void WarnBuggyBase(int, int);
880
881 static void InitTrap(int, int);
882 static void ActivateTrap(int, int);
883 static void ChangeActiveTrap(int, int);
884
885 static void InitRobotWheel(int, int);
886 static void RunRobotWheel(int, int);
887 static void StopRobotWheel(int, int);
888
889 static void InitTimegateWheel(int, int);
890 static void RunTimegateWheel(int, int);
891
892 static void InitMagicBallDelay(int, int);
893 static void ActivateMagicBall(int, int);
894
895 struct ChangingElementInfo
896 {
897   int element;
898   int target_element;
899   int change_delay;
900   void (*pre_change_function)(int x, int y);
901   void (*change_function)(int x, int y);
902   void (*post_change_function)(int x, int y);
903 };
904
905 static struct ChangingElementInfo change_delay_list[] =
906 {
907   {
908     EL_NUT_BREAKING,
909     EL_EMERALD,
910     6,
911     NULL,
912     NULL,
913     NULL
914   },
915   {
916     EL_PEARL_BREAKING,
917     EL_EMPTY,
918     8,
919     NULL,
920     NULL,
921     NULL
922   },
923   {
924     EL_EXIT_OPENING,
925     EL_EXIT_OPEN,
926     29,
927     NULL,
928     NULL,
929     NULL
930   },
931   {
932     EL_EXIT_CLOSING,
933     EL_EXIT_CLOSED,
934     29,
935     NULL,
936     NULL,
937     NULL
938   },
939   {
940     EL_STEEL_EXIT_OPENING,
941     EL_STEEL_EXIT_OPEN,
942     29,
943     NULL,
944     NULL,
945     NULL
946   },
947   {
948     EL_STEEL_EXIT_CLOSING,
949     EL_STEEL_EXIT_CLOSED,
950     29,
951     NULL,
952     NULL,
953     NULL
954   },
955   {
956     EL_EM_EXIT_OPENING,
957     EL_EM_EXIT_OPEN,
958     29,
959     NULL,
960     NULL,
961     NULL
962   },
963   {
964     EL_EM_EXIT_CLOSING,
965 #if 1
966     EL_EMPTY,
967 #else
968     EL_EM_EXIT_CLOSED,
969 #endif
970     29,
971     NULL,
972     NULL,
973     NULL
974   },
975   {
976     EL_EM_STEEL_EXIT_OPENING,
977     EL_EM_STEEL_EXIT_OPEN,
978     29,
979     NULL,
980     NULL,
981     NULL
982   },
983   {
984     EL_EM_STEEL_EXIT_CLOSING,
985 #if 1
986     EL_STEELWALL,
987 #else
988     EL_EM_STEEL_EXIT_CLOSED,
989 #endif
990     29,
991     NULL,
992     NULL,
993     NULL
994   },
995   {
996     EL_SP_EXIT_OPENING,
997     EL_SP_EXIT_OPEN,
998     29,
999     NULL,
1000     NULL,
1001     NULL
1002   },
1003   {
1004     EL_SP_EXIT_CLOSING,
1005     EL_SP_EXIT_CLOSED,
1006     29,
1007     NULL,
1008     NULL,
1009     NULL
1010   },
1011   {
1012     EL_SWITCHGATE_OPENING,
1013     EL_SWITCHGATE_OPEN,
1014     29,
1015     NULL,
1016     NULL,
1017     NULL
1018   },
1019   {
1020     EL_SWITCHGATE_CLOSING,
1021     EL_SWITCHGATE_CLOSED,
1022     29,
1023     NULL,
1024     NULL,
1025     NULL
1026   },
1027   {
1028     EL_TIMEGATE_OPENING,
1029     EL_TIMEGATE_OPEN,
1030     29,
1031     NULL,
1032     NULL,
1033     NULL
1034   },
1035   {
1036     EL_TIMEGATE_CLOSING,
1037     EL_TIMEGATE_CLOSED,
1038     29,
1039     NULL,
1040     NULL,
1041     NULL
1042   },
1043
1044   {
1045     EL_ACID_SPLASH_LEFT,
1046     EL_EMPTY,
1047     8,
1048     NULL,
1049     NULL,
1050     NULL
1051   },
1052   {
1053     EL_ACID_SPLASH_RIGHT,
1054     EL_EMPTY,
1055     8,
1056     NULL,
1057     NULL,
1058     NULL
1059   },
1060   {
1061     EL_SP_BUGGY_BASE,
1062     EL_SP_BUGGY_BASE_ACTIVATING,
1063     0,
1064     InitBuggyBase,
1065     NULL,
1066     NULL
1067   },
1068   {
1069     EL_SP_BUGGY_BASE_ACTIVATING,
1070     EL_SP_BUGGY_BASE_ACTIVE,
1071     0,
1072     InitBuggyBase,
1073     NULL,
1074     NULL
1075   },
1076   {
1077     EL_SP_BUGGY_BASE_ACTIVE,
1078     EL_SP_BUGGY_BASE,
1079     0,
1080     InitBuggyBase,
1081     WarnBuggyBase,
1082     NULL
1083   },
1084   {
1085     EL_TRAP,
1086     EL_TRAP_ACTIVE,
1087     0,
1088     InitTrap,
1089     NULL,
1090     ActivateTrap
1091   },
1092   {
1093     EL_TRAP_ACTIVE,
1094     EL_TRAP,
1095     31,
1096     NULL,
1097     ChangeActiveTrap,
1098     NULL
1099   },
1100   {
1101     EL_ROBOT_WHEEL_ACTIVE,
1102     EL_ROBOT_WHEEL,
1103     0,
1104     InitRobotWheel,
1105     RunRobotWheel,
1106     StopRobotWheel
1107   },
1108   {
1109     EL_TIMEGATE_SWITCH_ACTIVE,
1110     EL_TIMEGATE_SWITCH,
1111     0,
1112     InitTimegateWheel,
1113     RunTimegateWheel,
1114     NULL
1115   },
1116   {
1117     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1118     EL_DC_TIMEGATE_SWITCH,
1119     0,
1120     InitTimegateWheel,
1121     RunTimegateWheel,
1122     NULL
1123   },
1124   {
1125     EL_EMC_MAGIC_BALL_ACTIVE,
1126     EL_EMC_MAGIC_BALL_ACTIVE,
1127     0,
1128     InitMagicBallDelay,
1129     NULL,
1130     ActivateMagicBall
1131   },
1132   {
1133     EL_EMC_SPRING_BUMPER_ACTIVE,
1134     EL_EMC_SPRING_BUMPER,
1135     8,
1136     NULL,
1137     NULL,
1138     NULL
1139   },
1140   {
1141     EL_DIAGONAL_SHRINKING,
1142     EL_UNDEFINED,
1143     0,
1144     NULL,
1145     NULL,
1146     NULL
1147   },
1148   {
1149     EL_DIAGONAL_GROWING,
1150     EL_UNDEFINED,
1151     0,
1152     NULL,
1153     NULL,
1154     NULL,
1155   },
1156
1157   {
1158     EL_UNDEFINED,
1159     EL_UNDEFINED,
1160     -1,
1161     NULL,
1162     NULL,
1163     NULL
1164   }
1165 };
1166
1167 struct
1168 {
1169   int element;
1170   int push_delay_fixed, push_delay_random;
1171 }
1172 push_delay_list[] =
1173 {
1174   { EL_SPRING,                  0, 0 },
1175   { EL_BALLOON,                 0, 0 },
1176
1177   { EL_SOKOBAN_OBJECT,          2, 0 },
1178   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1179   { EL_SATELLITE,               2, 0 },
1180   { EL_SP_DISK_YELLOW,          2, 0 },
1181
1182   { EL_UNDEFINED,               0, 0 },
1183 };
1184
1185 struct
1186 {
1187   int element;
1188   int move_stepsize;
1189 }
1190 move_stepsize_list[] =
1191 {
1192   { EL_AMOEBA_DROP,             2 },
1193   { EL_AMOEBA_DROPPING,         2 },
1194   { EL_QUICKSAND_FILLING,       1 },
1195   { EL_QUICKSAND_EMPTYING,      1 },
1196   { EL_QUICKSAND_FAST_FILLING,  2 },
1197   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1198   { EL_MAGIC_WALL_FILLING,      2 },
1199   { EL_MAGIC_WALL_EMPTYING,     2 },
1200   { EL_BD_MAGIC_WALL_FILLING,   2 },
1201   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1202   { EL_DC_MAGIC_WALL_FILLING,   2 },
1203   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1204
1205   { EL_UNDEFINED,               0 },
1206 };
1207
1208 struct
1209 {
1210   int element;
1211   int count;
1212 }
1213 collect_count_list[] =
1214 {
1215   { EL_EMERALD,                 1 },
1216   { EL_BD_DIAMOND,              1 },
1217   { EL_EMERALD_YELLOW,          1 },
1218   { EL_EMERALD_RED,             1 },
1219   { EL_EMERALD_PURPLE,          1 },
1220   { EL_DIAMOND,                 3 },
1221   { EL_SP_INFOTRON,             1 },
1222   { EL_PEARL,                   5 },
1223   { EL_CRYSTAL,                 8 },
1224
1225   { EL_UNDEFINED,               0 },
1226 };
1227
1228 struct
1229 {
1230   int element;
1231   int direction;
1232 }
1233 access_direction_list[] =
1234 {
1235   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1236   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1237   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1238   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1239   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1240   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1241   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1242   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1243   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1244   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1245   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1246
1247   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1248   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1249   { EL_SP_PORT_UP,                                                   MV_DOWN },
1250   { EL_SP_PORT_DOWN,                                         MV_UP           },
1251   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1252   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1253   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1254   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1255   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1256   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1257   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1258   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1259   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1260   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1261   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1262   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1263   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1264   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1265   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1266
1267   { EL_UNDEFINED,                       MV_NONE                              }
1268 };
1269
1270 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1271
1272 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1273 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1274 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1275                                  IS_JUST_CHANGING(x, y))
1276
1277 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1278
1279 /* static variables for playfield scan mode (scanning forward or backward) */
1280 static int playfield_scan_start_x = 0;
1281 static int playfield_scan_start_y = 0;
1282 static int playfield_scan_delta_x = 1;
1283 static int playfield_scan_delta_y = 1;
1284
1285 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1286                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1287                                      (y) += playfield_scan_delta_y)     \
1288                                 for ((x) = playfield_scan_start_x;      \
1289                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1290                                      (x) += playfield_scan_delta_x)
1291
1292 #ifdef DEBUG
1293 void DEBUG_SetMaximumDynamite()
1294 {
1295   int i;
1296
1297   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1298     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1299       local_player->inventory_element[local_player->inventory_size++] =
1300         EL_DYNAMITE;
1301 }
1302 #endif
1303
1304 static void InitPlayfieldScanModeVars()
1305 {
1306   if (game.use_reverse_scan_direction)
1307   {
1308     playfield_scan_start_x = lev_fieldx - 1;
1309     playfield_scan_start_y = lev_fieldy - 1;
1310
1311     playfield_scan_delta_x = -1;
1312     playfield_scan_delta_y = -1;
1313   }
1314   else
1315   {
1316     playfield_scan_start_x = 0;
1317     playfield_scan_start_y = 0;
1318
1319     playfield_scan_delta_x = 1;
1320     playfield_scan_delta_y = 1;
1321   }
1322 }
1323
1324 static void InitPlayfieldScanMode(int mode)
1325 {
1326   game.use_reverse_scan_direction =
1327     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1328
1329   InitPlayfieldScanModeVars();
1330 }
1331
1332 static int get_move_delay_from_stepsize(int move_stepsize)
1333 {
1334   move_stepsize =
1335     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1336
1337   /* make sure that stepsize value is always a power of 2 */
1338   move_stepsize = (1 << log_2(move_stepsize));
1339
1340   return TILEX / move_stepsize;
1341 }
1342
1343 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1344                                boolean init_game)
1345 {
1346   int player_nr = player->index_nr;
1347   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1348   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1349
1350   /* do no immediately change move delay -- the player might just be moving */
1351   player->move_delay_value_next = move_delay;
1352
1353   /* information if player can move must be set separately */
1354   player->cannot_move = cannot_move;
1355
1356   if (init_game)
1357   {
1358     player->move_delay       = game.initial_move_delay[player_nr];
1359     player->move_delay_value = game.initial_move_delay_value[player_nr];
1360
1361     player->move_delay_value_next = -1;
1362
1363     player->move_delay_reset_counter = 0;
1364   }
1365 }
1366
1367 void GetPlayerConfig()
1368 {
1369   GameFrameDelay = setup.game_frame_delay;
1370
1371   if (!audio.sound_available)
1372     setup.sound_simple = FALSE;
1373
1374   if (!audio.loops_available)
1375     setup.sound_loops = FALSE;
1376
1377   if (!audio.music_available)
1378     setup.sound_music = FALSE;
1379
1380   if (!video.fullscreen_available)
1381     setup.fullscreen = FALSE;
1382
1383   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1384
1385   SetAudioMode(setup.sound);
1386   InitJoysticks();
1387 }
1388
1389 int GetElementFromGroupElement(int element)
1390 {
1391   if (IS_GROUP_ELEMENT(element))
1392   {
1393     struct ElementGroupInfo *group = element_info[element].group;
1394     int last_anim_random_frame = gfx.anim_random_frame;
1395     int element_pos;
1396
1397     if (group->choice_mode == ANIM_RANDOM)
1398       gfx.anim_random_frame = RND(group->num_elements_resolved);
1399
1400     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1401                                     group->choice_mode, 0,
1402                                     group->choice_pos);
1403
1404     if (group->choice_mode == ANIM_RANDOM)
1405       gfx.anim_random_frame = last_anim_random_frame;
1406
1407     group->choice_pos++;
1408
1409     element = group->element_resolved[element_pos];
1410   }
1411
1412   return element;
1413 }
1414
1415 static void InitPlayerField(int x, int y, int element, boolean init_game)
1416 {
1417   if (element == EL_SP_MURPHY)
1418   {
1419     if (init_game)
1420     {
1421       if (stored_player[0].present)
1422       {
1423         Feld[x][y] = EL_SP_MURPHY_CLONE;
1424
1425         return;
1426       }
1427       else
1428       {
1429         stored_player[0].use_murphy = TRUE;
1430
1431         if (!level.use_artwork_element[0])
1432           stored_player[0].artwork_element = EL_SP_MURPHY;
1433       }
1434
1435       Feld[x][y] = EL_PLAYER_1;
1436     }
1437   }
1438
1439   if (init_game)
1440   {
1441     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1442     int jx = player->jx, jy = player->jy;
1443
1444     player->present = TRUE;
1445
1446     player->block_last_field = (element == EL_SP_MURPHY ?
1447                                 level.sp_block_last_field :
1448                                 level.block_last_field);
1449
1450     /* ---------- initialize player's last field block delay --------------- */
1451
1452     /* always start with reliable default value (no adjustment needed) */
1453     player->block_delay_adjustment = 0;
1454
1455     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1456     if (player->block_last_field && element == EL_SP_MURPHY)
1457       player->block_delay_adjustment = 1;
1458
1459     /* special case 2: in game engines before 3.1.1, blocking was different */
1460     if (game.use_block_last_field_bug)
1461       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1462
1463     if (!options.network || player->connected)
1464     {
1465       player->active = TRUE;
1466
1467       /* remove potentially duplicate players */
1468       if (StorePlayer[jx][jy] == Feld[x][y])
1469         StorePlayer[jx][jy] = 0;
1470
1471       StorePlayer[x][y] = Feld[x][y];
1472
1473       if (options.debug)
1474       {
1475         printf("Player %d activated.\n", player->element_nr);
1476         printf("[Local player is %d and currently %s.]\n",
1477                local_player->element_nr,
1478                local_player->active ? "active" : "not active");
1479       }
1480     }
1481
1482     Feld[x][y] = EL_EMPTY;
1483
1484     player->jx = player->last_jx = x;
1485     player->jy = player->last_jy = y;
1486   }
1487 }
1488
1489 static void InitField(int x, int y, boolean init_game)
1490 {
1491   int element = Feld[x][y];
1492
1493   switch (element)
1494   {
1495     case EL_SP_MURPHY:
1496     case EL_PLAYER_1:
1497     case EL_PLAYER_2:
1498     case EL_PLAYER_3:
1499     case EL_PLAYER_4:
1500       InitPlayerField(x, y, element, init_game);
1501       break;
1502
1503     case EL_SOKOBAN_FIELD_PLAYER:
1504       element = Feld[x][y] = EL_PLAYER_1;
1505       InitField(x, y, init_game);
1506
1507       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1508       InitField(x, y, init_game);
1509       break;
1510
1511     case EL_SOKOBAN_FIELD_EMPTY:
1512       local_player->sokobanfields_still_needed++;
1513       break;
1514
1515     case EL_STONEBLOCK:
1516       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1517         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1518       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1519         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1520       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1521         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1522       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1523         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1524       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1525         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1526       break;
1527
1528     case EL_BUG:
1529     case EL_BUG_RIGHT:
1530     case EL_BUG_UP:
1531     case EL_BUG_LEFT:
1532     case EL_BUG_DOWN:
1533     case EL_SPACESHIP:
1534     case EL_SPACESHIP_RIGHT:
1535     case EL_SPACESHIP_UP:
1536     case EL_SPACESHIP_LEFT:
1537     case EL_SPACESHIP_DOWN:
1538     case EL_BD_BUTTERFLY:
1539     case EL_BD_BUTTERFLY_RIGHT:
1540     case EL_BD_BUTTERFLY_UP:
1541     case EL_BD_BUTTERFLY_LEFT:
1542     case EL_BD_BUTTERFLY_DOWN:
1543     case EL_BD_FIREFLY:
1544     case EL_BD_FIREFLY_RIGHT:
1545     case EL_BD_FIREFLY_UP:
1546     case EL_BD_FIREFLY_LEFT:
1547     case EL_BD_FIREFLY_DOWN:
1548     case EL_PACMAN_RIGHT:
1549     case EL_PACMAN_UP:
1550     case EL_PACMAN_LEFT:
1551     case EL_PACMAN_DOWN:
1552     case EL_YAMYAM:
1553     case EL_YAMYAM_LEFT:
1554     case EL_YAMYAM_RIGHT:
1555     case EL_YAMYAM_UP:
1556     case EL_YAMYAM_DOWN:
1557     case EL_DARK_YAMYAM:
1558     case EL_ROBOT:
1559     case EL_PACMAN:
1560     case EL_SP_SNIKSNAK:
1561     case EL_SP_ELECTRON:
1562     case EL_MOLE:
1563     case EL_MOLE_LEFT:
1564     case EL_MOLE_RIGHT:
1565     case EL_MOLE_UP:
1566     case EL_MOLE_DOWN:
1567       InitMovDir(x, y);
1568       break;
1569
1570     case EL_AMOEBA_FULL:
1571     case EL_BD_AMOEBA:
1572       InitAmoebaNr(x, y);
1573       break;
1574
1575     case EL_AMOEBA_DROP:
1576       if (y == lev_fieldy - 1)
1577       {
1578         Feld[x][y] = EL_AMOEBA_GROWING;
1579         Store[x][y] = EL_AMOEBA_WET;
1580       }
1581       break;
1582
1583     case EL_DYNAMITE_ACTIVE:
1584     case EL_SP_DISK_RED_ACTIVE:
1585     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1586     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1587     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1588     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1589       MovDelay[x][y] = 96;
1590       break;
1591
1592     case EL_EM_DYNAMITE_ACTIVE:
1593       MovDelay[x][y] = 32;
1594       break;
1595
1596     case EL_LAMP:
1597       local_player->lights_still_needed++;
1598       break;
1599
1600     case EL_PENGUIN:
1601       local_player->friends_still_needed++;
1602       break;
1603
1604     case EL_PIG:
1605     case EL_DRAGON:
1606       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1607       break;
1608
1609     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1610     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1611     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1612     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1613     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1614     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1615     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1616     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1617     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1618     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1619     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1620     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1621       if (init_game)
1622       {
1623         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1624         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1625         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1626
1627         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1628         {
1629           game.belt_dir[belt_nr] = belt_dir;
1630           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1631         }
1632         else    /* more than one switch -- set it like the first switch */
1633         {
1634           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1635         }
1636       }
1637       break;
1638
1639 #if !USE_BOTH_SWITCHGATE_SWITCHES
1640     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1641       if (init_game)
1642         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1643       break;
1644
1645     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1646       if (init_game)
1647         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1648       break;
1649 #endif
1650
1651     case EL_LIGHT_SWITCH_ACTIVE:
1652       if (init_game)
1653         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1654       break;
1655
1656     case EL_INVISIBLE_STEELWALL:
1657     case EL_INVISIBLE_WALL:
1658     case EL_INVISIBLE_SAND:
1659       if (game.light_time_left > 0 ||
1660           game.lenses_time_left > 0)
1661         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1662       break;
1663
1664     case EL_EMC_MAGIC_BALL:
1665       if (game.ball_state)
1666         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1667       break;
1668
1669     case EL_EMC_MAGIC_BALL_SWITCH:
1670       if (game.ball_state)
1671         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1672       break;
1673
1674     default:
1675       if (IS_CUSTOM_ELEMENT(element))
1676       {
1677         if (CAN_MOVE(element))
1678           InitMovDir(x, y);
1679
1680 #if USE_NEW_CUSTOM_VALUE
1681         if (!element_info[element].use_last_ce_value || init_game)
1682           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1683 #endif
1684       }
1685       else if (IS_GROUP_ELEMENT(element))
1686       {
1687         Feld[x][y] = GetElementFromGroupElement(element);
1688
1689         InitField(x, y, init_game);
1690       }
1691
1692       break;
1693   }
1694
1695   if (!init_game)
1696     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1697 }
1698
1699 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1700 {
1701   InitField(x, y, init_game);
1702
1703   /* not needed to call InitMovDir() -- already done by InitField()! */
1704   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1705       CAN_MOVE(Feld[x][y]))
1706     InitMovDir(x, y);
1707 }
1708
1709 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1710 {
1711   int old_element = Feld[x][y];
1712
1713   InitField(x, y, init_game);
1714
1715   /* not needed to call InitMovDir() -- already done by InitField()! */
1716   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1717       CAN_MOVE(old_element) &&
1718       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1719     InitMovDir(x, y);
1720
1721   /* this case is in fact a combination of not less than three bugs:
1722      first, it calls InitMovDir() for elements that can move, although this is
1723      already done by InitField(); then, it checks the element that was at this
1724      field _before_ the call to InitField() (which can change it); lastly, it
1725      was not called for "mole with direction" elements, which were treated as
1726      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1727   */
1728 }
1729
1730 #if 1
1731
1732 void DrawGameValue_Emeralds(int value)
1733 {
1734   struct TextPosInfo *pos = &game.panel.gems;
1735 #if 1
1736   int font_nr = pos->font;
1737 #else
1738   int font_nr = FONT_TEXT_2;
1739 #endif
1740   int font_width = getFontWidth(font_nr);
1741   int chars = pos->chars;
1742
1743   if (PANEL_DEACTIVATED(pos))
1744     return;
1745
1746   pos->width = chars * font_width;
1747
1748   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1749 }
1750
1751 void DrawGameValue_Dynamite(int value)
1752 {
1753   struct TextPosInfo *pos = &game.panel.inventory;
1754 #if 1
1755   int font_nr = pos->font;
1756 #else
1757   int font_nr = FONT_TEXT_2;
1758 #endif
1759   int font_width = getFontWidth(font_nr);
1760   int chars = pos->chars;
1761
1762   if (PANEL_DEACTIVATED(pos))
1763     return;
1764
1765   pos->width = chars * font_width;
1766
1767   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1768 }
1769
1770 void DrawGameValue_Score(int value)
1771 {
1772   struct TextPosInfo *pos = &game.panel.score;
1773 #if 1
1774   int font_nr = pos->font;
1775 #else
1776   int font_nr = FONT_TEXT_2;
1777 #endif
1778   int font_width = getFontWidth(font_nr);
1779   int chars = pos->chars;
1780
1781   if (PANEL_DEACTIVATED(pos))
1782     return;
1783
1784   pos->width = chars * font_width;
1785
1786   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1787 }
1788
1789 void DrawGameValue_Time(int value)
1790 {
1791   struct TextPosInfo *pos = &game.panel.time;
1792   static int last_value = -1;
1793   int chars1 = 3;
1794   int chars2 = 4;
1795   int chars = pos->chars;
1796 #if 1
1797   int font1_nr = pos->font;
1798   int font2_nr = pos->font_alt;
1799 #else
1800   int font1_nr = FONT_TEXT_2;
1801   int font2_nr = FONT_TEXT_1;
1802 #endif
1803   int font_nr = font1_nr;
1804   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
1805
1806   if (PANEL_DEACTIVATED(pos))
1807     return;
1808
1809   if (use_dynamic_chars)                /* use dynamic number of chars */
1810   {
1811     chars   = (value < 1000 ? chars1   : chars2);
1812     font_nr = (value < 1000 ? font1_nr : font2_nr);
1813   }
1814
1815   /* clear background if value just changed its size (dynamic chars only) */
1816   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
1817   {
1818     int width1 = chars1 * getFontWidth(font1_nr);
1819     int width2 = chars2 * getFontWidth(font2_nr);
1820     int max_width = MAX(width1, width2);
1821     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
1822
1823     pos->width = max_width;
1824
1825     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
1826                                max_width, max_height);
1827   }
1828
1829   pos->width = chars * getFontWidth(font_nr);
1830
1831   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1832
1833   last_value = value;
1834 }
1835
1836 void DrawGameValue_Level(int value)
1837 {
1838   struct TextPosInfo *pos = &game.panel.level_number;
1839   int chars1 = 2;
1840   int chars2 = 3;
1841   int chars = pos->chars;
1842 #if 1
1843   int font1_nr = pos->font;
1844   int font2_nr = pos->font_alt;
1845 #else
1846   int font1_nr = FONT_TEXT_2;
1847   int font2_nr = FONT_TEXT_1;
1848 #endif
1849   int font_nr = font1_nr;
1850   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
1851
1852   if (PANEL_DEACTIVATED(pos))
1853     return;
1854
1855   if (use_dynamic_chars)                /* use dynamic number of chars */
1856   {
1857     chars   = (level_nr < 100 ? chars1   : chars2);
1858     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
1859   }
1860
1861   pos->width = chars * getFontWidth(font_nr);
1862
1863   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1864 }
1865
1866 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1867 {
1868 #if 0
1869   struct TextPosInfo *pos = &game.panel.keys;
1870 #endif
1871 #if 0
1872   int base_key_graphic = EL_KEY_1;
1873 #endif
1874   int i;
1875
1876 #if 0
1877   if (PANEL_DEACTIVATED(pos))
1878     return;
1879 #endif
1880
1881 #if 0
1882   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1883     base_key_graphic = EL_EM_KEY_1;
1884 #endif
1885
1886 #if 0
1887   pos->width = 4 * MINI_TILEX;
1888 #endif
1889
1890 #if 1
1891   for (i = 0; i < MAX_NUM_KEYS; i++)
1892 #else
1893   /* currently only 4 of 8 possible keys are displayed */
1894   for (i = 0; i < STD_NUM_KEYS; i++)
1895 #endif
1896   {
1897 #if 1
1898     struct TextPosInfo *pos = &game.panel.key[i];
1899 #endif
1900     int src_x = DOOR_GFX_PAGEX5 + 18;
1901     int src_y = DOOR_GFX_PAGEY1 + 123;
1902 #if 1
1903     int dst_x = PANEL_XPOS(pos);
1904     int dst_y = PANEL_YPOS(pos);
1905 #else
1906     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
1907     int dst_y = PANEL_YPOS(pos);
1908 #endif
1909
1910 #if 1
1911     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
1912                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
1913                    EL_KEY_1) + i;
1914     int graphic = el2edimg(element);
1915 #endif
1916
1917 #if 1
1918     if (PANEL_DEACTIVATED(pos))
1919       continue;
1920 #endif
1921
1922 #if 0
1923     /* masked blit with tiles from half-size scaled bitmap does not work yet
1924        (no mask bitmap created for these sizes after loading and scaling) --
1925        solution: load without creating mask, scale, then create final mask */
1926
1927     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1928                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1929
1930     if (key[i])
1931     {
1932 #if 0
1933       int graphic = el2edimg(base_key_graphic + i);
1934 #endif
1935       Bitmap *src_bitmap;
1936       int src_x, src_y;
1937
1938       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1939
1940       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
1941                     dst_x - src_x, dst_y - src_y);
1942       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
1943                        dst_x, dst_y);
1944     }
1945 #else
1946 #if 1
1947     if (key[i])
1948       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
1949     else
1950       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1951                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1952 #else
1953     if (key[i])
1954       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
1955     else
1956       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1957                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1958 #endif
1959 #endif
1960   }
1961 }
1962
1963 #else
1964
1965 void DrawGameValue_Emeralds(int value)
1966 {
1967   int font_nr = FONT_TEXT_2;
1968   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1969
1970   if (PANEL_DEACTIVATED(game.panel.gems))
1971     return;
1972
1973   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
1974 }
1975
1976 void DrawGameValue_Dynamite(int value)
1977 {
1978   int font_nr = FONT_TEXT_2;
1979   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1980
1981   if (PANEL_DEACTIVATED(game.panel.inventory))
1982     return;
1983
1984   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
1985 }
1986
1987 void DrawGameValue_Score(int value)
1988 {
1989   int font_nr = FONT_TEXT_2;
1990   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
1991
1992   if (PANEL_DEACTIVATED(game.panel.score))
1993     return;
1994
1995   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
1996 }
1997
1998 void DrawGameValue_Time(int value)
1999 {
2000   int font1_nr = FONT_TEXT_2;
2001 #if 1
2002   int font2_nr = FONT_TEXT_1;
2003 #else
2004   int font2_nr = FONT_LEVEL_NUMBER;
2005 #endif
2006   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2007   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2008
2009   if (PANEL_DEACTIVATED(game.panel.time))
2010     return;
2011
2012   /* clear background if value just changed its size */
2013   if (value == 999 || value == 1000)
2014     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2015
2016   if (value < 1000)
2017     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2018   else
2019     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2020 }
2021
2022 void DrawGameValue_Level(int value)
2023 {
2024   int font1_nr = FONT_TEXT_2;
2025 #if 1
2026   int font2_nr = FONT_TEXT_1;
2027 #else
2028   int font2_nr = FONT_LEVEL_NUMBER;
2029 #endif
2030
2031   if (PANEL_DEACTIVATED(game.panel.level))
2032     return;
2033
2034   if (level_nr < 100)
2035     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2036   else
2037     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2038 }
2039
2040 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2041 {
2042   int base_key_graphic = EL_KEY_1;
2043   int i;
2044
2045   if (PANEL_DEACTIVATED(game.panel.keys))
2046     return;
2047
2048   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2049     base_key_graphic = EL_EM_KEY_1;
2050
2051   /* currently only 4 of 8 possible keys are displayed */
2052   for (i = 0; i < STD_NUM_KEYS; i++)
2053   {
2054     int x = XX_KEYS + i * MINI_TILEX;
2055     int y = YY_KEYS;
2056
2057     if (key[i])
2058       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2059     else
2060       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2061                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2062   }
2063 }
2064
2065 #endif
2066
2067 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2068                        int key_bits)
2069 {
2070   int key[MAX_NUM_KEYS];
2071   int i;
2072
2073   /* prevent EM engine from updating time/score values parallel to GameWon() */
2074   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2075       local_player->LevelSolved)
2076     return;
2077
2078   for (i = 0; i < MAX_NUM_KEYS; i++)
2079     key[i] = key_bits & (1 << i);
2080
2081   DrawGameValue_Level(level_nr);
2082
2083   DrawGameValue_Emeralds(emeralds);
2084   DrawGameValue_Dynamite(dynamite);
2085   DrawGameValue_Score(score);
2086   DrawGameValue_Time(time);
2087
2088   DrawGameValue_Keys(key);
2089 }
2090
2091 void DrawGameDoorValues()
2092 {
2093   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2094   int dynamite_value = 0;
2095   int score_value = (local_player->LevelSolved ? local_player->score_final :
2096                      local_player->score);
2097   int gems_value = local_player->gems_still_needed;
2098   int key_bits = 0;
2099   int i, j;
2100
2101   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2102   {
2103     DrawGameDoorValues_EM();
2104
2105     return;
2106   }
2107
2108   if (game.centered_player_nr == -1)
2109   {
2110     for (i = 0; i < MAX_PLAYERS; i++)
2111     {
2112       for (j = 0; j < MAX_NUM_KEYS; j++)
2113         if (stored_player[i].key[j])
2114           key_bits |= (1 << j);
2115
2116       dynamite_value += stored_player[i].inventory_size;
2117     }
2118   }
2119   else
2120   {
2121     int player_nr = game.centered_player_nr;
2122
2123     for (i = 0; i < MAX_NUM_KEYS; i++)
2124       if (stored_player[player_nr].key[i])
2125         key_bits |= (1 << i);
2126
2127     dynamite_value = stored_player[player_nr].inventory_size;
2128   }
2129
2130   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2131                     key_bits);
2132 }
2133
2134
2135 /*
2136   =============================================================================
2137   InitGameEngine()
2138   -----------------------------------------------------------------------------
2139   initialize game engine due to level / tape version number
2140   =============================================================================
2141 */
2142
2143 static void InitGameEngine()
2144 {
2145   int i, j, k, l, x, y;
2146
2147   /* set game engine from tape file when re-playing, else from level file */
2148   game.engine_version = (tape.playing ? tape.engine_version :
2149                          level.game_version);
2150
2151   /* ---------------------------------------------------------------------- */
2152   /* set flags for bugs and changes according to active game engine version */
2153   /* ---------------------------------------------------------------------- */
2154
2155   /*
2156     Summary of bugfix/change:
2157     Fixed handling for custom elements that change when pushed by the player.
2158
2159     Fixed/changed in version:
2160     3.1.0
2161
2162     Description:
2163     Before 3.1.0, custom elements that "change when pushing" changed directly
2164     after the player started pushing them (until then handled in "DigField()").
2165     Since 3.1.0, these custom elements are not changed until the "pushing"
2166     move of the element is finished (now handled in "ContinueMoving()").
2167
2168     Affected levels/tapes:
2169     The first condition is generally needed for all levels/tapes before version
2170     3.1.0, which might use the old behaviour before it was changed; known tapes
2171     that are affected are some tapes from the level set "Walpurgis Gardens" by
2172     Jamie Cullen.
2173     The second condition is an exception from the above case and is needed for
2174     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2175     above (including some development versions of 3.1.0), but before it was
2176     known that this change would break tapes like the above and was fixed in
2177     3.1.1, so that the changed behaviour was active although the engine version
2178     while recording maybe was before 3.1.0. There is at least one tape that is
2179     affected by this exception, which is the tape for the one-level set "Bug
2180     Machine" by Juergen Bonhagen.
2181   */
2182
2183   game.use_change_when_pushing_bug =
2184     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2185      !(tape.playing &&
2186        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2187        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2188
2189   /*
2190     Summary of bugfix/change:
2191     Fixed handling for blocking the field the player leaves when moving.
2192
2193     Fixed/changed in version:
2194     3.1.1
2195
2196     Description:
2197     Before 3.1.1, when "block last field when moving" was enabled, the field
2198     the player is leaving when moving was blocked for the time of the move,
2199     and was directly unblocked afterwards. This resulted in the last field
2200     being blocked for exactly one less than the number of frames of one player
2201     move. Additionally, even when blocking was disabled, the last field was
2202     blocked for exactly one frame.
2203     Since 3.1.1, due to changes in player movement handling, the last field
2204     is not blocked at all when blocking is disabled. When blocking is enabled,
2205     the last field is blocked for exactly the number of frames of one player
2206     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2207     last field is blocked for exactly one more than the number of frames of
2208     one player move.
2209
2210     Affected levels/tapes:
2211     (!!! yet to be determined -- probably many !!!)
2212   */
2213
2214   game.use_block_last_field_bug =
2215     (game.engine_version < VERSION_IDENT(3,1,1,0));
2216
2217   /*
2218     Summary of bugfix/change:
2219     Changed behaviour of CE changes with multiple changes per single frame.
2220
2221     Fixed/changed in version:
2222     3.2.0-6
2223
2224     Description:
2225     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
2226     This resulted in race conditions where CEs seem to behave strange in some
2227     situations (where triggered CE changes were just skipped because there was
2228     already a CE change on that tile in the playfield in that engine frame).
2229     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
2230     (The number of changes per frame must be limited in any case, because else
2231     it is easily possible to define CE changes that would result in an infinite
2232     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
2233     should be set large enough so that it would only be reached in cases where
2234     the corresponding CE change conditions run into a loop. Therefore, it seems
2235     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
2236     maximal number of change pages for custom elements.)
2237
2238     Affected levels/tapes:
2239     Probably many.
2240   */
2241
2242 #if USE_ONLY_ONE_CHANGE_PER_FRAME
2243   game.max_num_changes_per_frame = 1;
2244 #else
2245   game.max_num_changes_per_frame =
2246     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
2247 #endif
2248
2249   /* ---------------------------------------------------------------------- */
2250
2251   /* default scan direction: scan playfield from top/left to bottom/right */
2252   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2253
2254   /* dynamically adjust element properties according to game engine version */
2255   InitElementPropertiesEngine(game.engine_version);
2256
2257 #if 0
2258   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2259   printf("          tape version == %06d [%s] [file: %06d]\n",
2260          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2261          tape.file_version);
2262   printf("       => game.engine_version == %06d\n", game.engine_version);
2263 #endif
2264
2265   /* ---------- initialize player's initial move delay --------------------- */
2266
2267   /* dynamically adjust player properties according to level information */
2268   for (i = 0; i < MAX_PLAYERS; i++)
2269     game.initial_move_delay_value[i] =
2270       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2271
2272   /* dynamically adjust player properties according to game engine version */
2273   for (i = 0; i < MAX_PLAYERS; i++)
2274     game.initial_move_delay[i] =
2275       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2276        game.initial_move_delay_value[i] : 0);
2277
2278   /* ---------- initialize player's initial push delay --------------------- */
2279
2280   /* dynamically adjust player properties according to game engine version */
2281   game.initial_push_delay_value =
2282     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2283
2284   /* ---------- initialize changing elements ------------------------------- */
2285
2286   /* initialize changing elements information */
2287   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2288   {
2289     struct ElementInfo *ei = &element_info[i];
2290
2291     /* this pointer might have been changed in the level editor */
2292     ei->change = &ei->change_page[0];
2293
2294     if (!IS_CUSTOM_ELEMENT(i))
2295     {
2296       ei->change->target_element = EL_EMPTY_SPACE;
2297       ei->change->delay_fixed = 0;
2298       ei->change->delay_random = 0;
2299       ei->change->delay_frames = 1;
2300     }
2301
2302     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2303     {
2304       ei->has_change_event[j] = FALSE;
2305
2306       ei->event_page_nr[j] = 0;
2307       ei->event_page[j] = &ei->change_page[0];
2308     }
2309   }
2310
2311   /* add changing elements from pre-defined list */
2312   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2313   {
2314     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2315     struct ElementInfo *ei = &element_info[ch_delay->element];
2316
2317     ei->change->target_element       = ch_delay->target_element;
2318     ei->change->delay_fixed          = ch_delay->change_delay;
2319
2320     ei->change->pre_change_function  = ch_delay->pre_change_function;
2321     ei->change->change_function      = ch_delay->change_function;
2322     ei->change->post_change_function = ch_delay->post_change_function;
2323
2324     ei->change->can_change = TRUE;
2325     ei->change->can_change_or_has_action = TRUE;
2326
2327     ei->has_change_event[CE_DELAY] = TRUE;
2328
2329     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2330     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2331   }
2332
2333   /* ---------- initialize internal run-time variables ------------- */
2334
2335   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2336   {
2337     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2338
2339     for (j = 0; j < ei->num_change_pages; j++)
2340     {
2341       ei->change_page[j].can_change_or_has_action =
2342         (ei->change_page[j].can_change |
2343          ei->change_page[j].has_action);
2344     }
2345   }
2346
2347   /* add change events from custom element configuration */
2348   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2349   {
2350     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2351
2352     for (j = 0; j < ei->num_change_pages; j++)
2353     {
2354       if (!ei->change_page[j].can_change_or_has_action)
2355         continue;
2356
2357       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2358       {
2359         /* only add event page for the first page found with this event */
2360         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2361         {
2362           ei->has_change_event[k] = TRUE;
2363
2364           ei->event_page_nr[k] = j;
2365           ei->event_page[k] = &ei->change_page[j];
2366         }
2367       }
2368     }
2369   }
2370
2371   /* ---------- initialize run-time trigger player and element ------------- */
2372
2373   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2374   {
2375     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2376
2377     for (j = 0; j < ei->num_change_pages; j++)
2378     {
2379       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2380       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
2381       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2382       ei->change_page[j].actual_trigger_ce_value = 0;
2383       ei->change_page[j].actual_trigger_ce_score = 0;
2384     }
2385   }
2386
2387   /* ---------- initialize trigger events ---------------------------------- */
2388
2389   /* initialize trigger events information */
2390   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2391     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2392       trigger_events[i][j] = FALSE;
2393
2394   /* add trigger events from element change event properties */
2395   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2396   {
2397     struct ElementInfo *ei = &element_info[i];
2398
2399     for (j = 0; j < ei->num_change_pages; j++)
2400     {
2401       if (!ei->change_page[j].can_change_or_has_action)
2402         continue;
2403
2404       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2405       {
2406         int trigger_element = ei->change_page[j].trigger_element;
2407
2408         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2409         {
2410           if (ei->change_page[j].has_event[k])
2411           {
2412             if (IS_GROUP_ELEMENT(trigger_element))
2413             {
2414               struct ElementGroupInfo *group =
2415                 element_info[trigger_element].group;
2416
2417               for (l = 0; l < group->num_elements_resolved; l++)
2418                 trigger_events[group->element_resolved[l]][k] = TRUE;
2419             }
2420             else if (trigger_element == EL_ANY_ELEMENT)
2421               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2422                 trigger_events[l][k] = TRUE;
2423             else
2424               trigger_events[trigger_element][k] = TRUE;
2425           }
2426         }
2427       }
2428     }
2429   }
2430
2431   /* ---------- initialize push delay -------------------------------------- */
2432
2433   /* initialize push delay values to default */
2434   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2435   {
2436     if (!IS_CUSTOM_ELEMENT(i))
2437     {
2438       /* set default push delay values (corrected since version 3.0.7-1) */
2439       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2440       {
2441         element_info[i].push_delay_fixed = 2;
2442         element_info[i].push_delay_random = 8;
2443       }
2444       else
2445       {
2446         element_info[i].push_delay_fixed = 8;
2447         element_info[i].push_delay_random = 8;
2448       }
2449     }
2450   }
2451
2452   /* set push delay value for certain elements from pre-defined list */
2453   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2454   {
2455     int e = push_delay_list[i].element;
2456
2457     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2458     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2459   }
2460
2461   /* set push delay value for Supaplex elements for newer engine versions */
2462   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2463   {
2464     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2465     {
2466       if (IS_SP_ELEMENT(i))
2467       {
2468         /* set SP push delay to just enough to push under a falling zonk */
2469         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2470
2471         element_info[i].push_delay_fixed  = delay;
2472         element_info[i].push_delay_random = 0;
2473       }
2474     }
2475   }
2476
2477   /* ---------- initialize move stepsize ----------------------------------- */
2478
2479   /* initialize move stepsize values to default */
2480   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2481     if (!IS_CUSTOM_ELEMENT(i))
2482       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2483
2484   /* set move stepsize value for certain elements from pre-defined list */
2485   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2486   {
2487     int e = move_stepsize_list[i].element;
2488
2489     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2490   }
2491
2492   /* ---------- initialize collect score ----------------------------------- */
2493
2494   /* initialize collect score values for custom elements from initial value */
2495   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2496     if (IS_CUSTOM_ELEMENT(i))
2497       element_info[i].collect_score = element_info[i].collect_score_initial;
2498
2499   /* ---------- initialize collect count ----------------------------------- */
2500
2501   /* initialize collect count values for non-custom elements */
2502   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2503     if (!IS_CUSTOM_ELEMENT(i))
2504       element_info[i].collect_count_initial = 0;
2505
2506   /* add collect count values for all elements from pre-defined list */
2507   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2508     element_info[collect_count_list[i].element].collect_count_initial =
2509       collect_count_list[i].count;
2510
2511   /* ---------- initialize access direction -------------------------------- */
2512
2513   /* initialize access direction values to default (access from every side) */
2514   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2515     if (!IS_CUSTOM_ELEMENT(i))
2516       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2517
2518   /* set access direction value for certain elements from pre-defined list */
2519   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2520     element_info[access_direction_list[i].element].access_direction =
2521       access_direction_list[i].direction;
2522
2523   /* ---------- initialize explosion content ------------------------------- */
2524   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2525   {
2526     if (IS_CUSTOM_ELEMENT(i))
2527       continue;
2528
2529     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2530     {
2531       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2532
2533       element_info[i].content.e[x][y] =
2534         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2535          i == EL_PLAYER_2 ? EL_EMERALD_RED :
2536          i == EL_PLAYER_3 ? EL_EMERALD :
2537          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2538          i == EL_MOLE ? EL_EMERALD_RED :
2539          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2540          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2541          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2542          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2543          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2544          i == EL_WALL_EMERALD ? EL_EMERALD :
2545          i == EL_WALL_DIAMOND ? EL_DIAMOND :
2546          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2547          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2548          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2549          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2550          i == EL_WALL_PEARL ? EL_PEARL :
2551          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2552          EL_EMPTY);
2553     }
2554   }
2555
2556   /* ---------- initialize recursion detection ------------------------------ */
2557   recursion_loop_depth = 0;
2558   recursion_loop_detected = FALSE;
2559   recursion_loop_element = EL_UNDEFINED;
2560 }
2561
2562 int get_num_special_action(int element, int action_first, int action_last)
2563 {
2564   int num_special_action = 0;
2565   int i, j;
2566
2567   for (i = action_first; i <= action_last; i++)
2568   {
2569     boolean found = FALSE;
2570
2571     for (j = 0; j < NUM_DIRECTIONS; j++)
2572       if (el_act_dir2img(element, i, j) !=
2573           el_act_dir2img(element, ACTION_DEFAULT, j))
2574         found = TRUE;
2575
2576     if (found)
2577       num_special_action++;
2578     else
2579       break;
2580   }
2581
2582   return num_special_action;
2583 }
2584
2585
2586 /*
2587   =============================================================================
2588   InitGame()
2589   -----------------------------------------------------------------------------
2590   initialize and start new game
2591   =============================================================================
2592 */
2593
2594 void InitGame()
2595 {
2596   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
2597   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
2598   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
2599   boolean do_fading = (game_status == GAME_MODE_MAIN);
2600   int i, j, x, y;
2601
2602   game_status = GAME_MODE_PLAYING;
2603
2604   InitGameEngine();
2605
2606   /* don't play tapes over network */
2607   network_playing = (options.network && !tape.playing);
2608
2609   for (i = 0; i < MAX_PLAYERS; i++)
2610   {
2611     struct PlayerInfo *player = &stored_player[i];
2612
2613     player->index_nr = i;
2614     player->index_bit = (1 << i);
2615     player->element_nr = EL_PLAYER_1 + i;
2616
2617     player->present = FALSE;
2618     player->active = FALSE;
2619     player->killed = FALSE;
2620
2621     player->action = 0;
2622     player->effective_action = 0;
2623     player->programmed_action = 0;
2624
2625     player->score = 0;
2626     player->score_final = 0;
2627
2628     player->gems_still_needed = level.gems_needed;
2629     player->sokobanfields_still_needed = 0;
2630     player->lights_still_needed = 0;
2631     player->friends_still_needed = 0;
2632
2633     for (j = 0; j < MAX_NUM_KEYS; j++)
2634       player->key[j] = FALSE;
2635
2636     player->num_white_keys = 0;
2637
2638     player->dynabomb_count = 0;
2639     player->dynabomb_size = 1;
2640     player->dynabombs_left = 0;
2641     player->dynabomb_xl = FALSE;
2642
2643     player->MovDir = MV_NONE;
2644     player->MovPos = 0;
2645     player->GfxPos = 0;
2646     player->GfxDir = MV_NONE;
2647     player->GfxAction = ACTION_DEFAULT;
2648     player->Frame = 0;
2649     player->StepFrame = 0;
2650
2651     player->use_murphy = FALSE;
2652     player->artwork_element =
2653       (level.use_artwork_element[i] ? level.artwork_element[i] :
2654        player->element_nr);
2655
2656     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
2657     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2658
2659     player->gravity = level.initial_player_gravity[i];
2660
2661     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2662
2663     player->actual_frame_counter = 0;
2664
2665     player->step_counter = 0;
2666
2667     player->last_move_dir = MV_NONE;
2668
2669     player->is_active = FALSE;
2670
2671     player->is_waiting = FALSE;
2672     player->is_moving = FALSE;
2673     player->is_auto_moving = FALSE;
2674     player->is_digging = FALSE;
2675     player->is_snapping = FALSE;
2676     player->is_collecting = FALSE;
2677     player->is_pushing = FALSE;
2678     player->is_switching = FALSE;
2679     player->is_dropping = FALSE;
2680     player->is_dropping_pressed = FALSE;
2681
2682     player->is_bored = FALSE;
2683     player->is_sleeping = FALSE;
2684
2685     player->frame_counter_bored = -1;
2686     player->frame_counter_sleeping = -1;
2687
2688     player->anim_delay_counter = 0;
2689     player->post_delay_counter = 0;
2690
2691     player->dir_waiting = MV_NONE;
2692     player->action_waiting = ACTION_DEFAULT;
2693     player->last_action_waiting = ACTION_DEFAULT;
2694     player->special_action_bored = ACTION_DEFAULT;
2695     player->special_action_sleeping = ACTION_DEFAULT;
2696
2697     player->switch_x = -1;
2698     player->switch_y = -1;
2699
2700     player->drop_x = -1;
2701     player->drop_y = -1;
2702
2703     player->show_envelope = 0;
2704
2705     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2706
2707     player->push_delay       = -1;      /* initialized when pushing starts */
2708     player->push_delay_value = game.initial_push_delay_value;
2709
2710     player->drop_delay = 0;
2711     player->drop_pressed_delay = 0;
2712
2713     player->last_jx = -1;
2714     player->last_jy = -1;
2715     player->jx = -1;
2716     player->jy = -1;
2717
2718     player->shield_normal_time_left = 0;
2719     player->shield_deadly_time_left = 0;
2720
2721     player->inventory_infinite_element = EL_UNDEFINED;
2722     player->inventory_size = 0;
2723
2724     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2725     SnapField(player, 0, 0);
2726
2727     player->LevelSolved = FALSE;
2728     player->GameOver = FALSE;
2729
2730     player->LevelSolved_GameWon = FALSE;
2731     player->LevelSolved_GameEnd = FALSE;
2732     player->LevelSolved_PanelOff = FALSE;
2733     player->LevelSolved_SaveTape = FALSE;
2734     player->LevelSolved_SaveScore = FALSE;
2735   }
2736
2737   network_player_action_received = FALSE;
2738
2739 #if defined(NETWORK_AVALIABLE)
2740   /* initial null action */
2741   if (network_playing)
2742     SendToServer_MovePlayer(MV_NONE);
2743 #endif
2744
2745   ZX = ZY = -1;
2746   ExitX = ExitY = -1;
2747
2748   FrameCounter = 0;
2749   TimeFrames = 0;
2750   TimePlayed = 0;
2751   TimeLeft = level.time;
2752   TapeTime = 0;
2753
2754   ScreenMovDir = MV_NONE;
2755   ScreenMovPos = 0;
2756   ScreenGfxPos = 0;
2757
2758   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
2759
2760   AllPlayersGone = FALSE;
2761
2762   game.yamyam_content_nr = 0;
2763   game.magic_wall_active = FALSE;
2764   game.magic_wall_time_left = 0;
2765   game.light_time_left = 0;
2766   game.timegate_time_left = 0;
2767   game.switchgate_pos = 0;
2768   game.wind_direction = level.wind_direction_initial;
2769
2770 #if !USE_PLAYER_GRAVITY
2771   game.gravity = FALSE;
2772   game.explosions_delayed = TRUE;
2773 #endif
2774
2775   game.lenses_time_left = 0;
2776   game.magnify_time_left = 0;
2777
2778   game.ball_state = level.ball_state_initial;
2779   game.ball_content_nr = 0;
2780
2781   game.envelope_active = FALSE;
2782
2783   /* set focus to local player for network games, else to all players */
2784   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2785   game.centered_player_nr_next = game.centered_player_nr;
2786   game.set_centered_player = FALSE;
2787
2788   if (network_playing && tape.recording)
2789   {
2790     /* store client dependent player focus when recording network games */
2791     tape.centered_player_nr_next = game.centered_player_nr_next;
2792     tape.set_centered_player = TRUE;
2793   }
2794
2795   for (i = 0; i < NUM_BELTS; i++)
2796   {
2797     game.belt_dir[i] = MV_NONE;
2798     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
2799   }
2800
2801   for (i = 0; i < MAX_NUM_AMOEBA; i++)
2802     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2803
2804   SCAN_PLAYFIELD(x, y)
2805   {
2806     Feld[x][y] = level.field[x][y];
2807     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2808     ChangeDelay[x][y] = 0;
2809     ChangePage[x][y] = -1;
2810 #if USE_NEW_CUSTOM_VALUE
2811     CustomValue[x][y] = 0;              /* initialized in InitField() */
2812 #endif
2813     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2814     AmoebaNr[x][y] = 0;
2815     WasJustMoving[x][y] = 0;
2816     WasJustFalling[x][y] = 0;
2817     CheckCollision[x][y] = 0;
2818     CheckImpact[x][y] = 0;
2819     Stop[x][y] = FALSE;
2820     Pushed[x][y] = FALSE;
2821
2822     ChangeCount[x][y] = 0;
2823     ChangeEvent[x][y] = -1;
2824
2825     ExplodePhase[x][y] = 0;
2826     ExplodeDelay[x][y] = 0;
2827     ExplodeField[x][y] = EX_TYPE_NONE;
2828
2829     RunnerVisit[x][y] = 0;
2830     PlayerVisit[x][y] = 0;
2831
2832     GfxFrame[x][y] = 0;
2833     GfxRandom[x][y] = INIT_GFX_RANDOM();
2834     GfxElement[x][y] = EL_UNDEFINED;
2835     GfxAction[x][y] = ACTION_DEFAULT;
2836     GfxDir[x][y] = MV_NONE;
2837   }
2838
2839   SCAN_PLAYFIELD(x, y)
2840   {
2841     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2842       emulate_bd = FALSE;
2843     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2844       emulate_sb = FALSE;
2845     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2846       emulate_sp = FALSE;
2847
2848     InitField(x, y, TRUE);
2849   }
2850
2851   InitBeltMovement();
2852
2853   for (i = 0; i < MAX_PLAYERS; i++)
2854   {
2855     struct PlayerInfo *player = &stored_player[i];
2856
2857     /* set number of special actions for bored and sleeping animation */
2858     player->num_special_action_bored =
2859       get_num_special_action(player->artwork_element,
2860                              ACTION_BORING_1, ACTION_BORING_LAST);
2861     player->num_special_action_sleeping =
2862       get_num_special_action(player->artwork_element,
2863                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2864   }
2865
2866   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2867                     emulate_sb ? EMU_SOKOBAN :
2868                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2869
2870 #if USE_NEW_ALL_SLIPPERY
2871   /* initialize type of slippery elements */
2872   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2873   {
2874     if (!IS_CUSTOM_ELEMENT(i))
2875     {
2876       /* default: elements slip down either to the left or right randomly */
2877       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2878
2879       /* SP style elements prefer to slip down on the left side */
2880       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2881         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2882
2883       /* BD style elements prefer to slip down on the left side */
2884       if (game.emulation == EMU_BOULDERDASH)
2885         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2886     }
2887   }
2888 #endif
2889
2890   /* initialize explosion and ignition delay */
2891   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2892   {
2893     if (!IS_CUSTOM_ELEMENT(i))
2894     {
2895       int num_phase = 8;
2896       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2897                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2898                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
2899       int last_phase = (num_phase + 1) * delay;
2900       int half_phase = (num_phase / 2) * delay;
2901
2902       element_info[i].explosion_delay = last_phase - 1;
2903       element_info[i].ignition_delay = half_phase;
2904
2905       if (i == EL_BLACK_ORB)
2906         element_info[i].ignition_delay = 1;
2907     }
2908
2909 #if 0
2910     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
2911       element_info[i].explosion_delay = 1;
2912
2913     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
2914       element_info[i].ignition_delay = 1;
2915 #endif
2916   }
2917
2918   /* correct non-moving belts to start moving left */
2919   for (i = 0; i < NUM_BELTS; i++)
2920     if (game.belt_dir[i] == MV_NONE)
2921       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
2922
2923   /* check if any connected player was not found in playfield */
2924   for (i = 0; i < MAX_PLAYERS; i++)
2925   {
2926     struct PlayerInfo *player = &stored_player[i];
2927
2928     if (player->connected && !player->present)
2929     {
2930       for (j = 0; j < MAX_PLAYERS; j++)
2931       {
2932         struct PlayerInfo *some_player = &stored_player[j];
2933         int jx = some_player->jx, jy = some_player->jy;
2934
2935         /* assign first free player found that is present in the playfield */
2936         if (some_player->present && !some_player->connected)
2937         {
2938           player->present = TRUE;
2939           player->active = TRUE;
2940
2941           some_player->present = FALSE;
2942           some_player->active = FALSE;
2943
2944           player->artwork_element = some_player->artwork_element;
2945
2946           player->block_last_field       = some_player->block_last_field;
2947           player->block_delay_adjustment = some_player->block_delay_adjustment;
2948
2949           StorePlayer[jx][jy] = player->element_nr;
2950           player->jx = player->last_jx = jx;
2951           player->jy = player->last_jy = jy;
2952
2953           break;
2954         }
2955       }
2956     }
2957   }
2958
2959   if (tape.playing)
2960   {
2961     /* when playing a tape, eliminate all players who do not participate */
2962
2963     for (i = 0; i < MAX_PLAYERS; i++)
2964     {
2965       if (stored_player[i].active && !tape.player_participates[i])
2966       {
2967         struct PlayerInfo *player = &stored_player[i];
2968         int jx = player->jx, jy = player->jy;
2969
2970         player->active = FALSE;
2971         StorePlayer[jx][jy] = 0;
2972         Feld[jx][jy] = EL_EMPTY;
2973       }
2974     }
2975   }
2976   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
2977   {
2978     /* when in single player mode, eliminate all but the first active player */
2979
2980     for (i = 0; i < MAX_PLAYERS; i++)
2981     {
2982       if (stored_player[i].active)
2983       {
2984         for (j = i + 1; j < MAX_PLAYERS; j++)
2985         {
2986           if (stored_player[j].active)
2987           {
2988             struct PlayerInfo *player = &stored_player[j];
2989             int jx = player->jx, jy = player->jy;
2990
2991             player->active = FALSE;
2992             player->present = FALSE;
2993
2994             StorePlayer[jx][jy] = 0;
2995             Feld[jx][jy] = EL_EMPTY;
2996           }
2997         }
2998       }
2999     }
3000   }
3001
3002   /* when recording the game, store which players take part in the game */
3003   if (tape.recording)
3004   {
3005     for (i = 0; i < MAX_PLAYERS; i++)
3006       if (stored_player[i].active)
3007         tape.player_participates[i] = TRUE;
3008   }
3009
3010   if (options.debug)
3011   {
3012     for (i = 0; i < MAX_PLAYERS; i++)
3013     {
3014       struct PlayerInfo *player = &stored_player[i];
3015
3016       printf("Player %d: present == %d, connected == %d, active == %d.\n",
3017              i+1,
3018              player->present,
3019              player->connected,
3020              player->active);
3021       if (local_player == player)
3022         printf("Player  %d is local player.\n", i+1);
3023     }
3024   }
3025
3026   if (BorderElement == EL_EMPTY)
3027   {
3028     SBX_Left = 0;
3029     SBX_Right = lev_fieldx - SCR_FIELDX;
3030     SBY_Upper = 0;
3031     SBY_Lower = lev_fieldy - SCR_FIELDY;
3032   }
3033   else
3034   {
3035     SBX_Left = -1;
3036     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3037     SBY_Upper = -1;
3038     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3039   }
3040
3041   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3042     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3043
3044   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3045     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3046
3047   /* if local player not found, look for custom element that might create
3048      the player (make some assumptions about the right custom element) */
3049   if (!local_player->present)
3050   {
3051     int start_x = 0, start_y = 0;
3052     int found_rating = 0;
3053     int found_element = EL_UNDEFINED;
3054     int player_nr = local_player->index_nr;
3055
3056     SCAN_PLAYFIELD(x, y)
3057     {
3058       int element = Feld[x][y];
3059       int content;
3060       int xx, yy;
3061       boolean is_player;
3062
3063       if (level.use_start_element[player_nr] &&
3064           level.start_element[player_nr] == element &&
3065           found_rating < 4)
3066       {
3067         start_x = x;
3068         start_y = y;
3069
3070         found_rating = 4;
3071         found_element = element;
3072       }
3073
3074       if (!IS_CUSTOM_ELEMENT(element))
3075         continue;
3076
3077       if (CAN_CHANGE(element))
3078       {
3079         for (i = 0; i < element_info[element].num_change_pages; i++)
3080         {
3081           /* check for player created from custom element as single target */
3082           content = element_info[element].change_page[i].target_element;
3083           is_player = ELEM_IS_PLAYER(content);
3084
3085           if (is_player && (found_rating < 3 || element < found_element))
3086           {
3087             start_x = x;
3088             start_y = y;
3089
3090             found_rating = 3;
3091             found_element = element;
3092           }
3093         }
3094       }
3095
3096       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3097       {
3098         /* check for player created from custom element as explosion content */
3099         content = element_info[element].content.e[xx][yy];
3100         is_player = ELEM_IS_PLAYER(content);
3101
3102         if (is_player && (found_rating < 2 || element < found_element))
3103         {
3104           start_x = x + xx - 1;
3105           start_y = y + yy - 1;
3106
3107           found_rating = 2;
3108           found_element = element;
3109         }
3110
3111         if (!CAN_CHANGE(element))
3112           continue;
3113
3114         for (i = 0; i < element_info[element].num_change_pages; i++)
3115         {
3116           /* check for player created from custom element as extended target */
3117           content =
3118             element_info[element].change_page[i].target_content.e[xx][yy];
3119
3120           is_player = ELEM_IS_PLAYER(content);
3121
3122           if (is_player && (found_rating < 1 || element < found_element))
3123           {
3124             start_x = x + xx - 1;
3125             start_y = y + yy - 1;
3126
3127             found_rating = 1;
3128             found_element = element;
3129           }
3130         }
3131       }
3132     }
3133
3134     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3135                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3136                 start_x - MIDPOSX);
3137
3138     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3139                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3140                 start_y - MIDPOSY);
3141   }
3142   else
3143   {
3144     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3145                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3146                 local_player->jx - MIDPOSX);
3147
3148     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3149                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3150                 local_player->jy - MIDPOSY);
3151   }
3152
3153   StopAnimation();
3154
3155   if (!game.restart_level)
3156     CloseDoor(DOOR_CLOSE_1);
3157
3158   if (level_editor_test_game)
3159     fading = fading_none;
3160   else
3161     fading = menu.destination;
3162
3163 #if 1
3164   if (fading.anim_mode == ANIM_CROSSFADE)
3165     FadeCrossSaveBackbuffer();
3166   else
3167     FadeOut(REDRAW_FIELD);
3168 #else
3169   if (do_fading)
3170     FadeOut(REDRAW_FIELD);
3171 #endif
3172
3173   /* !!! FIX THIS (START) !!! */
3174   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3175   {
3176     InitGameEngine_EM();
3177
3178     /* blit playfield from scroll buffer to normal back buffer for fading in */
3179     BlitScreenToBitmap_EM(backbuffer);
3180   }
3181   else
3182   {
3183     DrawLevel();
3184     DrawAllPlayers();
3185
3186     /* after drawing the level, correct some elements */
3187     if (game.timegate_time_left == 0)
3188       CloseAllOpenTimegates();
3189
3190     /* blit playfield from scroll buffer to normal back buffer for fading in */
3191     if (setup.soft_scrolling)
3192       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3193
3194     redraw_mask |= REDRAW_FROM_BACKBUFFER;
3195   }
3196   /* !!! FIX THIS (END) !!! */
3197
3198 #if 1
3199   if (fading.anim_mode == ANIM_CROSSFADE)
3200     FadeCross(redraw_mask);
3201   else
3202     FadeIn(redraw_mask);
3203 #else
3204   if (do_fading)
3205     FadeIn(REDRAW_FIELD);
3206
3207   BackToFront();
3208 #endif
3209
3210   if (!game.restart_level)
3211   {
3212     /* copy default game door content to main double buffer */
3213     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3214                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3215   }
3216
3217   SetPanelBackground();
3218   SetDrawBackgroundMask(REDRAW_DOOR_1);
3219
3220   DrawGameDoorValues();
3221
3222   if (!game.restart_level)
3223   {
3224     UnmapGameButtons();
3225     UnmapTapeButtons();
3226     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3227     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3228     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3229     MapGameButtons();
3230     MapTapeButtons();
3231
3232     /* copy actual game door content to door double buffer for OpenDoor() */
3233     BlitBitmap(drawto, bitmap_db_door,
3234                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3235
3236     OpenDoor(DOOR_OPEN_ALL);
3237
3238     PlaySound(SND_GAME_STARTING);
3239
3240     if (setup.sound_music)
3241       PlayLevelMusic();
3242
3243     KeyboardAutoRepeatOffUnlessAutoplay();
3244
3245     if (options.debug)
3246     {
3247       for (i = 0; i < MAX_PLAYERS; i++)
3248         printf("Player %d %sactive.\n",
3249                i + 1, (stored_player[i].active ? "" : "not "));
3250     }
3251   }
3252
3253 #if 1
3254   UnmapAllGadgets();
3255
3256   MapGameButtons();
3257   MapTapeButtons();
3258 #endif
3259
3260   game.restart_level = FALSE;
3261 }
3262
3263 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3264 {
3265   /* this is used for non-R'n'D game engines to update certain engine values */
3266
3267   /* needed to determine if sounds are played within the visible screen area */
3268   scroll_x = actual_scroll_x;
3269   scroll_y = actual_scroll_y;
3270 }
3271
3272 void InitMovDir(int x, int y)
3273 {
3274   int i, element = Feld[x][y];
3275   static int xy[4][2] =
3276   {
3277     {  0, +1 },
3278     { +1,  0 },
3279     {  0, -1 },
3280     { -1,  0 }
3281   };
3282   static int direction[3][4] =
3283   {
3284     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
3285     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
3286     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
3287   };
3288
3289   switch (element)
3290   {
3291     case EL_BUG_RIGHT:
3292     case EL_BUG_UP:
3293     case EL_BUG_LEFT:
3294     case EL_BUG_DOWN:
3295       Feld[x][y] = EL_BUG;
3296       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3297       break;
3298
3299     case EL_SPACESHIP_RIGHT:
3300     case EL_SPACESHIP_UP:
3301     case EL_SPACESHIP_LEFT:
3302     case EL_SPACESHIP_DOWN:
3303       Feld[x][y] = EL_SPACESHIP;
3304       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3305       break;
3306
3307     case EL_BD_BUTTERFLY_RIGHT:
3308     case EL_BD_BUTTERFLY_UP:
3309     case EL_BD_BUTTERFLY_LEFT:
3310     case EL_BD_BUTTERFLY_DOWN:
3311       Feld[x][y] = EL_BD_BUTTERFLY;
3312       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3313       break;
3314
3315     case EL_BD_FIREFLY_RIGHT:
3316     case EL_BD_FIREFLY_UP:
3317     case EL_BD_FIREFLY_LEFT:
3318     case EL_BD_FIREFLY_DOWN:
3319       Feld[x][y] = EL_BD_FIREFLY;
3320       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3321       break;
3322
3323     case EL_PACMAN_RIGHT:
3324     case EL_PACMAN_UP:
3325     case EL_PACMAN_LEFT:
3326     case EL_PACMAN_DOWN:
3327       Feld[x][y] = EL_PACMAN;
3328       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3329       break;
3330
3331     case EL_YAMYAM_LEFT:
3332     case EL_YAMYAM_RIGHT:
3333     case EL_YAMYAM_UP:
3334     case EL_YAMYAM_DOWN:
3335       Feld[x][y] = EL_YAMYAM;
3336       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3337       break;
3338
3339     case EL_SP_SNIKSNAK:
3340       MovDir[x][y] = MV_UP;
3341       break;
3342
3343     case EL_SP_ELECTRON:
3344       MovDir[x][y] = MV_LEFT;
3345       break;
3346
3347     case EL_MOLE_LEFT:
3348     case EL_MOLE_RIGHT:
3349     case EL_MOLE_UP:
3350     case EL_MOLE_DOWN:
3351       Feld[x][y] = EL_MOLE;
3352       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3353       break;
3354
3355     default:
3356       if (IS_CUSTOM_ELEMENT(element))
3357       {
3358         struct ElementInfo *ei = &element_info[element];
3359         int move_direction_initial = ei->move_direction_initial;
3360         int move_pattern = ei->move_pattern;
3361
3362         if (move_direction_initial == MV_START_PREVIOUS)
3363         {
3364           if (MovDir[x][y] != MV_NONE)
3365             return;
3366
3367           move_direction_initial = MV_START_AUTOMATIC;
3368         }
3369
3370         if (move_direction_initial == MV_START_RANDOM)
3371           MovDir[x][y] = 1 << RND(4);
3372         else if (move_direction_initial & MV_ANY_DIRECTION)
3373           MovDir[x][y] = move_direction_initial;
3374         else if (move_pattern == MV_ALL_DIRECTIONS ||
3375                  move_pattern == MV_TURNING_LEFT ||
3376                  move_pattern == MV_TURNING_RIGHT ||
3377                  move_pattern == MV_TURNING_LEFT_RIGHT ||
3378                  move_pattern == MV_TURNING_RIGHT_LEFT ||
3379                  move_pattern == MV_TURNING_RANDOM)
3380           MovDir[x][y] = 1 << RND(4);
3381         else if (move_pattern == MV_HORIZONTAL)
3382           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3383         else if (move_pattern == MV_VERTICAL)
3384           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3385         else if (move_pattern & MV_ANY_DIRECTION)
3386           MovDir[x][y] = element_info[element].move_pattern;
3387         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
3388                  move_pattern == MV_ALONG_RIGHT_SIDE)
3389         {
3390           /* use random direction as default start direction */
3391           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3392             MovDir[x][y] = 1 << RND(4);
3393
3394           for (i = 0; i < NUM_DIRECTIONS; i++)
3395           {
3396             int x1 = x + xy[i][0];
3397             int y1 = y + xy[i][1];
3398
3399             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3400             {
3401               if (move_pattern == MV_ALONG_RIGHT_SIDE)
3402                 MovDir[x][y] = direction[0][i];
3403               else
3404                 MovDir[x][y] = direction[1][i];
3405
3406               break;
3407             }
3408           }
3409         }                
3410       }
3411       else
3412       {
3413         MovDir[x][y] = 1 << RND(4);
3414
3415         if (element != EL_BUG &&
3416             element != EL_SPACESHIP &&
3417             element != EL_BD_BUTTERFLY &&
3418             element != EL_BD_FIREFLY)
3419           break;
3420
3421         for (i = 0; i < NUM_DIRECTIONS; i++)
3422         {
3423           int x1 = x + xy[i][0];
3424           int y1 = y + xy[i][1];
3425
3426           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3427           {
3428             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3429             {
3430               MovDir[x][y] = direction[0][i];
3431               break;
3432             }
3433             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3434                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3435             {
3436               MovDir[x][y] = direction[1][i];
3437               break;
3438             }
3439           }
3440         }
3441       }
3442       break;
3443   }
3444
3445   GfxDir[x][y] = MovDir[x][y];
3446 }
3447
3448 void InitAmoebaNr(int x, int y)
3449 {
3450   int i;
3451   int group_nr = AmoebeNachbarNr(x, y);
3452
3453   if (group_nr == 0)
3454   {
3455     for (i = 1; i < MAX_NUM_AMOEBA; i++)
3456     {
3457       if (AmoebaCnt[i] == 0)
3458       {
3459         group_nr = i;
3460         break;
3461       }
3462     }
3463   }
3464
3465   AmoebaNr[x][y] = group_nr;
3466   AmoebaCnt[group_nr]++;
3467   AmoebaCnt2[group_nr]++;
3468 }
3469
3470 static void PlayerWins(struct PlayerInfo *player)
3471 {
3472   player->LevelSolved = TRUE;
3473   player->GameOver = TRUE;
3474
3475   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
3476                          level.native_em_level->lev->score : player->score);
3477 }
3478
3479 void GameWon()
3480 {
3481   static int time, time_final;
3482   static int score, score_final;
3483   static int game_over_delay_1 = 0;
3484   static int game_over_delay_2 = 0;
3485   int game_over_delay_value_1 = 50;
3486   int game_over_delay_value_2 = 50;
3487
3488   if (!local_player->LevelSolved_GameWon)
3489   {
3490     int i;
3491
3492     /* do not start end game actions before the player stops moving (to exit) */
3493     if (local_player->MovPos)
3494       return;
3495
3496     local_player->LevelSolved_GameWon = TRUE;
3497     local_player->LevelSolved_SaveTape = tape.recording;
3498     local_player->LevelSolved_SaveScore = !tape.playing;
3499
3500     if (tape.auto_play)         /* tape might already be stopped here */
3501       tape.auto_play_level_solved = TRUE;
3502
3503 #if 1
3504     TapeStop();
3505 #endif
3506
3507     game_over_delay_1 = game_over_delay_value_1;
3508     game_over_delay_2 = game_over_delay_value_2;
3509
3510     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3511     score = score_final = local_player->score_final;
3512
3513     if (TimeLeft > 0)
3514     {
3515       time_final = 0;
3516       score_final += TimeLeft * level.score[SC_TIME_BONUS];
3517     }
3518     else if (level.time == 0 && TimePlayed < 999)
3519     {
3520       time_final = 999;
3521       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3522     }
3523
3524     local_player->score_final = score_final;
3525
3526     if (level_editor_test_game)
3527     {
3528       time = time_final;
3529       score = score_final;
3530
3531       DrawGameValue_Time(time);
3532       DrawGameValue_Score(score);
3533     }
3534
3535     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3536     {
3537       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
3538       {
3539         /* close exit door after last player */
3540         if ((AllPlayersGone &&
3541              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3542               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3543               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3544             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3545             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3546         {
3547           int element = Feld[ExitX][ExitY];
3548
3549 #if 0
3550           if (element == EL_EM_EXIT_OPEN ||
3551               element == EL_EM_STEEL_EXIT_OPEN)
3552           {
3553             Bang(ExitX, ExitY);
3554           }
3555           else
3556 #endif
3557           {
3558             Feld[ExitX][ExitY] =
3559               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
3560                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
3561                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
3562                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
3563                EL_EM_STEEL_EXIT_CLOSING);
3564
3565             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3566           }
3567         }
3568
3569         /* player disappears */
3570         DrawLevelField(ExitX, ExitY);
3571       }
3572
3573       for (i = 0; i < MAX_PLAYERS; i++)
3574       {
3575         struct PlayerInfo *player = &stored_player[i];
3576
3577         if (player->present)
3578         {
3579           RemovePlayer(player);
3580
3581           /* player disappears */
3582           DrawLevelField(player->jx, player->jy);
3583         }
3584       }
3585     }
3586
3587     PlaySound(SND_GAME_WINNING);
3588   }
3589
3590   if (game_over_delay_1 > 0)
3591   {
3592     game_over_delay_1--;
3593
3594     return;
3595   }
3596
3597   if (time != time_final)
3598   {
3599     int time_to_go = ABS(time_final - time);
3600     int time_count_dir = (time < time_final ? +1 : -1);
3601     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
3602
3603     time  += time_count_steps * time_count_dir;
3604     score += time_count_steps * level.score[SC_TIME_BONUS];
3605
3606     DrawGameValue_Time(time);
3607     DrawGameValue_Score(score);
3608
3609     if (time == time_final)
3610       StopSound(SND_GAME_LEVELTIME_BONUS);
3611     else if (setup.sound_loops)
3612       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
3613     else
3614       PlaySound(SND_GAME_LEVELTIME_BONUS);
3615
3616     return;
3617   }
3618
3619   local_player->LevelSolved_PanelOff = TRUE;
3620
3621   if (game_over_delay_2 > 0)
3622   {
3623     game_over_delay_2--;
3624
3625     return;
3626   }
3627
3628 #if 1
3629   GameEnd();
3630 #endif
3631 }
3632
3633 void GameEnd()
3634 {
3635   int hi_pos;
3636   boolean raise_level = FALSE;
3637
3638   local_player->LevelSolved_GameEnd = TRUE;
3639
3640   CloseDoor(DOOR_CLOSE_1);
3641
3642   if (local_player->LevelSolved_SaveTape)
3643   {
3644 #if 0
3645     TapeStop();
3646 #endif
3647
3648 #if 1
3649     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
3650 #else
3651     SaveTape(tape.level_nr);            /* ask to save tape */
3652 #endif
3653   }
3654
3655   if (level_editor_test_game)
3656   {
3657     game_status = GAME_MODE_MAIN;
3658
3659 #if 1
3660     DrawAndFadeInMainMenu(REDRAW_FIELD);
3661 #else
3662     DrawMainMenu();
3663 #endif
3664
3665     return;
3666   }
3667
3668   if (!local_player->LevelSolved_SaveScore)
3669   {
3670 #if 1
3671     if (fading.anim_mode == ANIM_CROSSFADE)
3672       FadeCrossSaveBackbuffer();
3673     else
3674       FadeOut(REDRAW_FIELD);
3675 #else
3676     FadeOut(REDRAW_FIELD);
3677 #endif
3678
3679     game_status = GAME_MODE_MAIN;
3680
3681     DrawAndFadeInMainMenu(REDRAW_FIELD);
3682
3683     return;
3684   }
3685
3686   if (level_nr == leveldir_current->handicap_level)
3687   {
3688     leveldir_current->handicap_level++;
3689     SaveLevelSetup_SeriesInfo();
3690   }
3691
3692   if (level_nr < leveldir_current->last_level)
3693     raise_level = TRUE;                 /* advance to next level */
3694
3695   if ((hi_pos = NewHiScore()) >= 0) 
3696   {
3697     game_status = GAME_MODE_SCORES;
3698
3699     DrawHallOfFame(hi_pos);
3700
3701     if (raise_level)
3702     {
3703       level_nr++;
3704       TapeErase();
3705     }
3706   }
3707   else
3708   {
3709 #if 1
3710     if (fading.anim_mode == ANIM_CROSSFADE)
3711       FadeCrossSaveBackbuffer();
3712     else
3713       FadeOut(REDRAW_FIELD);
3714 #else
3715     FadeOut(REDRAW_FIELD);
3716 #endif
3717
3718     game_status = GAME_MODE_MAIN;
3719
3720     if (raise_level)
3721     {
3722       level_nr++;
3723       TapeErase();
3724     }
3725
3726     DrawAndFadeInMainMenu(REDRAW_FIELD);
3727   }
3728 }
3729
3730 int NewHiScore()
3731 {
3732   int k, l;
3733   int position = -1;
3734
3735   LoadScore(level_nr);
3736
3737   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3738       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
3739     return -1;
3740
3741   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
3742   {
3743     if (local_player->score_final > highscore[k].Score)
3744     {
3745       /* player has made it to the hall of fame */
3746
3747       if (k < MAX_SCORE_ENTRIES - 1)
3748       {
3749         int m = MAX_SCORE_ENTRIES - 1;
3750
3751 #ifdef ONE_PER_NAME
3752         for (l = k; l < MAX_SCORE_ENTRIES; l++)
3753           if (strEqual(setup.player_name, highscore[l].Name))
3754             m = l;
3755         if (m == k)     /* player's new highscore overwrites his old one */
3756           goto put_into_list;
3757 #endif
3758
3759         for (l = m; l > k; l--)
3760         {
3761           strcpy(highscore[l].Name, highscore[l - 1].Name);
3762           highscore[l].Score = highscore[l - 1].Score;
3763         }
3764       }
3765
3766 #ifdef ONE_PER_NAME
3767       put_into_list:
3768 #endif
3769       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3770       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3771       highscore[k].Score = local_player->score_final; 
3772       position = k;
3773       break;
3774     }
3775
3776 #ifdef ONE_PER_NAME
3777     else if (!strncmp(setup.player_name, highscore[k].Name,
3778                       MAX_PLAYER_NAME_LEN))
3779       break;    /* player already there with a higher score */
3780 #endif
3781
3782   }
3783
3784   if (position >= 0) 
3785     SaveScore(level_nr);
3786
3787   return position;
3788 }
3789
3790 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
3791 {
3792   int element = Feld[x][y];
3793   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3794   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3795   int horiz_move = (dx != 0);
3796   int sign = (horiz_move ? dx : dy);
3797   int step = sign * element_info[element].move_stepsize;
3798
3799   /* special values for move stepsize for spring and things on conveyor belt */
3800   if (horiz_move)
3801   {
3802     if (CAN_FALL(element) &&
3803         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3804       step = sign * MOVE_STEPSIZE_NORMAL / 2;
3805     else if (element == EL_SPRING)
3806       step = sign * MOVE_STEPSIZE_NORMAL * 2;
3807   }
3808
3809   return step;
3810 }
3811
3812 inline static int getElementMoveStepsize(int x, int y)
3813 {
3814   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
3815 }
3816
3817 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3818 {
3819   if (player->GfxAction != action || player->GfxDir != dir)
3820   {
3821 #if 0
3822     printf("Player frame reset! (%d => %d, %d => %d)\n",
3823            player->GfxAction, action, player->GfxDir, dir);
3824 #endif
3825
3826     player->GfxAction = action;
3827     player->GfxDir = dir;
3828     player->Frame = 0;
3829     player->StepFrame = 0;
3830   }
3831 }
3832
3833 #if USE_GFX_RESET_GFX_ANIMATION
3834 static void ResetGfxFrame(int x, int y, boolean redraw)
3835 {
3836   int element = Feld[x][y];
3837   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3838   int last_gfx_frame = GfxFrame[x][y];
3839
3840   if (graphic_info[graphic].anim_global_sync)
3841     GfxFrame[x][y] = FrameCounter;
3842   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3843     GfxFrame[x][y] = CustomValue[x][y];
3844   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3845     GfxFrame[x][y] = element_info[element].collect_score;
3846   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3847     GfxFrame[x][y] = ChangeDelay[x][y];
3848
3849   if (redraw && GfxFrame[x][y] != last_gfx_frame)
3850     DrawLevelGraphicAnimation(x, y, graphic);
3851 }
3852 #endif
3853
3854 static void ResetGfxAnimation(int x, int y)
3855 {
3856   GfxAction[x][y] = ACTION_DEFAULT;
3857   GfxDir[x][y] = MovDir[x][y];
3858   GfxFrame[x][y] = 0;
3859
3860 #if USE_GFX_RESET_GFX_ANIMATION
3861   ResetGfxFrame(x, y, FALSE);
3862 #endif
3863 }
3864
3865 static void ResetRandomAnimationValue(int x, int y)
3866 {
3867   GfxRandom[x][y] = INIT_GFX_RANDOM();
3868 }
3869
3870 void InitMovingField(int x, int y, int direction)
3871 {
3872   int element = Feld[x][y];
3873   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3874   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3875   int newx = x + dx;
3876   int newy = y + dy;
3877   boolean is_moving_before, is_moving_after;
3878 #if 0
3879   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3880 #endif
3881
3882   /* check if element was/is moving or being moved before/after mode change */
3883 #if 1
3884 #if 1
3885   is_moving_before = (WasJustMoving[x][y] != 0);
3886 #else
3887   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
3888   is_moving_before = WasJustMoving[x][y];
3889 #endif
3890 #else
3891   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3892 #endif
3893   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
3894
3895   /* reset animation only for moving elements which change direction of moving
3896      or which just started or stopped moving
3897      (else CEs with property "can move" / "not moving" are reset each frame) */
3898 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3899 #if 1
3900   if (is_moving_before != is_moving_after ||
3901       direction != MovDir[x][y])
3902     ResetGfxAnimation(x, y);
3903 #else
3904   if ((is_moving_before || is_moving_after) && !continues_moving)
3905     ResetGfxAnimation(x, y);
3906 #endif
3907 #else
3908   if (!continues_moving)
3909     ResetGfxAnimation(x, y);
3910 #endif
3911
3912   MovDir[x][y] = direction;
3913   GfxDir[x][y] = direction;
3914
3915 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3916   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3917                      direction == MV_DOWN && CAN_FALL(element) ?
3918                      ACTION_FALLING : ACTION_MOVING);
3919 #else
3920   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3921                      ACTION_FALLING : ACTION_MOVING);
3922 #endif
3923
3924   /* this is needed for CEs with property "can move" / "not moving" */
3925
3926   if (is_moving_after)
3927   {
3928     if (Feld[newx][newy] == EL_EMPTY)
3929       Feld[newx][newy] = EL_BLOCKED;
3930
3931     MovDir[newx][newy] = MovDir[x][y];
3932
3933 #if USE_NEW_CUSTOM_VALUE
3934     CustomValue[newx][newy] = CustomValue[x][y];
3935 #endif
3936
3937     GfxFrame[newx][newy] = GfxFrame[x][y];
3938     GfxRandom[newx][newy] = GfxRandom[x][y];
3939     GfxAction[newx][newy] = GfxAction[x][y];
3940     GfxDir[newx][newy] = GfxDir[x][y];
3941   }
3942 }
3943
3944 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3945 {
3946   int direction = MovDir[x][y];
3947   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3948   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
3949
3950   *goes_to_x = newx;
3951   *goes_to_y = newy;
3952 }
3953
3954 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3955 {
3956   int oldx = x, oldy = y;
3957   int direction = MovDir[x][y];
3958
3959   if (direction == MV_LEFT)
3960     oldx++;
3961   else if (direction == MV_RIGHT)
3962     oldx--;
3963   else if (direction == MV_UP)
3964     oldy++;
3965   else if (direction == MV_DOWN)
3966     oldy--;
3967
3968   *comes_from_x = oldx;
3969   *comes_from_y = oldy;
3970 }
3971
3972 int MovingOrBlocked2Element(int x, int y)
3973 {
3974   int element = Feld[x][y];
3975
3976   if (element == EL_BLOCKED)
3977   {
3978     int oldx, oldy;
3979
3980     Blocked2Moving(x, y, &oldx, &oldy);
3981     return Feld[oldx][oldy];
3982   }
3983   else
3984     return element;
3985 }
3986
3987 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3988 {
3989   /* like MovingOrBlocked2Element(), but if element is moving
3990      and (x,y) is the field the moving element is just leaving,
3991      return EL_BLOCKED instead of the element value */
3992   int element = Feld[x][y];
3993
3994   if (IS_MOVING(x, y))
3995   {
3996     if (element == EL_BLOCKED)
3997     {
3998       int oldx, oldy;
3999
4000       Blocked2Moving(x, y, &oldx, &oldy);
4001       return Feld[oldx][oldy];
4002     }
4003     else
4004       return EL_BLOCKED;
4005   }
4006   else
4007     return element;
4008 }
4009
4010 static void RemoveField(int x, int y)
4011 {
4012   Feld[x][y] = EL_EMPTY;
4013
4014   MovPos[x][y] = 0;
4015   MovDir[x][y] = 0;
4016   MovDelay[x][y] = 0;
4017
4018 #if USE_NEW_CUSTOM_VALUE
4019   CustomValue[x][y] = 0;
4020 #endif
4021
4022   AmoebaNr[x][y] = 0;
4023   ChangeDelay[x][y] = 0;
4024   ChangePage[x][y] = -1;
4025   Pushed[x][y] = FALSE;
4026
4027 #if 0
4028   ExplodeField[x][y] = EX_TYPE_NONE;
4029 #endif
4030
4031   GfxElement[x][y] = EL_UNDEFINED;
4032   GfxAction[x][y] = ACTION_DEFAULT;
4033   GfxDir[x][y] = MV_NONE;
4034 }
4035
4036 void RemoveMovingField(int x, int y)
4037 {
4038   int oldx = x, oldy = y, newx = x, newy = y;
4039   int element = Feld[x][y];
4040   int next_element = EL_UNDEFINED;
4041
4042   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4043     return;
4044
4045   if (IS_MOVING(x, y))
4046   {
4047     Moving2Blocked(x, y, &newx, &newy);
4048
4049     if (Feld[newx][newy] != EL_BLOCKED)
4050     {
4051       /* element is moving, but target field is not free (blocked), but
4052          already occupied by something different (example: acid pool);
4053          in this case, only remove the moving field, but not the target */
4054
4055       RemoveField(oldx, oldy);
4056
4057       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4058
4059       DrawLevelField(oldx, oldy);
4060
4061       return;
4062     }
4063   }
4064   else if (element == EL_BLOCKED)
4065   {
4066     Blocked2Moving(x, y, &oldx, &oldy);
4067     if (!IS_MOVING(oldx, oldy))
4068       return;
4069   }
4070
4071   if (element == EL_BLOCKED &&
4072       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4073        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4074        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4075        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4076        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4077        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4078     next_element = get_next_element(Feld[oldx][oldy]);
4079
4080   RemoveField(oldx, oldy);
4081   RemoveField(newx, newy);
4082
4083   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4084
4085   if (next_element != EL_UNDEFINED)
4086     Feld[oldx][oldy] = next_element;
4087
4088   DrawLevelField(oldx, oldy);
4089   DrawLevelField(newx, newy);
4090 }
4091
4092 void DrawDynamite(int x, int y)
4093 {
4094   int sx = SCREENX(x), sy = SCREENY(y);
4095   int graphic = el2img(Feld[x][y]);
4096   int frame;
4097
4098   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4099     return;
4100
4101   if (IS_WALKABLE_INSIDE(Back[x][y]))
4102     return;
4103
4104   if (Back[x][y])
4105     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4106   else if (Store[x][y])
4107     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4108
4109   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4110
4111   if (Back[x][y] || Store[x][y])
4112     DrawGraphicThruMask(sx, sy, graphic, frame);
4113   else
4114     DrawGraphic(sx, sy, graphic, frame);
4115 }
4116
4117 void CheckDynamite(int x, int y)
4118 {
4119   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4120   {
4121     MovDelay[x][y]--;
4122
4123     if (MovDelay[x][y] != 0)
4124     {
4125       DrawDynamite(x, y);
4126       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4127
4128       return;
4129     }
4130   }
4131
4132   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4133
4134   Bang(x, y);
4135 }
4136
4137 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4138 {
4139   boolean num_checked_players = 0;
4140   int i;
4141
4142   for (i = 0; i < MAX_PLAYERS; i++)
4143   {
4144     if (stored_player[i].active)
4145     {
4146       int sx = stored_player[i].jx;
4147       int sy = stored_player[i].jy;
4148
4149       if (num_checked_players == 0)
4150       {
4151         *sx1 = *sx2 = sx;
4152         *sy1 = *sy2 = sy;
4153       }
4154       else
4155       {
4156         *sx1 = MIN(*sx1, sx);
4157         *sy1 = MIN(*sy1, sy);
4158         *sx2 = MAX(*sx2, sx);
4159         *sy2 = MAX(*sy2, sy);
4160       }
4161
4162       num_checked_players++;
4163     }
4164   }
4165 }
4166
4167 static boolean checkIfAllPlayersFitToScreen_RND()
4168 {
4169   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4170
4171   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4172
4173   return (sx2 - sx1 < SCR_FIELDX &&
4174           sy2 - sy1 < SCR_FIELDY);
4175 }
4176
4177 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4178 {
4179   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4180
4181   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4182
4183   *sx = (sx1 + sx2) / 2;
4184   *sy = (sy1 + sy2) / 2;
4185 }
4186
4187 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4188                         boolean center_screen, boolean quick_relocation)
4189 {
4190   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4191   boolean no_delay = (tape.warp_forward);
4192   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4193   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4194
4195   if (quick_relocation)
4196   {
4197     int offset = (setup.scroll_delay ? 3 : 0);
4198
4199     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4200     {
4201       if (center_screen)
4202       {
4203         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4204                     x > SBX_Right + MIDPOSX ? SBX_Right :
4205                     x - MIDPOSX);
4206
4207         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4208                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4209                     y - MIDPOSY);
4210       }
4211       else
4212       {
4213         /* quick relocation (without scrolling), but do not center screen */
4214
4215         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4216                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4217                                old_x - MIDPOSX);
4218
4219         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4220                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4221                                old_y - MIDPOSY);
4222
4223         int offset_x = x + (scroll_x - center_scroll_x);
4224         int offset_y = y + (scroll_y - center_scroll_y);
4225
4226         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4227                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4228                     offset_x - MIDPOSX);
4229
4230         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4231                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4232                     offset_y - MIDPOSY);
4233       }
4234     }
4235     else
4236     {
4237       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
4238           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4239         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4240
4241       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
4242           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4243         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4244
4245       /* don't scroll over playfield boundaries */
4246       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4247         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4248
4249       /* don't scroll over playfield boundaries */
4250       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4251         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4252     }
4253
4254     RedrawPlayfield(TRUE, 0,0,0,0);
4255   }
4256   else
4257   {
4258     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4259                      x > SBX_Right + MIDPOSX ? SBX_Right :
4260                      x - MIDPOSX);
4261
4262     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4263                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
4264                      y - MIDPOSY);
4265
4266     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
4267
4268     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4269     {
4270       int dx = 0, dy = 0;
4271       int fx = FX, fy = FY;
4272
4273       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4274       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4275
4276       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
4277         break;
4278
4279       scroll_x -= dx;
4280       scroll_y -= dy;
4281
4282       fx += dx * TILEX / 2;
4283       fy += dy * TILEY / 2;
4284
4285       ScrollLevel(dx, dy);
4286       DrawAllPlayers();
4287
4288       /* scroll in two steps of half tile size to make things smoother */
4289       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4290       FlushDisplay();
4291       Delay(wait_delay_value);
4292
4293       /* scroll second step to align at full tile size */
4294       BackToFront();
4295       Delay(wait_delay_value);
4296     }
4297
4298     DrawAllPlayers();
4299     BackToFront();
4300     Delay(wait_delay_value);
4301   }
4302 }
4303
4304 void RelocatePlayer(int jx, int jy, int el_player_raw)
4305 {
4306   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4307   int player_nr = GET_PLAYER_NR(el_player);
4308   struct PlayerInfo *player = &stored_player[player_nr];
4309   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4310   boolean no_delay = (tape.warp_forward);
4311   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4312   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4313   int old_jx = player->jx;
4314   int old_jy = player->jy;
4315   int old_element = Feld[old_jx][old_jy];
4316   int element = Feld[jx][jy];
4317   boolean player_relocated = (old_jx != jx || old_jy != jy);
4318
4319   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4320   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
4321   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4322   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
4323   int leave_side_horiz = move_dir_horiz;
4324   int leave_side_vert  = move_dir_vert;
4325   int enter_side = enter_side_horiz | enter_side_vert;
4326   int leave_side = leave_side_horiz | leave_side_vert;
4327
4328   if (player->GameOver)         /* do not reanimate dead player */
4329     return;
4330
4331   if (!player_relocated)        /* no need to relocate the player */
4332     return;
4333
4334   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
4335   {
4336     RemoveField(jx, jy);        /* temporarily remove newly placed player */
4337     DrawLevelField(jx, jy);
4338   }
4339
4340   if (player->present)
4341   {
4342     while (player->MovPos)
4343     {
4344       ScrollPlayer(player, SCROLL_GO_ON);
4345       ScrollScreen(NULL, SCROLL_GO_ON);
4346
4347       AdvanceFrameAndPlayerCounters(player->index_nr);
4348
4349       DrawPlayer(player);
4350
4351       BackToFront();
4352       Delay(wait_delay_value);
4353     }
4354
4355     DrawPlayer(player);         /* needed here only to cleanup last field */
4356     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
4357
4358     player->is_moving = FALSE;
4359   }
4360
4361   if (IS_CUSTOM_ELEMENT(old_element))
4362     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4363                                CE_LEFT_BY_PLAYER,
4364                                player->index_bit, leave_side);
4365
4366   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4367                                       CE_PLAYER_LEAVES_X,
4368                                       player->index_bit, leave_side);
4369
4370   Feld[jx][jy] = el_player;
4371   InitPlayerField(jx, jy, el_player, TRUE);
4372
4373   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4374   {
4375     Feld[jx][jy] = element;
4376     InitField(jx, jy, FALSE);
4377   }
4378
4379   /* only visually relocate centered player */
4380   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4381                      FALSE, level.instant_relocation);
4382
4383   TestIfPlayerTouchesBadThing(jx, jy);
4384   TestIfPlayerTouchesCustomElement(jx, jy);
4385
4386   if (IS_CUSTOM_ELEMENT(element))
4387     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4388                                player->index_bit, enter_side);
4389
4390   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4391                                       player->index_bit, enter_side);
4392 }
4393
4394 void Explode(int ex, int ey, int phase, int mode)
4395 {
4396   int x, y;
4397   int last_phase;
4398   int border_element;
4399
4400   /* !!! eliminate this variable !!! */
4401   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4402
4403   if (game.explosions_delayed)
4404   {
4405     ExplodeField[ex][ey] = mode;
4406     return;
4407   }
4408
4409   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
4410   {
4411     int center_element = Feld[ex][ey];
4412     int artwork_element, explosion_element;     /* set these values later */
4413
4414 #if 0
4415     /* --- This is only really needed (and now handled) in "Impact()". --- */
4416     /* do not explode moving elements that left the explode field in time */
4417     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4418         center_element == EL_EMPTY &&
4419         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4420       return;
4421 #endif
4422
4423 #if 0
4424     /* !!! at this place, the center element may be EL_BLOCKED !!! */
4425     if (mode == EX_TYPE_NORMAL ||
4426         mode == EX_TYPE_CENTER ||
4427         mode == EX_TYPE_CROSS)
4428       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4429 #endif
4430
4431     /* remove things displayed in background while burning dynamite */
4432     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4433       Back[ex][ey] = 0;
4434
4435     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4436     {
4437       /* put moving element to center field (and let it explode there) */
4438       center_element = MovingOrBlocked2Element(ex, ey);
4439       RemoveMovingField(ex, ey);
4440       Feld[ex][ey] = center_element;
4441     }
4442
4443     /* now "center_element" is finally determined -- set related values now */
4444     artwork_element = center_element;           /* for custom player artwork */
4445     explosion_element = center_element;         /* for custom player artwork */
4446
4447     if (IS_PLAYER(ex, ey))
4448     {
4449       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4450
4451       artwork_element = stored_player[player_nr].artwork_element;
4452
4453       if (level.use_explosion_element[player_nr])
4454       {
4455         explosion_element = level.explosion_element[player_nr];
4456         artwork_element = explosion_element;
4457       }
4458     }
4459
4460 #if 1
4461     if (mode == EX_TYPE_NORMAL ||
4462         mode == EX_TYPE_CENTER ||
4463         mode == EX_TYPE_CROSS)
4464       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4465 #endif
4466
4467     last_phase = element_info[explosion_element].explosion_delay + 1;
4468
4469     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4470     {
4471       int xx = x - ex + 1;
4472       int yy = y - ey + 1;
4473       int element;
4474
4475       if (!IN_LEV_FIELD(x, y) ||
4476           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4477           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
4478         continue;
4479
4480       element = Feld[x][y];
4481
4482       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4483       {
4484         element = MovingOrBlocked2Element(x, y);
4485
4486         if (!IS_EXPLOSION_PROOF(element))
4487           RemoveMovingField(x, y);
4488       }
4489
4490       /* indestructible elements can only explode in center (but not flames) */
4491       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4492                                            mode == EX_TYPE_BORDER)) ||
4493           element == EL_FLAMES)
4494         continue;
4495
4496       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4497          behaviour, for example when touching a yamyam that explodes to rocks
4498          with active deadly shield, a rock is created under the player !!! */
4499       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4500 #if 0
4501       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4502           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
4503            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
4504 #else
4505       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
4506 #endif
4507       {
4508         if (IS_ACTIVE_BOMB(element))
4509         {
4510           /* re-activate things under the bomb like gate or penguin */
4511           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
4512           Back[x][y] = 0;
4513         }
4514
4515         continue;
4516       }
4517
4518       /* save walkable background elements while explosion on same tile */
4519       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
4520           (x != ex || y != ey || mode == EX_TYPE_BORDER))
4521         Back[x][y] = element;
4522
4523       /* ignite explodable elements reached by other explosion */
4524       if (element == EL_EXPLOSION)
4525         element = Store2[x][y];
4526
4527       if (AmoebaNr[x][y] &&
4528           (element == EL_AMOEBA_FULL ||
4529            element == EL_BD_AMOEBA ||
4530            element == EL_AMOEBA_GROWING))
4531       {
4532         AmoebaCnt[AmoebaNr[x][y]]--;
4533         AmoebaCnt2[AmoebaNr[x][y]]--;
4534       }
4535
4536       RemoveField(x, y);
4537
4538       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
4539       {
4540         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
4541
4542         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
4543
4544         if (PLAYERINFO(ex, ey)->use_murphy)
4545           Store[x][y] = EL_EMPTY;
4546       }
4547
4548       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
4549          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
4550       else if (ELEM_IS_PLAYER(center_element))
4551         Store[x][y] = EL_EMPTY;
4552       else if (center_element == EL_YAMYAM)
4553         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4554       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4555         Store[x][y] = element_info[center_element].content.e[xx][yy];
4556 #if 1
4557       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4558          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4559          otherwise) -- FIX THIS !!! */
4560       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4561         Store[x][y] = element_info[element].content.e[1][1];
4562 #else
4563       else if (!CAN_EXPLODE(element))
4564         Store[x][y] = element_info[element].content.e[1][1];
4565 #endif
4566       else
4567         Store[x][y] = EL_EMPTY;
4568
4569       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4570           center_element == EL_AMOEBA_TO_DIAMOND)
4571         Store2[x][y] = element;
4572
4573       Feld[x][y] = EL_EXPLOSION;
4574       GfxElement[x][y] = artwork_element;
4575
4576       ExplodePhase[x][y] = 1;
4577       ExplodeDelay[x][y] = last_phase;
4578
4579       Stop[x][y] = TRUE;
4580     }
4581
4582     if (center_element == EL_YAMYAM)
4583       game.yamyam_content_nr =
4584         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4585
4586     return;
4587   }
4588
4589   if (Stop[ex][ey])
4590     return;
4591
4592   x = ex;
4593   y = ey;
4594
4595   if (phase == 1)
4596     GfxFrame[x][y] = 0;         /* restart explosion animation */
4597
4598   last_phase = ExplodeDelay[x][y];
4599
4600   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4601
4602 #ifdef DEBUG
4603
4604   /* activate this even in non-DEBUG version until cause for crash in
4605      getGraphicAnimationFrame() (see below) is found and eliminated */
4606
4607 #endif
4608 #if 1
4609
4610 #if 1
4611   /* this can happen if the player leaves an explosion just in time */
4612   if (GfxElement[x][y] == EL_UNDEFINED)
4613     GfxElement[x][y] = EL_EMPTY;
4614 #else
4615   if (GfxElement[x][y] == EL_UNDEFINED)
4616   {
4617     printf("\n\n");
4618     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4619     printf("Explode(): This should never happen!\n");
4620     printf("\n\n");
4621
4622     GfxElement[x][y] = EL_EMPTY;
4623   }
4624 #endif
4625
4626 #endif
4627
4628   border_element = Store2[x][y];
4629   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4630     border_element = StorePlayer[x][y];
4631
4632   if (phase == element_info[border_element].ignition_delay ||
4633       phase == last_phase)
4634   {
4635     boolean border_explosion = FALSE;
4636
4637     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4638         !PLAYER_EXPLOSION_PROTECTED(x, y))
4639     {
4640       KillPlayerUnlessExplosionProtected(x, y);
4641       border_explosion = TRUE;
4642     }
4643     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4644     {
4645       Feld[x][y] = Store2[x][y];
4646       Store2[x][y] = 0;
4647       Bang(x, y);
4648       border_explosion = TRUE;
4649     }
4650     else if (border_element == EL_AMOEBA_TO_DIAMOND)
4651     {
4652       AmoebeUmwandeln(x, y);
4653       Store2[x][y] = 0;
4654       border_explosion = TRUE;
4655     }
4656
4657     /* if an element just explodes due to another explosion (chain-reaction),
4658        do not immediately end the new explosion when it was the last frame of
4659        the explosion (as it would be done in the following "if"-statement!) */
4660     if (border_explosion && phase == last_phase)
4661       return;
4662   }
4663
4664   if (phase == last_phase)
4665   {
4666     int element;
4667
4668     element = Feld[x][y] = Store[x][y];
4669     Store[x][y] = Store2[x][y] = 0;
4670     GfxElement[x][y] = EL_UNDEFINED;
4671
4672     /* player can escape from explosions and might therefore be still alive */
4673     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4674         element <= EL_PLAYER_IS_EXPLODING_4)
4675     {
4676       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4677       int explosion_element = EL_PLAYER_1 + player_nr;
4678       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4679       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4680
4681       if (level.use_explosion_element[player_nr])
4682         explosion_element = level.explosion_element[player_nr];
4683
4684       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4685                     element_info[explosion_element].content.e[xx][yy]);
4686     }
4687
4688     /* restore probably existing indestructible background element */
4689     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4690       element = Feld[x][y] = Back[x][y];
4691     Back[x][y] = 0;
4692
4693     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4694     GfxDir[x][y] = MV_NONE;
4695     ChangeDelay[x][y] = 0;
4696     ChangePage[x][y] = -1;
4697
4698 #if USE_NEW_CUSTOM_VALUE
4699     CustomValue[x][y] = 0;
4700 #endif
4701
4702     InitField_WithBug2(x, y, FALSE);
4703
4704     DrawLevelField(x, y);
4705
4706     TestIfElementTouchesCustomElement(x, y);
4707
4708     if (GFX_CRUMBLED(element))
4709       DrawLevelFieldCrumbledSandNeighbours(x, y);
4710
4711     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4712       StorePlayer[x][y] = 0;
4713
4714     if (ELEM_IS_PLAYER(element))
4715       RelocatePlayer(x, y, element);
4716   }
4717   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4718   {
4719     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4720     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4721
4722     if (phase == delay)
4723       DrawLevelFieldCrumbledSand(x, y);
4724
4725     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4726     {
4727       DrawLevelElement(x, y, Back[x][y]);
4728       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4729     }
4730     else if (IS_WALKABLE_UNDER(Back[x][y]))
4731     {
4732       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4733       DrawLevelElementThruMask(x, y, Back[x][y]);
4734     }
4735     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4736       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4737   }
4738 }
4739
4740 void DynaExplode(int ex, int ey)
4741 {
4742   int i, j;
4743   int dynabomb_element = Feld[ex][ey];
4744   int dynabomb_size = 1;
4745   boolean dynabomb_xl = FALSE;
4746   struct PlayerInfo *player;
4747   static int xy[4][2] =
4748   {
4749     { 0, -1 },
4750     { -1, 0 },
4751     { +1, 0 },
4752     { 0, +1 }
4753   };
4754
4755   if (IS_ACTIVE_BOMB(dynabomb_element))
4756   {
4757     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4758     dynabomb_size = player->dynabomb_size;
4759     dynabomb_xl = player->dynabomb_xl;
4760     player->dynabombs_left++;
4761   }
4762
4763   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4764
4765   for (i = 0; i < NUM_DIRECTIONS; i++)
4766   {
4767     for (j = 1; j <= dynabomb_size; j++)
4768     {
4769       int x = ex + j * xy[i][0];
4770       int y = ey + j * xy[i][1];
4771       int element;
4772
4773       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4774         break;
4775
4776       element = Feld[x][y];
4777
4778       /* do not restart explosions of fields with active bombs */
4779       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4780         continue;
4781
4782       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4783
4784       if (element != EL_EMPTY && element != EL_EXPLOSION &&
4785           !IS_DIGGABLE(element) && !dynabomb_xl)
4786         break;
4787     }
4788   }
4789 }
4790
4791 void Bang(int x, int y)
4792 {
4793   int element = MovingOrBlocked2Element(x, y);
4794   int explosion_type = EX_TYPE_NORMAL;
4795
4796   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4797   {
4798     struct PlayerInfo *player = PLAYERINFO(x, y);
4799
4800     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4801                             player->element_nr);
4802
4803     if (level.use_explosion_element[player->index_nr])
4804     {
4805       int explosion_element = level.explosion_element[player->index_nr];
4806
4807       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4808         explosion_type = EX_TYPE_CROSS;
4809       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4810         explosion_type = EX_TYPE_CENTER;
4811     }
4812   }
4813
4814   switch (element)
4815   {
4816     case EL_BUG:
4817     case EL_SPACESHIP:
4818     case EL_BD_BUTTERFLY:
4819     case EL_BD_FIREFLY:
4820     case EL_YAMYAM:
4821     case EL_DARK_YAMYAM:
4822     case EL_ROBOT:
4823     case EL_PACMAN:
4824     case EL_MOLE:
4825       RaiseScoreElement(element);
4826       break;
4827
4828     case EL_DYNABOMB_PLAYER_1_ACTIVE:
4829     case EL_DYNABOMB_PLAYER_2_ACTIVE:
4830     case EL_DYNABOMB_PLAYER_3_ACTIVE:
4831     case EL_DYNABOMB_PLAYER_4_ACTIVE:
4832     case EL_DYNABOMB_INCREASE_NUMBER:
4833     case EL_DYNABOMB_INCREASE_SIZE:
4834     case EL_DYNABOMB_INCREASE_POWER:
4835       explosion_type = EX_TYPE_DYNA;
4836       break;
4837
4838     case EL_DC_LANDMINE:
4839 #if 0
4840     case EL_EM_EXIT_OPEN:
4841     case EL_EM_STEEL_EXIT_OPEN:
4842 #endif
4843       explosion_type = EX_TYPE_CENTER;
4844       break;
4845
4846     case EL_PENGUIN:
4847     case EL_LAMP:
4848     case EL_LAMP_ACTIVE:
4849     case EL_AMOEBA_TO_DIAMOND:
4850       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
4851         explosion_type = EX_TYPE_CENTER;
4852       break;
4853
4854     default:
4855       if (element_info[element].explosion_type == EXPLODES_CROSS)
4856         explosion_type = EX_TYPE_CROSS;
4857       else if (element_info[element].explosion_type == EXPLODES_1X1)
4858         explosion_type = EX_TYPE_CENTER;
4859       break;
4860   }
4861
4862   if (explosion_type == EX_TYPE_DYNA)
4863     DynaExplode(x, y);
4864   else
4865     Explode(x, y, EX_PHASE_START, explosion_type);
4866
4867   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4868 }
4869
4870 void SplashAcid(int x, int y)
4871 {
4872   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4873       (!IN_LEV_FIELD(x - 1, y - 2) ||
4874        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4875     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4876
4877   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4878       (!IN_LEV_FIELD(x + 1, y - 2) ||
4879        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4880     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4881
4882   PlayLevelSound(x, y, SND_ACID_SPLASHING);
4883 }
4884
4885 static void InitBeltMovement()
4886 {
4887   static int belt_base_element[4] =
4888   {
4889     EL_CONVEYOR_BELT_1_LEFT,
4890     EL_CONVEYOR_BELT_2_LEFT,
4891     EL_CONVEYOR_BELT_3_LEFT,
4892     EL_CONVEYOR_BELT_4_LEFT
4893   };
4894   static int belt_base_active_element[4] =
4895   {
4896     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4897     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4898     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4899     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4900   };
4901
4902   int x, y, i, j;
4903
4904   /* set frame order for belt animation graphic according to belt direction */
4905   for (i = 0; i < NUM_BELTS; i++)
4906   {
4907     int belt_nr = i;
4908
4909     for (j = 0; j < NUM_BELT_PARTS; j++)
4910     {
4911       int element = belt_base_active_element[belt_nr] + j;
4912       int graphic = el2img(element);
4913
4914       if (game.belt_dir[i] == MV_LEFT)
4915         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4916       else
4917         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4918     }
4919   }
4920
4921   SCAN_PLAYFIELD(x, y)
4922   {
4923     int element = Feld[x][y];
4924
4925     for (i = 0; i < NUM_BELTS; i++)
4926     {
4927       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4928       {
4929         int e_belt_nr = getBeltNrFromBeltElement(element);
4930         int belt_nr = i;
4931
4932         if (e_belt_nr == belt_nr)
4933         {
4934           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4935
4936           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4937         }
4938       }
4939     }
4940   }
4941 }
4942
4943 static void ToggleBeltSwitch(int x, int y)
4944 {
4945   static int belt_base_element[4] =
4946   {
4947     EL_CONVEYOR_BELT_1_LEFT,
4948     EL_CONVEYOR_BELT_2_LEFT,
4949     EL_CONVEYOR_BELT_3_LEFT,
4950     EL_CONVEYOR_BELT_4_LEFT
4951   };
4952   static int belt_base_active_element[4] =
4953   {
4954     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4955     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4956     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4957     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4958   };
4959   static int belt_base_switch_element[4] =
4960   {
4961     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4962     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4963     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4964     EL_CONVEYOR_BELT_4_SWITCH_LEFT
4965   };
4966   static int belt_move_dir[4] =
4967   {
4968     MV_LEFT,
4969     MV_NONE,
4970     MV_RIGHT,
4971     MV_NONE,
4972   };
4973
4974   int element = Feld[x][y];
4975   int belt_nr = getBeltNrFromBeltSwitchElement(element);
4976   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4977   int belt_dir = belt_move_dir[belt_dir_nr];
4978   int xx, yy, i;
4979
4980   if (!IS_BELT_SWITCH(element))
4981     return;
4982
4983   game.belt_dir_nr[belt_nr] = belt_dir_nr;
4984   game.belt_dir[belt_nr] = belt_dir;
4985
4986   if (belt_dir_nr == 3)
4987     belt_dir_nr = 1;
4988
4989   /* set frame order for belt animation graphic according to belt direction */
4990   for (i = 0; i < NUM_BELT_PARTS; i++)
4991   {
4992     int element = belt_base_active_element[belt_nr] + i;
4993     int graphic = el2img(element);
4994
4995     if (belt_dir == MV_LEFT)
4996       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4997     else
4998       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4999   }
5000
5001   SCAN_PLAYFIELD(xx, yy)
5002   {
5003     int element = Feld[xx][yy];
5004
5005     if (IS_BELT_SWITCH(element))
5006     {
5007       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5008
5009       if (e_belt_nr == belt_nr)
5010       {
5011         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5012         DrawLevelField(xx, yy);
5013       }
5014     }
5015     else if (IS_BELT(element) && belt_dir != MV_NONE)
5016     {
5017       int e_belt_nr = getBeltNrFromBeltElement(element);
5018
5019       if (e_belt_nr == belt_nr)
5020       {
5021         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5022
5023         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5024         DrawLevelField(xx, yy);
5025       }
5026     }
5027     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5028     {
5029       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5030
5031       if (e_belt_nr == belt_nr)
5032       {
5033         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5034
5035         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5036         DrawLevelField(xx, yy);
5037       }
5038     }
5039   }
5040 }
5041
5042 static void ToggleSwitchgateSwitch(int x, int y)
5043 {
5044   int xx, yy;
5045
5046   game.switchgate_pos = !game.switchgate_pos;
5047
5048   SCAN_PLAYFIELD(xx, yy)
5049   {
5050     int element = Feld[xx][yy];
5051
5052 #if !USE_BOTH_SWITCHGATE_SWITCHES
5053     if (element == EL_SWITCHGATE_SWITCH_UP ||
5054         element == EL_SWITCHGATE_SWITCH_DOWN)
5055     {
5056       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5057       DrawLevelField(xx, yy);
5058     }
5059     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5060              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5061     {
5062       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5063       DrawLevelField(xx, yy);
5064     }
5065 #else
5066     if (element == EL_SWITCHGATE_SWITCH_UP)
5067     {
5068       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5069       DrawLevelField(xx, yy);
5070     }
5071     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5072     {
5073       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5074       DrawLevelField(xx, yy);
5075     }
5076     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5077     {
5078       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5079       DrawLevelField(xx, yy);
5080     }
5081     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5082     {
5083       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5084       DrawLevelField(xx, yy);
5085     }
5086 #endif
5087     else if (element == EL_SWITCHGATE_OPEN ||
5088              element == EL_SWITCHGATE_OPENING)
5089     {
5090       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5091
5092       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5093     }
5094     else if (element == EL_SWITCHGATE_CLOSED ||
5095              element == EL_SWITCHGATE_CLOSING)
5096     {
5097       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5098
5099       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5100     }
5101   }
5102 }
5103
5104 static int getInvisibleActiveFromInvisibleElement(int element)
5105 {
5106   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5107           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5108           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5109           element);
5110 }
5111
5112 static int getInvisibleFromInvisibleActiveElement(int element)
5113 {
5114   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5115           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5116           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5117           element);
5118 }
5119
5120 static void RedrawAllLightSwitchesAndInvisibleElements()
5121 {
5122   int x, y;
5123
5124   SCAN_PLAYFIELD(x, y)
5125   {
5126     int element = Feld[x][y];
5127
5128     if (element == EL_LIGHT_SWITCH &&
5129         game.light_time_left > 0)
5130     {
5131       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5132       DrawLevelField(x, y);
5133     }
5134     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5135              game.light_time_left == 0)
5136     {
5137       Feld[x][y] = EL_LIGHT_SWITCH;
5138       DrawLevelField(x, y);
5139     }
5140     else if (element == EL_EMC_DRIPPER &&
5141              game.light_time_left > 0)
5142     {
5143       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5144       DrawLevelField(x, y);
5145     }
5146     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5147              game.light_time_left == 0)
5148     {
5149       Feld[x][y] = EL_EMC_DRIPPER;
5150       DrawLevelField(x, y);
5151     }
5152     else if (element == EL_INVISIBLE_STEELWALL ||
5153              element == EL_INVISIBLE_WALL ||
5154              element == EL_INVISIBLE_SAND)
5155     {
5156       if (game.light_time_left > 0)
5157         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5158
5159       DrawLevelField(x, y);
5160
5161       /* uncrumble neighbour fields, if needed */
5162       if (element == EL_INVISIBLE_SAND)
5163         DrawLevelFieldCrumbledSandNeighbours(x, y);
5164     }
5165     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5166              element == EL_INVISIBLE_WALL_ACTIVE ||
5167              element == EL_INVISIBLE_SAND_ACTIVE)
5168     {
5169       if (game.light_time_left == 0)
5170         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5171
5172       DrawLevelField(x, y);
5173
5174       /* re-crumble neighbour fields, if needed */
5175       if (element == EL_INVISIBLE_SAND)
5176         DrawLevelFieldCrumbledSandNeighbours(x, y);
5177     }
5178   }
5179 }
5180
5181 static void RedrawAllInvisibleElementsForLenses()
5182 {
5183   int x, y;
5184
5185   SCAN_PLAYFIELD(x, y)
5186   {
5187     int element = Feld[x][y];
5188
5189     if (element == EL_EMC_DRIPPER &&
5190         game.lenses_time_left > 0)
5191     {
5192       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5193       DrawLevelField(x, y);
5194     }
5195     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5196              game.lenses_time_left == 0)
5197     {
5198       Feld[x][y] = EL_EMC_DRIPPER;
5199       DrawLevelField(x, y);
5200     }
5201     else if (element == EL_INVISIBLE_STEELWALL ||
5202              element == EL_INVISIBLE_WALL ||
5203              element == EL_INVISIBLE_SAND)
5204     {
5205       if (game.lenses_time_left > 0)
5206         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5207
5208       DrawLevelField(x, y);
5209
5210       /* uncrumble neighbour fields, if needed */
5211       if (element == EL_INVISIBLE_SAND)
5212         DrawLevelFieldCrumbledSandNeighbours(x, y);
5213     }
5214     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5215              element == EL_INVISIBLE_WALL_ACTIVE ||
5216              element == EL_INVISIBLE_SAND_ACTIVE)
5217     {
5218       if (game.lenses_time_left == 0)
5219         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5220
5221       DrawLevelField(x, y);
5222
5223       /* re-crumble neighbour fields, if needed */
5224       if (element == EL_INVISIBLE_SAND)
5225         DrawLevelFieldCrumbledSandNeighbours(x, y);
5226     }
5227   }
5228 }
5229
5230 static void RedrawAllInvisibleElementsForMagnifier()
5231 {
5232   int x, y;
5233
5234   SCAN_PLAYFIELD(x, y)
5235   {
5236     int element = Feld[x][y];
5237
5238     if (element == EL_EMC_FAKE_GRASS &&
5239         game.magnify_time_left > 0)
5240     {
5241       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5242       DrawLevelField(x, y);
5243     }
5244     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5245              game.magnify_time_left == 0)
5246     {
5247       Feld[x][y] = EL_EMC_FAKE_GRASS;
5248       DrawLevelField(x, y);
5249     }
5250     else if (IS_GATE_GRAY(element) &&
5251              game.magnify_time_left > 0)
5252     {
5253       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5254                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5255                     IS_EM_GATE_GRAY(element) ?
5256                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5257                     IS_EMC_GATE_GRAY(element) ?
5258                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5259                     element);
5260       DrawLevelField(x, y);
5261     }
5262     else if (IS_GATE_GRAY_ACTIVE(element) &&
5263              game.magnify_time_left == 0)
5264     {
5265       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5266                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5267                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5268                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5269                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5270                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5271                     element);
5272       DrawLevelField(x, y);
5273     }
5274   }
5275 }
5276
5277 static void ToggleLightSwitch(int x, int y)
5278 {
5279   int element = Feld[x][y];
5280
5281   game.light_time_left =
5282     (element == EL_LIGHT_SWITCH ?
5283      level.time_light * FRAMES_PER_SECOND : 0);
5284
5285   RedrawAllLightSwitchesAndInvisibleElements();
5286 }
5287
5288 static void ActivateTimegateSwitch(int x, int y)
5289 {
5290   int xx, yy;
5291
5292   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5293
5294   SCAN_PLAYFIELD(xx, yy)
5295   {
5296     int element = Feld[xx][yy];
5297
5298     if (element == EL_TIMEGATE_CLOSED ||
5299         element == EL_TIMEGATE_CLOSING)
5300     {
5301       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5302       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5303     }
5304
5305     /*
5306     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5307     {
5308       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5309       DrawLevelField(xx, yy);
5310     }
5311     */
5312
5313   }
5314
5315 #if 1
5316   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5317                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5318 #else
5319   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5320 #endif
5321 }
5322
5323 void Impact(int x, int y)
5324 {
5325   boolean last_line = (y == lev_fieldy - 1);
5326   boolean object_hit = FALSE;
5327   boolean impact = (last_line || object_hit);
5328   int element = Feld[x][y];
5329   int smashed = EL_STEELWALL;
5330
5331   if (!last_line)       /* check if element below was hit */
5332   {
5333     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5334       return;
5335
5336     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5337                                          MovDir[x][y + 1] != MV_DOWN ||
5338                                          MovPos[x][y + 1] <= TILEY / 2));
5339
5340     /* do not smash moving elements that left the smashed field in time */
5341     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5342         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5343       object_hit = FALSE;
5344
5345 #if USE_QUICKSAND_IMPACT_BUGFIX
5346     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5347     {
5348       RemoveMovingField(x, y + 1);
5349       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5350       Feld[x][y + 2] = EL_ROCK;
5351       DrawLevelField(x, y + 2);
5352
5353       object_hit = TRUE;
5354     }
5355
5356     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5357     {
5358       RemoveMovingField(x, y + 1);
5359       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5360       Feld[x][y + 2] = EL_ROCK;
5361       DrawLevelField(x, y + 2);
5362
5363       object_hit = TRUE;
5364     }
5365 #endif
5366
5367     if (object_hit)
5368       smashed = MovingOrBlocked2Element(x, y + 1);
5369
5370     impact = (last_line || object_hit);
5371   }
5372
5373   if (!last_line && smashed == EL_ACID) /* element falls into acid */
5374   {
5375     SplashAcid(x, y + 1);
5376     return;
5377   }
5378
5379   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5380   /* only reset graphic animation if graphic really changes after impact */
5381   if (impact &&
5382       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5383   {
5384     ResetGfxAnimation(x, y);
5385     DrawLevelField(x, y);
5386   }
5387
5388   if (impact && CAN_EXPLODE_IMPACT(element))
5389   {
5390     Bang(x, y);
5391     return;
5392   }
5393   else if (impact && element == EL_PEARL &&
5394            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5395   {
5396     ResetGfxAnimation(x, y);
5397
5398     Feld[x][y] = EL_PEARL_BREAKING;
5399     PlayLevelSound(x, y, SND_PEARL_BREAKING);
5400     return;
5401   }
5402   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5403   {
5404     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5405
5406     return;
5407   }
5408
5409   if (impact && element == EL_AMOEBA_DROP)
5410   {
5411     if (object_hit && IS_PLAYER(x, y + 1))
5412       KillPlayerUnlessEnemyProtected(x, y + 1);
5413     else if (object_hit && smashed == EL_PENGUIN)
5414       Bang(x, y + 1);
5415     else
5416     {
5417       Feld[x][y] = EL_AMOEBA_GROWING;
5418       Store[x][y] = EL_AMOEBA_WET;
5419
5420       ResetRandomAnimationValue(x, y);
5421     }
5422     return;
5423   }
5424
5425   if (object_hit)               /* check which object was hit */
5426   {
5427     if ((CAN_PASS_MAGIC_WALL(element) && 
5428          (smashed == EL_MAGIC_WALL ||
5429           smashed == EL_BD_MAGIC_WALL)) ||
5430         (CAN_PASS_DC_MAGIC_WALL(element) &&
5431          smashed == EL_DC_MAGIC_WALL))
5432     {
5433       int xx, yy;
5434       int activated_magic_wall =
5435         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5436          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5437          EL_DC_MAGIC_WALL_ACTIVE);
5438
5439       /* activate magic wall / mill */
5440       SCAN_PLAYFIELD(xx, yy)
5441       {
5442         if (Feld[xx][yy] == smashed)
5443           Feld[xx][yy] = activated_magic_wall;
5444       }
5445
5446       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5447       game.magic_wall_active = TRUE;
5448
5449       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5450                             SND_MAGIC_WALL_ACTIVATING :
5451                             smashed == EL_BD_MAGIC_WALL ?
5452                             SND_BD_MAGIC_WALL_ACTIVATING :
5453                             SND_DC_MAGIC_WALL_ACTIVATING));
5454     }
5455
5456     if (IS_PLAYER(x, y + 1))
5457     {
5458       if (CAN_SMASH_PLAYER(element))
5459       {
5460         KillPlayerUnlessEnemyProtected(x, y + 1);
5461         return;
5462       }
5463     }
5464     else if (smashed == EL_PENGUIN)
5465     {
5466       if (CAN_SMASH_PLAYER(element))
5467       {
5468         Bang(x, y + 1);
5469         return;
5470       }
5471     }
5472     else if (element == EL_BD_DIAMOND)
5473     {
5474       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5475       {
5476         Bang(x, y + 1);
5477         return;
5478       }
5479     }
5480     else if (((element == EL_SP_INFOTRON ||
5481                element == EL_SP_ZONK) &&
5482               (smashed == EL_SP_SNIKSNAK ||
5483                smashed == EL_SP_ELECTRON ||
5484                smashed == EL_SP_DISK_ORANGE)) ||
5485              (element == EL_SP_INFOTRON &&
5486               smashed == EL_SP_DISK_YELLOW))
5487     {
5488       Bang(x, y + 1);
5489       return;
5490     }
5491     else if (CAN_SMASH_EVERYTHING(element))
5492     {
5493       if (IS_CLASSIC_ENEMY(smashed) ||
5494           CAN_EXPLODE_SMASHED(smashed))
5495       {
5496         Bang(x, y + 1);
5497         return;
5498       }
5499       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5500       {
5501         if (smashed == EL_LAMP ||
5502             smashed == EL_LAMP_ACTIVE)
5503         {
5504           Bang(x, y + 1);
5505           return;
5506         }
5507         else if (smashed == EL_NUT)
5508         {
5509           Feld[x][y + 1] = EL_NUT_BREAKING;
5510           PlayLevelSound(x, y, SND_NUT_BREAKING);
5511           RaiseScoreElement(EL_NUT);
5512           return;
5513         }
5514         else if (smashed == EL_PEARL)
5515         {
5516           ResetGfxAnimation(x, y);
5517
5518           Feld[x][y + 1] = EL_PEARL_BREAKING;
5519           PlayLevelSound(x, y, SND_PEARL_BREAKING);
5520           return;
5521         }
5522         else if (smashed == EL_DIAMOND)
5523         {
5524           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
5525           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
5526           return;
5527         }
5528         else if (IS_BELT_SWITCH(smashed))
5529         {
5530           ToggleBeltSwitch(x, y + 1);
5531         }
5532         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
5533                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
5534                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
5535                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
5536         {
5537           ToggleSwitchgateSwitch(x, y + 1);
5538         }
5539         else if (smashed == EL_LIGHT_SWITCH ||
5540                  smashed == EL_LIGHT_SWITCH_ACTIVE)
5541         {
5542           ToggleLightSwitch(x, y + 1);
5543         }
5544         else
5545         {
5546 #if 0
5547           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
5548 #endif
5549
5550           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5551
5552           CheckElementChangeBySide(x, y + 1, smashed, element,
5553                                    CE_SWITCHED, CH_SIDE_TOP);
5554           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5555                                             CH_SIDE_TOP);
5556         }
5557       }
5558       else
5559       {
5560         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5561       }
5562     }
5563   }
5564
5565   /* play sound of magic wall / mill */
5566   if (!last_line &&
5567       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5568        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
5569        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
5570   {
5571     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5572       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5573     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5574       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5575     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5576       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5577
5578     return;
5579   }
5580
5581   /* play sound of object that hits the ground */
5582   if (last_line || object_hit)
5583     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5584 }
5585
5586 inline static void TurnRoundExt(int x, int y)
5587 {
5588   static struct
5589   {
5590     int dx, dy;
5591   } move_xy[] =
5592   {
5593     {  0,  0 },
5594     { -1,  0 },
5595     { +1,  0 },
5596     {  0,  0 },
5597     {  0, -1 },
5598     {  0,  0 }, { 0, 0 }, { 0, 0 },
5599     {  0, +1 }
5600   };
5601   static struct
5602   {
5603     int left, right, back;
5604   } turn[] =
5605   {
5606     { 0,        0,              0        },
5607     { MV_DOWN,  MV_UP,          MV_RIGHT },
5608     { MV_UP,    MV_DOWN,        MV_LEFT  },
5609     { 0,        0,              0        },
5610     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
5611     { 0,        0,              0        },
5612     { 0,        0,              0        },
5613     { 0,        0,              0        },
5614     { MV_RIGHT, MV_LEFT,        MV_UP    }
5615   };
5616
5617   int element = Feld[x][y];
5618   int move_pattern = element_info[element].move_pattern;
5619
5620   int old_move_dir = MovDir[x][y];
5621   int left_dir  = turn[old_move_dir].left;
5622   int right_dir = turn[old_move_dir].right;
5623   int back_dir  = turn[old_move_dir].back;
5624
5625   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
5626   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
5627   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
5628   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
5629
5630   int left_x  = x + left_dx,  left_y  = y + left_dy;
5631   int right_x = x + right_dx, right_y = y + right_dy;
5632   int move_x  = x + move_dx,  move_y  = y + move_dy;
5633
5634   int xx, yy;
5635
5636   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5637   {
5638     TestIfBadThingTouchesOtherBadThing(x, y);
5639
5640     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5641       MovDir[x][y] = right_dir;
5642     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5643       MovDir[x][y] = left_dir;
5644
5645     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5646       MovDelay[x][y] = 9;
5647     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
5648       MovDelay[x][y] = 1;
5649   }
5650   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5651   {
5652     TestIfBadThingTouchesOtherBadThing(x, y);
5653
5654     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5655       MovDir[x][y] = left_dir;
5656     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5657       MovDir[x][y] = right_dir;
5658
5659     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5660       MovDelay[x][y] = 9;
5661     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
5662       MovDelay[x][y] = 1;
5663   }
5664   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5665   {
5666     TestIfBadThingTouchesOtherBadThing(x, y);
5667
5668     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5669       MovDir[x][y] = left_dir;
5670     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5671       MovDir[x][y] = right_dir;
5672
5673     if (MovDir[x][y] != old_move_dir)
5674       MovDelay[x][y] = 9;
5675   }
5676   else if (element == EL_YAMYAM)
5677   {
5678     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5679     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5680
5681     if (can_turn_left && can_turn_right)
5682       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5683     else if (can_turn_left)
5684       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5685     else if (can_turn_right)
5686       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5687     else
5688       MovDir[x][y] = back_dir;
5689
5690     MovDelay[x][y] = 16 + 16 * RND(3);
5691   }
5692   else if (element == EL_DARK_YAMYAM)
5693   {
5694     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5695                                                          left_x, left_y);
5696     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5697                                                          right_x, right_y);
5698
5699     if (can_turn_left && can_turn_right)
5700       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5701     else if (can_turn_left)
5702       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5703     else if (can_turn_right)
5704       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5705     else
5706       MovDir[x][y] = back_dir;
5707
5708     MovDelay[x][y] = 16 + 16 * RND(3);
5709   }
5710   else if (element == EL_PACMAN)
5711   {
5712     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5713     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5714
5715     if (can_turn_left && can_turn_right)
5716       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5717     else if (can_turn_left)
5718       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5719     else if (can_turn_right)
5720       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5721     else
5722       MovDir[x][y] = back_dir;
5723
5724     MovDelay[x][y] = 6 + RND(40);
5725   }
5726   else if (element == EL_PIG)
5727   {
5728     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5729     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5730     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5731     boolean should_turn_left, should_turn_right, should_move_on;
5732     int rnd_value = 24;
5733     int rnd = RND(rnd_value);
5734
5735     should_turn_left = (can_turn_left &&
5736                         (!can_move_on ||
5737                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5738                                                    y + back_dy + left_dy)));
5739     should_turn_right = (can_turn_right &&
5740                          (!can_move_on ||
5741                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5742                                                     y + back_dy + right_dy)));
5743     should_move_on = (can_move_on &&
5744                       (!can_turn_left ||
5745                        !can_turn_right ||
5746                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5747                                                  y + move_dy + left_dy) ||
5748                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5749                                                  y + move_dy + right_dy)));
5750
5751     if (should_turn_left || should_turn_right || should_move_on)
5752     {
5753       if (should_turn_left && should_turn_right && should_move_on)
5754         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
5755                         rnd < 2 * rnd_value / 3 ? right_dir :
5756                         old_move_dir);
5757       else if (should_turn_left && should_turn_right)
5758         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5759       else if (should_turn_left && should_move_on)
5760         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5761       else if (should_turn_right && should_move_on)
5762         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5763       else if (should_turn_left)
5764         MovDir[x][y] = left_dir;
5765       else if (should_turn_right)
5766         MovDir[x][y] = right_dir;
5767       else if (should_move_on)
5768         MovDir[x][y] = old_move_dir;
5769     }
5770     else if (can_move_on && rnd > rnd_value / 8)
5771       MovDir[x][y] = old_move_dir;
5772     else if (can_turn_left && can_turn_right)
5773       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5774     else if (can_turn_left && rnd > rnd_value / 8)
5775       MovDir[x][y] = left_dir;
5776     else if (can_turn_right && rnd > rnd_value/8)
5777       MovDir[x][y] = right_dir;
5778     else
5779       MovDir[x][y] = back_dir;
5780
5781     xx = x + move_xy[MovDir[x][y]].dx;
5782     yy = y + move_xy[MovDir[x][y]].dy;
5783
5784     if (!IN_LEV_FIELD(xx, yy) ||
5785         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5786       MovDir[x][y] = old_move_dir;
5787
5788     MovDelay[x][y] = 0;
5789   }
5790   else if (element == EL_DRAGON)
5791   {
5792     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5793     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5794     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5795     int rnd_value = 24;
5796     int rnd = RND(rnd_value);
5797
5798     if (can_move_on && rnd > rnd_value / 8)
5799       MovDir[x][y] = old_move_dir;
5800     else if (can_turn_left && can_turn_right)
5801       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5802     else if (can_turn_left && rnd > rnd_value / 8)
5803       MovDir[x][y] = left_dir;
5804     else if (can_turn_right && rnd > rnd_value / 8)
5805       MovDir[x][y] = right_dir;
5806     else
5807       MovDir[x][y] = back_dir;
5808
5809     xx = x + move_xy[MovDir[x][y]].dx;
5810     yy = y + move_xy[MovDir[x][y]].dy;
5811
5812     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5813       MovDir[x][y] = old_move_dir;
5814
5815     MovDelay[x][y] = 0;
5816   }
5817   else if (element == EL_MOLE)
5818   {
5819     boolean can_move_on =
5820       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5821                             IS_AMOEBOID(Feld[move_x][move_y]) ||
5822                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5823     if (!can_move_on)
5824     {
5825       boolean can_turn_left =
5826         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5827                               IS_AMOEBOID(Feld[left_x][left_y])));
5828
5829       boolean can_turn_right =
5830         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5831                               IS_AMOEBOID(Feld[right_x][right_y])));
5832
5833       if (can_turn_left && can_turn_right)
5834         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5835       else if (can_turn_left)
5836         MovDir[x][y] = left_dir;
5837       else
5838         MovDir[x][y] = right_dir;
5839     }
5840
5841     if (MovDir[x][y] != old_move_dir)
5842       MovDelay[x][y] = 9;
5843   }
5844   else if (element == EL_BALLOON)
5845   {
5846     MovDir[x][y] = game.wind_direction;
5847     MovDelay[x][y] = 0;
5848   }
5849   else if (element == EL_SPRING)
5850   {
5851 #if USE_NEW_SPRING_BUMPER
5852     if (MovDir[x][y] & MV_HORIZONTAL)
5853     {
5854       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5855           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5856       {
5857         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5858         ResetGfxAnimation(move_x, move_y);
5859         DrawLevelField(move_x, move_y);
5860
5861         MovDir[x][y] = back_dir;
5862       }
5863       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5864                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5865         MovDir[x][y] = MV_NONE;
5866     }
5867 #else
5868     if (MovDir[x][y] & MV_HORIZONTAL &&
5869         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5870          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5871       MovDir[x][y] = MV_NONE;
5872 #endif
5873
5874     MovDelay[x][y] = 0;
5875   }
5876   else if (element == EL_ROBOT ||
5877            element == EL_SATELLITE ||
5878            element == EL_PENGUIN ||
5879            element == EL_EMC_ANDROID)
5880   {
5881     int attr_x = -1, attr_y = -1;
5882
5883     if (AllPlayersGone)
5884     {
5885       attr_x = ExitX;
5886       attr_y = ExitY;
5887     }
5888     else
5889     {
5890       int i;
5891
5892       for (i = 0; i < MAX_PLAYERS; i++)
5893       {
5894         struct PlayerInfo *player = &stored_player[i];
5895         int jx = player->jx, jy = player->jy;
5896
5897         if (!player->active)
5898           continue;
5899
5900         if (attr_x == -1 ||
5901             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5902         {
5903           attr_x = jx;
5904           attr_y = jy;
5905         }
5906       }
5907     }
5908
5909     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5910         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5911          game.engine_version < VERSION_IDENT(3,1,0,0)))
5912     {
5913       attr_x = ZX;
5914       attr_y = ZY;
5915     }
5916
5917     if (element == EL_PENGUIN)
5918     {
5919       int i;
5920       static int xy[4][2] =
5921       {
5922         { 0, -1 },
5923         { -1, 0 },
5924         { +1, 0 },
5925         { 0, +1 }
5926       };
5927
5928       for (i = 0; i < NUM_DIRECTIONS; i++)
5929       {
5930         int ex = x + xy[i][0];
5931         int ey = y + xy[i][1];
5932
5933         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
5934                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
5935                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
5936                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
5937         {
5938           attr_x = ex;
5939           attr_y = ey;
5940           break;
5941         }
5942       }
5943     }
5944
5945     MovDir[x][y] = MV_NONE;
5946     if (attr_x < x)
5947       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5948     else if (attr_x > x)
5949       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5950     if (attr_y < y)
5951       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5952     else if (attr_y > y)
5953       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5954
5955     if (element == EL_ROBOT)
5956     {
5957       int newx, newy;
5958
5959       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5960         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5961       Moving2Blocked(x, y, &newx, &newy);
5962
5963       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5964         MovDelay[x][y] = 8 + 8 * !RND(3);
5965       else
5966         MovDelay[x][y] = 16;
5967     }
5968     else if (element == EL_PENGUIN)
5969     {
5970       int newx, newy;
5971
5972       MovDelay[x][y] = 1;
5973
5974       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5975       {
5976         boolean first_horiz = RND(2);
5977         int new_move_dir = MovDir[x][y];
5978
5979         MovDir[x][y] =
5980           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5981         Moving2Blocked(x, y, &newx, &newy);
5982
5983         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5984           return;
5985
5986         MovDir[x][y] =
5987           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5988         Moving2Blocked(x, y, &newx, &newy);
5989
5990         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5991           return;
5992
5993         MovDir[x][y] = old_move_dir;
5994         return;
5995       }
5996     }
5997     else if (element == EL_SATELLITE)
5998     {
5999       int newx, newy;
6000
6001       MovDelay[x][y] = 1;
6002
6003       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6004       {
6005         boolean first_horiz = RND(2);
6006         int new_move_dir = MovDir[x][y];
6007
6008         MovDir[x][y] =
6009           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6010         Moving2Blocked(x, y, &newx, &newy);
6011
6012         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6013           return;
6014
6015         MovDir[x][y] =
6016           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6017         Moving2Blocked(x, y, &newx, &newy);
6018
6019         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6020           return;
6021
6022         MovDir[x][y] = old_move_dir;
6023         return;
6024       }
6025     }
6026     else if (element == EL_EMC_ANDROID)
6027     {
6028       static int check_pos[16] =
6029       {
6030         -1,             /*  0 => (invalid)          */
6031         7,              /*  1 => MV_LEFT            */
6032         3,              /*  2 => MV_RIGHT           */
6033         -1,             /*  3 => (invalid)          */
6034         1,              /*  4 =>            MV_UP   */
6035         0,              /*  5 => MV_LEFT  | MV_UP   */
6036         2,              /*  6 => MV_RIGHT | MV_UP   */
6037         -1,             /*  7 => (invalid)          */
6038         5,              /*  8 =>            MV_DOWN */
6039         6,              /*  9 => MV_LEFT  | MV_DOWN */
6040         4,              /* 10 => MV_RIGHT | MV_DOWN */
6041         -1,             /* 11 => (invalid)          */
6042         -1,             /* 12 => (invalid)          */
6043         -1,             /* 13 => (invalid)          */
6044         -1,             /* 14 => (invalid)          */
6045         -1,             /* 15 => (invalid)          */
6046       };
6047       static struct
6048       {
6049         int dx, dy;
6050         int dir;
6051       } check_xy[8] =
6052       {
6053         { -1, -1,       MV_LEFT  | MV_UP   },
6054         {  0, -1,                  MV_UP   },
6055         { +1, -1,       MV_RIGHT | MV_UP   },
6056         { +1,  0,       MV_RIGHT           },
6057         { +1, +1,       MV_RIGHT | MV_DOWN },
6058         {  0, +1,                  MV_DOWN },
6059         { -1, +1,       MV_LEFT  | MV_DOWN },
6060         { -1,  0,       MV_LEFT            },
6061       };
6062       int start_pos, check_order;
6063       boolean can_clone = FALSE;
6064       int i;
6065
6066       /* check if there is any free field around current position */
6067       for (i = 0; i < 8; i++)
6068       {
6069         int newx = x + check_xy[i].dx;
6070         int newy = y + check_xy[i].dy;
6071
6072         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6073         {
6074           can_clone = TRUE;
6075
6076           break;
6077         }
6078       }
6079
6080       if (can_clone)            /* randomly find an element to clone */
6081       {
6082         can_clone = FALSE;
6083
6084         start_pos = check_pos[RND(8)];
6085         check_order = (RND(2) ? -1 : +1);
6086
6087         for (i = 0; i < 8; i++)
6088         {
6089           int pos_raw = start_pos + i * check_order;
6090           int pos = (pos_raw + 8) % 8;
6091           int newx = x + check_xy[pos].dx;
6092           int newy = y + check_xy[pos].dy;
6093
6094           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6095           {
6096             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6097             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6098
6099             Store[x][y] = Feld[newx][newy];
6100
6101             can_clone = TRUE;
6102
6103             break;
6104           }
6105         }
6106       }
6107
6108       if (can_clone)            /* randomly find a direction to move */
6109       {
6110         can_clone = FALSE;
6111
6112         start_pos = check_pos[RND(8)];
6113         check_order = (RND(2) ? -1 : +1);
6114
6115         for (i = 0; i < 8; i++)
6116         {
6117           int pos_raw = start_pos + i * check_order;
6118           int pos = (pos_raw + 8) % 8;
6119           int newx = x + check_xy[pos].dx;
6120           int newy = y + check_xy[pos].dy;
6121           int new_move_dir = check_xy[pos].dir;
6122
6123           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6124           {
6125             MovDir[x][y] = new_move_dir;
6126             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6127
6128             can_clone = TRUE;
6129
6130             break;
6131           }
6132         }
6133       }
6134
6135       if (can_clone)            /* cloning and moving successful */
6136         return;
6137
6138       /* cannot clone -- try to move towards player */
6139
6140       start_pos = check_pos[MovDir[x][y] & 0x0f];
6141       check_order = (RND(2) ? -1 : +1);
6142
6143       for (i = 0; i < 3; i++)
6144       {
6145         /* first check start_pos, then previous/next or (next/previous) pos */
6146         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6147         int pos = (pos_raw + 8) % 8;
6148         int newx = x + check_xy[pos].dx;
6149         int newy = y + check_xy[pos].dy;
6150         int new_move_dir = check_xy[pos].dir;
6151
6152         if (IS_PLAYER(newx, newy))
6153           break;
6154
6155         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6156         {
6157           MovDir[x][y] = new_move_dir;
6158           MovDelay[x][y] = level.android_move_time * 8 + 1;
6159
6160           break;
6161         }
6162       }
6163     }
6164   }
6165   else if (move_pattern == MV_TURNING_LEFT ||
6166            move_pattern == MV_TURNING_RIGHT ||
6167            move_pattern == MV_TURNING_LEFT_RIGHT ||
6168            move_pattern == MV_TURNING_RIGHT_LEFT ||
6169            move_pattern == MV_TURNING_RANDOM ||
6170            move_pattern == MV_ALL_DIRECTIONS)
6171   {
6172     boolean can_turn_left =
6173       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6174     boolean can_turn_right =
6175       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6176
6177     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6178       return;
6179
6180     if (move_pattern == MV_TURNING_LEFT)
6181       MovDir[x][y] = left_dir;
6182     else if (move_pattern == MV_TURNING_RIGHT)
6183       MovDir[x][y] = right_dir;
6184     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6185       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6186     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6187       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6188     else if (move_pattern == MV_TURNING_RANDOM)
6189       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6190                       can_turn_right && !can_turn_left ? right_dir :
6191                       RND(2) ? left_dir : right_dir);
6192     else if (can_turn_left && can_turn_right)
6193       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6194     else if (can_turn_left)
6195       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6196     else if (can_turn_right)
6197       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6198     else
6199       MovDir[x][y] = back_dir;
6200
6201     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6202   }
6203   else if (move_pattern == MV_HORIZONTAL ||
6204            move_pattern == MV_VERTICAL)
6205   {
6206     if (move_pattern & old_move_dir)
6207       MovDir[x][y] = back_dir;
6208     else if (move_pattern == MV_HORIZONTAL)
6209       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6210     else if (move_pattern == MV_VERTICAL)
6211       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6212
6213     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6214   }
6215   else if (move_pattern & MV_ANY_DIRECTION)
6216   {
6217     MovDir[x][y] = move_pattern;
6218     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6219   }
6220   else if (move_pattern & MV_WIND_DIRECTION)
6221   {
6222     MovDir[x][y] = game.wind_direction;
6223     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6224   }
6225   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6226   {
6227     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6228       MovDir[x][y] = left_dir;
6229     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6230       MovDir[x][y] = right_dir;
6231
6232     if (MovDir[x][y] != old_move_dir)
6233       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6234   }
6235   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6236   {
6237     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6238       MovDir[x][y] = right_dir;
6239     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6240       MovDir[x][y] = left_dir;
6241
6242     if (MovDir[x][y] != old_move_dir)
6243       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6244   }
6245   else if (move_pattern == MV_TOWARDS_PLAYER ||
6246            move_pattern == MV_AWAY_FROM_PLAYER)
6247   {
6248     int attr_x = -1, attr_y = -1;
6249     int newx, newy;
6250     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6251
6252     if (AllPlayersGone)
6253     {
6254       attr_x = ExitX;
6255       attr_y = ExitY;
6256     }
6257     else
6258     {
6259       int i;
6260
6261       for (i = 0; i < MAX_PLAYERS; i++)
6262       {
6263         struct PlayerInfo *player = &stored_player[i];
6264         int jx = player->jx, jy = player->jy;
6265
6266         if (!player->active)
6267           continue;
6268
6269         if (attr_x == -1 ||
6270             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6271         {
6272           attr_x = jx;
6273           attr_y = jy;
6274         }
6275       }
6276     }
6277
6278     MovDir[x][y] = MV_NONE;
6279     if (attr_x < x)
6280       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6281     else if (attr_x > x)
6282       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6283     if (attr_y < y)
6284       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6285     else if (attr_y > y)
6286       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6287
6288     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6289
6290     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6291     {
6292       boolean first_horiz = RND(2);
6293       int new_move_dir = MovDir[x][y];
6294
6295       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6296       {
6297         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6298         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6299
6300         return;
6301       }
6302
6303       MovDir[x][y] =
6304         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6305       Moving2Blocked(x, y, &newx, &newy);
6306
6307       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6308         return;
6309
6310       MovDir[x][y] =
6311         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6312       Moving2Blocked(x, y, &newx, &newy);
6313
6314       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6315         return;
6316
6317       MovDir[x][y] = old_move_dir;
6318     }
6319   }
6320   else if (move_pattern == MV_WHEN_PUSHED ||
6321            move_pattern == MV_WHEN_DROPPED)
6322   {
6323     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6324       MovDir[x][y] = MV_NONE;
6325
6326     MovDelay[x][y] = 0;
6327   }
6328   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6329   {
6330     static int test_xy[7][2] =
6331     {
6332       { 0, -1 },
6333       { -1, 0 },
6334       { +1, 0 },
6335       { 0, +1 },
6336       { 0, -1 },
6337       { -1, 0 },
6338       { +1, 0 },
6339     };
6340     static int test_dir[7] =
6341     {
6342       MV_UP,
6343       MV_LEFT,
6344       MV_RIGHT,
6345       MV_DOWN,
6346       MV_UP,
6347       MV_LEFT,
6348       MV_RIGHT,
6349     };
6350     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6351     int move_preference = -1000000;     /* start with very low preference */
6352     int new_move_dir = MV_NONE;
6353     int start_test = RND(4);
6354     int i;
6355
6356     for (i = 0; i < NUM_DIRECTIONS; i++)
6357     {
6358       int move_dir = test_dir[start_test + i];
6359       int move_dir_preference;
6360
6361       xx = x + test_xy[start_test + i][0];
6362       yy = y + test_xy[start_test + i][1];
6363
6364       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6365           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6366       {
6367         new_move_dir = move_dir;
6368
6369         break;
6370       }
6371
6372       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6373         continue;
6374
6375       move_dir_preference = -1 * RunnerVisit[xx][yy];
6376       if (hunter_mode && PlayerVisit[xx][yy] > 0)
6377         move_dir_preference = PlayerVisit[xx][yy];
6378
6379       if (move_dir_preference > move_preference)
6380       {
6381         /* prefer field that has not been visited for the longest time */
6382         move_preference = move_dir_preference;
6383         new_move_dir = move_dir;
6384       }
6385       else if (move_dir_preference == move_preference &&
6386                move_dir == old_move_dir)
6387       {
6388         /* prefer last direction when all directions are preferred equally */
6389         move_preference = move_dir_preference;
6390         new_move_dir = move_dir;
6391       }
6392     }
6393
6394     MovDir[x][y] = new_move_dir;
6395     if (old_move_dir != new_move_dir)
6396       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6397   }
6398 }
6399
6400 static void TurnRound(int x, int y)
6401 {
6402   int direction = MovDir[x][y];
6403
6404   TurnRoundExt(x, y);
6405
6406   GfxDir[x][y] = MovDir[x][y];
6407
6408   if (direction != MovDir[x][y])
6409     GfxFrame[x][y] = 0;
6410
6411   if (MovDelay[x][y])
6412     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6413
6414   ResetGfxFrame(x, y, FALSE);
6415 }
6416
6417 static boolean JustBeingPushed(int x, int y)
6418 {
6419   int i;
6420
6421   for (i = 0; i < MAX_PLAYERS; i++)
6422   {
6423     struct PlayerInfo *player = &stored_player[i];
6424
6425     if (player->active && player->is_pushing && player->MovPos)
6426     {
6427       int next_jx = player->jx + (player->jx - player->last_jx);
6428       int next_jy = player->jy + (player->jy - player->last_jy);
6429
6430       if (x == next_jx && y == next_jy)
6431         return TRUE;
6432     }
6433   }
6434
6435   return FALSE;
6436 }
6437
6438 void StartMoving(int x, int y)
6439 {
6440   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
6441   int element = Feld[x][y];
6442
6443   if (Stop[x][y])
6444     return;
6445
6446   if (MovDelay[x][y] == 0)
6447     GfxAction[x][y] = ACTION_DEFAULT;
6448
6449   if (CAN_FALL(element) && y < lev_fieldy - 1)
6450   {
6451     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
6452         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6453       if (JustBeingPushed(x, y))
6454         return;
6455
6456     if (element == EL_QUICKSAND_FULL)
6457     {
6458       if (IS_FREE(x, y + 1))
6459       {
6460         InitMovingField(x, y, MV_DOWN);
6461         started_moving = TRUE;
6462
6463         Feld[x][y] = EL_QUICKSAND_EMPTYING;
6464 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6465         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6466           Store[x][y] = EL_ROCK;
6467 #else
6468         Store[x][y] = EL_ROCK;
6469 #endif
6470
6471         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6472       }
6473       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6474       {
6475         if (!MovDelay[x][y])
6476           MovDelay[x][y] = TILEY + 1;
6477
6478         if (MovDelay[x][y])
6479         {
6480           MovDelay[x][y]--;
6481           if (MovDelay[x][y])
6482             return;
6483         }
6484
6485         Feld[x][y] = EL_QUICKSAND_EMPTY;
6486         Feld[x][y + 1] = EL_QUICKSAND_FULL;
6487         Store[x][y + 1] = Store[x][y];
6488         Store[x][y] = 0;
6489
6490         PlayLevelSoundAction(x, y, ACTION_FILLING);
6491       }
6492     }
6493     else if (element == EL_QUICKSAND_FAST_FULL)
6494     {
6495       if (IS_FREE(x, y + 1))
6496       {
6497         InitMovingField(x, y, MV_DOWN);
6498         started_moving = TRUE;
6499
6500         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
6501 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6502         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6503           Store[x][y] = EL_ROCK;
6504 #else
6505         Store[x][y] = EL_ROCK;
6506 #endif
6507
6508         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6509       }
6510       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6511       {
6512         if (!MovDelay[x][y])
6513           MovDelay[x][y] = TILEY + 1;
6514
6515         if (MovDelay[x][y])
6516         {
6517           MovDelay[x][y]--;
6518           if (MovDelay[x][y])
6519             return;
6520         }
6521
6522         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
6523         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
6524         Store[x][y + 1] = Store[x][y];
6525         Store[x][y] = 0;
6526
6527         PlayLevelSoundAction(x, y, ACTION_FILLING);
6528       }
6529     }
6530     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6531              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6532     {
6533       InitMovingField(x, y, MV_DOWN);
6534       started_moving = TRUE;
6535
6536       Feld[x][y] = EL_QUICKSAND_FILLING;
6537       Store[x][y] = element;
6538
6539       PlayLevelSoundAction(x, y, ACTION_FILLING);
6540     }
6541     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6542              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6543     {
6544       InitMovingField(x, y, MV_DOWN);
6545       started_moving = TRUE;
6546
6547       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
6548       Store[x][y] = element;
6549
6550       PlayLevelSoundAction(x, y, ACTION_FILLING);
6551     }
6552     else if (element == EL_MAGIC_WALL_FULL)
6553     {
6554       if (IS_FREE(x, y + 1))
6555       {
6556         InitMovingField(x, y, MV_DOWN);
6557         started_moving = TRUE;
6558
6559         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6560         Store[x][y] = EL_CHANGED(Store[x][y]);
6561       }
6562       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6563       {
6564         if (!MovDelay[x][y])
6565           MovDelay[x][y] = TILEY/4 + 1;
6566
6567         if (MovDelay[x][y])
6568         {
6569           MovDelay[x][y]--;
6570           if (MovDelay[x][y])
6571             return;
6572         }
6573
6574         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6575         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6576         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6577         Store[x][y] = 0;
6578       }
6579     }
6580     else if (element == EL_BD_MAGIC_WALL_FULL)
6581     {
6582       if (IS_FREE(x, y + 1))
6583       {
6584         InitMovingField(x, y, MV_DOWN);
6585         started_moving = TRUE;
6586
6587         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6588         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6589       }
6590       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6591       {
6592         if (!MovDelay[x][y])
6593           MovDelay[x][y] = TILEY/4 + 1;
6594
6595         if (MovDelay[x][y])
6596         {
6597           MovDelay[x][y]--;
6598           if (MovDelay[x][y])
6599             return;
6600         }
6601
6602         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6603         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6604         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6605         Store[x][y] = 0;
6606       }
6607     }
6608     else if (element == EL_DC_MAGIC_WALL_FULL)
6609     {
6610       if (IS_FREE(x, y + 1))
6611       {
6612         InitMovingField(x, y, MV_DOWN);
6613         started_moving = TRUE;
6614
6615         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
6616         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
6617       }
6618       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6619       {
6620         if (!MovDelay[x][y])
6621           MovDelay[x][y] = TILEY/4 + 1;
6622
6623         if (MovDelay[x][y])
6624         {
6625           MovDelay[x][y]--;
6626           if (MovDelay[x][y])
6627             return;
6628         }
6629
6630         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
6631         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
6632         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
6633         Store[x][y] = 0;
6634       }
6635     }
6636     else if ((CAN_PASS_MAGIC_WALL(element) &&
6637               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6638                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
6639              (CAN_PASS_DC_MAGIC_WALL(element) &&
6640               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
6641
6642     {
6643       InitMovingField(x, y, MV_DOWN);
6644       started_moving = TRUE;
6645
6646       Feld[x][y] =
6647         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6648          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
6649          EL_DC_MAGIC_WALL_FILLING);
6650       Store[x][y] = element;
6651     }
6652     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6653     {
6654       SplashAcid(x, y + 1);
6655
6656       InitMovingField(x, y, MV_DOWN);
6657       started_moving = TRUE;
6658
6659       Store[x][y] = EL_ACID;
6660     }
6661     else if (
6662 #if USE_FIX_IMPACT_COLLISION
6663              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6664               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
6665 #else
6666              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6667               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
6668 #endif
6669              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
6670               CAN_FALL(element) && WasJustFalling[x][y] &&
6671               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
6672
6673              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
6674               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
6675               (Feld[x][y + 1] == EL_BLOCKED)))
6676     {
6677       /* this is needed for a special case not covered by calling "Impact()"
6678          from "ContinueMoving()": if an element moves to a tile directly below
6679          another element which was just falling on that tile (which was empty
6680          in the previous frame), the falling element above would just stop
6681          instead of smashing the element below (in previous version, the above
6682          element was just checked for "moving" instead of "falling", resulting
6683          in incorrect smashes caused by horizontal movement of the above
6684          element; also, the case of the player being the element to smash was
6685          simply not covered here... :-/ ) */
6686
6687       CheckCollision[x][y] = 0;
6688       CheckImpact[x][y] = 0;
6689
6690       Impact(x, y);
6691     }
6692     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6693     {
6694       if (MovDir[x][y] == MV_NONE)
6695       {
6696         InitMovingField(x, y, MV_DOWN);
6697         started_moving = TRUE;
6698       }
6699     }
6700     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6701     {
6702       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6703         MovDir[x][y] = MV_DOWN;
6704
6705       InitMovingField(x, y, MV_DOWN);
6706       started_moving = TRUE;
6707     }
6708     else if (element == EL_AMOEBA_DROP)
6709     {
6710       Feld[x][y] = EL_AMOEBA_GROWING;
6711       Store[x][y] = EL_AMOEBA_WET;
6712     }
6713     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6714               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6715              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6716              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6717     {
6718       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
6719                                 (IS_FREE(x - 1, y + 1) ||
6720                                  Feld[x - 1][y + 1] == EL_ACID));
6721       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6722                                 (IS_FREE(x + 1, y + 1) ||
6723                                  Feld[x + 1][y + 1] == EL_ACID));
6724       boolean can_fall_any  = (can_fall_left || can_fall_right);
6725       boolean can_fall_both = (can_fall_left && can_fall_right);
6726       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6727
6728 #if USE_NEW_ALL_SLIPPERY
6729       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6730       {
6731         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6732           can_fall_right = FALSE;
6733         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6734           can_fall_left = FALSE;
6735         else if (slippery_type == SLIPPERY_ONLY_LEFT)
6736           can_fall_right = FALSE;
6737         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6738           can_fall_left = FALSE;
6739
6740         can_fall_any  = (can_fall_left || can_fall_right);
6741         can_fall_both = FALSE;
6742       }
6743 #else
6744       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6745       {
6746         if (slippery_type == SLIPPERY_ONLY_LEFT)
6747           can_fall_right = FALSE;
6748         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6749           can_fall_left = FALSE;
6750         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6751           can_fall_right = FALSE;
6752         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6753           can_fall_left = FALSE;
6754
6755         can_fall_any  = (can_fall_left || can_fall_right);
6756         can_fall_both = (can_fall_left && can_fall_right);
6757       }
6758 #endif
6759
6760 #if USE_NEW_ALL_SLIPPERY
6761 #else
6762 #if USE_NEW_SP_SLIPPERY
6763       /* !!! better use the same properties as for custom elements here !!! */
6764       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6765                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6766       {
6767         can_fall_right = FALSE;         /* slip down on left side */
6768         can_fall_both = FALSE;
6769       }
6770 #endif
6771 #endif
6772
6773 #if USE_NEW_ALL_SLIPPERY
6774       if (can_fall_both)
6775       {
6776         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6777           can_fall_right = FALSE;       /* slip down on left side */
6778         else
6779           can_fall_left = !(can_fall_right = RND(2));
6780
6781         can_fall_both = FALSE;
6782       }
6783 #else
6784       if (can_fall_both)
6785       {
6786         if (game.emulation == EMU_BOULDERDASH ||
6787             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6788           can_fall_right = FALSE;       /* slip down on left side */
6789         else
6790           can_fall_left = !(can_fall_right = RND(2));
6791
6792         can_fall_both = FALSE;
6793       }
6794 #endif
6795
6796       if (can_fall_any)
6797       {
6798         /* if not determined otherwise, prefer left side for slipping down */
6799         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6800         started_moving = TRUE;
6801       }
6802     }
6803 #if 0
6804     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6805 #else
6806     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6807 #endif
6808     {
6809       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
6810       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6811       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6812       int belt_dir = game.belt_dir[belt_nr];
6813
6814       if ((belt_dir == MV_LEFT  && left_is_free) ||
6815           (belt_dir == MV_RIGHT && right_is_free))
6816       {
6817         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6818
6819         InitMovingField(x, y, belt_dir);
6820         started_moving = TRUE;
6821
6822         Pushed[x][y] = TRUE;
6823         Pushed[nextx][y] = TRUE;
6824
6825         GfxAction[x][y] = ACTION_DEFAULT;
6826       }
6827       else
6828       {
6829         MovDir[x][y] = 0;       /* if element was moving, stop it */
6830       }
6831     }
6832   }
6833
6834   /* not "else if" because of elements that can fall and move (EL_SPRING) */
6835 #if 0
6836   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6837 #else
6838   if (CAN_MOVE(element) && !started_moving)
6839 #endif
6840   {
6841     int move_pattern = element_info[element].move_pattern;
6842     int newx, newy;
6843
6844 #if 0
6845 #if DEBUG
6846     if (MovDir[x][y] == MV_NONE)
6847     {
6848       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6849              x, y, element, element_info[element].token_name);
6850       printf("StartMoving(): This should never happen!\n");
6851     }
6852 #endif
6853 #endif
6854
6855     Moving2Blocked(x, y, &newx, &newy);
6856
6857     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6858       return;
6859
6860     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6861         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6862     {
6863       WasJustMoving[x][y] = 0;
6864       CheckCollision[x][y] = 0;
6865
6866       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6867
6868       if (Feld[x][y] != element)        /* element has changed */
6869         return;
6870     }
6871
6872     if (!MovDelay[x][y])        /* start new movement phase */
6873     {
6874       /* all objects that can change their move direction after each step
6875          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6876
6877       if (element != EL_YAMYAM &&
6878           element != EL_DARK_YAMYAM &&
6879           element != EL_PACMAN &&
6880           !(move_pattern & MV_ANY_DIRECTION) &&
6881           move_pattern != MV_TURNING_LEFT &&
6882           move_pattern != MV_TURNING_RIGHT &&
6883           move_pattern != MV_TURNING_LEFT_RIGHT &&
6884           move_pattern != MV_TURNING_RIGHT_LEFT &&
6885           move_pattern != MV_TURNING_RANDOM)
6886       {
6887         TurnRound(x, y);
6888
6889         if (MovDelay[x][y] && (element == EL_BUG ||
6890                                element == EL_SPACESHIP ||
6891                                element == EL_SP_SNIKSNAK ||
6892                                element == EL_SP_ELECTRON ||
6893                                element == EL_MOLE))
6894           DrawLevelField(x, y);
6895       }
6896     }
6897
6898     if (MovDelay[x][y])         /* wait some time before next movement */
6899     {
6900       MovDelay[x][y]--;
6901
6902       if (element == EL_ROBOT ||
6903           element == EL_YAMYAM ||
6904           element == EL_DARK_YAMYAM)
6905       {
6906         DrawLevelElementAnimationIfNeeded(x, y, element);
6907         PlayLevelSoundAction(x, y, ACTION_WAITING);
6908       }
6909       else if (element == EL_SP_ELECTRON)
6910         DrawLevelElementAnimationIfNeeded(x, y, element);
6911       else if (element == EL_DRAGON)
6912       {
6913         int i;
6914         int dir = MovDir[x][y];
6915         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6916         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
6917         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
6918                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
6919                        dir == MV_UP     ? IMG_FLAMES_1_UP :
6920                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6921         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6922
6923         GfxAction[x][y] = ACTION_ATTACKING;
6924
6925         if (IS_PLAYER(x, y))
6926           DrawPlayerField(x, y);
6927         else
6928           DrawLevelField(x, y);
6929
6930         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6931
6932         for (i = 1; i <= 3; i++)
6933         {
6934           int xx = x + i * dx;
6935           int yy = y + i * dy;
6936           int sx = SCREENX(xx);
6937           int sy = SCREENY(yy);
6938           int flame_graphic = graphic + (i - 1);
6939
6940           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6941             break;
6942
6943           if (MovDelay[x][y])
6944           {
6945             int flamed = MovingOrBlocked2Element(xx, yy);
6946
6947             /* !!! */
6948 #if 0
6949             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6950               Bang(xx, yy);
6951             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6952               RemoveMovingField(xx, yy);
6953             else
6954               RemoveField(xx, yy);
6955 #else
6956             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6957               Bang(xx, yy);
6958             else
6959               RemoveMovingField(xx, yy);
6960 #endif
6961
6962             ChangeDelay[xx][yy] = 0;
6963
6964             Feld[xx][yy] = EL_FLAMES;
6965
6966             if (IN_SCR_FIELD(sx, sy))
6967             {
6968               DrawLevelFieldCrumbledSand(xx, yy);
6969               DrawGraphic(sx, sy, flame_graphic, frame);
6970             }
6971           }
6972           else
6973           {
6974             if (Feld[xx][yy] == EL_FLAMES)
6975               Feld[xx][yy] = EL_EMPTY;
6976             DrawLevelField(xx, yy);
6977           }
6978         }
6979       }
6980
6981       if (MovDelay[x][y])       /* element still has to wait some time */
6982       {
6983         PlayLevelSoundAction(x, y, ACTION_WAITING);
6984
6985         return;
6986       }
6987     }
6988
6989     /* now make next step */
6990
6991     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6992
6993     if (DONT_COLLIDE_WITH(element) &&
6994         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6995         !PLAYER_ENEMY_PROTECTED(newx, newy))
6996     {
6997       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6998
6999       return;
7000     }
7001
7002     else if (CAN_MOVE_INTO_ACID(element) &&
7003              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7004              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7005              (MovDir[x][y] == MV_DOWN ||
7006               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7007     {
7008       SplashAcid(newx, newy);
7009       Store[x][y] = EL_ACID;
7010     }
7011     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7012     {
7013       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7014           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7015           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7016           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7017       {
7018         RemoveField(x, y);
7019         DrawLevelField(x, y);
7020
7021         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7022         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7023           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7024
7025         local_player->friends_still_needed--;
7026         if (!local_player->friends_still_needed &&
7027             !local_player->GameOver && AllPlayersGone)
7028           PlayerWins(local_player);
7029
7030         return;
7031       }
7032       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7033       {
7034         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7035           DrawLevelField(newx, newy);
7036         else
7037           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7038       }
7039       else if (!IS_FREE(newx, newy))
7040       {
7041         GfxAction[x][y] = ACTION_WAITING;
7042
7043         if (IS_PLAYER(x, y))
7044           DrawPlayerField(x, y);
7045         else
7046           DrawLevelField(x, y);
7047
7048         return;
7049       }
7050     }
7051     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7052     {
7053       if (IS_FOOD_PIG(Feld[newx][newy]))
7054       {
7055         if (IS_MOVING(newx, newy))
7056           RemoveMovingField(newx, newy);
7057         else
7058         {
7059           Feld[newx][newy] = EL_EMPTY;
7060           DrawLevelField(newx, newy);
7061         }
7062
7063         PlayLevelSound(x, y, SND_PIG_DIGGING);
7064       }
7065       else if (!IS_FREE(newx, newy))
7066       {
7067         if (IS_PLAYER(x, y))
7068           DrawPlayerField(x, y);
7069         else
7070           DrawLevelField(x, y);
7071
7072         return;
7073       }
7074     }
7075     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7076     {
7077       if (Store[x][y] != EL_EMPTY)
7078       {
7079         boolean can_clone = FALSE;
7080         int xx, yy;
7081
7082         /* check if element to clone is still there */
7083         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7084         {
7085           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7086           {
7087             can_clone = TRUE;
7088
7089             break;
7090           }
7091         }
7092
7093         /* cannot clone or target field not free anymore -- do not clone */
7094         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7095           Store[x][y] = EL_EMPTY;
7096       }
7097
7098       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7099       {
7100         if (IS_MV_DIAGONAL(MovDir[x][y]))
7101         {
7102           int diagonal_move_dir = MovDir[x][y];
7103           int stored = Store[x][y];
7104           int change_delay = 8;
7105           int graphic;
7106
7107           /* android is moving diagonally */
7108
7109           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7110
7111           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7112           GfxElement[x][y] = EL_EMC_ANDROID;
7113           GfxAction[x][y] = ACTION_SHRINKING;
7114           GfxDir[x][y] = diagonal_move_dir;
7115           ChangeDelay[x][y] = change_delay;
7116
7117           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7118                                    GfxDir[x][y]);
7119
7120           DrawLevelGraphicAnimation(x, y, graphic);
7121           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7122
7123           if (Feld[newx][newy] == EL_ACID)
7124           {
7125             SplashAcid(newx, newy);
7126
7127             return;
7128           }
7129
7130           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7131
7132           Store[newx][newy] = EL_EMC_ANDROID;
7133           GfxElement[newx][newy] = EL_EMC_ANDROID;
7134           GfxAction[newx][newy] = ACTION_GROWING;
7135           GfxDir[newx][newy] = diagonal_move_dir;
7136           ChangeDelay[newx][newy] = change_delay;
7137
7138           graphic = el_act_dir2img(GfxElement[newx][newy],
7139                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7140
7141           DrawLevelGraphicAnimation(newx, newy, graphic);
7142           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7143
7144           return;
7145         }
7146         else
7147         {
7148           Feld[newx][newy] = EL_EMPTY;
7149           DrawLevelField(newx, newy);
7150
7151           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7152         }
7153       }
7154       else if (!IS_FREE(newx, newy))
7155       {
7156 #if 0
7157         if (IS_PLAYER(x, y))
7158           DrawPlayerField(x, y);
7159         else
7160           DrawLevelField(x, y);
7161 #endif
7162
7163         return;
7164       }
7165     }
7166     else if (IS_CUSTOM_ELEMENT(element) &&
7167              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7168     {
7169       int new_element = Feld[newx][newy];
7170
7171       if (!IS_FREE(newx, newy))
7172       {
7173         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7174                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7175                       ACTION_BREAKING);
7176
7177         /* no element can dig solid indestructible elements */
7178         if (IS_INDESTRUCTIBLE(new_element) &&
7179             !IS_DIGGABLE(new_element) &&
7180             !IS_COLLECTIBLE(new_element))
7181           return;
7182
7183         if (AmoebaNr[newx][newy] &&
7184             (new_element == EL_AMOEBA_FULL ||
7185              new_element == EL_BD_AMOEBA ||
7186              new_element == EL_AMOEBA_GROWING))
7187         {
7188           AmoebaCnt[AmoebaNr[newx][newy]]--;
7189           AmoebaCnt2[AmoebaNr[newx][newy]]--;
7190         }
7191
7192         if (IS_MOVING(newx, newy))
7193           RemoveMovingField(newx, newy);
7194         else
7195         {
7196           RemoveField(newx, newy);
7197           DrawLevelField(newx, newy);
7198         }
7199
7200         /* if digged element was about to explode, prevent the explosion */
7201         ExplodeField[newx][newy] = EX_TYPE_NONE;
7202
7203         PlayLevelSoundAction(x, y, action);
7204       }
7205
7206       Store[newx][newy] = EL_EMPTY;
7207 #if 1
7208       /* this makes it possible to leave the removed element again */
7209       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7210         Store[newx][newy] = new_element;
7211 #else
7212       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7213       {
7214         int move_leave_element = element_info[element].move_leave_element;
7215
7216         /* this makes it possible to leave the removed element again */
7217         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7218                              new_element : move_leave_element);
7219       }
7220 #endif
7221
7222       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7223       {
7224         RunnerVisit[x][y] = FrameCounter;
7225         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7226       }
7227     }
7228     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7229     {
7230       if (!IS_FREE(newx, newy))
7231       {
7232         if (IS_PLAYER(x, y))
7233           DrawPlayerField(x, y);
7234         else
7235           DrawLevelField(x, y);
7236
7237         return;
7238       }
7239       else
7240       {
7241         boolean wanna_flame = !RND(10);
7242         int dx = newx - x, dy = newy - y;
7243         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7244         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7245         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7246                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7247         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7248                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7249
7250         if ((wanna_flame ||
7251              IS_CLASSIC_ENEMY(element1) ||
7252              IS_CLASSIC_ENEMY(element2)) &&
7253             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7254             element1 != EL_FLAMES && element2 != EL_FLAMES)
7255         {
7256           ResetGfxAnimation(x, y);
7257           GfxAction[x][y] = ACTION_ATTACKING;
7258
7259           if (IS_PLAYER(x, y))
7260             DrawPlayerField(x, y);
7261           else
7262             DrawLevelField(x, y);
7263
7264           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7265
7266           MovDelay[x][y] = 50;
7267
7268           /* !!! */
7269 #if 0
7270           RemoveField(newx, newy);
7271 #endif
7272           Feld[newx][newy] = EL_FLAMES;
7273           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7274           {
7275 #if 0
7276             RemoveField(newx1, newy1);
7277 #endif
7278             Feld[newx1][newy1] = EL_FLAMES;
7279           }
7280           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7281           {
7282 #if 0
7283             RemoveField(newx2, newy2);
7284 #endif
7285             Feld[newx2][newy2] = EL_FLAMES;
7286           }
7287
7288           return;
7289         }
7290       }
7291     }
7292     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7293              Feld[newx][newy] == EL_DIAMOND)
7294     {
7295       if (IS_MOVING(newx, newy))
7296         RemoveMovingField(newx, newy);
7297       else
7298       {
7299         Feld[newx][newy] = EL_EMPTY;
7300         DrawLevelField(newx, newy);
7301       }
7302
7303       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7304     }
7305     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7306              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7307     {
7308       if (AmoebaNr[newx][newy])
7309       {
7310         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7311         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7312             Feld[newx][newy] == EL_BD_AMOEBA)
7313           AmoebaCnt[AmoebaNr[newx][newy]]--;
7314       }
7315
7316 #if 0
7317       /* !!! test !!! */
7318       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7319       {
7320         RemoveMovingField(newx, newy);
7321       }
7322 #else
7323       if (IS_MOVING(newx, newy))
7324       {
7325         RemoveMovingField(newx, newy);
7326       }
7327 #endif
7328       else
7329       {
7330         Feld[newx][newy] = EL_EMPTY;
7331         DrawLevelField(newx, newy);
7332       }
7333
7334       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7335     }
7336     else if ((element == EL_PACMAN || element == EL_MOLE)
7337              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7338     {
7339       if (AmoebaNr[newx][newy])
7340       {
7341         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7342         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7343             Feld[newx][newy] == EL_BD_AMOEBA)
7344           AmoebaCnt[AmoebaNr[newx][newy]]--;
7345       }
7346
7347       if (element == EL_MOLE)
7348       {
7349         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7350         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7351
7352         ResetGfxAnimation(x, y);
7353         GfxAction[x][y] = ACTION_DIGGING;
7354         DrawLevelField(x, y);
7355
7356         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7357
7358         return;                         /* wait for shrinking amoeba */
7359       }
7360       else      /* element == EL_PACMAN */
7361       {
7362         Feld[newx][newy] = EL_EMPTY;
7363         DrawLevelField(newx, newy);
7364         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7365       }
7366     }
7367     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7368              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7369               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7370     {
7371       /* wait for shrinking amoeba to completely disappear */
7372       return;
7373     }
7374     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7375     {
7376       /* object was running against a wall */
7377
7378       TurnRound(x, y);
7379
7380 #if 0
7381       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7382       if (move_pattern & MV_ANY_DIRECTION &&
7383           move_pattern == MovDir[x][y])
7384       {
7385         int blocking_element =
7386           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7387
7388         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7389                                  MovDir[x][y]);
7390
7391         element = Feld[x][y];   /* element might have changed */
7392       }
7393 #endif
7394
7395       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7396         DrawLevelElementAnimation(x, y, element);
7397
7398       if (DONT_TOUCH(element))
7399         TestIfBadThingTouchesPlayer(x, y);
7400
7401       return;
7402     }
7403
7404     InitMovingField(x, y, MovDir[x][y]);
7405
7406     PlayLevelSoundAction(x, y, ACTION_MOVING);
7407   }
7408
7409   if (MovDir[x][y])
7410     ContinueMoving(x, y);
7411 }
7412
7413 void ContinueMoving(int x, int y)
7414 {
7415   int element = Feld[x][y];
7416   struct ElementInfo *ei = &element_info[element];
7417   int direction = MovDir[x][y];
7418   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7419   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7420   int newx = x + dx, newy = y + dy;
7421   int stored = Store[x][y];
7422   int stored_new = Store[newx][newy];
7423   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7424   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7425   boolean last_line = (newy == lev_fieldy - 1);
7426
7427   MovPos[x][y] += getElementMoveStepsize(x, y);
7428
7429   if (pushed_by_player) /* special case: moving object pushed by player */
7430     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7431
7432   if (ABS(MovPos[x][y]) < TILEX)
7433   {
7434 #if 0
7435     int ee = Feld[x][y];
7436     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7437     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7438
7439     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7440            x, y, ABS(MovPos[x][y]),
7441            ee, gg, ff,
7442            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
7443 #endif
7444
7445     DrawLevelField(x, y);
7446
7447     return;     /* element is still moving */
7448   }
7449
7450   /* element reached destination field */
7451
7452   Feld[x][y] = EL_EMPTY;
7453   Feld[newx][newy] = element;
7454   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
7455
7456   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
7457   {
7458     element = Feld[newx][newy] = EL_ACID;
7459   }
7460   else if (element == EL_MOLE)
7461   {
7462     Feld[x][y] = EL_SAND;
7463
7464     DrawLevelFieldCrumbledSandNeighbours(x, y);
7465   }
7466   else if (element == EL_QUICKSAND_FILLING)
7467   {
7468     element = Feld[newx][newy] = get_next_element(element);
7469     Store[newx][newy] = Store[x][y];
7470   }
7471   else if (element == EL_QUICKSAND_EMPTYING)
7472   {
7473     Feld[x][y] = get_next_element(element);
7474     element = Feld[newx][newy] = Store[x][y];
7475   }
7476   else if (element == EL_QUICKSAND_FAST_FILLING)
7477   {
7478     element = Feld[newx][newy] = get_next_element(element);
7479     Store[newx][newy] = Store[x][y];
7480   }
7481   else if (element == EL_QUICKSAND_FAST_EMPTYING)
7482   {
7483     Feld[x][y] = get_next_element(element);
7484     element = Feld[newx][newy] = Store[x][y];
7485   }
7486   else if (element == EL_MAGIC_WALL_FILLING)
7487   {
7488     element = Feld[newx][newy] = get_next_element(element);
7489     if (!game.magic_wall_active)
7490       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7491     Store[newx][newy] = Store[x][y];
7492   }
7493   else if (element == EL_MAGIC_WALL_EMPTYING)
7494   {
7495     Feld[x][y] = get_next_element(element);
7496     if (!game.magic_wall_active)
7497       Feld[x][y] = EL_MAGIC_WALL_DEAD;
7498     element = Feld[newx][newy] = Store[x][y];
7499
7500 #if USE_NEW_CUSTOM_VALUE
7501     InitField(newx, newy, FALSE);
7502 #endif
7503   }
7504   else if (element == EL_BD_MAGIC_WALL_FILLING)
7505   {
7506     element = Feld[newx][newy] = get_next_element(element);
7507     if (!game.magic_wall_active)
7508       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
7509     Store[newx][newy] = Store[x][y];
7510   }
7511   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
7512   {
7513     Feld[x][y] = get_next_element(element);
7514     if (!game.magic_wall_active)
7515       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7516     element = Feld[newx][newy] = Store[x][y];
7517
7518 #if USE_NEW_CUSTOM_VALUE
7519     InitField(newx, newy, FALSE);
7520 #endif
7521   }
7522   else if (element == EL_DC_MAGIC_WALL_FILLING)
7523   {
7524     element = Feld[newx][newy] = get_next_element(element);
7525     if (!game.magic_wall_active)
7526       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
7527     Store[newx][newy] = Store[x][y];
7528   }
7529   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
7530   {
7531     Feld[x][y] = get_next_element(element);
7532     if (!game.magic_wall_active)
7533       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
7534     element = Feld[newx][newy] = Store[x][y];
7535
7536 #if USE_NEW_CUSTOM_VALUE
7537     InitField(newx, newy, FALSE);
7538 #endif
7539   }
7540   else if (element == EL_AMOEBA_DROPPING)
7541   {
7542     Feld[x][y] = get_next_element(element);
7543     element = Feld[newx][newy] = Store[x][y];
7544   }
7545   else if (element == EL_SOKOBAN_OBJECT)
7546   {
7547     if (Back[x][y])
7548       Feld[x][y] = Back[x][y];
7549
7550     if (Back[newx][newy])
7551       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
7552
7553     Back[x][y] = Back[newx][newy] = 0;
7554   }
7555
7556   Store[x][y] = EL_EMPTY;
7557   MovPos[x][y] = 0;
7558   MovDir[x][y] = 0;
7559   MovDelay[x][y] = 0;
7560
7561   MovDelay[newx][newy] = 0;
7562
7563   if (CAN_CHANGE_OR_HAS_ACTION(element))
7564   {
7565     /* copy element change control values to new field */
7566     ChangeDelay[newx][newy] = ChangeDelay[x][y];
7567     ChangePage[newx][newy]  = ChangePage[x][y];
7568     ChangeCount[newx][newy] = ChangeCount[x][y];
7569     ChangeEvent[newx][newy] = ChangeEvent[x][y];
7570   }
7571
7572 #if USE_NEW_CUSTOM_VALUE
7573     CustomValue[newx][newy] = CustomValue[x][y];
7574 #endif
7575
7576   ChangeDelay[x][y] = 0;
7577   ChangePage[x][y] = -1;
7578   ChangeCount[x][y] = 0;
7579   ChangeEvent[x][y] = -1;
7580
7581 #if USE_NEW_CUSTOM_VALUE
7582   CustomValue[x][y] = 0;
7583 #endif
7584
7585   /* copy animation control values to new field */
7586   GfxFrame[newx][newy]  = GfxFrame[x][y];
7587   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
7588   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
7589   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
7590
7591   Pushed[x][y] = Pushed[newx][newy] = FALSE;
7592
7593   /* some elements can leave other elements behind after moving */
7594 #if 1
7595   if (ei->move_leave_element != EL_EMPTY &&
7596       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7597       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7598 #else
7599   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7600       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7601       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7602 #endif
7603   {
7604     int move_leave_element = ei->move_leave_element;
7605
7606 #if 1
7607 #if 1
7608     /* this makes it possible to leave the removed element again */
7609     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7610       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7611 #else
7612     /* this makes it possible to leave the removed element again */
7613     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7614       move_leave_element = stored;
7615 #endif
7616 #else
7617     /* this makes it possible to leave the removed element again */
7618     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7619         ei->move_leave_element == EL_TRIGGER_ELEMENT)
7620       move_leave_element = stored;
7621 #endif
7622
7623     Feld[x][y] = move_leave_element;
7624
7625     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7626       MovDir[x][y] = direction;
7627
7628     InitField(x, y, FALSE);
7629
7630     if (GFX_CRUMBLED(Feld[x][y]))
7631       DrawLevelFieldCrumbledSandNeighbours(x, y);
7632
7633     if (ELEM_IS_PLAYER(move_leave_element))
7634       RelocatePlayer(x, y, move_leave_element);
7635   }
7636
7637   /* do this after checking for left-behind element */
7638   ResetGfxAnimation(x, y);      /* reset animation values for old field */
7639
7640   if (!CAN_MOVE(element) ||
7641       (CAN_FALL(element) && direction == MV_DOWN &&
7642        (element == EL_SPRING ||
7643         element_info[element].move_pattern == MV_WHEN_PUSHED ||
7644         element_info[element].move_pattern == MV_WHEN_DROPPED)))
7645     GfxDir[x][y] = MovDir[newx][newy] = 0;
7646
7647   DrawLevelField(x, y);
7648   DrawLevelField(newx, newy);
7649
7650   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
7651
7652   /* prevent pushed element from moving on in pushed direction */
7653   if (pushed_by_player && CAN_MOVE(element) &&
7654       element_info[element].move_pattern & MV_ANY_DIRECTION &&
7655       !(element_info[element].move_pattern & direction))
7656     TurnRound(newx, newy);
7657
7658   /* prevent elements on conveyor belt from moving on in last direction */
7659   if (pushed_by_conveyor && CAN_FALL(element) &&
7660       direction & MV_HORIZONTAL)
7661     MovDir[newx][newy] = 0;
7662
7663   if (!pushed_by_player)
7664   {
7665     int nextx = newx + dx, nexty = newy + dy;
7666     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
7667
7668     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
7669
7670     if (CAN_FALL(element) && direction == MV_DOWN)
7671       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
7672
7673     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
7674       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
7675
7676 #if USE_FIX_IMPACT_COLLISION
7677     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
7678       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
7679 #endif
7680   }
7681
7682   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
7683   {
7684     TestIfBadThingTouchesPlayer(newx, newy);
7685     TestIfBadThingTouchesFriend(newx, newy);
7686
7687     if (!IS_CUSTOM_ELEMENT(element))
7688       TestIfBadThingTouchesOtherBadThing(newx, newy);
7689   }
7690   else if (element == EL_PENGUIN)
7691     TestIfFriendTouchesBadThing(newx, newy);
7692
7693   /* give the player one last chance (one more frame) to move away */
7694   if (CAN_FALL(element) && direction == MV_DOWN &&
7695       (last_line || (!IS_FREE(x, newy + 1) &&
7696                      (!IS_PLAYER(x, newy + 1) ||
7697                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
7698     Impact(x, newy);
7699
7700   if (pushed_by_player && !game.use_change_when_pushing_bug)
7701   {
7702     int push_side = MV_DIR_OPPOSITE(direction);
7703     struct PlayerInfo *player = PLAYERINFO(x, y);
7704
7705     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
7706                                player->index_bit, push_side);
7707     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
7708                                         player->index_bit, push_side);
7709   }
7710
7711   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
7712     MovDelay[newx][newy] = 1;
7713
7714   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
7715
7716   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
7717
7718 #if 0
7719   if (ChangePage[newx][newy] != -1)             /* delayed change */
7720   {
7721     int page = ChangePage[newx][newy];
7722     struct ElementChangeInfo *change = &ei->change_page[page];
7723
7724     ChangePage[newx][newy] = -1;
7725
7726     if (change->can_change)
7727     {
7728       if (ChangeElement(newx, newy, element, page))
7729       {
7730         if (change->post_change_function)
7731           change->post_change_function(newx, newy);
7732       }
7733     }
7734
7735     if (change->has_action)
7736       ExecuteCustomElementAction(newx, newy, element, page);
7737   }
7738 #endif
7739
7740   TestIfElementHitsCustomElement(newx, newy, direction);
7741   TestIfPlayerTouchesCustomElement(newx, newy);
7742   TestIfElementTouchesCustomElement(newx, newy);
7743
7744   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7745       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7746     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7747                              MV_DIR_OPPOSITE(direction));
7748 }
7749
7750 int AmoebeNachbarNr(int ax, int ay)
7751 {
7752   int i;
7753   int element = Feld[ax][ay];
7754   int group_nr = 0;
7755   static int xy[4][2] =
7756   {
7757     { 0, -1 },
7758     { -1, 0 },
7759     { +1, 0 },
7760     { 0, +1 }
7761   };
7762
7763   for (i = 0; i < NUM_DIRECTIONS; i++)
7764   {
7765     int x = ax + xy[i][0];
7766     int y = ay + xy[i][1];
7767
7768     if (!IN_LEV_FIELD(x, y))
7769       continue;
7770
7771     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7772       group_nr = AmoebaNr[x][y];
7773   }
7774
7775   return group_nr;
7776 }
7777
7778 void AmoebenVereinigen(int ax, int ay)
7779 {
7780   int i, x, y, xx, yy;
7781   int new_group_nr = AmoebaNr[ax][ay];
7782   static int xy[4][2] =
7783   {
7784     { 0, -1 },
7785     { -1, 0 },
7786     { +1, 0 },
7787     { 0, +1 }
7788   };
7789
7790   if (new_group_nr == 0)
7791     return;
7792
7793   for (i = 0; i < NUM_DIRECTIONS; i++)
7794   {
7795     x = ax + xy[i][0];
7796     y = ay + xy[i][1];
7797
7798     if (!IN_LEV_FIELD(x, y))
7799       continue;
7800
7801     if ((Feld[x][y] == EL_AMOEBA_FULL ||
7802          Feld[x][y] == EL_BD_AMOEBA ||
7803          Feld[x][y] == EL_AMOEBA_DEAD) &&
7804         AmoebaNr[x][y] != new_group_nr)
7805     {
7806       int old_group_nr = AmoebaNr[x][y];
7807
7808       if (old_group_nr == 0)
7809         return;
7810
7811       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7812       AmoebaCnt[old_group_nr] = 0;
7813       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7814       AmoebaCnt2[old_group_nr] = 0;
7815
7816       SCAN_PLAYFIELD(xx, yy)
7817       {
7818         if (AmoebaNr[xx][yy] == old_group_nr)
7819           AmoebaNr[xx][yy] = new_group_nr;
7820       }
7821     }
7822   }
7823 }
7824
7825 void AmoebeUmwandeln(int ax, int ay)
7826 {
7827   int i, x, y;
7828
7829   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7830   {
7831     int group_nr = AmoebaNr[ax][ay];
7832
7833 #ifdef DEBUG
7834     if (group_nr == 0)
7835     {
7836       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7837       printf("AmoebeUmwandeln(): This should never happen!\n");
7838       return;
7839     }
7840 #endif
7841
7842     SCAN_PLAYFIELD(x, y)
7843     {
7844       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7845       {
7846         AmoebaNr[x][y] = 0;
7847         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7848       }
7849     }
7850
7851     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7852                             SND_AMOEBA_TURNING_TO_GEM :
7853                             SND_AMOEBA_TURNING_TO_ROCK));
7854     Bang(ax, ay);
7855   }
7856   else
7857   {
7858     static int xy[4][2] =
7859     {
7860       { 0, -1 },
7861       { -1, 0 },
7862       { +1, 0 },
7863       { 0, +1 }
7864     };
7865
7866     for (i = 0; i < NUM_DIRECTIONS; i++)
7867     {
7868       x = ax + xy[i][0];
7869       y = ay + xy[i][1];
7870
7871       if (!IN_LEV_FIELD(x, y))
7872         continue;
7873
7874       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7875       {
7876         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7877                               SND_AMOEBA_TURNING_TO_GEM :
7878                               SND_AMOEBA_TURNING_TO_ROCK));
7879         Bang(x, y);
7880       }
7881     }
7882   }
7883 }
7884
7885 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7886 {
7887   int x, y;
7888   int group_nr = AmoebaNr[ax][ay];
7889   boolean done = FALSE;
7890
7891 #ifdef DEBUG
7892   if (group_nr == 0)
7893   {
7894     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7895     printf("AmoebeUmwandelnBD(): This should never happen!\n");
7896     return;
7897   }
7898 #endif
7899
7900   SCAN_PLAYFIELD(x, y)
7901   {
7902     if (AmoebaNr[x][y] == group_nr &&
7903         (Feld[x][y] == EL_AMOEBA_DEAD ||
7904          Feld[x][y] == EL_BD_AMOEBA ||
7905          Feld[x][y] == EL_AMOEBA_GROWING))
7906     {
7907       AmoebaNr[x][y] = 0;
7908       Feld[x][y] = new_element;
7909       InitField(x, y, FALSE);
7910       DrawLevelField(x, y);
7911       done = TRUE;
7912     }
7913   }
7914
7915   if (done)
7916     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7917                             SND_BD_AMOEBA_TURNING_TO_ROCK :
7918                             SND_BD_AMOEBA_TURNING_TO_GEM));
7919 }
7920
7921 void AmoebeWaechst(int x, int y)
7922 {
7923   static unsigned long sound_delay = 0;
7924   static unsigned long sound_delay_value = 0;
7925
7926   if (!MovDelay[x][y])          /* start new growing cycle */
7927   {
7928     MovDelay[x][y] = 7;
7929
7930     if (DelayReached(&sound_delay, sound_delay_value))
7931     {
7932       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7933       sound_delay_value = 30;
7934     }
7935   }
7936
7937   if (MovDelay[x][y])           /* wait some time before growing bigger */
7938   {
7939     MovDelay[x][y]--;
7940     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7941     {
7942       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7943                                            6 - MovDelay[x][y]);
7944
7945       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7946     }
7947
7948     if (!MovDelay[x][y])
7949     {
7950       Feld[x][y] = Store[x][y];
7951       Store[x][y] = 0;
7952       DrawLevelField(x, y);
7953     }
7954   }
7955 }
7956
7957 void AmoebaDisappearing(int x, int y)
7958 {
7959   static unsigned long sound_delay = 0;
7960   static unsigned long sound_delay_value = 0;
7961
7962   if (!MovDelay[x][y])          /* start new shrinking cycle */
7963   {
7964     MovDelay[x][y] = 7;
7965
7966     if (DelayReached(&sound_delay, sound_delay_value))
7967       sound_delay_value = 30;
7968   }
7969
7970   if (MovDelay[x][y])           /* wait some time before shrinking */
7971   {
7972     MovDelay[x][y]--;
7973     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7974     {
7975       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7976                                            6 - MovDelay[x][y]);
7977
7978       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7979     }
7980
7981     if (!MovDelay[x][y])
7982     {
7983       Feld[x][y] = EL_EMPTY;
7984       DrawLevelField(x, y);
7985
7986       /* don't let mole enter this field in this cycle;
7987          (give priority to objects falling to this field from above) */
7988       Stop[x][y] = TRUE;
7989     }
7990   }
7991 }
7992
7993 void AmoebeAbleger(int ax, int ay)
7994 {
7995   int i;
7996   int element = Feld[ax][ay];
7997   int graphic = el2img(element);
7998   int newax = ax, neway = ay;
7999   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8000   static int xy[4][2] =
8001   {
8002     { 0, -1 },
8003     { -1, 0 },
8004     { +1, 0 },
8005     { 0, +1 }
8006   };
8007
8008   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8009   {
8010     Feld[ax][ay] = EL_AMOEBA_DEAD;
8011     DrawLevelField(ax, ay);
8012     return;
8013   }
8014
8015   if (IS_ANIMATED(graphic))
8016     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8017
8018   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8019     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8020
8021   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8022   {
8023     MovDelay[ax][ay]--;
8024     if (MovDelay[ax][ay])
8025       return;
8026   }
8027
8028   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8029   {
8030     int start = RND(4);
8031     int x = ax + xy[start][0];
8032     int y = ay + xy[start][1];
8033
8034     if (!IN_LEV_FIELD(x, y))
8035       return;
8036
8037     if (IS_FREE(x, y) ||
8038         CAN_GROW_INTO(Feld[x][y]) ||
8039         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8040         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8041     {
8042       newax = x;
8043       neway = y;
8044     }
8045
8046     if (newax == ax && neway == ay)
8047       return;
8048   }
8049   else                          /* normal or "filled" (BD style) amoeba */
8050   {
8051     int start = RND(4);
8052     boolean waiting_for_player = FALSE;
8053
8054     for (i = 0; i < NUM_DIRECTIONS; i++)
8055     {
8056       int j = (start + i) % 4;
8057       int x = ax + xy[j][0];
8058       int y = ay + xy[j][1];
8059
8060       if (!IN_LEV_FIELD(x, y))
8061         continue;
8062
8063       if (IS_FREE(x, y) ||
8064           CAN_GROW_INTO(Feld[x][y]) ||
8065           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8066           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8067       {
8068         newax = x;
8069         neway = y;
8070         break;
8071       }
8072       else if (IS_PLAYER(x, y))
8073         waiting_for_player = TRUE;
8074     }
8075
8076     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8077     {
8078       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8079       {
8080         Feld[ax][ay] = EL_AMOEBA_DEAD;
8081         DrawLevelField(ax, ay);
8082         AmoebaCnt[AmoebaNr[ax][ay]]--;
8083
8084         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8085         {
8086           if (element == EL_AMOEBA_FULL)
8087             AmoebeUmwandeln(ax, ay);
8088           else if (element == EL_BD_AMOEBA)
8089             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8090         }
8091       }
8092       return;
8093     }
8094     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8095     {
8096       /* amoeba gets larger by growing in some direction */
8097
8098       int new_group_nr = AmoebaNr[ax][ay];
8099
8100 #ifdef DEBUG
8101   if (new_group_nr == 0)
8102   {
8103     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8104     printf("AmoebeAbleger(): This should never happen!\n");
8105     return;
8106   }
8107 #endif
8108
8109       AmoebaNr[newax][neway] = new_group_nr;
8110       AmoebaCnt[new_group_nr]++;
8111       AmoebaCnt2[new_group_nr]++;
8112
8113       /* if amoeba touches other amoeba(s) after growing, unify them */
8114       AmoebenVereinigen(newax, neway);
8115
8116       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8117       {
8118         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8119         return;
8120       }
8121     }
8122   }
8123
8124   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8125       (neway == lev_fieldy - 1 && newax != ax))
8126   {
8127     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8128     Store[newax][neway] = element;
8129   }
8130   else if (neway == ay || element == EL_EMC_DRIPPER)
8131   {
8132     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8133
8134     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8135   }
8136   else
8137   {
8138     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8139     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8140     Store[ax][ay] = EL_AMOEBA_DROP;
8141     ContinueMoving(ax, ay);
8142     return;
8143   }
8144
8145   DrawLevelField(newax, neway);
8146 }
8147
8148 void Life(int ax, int ay)
8149 {
8150   int x1, y1, x2, y2;
8151   int life_time = 40;
8152   int element = Feld[ax][ay];
8153   int graphic = el2img(element);
8154   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8155                          level.biomaze);
8156   boolean changed = FALSE;
8157
8158   if (IS_ANIMATED(graphic))
8159     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8160
8161   if (Stop[ax][ay])
8162     return;
8163
8164   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8165     MovDelay[ax][ay] = life_time;
8166
8167   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8168   {
8169     MovDelay[ax][ay]--;
8170     if (MovDelay[ax][ay])
8171       return;
8172   }
8173
8174   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8175   {
8176     int xx = ax+x1, yy = ay+y1;
8177     int nachbarn = 0;
8178
8179     if (!IN_LEV_FIELD(xx, yy))
8180       continue;
8181
8182     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8183     {
8184       int x = xx+x2, y = yy+y2;
8185
8186       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8187         continue;
8188
8189       if (((Feld[x][y] == element ||
8190             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8191            !Stop[x][y]) ||
8192           (IS_FREE(x, y) && Stop[x][y]))
8193         nachbarn++;
8194     }
8195
8196     if (xx == ax && yy == ay)           /* field in the middle */
8197     {
8198       if (nachbarn < life_parameter[0] ||
8199           nachbarn > life_parameter[1])
8200       {
8201         Feld[xx][yy] = EL_EMPTY;
8202         if (!Stop[xx][yy])
8203           DrawLevelField(xx, yy);
8204         Stop[xx][yy] = TRUE;
8205         changed = TRUE;
8206       }
8207     }
8208     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8209     {                                   /* free border field */
8210       if (nachbarn >= life_parameter[2] &&
8211           nachbarn <= life_parameter[3])
8212       {
8213         Feld[xx][yy] = element;
8214         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8215         if (!Stop[xx][yy])
8216           DrawLevelField(xx, yy);
8217         Stop[xx][yy] = TRUE;
8218         changed = TRUE;
8219       }
8220     }
8221   }
8222
8223   if (changed)
8224     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8225                    SND_GAME_OF_LIFE_GROWING);
8226 }
8227
8228 static void InitRobotWheel(int x, int y)
8229 {
8230   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8231 }
8232
8233 static void RunRobotWheel(int x, int y)
8234 {
8235   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8236 }
8237
8238 static void StopRobotWheel(int x, int y)
8239 {
8240   if (ZX == x && ZY == y)
8241     ZX = ZY = -1;
8242 }
8243
8244 static void InitTimegateWheel(int x, int y)
8245 {
8246   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8247 }
8248
8249 static void RunTimegateWheel(int x, int y)
8250 {
8251   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8252 }
8253
8254 static void InitMagicBallDelay(int x, int y)
8255 {
8256 #if 1
8257   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8258 #else
8259   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8260 #endif
8261 }
8262
8263 static void ActivateMagicBall(int bx, int by)
8264 {
8265   int x, y;
8266
8267   if (level.ball_random)
8268   {
8269     int pos_border = RND(8);    /* select one of the eight border elements */
8270     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8271     int xx = pos_content % 3;
8272     int yy = pos_content / 3;
8273
8274     x = bx - 1 + xx;
8275     y = by - 1 + yy;
8276
8277     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8278       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8279   }
8280   else
8281   {
8282     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8283     {
8284       int xx = x - bx + 1;
8285       int yy = y - by + 1;
8286
8287       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8288         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8289     }
8290   }
8291
8292   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8293 }
8294
8295 void CheckExit(int x, int y)
8296 {
8297   if (local_player->gems_still_needed > 0 ||
8298       local_player->sokobanfields_still_needed > 0 ||
8299       local_player->lights_still_needed > 0)
8300   {
8301     int element = Feld[x][y];
8302     int graphic = el2img(element);
8303
8304     if (IS_ANIMATED(graphic))
8305       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8306
8307     return;
8308   }
8309
8310   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8311     return;
8312
8313   Feld[x][y] = EL_EXIT_OPENING;
8314
8315   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8316 }
8317
8318 void CheckExitEM(int x, int y)
8319 {
8320   if (local_player->gems_still_needed > 0 ||
8321       local_player->sokobanfields_still_needed > 0 ||
8322       local_player->lights_still_needed > 0)
8323   {
8324     int element = Feld[x][y];
8325     int graphic = el2img(element);
8326
8327     if (IS_ANIMATED(graphic))
8328       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8329
8330     return;
8331   }
8332
8333   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8334     return;
8335
8336   Feld[x][y] = EL_EM_EXIT_OPENING;
8337
8338   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8339 }
8340
8341 void CheckExitSteel(int x, int y)
8342 {
8343   if (local_player->gems_still_needed > 0 ||
8344       local_player->sokobanfields_still_needed > 0 ||
8345       local_player->lights_still_needed > 0)
8346   {
8347     int element = Feld[x][y];
8348     int graphic = el2img(element);
8349
8350     if (IS_ANIMATED(graphic))
8351       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8352
8353     return;
8354   }
8355
8356   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8357     return;
8358
8359   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8360
8361   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8362 }
8363
8364 void CheckExitSteelEM(int x, int y)
8365 {
8366   if (local_player->gems_still_needed > 0 ||
8367       local_player->sokobanfields_still_needed > 0 ||
8368       local_player->lights_still_needed > 0)
8369   {
8370     int element = Feld[x][y];
8371     int graphic = el2img(element);
8372
8373     if (IS_ANIMATED(graphic))
8374       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8375
8376     return;
8377   }
8378
8379   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8380     return;
8381
8382   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8383
8384   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8385 }
8386
8387 void CheckExitSP(int x, int y)
8388 {
8389   if (local_player->gems_still_needed > 0)
8390   {
8391     int element = Feld[x][y];
8392     int graphic = el2img(element);
8393
8394     if (IS_ANIMATED(graphic))
8395       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8396
8397     return;
8398   }
8399
8400   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8401     return;
8402
8403   Feld[x][y] = EL_SP_EXIT_OPENING;
8404
8405   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8406 }
8407
8408 static void CloseAllOpenTimegates()
8409 {
8410   int x, y;
8411
8412   SCAN_PLAYFIELD(x, y)
8413   {
8414     int element = Feld[x][y];
8415
8416     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8417     {
8418       Feld[x][y] = EL_TIMEGATE_CLOSING;
8419
8420       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8421     }
8422   }
8423 }
8424
8425 void DrawTwinkleOnField(int x, int y)
8426 {
8427   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8428     return;
8429
8430   if (Feld[x][y] == EL_BD_DIAMOND)
8431     return;
8432
8433   if (MovDelay[x][y] == 0)      /* next animation frame */
8434     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8435
8436   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8437   {
8438     MovDelay[x][y]--;
8439
8440     if (setup.direct_draw && MovDelay[x][y])
8441       SetDrawtoField(DRAW_BUFFERED);
8442
8443     DrawLevelElementAnimation(x, y, Feld[x][y]);
8444
8445     if (MovDelay[x][y] != 0)
8446     {
8447       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8448                                            10 - MovDelay[x][y]);
8449
8450       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8451
8452       if (setup.direct_draw)
8453       {
8454         int dest_x, dest_y;
8455
8456         dest_x = FX + SCREENX(x) * TILEX;
8457         dest_y = FY + SCREENY(y) * TILEY;
8458
8459         BlitBitmap(drawto_field, window,
8460                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8461         SetDrawtoField(DRAW_DIRECT);
8462       }
8463     }
8464   }
8465 }
8466
8467 void MauerWaechst(int x, int y)
8468 {
8469   int delay = 6;
8470
8471   if (!MovDelay[x][y])          /* next animation frame */
8472     MovDelay[x][y] = 3 * delay;
8473
8474   if (MovDelay[x][y])           /* wait some time before next frame */
8475   {
8476     MovDelay[x][y]--;
8477
8478     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8479     {
8480       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8481       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8482
8483       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8484     }
8485
8486     if (!MovDelay[x][y])
8487     {
8488       if (MovDir[x][y] == MV_LEFT)
8489       {
8490         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8491           DrawLevelField(x - 1, y);
8492       }
8493       else if (MovDir[x][y] == MV_RIGHT)
8494       {
8495         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8496           DrawLevelField(x + 1, y);
8497       }
8498       else if (MovDir[x][y] == MV_UP)
8499       {
8500         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8501           DrawLevelField(x, y - 1);
8502       }
8503       else
8504       {
8505         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8506           DrawLevelField(x, y + 1);
8507       }
8508
8509       Feld[x][y] = Store[x][y];
8510       Store[x][y] = 0;
8511       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8512       DrawLevelField(x, y);
8513     }
8514   }
8515 }
8516
8517 void MauerAbleger(int ax, int ay)
8518 {
8519   int element = Feld[ax][ay];
8520   int graphic = el2img(element);
8521   boolean oben_frei = FALSE, unten_frei = FALSE;
8522   boolean links_frei = FALSE, rechts_frei = FALSE;
8523   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8524   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8525   boolean new_wall = FALSE;
8526
8527   if (IS_ANIMATED(graphic))
8528     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8529
8530   if (!MovDelay[ax][ay])        /* start building new wall */
8531     MovDelay[ax][ay] = 6;
8532
8533   if (MovDelay[ax][ay])         /* wait some time before building new wall */
8534   {
8535     MovDelay[ax][ay]--;
8536     if (MovDelay[ax][ay])
8537       return;
8538   }
8539
8540   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8541     oben_frei = TRUE;
8542   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8543     unten_frei = TRUE;
8544   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8545     links_frei = TRUE;
8546   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8547     rechts_frei = TRUE;
8548
8549   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
8550       element == EL_EXPANDABLE_WALL_ANY)
8551   {
8552     if (oben_frei)
8553     {
8554       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
8555       Store[ax][ay-1] = element;
8556       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8557       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8558         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8559                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
8560       new_wall = TRUE;
8561     }
8562     if (unten_frei)
8563     {
8564       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
8565       Store[ax][ay+1] = element;
8566       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8567       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8568         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8569                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8570       new_wall = TRUE;
8571     }
8572   }
8573
8574   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8575       element == EL_EXPANDABLE_WALL_ANY ||
8576       element == EL_EXPANDABLE_WALL ||
8577       element == EL_BD_EXPANDABLE_WALL)
8578   {
8579     if (links_frei)
8580     {
8581       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8582       Store[ax-1][ay] = element;
8583       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8584       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8585         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8586                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8587       new_wall = TRUE;
8588     }
8589
8590     if (rechts_frei)
8591     {
8592       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8593       Store[ax+1][ay] = element;
8594       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8595       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8596         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8597                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8598       new_wall = TRUE;
8599     }
8600   }
8601
8602   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8603     DrawLevelField(ax, ay);
8604
8605   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8606     oben_massiv = TRUE;
8607   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8608     unten_massiv = TRUE;
8609   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8610     links_massiv = TRUE;
8611   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8612     rechts_massiv = TRUE;
8613
8614   if (((oben_massiv && unten_massiv) ||
8615        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8616        element == EL_EXPANDABLE_WALL) &&
8617       ((links_massiv && rechts_massiv) ||
8618        element == EL_EXPANDABLE_WALL_VERTICAL))
8619     Feld[ax][ay] = EL_WALL;
8620
8621   if (new_wall)
8622     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8623 }
8624
8625 void MauerAblegerStahl(int ax, int ay)
8626 {
8627   int element = Feld[ax][ay];
8628   int graphic = el2img(element);
8629   boolean oben_frei = FALSE, unten_frei = FALSE;
8630   boolean links_frei = FALSE, rechts_frei = FALSE;
8631   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8632   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8633   boolean new_wall = FALSE;
8634
8635   if (IS_ANIMATED(graphic))
8636     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8637
8638   if (!MovDelay[ax][ay])        /* start building new wall */
8639     MovDelay[ax][ay] = 6;
8640
8641   if (MovDelay[ax][ay])         /* wait some time before building new wall */
8642   {
8643     MovDelay[ax][ay]--;
8644     if (MovDelay[ax][ay])
8645       return;
8646   }
8647
8648   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8649     oben_frei = TRUE;
8650   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8651     unten_frei = TRUE;
8652   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8653     links_frei = TRUE;
8654   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8655     rechts_frei = TRUE;
8656
8657   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
8658       element == EL_EXPANDABLE_STEELWALL_ANY)
8659   {
8660     if (oben_frei)
8661     {
8662       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
8663       Store[ax][ay-1] = element;
8664       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8665       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8666         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8667                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
8668       new_wall = TRUE;
8669     }
8670     if (unten_frei)
8671     {
8672       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
8673       Store[ax][ay+1] = element;
8674       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8675       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8676         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8677                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
8678       new_wall = TRUE;
8679     }
8680   }
8681
8682   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
8683       element == EL_EXPANDABLE_STEELWALL_ANY)
8684   {
8685     if (links_frei)
8686     {
8687       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8688       Store[ax-1][ay] = element;
8689       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8690       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8691         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8692                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
8693       new_wall = TRUE;
8694     }
8695
8696     if (rechts_frei)
8697     {
8698       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8699       Store[ax+1][ay] = element;
8700       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8701       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8702         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8703                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
8704       new_wall = TRUE;
8705     }
8706   }
8707
8708   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8709     oben_massiv = TRUE;
8710   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8711     unten_massiv = TRUE;
8712   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8713     links_massiv = TRUE;
8714   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8715     rechts_massiv = TRUE;
8716
8717   if (((oben_massiv && unten_massiv) ||
8718        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
8719       ((links_massiv && rechts_massiv) ||
8720        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
8721     Feld[ax][ay] = EL_WALL;
8722
8723   if (new_wall)
8724     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8725 }
8726
8727 void CheckForDragon(int x, int y)
8728 {
8729   int i, j;
8730   boolean dragon_found = FALSE;
8731   static int xy[4][2] =
8732   {
8733     { 0, -1 },
8734     { -1, 0 },
8735     { +1, 0 },
8736     { 0, +1 }
8737   };
8738
8739   for (i = 0; i < NUM_DIRECTIONS; i++)
8740   {
8741     for (j = 0; j < 4; j++)
8742     {
8743       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8744
8745       if (IN_LEV_FIELD(xx, yy) &&
8746           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
8747       {
8748         if (Feld[xx][yy] == EL_DRAGON)
8749           dragon_found = TRUE;
8750       }
8751       else
8752         break;
8753     }
8754   }
8755
8756   if (!dragon_found)
8757   {
8758     for (i = 0; i < NUM_DIRECTIONS; i++)
8759     {
8760       for (j = 0; j < 3; j++)
8761       {
8762         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8763   
8764         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
8765         {
8766           Feld[xx][yy] = EL_EMPTY;
8767           DrawLevelField(xx, yy);
8768         }
8769         else
8770           break;
8771       }
8772     }
8773   }
8774 }
8775
8776 static void InitBuggyBase(int x, int y)
8777 {
8778   int element = Feld[x][y];
8779   int activating_delay = FRAMES_PER_SECOND / 4;
8780
8781   ChangeDelay[x][y] =
8782     (element == EL_SP_BUGGY_BASE ?
8783      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
8784      element == EL_SP_BUGGY_BASE_ACTIVATING ?
8785      activating_delay :
8786      element == EL_SP_BUGGY_BASE_ACTIVE ?
8787      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
8788 }
8789
8790 static void WarnBuggyBase(int x, int y)
8791 {
8792   int i;
8793   static int xy[4][2] =
8794   {
8795     { 0, -1 },
8796     { -1, 0 },
8797     { +1, 0 },
8798     { 0, +1 }
8799   };
8800
8801   for (i = 0; i < NUM_DIRECTIONS; i++)
8802   {
8803     int xx = x + xy[i][0];
8804     int yy = y + xy[i][1];
8805
8806     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
8807     {
8808       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8809
8810       break;
8811     }
8812   }
8813 }
8814
8815 static void InitTrap(int x, int y)
8816 {
8817   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8818 }
8819
8820 static void ActivateTrap(int x, int y)
8821 {
8822   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8823 }
8824
8825 static void ChangeActiveTrap(int x, int y)
8826 {
8827   int graphic = IMG_TRAP_ACTIVE;
8828
8829   /* if new animation frame was drawn, correct crumbled sand border */
8830   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8831     DrawLevelFieldCrumbledSand(x, y);
8832 }
8833
8834 static int getSpecialActionElement(int element, int number, int base_element)
8835 {
8836   return (element != EL_EMPTY ? element :
8837           number != -1 ? base_element + number - 1 :
8838           EL_EMPTY);
8839 }
8840
8841 static int getModifiedActionNumber(int value_old, int operator, int operand,
8842                                    int value_min, int value_max)
8843 {
8844   int value_new = (operator == CA_MODE_SET      ? operand :
8845                    operator == CA_MODE_ADD      ? value_old + operand :
8846                    operator == CA_MODE_SUBTRACT ? value_old - operand :
8847                    operator == CA_MODE_MULTIPLY ? value_old * operand :
8848                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
8849                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
8850                    value_old);
8851
8852   return (value_new < value_min ? value_min :
8853           value_new > value_max ? value_max :
8854           value_new);
8855 }
8856
8857 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8858 {
8859   struct ElementInfo *ei = &element_info[element];
8860   struct ElementChangeInfo *change = &ei->change_page[page];
8861   int target_element = change->target_element;
8862   int action_type = change->action_type;
8863   int action_mode = change->action_mode;
8864   int action_arg = change->action_arg;
8865   int i;
8866
8867   if (!change->has_action)
8868     return;
8869
8870   /* ---------- determine action paramater values -------------------------- */
8871
8872   int level_time_value =
8873     (level.time > 0 ? TimeLeft :
8874      TimePlayed);
8875
8876   int action_arg_element =
8877     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
8878      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8879      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
8880      EL_EMPTY);
8881
8882   int action_arg_direction =
8883     (action_arg >= CA_ARG_DIRECTION_LEFT &&
8884      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8885      action_arg == CA_ARG_DIRECTION_TRIGGER ?
8886      change->actual_trigger_side :
8887      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8888      MV_DIR_OPPOSITE(change->actual_trigger_side) :
8889      MV_NONE);
8890
8891   int action_arg_number_min =
8892     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8893      CA_ARG_MIN);
8894
8895   int action_arg_number_max =
8896     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8897      action_type == CA_SET_LEVEL_GEMS ? 999 :
8898      action_type == CA_SET_LEVEL_TIME ? 9999 :
8899      action_type == CA_SET_LEVEL_SCORE ? 99999 :
8900      action_type == CA_SET_CE_VALUE ? 9999 :
8901      action_type == CA_SET_CE_SCORE ? 9999 :
8902      CA_ARG_MAX);
8903
8904   int action_arg_number_reset =
8905     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8906      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8907      action_type == CA_SET_LEVEL_TIME ? level.time :
8908      action_type == CA_SET_LEVEL_SCORE ? 0 :
8909 #if USE_NEW_CUSTOM_VALUE
8910      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8911 #else
8912      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8913 #endif
8914      action_type == CA_SET_CE_SCORE ? 0 :
8915      0);
8916
8917   int action_arg_number =
8918     (action_arg <= CA_ARG_MAX ? action_arg :
8919      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8920      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8921      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8922      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8923      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8924      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8925 #if USE_NEW_CUSTOM_VALUE
8926      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8927 #else
8928      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8929 #endif
8930      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8931      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8932      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8933      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8934      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8935      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8936      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8937      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8938      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8939      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
8940      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8941      -1);
8942
8943   int action_arg_number_old =
8944     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8945      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8946      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8947      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8948      action_type == CA_SET_CE_SCORE ? ei->collect_score :
8949      0);
8950
8951   int action_arg_number_new =
8952     getModifiedActionNumber(action_arg_number_old,
8953                             action_mode, action_arg_number,
8954                             action_arg_number_min, action_arg_number_max);
8955
8956   int trigger_player_bits =
8957     (change->actual_trigger_player >= EL_PLAYER_1 &&
8958      change->actual_trigger_player <= EL_PLAYER_4 ?
8959      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8960      PLAYER_BITS_ANY);
8961
8962   int action_arg_player_bits =
8963     (action_arg >= CA_ARG_PLAYER_1 &&
8964      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8965      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8966      PLAYER_BITS_ANY);
8967
8968   /* ---------- execute action  -------------------------------------------- */
8969
8970   switch (action_type)
8971   {
8972     case CA_NO_ACTION:
8973     {
8974       return;
8975     }
8976
8977     /* ---------- level actions  ------------------------------------------- */
8978
8979     case CA_RESTART_LEVEL:
8980     {
8981       game.restart_level = TRUE;
8982
8983       break;
8984     }
8985
8986     case CA_SHOW_ENVELOPE:
8987     {
8988       int element = getSpecialActionElement(action_arg_element,
8989                                             action_arg_number, EL_ENVELOPE_1);
8990
8991       if (IS_ENVELOPE(element))
8992         local_player->show_envelope = element;
8993
8994       break;
8995     }
8996
8997     case CA_SET_LEVEL_TIME:
8998     {
8999       if (level.time > 0)       /* only modify limited time value */
9000       {
9001         TimeLeft = action_arg_number_new;
9002
9003         DrawGameValue_Time(TimeLeft);
9004
9005         if (!TimeLeft && setup.time_limit)
9006           for (i = 0; i < MAX_PLAYERS; i++)
9007             KillPlayer(&stored_player[i]);
9008       }
9009
9010       break;
9011     }
9012
9013     case CA_SET_LEVEL_SCORE:
9014     {
9015       local_player->score = action_arg_number_new;
9016
9017       DrawGameValue_Score(local_player->score);
9018
9019       break;
9020     }
9021
9022     case CA_SET_LEVEL_GEMS:
9023     {
9024       local_player->gems_still_needed = action_arg_number_new;
9025
9026       DrawGameValue_Emeralds(local_player->gems_still_needed);
9027
9028       break;
9029     }
9030
9031 #if !USE_PLAYER_GRAVITY
9032     case CA_SET_LEVEL_GRAVITY:
9033     {
9034       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
9035                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
9036                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9037                       game.gravity);
9038       break;
9039     }
9040 #endif
9041
9042     case CA_SET_LEVEL_WIND:
9043     {
9044       game.wind_direction = action_arg_direction;
9045
9046       break;
9047     }
9048
9049     /* ---------- player actions  ------------------------------------------ */
9050
9051     case CA_MOVE_PLAYER:
9052     {
9053       /* automatically move to the next field in specified direction */
9054       for (i = 0; i < MAX_PLAYERS; i++)
9055         if (trigger_player_bits & (1 << i))
9056           stored_player[i].programmed_action = action_arg_direction;
9057
9058       break;
9059     }
9060
9061     case CA_EXIT_PLAYER:
9062     {
9063       for (i = 0; i < MAX_PLAYERS; i++)
9064         if (action_arg_player_bits & (1 << i))
9065           PlayerWins(&stored_player[i]);
9066
9067       break;
9068     }
9069
9070     case CA_KILL_PLAYER:
9071     {
9072       for (i = 0; i < MAX_PLAYERS; i++)
9073         if (action_arg_player_bits & (1 << i))
9074           KillPlayer(&stored_player[i]);
9075
9076       break;
9077     }
9078
9079     case CA_SET_PLAYER_KEYS:
9080     {
9081       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9082       int element = getSpecialActionElement(action_arg_element,
9083                                             action_arg_number, EL_KEY_1);
9084
9085       if (IS_KEY(element))
9086       {
9087         for (i = 0; i < MAX_PLAYERS; i++)
9088         {
9089           if (trigger_player_bits & (1 << i))
9090           {
9091             stored_player[i].key[KEY_NR(element)] = key_state;
9092
9093             DrawGameDoorValues();
9094           }
9095         }
9096       }
9097
9098       break;
9099     }
9100
9101     case CA_SET_PLAYER_SPEED:
9102     {
9103       for (i = 0; i < MAX_PLAYERS; i++)
9104       {
9105         if (trigger_player_bits & (1 << i))
9106         {
9107           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9108
9109           if (action_arg == CA_ARG_SPEED_FASTER &&
9110               stored_player[i].cannot_move)
9111           {
9112             action_arg_number = STEPSIZE_VERY_SLOW;
9113           }
9114           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9115                    action_arg == CA_ARG_SPEED_FASTER)
9116           {
9117             action_arg_number = 2;
9118             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9119                            CA_MODE_MULTIPLY);
9120           }
9121           else if (action_arg == CA_ARG_NUMBER_RESET)
9122           {
9123             action_arg_number = level.initial_player_stepsize[i];
9124           }
9125
9126           move_stepsize =
9127             getModifiedActionNumber(move_stepsize,
9128                                     action_mode,
9129                                     action_arg_number,
9130                                     action_arg_number_min,
9131                                     action_arg_number_max);
9132
9133           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9134         }
9135       }
9136
9137       break;
9138     }
9139
9140     case CA_SET_PLAYER_SHIELD:
9141     {
9142       for (i = 0; i < MAX_PLAYERS; i++)
9143       {
9144         if (trigger_player_bits & (1 << i))
9145         {
9146           if (action_arg == CA_ARG_SHIELD_OFF)
9147           {
9148             stored_player[i].shield_normal_time_left = 0;
9149             stored_player[i].shield_deadly_time_left = 0;
9150           }
9151           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9152           {
9153             stored_player[i].shield_normal_time_left = 999999;
9154           }
9155           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9156           {
9157             stored_player[i].shield_normal_time_left = 999999;
9158             stored_player[i].shield_deadly_time_left = 999999;
9159           }
9160         }
9161       }
9162
9163       break;
9164     }
9165
9166 #if USE_PLAYER_GRAVITY
9167     case CA_SET_PLAYER_GRAVITY:
9168     {
9169       for (i = 0; i < MAX_PLAYERS; i++)
9170       {
9171         if (trigger_player_bits & (1 << i))
9172         {
9173           stored_player[i].gravity =
9174             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9175              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9176              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9177              stored_player[i].gravity);
9178         }
9179       }
9180
9181       break;
9182     }
9183 #endif
9184
9185     case CA_SET_PLAYER_ARTWORK:
9186     {
9187       for (i = 0; i < MAX_PLAYERS; i++)
9188       {
9189         if (trigger_player_bits & (1 << i))
9190         {
9191           int artwork_element = action_arg_element;
9192
9193           if (action_arg == CA_ARG_ELEMENT_RESET)
9194             artwork_element =
9195               (level.use_artwork_element[i] ? level.artwork_element[i] :
9196                stored_player[i].element_nr);
9197
9198 #if USE_GFX_RESET_PLAYER_ARTWORK
9199           if (stored_player[i].artwork_element != artwork_element)
9200             stored_player[i].Frame = 0;
9201 #endif
9202
9203           stored_player[i].artwork_element = artwork_element;
9204
9205           SetPlayerWaiting(&stored_player[i], FALSE);
9206
9207           /* set number of special actions for bored and sleeping animation */
9208           stored_player[i].num_special_action_bored =
9209             get_num_special_action(artwork_element,
9210                                    ACTION_BORING_1, ACTION_BORING_LAST);
9211           stored_player[i].num_special_action_sleeping =
9212             get_num_special_action(artwork_element,
9213                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9214         }
9215       }
9216
9217       break;
9218     }
9219
9220     /* ---------- CE actions  ---------------------------------------------- */
9221
9222     case CA_SET_CE_VALUE:
9223     {
9224 #if USE_NEW_CUSTOM_VALUE
9225       int last_ce_value = CustomValue[x][y];
9226
9227       CustomValue[x][y] = action_arg_number_new;
9228
9229       if (CustomValue[x][y] != last_ce_value)
9230       {
9231         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9232         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9233
9234         if (CustomValue[x][y] == 0)
9235         {
9236           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9237           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9238         }
9239       }
9240 #endif
9241
9242       break;
9243     }
9244
9245     case CA_SET_CE_SCORE:
9246     {
9247 #if USE_NEW_CUSTOM_VALUE
9248       int last_ce_score = ei->collect_score;
9249
9250       ei->collect_score = action_arg_number_new;
9251
9252       if (ei->collect_score != last_ce_score)
9253       {
9254         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9255         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9256
9257         if (ei->collect_score == 0)
9258         {
9259           int xx, yy;
9260
9261           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9262           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9263
9264           /*
9265             This is a very special case that seems to be a mixture between
9266             CheckElementChange() and CheckTriggeredElementChange(): while
9267             the first one only affects single elements that are triggered
9268             directly, the second one affects multiple elements in the playfield
9269             that are triggered indirectly by another element. This is a third
9270             case: Changing the CE score always affects multiple identical CEs,
9271             so every affected CE must be checked, not only the single CE for
9272             which the CE score was changed in the first place (as every instance
9273             of that CE shares the same CE score, and therefore also can change)!
9274           */
9275           SCAN_PLAYFIELD(xx, yy)
9276           {
9277             if (Feld[xx][yy] == element)
9278               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9279                                  CE_SCORE_GETS_ZERO);
9280           }
9281         }
9282       }
9283 #endif
9284
9285       break;
9286     }
9287
9288     /* ---------- engine actions  ------------------------------------------ */
9289
9290     case CA_SET_ENGINE_SCAN_MODE:
9291     {
9292       InitPlayfieldScanMode(action_arg);
9293
9294       break;
9295     }
9296
9297     default:
9298       break;
9299   }
9300 }
9301
9302 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9303 {
9304   int old_element = Feld[x][y];
9305   int new_element = GetElementFromGroupElement(element);
9306   int previous_move_direction = MovDir[x][y];
9307 #if USE_NEW_CUSTOM_VALUE
9308   int last_ce_value = CustomValue[x][y];
9309 #endif
9310   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9311   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9312   boolean add_player_onto_element = (new_element_is_player &&
9313 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9314                                      /* this breaks SnakeBite when a snake is
9315                                         halfway through a door that closes */
9316                                      /* NOW FIXED AT LEVEL INIT IN files.c */
9317                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9318 #endif
9319                                      IS_WALKABLE(old_element));
9320
9321 #if 0
9322   /* check if element under the player changes from accessible to unaccessible
9323      (needed for special case of dropping element which then changes) */
9324   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9325       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9326   {
9327     Bang(x, y);
9328
9329     return;
9330   }
9331 #endif
9332
9333   if (!add_player_onto_element)
9334   {
9335     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9336       RemoveMovingField(x, y);
9337     else
9338       RemoveField(x, y);
9339
9340     Feld[x][y] = new_element;
9341
9342 #if !USE_GFX_RESET_GFX_ANIMATION
9343     ResetGfxAnimation(x, y);
9344     ResetRandomAnimationValue(x, y);
9345 #endif
9346
9347     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9348       MovDir[x][y] = previous_move_direction;
9349
9350 #if USE_NEW_CUSTOM_VALUE
9351     if (element_info[new_element].use_last_ce_value)
9352       CustomValue[x][y] = last_ce_value;
9353 #endif
9354
9355     InitField_WithBug1(x, y, FALSE);
9356
9357     new_element = Feld[x][y];   /* element may have changed */
9358
9359 #if USE_GFX_RESET_GFX_ANIMATION
9360     ResetGfxAnimation(x, y);
9361     ResetRandomAnimationValue(x, y);
9362 #endif
9363
9364     DrawLevelField(x, y);
9365
9366     if (GFX_CRUMBLED(new_element))
9367       DrawLevelFieldCrumbledSandNeighbours(x, y);
9368   }
9369
9370 #if 1
9371   /* check if element under the player changes from accessible to unaccessible
9372      (needed for special case of dropping element which then changes) */
9373   /* (must be checked after creating new element for walkable group elements) */
9374 #if USE_FIX_KILLED_BY_NON_WALKABLE
9375   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9376       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9377   {
9378     Bang(x, y);
9379
9380     return;
9381   }
9382 #else
9383   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9384       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9385   {
9386     Bang(x, y);
9387
9388     return;
9389   }
9390 #endif
9391 #endif
9392
9393   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9394   if (new_element_is_player)
9395     RelocatePlayer(x, y, new_element);
9396
9397   if (is_change)
9398     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9399
9400   TestIfBadThingTouchesPlayer(x, y);
9401   TestIfPlayerTouchesCustomElement(x, y);
9402   TestIfElementTouchesCustomElement(x, y);
9403 }
9404
9405 static void CreateField(int x, int y, int element)
9406 {
9407   CreateFieldExt(x, y, element, FALSE);
9408 }
9409
9410 static void CreateElementFromChange(int x, int y, int element)
9411 {
9412   element = GET_VALID_RUNTIME_ELEMENT(element);
9413
9414 #if USE_STOP_CHANGED_ELEMENTS
9415   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9416   {
9417     int old_element = Feld[x][y];
9418
9419     /* prevent changed element from moving in same engine frame
9420        unless both old and new element can either fall or move */
9421     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9422         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9423       Stop[x][y] = TRUE;
9424   }
9425 #endif
9426
9427   CreateFieldExt(x, y, element, TRUE);
9428 }
9429
9430 static boolean ChangeElement(int x, int y, int element, int page)
9431 {
9432   struct ElementInfo *ei = &element_info[element];
9433   struct ElementChangeInfo *change = &ei->change_page[page];
9434   int ce_value = CustomValue[x][y];
9435   int ce_score = ei->collect_score;
9436   int target_element;
9437   int old_element = Feld[x][y];
9438
9439   /* always use default change event to prevent running into a loop */
9440   if (ChangeEvent[x][y] == -1)
9441     ChangeEvent[x][y] = CE_DELAY;
9442
9443   if (ChangeEvent[x][y] == CE_DELAY)
9444   {
9445     /* reset actual trigger element, trigger player and action element */
9446     change->actual_trigger_element = EL_EMPTY;
9447     change->actual_trigger_player = EL_PLAYER_1;
9448     change->actual_trigger_side = CH_SIDE_NONE;
9449     change->actual_trigger_ce_value = 0;
9450     change->actual_trigger_ce_score = 0;
9451   }
9452
9453   /* do not change elements more than a specified maximum number of changes */
9454   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
9455     return FALSE;
9456
9457   ChangeCount[x][y]++;          /* count number of changes in the same frame */
9458
9459   if (change->explode)
9460   {
9461     Bang(x, y);
9462
9463     return TRUE;
9464   }
9465
9466   if (change->use_target_content)
9467   {
9468     boolean complete_replace = TRUE;
9469     boolean can_replace[3][3];
9470     int xx, yy;
9471
9472     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9473     {
9474       boolean is_empty;
9475       boolean is_walkable;
9476       boolean is_diggable;
9477       boolean is_collectible;
9478       boolean is_removable;
9479       boolean is_destructible;
9480       int ex = x + xx - 1;
9481       int ey = y + yy - 1;
9482       int content_element = change->target_content.e[xx][yy];
9483       int e;
9484
9485       can_replace[xx][yy] = TRUE;
9486
9487       if (ex == x && ey == y)   /* do not check changing element itself */
9488         continue;
9489
9490       if (content_element == EL_EMPTY_SPACE)
9491       {
9492         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
9493
9494         continue;
9495       }
9496
9497       if (!IN_LEV_FIELD(ex, ey))
9498       {
9499         can_replace[xx][yy] = FALSE;
9500         complete_replace = FALSE;
9501
9502         continue;
9503       }
9504
9505       e = Feld[ex][ey];
9506
9507       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9508         e = MovingOrBlocked2Element(ex, ey);
9509
9510       is_empty = (IS_FREE(ex, ey) ||
9511                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
9512
9513       is_walkable     = (is_empty || IS_WALKABLE(e));
9514       is_diggable     = (is_empty || IS_DIGGABLE(e));
9515       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
9516       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
9517       is_removable    = (is_diggable || is_collectible);
9518
9519       can_replace[xx][yy] =
9520         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
9521           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
9522           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
9523           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
9524           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
9525           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
9526          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
9527
9528       if (!can_replace[xx][yy])
9529         complete_replace = FALSE;
9530     }
9531
9532     if (!change->only_if_complete || complete_replace)
9533     {
9534       boolean something_has_changed = FALSE;
9535
9536       if (change->only_if_complete && change->use_random_replace &&
9537           RND(100) < change->random_percentage)
9538         return FALSE;
9539
9540       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9541       {
9542         int ex = x + xx - 1;
9543         int ey = y + yy - 1;
9544         int content_element;
9545
9546         if (can_replace[xx][yy] && (!change->use_random_replace ||
9547                                     RND(100) < change->random_percentage))
9548         {
9549           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9550             RemoveMovingField(ex, ey);
9551
9552           ChangeEvent[ex][ey] = ChangeEvent[x][y];
9553
9554           content_element = change->target_content.e[xx][yy];
9555           target_element = GET_TARGET_ELEMENT(element, content_element, change,
9556                                               ce_value, ce_score);
9557
9558           CreateElementFromChange(ex, ey, target_element);
9559
9560           something_has_changed = TRUE;
9561
9562           /* for symmetry reasons, freeze newly created border elements */
9563           if (ex != x || ey != y)
9564             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
9565         }
9566       }
9567
9568       if (something_has_changed)
9569       {
9570         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9571         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9572       }
9573     }
9574   }
9575   else
9576   {
9577     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9578                                         ce_value, ce_score);
9579
9580     if (element == EL_DIAGONAL_GROWING ||
9581         element == EL_DIAGONAL_SHRINKING)
9582     {
9583       target_element = Store[x][y];
9584
9585       Store[x][y] = EL_EMPTY;
9586     }
9587
9588     CreateElementFromChange(x, y, target_element);
9589
9590     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9591     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9592   }
9593
9594   /* this uses direct change before indirect change */
9595   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9596
9597   return TRUE;
9598 }
9599
9600 #if USE_NEW_DELAYED_ACTION
9601
9602 static void HandleElementChange(int x, int y, int page)
9603 {
9604   int element = MovingOrBlocked2Element(x, y);
9605   struct ElementInfo *ei = &element_info[element];
9606   struct ElementChangeInfo *change = &ei->change_page[page];
9607
9608 #ifdef DEBUG
9609   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
9610       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
9611   {
9612     printf("\n\n");
9613     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9614            x, y, element, element_info[element].token_name);
9615     printf("HandleElementChange(): This should never happen!\n");
9616     printf("\n\n");
9617   }
9618 #endif
9619
9620   /* this can happen with classic bombs on walkable, changing elements */
9621   if (!CAN_CHANGE_OR_HAS_ACTION(element))
9622   {
9623 #if 0
9624     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9625       ChangeDelay[x][y] = 0;
9626 #endif
9627
9628     return;
9629   }
9630
9631   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9632   {
9633     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9634
9635     if (change->can_change)
9636     {
9637 #if 1
9638       /* !!! not clear why graphic animation should be reset at all here !!! */
9639       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
9640 #if USE_GFX_RESET_WHEN_NOT_MOVING
9641       /* when a custom element is about to change (for example by change delay),
9642          do not reset graphic animation when the custom element is moving */
9643       if (!IS_MOVING(x, y))
9644 #endif
9645       {
9646         ResetGfxAnimation(x, y);
9647         ResetRandomAnimationValue(x, y);
9648       }
9649 #endif
9650
9651       if (change->pre_change_function)
9652         change->pre_change_function(x, y);
9653     }
9654   }
9655
9656   ChangeDelay[x][y]--;
9657
9658   if (ChangeDelay[x][y] != 0)           /* continue element change */
9659   {
9660     if (change->can_change)
9661     {
9662       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9663
9664       if (IS_ANIMATED(graphic))
9665         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9666
9667       if (change->change_function)
9668         change->change_function(x, y);
9669     }
9670   }
9671   else                                  /* finish element change */
9672   {
9673     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9674     {
9675       page = ChangePage[x][y];
9676       ChangePage[x][y] = -1;
9677
9678       change = &ei->change_page[page];
9679     }
9680
9681     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9682     {
9683       ChangeDelay[x][y] = 1;            /* try change after next move step */
9684       ChangePage[x][y] = page;          /* remember page to use for change */
9685
9686       return;
9687     }
9688
9689     if (change->can_change)
9690     {
9691       if (ChangeElement(x, y, element, page))
9692       {
9693         if (change->post_change_function)
9694           change->post_change_function(x, y);
9695       }
9696     }
9697
9698     if (change->has_action)
9699       ExecuteCustomElementAction(x, y, element, page);
9700   }
9701 }
9702
9703 #else
9704
9705 static void HandleElementChange(int x, int y, int page)
9706 {
9707   int element = MovingOrBlocked2Element(x, y);
9708   struct ElementInfo *ei = &element_info[element];
9709   struct ElementChangeInfo *change = &ei->change_page[page];
9710
9711 #ifdef DEBUG
9712   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
9713   {
9714     printf("\n\n");
9715     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9716            x, y, element, element_info[element].token_name);
9717     printf("HandleElementChange(): This should never happen!\n");
9718     printf("\n\n");
9719   }
9720 #endif
9721
9722   /* this can happen with classic bombs on walkable, changing elements */
9723   if (!CAN_CHANGE(element))
9724   {
9725 #if 0
9726     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9727       ChangeDelay[x][y] = 0;
9728 #endif
9729
9730     return;
9731   }
9732
9733   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9734   {
9735     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9736
9737     ResetGfxAnimation(x, y);
9738     ResetRandomAnimationValue(x, y);
9739
9740     if (change->pre_change_function)
9741       change->pre_change_function(x, y);
9742   }
9743
9744   ChangeDelay[x][y]--;
9745
9746   if (ChangeDelay[x][y] != 0)           /* continue element change */
9747   {
9748     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9749
9750     if (IS_ANIMATED(graphic))
9751       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9752
9753     if (change->change_function)
9754       change->change_function(x, y);
9755   }
9756   else                                  /* finish element change */
9757   {
9758     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9759     {
9760       page = ChangePage[x][y];
9761       ChangePage[x][y] = -1;
9762
9763       change = &ei->change_page[page];
9764     }
9765
9766     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9767     {
9768       ChangeDelay[x][y] = 1;            /* try change after next move step */
9769       ChangePage[x][y] = page;          /* remember page to use for change */
9770
9771       return;
9772     }
9773
9774     if (ChangeElement(x, y, element, page))
9775     {
9776       if (change->post_change_function)
9777         change->post_change_function(x, y);
9778     }
9779   }
9780 }
9781
9782 #endif
9783
9784 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
9785                                               int trigger_element,
9786                                               int trigger_event,
9787                                               int trigger_player,
9788                                               int trigger_side,
9789                                               int trigger_page)
9790 {
9791   boolean change_done_any = FALSE;
9792   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9793   int i;
9794
9795   if (!(trigger_events[trigger_element][trigger_event]))
9796     return FALSE;
9797
9798 #if 0
9799   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9800          trigger_event, recursion_loop_depth, recursion_loop_detected,
9801          recursion_loop_element, EL_NAME(recursion_loop_element));
9802 #endif
9803
9804   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9805
9806   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9807   {
9808     int element = EL_CUSTOM_START + i;
9809     boolean change_done = FALSE;
9810     int p;
9811
9812     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9813         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9814       continue;
9815
9816     for (p = 0; p < element_info[element].num_change_pages; p++)
9817     {
9818       struct ElementChangeInfo *change = &element_info[element].change_page[p];
9819
9820       if (change->can_change_or_has_action &&
9821           change->has_event[trigger_event] &&
9822           change->trigger_side & trigger_side &&
9823           change->trigger_player & trigger_player &&
9824           change->trigger_page & trigger_page_bits &&
9825           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9826       {
9827         change->actual_trigger_element = trigger_element;
9828         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9829         change->actual_trigger_side = trigger_side;
9830         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9831         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9832
9833         if ((change->can_change && !change_done) || change->has_action)
9834         {
9835           int x, y;
9836
9837           SCAN_PLAYFIELD(x, y)
9838           {
9839             if (Feld[x][y] == element)
9840             {
9841               if (change->can_change && !change_done)
9842               {
9843                 ChangeDelay[x][y] = 1;
9844                 ChangeEvent[x][y] = trigger_event;
9845
9846                 HandleElementChange(x, y, p);
9847               }
9848 #if USE_NEW_DELAYED_ACTION
9849               else if (change->has_action)
9850               {
9851                 ExecuteCustomElementAction(x, y, element, p);
9852                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9853               }
9854 #else
9855               if (change->has_action)
9856               {
9857                 ExecuteCustomElementAction(x, y, element, p);
9858                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9859               }
9860 #endif
9861             }
9862           }
9863
9864           if (change->can_change)
9865           {
9866             change_done = TRUE;
9867             change_done_any = TRUE;
9868           }
9869         }
9870       }
9871     }
9872   }
9873
9874   RECURSION_LOOP_DETECTION_END();
9875
9876   return change_done_any;
9877 }
9878
9879 static boolean CheckElementChangeExt(int x, int y,
9880                                      int element,
9881                                      int trigger_element,
9882                                      int trigger_event,
9883                                      int trigger_player,
9884                                      int trigger_side)
9885 {
9886   boolean change_done = FALSE;
9887   int p;
9888
9889   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9890       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9891     return FALSE;
9892
9893   if (Feld[x][y] == EL_BLOCKED)
9894   {
9895     Blocked2Moving(x, y, &x, &y);
9896     element = Feld[x][y];
9897   }
9898
9899 #if 0
9900   /* check if element has already changed */
9901   if (Feld[x][y] != element)
9902     return FALSE;
9903 #else
9904   /* check if element has already changed or is about to change after moving */
9905   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9906        Feld[x][y] != element) ||
9907
9908       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9909        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9910         ChangePage[x][y] != -1)))
9911     return FALSE;
9912 #endif
9913
9914 #if 0
9915   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9916          trigger_event, recursion_loop_depth, recursion_loop_detected,
9917          recursion_loop_element, EL_NAME(recursion_loop_element));
9918 #endif
9919
9920   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9921
9922   for (p = 0; p < element_info[element].num_change_pages; p++)
9923   {
9924     struct ElementChangeInfo *change = &element_info[element].change_page[p];
9925
9926     /* check trigger element for all events where the element that is checked
9927        for changing interacts with a directly adjacent element -- this is
9928        different to element changes that affect other elements to change on the
9929        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
9930     boolean check_trigger_element =
9931       (trigger_event == CE_TOUCHING_X ||
9932        trigger_event == CE_HITTING_X ||
9933        trigger_event == CE_HIT_BY_X ||
9934 #if 1
9935        /* this one was forgotten until 3.2.3 */
9936        trigger_event == CE_DIGGING_X);
9937 #endif
9938
9939     if (change->can_change_or_has_action &&
9940         change->has_event[trigger_event] &&
9941         change->trigger_side & trigger_side &&
9942         change->trigger_player & trigger_player &&
9943         (!check_trigger_element ||
9944          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9945     {
9946       change->actual_trigger_element = trigger_element;
9947       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9948       change->actual_trigger_side = trigger_side;
9949       change->actual_trigger_ce_value = CustomValue[x][y];
9950       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9951
9952       /* special case: trigger element not at (x,y) position for some events */
9953       if (check_trigger_element)
9954       {
9955         static struct
9956         {
9957           int dx, dy;
9958         } move_xy[] =
9959           {
9960             {  0,  0 },
9961             { -1,  0 },
9962             { +1,  0 },
9963             {  0,  0 },
9964             {  0, -1 },
9965             {  0,  0 }, { 0, 0 }, { 0, 0 },
9966             {  0, +1 }
9967           };
9968
9969         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9970         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9971
9972         change->actual_trigger_ce_value = CustomValue[xx][yy];
9973         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9974       }
9975
9976       if (change->can_change && !change_done)
9977       {
9978         ChangeDelay[x][y] = 1;
9979         ChangeEvent[x][y] = trigger_event;
9980
9981         HandleElementChange(x, y, p);
9982
9983         change_done = TRUE;
9984       }
9985 #if USE_NEW_DELAYED_ACTION
9986       else if (change->has_action)
9987       {
9988         ExecuteCustomElementAction(x, y, element, p);
9989         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9990       }
9991 #else
9992       if (change->has_action)
9993       {
9994         ExecuteCustomElementAction(x, y, element, p);
9995         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9996       }
9997 #endif
9998     }
9999   }
10000
10001   RECURSION_LOOP_DETECTION_END();
10002
10003   return change_done;
10004 }
10005
10006 static void PlayPlayerSound(struct PlayerInfo *player)
10007 {
10008   int jx = player->jx, jy = player->jy;
10009   int sound_element = player->artwork_element;
10010   int last_action = player->last_action_waiting;
10011   int action = player->action_waiting;
10012
10013   if (player->is_waiting)
10014   {
10015     if (action != last_action)
10016       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10017     else
10018       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10019   }
10020   else
10021   {
10022     if (action != last_action)
10023       StopSound(element_info[sound_element].sound[last_action]);
10024
10025     if (last_action == ACTION_SLEEPING)
10026       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10027   }
10028 }
10029
10030 static void PlayAllPlayersSound()
10031 {
10032   int i;
10033
10034   for (i = 0; i < MAX_PLAYERS; i++)
10035     if (stored_player[i].active)
10036       PlayPlayerSound(&stored_player[i]);
10037 }
10038
10039 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10040 {
10041   boolean last_waiting = player->is_waiting;
10042   int move_dir = player->MovDir;
10043
10044   player->dir_waiting = move_dir;
10045   player->last_action_waiting = player->action_waiting;
10046
10047   if (is_waiting)
10048   {
10049     if (!last_waiting)          /* not waiting -> waiting */
10050     {
10051       player->is_waiting = TRUE;
10052
10053       player->frame_counter_bored =
10054         FrameCounter +
10055         game.player_boring_delay_fixed +
10056         GetSimpleRandom(game.player_boring_delay_random);
10057       player->frame_counter_sleeping =
10058         FrameCounter +
10059         game.player_sleeping_delay_fixed +
10060         GetSimpleRandom(game.player_sleeping_delay_random);
10061
10062       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10063     }
10064
10065     if (game.player_sleeping_delay_fixed +
10066         game.player_sleeping_delay_random > 0 &&
10067         player->anim_delay_counter == 0 &&
10068         player->post_delay_counter == 0 &&
10069         FrameCounter >= player->frame_counter_sleeping)
10070       player->is_sleeping = TRUE;
10071     else if (game.player_boring_delay_fixed +
10072              game.player_boring_delay_random > 0 &&
10073              FrameCounter >= player->frame_counter_bored)
10074       player->is_bored = TRUE;
10075
10076     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10077                               player->is_bored ? ACTION_BORING :
10078                               ACTION_WAITING);
10079
10080     if (player->is_sleeping && player->use_murphy)
10081     {
10082       /* special case for sleeping Murphy when leaning against non-free tile */
10083
10084       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10085           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10086            !IS_MOVING(player->jx - 1, player->jy)))
10087         move_dir = MV_LEFT;
10088       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10089                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10090                 !IS_MOVING(player->jx + 1, player->jy)))
10091         move_dir = MV_RIGHT;
10092       else
10093         player->is_sleeping = FALSE;
10094
10095       player->dir_waiting = move_dir;
10096     }
10097
10098     if (player->is_sleeping)
10099     {
10100       if (player->num_special_action_sleeping > 0)
10101       {
10102         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10103         {
10104           int last_special_action = player->special_action_sleeping;
10105           int num_special_action = player->num_special_action_sleeping;
10106           int special_action =
10107             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10108              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10109              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10110              last_special_action + 1 : ACTION_SLEEPING);
10111           int special_graphic =
10112             el_act_dir2img(player->artwork_element, special_action, move_dir);
10113
10114           player->anim_delay_counter =
10115             graphic_info[special_graphic].anim_delay_fixed +
10116             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10117           player->post_delay_counter =
10118             graphic_info[special_graphic].post_delay_fixed +
10119             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10120
10121           player->special_action_sleeping = special_action;
10122         }
10123
10124         if (player->anim_delay_counter > 0)
10125         {
10126           player->action_waiting = player->special_action_sleeping;
10127           player->anim_delay_counter--;
10128         }
10129         else if (player->post_delay_counter > 0)
10130         {
10131           player->post_delay_counter--;
10132         }
10133       }
10134     }
10135     else if (player->is_bored)
10136     {
10137       if (player->num_special_action_bored > 0)
10138       {
10139         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10140         {
10141           int special_action =
10142             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10143           int special_graphic =
10144             el_act_dir2img(player->artwork_element, special_action, move_dir);
10145
10146           player->anim_delay_counter =
10147             graphic_info[special_graphic].anim_delay_fixed +
10148             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10149           player->post_delay_counter =
10150             graphic_info[special_graphic].post_delay_fixed +
10151             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10152
10153           player->special_action_bored = special_action;
10154         }
10155
10156         if (player->anim_delay_counter > 0)
10157         {
10158           player->action_waiting = player->special_action_bored;
10159           player->anim_delay_counter--;
10160         }
10161         else if (player->post_delay_counter > 0)
10162         {
10163           player->post_delay_counter--;
10164         }
10165       }
10166     }
10167   }
10168   else if (last_waiting)        /* waiting -> not waiting */
10169   {
10170     player->is_waiting = FALSE;
10171     player->is_bored = FALSE;
10172     player->is_sleeping = FALSE;
10173
10174     player->frame_counter_bored = -1;
10175     player->frame_counter_sleeping = -1;
10176
10177     player->anim_delay_counter = 0;
10178     player->post_delay_counter = 0;
10179
10180     player->dir_waiting = player->MovDir;
10181     player->action_waiting = ACTION_DEFAULT;
10182
10183     player->special_action_bored = ACTION_DEFAULT;
10184     player->special_action_sleeping = ACTION_DEFAULT;
10185   }
10186 }
10187
10188 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10189 {
10190   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10191   int left      = player_action & JOY_LEFT;
10192   int right     = player_action & JOY_RIGHT;
10193   int up        = player_action & JOY_UP;
10194   int down      = player_action & JOY_DOWN;
10195   int button1   = player_action & JOY_BUTTON_1;
10196   int button2   = player_action & JOY_BUTTON_2;
10197   int dx        = (left ? -1 : right ? 1 : 0);
10198   int dy        = (up   ? -1 : down  ? 1 : 0);
10199
10200   if (!player->active || tape.pausing)
10201     return 0;
10202
10203   if (player_action)
10204   {
10205     if (button1)
10206       snapped = SnapField(player, dx, dy);
10207     else
10208     {
10209       if (button2)
10210         dropped = DropElement(player);
10211
10212       moved = MovePlayer(player, dx, dy);
10213     }
10214
10215     if (tape.single_step && tape.recording && !tape.pausing)
10216     {
10217       if (button1 || (dropped && !moved))
10218       {
10219         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10220         SnapField(player, 0, 0);                /* stop snapping */
10221       }
10222     }
10223
10224     SetPlayerWaiting(player, FALSE);
10225
10226     return player_action;
10227   }
10228   else
10229   {
10230     /* no actions for this player (no input at player's configured device) */
10231
10232     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10233     SnapField(player, 0, 0);
10234     CheckGravityMovementWhenNotMoving(player);
10235
10236     if (player->MovPos == 0)
10237       SetPlayerWaiting(player, TRUE);
10238
10239     if (player->MovPos == 0)    /* needed for tape.playing */
10240       player->is_moving = FALSE;
10241
10242     player->is_dropping = FALSE;
10243     player->is_dropping_pressed = FALSE;
10244     player->drop_pressed_delay = 0;
10245
10246     return 0;
10247   }
10248 }
10249
10250 static void CheckLevelTime()
10251 {
10252   int i;
10253
10254   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10255   {
10256     if (level.native_em_level->lev->home == 0)  /* all players at home */
10257     {
10258       PlayerWins(local_player);
10259
10260       AllPlayersGone = TRUE;
10261
10262       level.native_em_level->lev->home = -1;
10263     }
10264
10265     if (level.native_em_level->ply[0]->alive == 0 &&
10266         level.native_em_level->ply[1]->alive == 0 &&
10267         level.native_em_level->ply[2]->alive == 0 &&
10268         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10269       AllPlayersGone = TRUE;
10270   }
10271
10272   if (TimeFrames >= FRAMES_PER_SECOND)
10273   {
10274     TimeFrames = 0;
10275     TapeTime++;
10276
10277     for (i = 0; i < MAX_PLAYERS; i++)
10278     {
10279       struct PlayerInfo *player = &stored_player[i];
10280
10281       if (SHIELD_ON(player))
10282       {
10283         player->shield_normal_time_left--;
10284
10285         if (player->shield_deadly_time_left > 0)
10286           player->shield_deadly_time_left--;
10287       }
10288     }
10289
10290     if (!local_player->LevelSolved && !level.use_step_counter)
10291     {
10292       TimePlayed++;
10293
10294       if (TimeLeft > 0)
10295       {
10296         TimeLeft--;
10297
10298         if (TimeLeft <= 10 && setup.time_limit)
10299           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10300
10301         DrawGameValue_Time(TimeLeft);
10302
10303         if (!TimeLeft && setup.time_limit)
10304         {
10305           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10306             level.native_em_level->lev->killed_out_of_time = TRUE;
10307           else
10308             for (i = 0; i < MAX_PLAYERS; i++)
10309               KillPlayer(&stored_player[i]);
10310         }
10311       }
10312       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10313         DrawGameValue_Time(TimePlayed);
10314
10315       level.native_em_level->lev->time =
10316         (level.time == 0 ? TimePlayed : TimeLeft);
10317     }
10318
10319     if (tape.recording || tape.playing)
10320       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10321   }
10322 }
10323
10324 void AdvanceFrameAndPlayerCounters(int player_nr)
10325 {
10326   int i;
10327
10328   /* advance frame counters (global frame counter and time frame counter) */
10329   FrameCounter++;
10330   TimeFrames++;
10331
10332   /* advance player counters (counters for move delay, move animation etc.) */
10333   for (i = 0; i < MAX_PLAYERS; i++)
10334   {
10335     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10336     int move_delay_value = stored_player[i].move_delay_value;
10337     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10338
10339     if (!advance_player_counters)       /* not all players may be affected */
10340       continue;
10341
10342 #if USE_NEW_PLAYER_ANIM
10343     if (move_frames == 0)       /* less than one move per game frame */
10344     {
10345       int stepsize = TILEX / move_delay_value;
10346       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10347       int count = (stored_player[i].is_moving ?
10348                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10349
10350       if (count % delay == 0)
10351         move_frames = 1;
10352     }
10353 #endif
10354
10355     stored_player[i].Frame += move_frames;
10356
10357     if (stored_player[i].MovPos != 0)
10358       stored_player[i].StepFrame += move_frames;
10359
10360     if (stored_player[i].move_delay > 0)
10361       stored_player[i].move_delay--;
10362
10363     /* due to bugs in previous versions, counter must count up, not down */
10364     if (stored_player[i].push_delay != -1)
10365       stored_player[i].push_delay++;
10366
10367     if (stored_player[i].drop_delay > 0)
10368       stored_player[i].drop_delay--;
10369
10370     if (stored_player[i].is_dropping_pressed)
10371       stored_player[i].drop_pressed_delay++;
10372   }
10373 }
10374
10375 void StartGameActions(boolean init_network_game, boolean record_tape,
10376                       long random_seed)
10377 {
10378   unsigned long new_random_seed = InitRND(random_seed);
10379
10380   if (record_tape)
10381     TapeStartRecording(new_random_seed);
10382
10383 #if defined(NETWORK_AVALIABLE)
10384   if (init_network_game)
10385   {
10386     SendToServer_StartPlaying();
10387
10388     return;
10389   }
10390 #endif
10391
10392   InitGame();
10393 }
10394
10395 void GameActions()
10396 {
10397   static unsigned long game_frame_delay = 0;
10398   unsigned long game_frame_delay_value;
10399   byte *recorded_player_action;
10400   byte summarized_player_action = 0;
10401   byte tape_action[MAX_PLAYERS];
10402   int i;
10403
10404   /* detect endless loops, caused by custom element programming */
10405   if (recursion_loop_detected && recursion_loop_depth == 0)
10406   {
10407     char *message = getStringCat3("Internal Error ! Element ",
10408                                   EL_NAME(recursion_loop_element),
10409                                   " caused endless loop ! Quit the game ?");
10410
10411     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10412           EL_NAME(recursion_loop_element));
10413
10414     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10415
10416     recursion_loop_detected = FALSE;    /* if game should be continued */
10417
10418     free(message);
10419
10420     return;
10421   }
10422
10423   if (game.restart_level)
10424     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
10425
10426   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10427   {
10428     if (level.native_em_level->lev->home == 0)  /* all players at home */
10429     {
10430       PlayerWins(local_player);
10431
10432       AllPlayersGone = TRUE;
10433
10434       level.native_em_level->lev->home = -1;
10435     }
10436
10437     if (level.native_em_level->ply[0]->alive == 0 &&
10438         level.native_em_level->ply[1]->alive == 0 &&
10439         level.native_em_level->ply[2]->alive == 0 &&
10440         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10441       AllPlayersGone = TRUE;
10442   }
10443
10444   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10445     GameWon();
10446
10447   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10448     TapeStop();
10449
10450   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
10451     return;
10452
10453   game_frame_delay_value =
10454     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10455
10456   if (tape.playing && tape.warp_forward && !tape.pausing)
10457     game_frame_delay_value = 0;
10458
10459   /* ---------- main game synchronization point ---------- */
10460
10461   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10462
10463   if (network_playing && !network_player_action_received)
10464   {
10465     /* try to get network player actions in time */
10466
10467 #if defined(NETWORK_AVALIABLE)
10468     /* last chance to get network player actions without main loop delay */
10469     HandleNetworking();
10470 #endif
10471
10472     /* game was quit by network peer */
10473     if (game_status != GAME_MODE_PLAYING)
10474       return;
10475
10476     if (!network_player_action_received)
10477       return;           /* failed to get network player actions in time */
10478
10479     /* do not yet reset "network_player_action_received" (for tape.pausing) */
10480   }
10481
10482   if (tape.pausing)
10483     return;
10484
10485   /* at this point we know that we really continue executing the game */
10486
10487   network_player_action_received = FALSE;
10488
10489   /* when playing tape, read previously recorded player input from tape data */
10490   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
10491
10492 #if 1
10493   /* TapePlayAction() may return NULL when toggling to "pause before death" */
10494   if (tape.pausing)
10495     return;
10496 #endif
10497
10498   if (tape.set_centered_player)
10499   {
10500     game.centered_player_nr_next = tape.centered_player_nr_next;
10501     game.set_centered_player = TRUE;
10502   }
10503
10504   for (i = 0; i < MAX_PLAYERS; i++)
10505   {
10506     summarized_player_action |= stored_player[i].action;
10507
10508     if (!network_playing)
10509       stored_player[i].effective_action = stored_player[i].action;
10510   }
10511
10512 #if defined(NETWORK_AVALIABLE)
10513   if (network_playing)
10514     SendToServer_MovePlayer(summarized_player_action);
10515 #endif
10516
10517   if (!options.network && !setup.team_mode)
10518     local_player->effective_action = summarized_player_action;
10519
10520   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
10521   {
10522     for (i = 0; i < MAX_PLAYERS; i++)
10523       stored_player[i].effective_action =
10524         (i == game.centered_player_nr ? summarized_player_action : 0);
10525   }
10526
10527   if (recorded_player_action != NULL)
10528     for (i = 0; i < MAX_PLAYERS; i++)
10529       stored_player[i].effective_action = recorded_player_action[i];
10530
10531   for (i = 0; i < MAX_PLAYERS; i++)
10532   {
10533     tape_action[i] = stored_player[i].effective_action;
10534
10535     /* (this can only happen in the R'n'D game engine) */
10536     if (tape.recording && tape_action[i] && !tape.player_participates[i])
10537       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
10538   }
10539
10540   /* only record actions from input devices, but not programmed actions */
10541   if (tape.recording)
10542     TapeRecordAction(tape_action);
10543
10544   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10545   {
10546     GameActions_EM_Main();
10547   }
10548   else
10549   {
10550     GameActions_RND();
10551   }
10552 }
10553
10554 void GameActions_EM_Main()
10555 {
10556   byte effective_action[MAX_PLAYERS];
10557   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
10558   int i;
10559
10560   for (i = 0; i < MAX_PLAYERS; i++)
10561     effective_action[i] = stored_player[i].effective_action;
10562
10563   GameActions_EM(effective_action, warp_mode);
10564
10565   CheckLevelTime();
10566
10567   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
10568 }
10569
10570 void GameActions_RND()
10571 {
10572   int magic_wall_x = 0, magic_wall_y = 0;
10573   int i, x, y, element, graphic;
10574
10575   InitPlayfieldScanModeVars();
10576
10577 #if USE_ONE_MORE_CHANGE_PER_FRAME
10578   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10579   {
10580     SCAN_PLAYFIELD(x, y)
10581     {
10582       ChangeCount[x][y] = 0;
10583       ChangeEvent[x][y] = -1;
10584     }
10585   }
10586 #endif
10587
10588   if (game.set_centered_player)
10589   {
10590     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
10591
10592     /* switching to "all players" only possible if all players fit to screen */
10593     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
10594     {
10595       game.centered_player_nr_next = game.centered_player_nr;
10596       game.set_centered_player = FALSE;
10597     }
10598
10599     /* do not switch focus to non-existing (or non-active) player */
10600     if (game.centered_player_nr_next >= 0 &&
10601         !stored_player[game.centered_player_nr_next].active)
10602     {
10603       game.centered_player_nr_next = game.centered_player_nr;
10604       game.set_centered_player = FALSE;
10605     }
10606   }
10607
10608   if (game.set_centered_player &&
10609       ScreenMovPos == 0)        /* screen currently aligned at tile position */
10610   {
10611     int sx, sy;
10612
10613     if (game.centered_player_nr_next == -1)
10614     {
10615       setScreenCenteredToAllPlayers(&sx, &sy);
10616     }
10617     else
10618     {
10619       sx = stored_player[game.centered_player_nr_next].jx;
10620       sy = stored_player[game.centered_player_nr_next].jy;
10621     }
10622
10623     game.centered_player_nr = game.centered_player_nr_next;
10624     game.set_centered_player = FALSE;
10625
10626     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
10627     DrawGameDoorValues();
10628   }
10629
10630   for (i = 0; i < MAX_PLAYERS; i++)
10631   {
10632     int actual_player_action = stored_player[i].effective_action;
10633
10634 #if 1
10635     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
10636        - rnd_equinox_tetrachloride 048
10637        - rnd_equinox_tetrachloride_ii 096
10638        - rnd_emanuel_schmieg 002
10639        - doctor_sloan_ww 001, 020
10640     */
10641     if (stored_player[i].MovPos == 0)
10642       CheckGravityMovement(&stored_player[i]);
10643 #endif
10644
10645     /* overwrite programmed action with tape action */
10646     if (stored_player[i].programmed_action)
10647       actual_player_action = stored_player[i].programmed_action;
10648
10649     PlayerActions(&stored_player[i], actual_player_action);
10650
10651     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
10652   }
10653
10654   ScrollScreen(NULL, SCROLL_GO_ON);
10655
10656   /* for backwards compatibility, the following code emulates a fixed bug that
10657      occured when pushing elements (causing elements that just made their last
10658      pushing step to already (if possible) make their first falling step in the
10659      same game frame, which is bad); this code is also needed to use the famous
10660      "spring push bug" which is used in older levels and might be wanted to be
10661      used also in newer levels, but in this case the buggy pushing code is only
10662      affecting the "spring" element and no other elements */
10663
10664   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
10665   {
10666     for (i = 0; i < MAX_PLAYERS; i++)
10667     {
10668       struct PlayerInfo *player = &stored_player[i];
10669       int x = player->jx;
10670       int y = player->jy;
10671
10672       if (player->active && player->is_pushing && player->is_moving &&
10673           IS_MOVING(x, y) &&
10674           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
10675            Feld[x][y] == EL_SPRING))
10676       {
10677         ContinueMoving(x, y);
10678
10679         /* continue moving after pushing (this is actually a bug) */
10680         if (!IS_MOVING(x, y))
10681           Stop[x][y] = FALSE;
10682       }
10683     }
10684   }
10685
10686 #if 0
10687   debug_print_timestamp(0, "start main loop profiling");
10688 #endif
10689
10690   SCAN_PLAYFIELD(x, y)
10691   {
10692     ChangeCount[x][y] = 0;
10693     ChangeEvent[x][y] = -1;
10694
10695     /* this must be handled before main playfield loop */
10696     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
10697     {
10698       MovDelay[x][y]--;
10699       if (MovDelay[x][y] <= 0)
10700         RemoveField(x, y);
10701     }
10702
10703 #if USE_NEW_SNAP_DELAY
10704     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
10705     {
10706       MovDelay[x][y]--;
10707       if (MovDelay[x][y] <= 0)
10708       {
10709         RemoveField(x, y);
10710         DrawLevelField(x, y);
10711
10712         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
10713       }
10714     }
10715 #endif
10716
10717 #if DEBUG
10718     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
10719     {
10720       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
10721       printf("GameActions(): This should never happen!\n");
10722
10723       ChangePage[x][y] = -1;
10724     }
10725 #endif
10726
10727     Stop[x][y] = FALSE;
10728     if (WasJustMoving[x][y] > 0)
10729       WasJustMoving[x][y]--;
10730     if (WasJustFalling[x][y] > 0)
10731       WasJustFalling[x][y]--;
10732     if (CheckCollision[x][y] > 0)
10733       CheckCollision[x][y]--;
10734     if (CheckImpact[x][y] > 0)
10735       CheckImpact[x][y]--;
10736
10737     GfxFrame[x][y]++;
10738
10739     /* reset finished pushing action (not done in ContinueMoving() to allow
10740        continuous pushing animation for elements with zero push delay) */
10741     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
10742     {
10743       ResetGfxAnimation(x, y);
10744       DrawLevelField(x, y);
10745     }
10746
10747 #if DEBUG
10748     if (IS_BLOCKED(x, y))
10749     {
10750       int oldx, oldy;
10751
10752       Blocked2Moving(x, y, &oldx, &oldy);
10753       if (!IS_MOVING(oldx, oldy))
10754       {
10755         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
10756         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
10757         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
10758         printf("GameActions(): This should never happen!\n");
10759       }
10760     }
10761 #endif
10762   }
10763
10764 #if 0
10765   debug_print_timestamp(0, "- time for pre-main loop:");
10766 #endif
10767
10768 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
10769   SCAN_PLAYFIELD(x, y)
10770   {
10771     element = Feld[x][y];
10772     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10773
10774 #if 1
10775     {
10776 #if 1
10777       int element2 = element;
10778       int graphic2 = graphic;
10779 #else
10780       int element2 = Feld[x][y];
10781       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
10782 #endif
10783       int last_gfx_frame = GfxFrame[x][y];
10784
10785       if (graphic_info[graphic2].anim_global_sync)
10786         GfxFrame[x][y] = FrameCounter;
10787       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
10788         GfxFrame[x][y] = CustomValue[x][y];
10789       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
10790         GfxFrame[x][y] = element_info[element2].collect_score;
10791       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
10792         GfxFrame[x][y] = ChangeDelay[x][y];
10793
10794       if (redraw && GfxFrame[x][y] != last_gfx_frame)
10795         DrawLevelGraphicAnimation(x, y, graphic2);
10796     }
10797 #else
10798     ResetGfxFrame(x, y, TRUE);
10799 #endif
10800
10801 #if 1
10802     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10803         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10804       ResetRandomAnimationValue(x, y);
10805 #endif
10806
10807 #if 1
10808     SetRandomAnimationValue(x, y);
10809 #endif
10810
10811 #if 1
10812     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10813 #endif
10814   }
10815 #endif  // -------------------- !!! TEST ONLY !!! --------------------
10816
10817 #if 0
10818   debug_print_timestamp(0, "- time for TEST loop:     -->");
10819 #endif
10820
10821   SCAN_PLAYFIELD(x, y)
10822   {
10823     element = Feld[x][y];
10824     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10825
10826     ResetGfxFrame(x, y, TRUE);
10827
10828     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10829         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10830       ResetRandomAnimationValue(x, y);
10831
10832     SetRandomAnimationValue(x, y);
10833
10834     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10835
10836     if (IS_INACTIVE(element))
10837     {
10838       if (IS_ANIMATED(graphic))
10839         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10840
10841       continue;
10842     }
10843
10844     /* this may take place after moving, so 'element' may have changed */
10845     if (IS_CHANGING(x, y) &&
10846         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10847     {
10848       int page = element_info[element].event_page_nr[CE_DELAY];
10849
10850 #if 1
10851       HandleElementChange(x, y, page);
10852 #else
10853       if (CAN_CHANGE(element))
10854         HandleElementChange(x, y, page);
10855
10856       if (HAS_ACTION(element))
10857         ExecuteCustomElementAction(x, y, element, page);
10858 #endif
10859
10860       element = Feld[x][y];
10861       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10862     }
10863
10864 #if 0   // ---------------------------------------------------------------------
10865
10866     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10867     {
10868       StartMoving(x, y);
10869
10870       element = Feld[x][y];
10871       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10872
10873       if (IS_ANIMATED(graphic) &&
10874           !IS_MOVING(x, y) &&
10875           !Stop[x][y])
10876         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10877
10878       if (IS_GEM(element) || element == EL_SP_INFOTRON)
10879         DrawTwinkleOnField(x, y);
10880     }
10881     else if (IS_MOVING(x, y))
10882       ContinueMoving(x, y);
10883     else
10884     {
10885       switch (element)
10886       {
10887         case EL_ACID:
10888         case EL_EXIT_OPEN:
10889         case EL_EM_EXIT_OPEN:
10890         case EL_SP_EXIT_OPEN:
10891         case EL_STEEL_EXIT_OPEN:
10892         case EL_EM_STEEL_EXIT_OPEN:
10893         case EL_SP_TERMINAL:
10894         case EL_SP_TERMINAL_ACTIVE:
10895         case EL_EXTRA_TIME:
10896         case EL_SHIELD_NORMAL:
10897         case EL_SHIELD_DEADLY:
10898           if (IS_ANIMATED(graphic))
10899             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10900           break;
10901
10902         case EL_DYNAMITE_ACTIVE:
10903         case EL_EM_DYNAMITE_ACTIVE:
10904         case EL_DYNABOMB_PLAYER_1_ACTIVE:
10905         case EL_DYNABOMB_PLAYER_2_ACTIVE:
10906         case EL_DYNABOMB_PLAYER_3_ACTIVE:
10907         case EL_DYNABOMB_PLAYER_4_ACTIVE:
10908         case EL_SP_DISK_RED_ACTIVE:
10909           CheckDynamite(x, y);
10910           break;
10911
10912         case EL_AMOEBA_GROWING:
10913           AmoebeWaechst(x, y);
10914           break;
10915
10916         case EL_AMOEBA_SHRINKING:
10917           AmoebaDisappearing(x, y);
10918           break;
10919
10920 #if !USE_NEW_AMOEBA_CODE
10921         case EL_AMOEBA_WET:
10922         case EL_AMOEBA_DRY:
10923         case EL_AMOEBA_FULL:
10924         case EL_BD_AMOEBA:
10925         case EL_EMC_DRIPPER:
10926           AmoebeAbleger(x, y);
10927           break;
10928 #endif
10929
10930         case EL_GAME_OF_LIFE:
10931         case EL_BIOMAZE:
10932           Life(x, y);
10933           break;
10934
10935         case EL_EXIT_CLOSED:
10936           CheckExit(x, y);
10937           break;
10938
10939         case EL_EM_EXIT_CLOSED:
10940           CheckExitEM(x, y);
10941           break;
10942
10943         case EL_STEEL_EXIT_CLOSED:
10944           CheckExitSteel(x, y);
10945           break;
10946
10947         case EL_EM_STEEL_EXIT_CLOSED:
10948           CheckExitSteelEM(x, y);
10949           break;
10950
10951         case EL_SP_EXIT_CLOSED:
10952           CheckExitSP(x, y);
10953           break;
10954
10955         case EL_EXPANDABLE_WALL_GROWING:
10956         case EL_EXPANDABLE_STEELWALL_GROWING:
10957           MauerWaechst(x, y);
10958           break;
10959
10960         case EL_EXPANDABLE_WALL:
10961         case EL_EXPANDABLE_WALL_HORIZONTAL:
10962         case EL_EXPANDABLE_WALL_VERTICAL:
10963         case EL_EXPANDABLE_WALL_ANY:
10964         case EL_BD_EXPANDABLE_WALL:
10965           MauerAbleger(x, y);
10966           break;
10967
10968         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
10969         case EL_EXPANDABLE_STEELWALL_VERTICAL:
10970         case EL_EXPANDABLE_STEELWALL_ANY:
10971           MauerAblegerStahl(x, y);
10972           break;
10973
10974         case EL_FLAMES:
10975           CheckForDragon(x, y);
10976           break;
10977
10978         case EL_EXPLOSION:
10979           break;
10980
10981         case EL_ELEMENT_SNAPPING:
10982         case EL_DIAGONAL_SHRINKING:
10983         case EL_DIAGONAL_GROWING:
10984         {
10985           graphic =
10986             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10987
10988           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10989           break;
10990         }
10991
10992         default:
10993           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10994             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10995           break;
10996       }
10997     }
10998
10999 #else   // ---------------------------------------------------------------------
11000
11001     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11002     {
11003       StartMoving(x, y);
11004
11005       element = Feld[x][y];
11006       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11007
11008       if (IS_ANIMATED(graphic) &&
11009           !IS_MOVING(x, y) &&
11010           !Stop[x][y])
11011         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11012
11013       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11014         DrawTwinkleOnField(x, y);
11015     }
11016     else if ((element == EL_ACID ||
11017               element == EL_EXIT_OPEN ||
11018               element == EL_EM_EXIT_OPEN ||
11019               element == EL_SP_EXIT_OPEN ||
11020               element == EL_STEEL_EXIT_OPEN ||
11021               element == EL_EM_STEEL_EXIT_OPEN ||
11022               element == EL_SP_TERMINAL ||
11023               element == EL_SP_TERMINAL_ACTIVE ||
11024               element == EL_EXTRA_TIME ||
11025               element == EL_SHIELD_NORMAL ||
11026               element == EL_SHIELD_DEADLY) &&
11027              IS_ANIMATED(graphic))
11028       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11029     else if (IS_MOVING(x, y))
11030       ContinueMoving(x, y);
11031     else if (IS_ACTIVE_BOMB(element))
11032       CheckDynamite(x, y);
11033     else if (element == EL_AMOEBA_GROWING)
11034       AmoebeWaechst(x, y);
11035     else if (element == EL_AMOEBA_SHRINKING)
11036       AmoebaDisappearing(x, y);
11037
11038 #if !USE_NEW_AMOEBA_CODE
11039     else if (IS_AMOEBALIVE(element))
11040       AmoebeAbleger(x, y);
11041 #endif
11042
11043     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11044       Life(x, y);
11045     else if (element == EL_EXIT_CLOSED)
11046       CheckExit(x, y);
11047     else if (element == EL_EM_EXIT_CLOSED)
11048       CheckExitEM(x, y);
11049     else if (element == EL_STEEL_EXIT_CLOSED)
11050       CheckExitSteel(x, y);
11051     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11052       CheckExitSteelEM(x, y);
11053     else if (element == EL_SP_EXIT_CLOSED)
11054       CheckExitSP(x, y);
11055     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11056              element == EL_EXPANDABLE_STEELWALL_GROWING)
11057       MauerWaechst(x, y);
11058     else if (element == EL_EXPANDABLE_WALL ||
11059              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11060              element == EL_EXPANDABLE_WALL_VERTICAL ||
11061              element == EL_EXPANDABLE_WALL_ANY ||
11062              element == EL_BD_EXPANDABLE_WALL)
11063       MauerAbleger(x, y);
11064     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11065              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11066              element == EL_EXPANDABLE_STEELWALL_ANY)
11067       MauerAblegerStahl(x, y);
11068     else if (element == EL_FLAMES)
11069       CheckForDragon(x, y);
11070     else if (element == EL_EXPLOSION)
11071       ; /* drawing of correct explosion animation is handled separately */
11072     else if (element == EL_ELEMENT_SNAPPING ||
11073              element == EL_DIAGONAL_SHRINKING ||
11074              element == EL_DIAGONAL_GROWING)
11075     {
11076       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11077
11078       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11079     }
11080     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11081       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11082
11083 #endif  // ---------------------------------------------------------------------
11084
11085     if (IS_BELT_ACTIVE(element))
11086       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11087
11088     if (game.magic_wall_active)
11089     {
11090       int jx = local_player->jx, jy = local_player->jy;
11091
11092       /* play the element sound at the position nearest to the player */
11093       if ((element == EL_MAGIC_WALL_FULL ||
11094            element == EL_MAGIC_WALL_ACTIVE ||
11095            element == EL_MAGIC_WALL_EMPTYING ||
11096            element == EL_BD_MAGIC_WALL_FULL ||
11097            element == EL_BD_MAGIC_WALL_ACTIVE ||
11098            element == EL_BD_MAGIC_WALL_EMPTYING ||
11099            element == EL_DC_MAGIC_WALL_FULL ||
11100            element == EL_DC_MAGIC_WALL_ACTIVE ||
11101            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11102           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11103       {
11104         magic_wall_x = x;
11105         magic_wall_y = y;
11106       }
11107     }
11108   }
11109
11110 #if 0
11111   debug_print_timestamp(0, "- time for MAIN loop:     -->");
11112 #endif
11113
11114 #if USE_NEW_AMOEBA_CODE
11115   /* new experimental amoeba growth stuff */
11116   if (!(FrameCounter % 8))
11117   {
11118     static unsigned long random = 1684108901;
11119
11120     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11121     {
11122       x = RND(lev_fieldx);
11123       y = RND(lev_fieldy);
11124       element = Feld[x][y];
11125
11126       if (!IS_PLAYER(x,y) &&
11127           (element == EL_EMPTY ||
11128            CAN_GROW_INTO(element) ||
11129            element == EL_QUICKSAND_EMPTY ||
11130            element == EL_QUICKSAND_FAST_EMPTY ||
11131            element == EL_ACID_SPLASH_LEFT ||
11132            element == EL_ACID_SPLASH_RIGHT))
11133       {
11134         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11135             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11136             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11137             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11138           Feld[x][y] = EL_AMOEBA_DROP;
11139       }
11140
11141       random = random * 129 + 1;
11142     }
11143   }
11144 #endif
11145
11146 #if 0
11147   if (game.explosions_delayed)
11148 #endif
11149   {
11150     game.explosions_delayed = FALSE;
11151
11152     SCAN_PLAYFIELD(x, y)
11153     {
11154       element = Feld[x][y];
11155
11156       if (ExplodeField[x][y])
11157         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11158       else if (element == EL_EXPLOSION)
11159         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11160
11161       ExplodeField[x][y] = EX_TYPE_NONE;
11162     }
11163
11164     game.explosions_delayed = TRUE;
11165   }
11166
11167   if (game.magic_wall_active)
11168   {
11169     if (!(game.magic_wall_time_left % 4))
11170     {
11171       int element = Feld[magic_wall_x][magic_wall_y];
11172
11173       if (element == EL_BD_MAGIC_WALL_FULL ||
11174           element == EL_BD_MAGIC_WALL_ACTIVE ||
11175           element == EL_BD_MAGIC_WALL_EMPTYING)
11176         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11177       else if (element == EL_DC_MAGIC_WALL_FULL ||
11178                element == EL_DC_MAGIC_WALL_ACTIVE ||
11179                element == EL_DC_MAGIC_WALL_EMPTYING)
11180         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11181       else
11182         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11183     }
11184
11185     if (game.magic_wall_time_left > 0)
11186     {
11187       game.magic_wall_time_left--;
11188       if (!game.magic_wall_time_left)
11189       {
11190         SCAN_PLAYFIELD(x, y)
11191         {
11192           element = Feld[x][y];
11193
11194           if (element == EL_MAGIC_WALL_ACTIVE ||
11195               element == EL_MAGIC_WALL_FULL)
11196           {
11197             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11198             DrawLevelField(x, y);
11199           }
11200           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11201                    element == EL_BD_MAGIC_WALL_FULL)
11202           {
11203             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11204             DrawLevelField(x, y);
11205           }
11206           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11207                    element == EL_DC_MAGIC_WALL_FULL)
11208           {
11209             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11210             DrawLevelField(x, y);
11211           }
11212         }
11213
11214         game.magic_wall_active = FALSE;
11215       }
11216     }
11217   }
11218
11219   if (game.light_time_left > 0)
11220   {
11221     game.light_time_left--;
11222
11223     if (game.light_time_left == 0)
11224       RedrawAllLightSwitchesAndInvisibleElements();
11225   }
11226
11227   if (game.timegate_time_left > 0)
11228   {
11229     game.timegate_time_left--;
11230
11231     if (game.timegate_time_left == 0)
11232       CloseAllOpenTimegates();
11233   }
11234
11235   if (game.lenses_time_left > 0)
11236   {
11237     game.lenses_time_left--;
11238
11239     if (game.lenses_time_left == 0)
11240       RedrawAllInvisibleElementsForLenses();
11241   }
11242
11243   if (game.magnify_time_left > 0)
11244   {
11245     game.magnify_time_left--;
11246
11247     if (game.magnify_time_left == 0)
11248       RedrawAllInvisibleElementsForMagnifier();
11249   }
11250
11251   for (i = 0; i < MAX_PLAYERS; i++)
11252   {
11253     struct PlayerInfo *player = &stored_player[i];
11254
11255     if (SHIELD_ON(player))
11256     {
11257       if (player->shield_deadly_time_left)
11258         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11259       else if (player->shield_normal_time_left)
11260         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11261     }
11262   }
11263
11264   CheckLevelTime();
11265
11266   DrawAllPlayers();
11267   PlayAllPlayersSound();
11268
11269   if (options.debug)                    /* calculate frames per second */
11270   {
11271     static unsigned long fps_counter = 0;
11272     static int fps_frames = 0;
11273     unsigned long fps_delay_ms = Counter() - fps_counter;
11274
11275     fps_frames++;
11276
11277     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11278     {
11279       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11280
11281       fps_frames = 0;
11282       fps_counter = Counter();
11283     }
11284
11285     redraw_mask |= REDRAW_FPS;
11286   }
11287
11288   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11289
11290   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11291   {
11292     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11293
11294     local_player->show_envelope = 0;
11295   }
11296
11297 #if 0
11298   debug_print_timestamp(0, "stop main loop profiling ");
11299   printf("----------------------------------------------------------\n");
11300 #endif
11301
11302   /* use random number generator in every frame to make it less predictable */
11303   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11304     RND(1);
11305 }
11306
11307 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11308 {
11309   int min_x = x, min_y = y, max_x = x, max_y = y;
11310   int i;
11311
11312   for (i = 0; i < MAX_PLAYERS; i++)
11313   {
11314     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11315
11316     if (!stored_player[i].active || &stored_player[i] == player)
11317       continue;
11318
11319     min_x = MIN(min_x, jx);
11320     min_y = MIN(min_y, jy);
11321     max_x = MAX(max_x, jx);
11322     max_y = MAX(max_y, jy);
11323   }
11324
11325   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11326 }
11327
11328 static boolean AllPlayersInVisibleScreen()
11329 {
11330   int i;
11331
11332   for (i = 0; i < MAX_PLAYERS; i++)
11333   {
11334     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11335
11336     if (!stored_player[i].active)
11337       continue;
11338
11339     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11340       return FALSE;
11341   }
11342
11343   return TRUE;
11344 }
11345
11346 void ScrollLevel(int dx, int dy)
11347 {
11348 #if 1
11349   static Bitmap *bitmap_db_field2 = NULL;
11350   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11351   int x, y;
11352 #else
11353   int i, x, y;
11354 #endif
11355
11356 #if 0
11357   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11358   /* only horizontal XOR vertical scroll direction allowed */
11359   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11360     return;
11361 #endif
11362
11363 #if 1
11364   if (bitmap_db_field2 == NULL)
11365     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11366
11367   /* needed when blitting directly to same bitmap -- should not be needed with
11368      recent SDL libraries, but apparently does not work in 1.2.11 directly */
11369   BlitBitmap(drawto_field, bitmap_db_field2,
11370              FX + TILEX * (dx == -1) - softscroll_offset,
11371              FY + TILEY * (dy == -1) - softscroll_offset,
11372              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11373              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11374              FX + TILEX * (dx == 1) - softscroll_offset,
11375              FY + TILEY * (dy == 1) - softscroll_offset);
11376   BlitBitmap(bitmap_db_field2, drawto_field,
11377              FX + TILEX * (dx == 1) - softscroll_offset,
11378              FY + TILEY * (dy == 1) - softscroll_offset,
11379              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11380              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11381              FX + TILEX * (dx == 1) - softscroll_offset,
11382              FY + TILEY * (dy == 1) - softscroll_offset);
11383
11384 #else
11385
11386 #if 1
11387   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11388   int xsize = (BX2 - BX1 + 1);
11389   int ysize = (BY2 - BY1 + 1);
11390   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11391   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11392   int step  = (start < end ? +1 : -1);
11393
11394   for (i = start; i != end; i += step)
11395   {
11396     BlitBitmap(drawto_field, drawto_field,
11397                FX + TILEX * (dx != 0 ? i + step : 0),
11398                FY + TILEY * (dy != 0 ? i + step : 0),
11399                TILEX * (dx != 0 ? 1 : xsize),
11400                TILEY * (dy != 0 ? 1 : ysize),
11401                FX + TILEX * (dx != 0 ? i : 0),
11402                FY + TILEY * (dy != 0 ? i : 0));
11403   }
11404
11405 #else
11406
11407   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11408
11409   BlitBitmap(drawto_field, drawto_field,
11410              FX + TILEX * (dx == -1) - softscroll_offset,
11411              FY + TILEY * (dy == -1) - softscroll_offset,
11412              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11413              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11414              FX + TILEX * (dx == 1) - softscroll_offset,
11415              FY + TILEY * (dy == 1) - softscroll_offset);
11416 #endif
11417 #endif
11418
11419   if (dx != 0)
11420   {
11421     x = (dx == 1 ? BX1 : BX2);
11422     for (y = BY1; y <= BY2; y++)
11423       DrawScreenField(x, y);
11424   }
11425
11426   if (dy != 0)
11427   {
11428     y = (dy == 1 ? BY1 : BY2);
11429     for (x = BX1; x <= BX2; x++)
11430       DrawScreenField(x, y);
11431   }
11432
11433   redraw_mask |= REDRAW_FIELD;
11434 }
11435
11436 static boolean canFallDown(struct PlayerInfo *player)
11437 {
11438   int jx = player->jx, jy = player->jy;
11439
11440   return (IN_LEV_FIELD(jx, jy + 1) &&
11441           (IS_FREE(jx, jy + 1) ||
11442            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11443           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11444           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11445 }
11446
11447 static boolean canPassField(int x, int y, int move_dir)
11448 {
11449   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11450   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11451   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11452   int nextx = x + dx;
11453   int nexty = y + dy;
11454   int element = Feld[x][y];
11455
11456   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11457           !CAN_MOVE(element) &&
11458           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11459           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11460           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11461 }
11462
11463 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11464 {
11465   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11466   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11467   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11468   int newx = x + dx;
11469   int newy = y + dy;
11470
11471   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11472           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11473           (IS_DIGGABLE(Feld[newx][newy]) ||
11474            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11475            canPassField(newx, newy, move_dir)));
11476 }
11477
11478 static void CheckGravityMovement(struct PlayerInfo *player)
11479 {
11480 #if USE_PLAYER_GRAVITY
11481   if (player->gravity && !player->programmed_action)
11482 #else
11483   if (game.gravity && !player->programmed_action)
11484 #endif
11485   {
11486     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11487     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11488     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11489     int jx = player->jx, jy = player->jy;
11490     boolean player_is_moving_to_valid_field =
11491       (!player_is_snapping &&
11492        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11493         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11494     boolean player_can_fall_down = canFallDown(player);
11495
11496     if (player_can_fall_down &&
11497         !player_is_moving_to_valid_field)
11498       player->programmed_action = MV_DOWN;
11499   }
11500 }
11501
11502 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11503 {
11504   return CheckGravityMovement(player);
11505
11506 #if USE_PLAYER_GRAVITY
11507   if (player->gravity && !player->programmed_action)
11508 #else
11509   if (game.gravity && !player->programmed_action)
11510 #endif
11511   {
11512     int jx = player->jx, jy = player->jy;
11513     boolean field_under_player_is_free =
11514       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11515     boolean player_is_standing_on_valid_field =
11516       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11517        (IS_WALKABLE(Feld[jx][jy]) &&
11518         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11519
11520     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11521       player->programmed_action = MV_DOWN;
11522   }
11523 }
11524
11525 /*
11526   MovePlayerOneStep()
11527   -----------------------------------------------------------------------------
11528   dx, dy:               direction (non-diagonal) to try to move the player to
11529   real_dx, real_dy:     direction as read from input device (can be diagonal)
11530 */
11531
11532 boolean MovePlayerOneStep(struct PlayerInfo *player,
11533                           int dx, int dy, int real_dx, int real_dy)
11534 {
11535   int jx = player->jx, jy = player->jy;
11536   int new_jx = jx + dx, new_jy = jy + dy;
11537 #if !USE_FIXED_DONT_RUN_INTO
11538   int element;
11539 #endif
11540   int can_move;
11541   boolean player_can_move = !player->cannot_move;
11542
11543   if (!player->active || (!dx && !dy))
11544     return MP_NO_ACTION;
11545
11546   player->MovDir = (dx < 0 ? MV_LEFT :
11547                     dx > 0 ? MV_RIGHT :
11548                     dy < 0 ? MV_UP :
11549                     dy > 0 ? MV_DOWN :  MV_NONE);
11550
11551   if (!IN_LEV_FIELD(new_jx, new_jy))
11552     return MP_NO_ACTION;
11553
11554   if (!player_can_move)
11555   {
11556     if (player->MovPos == 0)
11557     {
11558       player->is_moving = FALSE;
11559       player->is_digging = FALSE;
11560       player->is_collecting = FALSE;
11561       player->is_snapping = FALSE;
11562       player->is_pushing = FALSE;
11563     }
11564   }
11565
11566 #if 1
11567   if (!options.network && game.centered_player_nr == -1 &&
11568       !AllPlayersInSight(player, new_jx, new_jy))
11569     return MP_NO_ACTION;
11570 #else
11571   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
11572     return MP_NO_ACTION;
11573 #endif
11574
11575 #if !USE_FIXED_DONT_RUN_INTO
11576   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
11577
11578   /* (moved to DigField()) */
11579   if (player_can_move && DONT_RUN_INTO(element))
11580   {
11581     if (element == EL_ACID && dx == 0 && dy == 1)
11582     {
11583       SplashAcid(new_jx, new_jy);
11584       Feld[jx][jy] = EL_PLAYER_1;
11585       InitMovingField(jx, jy, MV_DOWN);
11586       Store[jx][jy] = EL_ACID;
11587       ContinueMoving(jx, jy);
11588       BuryPlayer(player);
11589     }
11590     else
11591       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11592
11593     return MP_MOVING;
11594   }
11595 #endif
11596
11597   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11598   if (can_move != MP_MOVING)
11599     return can_move;
11600
11601   /* check if DigField() has caused relocation of the player */
11602   if (player->jx != jx || player->jy != jy)
11603     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11604
11605   StorePlayer[jx][jy] = 0;
11606   player->last_jx = jx;
11607   player->last_jy = jy;
11608   player->jx = new_jx;
11609   player->jy = new_jy;
11610   StorePlayer[new_jx][new_jy] = player->element_nr;
11611
11612   if (player->move_delay_value_next != -1)
11613   {
11614     player->move_delay_value = player->move_delay_value_next;
11615     player->move_delay_value_next = -1;
11616   }
11617
11618   player->MovPos =
11619     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11620
11621   player->step_counter++;
11622
11623   PlayerVisit[jx][jy] = FrameCounter;
11624
11625 #if USE_UFAST_PLAYER_EXIT_BUGFIX
11626   player->is_moving = TRUE;
11627 #endif
11628
11629 #if 1
11630   /* should better be called in MovePlayer(), but this breaks some tapes */
11631   ScrollPlayer(player, SCROLL_INIT);
11632 #endif
11633
11634   return MP_MOVING;
11635 }
11636
11637 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
11638 {
11639   int jx = player->jx, jy = player->jy;
11640   int old_jx = jx, old_jy = jy;
11641   int moved = MP_NO_ACTION;
11642
11643   if (!player->active)
11644     return FALSE;
11645
11646   if (!dx && !dy)
11647   {
11648     if (player->MovPos == 0)
11649     {
11650       player->is_moving = FALSE;
11651       player->is_digging = FALSE;
11652       player->is_collecting = FALSE;
11653       player->is_snapping = FALSE;
11654       player->is_pushing = FALSE;
11655     }
11656
11657     return FALSE;
11658   }
11659
11660   if (player->move_delay > 0)
11661     return FALSE;
11662
11663   player->move_delay = -1;              /* set to "uninitialized" value */
11664
11665   /* store if player is automatically moved to next field */
11666   player->is_auto_moving = (player->programmed_action != MV_NONE);
11667
11668   /* remove the last programmed player action */
11669   player->programmed_action = 0;
11670
11671   if (player->MovPos)
11672   {
11673     /* should only happen if pre-1.2 tape recordings are played */
11674     /* this is only for backward compatibility */
11675
11676     int original_move_delay_value = player->move_delay_value;
11677
11678 #if DEBUG
11679     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
11680            tape.counter);
11681 #endif
11682
11683     /* scroll remaining steps with finest movement resolution */
11684     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
11685
11686     while (player->MovPos)
11687     {
11688       ScrollPlayer(player, SCROLL_GO_ON);
11689       ScrollScreen(NULL, SCROLL_GO_ON);
11690
11691       AdvanceFrameAndPlayerCounters(player->index_nr);
11692
11693       DrawAllPlayers();
11694       BackToFront();
11695     }
11696
11697     player->move_delay_value = original_move_delay_value;
11698   }
11699
11700   player->is_active = FALSE;
11701
11702   if (player->last_move_dir & MV_HORIZONTAL)
11703   {
11704     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
11705       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
11706   }
11707   else
11708   {
11709     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
11710       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
11711   }
11712
11713 #if USE_FIXED_BORDER_RUNNING_GFX
11714   if (!moved && !player->is_active)
11715   {
11716     player->is_moving = FALSE;
11717     player->is_digging = FALSE;
11718     player->is_collecting = FALSE;
11719     player->is_snapping = FALSE;
11720     player->is_pushing = FALSE;
11721   }
11722 #endif
11723
11724   jx = player->jx;
11725   jy = player->jy;
11726
11727 #if 1
11728   if (moved & MP_MOVING && !ScreenMovPos &&
11729       (player->index_nr == game.centered_player_nr ||
11730        game.centered_player_nr == -1))
11731 #else
11732   if (moved & MP_MOVING && !ScreenMovPos &&
11733       (player == local_player || !options.network))
11734 #endif
11735   {
11736     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
11737     int offset = (setup.scroll_delay ? 3 : 0);
11738
11739     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11740     {
11741       /* actual player has left the screen -- scroll in that direction */
11742       if (jx != old_jx)         /* player has moved horizontally */
11743         scroll_x += (jx - old_jx);
11744       else                      /* player has moved vertically */
11745         scroll_y += (jy - old_jy);
11746     }
11747     else
11748     {
11749       if (jx != old_jx)         /* player has moved horizontally */
11750       {
11751         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
11752             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
11753           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
11754
11755         /* don't scroll over playfield boundaries */
11756         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
11757           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
11758
11759         /* don't scroll more than one field at a time */
11760         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
11761
11762         /* don't scroll against the player's moving direction */
11763         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
11764             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
11765           scroll_x = old_scroll_x;
11766       }
11767       else                      /* player has moved vertically */
11768       {
11769         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
11770             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
11771           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
11772
11773         /* don't scroll over playfield boundaries */
11774         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
11775           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
11776
11777         /* don't scroll more than one field at a time */
11778         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
11779
11780         /* don't scroll against the player's moving direction */
11781         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
11782             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
11783           scroll_y = old_scroll_y;
11784       }
11785     }
11786
11787     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
11788     {
11789 #if 1
11790       if (!options.network && game.centered_player_nr == -1 &&
11791           !AllPlayersInVisibleScreen())
11792       {
11793         scroll_x = old_scroll_x;
11794         scroll_y = old_scroll_y;
11795       }
11796       else
11797 #else
11798       if (!options.network && !AllPlayersInVisibleScreen())
11799       {
11800         scroll_x = old_scroll_x;
11801         scroll_y = old_scroll_y;
11802       }
11803       else
11804 #endif
11805       {
11806         ScrollScreen(player, SCROLL_INIT);
11807         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
11808       }
11809     }
11810   }
11811
11812   player->StepFrame = 0;
11813
11814   if (moved & MP_MOVING)
11815   {
11816     if (old_jx != jx && old_jy == jy)
11817       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
11818     else if (old_jx == jx && old_jy != jy)
11819       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
11820
11821     DrawLevelField(jx, jy);     /* for "crumbled sand" */
11822
11823     player->last_move_dir = player->MovDir;
11824     player->is_moving = TRUE;
11825     player->is_snapping = FALSE;
11826     player->is_switching = FALSE;
11827     player->is_dropping = FALSE;
11828     player->is_dropping_pressed = FALSE;
11829     player->drop_pressed_delay = 0;
11830
11831 #if 0
11832     /* should better be called here than above, but this breaks some tapes */
11833     ScrollPlayer(player, SCROLL_INIT);
11834 #endif
11835   }
11836   else
11837   {
11838     CheckGravityMovementWhenNotMoving(player);
11839
11840     player->is_moving = FALSE;
11841
11842     /* at this point, the player is allowed to move, but cannot move right now
11843        (e.g. because of something blocking the way) -- ensure that the player
11844        is also allowed to move in the next frame (in old versions before 3.1.1,
11845        the player was forced to wait again for eight frames before next try) */
11846
11847     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11848       player->move_delay = 0;   /* allow direct movement in the next frame */
11849   }
11850
11851   if (player->move_delay == -1)         /* not yet initialized by DigField() */
11852     player->move_delay = player->move_delay_value;
11853
11854   if (game.engine_version < VERSION_IDENT(3,0,7,0))
11855   {
11856     TestIfPlayerTouchesBadThing(jx, jy);
11857     TestIfPlayerTouchesCustomElement(jx, jy);
11858   }
11859
11860   if (!player->active)
11861     RemovePlayer(player);
11862
11863   return moved;
11864 }
11865
11866 void ScrollPlayer(struct PlayerInfo *player, int mode)
11867 {
11868   int jx = player->jx, jy = player->jy;
11869   int last_jx = player->last_jx, last_jy = player->last_jy;
11870   int move_stepsize = TILEX / player->move_delay_value;
11871
11872 #if USE_NEW_PLAYER_SPEED
11873   if (!player->active)
11874     return;
11875
11876   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
11877     return;
11878 #else
11879   if (!player->active || player->MovPos == 0)
11880     return;
11881 #endif
11882
11883   if (mode == SCROLL_INIT)
11884   {
11885     player->actual_frame_counter = FrameCounter;
11886     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11887
11888     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
11889         Feld[last_jx][last_jy] == EL_EMPTY)
11890     {
11891       int last_field_block_delay = 0;   /* start with no blocking at all */
11892       int block_delay_adjustment = player->block_delay_adjustment;
11893
11894       /* if player blocks last field, add delay for exactly one move */
11895       if (player->block_last_field)
11896       {
11897         last_field_block_delay += player->move_delay_value;
11898
11899         /* when blocking enabled, prevent moving up despite gravity */
11900 #if USE_PLAYER_GRAVITY
11901         if (player->gravity && player->MovDir == MV_UP)
11902           block_delay_adjustment = -1;
11903 #else
11904         if (game.gravity && player->MovDir == MV_UP)
11905           block_delay_adjustment = -1;
11906 #endif
11907       }
11908
11909       /* add block delay adjustment (also possible when not blocking) */
11910       last_field_block_delay += block_delay_adjustment;
11911
11912       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
11913       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
11914     }
11915
11916 #if USE_NEW_PLAYER_SPEED
11917     if (player->MovPos != 0)    /* player has not yet reached destination */
11918       return;
11919 #else
11920     return;
11921 #endif
11922   }
11923   else if (!FrameReached(&player->actual_frame_counter, 1))
11924     return;
11925
11926 #if USE_NEW_PLAYER_SPEED
11927   if (player->MovPos != 0)
11928   {
11929     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11930     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11931
11932     /* before DrawPlayer() to draw correct player graphic for this case */
11933     if (player->MovPos == 0)
11934       CheckGravityMovement(player);
11935   }
11936 #else
11937   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11938   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11939
11940   /* before DrawPlayer() to draw correct player graphic for this case */
11941   if (player->MovPos == 0)
11942     CheckGravityMovement(player);
11943 #endif
11944
11945   if (player->MovPos == 0)      /* player reached destination field */
11946   {
11947     if (player->move_delay_reset_counter > 0)
11948     {
11949       player->move_delay_reset_counter--;
11950
11951       if (player->move_delay_reset_counter == 0)
11952       {
11953         /* continue with normal speed after quickly moving through gate */
11954         HALVE_PLAYER_SPEED(player);
11955
11956         /* be able to make the next move without delay */
11957         player->move_delay = 0;
11958       }
11959     }
11960
11961     player->last_jx = jx;
11962     player->last_jy = jy;
11963
11964     if (Feld[jx][jy] == EL_EXIT_OPEN ||
11965         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
11966         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
11967         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
11968         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
11969         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
11970     {
11971       DrawPlayer(player);       /* needed here only to cleanup last field */
11972       RemovePlayer(player);
11973
11974       if (local_player->friends_still_needed == 0 ||
11975           IS_SP_ELEMENT(Feld[jx][jy]))
11976         PlayerWins(player);
11977     }
11978
11979     /* this breaks one level: "machine", level 000 */
11980     {
11981       int move_direction = player->MovDir;
11982       int enter_side = MV_DIR_OPPOSITE(move_direction);
11983       int leave_side = move_direction;
11984       int old_jx = last_jx;
11985       int old_jy = last_jy;
11986       int old_element = Feld[old_jx][old_jy];
11987       int new_element = Feld[jx][jy];
11988
11989       if (IS_CUSTOM_ELEMENT(old_element))
11990         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
11991                                    CE_LEFT_BY_PLAYER,
11992                                    player->index_bit, leave_side);
11993
11994       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
11995                                           CE_PLAYER_LEAVES_X,
11996                                           player->index_bit, leave_side);
11997
11998       if (IS_CUSTOM_ELEMENT(new_element))
11999         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12000                                    player->index_bit, enter_side);
12001
12002       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12003                                           CE_PLAYER_ENTERS_X,
12004                                           player->index_bit, enter_side);
12005
12006       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12007                                         CE_MOVE_OF_X, move_direction);
12008     }
12009
12010     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12011     {
12012       TestIfPlayerTouchesBadThing(jx, jy);
12013       TestIfPlayerTouchesCustomElement(jx, jy);
12014
12015       /* needed because pushed element has not yet reached its destination,
12016          so it would trigger a change event at its previous field location */
12017       if (!player->is_pushing)
12018         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12019
12020       if (!player->active)
12021         RemovePlayer(player);
12022     }
12023
12024     if (!local_player->LevelSolved && level.use_step_counter)
12025     {
12026       int i;
12027
12028       TimePlayed++;
12029
12030       if (TimeLeft > 0)
12031       {
12032         TimeLeft--;
12033
12034         if (TimeLeft <= 10 && setup.time_limit)
12035           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12036
12037         DrawGameValue_Time(TimeLeft);
12038
12039         if (!TimeLeft && setup.time_limit)
12040           for (i = 0; i < MAX_PLAYERS; i++)
12041             KillPlayer(&stored_player[i]);
12042       }
12043       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12044         DrawGameValue_Time(TimePlayed);
12045     }
12046
12047     if (tape.single_step && tape.recording && !tape.pausing &&
12048         !player->programmed_action)
12049       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12050   }
12051 }
12052
12053 void ScrollScreen(struct PlayerInfo *player, int mode)
12054 {
12055   static unsigned long screen_frame_counter = 0;
12056
12057   if (mode == SCROLL_INIT)
12058   {
12059     /* set scrolling step size according to actual player's moving speed */
12060     ScrollStepSize = TILEX / player->move_delay_value;
12061
12062     screen_frame_counter = FrameCounter;
12063     ScreenMovDir = player->MovDir;
12064     ScreenMovPos = player->MovPos;
12065     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12066     return;
12067   }
12068   else if (!FrameReached(&screen_frame_counter, 1))
12069     return;
12070
12071   if (ScreenMovPos)
12072   {
12073     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12074     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12075     redraw_mask |= REDRAW_FIELD;
12076   }
12077   else
12078     ScreenMovDir = MV_NONE;
12079 }
12080
12081 void TestIfPlayerTouchesCustomElement(int x, int y)
12082 {
12083   static int xy[4][2] =
12084   {
12085     { 0, -1 },
12086     { -1, 0 },
12087     { +1, 0 },
12088     { 0, +1 }
12089   };
12090   static int trigger_sides[4][2] =
12091   {
12092     /* center side       border side */
12093     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12094     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12095     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12096     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12097   };
12098   static int touch_dir[4] =
12099   {
12100     MV_LEFT | MV_RIGHT,
12101     MV_UP   | MV_DOWN,
12102     MV_UP   | MV_DOWN,
12103     MV_LEFT | MV_RIGHT
12104   };
12105   int center_element = Feld[x][y];      /* should always be non-moving! */
12106   int i;
12107
12108   for (i = 0; i < NUM_DIRECTIONS; i++)
12109   {
12110     int xx = x + xy[i][0];
12111     int yy = y + xy[i][1];
12112     int center_side = trigger_sides[i][0];
12113     int border_side = trigger_sides[i][1];
12114     int border_element;
12115
12116     if (!IN_LEV_FIELD(xx, yy))
12117       continue;
12118
12119     if (IS_PLAYER(x, y))
12120     {
12121       struct PlayerInfo *player = PLAYERINFO(x, y);
12122
12123       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12124         border_element = Feld[xx][yy];          /* may be moving! */
12125       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12126         border_element = Feld[xx][yy];
12127       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12128         border_element = MovingOrBlocked2Element(xx, yy);
12129       else
12130         continue;               /* center and border element do not touch */
12131
12132       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12133                                  player->index_bit, border_side);
12134       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12135                                           CE_PLAYER_TOUCHES_X,
12136                                           player->index_bit, border_side);
12137     }
12138     else if (IS_PLAYER(xx, yy))
12139     {
12140       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12141
12142       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12143       {
12144         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12145           continue;             /* center and border element do not touch */
12146       }
12147
12148       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12149                                  player->index_bit, center_side);
12150       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12151                                           CE_PLAYER_TOUCHES_X,
12152                                           player->index_bit, center_side);
12153       break;
12154     }
12155   }
12156 }
12157
12158 #if USE_ELEMENT_TOUCHING_BUGFIX
12159
12160 void TestIfElementTouchesCustomElement(int x, int y)
12161 {
12162   static int xy[4][2] =
12163   {
12164     { 0, -1 },
12165     { -1, 0 },
12166     { +1, 0 },
12167     { 0, +1 }
12168   };
12169   static int trigger_sides[4][2] =
12170   {
12171     /* center side      border side */
12172     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12173     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12174     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12175     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12176   };
12177   static int touch_dir[4] =
12178   {
12179     MV_LEFT | MV_RIGHT,
12180     MV_UP   | MV_DOWN,
12181     MV_UP   | MV_DOWN,
12182     MV_LEFT | MV_RIGHT
12183   };
12184   boolean change_center_element = FALSE;
12185   int center_element = Feld[x][y];      /* should always be non-moving! */
12186   int border_element_old[NUM_DIRECTIONS];
12187   int i;
12188
12189   for (i = 0; i < NUM_DIRECTIONS; i++)
12190   {
12191     int xx = x + xy[i][0];
12192     int yy = y + xy[i][1];
12193     int border_element;
12194
12195     border_element_old[i] = -1;
12196
12197     if (!IN_LEV_FIELD(xx, yy))
12198       continue;
12199
12200     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12201       border_element = Feld[xx][yy];    /* may be moving! */
12202     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12203       border_element = Feld[xx][yy];
12204     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12205       border_element = MovingOrBlocked2Element(xx, yy);
12206     else
12207       continue;                 /* center and border element do not touch */
12208
12209     border_element_old[i] = border_element;
12210   }
12211
12212   for (i = 0; i < NUM_DIRECTIONS; i++)
12213   {
12214     int xx = x + xy[i][0];
12215     int yy = y + xy[i][1];
12216     int center_side = trigger_sides[i][0];
12217     int border_element = border_element_old[i];
12218
12219     if (border_element == -1)
12220       continue;
12221
12222     /* check for change of border element */
12223     CheckElementChangeBySide(xx, yy, border_element, center_element,
12224                              CE_TOUCHING_X, center_side);
12225   }
12226
12227   for (i = 0; i < NUM_DIRECTIONS; i++)
12228   {
12229     int border_side = trigger_sides[i][1];
12230     int border_element = border_element_old[i];
12231
12232     if (border_element == -1)
12233       continue;
12234
12235     /* check for change of center element (but change it only once) */
12236     if (!change_center_element)
12237       change_center_element =
12238         CheckElementChangeBySide(x, y, center_element, border_element,
12239                                  CE_TOUCHING_X, border_side);
12240   }
12241 }
12242
12243 #else
12244
12245 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12246 {
12247   static int xy[4][2] =
12248   {
12249     { 0, -1 },
12250     { -1, 0 },
12251     { +1, 0 },
12252     { 0, +1 }
12253   };
12254   static int trigger_sides[4][2] =
12255   {
12256     /* center side      border side */
12257     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12258     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12259     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12260     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12261   };
12262   static int touch_dir[4] =
12263   {
12264     MV_LEFT | MV_RIGHT,
12265     MV_UP   | MV_DOWN,
12266     MV_UP   | MV_DOWN,
12267     MV_LEFT | MV_RIGHT
12268   };
12269   boolean change_center_element = FALSE;
12270   int center_element = Feld[x][y];      /* should always be non-moving! */
12271   int i;
12272
12273   for (i = 0; i < NUM_DIRECTIONS; i++)
12274   {
12275     int xx = x + xy[i][0];
12276     int yy = y + xy[i][1];
12277     int center_side = trigger_sides[i][0];
12278     int border_side = trigger_sides[i][1];
12279     int border_element;
12280
12281     if (!IN_LEV_FIELD(xx, yy))
12282       continue;
12283
12284     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12285       border_element = Feld[xx][yy];    /* may be moving! */
12286     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12287       border_element = Feld[xx][yy];
12288     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12289       border_element = MovingOrBlocked2Element(xx, yy);
12290     else
12291       continue;                 /* center and border element do not touch */
12292
12293     /* check for change of center element (but change it only once) */
12294     if (!change_center_element)
12295       change_center_element =
12296         CheckElementChangeBySide(x, y, center_element, border_element,
12297                                  CE_TOUCHING_X, border_side);
12298
12299     /* check for change of border element */
12300     CheckElementChangeBySide(xx, yy, border_element, center_element,
12301                              CE_TOUCHING_X, center_side);
12302   }
12303 }
12304
12305 #endif
12306
12307 void TestIfElementHitsCustomElement(int x, int y, int direction)
12308 {
12309   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12310   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12311   int hitx = x + dx, hity = y + dy;
12312   int hitting_element = Feld[x][y];
12313   int touched_element;
12314
12315   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12316     return;
12317
12318   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12319                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12320
12321   if (IN_LEV_FIELD(hitx, hity))
12322   {
12323     int opposite_direction = MV_DIR_OPPOSITE(direction);
12324     int hitting_side = direction;
12325     int touched_side = opposite_direction;
12326     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12327                           MovDir[hitx][hity] != direction ||
12328                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12329
12330     object_hit = TRUE;
12331
12332     if (object_hit)
12333     {
12334       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12335                                CE_HITTING_X, touched_side);
12336
12337       CheckElementChangeBySide(hitx, hity, touched_element,
12338                                hitting_element, CE_HIT_BY_X, hitting_side);
12339
12340       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12341                                CE_HIT_BY_SOMETHING, opposite_direction);
12342     }
12343   }
12344
12345   /* "hitting something" is also true when hitting the playfield border */
12346   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12347                            CE_HITTING_SOMETHING, direction);
12348 }
12349
12350 #if 0
12351 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12352 {
12353   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12354   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12355   int hitx = x + dx, hity = y + dy;
12356   int hitting_element = Feld[x][y];
12357   int touched_element;
12358 #if 0
12359   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12360                         !IS_FREE(hitx, hity) &&
12361                         (!IS_MOVING(hitx, hity) ||
12362                          MovDir[hitx][hity] != direction ||
12363                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
12364 #endif
12365
12366   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12367     return;
12368
12369 #if 0
12370   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12371     return;
12372 #endif
12373
12374   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12375                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12376
12377   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12378                            EP_CAN_SMASH_EVERYTHING, direction);
12379
12380   if (IN_LEV_FIELD(hitx, hity))
12381   {
12382     int opposite_direction = MV_DIR_OPPOSITE(direction);
12383     int hitting_side = direction;
12384     int touched_side = opposite_direction;
12385 #if 0
12386     int touched_element = MovingOrBlocked2Element(hitx, hity);
12387 #endif
12388 #if 1
12389     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12390                           MovDir[hitx][hity] != direction ||
12391                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12392
12393     object_hit = TRUE;
12394 #endif
12395
12396     if (object_hit)
12397     {
12398       int i;
12399
12400       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12401                                CE_SMASHED_BY_SOMETHING, opposite_direction);
12402
12403       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12404                                CE_OTHER_IS_SMASHING, touched_side);
12405
12406       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12407                                CE_OTHER_GETS_SMASHED, hitting_side);
12408     }
12409   }
12410 }
12411 #endif
12412
12413 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12414 {
12415   int i, kill_x = -1, kill_y = -1;
12416
12417   int bad_element = -1;
12418   static int test_xy[4][2] =
12419   {
12420     { 0, -1 },
12421     { -1, 0 },
12422     { +1, 0 },
12423     { 0, +1 }
12424   };
12425   static int test_dir[4] =
12426   {
12427     MV_UP,
12428     MV_LEFT,
12429     MV_RIGHT,
12430     MV_DOWN
12431   };
12432
12433   for (i = 0; i < NUM_DIRECTIONS; i++)
12434   {
12435     int test_x, test_y, test_move_dir, test_element;
12436
12437     test_x = good_x + test_xy[i][0];
12438     test_y = good_y + test_xy[i][1];
12439
12440     if (!IN_LEV_FIELD(test_x, test_y))
12441       continue;
12442
12443     test_move_dir =
12444       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12445
12446     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12447
12448     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12449        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12450     */
12451     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12452         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12453     {
12454       kill_x = test_x;
12455       kill_y = test_y;
12456       bad_element = test_element;
12457
12458       break;
12459     }
12460   }
12461
12462   if (kill_x != -1 || kill_y != -1)
12463   {
12464     if (IS_PLAYER(good_x, good_y))
12465     {
12466       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12467
12468       if (player->shield_deadly_time_left > 0 &&
12469           !IS_INDESTRUCTIBLE(bad_element))
12470         Bang(kill_x, kill_y);
12471       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12472         KillPlayer(player);
12473     }
12474     else
12475       Bang(good_x, good_y);
12476   }
12477 }
12478
12479 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12480 {
12481   int i, kill_x = -1, kill_y = -1;
12482   int bad_element = Feld[bad_x][bad_y];
12483   static int test_xy[4][2] =
12484   {
12485     { 0, -1 },
12486     { -1, 0 },
12487     { +1, 0 },
12488     { 0, +1 }
12489   };
12490   static int touch_dir[4] =
12491   {
12492     MV_LEFT | MV_RIGHT,
12493     MV_UP   | MV_DOWN,
12494     MV_UP   | MV_DOWN,
12495     MV_LEFT | MV_RIGHT
12496   };
12497   static int test_dir[4] =
12498   {
12499     MV_UP,
12500     MV_LEFT,
12501     MV_RIGHT,
12502     MV_DOWN
12503   };
12504
12505   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12506     return;
12507
12508   for (i = 0; i < NUM_DIRECTIONS; i++)
12509   {
12510     int test_x, test_y, test_move_dir, test_element;
12511
12512     test_x = bad_x + test_xy[i][0];
12513     test_y = bad_y + test_xy[i][1];
12514     if (!IN_LEV_FIELD(test_x, test_y))
12515       continue;
12516
12517     test_move_dir =
12518       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12519
12520     test_element = Feld[test_x][test_y];
12521
12522     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12523        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12524     */
12525     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12526         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12527     {
12528       /* good thing is player or penguin that does not move away */
12529       if (IS_PLAYER(test_x, test_y))
12530       {
12531         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12532
12533         if (bad_element == EL_ROBOT && player->is_moving)
12534           continue;     /* robot does not kill player if he is moving */
12535
12536         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12537         {
12538           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12539             continue;           /* center and border element do not touch */
12540         }
12541
12542         kill_x = test_x;
12543         kill_y = test_y;
12544         break;
12545       }
12546       else if (test_element == EL_PENGUIN)
12547       {
12548         kill_x = test_x;
12549         kill_y = test_y;
12550         break;
12551       }
12552     }
12553   }
12554
12555   if (kill_x != -1 || kill_y != -1)
12556   {
12557     if (IS_PLAYER(kill_x, kill_y))
12558     {
12559       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12560
12561       if (player->shield_deadly_time_left > 0 &&
12562           !IS_INDESTRUCTIBLE(bad_element))
12563         Bang(bad_x, bad_y);
12564       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12565         KillPlayer(player);
12566     }
12567     else
12568       Bang(kill_x, kill_y);
12569   }
12570 }
12571
12572 void TestIfPlayerTouchesBadThing(int x, int y)
12573 {
12574   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12575 }
12576
12577 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12578 {
12579   TestIfGoodThingHitsBadThing(x, y, move_dir);
12580 }
12581
12582 void TestIfBadThingTouchesPlayer(int x, int y)
12583 {
12584   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12585 }
12586
12587 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12588 {
12589   TestIfBadThingHitsGoodThing(x, y, move_dir);
12590 }
12591
12592 void TestIfFriendTouchesBadThing(int x, int y)
12593 {
12594   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12595 }
12596
12597 void TestIfBadThingTouchesFriend(int x, int y)
12598 {
12599   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12600 }
12601
12602 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12603 {
12604   int i, kill_x = bad_x, kill_y = bad_y;
12605   static int xy[4][2] =
12606   {
12607     { 0, -1 },
12608     { -1, 0 },
12609     { +1, 0 },
12610     { 0, +1 }
12611   };
12612
12613   for (i = 0; i < NUM_DIRECTIONS; i++)
12614   {
12615     int x, y, element;
12616
12617     x = bad_x + xy[i][0];
12618     y = bad_y + xy[i][1];
12619     if (!IN_LEV_FIELD(x, y))
12620       continue;
12621
12622     element = Feld[x][y];
12623     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12624         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12625     {
12626       kill_x = x;
12627       kill_y = y;
12628       break;
12629     }
12630   }
12631
12632   if (kill_x != bad_x || kill_y != bad_y)
12633     Bang(bad_x, bad_y);
12634 }
12635
12636 void KillPlayer(struct PlayerInfo *player)
12637 {
12638   int jx = player->jx, jy = player->jy;
12639
12640   if (!player->active)
12641     return;
12642
12643   /* the following code was introduced to prevent an infinite loop when calling
12644      -> Bang()
12645      -> CheckTriggeredElementChangeExt()
12646      -> ExecuteCustomElementAction()
12647      -> KillPlayer()
12648      -> (infinitely repeating the above sequence of function calls)
12649      which occurs when killing the player while having a CE with the setting
12650      "kill player X when explosion of <player X>"; the solution using a new
12651      field "player->killed" was chosen for backwards compatibility, although
12652      clever use of the fields "player->active" etc. would probably also work */
12653 #if 1
12654   if (player->killed)
12655     return;
12656 #endif
12657
12658   player->killed = TRUE;
12659
12660   /* remove accessible field at the player's position */
12661   Feld[jx][jy] = EL_EMPTY;
12662
12663   /* deactivate shield (else Bang()/Explode() would not work right) */
12664   player->shield_normal_time_left = 0;
12665   player->shield_deadly_time_left = 0;
12666
12667   Bang(jx, jy);
12668   BuryPlayer(player);
12669 }
12670
12671 static void KillPlayerUnlessEnemyProtected(int x, int y)
12672 {
12673   if (!PLAYER_ENEMY_PROTECTED(x, y))
12674     KillPlayer(PLAYERINFO(x, y));
12675 }
12676
12677 static void KillPlayerUnlessExplosionProtected(int x, int y)
12678 {
12679   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
12680     KillPlayer(PLAYERINFO(x, y));
12681 }
12682
12683 void BuryPlayer(struct PlayerInfo *player)
12684 {
12685   int jx = player->jx, jy = player->jy;
12686
12687   if (!player->active)
12688     return;
12689
12690   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
12691   PlayLevelSound(jx, jy, SND_GAME_LOSING);
12692
12693   player->GameOver = TRUE;
12694   RemovePlayer(player);
12695 }
12696
12697 void RemovePlayer(struct PlayerInfo *player)
12698 {
12699   int jx = player->jx, jy = player->jy;
12700   int i, found = FALSE;
12701
12702   player->present = FALSE;
12703   player->active = FALSE;
12704
12705   if (!ExplodeField[jx][jy])
12706     StorePlayer[jx][jy] = 0;
12707
12708   if (player->is_moving)
12709     DrawLevelField(player->last_jx, player->last_jy);
12710
12711   for (i = 0; i < MAX_PLAYERS; i++)
12712     if (stored_player[i].active)
12713       found = TRUE;
12714
12715   if (!found)
12716     AllPlayersGone = TRUE;
12717
12718   ExitX = ZX = jx;
12719   ExitY = ZY = jy;
12720 }
12721
12722 #if USE_NEW_SNAP_DELAY
12723 static void setFieldForSnapping(int x, int y, int element, int direction)
12724 {
12725   struct ElementInfo *ei = &element_info[element];
12726   int direction_bit = MV_DIR_TO_BIT(direction);
12727   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
12728   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
12729                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
12730
12731   Feld[x][y] = EL_ELEMENT_SNAPPING;
12732   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
12733
12734   ResetGfxAnimation(x, y);
12735
12736   GfxElement[x][y] = element;
12737   GfxAction[x][y] = action;
12738   GfxDir[x][y] = direction;
12739   GfxFrame[x][y] = -1;
12740 }
12741 #endif
12742
12743 /*
12744   =============================================================================
12745   checkDiagonalPushing()
12746   -----------------------------------------------------------------------------
12747   check if diagonal input device direction results in pushing of object
12748   (by checking if the alternative direction is walkable, diggable, ...)
12749   =============================================================================
12750 */
12751
12752 static boolean checkDiagonalPushing(struct PlayerInfo *player,
12753                                     int x, int y, int real_dx, int real_dy)
12754 {
12755   int jx, jy, dx, dy, xx, yy;
12756
12757   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
12758     return TRUE;
12759
12760   /* diagonal direction: check alternative direction */
12761   jx = player->jx;
12762   jy = player->jy;
12763   dx = x - jx;
12764   dy = y - jy;
12765   xx = jx + (dx == 0 ? real_dx : 0);
12766   yy = jy + (dy == 0 ? real_dy : 0);
12767
12768   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
12769 }
12770
12771 /*
12772   =============================================================================
12773   DigField()
12774   -----------------------------------------------------------------------------
12775   x, y:                 field next to player (non-diagonal) to try to dig to
12776   real_dx, real_dy:     direction as read from input device (can be diagonal)
12777   =============================================================================
12778 */
12779
12780 int DigField(struct PlayerInfo *player,
12781              int oldx, int oldy, int x, int y,
12782              int real_dx, int real_dy, int mode)
12783 {
12784   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
12785   boolean player_was_pushing = player->is_pushing;
12786   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
12787   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
12788   int jx = oldx, jy = oldy;
12789   int dx = x - jx, dy = y - jy;
12790   int nextx = x + dx, nexty = y + dy;
12791   int move_direction = (dx == -1 ? MV_LEFT  :
12792                         dx == +1 ? MV_RIGHT :
12793                         dy == -1 ? MV_UP    :
12794                         dy == +1 ? MV_DOWN  : MV_NONE);
12795   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
12796   int dig_side = MV_DIR_OPPOSITE(move_direction);
12797   int old_element = Feld[jx][jy];
12798 #if USE_FIXED_DONT_RUN_INTO
12799   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
12800 #else
12801   int element;
12802 #endif
12803   int collect_count;
12804
12805   if (is_player)                /* function can also be called by EL_PENGUIN */
12806   {
12807     if (player->MovPos == 0)
12808     {
12809       player->is_digging = FALSE;
12810       player->is_collecting = FALSE;
12811     }
12812
12813     if (player->MovPos == 0)    /* last pushing move finished */
12814       player->is_pushing = FALSE;
12815
12816     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
12817     {
12818       player->is_switching = FALSE;
12819       player->push_delay = -1;
12820
12821       return MP_NO_ACTION;
12822     }
12823   }
12824
12825 #if !USE_FIXED_DONT_RUN_INTO
12826   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12827     return MP_NO_ACTION;
12828 #endif
12829
12830   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
12831     old_element = Back[jx][jy];
12832
12833   /* in case of element dropped at player position, check background */
12834   else if (Back[jx][jy] != EL_EMPTY &&
12835            game.engine_version >= VERSION_IDENT(2,2,0,0))
12836     old_element = Back[jx][jy];
12837
12838   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12839     return MP_NO_ACTION;        /* field has no opening in this direction */
12840
12841   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12842     return MP_NO_ACTION;        /* field has no opening in this direction */
12843
12844 #if USE_FIXED_DONT_RUN_INTO
12845   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12846   {
12847     SplashAcid(x, y);
12848
12849     Feld[jx][jy] = player->artwork_element;
12850     InitMovingField(jx, jy, MV_DOWN);
12851     Store[jx][jy] = EL_ACID;
12852     ContinueMoving(jx, jy);
12853     BuryPlayer(player);
12854
12855     return MP_DONT_RUN_INTO;
12856   }
12857 #endif
12858
12859 #if USE_FIXED_DONT_RUN_INTO
12860   if (player_can_move && DONT_RUN_INTO(element))
12861   {
12862     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12863
12864     return MP_DONT_RUN_INTO;
12865   }
12866 #endif
12867
12868 #if USE_FIXED_DONT_RUN_INTO
12869   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12870     return MP_NO_ACTION;
12871 #endif
12872
12873 #if !USE_FIXED_DONT_RUN_INTO
12874   element = Feld[x][y];
12875 #endif
12876
12877   collect_count = element_info[element].collect_count_initial;
12878
12879   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
12880     return MP_NO_ACTION;
12881
12882   if (game.engine_version < VERSION_IDENT(2,2,0,0))
12883     player_can_move = player_can_move_or_snap;
12884
12885   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
12886       game.engine_version >= VERSION_IDENT(2,2,0,0))
12887   {
12888     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
12889                                player->index_bit, dig_side);
12890     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12891                                         player->index_bit, dig_side);
12892
12893     if (element == EL_DC_LANDMINE)
12894       Bang(x, y);
12895
12896     if (Feld[x][y] != element)          /* field changed by snapping */
12897       return MP_ACTION;
12898
12899     return MP_NO_ACTION;
12900   }
12901
12902 #if USE_PLAYER_GRAVITY
12903   if (player->gravity && is_player && !player->is_auto_moving &&
12904       canFallDown(player) && move_direction != MV_DOWN &&
12905       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12906     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12907 #else
12908   if (game.gravity && is_player && !player->is_auto_moving &&
12909       canFallDown(player) && move_direction != MV_DOWN &&
12910       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12911     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12912 #endif
12913
12914   if (player_can_move &&
12915       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12916   {
12917     int sound_element = SND_ELEMENT(element);
12918     int sound_action = ACTION_WALKING;
12919
12920     if (IS_RND_GATE(element))
12921     {
12922       if (!player->key[RND_GATE_NR(element)])
12923         return MP_NO_ACTION;
12924     }
12925     else if (IS_RND_GATE_GRAY(element))
12926     {
12927       if (!player->key[RND_GATE_GRAY_NR(element)])
12928         return MP_NO_ACTION;
12929     }
12930     else if (IS_RND_GATE_GRAY_ACTIVE(element))
12931     {
12932       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
12933         return MP_NO_ACTION;
12934     }
12935     else if (element == EL_EXIT_OPEN ||
12936              element == EL_EM_EXIT_OPEN ||
12937              element == EL_STEEL_EXIT_OPEN ||
12938              element == EL_EM_STEEL_EXIT_OPEN ||
12939              element == EL_SP_EXIT_OPEN ||
12940              element == EL_SP_EXIT_OPENING)
12941     {
12942       sound_action = ACTION_PASSING;    /* player is passing exit */
12943     }
12944     else if (element == EL_EMPTY)
12945     {
12946       sound_action = ACTION_MOVING;             /* nothing to walk on */
12947     }
12948
12949     /* play sound from background or player, whatever is available */
12950     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12951       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12952     else
12953       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
12954   }
12955   else if (player_can_move &&
12956            IS_PASSABLE(element) && canPassField(x, y, move_direction))
12957   {
12958     if (!ACCESS_FROM(element, opposite_direction))
12959       return MP_NO_ACTION;      /* field not accessible from this direction */
12960
12961     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
12962       return MP_NO_ACTION;
12963
12964     if (IS_EM_GATE(element))
12965     {
12966       if (!player->key[EM_GATE_NR(element)])
12967         return MP_NO_ACTION;
12968     }
12969     else if (IS_EM_GATE_GRAY(element))
12970     {
12971       if (!player->key[EM_GATE_GRAY_NR(element)])
12972         return MP_NO_ACTION;
12973     }
12974     else if (IS_EM_GATE_GRAY_ACTIVE(element))
12975     {
12976       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
12977         return MP_NO_ACTION;
12978     }
12979     else if (IS_EMC_GATE(element))
12980     {
12981       if (!player->key[EMC_GATE_NR(element)])
12982         return MP_NO_ACTION;
12983     }
12984     else if (IS_EMC_GATE_GRAY(element))
12985     {
12986       if (!player->key[EMC_GATE_GRAY_NR(element)])
12987         return MP_NO_ACTION;
12988     }
12989     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
12990     {
12991       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
12992         return MP_NO_ACTION;
12993     }
12994     else if (element == EL_DC_GATE_WHITE ||
12995              element == EL_DC_GATE_WHITE_GRAY ||
12996              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
12997     {
12998       if (player->num_white_keys == 0)
12999         return MP_NO_ACTION;
13000
13001       player->num_white_keys--;
13002     }
13003     else if (IS_SP_PORT(element))
13004     {
13005       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13006           element == EL_SP_GRAVITY_PORT_RIGHT ||
13007           element == EL_SP_GRAVITY_PORT_UP ||
13008           element == EL_SP_GRAVITY_PORT_DOWN)
13009 #if USE_PLAYER_GRAVITY
13010         player->gravity = !player->gravity;
13011 #else
13012         game.gravity = !game.gravity;
13013 #endif
13014       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13015                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13016                element == EL_SP_GRAVITY_ON_PORT_UP ||
13017                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13018 #if USE_PLAYER_GRAVITY
13019         player->gravity = TRUE;
13020 #else
13021         game.gravity = TRUE;
13022 #endif
13023       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13024                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13025                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13026                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13027 #if USE_PLAYER_GRAVITY
13028         player->gravity = FALSE;
13029 #else
13030         game.gravity = FALSE;
13031 #endif
13032     }
13033
13034     /* automatically move to the next field with double speed */
13035     player->programmed_action = move_direction;
13036
13037     if (player->move_delay_reset_counter == 0)
13038     {
13039       player->move_delay_reset_counter = 2;     /* two double speed steps */
13040
13041       DOUBLE_PLAYER_SPEED(player);
13042     }
13043
13044     PlayLevelSoundAction(x, y, ACTION_PASSING);
13045   }
13046   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13047   {
13048     RemoveField(x, y);
13049
13050     if (mode != DF_SNAP)
13051     {
13052       GfxElement[x][y] = GFX_ELEMENT(element);
13053       player->is_digging = TRUE;
13054     }
13055
13056     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13057
13058     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13059                                         player->index_bit, dig_side);
13060
13061     if (mode == DF_SNAP)
13062     {
13063 #if USE_NEW_SNAP_DELAY
13064       if (level.block_snap_field)
13065         setFieldForSnapping(x, y, element, move_direction);
13066       else
13067         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13068 #else
13069       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13070 #endif
13071
13072       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13073                                           player->index_bit, dig_side);
13074     }
13075   }
13076   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13077   {
13078     RemoveField(x, y);
13079
13080     if (is_player && mode != DF_SNAP)
13081     {
13082       GfxElement[x][y] = element;
13083       player->is_collecting = TRUE;
13084     }
13085
13086     if (element == EL_SPEED_PILL)
13087     {
13088       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13089     }
13090     else if (element == EL_EXTRA_TIME && level.time > 0)
13091     {
13092       TimeLeft += level.extra_time;
13093       DrawGameValue_Time(TimeLeft);
13094     }
13095     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13096     {
13097       player->shield_normal_time_left += level.shield_normal_time;
13098       if (element == EL_SHIELD_DEADLY)
13099         player->shield_deadly_time_left += level.shield_deadly_time;
13100     }
13101     else if (element == EL_DYNAMITE ||
13102              element == EL_EM_DYNAMITE ||
13103              element == EL_SP_DISK_RED)
13104     {
13105       if (player->inventory_size < MAX_INVENTORY_SIZE)
13106         player->inventory_element[player->inventory_size++] = element;
13107
13108       DrawGameDoorValues();
13109     }
13110     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13111     {
13112       player->dynabomb_count++;
13113       player->dynabombs_left++;
13114     }
13115     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13116     {
13117       player->dynabomb_size++;
13118     }
13119     else if (element == EL_DYNABOMB_INCREASE_POWER)
13120     {
13121       player->dynabomb_xl = TRUE;
13122     }
13123     else if (IS_KEY(element))
13124     {
13125       player->key[KEY_NR(element)] = TRUE;
13126
13127       DrawGameDoorValues();
13128     }
13129     else if (element == EL_DC_KEY_WHITE)
13130     {
13131       player->num_white_keys++;
13132
13133       /* display white keys? */
13134       /* DrawGameDoorValues(); */
13135     }
13136     else if (IS_ENVELOPE(element))
13137     {
13138       player->show_envelope = element;
13139     }
13140     else if (element == EL_EMC_LENSES)
13141     {
13142       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13143
13144       RedrawAllInvisibleElementsForLenses();
13145     }
13146     else if (element == EL_EMC_MAGNIFIER)
13147     {
13148       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13149
13150       RedrawAllInvisibleElementsForMagnifier();
13151     }
13152     else if (IS_DROPPABLE(element) ||
13153              IS_THROWABLE(element))     /* can be collected and dropped */
13154     {
13155       int i;
13156
13157       if (collect_count == 0)
13158         player->inventory_infinite_element = element;
13159       else
13160         for (i = 0; i < collect_count; i++)
13161           if (player->inventory_size < MAX_INVENTORY_SIZE)
13162             player->inventory_element[player->inventory_size++] = element;
13163
13164       DrawGameDoorValues();
13165     }
13166     else if (collect_count > 0)
13167     {
13168       local_player->gems_still_needed -= collect_count;
13169       if (local_player->gems_still_needed < 0)
13170         local_player->gems_still_needed = 0;
13171
13172       DrawGameValue_Emeralds(local_player->gems_still_needed);
13173     }
13174
13175     RaiseScoreElement(element);
13176     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13177
13178     if (is_player)
13179       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13180                                           player->index_bit, dig_side);
13181
13182     if (mode == DF_SNAP)
13183     {
13184 #if USE_NEW_SNAP_DELAY
13185       if (level.block_snap_field)
13186         setFieldForSnapping(x, y, element, move_direction);
13187       else
13188         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13189 #else
13190       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13191 #endif
13192
13193       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13194                                           player->index_bit, dig_side);
13195     }
13196   }
13197   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13198   {
13199     if (mode == DF_SNAP && element != EL_BD_ROCK)
13200       return MP_NO_ACTION;
13201
13202     if (CAN_FALL(element) && dy)
13203       return MP_NO_ACTION;
13204
13205     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13206         !(element == EL_SPRING && level.use_spring_bug))
13207       return MP_NO_ACTION;
13208
13209     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13210         ((move_direction & MV_VERTICAL &&
13211           ((element_info[element].move_pattern & MV_LEFT &&
13212             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13213            (element_info[element].move_pattern & MV_RIGHT &&
13214             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13215          (move_direction & MV_HORIZONTAL &&
13216           ((element_info[element].move_pattern & MV_UP &&
13217             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13218            (element_info[element].move_pattern & MV_DOWN &&
13219             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13220       return MP_NO_ACTION;
13221
13222     /* do not push elements already moving away faster than player */
13223     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13224         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13225       return MP_NO_ACTION;
13226
13227     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13228     {
13229       if (player->push_delay_value == -1 || !player_was_pushing)
13230         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13231     }
13232     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13233     {
13234       if (player->push_delay_value == -1)
13235         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13236     }
13237     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13238     {
13239       if (!player->is_pushing)
13240         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13241     }
13242
13243     player->is_pushing = TRUE;
13244     player->is_active = TRUE;
13245
13246     if (!(IN_LEV_FIELD(nextx, nexty) &&
13247           (IS_FREE(nextx, nexty) ||
13248            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13249             IS_SB_ELEMENT(element)))))
13250       return MP_NO_ACTION;
13251
13252     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13253       return MP_NO_ACTION;
13254
13255     if (player->push_delay == -1)       /* new pushing; restart delay */
13256       player->push_delay = 0;
13257
13258     if (player->push_delay < player->push_delay_value &&
13259         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13260         element != EL_SPRING && element != EL_BALLOON)
13261     {
13262       /* make sure that there is no move delay before next try to push */
13263       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13264         player->move_delay = 0;
13265
13266       return MP_NO_ACTION;
13267     }
13268
13269     if (IS_SB_ELEMENT(element))
13270     {
13271       if (element == EL_SOKOBAN_FIELD_FULL)
13272       {
13273         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13274         local_player->sokobanfields_still_needed++;
13275       }
13276
13277       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13278       {
13279         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13280         local_player->sokobanfields_still_needed--;
13281       }
13282
13283       Feld[x][y] = EL_SOKOBAN_OBJECT;
13284
13285       if (Back[x][y] == Back[nextx][nexty])
13286         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13287       else if (Back[x][y] != 0)
13288         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13289                                     ACTION_EMPTYING);
13290       else
13291         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13292                                     ACTION_FILLING);
13293
13294       if (local_player->sokobanfields_still_needed == 0 &&
13295           game.emulation == EMU_SOKOBAN)
13296       {
13297         PlayerWins(player);
13298
13299         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13300       }
13301     }
13302     else
13303       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13304
13305     InitMovingField(x, y, move_direction);
13306     GfxAction[x][y] = ACTION_PUSHING;
13307
13308     if (mode == DF_SNAP)
13309       ContinueMoving(x, y);
13310     else
13311       MovPos[x][y] = (dx != 0 ? dx : dy);
13312
13313     Pushed[x][y] = TRUE;
13314     Pushed[nextx][nexty] = TRUE;
13315
13316     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13317       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13318     else
13319       player->push_delay_value = -1;    /* get new value later */
13320
13321     /* check for element change _after_ element has been pushed */
13322     if (game.use_change_when_pushing_bug)
13323     {
13324       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13325                                  player->index_bit, dig_side);
13326       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13327                                           player->index_bit, dig_side);
13328     }
13329   }
13330   else if (IS_SWITCHABLE(element))
13331   {
13332     if (PLAYER_SWITCHING(player, x, y))
13333     {
13334       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13335                                           player->index_bit, dig_side);
13336
13337       return MP_ACTION;
13338     }
13339
13340     player->is_switching = TRUE;
13341     player->switch_x = x;
13342     player->switch_y = y;
13343
13344     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13345
13346     if (element == EL_ROBOT_WHEEL)
13347     {
13348       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13349       ZX = x;
13350       ZY = y;
13351
13352       DrawLevelField(x, y);
13353     }
13354     else if (element == EL_SP_TERMINAL)
13355     {
13356       int xx, yy;
13357
13358       SCAN_PLAYFIELD(xx, yy)
13359       {
13360         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13361           Bang(xx, yy);
13362         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13363           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13364       }
13365     }
13366     else if (IS_BELT_SWITCH(element))
13367     {
13368       ToggleBeltSwitch(x, y);
13369     }
13370     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13371              element == EL_SWITCHGATE_SWITCH_DOWN ||
13372              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13373              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13374     {
13375       ToggleSwitchgateSwitch(x, y);
13376     }
13377     else if (element == EL_LIGHT_SWITCH ||
13378              element == EL_LIGHT_SWITCH_ACTIVE)
13379     {
13380       ToggleLightSwitch(x, y);
13381     }
13382     else if (element == EL_TIMEGATE_SWITCH ||
13383              element == EL_DC_TIMEGATE_SWITCH)
13384     {
13385       ActivateTimegateSwitch(x, y);
13386     }
13387     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13388              element == EL_BALLOON_SWITCH_RIGHT ||
13389              element == EL_BALLOON_SWITCH_UP    ||
13390              element == EL_BALLOON_SWITCH_DOWN  ||
13391              element == EL_BALLOON_SWITCH_NONE  ||
13392              element == EL_BALLOON_SWITCH_ANY)
13393     {
13394       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13395                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13396                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13397                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13398                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13399                              move_direction);
13400     }
13401     else if (element == EL_LAMP)
13402     {
13403       Feld[x][y] = EL_LAMP_ACTIVE;
13404       local_player->lights_still_needed--;
13405
13406       ResetGfxAnimation(x, y);
13407       DrawLevelField(x, y);
13408     }
13409     else if (element == EL_TIME_ORB_FULL)
13410     {
13411       Feld[x][y] = EL_TIME_ORB_EMPTY;
13412
13413       if (level.time > 0 || level.use_time_orb_bug)
13414       {
13415         TimeLeft += level.time_orb_time;
13416         DrawGameValue_Time(TimeLeft);
13417       }
13418
13419       ResetGfxAnimation(x, y);
13420       DrawLevelField(x, y);
13421     }
13422     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13423              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13424     {
13425       int xx, yy;
13426
13427       game.ball_state = !game.ball_state;
13428
13429       SCAN_PLAYFIELD(xx, yy)
13430       {
13431         int e = Feld[xx][yy];
13432
13433         if (game.ball_state)
13434         {
13435           if (e == EL_EMC_MAGIC_BALL)
13436             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13437           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13438             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13439         }
13440         else
13441         {
13442           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13443             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13444           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13445             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13446         }
13447       }
13448     }
13449
13450     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13451                                         player->index_bit, dig_side);
13452
13453     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13454                                         player->index_bit, dig_side);
13455
13456     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13457                                         player->index_bit, dig_side);
13458
13459     return MP_ACTION;
13460   }
13461   else
13462   {
13463     if (!PLAYER_SWITCHING(player, x, y))
13464     {
13465       player->is_switching = TRUE;
13466       player->switch_x = x;
13467       player->switch_y = y;
13468
13469       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13470                                  player->index_bit, dig_side);
13471       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13472                                           player->index_bit, dig_side);
13473
13474       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13475                                  player->index_bit, dig_side);
13476       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13477                                           player->index_bit, dig_side);
13478     }
13479
13480     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13481                                player->index_bit, dig_side);
13482     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13483                                         player->index_bit, dig_side);
13484
13485     return MP_NO_ACTION;
13486   }
13487
13488   player->push_delay = -1;
13489
13490   if (is_player)                /* function can also be called by EL_PENGUIN */
13491   {
13492     if (Feld[x][y] != element)          /* really digged/collected something */
13493     {
13494       player->is_collecting = !player->is_digging;
13495       player->is_active = TRUE;
13496     }
13497   }
13498
13499   return MP_MOVING;
13500 }
13501
13502 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13503 {
13504   int jx = player->jx, jy = player->jy;
13505   int x = jx + dx, y = jy + dy;
13506   int snap_direction = (dx == -1 ? MV_LEFT  :
13507                         dx == +1 ? MV_RIGHT :
13508                         dy == -1 ? MV_UP    :
13509                         dy == +1 ? MV_DOWN  : MV_NONE);
13510   boolean can_continue_snapping = (level.continuous_snapping &&
13511                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13512
13513   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13514     return FALSE;
13515
13516   if (!player->active || !IN_LEV_FIELD(x, y))
13517     return FALSE;
13518
13519   if (dx && dy)
13520     return FALSE;
13521
13522   if (!dx && !dy)
13523   {
13524     if (player->MovPos == 0)
13525       player->is_pushing = FALSE;
13526
13527     player->is_snapping = FALSE;
13528
13529     if (player->MovPos == 0)
13530     {
13531       player->is_moving = FALSE;
13532       player->is_digging = FALSE;
13533       player->is_collecting = FALSE;
13534     }
13535
13536     return FALSE;
13537   }
13538
13539 #if USE_NEW_CONTINUOUS_SNAPPING
13540   /* prevent snapping with already pressed snap key when not allowed */
13541   if (player->is_snapping && !can_continue_snapping)
13542     return FALSE;
13543 #else
13544   if (player->is_snapping)
13545     return FALSE;
13546 #endif
13547
13548   player->MovDir = snap_direction;
13549
13550   if (player->MovPos == 0)
13551   {
13552     player->is_moving = FALSE;
13553     player->is_digging = FALSE;
13554     player->is_collecting = FALSE;
13555   }
13556
13557   player->is_dropping = FALSE;
13558   player->is_dropping_pressed = FALSE;
13559   player->drop_pressed_delay = 0;
13560
13561   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13562     return FALSE;
13563
13564   player->is_snapping = TRUE;
13565   player->is_active = TRUE;
13566
13567   if (player->MovPos == 0)
13568   {
13569     player->is_moving = FALSE;
13570     player->is_digging = FALSE;
13571     player->is_collecting = FALSE;
13572   }
13573
13574   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13575     DrawLevelField(player->last_jx, player->last_jy);
13576
13577   DrawLevelField(x, y);
13578
13579   return TRUE;
13580 }
13581
13582 boolean DropElement(struct PlayerInfo *player)
13583 {
13584   int old_element, new_element;
13585   int dropx = player->jx, dropy = player->jy;
13586   int drop_direction = player->MovDir;
13587   int drop_side = drop_direction;
13588   int drop_element = (player->inventory_size > 0 ?
13589                       player->inventory_element[player->inventory_size - 1] :
13590                       player->inventory_infinite_element != EL_UNDEFINED ?
13591                       player->inventory_infinite_element :
13592                       player->dynabombs_left > 0 ?
13593                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
13594                       EL_UNDEFINED);
13595
13596   player->is_dropping_pressed = TRUE;
13597
13598   /* do not drop an element on top of another element; when holding drop key
13599      pressed without moving, dropped element must move away before the next
13600      element can be dropped (this is especially important if the next element
13601      is dynamite, which can be placed on background for historical reasons) */
13602   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13603     return MP_ACTION;
13604
13605   if (IS_THROWABLE(drop_element))
13606   {
13607     dropx += GET_DX_FROM_DIR(drop_direction);
13608     dropy += GET_DY_FROM_DIR(drop_direction);
13609
13610     if (!IN_LEV_FIELD(dropx, dropy))
13611       return FALSE;
13612   }
13613
13614   old_element = Feld[dropx][dropy];     /* old element at dropping position */
13615   new_element = drop_element;           /* default: no change when dropping */
13616
13617   /* check if player is active, not moving and ready to drop */
13618   if (!player->active || player->MovPos || player->drop_delay > 0)
13619     return FALSE;
13620
13621   /* check if player has anything that can be dropped */
13622   if (new_element == EL_UNDEFINED)
13623     return FALSE;
13624
13625   /* check if drop key was pressed long enough for EM style dynamite */
13626   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13627     return FALSE;
13628
13629   /* check if anything can be dropped at the current position */
13630   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13631     return FALSE;
13632
13633   /* collected custom elements can only be dropped on empty fields */
13634   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13635     return FALSE;
13636
13637   if (old_element != EL_EMPTY)
13638     Back[dropx][dropy] = old_element;   /* store old element on this field */
13639
13640   ResetGfxAnimation(dropx, dropy);
13641   ResetRandomAnimationValue(dropx, dropy);
13642
13643   if (player->inventory_size > 0 ||
13644       player->inventory_infinite_element != EL_UNDEFINED)
13645   {
13646     if (player->inventory_size > 0)
13647     {
13648       player->inventory_size--;
13649
13650       DrawGameDoorValues();
13651
13652       if (new_element == EL_DYNAMITE)
13653         new_element = EL_DYNAMITE_ACTIVE;
13654       else if (new_element == EL_EM_DYNAMITE)
13655         new_element = EL_EM_DYNAMITE_ACTIVE;
13656       else if (new_element == EL_SP_DISK_RED)
13657         new_element = EL_SP_DISK_RED_ACTIVE;
13658     }
13659
13660     Feld[dropx][dropy] = new_element;
13661
13662     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13663       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13664                           el2img(Feld[dropx][dropy]), 0);
13665
13666     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13667
13668     /* needed if previous element just changed to "empty" in the last frame */
13669     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
13670
13671     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13672                                player->index_bit, drop_side);
13673     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13674                                         CE_PLAYER_DROPS_X,
13675                                         player->index_bit, drop_side);
13676
13677     TestIfElementTouchesCustomElement(dropx, dropy);
13678   }
13679   else          /* player is dropping a dyna bomb */
13680   {
13681     player->dynabombs_left--;
13682
13683     Feld[dropx][dropy] = new_element;
13684
13685     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13686       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13687                           el2img(Feld[dropx][dropy]), 0);
13688
13689     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13690   }
13691
13692   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
13693     InitField_WithBug1(dropx, dropy, FALSE);
13694
13695   new_element = Feld[dropx][dropy];     /* element might have changed */
13696
13697   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
13698       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
13699   {
13700     int move_direction, nextx, nexty;
13701
13702     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
13703       MovDir[dropx][dropy] = drop_direction;
13704
13705     move_direction = MovDir[dropx][dropy];
13706     nextx = dropx + GET_DX_FROM_DIR(move_direction);
13707     nexty = dropy + GET_DY_FROM_DIR(move_direction);
13708
13709     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
13710
13711 #if USE_FIX_IMPACT_COLLISION
13712     /* do not cause impact style collision by dropping elements that can fall */
13713     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13714 #else
13715     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13716 #endif
13717   }
13718
13719   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
13720   player->is_dropping = TRUE;
13721
13722   player->drop_pressed_delay = 0;
13723   player->is_dropping_pressed = FALSE;
13724
13725   player->drop_x = dropx;
13726   player->drop_y = dropy;
13727
13728   return TRUE;
13729 }
13730
13731 /* ------------------------------------------------------------------------- */
13732 /* game sound playing functions                                              */
13733 /* ------------------------------------------------------------------------- */
13734
13735 static int *loop_sound_frame = NULL;
13736 static int *loop_sound_volume = NULL;
13737
13738 void InitPlayLevelSound()
13739 {
13740   int num_sounds = getSoundListSize();
13741
13742   checked_free(loop_sound_frame);
13743   checked_free(loop_sound_volume);
13744
13745   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
13746   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
13747 }
13748
13749 static void PlayLevelSound(int x, int y, int nr)
13750 {
13751   int sx = SCREENX(x), sy = SCREENY(y);
13752   int volume, stereo_position;
13753   int max_distance = 8;
13754   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
13755
13756   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
13757       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
13758     return;
13759
13760   if (!IN_LEV_FIELD(x, y) ||
13761       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
13762       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
13763     return;
13764
13765   volume = SOUND_MAX_VOLUME;
13766
13767   if (!IN_SCR_FIELD(sx, sy))
13768   {
13769     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
13770     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
13771
13772     volume -= volume * (dx > dy ? dx : dy) / max_distance;
13773   }
13774
13775   stereo_position = (SOUND_MAX_LEFT +
13776                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
13777                      (SCR_FIELDX + 2 * max_distance));
13778
13779   if (IS_LOOP_SOUND(nr))
13780   {
13781     /* This assures that quieter loop sounds do not overwrite louder ones,
13782        while restarting sound volume comparison with each new game frame. */
13783
13784     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
13785       return;
13786
13787     loop_sound_volume[nr] = volume;
13788     loop_sound_frame[nr] = FrameCounter;
13789   }
13790
13791   PlaySoundExt(nr, volume, stereo_position, type);
13792 }
13793
13794 static void PlayLevelSoundNearest(int x, int y, int sound_action)
13795 {
13796   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
13797                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
13798                  y < LEVELY(BY1) ? LEVELY(BY1) :
13799                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
13800                  sound_action);
13801 }
13802
13803 static void PlayLevelSoundAction(int x, int y, int action)
13804 {
13805   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
13806 }
13807
13808 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
13809 {
13810   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13811
13812   if (sound_effect != SND_UNDEFINED)
13813     PlayLevelSound(x, y, sound_effect);
13814 }
13815
13816 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
13817                                               int action)
13818 {
13819   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13820
13821   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13822     PlayLevelSound(x, y, sound_effect);
13823 }
13824
13825 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
13826 {
13827   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13828
13829   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13830     PlayLevelSound(x, y, sound_effect);
13831 }
13832
13833 static void StopLevelSoundActionIfLoop(int x, int y, int action)
13834 {
13835   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13836
13837   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13838     StopSound(sound_effect);
13839 }
13840
13841 static void PlayLevelMusic()
13842 {
13843   if (levelset.music[level_nr] != MUS_UNDEFINED)
13844     PlayMusic(levelset.music[level_nr]);        /* from config file */
13845   else
13846     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
13847 }
13848
13849 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
13850 {
13851   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13852   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
13853   int x = xx - 1 - offset;
13854   int y = yy - 1 - offset;
13855
13856   switch (sample)
13857   {
13858     case SAMPLE_blank:
13859       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13860       break;
13861
13862     case SAMPLE_roll:
13863       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13864       break;
13865
13866     case SAMPLE_stone:
13867       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13868       break;
13869
13870     case SAMPLE_nut:
13871       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13872       break;
13873
13874     case SAMPLE_crack:
13875       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13876       break;
13877
13878     case SAMPLE_bug:
13879       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13880       break;
13881
13882     case SAMPLE_tank:
13883       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13884       break;
13885
13886     case SAMPLE_android_clone:
13887       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13888       break;
13889
13890     case SAMPLE_android_move:
13891       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13892       break;
13893
13894     case SAMPLE_spring:
13895       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13896       break;
13897
13898     case SAMPLE_slurp:
13899       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
13900       break;
13901
13902     case SAMPLE_eater:
13903       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13904       break;
13905
13906     case SAMPLE_eater_eat:
13907       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13908       break;
13909
13910     case SAMPLE_alien:
13911       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13912       break;
13913
13914     case SAMPLE_collect:
13915       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13916       break;
13917
13918     case SAMPLE_diamond:
13919       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13920       break;
13921
13922     case SAMPLE_squash:
13923       /* !!! CHECK THIS !!! */
13924 #if 1
13925       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13926 #else
13927       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13928 #endif
13929       break;
13930
13931     case SAMPLE_wonderfall:
13932       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13933       break;
13934
13935     case SAMPLE_drip:
13936       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13937       break;
13938
13939     case SAMPLE_push:
13940       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13941       break;
13942
13943     case SAMPLE_dirt:
13944       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13945       break;
13946
13947     case SAMPLE_acid:
13948       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13949       break;
13950
13951     case SAMPLE_ball:
13952       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13953       break;
13954
13955     case SAMPLE_grow:
13956       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13957       break;
13958
13959     case SAMPLE_wonder:
13960       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13961       break;
13962
13963     case SAMPLE_door:
13964       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13965       break;
13966
13967     case SAMPLE_exit_open:
13968       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13969       break;
13970
13971     case SAMPLE_exit_leave:
13972       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13973       break;
13974
13975     case SAMPLE_dynamite:
13976       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13977       break;
13978
13979     case SAMPLE_tick:
13980       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13981       break;
13982
13983     case SAMPLE_press:
13984       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13985       break;
13986
13987     case SAMPLE_wheel:
13988       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13989       break;
13990
13991     case SAMPLE_boom:
13992       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13993       break;
13994
13995     case SAMPLE_die:
13996       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13997       break;
13998
13999     case SAMPLE_time:
14000       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14001       break;
14002
14003     default:
14004       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14005       break;
14006   }
14007 }
14008
14009 #if 0
14010 void ChangeTime(int value)
14011 {
14012   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
14013
14014   *time += value;
14015
14016   /* EMC game engine uses value from time counter of RND game engine */
14017   level.native_em_level->lev->time = *time;
14018
14019   DrawGameValue_Time(*time);
14020 }
14021
14022 void RaiseScore(int value)
14023 {
14024   /* EMC game engine and RND game engine have separate score counters */
14025   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
14026                 &level.native_em_level->lev->score : &local_player->score);
14027
14028   *score += value;
14029
14030   DrawGameValue_Score(*score);
14031 }
14032 #endif
14033
14034 void RaiseScore(int value)
14035 {
14036   local_player->score += value;
14037
14038   DrawGameValue_Score(local_player->score);
14039 }
14040
14041 void RaiseScoreElement(int element)
14042 {
14043   switch (element)
14044   {
14045     case EL_EMERALD:
14046     case EL_BD_DIAMOND:
14047     case EL_EMERALD_YELLOW:
14048     case EL_EMERALD_RED:
14049     case EL_EMERALD_PURPLE:
14050     case EL_SP_INFOTRON:
14051       RaiseScore(level.score[SC_EMERALD]);
14052       break;
14053     case EL_DIAMOND:
14054       RaiseScore(level.score[SC_DIAMOND]);
14055       break;
14056     case EL_CRYSTAL:
14057       RaiseScore(level.score[SC_CRYSTAL]);
14058       break;
14059     case EL_PEARL:
14060       RaiseScore(level.score[SC_PEARL]);
14061       break;
14062     case EL_BUG:
14063     case EL_BD_BUTTERFLY:
14064     case EL_SP_ELECTRON:
14065       RaiseScore(level.score[SC_BUG]);
14066       break;
14067     case EL_SPACESHIP:
14068     case EL_BD_FIREFLY:
14069     case EL_SP_SNIKSNAK:
14070       RaiseScore(level.score[SC_SPACESHIP]);
14071       break;
14072     case EL_YAMYAM:
14073     case EL_DARK_YAMYAM:
14074       RaiseScore(level.score[SC_YAMYAM]);
14075       break;
14076     case EL_ROBOT:
14077       RaiseScore(level.score[SC_ROBOT]);
14078       break;
14079     case EL_PACMAN:
14080       RaiseScore(level.score[SC_PACMAN]);
14081       break;
14082     case EL_NUT:
14083       RaiseScore(level.score[SC_NUT]);
14084       break;
14085     case EL_DYNAMITE:
14086     case EL_EM_DYNAMITE:
14087     case EL_SP_DISK_RED:
14088     case EL_DYNABOMB_INCREASE_NUMBER:
14089     case EL_DYNABOMB_INCREASE_SIZE:
14090     case EL_DYNABOMB_INCREASE_POWER:
14091       RaiseScore(level.score[SC_DYNAMITE]);
14092       break;
14093     case EL_SHIELD_NORMAL:
14094     case EL_SHIELD_DEADLY:
14095       RaiseScore(level.score[SC_SHIELD]);
14096       break;
14097     case EL_EXTRA_TIME:
14098       RaiseScore(level.extra_time_score);
14099       break;
14100     case EL_KEY_1:
14101     case EL_KEY_2:
14102     case EL_KEY_3:
14103     case EL_KEY_4:
14104     case EL_EM_KEY_1:
14105     case EL_EM_KEY_2:
14106     case EL_EM_KEY_3:
14107     case EL_EM_KEY_4:
14108     case EL_EMC_KEY_5:
14109     case EL_EMC_KEY_6:
14110     case EL_EMC_KEY_7:
14111     case EL_EMC_KEY_8:
14112     case EL_DC_KEY_WHITE:
14113       RaiseScore(level.score[SC_KEY]);
14114       break;
14115     default:
14116       RaiseScore(element_info[element].collect_score);
14117       break;
14118   }
14119 }
14120
14121 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14122 {
14123   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14124   {
14125 #if defined(NETWORK_AVALIABLE)
14126     if (options.network)
14127       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14128     else
14129 #endif
14130     {
14131       if (quick_quit)
14132       {
14133 #if 1
14134         fading = fading_none;
14135 #else
14136         OpenDoor(DOOR_CLOSE_1);
14137 #endif
14138
14139         game_status = GAME_MODE_MAIN;
14140
14141 #if 1
14142         DrawAndFadeInMainMenu(REDRAW_FIELD);
14143 #else
14144         DrawMainMenu();
14145 #endif
14146       }
14147       else
14148       {
14149 #if 0
14150 #if 1
14151         if (fading.anim_mode == ANIM_CROSSFADE)
14152           FadeCrossSaveBackbuffer();
14153         else
14154           FadeOut(REDRAW_FIELD);
14155 #else
14156         FadeOut(REDRAW_FIELD);
14157 #endif
14158 #endif
14159
14160         game_status = GAME_MODE_MAIN;
14161
14162         DrawAndFadeInMainMenu(REDRAW_FIELD);
14163       }
14164     }
14165   }
14166   else          /* continue playing the game */
14167   {
14168     if (tape.playing && tape.deactivate_display)
14169       TapeDeactivateDisplayOff(TRUE);
14170
14171     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14172
14173     if (tape.playing && tape.deactivate_display)
14174       TapeDeactivateDisplayOn();
14175   }
14176 }
14177
14178 void RequestQuitGame(boolean ask_if_really_quit)
14179 {
14180   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14181   boolean skip_request = AllPlayersGone || quick_quit;
14182
14183   RequestQuitGameExt(skip_request, quick_quit,
14184                      "Do you really want to quit the game ?");
14185 }
14186
14187
14188 /* ------------------------------------------------------------------------- */
14189 /* random generator functions                                                */
14190 /* ------------------------------------------------------------------------- */
14191
14192 unsigned int InitEngineRandom_RND(long seed)
14193 {
14194   game.num_random_calls = 0;
14195
14196 #if 0
14197   unsigned int rnd_seed = InitEngineRandom(seed);
14198
14199   printf("::: START RND: %d\n", rnd_seed);
14200
14201   return rnd_seed;
14202 #else
14203
14204   return InitEngineRandom(seed);
14205
14206 #endif
14207
14208 }
14209
14210 unsigned int RND(int max)
14211 {
14212   if (max > 0)
14213   {
14214     game.num_random_calls++;
14215
14216     return GetEngineRandom(max);
14217   }
14218
14219   return 0;
14220 }
14221
14222
14223 /* ------------------------------------------------------------------------- */
14224 /* game engine snapshot handling functions                                   */
14225 /* ------------------------------------------------------------------------- */
14226
14227 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
14228
14229 struct EngineSnapshotInfo
14230 {
14231   /* runtime values for custom element collect score */
14232   int collect_score[NUM_CUSTOM_ELEMENTS];
14233
14234   /* runtime values for group element choice position */
14235   int choice_pos[NUM_GROUP_ELEMENTS];
14236
14237   /* runtime values for belt position animations */
14238   int belt_graphic[4 * NUM_BELT_PARTS];
14239   int belt_anim_mode[4 * NUM_BELT_PARTS];
14240 };
14241
14242 struct EngineSnapshotNodeInfo
14243 {
14244   void *buffer_orig;
14245   void *buffer_copy;
14246   int size;
14247 };
14248
14249 static struct EngineSnapshotInfo engine_snapshot_rnd;
14250 static ListNode *engine_snapshot_list = NULL;
14251 static char *snapshot_level_identifier = NULL;
14252 static int snapshot_level_nr = -1;
14253
14254 void FreeEngineSnapshot()
14255 {
14256   while (engine_snapshot_list != NULL)
14257     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14258                        checked_free);
14259
14260   setString(&snapshot_level_identifier, NULL);
14261   snapshot_level_nr = -1;
14262 }
14263
14264 static void SaveEngineSnapshotValues_RND()
14265 {
14266   static int belt_base_active_element[4] =
14267   {
14268     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14269     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14270     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14271     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14272   };
14273   int i, j;
14274
14275   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14276   {
14277     int element = EL_CUSTOM_START + i;
14278
14279     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14280   }
14281
14282   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14283   {
14284     int element = EL_GROUP_START + i;
14285
14286     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14287   }
14288
14289   for (i = 0; i < 4; i++)
14290   {
14291     for (j = 0; j < NUM_BELT_PARTS; j++)
14292     {
14293       int element = belt_base_active_element[i] + j;
14294       int graphic = el2img(element);
14295       int anim_mode = graphic_info[graphic].anim_mode;
14296
14297       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14298       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14299     }
14300   }
14301 }
14302
14303 static void LoadEngineSnapshotValues_RND()
14304 {
14305   unsigned long num_random_calls = game.num_random_calls;
14306   int i, j;
14307
14308   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14309   {
14310     int element = EL_CUSTOM_START + i;
14311
14312     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14313   }
14314
14315   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14316   {
14317     int element = EL_GROUP_START + i;
14318
14319     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14320   }
14321
14322   for (i = 0; i < 4; i++)
14323   {
14324     for (j = 0; j < NUM_BELT_PARTS; j++)
14325     {
14326       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14327       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14328
14329       graphic_info[graphic].anim_mode = anim_mode;
14330     }
14331   }
14332
14333   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14334   {
14335     InitRND(tape.random_seed);
14336     for (i = 0; i < num_random_calls; i++)
14337       RND(1);
14338   }
14339
14340   if (game.num_random_calls != num_random_calls)
14341   {
14342     Error(ERR_INFO, "number of random calls out of sync");
14343     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14344     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14345     Error(ERR_EXIT, "this should not happen -- please debug");
14346   }
14347 }
14348
14349 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14350 {
14351   struct EngineSnapshotNodeInfo *bi =
14352     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14353
14354   bi->buffer_orig = buffer;
14355   bi->buffer_copy = checked_malloc(size);
14356   bi->size = size;
14357
14358   memcpy(bi->buffer_copy, buffer, size);
14359
14360   addNodeToList(&engine_snapshot_list, NULL, bi);
14361 }
14362
14363 void SaveEngineSnapshot()
14364 {
14365   FreeEngineSnapshot();         /* free previous snapshot, if needed */
14366
14367   if (level_editor_test_game)   /* do not save snapshots from editor */
14368     return;
14369
14370   /* copy some special values to a structure better suited for the snapshot */
14371
14372   SaveEngineSnapshotValues_RND();
14373   SaveEngineSnapshotValues_EM();
14374
14375   /* save values stored in special snapshot structure */
14376
14377   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14378   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14379
14380   /* save further RND engine values */
14381
14382   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14383   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14384   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14385
14386   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14387   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14388   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14389   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14390
14391   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14392   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14393   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14394   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14395   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14396
14397   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14398   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14399   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14400
14401   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14402
14403   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14404
14405   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14406   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14407
14408   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14409   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14410   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14411   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14412   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14413   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14414   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14415   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14416   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14417   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14418   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14419   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14420   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14421   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14422   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14423   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14424   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
14425   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
14426
14427   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14428   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14429
14430   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14431   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14432   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14433
14434   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14435   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14436
14437   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14438   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14439   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14440   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14441   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14442
14443   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14444   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14445
14446   /* save level identification information */
14447
14448   setString(&snapshot_level_identifier, leveldir_current->identifier);
14449   snapshot_level_nr = level_nr;
14450
14451 #if 0
14452   ListNode *node = engine_snapshot_list;
14453   int num_bytes = 0;
14454
14455   while (node != NULL)
14456   {
14457     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14458
14459     node = node->next;
14460   }
14461
14462   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14463 #endif
14464 }
14465
14466 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
14467 {
14468   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
14469 }
14470
14471 void LoadEngineSnapshot()
14472 {
14473   ListNode *node = engine_snapshot_list;
14474
14475   if (engine_snapshot_list == NULL)
14476     return;
14477
14478   while (node != NULL)
14479   {
14480     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
14481
14482     node = node->next;
14483   }
14484
14485   /* restore special values from snapshot structure */
14486
14487   LoadEngineSnapshotValues_RND();
14488   LoadEngineSnapshotValues_EM();
14489 }
14490
14491 boolean CheckEngineSnapshot()
14492 {
14493   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14494           snapshot_level_nr == level_nr);
14495 }
14496
14497
14498 /* ---------- new game button stuff ---------------------------------------- */
14499
14500 /* graphic position values for game buttons */
14501 #define GAME_BUTTON_XSIZE       30
14502 #define GAME_BUTTON_YSIZE       30
14503 #define GAME_BUTTON_XPOS        5
14504 #define GAME_BUTTON_YPOS        215
14505 #define SOUND_BUTTON_XPOS       5
14506 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
14507
14508 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14509 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14510 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14511 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14512 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14513 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14514
14515 static struct
14516 {
14517   int *x, *y;
14518   int gd_x, gd_y;
14519   int gadget_id;
14520   char *infotext;
14521 } gamebutton_info[NUM_GAME_BUTTONS] =
14522 {
14523 #if 1
14524   {
14525     &game.button.stop.x,        &game.button.stop.y,
14526     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
14527     GAME_CTRL_ID_STOP,
14528     "stop game"
14529   },
14530   {
14531     &game.button.pause.x,       &game.button.pause.y,
14532     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
14533     GAME_CTRL_ID_PAUSE,
14534     "pause game"
14535   },
14536   {
14537     &game.button.play.x,        &game.button.play.y,
14538     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
14539     GAME_CTRL_ID_PLAY,
14540     "play game"
14541   },
14542   {
14543     &game.button.sound_music.x, &game.button.sound_music.y,
14544     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
14545     SOUND_CTRL_ID_MUSIC,
14546     "background music on/off"
14547   },
14548   {
14549     &game.button.sound_loops.x, &game.button.sound_loops.y,
14550     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
14551     SOUND_CTRL_ID_LOOPS,
14552     "sound loops on/off"
14553   },
14554   {
14555     &game.button.sound_simple.x,&game.button.sound_simple.y,
14556     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
14557     SOUND_CTRL_ID_SIMPLE,
14558     "normal sounds on/off"
14559   }
14560 #else
14561   {
14562     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
14563     GAME_CTRL_ID_STOP,
14564     "stop game"
14565   },
14566   {
14567     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
14568     GAME_CTRL_ID_PAUSE,
14569     "pause game"
14570   },
14571   {
14572     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
14573     GAME_CTRL_ID_PLAY,
14574     "play game"
14575   },
14576   {
14577     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
14578     SOUND_CTRL_ID_MUSIC,
14579     "background music on/off"
14580   },
14581   {
14582     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
14583     SOUND_CTRL_ID_LOOPS,
14584     "sound loops on/off"
14585   },
14586   {
14587     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
14588     SOUND_CTRL_ID_SIMPLE,
14589     "normal sounds on/off"
14590   }
14591 #endif
14592 };
14593
14594 void CreateGameButtons()
14595 {
14596   int i;
14597
14598   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14599   {
14600     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
14601     struct GadgetInfo *gi;
14602     int button_type;
14603     boolean checked;
14604     unsigned long event_mask;
14605     int x, y;
14606     int gd_xoffset, gd_yoffset;
14607     int gd_x1, gd_x2, gd_y1, gd_y2;
14608     int id = i;
14609
14610     x = DX + *gamebutton_info[i].x;
14611     y = DY + *gamebutton_info[i].y;
14612     gd_xoffset = gamebutton_info[i].gd_x;
14613     gd_yoffset = gamebutton_info[i].gd_y;
14614     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
14615     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
14616
14617     if (id == GAME_CTRL_ID_STOP ||
14618         id == GAME_CTRL_ID_PAUSE ||
14619         id == GAME_CTRL_ID_PLAY)
14620     {
14621       button_type = GD_TYPE_NORMAL_BUTTON;
14622       checked = FALSE;
14623       event_mask = GD_EVENT_RELEASED;
14624       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14625       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14626     }
14627     else
14628     {
14629       button_type = GD_TYPE_CHECK_BUTTON;
14630       checked =
14631         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14632          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14633          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14634       event_mask = GD_EVENT_PRESSED;
14635       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
14636       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14637     }
14638
14639     gi = CreateGadget(GDI_CUSTOM_ID, id,
14640                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14641 #if 1
14642                       GDI_X, x,
14643                       GDI_Y, y,
14644 #else
14645                       GDI_X, DX + gd_xoffset,
14646                       GDI_Y, DY + gd_yoffset,
14647 #endif
14648                       GDI_WIDTH, GAME_BUTTON_XSIZE,
14649                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
14650                       GDI_TYPE, button_type,
14651                       GDI_STATE, GD_BUTTON_UNPRESSED,
14652                       GDI_CHECKED, checked,
14653                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
14654                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
14655                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
14656                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
14657                       GDI_EVENT_MASK, event_mask,
14658                       GDI_CALLBACK_ACTION, HandleGameButtons,
14659                       GDI_END);
14660
14661     if (gi == NULL)
14662       Error(ERR_EXIT, "cannot create gadget");
14663
14664     game_gadget[id] = gi;
14665   }
14666 }
14667
14668 void FreeGameButtons()
14669 {
14670   int i;
14671
14672   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14673     FreeGadget(game_gadget[i]);
14674 }
14675
14676 static void MapGameButtons()
14677 {
14678   int i;
14679
14680   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14681     MapGadget(game_gadget[i]);
14682 }
14683
14684 void UnmapGameButtons()
14685 {
14686   int i;
14687
14688   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14689     UnmapGadget(game_gadget[i]);
14690 }
14691
14692 static void HandleGameButtons(struct GadgetInfo *gi)
14693 {
14694   int id = gi->custom_id;
14695
14696   if (game_status != GAME_MODE_PLAYING)
14697     return;
14698
14699   switch (id)
14700   {
14701     case GAME_CTRL_ID_STOP:
14702       if (tape.playing)
14703         TapeStop();
14704       else
14705         RequestQuitGame(TRUE);
14706       break;
14707
14708     case GAME_CTRL_ID_PAUSE:
14709       if (options.network)
14710       {
14711 #if defined(NETWORK_AVALIABLE)
14712         if (tape.pausing)
14713           SendToServer_ContinuePlaying();
14714         else
14715           SendToServer_PausePlaying();
14716 #endif
14717       }
14718       else
14719         TapeTogglePause(TAPE_TOGGLE_MANUAL);
14720       break;
14721
14722     case GAME_CTRL_ID_PLAY:
14723       if (tape.pausing)
14724       {
14725 #if defined(NETWORK_AVALIABLE)
14726         if (options.network)
14727           SendToServer_ContinuePlaying();
14728         else
14729 #endif
14730         {
14731           tape.pausing = FALSE;
14732           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
14733         }
14734       }
14735       break;
14736
14737     case SOUND_CTRL_ID_MUSIC:
14738       if (setup.sound_music)
14739       { 
14740         setup.sound_music = FALSE;
14741         FadeMusic();
14742       }
14743       else if (audio.music_available)
14744       { 
14745         setup.sound = setup.sound_music = TRUE;
14746
14747         SetAudioMode(setup.sound);
14748
14749         PlayLevelMusic();
14750       }
14751       break;
14752
14753     case SOUND_CTRL_ID_LOOPS:
14754       if (setup.sound_loops)
14755         setup.sound_loops = FALSE;
14756       else if (audio.loops_available)
14757       {
14758         setup.sound = setup.sound_loops = TRUE;
14759         SetAudioMode(setup.sound);
14760       }
14761       break;
14762
14763     case SOUND_CTRL_ID_SIMPLE:
14764       if (setup.sound_simple)
14765         setup.sound_simple = FALSE;
14766       else if (audio.sound_available)
14767       {
14768         setup.sound = setup.sound_simple = TRUE;
14769         SetAudioMode(setup.sound);
14770       }
14771       break;
14772
14773     default:
14774       break;
14775   }
14776 }