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