297efe8502254ba09972f195dbea379219e08713
[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 #if 1
91 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
92 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
93 #define PANEL_XPOS(p)           (DX + ALIGNED_MENU_XPOS(p))
94 #define PANEL_YPOS(p)           (DY + ALIGNED_MENU_YPOS(p))
95 #else
96 #define PANEL_DEACTIVATED(p)    ((p).x < 0 || (p).y < 0)
97 #define PANEL_XPOS(p)           (ALIGNED_XPOS((p).x, (p).width, (p).align))
98 #define PANEL_YPOS(p)           ((p).y)
99 #endif
100
101 /* special positions in the game control window (relative to control window) */
102 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
103 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
104 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
105 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
106 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
107 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
108 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
109 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
110 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
111 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
112 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
113 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
114 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
115 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
116 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
117 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
118
119 /* special positions in the game control window (relative to main window) */
120 #define DX_LEVEL1               (DX + XX_LEVEL1)
121 #define DX_LEVEL2               (DX + XX_LEVEL2)
122 #define DX_LEVEL                (DX + XX_LEVEL)
123 #define DY_LEVEL                (DY + YY_LEVEL)
124 #define DX_EMERALDS             (DX + XX_EMERALDS)
125 #define DY_EMERALDS             (DY + YY_EMERALDS)
126 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
127 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
128 #define DX_KEYS                 (DX + XX_KEYS)
129 #define DY_KEYS                 (DY + YY_KEYS)
130 #define DX_SCORE                (DX + XX_SCORE)
131 #define DY_SCORE                (DY + YY_SCORE)
132 #define DX_TIME1                (DX + XX_TIME1)
133 #define DX_TIME2                (DX + XX_TIME2)
134 #define DX_TIME                 (DX + XX_TIME)
135 #define DY_TIME                 (DY + YY_TIME)
136
137 #if 0
138 /* game panel display and control definitions */
139
140 #define GAME_CONTROL_LEVEL                      0
141 #define GAME_CONTROL_GEMS                       1
142 #define GAME_CONTROL_INVENTORY                  2
143 #define GAME_CONTROL_KEY_1                      3
144 #define GAME_CONTROL_KEY_2                      4
145 #define GAME_CONTROL_KEY_3                      5
146 #define GAME_CONTROL_KEY_4                      6
147 #define GAME_CONTROL_KEY_5                      7
148 #define GAME_CONTROL_KEY_6                      8
149 #define GAME_CONTROL_KEY_7                      9
150 #define GAME_CONTROL_KEY_8                      10
151 #define GAME_CONTROL_KEY_WHITE                  11
152 #define GAME_CONTROL_KEY_WHITE_COUNT            12
153 #define GAME_CONTROL_SCORE                      13
154 #define GAME_CONTROL_TIME                       14
155 #define GAME_CONTROL_TIME_HH                    15
156 #define GAME_CONTROL_TIME_MM                    16
157 #define GAME_CONTROL_TIME_SS                    17
158 #define GAME_CONTROL_DROP_NEXT_1                18
159 #define GAME_CONTROL_DROP_NEXT_2                19
160 #define GAME_CONTROL_DROP_NEXT_3                20
161 #define GAME_CONTROL_DROP_NEXT_4                21
162 #define GAME_CONTROL_DROP_NEXT_5                22
163 #define GAME_CONTROL_DROP_NEXT_6                23
164 #define GAME_CONTROL_DROP_NEXT_7                24
165 #define GAME_CONTROL_DROP_NEXT_8                25
166 #define GAME_CONTROL_SHIELD_NORMAL              26
167 #define GAME_CONTROL_SHIELD_NORMAL_TIME         27
168 #define GAME_CONTROL_SHIELD_DEADLY              28
169 #define GAME_CONTROL_SHIELD_DEADLY_TIME         29
170 #define GAME_CONTROL_EXIT                       30
171 #define GAME_CONTROL_EM_EXIT                    31
172 #define GAME_CONTROL_SP_EXIT                    32
173 #define GAME_CONTROL_STEEL_EXIT                 33
174 #define GAME_CONTROL_EM_STEEL_EXIT              34
175 #define GAME_CONTROL_EMC_MAGIC_BALL             35
176 #define GAME_CONTROL_EMC_MAGIC_BALL_TIME        36
177 #define GAME_CONTROL_LIGHT_SWITCH               37
178 #define GAME_CONTROL_LIGHT_SWITCH_TIME          38
179 #define GAME_CONTROL_TIMEGATE_SWITCH            39
180 #define GAME_CONTROL_TIMEGATE_SWITCH_TIME       40
181 #define GAME_CONTROL_SWITCHGATE_SWITCH          41
182 #define GAME_CONTROL_EMC_LENSES                 42
183 #define GAME_CONTROL_EMC_LENSES_TIME            43
184 #define GAME_CONTROL_EMC_MAGNIFIER              44
185 #define GAME_CONTROL_EMC_MAGNIFIER_TIME         45
186 #define GAME_CONTROL_BALLOON_SWITCH             46
187 #define GAME_CONTROL_DYNABOMB_NUMBER            47
188 #define GAME_CONTROL_DYNABOMB_SIZE              48
189 #define GAME_CONTROL_DYNABOMB_POWER             49
190 #define GAME_CONTROL_PENGUINS                   50
191 #define GAME_CONTROL_SOKOBAN_OBJECTS            51
192 #define GAME_CONTROL_SOKOBAN_FIELDS             52
193 #define GAME_CONTROL_ROBOT_WHEEL                53
194 #define GAME_CONTROL_CONVEYOR_BELT_1            54
195 #define GAME_CONTROL_CONVEYOR_BELT_1_SWITCH     55
196 #define GAME_CONTROL_CONVEYOR_BELT_2            56
197 #define GAME_CONTROL_CONVEYOR_BELT_2_SWITCH     57
198 #define GAME_CONTROL_CONVEYOR_BELT_3            58
199 #define GAME_CONTROL_CONVEYOR_BELT_3_SWITCH     59
200 #define GAME_CONTROL_CONVEYOR_BELT_4            60
201 #define GAME_CONTROL_CONVEYOR_BELT_4_SWITCH     61
202 #define GAME_CONTROL_MAGIC_WALL                 62
203 #define GAME_CONTROL_MAGIC_WALL_TIME            63
204 #define GAME_CONTROL_BD_MAGIC_WALL              64
205 #define GAME_CONTROL_DC_MAGIC_WALL              65
206 #define GAME_CONTROL_PLAYER_NAME                66
207 #define GAME_CONTROL_LEVEL_NAME                 67
208 #define GAME_CONTROL_LEVEL_AUTHOR               68
209
210 struct GameControlInfo
211 {
212   int nr;
213
214   struct TextPosInfo *pos_text;
215   int type;
216   void *ptr;
217 };
218
219 static struct GameControlInfo game_controls[] =
220 {
221   {
222     GAME_CONTROL_LEVEL,
223     &game.panel.level,
224     TYPE_INTEGER,
225   },
226   {
227     GAME_CONTROL_GEMS,
228     &game.panel.gems,
229     TYPE_INTEGER,
230   },
231   {
232     GAME_CONTROL_INVENTORY,
233     &game.panel.inventory,
234     TYPE_INTEGER,
235   },
236   {
237     GAME_CONTROL_KEYS,
238     &game.panel.keys,
239     TYPE_INTEGER,
240   },
241   {
242     GAME_CONTROL_SCORE,
243     &game.panel.score,
244     TYPE_INTEGER,
245   },
246   {
247     GAME_CONTROL_TIME,
248     &game.panel.time,
249     TYPE_INTEGER,
250   },
251   {
252     GAME_CONTROL_TIME_HH,
253     &game.panel.time_hh,
254     TYPE_INTEGER,
255   },
256   {
257     GAME_CONTROL_TIME_MM,
258     &game.panel.time_mm,
259     TYPE_INTEGER,
260   },
261   {
262     GAME_CONTROL_TIME_SS,
263     &game.panel.time_ss,
264     TYPE_INTEGER,
265   },
266   {
267     GAME_CONTROL_DROP_NEXT_1,
268     &game.panel.drop_next_1,
269     TYPE_INTEGER,
270   },
271   {
272     GAME_CONTROL_DROP_NEXT_2,
273     &game.panel.drop_next_2,
274     TYPE_INTEGER,
275   },
276   {
277     GAME_CONTROL_DROP_NEXT_3,
278     &game.panel.drop_next_3,
279     TYPE_INTEGER,
280   },
281   {
282     GAME_CONTROL_DROP_NEXT_4,
283     &game.panel.drop_next_4,
284     TYPE_INTEGER,
285   },
286   {
287     GAME_CONTROL_DROP_NEXT_5,
288     &game.panel.drop_next_5,
289     TYPE_INTEGER,
290   },
291   {
292     GAME_CONTROL_DROP_NEXT_6,
293     &game.panel.drop_next_6,
294     TYPE_INTEGER,
295   },
296   {
297     GAME_CONTROL_DROP_NEXT_7,
298     &game.panel.drop_next_7,
299     TYPE_INTEGER,
300   },
301   {
302     GAME_CONTROL_DROP_NEXT_8,
303     &game.panel.drop_next_8,
304     TYPE_INTEGER,
305   },
306   {
307     GAME_CONTROL_EMC_KEYS,
308     &game.panel.emc_keys,
309     TYPE_INTEGER,
310   },
311   {
312     GAME_CONTROL_KEY_1,
313     &game.panel.key_1,
314     TYPE_INTEGER,
315   },
316   {
317     GAME_CONTROL_KEY_2,
318     &game.panel.key_2,
319     TYPE_INTEGER,
320   },
321   {
322     GAME_CONTROL_KEY_3,
323     &game.panel.key_3,
324     TYPE_INTEGER,
325   },
326   {
327     GAME_CONTROL_KEY_4,
328     &game.panel.key_4,
329     TYPE_INTEGER,
330   },
331   {
332     GAME_CONTROL_KEY_5,
333     &game.panel.key_5,
334     TYPE_INTEGER,
335   },
336   {
337     GAME_CONTROL_KEY_6,
338     &game.panel.key_6,
339     TYPE_INTEGER,
340   },
341   {
342     GAME_CONTROL_KEY_7,
343     &game.panel.key_7,
344     TYPE_INTEGER,
345   },
346   {
347     GAME_CONTROL_KEY_8,
348     &game.panel.key_8,
349     TYPE_INTEGER,
350   },
351   {
352     GAME_CONTROL_KEY_WHITE,
353     &game.panel.key_white,
354     TYPE_INTEGER,
355   },
356   {
357     GAME_CONTROL_KEY_WHITE_COUNT,
358     &game.panel.key_white_count,
359     TYPE_INTEGER,
360   },
361   {
362     GAME_CONTROL_SHIELD_NORMAL,
363     &game.panel.shield_normal,
364     TYPE_INTEGER,
365   },
366   {
367     GAME_CONTROL_SHIELD_NORMAL_TIME,
368     &game.panel.shield_normal_time,
369     TYPE_INTEGER,
370   },
371   {
372     GAME_CONTROL_SHIELD_DEADLY,
373     &game.panel.shield_deadly,
374     TYPE_INTEGER,
375   },
376   {
377     GAME_CONTROL_SHIELD_DEADLY_TIME,
378     &game.panel.shield_deadly_time,
379     TYPE_INTEGER,
380   },
381   {
382     GAME_CONTROL_EXIT,
383     &game.panel.exit,
384     TYPE_INTEGER,
385   },
386   {
387     GAME_CONTROL_EM_EXIT,
388     &game.panel.em_exit,
389     TYPE_INTEGER,
390   },
391   {
392     GAME_CONTROL_SP_EXIT,
393     &game.panel.sp_exit,
394     TYPE_INTEGER,
395   },
396   {
397     GAME_CONTROL_STEEL_EXIT,
398     &game.panel.steel_exit,
399     TYPE_INTEGER,
400   },
401   {
402     GAME_CONTROL_EM_STEEL_EXIT,
403     &game.panel.em_steel_exit,
404     TYPE_INTEGER,
405   },
406   {
407     GAME_CONTROL_EMC_MAGIC_BALL,
408     &game.panel.emc_magic_ball,
409     TYPE_INTEGER,
410   },
411   {
412     GAME_CONTROL_EMC_MAGIC_BALL_TIME,
413     &game.panel.emc_magic_ball_time,
414     TYPE_INTEGER,
415   },
416   {
417     GAME_CONTROL_LIGHT_SWITCH,
418     &game.panel.light_switch,
419     TYPE_INTEGER,
420   },
421   {
422     GAME_CONTROL_LIGHT_SWITCH_TIME,
423     &game.panel.light_switch_time,
424     TYPE_INTEGER,
425   },
426   {
427     GAME_CONTROL_TIMEGATE_SWITCH,
428     &game.panel.timegate_switch,
429     TYPE_INTEGER,
430   },
431   {
432     GAME_CONTROL_TIMEGATE_SWITCH_TIME,
433     &game.panel.timegate_switch_time,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_CONTROL_SWITCHGATE_SWITCH,
438     &game.panel.switchgate_switch,
439     TYPE_INTEGER,
440   },
441   {
442     GAME_CONTROL_EMC_LENSES,
443     &game.panel.emc_lenses,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_CONTROL_EMC_LENSES_TIME,
448     &game.panel.emc_lenses_time,
449     TYPE_INTEGER,
450   },
451   {
452     GAME_CONTROL_EMC_MAGNIFIER,
453     &game.panel.emc_magnifier,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_CONTROL_EMC_MAGNIFIER_TIME,
458     &game.panel.emc_magnifier_time,
459     TYPE_INTEGER,
460   },
461   {
462     GAME_CONTROL_BALLOON_SWITCH,
463     &game.panel.balloon_switch,
464     TYPE_INTEGER,
465   },
466   {
467     GAME_CONTROL_DYNABOMB_NUMBER,
468     &game.panel.dynabomb_number,
469     TYPE_INTEGER,
470   },
471   {
472     GAME_CONTROL_DYNABOMB_SIZE,
473     &game.panel.dynabomb_size,
474     TYPE_INTEGER,
475   },
476   {
477     GAME_CONTROL_DYNABOMB_POWER,
478     &game.panel.dynabomb_power,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_CONTROL_PENGUINS,
483     &game.panel.penguins,
484     TYPE_INTEGER,
485   },
486   {
487     GAME_CONTROL_SOKOBAN_OBJECTS,
488     &game.panel.sokoban_objects,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_CONTROL_SOKOBAN_FIELDS,
493     &game.panel.sokoban_fields,
494     TYPE_INTEGER,
495   },
496   {
497     GAME_CONTROL_ROBOT_WHEEL,
498     &game.panel.robot_wheel,
499     TYPE_INTEGER,
500   },
501   {
502     GAME_CONTROL_CONVEYOR_BELT_1,
503     &game.panel.conveyor_belt_1,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_CONTROL_CONVEYOR_BELT_1_SWITCH,
508     &game.panel.conveyor_belt_1_switch,
509     TYPE_INTEGER,
510   },
511   {
512     GAME_CONTROL_CONVEYOR_BELT_2,
513     &game.panel.conveyor_belt_2,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_CONTROL_CONVEYOR_BELT_2_SWITCH,
518     &game.panel.conveyor_belt_2_switch,
519     TYPE_INTEGER,
520   },
521   {
522     GAME_CONTROL_CONVEYOR_BELT_3,
523     &game.panel.conveyor_belt_3,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_CONTROL_CONVEYOR_BELT_3_SWITCH,
528     &game.panel.conveyor_belt_3_switch,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_CONTROL_CONVEYOR_BELT_4,
533     &game.panel.conveyor_belt_4,
534     TYPE_INTEGER,
535   },
536   {
537     GAME_CONTROL_CONVEYOR_BELT_4_SWITCH,
538     &game.panel.conveyor_belt_4_switch,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_CONTROL_MAGIC_WALL,
543     &game.panel.magic_wall,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_CONTROL_MAGIC_WALL_TIME,
548     &game.panel.magic_wall_time,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_CONTROL_BD_MAGIC_WALL,
553     &game.panel.bd_magic_wall,
554     TYPE_INTEGER,
555   },
556   {
557     GAME_CONTROL_DC_MAGIC_WALL,
558     &game.panel.dc_magic_wall,
559     TYPE_INTEGER,
560   },
561   {
562     GAME_CONTROL_PLAYER_NAME,
563     &game.panel.player_name,
564     TYPE_INTEGER,
565   },
566   {
567     GAME_CONTROL_LEVEL_NAME,
568     &game.panel.level_name,
569     TYPE_INTEGER,
570   },
571   {
572     GAME_CONTROL_LEVEL_AUTHOR,
573     &game.panel.level_author,
574     TYPE_INTEGER,
575   },
576
577   {
578     -1,
579     NULL,
580     -1,
581     NULL
582   }
583 };
584 #endif
585
586
587 /* values for delayed check of falling and moving elements and for collision */
588 #define CHECK_DELAY_MOVING      3
589 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
590 #define CHECK_DELAY_COLLISION   2
591 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
592
593 /* values for initial player move delay (initial delay counter value) */
594 #define INITIAL_MOVE_DELAY_OFF  -1
595 #define INITIAL_MOVE_DELAY_ON   0
596
597 /* values for player movement speed (which is in fact a delay value) */
598 #define MOVE_DELAY_MIN_SPEED    32
599 #define MOVE_DELAY_NORMAL_SPEED 8
600 #define MOVE_DELAY_HIGH_SPEED   4
601 #define MOVE_DELAY_MAX_SPEED    1
602
603 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
604 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
605
606 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
607 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
608
609 /* values for other actions */
610 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
611 #define MOVE_STEPSIZE_MIN       (1)
612 #define MOVE_STEPSIZE_MAX       (TILEX)
613
614 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
615 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
616
617 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
618
619 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
620                                  RND(element_info[e].push_delay_random))
621 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
622                                  RND(element_info[e].drop_delay_random))
623 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
624                                  RND(element_info[e].move_delay_random))
625 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
626                                     (element_info[e].move_delay_random))
627 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
628                                  RND(element_info[e].ce_value_random_initial))
629 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
630 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
631                                  RND((c)->delay_random * (c)->delay_frames))
632 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
633                                  RND((c)->delay_random))
634
635
636 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
637          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
638
639 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
640         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
641          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
642          (be) + (e) - EL_SELF)
643
644 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
645         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
646          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
647          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
648          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
649          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
650          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
651          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
652          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
653          (e))
654
655 #define CAN_GROW_INTO(e)                                                \
656         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
657
658 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
659                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
660                                         (condition)))
661
662 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
663                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
664                                         (CAN_MOVE_INTO_ACID(e) &&       \
665                                          Feld[x][y] == EL_ACID) ||      \
666                                         (condition)))
667
668 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
669                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
670                                         (CAN_MOVE_INTO_ACID(e) &&       \
671                                          Feld[x][y] == EL_ACID) ||      \
672                                         (condition)))
673
674 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
675                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
676                                         (condition) ||                  \
677                                         (CAN_MOVE_INTO_ACID(e) &&       \
678                                          Feld[x][y] == EL_ACID) ||      \
679                                         (DONT_COLLIDE_WITH(e) &&        \
680                                          IS_PLAYER(x, y) &&             \
681                                          !PLAYER_ENEMY_PROTECTED(x, y))))
682
683 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
684         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
685
686 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
687         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
688
689 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
690         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
691
692 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
693         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
694                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
695
696 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
697         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
698
699 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
700         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
701
702 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
703         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
704
705 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
706         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
707
708 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
709         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
710
711 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
712         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
713                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
714                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
715                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
716                                                  IS_FOOD_PENGUIN(Feld[x][y])))
717 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
718         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
719
720 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
721         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
722
723 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
724         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
725
726 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
727         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
728                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
729
730 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
731
732 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
733                 (!IS_PLAYER(x, y) &&                                    \
734                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
735
736 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
737         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
738
739 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
740 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
741
742 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
743 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
744 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
745 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
746
747 /* game button identifiers */
748 #define GAME_CTRL_ID_STOP               0
749 #define GAME_CTRL_ID_PAUSE              1
750 #define GAME_CTRL_ID_PLAY               2
751 #define SOUND_CTRL_ID_MUSIC             3
752 #define SOUND_CTRL_ID_LOOPS             4
753 #define SOUND_CTRL_ID_SIMPLE            5
754
755 #define NUM_GAME_BUTTONS                6
756
757
758 /* forward declaration for internal use */
759
760 static void CreateField(int, int, int);
761
762 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
763 static void AdvanceFrameAndPlayerCounters(int);
764
765 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
766 static boolean MovePlayer(struct PlayerInfo *, int, int);
767 static void ScrollPlayer(struct PlayerInfo *, int);
768 static void ScrollScreen(struct PlayerInfo *, int);
769
770 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
771
772 static void InitBeltMovement(void);
773 static void CloseAllOpenTimegates(void);
774 static void CheckGravityMovement(struct PlayerInfo *);
775 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
776 static void KillPlayerUnlessEnemyProtected(int, int);
777 static void KillPlayerUnlessExplosionProtected(int, int);
778
779 static void TestIfPlayerTouchesCustomElement(int, int);
780 static void TestIfElementTouchesCustomElement(int, int);
781 static void TestIfElementHitsCustomElement(int, int, int);
782 #if 0
783 static void TestIfElementSmashesCustomElement(int, int, int);
784 #endif
785
786 static void HandleElementChange(int, int, int);
787 static void ExecuteCustomElementAction(int, int, int, int);
788 static boolean ChangeElement(int, int, int, int);
789
790 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
791 #define CheckTriggeredElementChange(x, y, e, ev)                        \
792         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
793 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
794         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
795 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
796         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
797 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
798         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
799
800 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
801 #define CheckElementChange(x, y, e, te, ev)                             \
802         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
803 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
804         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
805 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
806         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
807
808 static void PlayLevelSound(int, int, int);
809 static void PlayLevelSoundNearest(int, int, int);
810 static void PlayLevelSoundAction(int, int, int);
811 static void PlayLevelSoundElementAction(int, int, int, int);
812 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
813 static void PlayLevelSoundActionIfLoop(int, int, int);
814 static void StopLevelSoundActionIfLoop(int, int, int);
815 static void PlayLevelMusic();
816
817 static void MapGameButtons();
818 static void HandleGameButtons(struct GadgetInfo *);
819
820 int AmoebeNachbarNr(int, int);
821 void AmoebeUmwandeln(int, int);
822 void ContinueMoving(int, int);
823 void Bang(int, int);
824 void InitMovDir(int, int);
825 void InitAmoebaNr(int, int);
826 int NewHiScore(void);
827
828 void TestIfGoodThingHitsBadThing(int, int, int);
829 void TestIfBadThingHitsGoodThing(int, int, int);
830 void TestIfPlayerTouchesBadThing(int, int);
831 void TestIfPlayerRunsIntoBadThing(int, int, int);
832 void TestIfBadThingTouchesPlayer(int, int);
833 void TestIfBadThingRunsIntoPlayer(int, int, int);
834 void TestIfFriendTouchesBadThing(int, int);
835 void TestIfBadThingTouchesFriend(int, int);
836 void TestIfBadThingTouchesOtherBadThing(int, int);
837
838 void KillPlayer(struct PlayerInfo *);
839 void BuryPlayer(struct PlayerInfo *);
840 void RemovePlayer(struct PlayerInfo *);
841
842 boolean SnapField(struct PlayerInfo *, int, int);
843 boolean DropElement(struct PlayerInfo *);
844
845 static int getInvisibleActiveFromInvisibleElement(int);
846 static int getInvisibleFromInvisibleActiveElement(int);
847
848 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
849
850 /* for detection of endless loops, caused by custom element programming */
851 /* (using maximal playfield width x 10 is just a rough approximation) */
852 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
853
854 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
855 {                                                                       \
856   if (recursion_loop_detected)                                          \
857     return (rc);                                                        \
858                                                                         \
859   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
860   {                                                                     \
861     recursion_loop_detected = TRUE;                                     \
862     recursion_loop_element = (e);                                       \
863   }                                                                     \
864                                                                         \
865   recursion_loop_depth++;                                               \
866 }
867
868 #define RECURSION_LOOP_DETECTION_END()                                  \
869 {                                                                       \
870   recursion_loop_depth--;                                               \
871 }
872
873 static int recursion_loop_depth;
874 static boolean recursion_loop_detected;
875 static boolean recursion_loop_element;
876
877
878 /* ------------------------------------------------------------------------- */
879 /* definition of elements that automatically change to other elements after  */
880 /* a specified time, eventually calling a function when changing             */
881 /* ------------------------------------------------------------------------- */
882
883 /* forward declaration for changer functions */
884 static void InitBuggyBase(int, int);
885 static void WarnBuggyBase(int, int);
886
887 static void InitTrap(int, int);
888 static void ActivateTrap(int, int);
889 static void ChangeActiveTrap(int, int);
890
891 static void InitRobotWheel(int, int);
892 static void RunRobotWheel(int, int);
893 static void StopRobotWheel(int, int);
894
895 static void InitTimegateWheel(int, int);
896 static void RunTimegateWheel(int, int);
897
898 static void InitMagicBallDelay(int, int);
899 static void ActivateMagicBall(int, int);
900
901 struct ChangingElementInfo
902 {
903   int element;
904   int target_element;
905   int change_delay;
906   void (*pre_change_function)(int x, int y);
907   void (*change_function)(int x, int y);
908   void (*post_change_function)(int x, int y);
909 };
910
911 static struct ChangingElementInfo change_delay_list[] =
912 {
913   {
914     EL_NUT_BREAKING,
915     EL_EMERALD,
916     6,
917     NULL,
918     NULL,
919     NULL
920   },
921   {
922     EL_PEARL_BREAKING,
923     EL_EMPTY,
924     8,
925     NULL,
926     NULL,
927     NULL
928   },
929   {
930     EL_EXIT_OPENING,
931     EL_EXIT_OPEN,
932     29,
933     NULL,
934     NULL,
935     NULL
936   },
937   {
938     EL_EXIT_CLOSING,
939     EL_EXIT_CLOSED,
940     29,
941     NULL,
942     NULL,
943     NULL
944   },
945   {
946     EL_STEEL_EXIT_OPENING,
947     EL_STEEL_EXIT_OPEN,
948     29,
949     NULL,
950     NULL,
951     NULL
952   },
953   {
954     EL_STEEL_EXIT_CLOSING,
955     EL_STEEL_EXIT_CLOSED,
956     29,
957     NULL,
958     NULL,
959     NULL
960   },
961   {
962     EL_EM_EXIT_OPENING,
963     EL_EM_EXIT_OPEN,
964     29,
965     NULL,
966     NULL,
967     NULL
968   },
969   {
970     EL_EM_EXIT_CLOSING,
971 #if 1
972     EL_EMPTY,
973 #else
974     EL_EM_EXIT_CLOSED,
975 #endif
976     29,
977     NULL,
978     NULL,
979     NULL
980   },
981   {
982     EL_EM_STEEL_EXIT_OPENING,
983     EL_EM_STEEL_EXIT_OPEN,
984     29,
985     NULL,
986     NULL,
987     NULL
988   },
989   {
990     EL_EM_STEEL_EXIT_CLOSING,
991 #if 1
992     EL_STEELWALL,
993 #else
994     EL_EM_STEEL_EXIT_CLOSED,
995 #endif
996     29,
997     NULL,
998     NULL,
999     NULL
1000   },
1001   {
1002     EL_SP_EXIT_OPENING,
1003     EL_SP_EXIT_OPEN,
1004     29,
1005     NULL,
1006     NULL,
1007     NULL
1008   },
1009   {
1010     EL_SP_EXIT_CLOSING,
1011     EL_SP_EXIT_CLOSED,
1012     29,
1013     NULL,
1014     NULL,
1015     NULL
1016   },
1017   {
1018     EL_SWITCHGATE_OPENING,
1019     EL_SWITCHGATE_OPEN,
1020     29,
1021     NULL,
1022     NULL,
1023     NULL
1024   },
1025   {
1026     EL_SWITCHGATE_CLOSING,
1027     EL_SWITCHGATE_CLOSED,
1028     29,
1029     NULL,
1030     NULL,
1031     NULL
1032   },
1033   {
1034     EL_TIMEGATE_OPENING,
1035     EL_TIMEGATE_OPEN,
1036     29,
1037     NULL,
1038     NULL,
1039     NULL
1040   },
1041   {
1042     EL_TIMEGATE_CLOSING,
1043     EL_TIMEGATE_CLOSED,
1044     29,
1045     NULL,
1046     NULL,
1047     NULL
1048   },
1049
1050   {
1051     EL_ACID_SPLASH_LEFT,
1052     EL_EMPTY,
1053     8,
1054     NULL,
1055     NULL,
1056     NULL
1057   },
1058   {
1059     EL_ACID_SPLASH_RIGHT,
1060     EL_EMPTY,
1061     8,
1062     NULL,
1063     NULL,
1064     NULL
1065   },
1066   {
1067     EL_SP_BUGGY_BASE,
1068     EL_SP_BUGGY_BASE_ACTIVATING,
1069     0,
1070     InitBuggyBase,
1071     NULL,
1072     NULL
1073   },
1074   {
1075     EL_SP_BUGGY_BASE_ACTIVATING,
1076     EL_SP_BUGGY_BASE_ACTIVE,
1077     0,
1078     InitBuggyBase,
1079     NULL,
1080     NULL
1081   },
1082   {
1083     EL_SP_BUGGY_BASE_ACTIVE,
1084     EL_SP_BUGGY_BASE,
1085     0,
1086     InitBuggyBase,
1087     WarnBuggyBase,
1088     NULL
1089   },
1090   {
1091     EL_TRAP,
1092     EL_TRAP_ACTIVE,
1093     0,
1094     InitTrap,
1095     NULL,
1096     ActivateTrap
1097   },
1098   {
1099     EL_TRAP_ACTIVE,
1100     EL_TRAP,
1101     31,
1102     NULL,
1103     ChangeActiveTrap,
1104     NULL
1105   },
1106   {
1107     EL_ROBOT_WHEEL_ACTIVE,
1108     EL_ROBOT_WHEEL,
1109     0,
1110     InitRobotWheel,
1111     RunRobotWheel,
1112     StopRobotWheel
1113   },
1114   {
1115     EL_TIMEGATE_SWITCH_ACTIVE,
1116     EL_TIMEGATE_SWITCH,
1117     0,
1118     InitTimegateWheel,
1119     RunTimegateWheel,
1120     NULL
1121   },
1122   {
1123     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1124     EL_DC_TIMEGATE_SWITCH,
1125     0,
1126     InitTimegateWheel,
1127     RunTimegateWheel,
1128     NULL
1129   },
1130   {
1131     EL_EMC_MAGIC_BALL_ACTIVE,
1132     EL_EMC_MAGIC_BALL_ACTIVE,
1133     0,
1134     InitMagicBallDelay,
1135     NULL,
1136     ActivateMagicBall
1137   },
1138   {
1139     EL_EMC_SPRING_BUMPER_ACTIVE,
1140     EL_EMC_SPRING_BUMPER,
1141     8,
1142     NULL,
1143     NULL,
1144     NULL
1145   },
1146   {
1147     EL_DIAGONAL_SHRINKING,
1148     EL_UNDEFINED,
1149     0,
1150     NULL,
1151     NULL,
1152     NULL
1153   },
1154   {
1155     EL_DIAGONAL_GROWING,
1156     EL_UNDEFINED,
1157     0,
1158     NULL,
1159     NULL,
1160     NULL,
1161   },
1162
1163   {
1164     EL_UNDEFINED,
1165     EL_UNDEFINED,
1166     -1,
1167     NULL,
1168     NULL,
1169     NULL
1170   }
1171 };
1172
1173 struct
1174 {
1175   int element;
1176   int push_delay_fixed, push_delay_random;
1177 }
1178 push_delay_list[] =
1179 {
1180   { EL_SPRING,                  0, 0 },
1181   { EL_BALLOON,                 0, 0 },
1182
1183   { EL_SOKOBAN_OBJECT,          2, 0 },
1184   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1185   { EL_SATELLITE,               2, 0 },
1186   { EL_SP_DISK_YELLOW,          2, 0 },
1187
1188   { EL_UNDEFINED,               0, 0 },
1189 };
1190
1191 struct
1192 {
1193   int element;
1194   int move_stepsize;
1195 }
1196 move_stepsize_list[] =
1197 {
1198   { EL_AMOEBA_DROP,             2 },
1199   { EL_AMOEBA_DROPPING,         2 },
1200   { EL_QUICKSAND_FILLING,       1 },
1201   { EL_QUICKSAND_EMPTYING,      1 },
1202   { EL_QUICKSAND_FAST_FILLING,  2 },
1203   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1204   { EL_MAGIC_WALL_FILLING,      2 },
1205   { EL_MAGIC_WALL_EMPTYING,     2 },
1206   { EL_BD_MAGIC_WALL_FILLING,   2 },
1207   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1208   { EL_DC_MAGIC_WALL_FILLING,   2 },
1209   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1210
1211   { EL_UNDEFINED,               0 },
1212 };
1213
1214 struct
1215 {
1216   int element;
1217   int count;
1218 }
1219 collect_count_list[] =
1220 {
1221   { EL_EMERALD,                 1 },
1222   { EL_BD_DIAMOND,              1 },
1223   { EL_EMERALD_YELLOW,          1 },
1224   { EL_EMERALD_RED,             1 },
1225   { EL_EMERALD_PURPLE,          1 },
1226   { EL_DIAMOND,                 3 },
1227   { EL_SP_INFOTRON,             1 },
1228   { EL_PEARL,                   5 },
1229   { EL_CRYSTAL,                 8 },
1230
1231   { EL_UNDEFINED,               0 },
1232 };
1233
1234 struct
1235 {
1236   int element;
1237   int direction;
1238 }
1239 access_direction_list[] =
1240 {
1241   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1242   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1243   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1244   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1245   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1246   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1247   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1248   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1249   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1250   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1251   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1252
1253   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1254   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1255   { EL_SP_PORT_UP,                                                   MV_DOWN },
1256   { EL_SP_PORT_DOWN,                                         MV_UP           },
1257   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1258   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1259   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1260   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1261   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1262   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1263   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1264   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1265   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1266   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1267   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1268   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1269   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1270   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1271   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1272
1273   { EL_UNDEFINED,                       MV_NONE                              }
1274 };
1275
1276 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1277
1278 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1279 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1280 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1281                                  IS_JUST_CHANGING(x, y))
1282
1283 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1284
1285 /* static variables for playfield scan mode (scanning forward or backward) */
1286 static int playfield_scan_start_x = 0;
1287 static int playfield_scan_start_y = 0;
1288 static int playfield_scan_delta_x = 1;
1289 static int playfield_scan_delta_y = 1;
1290
1291 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1292                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1293                                      (y) += playfield_scan_delta_y)     \
1294                                 for ((x) = playfield_scan_start_x;      \
1295                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1296                                      (x) += playfield_scan_delta_x)
1297
1298 #ifdef DEBUG
1299 void DEBUG_SetMaximumDynamite()
1300 {
1301   int i;
1302
1303   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1304     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1305       local_player->inventory_element[local_player->inventory_size++] =
1306         EL_DYNAMITE;
1307 }
1308 #endif
1309
1310 static void InitPlayfieldScanModeVars()
1311 {
1312   if (game.use_reverse_scan_direction)
1313   {
1314     playfield_scan_start_x = lev_fieldx - 1;
1315     playfield_scan_start_y = lev_fieldy - 1;
1316
1317     playfield_scan_delta_x = -1;
1318     playfield_scan_delta_y = -1;
1319   }
1320   else
1321   {
1322     playfield_scan_start_x = 0;
1323     playfield_scan_start_y = 0;
1324
1325     playfield_scan_delta_x = 1;
1326     playfield_scan_delta_y = 1;
1327   }
1328 }
1329
1330 static void InitPlayfieldScanMode(int mode)
1331 {
1332   game.use_reverse_scan_direction =
1333     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1334
1335   InitPlayfieldScanModeVars();
1336 }
1337
1338 static int get_move_delay_from_stepsize(int move_stepsize)
1339 {
1340   move_stepsize =
1341     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1342
1343   /* make sure that stepsize value is always a power of 2 */
1344   move_stepsize = (1 << log_2(move_stepsize));
1345
1346   return TILEX / move_stepsize;
1347 }
1348
1349 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1350                                boolean init_game)
1351 {
1352   int player_nr = player->index_nr;
1353   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1354   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1355
1356   /* do no immediately change move delay -- the player might just be moving */
1357   player->move_delay_value_next = move_delay;
1358
1359   /* information if player can move must be set separately */
1360   player->cannot_move = cannot_move;
1361
1362   if (init_game)
1363   {
1364     player->move_delay       = game.initial_move_delay[player_nr];
1365     player->move_delay_value = game.initial_move_delay_value[player_nr];
1366
1367     player->move_delay_value_next = -1;
1368
1369     player->move_delay_reset_counter = 0;
1370   }
1371 }
1372
1373 void GetPlayerConfig()
1374 {
1375   GameFrameDelay = setup.game_frame_delay;
1376
1377   if (!audio.sound_available)
1378     setup.sound_simple = FALSE;
1379
1380   if (!audio.loops_available)
1381     setup.sound_loops = FALSE;
1382
1383   if (!audio.music_available)
1384     setup.sound_music = FALSE;
1385
1386   if (!video.fullscreen_available)
1387     setup.fullscreen = FALSE;
1388
1389   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1390
1391   SetAudioMode(setup.sound);
1392   InitJoysticks();
1393 }
1394
1395 int GetElementFromGroupElement(int element)
1396 {
1397   if (IS_GROUP_ELEMENT(element))
1398   {
1399     struct ElementGroupInfo *group = element_info[element].group;
1400     int last_anim_random_frame = gfx.anim_random_frame;
1401     int element_pos;
1402
1403     if (group->choice_mode == ANIM_RANDOM)
1404       gfx.anim_random_frame = RND(group->num_elements_resolved);
1405
1406     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1407                                     group->choice_mode, 0,
1408                                     group->choice_pos);
1409
1410     if (group->choice_mode == ANIM_RANDOM)
1411       gfx.anim_random_frame = last_anim_random_frame;
1412
1413     group->choice_pos++;
1414
1415     element = group->element_resolved[element_pos];
1416   }
1417
1418   return element;
1419 }
1420
1421 static void InitPlayerField(int x, int y, int element, boolean init_game)
1422 {
1423   if (element == EL_SP_MURPHY)
1424   {
1425     if (init_game)
1426     {
1427       if (stored_player[0].present)
1428       {
1429         Feld[x][y] = EL_SP_MURPHY_CLONE;
1430
1431         return;
1432       }
1433       else
1434       {
1435         stored_player[0].use_murphy = TRUE;
1436
1437         if (!level.use_artwork_element[0])
1438           stored_player[0].artwork_element = EL_SP_MURPHY;
1439       }
1440
1441       Feld[x][y] = EL_PLAYER_1;
1442     }
1443   }
1444
1445   if (init_game)
1446   {
1447     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1448     int jx = player->jx, jy = player->jy;
1449
1450     player->present = TRUE;
1451
1452     player->block_last_field = (element == EL_SP_MURPHY ?
1453                                 level.sp_block_last_field :
1454                                 level.block_last_field);
1455
1456     /* ---------- initialize player's last field block delay --------------- */
1457
1458     /* always start with reliable default value (no adjustment needed) */
1459     player->block_delay_adjustment = 0;
1460
1461     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1462     if (player->block_last_field && element == EL_SP_MURPHY)
1463       player->block_delay_adjustment = 1;
1464
1465     /* special case 2: in game engines before 3.1.1, blocking was different */
1466     if (game.use_block_last_field_bug)
1467       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1468
1469     if (!options.network || player->connected)
1470     {
1471       player->active = TRUE;
1472
1473       /* remove potentially duplicate players */
1474       if (StorePlayer[jx][jy] == Feld[x][y])
1475         StorePlayer[jx][jy] = 0;
1476
1477       StorePlayer[x][y] = Feld[x][y];
1478
1479       if (options.debug)
1480       {
1481         printf("Player %d activated.\n", player->element_nr);
1482         printf("[Local player is %d and currently %s.]\n",
1483                local_player->element_nr,
1484                local_player->active ? "active" : "not active");
1485       }
1486     }
1487
1488     Feld[x][y] = EL_EMPTY;
1489
1490     player->jx = player->last_jx = x;
1491     player->jy = player->last_jy = y;
1492   }
1493 }
1494
1495 static void InitField(int x, int y, boolean init_game)
1496 {
1497   int element = Feld[x][y];
1498
1499   switch (element)
1500   {
1501     case EL_SP_MURPHY:
1502     case EL_PLAYER_1:
1503     case EL_PLAYER_2:
1504     case EL_PLAYER_3:
1505     case EL_PLAYER_4:
1506       InitPlayerField(x, y, element, init_game);
1507       break;
1508
1509     case EL_SOKOBAN_FIELD_PLAYER:
1510       element = Feld[x][y] = EL_PLAYER_1;
1511       InitField(x, y, init_game);
1512
1513       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1514       InitField(x, y, init_game);
1515       break;
1516
1517     case EL_SOKOBAN_FIELD_EMPTY:
1518       local_player->sokobanfields_still_needed++;
1519       break;
1520
1521     case EL_STONEBLOCK:
1522       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1523         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1524       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1525         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1526       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1527         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1528       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1529         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1530       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1531         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1532       break;
1533
1534     case EL_BUG:
1535     case EL_BUG_RIGHT:
1536     case EL_BUG_UP:
1537     case EL_BUG_LEFT:
1538     case EL_BUG_DOWN:
1539     case EL_SPACESHIP:
1540     case EL_SPACESHIP_RIGHT:
1541     case EL_SPACESHIP_UP:
1542     case EL_SPACESHIP_LEFT:
1543     case EL_SPACESHIP_DOWN:
1544     case EL_BD_BUTTERFLY:
1545     case EL_BD_BUTTERFLY_RIGHT:
1546     case EL_BD_BUTTERFLY_UP:
1547     case EL_BD_BUTTERFLY_LEFT:
1548     case EL_BD_BUTTERFLY_DOWN:
1549     case EL_BD_FIREFLY:
1550     case EL_BD_FIREFLY_RIGHT:
1551     case EL_BD_FIREFLY_UP:
1552     case EL_BD_FIREFLY_LEFT:
1553     case EL_BD_FIREFLY_DOWN:
1554     case EL_PACMAN_RIGHT:
1555     case EL_PACMAN_UP:
1556     case EL_PACMAN_LEFT:
1557     case EL_PACMAN_DOWN:
1558     case EL_YAMYAM:
1559     case EL_YAMYAM_LEFT:
1560     case EL_YAMYAM_RIGHT:
1561     case EL_YAMYAM_UP:
1562     case EL_YAMYAM_DOWN:
1563     case EL_DARK_YAMYAM:
1564     case EL_ROBOT:
1565     case EL_PACMAN:
1566     case EL_SP_SNIKSNAK:
1567     case EL_SP_ELECTRON:
1568     case EL_MOLE:
1569     case EL_MOLE_LEFT:
1570     case EL_MOLE_RIGHT:
1571     case EL_MOLE_UP:
1572     case EL_MOLE_DOWN:
1573       InitMovDir(x, y);
1574       break;
1575
1576     case EL_AMOEBA_FULL:
1577     case EL_BD_AMOEBA:
1578       InitAmoebaNr(x, y);
1579       break;
1580
1581     case EL_AMOEBA_DROP:
1582       if (y == lev_fieldy - 1)
1583       {
1584         Feld[x][y] = EL_AMOEBA_GROWING;
1585         Store[x][y] = EL_AMOEBA_WET;
1586       }
1587       break;
1588
1589     case EL_DYNAMITE_ACTIVE:
1590     case EL_SP_DISK_RED_ACTIVE:
1591     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1592     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1593     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1594     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1595       MovDelay[x][y] = 96;
1596       break;
1597
1598     case EL_EM_DYNAMITE_ACTIVE:
1599       MovDelay[x][y] = 32;
1600       break;
1601
1602     case EL_LAMP:
1603       local_player->lights_still_needed++;
1604       break;
1605
1606     case EL_PENGUIN:
1607       local_player->friends_still_needed++;
1608       break;
1609
1610     case EL_PIG:
1611     case EL_DRAGON:
1612       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1613       break;
1614
1615     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1616     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1617     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1618     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1619     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1620     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1621     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1622     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1623     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1624     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1625     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1626     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1627       if (init_game)
1628       {
1629         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1630         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1631         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1632
1633         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1634         {
1635           game.belt_dir[belt_nr] = belt_dir;
1636           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1637         }
1638         else    /* more than one switch -- set it like the first switch */
1639         {
1640           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1641         }
1642       }
1643       break;
1644
1645 #if !USE_BOTH_SWITCHGATE_SWITCHES
1646     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1647       if (init_game)
1648         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1649       break;
1650
1651     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1652       if (init_game)
1653         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1654       break;
1655 #endif
1656
1657     case EL_LIGHT_SWITCH_ACTIVE:
1658       if (init_game)
1659         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1660       break;
1661
1662     case EL_INVISIBLE_STEELWALL:
1663     case EL_INVISIBLE_WALL:
1664     case EL_INVISIBLE_SAND:
1665       if (game.light_time_left > 0 ||
1666           game.lenses_time_left > 0)
1667         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1668       break;
1669
1670     case EL_EMC_MAGIC_BALL:
1671       if (game.ball_state)
1672         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1673       break;
1674
1675     case EL_EMC_MAGIC_BALL_SWITCH:
1676       if (game.ball_state)
1677         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1678       break;
1679
1680     default:
1681       if (IS_CUSTOM_ELEMENT(element))
1682       {
1683         if (CAN_MOVE(element))
1684           InitMovDir(x, y);
1685
1686 #if USE_NEW_CUSTOM_VALUE
1687         if (!element_info[element].use_last_ce_value || init_game)
1688           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1689 #endif
1690       }
1691       else if (IS_GROUP_ELEMENT(element))
1692       {
1693         Feld[x][y] = GetElementFromGroupElement(element);
1694
1695         InitField(x, y, init_game);
1696       }
1697
1698       break;
1699   }
1700
1701   if (!init_game)
1702     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1703 }
1704
1705 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1706 {
1707   InitField(x, y, init_game);
1708
1709   /* not needed to call InitMovDir() -- already done by InitField()! */
1710   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1711       CAN_MOVE(Feld[x][y]))
1712     InitMovDir(x, y);
1713 }
1714
1715 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1716 {
1717   int old_element = Feld[x][y];
1718
1719   InitField(x, y, init_game);
1720
1721   /* not needed to call InitMovDir() -- already done by InitField()! */
1722   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1723       CAN_MOVE(old_element) &&
1724       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1725     InitMovDir(x, y);
1726
1727   /* this case is in fact a combination of not less than three bugs:
1728      first, it calls InitMovDir() for elements that can move, although this is
1729      already done by InitField(); then, it checks the element that was at this
1730      field _before_ the call to InitField() (which can change it); lastly, it
1731      was not called for "mole with direction" elements, which were treated as
1732      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1733   */
1734 }
1735
1736 #if 1
1737
1738 void DrawGameValue_Emeralds(int value)
1739 {
1740   struct TextPosInfo *pos = &game.panel.gems;
1741 #if 1
1742   int font_nr = pos->font;
1743 #else
1744   int font_nr = FONT_TEXT_2;
1745 #endif
1746   int font_width = getFontWidth(font_nr);
1747   int chars = pos->chars;
1748
1749   if (PANEL_DEACTIVATED(pos))
1750     return;
1751
1752   pos->width = chars * font_width;
1753
1754   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1755 }
1756
1757 void DrawGameValue_Dynamite(int value)
1758 {
1759   struct TextPosInfo *pos = &game.panel.inventory;
1760 #if 1
1761   int font_nr = pos->font;
1762 #else
1763   int font_nr = FONT_TEXT_2;
1764 #endif
1765   int font_width = getFontWidth(font_nr);
1766   int chars = pos->chars;
1767
1768   if (PANEL_DEACTIVATED(pos))
1769     return;
1770
1771   pos->width = chars * font_width;
1772
1773   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1774 }
1775
1776 void DrawGameValue_Score(int value)
1777 {
1778   struct TextPosInfo *pos = &game.panel.score;
1779 #if 1
1780   int font_nr = pos->font;
1781 #else
1782   int font_nr = FONT_TEXT_2;
1783 #endif
1784   int font_width = getFontWidth(font_nr);
1785   int chars = pos->chars;
1786
1787   if (PANEL_DEACTIVATED(pos))
1788     return;
1789
1790   pos->width = chars * font_width;
1791
1792   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1793 }
1794
1795 void DrawGameValue_Time(int value)
1796 {
1797   struct TextPosInfo *pos = &game.panel.time;
1798   static int last_value = -1;
1799   int chars1 = 3;
1800   int chars2 = 4;
1801   int chars = pos->chars;
1802 #if 1
1803   int font1_nr = pos->font;
1804   int font2_nr = pos->font_alt;
1805 #else
1806   int font1_nr = FONT_TEXT_2;
1807   int font2_nr = FONT_TEXT_1;
1808 #endif
1809   int font_nr = font1_nr;
1810   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
1811
1812   if (PANEL_DEACTIVATED(pos))
1813     return;
1814
1815   if (use_dynamic_chars)                /* use dynamic number of chars */
1816   {
1817     chars   = (value < 1000 ? chars1   : chars2);
1818     font_nr = (value < 1000 ? font1_nr : font2_nr);
1819   }
1820
1821   /* clear background if value just changed its size (dynamic chars only) */
1822   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
1823   {
1824     int width1 = chars1 * getFontWidth(font1_nr);
1825     int width2 = chars2 * getFontWidth(font2_nr);
1826     int max_width = MAX(width1, width2);
1827     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
1828
1829     pos->width = max_width;
1830
1831     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
1832                                max_width, max_height);
1833   }
1834
1835   pos->width = chars * getFontWidth(font_nr);
1836
1837   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1838
1839   last_value = value;
1840 }
1841
1842 void DrawGameValue_Level(int value)
1843 {
1844   struct TextPosInfo *pos = &game.panel.level_number;
1845   int chars1 = 2;
1846   int chars2 = 3;
1847   int chars = pos->chars;
1848 #if 1
1849   int font1_nr = pos->font;
1850   int font2_nr = pos->font_alt;
1851 #else
1852   int font1_nr = FONT_TEXT_2;
1853   int font2_nr = FONT_TEXT_1;
1854 #endif
1855   int font_nr = font1_nr;
1856   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
1857
1858   if (PANEL_DEACTIVATED(pos))
1859     return;
1860
1861   if (use_dynamic_chars)                /* use dynamic number of chars */
1862   {
1863     chars   = (level_nr < 100 ? chars1   : chars2);
1864     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
1865   }
1866
1867   pos->width = chars * getFontWidth(font_nr);
1868
1869   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1870 }
1871
1872 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1873 {
1874 #if 0
1875   struct TextPosInfo *pos = &game.panel.keys;
1876 #endif
1877 #if 0
1878   int base_key_graphic = EL_KEY_1;
1879 #endif
1880   int i;
1881
1882 #if 0
1883   if (PANEL_DEACTIVATED(pos))
1884     return;
1885 #endif
1886
1887 #if 0
1888   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1889     base_key_graphic = EL_EM_KEY_1;
1890 #endif
1891
1892 #if 0
1893   pos->width = 4 * MINI_TILEX;
1894 #endif
1895
1896 #if 1
1897   for (i = 0; i < MAX_NUM_KEYS; i++)
1898 #else
1899   /* currently only 4 of 8 possible keys are displayed */
1900   for (i = 0; i < STD_NUM_KEYS; i++)
1901 #endif
1902   {
1903 #if 1
1904     struct TextPosInfo *pos = &game.panel.key[i];
1905 #endif
1906     int src_x = DOOR_GFX_PAGEX5 + 18;
1907     int src_y = DOOR_GFX_PAGEY1 + 123;
1908 #if 1
1909     int dst_x = PANEL_XPOS(pos);
1910     int dst_y = PANEL_YPOS(pos);
1911 #else
1912     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
1913     int dst_y = PANEL_YPOS(pos);
1914 #endif
1915
1916 #if 1
1917     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
1918                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
1919                    EL_KEY_1) + i;
1920     int graphic = el2edimg(element);
1921 #endif
1922
1923 #if 1
1924     if (PANEL_DEACTIVATED(pos))
1925       continue;
1926 #endif
1927
1928 #if 0
1929     /* masked blit with tiles from half-size scaled bitmap does not work yet
1930        (no mask bitmap created for these sizes after loading and scaling) --
1931        solution: load without creating mask, scale, then create final mask */
1932
1933     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1934                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1935
1936     if (key[i])
1937     {
1938 #if 0
1939       int graphic = el2edimg(base_key_graphic + i);
1940 #endif
1941       Bitmap *src_bitmap;
1942       int src_x, src_y;
1943
1944       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1945
1946       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
1947                     dst_x - src_x, dst_y - src_y);
1948       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
1949                        dst_x, dst_y);
1950     }
1951 #else
1952 #if 1
1953     if (key[i])
1954       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
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 #else
1959     if (key[i])
1960       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
1961     else
1962       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1963                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1964 #endif
1965 #endif
1966   }
1967 }
1968
1969 #else
1970
1971 void DrawGameValue_Emeralds(int value)
1972 {
1973   int font_nr = FONT_TEXT_2;
1974   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1975
1976   if (PANEL_DEACTIVATED(game.panel.gems))
1977     return;
1978
1979   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
1980 }
1981
1982 void DrawGameValue_Dynamite(int value)
1983 {
1984   int font_nr = FONT_TEXT_2;
1985   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1986
1987   if (PANEL_DEACTIVATED(game.panel.inventory))
1988     return;
1989
1990   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
1991 }
1992
1993 void DrawGameValue_Score(int value)
1994 {
1995   int font_nr = FONT_TEXT_2;
1996   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
1997
1998   if (PANEL_DEACTIVATED(game.panel.score))
1999     return;
2000
2001   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2002 }
2003
2004 void DrawGameValue_Time(int value)
2005 {
2006   int font1_nr = FONT_TEXT_2;
2007 #if 1
2008   int font2_nr = FONT_TEXT_1;
2009 #else
2010   int font2_nr = FONT_LEVEL_NUMBER;
2011 #endif
2012   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2013   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2014
2015   if (PANEL_DEACTIVATED(game.panel.time))
2016     return;
2017
2018   /* clear background if value just changed its size */
2019   if (value == 999 || value == 1000)
2020     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2021
2022   if (value < 1000)
2023     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2024   else
2025     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2026 }
2027
2028 void DrawGameValue_Level(int value)
2029 {
2030   int font1_nr = FONT_TEXT_2;
2031 #if 1
2032   int font2_nr = FONT_TEXT_1;
2033 #else
2034   int font2_nr = FONT_LEVEL_NUMBER;
2035 #endif
2036
2037   if (PANEL_DEACTIVATED(game.panel.level))
2038     return;
2039
2040   if (level_nr < 100)
2041     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2042   else
2043     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2044 }
2045
2046 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2047 {
2048   int base_key_graphic = EL_KEY_1;
2049   int i;
2050
2051   if (PANEL_DEACTIVATED(game.panel.keys))
2052     return;
2053
2054   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2055     base_key_graphic = EL_EM_KEY_1;
2056
2057   /* currently only 4 of 8 possible keys are displayed */
2058   for (i = 0; i < STD_NUM_KEYS; i++)
2059   {
2060     int x = XX_KEYS + i * MINI_TILEX;
2061     int y = YY_KEYS;
2062
2063     if (key[i])
2064       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2065     else
2066       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2067                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2068   }
2069 }
2070
2071 #endif
2072
2073 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2074                        int key_bits)
2075 {
2076   int key[MAX_NUM_KEYS];
2077   int i;
2078
2079   /* prevent EM engine from updating time/score values parallel to GameWon() */
2080   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2081       local_player->LevelSolved)
2082     return;
2083
2084   for (i = 0; i < MAX_NUM_KEYS; i++)
2085     key[i] = key_bits & (1 << i);
2086
2087   DrawGameValue_Level(level_nr);
2088
2089   DrawGameValue_Emeralds(emeralds);
2090   DrawGameValue_Dynamite(dynamite);
2091   DrawGameValue_Score(score);
2092   DrawGameValue_Time(time);
2093
2094   DrawGameValue_Keys(key);
2095 }
2096
2097 void DrawGameDoorValues()
2098 {
2099   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2100   int dynamite_value = 0;
2101   int score_value = (local_player->LevelSolved ? local_player->score_final :
2102                      local_player->score);
2103   int gems_value = local_player->gems_still_needed;
2104   int key_bits = 0;
2105   int i, j;
2106
2107   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2108   {
2109     DrawGameDoorValues_EM();
2110
2111     return;
2112   }
2113
2114   if (game.centered_player_nr == -1)
2115   {
2116     for (i = 0; i < MAX_PLAYERS; i++)
2117     {
2118       for (j = 0; j < MAX_NUM_KEYS; j++)
2119         if (stored_player[i].key[j])
2120           key_bits |= (1 << j);
2121
2122       dynamite_value += stored_player[i].inventory_size;
2123     }
2124   }
2125   else
2126   {
2127     int player_nr = game.centered_player_nr;
2128
2129     for (i = 0; i < MAX_NUM_KEYS; i++)
2130       if (stored_player[player_nr].key[i])
2131         key_bits |= (1 << i);
2132
2133     dynamite_value = stored_player[player_nr].inventory_size;
2134   }
2135
2136   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2137                     key_bits);
2138 }
2139
2140
2141 /*
2142   =============================================================================
2143   InitGameEngine()
2144   -----------------------------------------------------------------------------
2145   initialize game engine due to level / tape version number
2146   =============================================================================
2147 */
2148
2149 static void InitGameEngine()
2150 {
2151   int i, j, k, l, x, y;
2152
2153   /* set game engine from tape file when re-playing, else from level file */
2154   game.engine_version = (tape.playing ? tape.engine_version :
2155                          level.game_version);
2156
2157   /* ---------------------------------------------------------------------- */
2158   /* set flags for bugs and changes according to active game engine version */
2159   /* ---------------------------------------------------------------------- */
2160
2161   /*
2162     Summary of bugfix/change:
2163     Fixed handling for custom elements that change when pushed by the player.
2164
2165     Fixed/changed in version:
2166     3.1.0
2167
2168     Description:
2169     Before 3.1.0, custom elements that "change when pushing" changed directly
2170     after the player started pushing them (until then handled in "DigField()").
2171     Since 3.1.0, these custom elements are not changed until the "pushing"
2172     move of the element is finished (now handled in "ContinueMoving()").
2173
2174     Affected levels/tapes:
2175     The first condition is generally needed for all levels/tapes before version
2176     3.1.0, which might use the old behaviour before it was changed; known tapes
2177     that are affected are some tapes from the level set "Walpurgis Gardens" by
2178     Jamie Cullen.
2179     The second condition is an exception from the above case and is needed for
2180     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2181     above (including some development versions of 3.1.0), but before it was
2182     known that this change would break tapes like the above and was fixed in
2183     3.1.1, so that the changed behaviour was active although the engine version
2184     while recording maybe was before 3.1.0. There is at least one tape that is
2185     affected by this exception, which is the tape for the one-level set "Bug
2186     Machine" by Juergen Bonhagen.
2187   */
2188
2189   game.use_change_when_pushing_bug =
2190     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2191      !(tape.playing &&
2192        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2193        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2194
2195   /*
2196     Summary of bugfix/change:
2197     Fixed handling for blocking the field the player leaves when moving.
2198
2199     Fixed/changed in version:
2200     3.1.1
2201
2202     Description:
2203     Before 3.1.1, when "block last field when moving" was enabled, the field
2204     the player is leaving when moving was blocked for the time of the move,
2205     and was directly unblocked afterwards. This resulted in the last field
2206     being blocked for exactly one less than the number of frames of one player
2207     move. Additionally, even when blocking was disabled, the last field was
2208     blocked for exactly one frame.
2209     Since 3.1.1, due to changes in player movement handling, the last field
2210     is not blocked at all when blocking is disabled. When blocking is enabled,
2211     the last field is blocked for exactly the number of frames of one player
2212     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2213     last field is blocked for exactly one more than the number of frames of
2214     one player move.
2215
2216     Affected levels/tapes:
2217     (!!! yet to be determined -- probably many !!!)
2218   */
2219
2220   game.use_block_last_field_bug =
2221     (game.engine_version < VERSION_IDENT(3,1,1,0));
2222
2223   /*
2224     Summary of bugfix/change:
2225     Changed behaviour of CE changes with multiple changes per single frame.
2226
2227     Fixed/changed in version:
2228     3.2.0-6
2229
2230     Description:
2231     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
2232     This resulted in race conditions where CEs seem to behave strange in some
2233     situations (where triggered CE changes were just skipped because there was
2234     already a CE change on that tile in the playfield in that engine frame).
2235     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
2236     (The number of changes per frame must be limited in any case, because else
2237     it is easily possible to define CE changes that would result in an infinite
2238     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
2239     should be set large enough so that it would only be reached in cases where
2240     the corresponding CE change conditions run into a loop. Therefore, it seems
2241     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
2242     maximal number of change pages for custom elements.)
2243
2244     Affected levels/tapes:
2245     Probably many.
2246   */
2247
2248 #if USE_ONLY_ONE_CHANGE_PER_FRAME
2249   game.max_num_changes_per_frame = 1;
2250 #else
2251   game.max_num_changes_per_frame =
2252     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
2253 #endif
2254
2255   /* ---------------------------------------------------------------------- */
2256
2257   /* default scan direction: scan playfield from top/left to bottom/right */
2258   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2259
2260   /* dynamically adjust element properties according to game engine version */
2261   InitElementPropertiesEngine(game.engine_version);
2262
2263 #if 0
2264   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2265   printf("          tape version == %06d [%s] [file: %06d]\n",
2266          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2267          tape.file_version);
2268   printf("       => game.engine_version == %06d\n", game.engine_version);
2269 #endif
2270
2271   /* ---------- initialize player's initial move delay --------------------- */
2272
2273   /* dynamically adjust player properties according to level information */
2274   for (i = 0; i < MAX_PLAYERS; i++)
2275     game.initial_move_delay_value[i] =
2276       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2277
2278   /* dynamically adjust player properties according to game engine version */
2279   for (i = 0; i < MAX_PLAYERS; i++)
2280     game.initial_move_delay[i] =
2281       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2282        game.initial_move_delay_value[i] : 0);
2283
2284   /* ---------- initialize player's initial push delay --------------------- */
2285
2286   /* dynamically adjust player properties according to game engine version */
2287   game.initial_push_delay_value =
2288     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2289
2290   /* ---------- initialize changing elements ------------------------------- */
2291
2292   /* initialize changing elements information */
2293   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2294   {
2295     struct ElementInfo *ei = &element_info[i];
2296
2297     /* this pointer might have been changed in the level editor */
2298     ei->change = &ei->change_page[0];
2299
2300     if (!IS_CUSTOM_ELEMENT(i))
2301     {
2302       ei->change->target_element = EL_EMPTY_SPACE;
2303       ei->change->delay_fixed = 0;
2304       ei->change->delay_random = 0;
2305       ei->change->delay_frames = 1;
2306     }
2307
2308     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2309     {
2310       ei->has_change_event[j] = FALSE;
2311
2312       ei->event_page_nr[j] = 0;
2313       ei->event_page[j] = &ei->change_page[0];
2314     }
2315   }
2316
2317   /* add changing elements from pre-defined list */
2318   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2319   {
2320     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2321     struct ElementInfo *ei = &element_info[ch_delay->element];
2322
2323     ei->change->target_element       = ch_delay->target_element;
2324     ei->change->delay_fixed          = ch_delay->change_delay;
2325
2326     ei->change->pre_change_function  = ch_delay->pre_change_function;
2327     ei->change->change_function      = ch_delay->change_function;
2328     ei->change->post_change_function = ch_delay->post_change_function;
2329
2330     ei->change->can_change = TRUE;
2331     ei->change->can_change_or_has_action = TRUE;
2332
2333     ei->has_change_event[CE_DELAY] = TRUE;
2334
2335     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2336     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2337   }
2338
2339   /* ---------- initialize internal run-time variables ------------- */
2340
2341   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2342   {
2343     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2344
2345     for (j = 0; j < ei->num_change_pages; j++)
2346     {
2347       ei->change_page[j].can_change_or_has_action =
2348         (ei->change_page[j].can_change |
2349          ei->change_page[j].has_action);
2350     }
2351   }
2352
2353   /* add change events from custom element configuration */
2354   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2355   {
2356     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2357
2358     for (j = 0; j < ei->num_change_pages; j++)
2359     {
2360       if (!ei->change_page[j].can_change_or_has_action)
2361         continue;
2362
2363       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2364       {
2365         /* only add event page for the first page found with this event */
2366         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2367         {
2368           ei->has_change_event[k] = TRUE;
2369
2370           ei->event_page_nr[k] = j;
2371           ei->event_page[k] = &ei->change_page[j];
2372         }
2373       }
2374     }
2375   }
2376
2377   /* ---------- initialize run-time trigger player and element ------------- */
2378
2379   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2380   {
2381     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2382
2383     for (j = 0; j < ei->num_change_pages; j++)
2384     {
2385       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2386       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
2387       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2388       ei->change_page[j].actual_trigger_ce_value = 0;
2389       ei->change_page[j].actual_trigger_ce_score = 0;
2390     }
2391   }
2392
2393   /* ---------- initialize trigger events ---------------------------------- */
2394
2395   /* initialize trigger events information */
2396   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2397     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2398       trigger_events[i][j] = FALSE;
2399
2400   /* add trigger events from element change event properties */
2401   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2402   {
2403     struct ElementInfo *ei = &element_info[i];
2404
2405     for (j = 0; j < ei->num_change_pages; j++)
2406     {
2407       if (!ei->change_page[j].can_change_or_has_action)
2408         continue;
2409
2410       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2411       {
2412         int trigger_element = ei->change_page[j].trigger_element;
2413
2414         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2415         {
2416           if (ei->change_page[j].has_event[k])
2417           {
2418             if (IS_GROUP_ELEMENT(trigger_element))
2419             {
2420               struct ElementGroupInfo *group =
2421                 element_info[trigger_element].group;
2422
2423               for (l = 0; l < group->num_elements_resolved; l++)
2424                 trigger_events[group->element_resolved[l]][k] = TRUE;
2425             }
2426             else if (trigger_element == EL_ANY_ELEMENT)
2427               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2428                 trigger_events[l][k] = TRUE;
2429             else
2430               trigger_events[trigger_element][k] = TRUE;
2431           }
2432         }
2433       }
2434     }
2435   }
2436
2437   /* ---------- initialize push delay -------------------------------------- */
2438
2439   /* initialize push delay values to default */
2440   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2441   {
2442     if (!IS_CUSTOM_ELEMENT(i))
2443     {
2444       /* set default push delay values (corrected since version 3.0.7-1) */
2445       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2446       {
2447         element_info[i].push_delay_fixed = 2;
2448         element_info[i].push_delay_random = 8;
2449       }
2450       else
2451       {
2452         element_info[i].push_delay_fixed = 8;
2453         element_info[i].push_delay_random = 8;
2454       }
2455     }
2456   }
2457
2458   /* set push delay value for certain elements from pre-defined list */
2459   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2460   {
2461     int e = push_delay_list[i].element;
2462
2463     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2464     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2465   }
2466
2467   /* set push delay value for Supaplex elements for newer engine versions */
2468   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2469   {
2470     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2471     {
2472       if (IS_SP_ELEMENT(i))
2473       {
2474         /* set SP push delay to just enough to push under a falling zonk */
2475         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2476
2477         element_info[i].push_delay_fixed  = delay;
2478         element_info[i].push_delay_random = 0;
2479       }
2480     }
2481   }
2482
2483   /* ---------- initialize move stepsize ----------------------------------- */
2484
2485   /* initialize move stepsize values to default */
2486   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2487     if (!IS_CUSTOM_ELEMENT(i))
2488       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2489
2490   /* set move stepsize value for certain elements from pre-defined list */
2491   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2492   {
2493     int e = move_stepsize_list[i].element;
2494
2495     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2496   }
2497
2498   /* ---------- initialize collect score ----------------------------------- */
2499
2500   /* initialize collect score values for custom elements from initial value */
2501   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2502     if (IS_CUSTOM_ELEMENT(i))
2503       element_info[i].collect_score = element_info[i].collect_score_initial;
2504
2505   /* ---------- initialize collect count ----------------------------------- */
2506
2507   /* initialize collect count values for non-custom elements */
2508   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2509     if (!IS_CUSTOM_ELEMENT(i))
2510       element_info[i].collect_count_initial = 0;
2511
2512   /* add collect count values for all elements from pre-defined list */
2513   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2514     element_info[collect_count_list[i].element].collect_count_initial =
2515       collect_count_list[i].count;
2516
2517   /* ---------- initialize access direction -------------------------------- */
2518
2519   /* initialize access direction values to default (access from every side) */
2520   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2521     if (!IS_CUSTOM_ELEMENT(i))
2522       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2523
2524   /* set access direction value for certain elements from pre-defined list */
2525   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2526     element_info[access_direction_list[i].element].access_direction =
2527       access_direction_list[i].direction;
2528
2529   /* ---------- initialize explosion content ------------------------------- */
2530   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2531   {
2532     if (IS_CUSTOM_ELEMENT(i))
2533       continue;
2534
2535     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2536     {
2537       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2538
2539       element_info[i].content.e[x][y] =
2540         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2541          i == EL_PLAYER_2 ? EL_EMERALD_RED :
2542          i == EL_PLAYER_3 ? EL_EMERALD :
2543          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2544          i == EL_MOLE ? EL_EMERALD_RED :
2545          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2546          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2547          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2548          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2549          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2550          i == EL_WALL_EMERALD ? EL_EMERALD :
2551          i == EL_WALL_DIAMOND ? EL_DIAMOND :
2552          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2553          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2554          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2555          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2556          i == EL_WALL_PEARL ? EL_PEARL :
2557          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2558          EL_EMPTY);
2559     }
2560   }
2561
2562   /* ---------- initialize recursion detection ------------------------------ */
2563   recursion_loop_depth = 0;
2564   recursion_loop_detected = FALSE;
2565   recursion_loop_element = EL_UNDEFINED;
2566 }
2567
2568 int get_num_special_action(int element, int action_first, int action_last)
2569 {
2570   int num_special_action = 0;
2571   int i, j;
2572
2573   for (i = action_first; i <= action_last; i++)
2574   {
2575     boolean found = FALSE;
2576
2577     for (j = 0; j < NUM_DIRECTIONS; j++)
2578       if (el_act_dir2img(element, i, j) !=
2579           el_act_dir2img(element, ACTION_DEFAULT, j))
2580         found = TRUE;
2581
2582     if (found)
2583       num_special_action++;
2584     else
2585       break;
2586   }
2587
2588   return num_special_action;
2589 }
2590
2591
2592 /*
2593   =============================================================================
2594   InitGame()
2595   -----------------------------------------------------------------------------
2596   initialize and start new game
2597   =============================================================================
2598 */
2599
2600 void InitGame()
2601 {
2602   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
2603   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
2604   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
2605   boolean do_fading = (game_status == GAME_MODE_MAIN);
2606   int i, j, x, y;
2607
2608   game_status = GAME_MODE_PLAYING;
2609
2610   InitGameEngine();
2611
2612   /* don't play tapes over network */
2613   network_playing = (options.network && !tape.playing);
2614
2615   for (i = 0; i < MAX_PLAYERS; i++)
2616   {
2617     struct PlayerInfo *player = &stored_player[i];
2618
2619     player->index_nr = i;
2620     player->index_bit = (1 << i);
2621     player->element_nr = EL_PLAYER_1 + i;
2622
2623     player->present = FALSE;
2624     player->active = FALSE;
2625     player->killed = FALSE;
2626
2627     player->action = 0;
2628     player->effective_action = 0;
2629     player->programmed_action = 0;
2630
2631     player->score = 0;
2632     player->score_final = 0;
2633
2634     player->gems_still_needed = level.gems_needed;
2635     player->sokobanfields_still_needed = 0;
2636     player->lights_still_needed = 0;
2637     player->friends_still_needed = 0;
2638
2639     for (j = 0; j < MAX_NUM_KEYS; j++)
2640       player->key[j] = FALSE;
2641
2642     player->num_white_keys = 0;
2643
2644     player->dynabomb_count = 0;
2645     player->dynabomb_size = 1;
2646     player->dynabombs_left = 0;
2647     player->dynabomb_xl = FALSE;
2648
2649     player->MovDir = MV_NONE;
2650     player->MovPos = 0;
2651     player->GfxPos = 0;
2652     player->GfxDir = MV_NONE;
2653     player->GfxAction = ACTION_DEFAULT;
2654     player->Frame = 0;
2655     player->StepFrame = 0;
2656
2657     player->use_murphy = FALSE;
2658     player->artwork_element =
2659       (level.use_artwork_element[i] ? level.artwork_element[i] :
2660        player->element_nr);
2661
2662     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
2663     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2664
2665     player->gravity = level.initial_player_gravity[i];
2666
2667     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2668
2669     player->actual_frame_counter = 0;
2670
2671     player->step_counter = 0;
2672
2673     player->last_move_dir = MV_NONE;
2674
2675     player->is_active = FALSE;
2676
2677     player->is_waiting = FALSE;
2678     player->is_moving = FALSE;
2679     player->is_auto_moving = FALSE;
2680     player->is_digging = FALSE;
2681     player->is_snapping = FALSE;
2682     player->is_collecting = FALSE;
2683     player->is_pushing = FALSE;
2684     player->is_switching = FALSE;
2685     player->is_dropping = FALSE;
2686     player->is_dropping_pressed = FALSE;
2687
2688     player->is_bored = FALSE;
2689     player->is_sleeping = FALSE;
2690
2691     player->frame_counter_bored = -1;
2692     player->frame_counter_sleeping = -1;
2693
2694     player->anim_delay_counter = 0;
2695     player->post_delay_counter = 0;
2696
2697     player->dir_waiting = MV_NONE;
2698     player->action_waiting = ACTION_DEFAULT;
2699     player->last_action_waiting = ACTION_DEFAULT;
2700     player->special_action_bored = ACTION_DEFAULT;
2701     player->special_action_sleeping = ACTION_DEFAULT;
2702
2703     player->switch_x = -1;
2704     player->switch_y = -1;
2705
2706     player->drop_x = -1;
2707     player->drop_y = -1;
2708
2709     player->show_envelope = 0;
2710
2711     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2712
2713     player->push_delay       = -1;      /* initialized when pushing starts */
2714     player->push_delay_value = game.initial_push_delay_value;
2715
2716     player->drop_delay = 0;
2717     player->drop_pressed_delay = 0;
2718
2719     player->last_jx = -1;
2720     player->last_jy = -1;
2721     player->jx = -1;
2722     player->jy = -1;
2723
2724     player->shield_normal_time_left = 0;
2725     player->shield_deadly_time_left = 0;
2726
2727     player->inventory_infinite_element = EL_UNDEFINED;
2728     player->inventory_size = 0;
2729
2730     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2731     SnapField(player, 0, 0);
2732
2733     player->LevelSolved = FALSE;
2734     player->GameOver = FALSE;
2735
2736     player->LevelSolved_GameWon = FALSE;
2737     player->LevelSolved_GameEnd = FALSE;
2738     player->LevelSolved_PanelOff = FALSE;
2739     player->LevelSolved_SaveTape = FALSE;
2740     player->LevelSolved_SaveScore = FALSE;
2741   }
2742
2743   network_player_action_received = FALSE;
2744
2745 #if defined(NETWORK_AVALIABLE)
2746   /* initial null action */
2747   if (network_playing)
2748     SendToServer_MovePlayer(MV_NONE);
2749 #endif
2750
2751   ZX = ZY = -1;
2752   ExitX = ExitY = -1;
2753
2754   FrameCounter = 0;
2755   TimeFrames = 0;
2756   TimePlayed = 0;
2757   TimeLeft = level.time;
2758   TapeTime = 0;
2759
2760   ScreenMovDir = MV_NONE;
2761   ScreenMovPos = 0;
2762   ScreenGfxPos = 0;
2763
2764   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
2765
2766   AllPlayersGone = FALSE;
2767
2768   game.yamyam_content_nr = 0;
2769   game.magic_wall_active = FALSE;
2770   game.magic_wall_time_left = 0;
2771   game.light_time_left = 0;
2772   game.timegate_time_left = 0;
2773   game.switchgate_pos = 0;
2774   game.wind_direction = level.wind_direction_initial;
2775
2776 #if !USE_PLAYER_GRAVITY
2777   game.gravity = FALSE;
2778   game.explosions_delayed = TRUE;
2779 #endif
2780
2781   game.lenses_time_left = 0;
2782   game.magnify_time_left = 0;
2783
2784   game.ball_state = level.ball_state_initial;
2785   game.ball_content_nr = 0;
2786
2787   game.envelope_active = FALSE;
2788
2789   /* set focus to local player for network games, else to all players */
2790   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2791   game.centered_player_nr_next = game.centered_player_nr;
2792   game.set_centered_player = FALSE;
2793
2794   if (network_playing && tape.recording)
2795   {
2796     /* store client dependent player focus when recording network games */
2797     tape.centered_player_nr_next = game.centered_player_nr_next;
2798     tape.set_centered_player = TRUE;
2799   }
2800
2801   for (i = 0; i < NUM_BELTS; i++)
2802   {
2803     game.belt_dir[i] = MV_NONE;
2804     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
2805   }
2806
2807   for (i = 0; i < MAX_NUM_AMOEBA; i++)
2808     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2809
2810   SCAN_PLAYFIELD(x, y)
2811   {
2812     Feld[x][y] = level.field[x][y];
2813     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2814     ChangeDelay[x][y] = 0;
2815     ChangePage[x][y] = -1;
2816 #if USE_NEW_CUSTOM_VALUE
2817     CustomValue[x][y] = 0;              /* initialized in InitField() */
2818 #endif
2819     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2820     AmoebaNr[x][y] = 0;
2821     WasJustMoving[x][y] = 0;
2822     WasJustFalling[x][y] = 0;
2823     CheckCollision[x][y] = 0;
2824     CheckImpact[x][y] = 0;
2825     Stop[x][y] = FALSE;
2826     Pushed[x][y] = FALSE;
2827
2828     ChangeCount[x][y] = 0;
2829     ChangeEvent[x][y] = -1;
2830
2831     ExplodePhase[x][y] = 0;
2832     ExplodeDelay[x][y] = 0;
2833     ExplodeField[x][y] = EX_TYPE_NONE;
2834
2835     RunnerVisit[x][y] = 0;
2836     PlayerVisit[x][y] = 0;
2837
2838     GfxFrame[x][y] = 0;
2839     GfxRandom[x][y] = INIT_GFX_RANDOM();
2840     GfxElement[x][y] = EL_UNDEFINED;
2841     GfxAction[x][y] = ACTION_DEFAULT;
2842     GfxDir[x][y] = MV_NONE;
2843   }
2844
2845   SCAN_PLAYFIELD(x, y)
2846   {
2847     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2848       emulate_bd = FALSE;
2849     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2850       emulate_sb = FALSE;
2851     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2852       emulate_sp = FALSE;
2853
2854     InitField(x, y, TRUE);
2855   }
2856
2857   InitBeltMovement();
2858
2859   for (i = 0; i < MAX_PLAYERS; i++)
2860   {
2861     struct PlayerInfo *player = &stored_player[i];
2862
2863     /* set number of special actions for bored and sleeping animation */
2864     player->num_special_action_bored =
2865       get_num_special_action(player->artwork_element,
2866                              ACTION_BORING_1, ACTION_BORING_LAST);
2867     player->num_special_action_sleeping =
2868       get_num_special_action(player->artwork_element,
2869                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2870   }
2871
2872   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2873                     emulate_sb ? EMU_SOKOBAN :
2874                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2875
2876 #if USE_NEW_ALL_SLIPPERY
2877   /* initialize type of slippery elements */
2878   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2879   {
2880     if (!IS_CUSTOM_ELEMENT(i))
2881     {
2882       /* default: elements slip down either to the left or right randomly */
2883       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2884
2885       /* SP style elements prefer to slip down on the left side */
2886       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2887         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2888
2889       /* BD style elements prefer to slip down on the left side */
2890       if (game.emulation == EMU_BOULDERDASH)
2891         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2892     }
2893   }
2894 #endif
2895
2896   /* initialize explosion and ignition delay */
2897   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2898   {
2899     if (!IS_CUSTOM_ELEMENT(i))
2900     {
2901       int num_phase = 8;
2902       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2903                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2904                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
2905       int last_phase = (num_phase + 1) * delay;
2906       int half_phase = (num_phase / 2) * delay;
2907
2908       element_info[i].explosion_delay = last_phase - 1;
2909       element_info[i].ignition_delay = half_phase;
2910
2911       if (i == EL_BLACK_ORB)
2912         element_info[i].ignition_delay = 1;
2913     }
2914
2915 #if 0
2916     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
2917       element_info[i].explosion_delay = 1;
2918
2919     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
2920       element_info[i].ignition_delay = 1;
2921 #endif
2922   }
2923
2924   /* correct non-moving belts to start moving left */
2925   for (i = 0; i < NUM_BELTS; i++)
2926     if (game.belt_dir[i] == MV_NONE)
2927       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
2928
2929   /* check if any connected player was not found in playfield */
2930   for (i = 0; i < MAX_PLAYERS; i++)
2931   {
2932     struct PlayerInfo *player = &stored_player[i];
2933
2934     if (player->connected && !player->present)
2935     {
2936       for (j = 0; j < MAX_PLAYERS; j++)
2937       {
2938         struct PlayerInfo *some_player = &stored_player[j];
2939         int jx = some_player->jx, jy = some_player->jy;
2940
2941         /* assign first free player found that is present in the playfield */
2942         if (some_player->present && !some_player->connected)
2943         {
2944           player->present = TRUE;
2945           player->active = TRUE;
2946
2947           some_player->present = FALSE;
2948           some_player->active = FALSE;
2949
2950           player->artwork_element = some_player->artwork_element;
2951
2952           player->block_last_field       = some_player->block_last_field;
2953           player->block_delay_adjustment = some_player->block_delay_adjustment;
2954
2955           StorePlayer[jx][jy] = player->element_nr;
2956           player->jx = player->last_jx = jx;
2957           player->jy = player->last_jy = jy;
2958
2959           break;
2960         }
2961       }
2962     }
2963   }
2964
2965   if (tape.playing)
2966   {
2967     /* when playing a tape, eliminate all players who do not participate */
2968
2969     for (i = 0; i < MAX_PLAYERS; i++)
2970     {
2971       if (stored_player[i].active && !tape.player_participates[i])
2972       {
2973         struct PlayerInfo *player = &stored_player[i];
2974         int jx = player->jx, jy = player->jy;
2975
2976         player->active = FALSE;
2977         StorePlayer[jx][jy] = 0;
2978         Feld[jx][jy] = EL_EMPTY;
2979       }
2980     }
2981   }
2982   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
2983   {
2984     /* when in single player mode, eliminate all but the first active player */
2985
2986     for (i = 0; i < MAX_PLAYERS; i++)
2987     {
2988       if (stored_player[i].active)
2989       {
2990         for (j = i + 1; j < MAX_PLAYERS; j++)
2991         {
2992           if (stored_player[j].active)
2993           {
2994             struct PlayerInfo *player = &stored_player[j];
2995             int jx = player->jx, jy = player->jy;
2996
2997             player->active = FALSE;
2998             player->present = FALSE;
2999
3000             StorePlayer[jx][jy] = 0;
3001             Feld[jx][jy] = EL_EMPTY;
3002           }
3003         }
3004       }
3005     }
3006   }
3007
3008   /* when recording the game, store which players take part in the game */
3009   if (tape.recording)
3010   {
3011     for (i = 0; i < MAX_PLAYERS; i++)
3012       if (stored_player[i].active)
3013         tape.player_participates[i] = TRUE;
3014   }
3015
3016   if (options.debug)
3017   {
3018     for (i = 0; i < MAX_PLAYERS; i++)
3019     {
3020       struct PlayerInfo *player = &stored_player[i];
3021
3022       printf("Player %d: present == %d, connected == %d, active == %d.\n",
3023              i+1,
3024              player->present,
3025              player->connected,
3026              player->active);
3027       if (local_player == player)
3028         printf("Player  %d is local player.\n", i+1);
3029     }
3030   }
3031
3032   if (BorderElement == EL_EMPTY)
3033   {
3034     SBX_Left = 0;
3035     SBX_Right = lev_fieldx - SCR_FIELDX;
3036     SBY_Upper = 0;
3037     SBY_Lower = lev_fieldy - SCR_FIELDY;
3038   }
3039   else
3040   {
3041     SBX_Left = -1;
3042     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3043     SBY_Upper = -1;
3044     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3045   }
3046
3047   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3048     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3049
3050   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3051     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3052
3053   /* if local player not found, look for custom element that might create
3054      the player (make some assumptions about the right custom element) */
3055   if (!local_player->present)
3056   {
3057     int start_x = 0, start_y = 0;
3058     int found_rating = 0;
3059     int found_element = EL_UNDEFINED;
3060     int player_nr = local_player->index_nr;
3061
3062     SCAN_PLAYFIELD(x, y)
3063     {
3064       int element = Feld[x][y];
3065       int content;
3066       int xx, yy;
3067       boolean is_player;
3068
3069       if (level.use_start_element[player_nr] &&
3070           level.start_element[player_nr] == element &&
3071           found_rating < 4)
3072       {
3073         start_x = x;
3074         start_y = y;
3075
3076         found_rating = 4;
3077         found_element = element;
3078       }
3079
3080       if (!IS_CUSTOM_ELEMENT(element))
3081         continue;
3082
3083       if (CAN_CHANGE(element))
3084       {
3085         for (i = 0; i < element_info[element].num_change_pages; i++)
3086         {
3087           /* check for player created from custom element as single target */
3088           content = element_info[element].change_page[i].target_element;
3089           is_player = ELEM_IS_PLAYER(content);
3090
3091           if (is_player && (found_rating < 3 || element < found_element))
3092           {
3093             start_x = x;
3094             start_y = y;
3095
3096             found_rating = 3;
3097             found_element = element;
3098           }
3099         }
3100       }
3101
3102       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3103       {
3104         /* check for player created from custom element as explosion content */
3105         content = element_info[element].content.e[xx][yy];
3106         is_player = ELEM_IS_PLAYER(content);
3107
3108         if (is_player && (found_rating < 2 || element < found_element))
3109         {
3110           start_x = x + xx - 1;
3111           start_y = y + yy - 1;
3112
3113           found_rating = 2;
3114           found_element = element;
3115         }
3116
3117         if (!CAN_CHANGE(element))
3118           continue;
3119
3120         for (i = 0; i < element_info[element].num_change_pages; i++)
3121         {
3122           /* check for player created from custom element as extended target */
3123           content =
3124             element_info[element].change_page[i].target_content.e[xx][yy];
3125
3126           is_player = ELEM_IS_PLAYER(content);
3127
3128           if (is_player && (found_rating < 1 || element < found_element))
3129           {
3130             start_x = x + xx - 1;
3131             start_y = y + yy - 1;
3132
3133             found_rating = 1;
3134             found_element = element;
3135           }
3136         }
3137       }
3138     }
3139
3140     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3141                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3142                 start_x - MIDPOSX);
3143
3144     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3145                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3146                 start_y - MIDPOSY);
3147   }
3148   else
3149   {
3150     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3151                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3152                 local_player->jx - MIDPOSX);
3153
3154     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3155                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3156                 local_player->jy - MIDPOSY);
3157   }
3158
3159   StopAnimation();
3160
3161   if (!game.restart_level)
3162     CloseDoor(DOOR_CLOSE_1);
3163
3164   if (do_fading)
3165     FadeOut(REDRAW_FIELD);
3166
3167   /* !!! FIX THIS (START) !!! */
3168   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3169   {
3170     InitGameEngine_EM();
3171
3172     /* blit playfield from scroll buffer to normal back buffer for fading in */
3173     BlitScreenToBitmap_EM(backbuffer);
3174   }
3175   else
3176   {
3177     DrawLevel();
3178     DrawAllPlayers();
3179
3180     /* after drawing the level, correct some elements */
3181     if (game.timegate_time_left == 0)
3182       CloseAllOpenTimegates();
3183
3184     /* blit playfield from scroll buffer to normal back buffer for fading in */
3185     if (setup.soft_scrolling)
3186       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3187
3188     redraw_mask |= REDRAW_FROM_BACKBUFFER;
3189   }
3190   /* !!! FIX THIS (END) !!! */
3191
3192   if (do_fading)
3193     FadeIn(REDRAW_FIELD);
3194
3195   BackToFront();
3196
3197   if (!game.restart_level)
3198   {
3199     /* copy default game door content to main double buffer */
3200     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3201                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3202   }
3203
3204   SetPanelBackground();
3205   SetDrawBackgroundMask(REDRAW_DOOR_1);
3206
3207   DrawGameDoorValues();
3208
3209   if (!game.restart_level)
3210   {
3211     UnmapGameButtons();
3212     UnmapTapeButtons();
3213     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3214     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3215     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3216     MapGameButtons();
3217     MapTapeButtons();
3218
3219     /* copy actual game door content to door double buffer for OpenDoor() */
3220     BlitBitmap(drawto, bitmap_db_door,
3221                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3222
3223     OpenDoor(DOOR_OPEN_ALL);
3224
3225     PlaySound(SND_GAME_STARTING);
3226
3227     if (setup.sound_music)
3228       PlayLevelMusic();
3229
3230     KeyboardAutoRepeatOffUnlessAutoplay();
3231
3232     if (options.debug)
3233     {
3234       for (i = 0; i < MAX_PLAYERS; i++)
3235         printf("Player %d %sactive.\n",
3236                i + 1, (stored_player[i].active ? "" : "not "));
3237     }
3238   }
3239
3240 #if 1
3241   UnmapAllGadgets();
3242
3243   MapGameButtons();
3244   MapTapeButtons();
3245 #endif
3246
3247   game.restart_level = FALSE;
3248 }
3249
3250 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3251 {
3252   /* this is used for non-R'n'D game engines to update certain engine values */
3253
3254   /* needed to determine if sounds are played within the visible screen area */
3255   scroll_x = actual_scroll_x;
3256   scroll_y = actual_scroll_y;
3257 }
3258
3259 void InitMovDir(int x, int y)
3260 {
3261   int i, element = Feld[x][y];
3262   static int xy[4][2] =
3263   {
3264     {  0, +1 },
3265     { +1,  0 },
3266     {  0, -1 },
3267     { -1,  0 }
3268   };
3269   static int direction[3][4] =
3270   {
3271     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
3272     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
3273     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
3274   };
3275
3276   switch (element)
3277   {
3278     case EL_BUG_RIGHT:
3279     case EL_BUG_UP:
3280     case EL_BUG_LEFT:
3281     case EL_BUG_DOWN:
3282       Feld[x][y] = EL_BUG;
3283       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3284       break;
3285
3286     case EL_SPACESHIP_RIGHT:
3287     case EL_SPACESHIP_UP:
3288     case EL_SPACESHIP_LEFT:
3289     case EL_SPACESHIP_DOWN:
3290       Feld[x][y] = EL_SPACESHIP;
3291       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3292       break;
3293
3294     case EL_BD_BUTTERFLY_RIGHT:
3295     case EL_BD_BUTTERFLY_UP:
3296     case EL_BD_BUTTERFLY_LEFT:
3297     case EL_BD_BUTTERFLY_DOWN:
3298       Feld[x][y] = EL_BD_BUTTERFLY;
3299       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3300       break;
3301
3302     case EL_BD_FIREFLY_RIGHT:
3303     case EL_BD_FIREFLY_UP:
3304     case EL_BD_FIREFLY_LEFT:
3305     case EL_BD_FIREFLY_DOWN:
3306       Feld[x][y] = EL_BD_FIREFLY;
3307       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3308       break;
3309
3310     case EL_PACMAN_RIGHT:
3311     case EL_PACMAN_UP:
3312     case EL_PACMAN_LEFT:
3313     case EL_PACMAN_DOWN:
3314       Feld[x][y] = EL_PACMAN;
3315       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3316       break;
3317
3318     case EL_YAMYAM_LEFT:
3319     case EL_YAMYAM_RIGHT:
3320     case EL_YAMYAM_UP:
3321     case EL_YAMYAM_DOWN:
3322       Feld[x][y] = EL_YAMYAM;
3323       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3324       break;
3325
3326     case EL_SP_SNIKSNAK:
3327       MovDir[x][y] = MV_UP;
3328       break;
3329
3330     case EL_SP_ELECTRON:
3331       MovDir[x][y] = MV_LEFT;
3332       break;
3333
3334     case EL_MOLE_LEFT:
3335     case EL_MOLE_RIGHT:
3336     case EL_MOLE_UP:
3337     case EL_MOLE_DOWN:
3338       Feld[x][y] = EL_MOLE;
3339       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3340       break;
3341
3342     default:
3343       if (IS_CUSTOM_ELEMENT(element))
3344       {
3345         struct ElementInfo *ei = &element_info[element];
3346         int move_direction_initial = ei->move_direction_initial;
3347         int move_pattern = ei->move_pattern;
3348
3349         if (move_direction_initial == MV_START_PREVIOUS)
3350         {
3351           if (MovDir[x][y] != MV_NONE)
3352             return;
3353
3354           move_direction_initial = MV_START_AUTOMATIC;
3355         }
3356
3357         if (move_direction_initial == MV_START_RANDOM)
3358           MovDir[x][y] = 1 << RND(4);
3359         else if (move_direction_initial & MV_ANY_DIRECTION)
3360           MovDir[x][y] = move_direction_initial;
3361         else if (move_pattern == MV_ALL_DIRECTIONS ||
3362                  move_pattern == MV_TURNING_LEFT ||
3363                  move_pattern == MV_TURNING_RIGHT ||
3364                  move_pattern == MV_TURNING_LEFT_RIGHT ||
3365                  move_pattern == MV_TURNING_RIGHT_LEFT ||
3366                  move_pattern == MV_TURNING_RANDOM)
3367           MovDir[x][y] = 1 << RND(4);
3368         else if (move_pattern == MV_HORIZONTAL)
3369           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3370         else if (move_pattern == MV_VERTICAL)
3371           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3372         else if (move_pattern & MV_ANY_DIRECTION)
3373           MovDir[x][y] = element_info[element].move_pattern;
3374         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
3375                  move_pattern == MV_ALONG_RIGHT_SIDE)
3376         {
3377           /* use random direction as default start direction */
3378           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3379             MovDir[x][y] = 1 << RND(4);
3380
3381           for (i = 0; i < NUM_DIRECTIONS; i++)
3382           {
3383             int x1 = x + xy[i][0];
3384             int y1 = y + xy[i][1];
3385
3386             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3387             {
3388               if (move_pattern == MV_ALONG_RIGHT_SIDE)
3389                 MovDir[x][y] = direction[0][i];
3390               else
3391                 MovDir[x][y] = direction[1][i];
3392
3393               break;
3394             }
3395           }
3396         }                
3397       }
3398       else
3399       {
3400         MovDir[x][y] = 1 << RND(4);
3401
3402         if (element != EL_BUG &&
3403             element != EL_SPACESHIP &&
3404             element != EL_BD_BUTTERFLY &&
3405             element != EL_BD_FIREFLY)
3406           break;
3407
3408         for (i = 0; i < NUM_DIRECTIONS; i++)
3409         {
3410           int x1 = x + xy[i][0];
3411           int y1 = y + xy[i][1];
3412
3413           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3414           {
3415             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3416             {
3417               MovDir[x][y] = direction[0][i];
3418               break;
3419             }
3420             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3421                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3422             {
3423               MovDir[x][y] = direction[1][i];
3424               break;
3425             }
3426           }
3427         }
3428       }
3429       break;
3430   }
3431
3432   GfxDir[x][y] = MovDir[x][y];
3433 }
3434
3435 void InitAmoebaNr(int x, int y)
3436 {
3437   int i;
3438   int group_nr = AmoebeNachbarNr(x, y);
3439
3440   if (group_nr == 0)
3441   {
3442     for (i = 1; i < MAX_NUM_AMOEBA; i++)
3443     {
3444       if (AmoebaCnt[i] == 0)
3445       {
3446         group_nr = i;
3447         break;
3448       }
3449     }
3450   }
3451
3452   AmoebaNr[x][y] = group_nr;
3453   AmoebaCnt[group_nr]++;
3454   AmoebaCnt2[group_nr]++;
3455 }
3456
3457 static void PlayerWins(struct PlayerInfo *player)
3458 {
3459   player->LevelSolved = TRUE;
3460   player->GameOver = TRUE;
3461
3462   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
3463                          level.native_em_level->lev->score : player->score);
3464 }
3465
3466 void GameWon()
3467 {
3468   static int time, time_final;
3469   static int score, score_final;
3470   static int game_over_delay_1 = 0;
3471   static int game_over_delay_2 = 0;
3472   int game_over_delay_value_1 = 50;
3473   int game_over_delay_value_2 = 50;
3474
3475   if (!local_player->LevelSolved_GameWon)
3476   {
3477     int i;
3478
3479     /* do not start end game actions before the player stops moving (to exit) */
3480     if (local_player->MovPos)
3481       return;
3482
3483     local_player->LevelSolved_GameWon = TRUE;
3484     local_player->LevelSolved_SaveTape = tape.recording;
3485     local_player->LevelSolved_SaveScore = !tape.playing;
3486
3487     if (tape.auto_play)         /* tape might already be stopped here */
3488       tape.auto_play_level_solved = TRUE;
3489
3490 #if 1
3491     TapeStop();
3492 #endif
3493
3494     game_over_delay_1 = game_over_delay_value_1;
3495     game_over_delay_2 = game_over_delay_value_2;
3496
3497     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3498     score = score_final = local_player->score_final;
3499
3500     if (TimeLeft > 0)
3501     {
3502       time_final = 0;
3503       score_final += TimeLeft * level.score[SC_TIME_BONUS];
3504     }
3505     else if (level.time == 0 && TimePlayed < 999)
3506     {
3507       time_final = 999;
3508       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3509     }
3510
3511     local_player->score_final = score_final;
3512
3513     if (level_editor_test_game)
3514     {
3515       time = time_final;
3516       score = score_final;
3517
3518       DrawGameValue_Time(time);
3519       DrawGameValue_Score(score);
3520     }
3521
3522     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3523     {
3524       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
3525       {
3526         /* close exit door after last player */
3527         if ((AllPlayersGone &&
3528              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3529               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3530               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3531             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3532             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3533         {
3534           int element = Feld[ExitX][ExitY];
3535
3536 #if 0
3537           if (element == EL_EM_EXIT_OPEN ||
3538               element == EL_EM_STEEL_EXIT_OPEN)
3539           {
3540             Bang(ExitX, ExitY);
3541           }
3542           else
3543 #endif
3544           {
3545             Feld[ExitX][ExitY] =
3546               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
3547                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
3548                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
3549                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
3550                EL_EM_STEEL_EXIT_CLOSING);
3551
3552             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3553           }
3554         }
3555
3556         /* player disappears */
3557         DrawLevelField(ExitX, ExitY);
3558       }
3559
3560       for (i = 0; i < MAX_PLAYERS; i++)
3561       {
3562         struct PlayerInfo *player = &stored_player[i];
3563
3564         if (player->present)
3565         {
3566           RemovePlayer(player);
3567
3568           /* player disappears */
3569           DrawLevelField(player->jx, player->jy);
3570         }
3571       }
3572     }
3573
3574     PlaySound(SND_GAME_WINNING);
3575   }
3576
3577   if (game_over_delay_1 > 0)
3578   {
3579     game_over_delay_1--;
3580
3581     return;
3582   }
3583
3584   if (time != time_final)
3585   {
3586     int time_to_go = ABS(time_final - time);
3587     int time_count_dir = (time < time_final ? +1 : -1);
3588     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
3589
3590     time  += time_count_steps * time_count_dir;
3591     score += time_count_steps * level.score[SC_TIME_BONUS];
3592
3593     DrawGameValue_Time(time);
3594     DrawGameValue_Score(score);
3595
3596     if (time == time_final)
3597       StopSound(SND_GAME_LEVELTIME_BONUS);
3598     else if (setup.sound_loops)
3599       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
3600     else
3601       PlaySound(SND_GAME_LEVELTIME_BONUS);
3602
3603     return;
3604   }
3605
3606   local_player->LevelSolved_PanelOff = TRUE;
3607
3608   if (game_over_delay_2 > 0)
3609   {
3610     game_over_delay_2--;
3611
3612     return;
3613   }
3614
3615 #if 1
3616   GameEnd();
3617 #endif
3618 }
3619
3620 void GameEnd()
3621 {
3622   int hi_pos;
3623   boolean raise_level = FALSE;
3624
3625   local_player->LevelSolved_GameEnd = TRUE;
3626
3627   CloseDoor(DOOR_CLOSE_1);
3628
3629   if (local_player->LevelSolved_SaveTape)
3630   {
3631 #if 0
3632     TapeStop();
3633 #endif
3634
3635 #if 1
3636     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
3637 #else
3638     SaveTape(tape.level_nr);            /* ask to save tape */
3639 #endif
3640   }
3641
3642   if (level_editor_test_game)
3643   {
3644     game_status = GAME_MODE_MAIN;
3645
3646     DrawMainMenu();
3647
3648     return;
3649   }
3650
3651   if (!local_player->LevelSolved_SaveScore)
3652   {
3653     FadeOut(REDRAW_FIELD);
3654
3655     game_status = GAME_MODE_MAIN;
3656
3657     DrawAndFadeInMainMenu(REDRAW_FIELD);
3658
3659     return;
3660   }
3661
3662   if (level_nr == leveldir_current->handicap_level)
3663   {
3664     leveldir_current->handicap_level++;
3665     SaveLevelSetup_SeriesInfo();
3666   }
3667
3668   if (level_nr < leveldir_current->last_level)
3669     raise_level = TRUE;                 /* advance to next level */
3670
3671   if ((hi_pos = NewHiScore()) >= 0) 
3672   {
3673     game_status = GAME_MODE_SCORES;
3674
3675     DrawHallOfFame(hi_pos);
3676
3677     if (raise_level)
3678     {
3679       level_nr++;
3680       TapeErase();
3681     }
3682   }
3683   else
3684   {
3685     FadeOut(REDRAW_FIELD);
3686
3687     game_status = GAME_MODE_MAIN;
3688
3689     if (raise_level)
3690     {
3691       level_nr++;
3692       TapeErase();
3693     }
3694
3695     DrawAndFadeInMainMenu(REDRAW_FIELD);
3696   }
3697 }
3698
3699 int NewHiScore()
3700 {
3701   int k, l;
3702   int position = -1;
3703
3704   LoadScore(level_nr);
3705
3706   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3707       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
3708     return -1;
3709
3710   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
3711   {
3712     if (local_player->score_final > highscore[k].Score)
3713     {
3714       /* player has made it to the hall of fame */
3715
3716       if (k < MAX_SCORE_ENTRIES - 1)
3717       {
3718         int m = MAX_SCORE_ENTRIES - 1;
3719
3720 #ifdef ONE_PER_NAME
3721         for (l = k; l < MAX_SCORE_ENTRIES; l++)
3722           if (strEqual(setup.player_name, highscore[l].Name))
3723             m = l;
3724         if (m == k)     /* player's new highscore overwrites his old one */
3725           goto put_into_list;
3726 #endif
3727
3728         for (l = m; l > k; l--)
3729         {
3730           strcpy(highscore[l].Name, highscore[l - 1].Name);
3731           highscore[l].Score = highscore[l - 1].Score;
3732         }
3733       }
3734
3735 #ifdef ONE_PER_NAME
3736       put_into_list:
3737 #endif
3738       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3739       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3740       highscore[k].Score = local_player->score_final; 
3741       position = k;
3742       break;
3743     }
3744
3745 #ifdef ONE_PER_NAME
3746     else if (!strncmp(setup.player_name, highscore[k].Name,
3747                       MAX_PLAYER_NAME_LEN))
3748       break;    /* player already there with a higher score */
3749 #endif
3750
3751   }
3752
3753   if (position >= 0) 
3754     SaveScore(level_nr);
3755
3756   return position;
3757 }
3758
3759 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
3760 {
3761   int element = Feld[x][y];
3762   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3763   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3764   int horiz_move = (dx != 0);
3765   int sign = (horiz_move ? dx : dy);
3766   int step = sign * element_info[element].move_stepsize;
3767
3768   /* special values for move stepsize for spring and things on conveyor belt */
3769   if (horiz_move)
3770   {
3771     if (CAN_FALL(element) &&
3772         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3773       step = sign * MOVE_STEPSIZE_NORMAL / 2;
3774     else if (element == EL_SPRING)
3775       step = sign * MOVE_STEPSIZE_NORMAL * 2;
3776   }
3777
3778   return step;
3779 }
3780
3781 inline static int getElementMoveStepsize(int x, int y)
3782 {
3783   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
3784 }
3785
3786 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3787 {
3788   if (player->GfxAction != action || player->GfxDir != dir)
3789   {
3790 #if 0
3791     printf("Player frame reset! (%d => %d, %d => %d)\n",
3792            player->GfxAction, action, player->GfxDir, dir);
3793 #endif
3794
3795     player->GfxAction = action;
3796     player->GfxDir = dir;
3797     player->Frame = 0;
3798     player->StepFrame = 0;
3799   }
3800 }
3801
3802 #if USE_GFX_RESET_GFX_ANIMATION
3803 static void ResetGfxFrame(int x, int y, boolean redraw)
3804 {
3805   int element = Feld[x][y];
3806   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3807   int last_gfx_frame = GfxFrame[x][y];
3808
3809   if (graphic_info[graphic].anim_global_sync)
3810     GfxFrame[x][y] = FrameCounter;
3811   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3812     GfxFrame[x][y] = CustomValue[x][y];
3813   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3814     GfxFrame[x][y] = element_info[element].collect_score;
3815   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3816     GfxFrame[x][y] = ChangeDelay[x][y];
3817
3818   if (redraw && GfxFrame[x][y] != last_gfx_frame)
3819     DrawLevelGraphicAnimation(x, y, graphic);
3820 }
3821 #endif
3822
3823 static void ResetGfxAnimation(int x, int y)
3824 {
3825   GfxAction[x][y] = ACTION_DEFAULT;
3826   GfxDir[x][y] = MovDir[x][y];
3827   GfxFrame[x][y] = 0;
3828
3829 #if USE_GFX_RESET_GFX_ANIMATION
3830   ResetGfxFrame(x, y, FALSE);
3831 #endif
3832 }
3833
3834 static void ResetRandomAnimationValue(int x, int y)
3835 {
3836   GfxRandom[x][y] = INIT_GFX_RANDOM();
3837 }
3838
3839 void InitMovingField(int x, int y, int direction)
3840 {
3841   int element = Feld[x][y];
3842   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3843   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3844   int newx = x + dx;
3845   int newy = y + dy;
3846   boolean is_moving_before, is_moving_after;
3847 #if 0
3848   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3849 #endif
3850
3851   /* check if element was/is moving or being moved before/after mode change */
3852 #if 1
3853 #if 1
3854   is_moving_before = (WasJustMoving[x][y] != 0);
3855 #else
3856   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
3857   is_moving_before = WasJustMoving[x][y];
3858 #endif
3859 #else
3860   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3861 #endif
3862   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
3863
3864   /* reset animation only for moving elements which change direction of moving
3865      or which just started or stopped moving
3866      (else CEs with property "can move" / "not moving" are reset each frame) */
3867 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3868 #if 1
3869   if (is_moving_before != is_moving_after ||
3870       direction != MovDir[x][y])
3871     ResetGfxAnimation(x, y);
3872 #else
3873   if ((is_moving_before || is_moving_after) && !continues_moving)
3874     ResetGfxAnimation(x, y);
3875 #endif
3876 #else
3877   if (!continues_moving)
3878     ResetGfxAnimation(x, y);
3879 #endif
3880
3881   MovDir[x][y] = direction;
3882   GfxDir[x][y] = direction;
3883
3884 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3885   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3886                      direction == MV_DOWN && CAN_FALL(element) ?
3887                      ACTION_FALLING : ACTION_MOVING);
3888 #else
3889   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3890                      ACTION_FALLING : ACTION_MOVING);
3891 #endif
3892
3893   /* this is needed for CEs with property "can move" / "not moving" */
3894
3895   if (is_moving_after)
3896   {
3897     if (Feld[newx][newy] == EL_EMPTY)
3898       Feld[newx][newy] = EL_BLOCKED;
3899
3900     MovDir[newx][newy] = MovDir[x][y];
3901
3902 #if USE_NEW_CUSTOM_VALUE
3903     CustomValue[newx][newy] = CustomValue[x][y];
3904 #endif
3905
3906     GfxFrame[newx][newy] = GfxFrame[x][y];
3907     GfxRandom[newx][newy] = GfxRandom[x][y];
3908     GfxAction[newx][newy] = GfxAction[x][y];
3909     GfxDir[newx][newy] = GfxDir[x][y];
3910   }
3911 }
3912
3913 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3914 {
3915   int direction = MovDir[x][y];
3916   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3917   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
3918
3919   *goes_to_x = newx;
3920   *goes_to_y = newy;
3921 }
3922
3923 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3924 {
3925   int oldx = x, oldy = y;
3926   int direction = MovDir[x][y];
3927
3928   if (direction == MV_LEFT)
3929     oldx++;
3930   else if (direction == MV_RIGHT)
3931     oldx--;
3932   else if (direction == MV_UP)
3933     oldy++;
3934   else if (direction == MV_DOWN)
3935     oldy--;
3936
3937   *comes_from_x = oldx;
3938   *comes_from_y = oldy;
3939 }
3940
3941 int MovingOrBlocked2Element(int x, int y)
3942 {
3943   int element = Feld[x][y];
3944
3945   if (element == EL_BLOCKED)
3946   {
3947     int oldx, oldy;
3948
3949     Blocked2Moving(x, y, &oldx, &oldy);
3950     return Feld[oldx][oldy];
3951   }
3952   else
3953     return element;
3954 }
3955
3956 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3957 {
3958   /* like MovingOrBlocked2Element(), but if element is moving
3959      and (x,y) is the field the moving element is just leaving,
3960      return EL_BLOCKED instead of the element value */
3961   int element = Feld[x][y];
3962
3963   if (IS_MOVING(x, y))
3964   {
3965     if (element == EL_BLOCKED)
3966     {
3967       int oldx, oldy;
3968
3969       Blocked2Moving(x, y, &oldx, &oldy);
3970       return Feld[oldx][oldy];
3971     }
3972     else
3973       return EL_BLOCKED;
3974   }
3975   else
3976     return element;
3977 }
3978
3979 static void RemoveField(int x, int y)
3980 {
3981   Feld[x][y] = EL_EMPTY;
3982
3983   MovPos[x][y] = 0;
3984   MovDir[x][y] = 0;
3985   MovDelay[x][y] = 0;
3986
3987 #if USE_NEW_CUSTOM_VALUE
3988   CustomValue[x][y] = 0;
3989 #endif
3990
3991   AmoebaNr[x][y] = 0;
3992   ChangeDelay[x][y] = 0;
3993   ChangePage[x][y] = -1;
3994   Pushed[x][y] = FALSE;
3995
3996 #if 0
3997   ExplodeField[x][y] = EX_TYPE_NONE;
3998 #endif
3999
4000   GfxElement[x][y] = EL_UNDEFINED;
4001   GfxAction[x][y] = ACTION_DEFAULT;
4002   GfxDir[x][y] = MV_NONE;
4003 }
4004
4005 void RemoveMovingField(int x, int y)
4006 {
4007   int oldx = x, oldy = y, newx = x, newy = y;
4008   int element = Feld[x][y];
4009   int next_element = EL_UNDEFINED;
4010
4011   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4012     return;
4013
4014   if (IS_MOVING(x, y))
4015   {
4016     Moving2Blocked(x, y, &newx, &newy);
4017
4018     if (Feld[newx][newy] != EL_BLOCKED)
4019     {
4020       /* element is moving, but target field is not free (blocked), but
4021          already occupied by something different (example: acid pool);
4022          in this case, only remove the moving field, but not the target */
4023
4024       RemoveField(oldx, oldy);
4025
4026       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4027
4028       DrawLevelField(oldx, oldy);
4029
4030       return;
4031     }
4032   }
4033   else if (element == EL_BLOCKED)
4034   {
4035     Blocked2Moving(x, y, &oldx, &oldy);
4036     if (!IS_MOVING(oldx, oldy))
4037       return;
4038   }
4039
4040   if (element == EL_BLOCKED &&
4041       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4042        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4043        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4044        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4045        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4046        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4047     next_element = get_next_element(Feld[oldx][oldy]);
4048
4049   RemoveField(oldx, oldy);
4050   RemoveField(newx, newy);
4051
4052   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4053
4054   if (next_element != EL_UNDEFINED)
4055     Feld[oldx][oldy] = next_element;
4056
4057   DrawLevelField(oldx, oldy);
4058   DrawLevelField(newx, newy);
4059 }
4060
4061 void DrawDynamite(int x, int y)
4062 {
4063   int sx = SCREENX(x), sy = SCREENY(y);
4064   int graphic = el2img(Feld[x][y]);
4065   int frame;
4066
4067   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4068     return;
4069
4070   if (IS_WALKABLE_INSIDE(Back[x][y]))
4071     return;
4072
4073   if (Back[x][y])
4074     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4075   else if (Store[x][y])
4076     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4077
4078   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4079
4080   if (Back[x][y] || Store[x][y])
4081     DrawGraphicThruMask(sx, sy, graphic, frame);
4082   else
4083     DrawGraphic(sx, sy, graphic, frame);
4084 }
4085
4086 void CheckDynamite(int x, int y)
4087 {
4088   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4089   {
4090     MovDelay[x][y]--;
4091
4092     if (MovDelay[x][y] != 0)
4093     {
4094       DrawDynamite(x, y);
4095       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4096
4097       return;
4098     }
4099   }
4100
4101   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4102
4103   Bang(x, y);
4104 }
4105
4106 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4107 {
4108   boolean num_checked_players = 0;
4109   int i;
4110
4111   for (i = 0; i < MAX_PLAYERS; i++)
4112   {
4113     if (stored_player[i].active)
4114     {
4115       int sx = stored_player[i].jx;
4116       int sy = stored_player[i].jy;
4117
4118       if (num_checked_players == 0)
4119       {
4120         *sx1 = *sx2 = sx;
4121         *sy1 = *sy2 = sy;
4122       }
4123       else
4124       {
4125         *sx1 = MIN(*sx1, sx);
4126         *sy1 = MIN(*sy1, sy);
4127         *sx2 = MAX(*sx2, sx);
4128         *sy2 = MAX(*sy2, sy);
4129       }
4130
4131       num_checked_players++;
4132     }
4133   }
4134 }
4135
4136 static boolean checkIfAllPlayersFitToScreen_RND()
4137 {
4138   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4139
4140   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4141
4142   return (sx2 - sx1 < SCR_FIELDX &&
4143           sy2 - sy1 < SCR_FIELDY);
4144 }
4145
4146 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4147 {
4148   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4149
4150   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4151
4152   *sx = (sx1 + sx2) / 2;
4153   *sy = (sy1 + sy2) / 2;
4154 }
4155
4156 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4157                         boolean center_screen, boolean quick_relocation)
4158 {
4159   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4160   boolean no_delay = (tape.warp_forward);
4161   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4162   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4163
4164   if (quick_relocation)
4165   {
4166     int offset = (setup.scroll_delay ? 3 : 0);
4167
4168     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4169     {
4170       if (center_screen)
4171       {
4172         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4173                     x > SBX_Right + MIDPOSX ? SBX_Right :
4174                     x - MIDPOSX);
4175
4176         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4177                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4178                     y - MIDPOSY);
4179       }
4180       else
4181       {
4182         /* quick relocation (without scrolling), but do not center screen */
4183
4184         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4185                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4186                                old_x - MIDPOSX);
4187
4188         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4189                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4190                                old_y - MIDPOSY);
4191
4192         int offset_x = x + (scroll_x - center_scroll_x);
4193         int offset_y = y + (scroll_y - center_scroll_y);
4194
4195         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4196                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4197                     offset_x - MIDPOSX);
4198
4199         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4200                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4201                     offset_y - MIDPOSY);
4202       }
4203     }
4204     else
4205     {
4206       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
4207           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4208         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4209
4210       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
4211           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4212         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4213
4214       /* don't scroll over playfield boundaries */
4215       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4216         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4217
4218       /* don't scroll over playfield boundaries */
4219       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4220         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4221     }
4222
4223     RedrawPlayfield(TRUE, 0,0,0,0);
4224   }
4225   else
4226   {
4227     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4228                      x > SBX_Right + MIDPOSX ? SBX_Right :
4229                      x - MIDPOSX);
4230
4231     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4232                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
4233                      y - MIDPOSY);
4234
4235     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
4236
4237     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4238     {
4239       int dx = 0, dy = 0;
4240       int fx = FX, fy = FY;
4241
4242       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4243       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4244
4245       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
4246         break;
4247
4248       scroll_x -= dx;
4249       scroll_y -= dy;
4250
4251       fx += dx * TILEX / 2;
4252       fy += dy * TILEY / 2;
4253
4254       ScrollLevel(dx, dy);
4255       DrawAllPlayers();
4256
4257       /* scroll in two steps of half tile size to make things smoother */
4258       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4259       FlushDisplay();
4260       Delay(wait_delay_value);
4261
4262       /* scroll second step to align at full tile size */
4263       BackToFront();
4264       Delay(wait_delay_value);
4265     }
4266
4267     DrawAllPlayers();
4268     BackToFront();
4269     Delay(wait_delay_value);
4270   }
4271 }
4272
4273 void RelocatePlayer(int jx, int jy, int el_player_raw)
4274 {
4275   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4276   int player_nr = GET_PLAYER_NR(el_player);
4277   struct PlayerInfo *player = &stored_player[player_nr];
4278   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4279   boolean no_delay = (tape.warp_forward);
4280   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4281   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4282   int old_jx = player->jx;
4283   int old_jy = player->jy;
4284   int old_element = Feld[old_jx][old_jy];
4285   int element = Feld[jx][jy];
4286   boolean player_relocated = (old_jx != jx || old_jy != jy);
4287
4288   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4289   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
4290   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4291   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
4292   int leave_side_horiz = move_dir_horiz;
4293   int leave_side_vert  = move_dir_vert;
4294   int enter_side = enter_side_horiz | enter_side_vert;
4295   int leave_side = leave_side_horiz | leave_side_vert;
4296
4297   if (player->GameOver)         /* do not reanimate dead player */
4298     return;
4299
4300   if (!player_relocated)        /* no need to relocate the player */
4301     return;
4302
4303   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
4304   {
4305     RemoveField(jx, jy);        /* temporarily remove newly placed player */
4306     DrawLevelField(jx, jy);
4307   }
4308
4309   if (player->present)
4310   {
4311     while (player->MovPos)
4312     {
4313       ScrollPlayer(player, SCROLL_GO_ON);
4314       ScrollScreen(NULL, SCROLL_GO_ON);
4315
4316       AdvanceFrameAndPlayerCounters(player->index_nr);
4317
4318       DrawPlayer(player);
4319
4320       BackToFront();
4321       Delay(wait_delay_value);
4322     }
4323
4324     DrawPlayer(player);         /* needed here only to cleanup last field */
4325     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
4326
4327     player->is_moving = FALSE;
4328   }
4329
4330   if (IS_CUSTOM_ELEMENT(old_element))
4331     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4332                                CE_LEFT_BY_PLAYER,
4333                                player->index_bit, leave_side);
4334
4335   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4336                                       CE_PLAYER_LEAVES_X,
4337                                       player->index_bit, leave_side);
4338
4339   Feld[jx][jy] = el_player;
4340   InitPlayerField(jx, jy, el_player, TRUE);
4341
4342   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4343   {
4344     Feld[jx][jy] = element;
4345     InitField(jx, jy, FALSE);
4346   }
4347
4348   /* only visually relocate centered player */
4349   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4350                      FALSE, level.instant_relocation);
4351
4352   TestIfPlayerTouchesBadThing(jx, jy);
4353   TestIfPlayerTouchesCustomElement(jx, jy);
4354
4355   if (IS_CUSTOM_ELEMENT(element))
4356     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4357                                player->index_bit, enter_side);
4358
4359   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4360                                       player->index_bit, enter_side);
4361 }
4362
4363 void Explode(int ex, int ey, int phase, int mode)
4364 {
4365   int x, y;
4366   int last_phase;
4367   int border_element;
4368
4369   /* !!! eliminate this variable !!! */
4370   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4371
4372   if (game.explosions_delayed)
4373   {
4374     ExplodeField[ex][ey] = mode;
4375     return;
4376   }
4377
4378   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
4379   {
4380     int center_element = Feld[ex][ey];
4381     int artwork_element, explosion_element;     /* set these values later */
4382
4383 #if 0
4384     /* --- This is only really needed (and now handled) in "Impact()". --- */
4385     /* do not explode moving elements that left the explode field in time */
4386     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4387         center_element == EL_EMPTY &&
4388         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4389       return;
4390 #endif
4391
4392 #if 0
4393     /* !!! at this place, the center element may be EL_BLOCKED !!! */
4394     if (mode == EX_TYPE_NORMAL ||
4395         mode == EX_TYPE_CENTER ||
4396         mode == EX_TYPE_CROSS)
4397       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4398 #endif
4399
4400     /* remove things displayed in background while burning dynamite */
4401     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4402       Back[ex][ey] = 0;
4403
4404     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4405     {
4406       /* put moving element to center field (and let it explode there) */
4407       center_element = MovingOrBlocked2Element(ex, ey);
4408       RemoveMovingField(ex, ey);
4409       Feld[ex][ey] = center_element;
4410     }
4411
4412     /* now "center_element" is finally determined -- set related values now */
4413     artwork_element = center_element;           /* for custom player artwork */
4414     explosion_element = center_element;         /* for custom player artwork */
4415
4416     if (IS_PLAYER(ex, ey))
4417     {
4418       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4419
4420       artwork_element = stored_player[player_nr].artwork_element;
4421
4422       if (level.use_explosion_element[player_nr])
4423       {
4424         explosion_element = level.explosion_element[player_nr];
4425         artwork_element = explosion_element;
4426       }
4427     }
4428
4429 #if 1
4430     if (mode == EX_TYPE_NORMAL ||
4431         mode == EX_TYPE_CENTER ||
4432         mode == EX_TYPE_CROSS)
4433       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4434 #endif
4435
4436     last_phase = element_info[explosion_element].explosion_delay + 1;
4437
4438     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4439     {
4440       int xx = x - ex + 1;
4441       int yy = y - ey + 1;
4442       int element;
4443
4444       if (!IN_LEV_FIELD(x, y) ||
4445           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4446           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
4447         continue;
4448
4449       element = Feld[x][y];
4450
4451       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4452       {
4453         element = MovingOrBlocked2Element(x, y);
4454
4455         if (!IS_EXPLOSION_PROOF(element))
4456           RemoveMovingField(x, y);
4457       }
4458
4459       /* indestructible elements can only explode in center (but not flames) */
4460       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4461                                            mode == EX_TYPE_BORDER)) ||
4462           element == EL_FLAMES)
4463         continue;
4464
4465       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4466          behaviour, for example when touching a yamyam that explodes to rocks
4467          with active deadly shield, a rock is created under the player !!! */
4468       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4469 #if 0
4470       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4471           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
4472            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
4473 #else
4474       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
4475 #endif
4476       {
4477         if (IS_ACTIVE_BOMB(element))
4478         {
4479           /* re-activate things under the bomb like gate or penguin */
4480           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
4481           Back[x][y] = 0;
4482         }
4483
4484         continue;
4485       }
4486
4487       /* save walkable background elements while explosion on same tile */
4488       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
4489           (x != ex || y != ey || mode == EX_TYPE_BORDER))
4490         Back[x][y] = element;
4491
4492       /* ignite explodable elements reached by other explosion */
4493       if (element == EL_EXPLOSION)
4494         element = Store2[x][y];
4495
4496       if (AmoebaNr[x][y] &&
4497           (element == EL_AMOEBA_FULL ||
4498            element == EL_BD_AMOEBA ||
4499            element == EL_AMOEBA_GROWING))
4500       {
4501         AmoebaCnt[AmoebaNr[x][y]]--;
4502         AmoebaCnt2[AmoebaNr[x][y]]--;
4503       }
4504
4505       RemoveField(x, y);
4506
4507       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
4508       {
4509         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
4510
4511         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
4512
4513         if (PLAYERINFO(ex, ey)->use_murphy)
4514           Store[x][y] = EL_EMPTY;
4515       }
4516
4517       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
4518          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
4519       else if (ELEM_IS_PLAYER(center_element))
4520         Store[x][y] = EL_EMPTY;
4521       else if (center_element == EL_YAMYAM)
4522         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4523       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4524         Store[x][y] = element_info[center_element].content.e[xx][yy];
4525 #if 1
4526       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4527          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4528          otherwise) -- FIX THIS !!! */
4529       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4530         Store[x][y] = element_info[element].content.e[1][1];
4531 #else
4532       else if (!CAN_EXPLODE(element))
4533         Store[x][y] = element_info[element].content.e[1][1];
4534 #endif
4535       else
4536         Store[x][y] = EL_EMPTY;
4537
4538       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4539           center_element == EL_AMOEBA_TO_DIAMOND)
4540         Store2[x][y] = element;
4541
4542       Feld[x][y] = EL_EXPLOSION;
4543       GfxElement[x][y] = artwork_element;
4544
4545       ExplodePhase[x][y] = 1;
4546       ExplodeDelay[x][y] = last_phase;
4547
4548       Stop[x][y] = TRUE;
4549     }
4550
4551     if (center_element == EL_YAMYAM)
4552       game.yamyam_content_nr =
4553         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4554
4555     return;
4556   }
4557
4558   if (Stop[ex][ey])
4559     return;
4560
4561   x = ex;
4562   y = ey;
4563
4564   if (phase == 1)
4565     GfxFrame[x][y] = 0;         /* restart explosion animation */
4566
4567   last_phase = ExplodeDelay[x][y];
4568
4569   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4570
4571 #ifdef DEBUG
4572
4573   /* activate this even in non-DEBUG version until cause for crash in
4574      getGraphicAnimationFrame() (see below) is found and eliminated */
4575
4576 #endif
4577 #if 1
4578
4579 #if 1
4580   /* this can happen if the player leaves an explosion just in time */
4581   if (GfxElement[x][y] == EL_UNDEFINED)
4582     GfxElement[x][y] = EL_EMPTY;
4583 #else
4584   if (GfxElement[x][y] == EL_UNDEFINED)
4585   {
4586     printf("\n\n");
4587     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4588     printf("Explode(): This should never happen!\n");
4589     printf("\n\n");
4590
4591     GfxElement[x][y] = EL_EMPTY;
4592   }
4593 #endif
4594
4595 #endif
4596
4597   border_element = Store2[x][y];
4598   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4599     border_element = StorePlayer[x][y];
4600
4601   if (phase == element_info[border_element].ignition_delay ||
4602       phase == last_phase)
4603   {
4604     boolean border_explosion = FALSE;
4605
4606     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4607         !PLAYER_EXPLOSION_PROTECTED(x, y))
4608     {
4609       KillPlayerUnlessExplosionProtected(x, y);
4610       border_explosion = TRUE;
4611     }
4612     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4613     {
4614       Feld[x][y] = Store2[x][y];
4615       Store2[x][y] = 0;
4616       Bang(x, y);
4617       border_explosion = TRUE;
4618     }
4619     else if (border_element == EL_AMOEBA_TO_DIAMOND)
4620     {
4621       AmoebeUmwandeln(x, y);
4622       Store2[x][y] = 0;
4623       border_explosion = TRUE;
4624     }
4625
4626     /* if an element just explodes due to another explosion (chain-reaction),
4627        do not immediately end the new explosion when it was the last frame of
4628        the explosion (as it would be done in the following "if"-statement!) */
4629     if (border_explosion && phase == last_phase)
4630       return;
4631   }
4632
4633   if (phase == last_phase)
4634   {
4635     int element;
4636
4637     element = Feld[x][y] = Store[x][y];
4638     Store[x][y] = Store2[x][y] = 0;
4639     GfxElement[x][y] = EL_UNDEFINED;
4640
4641     /* player can escape from explosions and might therefore be still alive */
4642     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4643         element <= EL_PLAYER_IS_EXPLODING_4)
4644     {
4645       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4646       int explosion_element = EL_PLAYER_1 + player_nr;
4647       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4648       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4649
4650       if (level.use_explosion_element[player_nr])
4651         explosion_element = level.explosion_element[player_nr];
4652
4653       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4654                     element_info[explosion_element].content.e[xx][yy]);
4655     }
4656
4657     /* restore probably existing indestructible background element */
4658     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4659       element = Feld[x][y] = Back[x][y];
4660     Back[x][y] = 0;
4661
4662     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4663     GfxDir[x][y] = MV_NONE;
4664     ChangeDelay[x][y] = 0;
4665     ChangePage[x][y] = -1;
4666
4667 #if USE_NEW_CUSTOM_VALUE
4668     CustomValue[x][y] = 0;
4669 #endif
4670
4671     InitField_WithBug2(x, y, FALSE);
4672
4673     DrawLevelField(x, y);
4674
4675     TestIfElementTouchesCustomElement(x, y);
4676
4677     if (GFX_CRUMBLED(element))
4678       DrawLevelFieldCrumbledSandNeighbours(x, y);
4679
4680     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4681       StorePlayer[x][y] = 0;
4682
4683     if (ELEM_IS_PLAYER(element))
4684       RelocatePlayer(x, y, element);
4685   }
4686   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4687   {
4688     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4689     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4690
4691     if (phase == delay)
4692       DrawLevelFieldCrumbledSand(x, y);
4693
4694     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4695     {
4696       DrawLevelElement(x, y, Back[x][y]);
4697       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4698     }
4699     else if (IS_WALKABLE_UNDER(Back[x][y]))
4700     {
4701       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4702       DrawLevelElementThruMask(x, y, Back[x][y]);
4703     }
4704     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4705       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4706   }
4707 }
4708
4709 void DynaExplode(int ex, int ey)
4710 {
4711   int i, j;
4712   int dynabomb_element = Feld[ex][ey];
4713   int dynabomb_size = 1;
4714   boolean dynabomb_xl = FALSE;
4715   struct PlayerInfo *player;
4716   static int xy[4][2] =
4717   {
4718     { 0, -1 },
4719     { -1, 0 },
4720     { +1, 0 },
4721     { 0, +1 }
4722   };
4723
4724   if (IS_ACTIVE_BOMB(dynabomb_element))
4725   {
4726     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4727     dynabomb_size = player->dynabomb_size;
4728     dynabomb_xl = player->dynabomb_xl;
4729     player->dynabombs_left++;
4730   }
4731
4732   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4733
4734   for (i = 0; i < NUM_DIRECTIONS; i++)
4735   {
4736     for (j = 1; j <= dynabomb_size; j++)
4737     {
4738       int x = ex + j * xy[i][0];
4739       int y = ey + j * xy[i][1];
4740       int element;
4741
4742       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4743         break;
4744
4745       element = Feld[x][y];
4746
4747       /* do not restart explosions of fields with active bombs */
4748       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4749         continue;
4750
4751       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4752
4753       if (element != EL_EMPTY && element != EL_EXPLOSION &&
4754           !IS_DIGGABLE(element) && !dynabomb_xl)
4755         break;
4756     }
4757   }
4758 }
4759
4760 void Bang(int x, int y)
4761 {
4762   int element = MovingOrBlocked2Element(x, y);
4763   int explosion_type = EX_TYPE_NORMAL;
4764
4765   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4766   {
4767     struct PlayerInfo *player = PLAYERINFO(x, y);
4768
4769     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4770                             player->element_nr);
4771
4772     if (level.use_explosion_element[player->index_nr])
4773     {
4774       int explosion_element = level.explosion_element[player->index_nr];
4775
4776       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4777         explosion_type = EX_TYPE_CROSS;
4778       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4779         explosion_type = EX_TYPE_CENTER;
4780     }
4781   }
4782
4783   switch (element)
4784   {
4785     case EL_BUG:
4786     case EL_SPACESHIP:
4787     case EL_BD_BUTTERFLY:
4788     case EL_BD_FIREFLY:
4789     case EL_YAMYAM:
4790     case EL_DARK_YAMYAM:
4791     case EL_ROBOT:
4792     case EL_PACMAN:
4793     case EL_MOLE:
4794       RaiseScoreElement(element);
4795       break;
4796
4797     case EL_DYNABOMB_PLAYER_1_ACTIVE:
4798     case EL_DYNABOMB_PLAYER_2_ACTIVE:
4799     case EL_DYNABOMB_PLAYER_3_ACTIVE:
4800     case EL_DYNABOMB_PLAYER_4_ACTIVE:
4801     case EL_DYNABOMB_INCREASE_NUMBER:
4802     case EL_DYNABOMB_INCREASE_SIZE:
4803     case EL_DYNABOMB_INCREASE_POWER:
4804       explosion_type = EX_TYPE_DYNA;
4805       break;
4806
4807     case EL_DC_LANDMINE:
4808 #if 0
4809     case EL_EM_EXIT_OPEN:
4810     case EL_EM_STEEL_EXIT_OPEN:
4811 #endif
4812       explosion_type = EX_TYPE_CENTER;
4813       break;
4814
4815     case EL_PENGUIN:
4816     case EL_LAMP:
4817     case EL_LAMP_ACTIVE:
4818     case EL_AMOEBA_TO_DIAMOND:
4819       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
4820         explosion_type = EX_TYPE_CENTER;
4821       break;
4822
4823     default:
4824       if (element_info[element].explosion_type == EXPLODES_CROSS)
4825         explosion_type = EX_TYPE_CROSS;
4826       else if (element_info[element].explosion_type == EXPLODES_1X1)
4827         explosion_type = EX_TYPE_CENTER;
4828       break;
4829   }
4830
4831   if (explosion_type == EX_TYPE_DYNA)
4832     DynaExplode(x, y);
4833   else
4834     Explode(x, y, EX_PHASE_START, explosion_type);
4835
4836   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4837 }
4838
4839 void SplashAcid(int x, int y)
4840 {
4841   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4842       (!IN_LEV_FIELD(x - 1, y - 2) ||
4843        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4844     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4845
4846   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4847       (!IN_LEV_FIELD(x + 1, y - 2) ||
4848        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4849     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4850
4851   PlayLevelSound(x, y, SND_ACID_SPLASHING);
4852 }
4853
4854 static void InitBeltMovement()
4855 {
4856   static int belt_base_element[4] =
4857   {
4858     EL_CONVEYOR_BELT_1_LEFT,
4859     EL_CONVEYOR_BELT_2_LEFT,
4860     EL_CONVEYOR_BELT_3_LEFT,
4861     EL_CONVEYOR_BELT_4_LEFT
4862   };
4863   static int belt_base_active_element[4] =
4864   {
4865     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4866     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4867     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4868     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4869   };
4870
4871   int x, y, i, j;
4872
4873   /* set frame order for belt animation graphic according to belt direction */
4874   for (i = 0; i < NUM_BELTS; i++)
4875   {
4876     int belt_nr = i;
4877
4878     for (j = 0; j < NUM_BELT_PARTS; j++)
4879     {
4880       int element = belt_base_active_element[belt_nr] + j;
4881       int graphic = el2img(element);
4882
4883       if (game.belt_dir[i] == MV_LEFT)
4884         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4885       else
4886         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4887     }
4888   }
4889
4890   SCAN_PLAYFIELD(x, y)
4891   {
4892     int element = Feld[x][y];
4893
4894     for (i = 0; i < NUM_BELTS; i++)
4895     {
4896       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4897       {
4898         int e_belt_nr = getBeltNrFromBeltElement(element);
4899         int belt_nr = i;
4900
4901         if (e_belt_nr == belt_nr)
4902         {
4903           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4904
4905           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4906         }
4907       }
4908     }
4909   }
4910 }
4911
4912 static void ToggleBeltSwitch(int x, int y)
4913 {
4914   static int belt_base_element[4] =
4915   {
4916     EL_CONVEYOR_BELT_1_LEFT,
4917     EL_CONVEYOR_BELT_2_LEFT,
4918     EL_CONVEYOR_BELT_3_LEFT,
4919     EL_CONVEYOR_BELT_4_LEFT
4920   };
4921   static int belt_base_active_element[4] =
4922   {
4923     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4924     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4925     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4926     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4927   };
4928   static int belt_base_switch_element[4] =
4929   {
4930     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4931     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4932     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4933     EL_CONVEYOR_BELT_4_SWITCH_LEFT
4934   };
4935   static int belt_move_dir[4] =
4936   {
4937     MV_LEFT,
4938     MV_NONE,
4939     MV_RIGHT,
4940     MV_NONE,
4941   };
4942
4943   int element = Feld[x][y];
4944   int belt_nr = getBeltNrFromBeltSwitchElement(element);
4945   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4946   int belt_dir = belt_move_dir[belt_dir_nr];
4947   int xx, yy, i;
4948
4949   if (!IS_BELT_SWITCH(element))
4950     return;
4951
4952   game.belt_dir_nr[belt_nr] = belt_dir_nr;
4953   game.belt_dir[belt_nr] = belt_dir;
4954
4955   if (belt_dir_nr == 3)
4956     belt_dir_nr = 1;
4957
4958   /* set frame order for belt animation graphic according to belt direction */
4959   for (i = 0; i < NUM_BELT_PARTS; i++)
4960   {
4961     int element = belt_base_active_element[belt_nr] + i;
4962     int graphic = el2img(element);
4963
4964     if (belt_dir == MV_LEFT)
4965       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4966     else
4967       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4968   }
4969
4970   SCAN_PLAYFIELD(xx, yy)
4971   {
4972     int element = Feld[xx][yy];
4973
4974     if (IS_BELT_SWITCH(element))
4975     {
4976       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4977
4978       if (e_belt_nr == belt_nr)
4979       {
4980         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4981         DrawLevelField(xx, yy);
4982       }
4983     }
4984     else if (IS_BELT(element) && belt_dir != MV_NONE)
4985     {
4986       int e_belt_nr = getBeltNrFromBeltElement(element);
4987
4988       if (e_belt_nr == belt_nr)
4989       {
4990         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4991
4992         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4993         DrawLevelField(xx, yy);
4994       }
4995     }
4996     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4997     {
4998       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4999
5000       if (e_belt_nr == belt_nr)
5001       {
5002         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5003
5004         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5005         DrawLevelField(xx, yy);
5006       }
5007     }
5008   }
5009 }
5010
5011 static void ToggleSwitchgateSwitch(int x, int y)
5012 {
5013   int xx, yy;
5014
5015   game.switchgate_pos = !game.switchgate_pos;
5016
5017   SCAN_PLAYFIELD(xx, yy)
5018   {
5019     int element = Feld[xx][yy];
5020
5021 #if !USE_BOTH_SWITCHGATE_SWITCHES
5022     if (element == EL_SWITCHGATE_SWITCH_UP ||
5023         element == EL_SWITCHGATE_SWITCH_DOWN)
5024     {
5025       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5026       DrawLevelField(xx, yy);
5027     }
5028     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5029              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5030     {
5031       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5032       DrawLevelField(xx, yy);
5033     }
5034 #else
5035     if (element == EL_SWITCHGATE_SWITCH_UP)
5036     {
5037       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5038       DrawLevelField(xx, yy);
5039     }
5040     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5041     {
5042       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5043       DrawLevelField(xx, yy);
5044     }
5045     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5046     {
5047       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5048       DrawLevelField(xx, yy);
5049     }
5050     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5051     {
5052       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5053       DrawLevelField(xx, yy);
5054     }
5055 #endif
5056     else if (element == EL_SWITCHGATE_OPEN ||
5057              element == EL_SWITCHGATE_OPENING)
5058     {
5059       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5060
5061       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5062     }
5063     else if (element == EL_SWITCHGATE_CLOSED ||
5064              element == EL_SWITCHGATE_CLOSING)
5065     {
5066       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5067
5068       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5069     }
5070   }
5071 }
5072
5073 static int getInvisibleActiveFromInvisibleElement(int element)
5074 {
5075   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5076           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5077           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5078           element);
5079 }
5080
5081 static int getInvisibleFromInvisibleActiveElement(int element)
5082 {
5083   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5084           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5085           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5086           element);
5087 }
5088
5089 static void RedrawAllLightSwitchesAndInvisibleElements()
5090 {
5091   int x, y;
5092
5093   SCAN_PLAYFIELD(x, y)
5094   {
5095     int element = Feld[x][y];
5096
5097     if (element == EL_LIGHT_SWITCH &&
5098         game.light_time_left > 0)
5099     {
5100       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5101       DrawLevelField(x, y);
5102     }
5103     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5104              game.light_time_left == 0)
5105     {
5106       Feld[x][y] = EL_LIGHT_SWITCH;
5107       DrawLevelField(x, y);
5108     }
5109     else if (element == EL_EMC_DRIPPER &&
5110              game.light_time_left > 0)
5111     {
5112       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5113       DrawLevelField(x, y);
5114     }
5115     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5116              game.light_time_left == 0)
5117     {
5118       Feld[x][y] = EL_EMC_DRIPPER;
5119       DrawLevelField(x, y);
5120     }
5121     else if (element == EL_INVISIBLE_STEELWALL ||
5122              element == EL_INVISIBLE_WALL ||
5123              element == EL_INVISIBLE_SAND)
5124     {
5125       if (game.light_time_left > 0)
5126         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5127
5128       DrawLevelField(x, y);
5129
5130       /* uncrumble neighbour fields, if needed */
5131       if (element == EL_INVISIBLE_SAND)
5132         DrawLevelFieldCrumbledSandNeighbours(x, y);
5133     }
5134     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5135              element == EL_INVISIBLE_WALL_ACTIVE ||
5136              element == EL_INVISIBLE_SAND_ACTIVE)
5137     {
5138       if (game.light_time_left == 0)
5139         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5140
5141       DrawLevelField(x, y);
5142
5143       /* re-crumble neighbour fields, if needed */
5144       if (element == EL_INVISIBLE_SAND)
5145         DrawLevelFieldCrumbledSandNeighbours(x, y);
5146     }
5147   }
5148 }
5149
5150 static void RedrawAllInvisibleElementsForLenses()
5151 {
5152   int x, y;
5153
5154   SCAN_PLAYFIELD(x, y)
5155   {
5156     int element = Feld[x][y];
5157
5158     if (element == EL_EMC_DRIPPER &&
5159         game.lenses_time_left > 0)
5160     {
5161       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5162       DrawLevelField(x, y);
5163     }
5164     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5165              game.lenses_time_left == 0)
5166     {
5167       Feld[x][y] = EL_EMC_DRIPPER;
5168       DrawLevelField(x, y);
5169     }
5170     else if (element == EL_INVISIBLE_STEELWALL ||
5171              element == EL_INVISIBLE_WALL ||
5172              element == EL_INVISIBLE_SAND)
5173     {
5174       if (game.lenses_time_left > 0)
5175         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5176
5177       DrawLevelField(x, y);
5178
5179       /* uncrumble neighbour fields, if needed */
5180       if (element == EL_INVISIBLE_SAND)
5181         DrawLevelFieldCrumbledSandNeighbours(x, y);
5182     }
5183     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5184              element == EL_INVISIBLE_WALL_ACTIVE ||
5185              element == EL_INVISIBLE_SAND_ACTIVE)
5186     {
5187       if (game.lenses_time_left == 0)
5188         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5189
5190       DrawLevelField(x, y);
5191
5192       /* re-crumble neighbour fields, if needed */
5193       if (element == EL_INVISIBLE_SAND)
5194         DrawLevelFieldCrumbledSandNeighbours(x, y);
5195     }
5196   }
5197 }
5198
5199 static void RedrawAllInvisibleElementsForMagnifier()
5200 {
5201   int x, y;
5202
5203   SCAN_PLAYFIELD(x, y)
5204   {
5205     int element = Feld[x][y];
5206
5207     if (element == EL_EMC_FAKE_GRASS &&
5208         game.magnify_time_left > 0)
5209     {
5210       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5211       DrawLevelField(x, y);
5212     }
5213     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5214              game.magnify_time_left == 0)
5215     {
5216       Feld[x][y] = EL_EMC_FAKE_GRASS;
5217       DrawLevelField(x, y);
5218     }
5219     else if (IS_GATE_GRAY(element) &&
5220              game.magnify_time_left > 0)
5221     {
5222       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5223                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5224                     IS_EM_GATE_GRAY(element) ?
5225                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5226                     IS_EMC_GATE_GRAY(element) ?
5227                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5228                     element);
5229       DrawLevelField(x, y);
5230     }
5231     else if (IS_GATE_GRAY_ACTIVE(element) &&
5232              game.magnify_time_left == 0)
5233     {
5234       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5235                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5236                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5237                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5238                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5239                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5240                     element);
5241       DrawLevelField(x, y);
5242     }
5243   }
5244 }
5245
5246 static void ToggleLightSwitch(int x, int y)
5247 {
5248   int element = Feld[x][y];
5249
5250   game.light_time_left =
5251     (element == EL_LIGHT_SWITCH ?
5252      level.time_light * FRAMES_PER_SECOND : 0);
5253
5254   RedrawAllLightSwitchesAndInvisibleElements();
5255 }
5256
5257 static void ActivateTimegateSwitch(int x, int y)
5258 {
5259   int xx, yy;
5260
5261   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5262
5263   SCAN_PLAYFIELD(xx, yy)
5264   {
5265     int element = Feld[xx][yy];
5266
5267     if (element == EL_TIMEGATE_CLOSED ||
5268         element == EL_TIMEGATE_CLOSING)
5269     {
5270       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5271       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5272     }
5273
5274     /*
5275     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5276     {
5277       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5278       DrawLevelField(xx, yy);
5279     }
5280     */
5281
5282   }
5283
5284 #if 1
5285   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5286                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5287 #else
5288   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5289 #endif
5290 }
5291
5292 void Impact(int x, int y)
5293 {
5294   boolean last_line = (y == lev_fieldy - 1);
5295   boolean object_hit = FALSE;
5296   boolean impact = (last_line || object_hit);
5297   int element = Feld[x][y];
5298   int smashed = EL_STEELWALL;
5299
5300   if (!last_line)       /* check if element below was hit */
5301   {
5302     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5303       return;
5304
5305     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5306                                          MovDir[x][y + 1] != MV_DOWN ||
5307                                          MovPos[x][y + 1] <= TILEY / 2));
5308
5309     /* do not smash moving elements that left the smashed field in time */
5310     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5311         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5312       object_hit = FALSE;
5313
5314 #if USE_QUICKSAND_IMPACT_BUGFIX
5315     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5316     {
5317       RemoveMovingField(x, y + 1);
5318       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5319       Feld[x][y + 2] = EL_ROCK;
5320       DrawLevelField(x, y + 2);
5321
5322       object_hit = TRUE;
5323     }
5324
5325     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5326     {
5327       RemoveMovingField(x, y + 1);
5328       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5329       Feld[x][y + 2] = EL_ROCK;
5330       DrawLevelField(x, y + 2);
5331
5332       object_hit = TRUE;
5333     }
5334 #endif
5335
5336     if (object_hit)
5337       smashed = MovingOrBlocked2Element(x, y + 1);
5338
5339     impact = (last_line || object_hit);
5340   }
5341
5342   if (!last_line && smashed == EL_ACID) /* element falls into acid */
5343   {
5344     SplashAcid(x, y + 1);
5345     return;
5346   }
5347
5348   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5349   /* only reset graphic animation if graphic really changes after impact */
5350   if (impact &&
5351       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5352   {
5353     ResetGfxAnimation(x, y);
5354     DrawLevelField(x, y);
5355   }
5356
5357   if (impact && CAN_EXPLODE_IMPACT(element))
5358   {
5359     Bang(x, y);
5360     return;
5361   }
5362   else if (impact && element == EL_PEARL &&
5363            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5364   {
5365     ResetGfxAnimation(x, y);
5366
5367     Feld[x][y] = EL_PEARL_BREAKING;
5368     PlayLevelSound(x, y, SND_PEARL_BREAKING);
5369     return;
5370   }
5371   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5372   {
5373     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5374
5375     return;
5376   }
5377
5378   if (impact && element == EL_AMOEBA_DROP)
5379   {
5380     if (object_hit && IS_PLAYER(x, y + 1))
5381       KillPlayerUnlessEnemyProtected(x, y + 1);
5382     else if (object_hit && smashed == EL_PENGUIN)
5383       Bang(x, y + 1);
5384     else
5385     {
5386       Feld[x][y] = EL_AMOEBA_GROWING;
5387       Store[x][y] = EL_AMOEBA_WET;
5388
5389       ResetRandomAnimationValue(x, y);
5390     }
5391     return;
5392   }
5393
5394   if (object_hit)               /* check which object was hit */
5395   {
5396     if ((CAN_PASS_MAGIC_WALL(element) && 
5397          (smashed == EL_MAGIC_WALL ||
5398           smashed == EL_BD_MAGIC_WALL)) ||
5399         (CAN_PASS_DC_MAGIC_WALL(element) &&
5400          smashed == EL_DC_MAGIC_WALL))
5401     {
5402       int xx, yy;
5403       int activated_magic_wall =
5404         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5405          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5406          EL_DC_MAGIC_WALL_ACTIVE);
5407
5408       /* activate magic wall / mill */
5409       SCAN_PLAYFIELD(xx, yy)
5410       {
5411         if (Feld[xx][yy] == smashed)
5412           Feld[xx][yy] = activated_magic_wall;
5413       }
5414
5415       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5416       game.magic_wall_active = TRUE;
5417
5418       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5419                             SND_MAGIC_WALL_ACTIVATING :
5420                             smashed == EL_BD_MAGIC_WALL ?
5421                             SND_BD_MAGIC_WALL_ACTIVATING :
5422                             SND_DC_MAGIC_WALL_ACTIVATING));
5423     }
5424
5425     if (IS_PLAYER(x, y + 1))
5426     {
5427       if (CAN_SMASH_PLAYER(element))
5428       {
5429         KillPlayerUnlessEnemyProtected(x, y + 1);
5430         return;
5431       }
5432     }
5433     else if (smashed == EL_PENGUIN)
5434     {
5435       if (CAN_SMASH_PLAYER(element))
5436       {
5437         Bang(x, y + 1);
5438         return;
5439       }
5440     }
5441     else if (element == EL_BD_DIAMOND)
5442     {
5443       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5444       {
5445         Bang(x, y + 1);
5446         return;
5447       }
5448     }
5449     else if (((element == EL_SP_INFOTRON ||
5450                element == EL_SP_ZONK) &&
5451               (smashed == EL_SP_SNIKSNAK ||
5452                smashed == EL_SP_ELECTRON ||
5453                smashed == EL_SP_DISK_ORANGE)) ||
5454              (element == EL_SP_INFOTRON &&
5455               smashed == EL_SP_DISK_YELLOW))
5456     {
5457       Bang(x, y + 1);
5458       return;
5459     }
5460     else if (CAN_SMASH_EVERYTHING(element))
5461     {
5462       if (IS_CLASSIC_ENEMY(smashed) ||
5463           CAN_EXPLODE_SMASHED(smashed))
5464       {
5465         Bang(x, y + 1);
5466         return;
5467       }
5468       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5469       {
5470         if (smashed == EL_LAMP ||
5471             smashed == EL_LAMP_ACTIVE)
5472         {
5473           Bang(x, y + 1);
5474           return;
5475         }
5476         else if (smashed == EL_NUT)
5477         {
5478           Feld[x][y + 1] = EL_NUT_BREAKING;
5479           PlayLevelSound(x, y, SND_NUT_BREAKING);
5480           RaiseScoreElement(EL_NUT);
5481           return;
5482         }
5483         else if (smashed == EL_PEARL)
5484         {
5485           ResetGfxAnimation(x, y);
5486
5487           Feld[x][y + 1] = EL_PEARL_BREAKING;
5488           PlayLevelSound(x, y, SND_PEARL_BREAKING);
5489           return;
5490         }
5491         else if (smashed == EL_DIAMOND)
5492         {
5493           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
5494           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
5495           return;
5496         }
5497         else if (IS_BELT_SWITCH(smashed))
5498         {
5499           ToggleBeltSwitch(x, y + 1);
5500         }
5501         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
5502                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
5503                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
5504                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
5505         {
5506           ToggleSwitchgateSwitch(x, y + 1);
5507         }
5508         else if (smashed == EL_LIGHT_SWITCH ||
5509                  smashed == EL_LIGHT_SWITCH_ACTIVE)
5510         {
5511           ToggleLightSwitch(x, y + 1);
5512         }
5513         else
5514         {
5515 #if 0
5516           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
5517 #endif
5518
5519           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5520
5521           CheckElementChangeBySide(x, y + 1, smashed, element,
5522                                    CE_SWITCHED, CH_SIDE_TOP);
5523           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5524                                             CH_SIDE_TOP);
5525         }
5526       }
5527       else
5528       {
5529         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5530       }
5531     }
5532   }
5533
5534   /* play sound of magic wall / mill */
5535   if (!last_line &&
5536       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5537        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
5538        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
5539   {
5540     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5541       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5542     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5543       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5544     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5545       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5546
5547     return;
5548   }
5549
5550   /* play sound of object that hits the ground */
5551   if (last_line || object_hit)
5552     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5553 }
5554
5555 inline static void TurnRoundExt(int x, int y)
5556 {
5557   static struct
5558   {
5559     int dx, dy;
5560   } move_xy[] =
5561   {
5562     {  0,  0 },
5563     { -1,  0 },
5564     { +1,  0 },
5565     {  0,  0 },
5566     {  0, -1 },
5567     {  0,  0 }, { 0, 0 }, { 0, 0 },
5568     {  0, +1 }
5569   };
5570   static struct
5571   {
5572     int left, right, back;
5573   } turn[] =
5574   {
5575     { 0,        0,              0        },
5576     { MV_DOWN,  MV_UP,          MV_RIGHT },
5577     { MV_UP,    MV_DOWN,        MV_LEFT  },
5578     { 0,        0,              0        },
5579     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
5580     { 0,        0,              0        },
5581     { 0,        0,              0        },
5582     { 0,        0,              0        },
5583     { MV_RIGHT, MV_LEFT,        MV_UP    }
5584   };
5585
5586   int element = Feld[x][y];
5587   int move_pattern = element_info[element].move_pattern;
5588
5589   int old_move_dir = MovDir[x][y];
5590   int left_dir  = turn[old_move_dir].left;
5591   int right_dir = turn[old_move_dir].right;
5592   int back_dir  = turn[old_move_dir].back;
5593
5594   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
5595   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
5596   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
5597   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
5598
5599   int left_x  = x + left_dx,  left_y  = y + left_dy;
5600   int right_x = x + right_dx, right_y = y + right_dy;
5601   int move_x  = x + move_dx,  move_y  = y + move_dy;
5602
5603   int xx, yy;
5604
5605   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5606   {
5607     TestIfBadThingTouchesOtherBadThing(x, y);
5608
5609     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5610       MovDir[x][y] = right_dir;
5611     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5612       MovDir[x][y] = left_dir;
5613
5614     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5615       MovDelay[x][y] = 9;
5616     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
5617       MovDelay[x][y] = 1;
5618   }
5619   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5620   {
5621     TestIfBadThingTouchesOtherBadThing(x, y);
5622
5623     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5624       MovDir[x][y] = left_dir;
5625     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5626       MovDir[x][y] = right_dir;
5627
5628     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5629       MovDelay[x][y] = 9;
5630     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
5631       MovDelay[x][y] = 1;
5632   }
5633   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5634   {
5635     TestIfBadThingTouchesOtherBadThing(x, y);
5636
5637     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5638       MovDir[x][y] = left_dir;
5639     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5640       MovDir[x][y] = right_dir;
5641
5642     if (MovDir[x][y] != old_move_dir)
5643       MovDelay[x][y] = 9;
5644   }
5645   else if (element == EL_YAMYAM)
5646   {
5647     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5648     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5649
5650     if (can_turn_left && can_turn_right)
5651       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5652     else if (can_turn_left)
5653       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5654     else if (can_turn_right)
5655       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5656     else
5657       MovDir[x][y] = back_dir;
5658
5659     MovDelay[x][y] = 16 + 16 * RND(3);
5660   }
5661   else if (element == EL_DARK_YAMYAM)
5662   {
5663     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5664                                                          left_x, left_y);
5665     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5666                                                          right_x, right_y);
5667
5668     if (can_turn_left && can_turn_right)
5669       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5670     else if (can_turn_left)
5671       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5672     else if (can_turn_right)
5673       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5674     else
5675       MovDir[x][y] = back_dir;
5676
5677     MovDelay[x][y] = 16 + 16 * RND(3);
5678   }
5679   else if (element == EL_PACMAN)
5680   {
5681     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5682     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5683
5684     if (can_turn_left && can_turn_right)
5685       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5686     else if (can_turn_left)
5687       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5688     else if (can_turn_right)
5689       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5690     else
5691       MovDir[x][y] = back_dir;
5692
5693     MovDelay[x][y] = 6 + RND(40);
5694   }
5695   else if (element == EL_PIG)
5696   {
5697     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5698     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5699     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5700     boolean should_turn_left, should_turn_right, should_move_on;
5701     int rnd_value = 24;
5702     int rnd = RND(rnd_value);
5703
5704     should_turn_left = (can_turn_left &&
5705                         (!can_move_on ||
5706                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5707                                                    y + back_dy + left_dy)));
5708     should_turn_right = (can_turn_right &&
5709                          (!can_move_on ||
5710                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5711                                                     y + back_dy + right_dy)));
5712     should_move_on = (can_move_on &&
5713                       (!can_turn_left ||
5714                        !can_turn_right ||
5715                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5716                                                  y + move_dy + left_dy) ||
5717                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5718                                                  y + move_dy + right_dy)));
5719
5720     if (should_turn_left || should_turn_right || should_move_on)
5721     {
5722       if (should_turn_left && should_turn_right && should_move_on)
5723         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
5724                         rnd < 2 * rnd_value / 3 ? right_dir :
5725                         old_move_dir);
5726       else if (should_turn_left && should_turn_right)
5727         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5728       else if (should_turn_left && should_move_on)
5729         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5730       else if (should_turn_right && should_move_on)
5731         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5732       else if (should_turn_left)
5733         MovDir[x][y] = left_dir;
5734       else if (should_turn_right)
5735         MovDir[x][y] = right_dir;
5736       else if (should_move_on)
5737         MovDir[x][y] = old_move_dir;
5738     }
5739     else if (can_move_on && rnd > rnd_value / 8)
5740       MovDir[x][y] = old_move_dir;
5741     else if (can_turn_left && can_turn_right)
5742       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5743     else if (can_turn_left && rnd > rnd_value / 8)
5744       MovDir[x][y] = left_dir;
5745     else if (can_turn_right && rnd > rnd_value/8)
5746       MovDir[x][y] = right_dir;
5747     else
5748       MovDir[x][y] = back_dir;
5749
5750     xx = x + move_xy[MovDir[x][y]].dx;
5751     yy = y + move_xy[MovDir[x][y]].dy;
5752
5753     if (!IN_LEV_FIELD(xx, yy) ||
5754         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5755       MovDir[x][y] = old_move_dir;
5756
5757     MovDelay[x][y] = 0;
5758   }
5759   else if (element == EL_DRAGON)
5760   {
5761     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5762     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5763     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5764     int rnd_value = 24;
5765     int rnd = RND(rnd_value);
5766
5767     if (can_move_on && rnd > rnd_value / 8)
5768       MovDir[x][y] = old_move_dir;
5769     else if (can_turn_left && can_turn_right)
5770       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5771     else if (can_turn_left && rnd > rnd_value / 8)
5772       MovDir[x][y] = left_dir;
5773     else if (can_turn_right && rnd > rnd_value / 8)
5774       MovDir[x][y] = right_dir;
5775     else
5776       MovDir[x][y] = back_dir;
5777
5778     xx = x + move_xy[MovDir[x][y]].dx;
5779     yy = y + move_xy[MovDir[x][y]].dy;
5780
5781     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5782       MovDir[x][y] = old_move_dir;
5783
5784     MovDelay[x][y] = 0;
5785   }
5786   else if (element == EL_MOLE)
5787   {
5788     boolean can_move_on =
5789       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5790                             IS_AMOEBOID(Feld[move_x][move_y]) ||
5791                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5792     if (!can_move_on)
5793     {
5794       boolean can_turn_left =
5795         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5796                               IS_AMOEBOID(Feld[left_x][left_y])));
5797
5798       boolean can_turn_right =
5799         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5800                               IS_AMOEBOID(Feld[right_x][right_y])));
5801
5802       if (can_turn_left && can_turn_right)
5803         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5804       else if (can_turn_left)
5805         MovDir[x][y] = left_dir;
5806       else
5807         MovDir[x][y] = right_dir;
5808     }
5809
5810     if (MovDir[x][y] != old_move_dir)
5811       MovDelay[x][y] = 9;
5812   }
5813   else if (element == EL_BALLOON)
5814   {
5815     MovDir[x][y] = game.wind_direction;
5816     MovDelay[x][y] = 0;
5817   }
5818   else if (element == EL_SPRING)
5819   {
5820 #if USE_NEW_SPRING_BUMPER
5821     if (MovDir[x][y] & MV_HORIZONTAL)
5822     {
5823       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5824           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5825       {
5826         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5827         ResetGfxAnimation(move_x, move_y);
5828         DrawLevelField(move_x, move_y);
5829
5830         MovDir[x][y] = back_dir;
5831       }
5832       else if (!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     }
5836 #else
5837     if (MovDir[x][y] & MV_HORIZONTAL &&
5838         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5839          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5840       MovDir[x][y] = MV_NONE;
5841 #endif
5842
5843     MovDelay[x][y] = 0;
5844   }
5845   else if (element == EL_ROBOT ||
5846            element == EL_SATELLITE ||
5847            element == EL_PENGUIN ||
5848            element == EL_EMC_ANDROID)
5849   {
5850     int attr_x = -1, attr_y = -1;
5851
5852     if (AllPlayersGone)
5853     {
5854       attr_x = ExitX;
5855       attr_y = ExitY;
5856     }
5857     else
5858     {
5859       int i;
5860
5861       for (i = 0; i < MAX_PLAYERS; i++)
5862       {
5863         struct PlayerInfo *player = &stored_player[i];
5864         int jx = player->jx, jy = player->jy;
5865
5866         if (!player->active)
5867           continue;
5868
5869         if (attr_x == -1 ||
5870             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5871         {
5872           attr_x = jx;
5873           attr_y = jy;
5874         }
5875       }
5876     }
5877
5878     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5879         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5880          game.engine_version < VERSION_IDENT(3,1,0,0)))
5881     {
5882       attr_x = ZX;
5883       attr_y = ZY;
5884     }
5885
5886     if (element == EL_PENGUIN)
5887     {
5888       int i;
5889       static int xy[4][2] =
5890       {
5891         { 0, -1 },
5892         { -1, 0 },
5893         { +1, 0 },
5894         { 0, +1 }
5895       };
5896
5897       for (i = 0; i < NUM_DIRECTIONS; i++)
5898       {
5899         int ex = x + xy[i][0];
5900         int ey = y + xy[i][1];
5901
5902         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
5903                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
5904                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
5905                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
5906         {
5907           attr_x = ex;
5908           attr_y = ey;
5909           break;
5910         }
5911       }
5912     }
5913
5914     MovDir[x][y] = MV_NONE;
5915     if (attr_x < x)
5916       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5917     else if (attr_x > x)
5918       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5919     if (attr_y < y)
5920       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5921     else if (attr_y > y)
5922       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5923
5924     if (element == EL_ROBOT)
5925     {
5926       int newx, newy;
5927
5928       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5929         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5930       Moving2Blocked(x, y, &newx, &newy);
5931
5932       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5933         MovDelay[x][y] = 8 + 8 * !RND(3);
5934       else
5935         MovDelay[x][y] = 16;
5936     }
5937     else if (element == EL_PENGUIN)
5938     {
5939       int newx, newy;
5940
5941       MovDelay[x][y] = 1;
5942
5943       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5944       {
5945         boolean first_horiz = RND(2);
5946         int new_move_dir = MovDir[x][y];
5947
5948         MovDir[x][y] =
5949           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5950         Moving2Blocked(x, y, &newx, &newy);
5951
5952         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5953           return;
5954
5955         MovDir[x][y] =
5956           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5957         Moving2Blocked(x, y, &newx, &newy);
5958
5959         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5960           return;
5961
5962         MovDir[x][y] = old_move_dir;
5963         return;
5964       }
5965     }
5966     else if (element == EL_SATELLITE)
5967     {
5968       int newx, newy;
5969
5970       MovDelay[x][y] = 1;
5971
5972       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5973       {
5974         boolean first_horiz = RND(2);
5975         int new_move_dir = MovDir[x][y];
5976
5977         MovDir[x][y] =
5978           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5979         Moving2Blocked(x, y, &newx, &newy);
5980
5981         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5982           return;
5983
5984         MovDir[x][y] =
5985           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5986         Moving2Blocked(x, y, &newx, &newy);
5987
5988         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5989           return;
5990
5991         MovDir[x][y] = old_move_dir;
5992         return;
5993       }
5994     }
5995     else if (element == EL_EMC_ANDROID)
5996     {
5997       static int check_pos[16] =
5998       {
5999         -1,             /*  0 => (invalid)          */
6000         7,              /*  1 => MV_LEFT            */
6001         3,              /*  2 => MV_RIGHT           */
6002         -1,             /*  3 => (invalid)          */
6003         1,              /*  4 =>            MV_UP   */
6004         0,              /*  5 => MV_LEFT  | MV_UP   */
6005         2,              /*  6 => MV_RIGHT | MV_UP   */
6006         -1,             /*  7 => (invalid)          */
6007         5,              /*  8 =>            MV_DOWN */
6008         6,              /*  9 => MV_LEFT  | MV_DOWN */
6009         4,              /* 10 => MV_RIGHT | MV_DOWN */
6010         -1,             /* 11 => (invalid)          */
6011         -1,             /* 12 => (invalid)          */
6012         -1,             /* 13 => (invalid)          */
6013         -1,             /* 14 => (invalid)          */
6014         -1,             /* 15 => (invalid)          */
6015       };
6016       static struct
6017       {
6018         int dx, dy;
6019         int dir;
6020       } check_xy[8] =
6021       {
6022         { -1, -1,       MV_LEFT  | MV_UP   },
6023         {  0, -1,                  MV_UP   },
6024         { +1, -1,       MV_RIGHT | MV_UP   },
6025         { +1,  0,       MV_RIGHT           },
6026         { +1, +1,       MV_RIGHT | MV_DOWN },
6027         {  0, +1,                  MV_DOWN },
6028         { -1, +1,       MV_LEFT  | MV_DOWN },
6029         { -1,  0,       MV_LEFT            },
6030       };
6031       int start_pos, check_order;
6032       boolean can_clone = FALSE;
6033       int i;
6034
6035       /* check if there is any free field around current position */
6036       for (i = 0; i < 8; i++)
6037       {
6038         int newx = x + check_xy[i].dx;
6039         int newy = y + check_xy[i].dy;
6040
6041         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6042         {
6043           can_clone = TRUE;
6044
6045           break;
6046         }
6047       }
6048
6049       if (can_clone)            /* randomly find an element to clone */
6050       {
6051         can_clone = FALSE;
6052
6053         start_pos = check_pos[RND(8)];
6054         check_order = (RND(2) ? -1 : +1);
6055
6056         for (i = 0; i < 8; i++)
6057         {
6058           int pos_raw = start_pos + i * check_order;
6059           int pos = (pos_raw + 8) % 8;
6060           int newx = x + check_xy[pos].dx;
6061           int newy = y + check_xy[pos].dy;
6062
6063           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6064           {
6065             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6066             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6067
6068             Store[x][y] = Feld[newx][newy];
6069
6070             can_clone = TRUE;
6071
6072             break;
6073           }
6074         }
6075       }
6076
6077       if (can_clone)            /* randomly find a direction to move */
6078       {
6079         can_clone = FALSE;
6080
6081         start_pos = check_pos[RND(8)];
6082         check_order = (RND(2) ? -1 : +1);
6083
6084         for (i = 0; i < 8; i++)
6085         {
6086           int pos_raw = start_pos + i * check_order;
6087           int pos = (pos_raw + 8) % 8;
6088           int newx = x + check_xy[pos].dx;
6089           int newy = y + check_xy[pos].dy;
6090           int new_move_dir = check_xy[pos].dir;
6091
6092           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6093           {
6094             MovDir[x][y] = new_move_dir;
6095             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6096
6097             can_clone = TRUE;
6098
6099             break;
6100           }
6101         }
6102       }
6103
6104       if (can_clone)            /* cloning and moving successful */
6105         return;
6106
6107       /* cannot clone -- try to move towards player */
6108
6109       start_pos = check_pos[MovDir[x][y] & 0x0f];
6110       check_order = (RND(2) ? -1 : +1);
6111
6112       for (i = 0; i < 3; i++)
6113       {
6114         /* first check start_pos, then previous/next or (next/previous) pos */
6115         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6116         int pos = (pos_raw + 8) % 8;
6117         int newx = x + check_xy[pos].dx;
6118         int newy = y + check_xy[pos].dy;
6119         int new_move_dir = check_xy[pos].dir;
6120
6121         if (IS_PLAYER(newx, newy))
6122           break;
6123
6124         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6125         {
6126           MovDir[x][y] = new_move_dir;
6127           MovDelay[x][y] = level.android_move_time * 8 + 1;
6128
6129           break;
6130         }
6131       }
6132     }
6133   }
6134   else if (move_pattern == MV_TURNING_LEFT ||
6135            move_pattern == MV_TURNING_RIGHT ||
6136            move_pattern == MV_TURNING_LEFT_RIGHT ||
6137            move_pattern == MV_TURNING_RIGHT_LEFT ||
6138            move_pattern == MV_TURNING_RANDOM ||
6139            move_pattern == MV_ALL_DIRECTIONS)
6140   {
6141     boolean can_turn_left =
6142       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6143     boolean can_turn_right =
6144       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6145
6146     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6147       return;
6148
6149     if (move_pattern == MV_TURNING_LEFT)
6150       MovDir[x][y] = left_dir;
6151     else if (move_pattern == MV_TURNING_RIGHT)
6152       MovDir[x][y] = right_dir;
6153     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6154       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6155     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6156       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6157     else if (move_pattern == MV_TURNING_RANDOM)
6158       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6159                       can_turn_right && !can_turn_left ? right_dir :
6160                       RND(2) ? left_dir : right_dir);
6161     else if (can_turn_left && can_turn_right)
6162       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6163     else if (can_turn_left)
6164       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6165     else if (can_turn_right)
6166       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6167     else
6168       MovDir[x][y] = back_dir;
6169
6170     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6171   }
6172   else if (move_pattern == MV_HORIZONTAL ||
6173            move_pattern == MV_VERTICAL)
6174   {
6175     if (move_pattern & old_move_dir)
6176       MovDir[x][y] = back_dir;
6177     else if (move_pattern == MV_HORIZONTAL)
6178       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6179     else if (move_pattern == MV_VERTICAL)
6180       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6181
6182     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6183   }
6184   else if (move_pattern & MV_ANY_DIRECTION)
6185   {
6186     MovDir[x][y] = move_pattern;
6187     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6188   }
6189   else if (move_pattern & MV_WIND_DIRECTION)
6190   {
6191     MovDir[x][y] = game.wind_direction;
6192     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6193   }
6194   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6195   {
6196     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6197       MovDir[x][y] = left_dir;
6198     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6199       MovDir[x][y] = right_dir;
6200
6201     if (MovDir[x][y] != old_move_dir)
6202       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6203   }
6204   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6205   {
6206     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6207       MovDir[x][y] = right_dir;
6208     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6209       MovDir[x][y] = left_dir;
6210
6211     if (MovDir[x][y] != old_move_dir)
6212       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6213   }
6214   else if (move_pattern == MV_TOWARDS_PLAYER ||
6215            move_pattern == MV_AWAY_FROM_PLAYER)
6216   {
6217     int attr_x = -1, attr_y = -1;
6218     int newx, newy;
6219     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6220
6221     if (AllPlayersGone)
6222     {
6223       attr_x = ExitX;
6224       attr_y = ExitY;
6225     }
6226     else
6227     {
6228       int i;
6229
6230       for (i = 0; i < MAX_PLAYERS; i++)
6231       {
6232         struct PlayerInfo *player = &stored_player[i];
6233         int jx = player->jx, jy = player->jy;
6234
6235         if (!player->active)
6236           continue;
6237
6238         if (attr_x == -1 ||
6239             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6240         {
6241           attr_x = jx;
6242           attr_y = jy;
6243         }
6244       }
6245     }
6246
6247     MovDir[x][y] = MV_NONE;
6248     if (attr_x < x)
6249       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6250     else if (attr_x > x)
6251       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6252     if (attr_y < y)
6253       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6254     else if (attr_y > y)
6255       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6256
6257     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6258
6259     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6260     {
6261       boolean first_horiz = RND(2);
6262       int new_move_dir = MovDir[x][y];
6263
6264       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6265       {
6266         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6267         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6268
6269         return;
6270       }
6271
6272       MovDir[x][y] =
6273         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6274       Moving2Blocked(x, y, &newx, &newy);
6275
6276       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6277         return;
6278
6279       MovDir[x][y] =
6280         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6281       Moving2Blocked(x, y, &newx, &newy);
6282
6283       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6284         return;
6285
6286       MovDir[x][y] = old_move_dir;
6287     }
6288   }
6289   else if (move_pattern == MV_WHEN_PUSHED ||
6290            move_pattern == MV_WHEN_DROPPED)
6291   {
6292     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6293       MovDir[x][y] = MV_NONE;
6294
6295     MovDelay[x][y] = 0;
6296   }
6297   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6298   {
6299     static int test_xy[7][2] =
6300     {
6301       { 0, -1 },
6302       { -1, 0 },
6303       { +1, 0 },
6304       { 0, +1 },
6305       { 0, -1 },
6306       { -1, 0 },
6307       { +1, 0 },
6308     };
6309     static int test_dir[7] =
6310     {
6311       MV_UP,
6312       MV_LEFT,
6313       MV_RIGHT,
6314       MV_DOWN,
6315       MV_UP,
6316       MV_LEFT,
6317       MV_RIGHT,
6318     };
6319     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6320     int move_preference = -1000000;     /* start with very low preference */
6321     int new_move_dir = MV_NONE;
6322     int start_test = RND(4);
6323     int i;
6324
6325     for (i = 0; i < NUM_DIRECTIONS; i++)
6326     {
6327       int move_dir = test_dir[start_test + i];
6328       int move_dir_preference;
6329
6330       xx = x + test_xy[start_test + i][0];
6331       yy = y + test_xy[start_test + i][1];
6332
6333       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6334           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6335       {
6336         new_move_dir = move_dir;
6337
6338         break;
6339       }
6340
6341       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6342         continue;
6343
6344       move_dir_preference = -1 * RunnerVisit[xx][yy];
6345       if (hunter_mode && PlayerVisit[xx][yy] > 0)
6346         move_dir_preference = PlayerVisit[xx][yy];
6347
6348       if (move_dir_preference > move_preference)
6349       {
6350         /* prefer field that has not been visited for the longest time */
6351         move_preference = move_dir_preference;
6352         new_move_dir = move_dir;
6353       }
6354       else if (move_dir_preference == move_preference &&
6355                move_dir == old_move_dir)
6356       {
6357         /* prefer last direction when all directions are preferred equally */
6358         move_preference = move_dir_preference;
6359         new_move_dir = move_dir;
6360       }
6361     }
6362
6363     MovDir[x][y] = new_move_dir;
6364     if (old_move_dir != new_move_dir)
6365       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6366   }
6367 }
6368
6369 static void TurnRound(int x, int y)
6370 {
6371   int direction = MovDir[x][y];
6372
6373   TurnRoundExt(x, y);
6374
6375   GfxDir[x][y] = MovDir[x][y];
6376
6377   if (direction != MovDir[x][y])
6378     GfxFrame[x][y] = 0;
6379
6380   if (MovDelay[x][y])
6381     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6382
6383   ResetGfxFrame(x, y, FALSE);
6384 }
6385
6386 static boolean JustBeingPushed(int x, int y)
6387 {
6388   int i;
6389
6390   for (i = 0; i < MAX_PLAYERS; i++)
6391   {
6392     struct PlayerInfo *player = &stored_player[i];
6393
6394     if (player->active && player->is_pushing && player->MovPos)
6395     {
6396       int next_jx = player->jx + (player->jx - player->last_jx);
6397       int next_jy = player->jy + (player->jy - player->last_jy);
6398
6399       if (x == next_jx && y == next_jy)
6400         return TRUE;
6401     }
6402   }
6403
6404   return FALSE;
6405 }
6406
6407 void StartMoving(int x, int y)
6408 {
6409   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
6410   int element = Feld[x][y];
6411
6412   if (Stop[x][y])
6413     return;
6414
6415   if (MovDelay[x][y] == 0)
6416     GfxAction[x][y] = ACTION_DEFAULT;
6417
6418   if (CAN_FALL(element) && y < lev_fieldy - 1)
6419   {
6420     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
6421         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6422       if (JustBeingPushed(x, y))
6423         return;
6424
6425     if (element == EL_QUICKSAND_FULL)
6426     {
6427       if (IS_FREE(x, y + 1))
6428       {
6429         InitMovingField(x, y, MV_DOWN);
6430         started_moving = TRUE;
6431
6432         Feld[x][y] = EL_QUICKSAND_EMPTYING;
6433 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6434         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6435           Store[x][y] = EL_ROCK;
6436 #else
6437         Store[x][y] = EL_ROCK;
6438 #endif
6439
6440         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6441       }
6442       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6443       {
6444         if (!MovDelay[x][y])
6445           MovDelay[x][y] = TILEY + 1;
6446
6447         if (MovDelay[x][y])
6448         {
6449           MovDelay[x][y]--;
6450           if (MovDelay[x][y])
6451             return;
6452         }
6453
6454         Feld[x][y] = EL_QUICKSAND_EMPTY;
6455         Feld[x][y + 1] = EL_QUICKSAND_FULL;
6456         Store[x][y + 1] = Store[x][y];
6457         Store[x][y] = 0;
6458
6459         PlayLevelSoundAction(x, y, ACTION_FILLING);
6460       }
6461     }
6462     else if (element == EL_QUICKSAND_FAST_FULL)
6463     {
6464       if (IS_FREE(x, y + 1))
6465       {
6466         InitMovingField(x, y, MV_DOWN);
6467         started_moving = TRUE;
6468
6469         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
6470 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6471         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6472           Store[x][y] = EL_ROCK;
6473 #else
6474         Store[x][y] = EL_ROCK;
6475 #endif
6476
6477         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6478       }
6479       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6480       {
6481         if (!MovDelay[x][y])
6482           MovDelay[x][y] = TILEY + 1;
6483
6484         if (MovDelay[x][y])
6485         {
6486           MovDelay[x][y]--;
6487           if (MovDelay[x][y])
6488             return;
6489         }
6490
6491         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
6492         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
6493         Store[x][y + 1] = Store[x][y];
6494         Store[x][y] = 0;
6495
6496         PlayLevelSoundAction(x, y, ACTION_FILLING);
6497       }
6498     }
6499     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6500              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6501     {
6502       InitMovingField(x, y, MV_DOWN);
6503       started_moving = TRUE;
6504
6505       Feld[x][y] = EL_QUICKSAND_FILLING;
6506       Store[x][y] = element;
6507
6508       PlayLevelSoundAction(x, y, ACTION_FILLING);
6509     }
6510     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6511              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6512     {
6513       InitMovingField(x, y, MV_DOWN);
6514       started_moving = TRUE;
6515
6516       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
6517       Store[x][y] = element;
6518
6519       PlayLevelSoundAction(x, y, ACTION_FILLING);
6520     }
6521     else if (element == EL_MAGIC_WALL_FULL)
6522     {
6523       if (IS_FREE(x, y + 1))
6524       {
6525         InitMovingField(x, y, MV_DOWN);
6526         started_moving = TRUE;
6527
6528         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6529         Store[x][y] = EL_CHANGED(Store[x][y]);
6530       }
6531       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6532       {
6533         if (!MovDelay[x][y])
6534           MovDelay[x][y] = TILEY/4 + 1;
6535
6536         if (MovDelay[x][y])
6537         {
6538           MovDelay[x][y]--;
6539           if (MovDelay[x][y])
6540             return;
6541         }
6542
6543         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6544         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6545         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6546         Store[x][y] = 0;
6547       }
6548     }
6549     else if (element == EL_BD_MAGIC_WALL_FULL)
6550     {
6551       if (IS_FREE(x, y + 1))
6552       {
6553         InitMovingField(x, y, MV_DOWN);
6554         started_moving = TRUE;
6555
6556         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6557         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6558       }
6559       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6560       {
6561         if (!MovDelay[x][y])
6562           MovDelay[x][y] = TILEY/4 + 1;
6563
6564         if (MovDelay[x][y])
6565         {
6566           MovDelay[x][y]--;
6567           if (MovDelay[x][y])
6568             return;
6569         }
6570
6571         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6572         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6573         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6574         Store[x][y] = 0;
6575       }
6576     }
6577     else if (element == EL_DC_MAGIC_WALL_FULL)
6578     {
6579       if (IS_FREE(x, y + 1))
6580       {
6581         InitMovingField(x, y, MV_DOWN);
6582         started_moving = TRUE;
6583
6584         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
6585         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
6586       }
6587       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6588       {
6589         if (!MovDelay[x][y])
6590           MovDelay[x][y] = TILEY/4 + 1;
6591
6592         if (MovDelay[x][y])
6593         {
6594           MovDelay[x][y]--;
6595           if (MovDelay[x][y])
6596             return;
6597         }
6598
6599         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
6600         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
6601         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
6602         Store[x][y] = 0;
6603       }
6604     }
6605     else if ((CAN_PASS_MAGIC_WALL(element) &&
6606               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6607                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
6608              (CAN_PASS_DC_MAGIC_WALL(element) &&
6609               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
6610
6611     {
6612       InitMovingField(x, y, MV_DOWN);
6613       started_moving = TRUE;
6614
6615       Feld[x][y] =
6616         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6617          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
6618          EL_DC_MAGIC_WALL_FILLING);
6619       Store[x][y] = element;
6620     }
6621     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6622     {
6623       SplashAcid(x, y + 1);
6624
6625       InitMovingField(x, y, MV_DOWN);
6626       started_moving = TRUE;
6627
6628       Store[x][y] = EL_ACID;
6629     }
6630     else if (
6631 #if USE_FIX_IMPACT_COLLISION
6632              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6633               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
6634 #else
6635              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6636               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
6637 #endif
6638              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
6639               CAN_FALL(element) && WasJustFalling[x][y] &&
6640               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
6641
6642              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
6643               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
6644               (Feld[x][y + 1] == EL_BLOCKED)))
6645     {
6646       /* this is needed for a special case not covered by calling "Impact()"
6647          from "ContinueMoving()": if an element moves to a tile directly below
6648          another element which was just falling on that tile (which was empty
6649          in the previous frame), the falling element above would just stop
6650          instead of smashing the element below (in previous version, the above
6651          element was just checked for "moving" instead of "falling", resulting
6652          in incorrect smashes caused by horizontal movement of the above
6653          element; also, the case of the player being the element to smash was
6654          simply not covered here... :-/ ) */
6655
6656       CheckCollision[x][y] = 0;
6657       CheckImpact[x][y] = 0;
6658
6659       Impact(x, y);
6660     }
6661     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6662     {
6663       if (MovDir[x][y] == MV_NONE)
6664       {
6665         InitMovingField(x, y, MV_DOWN);
6666         started_moving = TRUE;
6667       }
6668     }
6669     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6670     {
6671       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6672         MovDir[x][y] = MV_DOWN;
6673
6674       InitMovingField(x, y, MV_DOWN);
6675       started_moving = TRUE;
6676     }
6677     else if (element == EL_AMOEBA_DROP)
6678     {
6679       Feld[x][y] = EL_AMOEBA_GROWING;
6680       Store[x][y] = EL_AMOEBA_WET;
6681     }
6682     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6683               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6684              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6685              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6686     {
6687       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
6688                                 (IS_FREE(x - 1, y + 1) ||
6689                                  Feld[x - 1][y + 1] == EL_ACID));
6690       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6691                                 (IS_FREE(x + 1, y + 1) ||
6692                                  Feld[x + 1][y + 1] == EL_ACID));
6693       boolean can_fall_any  = (can_fall_left || can_fall_right);
6694       boolean can_fall_both = (can_fall_left && can_fall_right);
6695       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6696
6697 #if USE_NEW_ALL_SLIPPERY
6698       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6699       {
6700         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6701           can_fall_right = FALSE;
6702         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6703           can_fall_left = FALSE;
6704         else if (slippery_type == SLIPPERY_ONLY_LEFT)
6705           can_fall_right = FALSE;
6706         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6707           can_fall_left = FALSE;
6708
6709         can_fall_any  = (can_fall_left || can_fall_right);
6710         can_fall_both = FALSE;
6711       }
6712 #else
6713       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6714       {
6715         if (slippery_type == SLIPPERY_ONLY_LEFT)
6716           can_fall_right = FALSE;
6717         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6718           can_fall_left = FALSE;
6719         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6720           can_fall_right = FALSE;
6721         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6722           can_fall_left = FALSE;
6723
6724         can_fall_any  = (can_fall_left || can_fall_right);
6725         can_fall_both = (can_fall_left && can_fall_right);
6726       }
6727 #endif
6728
6729 #if USE_NEW_ALL_SLIPPERY
6730 #else
6731 #if USE_NEW_SP_SLIPPERY
6732       /* !!! better use the same properties as for custom elements here !!! */
6733       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6734                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6735       {
6736         can_fall_right = FALSE;         /* slip down on left side */
6737         can_fall_both = FALSE;
6738       }
6739 #endif
6740 #endif
6741
6742 #if USE_NEW_ALL_SLIPPERY
6743       if (can_fall_both)
6744       {
6745         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6746           can_fall_right = FALSE;       /* slip down on left side */
6747         else
6748           can_fall_left = !(can_fall_right = RND(2));
6749
6750         can_fall_both = FALSE;
6751       }
6752 #else
6753       if (can_fall_both)
6754       {
6755         if (game.emulation == EMU_BOULDERDASH ||
6756             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6757           can_fall_right = FALSE;       /* slip down on left side */
6758         else
6759           can_fall_left = !(can_fall_right = RND(2));
6760
6761         can_fall_both = FALSE;
6762       }
6763 #endif
6764
6765       if (can_fall_any)
6766       {
6767         /* if not determined otherwise, prefer left side for slipping down */
6768         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6769         started_moving = TRUE;
6770       }
6771     }
6772 #if 0
6773     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6774 #else
6775     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6776 #endif
6777     {
6778       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
6779       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6780       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6781       int belt_dir = game.belt_dir[belt_nr];
6782
6783       if ((belt_dir == MV_LEFT  && left_is_free) ||
6784           (belt_dir == MV_RIGHT && right_is_free))
6785       {
6786         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6787
6788         InitMovingField(x, y, belt_dir);
6789         started_moving = TRUE;
6790
6791         Pushed[x][y] = TRUE;
6792         Pushed[nextx][y] = TRUE;
6793
6794         GfxAction[x][y] = ACTION_DEFAULT;
6795       }
6796       else
6797       {
6798         MovDir[x][y] = 0;       /* if element was moving, stop it */
6799       }
6800     }
6801   }
6802
6803   /* not "else if" because of elements that can fall and move (EL_SPRING) */
6804 #if 0
6805   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6806 #else
6807   if (CAN_MOVE(element) && !started_moving)
6808 #endif
6809   {
6810     int move_pattern = element_info[element].move_pattern;
6811     int newx, newy;
6812
6813 #if 0
6814 #if DEBUG
6815     if (MovDir[x][y] == MV_NONE)
6816     {
6817       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6818              x, y, element, element_info[element].token_name);
6819       printf("StartMoving(): This should never happen!\n");
6820     }
6821 #endif
6822 #endif
6823
6824     Moving2Blocked(x, y, &newx, &newy);
6825
6826     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6827       return;
6828
6829     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6830         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6831     {
6832       WasJustMoving[x][y] = 0;
6833       CheckCollision[x][y] = 0;
6834
6835       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6836
6837       if (Feld[x][y] != element)        /* element has changed */
6838         return;
6839     }
6840
6841     if (!MovDelay[x][y])        /* start new movement phase */
6842     {
6843       /* all objects that can change their move direction after each step
6844          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6845
6846       if (element != EL_YAMYAM &&
6847           element != EL_DARK_YAMYAM &&
6848           element != EL_PACMAN &&
6849           !(move_pattern & MV_ANY_DIRECTION) &&
6850           move_pattern != MV_TURNING_LEFT &&
6851           move_pattern != MV_TURNING_RIGHT &&
6852           move_pattern != MV_TURNING_LEFT_RIGHT &&
6853           move_pattern != MV_TURNING_RIGHT_LEFT &&
6854           move_pattern != MV_TURNING_RANDOM)
6855       {
6856         TurnRound(x, y);
6857
6858         if (MovDelay[x][y] && (element == EL_BUG ||
6859                                element == EL_SPACESHIP ||
6860                                element == EL_SP_SNIKSNAK ||
6861                                element == EL_SP_ELECTRON ||
6862                                element == EL_MOLE))
6863           DrawLevelField(x, y);
6864       }
6865     }
6866
6867     if (MovDelay[x][y])         /* wait some time before next movement */
6868     {
6869       MovDelay[x][y]--;
6870
6871       if (element == EL_ROBOT ||
6872           element == EL_YAMYAM ||
6873           element == EL_DARK_YAMYAM)
6874       {
6875         DrawLevelElementAnimationIfNeeded(x, y, element);
6876         PlayLevelSoundAction(x, y, ACTION_WAITING);
6877       }
6878       else if (element == EL_SP_ELECTRON)
6879         DrawLevelElementAnimationIfNeeded(x, y, element);
6880       else if (element == EL_DRAGON)
6881       {
6882         int i;
6883         int dir = MovDir[x][y];
6884         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6885         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
6886         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
6887                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
6888                        dir == MV_UP     ? IMG_FLAMES_1_UP :
6889                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6890         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6891
6892         GfxAction[x][y] = ACTION_ATTACKING;
6893
6894         if (IS_PLAYER(x, y))
6895           DrawPlayerField(x, y);
6896         else
6897           DrawLevelField(x, y);
6898
6899         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6900
6901         for (i = 1; i <= 3; i++)
6902         {
6903           int xx = x + i * dx;
6904           int yy = y + i * dy;
6905           int sx = SCREENX(xx);
6906           int sy = SCREENY(yy);
6907           int flame_graphic = graphic + (i - 1);
6908
6909           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6910             break;
6911
6912           if (MovDelay[x][y])
6913           {
6914             int flamed = MovingOrBlocked2Element(xx, yy);
6915
6916             /* !!! */
6917 #if 0
6918             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6919               Bang(xx, yy);
6920             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6921               RemoveMovingField(xx, yy);
6922             else
6923               RemoveField(xx, yy);
6924 #else
6925             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6926               Bang(xx, yy);
6927             else
6928               RemoveMovingField(xx, yy);
6929 #endif
6930
6931             ChangeDelay[xx][yy] = 0;
6932
6933             Feld[xx][yy] = EL_FLAMES;
6934
6935             if (IN_SCR_FIELD(sx, sy))
6936             {
6937               DrawLevelFieldCrumbledSand(xx, yy);
6938               DrawGraphic(sx, sy, flame_graphic, frame);
6939             }
6940           }
6941           else
6942           {
6943             if (Feld[xx][yy] == EL_FLAMES)
6944               Feld[xx][yy] = EL_EMPTY;
6945             DrawLevelField(xx, yy);
6946           }
6947         }
6948       }
6949
6950       if (MovDelay[x][y])       /* element still has to wait some time */
6951       {
6952         PlayLevelSoundAction(x, y, ACTION_WAITING);
6953
6954         return;
6955       }
6956     }
6957
6958     /* now make next step */
6959
6960     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6961
6962     if (DONT_COLLIDE_WITH(element) &&
6963         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6964         !PLAYER_ENEMY_PROTECTED(newx, newy))
6965     {
6966       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6967
6968       return;
6969     }
6970
6971     else if (CAN_MOVE_INTO_ACID(element) &&
6972              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6973              !IS_MV_DIAGONAL(MovDir[x][y]) &&
6974              (MovDir[x][y] == MV_DOWN ||
6975               game.engine_version >= VERSION_IDENT(3,1,0,0)))
6976     {
6977       SplashAcid(newx, newy);
6978       Store[x][y] = EL_ACID;
6979     }
6980     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6981     {
6982       if (Feld[newx][newy] == EL_EXIT_OPEN ||
6983           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
6984           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
6985           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
6986       {
6987         RemoveField(x, y);
6988         DrawLevelField(x, y);
6989
6990         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6991         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6992           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6993
6994         local_player->friends_still_needed--;
6995         if (!local_player->friends_still_needed &&
6996             !local_player->GameOver && AllPlayersGone)
6997           PlayerWins(local_player);
6998
6999         return;
7000       }
7001       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7002       {
7003         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7004           DrawLevelField(newx, newy);
7005         else
7006           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7007       }
7008       else if (!IS_FREE(newx, newy))
7009       {
7010         GfxAction[x][y] = ACTION_WAITING;
7011
7012         if (IS_PLAYER(x, y))
7013           DrawPlayerField(x, y);
7014         else
7015           DrawLevelField(x, y);
7016
7017         return;
7018       }
7019     }
7020     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7021     {
7022       if (IS_FOOD_PIG(Feld[newx][newy]))
7023       {
7024         if (IS_MOVING(newx, newy))
7025           RemoveMovingField(newx, newy);
7026         else
7027         {
7028           Feld[newx][newy] = EL_EMPTY;
7029           DrawLevelField(newx, newy);
7030         }
7031
7032         PlayLevelSound(x, y, SND_PIG_DIGGING);
7033       }
7034       else if (!IS_FREE(newx, newy))
7035       {
7036         if (IS_PLAYER(x, y))
7037           DrawPlayerField(x, y);
7038         else
7039           DrawLevelField(x, y);
7040
7041         return;
7042       }
7043     }
7044     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7045     {
7046       if (Store[x][y] != EL_EMPTY)
7047       {
7048         boolean can_clone = FALSE;
7049         int xx, yy;
7050
7051         /* check if element to clone is still there */
7052         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7053         {
7054           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7055           {
7056             can_clone = TRUE;
7057
7058             break;
7059           }
7060         }
7061
7062         /* cannot clone or target field not free anymore -- do not clone */
7063         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7064           Store[x][y] = EL_EMPTY;
7065       }
7066
7067       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7068       {
7069         if (IS_MV_DIAGONAL(MovDir[x][y]))
7070         {
7071           int diagonal_move_dir = MovDir[x][y];
7072           int stored = Store[x][y];
7073           int change_delay = 8;
7074           int graphic;
7075
7076           /* android is moving diagonally */
7077
7078           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7079
7080           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7081           GfxElement[x][y] = EL_EMC_ANDROID;
7082           GfxAction[x][y] = ACTION_SHRINKING;
7083           GfxDir[x][y] = diagonal_move_dir;
7084           ChangeDelay[x][y] = change_delay;
7085
7086           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7087                                    GfxDir[x][y]);
7088
7089           DrawLevelGraphicAnimation(x, y, graphic);
7090           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7091
7092           if (Feld[newx][newy] == EL_ACID)
7093           {
7094             SplashAcid(newx, newy);
7095
7096             return;
7097           }
7098
7099           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7100
7101           Store[newx][newy] = EL_EMC_ANDROID;
7102           GfxElement[newx][newy] = EL_EMC_ANDROID;
7103           GfxAction[newx][newy] = ACTION_GROWING;
7104           GfxDir[newx][newy] = diagonal_move_dir;
7105           ChangeDelay[newx][newy] = change_delay;
7106
7107           graphic = el_act_dir2img(GfxElement[newx][newy],
7108                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7109
7110           DrawLevelGraphicAnimation(newx, newy, graphic);
7111           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7112
7113           return;
7114         }
7115         else
7116         {
7117           Feld[newx][newy] = EL_EMPTY;
7118           DrawLevelField(newx, newy);
7119
7120           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7121         }
7122       }
7123       else if (!IS_FREE(newx, newy))
7124       {
7125 #if 0
7126         if (IS_PLAYER(x, y))
7127           DrawPlayerField(x, y);
7128         else
7129           DrawLevelField(x, y);
7130 #endif
7131
7132         return;
7133       }
7134     }
7135     else if (IS_CUSTOM_ELEMENT(element) &&
7136              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7137     {
7138       int new_element = Feld[newx][newy];
7139
7140       if (!IS_FREE(newx, newy))
7141       {
7142         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7143                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7144                       ACTION_BREAKING);
7145
7146         /* no element can dig solid indestructible elements */
7147         if (IS_INDESTRUCTIBLE(new_element) &&
7148             !IS_DIGGABLE(new_element) &&
7149             !IS_COLLECTIBLE(new_element))
7150           return;
7151
7152         if (AmoebaNr[newx][newy] &&
7153             (new_element == EL_AMOEBA_FULL ||
7154              new_element == EL_BD_AMOEBA ||
7155              new_element == EL_AMOEBA_GROWING))
7156         {
7157           AmoebaCnt[AmoebaNr[newx][newy]]--;
7158           AmoebaCnt2[AmoebaNr[newx][newy]]--;
7159         }
7160
7161         if (IS_MOVING(newx, newy))
7162           RemoveMovingField(newx, newy);
7163         else
7164         {
7165           RemoveField(newx, newy);
7166           DrawLevelField(newx, newy);
7167         }
7168
7169         /* if digged element was about to explode, prevent the explosion */
7170         ExplodeField[newx][newy] = EX_TYPE_NONE;
7171
7172         PlayLevelSoundAction(x, y, action);
7173       }
7174
7175       Store[newx][newy] = EL_EMPTY;
7176 #if 1
7177       /* this makes it possible to leave the removed element again */
7178       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7179         Store[newx][newy] = new_element;
7180 #else
7181       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7182       {
7183         int move_leave_element = element_info[element].move_leave_element;
7184
7185         /* this makes it possible to leave the removed element again */
7186         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7187                              new_element : move_leave_element);
7188       }
7189 #endif
7190
7191       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7192       {
7193         RunnerVisit[x][y] = FrameCounter;
7194         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7195       }
7196     }
7197     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7198     {
7199       if (!IS_FREE(newx, newy))
7200       {
7201         if (IS_PLAYER(x, y))
7202           DrawPlayerField(x, y);
7203         else
7204           DrawLevelField(x, y);
7205
7206         return;
7207       }
7208       else
7209       {
7210         boolean wanna_flame = !RND(10);
7211         int dx = newx - x, dy = newy - y;
7212         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7213         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7214         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7215                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7216         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7217                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7218
7219         if ((wanna_flame ||
7220              IS_CLASSIC_ENEMY(element1) ||
7221              IS_CLASSIC_ENEMY(element2)) &&
7222             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7223             element1 != EL_FLAMES && element2 != EL_FLAMES)
7224         {
7225           ResetGfxAnimation(x, y);
7226           GfxAction[x][y] = ACTION_ATTACKING;
7227
7228           if (IS_PLAYER(x, y))
7229             DrawPlayerField(x, y);
7230           else
7231             DrawLevelField(x, y);
7232
7233           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7234
7235           MovDelay[x][y] = 50;
7236
7237           /* !!! */
7238 #if 0
7239           RemoveField(newx, newy);
7240 #endif
7241           Feld[newx][newy] = EL_FLAMES;
7242           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7243           {
7244 #if 0
7245             RemoveField(newx1, newy1);
7246 #endif
7247             Feld[newx1][newy1] = EL_FLAMES;
7248           }
7249           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7250           {
7251 #if 0
7252             RemoveField(newx2, newy2);
7253 #endif
7254             Feld[newx2][newy2] = EL_FLAMES;
7255           }
7256
7257           return;
7258         }
7259       }
7260     }
7261     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7262              Feld[newx][newy] == EL_DIAMOND)
7263     {
7264       if (IS_MOVING(newx, newy))
7265         RemoveMovingField(newx, newy);
7266       else
7267       {
7268         Feld[newx][newy] = EL_EMPTY;
7269         DrawLevelField(newx, newy);
7270       }
7271
7272       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7273     }
7274     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7275              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7276     {
7277       if (AmoebaNr[newx][newy])
7278       {
7279         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7280         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7281             Feld[newx][newy] == EL_BD_AMOEBA)
7282           AmoebaCnt[AmoebaNr[newx][newy]]--;
7283       }
7284
7285 #if 0
7286       /* !!! test !!! */
7287       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7288       {
7289         RemoveMovingField(newx, newy);
7290       }
7291 #else
7292       if (IS_MOVING(newx, newy))
7293       {
7294         RemoveMovingField(newx, newy);
7295       }
7296 #endif
7297       else
7298       {
7299         Feld[newx][newy] = EL_EMPTY;
7300         DrawLevelField(newx, newy);
7301       }
7302
7303       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7304     }
7305     else if ((element == EL_PACMAN || element == EL_MOLE)
7306              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7307     {
7308       if (AmoebaNr[newx][newy])
7309       {
7310         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7311         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7312             Feld[newx][newy] == EL_BD_AMOEBA)
7313           AmoebaCnt[AmoebaNr[newx][newy]]--;
7314       }
7315
7316       if (element == EL_MOLE)
7317       {
7318         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7319         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7320
7321         ResetGfxAnimation(x, y);
7322         GfxAction[x][y] = ACTION_DIGGING;
7323         DrawLevelField(x, y);
7324
7325         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7326
7327         return;                         /* wait for shrinking amoeba */
7328       }
7329       else      /* element == EL_PACMAN */
7330       {
7331         Feld[newx][newy] = EL_EMPTY;
7332         DrawLevelField(newx, newy);
7333         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7334       }
7335     }
7336     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7337              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7338               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7339     {
7340       /* wait for shrinking amoeba to completely disappear */
7341       return;
7342     }
7343     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7344     {
7345       /* object was running against a wall */
7346
7347       TurnRound(x, y);
7348
7349 #if 0
7350       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7351       if (move_pattern & MV_ANY_DIRECTION &&
7352           move_pattern == MovDir[x][y])
7353       {
7354         int blocking_element =
7355           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7356
7357         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7358                                  MovDir[x][y]);
7359
7360         element = Feld[x][y];   /* element might have changed */
7361       }
7362 #endif
7363
7364       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7365         DrawLevelElementAnimation(x, y, element);
7366
7367       if (DONT_TOUCH(element))
7368         TestIfBadThingTouchesPlayer(x, y);
7369
7370       return;
7371     }
7372
7373     InitMovingField(x, y, MovDir[x][y]);
7374
7375     PlayLevelSoundAction(x, y, ACTION_MOVING);
7376   }
7377
7378   if (MovDir[x][y])
7379     ContinueMoving(x, y);
7380 }
7381
7382 void ContinueMoving(int x, int y)
7383 {
7384   int element = Feld[x][y];
7385   struct ElementInfo *ei = &element_info[element];
7386   int direction = MovDir[x][y];
7387   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7388   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7389   int newx = x + dx, newy = y + dy;
7390   int stored = Store[x][y];
7391   int stored_new = Store[newx][newy];
7392   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7393   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7394   boolean last_line = (newy == lev_fieldy - 1);
7395
7396   MovPos[x][y] += getElementMoveStepsize(x, y);
7397
7398   if (pushed_by_player) /* special case: moving object pushed by player */
7399     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7400
7401   if (ABS(MovPos[x][y]) < TILEX)
7402   {
7403 #if 0
7404     int ee = Feld[x][y];
7405     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7406     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7407
7408     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7409            x, y, ABS(MovPos[x][y]),
7410            ee, gg, ff,
7411            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
7412 #endif
7413
7414     DrawLevelField(x, y);
7415
7416     return;     /* element is still moving */
7417   }
7418
7419   /* element reached destination field */
7420
7421   Feld[x][y] = EL_EMPTY;
7422   Feld[newx][newy] = element;
7423   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
7424
7425   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
7426   {
7427     element = Feld[newx][newy] = EL_ACID;
7428   }
7429   else if (element == EL_MOLE)
7430   {
7431     Feld[x][y] = EL_SAND;
7432
7433     DrawLevelFieldCrumbledSandNeighbours(x, y);
7434   }
7435   else if (element == EL_QUICKSAND_FILLING)
7436   {
7437     element = Feld[newx][newy] = get_next_element(element);
7438     Store[newx][newy] = Store[x][y];
7439   }
7440   else if (element == EL_QUICKSAND_EMPTYING)
7441   {
7442     Feld[x][y] = get_next_element(element);
7443     element = Feld[newx][newy] = Store[x][y];
7444   }
7445   else if (element == EL_QUICKSAND_FAST_FILLING)
7446   {
7447     element = Feld[newx][newy] = get_next_element(element);
7448     Store[newx][newy] = Store[x][y];
7449   }
7450   else if (element == EL_QUICKSAND_FAST_EMPTYING)
7451   {
7452     Feld[x][y] = get_next_element(element);
7453     element = Feld[newx][newy] = Store[x][y];
7454   }
7455   else if (element == EL_MAGIC_WALL_FILLING)
7456   {
7457     element = Feld[newx][newy] = get_next_element(element);
7458     if (!game.magic_wall_active)
7459       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7460     Store[newx][newy] = Store[x][y];
7461   }
7462   else if (element == EL_MAGIC_WALL_EMPTYING)
7463   {
7464     Feld[x][y] = get_next_element(element);
7465     if (!game.magic_wall_active)
7466       Feld[x][y] = EL_MAGIC_WALL_DEAD;
7467     element = Feld[newx][newy] = Store[x][y];
7468
7469 #if USE_NEW_CUSTOM_VALUE
7470     InitField(newx, newy, FALSE);
7471 #endif
7472   }
7473   else if (element == EL_BD_MAGIC_WALL_FILLING)
7474   {
7475     element = Feld[newx][newy] = get_next_element(element);
7476     if (!game.magic_wall_active)
7477       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
7478     Store[newx][newy] = Store[x][y];
7479   }
7480   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
7481   {
7482     Feld[x][y] = get_next_element(element);
7483     if (!game.magic_wall_active)
7484       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7485     element = Feld[newx][newy] = Store[x][y];
7486
7487 #if USE_NEW_CUSTOM_VALUE
7488     InitField(newx, newy, FALSE);
7489 #endif
7490   }
7491   else if (element == EL_DC_MAGIC_WALL_FILLING)
7492   {
7493     element = Feld[newx][newy] = get_next_element(element);
7494     if (!game.magic_wall_active)
7495       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
7496     Store[newx][newy] = Store[x][y];
7497   }
7498   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
7499   {
7500     Feld[x][y] = get_next_element(element);
7501     if (!game.magic_wall_active)
7502       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
7503     element = Feld[newx][newy] = Store[x][y];
7504
7505 #if USE_NEW_CUSTOM_VALUE
7506     InitField(newx, newy, FALSE);
7507 #endif
7508   }
7509   else if (element == EL_AMOEBA_DROPPING)
7510   {
7511     Feld[x][y] = get_next_element(element);
7512     element = Feld[newx][newy] = Store[x][y];
7513   }
7514   else if (element == EL_SOKOBAN_OBJECT)
7515   {
7516     if (Back[x][y])
7517       Feld[x][y] = Back[x][y];
7518
7519     if (Back[newx][newy])
7520       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
7521
7522     Back[x][y] = Back[newx][newy] = 0;
7523   }
7524
7525   Store[x][y] = EL_EMPTY;
7526   MovPos[x][y] = 0;
7527   MovDir[x][y] = 0;
7528   MovDelay[x][y] = 0;
7529
7530   MovDelay[newx][newy] = 0;
7531
7532   if (CAN_CHANGE_OR_HAS_ACTION(element))
7533   {
7534     /* copy element change control values to new field */
7535     ChangeDelay[newx][newy] = ChangeDelay[x][y];
7536     ChangePage[newx][newy]  = ChangePage[x][y];
7537     ChangeCount[newx][newy] = ChangeCount[x][y];
7538     ChangeEvent[newx][newy] = ChangeEvent[x][y];
7539   }
7540
7541 #if USE_NEW_CUSTOM_VALUE
7542     CustomValue[newx][newy] = CustomValue[x][y];
7543 #endif
7544
7545   ChangeDelay[x][y] = 0;
7546   ChangePage[x][y] = -1;
7547   ChangeCount[x][y] = 0;
7548   ChangeEvent[x][y] = -1;
7549
7550 #if USE_NEW_CUSTOM_VALUE
7551   CustomValue[x][y] = 0;
7552 #endif
7553
7554   /* copy animation control values to new field */
7555   GfxFrame[newx][newy]  = GfxFrame[x][y];
7556   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
7557   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
7558   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
7559
7560   Pushed[x][y] = Pushed[newx][newy] = FALSE;
7561
7562   /* some elements can leave other elements behind after moving */
7563 #if 1
7564   if (ei->move_leave_element != EL_EMPTY &&
7565       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7566       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7567 #else
7568   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7569       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7570       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7571 #endif
7572   {
7573     int move_leave_element = ei->move_leave_element;
7574
7575 #if 1
7576 #if 1
7577     /* this makes it possible to leave the removed element again */
7578     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7579       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7580 #else
7581     /* this makes it possible to leave the removed element again */
7582     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7583       move_leave_element = stored;
7584 #endif
7585 #else
7586     /* this makes it possible to leave the removed element again */
7587     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7588         ei->move_leave_element == EL_TRIGGER_ELEMENT)
7589       move_leave_element = stored;
7590 #endif
7591
7592     Feld[x][y] = move_leave_element;
7593
7594     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7595       MovDir[x][y] = direction;
7596
7597     InitField(x, y, FALSE);
7598
7599     if (GFX_CRUMBLED(Feld[x][y]))
7600       DrawLevelFieldCrumbledSandNeighbours(x, y);
7601
7602     if (ELEM_IS_PLAYER(move_leave_element))
7603       RelocatePlayer(x, y, move_leave_element);
7604   }
7605
7606   /* do this after checking for left-behind element */
7607   ResetGfxAnimation(x, y);      /* reset animation values for old field */
7608
7609   if (!CAN_MOVE(element) ||
7610       (CAN_FALL(element) && direction == MV_DOWN &&
7611        (element == EL_SPRING ||
7612         element_info[element].move_pattern == MV_WHEN_PUSHED ||
7613         element_info[element].move_pattern == MV_WHEN_DROPPED)))
7614     GfxDir[x][y] = MovDir[newx][newy] = 0;
7615
7616   DrawLevelField(x, y);
7617   DrawLevelField(newx, newy);
7618
7619   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
7620
7621   /* prevent pushed element from moving on in pushed direction */
7622   if (pushed_by_player && CAN_MOVE(element) &&
7623       element_info[element].move_pattern & MV_ANY_DIRECTION &&
7624       !(element_info[element].move_pattern & direction))
7625     TurnRound(newx, newy);
7626
7627   /* prevent elements on conveyor belt from moving on in last direction */
7628   if (pushed_by_conveyor && CAN_FALL(element) &&
7629       direction & MV_HORIZONTAL)
7630     MovDir[newx][newy] = 0;
7631
7632   if (!pushed_by_player)
7633   {
7634     int nextx = newx + dx, nexty = newy + dy;
7635     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
7636
7637     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
7638
7639     if (CAN_FALL(element) && direction == MV_DOWN)
7640       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
7641
7642     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
7643       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
7644
7645 #if USE_FIX_IMPACT_COLLISION
7646     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
7647       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
7648 #endif
7649   }
7650
7651   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
7652   {
7653     TestIfBadThingTouchesPlayer(newx, newy);
7654     TestIfBadThingTouchesFriend(newx, newy);
7655
7656     if (!IS_CUSTOM_ELEMENT(element))
7657       TestIfBadThingTouchesOtherBadThing(newx, newy);
7658   }
7659   else if (element == EL_PENGUIN)
7660     TestIfFriendTouchesBadThing(newx, newy);
7661
7662   /* give the player one last chance (one more frame) to move away */
7663   if (CAN_FALL(element) && direction == MV_DOWN &&
7664       (last_line || (!IS_FREE(x, newy + 1) &&
7665                      (!IS_PLAYER(x, newy + 1) ||
7666                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
7667     Impact(x, newy);
7668
7669   if (pushed_by_player && !game.use_change_when_pushing_bug)
7670   {
7671     int push_side = MV_DIR_OPPOSITE(direction);
7672     struct PlayerInfo *player = PLAYERINFO(x, y);
7673
7674     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
7675                                player->index_bit, push_side);
7676     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
7677                                         player->index_bit, push_side);
7678   }
7679
7680   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
7681     MovDelay[newx][newy] = 1;
7682
7683   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
7684
7685   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
7686
7687 #if 0
7688   if (ChangePage[newx][newy] != -1)             /* delayed change */
7689   {
7690     int page = ChangePage[newx][newy];
7691     struct ElementChangeInfo *change = &ei->change_page[page];
7692
7693     ChangePage[newx][newy] = -1;
7694
7695     if (change->can_change)
7696     {
7697       if (ChangeElement(newx, newy, element, page))
7698       {
7699         if (change->post_change_function)
7700           change->post_change_function(newx, newy);
7701       }
7702     }
7703
7704     if (change->has_action)
7705       ExecuteCustomElementAction(newx, newy, element, page);
7706   }
7707 #endif
7708
7709   TestIfElementHitsCustomElement(newx, newy, direction);
7710   TestIfPlayerTouchesCustomElement(newx, newy);
7711   TestIfElementTouchesCustomElement(newx, newy);
7712
7713   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7714       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7715     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7716                              MV_DIR_OPPOSITE(direction));
7717 }
7718
7719 int AmoebeNachbarNr(int ax, int ay)
7720 {
7721   int i;
7722   int element = Feld[ax][ay];
7723   int group_nr = 0;
7724   static int xy[4][2] =
7725   {
7726     { 0, -1 },
7727     { -1, 0 },
7728     { +1, 0 },
7729     { 0, +1 }
7730   };
7731
7732   for (i = 0; i < NUM_DIRECTIONS; i++)
7733   {
7734     int x = ax + xy[i][0];
7735     int y = ay + xy[i][1];
7736
7737     if (!IN_LEV_FIELD(x, y))
7738       continue;
7739
7740     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7741       group_nr = AmoebaNr[x][y];
7742   }
7743
7744   return group_nr;
7745 }
7746
7747 void AmoebenVereinigen(int ax, int ay)
7748 {
7749   int i, x, y, xx, yy;
7750   int new_group_nr = AmoebaNr[ax][ay];
7751   static int xy[4][2] =
7752   {
7753     { 0, -1 },
7754     { -1, 0 },
7755     { +1, 0 },
7756     { 0, +1 }
7757   };
7758
7759   if (new_group_nr == 0)
7760     return;
7761
7762   for (i = 0; i < NUM_DIRECTIONS; i++)
7763   {
7764     x = ax + xy[i][0];
7765     y = ay + xy[i][1];
7766
7767     if (!IN_LEV_FIELD(x, y))
7768       continue;
7769
7770     if ((Feld[x][y] == EL_AMOEBA_FULL ||
7771          Feld[x][y] == EL_BD_AMOEBA ||
7772          Feld[x][y] == EL_AMOEBA_DEAD) &&
7773         AmoebaNr[x][y] != new_group_nr)
7774     {
7775       int old_group_nr = AmoebaNr[x][y];
7776
7777       if (old_group_nr == 0)
7778         return;
7779
7780       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7781       AmoebaCnt[old_group_nr] = 0;
7782       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7783       AmoebaCnt2[old_group_nr] = 0;
7784
7785       SCAN_PLAYFIELD(xx, yy)
7786       {
7787         if (AmoebaNr[xx][yy] == old_group_nr)
7788           AmoebaNr[xx][yy] = new_group_nr;
7789       }
7790     }
7791   }
7792 }
7793
7794 void AmoebeUmwandeln(int ax, int ay)
7795 {
7796   int i, x, y;
7797
7798   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7799   {
7800     int group_nr = AmoebaNr[ax][ay];
7801
7802 #ifdef DEBUG
7803     if (group_nr == 0)
7804     {
7805       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7806       printf("AmoebeUmwandeln(): This should never happen!\n");
7807       return;
7808     }
7809 #endif
7810
7811     SCAN_PLAYFIELD(x, y)
7812     {
7813       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7814       {
7815         AmoebaNr[x][y] = 0;
7816         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7817       }
7818     }
7819
7820     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7821                             SND_AMOEBA_TURNING_TO_GEM :
7822                             SND_AMOEBA_TURNING_TO_ROCK));
7823     Bang(ax, ay);
7824   }
7825   else
7826   {
7827     static int xy[4][2] =
7828     {
7829       { 0, -1 },
7830       { -1, 0 },
7831       { +1, 0 },
7832       { 0, +1 }
7833     };
7834
7835     for (i = 0; i < NUM_DIRECTIONS; i++)
7836     {
7837       x = ax + xy[i][0];
7838       y = ay + xy[i][1];
7839
7840       if (!IN_LEV_FIELD(x, y))
7841         continue;
7842
7843       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7844       {
7845         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7846                               SND_AMOEBA_TURNING_TO_GEM :
7847                               SND_AMOEBA_TURNING_TO_ROCK));
7848         Bang(x, y);
7849       }
7850     }
7851   }
7852 }
7853
7854 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7855 {
7856   int x, y;
7857   int group_nr = AmoebaNr[ax][ay];
7858   boolean done = FALSE;
7859
7860 #ifdef DEBUG
7861   if (group_nr == 0)
7862   {
7863     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7864     printf("AmoebeUmwandelnBD(): This should never happen!\n");
7865     return;
7866   }
7867 #endif
7868
7869   SCAN_PLAYFIELD(x, y)
7870   {
7871     if (AmoebaNr[x][y] == group_nr &&
7872         (Feld[x][y] == EL_AMOEBA_DEAD ||
7873          Feld[x][y] == EL_BD_AMOEBA ||
7874          Feld[x][y] == EL_AMOEBA_GROWING))
7875     {
7876       AmoebaNr[x][y] = 0;
7877       Feld[x][y] = new_element;
7878       InitField(x, y, FALSE);
7879       DrawLevelField(x, y);
7880       done = TRUE;
7881     }
7882   }
7883
7884   if (done)
7885     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7886                             SND_BD_AMOEBA_TURNING_TO_ROCK :
7887                             SND_BD_AMOEBA_TURNING_TO_GEM));
7888 }
7889
7890 void AmoebeWaechst(int x, int y)
7891 {
7892   static unsigned long sound_delay = 0;
7893   static unsigned long sound_delay_value = 0;
7894
7895   if (!MovDelay[x][y])          /* start new growing cycle */
7896   {
7897     MovDelay[x][y] = 7;
7898
7899     if (DelayReached(&sound_delay, sound_delay_value))
7900     {
7901       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7902       sound_delay_value = 30;
7903     }
7904   }
7905
7906   if (MovDelay[x][y])           /* wait some time before growing bigger */
7907   {
7908     MovDelay[x][y]--;
7909     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7910     {
7911       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7912                                            6 - MovDelay[x][y]);
7913
7914       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7915     }
7916
7917     if (!MovDelay[x][y])
7918     {
7919       Feld[x][y] = Store[x][y];
7920       Store[x][y] = 0;
7921       DrawLevelField(x, y);
7922     }
7923   }
7924 }
7925
7926 void AmoebaDisappearing(int x, int y)
7927 {
7928   static unsigned long sound_delay = 0;
7929   static unsigned long sound_delay_value = 0;
7930
7931   if (!MovDelay[x][y])          /* start new shrinking cycle */
7932   {
7933     MovDelay[x][y] = 7;
7934
7935     if (DelayReached(&sound_delay, sound_delay_value))
7936       sound_delay_value = 30;
7937   }
7938
7939   if (MovDelay[x][y])           /* wait some time before shrinking */
7940   {
7941     MovDelay[x][y]--;
7942     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7943     {
7944       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7945                                            6 - MovDelay[x][y]);
7946
7947       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7948     }
7949
7950     if (!MovDelay[x][y])
7951     {
7952       Feld[x][y] = EL_EMPTY;
7953       DrawLevelField(x, y);
7954
7955       /* don't let mole enter this field in this cycle;
7956          (give priority to objects falling to this field from above) */
7957       Stop[x][y] = TRUE;
7958     }
7959   }
7960 }
7961
7962 void AmoebeAbleger(int ax, int ay)
7963 {
7964   int i;
7965   int element = Feld[ax][ay];
7966   int graphic = el2img(element);
7967   int newax = ax, neway = ay;
7968   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7969   static int xy[4][2] =
7970   {
7971     { 0, -1 },
7972     { -1, 0 },
7973     { +1, 0 },
7974     { 0, +1 }
7975   };
7976
7977   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7978   {
7979     Feld[ax][ay] = EL_AMOEBA_DEAD;
7980     DrawLevelField(ax, ay);
7981     return;
7982   }
7983
7984   if (IS_ANIMATED(graphic))
7985     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7986
7987   if (!MovDelay[ax][ay])        /* start making new amoeba field */
7988     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7989
7990   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
7991   {
7992     MovDelay[ax][ay]--;
7993     if (MovDelay[ax][ay])
7994       return;
7995   }
7996
7997   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7998   {
7999     int start = RND(4);
8000     int x = ax + xy[start][0];
8001     int y = ay + xy[start][1];
8002
8003     if (!IN_LEV_FIELD(x, y))
8004       return;
8005
8006     if (IS_FREE(x, y) ||
8007         CAN_GROW_INTO(Feld[x][y]) ||
8008         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8009         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8010     {
8011       newax = x;
8012       neway = y;
8013     }
8014
8015     if (newax == ax && neway == ay)
8016       return;
8017   }
8018   else                          /* normal or "filled" (BD style) amoeba */
8019   {
8020     int start = RND(4);
8021     boolean waiting_for_player = FALSE;
8022
8023     for (i = 0; i < NUM_DIRECTIONS; i++)
8024     {
8025       int j = (start + i) % 4;
8026       int x = ax + xy[j][0];
8027       int y = ay + xy[j][1];
8028
8029       if (!IN_LEV_FIELD(x, y))
8030         continue;
8031
8032       if (IS_FREE(x, y) ||
8033           CAN_GROW_INTO(Feld[x][y]) ||
8034           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8035           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8036       {
8037         newax = x;
8038         neway = y;
8039         break;
8040       }
8041       else if (IS_PLAYER(x, y))
8042         waiting_for_player = TRUE;
8043     }
8044
8045     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8046     {
8047       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8048       {
8049         Feld[ax][ay] = EL_AMOEBA_DEAD;
8050         DrawLevelField(ax, ay);
8051         AmoebaCnt[AmoebaNr[ax][ay]]--;
8052
8053         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8054         {
8055           if (element == EL_AMOEBA_FULL)
8056             AmoebeUmwandeln(ax, ay);
8057           else if (element == EL_BD_AMOEBA)
8058             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8059         }
8060       }
8061       return;
8062     }
8063     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8064     {
8065       /* amoeba gets larger by growing in some direction */
8066
8067       int new_group_nr = AmoebaNr[ax][ay];
8068
8069 #ifdef DEBUG
8070   if (new_group_nr == 0)
8071   {
8072     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8073     printf("AmoebeAbleger(): This should never happen!\n");
8074     return;
8075   }
8076 #endif
8077
8078       AmoebaNr[newax][neway] = new_group_nr;
8079       AmoebaCnt[new_group_nr]++;
8080       AmoebaCnt2[new_group_nr]++;
8081
8082       /* if amoeba touches other amoeba(s) after growing, unify them */
8083       AmoebenVereinigen(newax, neway);
8084
8085       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8086       {
8087         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8088         return;
8089       }
8090     }
8091   }
8092
8093   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8094       (neway == lev_fieldy - 1 && newax != ax))
8095   {
8096     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8097     Store[newax][neway] = element;
8098   }
8099   else if (neway == ay || element == EL_EMC_DRIPPER)
8100   {
8101     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8102
8103     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8104   }
8105   else
8106   {
8107     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8108     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8109     Store[ax][ay] = EL_AMOEBA_DROP;
8110     ContinueMoving(ax, ay);
8111     return;
8112   }
8113
8114   DrawLevelField(newax, neway);
8115 }
8116
8117 void Life(int ax, int ay)
8118 {
8119   int x1, y1, x2, y2;
8120   int life_time = 40;
8121   int element = Feld[ax][ay];
8122   int graphic = el2img(element);
8123   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8124                          level.biomaze);
8125   boolean changed = FALSE;
8126
8127   if (IS_ANIMATED(graphic))
8128     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8129
8130   if (Stop[ax][ay])
8131     return;
8132
8133   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8134     MovDelay[ax][ay] = life_time;
8135
8136   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8137   {
8138     MovDelay[ax][ay]--;
8139     if (MovDelay[ax][ay])
8140       return;
8141   }
8142
8143   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8144   {
8145     int xx = ax+x1, yy = ay+y1;
8146     int nachbarn = 0;
8147
8148     if (!IN_LEV_FIELD(xx, yy))
8149       continue;
8150
8151     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8152     {
8153       int x = xx+x2, y = yy+y2;
8154
8155       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8156         continue;
8157
8158       if (((Feld[x][y] == element ||
8159             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8160            !Stop[x][y]) ||
8161           (IS_FREE(x, y) && Stop[x][y]))
8162         nachbarn++;
8163     }
8164
8165     if (xx == ax && yy == ay)           /* field in the middle */
8166     {
8167       if (nachbarn < life_parameter[0] ||
8168           nachbarn > life_parameter[1])
8169       {
8170         Feld[xx][yy] = EL_EMPTY;
8171         if (!Stop[xx][yy])
8172           DrawLevelField(xx, yy);
8173         Stop[xx][yy] = TRUE;
8174         changed = TRUE;
8175       }
8176     }
8177     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8178     {                                   /* free border field */
8179       if (nachbarn >= life_parameter[2] &&
8180           nachbarn <= life_parameter[3])
8181       {
8182         Feld[xx][yy] = element;
8183         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8184         if (!Stop[xx][yy])
8185           DrawLevelField(xx, yy);
8186         Stop[xx][yy] = TRUE;
8187         changed = TRUE;
8188       }
8189     }
8190   }
8191
8192   if (changed)
8193     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8194                    SND_GAME_OF_LIFE_GROWING);
8195 }
8196
8197 static void InitRobotWheel(int x, int y)
8198 {
8199   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8200 }
8201
8202 static void RunRobotWheel(int x, int y)
8203 {
8204   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8205 }
8206
8207 static void StopRobotWheel(int x, int y)
8208 {
8209   if (ZX == x && ZY == y)
8210     ZX = ZY = -1;
8211 }
8212
8213 static void InitTimegateWheel(int x, int y)
8214 {
8215   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8216 }
8217
8218 static void RunTimegateWheel(int x, int y)
8219 {
8220   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8221 }
8222
8223 static void InitMagicBallDelay(int x, int y)
8224 {
8225 #if 1
8226   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8227 #else
8228   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8229 #endif
8230 }
8231
8232 static void ActivateMagicBall(int bx, int by)
8233 {
8234   int x, y;
8235
8236   if (level.ball_random)
8237   {
8238     int pos_border = RND(8);    /* select one of the eight border elements */
8239     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8240     int xx = pos_content % 3;
8241     int yy = pos_content / 3;
8242
8243     x = bx - 1 + xx;
8244     y = by - 1 + yy;
8245
8246     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8247       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8248   }
8249   else
8250   {
8251     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8252     {
8253       int xx = x - bx + 1;
8254       int yy = y - by + 1;
8255
8256       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8257         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8258     }
8259   }
8260
8261   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8262 }
8263
8264 void CheckExit(int x, int y)
8265 {
8266   if (local_player->gems_still_needed > 0 ||
8267       local_player->sokobanfields_still_needed > 0 ||
8268       local_player->lights_still_needed > 0)
8269   {
8270     int element = Feld[x][y];
8271     int graphic = el2img(element);
8272
8273     if (IS_ANIMATED(graphic))
8274       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8275
8276     return;
8277   }
8278
8279   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8280     return;
8281
8282   Feld[x][y] = EL_EXIT_OPENING;
8283
8284   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8285 }
8286
8287 void CheckExitEM(int x, int y)
8288 {
8289   if (local_player->gems_still_needed > 0 ||
8290       local_player->sokobanfields_still_needed > 0 ||
8291       local_player->lights_still_needed > 0)
8292   {
8293     int element = Feld[x][y];
8294     int graphic = el2img(element);
8295
8296     if (IS_ANIMATED(graphic))
8297       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8298
8299     return;
8300   }
8301
8302   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8303     return;
8304
8305   Feld[x][y] = EL_EM_EXIT_OPENING;
8306
8307   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8308 }
8309
8310 void CheckExitSteel(int x, int y)
8311 {
8312   if (local_player->gems_still_needed > 0 ||
8313       local_player->sokobanfields_still_needed > 0 ||
8314       local_player->lights_still_needed > 0)
8315   {
8316     int element = Feld[x][y];
8317     int graphic = el2img(element);
8318
8319     if (IS_ANIMATED(graphic))
8320       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8321
8322     return;
8323   }
8324
8325   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8326     return;
8327
8328   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8329
8330   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8331 }
8332
8333 void CheckExitSteelEM(int x, int y)
8334 {
8335   if (local_player->gems_still_needed > 0 ||
8336       local_player->sokobanfields_still_needed > 0 ||
8337       local_player->lights_still_needed > 0)
8338   {
8339     int element = Feld[x][y];
8340     int graphic = el2img(element);
8341
8342     if (IS_ANIMATED(graphic))
8343       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8344
8345     return;
8346   }
8347
8348   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8349     return;
8350
8351   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8352
8353   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8354 }
8355
8356 void CheckExitSP(int x, int y)
8357 {
8358   if (local_player->gems_still_needed > 0)
8359   {
8360     int element = Feld[x][y];
8361     int graphic = el2img(element);
8362
8363     if (IS_ANIMATED(graphic))
8364       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8365
8366     return;
8367   }
8368
8369   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8370     return;
8371
8372   Feld[x][y] = EL_SP_EXIT_OPENING;
8373
8374   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8375 }
8376
8377 static void CloseAllOpenTimegates()
8378 {
8379   int x, y;
8380
8381   SCAN_PLAYFIELD(x, y)
8382   {
8383     int element = Feld[x][y];
8384
8385     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8386     {
8387       Feld[x][y] = EL_TIMEGATE_CLOSING;
8388
8389       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8390     }
8391   }
8392 }
8393
8394 void DrawTwinkleOnField(int x, int y)
8395 {
8396   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8397     return;
8398
8399   if (Feld[x][y] == EL_BD_DIAMOND)
8400     return;
8401
8402   if (MovDelay[x][y] == 0)      /* next animation frame */
8403     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8404
8405   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8406   {
8407     MovDelay[x][y]--;
8408
8409     if (setup.direct_draw && MovDelay[x][y])
8410       SetDrawtoField(DRAW_BUFFERED);
8411
8412     DrawLevelElementAnimation(x, y, Feld[x][y]);
8413
8414     if (MovDelay[x][y] != 0)
8415     {
8416       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8417                                            10 - MovDelay[x][y]);
8418
8419       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8420
8421       if (setup.direct_draw)
8422       {
8423         int dest_x, dest_y;
8424
8425         dest_x = FX + SCREENX(x) * TILEX;
8426         dest_y = FY + SCREENY(y) * TILEY;
8427
8428         BlitBitmap(drawto_field, window,
8429                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8430         SetDrawtoField(DRAW_DIRECT);
8431       }
8432     }
8433   }
8434 }
8435
8436 void MauerWaechst(int x, int y)
8437 {
8438   int delay = 6;
8439
8440   if (!MovDelay[x][y])          /* next animation frame */
8441     MovDelay[x][y] = 3 * delay;
8442
8443   if (MovDelay[x][y])           /* wait some time before next frame */
8444   {
8445     MovDelay[x][y]--;
8446
8447     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8448     {
8449       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8450       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8451
8452       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8453     }
8454
8455     if (!MovDelay[x][y])
8456     {
8457       if (MovDir[x][y] == MV_LEFT)
8458       {
8459         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8460           DrawLevelField(x - 1, y);
8461       }
8462       else if (MovDir[x][y] == MV_RIGHT)
8463       {
8464         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8465           DrawLevelField(x + 1, y);
8466       }
8467       else if (MovDir[x][y] == MV_UP)
8468       {
8469         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8470           DrawLevelField(x, y - 1);
8471       }
8472       else
8473       {
8474         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8475           DrawLevelField(x, y + 1);
8476       }
8477
8478       Feld[x][y] = Store[x][y];
8479       Store[x][y] = 0;
8480       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8481       DrawLevelField(x, y);
8482     }
8483   }
8484 }
8485
8486 void MauerAbleger(int ax, int ay)
8487 {
8488   int element = Feld[ax][ay];
8489   int graphic = el2img(element);
8490   boolean oben_frei = FALSE, unten_frei = FALSE;
8491   boolean links_frei = FALSE, rechts_frei = FALSE;
8492   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8493   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8494   boolean new_wall = FALSE;
8495
8496   if (IS_ANIMATED(graphic))
8497     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8498
8499   if (!MovDelay[ax][ay])        /* start building new wall */
8500     MovDelay[ax][ay] = 6;
8501
8502   if (MovDelay[ax][ay])         /* wait some time before building new wall */
8503   {
8504     MovDelay[ax][ay]--;
8505     if (MovDelay[ax][ay])
8506       return;
8507   }
8508
8509   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8510     oben_frei = TRUE;
8511   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8512     unten_frei = TRUE;
8513   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8514     links_frei = TRUE;
8515   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8516     rechts_frei = TRUE;
8517
8518   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
8519       element == EL_EXPANDABLE_WALL_ANY)
8520   {
8521     if (oben_frei)
8522     {
8523       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
8524       Store[ax][ay-1] = element;
8525       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8526       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8527         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8528                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
8529       new_wall = TRUE;
8530     }
8531     if (unten_frei)
8532     {
8533       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
8534       Store[ax][ay+1] = element;
8535       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8536       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8537         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8538                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8539       new_wall = TRUE;
8540     }
8541   }
8542
8543   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8544       element == EL_EXPANDABLE_WALL_ANY ||
8545       element == EL_EXPANDABLE_WALL ||
8546       element == EL_BD_EXPANDABLE_WALL)
8547   {
8548     if (links_frei)
8549     {
8550       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8551       Store[ax-1][ay] = element;
8552       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8553       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8554         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8555                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8556       new_wall = TRUE;
8557     }
8558
8559     if (rechts_frei)
8560     {
8561       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8562       Store[ax+1][ay] = element;
8563       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8564       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8565         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8566                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8567       new_wall = TRUE;
8568     }
8569   }
8570
8571   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8572     DrawLevelField(ax, ay);
8573
8574   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8575     oben_massiv = TRUE;
8576   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8577     unten_massiv = TRUE;
8578   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8579     links_massiv = TRUE;
8580   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8581     rechts_massiv = TRUE;
8582
8583   if (((oben_massiv && unten_massiv) ||
8584        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8585        element == EL_EXPANDABLE_WALL) &&
8586       ((links_massiv && rechts_massiv) ||
8587        element == EL_EXPANDABLE_WALL_VERTICAL))
8588     Feld[ax][ay] = EL_WALL;
8589
8590   if (new_wall)
8591     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8592 }
8593
8594 void MauerAblegerStahl(int ax, int ay)
8595 {
8596   int element = Feld[ax][ay];
8597   int graphic = el2img(element);
8598   boolean oben_frei = FALSE, unten_frei = FALSE;
8599   boolean links_frei = FALSE, rechts_frei = FALSE;
8600   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8601   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8602   boolean new_wall = FALSE;
8603
8604   if (IS_ANIMATED(graphic))
8605     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8606
8607   if (!MovDelay[ax][ay])        /* start building new wall */
8608     MovDelay[ax][ay] = 6;
8609
8610   if (MovDelay[ax][ay])         /* wait some time before building new wall */
8611   {
8612     MovDelay[ax][ay]--;
8613     if (MovDelay[ax][ay])
8614       return;
8615   }
8616
8617   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8618     oben_frei = TRUE;
8619   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8620     unten_frei = TRUE;
8621   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8622     links_frei = TRUE;
8623   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8624     rechts_frei = TRUE;
8625
8626   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
8627       element == EL_EXPANDABLE_STEELWALL_ANY)
8628   {
8629     if (oben_frei)
8630     {
8631       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
8632       Store[ax][ay-1] = element;
8633       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8634       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8635         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8636                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
8637       new_wall = TRUE;
8638     }
8639     if (unten_frei)
8640     {
8641       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
8642       Store[ax][ay+1] = element;
8643       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8644       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8645         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8646                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
8647       new_wall = TRUE;
8648     }
8649   }
8650
8651   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
8652       element == EL_EXPANDABLE_STEELWALL_ANY)
8653   {
8654     if (links_frei)
8655     {
8656       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8657       Store[ax-1][ay] = element;
8658       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8659       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8660         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8661                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
8662       new_wall = TRUE;
8663     }
8664
8665     if (rechts_frei)
8666     {
8667       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8668       Store[ax+1][ay] = element;
8669       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8670       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8671         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8672                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
8673       new_wall = TRUE;
8674     }
8675   }
8676
8677   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8678     oben_massiv = TRUE;
8679   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8680     unten_massiv = TRUE;
8681   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8682     links_massiv = TRUE;
8683   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8684     rechts_massiv = TRUE;
8685
8686   if (((oben_massiv && unten_massiv) ||
8687        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
8688       ((links_massiv && rechts_massiv) ||
8689        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
8690     Feld[ax][ay] = EL_WALL;
8691
8692   if (new_wall)
8693     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8694 }
8695
8696 void CheckForDragon(int x, int y)
8697 {
8698   int i, j;
8699   boolean dragon_found = FALSE;
8700   static int xy[4][2] =
8701   {
8702     { 0, -1 },
8703     { -1, 0 },
8704     { +1, 0 },
8705     { 0, +1 }
8706   };
8707
8708   for (i = 0; i < NUM_DIRECTIONS; i++)
8709   {
8710     for (j = 0; j < 4; j++)
8711     {
8712       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8713
8714       if (IN_LEV_FIELD(xx, yy) &&
8715           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
8716       {
8717         if (Feld[xx][yy] == EL_DRAGON)
8718           dragon_found = TRUE;
8719       }
8720       else
8721         break;
8722     }
8723   }
8724
8725   if (!dragon_found)
8726   {
8727     for (i = 0; i < NUM_DIRECTIONS; i++)
8728     {
8729       for (j = 0; j < 3; j++)
8730       {
8731         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8732   
8733         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
8734         {
8735           Feld[xx][yy] = EL_EMPTY;
8736           DrawLevelField(xx, yy);
8737         }
8738         else
8739           break;
8740       }
8741     }
8742   }
8743 }
8744
8745 static void InitBuggyBase(int x, int y)
8746 {
8747   int element = Feld[x][y];
8748   int activating_delay = FRAMES_PER_SECOND / 4;
8749
8750   ChangeDelay[x][y] =
8751     (element == EL_SP_BUGGY_BASE ?
8752      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
8753      element == EL_SP_BUGGY_BASE_ACTIVATING ?
8754      activating_delay :
8755      element == EL_SP_BUGGY_BASE_ACTIVE ?
8756      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
8757 }
8758
8759 static void WarnBuggyBase(int x, int y)
8760 {
8761   int i;
8762   static int xy[4][2] =
8763   {
8764     { 0, -1 },
8765     { -1, 0 },
8766     { +1, 0 },
8767     { 0, +1 }
8768   };
8769
8770   for (i = 0; i < NUM_DIRECTIONS; i++)
8771   {
8772     int xx = x + xy[i][0];
8773     int yy = y + xy[i][1];
8774
8775     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
8776     {
8777       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8778
8779       break;
8780     }
8781   }
8782 }
8783
8784 static void InitTrap(int x, int y)
8785 {
8786   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8787 }
8788
8789 static void ActivateTrap(int x, int y)
8790 {
8791   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8792 }
8793
8794 static void ChangeActiveTrap(int x, int y)
8795 {
8796   int graphic = IMG_TRAP_ACTIVE;
8797
8798   /* if new animation frame was drawn, correct crumbled sand border */
8799   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8800     DrawLevelFieldCrumbledSand(x, y);
8801 }
8802
8803 static int getSpecialActionElement(int element, int number, int base_element)
8804 {
8805   return (element != EL_EMPTY ? element :
8806           number != -1 ? base_element + number - 1 :
8807           EL_EMPTY);
8808 }
8809
8810 static int getModifiedActionNumber(int value_old, int operator, int operand,
8811                                    int value_min, int value_max)
8812 {
8813   int value_new = (operator == CA_MODE_SET      ? operand :
8814                    operator == CA_MODE_ADD      ? value_old + operand :
8815                    operator == CA_MODE_SUBTRACT ? value_old - operand :
8816                    operator == CA_MODE_MULTIPLY ? value_old * operand :
8817                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
8818                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
8819                    value_old);
8820
8821   return (value_new < value_min ? value_min :
8822           value_new > value_max ? value_max :
8823           value_new);
8824 }
8825
8826 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8827 {
8828   struct ElementInfo *ei = &element_info[element];
8829   struct ElementChangeInfo *change = &ei->change_page[page];
8830   int target_element = change->target_element;
8831   int action_type = change->action_type;
8832   int action_mode = change->action_mode;
8833   int action_arg = change->action_arg;
8834   int i;
8835
8836   if (!change->has_action)
8837     return;
8838
8839   /* ---------- determine action paramater values -------------------------- */
8840
8841   int level_time_value =
8842     (level.time > 0 ? TimeLeft :
8843      TimePlayed);
8844
8845   int action_arg_element =
8846     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
8847      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8848      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
8849      EL_EMPTY);
8850
8851   int action_arg_direction =
8852     (action_arg >= CA_ARG_DIRECTION_LEFT &&
8853      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8854      action_arg == CA_ARG_DIRECTION_TRIGGER ?
8855      change->actual_trigger_side :
8856      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8857      MV_DIR_OPPOSITE(change->actual_trigger_side) :
8858      MV_NONE);
8859
8860   int action_arg_number_min =
8861     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8862      CA_ARG_MIN);
8863
8864   int action_arg_number_max =
8865     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8866      action_type == CA_SET_LEVEL_GEMS ? 999 :
8867      action_type == CA_SET_LEVEL_TIME ? 9999 :
8868      action_type == CA_SET_LEVEL_SCORE ? 99999 :
8869      action_type == CA_SET_CE_VALUE ? 9999 :
8870      action_type == CA_SET_CE_SCORE ? 9999 :
8871      CA_ARG_MAX);
8872
8873   int action_arg_number_reset =
8874     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8875      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8876      action_type == CA_SET_LEVEL_TIME ? level.time :
8877      action_type == CA_SET_LEVEL_SCORE ? 0 :
8878 #if USE_NEW_CUSTOM_VALUE
8879      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8880 #else
8881      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8882 #endif
8883      action_type == CA_SET_CE_SCORE ? 0 :
8884      0);
8885
8886   int action_arg_number =
8887     (action_arg <= CA_ARG_MAX ? action_arg :
8888      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8889      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8890      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8891      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8892      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8893      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8894 #if USE_NEW_CUSTOM_VALUE
8895      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8896 #else
8897      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8898 #endif
8899      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8900      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8901      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8902      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8903      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8904      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8905      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8906      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8907      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8908      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
8909      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8910      -1);
8911
8912   int action_arg_number_old =
8913     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8914      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8915      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8916      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8917      action_type == CA_SET_CE_SCORE ? ei->collect_score :
8918      0);
8919
8920   int action_arg_number_new =
8921     getModifiedActionNumber(action_arg_number_old,
8922                             action_mode, action_arg_number,
8923                             action_arg_number_min, action_arg_number_max);
8924
8925   int trigger_player_bits =
8926     (change->actual_trigger_player >= EL_PLAYER_1 &&
8927      change->actual_trigger_player <= EL_PLAYER_4 ?
8928      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8929      PLAYER_BITS_ANY);
8930
8931   int action_arg_player_bits =
8932     (action_arg >= CA_ARG_PLAYER_1 &&
8933      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8934      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8935      PLAYER_BITS_ANY);
8936
8937   /* ---------- execute action  -------------------------------------------- */
8938
8939   switch (action_type)
8940   {
8941     case CA_NO_ACTION:
8942     {
8943       return;
8944     }
8945
8946     /* ---------- level actions  ------------------------------------------- */
8947
8948     case CA_RESTART_LEVEL:
8949     {
8950       game.restart_level = TRUE;
8951
8952       break;
8953     }
8954
8955     case CA_SHOW_ENVELOPE:
8956     {
8957       int element = getSpecialActionElement(action_arg_element,
8958                                             action_arg_number, EL_ENVELOPE_1);
8959
8960       if (IS_ENVELOPE(element))
8961         local_player->show_envelope = element;
8962
8963       break;
8964     }
8965
8966     case CA_SET_LEVEL_TIME:
8967     {
8968       if (level.time > 0)       /* only modify limited time value */
8969       {
8970         TimeLeft = action_arg_number_new;
8971
8972         DrawGameValue_Time(TimeLeft);
8973
8974         if (!TimeLeft && setup.time_limit)
8975           for (i = 0; i < MAX_PLAYERS; i++)
8976             KillPlayer(&stored_player[i]);
8977       }
8978
8979       break;
8980     }
8981
8982     case CA_SET_LEVEL_SCORE:
8983     {
8984       local_player->score = action_arg_number_new;
8985
8986       DrawGameValue_Score(local_player->score);
8987
8988       break;
8989     }
8990
8991     case CA_SET_LEVEL_GEMS:
8992     {
8993       local_player->gems_still_needed = action_arg_number_new;
8994
8995       DrawGameValue_Emeralds(local_player->gems_still_needed);
8996
8997       break;
8998     }
8999
9000 #if !USE_PLAYER_GRAVITY
9001     case CA_SET_LEVEL_GRAVITY:
9002     {
9003       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
9004                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
9005                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9006                       game.gravity);
9007       break;
9008     }
9009 #endif
9010
9011     case CA_SET_LEVEL_WIND:
9012     {
9013       game.wind_direction = action_arg_direction;
9014
9015       break;
9016     }
9017
9018     /* ---------- player actions  ------------------------------------------ */
9019
9020     case CA_MOVE_PLAYER:
9021     {
9022       /* automatically move to the next field in specified direction */
9023       for (i = 0; i < MAX_PLAYERS; i++)
9024         if (trigger_player_bits & (1 << i))
9025           stored_player[i].programmed_action = action_arg_direction;
9026
9027       break;
9028     }
9029
9030     case CA_EXIT_PLAYER:
9031     {
9032       for (i = 0; i < MAX_PLAYERS; i++)
9033         if (action_arg_player_bits & (1 << i))
9034           PlayerWins(&stored_player[i]);
9035
9036       break;
9037     }
9038
9039     case CA_KILL_PLAYER:
9040     {
9041       for (i = 0; i < MAX_PLAYERS; i++)
9042         if (action_arg_player_bits & (1 << i))
9043           KillPlayer(&stored_player[i]);
9044
9045       break;
9046     }
9047
9048     case CA_SET_PLAYER_KEYS:
9049     {
9050       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9051       int element = getSpecialActionElement(action_arg_element,
9052                                             action_arg_number, EL_KEY_1);
9053
9054       if (IS_KEY(element))
9055       {
9056         for (i = 0; i < MAX_PLAYERS; i++)
9057         {
9058           if (trigger_player_bits & (1 << i))
9059           {
9060             stored_player[i].key[KEY_NR(element)] = key_state;
9061
9062             DrawGameDoorValues();
9063           }
9064         }
9065       }
9066
9067       break;
9068     }
9069
9070     case CA_SET_PLAYER_SPEED:
9071     {
9072       for (i = 0; i < MAX_PLAYERS; i++)
9073       {
9074         if (trigger_player_bits & (1 << i))
9075         {
9076           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9077
9078           if (action_arg == CA_ARG_SPEED_FASTER &&
9079               stored_player[i].cannot_move)
9080           {
9081             action_arg_number = STEPSIZE_VERY_SLOW;
9082           }
9083           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9084                    action_arg == CA_ARG_SPEED_FASTER)
9085           {
9086             action_arg_number = 2;
9087             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9088                            CA_MODE_MULTIPLY);
9089           }
9090           else if (action_arg == CA_ARG_NUMBER_RESET)
9091           {
9092             action_arg_number = level.initial_player_stepsize[i];
9093           }
9094
9095           move_stepsize =
9096             getModifiedActionNumber(move_stepsize,
9097                                     action_mode,
9098                                     action_arg_number,
9099                                     action_arg_number_min,
9100                                     action_arg_number_max);
9101
9102           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9103         }
9104       }
9105
9106       break;
9107     }
9108
9109     case CA_SET_PLAYER_SHIELD:
9110     {
9111       for (i = 0; i < MAX_PLAYERS; i++)
9112       {
9113         if (trigger_player_bits & (1 << i))
9114         {
9115           if (action_arg == CA_ARG_SHIELD_OFF)
9116           {
9117             stored_player[i].shield_normal_time_left = 0;
9118             stored_player[i].shield_deadly_time_left = 0;
9119           }
9120           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9121           {
9122             stored_player[i].shield_normal_time_left = 999999;
9123           }
9124           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9125           {
9126             stored_player[i].shield_normal_time_left = 999999;
9127             stored_player[i].shield_deadly_time_left = 999999;
9128           }
9129         }
9130       }
9131
9132       break;
9133     }
9134
9135 #if USE_PLAYER_GRAVITY
9136     case CA_SET_PLAYER_GRAVITY:
9137     {
9138       for (i = 0; i < MAX_PLAYERS; i++)
9139       {
9140         if (trigger_player_bits & (1 << i))
9141         {
9142           stored_player[i].gravity =
9143             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9144              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9145              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9146              stored_player[i].gravity);
9147         }
9148       }
9149
9150       break;
9151     }
9152 #endif
9153
9154     case CA_SET_PLAYER_ARTWORK:
9155     {
9156       for (i = 0; i < MAX_PLAYERS; i++)
9157       {
9158         if (trigger_player_bits & (1 << i))
9159         {
9160           int artwork_element = action_arg_element;
9161
9162           if (action_arg == CA_ARG_ELEMENT_RESET)
9163             artwork_element =
9164               (level.use_artwork_element[i] ? level.artwork_element[i] :
9165                stored_player[i].element_nr);
9166
9167 #if USE_GFX_RESET_PLAYER_ARTWORK
9168           if (stored_player[i].artwork_element != artwork_element)
9169             stored_player[i].Frame = 0;
9170 #endif
9171
9172           stored_player[i].artwork_element = artwork_element;
9173
9174           SetPlayerWaiting(&stored_player[i], FALSE);
9175
9176           /* set number of special actions for bored and sleeping animation */
9177           stored_player[i].num_special_action_bored =
9178             get_num_special_action(artwork_element,
9179                                    ACTION_BORING_1, ACTION_BORING_LAST);
9180           stored_player[i].num_special_action_sleeping =
9181             get_num_special_action(artwork_element,
9182                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9183         }
9184       }
9185
9186       break;
9187     }
9188
9189     /* ---------- CE actions  ---------------------------------------------- */
9190
9191     case CA_SET_CE_VALUE:
9192     {
9193 #if USE_NEW_CUSTOM_VALUE
9194       int last_ce_value = CustomValue[x][y];
9195
9196       CustomValue[x][y] = action_arg_number_new;
9197
9198       if (CustomValue[x][y] != last_ce_value)
9199       {
9200         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9201         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9202
9203         if (CustomValue[x][y] == 0)
9204         {
9205           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9206           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9207         }
9208       }
9209 #endif
9210
9211       break;
9212     }
9213
9214     case CA_SET_CE_SCORE:
9215     {
9216 #if USE_NEW_CUSTOM_VALUE
9217       int last_ce_score = ei->collect_score;
9218
9219       ei->collect_score = action_arg_number_new;
9220
9221       if (ei->collect_score != last_ce_score)
9222       {
9223         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9224         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9225
9226         if (ei->collect_score == 0)
9227         {
9228           int xx, yy;
9229
9230           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9231           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9232
9233           /*
9234             This is a very special case that seems to be a mixture between
9235             CheckElementChange() and CheckTriggeredElementChange(): while
9236             the first one only affects single elements that are triggered
9237             directly, the second one affects multiple elements in the playfield
9238             that are triggered indirectly by another element. This is a third
9239             case: Changing the CE score always affects multiple identical CEs,
9240             so every affected CE must be checked, not only the single CE for
9241             which the CE score was changed in the first place (as every instance
9242             of that CE shares the same CE score, and therefore also can change)!
9243           */
9244           SCAN_PLAYFIELD(xx, yy)
9245           {
9246             if (Feld[xx][yy] == element)
9247               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9248                                  CE_SCORE_GETS_ZERO);
9249           }
9250         }
9251       }
9252 #endif
9253
9254       break;
9255     }
9256
9257     /* ---------- engine actions  ------------------------------------------ */
9258
9259     case CA_SET_ENGINE_SCAN_MODE:
9260     {
9261       InitPlayfieldScanMode(action_arg);
9262
9263       break;
9264     }
9265
9266     default:
9267       break;
9268   }
9269 }
9270
9271 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9272 {
9273   int old_element = Feld[x][y];
9274   int new_element = GetElementFromGroupElement(element);
9275   int previous_move_direction = MovDir[x][y];
9276 #if USE_NEW_CUSTOM_VALUE
9277   int last_ce_value = CustomValue[x][y];
9278 #endif
9279   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9280   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9281   boolean add_player_onto_element = (new_element_is_player &&
9282 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9283                                      /* this breaks SnakeBite when a snake is
9284                                         halfway through a door that closes */
9285                                      /* NOW FIXED AT LEVEL INIT IN files.c */
9286                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9287 #endif
9288                                      IS_WALKABLE(old_element));
9289
9290 #if 0
9291   /* check if element under the player changes from accessible to unaccessible
9292      (needed for special case of dropping element which then changes) */
9293   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9294       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9295   {
9296     Bang(x, y);
9297
9298     return;
9299   }
9300 #endif
9301
9302   if (!add_player_onto_element)
9303   {
9304     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9305       RemoveMovingField(x, y);
9306     else
9307       RemoveField(x, y);
9308
9309     Feld[x][y] = new_element;
9310
9311 #if !USE_GFX_RESET_GFX_ANIMATION
9312     ResetGfxAnimation(x, y);
9313     ResetRandomAnimationValue(x, y);
9314 #endif
9315
9316     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9317       MovDir[x][y] = previous_move_direction;
9318
9319 #if USE_NEW_CUSTOM_VALUE
9320     if (element_info[new_element].use_last_ce_value)
9321       CustomValue[x][y] = last_ce_value;
9322 #endif
9323
9324     InitField_WithBug1(x, y, FALSE);
9325
9326     new_element = Feld[x][y];   /* element may have changed */
9327
9328 #if USE_GFX_RESET_GFX_ANIMATION
9329     ResetGfxAnimation(x, y);
9330     ResetRandomAnimationValue(x, y);
9331 #endif
9332
9333     DrawLevelField(x, y);
9334
9335     if (GFX_CRUMBLED(new_element))
9336       DrawLevelFieldCrumbledSandNeighbours(x, y);
9337   }
9338
9339 #if 1
9340   /* check if element under the player changes from accessible to unaccessible
9341      (needed for special case of dropping element which then changes) */
9342   /* (must be checked after creating new element for walkable group elements) */
9343 #if USE_FIX_KILLED_BY_NON_WALKABLE
9344   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9345       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9346   {
9347     Bang(x, y);
9348
9349     return;
9350   }
9351 #else
9352   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9353       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9354   {
9355     Bang(x, y);
9356
9357     return;
9358   }
9359 #endif
9360 #endif
9361
9362   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9363   if (new_element_is_player)
9364     RelocatePlayer(x, y, new_element);
9365
9366   if (is_change)
9367     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9368
9369   TestIfBadThingTouchesPlayer(x, y);
9370   TestIfPlayerTouchesCustomElement(x, y);
9371   TestIfElementTouchesCustomElement(x, y);
9372 }
9373
9374 static void CreateField(int x, int y, int element)
9375 {
9376   CreateFieldExt(x, y, element, FALSE);
9377 }
9378
9379 static void CreateElementFromChange(int x, int y, int element)
9380 {
9381   element = GET_VALID_RUNTIME_ELEMENT(element);
9382
9383 #if USE_STOP_CHANGED_ELEMENTS
9384   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9385   {
9386     int old_element = Feld[x][y];
9387
9388     /* prevent changed element from moving in same engine frame
9389        unless both old and new element can either fall or move */
9390     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9391         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9392       Stop[x][y] = TRUE;
9393   }
9394 #endif
9395
9396   CreateFieldExt(x, y, element, TRUE);
9397 }
9398
9399 static boolean ChangeElement(int x, int y, int element, int page)
9400 {
9401   struct ElementInfo *ei = &element_info[element];
9402   struct ElementChangeInfo *change = &ei->change_page[page];
9403   int ce_value = CustomValue[x][y];
9404   int ce_score = ei->collect_score;
9405   int target_element;
9406   int old_element = Feld[x][y];
9407
9408   /* always use default change event to prevent running into a loop */
9409   if (ChangeEvent[x][y] == -1)
9410     ChangeEvent[x][y] = CE_DELAY;
9411
9412   if (ChangeEvent[x][y] == CE_DELAY)
9413   {
9414     /* reset actual trigger element, trigger player and action element */
9415     change->actual_trigger_element = EL_EMPTY;
9416     change->actual_trigger_player = EL_PLAYER_1;
9417     change->actual_trigger_side = CH_SIDE_NONE;
9418     change->actual_trigger_ce_value = 0;
9419     change->actual_trigger_ce_score = 0;
9420   }
9421
9422   /* do not change elements more than a specified maximum number of changes */
9423   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
9424     return FALSE;
9425
9426   ChangeCount[x][y]++;          /* count number of changes in the same frame */
9427
9428   if (change->explode)
9429   {
9430     Bang(x, y);
9431
9432     return TRUE;
9433   }
9434
9435   if (change->use_target_content)
9436   {
9437     boolean complete_replace = TRUE;
9438     boolean can_replace[3][3];
9439     int xx, yy;
9440
9441     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9442     {
9443       boolean is_empty;
9444       boolean is_walkable;
9445       boolean is_diggable;
9446       boolean is_collectible;
9447       boolean is_removable;
9448       boolean is_destructible;
9449       int ex = x + xx - 1;
9450       int ey = y + yy - 1;
9451       int content_element = change->target_content.e[xx][yy];
9452       int e;
9453
9454       can_replace[xx][yy] = TRUE;
9455
9456       if (ex == x && ey == y)   /* do not check changing element itself */
9457         continue;
9458
9459       if (content_element == EL_EMPTY_SPACE)
9460       {
9461         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
9462
9463         continue;
9464       }
9465
9466       if (!IN_LEV_FIELD(ex, ey))
9467       {
9468         can_replace[xx][yy] = FALSE;
9469         complete_replace = FALSE;
9470
9471         continue;
9472       }
9473
9474       e = Feld[ex][ey];
9475
9476       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9477         e = MovingOrBlocked2Element(ex, ey);
9478
9479       is_empty = (IS_FREE(ex, ey) ||
9480                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
9481
9482       is_walkable     = (is_empty || IS_WALKABLE(e));
9483       is_diggable     = (is_empty || IS_DIGGABLE(e));
9484       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
9485       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
9486       is_removable    = (is_diggable || is_collectible);
9487
9488       can_replace[xx][yy] =
9489         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
9490           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
9491           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
9492           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
9493           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
9494           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
9495          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
9496
9497       if (!can_replace[xx][yy])
9498         complete_replace = FALSE;
9499     }
9500
9501     if (!change->only_if_complete || complete_replace)
9502     {
9503       boolean something_has_changed = FALSE;
9504
9505       if (change->only_if_complete && change->use_random_replace &&
9506           RND(100) < change->random_percentage)
9507         return FALSE;
9508
9509       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9510       {
9511         int ex = x + xx - 1;
9512         int ey = y + yy - 1;
9513         int content_element;
9514
9515         if (can_replace[xx][yy] && (!change->use_random_replace ||
9516                                     RND(100) < change->random_percentage))
9517         {
9518           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9519             RemoveMovingField(ex, ey);
9520
9521           ChangeEvent[ex][ey] = ChangeEvent[x][y];
9522
9523           content_element = change->target_content.e[xx][yy];
9524           target_element = GET_TARGET_ELEMENT(element, content_element, change,
9525                                               ce_value, ce_score);
9526
9527           CreateElementFromChange(ex, ey, target_element);
9528
9529           something_has_changed = TRUE;
9530
9531           /* for symmetry reasons, freeze newly created border elements */
9532           if (ex != x || ey != y)
9533             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
9534         }
9535       }
9536
9537       if (something_has_changed)
9538       {
9539         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9540         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9541       }
9542     }
9543   }
9544   else
9545   {
9546     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9547                                         ce_value, ce_score);
9548
9549     if (element == EL_DIAGONAL_GROWING ||
9550         element == EL_DIAGONAL_SHRINKING)
9551     {
9552       target_element = Store[x][y];
9553
9554       Store[x][y] = EL_EMPTY;
9555     }
9556
9557     CreateElementFromChange(x, y, target_element);
9558
9559     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9560     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9561   }
9562
9563   /* this uses direct change before indirect change */
9564   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9565
9566   return TRUE;
9567 }
9568
9569 #if USE_NEW_DELAYED_ACTION
9570
9571 static void HandleElementChange(int x, int y, int page)
9572 {
9573   int element = MovingOrBlocked2Element(x, y);
9574   struct ElementInfo *ei = &element_info[element];
9575   struct ElementChangeInfo *change = &ei->change_page[page];
9576
9577 #ifdef DEBUG
9578   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
9579       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
9580   {
9581     printf("\n\n");
9582     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9583            x, y, element, element_info[element].token_name);
9584     printf("HandleElementChange(): This should never happen!\n");
9585     printf("\n\n");
9586   }
9587 #endif
9588
9589   /* this can happen with classic bombs on walkable, changing elements */
9590   if (!CAN_CHANGE_OR_HAS_ACTION(element))
9591   {
9592 #if 0
9593     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9594       ChangeDelay[x][y] = 0;
9595 #endif
9596
9597     return;
9598   }
9599
9600   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9601   {
9602     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9603
9604     if (change->can_change)
9605     {
9606 #if 1
9607       /* !!! not clear why graphic animation should be reset at all here !!! */
9608       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
9609 #if USE_GFX_RESET_WHEN_NOT_MOVING
9610       /* when a custom element is about to change (for example by change delay),
9611          do not reset graphic animation when the custom element is moving */
9612       if (!IS_MOVING(x, y))
9613 #endif
9614       {
9615         ResetGfxAnimation(x, y);
9616         ResetRandomAnimationValue(x, y);
9617       }
9618 #endif
9619
9620       if (change->pre_change_function)
9621         change->pre_change_function(x, y);
9622     }
9623   }
9624
9625   ChangeDelay[x][y]--;
9626
9627   if (ChangeDelay[x][y] != 0)           /* continue element change */
9628   {
9629     if (change->can_change)
9630     {
9631       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9632
9633       if (IS_ANIMATED(graphic))
9634         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9635
9636       if (change->change_function)
9637         change->change_function(x, y);
9638     }
9639   }
9640   else                                  /* finish element change */
9641   {
9642     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9643     {
9644       page = ChangePage[x][y];
9645       ChangePage[x][y] = -1;
9646
9647       change = &ei->change_page[page];
9648     }
9649
9650     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9651     {
9652       ChangeDelay[x][y] = 1;            /* try change after next move step */
9653       ChangePage[x][y] = page;          /* remember page to use for change */
9654
9655       return;
9656     }
9657
9658     if (change->can_change)
9659     {
9660       if (ChangeElement(x, y, element, page))
9661       {
9662         if (change->post_change_function)
9663           change->post_change_function(x, y);
9664       }
9665     }
9666
9667     if (change->has_action)
9668       ExecuteCustomElementAction(x, y, element, page);
9669   }
9670 }
9671
9672 #else
9673
9674 static void HandleElementChange(int x, int y, int page)
9675 {
9676   int element = MovingOrBlocked2Element(x, y);
9677   struct ElementInfo *ei = &element_info[element];
9678   struct ElementChangeInfo *change = &ei->change_page[page];
9679
9680 #ifdef DEBUG
9681   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
9682   {
9683     printf("\n\n");
9684     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9685            x, y, element, element_info[element].token_name);
9686     printf("HandleElementChange(): This should never happen!\n");
9687     printf("\n\n");
9688   }
9689 #endif
9690
9691   /* this can happen with classic bombs on walkable, changing elements */
9692   if (!CAN_CHANGE(element))
9693   {
9694 #if 0
9695     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9696       ChangeDelay[x][y] = 0;
9697 #endif
9698
9699     return;
9700   }
9701
9702   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9703   {
9704     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9705
9706     ResetGfxAnimation(x, y);
9707     ResetRandomAnimationValue(x, y);
9708
9709     if (change->pre_change_function)
9710       change->pre_change_function(x, y);
9711   }
9712
9713   ChangeDelay[x][y]--;
9714
9715   if (ChangeDelay[x][y] != 0)           /* continue element change */
9716   {
9717     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9718
9719     if (IS_ANIMATED(graphic))
9720       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9721
9722     if (change->change_function)
9723       change->change_function(x, y);
9724   }
9725   else                                  /* finish element change */
9726   {
9727     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9728     {
9729       page = ChangePage[x][y];
9730       ChangePage[x][y] = -1;
9731
9732       change = &ei->change_page[page];
9733     }
9734
9735     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9736     {
9737       ChangeDelay[x][y] = 1;            /* try change after next move step */
9738       ChangePage[x][y] = page;          /* remember page to use for change */
9739
9740       return;
9741     }
9742
9743     if (ChangeElement(x, y, element, page))
9744     {
9745       if (change->post_change_function)
9746         change->post_change_function(x, y);
9747     }
9748   }
9749 }
9750
9751 #endif
9752
9753 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
9754                                               int trigger_element,
9755                                               int trigger_event,
9756                                               int trigger_player,
9757                                               int trigger_side,
9758                                               int trigger_page)
9759 {
9760   boolean change_done_any = FALSE;
9761   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9762   int i;
9763
9764   if (!(trigger_events[trigger_element][trigger_event]))
9765     return FALSE;
9766
9767 #if 0
9768   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9769          trigger_event, recursion_loop_depth, recursion_loop_detected,
9770          recursion_loop_element, EL_NAME(recursion_loop_element));
9771 #endif
9772
9773   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9774
9775   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9776   {
9777     int element = EL_CUSTOM_START + i;
9778     boolean change_done = FALSE;
9779     int p;
9780
9781     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9782         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9783       continue;
9784
9785     for (p = 0; p < element_info[element].num_change_pages; p++)
9786     {
9787       struct ElementChangeInfo *change = &element_info[element].change_page[p];
9788
9789       if (change->can_change_or_has_action &&
9790           change->has_event[trigger_event] &&
9791           change->trigger_side & trigger_side &&
9792           change->trigger_player & trigger_player &&
9793           change->trigger_page & trigger_page_bits &&
9794           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9795       {
9796         change->actual_trigger_element = trigger_element;
9797         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9798         change->actual_trigger_side = trigger_side;
9799         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9800         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9801
9802         if ((change->can_change && !change_done) || change->has_action)
9803         {
9804           int x, y;
9805
9806           SCAN_PLAYFIELD(x, y)
9807           {
9808             if (Feld[x][y] == element)
9809             {
9810               if (change->can_change && !change_done)
9811               {
9812                 ChangeDelay[x][y] = 1;
9813                 ChangeEvent[x][y] = trigger_event;
9814
9815                 HandleElementChange(x, y, p);
9816               }
9817 #if USE_NEW_DELAYED_ACTION
9818               else if (change->has_action)
9819               {
9820                 ExecuteCustomElementAction(x, y, element, p);
9821                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9822               }
9823 #else
9824               if (change->has_action)
9825               {
9826                 ExecuteCustomElementAction(x, y, element, p);
9827                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9828               }
9829 #endif
9830             }
9831           }
9832
9833           if (change->can_change)
9834           {
9835             change_done = TRUE;
9836             change_done_any = TRUE;
9837           }
9838         }
9839       }
9840     }
9841   }
9842
9843   RECURSION_LOOP_DETECTION_END();
9844
9845   return change_done_any;
9846 }
9847
9848 static boolean CheckElementChangeExt(int x, int y,
9849                                      int element,
9850                                      int trigger_element,
9851                                      int trigger_event,
9852                                      int trigger_player,
9853                                      int trigger_side)
9854 {
9855   boolean change_done = FALSE;
9856   int p;
9857
9858   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9859       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9860     return FALSE;
9861
9862   if (Feld[x][y] == EL_BLOCKED)
9863   {
9864     Blocked2Moving(x, y, &x, &y);
9865     element = Feld[x][y];
9866   }
9867
9868 #if 0
9869   /* check if element has already changed */
9870   if (Feld[x][y] != element)
9871     return FALSE;
9872 #else
9873   /* check if element has already changed or is about to change after moving */
9874   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9875        Feld[x][y] != element) ||
9876
9877       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9878        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9879         ChangePage[x][y] != -1)))
9880     return FALSE;
9881 #endif
9882
9883 #if 0
9884   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9885          trigger_event, recursion_loop_depth, recursion_loop_detected,
9886          recursion_loop_element, EL_NAME(recursion_loop_element));
9887 #endif
9888
9889   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9890
9891   for (p = 0; p < element_info[element].num_change_pages; p++)
9892   {
9893     struct ElementChangeInfo *change = &element_info[element].change_page[p];
9894
9895     /* check trigger element for all events where the element that is checked
9896        for changing interacts with a directly adjacent element -- this is
9897        different to element changes that affect other elements to change on the
9898        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
9899     boolean check_trigger_element =
9900       (trigger_event == CE_TOUCHING_X ||
9901        trigger_event == CE_HITTING_X ||
9902        trigger_event == CE_HIT_BY_X ||
9903 #if 1
9904        /* this one was forgotten until 3.2.3 */
9905        trigger_event == CE_DIGGING_X);
9906 #endif
9907
9908     if (change->can_change_or_has_action &&
9909         change->has_event[trigger_event] &&
9910         change->trigger_side & trigger_side &&
9911         change->trigger_player & trigger_player &&
9912         (!check_trigger_element ||
9913          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9914     {
9915       change->actual_trigger_element = trigger_element;
9916       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9917       change->actual_trigger_side = trigger_side;
9918       change->actual_trigger_ce_value = CustomValue[x][y];
9919       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9920
9921       /* special case: trigger element not at (x,y) position for some events */
9922       if (check_trigger_element)
9923       {
9924         static struct
9925         {
9926           int dx, dy;
9927         } move_xy[] =
9928           {
9929             {  0,  0 },
9930             { -1,  0 },
9931             { +1,  0 },
9932             {  0,  0 },
9933             {  0, -1 },
9934             {  0,  0 }, { 0, 0 }, { 0, 0 },
9935             {  0, +1 }
9936           };
9937
9938         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9939         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9940
9941         change->actual_trigger_ce_value = CustomValue[xx][yy];
9942         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9943       }
9944
9945       if (change->can_change && !change_done)
9946       {
9947         ChangeDelay[x][y] = 1;
9948         ChangeEvent[x][y] = trigger_event;
9949
9950         HandleElementChange(x, y, p);
9951
9952         change_done = TRUE;
9953       }
9954 #if USE_NEW_DELAYED_ACTION
9955       else if (change->has_action)
9956       {
9957         ExecuteCustomElementAction(x, y, element, p);
9958         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9959       }
9960 #else
9961       if (change->has_action)
9962       {
9963         ExecuteCustomElementAction(x, y, element, p);
9964         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9965       }
9966 #endif
9967     }
9968   }
9969
9970   RECURSION_LOOP_DETECTION_END();
9971
9972   return change_done;
9973 }
9974
9975 static void PlayPlayerSound(struct PlayerInfo *player)
9976 {
9977   int jx = player->jx, jy = player->jy;
9978   int sound_element = player->artwork_element;
9979   int last_action = player->last_action_waiting;
9980   int action = player->action_waiting;
9981
9982   if (player->is_waiting)
9983   {
9984     if (action != last_action)
9985       PlayLevelSoundElementAction(jx, jy, sound_element, action);
9986     else
9987       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9988   }
9989   else
9990   {
9991     if (action != last_action)
9992       StopSound(element_info[sound_element].sound[last_action]);
9993
9994     if (last_action == ACTION_SLEEPING)
9995       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9996   }
9997 }
9998
9999 static void PlayAllPlayersSound()
10000 {
10001   int i;
10002
10003   for (i = 0; i < MAX_PLAYERS; i++)
10004     if (stored_player[i].active)
10005       PlayPlayerSound(&stored_player[i]);
10006 }
10007
10008 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10009 {
10010   boolean last_waiting = player->is_waiting;
10011   int move_dir = player->MovDir;
10012
10013   player->dir_waiting = move_dir;
10014   player->last_action_waiting = player->action_waiting;
10015
10016   if (is_waiting)
10017   {
10018     if (!last_waiting)          /* not waiting -> waiting */
10019     {
10020       player->is_waiting = TRUE;
10021
10022       player->frame_counter_bored =
10023         FrameCounter +
10024         game.player_boring_delay_fixed +
10025         GetSimpleRandom(game.player_boring_delay_random);
10026       player->frame_counter_sleeping =
10027         FrameCounter +
10028         game.player_sleeping_delay_fixed +
10029         GetSimpleRandom(game.player_sleeping_delay_random);
10030
10031       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10032     }
10033
10034     if (game.player_sleeping_delay_fixed +
10035         game.player_sleeping_delay_random > 0 &&
10036         player->anim_delay_counter == 0 &&
10037         player->post_delay_counter == 0 &&
10038         FrameCounter >= player->frame_counter_sleeping)
10039       player->is_sleeping = TRUE;
10040     else if (game.player_boring_delay_fixed +
10041              game.player_boring_delay_random > 0 &&
10042              FrameCounter >= player->frame_counter_bored)
10043       player->is_bored = TRUE;
10044
10045     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10046                               player->is_bored ? ACTION_BORING :
10047                               ACTION_WAITING);
10048
10049     if (player->is_sleeping && player->use_murphy)
10050     {
10051       /* special case for sleeping Murphy when leaning against non-free tile */
10052
10053       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10054           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10055            !IS_MOVING(player->jx - 1, player->jy)))
10056         move_dir = MV_LEFT;
10057       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10058                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10059                 !IS_MOVING(player->jx + 1, player->jy)))
10060         move_dir = MV_RIGHT;
10061       else
10062         player->is_sleeping = FALSE;
10063
10064       player->dir_waiting = move_dir;
10065     }
10066
10067     if (player->is_sleeping)
10068     {
10069       if (player->num_special_action_sleeping > 0)
10070       {
10071         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10072         {
10073           int last_special_action = player->special_action_sleeping;
10074           int num_special_action = player->num_special_action_sleeping;
10075           int special_action =
10076             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10077              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10078              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10079              last_special_action + 1 : ACTION_SLEEPING);
10080           int special_graphic =
10081             el_act_dir2img(player->artwork_element, special_action, move_dir);
10082
10083           player->anim_delay_counter =
10084             graphic_info[special_graphic].anim_delay_fixed +
10085             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10086           player->post_delay_counter =
10087             graphic_info[special_graphic].post_delay_fixed +
10088             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10089
10090           player->special_action_sleeping = special_action;
10091         }
10092
10093         if (player->anim_delay_counter > 0)
10094         {
10095           player->action_waiting = player->special_action_sleeping;
10096           player->anim_delay_counter--;
10097         }
10098         else if (player->post_delay_counter > 0)
10099         {
10100           player->post_delay_counter--;
10101         }
10102       }
10103     }
10104     else if (player->is_bored)
10105     {
10106       if (player->num_special_action_bored > 0)
10107       {
10108         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10109         {
10110           int special_action =
10111             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10112           int special_graphic =
10113             el_act_dir2img(player->artwork_element, special_action, move_dir);
10114
10115           player->anim_delay_counter =
10116             graphic_info[special_graphic].anim_delay_fixed +
10117             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10118           player->post_delay_counter =
10119             graphic_info[special_graphic].post_delay_fixed +
10120             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10121
10122           player->special_action_bored = special_action;
10123         }
10124
10125         if (player->anim_delay_counter > 0)
10126         {
10127           player->action_waiting = player->special_action_bored;
10128           player->anim_delay_counter--;
10129         }
10130         else if (player->post_delay_counter > 0)
10131         {
10132           player->post_delay_counter--;
10133         }
10134       }
10135     }
10136   }
10137   else if (last_waiting)        /* waiting -> not waiting */
10138   {
10139     player->is_waiting = FALSE;
10140     player->is_bored = FALSE;
10141     player->is_sleeping = FALSE;
10142
10143     player->frame_counter_bored = -1;
10144     player->frame_counter_sleeping = -1;
10145
10146     player->anim_delay_counter = 0;
10147     player->post_delay_counter = 0;
10148
10149     player->dir_waiting = player->MovDir;
10150     player->action_waiting = ACTION_DEFAULT;
10151
10152     player->special_action_bored = ACTION_DEFAULT;
10153     player->special_action_sleeping = ACTION_DEFAULT;
10154   }
10155 }
10156
10157 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10158 {
10159   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10160   int left      = player_action & JOY_LEFT;
10161   int right     = player_action & JOY_RIGHT;
10162   int up        = player_action & JOY_UP;
10163   int down      = player_action & JOY_DOWN;
10164   int button1   = player_action & JOY_BUTTON_1;
10165   int button2   = player_action & JOY_BUTTON_2;
10166   int dx        = (left ? -1 : right ? 1 : 0);
10167   int dy        = (up   ? -1 : down  ? 1 : 0);
10168
10169   if (!player->active || tape.pausing)
10170     return 0;
10171
10172   if (player_action)
10173   {
10174     if (button1)
10175       snapped = SnapField(player, dx, dy);
10176     else
10177     {
10178       if (button2)
10179         dropped = DropElement(player);
10180
10181       moved = MovePlayer(player, dx, dy);
10182     }
10183
10184     if (tape.single_step && tape.recording && !tape.pausing)
10185     {
10186       if (button1 || (dropped && !moved))
10187       {
10188         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10189         SnapField(player, 0, 0);                /* stop snapping */
10190       }
10191     }
10192
10193     SetPlayerWaiting(player, FALSE);
10194
10195     return player_action;
10196   }
10197   else
10198   {
10199     /* no actions for this player (no input at player's configured device) */
10200
10201     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10202     SnapField(player, 0, 0);
10203     CheckGravityMovementWhenNotMoving(player);
10204
10205     if (player->MovPos == 0)
10206       SetPlayerWaiting(player, TRUE);
10207
10208     if (player->MovPos == 0)    /* needed for tape.playing */
10209       player->is_moving = FALSE;
10210
10211     player->is_dropping = FALSE;
10212     player->is_dropping_pressed = FALSE;
10213     player->drop_pressed_delay = 0;
10214
10215     return 0;
10216   }
10217 }
10218
10219 static void CheckLevelTime()
10220 {
10221   int i;
10222
10223   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10224   {
10225     if (level.native_em_level->lev->home == 0)  /* all players at home */
10226     {
10227       PlayerWins(local_player);
10228
10229       AllPlayersGone = TRUE;
10230
10231       level.native_em_level->lev->home = -1;
10232     }
10233
10234     if (level.native_em_level->ply[0]->alive == 0 &&
10235         level.native_em_level->ply[1]->alive == 0 &&
10236         level.native_em_level->ply[2]->alive == 0 &&
10237         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10238       AllPlayersGone = TRUE;
10239   }
10240
10241   if (TimeFrames >= FRAMES_PER_SECOND)
10242   {
10243     TimeFrames = 0;
10244     TapeTime++;
10245
10246     for (i = 0; i < MAX_PLAYERS; i++)
10247     {
10248       struct PlayerInfo *player = &stored_player[i];
10249
10250       if (SHIELD_ON(player))
10251       {
10252         player->shield_normal_time_left--;
10253
10254         if (player->shield_deadly_time_left > 0)
10255           player->shield_deadly_time_left--;
10256       }
10257     }
10258
10259     if (!local_player->LevelSolved && !level.use_step_counter)
10260     {
10261       TimePlayed++;
10262
10263       if (TimeLeft > 0)
10264       {
10265         TimeLeft--;
10266
10267         if (TimeLeft <= 10 && setup.time_limit)
10268           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10269
10270         DrawGameValue_Time(TimeLeft);
10271
10272         if (!TimeLeft && setup.time_limit)
10273         {
10274           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10275             level.native_em_level->lev->killed_out_of_time = TRUE;
10276           else
10277             for (i = 0; i < MAX_PLAYERS; i++)
10278               KillPlayer(&stored_player[i]);
10279         }
10280       }
10281       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10282         DrawGameValue_Time(TimePlayed);
10283
10284       level.native_em_level->lev->time =
10285         (level.time == 0 ? TimePlayed : TimeLeft);
10286     }
10287
10288     if (tape.recording || tape.playing)
10289       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10290   }
10291 }
10292
10293 void AdvanceFrameAndPlayerCounters(int player_nr)
10294 {
10295   int i;
10296
10297   /* advance frame counters (global frame counter and time frame counter) */
10298   FrameCounter++;
10299   TimeFrames++;
10300
10301   /* advance player counters (counters for move delay, move animation etc.) */
10302   for (i = 0; i < MAX_PLAYERS; i++)
10303   {
10304     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10305     int move_delay_value = stored_player[i].move_delay_value;
10306     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10307
10308     if (!advance_player_counters)       /* not all players may be affected */
10309       continue;
10310
10311 #if USE_NEW_PLAYER_ANIM
10312     if (move_frames == 0)       /* less than one move per game frame */
10313     {
10314       int stepsize = TILEX / move_delay_value;
10315       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10316       int count = (stored_player[i].is_moving ?
10317                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10318
10319       if (count % delay == 0)
10320         move_frames = 1;
10321     }
10322 #endif
10323
10324     stored_player[i].Frame += move_frames;
10325
10326     if (stored_player[i].MovPos != 0)
10327       stored_player[i].StepFrame += move_frames;
10328
10329     if (stored_player[i].move_delay > 0)
10330       stored_player[i].move_delay--;
10331
10332     /* due to bugs in previous versions, counter must count up, not down */
10333     if (stored_player[i].push_delay != -1)
10334       stored_player[i].push_delay++;
10335
10336     if (stored_player[i].drop_delay > 0)
10337       stored_player[i].drop_delay--;
10338
10339     if (stored_player[i].is_dropping_pressed)
10340       stored_player[i].drop_pressed_delay++;
10341   }
10342 }
10343
10344 void StartGameActions(boolean init_network_game, boolean record_tape,
10345                       long random_seed)
10346 {
10347   unsigned long new_random_seed = InitRND(random_seed);
10348
10349   if (record_tape)
10350     TapeStartRecording(new_random_seed);
10351
10352 #if defined(NETWORK_AVALIABLE)
10353   if (init_network_game)
10354   {
10355     SendToServer_StartPlaying();
10356
10357     return;
10358   }
10359 #endif
10360
10361   InitGame();
10362 }
10363
10364 void GameActions()
10365 {
10366   static unsigned long game_frame_delay = 0;
10367   unsigned long game_frame_delay_value;
10368   byte *recorded_player_action;
10369   byte summarized_player_action = 0;
10370   byte tape_action[MAX_PLAYERS];
10371   int i;
10372
10373   /* detect endless loops, caused by custom element programming */
10374   if (recursion_loop_detected && recursion_loop_depth == 0)
10375   {
10376     char *message = getStringCat3("Internal Error ! Element ",
10377                                   EL_NAME(recursion_loop_element),
10378                                   " caused endless loop ! Quit the game ?");
10379
10380     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10381           EL_NAME(recursion_loop_element));
10382
10383     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10384
10385     recursion_loop_detected = FALSE;    /* if game should be continued */
10386
10387     free(message);
10388
10389     return;
10390   }
10391
10392   if (game.restart_level)
10393     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
10394
10395   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10396   {
10397     if (level.native_em_level->lev->home == 0)  /* all players at home */
10398     {
10399       PlayerWins(local_player);
10400
10401       AllPlayersGone = TRUE;
10402
10403       level.native_em_level->lev->home = -1;
10404     }
10405
10406     if (level.native_em_level->ply[0]->alive == 0 &&
10407         level.native_em_level->ply[1]->alive == 0 &&
10408         level.native_em_level->ply[2]->alive == 0 &&
10409         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10410       AllPlayersGone = TRUE;
10411   }
10412
10413   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10414     GameWon();
10415
10416   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10417     TapeStop();
10418
10419   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
10420     return;
10421
10422   game_frame_delay_value =
10423     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10424
10425   if (tape.playing && tape.warp_forward && !tape.pausing)
10426     game_frame_delay_value = 0;
10427
10428   /* ---------- main game synchronization point ---------- */
10429
10430   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10431
10432   if (network_playing && !network_player_action_received)
10433   {
10434     /* try to get network player actions in time */
10435
10436 #if defined(NETWORK_AVALIABLE)
10437     /* last chance to get network player actions without main loop delay */
10438     HandleNetworking();
10439 #endif
10440
10441     /* game was quit by network peer */
10442     if (game_status != GAME_MODE_PLAYING)
10443       return;
10444
10445     if (!network_player_action_received)
10446       return;           /* failed to get network player actions in time */
10447
10448     /* do not yet reset "network_player_action_received" (for tape.pausing) */
10449   }
10450
10451   if (tape.pausing)
10452     return;
10453
10454   /* at this point we know that we really continue executing the game */
10455
10456   network_player_action_received = FALSE;
10457
10458   /* when playing tape, read previously recorded player input from tape data */
10459   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
10460
10461 #if 1
10462   /* TapePlayAction() may return NULL when toggling to "pause before death" */
10463   if (tape.pausing)
10464     return;
10465 #endif
10466
10467   if (tape.set_centered_player)
10468   {
10469     game.centered_player_nr_next = tape.centered_player_nr_next;
10470     game.set_centered_player = TRUE;
10471   }
10472
10473   for (i = 0; i < MAX_PLAYERS; i++)
10474   {
10475     summarized_player_action |= stored_player[i].action;
10476
10477     if (!network_playing)
10478       stored_player[i].effective_action = stored_player[i].action;
10479   }
10480
10481 #if defined(NETWORK_AVALIABLE)
10482   if (network_playing)
10483     SendToServer_MovePlayer(summarized_player_action);
10484 #endif
10485
10486   if (!options.network && !setup.team_mode)
10487     local_player->effective_action = summarized_player_action;
10488
10489   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
10490   {
10491     for (i = 0; i < MAX_PLAYERS; i++)
10492       stored_player[i].effective_action =
10493         (i == game.centered_player_nr ? summarized_player_action : 0);
10494   }
10495
10496   if (recorded_player_action != NULL)
10497     for (i = 0; i < MAX_PLAYERS; i++)
10498       stored_player[i].effective_action = recorded_player_action[i];
10499
10500   for (i = 0; i < MAX_PLAYERS; i++)
10501   {
10502     tape_action[i] = stored_player[i].effective_action;
10503
10504     /* (this can only happen in the R'n'D game engine) */
10505     if (tape.recording && tape_action[i] && !tape.player_participates[i])
10506       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
10507   }
10508
10509   /* only record actions from input devices, but not programmed actions */
10510   if (tape.recording)
10511     TapeRecordAction(tape_action);
10512
10513   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10514   {
10515     GameActions_EM_Main();
10516   }
10517   else
10518   {
10519     GameActions_RND();
10520   }
10521 }
10522
10523 void GameActions_EM_Main()
10524 {
10525   byte effective_action[MAX_PLAYERS];
10526   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
10527   int i;
10528
10529   for (i = 0; i < MAX_PLAYERS; i++)
10530     effective_action[i] = stored_player[i].effective_action;
10531
10532   GameActions_EM(effective_action, warp_mode);
10533
10534   CheckLevelTime();
10535
10536   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
10537 }
10538
10539 void GameActions_RND()
10540 {
10541   int magic_wall_x = 0, magic_wall_y = 0;
10542   int i, x, y, element, graphic;
10543
10544   InitPlayfieldScanModeVars();
10545
10546 #if USE_ONE_MORE_CHANGE_PER_FRAME
10547   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10548   {
10549     SCAN_PLAYFIELD(x, y)
10550     {
10551       ChangeCount[x][y] = 0;
10552       ChangeEvent[x][y] = -1;
10553     }
10554   }
10555 #endif
10556
10557   if (game.set_centered_player)
10558   {
10559     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
10560
10561     /* switching to "all players" only possible if all players fit to screen */
10562     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
10563     {
10564       game.centered_player_nr_next = game.centered_player_nr;
10565       game.set_centered_player = FALSE;
10566     }
10567
10568     /* do not switch focus to non-existing (or non-active) player */
10569     if (game.centered_player_nr_next >= 0 &&
10570         !stored_player[game.centered_player_nr_next].active)
10571     {
10572       game.centered_player_nr_next = game.centered_player_nr;
10573       game.set_centered_player = FALSE;
10574     }
10575   }
10576
10577   if (game.set_centered_player &&
10578       ScreenMovPos == 0)        /* screen currently aligned at tile position */
10579   {
10580     int sx, sy;
10581
10582     if (game.centered_player_nr_next == -1)
10583     {
10584       setScreenCenteredToAllPlayers(&sx, &sy);
10585     }
10586     else
10587     {
10588       sx = stored_player[game.centered_player_nr_next].jx;
10589       sy = stored_player[game.centered_player_nr_next].jy;
10590     }
10591
10592     game.centered_player_nr = game.centered_player_nr_next;
10593     game.set_centered_player = FALSE;
10594
10595     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
10596     DrawGameDoorValues();
10597   }
10598
10599   for (i = 0; i < MAX_PLAYERS; i++)
10600   {
10601     int actual_player_action = stored_player[i].effective_action;
10602
10603 #if 1
10604     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
10605        - rnd_equinox_tetrachloride 048
10606        - rnd_equinox_tetrachloride_ii 096
10607        - rnd_emanuel_schmieg 002
10608        - doctor_sloan_ww 001, 020
10609     */
10610     if (stored_player[i].MovPos == 0)
10611       CheckGravityMovement(&stored_player[i]);
10612 #endif
10613
10614     /* overwrite programmed action with tape action */
10615     if (stored_player[i].programmed_action)
10616       actual_player_action = stored_player[i].programmed_action;
10617
10618     PlayerActions(&stored_player[i], actual_player_action);
10619
10620     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
10621   }
10622
10623   ScrollScreen(NULL, SCROLL_GO_ON);
10624
10625   /* for backwards compatibility, the following code emulates a fixed bug that
10626      occured when pushing elements (causing elements that just made their last
10627      pushing step to already (if possible) make their first falling step in the
10628      same game frame, which is bad); this code is also needed to use the famous
10629      "spring push bug" which is used in older levels and might be wanted to be
10630      used also in newer levels, but in this case the buggy pushing code is only
10631      affecting the "spring" element and no other elements */
10632
10633   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
10634   {
10635     for (i = 0; i < MAX_PLAYERS; i++)
10636     {
10637       struct PlayerInfo *player = &stored_player[i];
10638       int x = player->jx;
10639       int y = player->jy;
10640
10641       if (player->active && player->is_pushing && player->is_moving &&
10642           IS_MOVING(x, y) &&
10643           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
10644            Feld[x][y] == EL_SPRING))
10645       {
10646         ContinueMoving(x, y);
10647
10648         /* continue moving after pushing (this is actually a bug) */
10649         if (!IS_MOVING(x, y))
10650           Stop[x][y] = FALSE;
10651       }
10652     }
10653   }
10654
10655 #if 0
10656   debug_print_timestamp(0, "start main loop profiling");
10657 #endif
10658
10659   SCAN_PLAYFIELD(x, y)
10660   {
10661     ChangeCount[x][y] = 0;
10662     ChangeEvent[x][y] = -1;
10663
10664     /* this must be handled before main playfield loop */
10665     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
10666     {
10667       MovDelay[x][y]--;
10668       if (MovDelay[x][y] <= 0)
10669         RemoveField(x, y);
10670     }
10671
10672 #if USE_NEW_SNAP_DELAY
10673     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
10674     {
10675       MovDelay[x][y]--;
10676       if (MovDelay[x][y] <= 0)
10677       {
10678         RemoveField(x, y);
10679         DrawLevelField(x, y);
10680
10681         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
10682       }
10683     }
10684 #endif
10685
10686 #if DEBUG
10687     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
10688     {
10689       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
10690       printf("GameActions(): This should never happen!\n");
10691
10692       ChangePage[x][y] = -1;
10693     }
10694 #endif
10695
10696     Stop[x][y] = FALSE;
10697     if (WasJustMoving[x][y] > 0)
10698       WasJustMoving[x][y]--;
10699     if (WasJustFalling[x][y] > 0)
10700       WasJustFalling[x][y]--;
10701     if (CheckCollision[x][y] > 0)
10702       CheckCollision[x][y]--;
10703     if (CheckImpact[x][y] > 0)
10704       CheckImpact[x][y]--;
10705
10706     GfxFrame[x][y]++;
10707
10708     /* reset finished pushing action (not done in ContinueMoving() to allow
10709        continuous pushing animation for elements with zero push delay) */
10710     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
10711     {
10712       ResetGfxAnimation(x, y);
10713       DrawLevelField(x, y);
10714     }
10715
10716 #if DEBUG
10717     if (IS_BLOCKED(x, y))
10718     {
10719       int oldx, oldy;
10720
10721       Blocked2Moving(x, y, &oldx, &oldy);
10722       if (!IS_MOVING(oldx, oldy))
10723       {
10724         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
10725         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
10726         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
10727         printf("GameActions(): This should never happen!\n");
10728       }
10729     }
10730 #endif
10731   }
10732
10733 #if 0
10734   debug_print_timestamp(0, "- time for pre-main loop:");
10735 #endif
10736
10737 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
10738   SCAN_PLAYFIELD(x, y)
10739   {
10740     element = Feld[x][y];
10741     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10742
10743 #if 1
10744     {
10745 #if 1
10746       int element2 = element;
10747       int graphic2 = graphic;
10748 #else
10749       int element2 = Feld[x][y];
10750       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
10751 #endif
10752       int last_gfx_frame = GfxFrame[x][y];
10753
10754       if (graphic_info[graphic2].anim_global_sync)
10755         GfxFrame[x][y] = FrameCounter;
10756       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
10757         GfxFrame[x][y] = CustomValue[x][y];
10758       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
10759         GfxFrame[x][y] = element_info[element2].collect_score;
10760       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
10761         GfxFrame[x][y] = ChangeDelay[x][y];
10762
10763       if (redraw && GfxFrame[x][y] != last_gfx_frame)
10764         DrawLevelGraphicAnimation(x, y, graphic2);
10765     }
10766 #else
10767     ResetGfxFrame(x, y, TRUE);
10768 #endif
10769
10770 #if 1
10771     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10772         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10773       ResetRandomAnimationValue(x, y);
10774 #endif
10775
10776 #if 1
10777     SetRandomAnimationValue(x, y);
10778 #endif
10779
10780 #if 1
10781     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10782 #endif
10783   }
10784 #endif  // -------------------- !!! TEST ONLY !!! --------------------
10785
10786 #if 0
10787   debug_print_timestamp(0, "- time for TEST loop:     -->");
10788 #endif
10789
10790   SCAN_PLAYFIELD(x, y)
10791   {
10792     element = Feld[x][y];
10793     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10794
10795     ResetGfxFrame(x, y, TRUE);
10796
10797     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10798         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10799       ResetRandomAnimationValue(x, y);
10800
10801     SetRandomAnimationValue(x, y);
10802
10803     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10804
10805     if (IS_INACTIVE(element))
10806     {
10807       if (IS_ANIMATED(graphic))
10808         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10809
10810       continue;
10811     }
10812
10813     /* this may take place after moving, so 'element' may have changed */
10814     if (IS_CHANGING(x, y) &&
10815         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10816     {
10817       int page = element_info[element].event_page_nr[CE_DELAY];
10818
10819 #if 1
10820       HandleElementChange(x, y, page);
10821 #else
10822       if (CAN_CHANGE(element))
10823         HandleElementChange(x, y, page);
10824
10825       if (HAS_ACTION(element))
10826         ExecuteCustomElementAction(x, y, element, page);
10827 #endif
10828
10829       element = Feld[x][y];
10830       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10831     }
10832
10833 #if 0   // ---------------------------------------------------------------------
10834
10835     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10836     {
10837       StartMoving(x, y);
10838
10839       element = Feld[x][y];
10840       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10841
10842       if (IS_ANIMATED(graphic) &&
10843           !IS_MOVING(x, y) &&
10844           !Stop[x][y])
10845         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10846
10847       if (IS_GEM(element) || element == EL_SP_INFOTRON)
10848         DrawTwinkleOnField(x, y);
10849     }
10850     else if (IS_MOVING(x, y))
10851       ContinueMoving(x, y);
10852     else
10853     {
10854       switch (element)
10855       {
10856         case EL_ACID:
10857         case EL_EXIT_OPEN:
10858         case EL_EM_EXIT_OPEN:
10859         case EL_SP_EXIT_OPEN:
10860         case EL_STEEL_EXIT_OPEN:
10861         case EL_EM_STEEL_EXIT_OPEN:
10862         case EL_SP_TERMINAL:
10863         case EL_SP_TERMINAL_ACTIVE:
10864         case EL_EXTRA_TIME:
10865         case EL_SHIELD_NORMAL:
10866         case EL_SHIELD_DEADLY:
10867           if (IS_ANIMATED(graphic))
10868             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10869           break;
10870
10871         case EL_DYNAMITE_ACTIVE:
10872         case EL_EM_DYNAMITE_ACTIVE:
10873         case EL_DYNABOMB_PLAYER_1_ACTIVE:
10874         case EL_DYNABOMB_PLAYER_2_ACTIVE:
10875         case EL_DYNABOMB_PLAYER_3_ACTIVE:
10876         case EL_DYNABOMB_PLAYER_4_ACTIVE:
10877         case EL_SP_DISK_RED_ACTIVE:
10878           CheckDynamite(x, y);
10879           break;
10880
10881         case EL_AMOEBA_GROWING:
10882           AmoebeWaechst(x, y);
10883           break;
10884
10885         case EL_AMOEBA_SHRINKING:
10886           AmoebaDisappearing(x, y);
10887           break;
10888
10889 #if !USE_NEW_AMOEBA_CODE
10890         case EL_AMOEBA_WET:
10891         case EL_AMOEBA_DRY:
10892         case EL_AMOEBA_FULL:
10893         case EL_BD_AMOEBA:
10894         case EL_EMC_DRIPPER:
10895           AmoebeAbleger(x, y);
10896           break;
10897 #endif
10898
10899         case EL_GAME_OF_LIFE:
10900         case EL_BIOMAZE:
10901           Life(x, y);
10902           break;
10903
10904         case EL_EXIT_CLOSED:
10905           CheckExit(x, y);
10906           break;
10907
10908         case EL_EM_EXIT_CLOSED:
10909           CheckExitEM(x, y);
10910           break;
10911
10912         case EL_STEEL_EXIT_CLOSED:
10913           CheckExitSteel(x, y);
10914           break;
10915
10916         case EL_EM_STEEL_EXIT_CLOSED:
10917           CheckExitSteelEM(x, y);
10918           break;
10919
10920         case EL_SP_EXIT_CLOSED:
10921           CheckExitSP(x, y);
10922           break;
10923
10924         case EL_EXPANDABLE_WALL_GROWING:
10925         case EL_EXPANDABLE_STEELWALL_GROWING:
10926           MauerWaechst(x, y);
10927           break;
10928
10929         case EL_EXPANDABLE_WALL:
10930         case EL_EXPANDABLE_WALL_HORIZONTAL:
10931         case EL_EXPANDABLE_WALL_VERTICAL:
10932         case EL_EXPANDABLE_WALL_ANY:
10933         case EL_BD_EXPANDABLE_WALL:
10934           MauerAbleger(x, y);
10935           break;
10936
10937         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
10938         case EL_EXPANDABLE_STEELWALL_VERTICAL:
10939         case EL_EXPANDABLE_STEELWALL_ANY:
10940           MauerAblegerStahl(x, y);
10941           break;
10942
10943         case EL_FLAMES:
10944           CheckForDragon(x, y);
10945           break;
10946
10947         case EL_EXPLOSION:
10948           break;
10949
10950         case EL_ELEMENT_SNAPPING:
10951         case EL_DIAGONAL_SHRINKING:
10952         case EL_DIAGONAL_GROWING:
10953         {
10954           graphic =
10955             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10956
10957           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10958           break;
10959         }
10960
10961         default:
10962           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10963             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10964           break;
10965       }
10966     }
10967
10968 #else   // ---------------------------------------------------------------------
10969
10970     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10971     {
10972       StartMoving(x, y);
10973
10974       element = Feld[x][y];
10975       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10976
10977       if (IS_ANIMATED(graphic) &&
10978           !IS_MOVING(x, y) &&
10979           !Stop[x][y])
10980         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10981
10982       if (IS_GEM(element) || element == EL_SP_INFOTRON)
10983         DrawTwinkleOnField(x, y);
10984     }
10985     else if ((element == EL_ACID ||
10986               element == EL_EXIT_OPEN ||
10987               element == EL_EM_EXIT_OPEN ||
10988               element == EL_SP_EXIT_OPEN ||
10989               element == EL_STEEL_EXIT_OPEN ||
10990               element == EL_EM_STEEL_EXIT_OPEN ||
10991               element == EL_SP_TERMINAL ||
10992               element == EL_SP_TERMINAL_ACTIVE ||
10993               element == EL_EXTRA_TIME ||
10994               element == EL_SHIELD_NORMAL ||
10995               element == EL_SHIELD_DEADLY) &&
10996              IS_ANIMATED(graphic))
10997       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10998     else if (IS_MOVING(x, y))
10999       ContinueMoving(x, y);
11000     else if (IS_ACTIVE_BOMB(element))
11001       CheckDynamite(x, y);
11002     else if (element == EL_AMOEBA_GROWING)
11003       AmoebeWaechst(x, y);
11004     else if (element == EL_AMOEBA_SHRINKING)
11005       AmoebaDisappearing(x, y);
11006
11007 #if !USE_NEW_AMOEBA_CODE
11008     else if (IS_AMOEBALIVE(element))
11009       AmoebeAbleger(x, y);
11010 #endif
11011
11012     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11013       Life(x, y);
11014     else if (element == EL_EXIT_CLOSED)
11015       CheckExit(x, y);
11016     else if (element == EL_EM_EXIT_CLOSED)
11017       CheckExitEM(x, y);
11018     else if (element == EL_STEEL_EXIT_CLOSED)
11019       CheckExitSteel(x, y);
11020     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11021       CheckExitSteelEM(x, y);
11022     else if (element == EL_SP_EXIT_CLOSED)
11023       CheckExitSP(x, y);
11024     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11025              element == EL_EXPANDABLE_STEELWALL_GROWING)
11026       MauerWaechst(x, y);
11027     else if (element == EL_EXPANDABLE_WALL ||
11028              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11029              element == EL_EXPANDABLE_WALL_VERTICAL ||
11030              element == EL_EXPANDABLE_WALL_ANY ||
11031              element == EL_BD_EXPANDABLE_WALL)
11032       MauerAbleger(x, y);
11033     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11034              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11035              element == EL_EXPANDABLE_STEELWALL_ANY)
11036       MauerAblegerStahl(x, y);
11037     else if (element == EL_FLAMES)
11038       CheckForDragon(x, y);
11039     else if (element == EL_EXPLOSION)
11040       ; /* drawing of correct explosion animation is handled separately */
11041     else if (element == EL_ELEMENT_SNAPPING ||
11042              element == EL_DIAGONAL_SHRINKING ||
11043              element == EL_DIAGONAL_GROWING)
11044     {
11045       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11046
11047       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11048     }
11049     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11050       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11051
11052 #endif  // ---------------------------------------------------------------------
11053
11054     if (IS_BELT_ACTIVE(element))
11055       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11056
11057     if (game.magic_wall_active)
11058     {
11059       int jx = local_player->jx, jy = local_player->jy;
11060
11061       /* play the element sound at the position nearest to the player */
11062       if ((element == EL_MAGIC_WALL_FULL ||
11063            element == EL_MAGIC_WALL_ACTIVE ||
11064            element == EL_MAGIC_WALL_EMPTYING ||
11065            element == EL_BD_MAGIC_WALL_FULL ||
11066            element == EL_BD_MAGIC_WALL_ACTIVE ||
11067            element == EL_BD_MAGIC_WALL_EMPTYING ||
11068            element == EL_DC_MAGIC_WALL_FULL ||
11069            element == EL_DC_MAGIC_WALL_ACTIVE ||
11070            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11071           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11072       {
11073         magic_wall_x = x;
11074         magic_wall_y = y;
11075       }
11076     }
11077   }
11078
11079 #if 0
11080   debug_print_timestamp(0, "- time for MAIN loop:     -->");
11081 #endif
11082
11083 #if USE_NEW_AMOEBA_CODE
11084   /* new experimental amoeba growth stuff */
11085   if (!(FrameCounter % 8))
11086   {
11087     static unsigned long random = 1684108901;
11088
11089     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11090     {
11091       x = RND(lev_fieldx);
11092       y = RND(lev_fieldy);
11093       element = Feld[x][y];
11094
11095       if (!IS_PLAYER(x,y) &&
11096           (element == EL_EMPTY ||
11097            CAN_GROW_INTO(element) ||
11098            element == EL_QUICKSAND_EMPTY ||
11099            element == EL_QUICKSAND_FAST_EMPTY ||
11100            element == EL_ACID_SPLASH_LEFT ||
11101            element == EL_ACID_SPLASH_RIGHT))
11102       {
11103         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11104             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11105             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11106             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11107           Feld[x][y] = EL_AMOEBA_DROP;
11108       }
11109
11110       random = random * 129 + 1;
11111     }
11112   }
11113 #endif
11114
11115 #if 0
11116   if (game.explosions_delayed)
11117 #endif
11118   {
11119     game.explosions_delayed = FALSE;
11120
11121     SCAN_PLAYFIELD(x, y)
11122     {
11123       element = Feld[x][y];
11124
11125       if (ExplodeField[x][y])
11126         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11127       else if (element == EL_EXPLOSION)
11128         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11129
11130       ExplodeField[x][y] = EX_TYPE_NONE;
11131     }
11132
11133     game.explosions_delayed = TRUE;
11134   }
11135
11136   if (game.magic_wall_active)
11137   {
11138     if (!(game.magic_wall_time_left % 4))
11139     {
11140       int element = Feld[magic_wall_x][magic_wall_y];
11141
11142       if (element == EL_BD_MAGIC_WALL_FULL ||
11143           element == EL_BD_MAGIC_WALL_ACTIVE ||
11144           element == EL_BD_MAGIC_WALL_EMPTYING)
11145         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11146       else if (element == EL_DC_MAGIC_WALL_FULL ||
11147                element == EL_DC_MAGIC_WALL_ACTIVE ||
11148                element == EL_DC_MAGIC_WALL_EMPTYING)
11149         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11150       else
11151         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11152     }
11153
11154     if (game.magic_wall_time_left > 0)
11155     {
11156       game.magic_wall_time_left--;
11157       if (!game.magic_wall_time_left)
11158       {
11159         SCAN_PLAYFIELD(x, y)
11160         {
11161           element = Feld[x][y];
11162
11163           if (element == EL_MAGIC_WALL_ACTIVE ||
11164               element == EL_MAGIC_WALL_FULL)
11165           {
11166             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11167             DrawLevelField(x, y);
11168           }
11169           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11170                    element == EL_BD_MAGIC_WALL_FULL)
11171           {
11172             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11173             DrawLevelField(x, y);
11174           }
11175           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11176                    element == EL_DC_MAGIC_WALL_FULL)
11177           {
11178             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11179             DrawLevelField(x, y);
11180           }
11181         }
11182
11183         game.magic_wall_active = FALSE;
11184       }
11185     }
11186   }
11187
11188   if (game.light_time_left > 0)
11189   {
11190     game.light_time_left--;
11191
11192     if (game.light_time_left == 0)
11193       RedrawAllLightSwitchesAndInvisibleElements();
11194   }
11195
11196   if (game.timegate_time_left > 0)
11197   {
11198     game.timegate_time_left--;
11199
11200     if (game.timegate_time_left == 0)
11201       CloseAllOpenTimegates();
11202   }
11203
11204   if (game.lenses_time_left > 0)
11205   {
11206     game.lenses_time_left--;
11207
11208     if (game.lenses_time_left == 0)
11209       RedrawAllInvisibleElementsForLenses();
11210   }
11211
11212   if (game.magnify_time_left > 0)
11213   {
11214     game.magnify_time_left--;
11215
11216     if (game.magnify_time_left == 0)
11217       RedrawAllInvisibleElementsForMagnifier();
11218   }
11219
11220   for (i = 0; i < MAX_PLAYERS; i++)
11221   {
11222     struct PlayerInfo *player = &stored_player[i];
11223
11224     if (SHIELD_ON(player))
11225     {
11226       if (player->shield_deadly_time_left)
11227         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11228       else if (player->shield_normal_time_left)
11229         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11230     }
11231   }
11232
11233   CheckLevelTime();
11234
11235   DrawAllPlayers();
11236   PlayAllPlayersSound();
11237
11238   if (options.debug)                    /* calculate frames per second */
11239   {
11240     static unsigned long fps_counter = 0;
11241     static int fps_frames = 0;
11242     unsigned long fps_delay_ms = Counter() - fps_counter;
11243
11244     fps_frames++;
11245
11246     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11247     {
11248       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11249
11250       fps_frames = 0;
11251       fps_counter = Counter();
11252     }
11253
11254     redraw_mask |= REDRAW_FPS;
11255   }
11256
11257   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11258
11259   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11260   {
11261     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11262
11263     local_player->show_envelope = 0;
11264   }
11265
11266 #if 0
11267   debug_print_timestamp(0, "stop main loop profiling ");
11268   printf("----------------------------------------------------------\n");
11269 #endif
11270
11271   /* use random number generator in every frame to make it less predictable */
11272   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11273     RND(1);
11274 }
11275
11276 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11277 {
11278   int min_x = x, min_y = y, max_x = x, max_y = y;
11279   int i;
11280
11281   for (i = 0; i < MAX_PLAYERS; i++)
11282   {
11283     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11284
11285     if (!stored_player[i].active || &stored_player[i] == player)
11286       continue;
11287
11288     min_x = MIN(min_x, jx);
11289     min_y = MIN(min_y, jy);
11290     max_x = MAX(max_x, jx);
11291     max_y = MAX(max_y, jy);
11292   }
11293
11294   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11295 }
11296
11297 static boolean AllPlayersInVisibleScreen()
11298 {
11299   int i;
11300
11301   for (i = 0; i < MAX_PLAYERS; i++)
11302   {
11303     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11304
11305     if (!stored_player[i].active)
11306       continue;
11307
11308     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11309       return FALSE;
11310   }
11311
11312   return TRUE;
11313 }
11314
11315 void ScrollLevel(int dx, int dy)
11316 {
11317 #if 1
11318   static Bitmap *bitmap_db_field2 = NULL;
11319   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11320   int x, y;
11321 #else
11322   int i, x, y;
11323 #endif
11324
11325 #if 0
11326   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11327   /* only horizontal XOR vertical scroll direction allowed */
11328   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11329     return;
11330 #endif
11331
11332 #if 1
11333   if (bitmap_db_field2 == NULL)
11334     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11335
11336   /* needed when blitting directly to same bitmap -- should not be needed with
11337      recent SDL libraries, but apparently does not work in 1.2.11 directly */
11338   BlitBitmap(drawto_field, bitmap_db_field2,
11339              FX + TILEX * (dx == -1) - softscroll_offset,
11340              FY + TILEY * (dy == -1) - softscroll_offset,
11341              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11342              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11343              FX + TILEX * (dx == 1) - softscroll_offset,
11344              FY + TILEY * (dy == 1) - softscroll_offset);
11345   BlitBitmap(bitmap_db_field2, drawto_field,
11346              FX + TILEX * (dx == 1) - softscroll_offset,
11347              FY + TILEY * (dy == 1) - softscroll_offset,
11348              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11349              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11350              FX + TILEX * (dx == 1) - softscroll_offset,
11351              FY + TILEY * (dy == 1) - softscroll_offset);
11352
11353 #else
11354
11355 #if 1
11356   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11357   int xsize = (BX2 - BX1 + 1);
11358   int ysize = (BY2 - BY1 + 1);
11359   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11360   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11361   int step  = (start < end ? +1 : -1);
11362
11363   for (i = start; i != end; i += step)
11364   {
11365     BlitBitmap(drawto_field, drawto_field,
11366                FX + TILEX * (dx != 0 ? i + step : 0),
11367                FY + TILEY * (dy != 0 ? i + step : 0),
11368                TILEX * (dx != 0 ? 1 : xsize),
11369                TILEY * (dy != 0 ? 1 : ysize),
11370                FX + TILEX * (dx != 0 ? i : 0),
11371                FY + TILEY * (dy != 0 ? i : 0));
11372   }
11373
11374 #else
11375
11376   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11377
11378   BlitBitmap(drawto_field, drawto_field,
11379              FX + TILEX * (dx == -1) - softscroll_offset,
11380              FY + TILEY * (dy == -1) - softscroll_offset,
11381              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11382              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11383              FX + TILEX * (dx == 1) - softscroll_offset,
11384              FY + TILEY * (dy == 1) - softscroll_offset);
11385 #endif
11386 #endif
11387
11388   if (dx != 0)
11389   {
11390     x = (dx == 1 ? BX1 : BX2);
11391     for (y = BY1; y <= BY2; y++)
11392       DrawScreenField(x, y);
11393   }
11394
11395   if (dy != 0)
11396   {
11397     y = (dy == 1 ? BY1 : BY2);
11398     for (x = BX1; x <= BX2; x++)
11399       DrawScreenField(x, y);
11400   }
11401
11402   redraw_mask |= REDRAW_FIELD;
11403 }
11404
11405 static boolean canFallDown(struct PlayerInfo *player)
11406 {
11407   int jx = player->jx, jy = player->jy;
11408
11409   return (IN_LEV_FIELD(jx, jy + 1) &&
11410           (IS_FREE(jx, jy + 1) ||
11411            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11412           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11413           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11414 }
11415
11416 static boolean canPassField(int x, int y, int move_dir)
11417 {
11418   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11419   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11420   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11421   int nextx = x + dx;
11422   int nexty = y + dy;
11423   int element = Feld[x][y];
11424
11425   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11426           !CAN_MOVE(element) &&
11427           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11428           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11429           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11430 }
11431
11432 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11433 {
11434   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11435   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11436   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11437   int newx = x + dx;
11438   int newy = y + dy;
11439
11440   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11441           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11442           (IS_DIGGABLE(Feld[newx][newy]) ||
11443            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11444            canPassField(newx, newy, move_dir)));
11445 }
11446
11447 static void CheckGravityMovement(struct PlayerInfo *player)
11448 {
11449 #if USE_PLAYER_GRAVITY
11450   if (player->gravity && !player->programmed_action)
11451 #else
11452   if (game.gravity && !player->programmed_action)
11453 #endif
11454   {
11455     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11456     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11457     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11458     int jx = player->jx, jy = player->jy;
11459     boolean player_is_moving_to_valid_field =
11460       (!player_is_snapping &&
11461        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11462         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11463     boolean player_can_fall_down = canFallDown(player);
11464
11465     if (player_can_fall_down &&
11466         !player_is_moving_to_valid_field)
11467       player->programmed_action = MV_DOWN;
11468   }
11469 }
11470
11471 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11472 {
11473   return CheckGravityMovement(player);
11474
11475 #if USE_PLAYER_GRAVITY
11476   if (player->gravity && !player->programmed_action)
11477 #else
11478   if (game.gravity && !player->programmed_action)
11479 #endif
11480   {
11481     int jx = player->jx, jy = player->jy;
11482     boolean field_under_player_is_free =
11483       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11484     boolean player_is_standing_on_valid_field =
11485       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11486        (IS_WALKABLE(Feld[jx][jy]) &&
11487         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11488
11489     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11490       player->programmed_action = MV_DOWN;
11491   }
11492 }
11493
11494 /*
11495   MovePlayerOneStep()
11496   -----------------------------------------------------------------------------
11497   dx, dy:               direction (non-diagonal) to try to move the player to
11498   real_dx, real_dy:     direction as read from input device (can be diagonal)
11499 */
11500
11501 boolean MovePlayerOneStep(struct PlayerInfo *player,
11502                           int dx, int dy, int real_dx, int real_dy)
11503 {
11504   int jx = player->jx, jy = player->jy;
11505   int new_jx = jx + dx, new_jy = jy + dy;
11506 #if !USE_FIXED_DONT_RUN_INTO
11507   int element;
11508 #endif
11509   int can_move;
11510   boolean player_can_move = !player->cannot_move;
11511
11512   if (!player->active || (!dx && !dy))
11513     return MP_NO_ACTION;
11514
11515   player->MovDir = (dx < 0 ? MV_LEFT :
11516                     dx > 0 ? MV_RIGHT :
11517                     dy < 0 ? MV_UP :
11518                     dy > 0 ? MV_DOWN :  MV_NONE);
11519
11520   if (!IN_LEV_FIELD(new_jx, new_jy))
11521     return MP_NO_ACTION;
11522
11523   if (!player_can_move)
11524   {
11525     if (player->MovPos == 0)
11526     {
11527       player->is_moving = FALSE;
11528       player->is_digging = FALSE;
11529       player->is_collecting = FALSE;
11530       player->is_snapping = FALSE;
11531       player->is_pushing = FALSE;
11532     }
11533   }
11534
11535 #if 1
11536   if (!options.network && game.centered_player_nr == -1 &&
11537       !AllPlayersInSight(player, new_jx, new_jy))
11538     return MP_NO_ACTION;
11539 #else
11540   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
11541     return MP_NO_ACTION;
11542 #endif
11543
11544 #if !USE_FIXED_DONT_RUN_INTO
11545   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
11546
11547   /* (moved to DigField()) */
11548   if (player_can_move && DONT_RUN_INTO(element))
11549   {
11550     if (element == EL_ACID && dx == 0 && dy == 1)
11551     {
11552       SplashAcid(new_jx, new_jy);
11553       Feld[jx][jy] = EL_PLAYER_1;
11554       InitMovingField(jx, jy, MV_DOWN);
11555       Store[jx][jy] = EL_ACID;
11556       ContinueMoving(jx, jy);
11557       BuryPlayer(player);
11558     }
11559     else
11560       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11561
11562     return MP_MOVING;
11563   }
11564 #endif
11565
11566   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11567   if (can_move != MP_MOVING)
11568     return can_move;
11569
11570   /* check if DigField() has caused relocation of the player */
11571   if (player->jx != jx || player->jy != jy)
11572     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11573
11574   StorePlayer[jx][jy] = 0;
11575   player->last_jx = jx;
11576   player->last_jy = jy;
11577   player->jx = new_jx;
11578   player->jy = new_jy;
11579   StorePlayer[new_jx][new_jy] = player->element_nr;
11580
11581   if (player->move_delay_value_next != -1)
11582   {
11583     player->move_delay_value = player->move_delay_value_next;
11584     player->move_delay_value_next = -1;
11585   }
11586
11587   player->MovPos =
11588     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11589
11590   player->step_counter++;
11591
11592   PlayerVisit[jx][jy] = FrameCounter;
11593
11594 #if USE_UFAST_PLAYER_EXIT_BUGFIX
11595   player->is_moving = TRUE;
11596 #endif
11597
11598 #if 1
11599   /* should better be called in MovePlayer(), but this breaks some tapes */
11600   ScrollPlayer(player, SCROLL_INIT);
11601 #endif
11602
11603   return MP_MOVING;
11604 }
11605
11606 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
11607 {
11608   int jx = player->jx, jy = player->jy;
11609   int old_jx = jx, old_jy = jy;
11610   int moved = MP_NO_ACTION;
11611
11612   if (!player->active)
11613     return FALSE;
11614
11615   if (!dx && !dy)
11616   {
11617     if (player->MovPos == 0)
11618     {
11619       player->is_moving = FALSE;
11620       player->is_digging = FALSE;
11621       player->is_collecting = FALSE;
11622       player->is_snapping = FALSE;
11623       player->is_pushing = FALSE;
11624     }
11625
11626     return FALSE;
11627   }
11628
11629   if (player->move_delay > 0)
11630     return FALSE;
11631
11632   player->move_delay = -1;              /* set to "uninitialized" value */
11633
11634   /* store if player is automatically moved to next field */
11635   player->is_auto_moving = (player->programmed_action != MV_NONE);
11636
11637   /* remove the last programmed player action */
11638   player->programmed_action = 0;
11639
11640   if (player->MovPos)
11641   {
11642     /* should only happen if pre-1.2 tape recordings are played */
11643     /* this is only for backward compatibility */
11644
11645     int original_move_delay_value = player->move_delay_value;
11646
11647 #if DEBUG
11648     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
11649            tape.counter);
11650 #endif
11651
11652     /* scroll remaining steps with finest movement resolution */
11653     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
11654
11655     while (player->MovPos)
11656     {
11657       ScrollPlayer(player, SCROLL_GO_ON);
11658       ScrollScreen(NULL, SCROLL_GO_ON);
11659
11660       AdvanceFrameAndPlayerCounters(player->index_nr);
11661
11662       DrawAllPlayers();
11663       BackToFront();
11664     }
11665
11666     player->move_delay_value = original_move_delay_value;
11667   }
11668
11669   player->is_active = FALSE;
11670
11671   if (player->last_move_dir & MV_HORIZONTAL)
11672   {
11673     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
11674       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
11675   }
11676   else
11677   {
11678     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
11679       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
11680   }
11681
11682 #if USE_FIXED_BORDER_RUNNING_GFX
11683   if (!moved && !player->is_active)
11684   {
11685     player->is_moving = FALSE;
11686     player->is_digging = FALSE;
11687     player->is_collecting = FALSE;
11688     player->is_snapping = FALSE;
11689     player->is_pushing = FALSE;
11690   }
11691 #endif
11692
11693   jx = player->jx;
11694   jy = player->jy;
11695
11696 #if 1
11697   if (moved & MP_MOVING && !ScreenMovPos &&
11698       (player->index_nr == game.centered_player_nr ||
11699        game.centered_player_nr == -1))
11700 #else
11701   if (moved & MP_MOVING && !ScreenMovPos &&
11702       (player == local_player || !options.network))
11703 #endif
11704   {
11705     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
11706     int offset = (setup.scroll_delay ? 3 : 0);
11707
11708     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11709     {
11710       /* actual player has left the screen -- scroll in that direction */
11711       if (jx != old_jx)         /* player has moved horizontally */
11712         scroll_x += (jx - old_jx);
11713       else                      /* player has moved vertically */
11714         scroll_y += (jy - old_jy);
11715     }
11716     else
11717     {
11718       if (jx != old_jx)         /* player has moved horizontally */
11719       {
11720         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
11721             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
11722           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
11723
11724         /* don't scroll over playfield boundaries */
11725         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
11726           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
11727
11728         /* don't scroll more than one field at a time */
11729         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
11730
11731         /* don't scroll against the player's moving direction */
11732         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
11733             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
11734           scroll_x = old_scroll_x;
11735       }
11736       else                      /* player has moved vertically */
11737       {
11738         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
11739             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
11740           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
11741
11742         /* don't scroll over playfield boundaries */
11743         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
11744           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
11745
11746         /* don't scroll more than one field at a time */
11747         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
11748
11749         /* don't scroll against the player's moving direction */
11750         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
11751             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
11752           scroll_y = old_scroll_y;
11753       }
11754     }
11755
11756     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
11757     {
11758 #if 1
11759       if (!options.network && game.centered_player_nr == -1 &&
11760           !AllPlayersInVisibleScreen())
11761       {
11762         scroll_x = old_scroll_x;
11763         scroll_y = old_scroll_y;
11764       }
11765       else
11766 #else
11767       if (!options.network && !AllPlayersInVisibleScreen())
11768       {
11769         scroll_x = old_scroll_x;
11770         scroll_y = old_scroll_y;
11771       }
11772       else
11773 #endif
11774       {
11775         ScrollScreen(player, SCROLL_INIT);
11776         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
11777       }
11778     }
11779   }
11780
11781   player->StepFrame = 0;
11782
11783   if (moved & MP_MOVING)
11784   {
11785     if (old_jx != jx && old_jy == jy)
11786       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
11787     else if (old_jx == jx && old_jy != jy)
11788       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
11789
11790     DrawLevelField(jx, jy);     /* for "crumbled sand" */
11791
11792     player->last_move_dir = player->MovDir;
11793     player->is_moving = TRUE;
11794     player->is_snapping = FALSE;
11795     player->is_switching = FALSE;
11796     player->is_dropping = FALSE;
11797     player->is_dropping_pressed = FALSE;
11798     player->drop_pressed_delay = 0;
11799
11800 #if 0
11801     /* should better be called here than above, but this breaks some tapes */
11802     ScrollPlayer(player, SCROLL_INIT);
11803 #endif
11804   }
11805   else
11806   {
11807     CheckGravityMovementWhenNotMoving(player);
11808
11809     player->is_moving = FALSE;
11810
11811     /* at this point, the player is allowed to move, but cannot move right now
11812        (e.g. because of something blocking the way) -- ensure that the player
11813        is also allowed to move in the next frame (in old versions before 3.1.1,
11814        the player was forced to wait again for eight frames before next try) */
11815
11816     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11817       player->move_delay = 0;   /* allow direct movement in the next frame */
11818   }
11819
11820   if (player->move_delay == -1)         /* not yet initialized by DigField() */
11821     player->move_delay = player->move_delay_value;
11822
11823   if (game.engine_version < VERSION_IDENT(3,0,7,0))
11824   {
11825     TestIfPlayerTouchesBadThing(jx, jy);
11826     TestIfPlayerTouchesCustomElement(jx, jy);
11827   }
11828
11829   if (!player->active)
11830     RemovePlayer(player);
11831
11832   return moved;
11833 }
11834
11835 void ScrollPlayer(struct PlayerInfo *player, int mode)
11836 {
11837   int jx = player->jx, jy = player->jy;
11838   int last_jx = player->last_jx, last_jy = player->last_jy;
11839   int move_stepsize = TILEX / player->move_delay_value;
11840
11841 #if USE_NEW_PLAYER_SPEED
11842   if (!player->active)
11843     return;
11844
11845   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
11846     return;
11847 #else
11848   if (!player->active || player->MovPos == 0)
11849     return;
11850 #endif
11851
11852   if (mode == SCROLL_INIT)
11853   {
11854     player->actual_frame_counter = FrameCounter;
11855     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11856
11857     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
11858         Feld[last_jx][last_jy] == EL_EMPTY)
11859     {
11860       int last_field_block_delay = 0;   /* start with no blocking at all */
11861       int block_delay_adjustment = player->block_delay_adjustment;
11862
11863       /* if player blocks last field, add delay for exactly one move */
11864       if (player->block_last_field)
11865       {
11866         last_field_block_delay += player->move_delay_value;
11867
11868         /* when blocking enabled, prevent moving up despite gravity */
11869 #if USE_PLAYER_GRAVITY
11870         if (player->gravity && player->MovDir == MV_UP)
11871           block_delay_adjustment = -1;
11872 #else
11873         if (game.gravity && player->MovDir == MV_UP)
11874           block_delay_adjustment = -1;
11875 #endif
11876       }
11877
11878       /* add block delay adjustment (also possible when not blocking) */
11879       last_field_block_delay += block_delay_adjustment;
11880
11881       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
11882       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
11883     }
11884
11885 #if USE_NEW_PLAYER_SPEED
11886     if (player->MovPos != 0)    /* player has not yet reached destination */
11887       return;
11888 #else
11889     return;
11890 #endif
11891   }
11892   else if (!FrameReached(&player->actual_frame_counter, 1))
11893     return;
11894
11895 #if USE_NEW_PLAYER_SPEED
11896   if (player->MovPos != 0)
11897   {
11898     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11899     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11900
11901     /* before DrawPlayer() to draw correct player graphic for this case */
11902     if (player->MovPos == 0)
11903       CheckGravityMovement(player);
11904   }
11905 #else
11906   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11907   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11908
11909   /* before DrawPlayer() to draw correct player graphic for this case */
11910   if (player->MovPos == 0)
11911     CheckGravityMovement(player);
11912 #endif
11913
11914   if (player->MovPos == 0)      /* player reached destination field */
11915   {
11916     if (player->move_delay_reset_counter > 0)
11917     {
11918       player->move_delay_reset_counter--;
11919
11920       if (player->move_delay_reset_counter == 0)
11921       {
11922         /* continue with normal speed after quickly moving through gate */
11923         HALVE_PLAYER_SPEED(player);
11924
11925         /* be able to make the next move without delay */
11926         player->move_delay = 0;
11927       }
11928     }
11929
11930     player->last_jx = jx;
11931     player->last_jy = jy;
11932
11933     if (Feld[jx][jy] == EL_EXIT_OPEN ||
11934         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
11935         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
11936         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
11937         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
11938         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
11939     {
11940       DrawPlayer(player);       /* needed here only to cleanup last field */
11941       RemovePlayer(player);
11942
11943       if (local_player->friends_still_needed == 0 ||
11944           IS_SP_ELEMENT(Feld[jx][jy]))
11945         PlayerWins(player);
11946     }
11947
11948     /* this breaks one level: "machine", level 000 */
11949     {
11950       int move_direction = player->MovDir;
11951       int enter_side = MV_DIR_OPPOSITE(move_direction);
11952       int leave_side = move_direction;
11953       int old_jx = last_jx;
11954       int old_jy = last_jy;
11955       int old_element = Feld[old_jx][old_jy];
11956       int new_element = Feld[jx][jy];
11957
11958       if (IS_CUSTOM_ELEMENT(old_element))
11959         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
11960                                    CE_LEFT_BY_PLAYER,
11961                                    player->index_bit, leave_side);
11962
11963       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
11964                                           CE_PLAYER_LEAVES_X,
11965                                           player->index_bit, leave_side);
11966
11967       if (IS_CUSTOM_ELEMENT(new_element))
11968         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
11969                                    player->index_bit, enter_side);
11970
11971       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
11972                                           CE_PLAYER_ENTERS_X,
11973                                           player->index_bit, enter_side);
11974
11975       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
11976                                         CE_MOVE_OF_X, move_direction);
11977     }
11978
11979     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11980     {
11981       TestIfPlayerTouchesBadThing(jx, jy);
11982       TestIfPlayerTouchesCustomElement(jx, jy);
11983
11984       /* needed because pushed element has not yet reached its destination,
11985          so it would trigger a change event at its previous field location */
11986       if (!player->is_pushing)
11987         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
11988
11989       if (!player->active)
11990         RemovePlayer(player);
11991     }
11992
11993     if (!local_player->LevelSolved && level.use_step_counter)
11994     {
11995       int i;
11996
11997       TimePlayed++;
11998
11999       if (TimeLeft > 0)
12000       {
12001         TimeLeft--;
12002
12003         if (TimeLeft <= 10 && setup.time_limit)
12004           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12005
12006         DrawGameValue_Time(TimeLeft);
12007
12008         if (!TimeLeft && setup.time_limit)
12009           for (i = 0; i < MAX_PLAYERS; i++)
12010             KillPlayer(&stored_player[i]);
12011       }
12012       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12013         DrawGameValue_Time(TimePlayed);
12014     }
12015
12016     if (tape.single_step && tape.recording && !tape.pausing &&
12017         !player->programmed_action)
12018       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12019   }
12020 }
12021
12022 void ScrollScreen(struct PlayerInfo *player, int mode)
12023 {
12024   static unsigned long screen_frame_counter = 0;
12025
12026   if (mode == SCROLL_INIT)
12027   {
12028     /* set scrolling step size according to actual player's moving speed */
12029     ScrollStepSize = TILEX / player->move_delay_value;
12030
12031     screen_frame_counter = FrameCounter;
12032     ScreenMovDir = player->MovDir;
12033     ScreenMovPos = player->MovPos;
12034     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12035     return;
12036   }
12037   else if (!FrameReached(&screen_frame_counter, 1))
12038     return;
12039
12040   if (ScreenMovPos)
12041   {
12042     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12043     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12044     redraw_mask |= REDRAW_FIELD;
12045   }
12046   else
12047     ScreenMovDir = MV_NONE;
12048 }
12049
12050 void TestIfPlayerTouchesCustomElement(int x, int y)
12051 {
12052   static int xy[4][2] =
12053   {
12054     { 0, -1 },
12055     { -1, 0 },
12056     { +1, 0 },
12057     { 0, +1 }
12058   };
12059   static int trigger_sides[4][2] =
12060   {
12061     /* center side       border side */
12062     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12063     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12064     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12065     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12066   };
12067   static int touch_dir[4] =
12068   {
12069     MV_LEFT | MV_RIGHT,
12070     MV_UP   | MV_DOWN,
12071     MV_UP   | MV_DOWN,
12072     MV_LEFT | MV_RIGHT
12073   };
12074   int center_element = Feld[x][y];      /* should always be non-moving! */
12075   int i;
12076
12077   for (i = 0; i < NUM_DIRECTIONS; i++)
12078   {
12079     int xx = x + xy[i][0];
12080     int yy = y + xy[i][1];
12081     int center_side = trigger_sides[i][0];
12082     int border_side = trigger_sides[i][1];
12083     int border_element;
12084
12085     if (!IN_LEV_FIELD(xx, yy))
12086       continue;
12087
12088     if (IS_PLAYER(x, y))
12089     {
12090       struct PlayerInfo *player = PLAYERINFO(x, y);
12091
12092       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12093         border_element = Feld[xx][yy];          /* may be moving! */
12094       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12095         border_element = Feld[xx][yy];
12096       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12097         border_element = MovingOrBlocked2Element(xx, yy);
12098       else
12099         continue;               /* center and border element do not touch */
12100
12101       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12102                                  player->index_bit, border_side);
12103       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12104                                           CE_PLAYER_TOUCHES_X,
12105                                           player->index_bit, border_side);
12106     }
12107     else if (IS_PLAYER(xx, yy))
12108     {
12109       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12110
12111       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12112       {
12113         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12114           continue;             /* center and border element do not touch */
12115       }
12116
12117       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12118                                  player->index_bit, center_side);
12119       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12120                                           CE_PLAYER_TOUCHES_X,
12121                                           player->index_bit, center_side);
12122       break;
12123     }
12124   }
12125 }
12126
12127 #if USE_ELEMENT_TOUCHING_BUGFIX
12128
12129 void TestIfElementTouchesCustomElement(int x, int y)
12130 {
12131   static int xy[4][2] =
12132   {
12133     { 0, -1 },
12134     { -1, 0 },
12135     { +1, 0 },
12136     { 0, +1 }
12137   };
12138   static int trigger_sides[4][2] =
12139   {
12140     /* center side      border side */
12141     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12142     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12143     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12144     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12145   };
12146   static int touch_dir[4] =
12147   {
12148     MV_LEFT | MV_RIGHT,
12149     MV_UP   | MV_DOWN,
12150     MV_UP   | MV_DOWN,
12151     MV_LEFT | MV_RIGHT
12152   };
12153   boolean change_center_element = FALSE;
12154   int center_element = Feld[x][y];      /* should always be non-moving! */
12155   int border_element_old[NUM_DIRECTIONS];
12156   int i;
12157
12158   for (i = 0; i < NUM_DIRECTIONS; i++)
12159   {
12160     int xx = x + xy[i][0];
12161     int yy = y + xy[i][1];
12162     int border_element;
12163
12164     border_element_old[i] = -1;
12165
12166     if (!IN_LEV_FIELD(xx, yy))
12167       continue;
12168
12169     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12170       border_element = Feld[xx][yy];    /* may be moving! */
12171     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12172       border_element = Feld[xx][yy];
12173     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12174       border_element = MovingOrBlocked2Element(xx, yy);
12175     else
12176       continue;                 /* center and border element do not touch */
12177
12178     border_element_old[i] = border_element;
12179   }
12180
12181   for (i = 0; i < NUM_DIRECTIONS; i++)
12182   {
12183     int xx = x + xy[i][0];
12184     int yy = y + xy[i][1];
12185     int center_side = trigger_sides[i][0];
12186     int border_element = border_element_old[i];
12187
12188     if (border_element == -1)
12189       continue;
12190
12191     /* check for change of border element */
12192     CheckElementChangeBySide(xx, yy, border_element, center_element,
12193                              CE_TOUCHING_X, center_side);
12194   }
12195
12196   for (i = 0; i < NUM_DIRECTIONS; i++)
12197   {
12198     int border_side = trigger_sides[i][1];
12199     int border_element = border_element_old[i];
12200
12201     if (border_element == -1)
12202       continue;
12203
12204     /* check for change of center element (but change it only once) */
12205     if (!change_center_element)
12206       change_center_element =
12207         CheckElementChangeBySide(x, y, center_element, border_element,
12208                                  CE_TOUCHING_X, border_side);
12209   }
12210 }
12211
12212 #else
12213
12214 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12215 {
12216   static int xy[4][2] =
12217   {
12218     { 0, -1 },
12219     { -1, 0 },
12220     { +1, 0 },
12221     { 0, +1 }
12222   };
12223   static int trigger_sides[4][2] =
12224   {
12225     /* center side      border side */
12226     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12227     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12228     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12229     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12230   };
12231   static int touch_dir[4] =
12232   {
12233     MV_LEFT | MV_RIGHT,
12234     MV_UP   | MV_DOWN,
12235     MV_UP   | MV_DOWN,
12236     MV_LEFT | MV_RIGHT
12237   };
12238   boolean change_center_element = FALSE;
12239   int center_element = Feld[x][y];      /* should always be non-moving! */
12240   int i;
12241
12242   for (i = 0; i < NUM_DIRECTIONS; i++)
12243   {
12244     int xx = x + xy[i][0];
12245     int yy = y + xy[i][1];
12246     int center_side = trigger_sides[i][0];
12247     int border_side = trigger_sides[i][1];
12248     int border_element;
12249
12250     if (!IN_LEV_FIELD(xx, yy))
12251       continue;
12252
12253     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12254       border_element = Feld[xx][yy];    /* may be moving! */
12255     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12256       border_element = Feld[xx][yy];
12257     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12258       border_element = MovingOrBlocked2Element(xx, yy);
12259     else
12260       continue;                 /* center and border element do not touch */
12261
12262     /* check for change of center element (but change it only once) */
12263     if (!change_center_element)
12264       change_center_element =
12265         CheckElementChangeBySide(x, y, center_element, border_element,
12266                                  CE_TOUCHING_X, border_side);
12267
12268     /* check for change of border element */
12269     CheckElementChangeBySide(xx, yy, border_element, center_element,
12270                              CE_TOUCHING_X, center_side);
12271   }
12272 }
12273
12274 #endif
12275
12276 void TestIfElementHitsCustomElement(int x, int y, int direction)
12277 {
12278   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12279   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12280   int hitx = x + dx, hity = y + dy;
12281   int hitting_element = Feld[x][y];
12282   int touched_element;
12283
12284   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12285     return;
12286
12287   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12288                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12289
12290   if (IN_LEV_FIELD(hitx, hity))
12291   {
12292     int opposite_direction = MV_DIR_OPPOSITE(direction);
12293     int hitting_side = direction;
12294     int touched_side = opposite_direction;
12295     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12296                           MovDir[hitx][hity] != direction ||
12297                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12298
12299     object_hit = TRUE;
12300
12301     if (object_hit)
12302     {
12303       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12304                                CE_HITTING_X, touched_side);
12305
12306       CheckElementChangeBySide(hitx, hity, touched_element,
12307                                hitting_element, CE_HIT_BY_X, hitting_side);
12308
12309       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12310                                CE_HIT_BY_SOMETHING, opposite_direction);
12311     }
12312   }
12313
12314   /* "hitting something" is also true when hitting the playfield border */
12315   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12316                            CE_HITTING_SOMETHING, direction);
12317 }
12318
12319 #if 0
12320 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12321 {
12322   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12323   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12324   int hitx = x + dx, hity = y + dy;
12325   int hitting_element = Feld[x][y];
12326   int touched_element;
12327 #if 0
12328   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12329                         !IS_FREE(hitx, hity) &&
12330                         (!IS_MOVING(hitx, hity) ||
12331                          MovDir[hitx][hity] != direction ||
12332                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
12333 #endif
12334
12335   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12336     return;
12337
12338 #if 0
12339   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12340     return;
12341 #endif
12342
12343   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12344                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12345
12346   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12347                            EP_CAN_SMASH_EVERYTHING, direction);
12348
12349   if (IN_LEV_FIELD(hitx, hity))
12350   {
12351     int opposite_direction = MV_DIR_OPPOSITE(direction);
12352     int hitting_side = direction;
12353     int touched_side = opposite_direction;
12354 #if 0
12355     int touched_element = MovingOrBlocked2Element(hitx, hity);
12356 #endif
12357 #if 1
12358     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12359                           MovDir[hitx][hity] != direction ||
12360                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12361
12362     object_hit = TRUE;
12363 #endif
12364
12365     if (object_hit)
12366     {
12367       int i;
12368
12369       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12370                                CE_SMASHED_BY_SOMETHING, opposite_direction);
12371
12372       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12373                                CE_OTHER_IS_SMASHING, touched_side);
12374
12375       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12376                                CE_OTHER_GETS_SMASHED, hitting_side);
12377     }
12378   }
12379 }
12380 #endif
12381
12382 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12383 {
12384   int i, kill_x = -1, kill_y = -1;
12385
12386   int bad_element = -1;
12387   static int test_xy[4][2] =
12388   {
12389     { 0, -1 },
12390     { -1, 0 },
12391     { +1, 0 },
12392     { 0, +1 }
12393   };
12394   static int test_dir[4] =
12395   {
12396     MV_UP,
12397     MV_LEFT,
12398     MV_RIGHT,
12399     MV_DOWN
12400   };
12401
12402   for (i = 0; i < NUM_DIRECTIONS; i++)
12403   {
12404     int test_x, test_y, test_move_dir, test_element;
12405
12406     test_x = good_x + test_xy[i][0];
12407     test_y = good_y + test_xy[i][1];
12408
12409     if (!IN_LEV_FIELD(test_x, test_y))
12410       continue;
12411
12412     test_move_dir =
12413       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12414
12415     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12416
12417     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12418        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12419     */
12420     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12421         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12422     {
12423       kill_x = test_x;
12424       kill_y = test_y;
12425       bad_element = test_element;
12426
12427       break;
12428     }
12429   }
12430
12431   if (kill_x != -1 || kill_y != -1)
12432   {
12433     if (IS_PLAYER(good_x, good_y))
12434     {
12435       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12436
12437       if (player->shield_deadly_time_left > 0 &&
12438           !IS_INDESTRUCTIBLE(bad_element))
12439         Bang(kill_x, kill_y);
12440       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12441         KillPlayer(player);
12442     }
12443     else
12444       Bang(good_x, good_y);
12445   }
12446 }
12447
12448 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12449 {
12450   int i, kill_x = -1, kill_y = -1;
12451   int bad_element = Feld[bad_x][bad_y];
12452   static int test_xy[4][2] =
12453   {
12454     { 0, -1 },
12455     { -1, 0 },
12456     { +1, 0 },
12457     { 0, +1 }
12458   };
12459   static int touch_dir[4] =
12460   {
12461     MV_LEFT | MV_RIGHT,
12462     MV_UP   | MV_DOWN,
12463     MV_UP   | MV_DOWN,
12464     MV_LEFT | MV_RIGHT
12465   };
12466   static int test_dir[4] =
12467   {
12468     MV_UP,
12469     MV_LEFT,
12470     MV_RIGHT,
12471     MV_DOWN
12472   };
12473
12474   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12475     return;
12476
12477   for (i = 0; i < NUM_DIRECTIONS; i++)
12478   {
12479     int test_x, test_y, test_move_dir, test_element;
12480
12481     test_x = bad_x + test_xy[i][0];
12482     test_y = bad_y + test_xy[i][1];
12483     if (!IN_LEV_FIELD(test_x, test_y))
12484       continue;
12485
12486     test_move_dir =
12487       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12488
12489     test_element = Feld[test_x][test_y];
12490
12491     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12492        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12493     */
12494     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12495         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12496     {
12497       /* good thing is player or penguin that does not move away */
12498       if (IS_PLAYER(test_x, test_y))
12499       {
12500         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12501
12502         if (bad_element == EL_ROBOT && player->is_moving)
12503           continue;     /* robot does not kill player if he is moving */
12504
12505         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12506         {
12507           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12508             continue;           /* center and border element do not touch */
12509         }
12510
12511         kill_x = test_x;
12512         kill_y = test_y;
12513         break;
12514       }
12515       else if (test_element == EL_PENGUIN)
12516       {
12517         kill_x = test_x;
12518         kill_y = test_y;
12519         break;
12520       }
12521     }
12522   }
12523
12524   if (kill_x != -1 || kill_y != -1)
12525   {
12526     if (IS_PLAYER(kill_x, kill_y))
12527     {
12528       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12529
12530       if (player->shield_deadly_time_left > 0 &&
12531           !IS_INDESTRUCTIBLE(bad_element))
12532         Bang(bad_x, bad_y);
12533       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12534         KillPlayer(player);
12535     }
12536     else
12537       Bang(kill_x, kill_y);
12538   }
12539 }
12540
12541 void TestIfPlayerTouchesBadThing(int x, int y)
12542 {
12543   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12544 }
12545
12546 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12547 {
12548   TestIfGoodThingHitsBadThing(x, y, move_dir);
12549 }
12550
12551 void TestIfBadThingTouchesPlayer(int x, int y)
12552 {
12553   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12554 }
12555
12556 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12557 {
12558   TestIfBadThingHitsGoodThing(x, y, move_dir);
12559 }
12560
12561 void TestIfFriendTouchesBadThing(int x, int y)
12562 {
12563   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12564 }
12565
12566 void TestIfBadThingTouchesFriend(int x, int y)
12567 {
12568   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12569 }
12570
12571 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12572 {
12573   int i, kill_x = bad_x, kill_y = bad_y;
12574   static int xy[4][2] =
12575   {
12576     { 0, -1 },
12577     { -1, 0 },
12578     { +1, 0 },
12579     { 0, +1 }
12580   };
12581
12582   for (i = 0; i < NUM_DIRECTIONS; i++)
12583   {
12584     int x, y, element;
12585
12586     x = bad_x + xy[i][0];
12587     y = bad_y + xy[i][1];
12588     if (!IN_LEV_FIELD(x, y))
12589       continue;
12590
12591     element = Feld[x][y];
12592     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12593         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12594     {
12595       kill_x = x;
12596       kill_y = y;
12597       break;
12598     }
12599   }
12600
12601   if (kill_x != bad_x || kill_y != bad_y)
12602     Bang(bad_x, bad_y);
12603 }
12604
12605 void KillPlayer(struct PlayerInfo *player)
12606 {
12607   int jx = player->jx, jy = player->jy;
12608
12609   if (!player->active)
12610     return;
12611
12612   /* the following code was introduced to prevent an infinite loop when calling
12613      -> Bang()
12614      -> CheckTriggeredElementChangeExt()
12615      -> ExecuteCustomElementAction()
12616      -> KillPlayer()
12617      -> (infinitely repeating the above sequence of function calls)
12618      which occurs when killing the player while having a CE with the setting
12619      "kill player X when explosion of <player X>"; the solution using a new
12620      field "player->killed" was chosen for backwards compatibility, although
12621      clever use of the fields "player->active" etc. would probably also work */
12622 #if 1
12623   if (player->killed)
12624     return;
12625 #endif
12626
12627   player->killed = TRUE;
12628
12629   /* remove accessible field at the player's position */
12630   Feld[jx][jy] = EL_EMPTY;
12631
12632   /* deactivate shield (else Bang()/Explode() would not work right) */
12633   player->shield_normal_time_left = 0;
12634   player->shield_deadly_time_left = 0;
12635
12636   Bang(jx, jy);
12637   BuryPlayer(player);
12638 }
12639
12640 static void KillPlayerUnlessEnemyProtected(int x, int y)
12641 {
12642   if (!PLAYER_ENEMY_PROTECTED(x, y))
12643     KillPlayer(PLAYERINFO(x, y));
12644 }
12645
12646 static void KillPlayerUnlessExplosionProtected(int x, int y)
12647 {
12648   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
12649     KillPlayer(PLAYERINFO(x, y));
12650 }
12651
12652 void BuryPlayer(struct PlayerInfo *player)
12653 {
12654   int jx = player->jx, jy = player->jy;
12655
12656   if (!player->active)
12657     return;
12658
12659   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
12660   PlayLevelSound(jx, jy, SND_GAME_LOSING);
12661
12662   player->GameOver = TRUE;
12663   RemovePlayer(player);
12664 }
12665
12666 void RemovePlayer(struct PlayerInfo *player)
12667 {
12668   int jx = player->jx, jy = player->jy;
12669   int i, found = FALSE;
12670
12671   player->present = FALSE;
12672   player->active = FALSE;
12673
12674   if (!ExplodeField[jx][jy])
12675     StorePlayer[jx][jy] = 0;
12676
12677   if (player->is_moving)
12678     DrawLevelField(player->last_jx, player->last_jy);
12679
12680   for (i = 0; i < MAX_PLAYERS; i++)
12681     if (stored_player[i].active)
12682       found = TRUE;
12683
12684   if (!found)
12685     AllPlayersGone = TRUE;
12686
12687   ExitX = ZX = jx;
12688   ExitY = ZY = jy;
12689 }
12690
12691 #if USE_NEW_SNAP_DELAY
12692 static void setFieldForSnapping(int x, int y, int element, int direction)
12693 {
12694   struct ElementInfo *ei = &element_info[element];
12695   int direction_bit = MV_DIR_TO_BIT(direction);
12696   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
12697   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
12698                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
12699
12700   Feld[x][y] = EL_ELEMENT_SNAPPING;
12701   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
12702
12703   ResetGfxAnimation(x, y);
12704
12705   GfxElement[x][y] = element;
12706   GfxAction[x][y] = action;
12707   GfxDir[x][y] = direction;
12708   GfxFrame[x][y] = -1;
12709 }
12710 #endif
12711
12712 /*
12713   =============================================================================
12714   checkDiagonalPushing()
12715   -----------------------------------------------------------------------------
12716   check if diagonal input device direction results in pushing of object
12717   (by checking if the alternative direction is walkable, diggable, ...)
12718   =============================================================================
12719 */
12720
12721 static boolean checkDiagonalPushing(struct PlayerInfo *player,
12722                                     int x, int y, int real_dx, int real_dy)
12723 {
12724   int jx, jy, dx, dy, xx, yy;
12725
12726   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
12727     return TRUE;
12728
12729   /* diagonal direction: check alternative direction */
12730   jx = player->jx;
12731   jy = player->jy;
12732   dx = x - jx;
12733   dy = y - jy;
12734   xx = jx + (dx == 0 ? real_dx : 0);
12735   yy = jy + (dy == 0 ? real_dy : 0);
12736
12737   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
12738 }
12739
12740 /*
12741   =============================================================================
12742   DigField()
12743   -----------------------------------------------------------------------------
12744   x, y:                 field next to player (non-diagonal) to try to dig to
12745   real_dx, real_dy:     direction as read from input device (can be diagonal)
12746   =============================================================================
12747 */
12748
12749 int DigField(struct PlayerInfo *player,
12750              int oldx, int oldy, int x, int y,
12751              int real_dx, int real_dy, int mode)
12752 {
12753   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
12754   boolean player_was_pushing = player->is_pushing;
12755   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
12756   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
12757   int jx = oldx, jy = oldy;
12758   int dx = x - jx, dy = y - jy;
12759   int nextx = x + dx, nexty = y + dy;
12760   int move_direction = (dx == -1 ? MV_LEFT  :
12761                         dx == +1 ? MV_RIGHT :
12762                         dy == -1 ? MV_UP    :
12763                         dy == +1 ? MV_DOWN  : MV_NONE);
12764   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
12765   int dig_side = MV_DIR_OPPOSITE(move_direction);
12766   int old_element = Feld[jx][jy];
12767 #if USE_FIXED_DONT_RUN_INTO
12768   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
12769 #else
12770   int element;
12771 #endif
12772   int collect_count;
12773
12774   if (is_player)                /* function can also be called by EL_PENGUIN */
12775   {
12776     if (player->MovPos == 0)
12777     {
12778       player->is_digging = FALSE;
12779       player->is_collecting = FALSE;
12780     }
12781
12782     if (player->MovPos == 0)    /* last pushing move finished */
12783       player->is_pushing = FALSE;
12784
12785     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
12786     {
12787       player->is_switching = FALSE;
12788       player->push_delay = -1;
12789
12790       return MP_NO_ACTION;
12791     }
12792   }
12793
12794 #if !USE_FIXED_DONT_RUN_INTO
12795   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12796     return MP_NO_ACTION;
12797 #endif
12798
12799   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
12800     old_element = Back[jx][jy];
12801
12802   /* in case of element dropped at player position, check background */
12803   else if (Back[jx][jy] != EL_EMPTY &&
12804            game.engine_version >= VERSION_IDENT(2,2,0,0))
12805     old_element = Back[jx][jy];
12806
12807   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12808     return MP_NO_ACTION;        /* field has no opening in this direction */
12809
12810   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12811     return MP_NO_ACTION;        /* field has no opening in this direction */
12812
12813 #if USE_FIXED_DONT_RUN_INTO
12814   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12815   {
12816     SplashAcid(x, y);
12817
12818     Feld[jx][jy] = player->artwork_element;
12819     InitMovingField(jx, jy, MV_DOWN);
12820     Store[jx][jy] = EL_ACID;
12821     ContinueMoving(jx, jy);
12822     BuryPlayer(player);
12823
12824     return MP_DONT_RUN_INTO;
12825   }
12826 #endif
12827
12828 #if USE_FIXED_DONT_RUN_INTO
12829   if (player_can_move && DONT_RUN_INTO(element))
12830   {
12831     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12832
12833     return MP_DONT_RUN_INTO;
12834   }
12835 #endif
12836
12837 #if USE_FIXED_DONT_RUN_INTO
12838   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12839     return MP_NO_ACTION;
12840 #endif
12841
12842 #if !USE_FIXED_DONT_RUN_INTO
12843   element = Feld[x][y];
12844 #endif
12845
12846   collect_count = element_info[element].collect_count_initial;
12847
12848   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
12849     return MP_NO_ACTION;
12850
12851   if (game.engine_version < VERSION_IDENT(2,2,0,0))
12852     player_can_move = player_can_move_or_snap;
12853
12854   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
12855       game.engine_version >= VERSION_IDENT(2,2,0,0))
12856   {
12857     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
12858                                player->index_bit, dig_side);
12859     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12860                                         player->index_bit, dig_side);
12861
12862     if (element == EL_DC_LANDMINE)
12863       Bang(x, y);
12864
12865     if (Feld[x][y] != element)          /* field changed by snapping */
12866       return MP_ACTION;
12867
12868     return MP_NO_ACTION;
12869   }
12870
12871 #if USE_PLAYER_GRAVITY
12872   if (player->gravity && is_player && !player->is_auto_moving &&
12873       canFallDown(player) && move_direction != MV_DOWN &&
12874       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12875     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12876 #else
12877   if (game.gravity && is_player && !player->is_auto_moving &&
12878       canFallDown(player) && move_direction != MV_DOWN &&
12879       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12880     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12881 #endif
12882
12883   if (player_can_move &&
12884       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12885   {
12886     int sound_element = SND_ELEMENT(element);
12887     int sound_action = ACTION_WALKING;
12888
12889     if (IS_RND_GATE(element))
12890     {
12891       if (!player->key[RND_GATE_NR(element)])
12892         return MP_NO_ACTION;
12893     }
12894     else if (IS_RND_GATE_GRAY(element))
12895     {
12896       if (!player->key[RND_GATE_GRAY_NR(element)])
12897         return MP_NO_ACTION;
12898     }
12899     else if (IS_RND_GATE_GRAY_ACTIVE(element))
12900     {
12901       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
12902         return MP_NO_ACTION;
12903     }
12904     else if (element == EL_EXIT_OPEN ||
12905              element == EL_EM_EXIT_OPEN ||
12906              element == EL_STEEL_EXIT_OPEN ||
12907              element == EL_EM_STEEL_EXIT_OPEN ||
12908              element == EL_SP_EXIT_OPEN ||
12909              element == EL_SP_EXIT_OPENING)
12910     {
12911       sound_action = ACTION_PASSING;    /* player is passing exit */
12912     }
12913     else if (element == EL_EMPTY)
12914     {
12915       sound_action = ACTION_MOVING;             /* nothing to walk on */
12916     }
12917
12918     /* play sound from background or player, whatever is available */
12919     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12920       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12921     else
12922       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
12923   }
12924   else if (player_can_move &&
12925            IS_PASSABLE(element) && canPassField(x, y, move_direction))
12926   {
12927     if (!ACCESS_FROM(element, opposite_direction))
12928       return MP_NO_ACTION;      /* field not accessible from this direction */
12929
12930     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
12931       return MP_NO_ACTION;
12932
12933     if (IS_EM_GATE(element))
12934     {
12935       if (!player->key[EM_GATE_NR(element)])
12936         return MP_NO_ACTION;
12937     }
12938     else if (IS_EM_GATE_GRAY(element))
12939     {
12940       if (!player->key[EM_GATE_GRAY_NR(element)])
12941         return MP_NO_ACTION;
12942     }
12943     else if (IS_EM_GATE_GRAY_ACTIVE(element))
12944     {
12945       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
12946         return MP_NO_ACTION;
12947     }
12948     else if (IS_EMC_GATE(element))
12949     {
12950       if (!player->key[EMC_GATE_NR(element)])
12951         return MP_NO_ACTION;
12952     }
12953     else if (IS_EMC_GATE_GRAY(element))
12954     {
12955       if (!player->key[EMC_GATE_GRAY_NR(element)])
12956         return MP_NO_ACTION;
12957     }
12958     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
12959     {
12960       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
12961         return MP_NO_ACTION;
12962     }
12963     else if (element == EL_DC_GATE_WHITE ||
12964              element == EL_DC_GATE_WHITE_GRAY ||
12965              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
12966     {
12967       if (player->num_white_keys == 0)
12968         return MP_NO_ACTION;
12969
12970       player->num_white_keys--;
12971     }
12972     else if (IS_SP_PORT(element))
12973     {
12974       if (element == EL_SP_GRAVITY_PORT_LEFT ||
12975           element == EL_SP_GRAVITY_PORT_RIGHT ||
12976           element == EL_SP_GRAVITY_PORT_UP ||
12977           element == EL_SP_GRAVITY_PORT_DOWN)
12978 #if USE_PLAYER_GRAVITY
12979         player->gravity = !player->gravity;
12980 #else
12981         game.gravity = !game.gravity;
12982 #endif
12983       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
12984                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12985                element == EL_SP_GRAVITY_ON_PORT_UP ||
12986                element == EL_SP_GRAVITY_ON_PORT_DOWN)
12987 #if USE_PLAYER_GRAVITY
12988         player->gravity = TRUE;
12989 #else
12990         game.gravity = TRUE;
12991 #endif
12992       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12993                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12994                element == EL_SP_GRAVITY_OFF_PORT_UP ||
12995                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12996 #if USE_PLAYER_GRAVITY
12997         player->gravity = FALSE;
12998 #else
12999         game.gravity = FALSE;
13000 #endif
13001     }
13002
13003     /* automatically move to the next field with double speed */
13004     player->programmed_action = move_direction;
13005
13006     if (player->move_delay_reset_counter == 0)
13007     {
13008       player->move_delay_reset_counter = 2;     /* two double speed steps */
13009
13010       DOUBLE_PLAYER_SPEED(player);
13011     }
13012
13013     PlayLevelSoundAction(x, y, ACTION_PASSING);
13014   }
13015   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13016   {
13017     RemoveField(x, y);
13018
13019     if (mode != DF_SNAP)
13020     {
13021       GfxElement[x][y] = GFX_ELEMENT(element);
13022       player->is_digging = TRUE;
13023     }
13024
13025     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13026
13027     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13028                                         player->index_bit, dig_side);
13029
13030     if (mode == DF_SNAP)
13031     {
13032 #if USE_NEW_SNAP_DELAY
13033       if (level.block_snap_field)
13034         setFieldForSnapping(x, y, element, move_direction);
13035       else
13036         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13037 #else
13038       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13039 #endif
13040
13041       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13042                                           player->index_bit, dig_side);
13043     }
13044   }
13045   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13046   {
13047     RemoveField(x, y);
13048
13049     if (is_player && mode != DF_SNAP)
13050     {
13051       GfxElement[x][y] = element;
13052       player->is_collecting = TRUE;
13053     }
13054
13055     if (element == EL_SPEED_PILL)
13056     {
13057       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13058     }
13059     else if (element == EL_EXTRA_TIME && level.time > 0)
13060     {
13061       TimeLeft += level.extra_time;
13062       DrawGameValue_Time(TimeLeft);
13063     }
13064     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13065     {
13066       player->shield_normal_time_left += level.shield_normal_time;
13067       if (element == EL_SHIELD_DEADLY)
13068         player->shield_deadly_time_left += level.shield_deadly_time;
13069     }
13070     else if (element == EL_DYNAMITE ||
13071              element == EL_EM_DYNAMITE ||
13072              element == EL_SP_DISK_RED)
13073     {
13074       if (player->inventory_size < MAX_INVENTORY_SIZE)
13075         player->inventory_element[player->inventory_size++] = element;
13076
13077       DrawGameDoorValues();
13078     }
13079     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13080     {
13081       player->dynabomb_count++;
13082       player->dynabombs_left++;
13083     }
13084     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13085     {
13086       player->dynabomb_size++;
13087     }
13088     else if (element == EL_DYNABOMB_INCREASE_POWER)
13089     {
13090       player->dynabomb_xl = TRUE;
13091     }
13092     else if (IS_KEY(element))
13093     {
13094       player->key[KEY_NR(element)] = TRUE;
13095
13096       DrawGameDoorValues();
13097     }
13098     else if (element == EL_DC_KEY_WHITE)
13099     {
13100       player->num_white_keys++;
13101
13102       /* display white keys? */
13103       /* DrawGameDoorValues(); */
13104     }
13105     else if (IS_ENVELOPE(element))
13106     {
13107       player->show_envelope = element;
13108     }
13109     else if (element == EL_EMC_LENSES)
13110     {
13111       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13112
13113       RedrawAllInvisibleElementsForLenses();
13114     }
13115     else if (element == EL_EMC_MAGNIFIER)
13116     {
13117       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13118
13119       RedrawAllInvisibleElementsForMagnifier();
13120     }
13121     else if (IS_DROPPABLE(element) ||
13122              IS_THROWABLE(element))     /* can be collected and dropped */
13123     {
13124       int i;
13125
13126       if (collect_count == 0)
13127         player->inventory_infinite_element = element;
13128       else
13129         for (i = 0; i < collect_count; i++)
13130           if (player->inventory_size < MAX_INVENTORY_SIZE)
13131             player->inventory_element[player->inventory_size++] = element;
13132
13133       DrawGameDoorValues();
13134     }
13135     else if (collect_count > 0)
13136     {
13137       local_player->gems_still_needed -= collect_count;
13138       if (local_player->gems_still_needed < 0)
13139         local_player->gems_still_needed = 0;
13140
13141       DrawGameValue_Emeralds(local_player->gems_still_needed);
13142     }
13143
13144     RaiseScoreElement(element);
13145     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13146
13147     if (is_player)
13148       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13149                                           player->index_bit, dig_side);
13150
13151     if (mode == DF_SNAP)
13152     {
13153 #if USE_NEW_SNAP_DELAY
13154       if (level.block_snap_field)
13155         setFieldForSnapping(x, y, element, move_direction);
13156       else
13157         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13158 #else
13159       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13160 #endif
13161
13162       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13163                                           player->index_bit, dig_side);
13164     }
13165   }
13166   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13167   {
13168     if (mode == DF_SNAP && element != EL_BD_ROCK)
13169       return MP_NO_ACTION;
13170
13171     if (CAN_FALL(element) && dy)
13172       return MP_NO_ACTION;
13173
13174     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13175         !(element == EL_SPRING && level.use_spring_bug))
13176       return MP_NO_ACTION;
13177
13178     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13179         ((move_direction & MV_VERTICAL &&
13180           ((element_info[element].move_pattern & MV_LEFT &&
13181             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13182            (element_info[element].move_pattern & MV_RIGHT &&
13183             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13184          (move_direction & MV_HORIZONTAL &&
13185           ((element_info[element].move_pattern & MV_UP &&
13186             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13187            (element_info[element].move_pattern & MV_DOWN &&
13188             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13189       return MP_NO_ACTION;
13190
13191     /* do not push elements already moving away faster than player */
13192     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13193         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13194       return MP_NO_ACTION;
13195
13196     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13197     {
13198       if (player->push_delay_value == -1 || !player_was_pushing)
13199         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13200     }
13201     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13202     {
13203       if (player->push_delay_value == -1)
13204         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13205     }
13206     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13207     {
13208       if (!player->is_pushing)
13209         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13210     }
13211
13212     player->is_pushing = TRUE;
13213     player->is_active = TRUE;
13214
13215     if (!(IN_LEV_FIELD(nextx, nexty) &&
13216           (IS_FREE(nextx, nexty) ||
13217            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13218             IS_SB_ELEMENT(element)))))
13219       return MP_NO_ACTION;
13220
13221     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13222       return MP_NO_ACTION;
13223
13224     if (player->push_delay == -1)       /* new pushing; restart delay */
13225       player->push_delay = 0;
13226
13227     if (player->push_delay < player->push_delay_value &&
13228         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13229         element != EL_SPRING && element != EL_BALLOON)
13230     {
13231       /* make sure that there is no move delay before next try to push */
13232       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13233         player->move_delay = 0;
13234
13235       return MP_NO_ACTION;
13236     }
13237
13238     if (IS_SB_ELEMENT(element))
13239     {
13240       if (element == EL_SOKOBAN_FIELD_FULL)
13241       {
13242         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13243         local_player->sokobanfields_still_needed++;
13244       }
13245
13246       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13247       {
13248         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13249         local_player->sokobanfields_still_needed--;
13250       }
13251
13252       Feld[x][y] = EL_SOKOBAN_OBJECT;
13253
13254       if (Back[x][y] == Back[nextx][nexty])
13255         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13256       else if (Back[x][y] != 0)
13257         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13258                                     ACTION_EMPTYING);
13259       else
13260         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13261                                     ACTION_FILLING);
13262
13263       if (local_player->sokobanfields_still_needed == 0 &&
13264           game.emulation == EMU_SOKOBAN)
13265       {
13266         PlayerWins(player);
13267
13268         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13269       }
13270     }
13271     else
13272       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13273
13274     InitMovingField(x, y, move_direction);
13275     GfxAction[x][y] = ACTION_PUSHING;
13276
13277     if (mode == DF_SNAP)
13278       ContinueMoving(x, y);
13279     else
13280       MovPos[x][y] = (dx != 0 ? dx : dy);
13281
13282     Pushed[x][y] = TRUE;
13283     Pushed[nextx][nexty] = TRUE;
13284
13285     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13286       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13287     else
13288       player->push_delay_value = -1;    /* get new value later */
13289
13290     /* check for element change _after_ element has been pushed */
13291     if (game.use_change_when_pushing_bug)
13292     {
13293       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13294                                  player->index_bit, dig_side);
13295       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13296                                           player->index_bit, dig_side);
13297     }
13298   }
13299   else if (IS_SWITCHABLE(element))
13300   {
13301     if (PLAYER_SWITCHING(player, x, y))
13302     {
13303       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13304                                           player->index_bit, dig_side);
13305
13306       return MP_ACTION;
13307     }
13308
13309     player->is_switching = TRUE;
13310     player->switch_x = x;
13311     player->switch_y = y;
13312
13313     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13314
13315     if (element == EL_ROBOT_WHEEL)
13316     {
13317       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13318       ZX = x;
13319       ZY = y;
13320
13321       DrawLevelField(x, y);
13322     }
13323     else if (element == EL_SP_TERMINAL)
13324     {
13325       int xx, yy;
13326
13327       SCAN_PLAYFIELD(xx, yy)
13328       {
13329         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13330           Bang(xx, yy);
13331         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13332           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13333       }
13334     }
13335     else if (IS_BELT_SWITCH(element))
13336     {
13337       ToggleBeltSwitch(x, y);
13338     }
13339     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13340              element == EL_SWITCHGATE_SWITCH_DOWN ||
13341              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13342              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13343     {
13344       ToggleSwitchgateSwitch(x, y);
13345     }
13346     else if (element == EL_LIGHT_SWITCH ||
13347              element == EL_LIGHT_SWITCH_ACTIVE)
13348     {
13349       ToggleLightSwitch(x, y);
13350     }
13351     else if (element == EL_TIMEGATE_SWITCH ||
13352              element == EL_DC_TIMEGATE_SWITCH)
13353     {
13354       ActivateTimegateSwitch(x, y);
13355     }
13356     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13357              element == EL_BALLOON_SWITCH_RIGHT ||
13358              element == EL_BALLOON_SWITCH_UP    ||
13359              element == EL_BALLOON_SWITCH_DOWN  ||
13360              element == EL_BALLOON_SWITCH_NONE  ||
13361              element == EL_BALLOON_SWITCH_ANY)
13362     {
13363       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13364                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13365                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13366                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13367                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13368                              move_direction);
13369     }
13370     else if (element == EL_LAMP)
13371     {
13372       Feld[x][y] = EL_LAMP_ACTIVE;
13373       local_player->lights_still_needed--;
13374
13375       ResetGfxAnimation(x, y);
13376       DrawLevelField(x, y);
13377     }
13378     else if (element == EL_TIME_ORB_FULL)
13379     {
13380       Feld[x][y] = EL_TIME_ORB_EMPTY;
13381
13382       if (level.time > 0 || level.use_time_orb_bug)
13383       {
13384         TimeLeft += level.time_orb_time;
13385         DrawGameValue_Time(TimeLeft);
13386       }
13387
13388       ResetGfxAnimation(x, y);
13389       DrawLevelField(x, y);
13390     }
13391     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13392              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13393     {
13394       int xx, yy;
13395
13396       game.ball_state = !game.ball_state;
13397
13398       SCAN_PLAYFIELD(xx, yy)
13399       {
13400         int e = Feld[xx][yy];
13401
13402         if (game.ball_state)
13403         {
13404           if (e == EL_EMC_MAGIC_BALL)
13405             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13406           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13407             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13408         }
13409         else
13410         {
13411           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13412             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13413           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13414             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13415         }
13416       }
13417     }
13418
13419     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13420                                         player->index_bit, dig_side);
13421
13422     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13423                                         player->index_bit, dig_side);
13424
13425     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13426                                         player->index_bit, dig_side);
13427
13428     return MP_ACTION;
13429   }
13430   else
13431   {
13432     if (!PLAYER_SWITCHING(player, x, y))
13433     {
13434       player->is_switching = TRUE;
13435       player->switch_x = x;
13436       player->switch_y = y;
13437
13438       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13439                                  player->index_bit, dig_side);
13440       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13441                                           player->index_bit, dig_side);
13442
13443       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13444                                  player->index_bit, dig_side);
13445       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13446                                           player->index_bit, dig_side);
13447     }
13448
13449     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13450                                player->index_bit, dig_side);
13451     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13452                                         player->index_bit, dig_side);
13453
13454     return MP_NO_ACTION;
13455   }
13456
13457   player->push_delay = -1;
13458
13459   if (is_player)                /* function can also be called by EL_PENGUIN */
13460   {
13461     if (Feld[x][y] != element)          /* really digged/collected something */
13462     {
13463       player->is_collecting = !player->is_digging;
13464       player->is_active = TRUE;
13465     }
13466   }
13467
13468   return MP_MOVING;
13469 }
13470
13471 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13472 {
13473   int jx = player->jx, jy = player->jy;
13474   int x = jx + dx, y = jy + dy;
13475   int snap_direction = (dx == -1 ? MV_LEFT  :
13476                         dx == +1 ? MV_RIGHT :
13477                         dy == -1 ? MV_UP    :
13478                         dy == +1 ? MV_DOWN  : MV_NONE);
13479   boolean can_continue_snapping = (level.continuous_snapping &&
13480                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13481
13482   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13483     return FALSE;
13484
13485   if (!player->active || !IN_LEV_FIELD(x, y))
13486     return FALSE;
13487
13488   if (dx && dy)
13489     return FALSE;
13490
13491   if (!dx && !dy)
13492   {
13493     if (player->MovPos == 0)
13494       player->is_pushing = FALSE;
13495
13496     player->is_snapping = FALSE;
13497
13498     if (player->MovPos == 0)
13499     {
13500       player->is_moving = FALSE;
13501       player->is_digging = FALSE;
13502       player->is_collecting = FALSE;
13503     }
13504
13505     return FALSE;
13506   }
13507
13508 #if USE_NEW_CONTINUOUS_SNAPPING
13509   /* prevent snapping with already pressed snap key when not allowed */
13510   if (player->is_snapping && !can_continue_snapping)
13511     return FALSE;
13512 #else
13513   if (player->is_snapping)
13514     return FALSE;
13515 #endif
13516
13517   player->MovDir = snap_direction;
13518
13519   if (player->MovPos == 0)
13520   {
13521     player->is_moving = FALSE;
13522     player->is_digging = FALSE;
13523     player->is_collecting = FALSE;
13524   }
13525
13526   player->is_dropping = FALSE;
13527   player->is_dropping_pressed = FALSE;
13528   player->drop_pressed_delay = 0;
13529
13530   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13531     return FALSE;
13532
13533   player->is_snapping = TRUE;
13534   player->is_active = TRUE;
13535
13536   if (player->MovPos == 0)
13537   {
13538     player->is_moving = FALSE;
13539     player->is_digging = FALSE;
13540     player->is_collecting = FALSE;
13541   }
13542
13543   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13544     DrawLevelField(player->last_jx, player->last_jy);
13545
13546   DrawLevelField(x, y);
13547
13548   return TRUE;
13549 }
13550
13551 boolean DropElement(struct PlayerInfo *player)
13552 {
13553   int old_element, new_element;
13554   int dropx = player->jx, dropy = player->jy;
13555   int drop_direction = player->MovDir;
13556   int drop_side = drop_direction;
13557   int drop_element = (player->inventory_size > 0 ?
13558                       player->inventory_element[player->inventory_size - 1] :
13559                       player->inventory_infinite_element != EL_UNDEFINED ?
13560                       player->inventory_infinite_element :
13561                       player->dynabombs_left > 0 ?
13562                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
13563                       EL_UNDEFINED);
13564
13565   player->is_dropping_pressed = TRUE;
13566
13567   /* do not drop an element on top of another element; when holding drop key
13568      pressed without moving, dropped element must move away before the next
13569      element can be dropped (this is especially important if the next element
13570      is dynamite, which can be placed on background for historical reasons) */
13571   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13572     return MP_ACTION;
13573
13574   if (IS_THROWABLE(drop_element))
13575   {
13576     dropx += GET_DX_FROM_DIR(drop_direction);
13577     dropy += GET_DY_FROM_DIR(drop_direction);
13578
13579     if (!IN_LEV_FIELD(dropx, dropy))
13580       return FALSE;
13581   }
13582
13583   old_element = Feld[dropx][dropy];     /* old element at dropping position */
13584   new_element = drop_element;           /* default: no change when dropping */
13585
13586   /* check if player is active, not moving and ready to drop */
13587   if (!player->active || player->MovPos || player->drop_delay > 0)
13588     return FALSE;
13589
13590   /* check if player has anything that can be dropped */
13591   if (new_element == EL_UNDEFINED)
13592     return FALSE;
13593
13594   /* check if drop key was pressed long enough for EM style dynamite */
13595   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13596     return FALSE;
13597
13598   /* check if anything can be dropped at the current position */
13599   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13600     return FALSE;
13601
13602   /* collected custom elements can only be dropped on empty fields */
13603   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13604     return FALSE;
13605
13606   if (old_element != EL_EMPTY)
13607     Back[dropx][dropy] = old_element;   /* store old element on this field */
13608
13609   ResetGfxAnimation(dropx, dropy);
13610   ResetRandomAnimationValue(dropx, dropy);
13611
13612   if (player->inventory_size > 0 ||
13613       player->inventory_infinite_element != EL_UNDEFINED)
13614   {
13615     if (player->inventory_size > 0)
13616     {
13617       player->inventory_size--;
13618
13619       DrawGameDoorValues();
13620
13621       if (new_element == EL_DYNAMITE)
13622         new_element = EL_DYNAMITE_ACTIVE;
13623       else if (new_element == EL_EM_DYNAMITE)
13624         new_element = EL_EM_DYNAMITE_ACTIVE;
13625       else if (new_element == EL_SP_DISK_RED)
13626         new_element = EL_SP_DISK_RED_ACTIVE;
13627     }
13628
13629     Feld[dropx][dropy] = new_element;
13630
13631     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13632       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13633                           el2img(Feld[dropx][dropy]), 0);
13634
13635     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13636
13637     /* needed if previous element just changed to "empty" in the last frame */
13638     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
13639
13640     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13641                                player->index_bit, drop_side);
13642     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13643                                         CE_PLAYER_DROPS_X,
13644                                         player->index_bit, drop_side);
13645
13646     TestIfElementTouchesCustomElement(dropx, dropy);
13647   }
13648   else          /* player is dropping a dyna bomb */
13649   {
13650     player->dynabombs_left--;
13651
13652     Feld[dropx][dropy] = new_element;
13653
13654     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13655       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13656                           el2img(Feld[dropx][dropy]), 0);
13657
13658     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13659   }
13660
13661   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
13662     InitField_WithBug1(dropx, dropy, FALSE);
13663
13664   new_element = Feld[dropx][dropy];     /* element might have changed */
13665
13666   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
13667       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
13668   {
13669     int move_direction, nextx, nexty;
13670
13671     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
13672       MovDir[dropx][dropy] = drop_direction;
13673
13674     move_direction = MovDir[dropx][dropy];
13675     nextx = dropx + GET_DX_FROM_DIR(move_direction);
13676     nexty = dropy + GET_DY_FROM_DIR(move_direction);
13677
13678     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
13679
13680 #if USE_FIX_IMPACT_COLLISION
13681     /* do not cause impact style collision by dropping elements that can fall */
13682     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13683 #else
13684     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13685 #endif
13686   }
13687
13688   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
13689   player->is_dropping = TRUE;
13690
13691   player->drop_pressed_delay = 0;
13692   player->is_dropping_pressed = FALSE;
13693
13694   player->drop_x = dropx;
13695   player->drop_y = dropy;
13696
13697   return TRUE;
13698 }
13699
13700 /* ------------------------------------------------------------------------- */
13701 /* game sound playing functions                                              */
13702 /* ------------------------------------------------------------------------- */
13703
13704 static int *loop_sound_frame = NULL;
13705 static int *loop_sound_volume = NULL;
13706
13707 void InitPlayLevelSound()
13708 {
13709   int num_sounds = getSoundListSize();
13710
13711   checked_free(loop_sound_frame);
13712   checked_free(loop_sound_volume);
13713
13714   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
13715   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
13716 }
13717
13718 static void PlayLevelSound(int x, int y, int nr)
13719 {
13720   int sx = SCREENX(x), sy = SCREENY(y);
13721   int volume, stereo_position;
13722   int max_distance = 8;
13723   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
13724
13725   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
13726       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
13727     return;
13728
13729   if (!IN_LEV_FIELD(x, y) ||
13730       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
13731       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
13732     return;
13733
13734   volume = SOUND_MAX_VOLUME;
13735
13736   if (!IN_SCR_FIELD(sx, sy))
13737   {
13738     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
13739     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
13740
13741     volume -= volume * (dx > dy ? dx : dy) / max_distance;
13742   }
13743
13744   stereo_position = (SOUND_MAX_LEFT +
13745                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
13746                      (SCR_FIELDX + 2 * max_distance));
13747
13748   if (IS_LOOP_SOUND(nr))
13749   {
13750     /* This assures that quieter loop sounds do not overwrite louder ones,
13751        while restarting sound volume comparison with each new game frame. */
13752
13753     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
13754       return;
13755
13756     loop_sound_volume[nr] = volume;
13757     loop_sound_frame[nr] = FrameCounter;
13758   }
13759
13760   PlaySoundExt(nr, volume, stereo_position, type);
13761 }
13762
13763 static void PlayLevelSoundNearest(int x, int y, int sound_action)
13764 {
13765   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
13766                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
13767                  y < LEVELY(BY1) ? LEVELY(BY1) :
13768                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
13769                  sound_action);
13770 }
13771
13772 static void PlayLevelSoundAction(int x, int y, int action)
13773 {
13774   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
13775 }
13776
13777 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
13778 {
13779   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13780
13781   if (sound_effect != SND_UNDEFINED)
13782     PlayLevelSound(x, y, sound_effect);
13783 }
13784
13785 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
13786                                               int action)
13787 {
13788   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13789
13790   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13791     PlayLevelSound(x, y, sound_effect);
13792 }
13793
13794 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
13795 {
13796   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13797
13798   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13799     PlayLevelSound(x, y, sound_effect);
13800 }
13801
13802 static void StopLevelSoundActionIfLoop(int x, int y, int action)
13803 {
13804   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13805
13806   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13807     StopSound(sound_effect);
13808 }
13809
13810 static void PlayLevelMusic()
13811 {
13812   if (levelset.music[level_nr] != MUS_UNDEFINED)
13813     PlayMusic(levelset.music[level_nr]);        /* from config file */
13814   else
13815     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
13816 }
13817
13818 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
13819 {
13820   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13821   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
13822   int x = xx - 1 - offset;
13823   int y = yy - 1 - offset;
13824
13825   switch (sample)
13826   {
13827     case SAMPLE_blank:
13828       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13829       break;
13830
13831     case SAMPLE_roll:
13832       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13833       break;
13834
13835     case SAMPLE_stone:
13836       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13837       break;
13838
13839     case SAMPLE_nut:
13840       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13841       break;
13842
13843     case SAMPLE_crack:
13844       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13845       break;
13846
13847     case SAMPLE_bug:
13848       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13849       break;
13850
13851     case SAMPLE_tank:
13852       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13853       break;
13854
13855     case SAMPLE_android_clone:
13856       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13857       break;
13858
13859     case SAMPLE_android_move:
13860       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13861       break;
13862
13863     case SAMPLE_spring:
13864       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13865       break;
13866
13867     case SAMPLE_slurp:
13868       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
13869       break;
13870
13871     case SAMPLE_eater:
13872       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13873       break;
13874
13875     case SAMPLE_eater_eat:
13876       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13877       break;
13878
13879     case SAMPLE_alien:
13880       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13881       break;
13882
13883     case SAMPLE_collect:
13884       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13885       break;
13886
13887     case SAMPLE_diamond:
13888       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13889       break;
13890
13891     case SAMPLE_squash:
13892       /* !!! CHECK THIS !!! */
13893 #if 1
13894       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13895 #else
13896       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13897 #endif
13898       break;
13899
13900     case SAMPLE_wonderfall:
13901       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13902       break;
13903
13904     case SAMPLE_drip:
13905       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13906       break;
13907
13908     case SAMPLE_push:
13909       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13910       break;
13911
13912     case SAMPLE_dirt:
13913       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13914       break;
13915
13916     case SAMPLE_acid:
13917       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13918       break;
13919
13920     case SAMPLE_ball:
13921       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13922       break;
13923
13924     case SAMPLE_grow:
13925       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13926       break;
13927
13928     case SAMPLE_wonder:
13929       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13930       break;
13931
13932     case SAMPLE_door:
13933       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13934       break;
13935
13936     case SAMPLE_exit_open:
13937       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13938       break;
13939
13940     case SAMPLE_exit_leave:
13941       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13942       break;
13943
13944     case SAMPLE_dynamite:
13945       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13946       break;
13947
13948     case SAMPLE_tick:
13949       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13950       break;
13951
13952     case SAMPLE_press:
13953       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13954       break;
13955
13956     case SAMPLE_wheel:
13957       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13958       break;
13959
13960     case SAMPLE_boom:
13961       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13962       break;
13963
13964     case SAMPLE_die:
13965       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13966       break;
13967
13968     case SAMPLE_time:
13969       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13970       break;
13971
13972     default:
13973       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13974       break;
13975   }
13976 }
13977
13978 #if 0
13979 void ChangeTime(int value)
13980 {
13981   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
13982
13983   *time += value;
13984
13985   /* EMC game engine uses value from time counter of RND game engine */
13986   level.native_em_level->lev->time = *time;
13987
13988   DrawGameValue_Time(*time);
13989 }
13990
13991 void RaiseScore(int value)
13992 {
13993   /* EMC game engine and RND game engine have separate score counters */
13994   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
13995                 &level.native_em_level->lev->score : &local_player->score);
13996
13997   *score += value;
13998
13999   DrawGameValue_Score(*score);
14000 }
14001 #endif
14002
14003 void RaiseScore(int value)
14004 {
14005   local_player->score += value;
14006
14007   DrawGameValue_Score(local_player->score);
14008 }
14009
14010 void RaiseScoreElement(int element)
14011 {
14012   switch (element)
14013   {
14014     case EL_EMERALD:
14015     case EL_BD_DIAMOND:
14016     case EL_EMERALD_YELLOW:
14017     case EL_EMERALD_RED:
14018     case EL_EMERALD_PURPLE:
14019     case EL_SP_INFOTRON:
14020       RaiseScore(level.score[SC_EMERALD]);
14021       break;
14022     case EL_DIAMOND:
14023       RaiseScore(level.score[SC_DIAMOND]);
14024       break;
14025     case EL_CRYSTAL:
14026       RaiseScore(level.score[SC_CRYSTAL]);
14027       break;
14028     case EL_PEARL:
14029       RaiseScore(level.score[SC_PEARL]);
14030       break;
14031     case EL_BUG:
14032     case EL_BD_BUTTERFLY:
14033     case EL_SP_ELECTRON:
14034       RaiseScore(level.score[SC_BUG]);
14035       break;
14036     case EL_SPACESHIP:
14037     case EL_BD_FIREFLY:
14038     case EL_SP_SNIKSNAK:
14039       RaiseScore(level.score[SC_SPACESHIP]);
14040       break;
14041     case EL_YAMYAM:
14042     case EL_DARK_YAMYAM:
14043       RaiseScore(level.score[SC_YAMYAM]);
14044       break;
14045     case EL_ROBOT:
14046       RaiseScore(level.score[SC_ROBOT]);
14047       break;
14048     case EL_PACMAN:
14049       RaiseScore(level.score[SC_PACMAN]);
14050       break;
14051     case EL_NUT:
14052       RaiseScore(level.score[SC_NUT]);
14053       break;
14054     case EL_DYNAMITE:
14055     case EL_EM_DYNAMITE:
14056     case EL_SP_DISK_RED:
14057     case EL_DYNABOMB_INCREASE_NUMBER:
14058     case EL_DYNABOMB_INCREASE_SIZE:
14059     case EL_DYNABOMB_INCREASE_POWER:
14060       RaiseScore(level.score[SC_DYNAMITE]);
14061       break;
14062     case EL_SHIELD_NORMAL:
14063     case EL_SHIELD_DEADLY:
14064       RaiseScore(level.score[SC_SHIELD]);
14065       break;
14066     case EL_EXTRA_TIME:
14067       RaiseScore(level.extra_time_score);
14068       break;
14069     case EL_KEY_1:
14070     case EL_KEY_2:
14071     case EL_KEY_3:
14072     case EL_KEY_4:
14073     case EL_EM_KEY_1:
14074     case EL_EM_KEY_2:
14075     case EL_EM_KEY_3:
14076     case EL_EM_KEY_4:
14077     case EL_EMC_KEY_5:
14078     case EL_EMC_KEY_6:
14079     case EL_EMC_KEY_7:
14080     case EL_EMC_KEY_8:
14081     case EL_DC_KEY_WHITE:
14082       RaiseScore(level.score[SC_KEY]);
14083       break;
14084     default:
14085       RaiseScore(element_info[element].collect_score);
14086       break;
14087   }
14088 }
14089
14090 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14091 {
14092   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14093   {
14094 #if defined(NETWORK_AVALIABLE)
14095     if (options.network)
14096       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14097     else
14098 #endif
14099     {
14100       if (quick_quit)
14101       {
14102         game_status = GAME_MODE_MAIN;
14103
14104         DrawMainMenu();
14105       }
14106       else
14107       {
14108         FadeOut(REDRAW_FIELD);
14109
14110         game_status = GAME_MODE_MAIN;
14111
14112         DrawAndFadeInMainMenu(REDRAW_FIELD);
14113       }
14114     }
14115   }
14116   else          /* continue playing the game */
14117   {
14118     if (tape.playing && tape.deactivate_display)
14119       TapeDeactivateDisplayOff(TRUE);
14120
14121     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14122
14123     if (tape.playing && tape.deactivate_display)
14124       TapeDeactivateDisplayOn();
14125   }
14126 }
14127
14128 void RequestQuitGame(boolean ask_if_really_quit)
14129 {
14130   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14131   boolean skip_request = AllPlayersGone || quick_quit;
14132
14133   RequestQuitGameExt(skip_request, quick_quit,
14134                      "Do you really want to quit the game ?");
14135 }
14136
14137
14138 /* ------------------------------------------------------------------------- */
14139 /* random generator functions                                                */
14140 /* ------------------------------------------------------------------------- */
14141
14142 unsigned int InitEngineRandom_RND(long seed)
14143 {
14144   game.num_random_calls = 0;
14145
14146 #if 0
14147   unsigned int rnd_seed = InitEngineRandom(seed);
14148
14149   printf("::: START RND: %d\n", rnd_seed);
14150
14151   return rnd_seed;
14152 #else
14153
14154   return InitEngineRandom(seed);
14155
14156 #endif
14157
14158 }
14159
14160 unsigned int RND(int max)
14161 {
14162   if (max > 0)
14163   {
14164     game.num_random_calls++;
14165
14166     return GetEngineRandom(max);
14167   }
14168
14169   return 0;
14170 }
14171
14172
14173 /* ------------------------------------------------------------------------- */
14174 /* game engine snapshot handling functions                                   */
14175 /* ------------------------------------------------------------------------- */
14176
14177 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
14178
14179 struct EngineSnapshotInfo
14180 {
14181   /* runtime values for custom element collect score */
14182   int collect_score[NUM_CUSTOM_ELEMENTS];
14183
14184   /* runtime values for group element choice position */
14185   int choice_pos[NUM_GROUP_ELEMENTS];
14186
14187   /* runtime values for belt position animations */
14188   int belt_graphic[4 * NUM_BELT_PARTS];
14189   int belt_anim_mode[4 * NUM_BELT_PARTS];
14190 };
14191
14192 struct EngineSnapshotNodeInfo
14193 {
14194   void *buffer_orig;
14195   void *buffer_copy;
14196   int size;
14197 };
14198
14199 static struct EngineSnapshotInfo engine_snapshot_rnd;
14200 static ListNode *engine_snapshot_list = NULL;
14201 static char *snapshot_level_identifier = NULL;
14202 static int snapshot_level_nr = -1;
14203
14204 void FreeEngineSnapshot()
14205 {
14206   while (engine_snapshot_list != NULL)
14207     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14208                        checked_free);
14209
14210   setString(&snapshot_level_identifier, NULL);
14211   snapshot_level_nr = -1;
14212 }
14213
14214 static void SaveEngineSnapshotValues_RND()
14215 {
14216   static int belt_base_active_element[4] =
14217   {
14218     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14219     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14220     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14221     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14222   };
14223   int i, j;
14224
14225   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14226   {
14227     int element = EL_CUSTOM_START + i;
14228
14229     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14230   }
14231
14232   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14233   {
14234     int element = EL_GROUP_START + i;
14235
14236     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14237   }
14238
14239   for (i = 0; i < 4; i++)
14240   {
14241     for (j = 0; j < NUM_BELT_PARTS; j++)
14242     {
14243       int element = belt_base_active_element[i] + j;
14244       int graphic = el2img(element);
14245       int anim_mode = graphic_info[graphic].anim_mode;
14246
14247       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14248       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14249     }
14250   }
14251 }
14252
14253 static void LoadEngineSnapshotValues_RND()
14254 {
14255   unsigned long num_random_calls = game.num_random_calls;
14256   int i, j;
14257
14258   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14259   {
14260     int element = EL_CUSTOM_START + i;
14261
14262     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14263   }
14264
14265   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14266   {
14267     int element = EL_GROUP_START + i;
14268
14269     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14270   }
14271
14272   for (i = 0; i < 4; i++)
14273   {
14274     for (j = 0; j < NUM_BELT_PARTS; j++)
14275     {
14276       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14277       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14278
14279       graphic_info[graphic].anim_mode = anim_mode;
14280     }
14281   }
14282
14283   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14284   {
14285     InitRND(tape.random_seed);
14286     for (i = 0; i < num_random_calls; i++)
14287       RND(1);
14288   }
14289
14290   if (game.num_random_calls != num_random_calls)
14291   {
14292     Error(ERR_INFO, "number of random calls out of sync");
14293     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14294     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14295     Error(ERR_EXIT, "this should not happen -- please debug");
14296   }
14297 }
14298
14299 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14300 {
14301   struct EngineSnapshotNodeInfo *bi =
14302     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14303
14304   bi->buffer_orig = buffer;
14305   bi->buffer_copy = checked_malloc(size);
14306   bi->size = size;
14307
14308   memcpy(bi->buffer_copy, buffer, size);
14309
14310   addNodeToList(&engine_snapshot_list, NULL, bi);
14311 }
14312
14313 void SaveEngineSnapshot()
14314 {
14315   FreeEngineSnapshot();         /* free previous snapshot, if needed */
14316
14317   if (level_editor_test_game)   /* do not save snapshots from editor */
14318     return;
14319
14320   /* copy some special values to a structure better suited for the snapshot */
14321
14322   SaveEngineSnapshotValues_RND();
14323   SaveEngineSnapshotValues_EM();
14324
14325   /* save values stored in special snapshot structure */
14326
14327   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14328   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14329
14330   /* save further RND engine values */
14331
14332   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14333   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14334   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14335
14336   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14337   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14338   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14339   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14340
14341   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14342   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14343   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14344   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14345   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14346
14347   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14348   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14349   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14350
14351   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14352
14353   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14354
14355   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14356   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14357
14358   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14359   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14360   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14361   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14362   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14363   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14364   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14365   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14366   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14367   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14368   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14369   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14370   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14371   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14372   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14373   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14374   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
14375   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
14376
14377   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14378   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14379
14380   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14381   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14382   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14383
14384   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14385   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14386
14387   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14388   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14389   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14390   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14391   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14392
14393   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14394   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14395
14396   /* save level identification information */
14397
14398   setString(&snapshot_level_identifier, leveldir_current->identifier);
14399   snapshot_level_nr = level_nr;
14400
14401 #if 0
14402   ListNode *node = engine_snapshot_list;
14403   int num_bytes = 0;
14404
14405   while (node != NULL)
14406   {
14407     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14408
14409     node = node->next;
14410   }
14411
14412   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14413 #endif
14414 }
14415
14416 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
14417 {
14418   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
14419 }
14420
14421 void LoadEngineSnapshot()
14422 {
14423   ListNode *node = engine_snapshot_list;
14424
14425   if (engine_snapshot_list == NULL)
14426     return;
14427
14428   while (node != NULL)
14429   {
14430     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
14431
14432     node = node->next;
14433   }
14434
14435   /* restore special values from snapshot structure */
14436
14437   LoadEngineSnapshotValues_RND();
14438   LoadEngineSnapshotValues_EM();
14439 }
14440
14441 boolean CheckEngineSnapshot()
14442 {
14443   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14444           snapshot_level_nr == level_nr);
14445 }
14446
14447
14448 /* ---------- new game button stuff ---------------------------------------- */
14449
14450 /* graphic position values for game buttons */
14451 #define GAME_BUTTON_XSIZE       30
14452 #define GAME_BUTTON_YSIZE       30
14453 #define GAME_BUTTON_XPOS        5
14454 #define GAME_BUTTON_YPOS        215
14455 #define SOUND_BUTTON_XPOS       5
14456 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
14457
14458 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14459 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14460 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14461 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14462 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14463 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14464
14465 static struct
14466 {
14467   int *x, *y;
14468   int gd_x, gd_y;
14469   int gadget_id;
14470   char *infotext;
14471 } gamebutton_info[NUM_GAME_BUTTONS] =
14472 {
14473 #if 1
14474   {
14475     &game.button.stop.x,        &game.button.stop.y,
14476     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
14477     GAME_CTRL_ID_STOP,
14478     "stop game"
14479   },
14480   {
14481     &game.button.pause.x,       &game.button.pause.y,
14482     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
14483     GAME_CTRL_ID_PAUSE,
14484     "pause game"
14485   },
14486   {
14487     &game.button.play.x,        &game.button.play.y,
14488     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
14489     GAME_CTRL_ID_PLAY,
14490     "play game"
14491   },
14492   {
14493     &game.button.sound_music.x, &game.button.sound_music.y,
14494     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
14495     SOUND_CTRL_ID_MUSIC,
14496     "background music on/off"
14497   },
14498   {
14499     &game.button.sound_loops.x, &game.button.sound_loops.y,
14500     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
14501     SOUND_CTRL_ID_LOOPS,
14502     "sound loops on/off"
14503   },
14504   {
14505     &game.button.sound_simple.x,&game.button.sound_simple.y,
14506     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
14507     SOUND_CTRL_ID_SIMPLE,
14508     "normal sounds on/off"
14509   }
14510 #else
14511   {
14512     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
14513     GAME_CTRL_ID_STOP,
14514     "stop game"
14515   },
14516   {
14517     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
14518     GAME_CTRL_ID_PAUSE,
14519     "pause game"
14520   },
14521   {
14522     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
14523     GAME_CTRL_ID_PLAY,
14524     "play game"
14525   },
14526   {
14527     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
14528     SOUND_CTRL_ID_MUSIC,
14529     "background music on/off"
14530   },
14531   {
14532     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
14533     SOUND_CTRL_ID_LOOPS,
14534     "sound loops on/off"
14535   },
14536   {
14537     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
14538     SOUND_CTRL_ID_SIMPLE,
14539     "normal sounds on/off"
14540   }
14541 #endif
14542 };
14543
14544 void CreateGameButtons()
14545 {
14546   int i;
14547
14548   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14549   {
14550     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
14551     struct GadgetInfo *gi;
14552     int button_type;
14553     boolean checked;
14554     unsigned long event_mask;
14555     int x, y;
14556     int gd_xoffset, gd_yoffset;
14557     int gd_x1, gd_x2, gd_y1, gd_y2;
14558     int id = i;
14559
14560     x = DX + *gamebutton_info[i].x;
14561     y = DY + *gamebutton_info[i].y;
14562     gd_xoffset = gamebutton_info[i].gd_x;
14563     gd_yoffset = gamebutton_info[i].gd_y;
14564     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
14565     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
14566
14567     if (id == GAME_CTRL_ID_STOP ||
14568         id == GAME_CTRL_ID_PAUSE ||
14569         id == GAME_CTRL_ID_PLAY)
14570     {
14571       button_type = GD_TYPE_NORMAL_BUTTON;
14572       checked = FALSE;
14573       event_mask = GD_EVENT_RELEASED;
14574       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14575       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14576     }
14577     else
14578     {
14579       button_type = GD_TYPE_CHECK_BUTTON;
14580       checked =
14581         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14582          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14583          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14584       event_mask = GD_EVENT_PRESSED;
14585       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
14586       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14587     }
14588
14589     gi = CreateGadget(GDI_CUSTOM_ID, id,
14590                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14591 #if 1
14592                       GDI_X, x,
14593                       GDI_Y, y,
14594 #else
14595                       GDI_X, DX + gd_xoffset,
14596                       GDI_Y, DY + gd_yoffset,
14597 #endif
14598                       GDI_WIDTH, GAME_BUTTON_XSIZE,
14599                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
14600                       GDI_TYPE, button_type,
14601                       GDI_STATE, GD_BUTTON_UNPRESSED,
14602                       GDI_CHECKED, checked,
14603                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
14604                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
14605                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
14606                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
14607                       GDI_EVENT_MASK, event_mask,
14608                       GDI_CALLBACK_ACTION, HandleGameButtons,
14609                       GDI_END);
14610
14611     if (gi == NULL)
14612       Error(ERR_EXIT, "cannot create gadget");
14613
14614     game_gadget[id] = gi;
14615   }
14616 }
14617
14618 void FreeGameButtons()
14619 {
14620   int i;
14621
14622   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14623     FreeGadget(game_gadget[i]);
14624 }
14625
14626 static void MapGameButtons()
14627 {
14628   int i;
14629
14630   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14631     MapGadget(game_gadget[i]);
14632 }
14633
14634 void UnmapGameButtons()
14635 {
14636   int i;
14637
14638   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14639     UnmapGadget(game_gadget[i]);
14640 }
14641
14642 static void HandleGameButtons(struct GadgetInfo *gi)
14643 {
14644   int id = gi->custom_id;
14645
14646   if (game_status != GAME_MODE_PLAYING)
14647     return;
14648
14649   switch (id)
14650   {
14651     case GAME_CTRL_ID_STOP:
14652       if (tape.playing)
14653         TapeStop();
14654       else
14655         RequestQuitGame(TRUE);
14656       break;
14657
14658     case GAME_CTRL_ID_PAUSE:
14659       if (options.network)
14660       {
14661 #if defined(NETWORK_AVALIABLE)
14662         if (tape.pausing)
14663           SendToServer_ContinuePlaying();
14664         else
14665           SendToServer_PausePlaying();
14666 #endif
14667       }
14668       else
14669         TapeTogglePause(TAPE_TOGGLE_MANUAL);
14670       break;
14671
14672     case GAME_CTRL_ID_PLAY:
14673       if (tape.pausing)
14674       {
14675 #if defined(NETWORK_AVALIABLE)
14676         if (options.network)
14677           SendToServer_ContinuePlaying();
14678         else
14679 #endif
14680         {
14681           tape.pausing = FALSE;
14682           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
14683         }
14684       }
14685       break;
14686
14687     case SOUND_CTRL_ID_MUSIC:
14688       if (setup.sound_music)
14689       { 
14690         setup.sound_music = FALSE;
14691         FadeMusic();
14692       }
14693       else if (audio.music_available)
14694       { 
14695         setup.sound = setup.sound_music = TRUE;
14696
14697         SetAudioMode(setup.sound);
14698
14699         PlayLevelMusic();
14700       }
14701       break;
14702
14703     case SOUND_CTRL_ID_LOOPS:
14704       if (setup.sound_loops)
14705         setup.sound_loops = FALSE;
14706       else if (audio.loops_available)
14707       {
14708         setup.sound = setup.sound_loops = TRUE;
14709         SetAudioMode(setup.sound);
14710       }
14711       break;
14712
14713     case SOUND_CTRL_ID_SIMPLE:
14714       if (setup.sound_simple)
14715         setup.sound_simple = FALSE;
14716       else if (audio.sound_available)
14717       {
14718         setup.sound = setup.sound_simple = TRUE;
14719         SetAudioMode(setup.sound);
14720       }
14721       break;
14722
14723     default:
14724       break;
14725   }
14726 }