rnd-20070312-4-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61
62 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
63
64
65 /* for DigField() */
66 #define DF_NO_PUSH              0
67 #define DF_DIG                  1
68 #define DF_SNAP                 2
69
70 /* for MovePlayer() */
71 #define MP_NO_ACTION            0
72 #define MP_MOVING               1
73 #define MP_ACTION               2
74 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
75
76 /* for ScrollPlayer() */
77 #define SCROLL_INIT             0
78 #define SCROLL_GO_ON            1
79
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START          0
82 #define EX_TYPE_NONE            0
83 #define EX_TYPE_NORMAL          (1 << 0)
84 #define EX_TYPE_CENTER          (1 << 1)
85 #define EX_TYPE_BORDER          (1 << 2)
86 #define EX_TYPE_CROSS           (1 << 3)
87 #define EX_TYPE_DYNA            (1 << 4)
88 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
89
90 #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_KEYS                       3
144 #define GAME_CONTROL_SCORE                      4
145 #define GAME_CONTROL_TIME                       5
146 #define GAME_CONTROL_TIME_HH                    6
147 #define GAME_CONTROL_TIME_MM                    7
148 #define GAME_CONTROL_TIME_SS                    8
149 #define GAME_CONTROL_DROP_NEXT_1                9
150 #define GAME_CONTROL_DROP_NEXT_2                10
151 #define GAME_CONTROL_DROP_NEXT_3                11
152 #define GAME_CONTROL_DROP_NEXT_4                12
153 #define GAME_CONTROL_DROP_NEXT_5                13
154 #define GAME_CONTROL_DROP_NEXT_6                14
155 #define GAME_CONTROL_DROP_NEXT_7                15
156 #define GAME_CONTROL_DROP_NEXT_8                16
157 #define GAME_CONTROL_EMC_KEYS                   17
158 #define GAME_CONTROL_KEY_1                      18
159 #define GAME_CONTROL_KEY_2                      19
160 #define GAME_CONTROL_KEY_3                      20
161 #define GAME_CONTROL_KEY_4                      21
162 #define GAME_CONTROL_KEY_5                      22
163 #define GAME_CONTROL_KEY_6                      23
164 #define GAME_CONTROL_KEY_7                      24
165 #define GAME_CONTROL_KEY_8                      25
166 #define GAME_CONTROL_KEY_WHITE                  26
167 #define GAME_CONTROL_KEY_WHITE_COUNT            27
168 #define GAME_CONTROL_SHIELD_NORMAL              28
169 #define GAME_CONTROL_SHIELD_NORMAL_TIME         29
170 #define GAME_CONTROL_SHIELD_DEADLY              30
171 #define GAME_CONTROL_SHIELD_DEADLY_TIME         31
172 #define GAME_CONTROL_EXIT                       32
173 #define GAME_CONTROL_EM_EXIT                    33
174 #define GAME_CONTROL_SP_EXIT                    34
175 #define GAME_CONTROL_STEEL_EXIT                 35
176 #define GAME_CONTROL_EM_STEEL_EXIT              36
177 #define GAME_CONTROL_EMC_MAGIC_BALL             37
178 #define GAME_CONTROL_EMC_MAGIC_BALL_TIME        38
179 #define GAME_CONTROL_LIGHT_SWITCH               39
180 #define GAME_CONTROL_LIGHT_SWITCH_TIME          40
181 #define GAME_CONTROL_TIMEGATE_SWITCH            41
182 #define GAME_CONTROL_TIMEGATE_SWITCH_TIME       42
183 #define GAME_CONTROL_SWITCHGATE_SWITCH          43
184 #define GAME_CONTROL_EMC_LENSES                 44
185 #define GAME_CONTROL_EMC_LENSES_TIME            45
186 #define GAME_CONTROL_EMC_MAGNIFIER              46
187 #define GAME_CONTROL_EMC_MAGNIFIER_TIME         47
188 #define GAME_CONTROL_BALLOON_SWITCH             48
189 #define GAME_CONTROL_DYNABOMB_NUMBER            49
190 #define GAME_CONTROL_DYNABOMB_SIZE              50
191 #define GAME_CONTROL_DYNABOMB_POWER             51
192 #define GAME_CONTROL_PENGUINS                   52
193 #define GAME_CONTROL_SOKOBAN_OBJECTS            53
194 #define GAME_CONTROL_SOKOBAN_FIELDS             54
195 #define GAME_CONTROL_ROBOT_WHEEL                55
196 #define GAME_CONTROL_CONVEYOR_BELT_1            56
197 #define GAME_CONTROL_CONVEYOR_BELT_1_SWITCH     57
198 #define GAME_CONTROL_CONVEYOR_BELT_2            58
199 #define GAME_CONTROL_CONVEYOR_BELT_2_SWITCH     59
200 #define GAME_CONTROL_CONVEYOR_BELT_3            60
201 #define GAME_CONTROL_CONVEYOR_BELT_3_SWITCH     61
202 #define GAME_CONTROL_CONVEYOR_BELT_4            62
203 #define GAME_CONTROL_CONVEYOR_BELT_4_SWITCH     63
204 #define GAME_CONTROL_MAGIC_WALL                 64
205 #define GAME_CONTROL_MAGIC_WALL_TIME            65
206 #define GAME_CONTROL_BD_MAGIC_WALL              66
207 #define GAME_CONTROL_DC_MAGIC_WALL              67
208 #define GAME_CONTROL_PLAYER_NAME                68
209 #define GAME_CONTROL_LEVEL_NAME                 69
210 #define GAME_CONTROL_LEVEL_AUTHOR               70
211
212 struct GameControlInfo
213 {
214   int nr;
215
216   struct TextPosInfo *pos_text;
217   int type;
218   void *ptr;
219 };
220
221 static struct GameControlInfo game_controls[] =
222 {
223   {
224     GAME_CONTROL_LEVEL,
225     &game.panel.level,
226     TYPE_INTEGER,
227   },
228   {
229     GAME_CONTROL_GEMS,
230     &game.panel.gems,
231     TYPE_INTEGER,
232   },
233   {
234     GAME_CONTROL_INVENTORY,
235     &game.panel.inventory,
236     TYPE_INTEGER,
237   },
238   {
239     GAME_CONTROL_KEYS,
240     &game.panel.keys,
241     TYPE_INTEGER,
242   },
243   {
244     GAME_CONTROL_SCORE,
245     &game.panel.score,
246     TYPE_INTEGER,
247   },
248   {
249     GAME_CONTROL_TIME,
250     &game.panel.time,
251     TYPE_INTEGER,
252   },
253   {
254     GAME_CONTROL_TIME_HH,
255     &game.panel.time_hh,
256     TYPE_INTEGER,
257   },
258   {
259     GAME_CONTROL_TIME_MM,
260     &game.panel.time_mm,
261     TYPE_INTEGER,
262   },
263   {
264     GAME_CONTROL_TIME_SS,
265     &game.panel.time_ss,
266     TYPE_INTEGER,
267   },
268   {
269     GAME_CONTROL_DROP_NEXT_1,
270     &game.panel.drop_next_1,
271     TYPE_INTEGER,
272   },
273   {
274     GAME_CONTROL_DROP_NEXT_2,
275     &game.panel.drop_next_2,
276     TYPE_INTEGER,
277   },
278   {
279     GAME_CONTROL_DROP_NEXT_3,
280     &game.panel.drop_next_3,
281     TYPE_INTEGER,
282   },
283   {
284     GAME_CONTROL_DROP_NEXT_4,
285     &game.panel.drop_next_4,
286     TYPE_INTEGER,
287   },
288   {
289     GAME_CONTROL_DROP_NEXT_5,
290     &game.panel.drop_next_5,
291     TYPE_INTEGER,
292   },
293   {
294     GAME_CONTROL_DROP_NEXT_6,
295     &game.panel.drop_next_6,
296     TYPE_INTEGER,
297   },
298   {
299     GAME_CONTROL_DROP_NEXT_7,
300     &game.panel.drop_next_7,
301     TYPE_INTEGER,
302   },
303   {
304     GAME_CONTROL_DROP_NEXT_8,
305     &game.panel.drop_next_8,
306     TYPE_INTEGER,
307   },
308   {
309     GAME_CONTROL_EMC_KEYS,
310     &game.panel.emc_keys,
311     TYPE_INTEGER,
312   },
313   {
314     GAME_CONTROL_KEY_1,
315     &game.panel.key_1,
316     TYPE_INTEGER,
317   },
318   {
319     GAME_CONTROL_KEY_2,
320     &game.panel.key_2,
321     TYPE_INTEGER,
322   },
323   {
324     GAME_CONTROL_KEY_3,
325     &game.panel.key_3,
326     TYPE_INTEGER,
327   },
328   {
329     GAME_CONTROL_KEY_4,
330     &game.panel.key_4,
331     TYPE_INTEGER,
332   },
333   {
334     GAME_CONTROL_KEY_5,
335     &game.panel.key_5,
336     TYPE_INTEGER,
337   },
338   {
339     GAME_CONTROL_KEY_6,
340     &game.panel.key_6,
341     TYPE_INTEGER,
342   },
343   {
344     GAME_CONTROL_KEY_7,
345     &game.panel.key_7,
346     TYPE_INTEGER,
347   },
348   {
349     GAME_CONTROL_KEY_8,
350     &game.panel.key_8,
351     TYPE_INTEGER,
352   },
353   {
354     GAME_CONTROL_KEY_WHITE,
355     &game.panel.key_white,
356     TYPE_INTEGER,
357   },
358   {
359     GAME_CONTROL_KEY_WHITE_COUNT,
360     &game.panel.key_white_count,
361     TYPE_INTEGER,
362   },
363   {
364     GAME_CONTROL_SHIELD_NORMAL,
365     &game.panel.shield_normal,
366     TYPE_INTEGER,
367   },
368   {
369     GAME_CONTROL_SHIELD_NORMAL_TIME,
370     &game.panel.shield_normal_time,
371     TYPE_INTEGER,
372   },
373   {
374     GAME_CONTROL_SHIELD_DEADLY,
375     &game.panel.shield_deadly,
376     TYPE_INTEGER,
377   },
378   {
379     GAME_CONTROL_SHIELD_DEADLY_TIME,
380     &game.panel.shield_deadly_time,
381     TYPE_INTEGER,
382   },
383   {
384     GAME_CONTROL_EXIT,
385     &game.panel.exit,
386     TYPE_INTEGER,
387   },
388   {
389     GAME_CONTROL_EM_EXIT,
390     &game.panel.em_exit,
391     TYPE_INTEGER,
392   },
393   {
394     GAME_CONTROL_SP_EXIT,
395     &game.panel.sp_exit,
396     TYPE_INTEGER,
397   },
398   {
399     GAME_CONTROL_STEEL_EXIT,
400     &game.panel.steel_exit,
401     TYPE_INTEGER,
402   },
403   {
404     GAME_CONTROL_EM_STEEL_EXIT,
405     &game.panel.em_steel_exit,
406     TYPE_INTEGER,
407   },
408   {
409     GAME_CONTROL_EMC_MAGIC_BALL,
410     &game.panel.emc_magic_ball,
411     TYPE_INTEGER,
412   },
413   {
414     GAME_CONTROL_EMC_MAGIC_BALL_TIME,
415     &game.panel.emc_magic_ball_time,
416     TYPE_INTEGER,
417   },
418   {
419     GAME_CONTROL_LIGHT_SWITCH,
420     &game.panel.light_switch,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_CONTROL_LIGHT_SWITCH_TIME,
425     &game.panel.light_switch_time,
426     TYPE_INTEGER,
427   },
428   {
429     GAME_CONTROL_TIMEGATE_SWITCH,
430     &game.panel.timegate_switch,
431     TYPE_INTEGER,
432   },
433   {
434     GAME_CONTROL_TIMEGATE_SWITCH_TIME,
435     &game.panel.timegate_switch_time,
436     TYPE_INTEGER,
437   },
438   {
439     GAME_CONTROL_SWITCHGATE_SWITCH,
440     &game.panel.switchgate_switch,
441     TYPE_INTEGER,
442   },
443   {
444     GAME_CONTROL_EMC_LENSES,
445     &game.panel.emc_lenses,
446     TYPE_INTEGER,
447   },
448   {
449     GAME_CONTROL_EMC_LENSES_TIME,
450     &game.panel.emc_lenses_time,
451     TYPE_INTEGER,
452   },
453   {
454     GAME_CONTROL_EMC_MAGNIFIER,
455     &game.panel.emc_magnifier,
456     TYPE_INTEGER,
457   },
458   {
459     GAME_CONTROL_EMC_MAGNIFIER_TIME,
460     &game.panel.emc_magnifier_time,
461     TYPE_INTEGER,
462   },
463   {
464     GAME_CONTROL_BALLOON_SWITCH,
465     &game.panel.balloon_switch,
466     TYPE_INTEGER,
467   },
468   {
469     GAME_CONTROL_DYNABOMB_NUMBER,
470     &game.panel.dynabomb_number,
471     TYPE_INTEGER,
472   },
473   {
474     GAME_CONTROL_DYNABOMB_SIZE,
475     &game.panel.dynabomb_size,
476     TYPE_INTEGER,
477   },
478   {
479     GAME_CONTROL_DYNABOMB_POWER,
480     &game.panel.dynabomb_power,
481     TYPE_INTEGER,
482   },
483   {
484     GAME_CONTROL_PENGUINS,
485     &game.panel.penguins,
486     TYPE_INTEGER,
487   },
488   {
489     GAME_CONTROL_SOKOBAN_OBJECTS,
490     &game.panel.sokoban_objects,
491     TYPE_INTEGER,
492   },
493   {
494     GAME_CONTROL_SOKOBAN_FIELDS,
495     &game.panel.sokoban_fields,
496     TYPE_INTEGER,
497   },
498   {
499     GAME_CONTROL_ROBOT_WHEEL,
500     &game.panel.robot_wheel,
501     TYPE_INTEGER,
502   },
503   {
504     GAME_CONTROL_CONVEYOR_BELT_1,
505     &game.panel.conveyor_belt_1,
506     TYPE_INTEGER,
507   },
508   {
509     GAME_CONTROL_CONVEYOR_BELT_1_SWITCH,
510     &game.panel.conveyor_belt_1_switch,
511     TYPE_INTEGER,
512   },
513   {
514     GAME_CONTROL_CONVEYOR_BELT_2,
515     &game.panel.conveyor_belt_2,
516     TYPE_INTEGER,
517   },
518   {
519     GAME_CONTROL_CONVEYOR_BELT_2_SWITCH,
520     &game.panel.conveyor_belt_2_switch,
521     TYPE_INTEGER,
522   },
523   {
524     GAME_CONTROL_CONVEYOR_BELT_3,
525     &game.panel.conveyor_belt_3,
526     TYPE_INTEGER,
527   },
528   {
529     GAME_CONTROL_CONVEYOR_BELT_3_SWITCH,
530     &game.panel.conveyor_belt_3_switch,
531     TYPE_INTEGER,
532   },
533   {
534     GAME_CONTROL_CONVEYOR_BELT_4,
535     &game.panel.conveyor_belt_4,
536     TYPE_INTEGER,
537   },
538   {
539     GAME_CONTROL_CONVEYOR_BELT_4_SWITCH,
540     &game.panel.conveyor_belt_4_switch,
541     TYPE_INTEGER,
542   },
543   {
544     GAME_CONTROL_MAGIC_WALL,
545     &game.panel.magic_wall,
546     TYPE_INTEGER,
547   },
548   {
549     GAME_CONTROL_MAGIC_WALL_TIME,
550     &game.panel.magic_wall_time,
551     TYPE_INTEGER,
552   },
553   {
554     GAME_CONTROL_BD_MAGIC_WALL,
555     &game.panel.bd_magic_wall,
556     TYPE_INTEGER,
557   },
558   {
559     GAME_CONTROL_DC_MAGIC_WALL,
560     &game.panel.dc_magic_wall,
561     TYPE_INTEGER,
562   },
563   {
564     GAME_CONTROL_PLAYER_NAME,
565     &game.panel.player_name,
566     TYPE_INTEGER,
567   },
568   {
569     GAME_CONTROL_LEVEL_NAME,
570     &game.panel.level_name,
571     TYPE_INTEGER,
572   },
573   {
574     GAME_CONTROL_LEVEL_AUTHOR,
575     &game.panel.level_author,
576     TYPE_INTEGER,
577   },
578
579   {
580     -1,
581     NULL,
582     -1,
583     NULL
584   }
585 };
586 #endif
587
588
589 /* values for delayed check of falling and moving elements and for collision */
590 #define CHECK_DELAY_MOVING      3
591 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
592 #define CHECK_DELAY_COLLISION   2
593 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
594
595 /* values for initial player move delay (initial delay counter value) */
596 #define INITIAL_MOVE_DELAY_OFF  -1
597 #define INITIAL_MOVE_DELAY_ON   0
598
599 /* values for player movement speed (which is in fact a delay value) */
600 #define MOVE_DELAY_MIN_SPEED    32
601 #define MOVE_DELAY_NORMAL_SPEED 8
602 #define MOVE_DELAY_HIGH_SPEED   4
603 #define MOVE_DELAY_MAX_SPEED    1
604
605 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
606 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
607
608 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
609 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
610
611 /* values for other actions */
612 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
613 #define MOVE_STEPSIZE_MIN       (1)
614 #define MOVE_STEPSIZE_MAX       (TILEX)
615
616 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
617 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
618
619 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
620
621 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
622                                  RND(element_info[e].push_delay_random))
623 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
624                                  RND(element_info[e].drop_delay_random))
625 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
626                                  RND(element_info[e].move_delay_random))
627 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
628                                     (element_info[e].move_delay_random))
629 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
630                                  RND(element_info[e].ce_value_random_initial))
631 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
632 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
633                                  RND((c)->delay_random * (c)->delay_frames))
634 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
635                                  RND((c)->delay_random))
636
637
638 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
639          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
640
641 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
642         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
643          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
644          (be) + (e) - EL_SELF)
645
646 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
647         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
648          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
649          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
650          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
651          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
652          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
653          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
654          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
655          (e))
656
657 #define CAN_GROW_INTO(e)                                                \
658         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
659
660 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
661                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
662                                         (condition)))
663
664 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
665                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
666                                         (CAN_MOVE_INTO_ACID(e) &&       \
667                                          Feld[x][y] == EL_ACID) ||      \
668                                         (condition)))
669
670 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
671                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
672                                         (CAN_MOVE_INTO_ACID(e) &&       \
673                                          Feld[x][y] == EL_ACID) ||      \
674                                         (condition)))
675
676 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
677                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
678                                         (condition) ||                  \
679                                         (CAN_MOVE_INTO_ACID(e) &&       \
680                                          Feld[x][y] == EL_ACID) ||      \
681                                         (DONT_COLLIDE_WITH(e) &&        \
682                                          IS_PLAYER(x, y) &&             \
683                                          !PLAYER_ENEMY_PROTECTED(x, y))))
684
685 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
686         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
687
688 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
689         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
690
691 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
692         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
693
694 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
695         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
696                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
697
698 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
699         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
700
701 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
702         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
703
704 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
705         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
706
707 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
708         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
709
710 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
711         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
712
713 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
714         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
715                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
716                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
717                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
718                                                  IS_FOOD_PENGUIN(Feld[x][y])))
719 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
720         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
721
722 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
723         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
724
725 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
726         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
727
728 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
729         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
730                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
731
732 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
733
734 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
735                 (!IS_PLAYER(x, y) &&                                    \
736                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
737
738 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
739         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
740
741 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
742 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
743
744 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
745 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
746 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
747 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
748
749 /* game button identifiers */
750 #define GAME_CTRL_ID_STOP               0
751 #define GAME_CTRL_ID_PAUSE              1
752 #define GAME_CTRL_ID_PLAY               2
753 #define SOUND_CTRL_ID_MUSIC             3
754 #define SOUND_CTRL_ID_LOOPS             4
755 #define SOUND_CTRL_ID_SIMPLE            5
756
757 #define NUM_GAME_BUTTONS                6
758
759
760 /* forward declaration for internal use */
761
762 static void CreateField(int, int, int);
763
764 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
765 static void AdvanceFrameAndPlayerCounters(int);
766
767 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
768 static boolean MovePlayer(struct PlayerInfo *, int, int);
769 static void ScrollPlayer(struct PlayerInfo *, int);
770 static void ScrollScreen(struct PlayerInfo *, int);
771
772 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
773
774 static void InitBeltMovement(void);
775 static void CloseAllOpenTimegates(void);
776 static void CheckGravityMovement(struct PlayerInfo *);
777 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
778 static void KillPlayerUnlessEnemyProtected(int, int);
779 static void KillPlayerUnlessExplosionProtected(int, int);
780
781 static void TestIfPlayerTouchesCustomElement(int, int);
782 static void TestIfElementTouchesCustomElement(int, int);
783 static void TestIfElementHitsCustomElement(int, int, int);
784 #if 0
785 static void TestIfElementSmashesCustomElement(int, int, int);
786 #endif
787
788 static void HandleElementChange(int, int, int);
789 static void ExecuteCustomElementAction(int, int, int, int);
790 static boolean ChangeElement(int, int, int, int);
791
792 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
793 #define CheckTriggeredElementChange(x, y, e, ev)                        \
794         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
795 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
796         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
797 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
798         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
799 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
800         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
801
802 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
803 #define CheckElementChange(x, y, e, te, ev)                             \
804         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
805 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
806         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
807 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
808         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
809
810 static void PlayLevelSound(int, int, int);
811 static void PlayLevelSoundNearest(int, int, int);
812 static void PlayLevelSoundAction(int, int, int);
813 static void PlayLevelSoundElementAction(int, int, int, int);
814 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
815 static void PlayLevelSoundActionIfLoop(int, int, int);
816 static void StopLevelSoundActionIfLoop(int, int, int);
817 static void PlayLevelMusic();
818
819 static void MapGameButtons();
820 static void HandleGameButtons(struct GadgetInfo *);
821
822 int AmoebeNachbarNr(int, int);
823 void AmoebeUmwandeln(int, int);
824 void ContinueMoving(int, int);
825 void Bang(int, int);
826 void InitMovDir(int, int);
827 void InitAmoebaNr(int, int);
828 int NewHiScore(void);
829
830 void TestIfGoodThingHitsBadThing(int, int, int);
831 void TestIfBadThingHitsGoodThing(int, int, int);
832 void TestIfPlayerTouchesBadThing(int, int);
833 void TestIfPlayerRunsIntoBadThing(int, int, int);
834 void TestIfBadThingTouchesPlayer(int, int);
835 void TestIfBadThingRunsIntoPlayer(int, int, int);
836 void TestIfFriendTouchesBadThing(int, int);
837 void TestIfBadThingTouchesFriend(int, int);
838 void TestIfBadThingTouchesOtherBadThing(int, int);
839
840 void KillPlayer(struct PlayerInfo *);
841 void BuryPlayer(struct PlayerInfo *);
842 void RemovePlayer(struct PlayerInfo *);
843
844 boolean SnapField(struct PlayerInfo *, int, int);
845 boolean DropElement(struct PlayerInfo *);
846
847 static int getInvisibleActiveFromInvisibleElement(int);
848 static int getInvisibleFromInvisibleActiveElement(int);
849
850 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
851
852 /* for detection of endless loops, caused by custom element programming */
853 /* (using maximal playfield width x 10 is just a rough approximation) */
854 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
855
856 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
857 {                                                                       \
858   if (recursion_loop_detected)                                          \
859     return (rc);                                                        \
860                                                                         \
861   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
862   {                                                                     \
863     recursion_loop_detected = TRUE;                                     \
864     recursion_loop_element = (e);                                       \
865   }                                                                     \
866                                                                         \
867   recursion_loop_depth++;                                               \
868 }
869
870 #define RECURSION_LOOP_DETECTION_END()                                  \
871 {                                                                       \
872   recursion_loop_depth--;                                               \
873 }
874
875 static int recursion_loop_depth;
876 static boolean recursion_loop_detected;
877 static boolean recursion_loop_element;
878
879
880 /* ------------------------------------------------------------------------- */
881 /* definition of elements that automatically change to other elements after  */
882 /* a specified time, eventually calling a function when changing             */
883 /* ------------------------------------------------------------------------- */
884
885 /* forward declaration for changer functions */
886 static void InitBuggyBase(int, int);
887 static void WarnBuggyBase(int, int);
888
889 static void InitTrap(int, int);
890 static void ActivateTrap(int, int);
891 static void ChangeActiveTrap(int, int);
892
893 static void InitRobotWheel(int, int);
894 static void RunRobotWheel(int, int);
895 static void StopRobotWheel(int, int);
896
897 static void InitTimegateWheel(int, int);
898 static void RunTimegateWheel(int, int);
899
900 static void InitMagicBallDelay(int, int);
901 static void ActivateMagicBall(int, int);
902
903 struct ChangingElementInfo
904 {
905   int element;
906   int target_element;
907   int change_delay;
908   void (*pre_change_function)(int x, int y);
909   void (*change_function)(int x, int y);
910   void (*post_change_function)(int x, int y);
911 };
912
913 static struct ChangingElementInfo change_delay_list[] =
914 {
915   {
916     EL_NUT_BREAKING,
917     EL_EMERALD,
918     6,
919     NULL,
920     NULL,
921     NULL
922   },
923   {
924     EL_PEARL_BREAKING,
925     EL_EMPTY,
926     8,
927     NULL,
928     NULL,
929     NULL
930   },
931   {
932     EL_EXIT_OPENING,
933     EL_EXIT_OPEN,
934     29,
935     NULL,
936     NULL,
937     NULL
938   },
939   {
940     EL_EXIT_CLOSING,
941     EL_EXIT_CLOSED,
942     29,
943     NULL,
944     NULL,
945     NULL
946   },
947   {
948     EL_STEEL_EXIT_OPENING,
949     EL_STEEL_EXIT_OPEN,
950     29,
951     NULL,
952     NULL,
953     NULL
954   },
955   {
956     EL_STEEL_EXIT_CLOSING,
957     EL_STEEL_EXIT_CLOSED,
958     29,
959     NULL,
960     NULL,
961     NULL
962   },
963   {
964     EL_EM_EXIT_OPENING,
965     EL_EM_EXIT_OPEN,
966     29,
967     NULL,
968     NULL,
969     NULL
970   },
971   {
972     EL_EM_EXIT_CLOSING,
973 #if 1
974     EL_EMPTY,
975 #else
976     EL_EM_EXIT_CLOSED,
977 #endif
978     29,
979     NULL,
980     NULL,
981     NULL
982   },
983   {
984     EL_EM_STEEL_EXIT_OPENING,
985     EL_EM_STEEL_EXIT_OPEN,
986     29,
987     NULL,
988     NULL,
989     NULL
990   },
991   {
992     EL_EM_STEEL_EXIT_CLOSING,
993 #if 1
994     EL_STEELWALL,
995 #else
996     EL_EM_STEEL_EXIT_CLOSED,
997 #endif
998     29,
999     NULL,
1000     NULL,
1001     NULL
1002   },
1003   {
1004     EL_SP_EXIT_OPENING,
1005     EL_SP_EXIT_OPEN,
1006     29,
1007     NULL,
1008     NULL,
1009     NULL
1010   },
1011   {
1012     EL_SP_EXIT_CLOSING,
1013     EL_SP_EXIT_CLOSED,
1014     29,
1015     NULL,
1016     NULL,
1017     NULL
1018   },
1019   {
1020     EL_SWITCHGATE_OPENING,
1021     EL_SWITCHGATE_OPEN,
1022     29,
1023     NULL,
1024     NULL,
1025     NULL
1026   },
1027   {
1028     EL_SWITCHGATE_CLOSING,
1029     EL_SWITCHGATE_CLOSED,
1030     29,
1031     NULL,
1032     NULL,
1033     NULL
1034   },
1035   {
1036     EL_TIMEGATE_OPENING,
1037     EL_TIMEGATE_OPEN,
1038     29,
1039     NULL,
1040     NULL,
1041     NULL
1042   },
1043   {
1044     EL_TIMEGATE_CLOSING,
1045     EL_TIMEGATE_CLOSED,
1046     29,
1047     NULL,
1048     NULL,
1049     NULL
1050   },
1051
1052   {
1053     EL_ACID_SPLASH_LEFT,
1054     EL_EMPTY,
1055     8,
1056     NULL,
1057     NULL,
1058     NULL
1059   },
1060   {
1061     EL_ACID_SPLASH_RIGHT,
1062     EL_EMPTY,
1063     8,
1064     NULL,
1065     NULL,
1066     NULL
1067   },
1068   {
1069     EL_SP_BUGGY_BASE,
1070     EL_SP_BUGGY_BASE_ACTIVATING,
1071     0,
1072     InitBuggyBase,
1073     NULL,
1074     NULL
1075   },
1076   {
1077     EL_SP_BUGGY_BASE_ACTIVATING,
1078     EL_SP_BUGGY_BASE_ACTIVE,
1079     0,
1080     InitBuggyBase,
1081     NULL,
1082     NULL
1083   },
1084   {
1085     EL_SP_BUGGY_BASE_ACTIVE,
1086     EL_SP_BUGGY_BASE,
1087     0,
1088     InitBuggyBase,
1089     WarnBuggyBase,
1090     NULL
1091   },
1092   {
1093     EL_TRAP,
1094     EL_TRAP_ACTIVE,
1095     0,
1096     InitTrap,
1097     NULL,
1098     ActivateTrap
1099   },
1100   {
1101     EL_TRAP_ACTIVE,
1102     EL_TRAP,
1103     31,
1104     NULL,
1105     ChangeActiveTrap,
1106     NULL
1107   },
1108   {
1109     EL_ROBOT_WHEEL_ACTIVE,
1110     EL_ROBOT_WHEEL,
1111     0,
1112     InitRobotWheel,
1113     RunRobotWheel,
1114     StopRobotWheel
1115   },
1116   {
1117     EL_TIMEGATE_SWITCH_ACTIVE,
1118     EL_TIMEGATE_SWITCH,
1119     0,
1120     InitTimegateWheel,
1121     RunTimegateWheel,
1122     NULL
1123   },
1124   {
1125     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1126     EL_DC_TIMEGATE_SWITCH,
1127     0,
1128     InitTimegateWheel,
1129     RunTimegateWheel,
1130     NULL
1131   },
1132   {
1133     EL_EMC_MAGIC_BALL_ACTIVE,
1134     EL_EMC_MAGIC_BALL_ACTIVE,
1135     0,
1136     InitMagicBallDelay,
1137     NULL,
1138     ActivateMagicBall
1139   },
1140   {
1141     EL_EMC_SPRING_BUMPER_ACTIVE,
1142     EL_EMC_SPRING_BUMPER,
1143     8,
1144     NULL,
1145     NULL,
1146     NULL
1147   },
1148   {
1149     EL_DIAGONAL_SHRINKING,
1150     EL_UNDEFINED,
1151     0,
1152     NULL,
1153     NULL,
1154     NULL
1155   },
1156   {
1157     EL_DIAGONAL_GROWING,
1158     EL_UNDEFINED,
1159     0,
1160     NULL,
1161     NULL,
1162     NULL,
1163   },
1164
1165   {
1166     EL_UNDEFINED,
1167     EL_UNDEFINED,
1168     -1,
1169     NULL,
1170     NULL,
1171     NULL
1172   }
1173 };
1174
1175 struct
1176 {
1177   int element;
1178   int push_delay_fixed, push_delay_random;
1179 }
1180 push_delay_list[] =
1181 {
1182   { EL_SPRING,                  0, 0 },
1183   { EL_BALLOON,                 0, 0 },
1184
1185   { EL_SOKOBAN_OBJECT,          2, 0 },
1186   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1187   { EL_SATELLITE,               2, 0 },
1188   { EL_SP_DISK_YELLOW,          2, 0 },
1189
1190   { EL_UNDEFINED,               0, 0 },
1191 };
1192
1193 struct
1194 {
1195   int element;
1196   int move_stepsize;
1197 }
1198 move_stepsize_list[] =
1199 {
1200   { EL_AMOEBA_DROP,             2 },
1201   { EL_AMOEBA_DROPPING,         2 },
1202   { EL_QUICKSAND_FILLING,       1 },
1203   { EL_QUICKSAND_EMPTYING,      1 },
1204   { EL_QUICKSAND_FAST_FILLING,  2 },
1205   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1206   { EL_MAGIC_WALL_FILLING,      2 },
1207   { EL_MAGIC_WALL_EMPTYING,     2 },
1208   { EL_BD_MAGIC_WALL_FILLING,   2 },
1209   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1210   { EL_DC_MAGIC_WALL_FILLING,   2 },
1211   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1212
1213   { EL_UNDEFINED,               0 },
1214 };
1215
1216 struct
1217 {
1218   int element;
1219   int count;
1220 }
1221 collect_count_list[] =
1222 {
1223   { EL_EMERALD,                 1 },
1224   { EL_BD_DIAMOND,              1 },
1225   { EL_EMERALD_YELLOW,          1 },
1226   { EL_EMERALD_RED,             1 },
1227   { EL_EMERALD_PURPLE,          1 },
1228   { EL_DIAMOND,                 3 },
1229   { EL_SP_INFOTRON,             1 },
1230   { EL_PEARL,                   5 },
1231   { EL_CRYSTAL,                 8 },
1232
1233   { EL_UNDEFINED,               0 },
1234 };
1235
1236 struct
1237 {
1238   int element;
1239   int direction;
1240 }
1241 access_direction_list[] =
1242 {
1243   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1244   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1245   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1246   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1247   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1248   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1249   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1250   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1251   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1252   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1253   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1254
1255   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1256   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1257   { EL_SP_PORT_UP,                                                   MV_DOWN },
1258   { EL_SP_PORT_DOWN,                                         MV_UP           },
1259   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1260   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1261   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1262   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1263   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1264   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1265   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1266   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1267   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1268   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1269   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1270   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1271   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1272   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1273   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1274
1275   { EL_UNDEFINED,                       MV_NONE                              }
1276 };
1277
1278 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1279
1280 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1281 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1282 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1283                                  IS_JUST_CHANGING(x, y))
1284
1285 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1286
1287 /* static variables for playfield scan mode (scanning forward or backward) */
1288 static int playfield_scan_start_x = 0;
1289 static int playfield_scan_start_y = 0;
1290 static int playfield_scan_delta_x = 1;
1291 static int playfield_scan_delta_y = 1;
1292
1293 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1294                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1295                                      (y) += playfield_scan_delta_y)     \
1296                                 for ((x) = playfield_scan_start_x;      \
1297                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1298                                      (x) += playfield_scan_delta_x)
1299
1300 #ifdef DEBUG
1301 void DEBUG_SetMaximumDynamite()
1302 {
1303   int i;
1304
1305   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1306     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1307       local_player->inventory_element[local_player->inventory_size++] =
1308         EL_DYNAMITE;
1309 }
1310 #endif
1311
1312 static void InitPlayfieldScanModeVars()
1313 {
1314   if (game.use_reverse_scan_direction)
1315   {
1316     playfield_scan_start_x = lev_fieldx - 1;
1317     playfield_scan_start_y = lev_fieldy - 1;
1318
1319     playfield_scan_delta_x = -1;
1320     playfield_scan_delta_y = -1;
1321   }
1322   else
1323   {
1324     playfield_scan_start_x = 0;
1325     playfield_scan_start_y = 0;
1326
1327     playfield_scan_delta_x = 1;
1328     playfield_scan_delta_y = 1;
1329   }
1330 }
1331
1332 static void InitPlayfieldScanMode(int mode)
1333 {
1334   game.use_reverse_scan_direction =
1335     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1336
1337   InitPlayfieldScanModeVars();
1338 }
1339
1340 static int get_move_delay_from_stepsize(int move_stepsize)
1341 {
1342   move_stepsize =
1343     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1344
1345   /* make sure that stepsize value is always a power of 2 */
1346   move_stepsize = (1 << log_2(move_stepsize));
1347
1348   return TILEX / move_stepsize;
1349 }
1350
1351 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1352                                boolean init_game)
1353 {
1354   int player_nr = player->index_nr;
1355   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1356   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1357
1358   /* do no immediately change move delay -- the player might just be moving */
1359   player->move_delay_value_next = move_delay;
1360
1361   /* information if player can move must be set separately */
1362   player->cannot_move = cannot_move;
1363
1364   if (init_game)
1365   {
1366     player->move_delay       = game.initial_move_delay[player_nr];
1367     player->move_delay_value = game.initial_move_delay_value[player_nr];
1368
1369     player->move_delay_value_next = -1;
1370
1371     player->move_delay_reset_counter = 0;
1372   }
1373 }
1374
1375 void GetPlayerConfig()
1376 {
1377   GameFrameDelay = setup.game_frame_delay;
1378
1379   if (!audio.sound_available)
1380     setup.sound_simple = FALSE;
1381
1382   if (!audio.loops_available)
1383     setup.sound_loops = FALSE;
1384
1385   if (!audio.music_available)
1386     setup.sound_music = FALSE;
1387
1388   if (!video.fullscreen_available)
1389     setup.fullscreen = FALSE;
1390
1391   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1392
1393   SetAudioMode(setup.sound);
1394   InitJoysticks();
1395 }
1396
1397 int GetElementFromGroupElement(int element)
1398 {
1399   if (IS_GROUP_ELEMENT(element))
1400   {
1401     struct ElementGroupInfo *group = element_info[element].group;
1402     int last_anim_random_frame = gfx.anim_random_frame;
1403     int element_pos;
1404
1405     if (group->choice_mode == ANIM_RANDOM)
1406       gfx.anim_random_frame = RND(group->num_elements_resolved);
1407
1408     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1409                                     group->choice_mode, 0,
1410                                     group->choice_pos);
1411
1412     if (group->choice_mode == ANIM_RANDOM)
1413       gfx.anim_random_frame = last_anim_random_frame;
1414
1415     group->choice_pos++;
1416
1417     element = group->element_resolved[element_pos];
1418   }
1419
1420   return element;
1421 }
1422
1423 static void InitPlayerField(int x, int y, int element, boolean init_game)
1424 {
1425   if (element == EL_SP_MURPHY)
1426   {
1427     if (init_game)
1428     {
1429       if (stored_player[0].present)
1430       {
1431         Feld[x][y] = EL_SP_MURPHY_CLONE;
1432
1433         return;
1434       }
1435       else
1436       {
1437         stored_player[0].use_murphy = TRUE;
1438
1439         if (!level.use_artwork_element[0])
1440           stored_player[0].artwork_element = EL_SP_MURPHY;
1441       }
1442
1443       Feld[x][y] = EL_PLAYER_1;
1444     }
1445   }
1446
1447   if (init_game)
1448   {
1449     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1450     int jx = player->jx, jy = player->jy;
1451
1452     player->present = TRUE;
1453
1454     player->block_last_field = (element == EL_SP_MURPHY ?
1455                                 level.sp_block_last_field :
1456                                 level.block_last_field);
1457
1458     /* ---------- initialize player's last field block delay --------------- */
1459
1460     /* always start with reliable default value (no adjustment needed) */
1461     player->block_delay_adjustment = 0;
1462
1463     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1464     if (player->block_last_field && element == EL_SP_MURPHY)
1465       player->block_delay_adjustment = 1;
1466
1467     /* special case 2: in game engines before 3.1.1, blocking was different */
1468     if (game.use_block_last_field_bug)
1469       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1470
1471     if (!options.network || player->connected)
1472     {
1473       player->active = TRUE;
1474
1475       /* remove potentially duplicate players */
1476       if (StorePlayer[jx][jy] == Feld[x][y])
1477         StorePlayer[jx][jy] = 0;
1478
1479       StorePlayer[x][y] = Feld[x][y];
1480
1481       if (options.debug)
1482       {
1483         printf("Player %d activated.\n", player->element_nr);
1484         printf("[Local player is %d and currently %s.]\n",
1485                local_player->element_nr,
1486                local_player->active ? "active" : "not active");
1487       }
1488     }
1489
1490     Feld[x][y] = EL_EMPTY;
1491
1492     player->jx = player->last_jx = x;
1493     player->jy = player->last_jy = y;
1494   }
1495 }
1496
1497 static void InitField(int x, int y, boolean init_game)
1498 {
1499   int element = Feld[x][y];
1500
1501   switch (element)
1502   {
1503     case EL_SP_MURPHY:
1504     case EL_PLAYER_1:
1505     case EL_PLAYER_2:
1506     case EL_PLAYER_3:
1507     case EL_PLAYER_4:
1508       InitPlayerField(x, y, element, init_game);
1509       break;
1510
1511     case EL_SOKOBAN_FIELD_PLAYER:
1512       element = Feld[x][y] = EL_PLAYER_1;
1513       InitField(x, y, init_game);
1514
1515       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1516       InitField(x, y, init_game);
1517       break;
1518
1519     case EL_SOKOBAN_FIELD_EMPTY:
1520       local_player->sokobanfields_still_needed++;
1521       break;
1522
1523     case EL_STONEBLOCK:
1524       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1525         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1526       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1527         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1528       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1529         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1530       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1531         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1532       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1533         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1534       break;
1535
1536     case EL_BUG:
1537     case EL_BUG_RIGHT:
1538     case EL_BUG_UP:
1539     case EL_BUG_LEFT:
1540     case EL_BUG_DOWN:
1541     case EL_SPACESHIP:
1542     case EL_SPACESHIP_RIGHT:
1543     case EL_SPACESHIP_UP:
1544     case EL_SPACESHIP_LEFT:
1545     case EL_SPACESHIP_DOWN:
1546     case EL_BD_BUTTERFLY:
1547     case EL_BD_BUTTERFLY_RIGHT:
1548     case EL_BD_BUTTERFLY_UP:
1549     case EL_BD_BUTTERFLY_LEFT:
1550     case EL_BD_BUTTERFLY_DOWN:
1551     case EL_BD_FIREFLY:
1552     case EL_BD_FIREFLY_RIGHT:
1553     case EL_BD_FIREFLY_UP:
1554     case EL_BD_FIREFLY_LEFT:
1555     case EL_BD_FIREFLY_DOWN:
1556     case EL_PACMAN_RIGHT:
1557     case EL_PACMAN_UP:
1558     case EL_PACMAN_LEFT:
1559     case EL_PACMAN_DOWN:
1560     case EL_YAMYAM:
1561     case EL_YAMYAM_LEFT:
1562     case EL_YAMYAM_RIGHT:
1563     case EL_YAMYAM_UP:
1564     case EL_YAMYAM_DOWN:
1565     case EL_DARK_YAMYAM:
1566     case EL_ROBOT:
1567     case EL_PACMAN:
1568     case EL_SP_SNIKSNAK:
1569     case EL_SP_ELECTRON:
1570     case EL_MOLE:
1571     case EL_MOLE_LEFT:
1572     case EL_MOLE_RIGHT:
1573     case EL_MOLE_UP:
1574     case EL_MOLE_DOWN:
1575       InitMovDir(x, y);
1576       break;
1577
1578     case EL_AMOEBA_FULL:
1579     case EL_BD_AMOEBA:
1580       InitAmoebaNr(x, y);
1581       break;
1582
1583     case EL_AMOEBA_DROP:
1584       if (y == lev_fieldy - 1)
1585       {
1586         Feld[x][y] = EL_AMOEBA_GROWING;
1587         Store[x][y] = EL_AMOEBA_WET;
1588       }
1589       break;
1590
1591     case EL_DYNAMITE_ACTIVE:
1592     case EL_SP_DISK_RED_ACTIVE:
1593     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1594     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1595     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1596     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1597       MovDelay[x][y] = 96;
1598       break;
1599
1600     case EL_EM_DYNAMITE_ACTIVE:
1601       MovDelay[x][y] = 32;
1602       break;
1603
1604     case EL_LAMP:
1605       local_player->lights_still_needed++;
1606       break;
1607
1608     case EL_PENGUIN:
1609       local_player->friends_still_needed++;
1610       break;
1611
1612     case EL_PIG:
1613     case EL_DRAGON:
1614       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1615       break;
1616
1617     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1618     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1619     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1620     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1621     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1622     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1623     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1624     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1625     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1626     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1627     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1628     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1629       if (init_game)
1630       {
1631         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1632         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1633         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1634
1635         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1636         {
1637           game.belt_dir[belt_nr] = belt_dir;
1638           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1639         }
1640         else    /* more than one switch -- set it like the first switch */
1641         {
1642           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1643         }
1644       }
1645       break;
1646
1647 #if !USE_BOTH_SWITCHGATE_SWITCHES
1648     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1649       if (init_game)
1650         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1651       break;
1652
1653     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1654       if (init_game)
1655         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1656       break;
1657 #endif
1658
1659     case EL_LIGHT_SWITCH_ACTIVE:
1660       if (init_game)
1661         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1662       break;
1663
1664     case EL_INVISIBLE_STEELWALL:
1665     case EL_INVISIBLE_WALL:
1666     case EL_INVISIBLE_SAND:
1667       if (game.light_time_left > 0 ||
1668           game.lenses_time_left > 0)
1669         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1670       break;
1671
1672     case EL_EMC_MAGIC_BALL:
1673       if (game.ball_state)
1674         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1675       break;
1676
1677     case EL_EMC_MAGIC_BALL_SWITCH:
1678       if (game.ball_state)
1679         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1680       break;
1681
1682     default:
1683       if (IS_CUSTOM_ELEMENT(element))
1684       {
1685         if (CAN_MOVE(element))
1686           InitMovDir(x, y);
1687
1688 #if USE_NEW_CUSTOM_VALUE
1689         if (!element_info[element].use_last_ce_value || init_game)
1690           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1691 #endif
1692       }
1693       else if (IS_GROUP_ELEMENT(element))
1694       {
1695         Feld[x][y] = GetElementFromGroupElement(element);
1696
1697         InitField(x, y, init_game);
1698       }
1699
1700       break;
1701   }
1702
1703   if (!init_game)
1704     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1705 }
1706
1707 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1708 {
1709   InitField(x, y, init_game);
1710
1711   /* not needed to call InitMovDir() -- already done by InitField()! */
1712   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1713       CAN_MOVE(Feld[x][y]))
1714     InitMovDir(x, y);
1715 }
1716
1717 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1718 {
1719   int old_element = Feld[x][y];
1720
1721   InitField(x, y, init_game);
1722
1723   /* not needed to call InitMovDir() -- already done by InitField()! */
1724   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1725       CAN_MOVE(old_element) &&
1726       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1727     InitMovDir(x, y);
1728
1729   /* this case is in fact a combination of not less than three bugs:
1730      first, it calls InitMovDir() for elements that can move, although this is
1731      already done by InitField(); then, it checks the element that was at this
1732      field _before_ the call to InitField() (which can change it); lastly, it
1733      was not called for "mole with direction" elements, which were treated as
1734      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1735   */
1736 }
1737
1738 #if 1
1739
1740 void DrawGameValue_Emeralds(int value)
1741 {
1742   struct TextPosInfo *pos = &game.panel.gems;
1743 #if 1
1744   int font_nr = pos->font;
1745 #else
1746   int font_nr = FONT_TEXT_2;
1747 #endif
1748   int font_width = getFontWidth(font_nr);
1749   int chars = pos->chars;
1750
1751   if (PANEL_DEACTIVATED(pos))
1752     return;
1753
1754   pos->width = chars * font_width;
1755
1756   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1757 }
1758
1759 void DrawGameValue_Dynamite(int value)
1760 {
1761   struct TextPosInfo *pos = &game.panel.inventory;
1762 #if 1
1763   int font_nr = pos->font;
1764 #else
1765   int font_nr = FONT_TEXT_2;
1766 #endif
1767   int font_width = getFontWidth(font_nr);
1768   int chars = pos->chars;
1769
1770   if (PANEL_DEACTIVATED(pos))
1771     return;
1772
1773   pos->width = chars * font_width;
1774
1775   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1776 }
1777
1778 void DrawGameValue_Score(int value)
1779 {
1780   struct TextPosInfo *pos = &game.panel.score;
1781 #if 1
1782   int font_nr = pos->font;
1783 #else
1784   int font_nr = FONT_TEXT_2;
1785 #endif
1786   int font_width = getFontWidth(font_nr);
1787   int chars = pos->chars;
1788
1789   if (PANEL_DEACTIVATED(pos))
1790     return;
1791
1792   pos->width = chars * font_width;
1793
1794   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1795 }
1796
1797 void DrawGameValue_Time(int value)
1798 {
1799   struct TextPosInfo *pos = &game.panel.time;
1800   static int last_value = -1;
1801   int chars1 = 3;
1802   int chars2 = 4;
1803   int chars = pos->chars;
1804 #if 1
1805   int font1_nr = pos->font;
1806   int font2_nr = pos->font_alt;
1807 #else
1808   int font1_nr = FONT_TEXT_2;
1809   int font2_nr = FONT_TEXT_1;
1810 #endif
1811   int font_nr = font1_nr;
1812   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
1813
1814   if (PANEL_DEACTIVATED(pos))
1815     return;
1816
1817   if (use_dynamic_chars)                /* use dynamic number of chars */
1818   {
1819     chars   = (value < 1000 ? chars1   : chars2);
1820     font_nr = (value < 1000 ? font1_nr : font2_nr);
1821   }
1822
1823   /* clear background if value just changed its size (dynamic chars only) */
1824   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
1825   {
1826     int width1 = chars1 * getFontWidth(font1_nr);
1827     int width2 = chars2 * getFontWidth(font2_nr);
1828     int max_width = MAX(width1, width2);
1829     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
1830
1831     pos->width = max_width;
1832
1833     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
1834                                max_width, max_height);
1835   }
1836
1837   pos->width = chars * getFontWidth(font_nr);
1838
1839   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1840
1841   last_value = value;
1842 }
1843
1844 void DrawGameValue_Level(int value)
1845 {
1846   struct TextPosInfo *pos = &game.panel.level;
1847   int chars1 = 2;
1848   int chars2 = 3;
1849   int chars = pos->chars;
1850 #if 1
1851   int font1_nr = pos->font;
1852   int font2_nr = pos->font_alt;
1853 #else
1854   int font1_nr = FONT_TEXT_2;
1855   int font2_nr = FONT_TEXT_1;
1856 #endif
1857   int font_nr = font1_nr;
1858   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
1859
1860   if (PANEL_DEACTIVATED(pos))
1861     return;
1862
1863   if (use_dynamic_chars)                /* use dynamic number of chars */
1864   {
1865     chars   = (level_nr < 100 ? chars1   : chars2);
1866     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
1867   }
1868
1869   pos->width = chars * getFontWidth(font_nr);
1870
1871   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1872 }
1873
1874 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1875 {
1876   struct TextPosInfo *pos = &game.panel.keys;
1877   int base_key_graphic = EL_KEY_1;
1878   int i;
1879
1880   if (PANEL_DEACTIVATED(pos))
1881     return;
1882
1883   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1884     base_key_graphic = EL_EM_KEY_1;
1885
1886   pos->width = 4 * MINI_TILEX;
1887
1888   /* currently only 4 of 8 possible keys are displayed */
1889   for (i = 0; i < STD_NUM_KEYS; i++)
1890   {
1891     int src_x = DOOR_GFX_PAGEX5 + 18;
1892     int src_y = DOOR_GFX_PAGEY1 + 123;
1893     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
1894     int dst_y = PANEL_YPOS(pos);
1895
1896 #if 0
1897     /* masked blit with tiles from half-size scaled bitmap does not work yet
1898        (no mask bitmap created for these sizes after loading and scaling) --
1899        solution: load without creating mask, scale, then create final mask */
1900
1901     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1902                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1903
1904     if (key[i])
1905     {
1906       int graphic = el2edimg(base_key_graphic + i);
1907       Bitmap *src_bitmap;
1908       int src_x, src_y;
1909
1910       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1911
1912       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
1913                     dst_x - src_x, dst_y - src_y);
1914       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
1915                        dst_x, dst_y);
1916     }
1917 #else
1918     if (key[i])
1919       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
1920     else
1921       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1922                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1923 #endif
1924   }
1925 }
1926
1927 #else
1928
1929 void DrawGameValue_Emeralds(int value)
1930 {
1931   int font_nr = FONT_TEXT_2;
1932   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1933
1934   if (PANEL_DEACTIVATED(game.panel.gems))
1935     return;
1936
1937   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
1938 }
1939
1940 void DrawGameValue_Dynamite(int value)
1941 {
1942   int font_nr = FONT_TEXT_2;
1943   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1944
1945   if (PANEL_DEACTIVATED(game.panel.inventory))
1946     return;
1947
1948   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
1949 }
1950
1951 void DrawGameValue_Score(int value)
1952 {
1953   int font_nr = FONT_TEXT_2;
1954   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
1955
1956   if (PANEL_DEACTIVATED(game.panel.score))
1957     return;
1958
1959   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
1960 }
1961
1962 void DrawGameValue_Time(int value)
1963 {
1964   int font1_nr = FONT_TEXT_2;
1965 #if 1
1966   int font2_nr = FONT_TEXT_1;
1967 #else
1968   int font2_nr = FONT_LEVEL_NUMBER;
1969 #endif
1970   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
1971   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
1972
1973   if (PANEL_DEACTIVATED(game.panel.time))
1974     return;
1975
1976   /* clear background if value just changed its size */
1977   if (value == 999 || value == 1000)
1978     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1979
1980   if (value < 1000)
1981     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
1982   else
1983     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
1984 }
1985
1986 void DrawGameValue_Level(int value)
1987 {
1988   int font1_nr = FONT_TEXT_2;
1989 #if 1
1990   int font2_nr = FONT_TEXT_1;
1991 #else
1992   int font2_nr = FONT_LEVEL_NUMBER;
1993 #endif
1994
1995   if (PANEL_DEACTIVATED(game.panel.level))
1996     return;
1997
1998   if (level_nr < 100)
1999     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2000   else
2001     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2002 }
2003
2004 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2005 {
2006   int base_key_graphic = EL_KEY_1;
2007   int i;
2008
2009   if (PANEL_DEACTIVATED(game.panel.keys))
2010     return;
2011
2012   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2013     base_key_graphic = EL_EM_KEY_1;
2014
2015   /* currently only 4 of 8 possible keys are displayed */
2016   for (i = 0; i < STD_NUM_KEYS; i++)
2017   {
2018     int x = XX_KEYS + i * MINI_TILEX;
2019     int y = YY_KEYS;
2020
2021     if (key[i])
2022       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2023     else
2024       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2025                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2026   }
2027 }
2028
2029 #endif
2030
2031 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2032                        int key_bits)
2033 {
2034   int key[MAX_NUM_KEYS];
2035   int i;
2036
2037   /* prevent EM engine from updating time/score values parallel to GameWon() */
2038   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2039       local_player->LevelSolved)
2040     return;
2041
2042   for (i = 0; i < MAX_NUM_KEYS; i++)
2043     key[i] = key_bits & (1 << i);
2044
2045   DrawGameValue_Level(level_nr);
2046
2047   DrawGameValue_Emeralds(emeralds);
2048   DrawGameValue_Dynamite(dynamite);
2049   DrawGameValue_Score(score);
2050   DrawGameValue_Time(time);
2051
2052   DrawGameValue_Keys(key);
2053 }
2054
2055 void DrawGameDoorValues()
2056 {
2057   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2058   int dynamite_value = 0;
2059   int score_value = (local_player->LevelSolved ? local_player->score_final :
2060                      local_player->score);
2061   int gems_value = local_player->gems_still_needed;
2062   int key_bits = 0;
2063   int i, j;
2064
2065   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2066   {
2067     DrawGameDoorValues_EM();
2068
2069     return;
2070   }
2071
2072   if (game.centered_player_nr == -1)
2073   {
2074     for (i = 0; i < MAX_PLAYERS; i++)
2075     {
2076       for (j = 0; j < MAX_NUM_KEYS; j++)
2077         if (stored_player[i].key[j])
2078           key_bits |= (1 << j);
2079
2080       dynamite_value += stored_player[i].inventory_size;
2081     }
2082   }
2083   else
2084   {
2085     int player_nr = game.centered_player_nr;
2086
2087     for (i = 0; i < MAX_NUM_KEYS; i++)
2088       if (stored_player[player_nr].key[i])
2089         key_bits |= (1 << i);
2090
2091     dynamite_value = stored_player[player_nr].inventory_size;
2092   }
2093
2094   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2095                     key_bits);
2096 }
2097
2098
2099 /*
2100   =============================================================================
2101   InitGameEngine()
2102   -----------------------------------------------------------------------------
2103   initialize game engine due to level / tape version number
2104   =============================================================================
2105 */
2106
2107 static void InitGameEngine()
2108 {
2109   int i, j, k, l, x, y;
2110
2111   /* set game engine from tape file when re-playing, else from level file */
2112   game.engine_version = (tape.playing ? tape.engine_version :
2113                          level.game_version);
2114
2115   /* ---------------------------------------------------------------------- */
2116   /* set flags for bugs and changes according to active game engine version */
2117   /* ---------------------------------------------------------------------- */
2118
2119   /*
2120     Summary of bugfix/change:
2121     Fixed handling for custom elements that change when pushed by the player.
2122
2123     Fixed/changed in version:
2124     3.1.0
2125
2126     Description:
2127     Before 3.1.0, custom elements that "change when pushing" changed directly
2128     after the player started pushing them (until then handled in "DigField()").
2129     Since 3.1.0, these custom elements are not changed until the "pushing"
2130     move of the element is finished (now handled in "ContinueMoving()").
2131
2132     Affected levels/tapes:
2133     The first condition is generally needed for all levels/tapes before version
2134     3.1.0, which might use the old behaviour before it was changed; known tapes
2135     that are affected are some tapes from the level set "Walpurgis Gardens" by
2136     Jamie Cullen.
2137     The second condition is an exception from the above case and is needed for
2138     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2139     above (including some development versions of 3.1.0), but before it was
2140     known that this change would break tapes like the above and was fixed in
2141     3.1.1, so that the changed behaviour was active although the engine version
2142     while recording maybe was before 3.1.0. There is at least one tape that is
2143     affected by this exception, which is the tape for the one-level set "Bug
2144     Machine" by Juergen Bonhagen.
2145   */
2146
2147   game.use_change_when_pushing_bug =
2148     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2149      !(tape.playing &&
2150        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2151        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2152
2153   /*
2154     Summary of bugfix/change:
2155     Fixed handling for blocking the field the player leaves when moving.
2156
2157     Fixed/changed in version:
2158     3.1.1
2159
2160     Description:
2161     Before 3.1.1, when "block last field when moving" was enabled, the field
2162     the player is leaving when moving was blocked for the time of the move,
2163     and was directly unblocked afterwards. This resulted in the last field
2164     being blocked for exactly one less than the number of frames of one player
2165     move. Additionally, even when blocking was disabled, the last field was
2166     blocked for exactly one frame.
2167     Since 3.1.1, due to changes in player movement handling, the last field
2168     is not blocked at all when blocking is disabled. When blocking is enabled,
2169     the last field is blocked for exactly the number of frames of one player
2170     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2171     last field is blocked for exactly one more than the number of frames of
2172     one player move.
2173
2174     Affected levels/tapes:
2175     (!!! yet to be determined -- probably many !!!)
2176   */
2177
2178   game.use_block_last_field_bug =
2179     (game.engine_version < VERSION_IDENT(3,1,1,0));
2180
2181   /*
2182     Summary of bugfix/change:
2183     Changed behaviour of CE changes with multiple changes per single frame.
2184
2185     Fixed/changed in version:
2186     3.2.0-6
2187
2188     Description:
2189     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
2190     This resulted in race conditions where CEs seem to behave strange in some
2191     situations (where triggered CE changes were just skipped because there was
2192     already a CE change on that tile in the playfield in that engine frame).
2193     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
2194     (The number of changes per frame must be limited in any case, because else
2195     it is easily possible to define CE changes that would result in an infinite
2196     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
2197     should be set large enough so that it would only be reached in cases where
2198     the corresponding CE change conditions run into a loop. Therefore, it seems
2199     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
2200     maximal number of change pages for custom elements.)
2201
2202     Affected levels/tapes:
2203     Probably many.
2204   */
2205
2206 #if USE_ONLY_ONE_CHANGE_PER_FRAME
2207   game.max_num_changes_per_frame = 1;
2208 #else
2209   game.max_num_changes_per_frame =
2210     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
2211 #endif
2212
2213   /* ---------------------------------------------------------------------- */
2214
2215   /* default scan direction: scan playfield from top/left to bottom/right */
2216   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2217
2218   /* dynamically adjust element properties according to game engine version */
2219   InitElementPropertiesEngine(game.engine_version);
2220
2221 #if 0
2222   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2223   printf("          tape version == %06d [%s] [file: %06d]\n",
2224          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2225          tape.file_version);
2226   printf("       => game.engine_version == %06d\n", game.engine_version);
2227 #endif
2228
2229   /* ---------- initialize player's initial move delay --------------------- */
2230
2231   /* dynamically adjust player properties according to level information */
2232   for (i = 0; i < MAX_PLAYERS; i++)
2233     game.initial_move_delay_value[i] =
2234       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2235
2236   /* dynamically adjust player properties according to game engine version */
2237   for (i = 0; i < MAX_PLAYERS; i++)
2238     game.initial_move_delay[i] =
2239       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2240        game.initial_move_delay_value[i] : 0);
2241
2242   /* ---------- initialize player's initial push delay --------------------- */
2243
2244   /* dynamically adjust player properties according to game engine version */
2245   game.initial_push_delay_value =
2246     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2247
2248   /* ---------- initialize changing elements ------------------------------- */
2249
2250   /* initialize changing elements information */
2251   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2252   {
2253     struct ElementInfo *ei = &element_info[i];
2254
2255     /* this pointer might have been changed in the level editor */
2256     ei->change = &ei->change_page[0];
2257
2258     if (!IS_CUSTOM_ELEMENT(i))
2259     {
2260       ei->change->target_element = EL_EMPTY_SPACE;
2261       ei->change->delay_fixed = 0;
2262       ei->change->delay_random = 0;
2263       ei->change->delay_frames = 1;
2264     }
2265
2266     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2267     {
2268       ei->has_change_event[j] = FALSE;
2269
2270       ei->event_page_nr[j] = 0;
2271       ei->event_page[j] = &ei->change_page[0];
2272     }
2273   }
2274
2275   /* add changing elements from pre-defined list */
2276   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2277   {
2278     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2279     struct ElementInfo *ei = &element_info[ch_delay->element];
2280
2281     ei->change->target_element       = ch_delay->target_element;
2282     ei->change->delay_fixed          = ch_delay->change_delay;
2283
2284     ei->change->pre_change_function  = ch_delay->pre_change_function;
2285     ei->change->change_function      = ch_delay->change_function;
2286     ei->change->post_change_function = ch_delay->post_change_function;
2287
2288     ei->change->can_change = TRUE;
2289     ei->change->can_change_or_has_action = TRUE;
2290
2291     ei->has_change_event[CE_DELAY] = TRUE;
2292
2293     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2294     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2295   }
2296
2297   /* ---------- initialize internal run-time variables ------------- */
2298
2299   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2300   {
2301     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2302
2303     for (j = 0; j < ei->num_change_pages; j++)
2304     {
2305       ei->change_page[j].can_change_or_has_action =
2306         (ei->change_page[j].can_change |
2307          ei->change_page[j].has_action);
2308     }
2309   }
2310
2311   /* add change events from custom element configuration */
2312   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2313   {
2314     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2315
2316     for (j = 0; j < ei->num_change_pages; j++)
2317     {
2318       if (!ei->change_page[j].can_change_or_has_action)
2319         continue;
2320
2321       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2322       {
2323         /* only add event page for the first page found with this event */
2324         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2325         {
2326           ei->has_change_event[k] = TRUE;
2327
2328           ei->event_page_nr[k] = j;
2329           ei->event_page[k] = &ei->change_page[j];
2330         }
2331       }
2332     }
2333   }
2334
2335   /* ---------- initialize run-time trigger player and element ------------- */
2336
2337   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2338   {
2339     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2340
2341     for (j = 0; j < ei->num_change_pages; j++)
2342     {
2343       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2344       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
2345       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2346       ei->change_page[j].actual_trigger_ce_value = 0;
2347       ei->change_page[j].actual_trigger_ce_score = 0;
2348     }
2349   }
2350
2351   /* ---------- initialize trigger events ---------------------------------- */
2352
2353   /* initialize trigger events information */
2354   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2355     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2356       trigger_events[i][j] = FALSE;
2357
2358   /* add trigger events from element change event properties */
2359   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2360   {
2361     struct ElementInfo *ei = &element_info[i];
2362
2363     for (j = 0; j < ei->num_change_pages; j++)
2364     {
2365       if (!ei->change_page[j].can_change_or_has_action)
2366         continue;
2367
2368       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2369       {
2370         int trigger_element = ei->change_page[j].trigger_element;
2371
2372         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2373         {
2374           if (ei->change_page[j].has_event[k])
2375           {
2376             if (IS_GROUP_ELEMENT(trigger_element))
2377             {
2378               struct ElementGroupInfo *group =
2379                 element_info[trigger_element].group;
2380
2381               for (l = 0; l < group->num_elements_resolved; l++)
2382                 trigger_events[group->element_resolved[l]][k] = TRUE;
2383             }
2384             else if (trigger_element == EL_ANY_ELEMENT)
2385               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2386                 trigger_events[l][k] = TRUE;
2387             else
2388               trigger_events[trigger_element][k] = TRUE;
2389           }
2390         }
2391       }
2392     }
2393   }
2394
2395   /* ---------- initialize push delay -------------------------------------- */
2396
2397   /* initialize push delay values to default */
2398   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2399   {
2400     if (!IS_CUSTOM_ELEMENT(i))
2401     {
2402       /* set default push delay values (corrected since version 3.0.7-1) */
2403       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2404       {
2405         element_info[i].push_delay_fixed = 2;
2406         element_info[i].push_delay_random = 8;
2407       }
2408       else
2409       {
2410         element_info[i].push_delay_fixed = 8;
2411         element_info[i].push_delay_random = 8;
2412       }
2413     }
2414   }
2415
2416   /* set push delay value for certain elements from pre-defined list */
2417   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2418   {
2419     int e = push_delay_list[i].element;
2420
2421     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2422     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2423   }
2424
2425   /* set push delay value for Supaplex elements for newer engine versions */
2426   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2427   {
2428     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2429     {
2430       if (IS_SP_ELEMENT(i))
2431       {
2432         /* set SP push delay to just enough to push under a falling zonk */
2433         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2434
2435         element_info[i].push_delay_fixed  = delay;
2436         element_info[i].push_delay_random = 0;
2437       }
2438     }
2439   }
2440
2441   /* ---------- initialize move stepsize ----------------------------------- */
2442
2443   /* initialize move stepsize values to default */
2444   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2445     if (!IS_CUSTOM_ELEMENT(i))
2446       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2447
2448   /* set move stepsize value for certain elements from pre-defined list */
2449   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2450   {
2451     int e = move_stepsize_list[i].element;
2452
2453     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2454   }
2455
2456   /* ---------- initialize collect score ----------------------------------- */
2457
2458   /* initialize collect score values for custom elements from initial value */
2459   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2460     if (IS_CUSTOM_ELEMENT(i))
2461       element_info[i].collect_score = element_info[i].collect_score_initial;
2462
2463   /* ---------- initialize collect count ----------------------------------- */
2464
2465   /* initialize collect count values for non-custom elements */
2466   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2467     if (!IS_CUSTOM_ELEMENT(i))
2468       element_info[i].collect_count_initial = 0;
2469
2470   /* add collect count values for all elements from pre-defined list */
2471   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2472     element_info[collect_count_list[i].element].collect_count_initial =
2473       collect_count_list[i].count;
2474
2475   /* ---------- initialize access direction -------------------------------- */
2476
2477   /* initialize access direction values to default (access from every side) */
2478   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2479     if (!IS_CUSTOM_ELEMENT(i))
2480       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2481
2482   /* set access direction value for certain elements from pre-defined list */
2483   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2484     element_info[access_direction_list[i].element].access_direction =
2485       access_direction_list[i].direction;
2486
2487   /* ---------- initialize explosion content ------------------------------- */
2488   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2489   {
2490     if (IS_CUSTOM_ELEMENT(i))
2491       continue;
2492
2493     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2494     {
2495       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2496
2497       element_info[i].content.e[x][y] =
2498         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2499          i == EL_PLAYER_2 ? EL_EMERALD_RED :
2500          i == EL_PLAYER_3 ? EL_EMERALD :
2501          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2502          i == EL_MOLE ? EL_EMERALD_RED :
2503          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2504          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2505          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2506          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2507          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2508          i == EL_WALL_EMERALD ? EL_EMERALD :
2509          i == EL_WALL_DIAMOND ? EL_DIAMOND :
2510          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2511          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2512          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2513          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2514          i == EL_WALL_PEARL ? EL_PEARL :
2515          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2516          EL_EMPTY);
2517     }
2518   }
2519
2520   /* ---------- initialize recursion detection ------------------------------ */
2521   recursion_loop_depth = 0;
2522   recursion_loop_detected = FALSE;
2523   recursion_loop_element = EL_UNDEFINED;
2524 }
2525
2526 int get_num_special_action(int element, int action_first, int action_last)
2527 {
2528   int num_special_action = 0;
2529   int i, j;
2530
2531   for (i = action_first; i <= action_last; i++)
2532   {
2533     boolean found = FALSE;
2534
2535     for (j = 0; j < NUM_DIRECTIONS; j++)
2536       if (el_act_dir2img(element, i, j) !=
2537           el_act_dir2img(element, ACTION_DEFAULT, j))
2538         found = TRUE;
2539
2540     if (found)
2541       num_special_action++;
2542     else
2543       break;
2544   }
2545
2546   return num_special_action;
2547 }
2548
2549
2550 /*
2551   =============================================================================
2552   InitGame()
2553   -----------------------------------------------------------------------------
2554   initialize and start new game
2555   =============================================================================
2556 */
2557
2558 void InitGame()
2559 {
2560   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
2561   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
2562   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
2563   boolean do_fading = (game_status == GAME_MODE_MAIN);
2564   int i, j, x, y;
2565
2566   game_status = GAME_MODE_PLAYING;
2567
2568   InitGameEngine();
2569
2570   /* don't play tapes over network */
2571   network_playing = (options.network && !tape.playing);
2572
2573   for (i = 0; i < MAX_PLAYERS; i++)
2574   {
2575     struct PlayerInfo *player = &stored_player[i];
2576
2577     player->index_nr = i;
2578     player->index_bit = (1 << i);
2579     player->element_nr = EL_PLAYER_1 + i;
2580
2581     player->present = FALSE;
2582     player->active = FALSE;
2583     player->killed = FALSE;
2584
2585     player->action = 0;
2586     player->effective_action = 0;
2587     player->programmed_action = 0;
2588
2589     player->score = 0;
2590     player->score_final = 0;
2591
2592     player->gems_still_needed = level.gems_needed;
2593     player->sokobanfields_still_needed = 0;
2594     player->lights_still_needed = 0;
2595     player->friends_still_needed = 0;
2596
2597     for (j = 0; j < MAX_NUM_KEYS; j++)
2598       player->key[j] = FALSE;
2599
2600     player->num_white_keys = 0;
2601
2602     player->dynabomb_count = 0;
2603     player->dynabomb_size = 1;
2604     player->dynabombs_left = 0;
2605     player->dynabomb_xl = FALSE;
2606
2607     player->MovDir = MV_NONE;
2608     player->MovPos = 0;
2609     player->GfxPos = 0;
2610     player->GfxDir = MV_NONE;
2611     player->GfxAction = ACTION_DEFAULT;
2612     player->Frame = 0;
2613     player->StepFrame = 0;
2614
2615     player->use_murphy = FALSE;
2616     player->artwork_element =
2617       (level.use_artwork_element[i] ? level.artwork_element[i] :
2618        player->element_nr);
2619
2620     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
2621     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2622
2623     player->gravity = level.initial_player_gravity[i];
2624
2625     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2626
2627     player->actual_frame_counter = 0;
2628
2629     player->step_counter = 0;
2630
2631     player->last_move_dir = MV_NONE;
2632
2633     player->is_active = FALSE;
2634
2635     player->is_waiting = FALSE;
2636     player->is_moving = FALSE;
2637     player->is_auto_moving = FALSE;
2638     player->is_digging = FALSE;
2639     player->is_snapping = FALSE;
2640     player->is_collecting = FALSE;
2641     player->is_pushing = FALSE;
2642     player->is_switching = FALSE;
2643     player->is_dropping = FALSE;
2644     player->is_dropping_pressed = FALSE;
2645
2646     player->is_bored = FALSE;
2647     player->is_sleeping = FALSE;
2648
2649     player->frame_counter_bored = -1;
2650     player->frame_counter_sleeping = -1;
2651
2652     player->anim_delay_counter = 0;
2653     player->post_delay_counter = 0;
2654
2655     player->dir_waiting = MV_NONE;
2656     player->action_waiting = ACTION_DEFAULT;
2657     player->last_action_waiting = ACTION_DEFAULT;
2658     player->special_action_bored = ACTION_DEFAULT;
2659     player->special_action_sleeping = ACTION_DEFAULT;
2660
2661     player->switch_x = -1;
2662     player->switch_y = -1;
2663
2664     player->drop_x = -1;
2665     player->drop_y = -1;
2666
2667     player->show_envelope = 0;
2668
2669     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2670
2671     player->push_delay       = -1;      /* initialized when pushing starts */
2672     player->push_delay_value = game.initial_push_delay_value;
2673
2674     player->drop_delay = 0;
2675     player->drop_pressed_delay = 0;
2676
2677     player->last_jx = -1;
2678     player->last_jy = -1;
2679     player->jx = -1;
2680     player->jy = -1;
2681
2682     player->shield_normal_time_left = 0;
2683     player->shield_deadly_time_left = 0;
2684
2685     player->inventory_infinite_element = EL_UNDEFINED;
2686     player->inventory_size = 0;
2687
2688     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2689     SnapField(player, 0, 0);
2690
2691     player->LevelSolved = FALSE;
2692     player->GameOver = FALSE;
2693
2694     player->LevelSolved_GameWon = FALSE;
2695     player->LevelSolved_GameEnd = FALSE;
2696     player->LevelSolved_PanelOff = FALSE;
2697     player->LevelSolved_SaveTape = FALSE;
2698     player->LevelSolved_SaveScore = FALSE;
2699   }
2700
2701   network_player_action_received = FALSE;
2702
2703 #if defined(NETWORK_AVALIABLE)
2704   /* initial null action */
2705   if (network_playing)
2706     SendToServer_MovePlayer(MV_NONE);
2707 #endif
2708
2709   ZX = ZY = -1;
2710   ExitX = ExitY = -1;
2711
2712   FrameCounter = 0;
2713   TimeFrames = 0;
2714   TimePlayed = 0;
2715   TimeLeft = level.time;
2716   TapeTime = 0;
2717
2718   ScreenMovDir = MV_NONE;
2719   ScreenMovPos = 0;
2720   ScreenGfxPos = 0;
2721
2722   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
2723
2724   AllPlayersGone = FALSE;
2725
2726   game.yamyam_content_nr = 0;
2727   game.magic_wall_active = FALSE;
2728   game.magic_wall_time_left = 0;
2729   game.light_time_left = 0;
2730   game.timegate_time_left = 0;
2731   game.switchgate_pos = 0;
2732   game.wind_direction = level.wind_direction_initial;
2733
2734 #if !USE_PLAYER_GRAVITY
2735   game.gravity = FALSE;
2736   game.explosions_delayed = TRUE;
2737 #endif
2738
2739   game.lenses_time_left = 0;
2740   game.magnify_time_left = 0;
2741
2742   game.ball_state = level.ball_state_initial;
2743   game.ball_content_nr = 0;
2744
2745   game.envelope_active = FALSE;
2746
2747   /* set focus to local player for network games, else to all players */
2748   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2749   game.centered_player_nr_next = game.centered_player_nr;
2750   game.set_centered_player = FALSE;
2751
2752   if (network_playing && tape.recording)
2753   {
2754     /* store client dependent player focus when recording network games */
2755     tape.centered_player_nr_next = game.centered_player_nr_next;
2756     tape.set_centered_player = TRUE;
2757   }
2758
2759   for (i = 0; i < NUM_BELTS; i++)
2760   {
2761     game.belt_dir[i] = MV_NONE;
2762     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
2763   }
2764
2765   for (i = 0; i < MAX_NUM_AMOEBA; i++)
2766     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2767
2768   SCAN_PLAYFIELD(x, y)
2769   {
2770     Feld[x][y] = level.field[x][y];
2771     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2772     ChangeDelay[x][y] = 0;
2773     ChangePage[x][y] = -1;
2774 #if USE_NEW_CUSTOM_VALUE
2775     CustomValue[x][y] = 0;              /* initialized in InitField() */
2776 #endif
2777     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2778     AmoebaNr[x][y] = 0;
2779     WasJustMoving[x][y] = 0;
2780     WasJustFalling[x][y] = 0;
2781     CheckCollision[x][y] = 0;
2782     CheckImpact[x][y] = 0;
2783     Stop[x][y] = FALSE;
2784     Pushed[x][y] = FALSE;
2785
2786     ChangeCount[x][y] = 0;
2787     ChangeEvent[x][y] = -1;
2788
2789     ExplodePhase[x][y] = 0;
2790     ExplodeDelay[x][y] = 0;
2791     ExplodeField[x][y] = EX_TYPE_NONE;
2792
2793     RunnerVisit[x][y] = 0;
2794     PlayerVisit[x][y] = 0;
2795
2796     GfxFrame[x][y] = 0;
2797     GfxRandom[x][y] = INIT_GFX_RANDOM();
2798     GfxElement[x][y] = EL_UNDEFINED;
2799     GfxAction[x][y] = ACTION_DEFAULT;
2800     GfxDir[x][y] = MV_NONE;
2801   }
2802
2803   SCAN_PLAYFIELD(x, y)
2804   {
2805     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2806       emulate_bd = FALSE;
2807     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2808       emulate_sb = FALSE;
2809     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2810       emulate_sp = FALSE;
2811
2812     InitField(x, y, TRUE);
2813   }
2814
2815   InitBeltMovement();
2816
2817   for (i = 0; i < MAX_PLAYERS; i++)
2818   {
2819     struct PlayerInfo *player = &stored_player[i];
2820
2821     /* set number of special actions for bored and sleeping animation */
2822     player->num_special_action_bored =
2823       get_num_special_action(player->artwork_element,
2824                              ACTION_BORING_1, ACTION_BORING_LAST);
2825     player->num_special_action_sleeping =
2826       get_num_special_action(player->artwork_element,
2827                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2828   }
2829
2830   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2831                     emulate_sb ? EMU_SOKOBAN :
2832                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2833
2834 #if USE_NEW_ALL_SLIPPERY
2835   /* initialize type of slippery elements */
2836   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2837   {
2838     if (!IS_CUSTOM_ELEMENT(i))
2839     {
2840       /* default: elements slip down either to the left or right randomly */
2841       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2842
2843       /* SP style elements prefer to slip down on the left side */
2844       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2845         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2846
2847       /* BD style elements prefer to slip down on the left side */
2848       if (game.emulation == EMU_BOULDERDASH)
2849         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2850     }
2851   }
2852 #endif
2853
2854   /* initialize explosion and ignition delay */
2855   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2856   {
2857     if (!IS_CUSTOM_ELEMENT(i))
2858     {
2859       int num_phase = 8;
2860       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2861                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2862                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
2863       int last_phase = (num_phase + 1) * delay;
2864       int half_phase = (num_phase / 2) * delay;
2865
2866       element_info[i].explosion_delay = last_phase - 1;
2867       element_info[i].ignition_delay = half_phase;
2868
2869       if (i == EL_BLACK_ORB)
2870         element_info[i].ignition_delay = 1;
2871     }
2872
2873 #if 0
2874     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
2875       element_info[i].explosion_delay = 1;
2876
2877     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
2878       element_info[i].ignition_delay = 1;
2879 #endif
2880   }
2881
2882   /* correct non-moving belts to start moving left */
2883   for (i = 0; i < NUM_BELTS; i++)
2884     if (game.belt_dir[i] == MV_NONE)
2885       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
2886
2887   /* check if any connected player was not found in playfield */
2888   for (i = 0; i < MAX_PLAYERS; i++)
2889   {
2890     struct PlayerInfo *player = &stored_player[i];
2891
2892     if (player->connected && !player->present)
2893     {
2894       for (j = 0; j < MAX_PLAYERS; j++)
2895       {
2896         struct PlayerInfo *some_player = &stored_player[j];
2897         int jx = some_player->jx, jy = some_player->jy;
2898
2899         /* assign first free player found that is present in the playfield */
2900         if (some_player->present && !some_player->connected)
2901         {
2902           player->present = TRUE;
2903           player->active = TRUE;
2904
2905           some_player->present = FALSE;
2906           some_player->active = FALSE;
2907
2908           player->artwork_element = some_player->artwork_element;
2909
2910           player->block_last_field       = some_player->block_last_field;
2911           player->block_delay_adjustment = some_player->block_delay_adjustment;
2912
2913           StorePlayer[jx][jy] = player->element_nr;
2914           player->jx = player->last_jx = jx;
2915           player->jy = player->last_jy = jy;
2916
2917           break;
2918         }
2919       }
2920     }
2921   }
2922
2923   if (tape.playing)
2924   {
2925     /* when playing a tape, eliminate all players who do not participate */
2926
2927     for (i = 0; i < MAX_PLAYERS; i++)
2928     {
2929       if (stored_player[i].active && !tape.player_participates[i])
2930       {
2931         struct PlayerInfo *player = &stored_player[i];
2932         int jx = player->jx, jy = player->jy;
2933
2934         player->active = FALSE;
2935         StorePlayer[jx][jy] = 0;
2936         Feld[jx][jy] = EL_EMPTY;
2937       }
2938     }
2939   }
2940   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
2941   {
2942     /* when in single player mode, eliminate all but the first active player */
2943
2944     for (i = 0; i < MAX_PLAYERS; i++)
2945     {
2946       if (stored_player[i].active)
2947       {
2948         for (j = i + 1; j < MAX_PLAYERS; j++)
2949         {
2950           if (stored_player[j].active)
2951           {
2952             struct PlayerInfo *player = &stored_player[j];
2953             int jx = player->jx, jy = player->jy;
2954
2955             player->active = FALSE;
2956             player->present = FALSE;
2957
2958             StorePlayer[jx][jy] = 0;
2959             Feld[jx][jy] = EL_EMPTY;
2960           }
2961         }
2962       }
2963     }
2964   }
2965
2966   /* when recording the game, store which players take part in the game */
2967   if (tape.recording)
2968   {
2969     for (i = 0; i < MAX_PLAYERS; i++)
2970       if (stored_player[i].active)
2971         tape.player_participates[i] = TRUE;
2972   }
2973
2974   if (options.debug)
2975   {
2976     for (i = 0; i < MAX_PLAYERS; i++)
2977     {
2978       struct PlayerInfo *player = &stored_player[i];
2979
2980       printf("Player %d: present == %d, connected == %d, active == %d.\n",
2981              i+1,
2982              player->present,
2983              player->connected,
2984              player->active);
2985       if (local_player == player)
2986         printf("Player  %d is local player.\n", i+1);
2987     }
2988   }
2989
2990   if (BorderElement == EL_EMPTY)
2991   {
2992     SBX_Left = 0;
2993     SBX_Right = lev_fieldx - SCR_FIELDX;
2994     SBY_Upper = 0;
2995     SBY_Lower = lev_fieldy - SCR_FIELDY;
2996   }
2997   else
2998   {
2999     SBX_Left = -1;
3000     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3001     SBY_Upper = -1;
3002     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3003   }
3004
3005   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3006     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3007
3008   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3009     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3010
3011   /* if local player not found, look for custom element that might create
3012      the player (make some assumptions about the right custom element) */
3013   if (!local_player->present)
3014   {
3015     int start_x = 0, start_y = 0;
3016     int found_rating = 0;
3017     int found_element = EL_UNDEFINED;
3018     int player_nr = local_player->index_nr;
3019
3020     SCAN_PLAYFIELD(x, y)
3021     {
3022       int element = Feld[x][y];
3023       int content;
3024       int xx, yy;
3025       boolean is_player;
3026
3027       if (level.use_start_element[player_nr] &&
3028           level.start_element[player_nr] == element &&
3029           found_rating < 4)
3030       {
3031         start_x = x;
3032         start_y = y;
3033
3034         found_rating = 4;
3035         found_element = element;
3036       }
3037
3038       if (!IS_CUSTOM_ELEMENT(element))
3039         continue;
3040
3041       if (CAN_CHANGE(element))
3042       {
3043         for (i = 0; i < element_info[element].num_change_pages; i++)
3044         {
3045           /* check for player created from custom element as single target */
3046           content = element_info[element].change_page[i].target_element;
3047           is_player = ELEM_IS_PLAYER(content);
3048
3049           if (is_player && (found_rating < 3 || element < found_element))
3050           {
3051             start_x = x;
3052             start_y = y;
3053
3054             found_rating = 3;
3055             found_element = element;
3056           }
3057         }
3058       }
3059
3060       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3061       {
3062         /* check for player created from custom element as explosion content */
3063         content = element_info[element].content.e[xx][yy];
3064         is_player = ELEM_IS_PLAYER(content);
3065
3066         if (is_player && (found_rating < 2 || element < found_element))
3067         {
3068           start_x = x + xx - 1;
3069           start_y = y + yy - 1;
3070
3071           found_rating = 2;
3072           found_element = element;
3073         }
3074
3075         if (!CAN_CHANGE(element))
3076           continue;
3077
3078         for (i = 0; i < element_info[element].num_change_pages; i++)
3079         {
3080           /* check for player created from custom element as extended target */
3081           content =
3082             element_info[element].change_page[i].target_content.e[xx][yy];
3083
3084           is_player = ELEM_IS_PLAYER(content);
3085
3086           if (is_player && (found_rating < 1 || element < found_element))
3087           {
3088             start_x = x + xx - 1;
3089             start_y = y + yy - 1;
3090
3091             found_rating = 1;
3092             found_element = element;
3093           }
3094         }
3095       }
3096     }
3097
3098     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3099                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3100                 start_x - MIDPOSX);
3101
3102     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3103                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3104                 start_y - MIDPOSY);
3105   }
3106   else
3107   {
3108     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3109                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3110                 local_player->jx - MIDPOSX);
3111
3112     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3113                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3114                 local_player->jy - MIDPOSY);
3115   }
3116
3117   StopAnimation();
3118
3119   if (!game.restart_level)
3120     CloseDoor(DOOR_CLOSE_1);
3121
3122   if (do_fading)
3123     FadeOut(REDRAW_FIELD);
3124
3125   /* !!! FIX THIS (START) !!! */
3126   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3127   {
3128     InitGameEngine_EM();
3129
3130     /* blit playfield from scroll buffer to normal back buffer for fading in */
3131     BlitScreenToBitmap_EM(backbuffer);
3132   }
3133   else
3134   {
3135     DrawLevel();
3136     DrawAllPlayers();
3137
3138     /* after drawing the level, correct some elements */
3139     if (game.timegate_time_left == 0)
3140       CloseAllOpenTimegates();
3141
3142     /* blit playfield from scroll buffer to normal back buffer for fading in */
3143     if (setup.soft_scrolling)
3144       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3145
3146     redraw_mask |= REDRAW_FROM_BACKBUFFER;
3147   }
3148   /* !!! FIX THIS (END) !!! */
3149
3150   if (do_fading)
3151     FadeIn(REDRAW_FIELD);
3152
3153   BackToFront();
3154
3155   if (!game.restart_level)
3156   {
3157     /* copy default game door content to main double buffer */
3158     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3159                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3160   }
3161
3162   SetPanelBackground();
3163   SetDrawBackgroundMask(REDRAW_DOOR_1);
3164
3165   DrawGameDoorValues();
3166
3167   if (!game.restart_level)
3168   {
3169     UnmapGameButtons();
3170     UnmapTapeButtons();
3171     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3172     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3173     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3174     MapGameButtons();
3175     MapTapeButtons();
3176
3177     /* copy actual game door content to door double buffer for OpenDoor() */
3178     BlitBitmap(drawto, bitmap_db_door,
3179                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3180
3181     OpenDoor(DOOR_OPEN_ALL);
3182
3183     PlaySound(SND_GAME_STARTING);
3184
3185     if (setup.sound_music)
3186       PlayLevelMusic();
3187
3188     KeyboardAutoRepeatOffUnlessAutoplay();
3189
3190     if (options.debug)
3191     {
3192       for (i = 0; i < MAX_PLAYERS; i++)
3193         printf("Player %d %sactive.\n",
3194                i + 1, (stored_player[i].active ? "" : "not "));
3195     }
3196   }
3197
3198 #if 1
3199   UnmapAllGadgets();
3200
3201   MapGameButtons();
3202   MapTapeButtons();
3203 #endif
3204
3205   game.restart_level = FALSE;
3206 }
3207
3208 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3209 {
3210   /* this is used for non-R'n'D game engines to update certain engine values */
3211
3212   /* needed to determine if sounds are played within the visible screen area */
3213   scroll_x = actual_scroll_x;
3214   scroll_y = actual_scroll_y;
3215 }
3216
3217 void InitMovDir(int x, int y)
3218 {
3219   int i, element = Feld[x][y];
3220   static int xy[4][2] =
3221   {
3222     {  0, +1 },
3223     { +1,  0 },
3224     {  0, -1 },
3225     { -1,  0 }
3226   };
3227   static int direction[3][4] =
3228   {
3229     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
3230     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
3231     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
3232   };
3233
3234   switch (element)
3235   {
3236     case EL_BUG_RIGHT:
3237     case EL_BUG_UP:
3238     case EL_BUG_LEFT:
3239     case EL_BUG_DOWN:
3240       Feld[x][y] = EL_BUG;
3241       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3242       break;
3243
3244     case EL_SPACESHIP_RIGHT:
3245     case EL_SPACESHIP_UP:
3246     case EL_SPACESHIP_LEFT:
3247     case EL_SPACESHIP_DOWN:
3248       Feld[x][y] = EL_SPACESHIP;
3249       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3250       break;
3251
3252     case EL_BD_BUTTERFLY_RIGHT:
3253     case EL_BD_BUTTERFLY_UP:
3254     case EL_BD_BUTTERFLY_LEFT:
3255     case EL_BD_BUTTERFLY_DOWN:
3256       Feld[x][y] = EL_BD_BUTTERFLY;
3257       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3258       break;
3259
3260     case EL_BD_FIREFLY_RIGHT:
3261     case EL_BD_FIREFLY_UP:
3262     case EL_BD_FIREFLY_LEFT:
3263     case EL_BD_FIREFLY_DOWN:
3264       Feld[x][y] = EL_BD_FIREFLY;
3265       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3266       break;
3267
3268     case EL_PACMAN_RIGHT:
3269     case EL_PACMAN_UP:
3270     case EL_PACMAN_LEFT:
3271     case EL_PACMAN_DOWN:
3272       Feld[x][y] = EL_PACMAN;
3273       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3274       break;
3275
3276     case EL_YAMYAM_LEFT:
3277     case EL_YAMYAM_RIGHT:
3278     case EL_YAMYAM_UP:
3279     case EL_YAMYAM_DOWN:
3280       Feld[x][y] = EL_YAMYAM;
3281       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3282       break;
3283
3284     case EL_SP_SNIKSNAK:
3285       MovDir[x][y] = MV_UP;
3286       break;
3287
3288     case EL_SP_ELECTRON:
3289       MovDir[x][y] = MV_LEFT;
3290       break;
3291
3292     case EL_MOLE_LEFT:
3293     case EL_MOLE_RIGHT:
3294     case EL_MOLE_UP:
3295     case EL_MOLE_DOWN:
3296       Feld[x][y] = EL_MOLE;
3297       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3298       break;
3299
3300     default:
3301       if (IS_CUSTOM_ELEMENT(element))
3302       {
3303         struct ElementInfo *ei = &element_info[element];
3304         int move_direction_initial = ei->move_direction_initial;
3305         int move_pattern = ei->move_pattern;
3306
3307         if (move_direction_initial == MV_START_PREVIOUS)
3308         {
3309           if (MovDir[x][y] != MV_NONE)
3310             return;
3311
3312           move_direction_initial = MV_START_AUTOMATIC;
3313         }
3314
3315         if (move_direction_initial == MV_START_RANDOM)
3316           MovDir[x][y] = 1 << RND(4);
3317         else if (move_direction_initial & MV_ANY_DIRECTION)
3318           MovDir[x][y] = move_direction_initial;
3319         else if (move_pattern == MV_ALL_DIRECTIONS ||
3320                  move_pattern == MV_TURNING_LEFT ||
3321                  move_pattern == MV_TURNING_RIGHT ||
3322                  move_pattern == MV_TURNING_LEFT_RIGHT ||
3323                  move_pattern == MV_TURNING_RIGHT_LEFT ||
3324                  move_pattern == MV_TURNING_RANDOM)
3325           MovDir[x][y] = 1 << RND(4);
3326         else if (move_pattern == MV_HORIZONTAL)
3327           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3328         else if (move_pattern == MV_VERTICAL)
3329           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3330         else if (move_pattern & MV_ANY_DIRECTION)
3331           MovDir[x][y] = element_info[element].move_pattern;
3332         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
3333                  move_pattern == MV_ALONG_RIGHT_SIDE)
3334         {
3335           /* use random direction as default start direction */
3336           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3337             MovDir[x][y] = 1 << RND(4);
3338
3339           for (i = 0; i < NUM_DIRECTIONS; i++)
3340           {
3341             int x1 = x + xy[i][0];
3342             int y1 = y + xy[i][1];
3343
3344             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3345             {
3346               if (move_pattern == MV_ALONG_RIGHT_SIDE)
3347                 MovDir[x][y] = direction[0][i];
3348               else
3349                 MovDir[x][y] = direction[1][i];
3350
3351               break;
3352             }
3353           }
3354         }                
3355       }
3356       else
3357       {
3358         MovDir[x][y] = 1 << RND(4);
3359
3360         if (element != EL_BUG &&
3361             element != EL_SPACESHIP &&
3362             element != EL_BD_BUTTERFLY &&
3363             element != EL_BD_FIREFLY)
3364           break;
3365
3366         for (i = 0; i < NUM_DIRECTIONS; i++)
3367         {
3368           int x1 = x + xy[i][0];
3369           int y1 = y + xy[i][1];
3370
3371           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3372           {
3373             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3374             {
3375               MovDir[x][y] = direction[0][i];
3376               break;
3377             }
3378             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3379                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3380             {
3381               MovDir[x][y] = direction[1][i];
3382               break;
3383             }
3384           }
3385         }
3386       }
3387       break;
3388   }
3389
3390   GfxDir[x][y] = MovDir[x][y];
3391 }
3392
3393 void InitAmoebaNr(int x, int y)
3394 {
3395   int i;
3396   int group_nr = AmoebeNachbarNr(x, y);
3397
3398   if (group_nr == 0)
3399   {
3400     for (i = 1; i < MAX_NUM_AMOEBA; i++)
3401     {
3402       if (AmoebaCnt[i] == 0)
3403       {
3404         group_nr = i;
3405         break;
3406       }
3407     }
3408   }
3409
3410   AmoebaNr[x][y] = group_nr;
3411   AmoebaCnt[group_nr]++;
3412   AmoebaCnt2[group_nr]++;
3413 }
3414
3415 static void PlayerWins(struct PlayerInfo *player)
3416 {
3417   player->LevelSolved = TRUE;
3418   player->GameOver = TRUE;
3419
3420   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
3421                          level.native_em_level->lev->score : player->score);
3422 }
3423
3424 void GameWon()
3425 {
3426   static int time, time_final;
3427   static int score, score_final;
3428   static int game_over_delay_1 = 0;
3429   static int game_over_delay_2 = 0;
3430   int game_over_delay_value_1 = 50;
3431   int game_over_delay_value_2 = 50;
3432
3433   if (!local_player->LevelSolved_GameWon)
3434   {
3435     int i;
3436
3437     /* do not start end game actions before the player stops moving (to exit) */
3438     if (local_player->MovPos)
3439       return;
3440
3441     local_player->LevelSolved_GameWon = TRUE;
3442     local_player->LevelSolved_SaveTape = tape.recording;
3443     local_player->LevelSolved_SaveScore = !tape.playing;
3444
3445     if (tape.auto_play)         /* tape might already be stopped here */
3446       tape.auto_play_level_solved = TRUE;
3447
3448 #if 1
3449     TapeStop();
3450 #endif
3451
3452     game_over_delay_1 = game_over_delay_value_1;
3453     game_over_delay_2 = game_over_delay_value_2;
3454
3455     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3456     score = score_final = local_player->score_final;
3457
3458     if (TimeLeft > 0)
3459     {
3460       time_final = 0;
3461       score_final += TimeLeft * level.score[SC_TIME_BONUS];
3462     }
3463     else if (level.time == 0 && TimePlayed < 999)
3464     {
3465       time_final = 999;
3466       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3467     }
3468
3469     local_player->score_final = score_final;
3470
3471     if (level_editor_test_game)
3472     {
3473       time = time_final;
3474       score = score_final;
3475
3476       DrawGameValue_Time(time);
3477       DrawGameValue_Score(score);
3478     }
3479
3480     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3481     {
3482       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
3483       {
3484         /* close exit door after last player */
3485         if ((AllPlayersGone &&
3486              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3487               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3488               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3489             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3490             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3491         {
3492           int element = Feld[ExitX][ExitY];
3493
3494 #if 0
3495           if (element == EL_EM_EXIT_OPEN ||
3496               element == EL_EM_STEEL_EXIT_OPEN)
3497           {
3498             Bang(ExitX, ExitY);
3499           }
3500           else
3501 #endif
3502           {
3503             Feld[ExitX][ExitY] =
3504               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
3505                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
3506                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
3507                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
3508                EL_EM_STEEL_EXIT_CLOSING);
3509
3510             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3511           }
3512         }
3513
3514         /* player disappears */
3515         DrawLevelField(ExitX, ExitY);
3516       }
3517
3518       for (i = 0; i < MAX_PLAYERS; i++)
3519       {
3520         struct PlayerInfo *player = &stored_player[i];
3521
3522         if (player->present)
3523         {
3524           RemovePlayer(player);
3525
3526           /* player disappears */
3527           DrawLevelField(player->jx, player->jy);
3528         }
3529       }
3530     }
3531
3532     PlaySound(SND_GAME_WINNING);
3533   }
3534
3535   if (game_over_delay_1 > 0)
3536   {
3537     game_over_delay_1--;
3538
3539     return;
3540   }
3541
3542   if (time != time_final)
3543   {
3544     int time_to_go = ABS(time_final - time);
3545     int time_count_dir = (time < time_final ? +1 : -1);
3546     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
3547
3548     time  += time_count_steps * time_count_dir;
3549     score += time_count_steps * level.score[SC_TIME_BONUS];
3550
3551     DrawGameValue_Time(time);
3552     DrawGameValue_Score(score);
3553
3554     if (time == time_final)
3555       StopSound(SND_GAME_LEVELTIME_BONUS);
3556     else if (setup.sound_loops)
3557       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
3558     else
3559       PlaySound(SND_GAME_LEVELTIME_BONUS);
3560
3561     return;
3562   }
3563
3564   local_player->LevelSolved_PanelOff = TRUE;
3565
3566   if (game_over_delay_2 > 0)
3567   {
3568     game_over_delay_2--;
3569
3570     return;
3571   }
3572
3573 #if 1
3574   GameEnd();
3575 #endif
3576 }
3577
3578 void GameEnd()
3579 {
3580   int hi_pos;
3581   boolean raise_level = FALSE;
3582
3583   local_player->LevelSolved_GameEnd = TRUE;
3584
3585   CloseDoor(DOOR_CLOSE_1);
3586
3587   if (local_player->LevelSolved_SaveTape)
3588   {
3589 #if 0
3590     TapeStop();
3591 #endif
3592
3593 #if 1
3594     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
3595 #else
3596     SaveTape(tape.level_nr);            /* ask to save tape */
3597 #endif
3598   }
3599
3600   if (level_editor_test_game)
3601   {
3602     game_status = GAME_MODE_MAIN;
3603
3604     DrawMainMenu();
3605
3606     return;
3607   }
3608
3609   if (!local_player->LevelSolved_SaveScore)
3610   {
3611     FadeOut(REDRAW_FIELD);
3612
3613     game_status = GAME_MODE_MAIN;
3614
3615     DrawAndFadeInMainMenu(REDRAW_FIELD);
3616
3617     return;
3618   }
3619
3620   if (level_nr == leveldir_current->handicap_level)
3621   {
3622     leveldir_current->handicap_level++;
3623     SaveLevelSetup_SeriesInfo();
3624   }
3625
3626   if (level_nr < leveldir_current->last_level)
3627     raise_level = TRUE;                 /* advance to next level */
3628
3629   if ((hi_pos = NewHiScore()) >= 0) 
3630   {
3631     game_status = GAME_MODE_SCORES;
3632
3633     DrawHallOfFame(hi_pos);
3634
3635     if (raise_level)
3636     {
3637       level_nr++;
3638       TapeErase();
3639     }
3640   }
3641   else
3642   {
3643     FadeOut(REDRAW_FIELD);
3644
3645     game_status = GAME_MODE_MAIN;
3646
3647     if (raise_level)
3648     {
3649       level_nr++;
3650       TapeErase();
3651     }
3652
3653     DrawAndFadeInMainMenu(REDRAW_FIELD);
3654   }
3655 }
3656
3657 int NewHiScore()
3658 {
3659   int k, l;
3660   int position = -1;
3661
3662   LoadScore(level_nr);
3663
3664   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3665       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
3666     return -1;
3667
3668   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
3669   {
3670     if (local_player->score_final > highscore[k].Score)
3671     {
3672       /* player has made it to the hall of fame */
3673
3674       if (k < MAX_SCORE_ENTRIES - 1)
3675       {
3676         int m = MAX_SCORE_ENTRIES - 1;
3677
3678 #ifdef ONE_PER_NAME
3679         for (l = k; l < MAX_SCORE_ENTRIES; l++)
3680           if (strEqual(setup.player_name, highscore[l].Name))
3681             m = l;
3682         if (m == k)     /* player's new highscore overwrites his old one */
3683           goto put_into_list;
3684 #endif
3685
3686         for (l = m; l > k; l--)
3687         {
3688           strcpy(highscore[l].Name, highscore[l - 1].Name);
3689           highscore[l].Score = highscore[l - 1].Score;
3690         }
3691       }
3692
3693 #ifdef ONE_PER_NAME
3694       put_into_list:
3695 #endif
3696       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3697       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3698       highscore[k].Score = local_player->score_final; 
3699       position = k;
3700       break;
3701     }
3702
3703 #ifdef ONE_PER_NAME
3704     else if (!strncmp(setup.player_name, highscore[k].Name,
3705                       MAX_PLAYER_NAME_LEN))
3706       break;    /* player already there with a higher score */
3707 #endif
3708
3709   }
3710
3711   if (position >= 0) 
3712     SaveScore(level_nr);
3713
3714   return position;
3715 }
3716
3717 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
3718 {
3719   int element = Feld[x][y];
3720   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3721   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3722   int horiz_move = (dx != 0);
3723   int sign = (horiz_move ? dx : dy);
3724   int step = sign * element_info[element].move_stepsize;
3725
3726   /* special values for move stepsize for spring and things on conveyor belt */
3727   if (horiz_move)
3728   {
3729     if (CAN_FALL(element) &&
3730         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3731       step = sign * MOVE_STEPSIZE_NORMAL / 2;
3732     else if (element == EL_SPRING)
3733       step = sign * MOVE_STEPSIZE_NORMAL * 2;
3734   }
3735
3736   return step;
3737 }
3738
3739 inline static int getElementMoveStepsize(int x, int y)
3740 {
3741   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
3742 }
3743
3744 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3745 {
3746   if (player->GfxAction != action || player->GfxDir != dir)
3747   {
3748 #if 0
3749     printf("Player frame reset! (%d => %d, %d => %d)\n",
3750            player->GfxAction, action, player->GfxDir, dir);
3751 #endif
3752
3753     player->GfxAction = action;
3754     player->GfxDir = dir;
3755     player->Frame = 0;
3756     player->StepFrame = 0;
3757   }
3758 }
3759
3760 #if USE_GFX_RESET_GFX_ANIMATION
3761 static void ResetGfxFrame(int x, int y, boolean redraw)
3762 {
3763   int element = Feld[x][y];
3764   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3765   int last_gfx_frame = GfxFrame[x][y];
3766
3767   if (graphic_info[graphic].anim_global_sync)
3768     GfxFrame[x][y] = FrameCounter;
3769   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3770     GfxFrame[x][y] = CustomValue[x][y];
3771   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3772     GfxFrame[x][y] = element_info[element].collect_score;
3773   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3774     GfxFrame[x][y] = ChangeDelay[x][y];
3775
3776   if (redraw && GfxFrame[x][y] != last_gfx_frame)
3777     DrawLevelGraphicAnimation(x, y, graphic);
3778 }
3779 #endif
3780
3781 static void ResetGfxAnimation(int x, int y)
3782 {
3783   GfxAction[x][y] = ACTION_DEFAULT;
3784   GfxDir[x][y] = MovDir[x][y];
3785   GfxFrame[x][y] = 0;
3786
3787 #if USE_GFX_RESET_GFX_ANIMATION
3788   ResetGfxFrame(x, y, FALSE);
3789 #endif
3790 }
3791
3792 static void ResetRandomAnimationValue(int x, int y)
3793 {
3794   GfxRandom[x][y] = INIT_GFX_RANDOM();
3795 }
3796
3797 void InitMovingField(int x, int y, int direction)
3798 {
3799   int element = Feld[x][y];
3800   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3801   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3802   int newx = x + dx;
3803   int newy = y + dy;
3804   boolean is_moving_before, is_moving_after;
3805 #if 0
3806   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3807 #endif
3808
3809   /* check if element was/is moving or being moved before/after mode change */
3810 #if 1
3811 #if 1
3812   is_moving_before = (WasJustMoving[x][y] != 0);
3813 #else
3814   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
3815   is_moving_before = WasJustMoving[x][y];
3816 #endif
3817 #else
3818   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3819 #endif
3820   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
3821
3822   /* reset animation only for moving elements which change direction of moving
3823      or which just started or stopped moving
3824      (else CEs with property "can move" / "not moving" are reset each frame) */
3825 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3826 #if 1
3827   if (is_moving_before != is_moving_after ||
3828       direction != MovDir[x][y])
3829     ResetGfxAnimation(x, y);
3830 #else
3831   if ((is_moving_before || is_moving_after) && !continues_moving)
3832     ResetGfxAnimation(x, y);
3833 #endif
3834 #else
3835   if (!continues_moving)
3836     ResetGfxAnimation(x, y);
3837 #endif
3838
3839   MovDir[x][y] = direction;
3840   GfxDir[x][y] = direction;
3841
3842 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3843   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3844                      direction == MV_DOWN && CAN_FALL(element) ?
3845                      ACTION_FALLING : ACTION_MOVING);
3846 #else
3847   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3848                      ACTION_FALLING : ACTION_MOVING);
3849 #endif
3850
3851   /* this is needed for CEs with property "can move" / "not moving" */
3852
3853   if (is_moving_after)
3854   {
3855     if (Feld[newx][newy] == EL_EMPTY)
3856       Feld[newx][newy] = EL_BLOCKED;
3857
3858     MovDir[newx][newy] = MovDir[x][y];
3859
3860 #if USE_NEW_CUSTOM_VALUE
3861     CustomValue[newx][newy] = CustomValue[x][y];
3862 #endif
3863
3864     GfxFrame[newx][newy] = GfxFrame[x][y];
3865     GfxRandom[newx][newy] = GfxRandom[x][y];
3866     GfxAction[newx][newy] = GfxAction[x][y];
3867     GfxDir[newx][newy] = GfxDir[x][y];
3868   }
3869 }
3870
3871 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3872 {
3873   int direction = MovDir[x][y];
3874   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3875   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
3876
3877   *goes_to_x = newx;
3878   *goes_to_y = newy;
3879 }
3880
3881 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3882 {
3883   int oldx = x, oldy = y;
3884   int direction = MovDir[x][y];
3885
3886   if (direction == MV_LEFT)
3887     oldx++;
3888   else if (direction == MV_RIGHT)
3889     oldx--;
3890   else if (direction == MV_UP)
3891     oldy++;
3892   else if (direction == MV_DOWN)
3893     oldy--;
3894
3895   *comes_from_x = oldx;
3896   *comes_from_y = oldy;
3897 }
3898
3899 int MovingOrBlocked2Element(int x, int y)
3900 {
3901   int element = Feld[x][y];
3902
3903   if (element == EL_BLOCKED)
3904   {
3905     int oldx, oldy;
3906
3907     Blocked2Moving(x, y, &oldx, &oldy);
3908     return Feld[oldx][oldy];
3909   }
3910   else
3911     return element;
3912 }
3913
3914 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3915 {
3916   /* like MovingOrBlocked2Element(), but if element is moving
3917      and (x,y) is the field the moving element is just leaving,
3918      return EL_BLOCKED instead of the element value */
3919   int element = Feld[x][y];
3920
3921   if (IS_MOVING(x, y))
3922   {
3923     if (element == EL_BLOCKED)
3924     {
3925       int oldx, oldy;
3926
3927       Blocked2Moving(x, y, &oldx, &oldy);
3928       return Feld[oldx][oldy];
3929     }
3930     else
3931       return EL_BLOCKED;
3932   }
3933   else
3934     return element;
3935 }
3936
3937 static void RemoveField(int x, int y)
3938 {
3939   Feld[x][y] = EL_EMPTY;
3940
3941   MovPos[x][y] = 0;
3942   MovDir[x][y] = 0;
3943   MovDelay[x][y] = 0;
3944
3945 #if USE_NEW_CUSTOM_VALUE
3946   CustomValue[x][y] = 0;
3947 #endif
3948
3949   AmoebaNr[x][y] = 0;
3950   ChangeDelay[x][y] = 0;
3951   ChangePage[x][y] = -1;
3952   Pushed[x][y] = FALSE;
3953
3954 #if 0
3955   ExplodeField[x][y] = EX_TYPE_NONE;
3956 #endif
3957
3958   GfxElement[x][y] = EL_UNDEFINED;
3959   GfxAction[x][y] = ACTION_DEFAULT;
3960   GfxDir[x][y] = MV_NONE;
3961 }
3962
3963 void RemoveMovingField(int x, int y)
3964 {
3965   int oldx = x, oldy = y, newx = x, newy = y;
3966   int element = Feld[x][y];
3967   int next_element = EL_UNDEFINED;
3968
3969   if (element != EL_BLOCKED && !IS_MOVING(x, y))
3970     return;
3971
3972   if (IS_MOVING(x, y))
3973   {
3974     Moving2Blocked(x, y, &newx, &newy);
3975
3976     if (Feld[newx][newy] != EL_BLOCKED)
3977     {
3978       /* element is moving, but target field is not free (blocked), but
3979          already occupied by something different (example: acid pool);
3980          in this case, only remove the moving field, but not the target */
3981
3982       RemoveField(oldx, oldy);
3983
3984       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3985
3986       DrawLevelField(oldx, oldy);
3987
3988       return;
3989     }
3990   }
3991   else if (element == EL_BLOCKED)
3992   {
3993     Blocked2Moving(x, y, &oldx, &oldy);
3994     if (!IS_MOVING(oldx, oldy))
3995       return;
3996   }
3997
3998   if (element == EL_BLOCKED &&
3999       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4000        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4001        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4002        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4003        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4004        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4005     next_element = get_next_element(Feld[oldx][oldy]);
4006
4007   RemoveField(oldx, oldy);
4008   RemoveField(newx, newy);
4009
4010   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4011
4012   if (next_element != EL_UNDEFINED)
4013     Feld[oldx][oldy] = next_element;
4014
4015   DrawLevelField(oldx, oldy);
4016   DrawLevelField(newx, newy);
4017 }
4018
4019 void DrawDynamite(int x, int y)
4020 {
4021   int sx = SCREENX(x), sy = SCREENY(y);
4022   int graphic = el2img(Feld[x][y]);
4023   int frame;
4024
4025   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4026     return;
4027
4028   if (IS_WALKABLE_INSIDE(Back[x][y]))
4029     return;
4030
4031   if (Back[x][y])
4032     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4033   else if (Store[x][y])
4034     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4035
4036   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4037
4038   if (Back[x][y] || Store[x][y])
4039     DrawGraphicThruMask(sx, sy, graphic, frame);
4040   else
4041     DrawGraphic(sx, sy, graphic, frame);
4042 }
4043
4044 void CheckDynamite(int x, int y)
4045 {
4046   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4047   {
4048     MovDelay[x][y]--;
4049
4050     if (MovDelay[x][y] != 0)
4051     {
4052       DrawDynamite(x, y);
4053       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4054
4055       return;
4056     }
4057   }
4058
4059   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4060
4061   Bang(x, y);
4062 }
4063
4064 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4065 {
4066   boolean num_checked_players = 0;
4067   int i;
4068
4069   for (i = 0; i < MAX_PLAYERS; i++)
4070   {
4071     if (stored_player[i].active)
4072     {
4073       int sx = stored_player[i].jx;
4074       int sy = stored_player[i].jy;
4075
4076       if (num_checked_players == 0)
4077       {
4078         *sx1 = *sx2 = sx;
4079         *sy1 = *sy2 = sy;
4080       }
4081       else
4082       {
4083         *sx1 = MIN(*sx1, sx);
4084         *sy1 = MIN(*sy1, sy);
4085         *sx2 = MAX(*sx2, sx);
4086         *sy2 = MAX(*sy2, sy);
4087       }
4088
4089       num_checked_players++;
4090     }
4091   }
4092 }
4093
4094 static boolean checkIfAllPlayersFitToScreen_RND()
4095 {
4096   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4097
4098   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4099
4100   return (sx2 - sx1 < SCR_FIELDX &&
4101           sy2 - sy1 < SCR_FIELDY);
4102 }
4103
4104 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4105 {
4106   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4107
4108   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4109
4110   *sx = (sx1 + sx2) / 2;
4111   *sy = (sy1 + sy2) / 2;
4112 }
4113
4114 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4115                         boolean center_screen, boolean quick_relocation)
4116 {
4117   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4118   boolean no_delay = (tape.warp_forward);
4119   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4120   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4121
4122   if (quick_relocation)
4123   {
4124     int offset = (setup.scroll_delay ? 3 : 0);
4125
4126     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4127     {
4128       if (center_screen)
4129       {
4130         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4131                     x > SBX_Right + MIDPOSX ? SBX_Right :
4132                     x - MIDPOSX);
4133
4134         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4135                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4136                     y - MIDPOSY);
4137       }
4138       else
4139       {
4140         /* quick relocation (without scrolling), but do not center screen */
4141
4142         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4143                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4144                                old_x - MIDPOSX);
4145
4146         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4147                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4148                                old_y - MIDPOSY);
4149
4150         int offset_x = x + (scroll_x - center_scroll_x);
4151         int offset_y = y + (scroll_y - center_scroll_y);
4152
4153         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4154                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4155                     offset_x - MIDPOSX);
4156
4157         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4158                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4159                     offset_y - MIDPOSY);
4160       }
4161     }
4162     else
4163     {
4164       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
4165           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4166         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4167
4168       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
4169           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4170         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4171
4172       /* don't scroll over playfield boundaries */
4173       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4174         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4175
4176       /* don't scroll over playfield boundaries */
4177       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4178         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4179     }
4180
4181     RedrawPlayfield(TRUE, 0,0,0,0);
4182   }
4183   else
4184   {
4185     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4186                      x > SBX_Right + MIDPOSX ? SBX_Right :
4187                      x - MIDPOSX);
4188
4189     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4190                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
4191                      y - MIDPOSY);
4192
4193     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
4194
4195     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4196     {
4197       int dx = 0, dy = 0;
4198       int fx = FX, fy = FY;
4199
4200       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4201       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4202
4203       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
4204         break;
4205
4206       scroll_x -= dx;
4207       scroll_y -= dy;
4208
4209       fx += dx * TILEX / 2;
4210       fy += dy * TILEY / 2;
4211
4212       ScrollLevel(dx, dy);
4213       DrawAllPlayers();
4214
4215       /* scroll in two steps of half tile size to make things smoother */
4216       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4217       FlushDisplay();
4218       Delay(wait_delay_value);
4219
4220       /* scroll second step to align at full tile size */
4221       BackToFront();
4222       Delay(wait_delay_value);
4223     }
4224
4225     DrawAllPlayers();
4226     BackToFront();
4227     Delay(wait_delay_value);
4228   }
4229 }
4230
4231 void RelocatePlayer(int jx, int jy, int el_player_raw)
4232 {
4233   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4234   int player_nr = GET_PLAYER_NR(el_player);
4235   struct PlayerInfo *player = &stored_player[player_nr];
4236   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4237   boolean no_delay = (tape.warp_forward);
4238   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4239   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4240   int old_jx = player->jx;
4241   int old_jy = player->jy;
4242   int old_element = Feld[old_jx][old_jy];
4243   int element = Feld[jx][jy];
4244   boolean player_relocated = (old_jx != jx || old_jy != jy);
4245
4246   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4247   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
4248   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4249   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
4250   int leave_side_horiz = move_dir_horiz;
4251   int leave_side_vert  = move_dir_vert;
4252   int enter_side = enter_side_horiz | enter_side_vert;
4253   int leave_side = leave_side_horiz | leave_side_vert;
4254
4255   if (player->GameOver)         /* do not reanimate dead player */
4256     return;
4257
4258   if (!player_relocated)        /* no need to relocate the player */
4259     return;
4260
4261   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
4262   {
4263     RemoveField(jx, jy);        /* temporarily remove newly placed player */
4264     DrawLevelField(jx, jy);
4265   }
4266
4267   if (player->present)
4268   {
4269     while (player->MovPos)
4270     {
4271       ScrollPlayer(player, SCROLL_GO_ON);
4272       ScrollScreen(NULL, SCROLL_GO_ON);
4273
4274       AdvanceFrameAndPlayerCounters(player->index_nr);
4275
4276       DrawPlayer(player);
4277
4278       BackToFront();
4279       Delay(wait_delay_value);
4280     }
4281
4282     DrawPlayer(player);         /* needed here only to cleanup last field */
4283     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
4284
4285     player->is_moving = FALSE;
4286   }
4287
4288   if (IS_CUSTOM_ELEMENT(old_element))
4289     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4290                                CE_LEFT_BY_PLAYER,
4291                                player->index_bit, leave_side);
4292
4293   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4294                                       CE_PLAYER_LEAVES_X,
4295                                       player->index_bit, leave_side);
4296
4297   Feld[jx][jy] = el_player;
4298   InitPlayerField(jx, jy, el_player, TRUE);
4299
4300   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4301   {
4302     Feld[jx][jy] = element;
4303     InitField(jx, jy, FALSE);
4304   }
4305
4306   /* only visually relocate centered player */
4307   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4308                      FALSE, level.instant_relocation);
4309
4310   TestIfPlayerTouchesBadThing(jx, jy);
4311   TestIfPlayerTouchesCustomElement(jx, jy);
4312
4313   if (IS_CUSTOM_ELEMENT(element))
4314     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4315                                player->index_bit, enter_side);
4316
4317   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4318                                       player->index_bit, enter_side);
4319 }
4320
4321 void Explode(int ex, int ey, int phase, int mode)
4322 {
4323   int x, y;
4324   int last_phase;
4325   int border_element;
4326
4327   /* !!! eliminate this variable !!! */
4328   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4329
4330   if (game.explosions_delayed)
4331   {
4332     ExplodeField[ex][ey] = mode;
4333     return;
4334   }
4335
4336   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
4337   {
4338     int center_element = Feld[ex][ey];
4339     int artwork_element, explosion_element;     /* set these values later */
4340
4341 #if 0
4342     /* --- This is only really needed (and now handled) in "Impact()". --- */
4343     /* do not explode moving elements that left the explode field in time */
4344     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4345         center_element == EL_EMPTY &&
4346         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4347       return;
4348 #endif
4349
4350 #if 0
4351     /* !!! at this place, the center element may be EL_BLOCKED !!! */
4352     if (mode == EX_TYPE_NORMAL ||
4353         mode == EX_TYPE_CENTER ||
4354         mode == EX_TYPE_CROSS)
4355       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4356 #endif
4357
4358     /* remove things displayed in background while burning dynamite */
4359     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4360       Back[ex][ey] = 0;
4361
4362     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4363     {
4364       /* put moving element to center field (and let it explode there) */
4365       center_element = MovingOrBlocked2Element(ex, ey);
4366       RemoveMovingField(ex, ey);
4367       Feld[ex][ey] = center_element;
4368     }
4369
4370     /* now "center_element" is finally determined -- set related values now */
4371     artwork_element = center_element;           /* for custom player artwork */
4372     explosion_element = center_element;         /* for custom player artwork */
4373
4374     if (IS_PLAYER(ex, ey))
4375     {
4376       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4377
4378       artwork_element = stored_player[player_nr].artwork_element;
4379
4380       if (level.use_explosion_element[player_nr])
4381       {
4382         explosion_element = level.explosion_element[player_nr];
4383         artwork_element = explosion_element;
4384       }
4385     }
4386
4387 #if 1
4388     if (mode == EX_TYPE_NORMAL ||
4389         mode == EX_TYPE_CENTER ||
4390         mode == EX_TYPE_CROSS)
4391       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4392 #endif
4393
4394     last_phase = element_info[explosion_element].explosion_delay + 1;
4395
4396     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4397     {
4398       int xx = x - ex + 1;
4399       int yy = y - ey + 1;
4400       int element;
4401
4402       if (!IN_LEV_FIELD(x, y) ||
4403           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4404           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
4405         continue;
4406
4407       element = Feld[x][y];
4408
4409       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4410       {
4411         element = MovingOrBlocked2Element(x, y);
4412
4413         if (!IS_EXPLOSION_PROOF(element))
4414           RemoveMovingField(x, y);
4415       }
4416
4417       /* indestructible elements can only explode in center (but not flames) */
4418       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4419                                            mode == EX_TYPE_BORDER)) ||
4420           element == EL_FLAMES)
4421         continue;
4422
4423       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4424          behaviour, for example when touching a yamyam that explodes to rocks
4425          with active deadly shield, a rock is created under the player !!! */
4426       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4427 #if 0
4428       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4429           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
4430            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
4431 #else
4432       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
4433 #endif
4434       {
4435         if (IS_ACTIVE_BOMB(element))
4436         {
4437           /* re-activate things under the bomb like gate or penguin */
4438           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
4439           Back[x][y] = 0;
4440         }
4441
4442         continue;
4443       }
4444
4445       /* save walkable background elements while explosion on same tile */
4446       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
4447           (x != ex || y != ey || mode == EX_TYPE_BORDER))
4448         Back[x][y] = element;
4449
4450       /* ignite explodable elements reached by other explosion */
4451       if (element == EL_EXPLOSION)
4452         element = Store2[x][y];
4453
4454       if (AmoebaNr[x][y] &&
4455           (element == EL_AMOEBA_FULL ||
4456            element == EL_BD_AMOEBA ||
4457            element == EL_AMOEBA_GROWING))
4458       {
4459         AmoebaCnt[AmoebaNr[x][y]]--;
4460         AmoebaCnt2[AmoebaNr[x][y]]--;
4461       }
4462
4463       RemoveField(x, y);
4464
4465       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
4466       {
4467         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
4468
4469         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
4470
4471         if (PLAYERINFO(ex, ey)->use_murphy)
4472           Store[x][y] = EL_EMPTY;
4473       }
4474
4475       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
4476          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
4477       else if (ELEM_IS_PLAYER(center_element))
4478         Store[x][y] = EL_EMPTY;
4479       else if (center_element == EL_YAMYAM)
4480         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4481       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4482         Store[x][y] = element_info[center_element].content.e[xx][yy];
4483 #if 1
4484       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4485          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4486          otherwise) -- FIX THIS !!! */
4487       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4488         Store[x][y] = element_info[element].content.e[1][1];
4489 #else
4490       else if (!CAN_EXPLODE(element))
4491         Store[x][y] = element_info[element].content.e[1][1];
4492 #endif
4493       else
4494         Store[x][y] = EL_EMPTY;
4495
4496       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4497           center_element == EL_AMOEBA_TO_DIAMOND)
4498         Store2[x][y] = element;
4499
4500       Feld[x][y] = EL_EXPLOSION;
4501       GfxElement[x][y] = artwork_element;
4502
4503       ExplodePhase[x][y] = 1;
4504       ExplodeDelay[x][y] = last_phase;
4505
4506       Stop[x][y] = TRUE;
4507     }
4508
4509     if (center_element == EL_YAMYAM)
4510       game.yamyam_content_nr =
4511         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4512
4513     return;
4514   }
4515
4516   if (Stop[ex][ey])
4517     return;
4518
4519   x = ex;
4520   y = ey;
4521
4522   if (phase == 1)
4523     GfxFrame[x][y] = 0;         /* restart explosion animation */
4524
4525   last_phase = ExplodeDelay[x][y];
4526
4527   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4528
4529 #ifdef DEBUG
4530
4531   /* activate this even in non-DEBUG version until cause for crash in
4532      getGraphicAnimationFrame() (see below) is found and eliminated */
4533
4534 #endif
4535 #if 1
4536
4537 #if 1
4538   /* this can happen if the player leaves an explosion just in time */
4539   if (GfxElement[x][y] == EL_UNDEFINED)
4540     GfxElement[x][y] = EL_EMPTY;
4541 #else
4542   if (GfxElement[x][y] == EL_UNDEFINED)
4543   {
4544     printf("\n\n");
4545     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4546     printf("Explode(): This should never happen!\n");
4547     printf("\n\n");
4548
4549     GfxElement[x][y] = EL_EMPTY;
4550   }
4551 #endif
4552
4553 #endif
4554
4555   border_element = Store2[x][y];
4556   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4557     border_element = StorePlayer[x][y];
4558
4559   if (phase == element_info[border_element].ignition_delay ||
4560       phase == last_phase)
4561   {
4562     boolean border_explosion = FALSE;
4563
4564     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4565         !PLAYER_EXPLOSION_PROTECTED(x, y))
4566     {
4567       KillPlayerUnlessExplosionProtected(x, y);
4568       border_explosion = TRUE;
4569     }
4570     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4571     {
4572       Feld[x][y] = Store2[x][y];
4573       Store2[x][y] = 0;
4574       Bang(x, y);
4575       border_explosion = TRUE;
4576     }
4577     else if (border_element == EL_AMOEBA_TO_DIAMOND)
4578     {
4579       AmoebeUmwandeln(x, y);
4580       Store2[x][y] = 0;
4581       border_explosion = TRUE;
4582     }
4583
4584     /* if an element just explodes due to another explosion (chain-reaction),
4585        do not immediately end the new explosion when it was the last frame of
4586        the explosion (as it would be done in the following "if"-statement!) */
4587     if (border_explosion && phase == last_phase)
4588       return;
4589   }
4590
4591   if (phase == last_phase)
4592   {
4593     int element;
4594
4595     element = Feld[x][y] = Store[x][y];
4596     Store[x][y] = Store2[x][y] = 0;
4597     GfxElement[x][y] = EL_UNDEFINED;
4598
4599     /* player can escape from explosions and might therefore be still alive */
4600     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4601         element <= EL_PLAYER_IS_EXPLODING_4)
4602     {
4603       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4604       int explosion_element = EL_PLAYER_1 + player_nr;
4605       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4606       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4607
4608       if (level.use_explosion_element[player_nr])
4609         explosion_element = level.explosion_element[player_nr];
4610
4611       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4612                     element_info[explosion_element].content.e[xx][yy]);
4613     }
4614
4615     /* restore probably existing indestructible background element */
4616     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4617       element = Feld[x][y] = Back[x][y];
4618     Back[x][y] = 0;
4619
4620     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4621     GfxDir[x][y] = MV_NONE;
4622     ChangeDelay[x][y] = 0;
4623     ChangePage[x][y] = -1;
4624
4625 #if USE_NEW_CUSTOM_VALUE
4626     CustomValue[x][y] = 0;
4627 #endif
4628
4629     InitField_WithBug2(x, y, FALSE);
4630
4631     DrawLevelField(x, y);
4632
4633     TestIfElementTouchesCustomElement(x, y);
4634
4635     if (GFX_CRUMBLED(element))
4636       DrawLevelFieldCrumbledSandNeighbours(x, y);
4637
4638     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4639       StorePlayer[x][y] = 0;
4640
4641     if (ELEM_IS_PLAYER(element))
4642       RelocatePlayer(x, y, element);
4643   }
4644   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4645   {
4646     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4647     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4648
4649     if (phase == delay)
4650       DrawLevelFieldCrumbledSand(x, y);
4651
4652     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4653     {
4654       DrawLevelElement(x, y, Back[x][y]);
4655       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4656     }
4657     else if (IS_WALKABLE_UNDER(Back[x][y]))
4658     {
4659       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4660       DrawLevelElementThruMask(x, y, Back[x][y]);
4661     }
4662     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4663       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4664   }
4665 }
4666
4667 void DynaExplode(int ex, int ey)
4668 {
4669   int i, j;
4670   int dynabomb_element = Feld[ex][ey];
4671   int dynabomb_size = 1;
4672   boolean dynabomb_xl = FALSE;
4673   struct PlayerInfo *player;
4674   static int xy[4][2] =
4675   {
4676     { 0, -1 },
4677     { -1, 0 },
4678     { +1, 0 },
4679     { 0, +1 }
4680   };
4681
4682   if (IS_ACTIVE_BOMB(dynabomb_element))
4683   {
4684     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4685     dynabomb_size = player->dynabomb_size;
4686     dynabomb_xl = player->dynabomb_xl;
4687     player->dynabombs_left++;
4688   }
4689
4690   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4691
4692   for (i = 0; i < NUM_DIRECTIONS; i++)
4693   {
4694     for (j = 1; j <= dynabomb_size; j++)
4695     {
4696       int x = ex + j * xy[i][0];
4697       int y = ey + j * xy[i][1];
4698       int element;
4699
4700       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4701         break;
4702
4703       element = Feld[x][y];
4704
4705       /* do not restart explosions of fields with active bombs */
4706       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4707         continue;
4708
4709       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4710
4711       if (element != EL_EMPTY && element != EL_EXPLOSION &&
4712           !IS_DIGGABLE(element) && !dynabomb_xl)
4713         break;
4714     }
4715   }
4716 }
4717
4718 void Bang(int x, int y)
4719 {
4720   int element = MovingOrBlocked2Element(x, y);
4721   int explosion_type = EX_TYPE_NORMAL;
4722
4723   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4724   {
4725     struct PlayerInfo *player = PLAYERINFO(x, y);
4726
4727     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4728                             player->element_nr);
4729
4730     if (level.use_explosion_element[player->index_nr])
4731     {
4732       int explosion_element = level.explosion_element[player->index_nr];
4733
4734       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4735         explosion_type = EX_TYPE_CROSS;
4736       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4737         explosion_type = EX_TYPE_CENTER;
4738     }
4739   }
4740
4741   switch (element)
4742   {
4743     case EL_BUG:
4744     case EL_SPACESHIP:
4745     case EL_BD_BUTTERFLY:
4746     case EL_BD_FIREFLY:
4747     case EL_YAMYAM:
4748     case EL_DARK_YAMYAM:
4749     case EL_ROBOT:
4750     case EL_PACMAN:
4751     case EL_MOLE:
4752       RaiseScoreElement(element);
4753       break;
4754
4755     case EL_DYNABOMB_PLAYER_1_ACTIVE:
4756     case EL_DYNABOMB_PLAYER_2_ACTIVE:
4757     case EL_DYNABOMB_PLAYER_3_ACTIVE:
4758     case EL_DYNABOMB_PLAYER_4_ACTIVE:
4759     case EL_DYNABOMB_INCREASE_NUMBER:
4760     case EL_DYNABOMB_INCREASE_SIZE:
4761     case EL_DYNABOMB_INCREASE_POWER:
4762       explosion_type = EX_TYPE_DYNA;
4763       break;
4764
4765     case EL_DC_LANDMINE:
4766 #if 0
4767     case EL_EM_EXIT_OPEN:
4768     case EL_EM_STEEL_EXIT_OPEN:
4769 #endif
4770       explosion_type = EX_TYPE_CENTER;
4771       break;
4772
4773     case EL_PENGUIN:
4774     case EL_LAMP:
4775     case EL_LAMP_ACTIVE:
4776     case EL_AMOEBA_TO_DIAMOND:
4777       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
4778         explosion_type = EX_TYPE_CENTER;
4779       break;
4780
4781     default:
4782       if (element_info[element].explosion_type == EXPLODES_CROSS)
4783         explosion_type = EX_TYPE_CROSS;
4784       else if (element_info[element].explosion_type == EXPLODES_1X1)
4785         explosion_type = EX_TYPE_CENTER;
4786       break;
4787   }
4788
4789   if (explosion_type == EX_TYPE_DYNA)
4790     DynaExplode(x, y);
4791   else
4792     Explode(x, y, EX_PHASE_START, explosion_type);
4793
4794   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4795 }
4796
4797 void SplashAcid(int x, int y)
4798 {
4799   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4800       (!IN_LEV_FIELD(x - 1, y - 2) ||
4801        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4802     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4803
4804   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4805       (!IN_LEV_FIELD(x + 1, y - 2) ||
4806        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4807     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4808
4809   PlayLevelSound(x, y, SND_ACID_SPLASHING);
4810 }
4811
4812 static void InitBeltMovement()
4813 {
4814   static int belt_base_element[4] =
4815   {
4816     EL_CONVEYOR_BELT_1_LEFT,
4817     EL_CONVEYOR_BELT_2_LEFT,
4818     EL_CONVEYOR_BELT_3_LEFT,
4819     EL_CONVEYOR_BELT_4_LEFT
4820   };
4821   static int belt_base_active_element[4] =
4822   {
4823     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4824     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4825     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4826     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4827   };
4828
4829   int x, y, i, j;
4830
4831   /* set frame order for belt animation graphic according to belt direction */
4832   for (i = 0; i < NUM_BELTS; i++)
4833   {
4834     int belt_nr = i;
4835
4836     for (j = 0; j < NUM_BELT_PARTS; j++)
4837     {
4838       int element = belt_base_active_element[belt_nr] + j;
4839       int graphic = el2img(element);
4840
4841       if (game.belt_dir[i] == MV_LEFT)
4842         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4843       else
4844         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4845     }
4846   }
4847
4848   SCAN_PLAYFIELD(x, y)
4849   {
4850     int element = Feld[x][y];
4851
4852     for (i = 0; i < NUM_BELTS; i++)
4853     {
4854       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4855       {
4856         int e_belt_nr = getBeltNrFromBeltElement(element);
4857         int belt_nr = i;
4858
4859         if (e_belt_nr == belt_nr)
4860         {
4861           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4862
4863           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4864         }
4865       }
4866     }
4867   }
4868 }
4869
4870 static void ToggleBeltSwitch(int x, int y)
4871 {
4872   static int belt_base_element[4] =
4873   {
4874     EL_CONVEYOR_BELT_1_LEFT,
4875     EL_CONVEYOR_BELT_2_LEFT,
4876     EL_CONVEYOR_BELT_3_LEFT,
4877     EL_CONVEYOR_BELT_4_LEFT
4878   };
4879   static int belt_base_active_element[4] =
4880   {
4881     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4882     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4883     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4884     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4885   };
4886   static int belt_base_switch_element[4] =
4887   {
4888     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4889     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4890     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4891     EL_CONVEYOR_BELT_4_SWITCH_LEFT
4892   };
4893   static int belt_move_dir[4] =
4894   {
4895     MV_LEFT,
4896     MV_NONE,
4897     MV_RIGHT,
4898     MV_NONE,
4899   };
4900
4901   int element = Feld[x][y];
4902   int belt_nr = getBeltNrFromBeltSwitchElement(element);
4903   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4904   int belt_dir = belt_move_dir[belt_dir_nr];
4905   int xx, yy, i;
4906
4907   if (!IS_BELT_SWITCH(element))
4908     return;
4909
4910   game.belt_dir_nr[belt_nr] = belt_dir_nr;
4911   game.belt_dir[belt_nr] = belt_dir;
4912
4913   if (belt_dir_nr == 3)
4914     belt_dir_nr = 1;
4915
4916   /* set frame order for belt animation graphic according to belt direction */
4917   for (i = 0; i < NUM_BELT_PARTS; i++)
4918   {
4919     int element = belt_base_active_element[belt_nr] + i;
4920     int graphic = el2img(element);
4921
4922     if (belt_dir == MV_LEFT)
4923       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4924     else
4925       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4926   }
4927
4928   SCAN_PLAYFIELD(xx, yy)
4929   {
4930     int element = Feld[xx][yy];
4931
4932     if (IS_BELT_SWITCH(element))
4933     {
4934       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4935
4936       if (e_belt_nr == belt_nr)
4937       {
4938         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4939         DrawLevelField(xx, yy);
4940       }
4941     }
4942     else if (IS_BELT(element) && belt_dir != MV_NONE)
4943     {
4944       int e_belt_nr = getBeltNrFromBeltElement(element);
4945
4946       if (e_belt_nr == belt_nr)
4947       {
4948         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4949
4950         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4951         DrawLevelField(xx, yy);
4952       }
4953     }
4954     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4955     {
4956       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4957
4958       if (e_belt_nr == belt_nr)
4959       {
4960         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4961
4962         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4963         DrawLevelField(xx, yy);
4964       }
4965     }
4966   }
4967 }
4968
4969 static void ToggleSwitchgateSwitch(int x, int y)
4970 {
4971   int xx, yy;
4972
4973   game.switchgate_pos = !game.switchgate_pos;
4974
4975   SCAN_PLAYFIELD(xx, yy)
4976   {
4977     int element = Feld[xx][yy];
4978
4979 #if !USE_BOTH_SWITCHGATE_SWITCHES
4980     if (element == EL_SWITCHGATE_SWITCH_UP ||
4981         element == EL_SWITCHGATE_SWITCH_DOWN)
4982     {
4983       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4984       DrawLevelField(xx, yy);
4985     }
4986     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
4987              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4988     {
4989       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4990       DrawLevelField(xx, yy);
4991     }
4992 #else
4993     if (element == EL_SWITCHGATE_SWITCH_UP)
4994     {
4995       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4996       DrawLevelField(xx, yy);
4997     }
4998     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4999     {
5000       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5001       DrawLevelField(xx, yy);
5002     }
5003     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5004     {
5005       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5006       DrawLevelField(xx, yy);
5007     }
5008     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5009     {
5010       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5011       DrawLevelField(xx, yy);
5012     }
5013 #endif
5014     else if (element == EL_SWITCHGATE_OPEN ||
5015              element == EL_SWITCHGATE_OPENING)
5016     {
5017       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5018
5019       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5020     }
5021     else if (element == EL_SWITCHGATE_CLOSED ||
5022              element == EL_SWITCHGATE_CLOSING)
5023     {
5024       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5025
5026       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5027     }
5028   }
5029 }
5030
5031 static int getInvisibleActiveFromInvisibleElement(int element)
5032 {
5033   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5034           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5035           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5036           element);
5037 }
5038
5039 static int getInvisibleFromInvisibleActiveElement(int element)
5040 {
5041   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5042           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5043           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5044           element);
5045 }
5046
5047 static void RedrawAllLightSwitchesAndInvisibleElements()
5048 {
5049   int x, y;
5050
5051   SCAN_PLAYFIELD(x, y)
5052   {
5053     int element = Feld[x][y];
5054
5055     if (element == EL_LIGHT_SWITCH &&
5056         game.light_time_left > 0)
5057     {
5058       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5059       DrawLevelField(x, y);
5060     }
5061     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5062              game.light_time_left == 0)
5063     {
5064       Feld[x][y] = EL_LIGHT_SWITCH;
5065       DrawLevelField(x, y);
5066     }
5067     else if (element == EL_EMC_DRIPPER &&
5068              game.light_time_left > 0)
5069     {
5070       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5071       DrawLevelField(x, y);
5072     }
5073     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5074              game.light_time_left == 0)
5075     {
5076       Feld[x][y] = EL_EMC_DRIPPER;
5077       DrawLevelField(x, y);
5078     }
5079     else if (element == EL_INVISIBLE_STEELWALL ||
5080              element == EL_INVISIBLE_WALL ||
5081              element == EL_INVISIBLE_SAND)
5082     {
5083       if (game.light_time_left > 0)
5084         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5085
5086       DrawLevelField(x, y);
5087
5088       /* uncrumble neighbour fields, if needed */
5089       if (element == EL_INVISIBLE_SAND)
5090         DrawLevelFieldCrumbledSandNeighbours(x, y);
5091     }
5092     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5093              element == EL_INVISIBLE_WALL_ACTIVE ||
5094              element == EL_INVISIBLE_SAND_ACTIVE)
5095     {
5096       if (game.light_time_left == 0)
5097         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5098
5099       DrawLevelField(x, y);
5100
5101       /* re-crumble neighbour fields, if needed */
5102       if (element == EL_INVISIBLE_SAND)
5103         DrawLevelFieldCrumbledSandNeighbours(x, y);
5104     }
5105   }
5106 }
5107
5108 static void RedrawAllInvisibleElementsForLenses()
5109 {
5110   int x, y;
5111
5112   SCAN_PLAYFIELD(x, y)
5113   {
5114     int element = Feld[x][y];
5115
5116     if (element == EL_EMC_DRIPPER &&
5117         game.lenses_time_left > 0)
5118     {
5119       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5120       DrawLevelField(x, y);
5121     }
5122     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5123              game.lenses_time_left == 0)
5124     {
5125       Feld[x][y] = EL_EMC_DRIPPER;
5126       DrawLevelField(x, y);
5127     }
5128     else if (element == EL_INVISIBLE_STEELWALL ||
5129              element == EL_INVISIBLE_WALL ||
5130              element == EL_INVISIBLE_SAND)
5131     {
5132       if (game.lenses_time_left > 0)
5133         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5134
5135       DrawLevelField(x, y);
5136
5137       /* uncrumble neighbour fields, if needed */
5138       if (element == EL_INVISIBLE_SAND)
5139         DrawLevelFieldCrumbledSandNeighbours(x, y);
5140     }
5141     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5142              element == EL_INVISIBLE_WALL_ACTIVE ||
5143              element == EL_INVISIBLE_SAND_ACTIVE)
5144     {
5145       if (game.lenses_time_left == 0)
5146         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5147
5148       DrawLevelField(x, y);
5149
5150       /* re-crumble neighbour fields, if needed */
5151       if (element == EL_INVISIBLE_SAND)
5152         DrawLevelFieldCrumbledSandNeighbours(x, y);
5153     }
5154   }
5155 }
5156
5157 static void RedrawAllInvisibleElementsForMagnifier()
5158 {
5159   int x, y;
5160
5161   SCAN_PLAYFIELD(x, y)
5162   {
5163     int element = Feld[x][y];
5164
5165     if (element == EL_EMC_FAKE_GRASS &&
5166         game.magnify_time_left > 0)
5167     {
5168       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5169       DrawLevelField(x, y);
5170     }
5171     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5172              game.magnify_time_left == 0)
5173     {
5174       Feld[x][y] = EL_EMC_FAKE_GRASS;
5175       DrawLevelField(x, y);
5176     }
5177     else if (IS_GATE_GRAY(element) &&
5178              game.magnify_time_left > 0)
5179     {
5180       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5181                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5182                     IS_EM_GATE_GRAY(element) ?
5183                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5184                     IS_EMC_GATE_GRAY(element) ?
5185                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5186                     element);
5187       DrawLevelField(x, y);
5188     }
5189     else if (IS_GATE_GRAY_ACTIVE(element) &&
5190              game.magnify_time_left == 0)
5191     {
5192       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5193                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5194                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5195                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5196                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5197                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5198                     element);
5199       DrawLevelField(x, y);
5200     }
5201   }
5202 }
5203
5204 static void ToggleLightSwitch(int x, int y)
5205 {
5206   int element = Feld[x][y];
5207
5208   game.light_time_left =
5209     (element == EL_LIGHT_SWITCH ?
5210      level.time_light * FRAMES_PER_SECOND : 0);
5211
5212   RedrawAllLightSwitchesAndInvisibleElements();
5213 }
5214
5215 static void ActivateTimegateSwitch(int x, int y)
5216 {
5217   int xx, yy;
5218
5219   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5220
5221   SCAN_PLAYFIELD(xx, yy)
5222   {
5223     int element = Feld[xx][yy];
5224
5225     if (element == EL_TIMEGATE_CLOSED ||
5226         element == EL_TIMEGATE_CLOSING)
5227     {
5228       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5229       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5230     }
5231
5232     /*
5233     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5234     {
5235       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5236       DrawLevelField(xx, yy);
5237     }
5238     */
5239
5240   }
5241
5242 #if 1
5243   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5244                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5245 #else
5246   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5247 #endif
5248 }
5249
5250 void Impact(int x, int y)
5251 {
5252   boolean last_line = (y == lev_fieldy - 1);
5253   boolean object_hit = FALSE;
5254   boolean impact = (last_line || object_hit);
5255   int element = Feld[x][y];
5256   int smashed = EL_STEELWALL;
5257
5258   if (!last_line)       /* check if element below was hit */
5259   {
5260     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5261       return;
5262
5263     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5264                                          MovDir[x][y + 1] != MV_DOWN ||
5265                                          MovPos[x][y + 1] <= TILEY / 2));
5266
5267     /* do not smash moving elements that left the smashed field in time */
5268     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5269         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5270       object_hit = FALSE;
5271
5272 #if USE_QUICKSAND_IMPACT_BUGFIX
5273     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5274     {
5275       RemoveMovingField(x, y + 1);
5276       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5277       Feld[x][y + 2] = EL_ROCK;
5278       DrawLevelField(x, y + 2);
5279
5280       object_hit = TRUE;
5281     }
5282
5283     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5284     {
5285       RemoveMovingField(x, y + 1);
5286       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5287       Feld[x][y + 2] = EL_ROCK;
5288       DrawLevelField(x, y + 2);
5289
5290       object_hit = TRUE;
5291     }
5292 #endif
5293
5294     if (object_hit)
5295       smashed = MovingOrBlocked2Element(x, y + 1);
5296
5297     impact = (last_line || object_hit);
5298   }
5299
5300   if (!last_line && smashed == EL_ACID) /* element falls into acid */
5301   {
5302     SplashAcid(x, y + 1);
5303     return;
5304   }
5305
5306   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5307   /* only reset graphic animation if graphic really changes after impact */
5308   if (impact &&
5309       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5310   {
5311     ResetGfxAnimation(x, y);
5312     DrawLevelField(x, y);
5313   }
5314
5315   if (impact && CAN_EXPLODE_IMPACT(element))
5316   {
5317     Bang(x, y);
5318     return;
5319   }
5320   else if (impact && element == EL_PEARL &&
5321            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5322   {
5323     ResetGfxAnimation(x, y);
5324
5325     Feld[x][y] = EL_PEARL_BREAKING;
5326     PlayLevelSound(x, y, SND_PEARL_BREAKING);
5327     return;
5328   }
5329   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5330   {
5331     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5332
5333     return;
5334   }
5335
5336   if (impact && element == EL_AMOEBA_DROP)
5337   {
5338     if (object_hit && IS_PLAYER(x, y + 1))
5339       KillPlayerUnlessEnemyProtected(x, y + 1);
5340     else if (object_hit && smashed == EL_PENGUIN)
5341       Bang(x, y + 1);
5342     else
5343     {
5344       Feld[x][y] = EL_AMOEBA_GROWING;
5345       Store[x][y] = EL_AMOEBA_WET;
5346
5347       ResetRandomAnimationValue(x, y);
5348     }
5349     return;
5350   }
5351
5352   if (object_hit)               /* check which object was hit */
5353   {
5354     if ((CAN_PASS_MAGIC_WALL(element) && 
5355          (smashed == EL_MAGIC_WALL ||
5356           smashed == EL_BD_MAGIC_WALL)) ||
5357         (CAN_PASS_DC_MAGIC_WALL(element) &&
5358          smashed == EL_DC_MAGIC_WALL))
5359     {
5360       int xx, yy;
5361       int activated_magic_wall =
5362         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5363          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5364          EL_DC_MAGIC_WALL_ACTIVE);
5365
5366       /* activate magic wall / mill */
5367       SCAN_PLAYFIELD(xx, yy)
5368       {
5369         if (Feld[xx][yy] == smashed)
5370           Feld[xx][yy] = activated_magic_wall;
5371       }
5372
5373       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5374       game.magic_wall_active = TRUE;
5375
5376       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5377                             SND_MAGIC_WALL_ACTIVATING :
5378                             smashed == EL_BD_MAGIC_WALL ?
5379                             SND_BD_MAGIC_WALL_ACTIVATING :
5380                             SND_DC_MAGIC_WALL_ACTIVATING));
5381     }
5382
5383     if (IS_PLAYER(x, y + 1))
5384     {
5385       if (CAN_SMASH_PLAYER(element))
5386       {
5387         KillPlayerUnlessEnemyProtected(x, y + 1);
5388         return;
5389       }
5390     }
5391     else if (smashed == EL_PENGUIN)
5392     {
5393       if (CAN_SMASH_PLAYER(element))
5394       {
5395         Bang(x, y + 1);
5396         return;
5397       }
5398     }
5399     else if (element == EL_BD_DIAMOND)
5400     {
5401       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5402       {
5403         Bang(x, y + 1);
5404         return;
5405       }
5406     }
5407     else if (((element == EL_SP_INFOTRON ||
5408                element == EL_SP_ZONK) &&
5409               (smashed == EL_SP_SNIKSNAK ||
5410                smashed == EL_SP_ELECTRON ||
5411                smashed == EL_SP_DISK_ORANGE)) ||
5412              (element == EL_SP_INFOTRON &&
5413               smashed == EL_SP_DISK_YELLOW))
5414     {
5415       Bang(x, y + 1);
5416       return;
5417     }
5418     else if (CAN_SMASH_EVERYTHING(element))
5419     {
5420       if (IS_CLASSIC_ENEMY(smashed) ||
5421           CAN_EXPLODE_SMASHED(smashed))
5422       {
5423         Bang(x, y + 1);
5424         return;
5425       }
5426       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5427       {
5428         if (smashed == EL_LAMP ||
5429             smashed == EL_LAMP_ACTIVE)
5430         {
5431           Bang(x, y + 1);
5432           return;
5433         }
5434         else if (smashed == EL_NUT)
5435         {
5436           Feld[x][y + 1] = EL_NUT_BREAKING;
5437           PlayLevelSound(x, y, SND_NUT_BREAKING);
5438           RaiseScoreElement(EL_NUT);
5439           return;
5440         }
5441         else if (smashed == EL_PEARL)
5442         {
5443           ResetGfxAnimation(x, y);
5444
5445           Feld[x][y + 1] = EL_PEARL_BREAKING;
5446           PlayLevelSound(x, y, SND_PEARL_BREAKING);
5447           return;
5448         }
5449         else if (smashed == EL_DIAMOND)
5450         {
5451           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
5452           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
5453           return;
5454         }
5455         else if (IS_BELT_SWITCH(smashed))
5456         {
5457           ToggleBeltSwitch(x, y + 1);
5458         }
5459         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
5460                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
5461                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
5462                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
5463         {
5464           ToggleSwitchgateSwitch(x, y + 1);
5465         }
5466         else if (smashed == EL_LIGHT_SWITCH ||
5467                  smashed == EL_LIGHT_SWITCH_ACTIVE)
5468         {
5469           ToggleLightSwitch(x, y + 1);
5470         }
5471         else
5472         {
5473 #if 0
5474           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
5475 #endif
5476
5477           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5478
5479           CheckElementChangeBySide(x, y + 1, smashed, element,
5480                                    CE_SWITCHED, CH_SIDE_TOP);
5481           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5482                                             CH_SIDE_TOP);
5483         }
5484       }
5485       else
5486       {
5487         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5488       }
5489     }
5490   }
5491
5492   /* play sound of magic wall / mill */
5493   if (!last_line &&
5494       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5495        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
5496        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
5497   {
5498     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5499       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5500     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5501       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5502     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5503       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5504
5505     return;
5506   }
5507
5508   /* play sound of object that hits the ground */
5509   if (last_line || object_hit)
5510     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5511 }
5512
5513 inline static void TurnRoundExt(int x, int y)
5514 {
5515   static struct
5516   {
5517     int dx, dy;
5518   } move_xy[] =
5519   {
5520     {  0,  0 },
5521     { -1,  0 },
5522     { +1,  0 },
5523     {  0,  0 },
5524     {  0, -1 },
5525     {  0,  0 }, { 0, 0 }, { 0, 0 },
5526     {  0, +1 }
5527   };
5528   static struct
5529   {
5530     int left, right, back;
5531   } turn[] =
5532   {
5533     { 0,        0,              0        },
5534     { MV_DOWN,  MV_UP,          MV_RIGHT },
5535     { MV_UP,    MV_DOWN,        MV_LEFT  },
5536     { 0,        0,              0        },
5537     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
5538     { 0,        0,              0        },
5539     { 0,        0,              0        },
5540     { 0,        0,              0        },
5541     { MV_RIGHT, MV_LEFT,        MV_UP    }
5542   };
5543
5544   int element = Feld[x][y];
5545   int move_pattern = element_info[element].move_pattern;
5546
5547   int old_move_dir = MovDir[x][y];
5548   int left_dir  = turn[old_move_dir].left;
5549   int right_dir = turn[old_move_dir].right;
5550   int back_dir  = turn[old_move_dir].back;
5551
5552   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
5553   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
5554   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
5555   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
5556
5557   int left_x  = x + left_dx,  left_y  = y + left_dy;
5558   int right_x = x + right_dx, right_y = y + right_dy;
5559   int move_x  = x + move_dx,  move_y  = y + move_dy;
5560
5561   int xx, yy;
5562
5563   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5564   {
5565     TestIfBadThingTouchesOtherBadThing(x, y);
5566
5567     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5568       MovDir[x][y] = right_dir;
5569     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5570       MovDir[x][y] = left_dir;
5571
5572     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5573       MovDelay[x][y] = 9;
5574     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
5575       MovDelay[x][y] = 1;
5576   }
5577   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5578   {
5579     TestIfBadThingTouchesOtherBadThing(x, y);
5580
5581     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5582       MovDir[x][y] = left_dir;
5583     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5584       MovDir[x][y] = right_dir;
5585
5586     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5587       MovDelay[x][y] = 9;
5588     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
5589       MovDelay[x][y] = 1;
5590   }
5591   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5592   {
5593     TestIfBadThingTouchesOtherBadThing(x, y);
5594
5595     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5596       MovDir[x][y] = left_dir;
5597     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5598       MovDir[x][y] = right_dir;
5599
5600     if (MovDir[x][y] != old_move_dir)
5601       MovDelay[x][y] = 9;
5602   }
5603   else if (element == EL_YAMYAM)
5604   {
5605     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5606     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5607
5608     if (can_turn_left && can_turn_right)
5609       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5610     else if (can_turn_left)
5611       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5612     else if (can_turn_right)
5613       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5614     else
5615       MovDir[x][y] = back_dir;
5616
5617     MovDelay[x][y] = 16 + 16 * RND(3);
5618   }
5619   else if (element == EL_DARK_YAMYAM)
5620   {
5621     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5622                                                          left_x, left_y);
5623     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5624                                                          right_x, right_y);
5625
5626     if (can_turn_left && can_turn_right)
5627       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5628     else if (can_turn_left)
5629       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5630     else if (can_turn_right)
5631       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5632     else
5633       MovDir[x][y] = back_dir;
5634
5635     MovDelay[x][y] = 16 + 16 * RND(3);
5636   }
5637   else if (element == EL_PACMAN)
5638   {
5639     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5640     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5641
5642     if (can_turn_left && can_turn_right)
5643       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5644     else if (can_turn_left)
5645       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5646     else if (can_turn_right)
5647       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5648     else
5649       MovDir[x][y] = back_dir;
5650
5651     MovDelay[x][y] = 6 + RND(40);
5652   }
5653   else if (element == EL_PIG)
5654   {
5655     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5656     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5657     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5658     boolean should_turn_left, should_turn_right, should_move_on;
5659     int rnd_value = 24;
5660     int rnd = RND(rnd_value);
5661
5662     should_turn_left = (can_turn_left &&
5663                         (!can_move_on ||
5664                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5665                                                    y + back_dy + left_dy)));
5666     should_turn_right = (can_turn_right &&
5667                          (!can_move_on ||
5668                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5669                                                     y + back_dy + right_dy)));
5670     should_move_on = (can_move_on &&
5671                       (!can_turn_left ||
5672                        !can_turn_right ||
5673                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5674                                                  y + move_dy + left_dy) ||
5675                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5676                                                  y + move_dy + right_dy)));
5677
5678     if (should_turn_left || should_turn_right || should_move_on)
5679     {
5680       if (should_turn_left && should_turn_right && should_move_on)
5681         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
5682                         rnd < 2 * rnd_value / 3 ? right_dir :
5683                         old_move_dir);
5684       else if (should_turn_left && should_turn_right)
5685         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5686       else if (should_turn_left && should_move_on)
5687         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5688       else if (should_turn_right && should_move_on)
5689         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5690       else if (should_turn_left)
5691         MovDir[x][y] = left_dir;
5692       else if (should_turn_right)
5693         MovDir[x][y] = right_dir;
5694       else if (should_move_on)
5695         MovDir[x][y] = old_move_dir;
5696     }
5697     else if (can_move_on && rnd > rnd_value / 8)
5698       MovDir[x][y] = old_move_dir;
5699     else if (can_turn_left && can_turn_right)
5700       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5701     else if (can_turn_left && rnd > rnd_value / 8)
5702       MovDir[x][y] = left_dir;
5703     else if (can_turn_right && rnd > rnd_value/8)
5704       MovDir[x][y] = right_dir;
5705     else
5706       MovDir[x][y] = back_dir;
5707
5708     xx = x + move_xy[MovDir[x][y]].dx;
5709     yy = y + move_xy[MovDir[x][y]].dy;
5710
5711     if (!IN_LEV_FIELD(xx, yy) ||
5712         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5713       MovDir[x][y] = old_move_dir;
5714
5715     MovDelay[x][y] = 0;
5716   }
5717   else if (element == EL_DRAGON)
5718   {
5719     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5720     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5721     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5722     int rnd_value = 24;
5723     int rnd = RND(rnd_value);
5724
5725     if (can_move_on && rnd > rnd_value / 8)
5726       MovDir[x][y] = old_move_dir;
5727     else if (can_turn_left && can_turn_right)
5728       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5729     else if (can_turn_left && rnd > rnd_value / 8)
5730       MovDir[x][y] = left_dir;
5731     else if (can_turn_right && rnd > rnd_value / 8)
5732       MovDir[x][y] = right_dir;
5733     else
5734       MovDir[x][y] = back_dir;
5735
5736     xx = x + move_xy[MovDir[x][y]].dx;
5737     yy = y + move_xy[MovDir[x][y]].dy;
5738
5739     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5740       MovDir[x][y] = old_move_dir;
5741
5742     MovDelay[x][y] = 0;
5743   }
5744   else if (element == EL_MOLE)
5745   {
5746     boolean can_move_on =
5747       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5748                             IS_AMOEBOID(Feld[move_x][move_y]) ||
5749                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5750     if (!can_move_on)
5751     {
5752       boolean can_turn_left =
5753         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5754                               IS_AMOEBOID(Feld[left_x][left_y])));
5755
5756       boolean can_turn_right =
5757         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5758                               IS_AMOEBOID(Feld[right_x][right_y])));
5759
5760       if (can_turn_left && can_turn_right)
5761         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5762       else if (can_turn_left)
5763         MovDir[x][y] = left_dir;
5764       else
5765         MovDir[x][y] = right_dir;
5766     }
5767
5768     if (MovDir[x][y] != old_move_dir)
5769       MovDelay[x][y] = 9;
5770   }
5771   else if (element == EL_BALLOON)
5772   {
5773     MovDir[x][y] = game.wind_direction;
5774     MovDelay[x][y] = 0;
5775   }
5776   else if (element == EL_SPRING)
5777   {
5778 #if USE_NEW_SPRING_BUMPER
5779     if (MovDir[x][y] & MV_HORIZONTAL)
5780     {
5781       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5782           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5783       {
5784         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5785         ResetGfxAnimation(move_x, move_y);
5786         DrawLevelField(move_x, move_y);
5787
5788         MovDir[x][y] = back_dir;
5789       }
5790       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5791                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5792         MovDir[x][y] = MV_NONE;
5793     }
5794 #else
5795     if (MovDir[x][y] & MV_HORIZONTAL &&
5796         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5797          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5798       MovDir[x][y] = MV_NONE;
5799 #endif
5800
5801     MovDelay[x][y] = 0;
5802   }
5803   else if (element == EL_ROBOT ||
5804            element == EL_SATELLITE ||
5805            element == EL_PENGUIN ||
5806            element == EL_EMC_ANDROID)
5807   {
5808     int attr_x = -1, attr_y = -1;
5809
5810     if (AllPlayersGone)
5811     {
5812       attr_x = ExitX;
5813       attr_y = ExitY;
5814     }
5815     else
5816     {
5817       int i;
5818
5819       for (i = 0; i < MAX_PLAYERS; i++)
5820       {
5821         struct PlayerInfo *player = &stored_player[i];
5822         int jx = player->jx, jy = player->jy;
5823
5824         if (!player->active)
5825           continue;
5826
5827         if (attr_x == -1 ||
5828             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5829         {
5830           attr_x = jx;
5831           attr_y = jy;
5832         }
5833       }
5834     }
5835
5836     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5837         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5838          game.engine_version < VERSION_IDENT(3,1,0,0)))
5839     {
5840       attr_x = ZX;
5841       attr_y = ZY;
5842     }
5843
5844     if (element == EL_PENGUIN)
5845     {
5846       int i;
5847       static int xy[4][2] =
5848       {
5849         { 0, -1 },
5850         { -1, 0 },
5851         { +1, 0 },
5852         { 0, +1 }
5853       };
5854
5855       for (i = 0; i < NUM_DIRECTIONS; i++)
5856       {
5857         int ex = x + xy[i][0];
5858         int ey = y + xy[i][1];
5859
5860         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
5861                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
5862                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
5863                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
5864         {
5865           attr_x = ex;
5866           attr_y = ey;
5867           break;
5868         }
5869       }
5870     }
5871
5872     MovDir[x][y] = MV_NONE;
5873     if (attr_x < x)
5874       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5875     else if (attr_x > x)
5876       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5877     if (attr_y < y)
5878       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5879     else if (attr_y > y)
5880       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5881
5882     if (element == EL_ROBOT)
5883     {
5884       int newx, newy;
5885
5886       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5887         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5888       Moving2Blocked(x, y, &newx, &newy);
5889
5890       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5891         MovDelay[x][y] = 8 + 8 * !RND(3);
5892       else
5893         MovDelay[x][y] = 16;
5894     }
5895     else if (element == EL_PENGUIN)
5896     {
5897       int newx, newy;
5898
5899       MovDelay[x][y] = 1;
5900
5901       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5902       {
5903         boolean first_horiz = RND(2);
5904         int new_move_dir = MovDir[x][y];
5905
5906         MovDir[x][y] =
5907           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5908         Moving2Blocked(x, y, &newx, &newy);
5909
5910         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5911           return;
5912
5913         MovDir[x][y] =
5914           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5915         Moving2Blocked(x, y, &newx, &newy);
5916
5917         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5918           return;
5919
5920         MovDir[x][y] = old_move_dir;
5921         return;
5922       }
5923     }
5924     else if (element == EL_SATELLITE)
5925     {
5926       int newx, newy;
5927
5928       MovDelay[x][y] = 1;
5929
5930       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5931       {
5932         boolean first_horiz = RND(2);
5933         int new_move_dir = MovDir[x][y];
5934
5935         MovDir[x][y] =
5936           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5937         Moving2Blocked(x, y, &newx, &newy);
5938
5939         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5940           return;
5941
5942         MovDir[x][y] =
5943           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5944         Moving2Blocked(x, y, &newx, &newy);
5945
5946         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5947           return;
5948
5949         MovDir[x][y] = old_move_dir;
5950         return;
5951       }
5952     }
5953     else if (element == EL_EMC_ANDROID)
5954     {
5955       static int check_pos[16] =
5956       {
5957         -1,             /*  0 => (invalid)          */
5958         7,              /*  1 => MV_LEFT            */
5959         3,              /*  2 => MV_RIGHT           */
5960         -1,             /*  3 => (invalid)          */
5961         1,              /*  4 =>            MV_UP   */
5962         0,              /*  5 => MV_LEFT  | MV_UP   */
5963         2,              /*  6 => MV_RIGHT | MV_UP   */
5964         -1,             /*  7 => (invalid)          */
5965         5,              /*  8 =>            MV_DOWN */
5966         6,              /*  9 => MV_LEFT  | MV_DOWN */
5967         4,              /* 10 => MV_RIGHT | MV_DOWN */
5968         -1,             /* 11 => (invalid)          */
5969         -1,             /* 12 => (invalid)          */
5970         -1,             /* 13 => (invalid)          */
5971         -1,             /* 14 => (invalid)          */
5972         -1,             /* 15 => (invalid)          */
5973       };
5974       static struct
5975       {
5976         int dx, dy;
5977         int dir;
5978       } check_xy[8] =
5979       {
5980         { -1, -1,       MV_LEFT  | MV_UP   },
5981         {  0, -1,                  MV_UP   },
5982         { +1, -1,       MV_RIGHT | MV_UP   },
5983         { +1,  0,       MV_RIGHT           },
5984         { +1, +1,       MV_RIGHT | MV_DOWN },
5985         {  0, +1,                  MV_DOWN },
5986         { -1, +1,       MV_LEFT  | MV_DOWN },
5987         { -1,  0,       MV_LEFT            },
5988       };
5989       int start_pos, check_order;
5990       boolean can_clone = FALSE;
5991       int i;
5992
5993       /* check if there is any free field around current position */
5994       for (i = 0; i < 8; i++)
5995       {
5996         int newx = x + check_xy[i].dx;
5997         int newy = y + check_xy[i].dy;
5998
5999         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6000         {
6001           can_clone = TRUE;
6002
6003           break;
6004         }
6005       }
6006
6007       if (can_clone)            /* randomly find an element to clone */
6008       {
6009         can_clone = FALSE;
6010
6011         start_pos = check_pos[RND(8)];
6012         check_order = (RND(2) ? -1 : +1);
6013
6014         for (i = 0; i < 8; i++)
6015         {
6016           int pos_raw = start_pos + i * check_order;
6017           int pos = (pos_raw + 8) % 8;
6018           int newx = x + check_xy[pos].dx;
6019           int newy = y + check_xy[pos].dy;
6020
6021           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6022           {
6023             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6024             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6025
6026             Store[x][y] = Feld[newx][newy];
6027
6028             can_clone = TRUE;
6029
6030             break;
6031           }
6032         }
6033       }
6034
6035       if (can_clone)            /* randomly find a direction to move */
6036       {
6037         can_clone = FALSE;
6038
6039         start_pos = check_pos[RND(8)];
6040         check_order = (RND(2) ? -1 : +1);
6041
6042         for (i = 0; i < 8; i++)
6043         {
6044           int pos_raw = start_pos + i * check_order;
6045           int pos = (pos_raw + 8) % 8;
6046           int newx = x + check_xy[pos].dx;
6047           int newy = y + check_xy[pos].dy;
6048           int new_move_dir = check_xy[pos].dir;
6049
6050           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6051           {
6052             MovDir[x][y] = new_move_dir;
6053             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6054
6055             can_clone = TRUE;
6056
6057             break;
6058           }
6059         }
6060       }
6061
6062       if (can_clone)            /* cloning and moving successful */
6063         return;
6064
6065       /* cannot clone -- try to move towards player */
6066
6067       start_pos = check_pos[MovDir[x][y] & 0x0f];
6068       check_order = (RND(2) ? -1 : +1);
6069
6070       for (i = 0; i < 3; i++)
6071       {
6072         /* first check start_pos, then previous/next or (next/previous) pos */
6073         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6074         int pos = (pos_raw + 8) % 8;
6075         int newx = x + check_xy[pos].dx;
6076         int newy = y + check_xy[pos].dy;
6077         int new_move_dir = check_xy[pos].dir;
6078
6079         if (IS_PLAYER(newx, newy))
6080           break;
6081
6082         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6083         {
6084           MovDir[x][y] = new_move_dir;
6085           MovDelay[x][y] = level.android_move_time * 8 + 1;
6086
6087           break;
6088         }
6089       }
6090     }
6091   }
6092   else if (move_pattern == MV_TURNING_LEFT ||
6093            move_pattern == MV_TURNING_RIGHT ||
6094            move_pattern == MV_TURNING_LEFT_RIGHT ||
6095            move_pattern == MV_TURNING_RIGHT_LEFT ||
6096            move_pattern == MV_TURNING_RANDOM ||
6097            move_pattern == MV_ALL_DIRECTIONS)
6098   {
6099     boolean can_turn_left =
6100       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6101     boolean can_turn_right =
6102       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6103
6104     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6105       return;
6106
6107     if (move_pattern == MV_TURNING_LEFT)
6108       MovDir[x][y] = left_dir;
6109     else if (move_pattern == MV_TURNING_RIGHT)
6110       MovDir[x][y] = right_dir;
6111     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6112       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6113     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6114       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6115     else if (move_pattern == MV_TURNING_RANDOM)
6116       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6117                       can_turn_right && !can_turn_left ? right_dir :
6118                       RND(2) ? left_dir : right_dir);
6119     else if (can_turn_left && can_turn_right)
6120       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6121     else if (can_turn_left)
6122       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6123     else if (can_turn_right)
6124       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6125     else
6126       MovDir[x][y] = back_dir;
6127
6128     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6129   }
6130   else if (move_pattern == MV_HORIZONTAL ||
6131            move_pattern == MV_VERTICAL)
6132   {
6133     if (move_pattern & old_move_dir)
6134       MovDir[x][y] = back_dir;
6135     else if (move_pattern == MV_HORIZONTAL)
6136       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6137     else if (move_pattern == MV_VERTICAL)
6138       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6139
6140     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6141   }
6142   else if (move_pattern & MV_ANY_DIRECTION)
6143   {
6144     MovDir[x][y] = move_pattern;
6145     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6146   }
6147   else if (move_pattern & MV_WIND_DIRECTION)
6148   {
6149     MovDir[x][y] = game.wind_direction;
6150     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6151   }
6152   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6153   {
6154     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6155       MovDir[x][y] = left_dir;
6156     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6157       MovDir[x][y] = right_dir;
6158
6159     if (MovDir[x][y] != old_move_dir)
6160       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6161   }
6162   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6163   {
6164     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6165       MovDir[x][y] = right_dir;
6166     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6167       MovDir[x][y] = left_dir;
6168
6169     if (MovDir[x][y] != old_move_dir)
6170       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6171   }
6172   else if (move_pattern == MV_TOWARDS_PLAYER ||
6173            move_pattern == MV_AWAY_FROM_PLAYER)
6174   {
6175     int attr_x = -1, attr_y = -1;
6176     int newx, newy;
6177     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6178
6179     if (AllPlayersGone)
6180     {
6181       attr_x = ExitX;
6182       attr_y = ExitY;
6183     }
6184     else
6185     {
6186       int i;
6187
6188       for (i = 0; i < MAX_PLAYERS; i++)
6189       {
6190         struct PlayerInfo *player = &stored_player[i];
6191         int jx = player->jx, jy = player->jy;
6192
6193         if (!player->active)
6194           continue;
6195
6196         if (attr_x == -1 ||
6197             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6198         {
6199           attr_x = jx;
6200           attr_y = jy;
6201         }
6202       }
6203     }
6204
6205     MovDir[x][y] = MV_NONE;
6206     if (attr_x < x)
6207       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6208     else if (attr_x > x)
6209       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6210     if (attr_y < y)
6211       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6212     else if (attr_y > y)
6213       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6214
6215     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6216
6217     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6218     {
6219       boolean first_horiz = RND(2);
6220       int new_move_dir = MovDir[x][y];
6221
6222       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6223       {
6224         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6225         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6226
6227         return;
6228       }
6229
6230       MovDir[x][y] =
6231         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6232       Moving2Blocked(x, y, &newx, &newy);
6233
6234       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6235         return;
6236
6237       MovDir[x][y] =
6238         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6239       Moving2Blocked(x, y, &newx, &newy);
6240
6241       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6242         return;
6243
6244       MovDir[x][y] = old_move_dir;
6245     }
6246   }
6247   else if (move_pattern == MV_WHEN_PUSHED ||
6248            move_pattern == MV_WHEN_DROPPED)
6249   {
6250     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6251       MovDir[x][y] = MV_NONE;
6252
6253     MovDelay[x][y] = 0;
6254   }
6255   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6256   {
6257     static int test_xy[7][2] =
6258     {
6259       { 0, -1 },
6260       { -1, 0 },
6261       { +1, 0 },
6262       { 0, +1 },
6263       { 0, -1 },
6264       { -1, 0 },
6265       { +1, 0 },
6266     };
6267     static int test_dir[7] =
6268     {
6269       MV_UP,
6270       MV_LEFT,
6271       MV_RIGHT,
6272       MV_DOWN,
6273       MV_UP,
6274       MV_LEFT,
6275       MV_RIGHT,
6276     };
6277     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6278     int move_preference = -1000000;     /* start with very low preference */
6279     int new_move_dir = MV_NONE;
6280     int start_test = RND(4);
6281     int i;
6282
6283     for (i = 0; i < NUM_DIRECTIONS; i++)
6284     {
6285       int move_dir = test_dir[start_test + i];
6286       int move_dir_preference;
6287
6288       xx = x + test_xy[start_test + i][0];
6289       yy = y + test_xy[start_test + i][1];
6290
6291       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6292           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6293       {
6294         new_move_dir = move_dir;
6295
6296         break;
6297       }
6298
6299       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6300         continue;
6301
6302       move_dir_preference = -1 * RunnerVisit[xx][yy];
6303       if (hunter_mode && PlayerVisit[xx][yy] > 0)
6304         move_dir_preference = PlayerVisit[xx][yy];
6305
6306       if (move_dir_preference > move_preference)
6307       {
6308         /* prefer field that has not been visited for the longest time */
6309         move_preference = move_dir_preference;
6310         new_move_dir = move_dir;
6311       }
6312       else if (move_dir_preference == move_preference &&
6313                move_dir == old_move_dir)
6314       {
6315         /* prefer last direction when all directions are preferred equally */
6316         move_preference = move_dir_preference;
6317         new_move_dir = move_dir;
6318       }
6319     }
6320
6321     MovDir[x][y] = new_move_dir;
6322     if (old_move_dir != new_move_dir)
6323       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6324   }
6325 }
6326
6327 static void TurnRound(int x, int y)
6328 {
6329   int direction = MovDir[x][y];
6330
6331   TurnRoundExt(x, y);
6332
6333   GfxDir[x][y] = MovDir[x][y];
6334
6335   if (direction != MovDir[x][y])
6336     GfxFrame[x][y] = 0;
6337
6338   if (MovDelay[x][y])
6339     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6340
6341   ResetGfxFrame(x, y, FALSE);
6342 }
6343
6344 static boolean JustBeingPushed(int x, int y)
6345 {
6346   int i;
6347
6348   for (i = 0; i < MAX_PLAYERS; i++)
6349   {
6350     struct PlayerInfo *player = &stored_player[i];
6351
6352     if (player->active && player->is_pushing && player->MovPos)
6353     {
6354       int next_jx = player->jx + (player->jx - player->last_jx);
6355       int next_jy = player->jy + (player->jy - player->last_jy);
6356
6357       if (x == next_jx && y == next_jy)
6358         return TRUE;
6359     }
6360   }
6361
6362   return FALSE;
6363 }
6364
6365 void StartMoving(int x, int y)
6366 {
6367   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
6368   int element = Feld[x][y];
6369
6370   if (Stop[x][y])
6371     return;
6372
6373   if (MovDelay[x][y] == 0)
6374     GfxAction[x][y] = ACTION_DEFAULT;
6375
6376   if (CAN_FALL(element) && y < lev_fieldy - 1)
6377   {
6378     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
6379         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6380       if (JustBeingPushed(x, y))
6381         return;
6382
6383     if (element == EL_QUICKSAND_FULL)
6384     {
6385       if (IS_FREE(x, y + 1))
6386       {
6387         InitMovingField(x, y, MV_DOWN);
6388         started_moving = TRUE;
6389
6390         Feld[x][y] = EL_QUICKSAND_EMPTYING;
6391 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6392         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6393           Store[x][y] = EL_ROCK;
6394 #else
6395         Store[x][y] = EL_ROCK;
6396 #endif
6397
6398         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6399       }
6400       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6401       {
6402         if (!MovDelay[x][y])
6403           MovDelay[x][y] = TILEY + 1;
6404
6405         if (MovDelay[x][y])
6406         {
6407           MovDelay[x][y]--;
6408           if (MovDelay[x][y])
6409             return;
6410         }
6411
6412         Feld[x][y] = EL_QUICKSAND_EMPTY;
6413         Feld[x][y + 1] = EL_QUICKSAND_FULL;
6414         Store[x][y + 1] = Store[x][y];
6415         Store[x][y] = 0;
6416
6417         PlayLevelSoundAction(x, y, ACTION_FILLING);
6418       }
6419     }
6420     else if (element == EL_QUICKSAND_FAST_FULL)
6421     {
6422       if (IS_FREE(x, y + 1))
6423       {
6424         InitMovingField(x, y, MV_DOWN);
6425         started_moving = TRUE;
6426
6427         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
6428 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6429         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6430           Store[x][y] = EL_ROCK;
6431 #else
6432         Store[x][y] = EL_ROCK;
6433 #endif
6434
6435         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6436       }
6437       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6438       {
6439         if (!MovDelay[x][y])
6440           MovDelay[x][y] = TILEY + 1;
6441
6442         if (MovDelay[x][y])
6443         {
6444           MovDelay[x][y]--;
6445           if (MovDelay[x][y])
6446             return;
6447         }
6448
6449         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
6450         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
6451         Store[x][y + 1] = Store[x][y];
6452         Store[x][y] = 0;
6453
6454         PlayLevelSoundAction(x, y, ACTION_FILLING);
6455       }
6456     }
6457     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6458              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6459     {
6460       InitMovingField(x, y, MV_DOWN);
6461       started_moving = TRUE;
6462
6463       Feld[x][y] = EL_QUICKSAND_FILLING;
6464       Store[x][y] = element;
6465
6466       PlayLevelSoundAction(x, y, ACTION_FILLING);
6467     }
6468     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6469              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6470     {
6471       InitMovingField(x, y, MV_DOWN);
6472       started_moving = TRUE;
6473
6474       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
6475       Store[x][y] = element;
6476
6477       PlayLevelSoundAction(x, y, ACTION_FILLING);
6478     }
6479     else if (element == EL_MAGIC_WALL_FULL)
6480     {
6481       if (IS_FREE(x, y + 1))
6482       {
6483         InitMovingField(x, y, MV_DOWN);
6484         started_moving = TRUE;
6485
6486         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6487         Store[x][y] = EL_CHANGED(Store[x][y]);
6488       }
6489       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6490       {
6491         if (!MovDelay[x][y])
6492           MovDelay[x][y] = TILEY/4 + 1;
6493
6494         if (MovDelay[x][y])
6495         {
6496           MovDelay[x][y]--;
6497           if (MovDelay[x][y])
6498             return;
6499         }
6500
6501         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6502         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6503         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6504         Store[x][y] = 0;
6505       }
6506     }
6507     else if (element == EL_BD_MAGIC_WALL_FULL)
6508     {
6509       if (IS_FREE(x, y + 1))
6510       {
6511         InitMovingField(x, y, MV_DOWN);
6512         started_moving = TRUE;
6513
6514         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6515         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6516       }
6517       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6518       {
6519         if (!MovDelay[x][y])
6520           MovDelay[x][y] = TILEY/4 + 1;
6521
6522         if (MovDelay[x][y])
6523         {
6524           MovDelay[x][y]--;
6525           if (MovDelay[x][y])
6526             return;
6527         }
6528
6529         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6530         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6531         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6532         Store[x][y] = 0;
6533       }
6534     }
6535     else if (element == EL_DC_MAGIC_WALL_FULL)
6536     {
6537       if (IS_FREE(x, y + 1))
6538       {
6539         InitMovingField(x, y, MV_DOWN);
6540         started_moving = TRUE;
6541
6542         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
6543         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
6544       }
6545       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6546       {
6547         if (!MovDelay[x][y])
6548           MovDelay[x][y] = TILEY/4 + 1;
6549
6550         if (MovDelay[x][y])
6551         {
6552           MovDelay[x][y]--;
6553           if (MovDelay[x][y])
6554             return;
6555         }
6556
6557         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
6558         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
6559         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
6560         Store[x][y] = 0;
6561       }
6562     }
6563     else if ((CAN_PASS_MAGIC_WALL(element) &&
6564               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6565                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
6566              (CAN_PASS_DC_MAGIC_WALL(element) &&
6567               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
6568
6569     {
6570       InitMovingField(x, y, MV_DOWN);
6571       started_moving = TRUE;
6572
6573       Feld[x][y] =
6574         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6575          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
6576          EL_DC_MAGIC_WALL_FILLING);
6577       Store[x][y] = element;
6578     }
6579     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6580     {
6581       SplashAcid(x, y + 1);
6582
6583       InitMovingField(x, y, MV_DOWN);
6584       started_moving = TRUE;
6585
6586       Store[x][y] = EL_ACID;
6587     }
6588     else if (
6589 #if USE_FIX_IMPACT_COLLISION
6590              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6591               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
6592 #else
6593              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6594               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
6595 #endif
6596              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
6597               CAN_FALL(element) && WasJustFalling[x][y] &&
6598               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
6599
6600              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
6601               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
6602               (Feld[x][y + 1] == EL_BLOCKED)))
6603     {
6604       /* this is needed for a special case not covered by calling "Impact()"
6605          from "ContinueMoving()": if an element moves to a tile directly below
6606          another element which was just falling on that tile (which was empty
6607          in the previous frame), the falling element above would just stop
6608          instead of smashing the element below (in previous version, the above
6609          element was just checked for "moving" instead of "falling", resulting
6610          in incorrect smashes caused by horizontal movement of the above
6611          element; also, the case of the player being the element to smash was
6612          simply not covered here... :-/ ) */
6613
6614       CheckCollision[x][y] = 0;
6615       CheckImpact[x][y] = 0;
6616
6617       Impact(x, y);
6618     }
6619     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6620     {
6621       if (MovDir[x][y] == MV_NONE)
6622       {
6623         InitMovingField(x, y, MV_DOWN);
6624         started_moving = TRUE;
6625       }
6626     }
6627     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6628     {
6629       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6630         MovDir[x][y] = MV_DOWN;
6631
6632       InitMovingField(x, y, MV_DOWN);
6633       started_moving = TRUE;
6634     }
6635     else if (element == EL_AMOEBA_DROP)
6636     {
6637       Feld[x][y] = EL_AMOEBA_GROWING;
6638       Store[x][y] = EL_AMOEBA_WET;
6639     }
6640     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6641               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6642              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6643              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6644     {
6645       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
6646                                 (IS_FREE(x - 1, y + 1) ||
6647                                  Feld[x - 1][y + 1] == EL_ACID));
6648       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6649                                 (IS_FREE(x + 1, y + 1) ||
6650                                  Feld[x + 1][y + 1] == EL_ACID));
6651       boolean can_fall_any  = (can_fall_left || can_fall_right);
6652       boolean can_fall_both = (can_fall_left && can_fall_right);
6653       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6654
6655 #if USE_NEW_ALL_SLIPPERY
6656       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6657       {
6658         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6659           can_fall_right = FALSE;
6660         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6661           can_fall_left = FALSE;
6662         else if (slippery_type == SLIPPERY_ONLY_LEFT)
6663           can_fall_right = FALSE;
6664         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6665           can_fall_left = FALSE;
6666
6667         can_fall_any  = (can_fall_left || can_fall_right);
6668         can_fall_both = FALSE;
6669       }
6670 #else
6671       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6672       {
6673         if (slippery_type == SLIPPERY_ONLY_LEFT)
6674           can_fall_right = FALSE;
6675         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6676           can_fall_left = FALSE;
6677         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6678           can_fall_right = FALSE;
6679         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6680           can_fall_left = FALSE;
6681
6682         can_fall_any  = (can_fall_left || can_fall_right);
6683         can_fall_both = (can_fall_left && can_fall_right);
6684       }
6685 #endif
6686
6687 #if USE_NEW_ALL_SLIPPERY
6688 #else
6689 #if USE_NEW_SP_SLIPPERY
6690       /* !!! better use the same properties as for custom elements here !!! */
6691       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6692                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6693       {
6694         can_fall_right = FALSE;         /* slip down on left side */
6695         can_fall_both = FALSE;
6696       }
6697 #endif
6698 #endif
6699
6700 #if USE_NEW_ALL_SLIPPERY
6701       if (can_fall_both)
6702       {
6703         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6704           can_fall_right = FALSE;       /* slip down on left side */
6705         else
6706           can_fall_left = !(can_fall_right = RND(2));
6707
6708         can_fall_both = FALSE;
6709       }
6710 #else
6711       if (can_fall_both)
6712       {
6713         if (game.emulation == EMU_BOULDERDASH ||
6714             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6715           can_fall_right = FALSE;       /* slip down on left side */
6716         else
6717           can_fall_left = !(can_fall_right = RND(2));
6718
6719         can_fall_both = FALSE;
6720       }
6721 #endif
6722
6723       if (can_fall_any)
6724       {
6725         /* if not determined otherwise, prefer left side for slipping down */
6726         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6727         started_moving = TRUE;
6728       }
6729     }
6730 #if 0
6731     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6732 #else
6733     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6734 #endif
6735     {
6736       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
6737       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6738       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6739       int belt_dir = game.belt_dir[belt_nr];
6740
6741       if ((belt_dir == MV_LEFT  && left_is_free) ||
6742           (belt_dir == MV_RIGHT && right_is_free))
6743       {
6744         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6745
6746         InitMovingField(x, y, belt_dir);
6747         started_moving = TRUE;
6748
6749         Pushed[x][y] = TRUE;
6750         Pushed[nextx][y] = TRUE;
6751
6752         GfxAction[x][y] = ACTION_DEFAULT;
6753       }
6754       else
6755       {
6756         MovDir[x][y] = 0;       /* if element was moving, stop it */
6757       }
6758     }
6759   }
6760
6761   /* not "else if" because of elements that can fall and move (EL_SPRING) */
6762 #if 0
6763   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6764 #else
6765   if (CAN_MOVE(element) && !started_moving)
6766 #endif
6767   {
6768     int move_pattern = element_info[element].move_pattern;
6769     int newx, newy;
6770
6771 #if 0
6772 #if DEBUG
6773     if (MovDir[x][y] == MV_NONE)
6774     {
6775       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6776              x, y, element, element_info[element].token_name);
6777       printf("StartMoving(): This should never happen!\n");
6778     }
6779 #endif
6780 #endif
6781
6782     Moving2Blocked(x, y, &newx, &newy);
6783
6784     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6785       return;
6786
6787     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6788         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6789     {
6790       WasJustMoving[x][y] = 0;
6791       CheckCollision[x][y] = 0;
6792
6793       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6794
6795       if (Feld[x][y] != element)        /* element has changed */
6796         return;
6797     }
6798
6799     if (!MovDelay[x][y])        /* start new movement phase */
6800     {
6801       /* all objects that can change their move direction after each step
6802          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6803
6804       if (element != EL_YAMYAM &&
6805           element != EL_DARK_YAMYAM &&
6806           element != EL_PACMAN &&
6807           !(move_pattern & MV_ANY_DIRECTION) &&
6808           move_pattern != MV_TURNING_LEFT &&
6809           move_pattern != MV_TURNING_RIGHT &&
6810           move_pattern != MV_TURNING_LEFT_RIGHT &&
6811           move_pattern != MV_TURNING_RIGHT_LEFT &&
6812           move_pattern != MV_TURNING_RANDOM)
6813       {
6814         TurnRound(x, y);
6815
6816         if (MovDelay[x][y] && (element == EL_BUG ||
6817                                element == EL_SPACESHIP ||
6818                                element == EL_SP_SNIKSNAK ||
6819                                element == EL_SP_ELECTRON ||
6820                                element == EL_MOLE))
6821           DrawLevelField(x, y);
6822       }
6823     }
6824
6825     if (MovDelay[x][y])         /* wait some time before next movement */
6826     {
6827       MovDelay[x][y]--;
6828
6829       if (element == EL_ROBOT ||
6830           element == EL_YAMYAM ||
6831           element == EL_DARK_YAMYAM)
6832       {
6833         DrawLevelElementAnimationIfNeeded(x, y, element);
6834         PlayLevelSoundAction(x, y, ACTION_WAITING);
6835       }
6836       else if (element == EL_SP_ELECTRON)
6837         DrawLevelElementAnimationIfNeeded(x, y, element);
6838       else if (element == EL_DRAGON)
6839       {
6840         int i;
6841         int dir = MovDir[x][y];
6842         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6843         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
6844         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
6845                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
6846                        dir == MV_UP     ? IMG_FLAMES_1_UP :
6847                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6848         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6849
6850         GfxAction[x][y] = ACTION_ATTACKING;
6851
6852         if (IS_PLAYER(x, y))
6853           DrawPlayerField(x, y);
6854         else
6855           DrawLevelField(x, y);
6856
6857         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6858
6859         for (i = 1; i <= 3; i++)
6860         {
6861           int xx = x + i * dx;
6862           int yy = y + i * dy;
6863           int sx = SCREENX(xx);
6864           int sy = SCREENY(yy);
6865           int flame_graphic = graphic + (i - 1);
6866
6867           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6868             break;
6869
6870           if (MovDelay[x][y])
6871           {
6872             int flamed = MovingOrBlocked2Element(xx, yy);
6873
6874             /* !!! */
6875 #if 0
6876             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6877               Bang(xx, yy);
6878             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6879               RemoveMovingField(xx, yy);
6880             else
6881               RemoveField(xx, yy);
6882 #else
6883             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6884               Bang(xx, yy);
6885             else
6886               RemoveMovingField(xx, yy);
6887 #endif
6888
6889             ChangeDelay[xx][yy] = 0;
6890
6891             Feld[xx][yy] = EL_FLAMES;
6892
6893             if (IN_SCR_FIELD(sx, sy))
6894             {
6895               DrawLevelFieldCrumbledSand(xx, yy);
6896               DrawGraphic(sx, sy, flame_graphic, frame);
6897             }
6898           }
6899           else
6900           {
6901             if (Feld[xx][yy] == EL_FLAMES)
6902               Feld[xx][yy] = EL_EMPTY;
6903             DrawLevelField(xx, yy);
6904           }
6905         }
6906       }
6907
6908       if (MovDelay[x][y])       /* element still has to wait some time */
6909       {
6910         PlayLevelSoundAction(x, y, ACTION_WAITING);
6911
6912         return;
6913       }
6914     }
6915
6916     /* now make next step */
6917
6918     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6919
6920     if (DONT_COLLIDE_WITH(element) &&
6921         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6922         !PLAYER_ENEMY_PROTECTED(newx, newy))
6923     {
6924       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6925
6926       return;
6927     }
6928
6929     else if (CAN_MOVE_INTO_ACID(element) &&
6930              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6931              !IS_MV_DIAGONAL(MovDir[x][y]) &&
6932              (MovDir[x][y] == MV_DOWN ||
6933               game.engine_version >= VERSION_IDENT(3,1,0,0)))
6934     {
6935       SplashAcid(newx, newy);
6936       Store[x][y] = EL_ACID;
6937     }
6938     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6939     {
6940       if (Feld[newx][newy] == EL_EXIT_OPEN ||
6941           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
6942           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
6943           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
6944       {
6945         RemoveField(x, y);
6946         DrawLevelField(x, y);
6947
6948         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6949         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6950           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6951
6952         local_player->friends_still_needed--;
6953         if (!local_player->friends_still_needed &&
6954             !local_player->GameOver && AllPlayersGone)
6955           PlayerWins(local_player);
6956
6957         return;
6958       }
6959       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6960       {
6961         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6962           DrawLevelField(newx, newy);
6963         else
6964           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6965       }
6966       else if (!IS_FREE(newx, newy))
6967       {
6968         GfxAction[x][y] = ACTION_WAITING;
6969
6970         if (IS_PLAYER(x, y))
6971           DrawPlayerField(x, y);
6972         else
6973           DrawLevelField(x, y);
6974
6975         return;
6976       }
6977     }
6978     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6979     {
6980       if (IS_FOOD_PIG(Feld[newx][newy]))
6981       {
6982         if (IS_MOVING(newx, newy))
6983           RemoveMovingField(newx, newy);
6984         else
6985         {
6986           Feld[newx][newy] = EL_EMPTY;
6987           DrawLevelField(newx, newy);
6988         }
6989
6990         PlayLevelSound(x, y, SND_PIG_DIGGING);
6991       }
6992       else if (!IS_FREE(newx, newy))
6993       {
6994         if (IS_PLAYER(x, y))
6995           DrawPlayerField(x, y);
6996         else
6997           DrawLevelField(x, y);
6998
6999         return;
7000       }
7001     }
7002     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7003     {
7004       if (Store[x][y] != EL_EMPTY)
7005       {
7006         boolean can_clone = FALSE;
7007         int xx, yy;
7008
7009         /* check if element to clone is still there */
7010         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7011         {
7012           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7013           {
7014             can_clone = TRUE;
7015
7016             break;
7017           }
7018         }
7019
7020         /* cannot clone or target field not free anymore -- do not clone */
7021         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7022           Store[x][y] = EL_EMPTY;
7023       }
7024
7025       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7026       {
7027         if (IS_MV_DIAGONAL(MovDir[x][y]))
7028         {
7029           int diagonal_move_dir = MovDir[x][y];
7030           int stored = Store[x][y];
7031           int change_delay = 8;
7032           int graphic;
7033
7034           /* android is moving diagonally */
7035
7036           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7037
7038           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7039           GfxElement[x][y] = EL_EMC_ANDROID;
7040           GfxAction[x][y] = ACTION_SHRINKING;
7041           GfxDir[x][y] = diagonal_move_dir;
7042           ChangeDelay[x][y] = change_delay;
7043
7044           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7045                                    GfxDir[x][y]);
7046
7047           DrawLevelGraphicAnimation(x, y, graphic);
7048           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7049
7050           if (Feld[newx][newy] == EL_ACID)
7051           {
7052             SplashAcid(newx, newy);
7053
7054             return;
7055           }
7056
7057           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7058
7059           Store[newx][newy] = EL_EMC_ANDROID;
7060           GfxElement[newx][newy] = EL_EMC_ANDROID;
7061           GfxAction[newx][newy] = ACTION_GROWING;
7062           GfxDir[newx][newy] = diagonal_move_dir;
7063           ChangeDelay[newx][newy] = change_delay;
7064
7065           graphic = el_act_dir2img(GfxElement[newx][newy],
7066                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7067
7068           DrawLevelGraphicAnimation(newx, newy, graphic);
7069           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7070
7071           return;
7072         }
7073         else
7074         {
7075           Feld[newx][newy] = EL_EMPTY;
7076           DrawLevelField(newx, newy);
7077
7078           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7079         }
7080       }
7081       else if (!IS_FREE(newx, newy))
7082       {
7083 #if 0
7084         if (IS_PLAYER(x, y))
7085           DrawPlayerField(x, y);
7086         else
7087           DrawLevelField(x, y);
7088 #endif
7089
7090         return;
7091       }
7092     }
7093     else if (IS_CUSTOM_ELEMENT(element) &&
7094              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7095     {
7096       int new_element = Feld[newx][newy];
7097
7098       if (!IS_FREE(newx, newy))
7099       {
7100         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7101                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7102                       ACTION_BREAKING);
7103
7104         /* no element can dig solid indestructible elements */
7105         if (IS_INDESTRUCTIBLE(new_element) &&
7106             !IS_DIGGABLE(new_element) &&
7107             !IS_COLLECTIBLE(new_element))
7108           return;
7109
7110         if (AmoebaNr[newx][newy] &&
7111             (new_element == EL_AMOEBA_FULL ||
7112              new_element == EL_BD_AMOEBA ||
7113              new_element == EL_AMOEBA_GROWING))
7114         {
7115           AmoebaCnt[AmoebaNr[newx][newy]]--;
7116           AmoebaCnt2[AmoebaNr[newx][newy]]--;
7117         }
7118
7119         if (IS_MOVING(newx, newy))
7120           RemoveMovingField(newx, newy);
7121         else
7122         {
7123           RemoveField(newx, newy);
7124           DrawLevelField(newx, newy);
7125         }
7126
7127         /* if digged element was about to explode, prevent the explosion */
7128         ExplodeField[newx][newy] = EX_TYPE_NONE;
7129
7130         PlayLevelSoundAction(x, y, action);
7131       }
7132
7133       Store[newx][newy] = EL_EMPTY;
7134 #if 1
7135       /* this makes it possible to leave the removed element again */
7136       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7137         Store[newx][newy] = new_element;
7138 #else
7139       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7140       {
7141         int move_leave_element = element_info[element].move_leave_element;
7142
7143         /* this makes it possible to leave the removed element again */
7144         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7145                              new_element : move_leave_element);
7146       }
7147 #endif
7148
7149       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7150       {
7151         RunnerVisit[x][y] = FrameCounter;
7152         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7153       }
7154     }
7155     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7156     {
7157       if (!IS_FREE(newx, newy))
7158       {
7159         if (IS_PLAYER(x, y))
7160           DrawPlayerField(x, y);
7161         else
7162           DrawLevelField(x, y);
7163
7164         return;
7165       }
7166       else
7167       {
7168         boolean wanna_flame = !RND(10);
7169         int dx = newx - x, dy = newy - y;
7170         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7171         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7172         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7173                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7174         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7175                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7176
7177         if ((wanna_flame ||
7178              IS_CLASSIC_ENEMY(element1) ||
7179              IS_CLASSIC_ENEMY(element2)) &&
7180             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7181             element1 != EL_FLAMES && element2 != EL_FLAMES)
7182         {
7183           ResetGfxAnimation(x, y);
7184           GfxAction[x][y] = ACTION_ATTACKING;
7185
7186           if (IS_PLAYER(x, y))
7187             DrawPlayerField(x, y);
7188           else
7189             DrawLevelField(x, y);
7190
7191           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7192
7193           MovDelay[x][y] = 50;
7194
7195           /* !!! */
7196 #if 0
7197           RemoveField(newx, newy);
7198 #endif
7199           Feld[newx][newy] = EL_FLAMES;
7200           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7201           {
7202 #if 0
7203             RemoveField(newx1, newy1);
7204 #endif
7205             Feld[newx1][newy1] = EL_FLAMES;
7206           }
7207           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7208           {
7209 #if 0
7210             RemoveField(newx2, newy2);
7211 #endif
7212             Feld[newx2][newy2] = EL_FLAMES;
7213           }
7214
7215           return;
7216         }
7217       }
7218     }
7219     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7220              Feld[newx][newy] == EL_DIAMOND)
7221     {
7222       if (IS_MOVING(newx, newy))
7223         RemoveMovingField(newx, newy);
7224       else
7225       {
7226         Feld[newx][newy] = EL_EMPTY;
7227         DrawLevelField(newx, newy);
7228       }
7229
7230       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7231     }
7232     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7233              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7234     {
7235       if (AmoebaNr[newx][newy])
7236       {
7237         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7238         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7239             Feld[newx][newy] == EL_BD_AMOEBA)
7240           AmoebaCnt[AmoebaNr[newx][newy]]--;
7241       }
7242
7243 #if 0
7244       /* !!! test !!! */
7245       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7246       {
7247         RemoveMovingField(newx, newy);
7248       }
7249 #else
7250       if (IS_MOVING(newx, newy))
7251       {
7252         RemoveMovingField(newx, newy);
7253       }
7254 #endif
7255       else
7256       {
7257         Feld[newx][newy] = EL_EMPTY;
7258         DrawLevelField(newx, newy);
7259       }
7260
7261       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7262     }
7263     else if ((element == EL_PACMAN || element == EL_MOLE)
7264              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7265     {
7266       if (AmoebaNr[newx][newy])
7267       {
7268         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7269         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7270             Feld[newx][newy] == EL_BD_AMOEBA)
7271           AmoebaCnt[AmoebaNr[newx][newy]]--;
7272       }
7273
7274       if (element == EL_MOLE)
7275       {
7276         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7277         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7278
7279         ResetGfxAnimation(x, y);
7280         GfxAction[x][y] = ACTION_DIGGING;
7281         DrawLevelField(x, y);
7282
7283         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7284
7285         return;                         /* wait for shrinking amoeba */
7286       }
7287       else      /* element == EL_PACMAN */
7288       {
7289         Feld[newx][newy] = EL_EMPTY;
7290         DrawLevelField(newx, newy);
7291         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7292       }
7293     }
7294     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7295              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7296               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7297     {
7298       /* wait for shrinking amoeba to completely disappear */
7299       return;
7300     }
7301     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7302     {
7303       /* object was running against a wall */
7304
7305       TurnRound(x, y);
7306
7307 #if 0
7308       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7309       if (move_pattern & MV_ANY_DIRECTION &&
7310           move_pattern == MovDir[x][y])
7311       {
7312         int blocking_element =
7313           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7314
7315         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7316                                  MovDir[x][y]);
7317
7318         element = Feld[x][y];   /* element might have changed */
7319       }
7320 #endif
7321
7322       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7323         DrawLevelElementAnimation(x, y, element);
7324
7325       if (DONT_TOUCH(element))
7326         TestIfBadThingTouchesPlayer(x, y);
7327
7328       return;
7329     }
7330
7331     InitMovingField(x, y, MovDir[x][y]);
7332
7333     PlayLevelSoundAction(x, y, ACTION_MOVING);
7334   }
7335
7336   if (MovDir[x][y])
7337     ContinueMoving(x, y);
7338 }
7339
7340 void ContinueMoving(int x, int y)
7341 {
7342   int element = Feld[x][y];
7343   struct ElementInfo *ei = &element_info[element];
7344   int direction = MovDir[x][y];
7345   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7346   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7347   int newx = x + dx, newy = y + dy;
7348   int stored = Store[x][y];
7349   int stored_new = Store[newx][newy];
7350   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7351   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7352   boolean last_line = (newy == lev_fieldy - 1);
7353
7354   MovPos[x][y] += getElementMoveStepsize(x, y);
7355
7356   if (pushed_by_player) /* special case: moving object pushed by player */
7357     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7358
7359   if (ABS(MovPos[x][y]) < TILEX)
7360   {
7361 #if 0
7362     int ee = Feld[x][y];
7363     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7364     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7365
7366     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7367            x, y, ABS(MovPos[x][y]),
7368            ee, gg, ff,
7369            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
7370 #endif
7371
7372     DrawLevelField(x, y);
7373
7374     return;     /* element is still moving */
7375   }
7376
7377   /* element reached destination field */
7378
7379   Feld[x][y] = EL_EMPTY;
7380   Feld[newx][newy] = element;
7381   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
7382
7383   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
7384   {
7385     element = Feld[newx][newy] = EL_ACID;
7386   }
7387   else if (element == EL_MOLE)
7388   {
7389     Feld[x][y] = EL_SAND;
7390
7391     DrawLevelFieldCrumbledSandNeighbours(x, y);
7392   }
7393   else if (element == EL_QUICKSAND_FILLING)
7394   {
7395     element = Feld[newx][newy] = get_next_element(element);
7396     Store[newx][newy] = Store[x][y];
7397   }
7398   else if (element == EL_QUICKSAND_EMPTYING)
7399   {
7400     Feld[x][y] = get_next_element(element);
7401     element = Feld[newx][newy] = Store[x][y];
7402   }
7403   else if (element == EL_QUICKSAND_FAST_FILLING)
7404   {
7405     element = Feld[newx][newy] = get_next_element(element);
7406     Store[newx][newy] = Store[x][y];
7407   }
7408   else if (element == EL_QUICKSAND_FAST_EMPTYING)
7409   {
7410     Feld[x][y] = get_next_element(element);
7411     element = Feld[newx][newy] = Store[x][y];
7412   }
7413   else if (element == EL_MAGIC_WALL_FILLING)
7414   {
7415     element = Feld[newx][newy] = get_next_element(element);
7416     if (!game.magic_wall_active)
7417       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7418     Store[newx][newy] = Store[x][y];
7419   }
7420   else if (element == EL_MAGIC_WALL_EMPTYING)
7421   {
7422     Feld[x][y] = get_next_element(element);
7423     if (!game.magic_wall_active)
7424       Feld[x][y] = EL_MAGIC_WALL_DEAD;
7425     element = Feld[newx][newy] = Store[x][y];
7426
7427 #if USE_NEW_CUSTOM_VALUE
7428     InitField(newx, newy, FALSE);
7429 #endif
7430   }
7431   else if (element == EL_BD_MAGIC_WALL_FILLING)
7432   {
7433     element = Feld[newx][newy] = get_next_element(element);
7434     if (!game.magic_wall_active)
7435       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
7436     Store[newx][newy] = Store[x][y];
7437   }
7438   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
7439   {
7440     Feld[x][y] = get_next_element(element);
7441     if (!game.magic_wall_active)
7442       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7443     element = Feld[newx][newy] = Store[x][y];
7444
7445 #if USE_NEW_CUSTOM_VALUE
7446     InitField(newx, newy, FALSE);
7447 #endif
7448   }
7449   else if (element == EL_DC_MAGIC_WALL_FILLING)
7450   {
7451     element = Feld[newx][newy] = get_next_element(element);
7452     if (!game.magic_wall_active)
7453       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
7454     Store[newx][newy] = Store[x][y];
7455   }
7456   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
7457   {
7458     Feld[x][y] = get_next_element(element);
7459     if (!game.magic_wall_active)
7460       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
7461     element = Feld[newx][newy] = Store[x][y];
7462
7463 #if USE_NEW_CUSTOM_VALUE
7464     InitField(newx, newy, FALSE);
7465 #endif
7466   }
7467   else if (element == EL_AMOEBA_DROPPING)
7468   {
7469     Feld[x][y] = get_next_element(element);
7470     element = Feld[newx][newy] = Store[x][y];
7471   }
7472   else if (element == EL_SOKOBAN_OBJECT)
7473   {
7474     if (Back[x][y])
7475       Feld[x][y] = Back[x][y];
7476
7477     if (Back[newx][newy])
7478       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
7479
7480     Back[x][y] = Back[newx][newy] = 0;
7481   }
7482
7483   Store[x][y] = EL_EMPTY;
7484   MovPos[x][y] = 0;
7485   MovDir[x][y] = 0;
7486   MovDelay[x][y] = 0;
7487
7488   MovDelay[newx][newy] = 0;
7489
7490   if (CAN_CHANGE_OR_HAS_ACTION(element))
7491   {
7492     /* copy element change control values to new field */
7493     ChangeDelay[newx][newy] = ChangeDelay[x][y];
7494     ChangePage[newx][newy]  = ChangePage[x][y];
7495     ChangeCount[newx][newy] = ChangeCount[x][y];
7496     ChangeEvent[newx][newy] = ChangeEvent[x][y];
7497   }
7498
7499 #if USE_NEW_CUSTOM_VALUE
7500     CustomValue[newx][newy] = CustomValue[x][y];
7501 #endif
7502
7503   ChangeDelay[x][y] = 0;
7504   ChangePage[x][y] = -1;
7505   ChangeCount[x][y] = 0;
7506   ChangeEvent[x][y] = -1;
7507
7508 #if USE_NEW_CUSTOM_VALUE
7509   CustomValue[x][y] = 0;
7510 #endif
7511
7512   /* copy animation control values to new field */
7513   GfxFrame[newx][newy]  = GfxFrame[x][y];
7514   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
7515   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
7516   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
7517
7518   Pushed[x][y] = Pushed[newx][newy] = FALSE;
7519
7520   /* some elements can leave other elements behind after moving */
7521 #if 1
7522   if (ei->move_leave_element != EL_EMPTY &&
7523       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7524       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7525 #else
7526   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7527       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7528       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7529 #endif
7530   {
7531     int move_leave_element = ei->move_leave_element;
7532
7533 #if 1
7534 #if 1
7535     /* this makes it possible to leave the removed element again */
7536     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7537       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7538 #else
7539     /* this makes it possible to leave the removed element again */
7540     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7541       move_leave_element = stored;
7542 #endif
7543 #else
7544     /* this makes it possible to leave the removed element again */
7545     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7546         ei->move_leave_element == EL_TRIGGER_ELEMENT)
7547       move_leave_element = stored;
7548 #endif
7549
7550     Feld[x][y] = move_leave_element;
7551
7552     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7553       MovDir[x][y] = direction;
7554
7555     InitField(x, y, FALSE);
7556
7557     if (GFX_CRUMBLED(Feld[x][y]))
7558       DrawLevelFieldCrumbledSandNeighbours(x, y);
7559
7560     if (ELEM_IS_PLAYER(move_leave_element))
7561       RelocatePlayer(x, y, move_leave_element);
7562   }
7563
7564   /* do this after checking for left-behind element */
7565   ResetGfxAnimation(x, y);      /* reset animation values for old field */
7566
7567   if (!CAN_MOVE(element) ||
7568       (CAN_FALL(element) && direction == MV_DOWN &&
7569        (element == EL_SPRING ||
7570         element_info[element].move_pattern == MV_WHEN_PUSHED ||
7571         element_info[element].move_pattern == MV_WHEN_DROPPED)))
7572     GfxDir[x][y] = MovDir[newx][newy] = 0;
7573
7574   DrawLevelField(x, y);
7575   DrawLevelField(newx, newy);
7576
7577   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
7578
7579   /* prevent pushed element from moving on in pushed direction */
7580   if (pushed_by_player && CAN_MOVE(element) &&
7581       element_info[element].move_pattern & MV_ANY_DIRECTION &&
7582       !(element_info[element].move_pattern & direction))
7583     TurnRound(newx, newy);
7584
7585   /* prevent elements on conveyor belt from moving on in last direction */
7586   if (pushed_by_conveyor && CAN_FALL(element) &&
7587       direction & MV_HORIZONTAL)
7588     MovDir[newx][newy] = 0;
7589
7590   if (!pushed_by_player)
7591   {
7592     int nextx = newx + dx, nexty = newy + dy;
7593     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
7594
7595     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
7596
7597     if (CAN_FALL(element) && direction == MV_DOWN)
7598       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
7599
7600     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
7601       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
7602
7603 #if USE_FIX_IMPACT_COLLISION
7604     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
7605       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
7606 #endif
7607   }
7608
7609   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
7610   {
7611     TestIfBadThingTouchesPlayer(newx, newy);
7612     TestIfBadThingTouchesFriend(newx, newy);
7613
7614     if (!IS_CUSTOM_ELEMENT(element))
7615       TestIfBadThingTouchesOtherBadThing(newx, newy);
7616   }
7617   else if (element == EL_PENGUIN)
7618     TestIfFriendTouchesBadThing(newx, newy);
7619
7620   /* give the player one last chance (one more frame) to move away */
7621   if (CAN_FALL(element) && direction == MV_DOWN &&
7622       (last_line || (!IS_FREE(x, newy + 1) &&
7623                      (!IS_PLAYER(x, newy + 1) ||
7624                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
7625     Impact(x, newy);
7626
7627   if (pushed_by_player && !game.use_change_when_pushing_bug)
7628   {
7629     int push_side = MV_DIR_OPPOSITE(direction);
7630     struct PlayerInfo *player = PLAYERINFO(x, y);
7631
7632     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
7633                                player->index_bit, push_side);
7634     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
7635                                         player->index_bit, push_side);
7636   }
7637
7638   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
7639     MovDelay[newx][newy] = 1;
7640
7641   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
7642
7643   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
7644
7645 #if 0
7646   if (ChangePage[newx][newy] != -1)             /* delayed change */
7647   {
7648     int page = ChangePage[newx][newy];
7649     struct ElementChangeInfo *change = &ei->change_page[page];
7650
7651     ChangePage[newx][newy] = -1;
7652
7653     if (change->can_change)
7654     {
7655       if (ChangeElement(newx, newy, element, page))
7656       {
7657         if (change->post_change_function)
7658           change->post_change_function(newx, newy);
7659       }
7660     }
7661
7662     if (change->has_action)
7663       ExecuteCustomElementAction(newx, newy, element, page);
7664   }
7665 #endif
7666
7667   TestIfElementHitsCustomElement(newx, newy, direction);
7668   TestIfPlayerTouchesCustomElement(newx, newy);
7669   TestIfElementTouchesCustomElement(newx, newy);
7670
7671   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7672       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7673     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7674                              MV_DIR_OPPOSITE(direction));
7675 }
7676
7677 int AmoebeNachbarNr(int ax, int ay)
7678 {
7679   int i;
7680   int element = Feld[ax][ay];
7681   int group_nr = 0;
7682   static int xy[4][2] =
7683   {
7684     { 0, -1 },
7685     { -1, 0 },
7686     { +1, 0 },
7687     { 0, +1 }
7688   };
7689
7690   for (i = 0; i < NUM_DIRECTIONS; i++)
7691   {
7692     int x = ax + xy[i][0];
7693     int y = ay + xy[i][1];
7694
7695     if (!IN_LEV_FIELD(x, y))
7696       continue;
7697
7698     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7699       group_nr = AmoebaNr[x][y];
7700   }
7701
7702   return group_nr;
7703 }
7704
7705 void AmoebenVereinigen(int ax, int ay)
7706 {
7707   int i, x, y, xx, yy;
7708   int new_group_nr = AmoebaNr[ax][ay];
7709   static int xy[4][2] =
7710   {
7711     { 0, -1 },
7712     { -1, 0 },
7713     { +1, 0 },
7714     { 0, +1 }
7715   };
7716
7717   if (new_group_nr == 0)
7718     return;
7719
7720   for (i = 0; i < NUM_DIRECTIONS; i++)
7721   {
7722     x = ax + xy[i][0];
7723     y = ay + xy[i][1];
7724
7725     if (!IN_LEV_FIELD(x, y))
7726       continue;
7727
7728     if ((Feld[x][y] == EL_AMOEBA_FULL ||
7729          Feld[x][y] == EL_BD_AMOEBA ||
7730          Feld[x][y] == EL_AMOEBA_DEAD) &&
7731         AmoebaNr[x][y] != new_group_nr)
7732     {
7733       int old_group_nr = AmoebaNr[x][y];
7734
7735       if (old_group_nr == 0)
7736         return;
7737
7738       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7739       AmoebaCnt[old_group_nr] = 0;
7740       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7741       AmoebaCnt2[old_group_nr] = 0;
7742
7743       SCAN_PLAYFIELD(xx, yy)
7744       {
7745         if (AmoebaNr[xx][yy] == old_group_nr)
7746           AmoebaNr[xx][yy] = new_group_nr;
7747       }
7748     }
7749   }
7750 }
7751
7752 void AmoebeUmwandeln(int ax, int ay)
7753 {
7754   int i, x, y;
7755
7756   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7757   {
7758     int group_nr = AmoebaNr[ax][ay];
7759
7760 #ifdef DEBUG
7761     if (group_nr == 0)
7762     {
7763       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7764       printf("AmoebeUmwandeln(): This should never happen!\n");
7765       return;
7766     }
7767 #endif
7768
7769     SCAN_PLAYFIELD(x, y)
7770     {
7771       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7772       {
7773         AmoebaNr[x][y] = 0;
7774         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7775       }
7776     }
7777
7778     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7779                             SND_AMOEBA_TURNING_TO_GEM :
7780                             SND_AMOEBA_TURNING_TO_ROCK));
7781     Bang(ax, ay);
7782   }
7783   else
7784   {
7785     static int xy[4][2] =
7786     {
7787       { 0, -1 },
7788       { -1, 0 },
7789       { +1, 0 },
7790       { 0, +1 }
7791     };
7792
7793     for (i = 0; i < NUM_DIRECTIONS; i++)
7794     {
7795       x = ax + xy[i][0];
7796       y = ay + xy[i][1];
7797
7798       if (!IN_LEV_FIELD(x, y))
7799         continue;
7800
7801       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7802       {
7803         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7804                               SND_AMOEBA_TURNING_TO_GEM :
7805                               SND_AMOEBA_TURNING_TO_ROCK));
7806         Bang(x, y);
7807       }
7808     }
7809   }
7810 }
7811
7812 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7813 {
7814   int x, y;
7815   int group_nr = AmoebaNr[ax][ay];
7816   boolean done = FALSE;
7817
7818 #ifdef DEBUG
7819   if (group_nr == 0)
7820   {
7821     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7822     printf("AmoebeUmwandelnBD(): This should never happen!\n");
7823     return;
7824   }
7825 #endif
7826
7827   SCAN_PLAYFIELD(x, y)
7828   {
7829     if (AmoebaNr[x][y] == group_nr &&
7830         (Feld[x][y] == EL_AMOEBA_DEAD ||
7831          Feld[x][y] == EL_BD_AMOEBA ||
7832          Feld[x][y] == EL_AMOEBA_GROWING))
7833     {
7834       AmoebaNr[x][y] = 0;
7835       Feld[x][y] = new_element;
7836       InitField(x, y, FALSE);
7837       DrawLevelField(x, y);
7838       done = TRUE;
7839     }
7840   }
7841
7842   if (done)
7843     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7844                             SND_BD_AMOEBA_TURNING_TO_ROCK :
7845                             SND_BD_AMOEBA_TURNING_TO_GEM));
7846 }
7847
7848 void AmoebeWaechst(int x, int y)
7849 {
7850   static unsigned long sound_delay = 0;
7851   static unsigned long sound_delay_value = 0;
7852
7853   if (!MovDelay[x][y])          /* start new growing cycle */
7854   {
7855     MovDelay[x][y] = 7;
7856
7857     if (DelayReached(&sound_delay, sound_delay_value))
7858     {
7859       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7860       sound_delay_value = 30;
7861     }
7862   }
7863
7864   if (MovDelay[x][y])           /* wait some time before growing bigger */
7865   {
7866     MovDelay[x][y]--;
7867     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7868     {
7869       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7870                                            6 - MovDelay[x][y]);
7871
7872       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7873     }
7874
7875     if (!MovDelay[x][y])
7876     {
7877       Feld[x][y] = Store[x][y];
7878       Store[x][y] = 0;
7879       DrawLevelField(x, y);
7880     }
7881   }
7882 }
7883
7884 void AmoebaDisappearing(int x, int y)
7885 {
7886   static unsigned long sound_delay = 0;
7887   static unsigned long sound_delay_value = 0;
7888
7889   if (!MovDelay[x][y])          /* start new shrinking cycle */
7890   {
7891     MovDelay[x][y] = 7;
7892
7893     if (DelayReached(&sound_delay, sound_delay_value))
7894       sound_delay_value = 30;
7895   }
7896
7897   if (MovDelay[x][y])           /* wait some time before shrinking */
7898   {
7899     MovDelay[x][y]--;
7900     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7901     {
7902       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7903                                            6 - MovDelay[x][y]);
7904
7905       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7906     }
7907
7908     if (!MovDelay[x][y])
7909     {
7910       Feld[x][y] = EL_EMPTY;
7911       DrawLevelField(x, y);
7912
7913       /* don't let mole enter this field in this cycle;
7914          (give priority to objects falling to this field from above) */
7915       Stop[x][y] = TRUE;
7916     }
7917   }
7918 }
7919
7920 void AmoebeAbleger(int ax, int ay)
7921 {
7922   int i;
7923   int element = Feld[ax][ay];
7924   int graphic = el2img(element);
7925   int newax = ax, neway = ay;
7926   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7927   static int xy[4][2] =
7928   {
7929     { 0, -1 },
7930     { -1, 0 },
7931     { +1, 0 },
7932     { 0, +1 }
7933   };
7934
7935   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7936   {
7937     Feld[ax][ay] = EL_AMOEBA_DEAD;
7938     DrawLevelField(ax, ay);
7939     return;
7940   }
7941
7942   if (IS_ANIMATED(graphic))
7943     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7944
7945   if (!MovDelay[ax][ay])        /* start making new amoeba field */
7946     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7947
7948   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
7949   {
7950     MovDelay[ax][ay]--;
7951     if (MovDelay[ax][ay])
7952       return;
7953   }
7954
7955   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7956   {
7957     int start = RND(4);
7958     int x = ax + xy[start][0];
7959     int y = ay + xy[start][1];
7960
7961     if (!IN_LEV_FIELD(x, y))
7962       return;
7963
7964     if (IS_FREE(x, y) ||
7965         CAN_GROW_INTO(Feld[x][y]) ||
7966         Feld[x][y] == EL_QUICKSAND_EMPTY ||
7967         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7968     {
7969       newax = x;
7970       neway = y;
7971     }
7972
7973     if (newax == ax && neway == ay)
7974       return;
7975   }
7976   else                          /* normal or "filled" (BD style) amoeba */
7977   {
7978     int start = RND(4);
7979     boolean waiting_for_player = FALSE;
7980
7981     for (i = 0; i < NUM_DIRECTIONS; i++)
7982     {
7983       int j = (start + i) % 4;
7984       int x = ax + xy[j][0];
7985       int y = ay + xy[j][1];
7986
7987       if (!IN_LEV_FIELD(x, y))
7988         continue;
7989
7990       if (IS_FREE(x, y) ||
7991           CAN_GROW_INTO(Feld[x][y]) ||
7992           Feld[x][y] == EL_QUICKSAND_EMPTY ||
7993           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7994       {
7995         newax = x;
7996         neway = y;
7997         break;
7998       }
7999       else if (IS_PLAYER(x, y))
8000         waiting_for_player = TRUE;
8001     }
8002
8003     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8004     {
8005       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8006       {
8007         Feld[ax][ay] = EL_AMOEBA_DEAD;
8008         DrawLevelField(ax, ay);
8009         AmoebaCnt[AmoebaNr[ax][ay]]--;
8010
8011         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8012         {
8013           if (element == EL_AMOEBA_FULL)
8014             AmoebeUmwandeln(ax, ay);
8015           else if (element == EL_BD_AMOEBA)
8016             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8017         }
8018       }
8019       return;
8020     }
8021     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8022     {
8023       /* amoeba gets larger by growing in some direction */
8024
8025       int new_group_nr = AmoebaNr[ax][ay];
8026
8027 #ifdef DEBUG
8028   if (new_group_nr == 0)
8029   {
8030     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8031     printf("AmoebeAbleger(): This should never happen!\n");
8032     return;
8033   }
8034 #endif
8035
8036       AmoebaNr[newax][neway] = new_group_nr;
8037       AmoebaCnt[new_group_nr]++;
8038       AmoebaCnt2[new_group_nr]++;
8039
8040       /* if amoeba touches other amoeba(s) after growing, unify them */
8041       AmoebenVereinigen(newax, neway);
8042
8043       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8044       {
8045         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8046         return;
8047       }
8048     }
8049   }
8050
8051   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8052       (neway == lev_fieldy - 1 && newax != ax))
8053   {
8054     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8055     Store[newax][neway] = element;
8056   }
8057   else if (neway == ay || element == EL_EMC_DRIPPER)
8058   {
8059     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8060
8061     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8062   }
8063   else
8064   {
8065     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8066     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8067     Store[ax][ay] = EL_AMOEBA_DROP;
8068     ContinueMoving(ax, ay);
8069     return;
8070   }
8071
8072   DrawLevelField(newax, neway);
8073 }
8074
8075 void Life(int ax, int ay)
8076 {
8077   int x1, y1, x2, y2;
8078   int life_time = 40;
8079   int element = Feld[ax][ay];
8080   int graphic = el2img(element);
8081   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8082                          level.biomaze);
8083   boolean changed = FALSE;
8084
8085   if (IS_ANIMATED(graphic))
8086     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8087
8088   if (Stop[ax][ay])
8089     return;
8090
8091   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8092     MovDelay[ax][ay] = life_time;
8093
8094   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8095   {
8096     MovDelay[ax][ay]--;
8097     if (MovDelay[ax][ay])
8098       return;
8099   }
8100
8101   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8102   {
8103     int xx = ax+x1, yy = ay+y1;
8104     int nachbarn = 0;
8105
8106     if (!IN_LEV_FIELD(xx, yy))
8107       continue;
8108
8109     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8110     {
8111       int x = xx+x2, y = yy+y2;
8112
8113       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8114         continue;
8115
8116       if (((Feld[x][y] == element ||
8117             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8118            !Stop[x][y]) ||
8119           (IS_FREE(x, y) && Stop[x][y]))
8120         nachbarn++;
8121     }
8122
8123     if (xx == ax && yy == ay)           /* field in the middle */
8124     {
8125       if (nachbarn < life_parameter[0] ||
8126           nachbarn > life_parameter[1])
8127       {
8128         Feld[xx][yy] = EL_EMPTY;
8129         if (!Stop[xx][yy])
8130           DrawLevelField(xx, yy);
8131         Stop[xx][yy] = TRUE;
8132         changed = TRUE;
8133       }
8134     }
8135     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8136     {                                   /* free border field */
8137       if (nachbarn >= life_parameter[2] &&
8138           nachbarn <= life_parameter[3])
8139       {
8140         Feld[xx][yy] = element;
8141         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8142         if (!Stop[xx][yy])
8143           DrawLevelField(xx, yy);
8144         Stop[xx][yy] = TRUE;
8145         changed = TRUE;
8146       }
8147     }
8148   }
8149
8150   if (changed)
8151     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8152                    SND_GAME_OF_LIFE_GROWING);
8153 }
8154
8155 static void InitRobotWheel(int x, int y)
8156 {
8157   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8158 }
8159
8160 static void RunRobotWheel(int x, int y)
8161 {
8162   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8163 }
8164
8165 static void StopRobotWheel(int x, int y)
8166 {
8167   if (ZX == x && ZY == y)
8168     ZX = ZY = -1;
8169 }
8170
8171 static void InitTimegateWheel(int x, int y)
8172 {
8173   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8174 }
8175
8176 static void RunTimegateWheel(int x, int y)
8177 {
8178   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8179 }
8180
8181 static void InitMagicBallDelay(int x, int y)
8182 {
8183 #if 1
8184   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8185 #else
8186   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8187 #endif
8188 }
8189
8190 static void ActivateMagicBall(int bx, int by)
8191 {
8192   int x, y;
8193
8194   if (level.ball_random)
8195   {
8196     int pos_border = RND(8);    /* select one of the eight border elements */
8197     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8198     int xx = pos_content % 3;
8199     int yy = pos_content / 3;
8200
8201     x = bx - 1 + xx;
8202     y = by - 1 + yy;
8203
8204     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8205       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8206   }
8207   else
8208   {
8209     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8210     {
8211       int xx = x - bx + 1;
8212       int yy = y - by + 1;
8213
8214       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8215         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8216     }
8217   }
8218
8219   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8220 }
8221
8222 void CheckExit(int x, int y)
8223 {
8224   if (local_player->gems_still_needed > 0 ||
8225       local_player->sokobanfields_still_needed > 0 ||
8226       local_player->lights_still_needed > 0)
8227   {
8228     int element = Feld[x][y];
8229     int graphic = el2img(element);
8230
8231     if (IS_ANIMATED(graphic))
8232       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8233
8234     return;
8235   }
8236
8237   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8238     return;
8239
8240   Feld[x][y] = EL_EXIT_OPENING;
8241
8242   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8243 }
8244
8245 void CheckExitEM(int x, int y)
8246 {
8247   if (local_player->gems_still_needed > 0 ||
8248       local_player->sokobanfields_still_needed > 0 ||
8249       local_player->lights_still_needed > 0)
8250   {
8251     int element = Feld[x][y];
8252     int graphic = el2img(element);
8253
8254     if (IS_ANIMATED(graphic))
8255       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8256
8257     return;
8258   }
8259
8260   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8261     return;
8262
8263   Feld[x][y] = EL_EM_EXIT_OPENING;
8264
8265   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8266 }
8267
8268 void CheckExitSteel(int x, int y)
8269 {
8270   if (local_player->gems_still_needed > 0 ||
8271       local_player->sokobanfields_still_needed > 0 ||
8272       local_player->lights_still_needed > 0)
8273   {
8274     int element = Feld[x][y];
8275     int graphic = el2img(element);
8276
8277     if (IS_ANIMATED(graphic))
8278       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8279
8280     return;
8281   }
8282
8283   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8284     return;
8285
8286   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8287
8288   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8289 }
8290
8291 void CheckExitSteelEM(int x, int y)
8292 {
8293   if (local_player->gems_still_needed > 0 ||
8294       local_player->sokobanfields_still_needed > 0 ||
8295       local_player->lights_still_needed > 0)
8296   {
8297     int element = Feld[x][y];
8298     int graphic = el2img(element);
8299
8300     if (IS_ANIMATED(graphic))
8301       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8302
8303     return;
8304   }
8305
8306   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8307     return;
8308
8309   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8310
8311   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8312 }
8313
8314 void CheckExitSP(int x, int y)
8315 {
8316   if (local_player->gems_still_needed > 0)
8317   {
8318     int element = Feld[x][y];
8319     int graphic = el2img(element);
8320
8321     if (IS_ANIMATED(graphic))
8322       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8323
8324     return;
8325   }
8326
8327   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8328     return;
8329
8330   Feld[x][y] = EL_SP_EXIT_OPENING;
8331
8332   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8333 }
8334
8335 static void CloseAllOpenTimegates()
8336 {
8337   int x, y;
8338
8339   SCAN_PLAYFIELD(x, y)
8340   {
8341     int element = Feld[x][y];
8342
8343     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8344     {
8345       Feld[x][y] = EL_TIMEGATE_CLOSING;
8346
8347       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8348     }
8349   }
8350 }
8351
8352 void DrawTwinkleOnField(int x, int y)
8353 {
8354   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8355     return;
8356
8357   if (Feld[x][y] == EL_BD_DIAMOND)
8358     return;
8359
8360   if (MovDelay[x][y] == 0)      /* next animation frame */
8361     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8362
8363   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8364   {
8365     MovDelay[x][y]--;
8366
8367     if (setup.direct_draw && MovDelay[x][y])
8368       SetDrawtoField(DRAW_BUFFERED);
8369
8370     DrawLevelElementAnimation(x, y, Feld[x][y]);
8371
8372     if (MovDelay[x][y] != 0)
8373     {
8374       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8375                                            10 - MovDelay[x][y]);
8376
8377       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8378
8379       if (setup.direct_draw)
8380       {
8381         int dest_x, dest_y;
8382
8383         dest_x = FX + SCREENX(x) * TILEX;
8384         dest_y = FY + SCREENY(y) * TILEY;
8385
8386         BlitBitmap(drawto_field, window,
8387                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8388         SetDrawtoField(DRAW_DIRECT);
8389       }
8390     }
8391   }
8392 }
8393
8394 void MauerWaechst(int x, int y)
8395 {
8396   int delay = 6;
8397
8398   if (!MovDelay[x][y])          /* next animation frame */
8399     MovDelay[x][y] = 3 * delay;
8400
8401   if (MovDelay[x][y])           /* wait some time before next frame */
8402   {
8403     MovDelay[x][y]--;
8404
8405     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8406     {
8407       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8408       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8409
8410       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8411     }
8412
8413     if (!MovDelay[x][y])
8414     {
8415       if (MovDir[x][y] == MV_LEFT)
8416       {
8417         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8418           DrawLevelField(x - 1, y);
8419       }
8420       else if (MovDir[x][y] == MV_RIGHT)
8421       {
8422         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8423           DrawLevelField(x + 1, y);
8424       }
8425       else if (MovDir[x][y] == MV_UP)
8426       {
8427         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8428           DrawLevelField(x, y - 1);
8429       }
8430       else
8431       {
8432         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8433           DrawLevelField(x, y + 1);
8434       }
8435
8436       Feld[x][y] = Store[x][y];
8437       Store[x][y] = 0;
8438       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8439       DrawLevelField(x, y);
8440     }
8441   }
8442 }
8443
8444 void MauerAbleger(int ax, int ay)
8445 {
8446   int element = Feld[ax][ay];
8447   int graphic = el2img(element);
8448   boolean oben_frei = FALSE, unten_frei = FALSE;
8449   boolean links_frei = FALSE, rechts_frei = FALSE;
8450   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8451   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8452   boolean new_wall = FALSE;
8453
8454   if (IS_ANIMATED(graphic))
8455     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8456
8457   if (!MovDelay[ax][ay])        /* start building new wall */
8458     MovDelay[ax][ay] = 6;
8459
8460   if (MovDelay[ax][ay])         /* wait some time before building new wall */
8461   {
8462     MovDelay[ax][ay]--;
8463     if (MovDelay[ax][ay])
8464       return;
8465   }
8466
8467   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8468     oben_frei = TRUE;
8469   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8470     unten_frei = TRUE;
8471   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8472     links_frei = TRUE;
8473   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8474     rechts_frei = TRUE;
8475
8476   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
8477       element == EL_EXPANDABLE_WALL_ANY)
8478   {
8479     if (oben_frei)
8480     {
8481       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
8482       Store[ax][ay-1] = element;
8483       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8484       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8485         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8486                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
8487       new_wall = TRUE;
8488     }
8489     if (unten_frei)
8490     {
8491       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
8492       Store[ax][ay+1] = element;
8493       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8494       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8495         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8496                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8497       new_wall = TRUE;
8498     }
8499   }
8500
8501   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8502       element == EL_EXPANDABLE_WALL_ANY ||
8503       element == EL_EXPANDABLE_WALL ||
8504       element == EL_BD_EXPANDABLE_WALL)
8505   {
8506     if (links_frei)
8507     {
8508       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8509       Store[ax-1][ay] = element;
8510       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8511       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8512         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8513                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8514       new_wall = TRUE;
8515     }
8516
8517     if (rechts_frei)
8518     {
8519       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8520       Store[ax+1][ay] = element;
8521       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8522       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8523         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8524                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8525       new_wall = TRUE;
8526     }
8527   }
8528
8529   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8530     DrawLevelField(ax, ay);
8531
8532   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8533     oben_massiv = TRUE;
8534   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8535     unten_massiv = TRUE;
8536   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8537     links_massiv = TRUE;
8538   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8539     rechts_massiv = TRUE;
8540
8541   if (((oben_massiv && unten_massiv) ||
8542        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8543        element == EL_EXPANDABLE_WALL) &&
8544       ((links_massiv && rechts_massiv) ||
8545        element == EL_EXPANDABLE_WALL_VERTICAL))
8546     Feld[ax][ay] = EL_WALL;
8547
8548   if (new_wall)
8549     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8550 }
8551
8552 void MauerAblegerStahl(int ax, int ay)
8553 {
8554   int element = Feld[ax][ay];
8555   int graphic = el2img(element);
8556   boolean oben_frei = FALSE, unten_frei = FALSE;
8557   boolean links_frei = FALSE, rechts_frei = FALSE;
8558   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8559   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8560   boolean new_wall = FALSE;
8561
8562   if (IS_ANIMATED(graphic))
8563     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8564
8565   if (!MovDelay[ax][ay])        /* start building new wall */
8566     MovDelay[ax][ay] = 6;
8567
8568   if (MovDelay[ax][ay])         /* wait some time before building new wall */
8569   {
8570     MovDelay[ax][ay]--;
8571     if (MovDelay[ax][ay])
8572       return;
8573   }
8574
8575   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8576     oben_frei = TRUE;
8577   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8578     unten_frei = TRUE;
8579   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8580     links_frei = TRUE;
8581   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8582     rechts_frei = TRUE;
8583
8584   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
8585       element == EL_EXPANDABLE_STEELWALL_ANY)
8586   {
8587     if (oben_frei)
8588     {
8589       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
8590       Store[ax][ay-1] = element;
8591       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8592       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8593         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8594                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
8595       new_wall = TRUE;
8596     }
8597     if (unten_frei)
8598     {
8599       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
8600       Store[ax][ay+1] = element;
8601       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8602       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8603         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8604                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
8605       new_wall = TRUE;
8606     }
8607   }
8608
8609   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
8610       element == EL_EXPANDABLE_STEELWALL_ANY)
8611   {
8612     if (links_frei)
8613     {
8614       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8615       Store[ax-1][ay] = element;
8616       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8617       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8618         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8619                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
8620       new_wall = TRUE;
8621     }
8622
8623     if (rechts_frei)
8624     {
8625       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8626       Store[ax+1][ay] = element;
8627       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8628       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8629         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8630                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
8631       new_wall = TRUE;
8632     }
8633   }
8634
8635   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8636     oben_massiv = TRUE;
8637   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8638     unten_massiv = TRUE;
8639   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8640     links_massiv = TRUE;
8641   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8642     rechts_massiv = TRUE;
8643
8644   if (((oben_massiv && unten_massiv) ||
8645        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
8646       ((links_massiv && rechts_massiv) ||
8647        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
8648     Feld[ax][ay] = EL_WALL;
8649
8650   if (new_wall)
8651     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8652 }
8653
8654 void CheckForDragon(int x, int y)
8655 {
8656   int i, j;
8657   boolean dragon_found = FALSE;
8658   static int xy[4][2] =
8659   {
8660     { 0, -1 },
8661     { -1, 0 },
8662     { +1, 0 },
8663     { 0, +1 }
8664   };
8665
8666   for (i = 0; i < NUM_DIRECTIONS; i++)
8667   {
8668     for (j = 0; j < 4; j++)
8669     {
8670       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8671
8672       if (IN_LEV_FIELD(xx, yy) &&
8673           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
8674       {
8675         if (Feld[xx][yy] == EL_DRAGON)
8676           dragon_found = TRUE;
8677       }
8678       else
8679         break;
8680     }
8681   }
8682
8683   if (!dragon_found)
8684   {
8685     for (i = 0; i < NUM_DIRECTIONS; i++)
8686     {
8687       for (j = 0; j < 3; j++)
8688       {
8689         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8690   
8691         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
8692         {
8693           Feld[xx][yy] = EL_EMPTY;
8694           DrawLevelField(xx, yy);
8695         }
8696         else
8697           break;
8698       }
8699     }
8700   }
8701 }
8702
8703 static void InitBuggyBase(int x, int y)
8704 {
8705   int element = Feld[x][y];
8706   int activating_delay = FRAMES_PER_SECOND / 4;
8707
8708   ChangeDelay[x][y] =
8709     (element == EL_SP_BUGGY_BASE ?
8710      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
8711      element == EL_SP_BUGGY_BASE_ACTIVATING ?
8712      activating_delay :
8713      element == EL_SP_BUGGY_BASE_ACTIVE ?
8714      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
8715 }
8716
8717 static void WarnBuggyBase(int x, int y)
8718 {
8719   int i;
8720   static int xy[4][2] =
8721   {
8722     { 0, -1 },
8723     { -1, 0 },
8724     { +1, 0 },
8725     { 0, +1 }
8726   };
8727
8728   for (i = 0; i < NUM_DIRECTIONS; i++)
8729   {
8730     int xx = x + xy[i][0];
8731     int yy = y + xy[i][1];
8732
8733     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
8734     {
8735       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8736
8737       break;
8738     }
8739   }
8740 }
8741
8742 static void InitTrap(int x, int y)
8743 {
8744   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8745 }
8746
8747 static void ActivateTrap(int x, int y)
8748 {
8749   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8750 }
8751
8752 static void ChangeActiveTrap(int x, int y)
8753 {
8754   int graphic = IMG_TRAP_ACTIVE;
8755
8756   /* if new animation frame was drawn, correct crumbled sand border */
8757   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8758     DrawLevelFieldCrumbledSand(x, y);
8759 }
8760
8761 static int getSpecialActionElement(int element, int number, int base_element)
8762 {
8763   return (element != EL_EMPTY ? element :
8764           number != -1 ? base_element + number - 1 :
8765           EL_EMPTY);
8766 }
8767
8768 static int getModifiedActionNumber(int value_old, int operator, int operand,
8769                                    int value_min, int value_max)
8770 {
8771   int value_new = (operator == CA_MODE_SET      ? operand :
8772                    operator == CA_MODE_ADD      ? value_old + operand :
8773                    operator == CA_MODE_SUBTRACT ? value_old - operand :
8774                    operator == CA_MODE_MULTIPLY ? value_old * operand :
8775                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
8776                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
8777                    value_old);
8778
8779   return (value_new < value_min ? value_min :
8780           value_new > value_max ? value_max :
8781           value_new);
8782 }
8783
8784 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8785 {
8786   struct ElementInfo *ei = &element_info[element];
8787   struct ElementChangeInfo *change = &ei->change_page[page];
8788   int target_element = change->target_element;
8789   int action_type = change->action_type;
8790   int action_mode = change->action_mode;
8791   int action_arg = change->action_arg;
8792   int i;
8793
8794   if (!change->has_action)
8795     return;
8796
8797   /* ---------- determine action paramater values -------------------------- */
8798
8799   int level_time_value =
8800     (level.time > 0 ? TimeLeft :
8801      TimePlayed);
8802
8803   int action_arg_element =
8804     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
8805      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8806      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
8807      EL_EMPTY);
8808
8809   int action_arg_direction =
8810     (action_arg >= CA_ARG_DIRECTION_LEFT &&
8811      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8812      action_arg == CA_ARG_DIRECTION_TRIGGER ?
8813      change->actual_trigger_side :
8814      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8815      MV_DIR_OPPOSITE(change->actual_trigger_side) :
8816      MV_NONE);
8817
8818   int action_arg_number_min =
8819     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8820      CA_ARG_MIN);
8821
8822   int action_arg_number_max =
8823     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8824      action_type == CA_SET_LEVEL_GEMS ? 999 :
8825      action_type == CA_SET_LEVEL_TIME ? 9999 :
8826      action_type == CA_SET_LEVEL_SCORE ? 99999 :
8827      action_type == CA_SET_CE_VALUE ? 9999 :
8828      action_type == CA_SET_CE_SCORE ? 9999 :
8829      CA_ARG_MAX);
8830
8831   int action_arg_number_reset =
8832     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8833      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8834      action_type == CA_SET_LEVEL_TIME ? level.time :
8835      action_type == CA_SET_LEVEL_SCORE ? 0 :
8836 #if USE_NEW_CUSTOM_VALUE
8837      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8838 #else
8839      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8840 #endif
8841      action_type == CA_SET_CE_SCORE ? 0 :
8842      0);
8843
8844   int action_arg_number =
8845     (action_arg <= CA_ARG_MAX ? action_arg :
8846      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8847      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8848      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8849      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8850      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8851      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8852 #if USE_NEW_CUSTOM_VALUE
8853      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8854 #else
8855      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8856 #endif
8857      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8858      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8859      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8860      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8861      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8862      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8863      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8864      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8865      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8866      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
8867      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8868      -1);
8869
8870   int action_arg_number_old =
8871     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8872      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8873      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8874      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8875      action_type == CA_SET_CE_SCORE ? ei->collect_score :
8876      0);
8877
8878   int action_arg_number_new =
8879     getModifiedActionNumber(action_arg_number_old,
8880                             action_mode, action_arg_number,
8881                             action_arg_number_min, action_arg_number_max);
8882
8883   int trigger_player_bits =
8884     (change->actual_trigger_player >= EL_PLAYER_1 &&
8885      change->actual_trigger_player <= EL_PLAYER_4 ?
8886      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8887      PLAYER_BITS_ANY);
8888
8889   int action_arg_player_bits =
8890     (action_arg >= CA_ARG_PLAYER_1 &&
8891      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8892      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8893      PLAYER_BITS_ANY);
8894
8895   /* ---------- execute action  -------------------------------------------- */
8896
8897   switch (action_type)
8898   {
8899     case CA_NO_ACTION:
8900     {
8901       return;
8902     }
8903
8904     /* ---------- level actions  ------------------------------------------- */
8905
8906     case CA_RESTART_LEVEL:
8907     {
8908       game.restart_level = TRUE;
8909
8910       break;
8911     }
8912
8913     case CA_SHOW_ENVELOPE:
8914     {
8915       int element = getSpecialActionElement(action_arg_element,
8916                                             action_arg_number, EL_ENVELOPE_1);
8917
8918       if (IS_ENVELOPE(element))
8919         local_player->show_envelope = element;
8920
8921       break;
8922     }
8923
8924     case CA_SET_LEVEL_TIME:
8925     {
8926       if (level.time > 0)       /* only modify limited time value */
8927       {
8928         TimeLeft = action_arg_number_new;
8929
8930         DrawGameValue_Time(TimeLeft);
8931
8932         if (!TimeLeft && setup.time_limit)
8933           for (i = 0; i < MAX_PLAYERS; i++)
8934             KillPlayer(&stored_player[i]);
8935       }
8936
8937       break;
8938     }
8939
8940     case CA_SET_LEVEL_SCORE:
8941     {
8942       local_player->score = action_arg_number_new;
8943
8944       DrawGameValue_Score(local_player->score);
8945
8946       break;
8947     }
8948
8949     case CA_SET_LEVEL_GEMS:
8950     {
8951       local_player->gems_still_needed = action_arg_number_new;
8952
8953       DrawGameValue_Emeralds(local_player->gems_still_needed);
8954
8955       break;
8956     }
8957
8958 #if !USE_PLAYER_GRAVITY
8959     case CA_SET_LEVEL_GRAVITY:
8960     {
8961       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
8962                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
8963                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8964                       game.gravity);
8965       break;
8966     }
8967 #endif
8968
8969     case CA_SET_LEVEL_WIND:
8970     {
8971       game.wind_direction = action_arg_direction;
8972
8973       break;
8974     }
8975
8976     /* ---------- player actions  ------------------------------------------ */
8977
8978     case CA_MOVE_PLAYER:
8979     {
8980       /* automatically move to the next field in specified direction */
8981       for (i = 0; i < MAX_PLAYERS; i++)
8982         if (trigger_player_bits & (1 << i))
8983           stored_player[i].programmed_action = action_arg_direction;
8984
8985       break;
8986     }
8987
8988     case CA_EXIT_PLAYER:
8989     {
8990       for (i = 0; i < MAX_PLAYERS; i++)
8991         if (action_arg_player_bits & (1 << i))
8992           PlayerWins(&stored_player[i]);
8993
8994       break;
8995     }
8996
8997     case CA_KILL_PLAYER:
8998     {
8999       for (i = 0; i < MAX_PLAYERS; i++)
9000         if (action_arg_player_bits & (1 << i))
9001           KillPlayer(&stored_player[i]);
9002
9003       break;
9004     }
9005
9006     case CA_SET_PLAYER_KEYS:
9007     {
9008       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9009       int element = getSpecialActionElement(action_arg_element,
9010                                             action_arg_number, EL_KEY_1);
9011
9012       if (IS_KEY(element))
9013       {
9014         for (i = 0; i < MAX_PLAYERS; i++)
9015         {
9016           if (trigger_player_bits & (1 << i))
9017           {
9018             stored_player[i].key[KEY_NR(element)] = key_state;
9019
9020             DrawGameDoorValues();
9021           }
9022         }
9023       }
9024
9025       break;
9026     }
9027
9028     case CA_SET_PLAYER_SPEED:
9029     {
9030       for (i = 0; i < MAX_PLAYERS; i++)
9031       {
9032         if (trigger_player_bits & (1 << i))
9033         {
9034           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9035
9036           if (action_arg == CA_ARG_SPEED_FASTER &&
9037               stored_player[i].cannot_move)
9038           {
9039             action_arg_number = STEPSIZE_VERY_SLOW;
9040           }
9041           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9042                    action_arg == CA_ARG_SPEED_FASTER)
9043           {
9044             action_arg_number = 2;
9045             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9046                            CA_MODE_MULTIPLY);
9047           }
9048           else if (action_arg == CA_ARG_NUMBER_RESET)
9049           {
9050             action_arg_number = level.initial_player_stepsize[i];
9051           }
9052
9053           move_stepsize =
9054             getModifiedActionNumber(move_stepsize,
9055                                     action_mode,
9056                                     action_arg_number,
9057                                     action_arg_number_min,
9058                                     action_arg_number_max);
9059
9060           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9061         }
9062       }
9063
9064       break;
9065     }
9066
9067     case CA_SET_PLAYER_SHIELD:
9068     {
9069       for (i = 0; i < MAX_PLAYERS; i++)
9070       {
9071         if (trigger_player_bits & (1 << i))
9072         {
9073           if (action_arg == CA_ARG_SHIELD_OFF)
9074           {
9075             stored_player[i].shield_normal_time_left = 0;
9076             stored_player[i].shield_deadly_time_left = 0;
9077           }
9078           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9079           {
9080             stored_player[i].shield_normal_time_left = 999999;
9081           }
9082           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9083           {
9084             stored_player[i].shield_normal_time_left = 999999;
9085             stored_player[i].shield_deadly_time_left = 999999;
9086           }
9087         }
9088       }
9089
9090       break;
9091     }
9092
9093 #if USE_PLAYER_GRAVITY
9094     case CA_SET_PLAYER_GRAVITY:
9095     {
9096       for (i = 0; i < MAX_PLAYERS; i++)
9097       {
9098         if (trigger_player_bits & (1 << i))
9099         {
9100           stored_player[i].gravity =
9101             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9102              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9103              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9104              stored_player[i].gravity);
9105         }
9106       }
9107
9108       break;
9109     }
9110 #endif
9111
9112     case CA_SET_PLAYER_ARTWORK:
9113     {
9114       for (i = 0; i < MAX_PLAYERS; i++)
9115       {
9116         if (trigger_player_bits & (1 << i))
9117         {
9118           int artwork_element = action_arg_element;
9119
9120           if (action_arg == CA_ARG_ELEMENT_RESET)
9121             artwork_element =
9122               (level.use_artwork_element[i] ? level.artwork_element[i] :
9123                stored_player[i].element_nr);
9124
9125 #if USE_GFX_RESET_PLAYER_ARTWORK
9126           if (stored_player[i].artwork_element != artwork_element)
9127             stored_player[i].Frame = 0;
9128 #endif
9129
9130           stored_player[i].artwork_element = artwork_element;
9131
9132           SetPlayerWaiting(&stored_player[i], FALSE);
9133
9134           /* set number of special actions for bored and sleeping animation */
9135           stored_player[i].num_special_action_bored =
9136             get_num_special_action(artwork_element,
9137                                    ACTION_BORING_1, ACTION_BORING_LAST);
9138           stored_player[i].num_special_action_sleeping =
9139             get_num_special_action(artwork_element,
9140                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9141         }
9142       }
9143
9144       break;
9145     }
9146
9147     /* ---------- CE actions  ---------------------------------------------- */
9148
9149     case CA_SET_CE_VALUE:
9150     {
9151 #if USE_NEW_CUSTOM_VALUE
9152       int last_ce_value = CustomValue[x][y];
9153
9154       CustomValue[x][y] = action_arg_number_new;
9155
9156       if (CustomValue[x][y] != last_ce_value)
9157       {
9158         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9159         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9160
9161         if (CustomValue[x][y] == 0)
9162         {
9163           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9164           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9165         }
9166       }
9167 #endif
9168
9169       break;
9170     }
9171
9172     case CA_SET_CE_SCORE:
9173     {
9174 #if USE_NEW_CUSTOM_VALUE
9175       int last_ce_score = ei->collect_score;
9176
9177       ei->collect_score = action_arg_number_new;
9178
9179       if (ei->collect_score != last_ce_score)
9180       {
9181         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9182         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9183
9184         if (ei->collect_score == 0)
9185         {
9186           int xx, yy;
9187
9188           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9189           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9190
9191           /*
9192             This is a very special case that seems to be a mixture between
9193             CheckElementChange() and CheckTriggeredElementChange(): while
9194             the first one only affects single elements that are triggered
9195             directly, the second one affects multiple elements in the playfield
9196             that are triggered indirectly by another element. This is a third
9197             case: Changing the CE score always affects multiple identical CEs,
9198             so every affected CE must be checked, not only the single CE for
9199             which the CE score was changed in the first place (as every instance
9200             of that CE shares the same CE score, and therefore also can change)!
9201           */
9202           SCAN_PLAYFIELD(xx, yy)
9203           {
9204             if (Feld[xx][yy] == element)
9205               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9206                                  CE_SCORE_GETS_ZERO);
9207           }
9208         }
9209       }
9210 #endif
9211
9212       break;
9213     }
9214
9215     /* ---------- engine actions  ------------------------------------------ */
9216
9217     case CA_SET_ENGINE_SCAN_MODE:
9218     {
9219       InitPlayfieldScanMode(action_arg);
9220
9221       break;
9222     }
9223
9224     default:
9225       break;
9226   }
9227 }
9228
9229 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9230 {
9231   int old_element = Feld[x][y];
9232   int new_element = GetElementFromGroupElement(element);
9233   int previous_move_direction = MovDir[x][y];
9234 #if USE_NEW_CUSTOM_VALUE
9235   int last_ce_value = CustomValue[x][y];
9236 #endif
9237   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9238   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9239   boolean add_player_onto_element = (new_element_is_player &&
9240 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9241                                      /* this breaks SnakeBite when a snake is
9242                                         halfway through a door that closes */
9243                                      /* NOW FIXED AT LEVEL INIT IN files.c */
9244                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9245 #endif
9246                                      IS_WALKABLE(old_element));
9247
9248 #if 0
9249   /* check if element under the player changes from accessible to unaccessible
9250      (needed for special case of dropping element which then changes) */
9251   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9252       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9253   {
9254     Bang(x, y);
9255
9256     return;
9257   }
9258 #endif
9259
9260   if (!add_player_onto_element)
9261   {
9262     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9263       RemoveMovingField(x, y);
9264     else
9265       RemoveField(x, y);
9266
9267     Feld[x][y] = new_element;
9268
9269 #if !USE_GFX_RESET_GFX_ANIMATION
9270     ResetGfxAnimation(x, y);
9271     ResetRandomAnimationValue(x, y);
9272 #endif
9273
9274     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9275       MovDir[x][y] = previous_move_direction;
9276
9277 #if USE_NEW_CUSTOM_VALUE
9278     if (element_info[new_element].use_last_ce_value)
9279       CustomValue[x][y] = last_ce_value;
9280 #endif
9281
9282     InitField_WithBug1(x, y, FALSE);
9283
9284     new_element = Feld[x][y];   /* element may have changed */
9285
9286 #if USE_GFX_RESET_GFX_ANIMATION
9287     ResetGfxAnimation(x, y);
9288     ResetRandomAnimationValue(x, y);
9289 #endif
9290
9291     DrawLevelField(x, y);
9292
9293     if (GFX_CRUMBLED(new_element))
9294       DrawLevelFieldCrumbledSandNeighbours(x, y);
9295   }
9296
9297 #if 1
9298   /* check if element under the player changes from accessible to unaccessible
9299      (needed for special case of dropping element which then changes) */
9300   /* (must be checked after creating new element for walkable group elements) */
9301 #if USE_FIX_KILLED_BY_NON_WALKABLE
9302   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9303       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9304   {
9305     Bang(x, y);
9306
9307     return;
9308   }
9309 #else
9310   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9311       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9312   {
9313     Bang(x, y);
9314
9315     return;
9316   }
9317 #endif
9318 #endif
9319
9320   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9321   if (new_element_is_player)
9322     RelocatePlayer(x, y, new_element);
9323
9324   if (is_change)
9325     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9326
9327   TestIfBadThingTouchesPlayer(x, y);
9328   TestIfPlayerTouchesCustomElement(x, y);
9329   TestIfElementTouchesCustomElement(x, y);
9330 }
9331
9332 static void CreateField(int x, int y, int element)
9333 {
9334   CreateFieldExt(x, y, element, FALSE);
9335 }
9336
9337 static void CreateElementFromChange(int x, int y, int element)
9338 {
9339   element = GET_VALID_RUNTIME_ELEMENT(element);
9340
9341 #if USE_STOP_CHANGED_ELEMENTS
9342   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9343   {
9344     int old_element = Feld[x][y];
9345
9346     /* prevent changed element from moving in same engine frame
9347        unless both old and new element can either fall or move */
9348     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9349         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9350       Stop[x][y] = TRUE;
9351   }
9352 #endif
9353
9354   CreateFieldExt(x, y, element, TRUE);
9355 }
9356
9357 static boolean ChangeElement(int x, int y, int element, int page)
9358 {
9359   struct ElementInfo *ei = &element_info[element];
9360   struct ElementChangeInfo *change = &ei->change_page[page];
9361   int ce_value = CustomValue[x][y];
9362   int ce_score = ei->collect_score;
9363   int target_element;
9364   int old_element = Feld[x][y];
9365
9366   /* always use default change event to prevent running into a loop */
9367   if (ChangeEvent[x][y] == -1)
9368     ChangeEvent[x][y] = CE_DELAY;
9369
9370   if (ChangeEvent[x][y] == CE_DELAY)
9371   {
9372     /* reset actual trigger element, trigger player and action element */
9373     change->actual_trigger_element = EL_EMPTY;
9374     change->actual_trigger_player = EL_PLAYER_1;
9375     change->actual_trigger_side = CH_SIDE_NONE;
9376     change->actual_trigger_ce_value = 0;
9377     change->actual_trigger_ce_score = 0;
9378   }
9379
9380   /* do not change elements more than a specified maximum number of changes */
9381   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
9382     return FALSE;
9383
9384   ChangeCount[x][y]++;          /* count number of changes in the same frame */
9385
9386   if (change->explode)
9387   {
9388     Bang(x, y);
9389
9390     return TRUE;
9391   }
9392
9393   if (change->use_target_content)
9394   {
9395     boolean complete_replace = TRUE;
9396     boolean can_replace[3][3];
9397     int xx, yy;
9398
9399     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9400     {
9401       boolean is_empty;
9402       boolean is_walkable;
9403       boolean is_diggable;
9404       boolean is_collectible;
9405       boolean is_removable;
9406       boolean is_destructible;
9407       int ex = x + xx - 1;
9408       int ey = y + yy - 1;
9409       int content_element = change->target_content.e[xx][yy];
9410       int e;
9411
9412       can_replace[xx][yy] = TRUE;
9413
9414       if (ex == x && ey == y)   /* do not check changing element itself */
9415         continue;
9416
9417       if (content_element == EL_EMPTY_SPACE)
9418       {
9419         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
9420
9421         continue;
9422       }
9423
9424       if (!IN_LEV_FIELD(ex, ey))
9425       {
9426         can_replace[xx][yy] = FALSE;
9427         complete_replace = FALSE;
9428
9429         continue;
9430       }
9431
9432       e = Feld[ex][ey];
9433
9434       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9435         e = MovingOrBlocked2Element(ex, ey);
9436
9437       is_empty = (IS_FREE(ex, ey) ||
9438                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
9439
9440       is_walkable     = (is_empty || IS_WALKABLE(e));
9441       is_diggable     = (is_empty || IS_DIGGABLE(e));
9442       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
9443       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
9444       is_removable    = (is_diggable || is_collectible);
9445
9446       can_replace[xx][yy] =
9447         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
9448           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
9449           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
9450           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
9451           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
9452           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
9453          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
9454
9455       if (!can_replace[xx][yy])
9456         complete_replace = FALSE;
9457     }
9458
9459     if (!change->only_if_complete || complete_replace)
9460     {
9461       boolean something_has_changed = FALSE;
9462
9463       if (change->only_if_complete && change->use_random_replace &&
9464           RND(100) < change->random_percentage)
9465         return FALSE;
9466
9467       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9468       {
9469         int ex = x + xx - 1;
9470         int ey = y + yy - 1;
9471         int content_element;
9472
9473         if (can_replace[xx][yy] && (!change->use_random_replace ||
9474                                     RND(100) < change->random_percentage))
9475         {
9476           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9477             RemoveMovingField(ex, ey);
9478
9479           ChangeEvent[ex][ey] = ChangeEvent[x][y];
9480
9481           content_element = change->target_content.e[xx][yy];
9482           target_element = GET_TARGET_ELEMENT(element, content_element, change,
9483                                               ce_value, ce_score);
9484
9485           CreateElementFromChange(ex, ey, target_element);
9486
9487           something_has_changed = TRUE;
9488
9489           /* for symmetry reasons, freeze newly created border elements */
9490           if (ex != x || ey != y)
9491             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
9492         }
9493       }
9494
9495       if (something_has_changed)
9496       {
9497         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9498         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9499       }
9500     }
9501   }
9502   else
9503   {
9504     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9505                                         ce_value, ce_score);
9506
9507     if (element == EL_DIAGONAL_GROWING ||
9508         element == EL_DIAGONAL_SHRINKING)
9509     {
9510       target_element = Store[x][y];
9511
9512       Store[x][y] = EL_EMPTY;
9513     }
9514
9515     CreateElementFromChange(x, y, target_element);
9516
9517     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9518     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9519   }
9520
9521   /* this uses direct change before indirect change */
9522   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9523
9524   return TRUE;
9525 }
9526
9527 #if USE_NEW_DELAYED_ACTION
9528
9529 static void HandleElementChange(int x, int y, int page)
9530 {
9531   int element = MovingOrBlocked2Element(x, y);
9532   struct ElementInfo *ei = &element_info[element];
9533   struct ElementChangeInfo *change = &ei->change_page[page];
9534
9535 #ifdef DEBUG
9536   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
9537       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
9538   {
9539     printf("\n\n");
9540     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9541            x, y, element, element_info[element].token_name);
9542     printf("HandleElementChange(): This should never happen!\n");
9543     printf("\n\n");
9544   }
9545 #endif
9546
9547   /* this can happen with classic bombs on walkable, changing elements */
9548   if (!CAN_CHANGE_OR_HAS_ACTION(element))
9549   {
9550 #if 0
9551     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9552       ChangeDelay[x][y] = 0;
9553 #endif
9554
9555     return;
9556   }
9557
9558   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9559   {
9560     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9561
9562     if (change->can_change)
9563     {
9564 #if 1
9565       /* !!! not clear why graphic animation should be reset at all here !!! */
9566       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
9567 #if USE_GFX_RESET_WHEN_NOT_MOVING
9568       /* when a custom element is about to change (for example by change delay),
9569          do not reset graphic animation when the custom element is moving */
9570       if (!IS_MOVING(x, y))
9571 #endif
9572       {
9573         ResetGfxAnimation(x, y);
9574         ResetRandomAnimationValue(x, y);
9575       }
9576 #endif
9577
9578       if (change->pre_change_function)
9579         change->pre_change_function(x, y);
9580     }
9581   }
9582
9583   ChangeDelay[x][y]--;
9584
9585   if (ChangeDelay[x][y] != 0)           /* continue element change */
9586   {
9587     if (change->can_change)
9588     {
9589       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9590
9591       if (IS_ANIMATED(graphic))
9592         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9593
9594       if (change->change_function)
9595         change->change_function(x, y);
9596     }
9597   }
9598   else                                  /* finish element change */
9599   {
9600     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9601     {
9602       page = ChangePage[x][y];
9603       ChangePage[x][y] = -1;
9604
9605       change = &ei->change_page[page];
9606     }
9607
9608     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9609     {
9610       ChangeDelay[x][y] = 1;            /* try change after next move step */
9611       ChangePage[x][y] = page;          /* remember page to use for change */
9612
9613       return;
9614     }
9615
9616     if (change->can_change)
9617     {
9618       if (ChangeElement(x, y, element, page))
9619       {
9620         if (change->post_change_function)
9621           change->post_change_function(x, y);
9622       }
9623     }
9624
9625     if (change->has_action)
9626       ExecuteCustomElementAction(x, y, element, page);
9627   }
9628 }
9629
9630 #else
9631
9632 static void HandleElementChange(int x, int y, int page)
9633 {
9634   int element = MovingOrBlocked2Element(x, y);
9635   struct ElementInfo *ei = &element_info[element];
9636   struct ElementChangeInfo *change = &ei->change_page[page];
9637
9638 #ifdef DEBUG
9639   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
9640   {
9641     printf("\n\n");
9642     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9643            x, y, element, element_info[element].token_name);
9644     printf("HandleElementChange(): This should never happen!\n");
9645     printf("\n\n");
9646   }
9647 #endif
9648
9649   /* this can happen with classic bombs on walkable, changing elements */
9650   if (!CAN_CHANGE(element))
9651   {
9652 #if 0
9653     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9654       ChangeDelay[x][y] = 0;
9655 #endif
9656
9657     return;
9658   }
9659
9660   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9661   {
9662     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9663
9664     ResetGfxAnimation(x, y);
9665     ResetRandomAnimationValue(x, y);
9666
9667     if (change->pre_change_function)
9668       change->pre_change_function(x, y);
9669   }
9670
9671   ChangeDelay[x][y]--;
9672
9673   if (ChangeDelay[x][y] != 0)           /* continue element change */
9674   {
9675     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9676
9677     if (IS_ANIMATED(graphic))
9678       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9679
9680     if (change->change_function)
9681       change->change_function(x, y);
9682   }
9683   else                                  /* finish element change */
9684   {
9685     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9686     {
9687       page = ChangePage[x][y];
9688       ChangePage[x][y] = -1;
9689
9690       change = &ei->change_page[page];
9691     }
9692
9693     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9694     {
9695       ChangeDelay[x][y] = 1;            /* try change after next move step */
9696       ChangePage[x][y] = page;          /* remember page to use for change */
9697
9698       return;
9699     }
9700
9701     if (ChangeElement(x, y, element, page))
9702     {
9703       if (change->post_change_function)
9704         change->post_change_function(x, y);
9705     }
9706   }
9707 }
9708
9709 #endif
9710
9711 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
9712                                               int trigger_element,
9713                                               int trigger_event,
9714                                               int trigger_player,
9715                                               int trigger_side,
9716                                               int trigger_page)
9717 {
9718   boolean change_done_any = FALSE;
9719   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9720   int i;
9721
9722   if (!(trigger_events[trigger_element][trigger_event]))
9723     return FALSE;
9724
9725 #if 0
9726   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9727          trigger_event, recursion_loop_depth, recursion_loop_detected,
9728          recursion_loop_element, EL_NAME(recursion_loop_element));
9729 #endif
9730
9731   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9732
9733   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9734   {
9735     int element = EL_CUSTOM_START + i;
9736     boolean change_done = FALSE;
9737     int p;
9738
9739     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9740         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9741       continue;
9742
9743     for (p = 0; p < element_info[element].num_change_pages; p++)
9744     {
9745       struct ElementChangeInfo *change = &element_info[element].change_page[p];
9746
9747       if (change->can_change_or_has_action &&
9748           change->has_event[trigger_event] &&
9749           change->trigger_side & trigger_side &&
9750           change->trigger_player & trigger_player &&
9751           change->trigger_page & trigger_page_bits &&
9752           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9753       {
9754         change->actual_trigger_element = trigger_element;
9755         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9756         change->actual_trigger_side = trigger_side;
9757         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9758         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9759
9760         if ((change->can_change && !change_done) || change->has_action)
9761         {
9762           int x, y;
9763
9764           SCAN_PLAYFIELD(x, y)
9765           {
9766             if (Feld[x][y] == element)
9767             {
9768               if (change->can_change && !change_done)
9769               {
9770                 ChangeDelay[x][y] = 1;
9771                 ChangeEvent[x][y] = trigger_event;
9772
9773                 HandleElementChange(x, y, p);
9774               }
9775 #if USE_NEW_DELAYED_ACTION
9776               else if (change->has_action)
9777               {
9778                 ExecuteCustomElementAction(x, y, element, p);
9779                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9780               }
9781 #else
9782               if (change->has_action)
9783               {
9784                 ExecuteCustomElementAction(x, y, element, p);
9785                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9786               }
9787 #endif
9788             }
9789           }
9790
9791           if (change->can_change)
9792           {
9793             change_done = TRUE;
9794             change_done_any = TRUE;
9795           }
9796         }
9797       }
9798     }
9799   }
9800
9801   RECURSION_LOOP_DETECTION_END();
9802
9803   return change_done_any;
9804 }
9805
9806 static boolean CheckElementChangeExt(int x, int y,
9807                                      int element,
9808                                      int trigger_element,
9809                                      int trigger_event,
9810                                      int trigger_player,
9811                                      int trigger_side)
9812 {
9813   boolean change_done = FALSE;
9814   int p;
9815
9816   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9817       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9818     return FALSE;
9819
9820   if (Feld[x][y] == EL_BLOCKED)
9821   {
9822     Blocked2Moving(x, y, &x, &y);
9823     element = Feld[x][y];
9824   }
9825
9826 #if 0
9827   /* check if element has already changed */
9828   if (Feld[x][y] != element)
9829     return FALSE;
9830 #else
9831   /* check if element has already changed or is about to change after moving */
9832   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9833        Feld[x][y] != element) ||
9834
9835       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9836        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9837         ChangePage[x][y] != -1)))
9838     return FALSE;
9839 #endif
9840
9841 #if 0
9842   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9843          trigger_event, recursion_loop_depth, recursion_loop_detected,
9844          recursion_loop_element, EL_NAME(recursion_loop_element));
9845 #endif
9846
9847   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9848
9849   for (p = 0; p < element_info[element].num_change_pages; p++)
9850   {
9851     struct ElementChangeInfo *change = &element_info[element].change_page[p];
9852
9853     /* check trigger element for all events where the element that is checked
9854        for changing interacts with a directly adjacent element -- this is
9855        different to element changes that affect other elements to change on the
9856        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
9857     boolean check_trigger_element =
9858       (trigger_event == CE_TOUCHING_X ||
9859        trigger_event == CE_HITTING_X ||
9860        trigger_event == CE_HIT_BY_X ||
9861 #if 1
9862        /* this one was forgotten until 3.2.3 */
9863        trigger_event == CE_DIGGING_X);
9864 #endif
9865
9866     if (change->can_change_or_has_action &&
9867         change->has_event[trigger_event] &&
9868         change->trigger_side & trigger_side &&
9869         change->trigger_player & trigger_player &&
9870         (!check_trigger_element ||
9871          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9872     {
9873       change->actual_trigger_element = trigger_element;
9874       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9875       change->actual_trigger_side = trigger_side;
9876       change->actual_trigger_ce_value = CustomValue[x][y];
9877       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9878
9879       /* special case: trigger element not at (x,y) position for some events */
9880       if (check_trigger_element)
9881       {
9882         static struct
9883         {
9884           int dx, dy;
9885         } move_xy[] =
9886           {
9887             {  0,  0 },
9888             { -1,  0 },
9889             { +1,  0 },
9890             {  0,  0 },
9891             {  0, -1 },
9892             {  0,  0 }, { 0, 0 }, { 0, 0 },
9893             {  0, +1 }
9894           };
9895
9896         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9897         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9898
9899         change->actual_trigger_ce_value = CustomValue[xx][yy];
9900         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9901       }
9902
9903       if (change->can_change && !change_done)
9904       {
9905         ChangeDelay[x][y] = 1;
9906         ChangeEvent[x][y] = trigger_event;
9907
9908         HandleElementChange(x, y, p);
9909
9910         change_done = TRUE;
9911       }
9912 #if USE_NEW_DELAYED_ACTION
9913       else if (change->has_action)
9914       {
9915         ExecuteCustomElementAction(x, y, element, p);
9916         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9917       }
9918 #else
9919       if (change->has_action)
9920       {
9921         ExecuteCustomElementAction(x, y, element, p);
9922         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9923       }
9924 #endif
9925     }
9926   }
9927
9928   RECURSION_LOOP_DETECTION_END();
9929
9930   return change_done;
9931 }
9932
9933 static void PlayPlayerSound(struct PlayerInfo *player)
9934 {
9935   int jx = player->jx, jy = player->jy;
9936   int sound_element = player->artwork_element;
9937   int last_action = player->last_action_waiting;
9938   int action = player->action_waiting;
9939
9940   if (player->is_waiting)
9941   {
9942     if (action != last_action)
9943       PlayLevelSoundElementAction(jx, jy, sound_element, action);
9944     else
9945       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9946   }
9947   else
9948   {
9949     if (action != last_action)
9950       StopSound(element_info[sound_element].sound[last_action]);
9951
9952     if (last_action == ACTION_SLEEPING)
9953       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9954   }
9955 }
9956
9957 static void PlayAllPlayersSound()
9958 {
9959   int i;
9960
9961   for (i = 0; i < MAX_PLAYERS; i++)
9962     if (stored_player[i].active)
9963       PlayPlayerSound(&stored_player[i]);
9964 }
9965
9966 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9967 {
9968   boolean last_waiting = player->is_waiting;
9969   int move_dir = player->MovDir;
9970
9971   player->dir_waiting = move_dir;
9972   player->last_action_waiting = player->action_waiting;
9973
9974   if (is_waiting)
9975   {
9976     if (!last_waiting)          /* not waiting -> waiting */
9977     {
9978       player->is_waiting = TRUE;
9979
9980       player->frame_counter_bored =
9981         FrameCounter +
9982         game.player_boring_delay_fixed +
9983         GetSimpleRandom(game.player_boring_delay_random);
9984       player->frame_counter_sleeping =
9985         FrameCounter +
9986         game.player_sleeping_delay_fixed +
9987         GetSimpleRandom(game.player_sleeping_delay_random);
9988
9989       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9990     }
9991
9992     if (game.player_sleeping_delay_fixed +
9993         game.player_sleeping_delay_random > 0 &&
9994         player->anim_delay_counter == 0 &&
9995         player->post_delay_counter == 0 &&
9996         FrameCounter >= player->frame_counter_sleeping)
9997       player->is_sleeping = TRUE;
9998     else if (game.player_boring_delay_fixed +
9999              game.player_boring_delay_random > 0 &&
10000              FrameCounter >= player->frame_counter_bored)
10001       player->is_bored = TRUE;
10002
10003     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10004                               player->is_bored ? ACTION_BORING :
10005                               ACTION_WAITING);
10006
10007     if (player->is_sleeping && player->use_murphy)
10008     {
10009       /* special case for sleeping Murphy when leaning against non-free tile */
10010
10011       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10012           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10013            !IS_MOVING(player->jx - 1, player->jy)))
10014         move_dir = MV_LEFT;
10015       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10016                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10017                 !IS_MOVING(player->jx + 1, player->jy)))
10018         move_dir = MV_RIGHT;
10019       else
10020         player->is_sleeping = FALSE;
10021
10022       player->dir_waiting = move_dir;
10023     }
10024
10025     if (player->is_sleeping)
10026     {
10027       if (player->num_special_action_sleeping > 0)
10028       {
10029         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10030         {
10031           int last_special_action = player->special_action_sleeping;
10032           int num_special_action = player->num_special_action_sleeping;
10033           int special_action =
10034             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10035              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10036              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10037              last_special_action + 1 : ACTION_SLEEPING);
10038           int special_graphic =
10039             el_act_dir2img(player->artwork_element, special_action, move_dir);
10040
10041           player->anim_delay_counter =
10042             graphic_info[special_graphic].anim_delay_fixed +
10043             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10044           player->post_delay_counter =
10045             graphic_info[special_graphic].post_delay_fixed +
10046             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10047
10048           player->special_action_sleeping = special_action;
10049         }
10050
10051         if (player->anim_delay_counter > 0)
10052         {
10053           player->action_waiting = player->special_action_sleeping;
10054           player->anim_delay_counter--;
10055         }
10056         else if (player->post_delay_counter > 0)
10057         {
10058           player->post_delay_counter--;
10059         }
10060       }
10061     }
10062     else if (player->is_bored)
10063     {
10064       if (player->num_special_action_bored > 0)
10065       {
10066         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10067         {
10068           int special_action =
10069             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10070           int special_graphic =
10071             el_act_dir2img(player->artwork_element, special_action, move_dir);
10072
10073           player->anim_delay_counter =
10074             graphic_info[special_graphic].anim_delay_fixed +
10075             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10076           player->post_delay_counter =
10077             graphic_info[special_graphic].post_delay_fixed +
10078             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10079
10080           player->special_action_bored = special_action;
10081         }
10082
10083         if (player->anim_delay_counter > 0)
10084         {
10085           player->action_waiting = player->special_action_bored;
10086           player->anim_delay_counter--;
10087         }
10088         else if (player->post_delay_counter > 0)
10089         {
10090           player->post_delay_counter--;
10091         }
10092       }
10093     }
10094   }
10095   else if (last_waiting)        /* waiting -> not waiting */
10096   {
10097     player->is_waiting = FALSE;
10098     player->is_bored = FALSE;
10099     player->is_sleeping = FALSE;
10100
10101     player->frame_counter_bored = -1;
10102     player->frame_counter_sleeping = -1;
10103
10104     player->anim_delay_counter = 0;
10105     player->post_delay_counter = 0;
10106
10107     player->dir_waiting = player->MovDir;
10108     player->action_waiting = ACTION_DEFAULT;
10109
10110     player->special_action_bored = ACTION_DEFAULT;
10111     player->special_action_sleeping = ACTION_DEFAULT;
10112   }
10113 }
10114
10115 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10116 {
10117   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10118   int left      = player_action & JOY_LEFT;
10119   int right     = player_action & JOY_RIGHT;
10120   int up        = player_action & JOY_UP;
10121   int down      = player_action & JOY_DOWN;
10122   int button1   = player_action & JOY_BUTTON_1;
10123   int button2   = player_action & JOY_BUTTON_2;
10124   int dx        = (left ? -1 : right ? 1 : 0);
10125   int dy        = (up   ? -1 : down  ? 1 : 0);
10126
10127   if (!player->active || tape.pausing)
10128     return 0;
10129
10130   if (player_action)
10131   {
10132     if (button1)
10133       snapped = SnapField(player, dx, dy);
10134     else
10135     {
10136       if (button2)
10137         dropped = DropElement(player);
10138
10139       moved = MovePlayer(player, dx, dy);
10140     }
10141
10142     if (tape.single_step && tape.recording && !tape.pausing)
10143     {
10144       if (button1 || (dropped && !moved))
10145       {
10146         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10147         SnapField(player, 0, 0);                /* stop snapping */
10148       }
10149     }
10150
10151     SetPlayerWaiting(player, FALSE);
10152
10153     return player_action;
10154   }
10155   else
10156   {
10157     /* no actions for this player (no input at player's configured device) */
10158
10159     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10160     SnapField(player, 0, 0);
10161     CheckGravityMovementWhenNotMoving(player);
10162
10163     if (player->MovPos == 0)
10164       SetPlayerWaiting(player, TRUE);
10165
10166     if (player->MovPos == 0)    /* needed for tape.playing */
10167       player->is_moving = FALSE;
10168
10169     player->is_dropping = FALSE;
10170     player->is_dropping_pressed = FALSE;
10171     player->drop_pressed_delay = 0;
10172
10173     return 0;
10174   }
10175 }
10176
10177 static void CheckLevelTime()
10178 {
10179   int i;
10180
10181   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10182   {
10183     if (level.native_em_level->lev->home == 0)  /* all players at home */
10184     {
10185       PlayerWins(local_player);
10186
10187       AllPlayersGone = TRUE;
10188
10189       level.native_em_level->lev->home = -1;
10190     }
10191
10192     if (level.native_em_level->ply[0]->alive == 0 &&
10193         level.native_em_level->ply[1]->alive == 0 &&
10194         level.native_em_level->ply[2]->alive == 0 &&
10195         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10196       AllPlayersGone = TRUE;
10197   }
10198
10199   if (TimeFrames >= FRAMES_PER_SECOND)
10200   {
10201     TimeFrames = 0;
10202     TapeTime++;
10203
10204     for (i = 0; i < MAX_PLAYERS; i++)
10205     {
10206       struct PlayerInfo *player = &stored_player[i];
10207
10208       if (SHIELD_ON(player))
10209       {
10210         player->shield_normal_time_left--;
10211
10212         if (player->shield_deadly_time_left > 0)
10213           player->shield_deadly_time_left--;
10214       }
10215     }
10216
10217     if (!local_player->LevelSolved && !level.use_step_counter)
10218     {
10219       TimePlayed++;
10220
10221       if (TimeLeft > 0)
10222       {
10223         TimeLeft--;
10224
10225         if (TimeLeft <= 10 && setup.time_limit)
10226           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10227
10228         DrawGameValue_Time(TimeLeft);
10229
10230         if (!TimeLeft && setup.time_limit)
10231         {
10232           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10233             level.native_em_level->lev->killed_out_of_time = TRUE;
10234           else
10235             for (i = 0; i < MAX_PLAYERS; i++)
10236               KillPlayer(&stored_player[i]);
10237         }
10238       }
10239       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10240         DrawGameValue_Time(TimePlayed);
10241
10242       level.native_em_level->lev->time =
10243         (level.time == 0 ? TimePlayed : TimeLeft);
10244     }
10245
10246     if (tape.recording || tape.playing)
10247       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10248   }
10249 }
10250
10251 void AdvanceFrameAndPlayerCounters(int player_nr)
10252 {
10253   int i;
10254
10255   /* advance frame counters (global frame counter and time frame counter) */
10256   FrameCounter++;
10257   TimeFrames++;
10258
10259   /* advance player counters (counters for move delay, move animation etc.) */
10260   for (i = 0; i < MAX_PLAYERS; i++)
10261   {
10262     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10263     int move_delay_value = stored_player[i].move_delay_value;
10264     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10265
10266     if (!advance_player_counters)       /* not all players may be affected */
10267       continue;
10268
10269 #if USE_NEW_PLAYER_ANIM
10270     if (move_frames == 0)       /* less than one move per game frame */
10271     {
10272       int stepsize = TILEX / move_delay_value;
10273       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10274       int count = (stored_player[i].is_moving ?
10275                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10276
10277       if (count % delay == 0)
10278         move_frames = 1;
10279     }
10280 #endif
10281
10282     stored_player[i].Frame += move_frames;
10283
10284     if (stored_player[i].MovPos != 0)
10285       stored_player[i].StepFrame += move_frames;
10286
10287     if (stored_player[i].move_delay > 0)
10288       stored_player[i].move_delay--;
10289
10290     /* due to bugs in previous versions, counter must count up, not down */
10291     if (stored_player[i].push_delay != -1)
10292       stored_player[i].push_delay++;
10293
10294     if (stored_player[i].drop_delay > 0)
10295       stored_player[i].drop_delay--;
10296
10297     if (stored_player[i].is_dropping_pressed)
10298       stored_player[i].drop_pressed_delay++;
10299   }
10300 }
10301
10302 void StartGameActions(boolean init_network_game, boolean record_tape,
10303                       long random_seed)
10304 {
10305   unsigned long new_random_seed = InitRND(random_seed);
10306
10307   if (record_tape)
10308     TapeStartRecording(new_random_seed);
10309
10310 #if defined(NETWORK_AVALIABLE)
10311   if (init_network_game)
10312   {
10313     SendToServer_StartPlaying();
10314
10315     return;
10316   }
10317 #endif
10318
10319   InitGame();
10320 }
10321
10322 void GameActions()
10323 {
10324   static unsigned long game_frame_delay = 0;
10325   unsigned long game_frame_delay_value;
10326   byte *recorded_player_action;
10327   byte summarized_player_action = 0;
10328   byte tape_action[MAX_PLAYERS];
10329   int i;
10330
10331   /* detect endless loops, caused by custom element programming */
10332   if (recursion_loop_detected && recursion_loop_depth == 0)
10333   {
10334     char *message = getStringCat3("Internal Error ! Element ",
10335                                   EL_NAME(recursion_loop_element),
10336                                   " caused endless loop ! Quit the game ?");
10337
10338     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10339           EL_NAME(recursion_loop_element));
10340
10341     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10342
10343     recursion_loop_detected = FALSE;    /* if game should be continued */
10344
10345     free(message);
10346
10347     return;
10348   }
10349
10350   if (game.restart_level)
10351     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
10352
10353   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10354   {
10355     if (level.native_em_level->lev->home == 0)  /* all players at home */
10356     {
10357       PlayerWins(local_player);
10358
10359       AllPlayersGone = TRUE;
10360
10361       level.native_em_level->lev->home = -1;
10362     }
10363
10364     if (level.native_em_level->ply[0]->alive == 0 &&
10365         level.native_em_level->ply[1]->alive == 0 &&
10366         level.native_em_level->ply[2]->alive == 0 &&
10367         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10368       AllPlayersGone = TRUE;
10369   }
10370
10371   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10372     GameWon();
10373
10374   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10375     TapeStop();
10376
10377   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
10378     return;
10379
10380   game_frame_delay_value =
10381     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10382
10383   if (tape.playing && tape.warp_forward && !tape.pausing)
10384     game_frame_delay_value = 0;
10385
10386   /* ---------- main game synchronization point ---------- */
10387
10388   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10389
10390   if (network_playing && !network_player_action_received)
10391   {
10392     /* try to get network player actions in time */
10393
10394 #if defined(NETWORK_AVALIABLE)
10395     /* last chance to get network player actions without main loop delay */
10396     HandleNetworking();
10397 #endif
10398
10399     /* game was quit by network peer */
10400     if (game_status != GAME_MODE_PLAYING)
10401       return;
10402
10403     if (!network_player_action_received)
10404       return;           /* failed to get network player actions in time */
10405
10406     /* do not yet reset "network_player_action_received" (for tape.pausing) */
10407   }
10408
10409   if (tape.pausing)
10410     return;
10411
10412   /* at this point we know that we really continue executing the game */
10413
10414   network_player_action_received = FALSE;
10415
10416   /* when playing tape, read previously recorded player input from tape data */
10417   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
10418
10419 #if 1
10420   /* TapePlayAction() may return NULL when toggling to "pause before death" */
10421   if (tape.pausing)
10422     return;
10423 #endif
10424
10425   if (tape.set_centered_player)
10426   {
10427     game.centered_player_nr_next = tape.centered_player_nr_next;
10428     game.set_centered_player = TRUE;
10429   }
10430
10431   for (i = 0; i < MAX_PLAYERS; i++)
10432   {
10433     summarized_player_action |= stored_player[i].action;
10434
10435     if (!network_playing)
10436       stored_player[i].effective_action = stored_player[i].action;
10437   }
10438
10439 #if defined(NETWORK_AVALIABLE)
10440   if (network_playing)
10441     SendToServer_MovePlayer(summarized_player_action);
10442 #endif
10443
10444   if (!options.network && !setup.team_mode)
10445     local_player->effective_action = summarized_player_action;
10446
10447   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
10448   {
10449     for (i = 0; i < MAX_PLAYERS; i++)
10450       stored_player[i].effective_action =
10451         (i == game.centered_player_nr ? summarized_player_action : 0);
10452   }
10453
10454   if (recorded_player_action != NULL)
10455     for (i = 0; i < MAX_PLAYERS; i++)
10456       stored_player[i].effective_action = recorded_player_action[i];
10457
10458   for (i = 0; i < MAX_PLAYERS; i++)
10459   {
10460     tape_action[i] = stored_player[i].effective_action;
10461
10462     /* (this can only happen in the R'n'D game engine) */
10463     if (tape.recording && tape_action[i] && !tape.player_participates[i])
10464       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
10465   }
10466
10467   /* only record actions from input devices, but not programmed actions */
10468   if (tape.recording)
10469     TapeRecordAction(tape_action);
10470
10471   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10472   {
10473     GameActions_EM_Main();
10474   }
10475   else
10476   {
10477     GameActions_RND();
10478   }
10479 }
10480
10481 void GameActions_EM_Main()
10482 {
10483   byte effective_action[MAX_PLAYERS];
10484   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
10485   int i;
10486
10487   for (i = 0; i < MAX_PLAYERS; i++)
10488     effective_action[i] = stored_player[i].effective_action;
10489
10490   GameActions_EM(effective_action, warp_mode);
10491
10492   CheckLevelTime();
10493
10494   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
10495 }
10496
10497 void GameActions_RND()
10498 {
10499   int magic_wall_x = 0, magic_wall_y = 0;
10500   int i, x, y, element, graphic;
10501
10502   InitPlayfieldScanModeVars();
10503
10504 #if USE_ONE_MORE_CHANGE_PER_FRAME
10505   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10506   {
10507     SCAN_PLAYFIELD(x, y)
10508     {
10509       ChangeCount[x][y] = 0;
10510       ChangeEvent[x][y] = -1;
10511     }
10512   }
10513 #endif
10514
10515   if (game.set_centered_player)
10516   {
10517     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
10518
10519     /* switching to "all players" only possible if all players fit to screen */
10520     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
10521     {
10522       game.centered_player_nr_next = game.centered_player_nr;
10523       game.set_centered_player = FALSE;
10524     }
10525
10526     /* do not switch focus to non-existing (or non-active) player */
10527     if (game.centered_player_nr_next >= 0 &&
10528         !stored_player[game.centered_player_nr_next].active)
10529     {
10530       game.centered_player_nr_next = game.centered_player_nr;
10531       game.set_centered_player = FALSE;
10532     }
10533   }
10534
10535   if (game.set_centered_player &&
10536       ScreenMovPos == 0)        /* screen currently aligned at tile position */
10537   {
10538     int sx, sy;
10539
10540     if (game.centered_player_nr_next == -1)
10541     {
10542       setScreenCenteredToAllPlayers(&sx, &sy);
10543     }
10544     else
10545     {
10546       sx = stored_player[game.centered_player_nr_next].jx;
10547       sy = stored_player[game.centered_player_nr_next].jy;
10548     }
10549
10550     game.centered_player_nr = game.centered_player_nr_next;
10551     game.set_centered_player = FALSE;
10552
10553     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
10554     DrawGameDoorValues();
10555   }
10556
10557   for (i = 0; i < MAX_PLAYERS; i++)
10558   {
10559     int actual_player_action = stored_player[i].effective_action;
10560
10561 #if 1
10562     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
10563        - rnd_equinox_tetrachloride 048
10564        - rnd_equinox_tetrachloride_ii 096
10565        - rnd_emanuel_schmieg 002
10566        - doctor_sloan_ww 001, 020
10567     */
10568     if (stored_player[i].MovPos == 0)
10569       CheckGravityMovement(&stored_player[i]);
10570 #endif
10571
10572     /* overwrite programmed action with tape action */
10573     if (stored_player[i].programmed_action)
10574       actual_player_action = stored_player[i].programmed_action;
10575
10576     PlayerActions(&stored_player[i], actual_player_action);
10577
10578     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
10579   }
10580
10581   ScrollScreen(NULL, SCROLL_GO_ON);
10582
10583   /* for backwards compatibility, the following code emulates a fixed bug that
10584      occured when pushing elements (causing elements that just made their last
10585      pushing step to already (if possible) make their first falling step in the
10586      same game frame, which is bad); this code is also needed to use the famous
10587      "spring push bug" which is used in older levels and might be wanted to be
10588      used also in newer levels, but in this case the buggy pushing code is only
10589      affecting the "spring" element and no other elements */
10590
10591   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
10592   {
10593     for (i = 0; i < MAX_PLAYERS; i++)
10594     {
10595       struct PlayerInfo *player = &stored_player[i];
10596       int x = player->jx;
10597       int y = player->jy;
10598
10599       if (player->active && player->is_pushing && player->is_moving &&
10600           IS_MOVING(x, y) &&
10601           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
10602            Feld[x][y] == EL_SPRING))
10603       {
10604         ContinueMoving(x, y);
10605
10606         /* continue moving after pushing (this is actually a bug) */
10607         if (!IS_MOVING(x, y))
10608           Stop[x][y] = FALSE;
10609       }
10610     }
10611   }
10612
10613 #if 0
10614   debug_print_timestamp(0, "start main loop profiling");
10615 #endif
10616
10617   SCAN_PLAYFIELD(x, y)
10618   {
10619     ChangeCount[x][y] = 0;
10620     ChangeEvent[x][y] = -1;
10621
10622     /* this must be handled before main playfield loop */
10623     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
10624     {
10625       MovDelay[x][y]--;
10626       if (MovDelay[x][y] <= 0)
10627         RemoveField(x, y);
10628     }
10629
10630 #if USE_NEW_SNAP_DELAY
10631     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
10632     {
10633       MovDelay[x][y]--;
10634       if (MovDelay[x][y] <= 0)
10635       {
10636         RemoveField(x, y);
10637         DrawLevelField(x, y);
10638
10639         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
10640       }
10641     }
10642 #endif
10643
10644 #if DEBUG
10645     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
10646     {
10647       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
10648       printf("GameActions(): This should never happen!\n");
10649
10650       ChangePage[x][y] = -1;
10651     }
10652 #endif
10653
10654     Stop[x][y] = FALSE;
10655     if (WasJustMoving[x][y] > 0)
10656       WasJustMoving[x][y]--;
10657     if (WasJustFalling[x][y] > 0)
10658       WasJustFalling[x][y]--;
10659     if (CheckCollision[x][y] > 0)
10660       CheckCollision[x][y]--;
10661     if (CheckImpact[x][y] > 0)
10662       CheckImpact[x][y]--;
10663
10664     GfxFrame[x][y]++;
10665
10666     /* reset finished pushing action (not done in ContinueMoving() to allow
10667        continuous pushing animation for elements with zero push delay) */
10668     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
10669     {
10670       ResetGfxAnimation(x, y);
10671       DrawLevelField(x, y);
10672     }
10673
10674 #if DEBUG
10675     if (IS_BLOCKED(x, y))
10676     {
10677       int oldx, oldy;
10678
10679       Blocked2Moving(x, y, &oldx, &oldy);
10680       if (!IS_MOVING(oldx, oldy))
10681       {
10682         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
10683         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
10684         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
10685         printf("GameActions(): This should never happen!\n");
10686       }
10687     }
10688 #endif
10689   }
10690
10691 #if 0
10692   debug_print_timestamp(0, "- time for pre-main loop:");
10693 #endif
10694
10695 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
10696   SCAN_PLAYFIELD(x, y)
10697   {
10698     element = Feld[x][y];
10699     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10700
10701 #if 1
10702     {
10703 #if 1
10704       int element2 = element;
10705       int graphic2 = graphic;
10706 #else
10707       int element2 = Feld[x][y];
10708       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
10709 #endif
10710       int last_gfx_frame = GfxFrame[x][y];
10711
10712       if (graphic_info[graphic2].anim_global_sync)
10713         GfxFrame[x][y] = FrameCounter;
10714       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
10715         GfxFrame[x][y] = CustomValue[x][y];
10716       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
10717         GfxFrame[x][y] = element_info[element2].collect_score;
10718       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
10719         GfxFrame[x][y] = ChangeDelay[x][y];
10720
10721       if (redraw && GfxFrame[x][y] != last_gfx_frame)
10722         DrawLevelGraphicAnimation(x, y, graphic2);
10723     }
10724 #else
10725     ResetGfxFrame(x, y, TRUE);
10726 #endif
10727
10728 #if 1
10729     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10730         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10731       ResetRandomAnimationValue(x, y);
10732 #endif
10733
10734 #if 1
10735     SetRandomAnimationValue(x, y);
10736 #endif
10737
10738 #if 1
10739     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10740 #endif
10741   }
10742 #endif  // -------------------- !!! TEST ONLY !!! --------------------
10743
10744 #if 0
10745   debug_print_timestamp(0, "- time for TEST loop:     -->");
10746 #endif
10747
10748   SCAN_PLAYFIELD(x, y)
10749   {
10750     element = Feld[x][y];
10751     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10752
10753     ResetGfxFrame(x, y, TRUE);
10754
10755     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10756         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10757       ResetRandomAnimationValue(x, y);
10758
10759     SetRandomAnimationValue(x, y);
10760
10761     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10762
10763     if (IS_INACTIVE(element))
10764     {
10765       if (IS_ANIMATED(graphic))
10766         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10767
10768       continue;
10769     }
10770
10771     /* this may take place after moving, so 'element' may have changed */
10772     if (IS_CHANGING(x, y) &&
10773         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10774     {
10775       int page = element_info[element].event_page_nr[CE_DELAY];
10776
10777 #if 1
10778       HandleElementChange(x, y, page);
10779 #else
10780       if (CAN_CHANGE(element))
10781         HandleElementChange(x, y, page);
10782
10783       if (HAS_ACTION(element))
10784         ExecuteCustomElementAction(x, y, element, page);
10785 #endif
10786
10787       element = Feld[x][y];
10788       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10789     }
10790
10791 #if 0   // ---------------------------------------------------------------------
10792
10793     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10794     {
10795       StartMoving(x, y);
10796
10797       element = Feld[x][y];
10798       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10799
10800       if (IS_ANIMATED(graphic) &&
10801           !IS_MOVING(x, y) &&
10802           !Stop[x][y])
10803         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10804
10805       if (IS_GEM(element) || element == EL_SP_INFOTRON)
10806         DrawTwinkleOnField(x, y);
10807     }
10808     else if (IS_MOVING(x, y))
10809       ContinueMoving(x, y);
10810     else
10811     {
10812       switch (element)
10813       {
10814         case EL_ACID:
10815         case EL_EXIT_OPEN:
10816         case EL_EM_EXIT_OPEN:
10817         case EL_SP_EXIT_OPEN:
10818         case EL_STEEL_EXIT_OPEN:
10819         case EL_EM_STEEL_EXIT_OPEN:
10820         case EL_SP_TERMINAL:
10821         case EL_SP_TERMINAL_ACTIVE:
10822         case EL_EXTRA_TIME:
10823         case EL_SHIELD_NORMAL:
10824         case EL_SHIELD_DEADLY:
10825           if (IS_ANIMATED(graphic))
10826             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10827           break;
10828
10829         case EL_DYNAMITE_ACTIVE:
10830         case EL_EM_DYNAMITE_ACTIVE:
10831         case EL_DYNABOMB_PLAYER_1_ACTIVE:
10832         case EL_DYNABOMB_PLAYER_2_ACTIVE:
10833         case EL_DYNABOMB_PLAYER_3_ACTIVE:
10834         case EL_DYNABOMB_PLAYER_4_ACTIVE:
10835         case EL_SP_DISK_RED_ACTIVE:
10836           CheckDynamite(x, y);
10837           break;
10838
10839         case EL_AMOEBA_GROWING:
10840           AmoebeWaechst(x, y);
10841           break;
10842
10843         case EL_AMOEBA_SHRINKING:
10844           AmoebaDisappearing(x, y);
10845           break;
10846
10847 #if !USE_NEW_AMOEBA_CODE
10848         case EL_AMOEBA_WET:
10849         case EL_AMOEBA_DRY:
10850         case EL_AMOEBA_FULL:
10851         case EL_BD_AMOEBA:
10852         case EL_EMC_DRIPPER:
10853           AmoebeAbleger(x, y);
10854           break;
10855 #endif
10856
10857         case EL_GAME_OF_LIFE:
10858         case EL_BIOMAZE:
10859           Life(x, y);
10860           break;
10861
10862         case EL_EXIT_CLOSED:
10863           CheckExit(x, y);
10864           break;
10865
10866         case EL_EM_EXIT_CLOSED:
10867           CheckExitEM(x, y);
10868           break;
10869
10870         case EL_STEEL_EXIT_CLOSED:
10871           CheckExitSteel(x, y);
10872           break;
10873
10874         case EL_EM_STEEL_EXIT_CLOSED:
10875           CheckExitSteelEM(x, y);
10876           break;
10877
10878         case EL_SP_EXIT_CLOSED:
10879           CheckExitSP(x, y);
10880           break;
10881
10882         case EL_EXPANDABLE_WALL_GROWING:
10883         case EL_EXPANDABLE_STEELWALL_GROWING:
10884           MauerWaechst(x, y);
10885           break;
10886
10887         case EL_EXPANDABLE_WALL:
10888         case EL_EXPANDABLE_WALL_HORIZONTAL:
10889         case EL_EXPANDABLE_WALL_VERTICAL:
10890         case EL_EXPANDABLE_WALL_ANY:
10891         case EL_BD_EXPANDABLE_WALL:
10892           MauerAbleger(x, y);
10893           break;
10894
10895         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
10896         case EL_EXPANDABLE_STEELWALL_VERTICAL:
10897         case EL_EXPANDABLE_STEELWALL_ANY:
10898           MauerAblegerStahl(x, y);
10899           break;
10900
10901         case EL_FLAMES:
10902           CheckForDragon(x, y);
10903           break;
10904
10905         case EL_EXPLOSION:
10906           break;
10907
10908         case EL_ELEMENT_SNAPPING:
10909         case EL_DIAGONAL_SHRINKING:
10910         case EL_DIAGONAL_GROWING:
10911         {
10912           graphic =
10913             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10914
10915           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10916           break;
10917         }
10918
10919         default:
10920           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10921             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10922           break;
10923       }
10924     }
10925
10926 #else   // ---------------------------------------------------------------------
10927
10928     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10929     {
10930       StartMoving(x, y);
10931
10932       element = Feld[x][y];
10933       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10934
10935       if (IS_ANIMATED(graphic) &&
10936           !IS_MOVING(x, y) &&
10937           !Stop[x][y])
10938         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10939
10940       if (IS_GEM(element) || element == EL_SP_INFOTRON)
10941         DrawTwinkleOnField(x, y);
10942     }
10943     else if ((element == EL_ACID ||
10944               element == EL_EXIT_OPEN ||
10945               element == EL_EM_EXIT_OPEN ||
10946               element == EL_SP_EXIT_OPEN ||
10947               element == EL_STEEL_EXIT_OPEN ||
10948               element == EL_EM_STEEL_EXIT_OPEN ||
10949               element == EL_SP_TERMINAL ||
10950               element == EL_SP_TERMINAL_ACTIVE ||
10951               element == EL_EXTRA_TIME ||
10952               element == EL_SHIELD_NORMAL ||
10953               element == EL_SHIELD_DEADLY) &&
10954              IS_ANIMATED(graphic))
10955       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10956     else if (IS_MOVING(x, y))
10957       ContinueMoving(x, y);
10958     else if (IS_ACTIVE_BOMB(element))
10959       CheckDynamite(x, y);
10960     else if (element == EL_AMOEBA_GROWING)
10961       AmoebeWaechst(x, y);
10962     else if (element == EL_AMOEBA_SHRINKING)
10963       AmoebaDisappearing(x, y);
10964
10965 #if !USE_NEW_AMOEBA_CODE
10966     else if (IS_AMOEBALIVE(element))
10967       AmoebeAbleger(x, y);
10968 #endif
10969
10970     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10971       Life(x, y);
10972     else if (element == EL_EXIT_CLOSED)
10973       CheckExit(x, y);
10974     else if (element == EL_EM_EXIT_CLOSED)
10975       CheckExitEM(x, y);
10976     else if (element == EL_STEEL_EXIT_CLOSED)
10977       CheckExitSteel(x, y);
10978     else if (element == EL_EM_STEEL_EXIT_CLOSED)
10979       CheckExitSteelEM(x, y);
10980     else if (element == EL_SP_EXIT_CLOSED)
10981       CheckExitSP(x, y);
10982     else if (element == EL_EXPANDABLE_WALL_GROWING ||
10983              element == EL_EXPANDABLE_STEELWALL_GROWING)
10984       MauerWaechst(x, y);
10985     else if (element == EL_EXPANDABLE_WALL ||
10986              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10987              element == EL_EXPANDABLE_WALL_VERTICAL ||
10988              element == EL_EXPANDABLE_WALL_ANY ||
10989              element == EL_BD_EXPANDABLE_WALL)
10990       MauerAbleger(x, y);
10991     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10992              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10993              element == EL_EXPANDABLE_STEELWALL_ANY)
10994       MauerAblegerStahl(x, y);
10995     else if (element == EL_FLAMES)
10996       CheckForDragon(x, y);
10997     else if (element == EL_EXPLOSION)
10998       ; /* drawing of correct explosion animation is handled separately */
10999     else if (element == EL_ELEMENT_SNAPPING ||
11000              element == EL_DIAGONAL_SHRINKING ||
11001              element == EL_DIAGONAL_GROWING)
11002     {
11003       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11004
11005       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11006     }
11007     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11008       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11009
11010 #endif  // ---------------------------------------------------------------------
11011
11012     if (IS_BELT_ACTIVE(element))
11013       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11014
11015     if (game.magic_wall_active)
11016     {
11017       int jx = local_player->jx, jy = local_player->jy;
11018
11019       /* play the element sound at the position nearest to the player */
11020       if ((element == EL_MAGIC_WALL_FULL ||
11021            element == EL_MAGIC_WALL_ACTIVE ||
11022            element == EL_MAGIC_WALL_EMPTYING ||
11023            element == EL_BD_MAGIC_WALL_FULL ||
11024            element == EL_BD_MAGIC_WALL_ACTIVE ||
11025            element == EL_BD_MAGIC_WALL_EMPTYING ||
11026            element == EL_DC_MAGIC_WALL_FULL ||
11027            element == EL_DC_MAGIC_WALL_ACTIVE ||
11028            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11029           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11030       {
11031         magic_wall_x = x;
11032         magic_wall_y = y;
11033       }
11034     }
11035   }
11036
11037 #if 0
11038   debug_print_timestamp(0, "- time for MAIN loop:     -->");
11039 #endif
11040
11041 #if USE_NEW_AMOEBA_CODE
11042   /* new experimental amoeba growth stuff */
11043   if (!(FrameCounter % 8))
11044   {
11045     static unsigned long random = 1684108901;
11046
11047     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11048     {
11049       x = RND(lev_fieldx);
11050       y = RND(lev_fieldy);
11051       element = Feld[x][y];
11052
11053       if (!IS_PLAYER(x,y) &&
11054           (element == EL_EMPTY ||
11055            CAN_GROW_INTO(element) ||
11056            element == EL_QUICKSAND_EMPTY ||
11057            element == EL_QUICKSAND_FAST_EMPTY ||
11058            element == EL_ACID_SPLASH_LEFT ||
11059            element == EL_ACID_SPLASH_RIGHT))
11060       {
11061         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11062             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11063             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11064             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11065           Feld[x][y] = EL_AMOEBA_DROP;
11066       }
11067
11068       random = random * 129 + 1;
11069     }
11070   }
11071 #endif
11072
11073 #if 0
11074   if (game.explosions_delayed)
11075 #endif
11076   {
11077     game.explosions_delayed = FALSE;
11078
11079     SCAN_PLAYFIELD(x, y)
11080     {
11081       element = Feld[x][y];
11082
11083       if (ExplodeField[x][y])
11084         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11085       else if (element == EL_EXPLOSION)
11086         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11087
11088       ExplodeField[x][y] = EX_TYPE_NONE;
11089     }
11090
11091     game.explosions_delayed = TRUE;
11092   }
11093
11094   if (game.magic_wall_active)
11095   {
11096     if (!(game.magic_wall_time_left % 4))
11097     {
11098       int element = Feld[magic_wall_x][magic_wall_y];
11099
11100       if (element == EL_BD_MAGIC_WALL_FULL ||
11101           element == EL_BD_MAGIC_WALL_ACTIVE ||
11102           element == EL_BD_MAGIC_WALL_EMPTYING)
11103         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11104       else if (element == EL_DC_MAGIC_WALL_FULL ||
11105                element == EL_DC_MAGIC_WALL_ACTIVE ||
11106                element == EL_DC_MAGIC_WALL_EMPTYING)
11107         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11108       else
11109         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11110     }
11111
11112     if (game.magic_wall_time_left > 0)
11113     {
11114       game.magic_wall_time_left--;
11115       if (!game.magic_wall_time_left)
11116       {
11117         SCAN_PLAYFIELD(x, y)
11118         {
11119           element = Feld[x][y];
11120
11121           if (element == EL_MAGIC_WALL_ACTIVE ||
11122               element == EL_MAGIC_WALL_FULL)
11123           {
11124             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11125             DrawLevelField(x, y);
11126           }
11127           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11128                    element == EL_BD_MAGIC_WALL_FULL)
11129           {
11130             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11131             DrawLevelField(x, y);
11132           }
11133           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11134                    element == EL_DC_MAGIC_WALL_FULL)
11135           {
11136             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11137             DrawLevelField(x, y);
11138           }
11139         }
11140
11141         game.magic_wall_active = FALSE;
11142       }
11143     }
11144   }
11145
11146   if (game.light_time_left > 0)
11147   {
11148     game.light_time_left--;
11149
11150     if (game.light_time_left == 0)
11151       RedrawAllLightSwitchesAndInvisibleElements();
11152   }
11153
11154   if (game.timegate_time_left > 0)
11155   {
11156     game.timegate_time_left--;
11157
11158     if (game.timegate_time_left == 0)
11159       CloseAllOpenTimegates();
11160   }
11161
11162   if (game.lenses_time_left > 0)
11163   {
11164     game.lenses_time_left--;
11165
11166     if (game.lenses_time_left == 0)
11167       RedrawAllInvisibleElementsForLenses();
11168   }
11169
11170   if (game.magnify_time_left > 0)
11171   {
11172     game.magnify_time_left--;
11173
11174     if (game.magnify_time_left == 0)
11175       RedrawAllInvisibleElementsForMagnifier();
11176   }
11177
11178   for (i = 0; i < MAX_PLAYERS; i++)
11179   {
11180     struct PlayerInfo *player = &stored_player[i];
11181
11182     if (SHIELD_ON(player))
11183     {
11184       if (player->shield_deadly_time_left)
11185         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11186       else if (player->shield_normal_time_left)
11187         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11188     }
11189   }
11190
11191   CheckLevelTime();
11192
11193   DrawAllPlayers();
11194   PlayAllPlayersSound();
11195
11196   if (options.debug)                    /* calculate frames per second */
11197   {
11198     static unsigned long fps_counter = 0;
11199     static int fps_frames = 0;
11200     unsigned long fps_delay_ms = Counter() - fps_counter;
11201
11202     fps_frames++;
11203
11204     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11205     {
11206       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11207
11208       fps_frames = 0;
11209       fps_counter = Counter();
11210     }
11211
11212     redraw_mask |= REDRAW_FPS;
11213   }
11214
11215   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11216
11217   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11218   {
11219     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11220
11221     local_player->show_envelope = 0;
11222   }
11223
11224 #if 0
11225   debug_print_timestamp(0, "stop main loop profiling ");
11226   printf("----------------------------------------------------------\n");
11227 #endif
11228
11229   /* use random number generator in every frame to make it less predictable */
11230   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11231     RND(1);
11232 }
11233
11234 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11235 {
11236   int min_x = x, min_y = y, max_x = x, max_y = y;
11237   int i;
11238
11239   for (i = 0; i < MAX_PLAYERS; i++)
11240   {
11241     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11242
11243     if (!stored_player[i].active || &stored_player[i] == player)
11244       continue;
11245
11246     min_x = MIN(min_x, jx);
11247     min_y = MIN(min_y, jy);
11248     max_x = MAX(max_x, jx);
11249     max_y = MAX(max_y, jy);
11250   }
11251
11252   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11253 }
11254
11255 static boolean AllPlayersInVisibleScreen()
11256 {
11257   int i;
11258
11259   for (i = 0; i < MAX_PLAYERS; i++)
11260   {
11261     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11262
11263     if (!stored_player[i].active)
11264       continue;
11265
11266     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11267       return FALSE;
11268   }
11269
11270   return TRUE;
11271 }
11272
11273 void ScrollLevel(int dx, int dy)
11274 {
11275 #if 1
11276   static Bitmap *bitmap_db_field2 = NULL;
11277   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11278   int x, y;
11279 #else
11280   int i, x, y;
11281 #endif
11282
11283 #if 0
11284   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11285   /* only horizontal XOR vertical scroll direction allowed */
11286   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11287     return;
11288 #endif
11289
11290 #if 1
11291   if (bitmap_db_field2 == NULL)
11292     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11293
11294   /* needed when blitting directly to same bitmap -- should not be needed with
11295      recent SDL libraries, but apparently does not work in 1.2.11 directly */
11296   BlitBitmap(drawto_field, bitmap_db_field2,
11297              FX + TILEX * (dx == -1) - softscroll_offset,
11298              FY + TILEY * (dy == -1) - softscroll_offset,
11299              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11300              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11301              FX + TILEX * (dx == 1) - softscroll_offset,
11302              FY + TILEY * (dy == 1) - softscroll_offset);
11303   BlitBitmap(bitmap_db_field2, drawto_field,
11304              FX + TILEX * (dx == 1) - softscroll_offset,
11305              FY + TILEY * (dy == 1) - softscroll_offset,
11306              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11307              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11308              FX + TILEX * (dx == 1) - softscroll_offset,
11309              FY + TILEY * (dy == 1) - softscroll_offset);
11310
11311 #else
11312
11313 #if 1
11314   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11315   int xsize = (BX2 - BX1 + 1);
11316   int ysize = (BY2 - BY1 + 1);
11317   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11318   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11319   int step  = (start < end ? +1 : -1);
11320
11321   for (i = start; i != end; i += step)
11322   {
11323     BlitBitmap(drawto_field, drawto_field,
11324                FX + TILEX * (dx != 0 ? i + step : 0),
11325                FY + TILEY * (dy != 0 ? i + step : 0),
11326                TILEX * (dx != 0 ? 1 : xsize),
11327                TILEY * (dy != 0 ? 1 : ysize),
11328                FX + TILEX * (dx != 0 ? i : 0),
11329                FY + TILEY * (dy != 0 ? i : 0));
11330   }
11331
11332 #else
11333
11334   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11335
11336   BlitBitmap(drawto_field, drawto_field,
11337              FX + TILEX * (dx == -1) - softscroll_offset,
11338              FY + TILEY * (dy == -1) - softscroll_offset,
11339              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11340              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11341              FX + TILEX * (dx == 1) - softscroll_offset,
11342              FY + TILEY * (dy == 1) - softscroll_offset);
11343 #endif
11344 #endif
11345
11346   if (dx != 0)
11347   {
11348     x = (dx == 1 ? BX1 : BX2);
11349     for (y = BY1; y <= BY2; y++)
11350       DrawScreenField(x, y);
11351   }
11352
11353   if (dy != 0)
11354   {
11355     y = (dy == 1 ? BY1 : BY2);
11356     for (x = BX1; x <= BX2; x++)
11357       DrawScreenField(x, y);
11358   }
11359
11360   redraw_mask |= REDRAW_FIELD;
11361 }
11362
11363 static boolean canFallDown(struct PlayerInfo *player)
11364 {
11365   int jx = player->jx, jy = player->jy;
11366
11367   return (IN_LEV_FIELD(jx, jy + 1) &&
11368           (IS_FREE(jx, jy + 1) ||
11369            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11370           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11371           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11372 }
11373
11374 static boolean canPassField(int x, int y, int move_dir)
11375 {
11376   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11377   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11378   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11379   int nextx = x + dx;
11380   int nexty = y + dy;
11381   int element = Feld[x][y];
11382
11383   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11384           !CAN_MOVE(element) &&
11385           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11386           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11387           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11388 }
11389
11390 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11391 {
11392   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11393   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11394   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11395   int newx = x + dx;
11396   int newy = y + dy;
11397
11398   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11399           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11400           (IS_DIGGABLE(Feld[newx][newy]) ||
11401            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11402            canPassField(newx, newy, move_dir)));
11403 }
11404
11405 static void CheckGravityMovement(struct PlayerInfo *player)
11406 {
11407 #if USE_PLAYER_GRAVITY
11408   if (player->gravity && !player->programmed_action)
11409 #else
11410   if (game.gravity && !player->programmed_action)
11411 #endif
11412   {
11413     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11414     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11415     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11416     int jx = player->jx, jy = player->jy;
11417     boolean player_is_moving_to_valid_field =
11418       (!player_is_snapping &&
11419        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11420         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11421     boolean player_can_fall_down = canFallDown(player);
11422
11423     if (player_can_fall_down &&
11424         !player_is_moving_to_valid_field)
11425       player->programmed_action = MV_DOWN;
11426   }
11427 }
11428
11429 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11430 {
11431   return CheckGravityMovement(player);
11432
11433 #if USE_PLAYER_GRAVITY
11434   if (player->gravity && !player->programmed_action)
11435 #else
11436   if (game.gravity && !player->programmed_action)
11437 #endif
11438   {
11439     int jx = player->jx, jy = player->jy;
11440     boolean field_under_player_is_free =
11441       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11442     boolean player_is_standing_on_valid_field =
11443       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11444        (IS_WALKABLE(Feld[jx][jy]) &&
11445         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11446
11447     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11448       player->programmed_action = MV_DOWN;
11449   }
11450 }
11451
11452 /*
11453   MovePlayerOneStep()
11454   -----------------------------------------------------------------------------
11455   dx, dy:               direction (non-diagonal) to try to move the player to
11456   real_dx, real_dy:     direction as read from input device (can be diagonal)
11457 */
11458
11459 boolean MovePlayerOneStep(struct PlayerInfo *player,
11460                           int dx, int dy, int real_dx, int real_dy)
11461 {
11462   int jx = player->jx, jy = player->jy;
11463   int new_jx = jx + dx, new_jy = jy + dy;
11464 #if !USE_FIXED_DONT_RUN_INTO
11465   int element;
11466 #endif
11467   int can_move;
11468   boolean player_can_move = !player->cannot_move;
11469
11470   if (!player->active || (!dx && !dy))
11471     return MP_NO_ACTION;
11472
11473   player->MovDir = (dx < 0 ? MV_LEFT :
11474                     dx > 0 ? MV_RIGHT :
11475                     dy < 0 ? MV_UP :
11476                     dy > 0 ? MV_DOWN :  MV_NONE);
11477
11478   if (!IN_LEV_FIELD(new_jx, new_jy))
11479     return MP_NO_ACTION;
11480
11481   if (!player_can_move)
11482   {
11483     if (player->MovPos == 0)
11484     {
11485       player->is_moving = FALSE;
11486       player->is_digging = FALSE;
11487       player->is_collecting = FALSE;
11488       player->is_snapping = FALSE;
11489       player->is_pushing = FALSE;
11490     }
11491   }
11492
11493 #if 1
11494   if (!options.network && game.centered_player_nr == -1 &&
11495       !AllPlayersInSight(player, new_jx, new_jy))
11496     return MP_NO_ACTION;
11497 #else
11498   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
11499     return MP_NO_ACTION;
11500 #endif
11501
11502 #if !USE_FIXED_DONT_RUN_INTO
11503   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
11504
11505   /* (moved to DigField()) */
11506   if (player_can_move && DONT_RUN_INTO(element))
11507   {
11508     if (element == EL_ACID && dx == 0 && dy == 1)
11509     {
11510       SplashAcid(new_jx, new_jy);
11511       Feld[jx][jy] = EL_PLAYER_1;
11512       InitMovingField(jx, jy, MV_DOWN);
11513       Store[jx][jy] = EL_ACID;
11514       ContinueMoving(jx, jy);
11515       BuryPlayer(player);
11516     }
11517     else
11518       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11519
11520     return MP_MOVING;
11521   }
11522 #endif
11523
11524   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11525   if (can_move != MP_MOVING)
11526     return can_move;
11527
11528   /* check if DigField() has caused relocation of the player */
11529   if (player->jx != jx || player->jy != jy)
11530     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11531
11532   StorePlayer[jx][jy] = 0;
11533   player->last_jx = jx;
11534   player->last_jy = jy;
11535   player->jx = new_jx;
11536   player->jy = new_jy;
11537   StorePlayer[new_jx][new_jy] = player->element_nr;
11538
11539   if (player->move_delay_value_next != -1)
11540   {
11541     player->move_delay_value = player->move_delay_value_next;
11542     player->move_delay_value_next = -1;
11543   }
11544
11545   player->MovPos =
11546     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11547
11548   player->step_counter++;
11549
11550   PlayerVisit[jx][jy] = FrameCounter;
11551
11552 #if USE_UFAST_PLAYER_EXIT_BUGFIX
11553   player->is_moving = TRUE;
11554 #endif
11555
11556 #if 1
11557   /* should better be called in MovePlayer(), but this breaks some tapes */
11558   ScrollPlayer(player, SCROLL_INIT);
11559 #endif
11560
11561   return MP_MOVING;
11562 }
11563
11564 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
11565 {
11566   int jx = player->jx, jy = player->jy;
11567   int old_jx = jx, old_jy = jy;
11568   int moved = MP_NO_ACTION;
11569
11570   if (!player->active)
11571     return FALSE;
11572
11573   if (!dx && !dy)
11574   {
11575     if (player->MovPos == 0)
11576     {
11577       player->is_moving = FALSE;
11578       player->is_digging = FALSE;
11579       player->is_collecting = FALSE;
11580       player->is_snapping = FALSE;
11581       player->is_pushing = FALSE;
11582     }
11583
11584     return FALSE;
11585   }
11586
11587   if (player->move_delay > 0)
11588     return FALSE;
11589
11590   player->move_delay = -1;              /* set to "uninitialized" value */
11591
11592   /* store if player is automatically moved to next field */
11593   player->is_auto_moving = (player->programmed_action != MV_NONE);
11594
11595   /* remove the last programmed player action */
11596   player->programmed_action = 0;
11597
11598   if (player->MovPos)
11599   {
11600     /* should only happen if pre-1.2 tape recordings are played */
11601     /* this is only for backward compatibility */
11602
11603     int original_move_delay_value = player->move_delay_value;
11604
11605 #if DEBUG
11606     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
11607            tape.counter);
11608 #endif
11609
11610     /* scroll remaining steps with finest movement resolution */
11611     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
11612
11613     while (player->MovPos)
11614     {
11615       ScrollPlayer(player, SCROLL_GO_ON);
11616       ScrollScreen(NULL, SCROLL_GO_ON);
11617
11618       AdvanceFrameAndPlayerCounters(player->index_nr);
11619
11620       DrawAllPlayers();
11621       BackToFront();
11622     }
11623
11624     player->move_delay_value = original_move_delay_value;
11625   }
11626
11627   player->is_active = FALSE;
11628
11629   if (player->last_move_dir & MV_HORIZONTAL)
11630   {
11631     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
11632       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
11633   }
11634   else
11635   {
11636     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
11637       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
11638   }
11639
11640 #if USE_FIXED_BORDER_RUNNING_GFX
11641   if (!moved && !player->is_active)
11642   {
11643     player->is_moving = FALSE;
11644     player->is_digging = FALSE;
11645     player->is_collecting = FALSE;
11646     player->is_snapping = FALSE;
11647     player->is_pushing = FALSE;
11648   }
11649 #endif
11650
11651   jx = player->jx;
11652   jy = player->jy;
11653
11654 #if 1
11655   if (moved & MP_MOVING && !ScreenMovPos &&
11656       (player->index_nr == game.centered_player_nr ||
11657        game.centered_player_nr == -1))
11658 #else
11659   if (moved & MP_MOVING && !ScreenMovPos &&
11660       (player == local_player || !options.network))
11661 #endif
11662   {
11663     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
11664     int offset = (setup.scroll_delay ? 3 : 0);
11665
11666     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11667     {
11668       /* actual player has left the screen -- scroll in that direction */
11669       if (jx != old_jx)         /* player has moved horizontally */
11670         scroll_x += (jx - old_jx);
11671       else                      /* player has moved vertically */
11672         scroll_y += (jy - old_jy);
11673     }
11674     else
11675     {
11676       if (jx != old_jx)         /* player has moved horizontally */
11677       {
11678         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
11679             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
11680           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
11681
11682         /* don't scroll over playfield boundaries */
11683         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
11684           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
11685
11686         /* don't scroll more than one field at a time */
11687         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
11688
11689         /* don't scroll against the player's moving direction */
11690         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
11691             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
11692           scroll_x = old_scroll_x;
11693       }
11694       else                      /* player has moved vertically */
11695       {
11696         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
11697             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
11698           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
11699
11700         /* don't scroll over playfield boundaries */
11701         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
11702           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
11703
11704         /* don't scroll more than one field at a time */
11705         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
11706
11707         /* don't scroll against the player's moving direction */
11708         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
11709             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
11710           scroll_y = old_scroll_y;
11711       }
11712     }
11713
11714     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
11715     {
11716 #if 1
11717       if (!options.network && game.centered_player_nr == -1 &&
11718           !AllPlayersInVisibleScreen())
11719       {
11720         scroll_x = old_scroll_x;
11721         scroll_y = old_scroll_y;
11722       }
11723       else
11724 #else
11725       if (!options.network && !AllPlayersInVisibleScreen())
11726       {
11727         scroll_x = old_scroll_x;
11728         scroll_y = old_scroll_y;
11729       }
11730       else
11731 #endif
11732       {
11733         ScrollScreen(player, SCROLL_INIT);
11734         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
11735       }
11736     }
11737   }
11738
11739   player->StepFrame = 0;
11740
11741   if (moved & MP_MOVING)
11742   {
11743     if (old_jx != jx && old_jy == jy)
11744       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
11745     else if (old_jx == jx && old_jy != jy)
11746       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
11747
11748     DrawLevelField(jx, jy);     /* for "crumbled sand" */
11749
11750     player->last_move_dir = player->MovDir;
11751     player->is_moving = TRUE;
11752     player->is_snapping = FALSE;
11753     player->is_switching = FALSE;
11754     player->is_dropping = FALSE;
11755     player->is_dropping_pressed = FALSE;
11756     player->drop_pressed_delay = 0;
11757
11758 #if 0
11759     /* should better be called here than above, but this breaks some tapes */
11760     ScrollPlayer(player, SCROLL_INIT);
11761 #endif
11762   }
11763   else
11764   {
11765     CheckGravityMovementWhenNotMoving(player);
11766
11767     player->is_moving = FALSE;
11768
11769     /* at this point, the player is allowed to move, but cannot move right now
11770        (e.g. because of something blocking the way) -- ensure that the player
11771        is also allowed to move in the next frame (in old versions before 3.1.1,
11772        the player was forced to wait again for eight frames before next try) */
11773
11774     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11775       player->move_delay = 0;   /* allow direct movement in the next frame */
11776   }
11777
11778   if (player->move_delay == -1)         /* not yet initialized by DigField() */
11779     player->move_delay = player->move_delay_value;
11780
11781   if (game.engine_version < VERSION_IDENT(3,0,7,0))
11782   {
11783     TestIfPlayerTouchesBadThing(jx, jy);
11784     TestIfPlayerTouchesCustomElement(jx, jy);
11785   }
11786
11787   if (!player->active)
11788     RemovePlayer(player);
11789
11790   return moved;
11791 }
11792
11793 void ScrollPlayer(struct PlayerInfo *player, int mode)
11794 {
11795   int jx = player->jx, jy = player->jy;
11796   int last_jx = player->last_jx, last_jy = player->last_jy;
11797   int move_stepsize = TILEX / player->move_delay_value;
11798
11799 #if USE_NEW_PLAYER_SPEED
11800   if (!player->active)
11801     return;
11802
11803   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
11804     return;
11805 #else
11806   if (!player->active || player->MovPos == 0)
11807     return;
11808 #endif
11809
11810   if (mode == SCROLL_INIT)
11811   {
11812     player->actual_frame_counter = FrameCounter;
11813     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11814
11815     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
11816         Feld[last_jx][last_jy] == EL_EMPTY)
11817     {
11818       int last_field_block_delay = 0;   /* start with no blocking at all */
11819       int block_delay_adjustment = player->block_delay_adjustment;
11820
11821       /* if player blocks last field, add delay for exactly one move */
11822       if (player->block_last_field)
11823       {
11824         last_field_block_delay += player->move_delay_value;
11825
11826         /* when blocking enabled, prevent moving up despite gravity */
11827 #if USE_PLAYER_GRAVITY
11828         if (player->gravity && player->MovDir == MV_UP)
11829           block_delay_adjustment = -1;
11830 #else
11831         if (game.gravity && player->MovDir == MV_UP)
11832           block_delay_adjustment = -1;
11833 #endif
11834       }
11835
11836       /* add block delay adjustment (also possible when not blocking) */
11837       last_field_block_delay += block_delay_adjustment;
11838
11839       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
11840       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
11841     }
11842
11843 #if USE_NEW_PLAYER_SPEED
11844     if (player->MovPos != 0)    /* player has not yet reached destination */
11845       return;
11846 #else
11847     return;
11848 #endif
11849   }
11850   else if (!FrameReached(&player->actual_frame_counter, 1))
11851     return;
11852
11853 #if USE_NEW_PLAYER_SPEED
11854   if (player->MovPos != 0)
11855   {
11856     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11857     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11858
11859     /* before DrawPlayer() to draw correct player graphic for this case */
11860     if (player->MovPos == 0)
11861       CheckGravityMovement(player);
11862   }
11863 #else
11864   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11865   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11866
11867   /* before DrawPlayer() to draw correct player graphic for this case */
11868   if (player->MovPos == 0)
11869     CheckGravityMovement(player);
11870 #endif
11871
11872   if (player->MovPos == 0)      /* player reached destination field */
11873   {
11874     if (player->move_delay_reset_counter > 0)
11875     {
11876       player->move_delay_reset_counter--;
11877
11878       if (player->move_delay_reset_counter == 0)
11879       {
11880         /* continue with normal speed after quickly moving through gate */
11881         HALVE_PLAYER_SPEED(player);
11882
11883         /* be able to make the next move without delay */
11884         player->move_delay = 0;
11885       }
11886     }
11887
11888     player->last_jx = jx;
11889     player->last_jy = jy;
11890
11891     if (Feld[jx][jy] == EL_EXIT_OPEN ||
11892         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
11893         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
11894         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
11895         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
11896         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
11897     {
11898       DrawPlayer(player);       /* needed here only to cleanup last field */
11899       RemovePlayer(player);
11900
11901       if (local_player->friends_still_needed == 0 ||
11902           IS_SP_ELEMENT(Feld[jx][jy]))
11903         PlayerWins(player);
11904     }
11905
11906     /* this breaks one level: "machine", level 000 */
11907     {
11908       int move_direction = player->MovDir;
11909       int enter_side = MV_DIR_OPPOSITE(move_direction);
11910       int leave_side = move_direction;
11911       int old_jx = last_jx;
11912       int old_jy = last_jy;
11913       int old_element = Feld[old_jx][old_jy];
11914       int new_element = Feld[jx][jy];
11915
11916       if (IS_CUSTOM_ELEMENT(old_element))
11917         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
11918                                    CE_LEFT_BY_PLAYER,
11919                                    player->index_bit, leave_side);
11920
11921       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
11922                                           CE_PLAYER_LEAVES_X,
11923                                           player->index_bit, leave_side);
11924
11925       if (IS_CUSTOM_ELEMENT(new_element))
11926         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
11927                                    player->index_bit, enter_side);
11928
11929       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
11930                                           CE_PLAYER_ENTERS_X,
11931                                           player->index_bit, enter_side);
11932
11933       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
11934                                         CE_MOVE_OF_X, move_direction);
11935     }
11936
11937     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11938     {
11939       TestIfPlayerTouchesBadThing(jx, jy);
11940       TestIfPlayerTouchesCustomElement(jx, jy);
11941
11942       /* needed because pushed element has not yet reached its destination,
11943          so it would trigger a change event at its previous field location */
11944       if (!player->is_pushing)
11945         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
11946
11947       if (!player->active)
11948         RemovePlayer(player);
11949     }
11950
11951     if (!local_player->LevelSolved && level.use_step_counter)
11952     {
11953       int i;
11954
11955       TimePlayed++;
11956
11957       if (TimeLeft > 0)
11958       {
11959         TimeLeft--;
11960
11961         if (TimeLeft <= 10 && setup.time_limit)
11962           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11963
11964         DrawGameValue_Time(TimeLeft);
11965
11966         if (!TimeLeft && setup.time_limit)
11967           for (i = 0; i < MAX_PLAYERS; i++)
11968             KillPlayer(&stored_player[i]);
11969       }
11970       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11971         DrawGameValue_Time(TimePlayed);
11972     }
11973
11974     if (tape.single_step && tape.recording && !tape.pausing &&
11975         !player->programmed_action)
11976       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11977   }
11978 }
11979
11980 void ScrollScreen(struct PlayerInfo *player, int mode)
11981 {
11982   static unsigned long screen_frame_counter = 0;
11983
11984   if (mode == SCROLL_INIT)
11985   {
11986     /* set scrolling step size according to actual player's moving speed */
11987     ScrollStepSize = TILEX / player->move_delay_value;
11988
11989     screen_frame_counter = FrameCounter;
11990     ScreenMovDir = player->MovDir;
11991     ScreenMovPos = player->MovPos;
11992     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11993     return;
11994   }
11995   else if (!FrameReached(&screen_frame_counter, 1))
11996     return;
11997
11998   if (ScreenMovPos)
11999   {
12000     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12001     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12002     redraw_mask |= REDRAW_FIELD;
12003   }
12004   else
12005     ScreenMovDir = MV_NONE;
12006 }
12007
12008 void TestIfPlayerTouchesCustomElement(int x, int y)
12009 {
12010   static int xy[4][2] =
12011   {
12012     { 0, -1 },
12013     { -1, 0 },
12014     { +1, 0 },
12015     { 0, +1 }
12016   };
12017   static int trigger_sides[4][2] =
12018   {
12019     /* center side       border side */
12020     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12021     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12022     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12023     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12024   };
12025   static int touch_dir[4] =
12026   {
12027     MV_LEFT | MV_RIGHT,
12028     MV_UP   | MV_DOWN,
12029     MV_UP   | MV_DOWN,
12030     MV_LEFT | MV_RIGHT
12031   };
12032   int center_element = Feld[x][y];      /* should always be non-moving! */
12033   int i;
12034
12035   for (i = 0; i < NUM_DIRECTIONS; i++)
12036   {
12037     int xx = x + xy[i][0];
12038     int yy = y + xy[i][1];
12039     int center_side = trigger_sides[i][0];
12040     int border_side = trigger_sides[i][1];
12041     int border_element;
12042
12043     if (!IN_LEV_FIELD(xx, yy))
12044       continue;
12045
12046     if (IS_PLAYER(x, y))
12047     {
12048       struct PlayerInfo *player = PLAYERINFO(x, y);
12049
12050       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12051         border_element = Feld[xx][yy];          /* may be moving! */
12052       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12053         border_element = Feld[xx][yy];
12054       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12055         border_element = MovingOrBlocked2Element(xx, yy);
12056       else
12057         continue;               /* center and border element do not touch */
12058
12059       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12060                                  player->index_bit, border_side);
12061       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12062                                           CE_PLAYER_TOUCHES_X,
12063                                           player->index_bit, border_side);
12064     }
12065     else if (IS_PLAYER(xx, yy))
12066     {
12067       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12068
12069       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12070       {
12071         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12072           continue;             /* center and border element do not touch */
12073       }
12074
12075       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12076                                  player->index_bit, center_side);
12077       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12078                                           CE_PLAYER_TOUCHES_X,
12079                                           player->index_bit, center_side);
12080       break;
12081     }
12082   }
12083 }
12084
12085 #if USE_ELEMENT_TOUCHING_BUGFIX
12086
12087 void TestIfElementTouchesCustomElement(int x, int y)
12088 {
12089   static int xy[4][2] =
12090   {
12091     { 0, -1 },
12092     { -1, 0 },
12093     { +1, 0 },
12094     { 0, +1 }
12095   };
12096   static int trigger_sides[4][2] =
12097   {
12098     /* center side      border side */
12099     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12100     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12101     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12102     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12103   };
12104   static int touch_dir[4] =
12105   {
12106     MV_LEFT | MV_RIGHT,
12107     MV_UP   | MV_DOWN,
12108     MV_UP   | MV_DOWN,
12109     MV_LEFT | MV_RIGHT
12110   };
12111   boolean change_center_element = FALSE;
12112   int center_element = Feld[x][y];      /* should always be non-moving! */
12113   int border_element_old[NUM_DIRECTIONS];
12114   int i;
12115
12116   for (i = 0; i < NUM_DIRECTIONS; i++)
12117   {
12118     int xx = x + xy[i][0];
12119     int yy = y + xy[i][1];
12120     int border_element;
12121
12122     border_element_old[i] = -1;
12123
12124     if (!IN_LEV_FIELD(xx, yy))
12125       continue;
12126
12127     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12128       border_element = Feld[xx][yy];    /* may be moving! */
12129     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12130       border_element = Feld[xx][yy];
12131     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12132       border_element = MovingOrBlocked2Element(xx, yy);
12133     else
12134       continue;                 /* center and border element do not touch */
12135
12136     border_element_old[i] = border_element;
12137   }
12138
12139   for (i = 0; i < NUM_DIRECTIONS; i++)
12140   {
12141     int xx = x + xy[i][0];
12142     int yy = y + xy[i][1];
12143     int center_side = trigger_sides[i][0];
12144     int border_element = border_element_old[i];
12145
12146     if (border_element == -1)
12147       continue;
12148
12149     /* check for change of border element */
12150     CheckElementChangeBySide(xx, yy, border_element, center_element,
12151                              CE_TOUCHING_X, center_side);
12152   }
12153
12154   for (i = 0; i < NUM_DIRECTIONS; i++)
12155   {
12156     int border_side = trigger_sides[i][1];
12157     int border_element = border_element_old[i];
12158
12159     if (border_element == -1)
12160       continue;
12161
12162     /* check for change of center element (but change it only once) */
12163     if (!change_center_element)
12164       change_center_element =
12165         CheckElementChangeBySide(x, y, center_element, border_element,
12166                                  CE_TOUCHING_X, border_side);
12167   }
12168 }
12169
12170 #else
12171
12172 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12173 {
12174   static int xy[4][2] =
12175   {
12176     { 0, -1 },
12177     { -1, 0 },
12178     { +1, 0 },
12179     { 0, +1 }
12180   };
12181   static int trigger_sides[4][2] =
12182   {
12183     /* center side      border side */
12184     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12185     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12186     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12187     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12188   };
12189   static int touch_dir[4] =
12190   {
12191     MV_LEFT | MV_RIGHT,
12192     MV_UP   | MV_DOWN,
12193     MV_UP   | MV_DOWN,
12194     MV_LEFT | MV_RIGHT
12195   };
12196   boolean change_center_element = FALSE;
12197   int center_element = Feld[x][y];      /* should always be non-moving! */
12198   int i;
12199
12200   for (i = 0; i < NUM_DIRECTIONS; i++)
12201   {
12202     int xx = x + xy[i][0];
12203     int yy = y + xy[i][1];
12204     int center_side = trigger_sides[i][0];
12205     int border_side = trigger_sides[i][1];
12206     int border_element;
12207
12208     if (!IN_LEV_FIELD(xx, yy))
12209       continue;
12210
12211     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12212       border_element = Feld[xx][yy];    /* may be moving! */
12213     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12214       border_element = Feld[xx][yy];
12215     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12216       border_element = MovingOrBlocked2Element(xx, yy);
12217     else
12218       continue;                 /* center and border element do not touch */
12219
12220     /* check for change of center element (but change it only once) */
12221     if (!change_center_element)
12222       change_center_element =
12223         CheckElementChangeBySide(x, y, center_element, border_element,
12224                                  CE_TOUCHING_X, border_side);
12225
12226     /* check for change of border element */
12227     CheckElementChangeBySide(xx, yy, border_element, center_element,
12228                              CE_TOUCHING_X, center_side);
12229   }
12230 }
12231
12232 #endif
12233
12234 void TestIfElementHitsCustomElement(int x, int y, int direction)
12235 {
12236   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12237   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12238   int hitx = x + dx, hity = y + dy;
12239   int hitting_element = Feld[x][y];
12240   int touched_element;
12241
12242   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12243     return;
12244
12245   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12246                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12247
12248   if (IN_LEV_FIELD(hitx, hity))
12249   {
12250     int opposite_direction = MV_DIR_OPPOSITE(direction);
12251     int hitting_side = direction;
12252     int touched_side = opposite_direction;
12253     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12254                           MovDir[hitx][hity] != direction ||
12255                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12256
12257     object_hit = TRUE;
12258
12259     if (object_hit)
12260     {
12261       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12262                                CE_HITTING_X, touched_side);
12263
12264       CheckElementChangeBySide(hitx, hity, touched_element,
12265                                hitting_element, CE_HIT_BY_X, hitting_side);
12266
12267       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12268                                CE_HIT_BY_SOMETHING, opposite_direction);
12269     }
12270   }
12271
12272   /* "hitting something" is also true when hitting the playfield border */
12273   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12274                            CE_HITTING_SOMETHING, direction);
12275 }
12276
12277 #if 0
12278 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12279 {
12280   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12281   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12282   int hitx = x + dx, hity = y + dy;
12283   int hitting_element = Feld[x][y];
12284   int touched_element;
12285 #if 0
12286   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12287                         !IS_FREE(hitx, hity) &&
12288                         (!IS_MOVING(hitx, hity) ||
12289                          MovDir[hitx][hity] != direction ||
12290                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
12291 #endif
12292
12293   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12294     return;
12295
12296 #if 0
12297   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12298     return;
12299 #endif
12300
12301   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12302                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12303
12304   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12305                            EP_CAN_SMASH_EVERYTHING, direction);
12306
12307   if (IN_LEV_FIELD(hitx, hity))
12308   {
12309     int opposite_direction = MV_DIR_OPPOSITE(direction);
12310     int hitting_side = direction;
12311     int touched_side = opposite_direction;
12312 #if 0
12313     int touched_element = MovingOrBlocked2Element(hitx, hity);
12314 #endif
12315 #if 1
12316     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12317                           MovDir[hitx][hity] != direction ||
12318                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12319
12320     object_hit = TRUE;
12321 #endif
12322
12323     if (object_hit)
12324     {
12325       int i;
12326
12327       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12328                                CE_SMASHED_BY_SOMETHING, opposite_direction);
12329
12330       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12331                                CE_OTHER_IS_SMASHING, touched_side);
12332
12333       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12334                                CE_OTHER_GETS_SMASHED, hitting_side);
12335     }
12336   }
12337 }
12338 #endif
12339
12340 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12341 {
12342   int i, kill_x = -1, kill_y = -1;
12343
12344   int bad_element = -1;
12345   static int test_xy[4][2] =
12346   {
12347     { 0, -1 },
12348     { -1, 0 },
12349     { +1, 0 },
12350     { 0, +1 }
12351   };
12352   static int test_dir[4] =
12353   {
12354     MV_UP,
12355     MV_LEFT,
12356     MV_RIGHT,
12357     MV_DOWN
12358   };
12359
12360   for (i = 0; i < NUM_DIRECTIONS; i++)
12361   {
12362     int test_x, test_y, test_move_dir, test_element;
12363
12364     test_x = good_x + test_xy[i][0];
12365     test_y = good_y + test_xy[i][1];
12366
12367     if (!IN_LEV_FIELD(test_x, test_y))
12368       continue;
12369
12370     test_move_dir =
12371       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12372
12373     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12374
12375     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12376        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12377     */
12378     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12379         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12380     {
12381       kill_x = test_x;
12382       kill_y = test_y;
12383       bad_element = test_element;
12384
12385       break;
12386     }
12387   }
12388
12389   if (kill_x != -1 || kill_y != -1)
12390   {
12391     if (IS_PLAYER(good_x, good_y))
12392     {
12393       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12394
12395       if (player->shield_deadly_time_left > 0 &&
12396           !IS_INDESTRUCTIBLE(bad_element))
12397         Bang(kill_x, kill_y);
12398       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12399         KillPlayer(player);
12400     }
12401     else
12402       Bang(good_x, good_y);
12403   }
12404 }
12405
12406 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12407 {
12408   int i, kill_x = -1, kill_y = -1;
12409   int bad_element = Feld[bad_x][bad_y];
12410   static int test_xy[4][2] =
12411   {
12412     { 0, -1 },
12413     { -1, 0 },
12414     { +1, 0 },
12415     { 0, +1 }
12416   };
12417   static int touch_dir[4] =
12418   {
12419     MV_LEFT | MV_RIGHT,
12420     MV_UP   | MV_DOWN,
12421     MV_UP   | MV_DOWN,
12422     MV_LEFT | MV_RIGHT
12423   };
12424   static int test_dir[4] =
12425   {
12426     MV_UP,
12427     MV_LEFT,
12428     MV_RIGHT,
12429     MV_DOWN
12430   };
12431
12432   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12433     return;
12434
12435   for (i = 0; i < NUM_DIRECTIONS; i++)
12436   {
12437     int test_x, test_y, test_move_dir, test_element;
12438
12439     test_x = bad_x + test_xy[i][0];
12440     test_y = bad_y + test_xy[i][1];
12441     if (!IN_LEV_FIELD(test_x, test_y))
12442       continue;
12443
12444     test_move_dir =
12445       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12446
12447     test_element = Feld[test_x][test_y];
12448
12449     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12450        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12451     */
12452     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12453         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12454     {
12455       /* good thing is player or penguin that does not move away */
12456       if (IS_PLAYER(test_x, test_y))
12457       {
12458         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12459
12460         if (bad_element == EL_ROBOT && player->is_moving)
12461           continue;     /* robot does not kill player if he is moving */
12462
12463         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12464         {
12465           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12466             continue;           /* center and border element do not touch */
12467         }
12468
12469         kill_x = test_x;
12470         kill_y = test_y;
12471         break;
12472       }
12473       else if (test_element == EL_PENGUIN)
12474       {
12475         kill_x = test_x;
12476         kill_y = test_y;
12477         break;
12478       }
12479     }
12480   }
12481
12482   if (kill_x != -1 || kill_y != -1)
12483   {
12484     if (IS_PLAYER(kill_x, kill_y))
12485     {
12486       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12487
12488       if (player->shield_deadly_time_left > 0 &&
12489           !IS_INDESTRUCTIBLE(bad_element))
12490         Bang(bad_x, bad_y);
12491       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12492         KillPlayer(player);
12493     }
12494     else
12495       Bang(kill_x, kill_y);
12496   }
12497 }
12498
12499 void TestIfPlayerTouchesBadThing(int x, int y)
12500 {
12501   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12502 }
12503
12504 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12505 {
12506   TestIfGoodThingHitsBadThing(x, y, move_dir);
12507 }
12508
12509 void TestIfBadThingTouchesPlayer(int x, int y)
12510 {
12511   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12512 }
12513
12514 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12515 {
12516   TestIfBadThingHitsGoodThing(x, y, move_dir);
12517 }
12518
12519 void TestIfFriendTouchesBadThing(int x, int y)
12520 {
12521   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12522 }
12523
12524 void TestIfBadThingTouchesFriend(int x, int y)
12525 {
12526   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12527 }
12528
12529 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12530 {
12531   int i, kill_x = bad_x, kill_y = bad_y;
12532   static int xy[4][2] =
12533   {
12534     { 0, -1 },
12535     { -1, 0 },
12536     { +1, 0 },
12537     { 0, +1 }
12538   };
12539
12540   for (i = 0; i < NUM_DIRECTIONS; i++)
12541   {
12542     int x, y, element;
12543
12544     x = bad_x + xy[i][0];
12545     y = bad_y + xy[i][1];
12546     if (!IN_LEV_FIELD(x, y))
12547       continue;
12548
12549     element = Feld[x][y];
12550     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12551         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12552     {
12553       kill_x = x;
12554       kill_y = y;
12555       break;
12556     }
12557   }
12558
12559   if (kill_x != bad_x || kill_y != bad_y)
12560     Bang(bad_x, bad_y);
12561 }
12562
12563 void KillPlayer(struct PlayerInfo *player)
12564 {
12565   int jx = player->jx, jy = player->jy;
12566
12567   if (!player->active)
12568     return;
12569
12570   /* the following code was introduced to prevent an infinite loop when calling
12571      -> Bang()
12572      -> CheckTriggeredElementChangeExt()
12573      -> ExecuteCustomElementAction()
12574      -> KillPlayer()
12575      -> (infinitely repeating the above sequence of function calls)
12576      which occurs when killing the player while having a CE with the setting
12577      "kill player X when explosion of <player X>"; the solution using a new
12578      field "player->killed" was chosen for backwards compatibility, although
12579      clever use of the fields "player->active" etc. would probably also work */
12580 #if 1
12581   if (player->killed)
12582     return;
12583 #endif
12584
12585   player->killed = TRUE;
12586
12587   /* remove accessible field at the player's position */
12588   Feld[jx][jy] = EL_EMPTY;
12589
12590   /* deactivate shield (else Bang()/Explode() would not work right) */
12591   player->shield_normal_time_left = 0;
12592   player->shield_deadly_time_left = 0;
12593
12594   Bang(jx, jy);
12595   BuryPlayer(player);
12596 }
12597
12598 static void KillPlayerUnlessEnemyProtected(int x, int y)
12599 {
12600   if (!PLAYER_ENEMY_PROTECTED(x, y))
12601     KillPlayer(PLAYERINFO(x, y));
12602 }
12603
12604 static void KillPlayerUnlessExplosionProtected(int x, int y)
12605 {
12606   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
12607     KillPlayer(PLAYERINFO(x, y));
12608 }
12609
12610 void BuryPlayer(struct PlayerInfo *player)
12611 {
12612   int jx = player->jx, jy = player->jy;
12613
12614   if (!player->active)
12615     return;
12616
12617   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
12618   PlayLevelSound(jx, jy, SND_GAME_LOSING);
12619
12620   player->GameOver = TRUE;
12621   RemovePlayer(player);
12622 }
12623
12624 void RemovePlayer(struct PlayerInfo *player)
12625 {
12626   int jx = player->jx, jy = player->jy;
12627   int i, found = FALSE;
12628
12629   player->present = FALSE;
12630   player->active = FALSE;
12631
12632   if (!ExplodeField[jx][jy])
12633     StorePlayer[jx][jy] = 0;
12634
12635   if (player->is_moving)
12636     DrawLevelField(player->last_jx, player->last_jy);
12637
12638   for (i = 0; i < MAX_PLAYERS; i++)
12639     if (stored_player[i].active)
12640       found = TRUE;
12641
12642   if (!found)
12643     AllPlayersGone = TRUE;
12644
12645   ExitX = ZX = jx;
12646   ExitY = ZY = jy;
12647 }
12648
12649 #if USE_NEW_SNAP_DELAY
12650 static void setFieldForSnapping(int x, int y, int element, int direction)
12651 {
12652   struct ElementInfo *ei = &element_info[element];
12653   int direction_bit = MV_DIR_TO_BIT(direction);
12654   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
12655   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
12656                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
12657
12658   Feld[x][y] = EL_ELEMENT_SNAPPING;
12659   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
12660
12661   ResetGfxAnimation(x, y);
12662
12663   GfxElement[x][y] = element;
12664   GfxAction[x][y] = action;
12665   GfxDir[x][y] = direction;
12666   GfxFrame[x][y] = -1;
12667 }
12668 #endif
12669
12670 /*
12671   =============================================================================
12672   checkDiagonalPushing()
12673   -----------------------------------------------------------------------------
12674   check if diagonal input device direction results in pushing of object
12675   (by checking if the alternative direction is walkable, diggable, ...)
12676   =============================================================================
12677 */
12678
12679 static boolean checkDiagonalPushing(struct PlayerInfo *player,
12680                                     int x, int y, int real_dx, int real_dy)
12681 {
12682   int jx, jy, dx, dy, xx, yy;
12683
12684   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
12685     return TRUE;
12686
12687   /* diagonal direction: check alternative direction */
12688   jx = player->jx;
12689   jy = player->jy;
12690   dx = x - jx;
12691   dy = y - jy;
12692   xx = jx + (dx == 0 ? real_dx : 0);
12693   yy = jy + (dy == 0 ? real_dy : 0);
12694
12695   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
12696 }
12697
12698 /*
12699   =============================================================================
12700   DigField()
12701   -----------------------------------------------------------------------------
12702   x, y:                 field next to player (non-diagonal) to try to dig to
12703   real_dx, real_dy:     direction as read from input device (can be diagonal)
12704   =============================================================================
12705 */
12706
12707 int DigField(struct PlayerInfo *player,
12708              int oldx, int oldy, int x, int y,
12709              int real_dx, int real_dy, int mode)
12710 {
12711   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
12712   boolean player_was_pushing = player->is_pushing;
12713   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
12714   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
12715   int jx = oldx, jy = oldy;
12716   int dx = x - jx, dy = y - jy;
12717   int nextx = x + dx, nexty = y + dy;
12718   int move_direction = (dx == -1 ? MV_LEFT  :
12719                         dx == +1 ? MV_RIGHT :
12720                         dy == -1 ? MV_UP    :
12721                         dy == +1 ? MV_DOWN  : MV_NONE);
12722   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
12723   int dig_side = MV_DIR_OPPOSITE(move_direction);
12724   int old_element = Feld[jx][jy];
12725 #if USE_FIXED_DONT_RUN_INTO
12726   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
12727 #else
12728   int element;
12729 #endif
12730   int collect_count;
12731
12732   if (is_player)                /* function can also be called by EL_PENGUIN */
12733   {
12734     if (player->MovPos == 0)
12735     {
12736       player->is_digging = FALSE;
12737       player->is_collecting = FALSE;
12738     }
12739
12740     if (player->MovPos == 0)    /* last pushing move finished */
12741       player->is_pushing = FALSE;
12742
12743     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
12744     {
12745       player->is_switching = FALSE;
12746       player->push_delay = -1;
12747
12748       return MP_NO_ACTION;
12749     }
12750   }
12751
12752 #if !USE_FIXED_DONT_RUN_INTO
12753   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12754     return MP_NO_ACTION;
12755 #endif
12756
12757   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
12758     old_element = Back[jx][jy];
12759
12760   /* in case of element dropped at player position, check background */
12761   else if (Back[jx][jy] != EL_EMPTY &&
12762            game.engine_version >= VERSION_IDENT(2,2,0,0))
12763     old_element = Back[jx][jy];
12764
12765   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12766     return MP_NO_ACTION;        /* field has no opening in this direction */
12767
12768   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12769     return MP_NO_ACTION;        /* field has no opening in this direction */
12770
12771 #if USE_FIXED_DONT_RUN_INTO
12772   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12773   {
12774     SplashAcid(x, y);
12775
12776     Feld[jx][jy] = player->artwork_element;
12777     InitMovingField(jx, jy, MV_DOWN);
12778     Store[jx][jy] = EL_ACID;
12779     ContinueMoving(jx, jy);
12780     BuryPlayer(player);
12781
12782     return MP_DONT_RUN_INTO;
12783   }
12784 #endif
12785
12786 #if USE_FIXED_DONT_RUN_INTO
12787   if (player_can_move && DONT_RUN_INTO(element))
12788   {
12789     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12790
12791     return MP_DONT_RUN_INTO;
12792   }
12793 #endif
12794
12795 #if USE_FIXED_DONT_RUN_INTO
12796   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12797     return MP_NO_ACTION;
12798 #endif
12799
12800 #if !USE_FIXED_DONT_RUN_INTO
12801   element = Feld[x][y];
12802 #endif
12803
12804   collect_count = element_info[element].collect_count_initial;
12805
12806   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
12807     return MP_NO_ACTION;
12808
12809   if (game.engine_version < VERSION_IDENT(2,2,0,0))
12810     player_can_move = player_can_move_or_snap;
12811
12812   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
12813       game.engine_version >= VERSION_IDENT(2,2,0,0))
12814   {
12815     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
12816                                player->index_bit, dig_side);
12817     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12818                                         player->index_bit, dig_side);
12819
12820     if (element == EL_DC_LANDMINE)
12821       Bang(x, y);
12822
12823     if (Feld[x][y] != element)          /* field changed by snapping */
12824       return MP_ACTION;
12825
12826     return MP_NO_ACTION;
12827   }
12828
12829 #if USE_PLAYER_GRAVITY
12830   if (player->gravity && is_player && !player->is_auto_moving &&
12831       canFallDown(player) && move_direction != MV_DOWN &&
12832       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12833     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12834 #else
12835   if (game.gravity && is_player && !player->is_auto_moving &&
12836       canFallDown(player) && move_direction != MV_DOWN &&
12837       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12838     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12839 #endif
12840
12841   if (player_can_move &&
12842       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12843   {
12844     int sound_element = SND_ELEMENT(element);
12845     int sound_action = ACTION_WALKING;
12846
12847     if (IS_RND_GATE(element))
12848     {
12849       if (!player->key[RND_GATE_NR(element)])
12850         return MP_NO_ACTION;
12851     }
12852     else if (IS_RND_GATE_GRAY(element))
12853     {
12854       if (!player->key[RND_GATE_GRAY_NR(element)])
12855         return MP_NO_ACTION;
12856     }
12857     else if (IS_RND_GATE_GRAY_ACTIVE(element))
12858     {
12859       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
12860         return MP_NO_ACTION;
12861     }
12862     else if (element == EL_EXIT_OPEN ||
12863              element == EL_EM_EXIT_OPEN ||
12864              element == EL_STEEL_EXIT_OPEN ||
12865              element == EL_EM_STEEL_EXIT_OPEN ||
12866              element == EL_SP_EXIT_OPEN ||
12867              element == EL_SP_EXIT_OPENING)
12868     {
12869       sound_action = ACTION_PASSING;    /* player is passing exit */
12870     }
12871     else if (element == EL_EMPTY)
12872     {
12873       sound_action = ACTION_MOVING;             /* nothing to walk on */
12874     }
12875
12876     /* play sound from background or player, whatever is available */
12877     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12878       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12879     else
12880       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
12881   }
12882   else if (player_can_move &&
12883            IS_PASSABLE(element) && canPassField(x, y, move_direction))
12884   {
12885     if (!ACCESS_FROM(element, opposite_direction))
12886       return MP_NO_ACTION;      /* field not accessible from this direction */
12887
12888     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
12889       return MP_NO_ACTION;
12890
12891     if (IS_EM_GATE(element))
12892     {
12893       if (!player->key[EM_GATE_NR(element)])
12894         return MP_NO_ACTION;
12895     }
12896     else if (IS_EM_GATE_GRAY(element))
12897     {
12898       if (!player->key[EM_GATE_GRAY_NR(element)])
12899         return MP_NO_ACTION;
12900     }
12901     else if (IS_EM_GATE_GRAY_ACTIVE(element))
12902     {
12903       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
12904         return MP_NO_ACTION;
12905     }
12906     else if (IS_EMC_GATE(element))
12907     {
12908       if (!player->key[EMC_GATE_NR(element)])
12909         return MP_NO_ACTION;
12910     }
12911     else if (IS_EMC_GATE_GRAY(element))
12912     {
12913       if (!player->key[EMC_GATE_GRAY_NR(element)])
12914         return MP_NO_ACTION;
12915     }
12916     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
12917     {
12918       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
12919         return MP_NO_ACTION;
12920     }
12921     else if (element == EL_DC_GATE_WHITE ||
12922              element == EL_DC_GATE_WHITE_GRAY ||
12923              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
12924     {
12925       if (player->num_white_keys == 0)
12926         return MP_NO_ACTION;
12927
12928       player->num_white_keys--;
12929     }
12930     else if (IS_SP_PORT(element))
12931     {
12932       if (element == EL_SP_GRAVITY_PORT_LEFT ||
12933           element == EL_SP_GRAVITY_PORT_RIGHT ||
12934           element == EL_SP_GRAVITY_PORT_UP ||
12935           element == EL_SP_GRAVITY_PORT_DOWN)
12936 #if USE_PLAYER_GRAVITY
12937         player->gravity = !player->gravity;
12938 #else
12939         game.gravity = !game.gravity;
12940 #endif
12941       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
12942                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12943                element == EL_SP_GRAVITY_ON_PORT_UP ||
12944                element == EL_SP_GRAVITY_ON_PORT_DOWN)
12945 #if USE_PLAYER_GRAVITY
12946         player->gravity = TRUE;
12947 #else
12948         game.gravity = TRUE;
12949 #endif
12950       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12951                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12952                element == EL_SP_GRAVITY_OFF_PORT_UP ||
12953                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12954 #if USE_PLAYER_GRAVITY
12955         player->gravity = FALSE;
12956 #else
12957         game.gravity = FALSE;
12958 #endif
12959     }
12960
12961     /* automatically move to the next field with double speed */
12962     player->programmed_action = move_direction;
12963
12964     if (player->move_delay_reset_counter == 0)
12965     {
12966       player->move_delay_reset_counter = 2;     /* two double speed steps */
12967
12968       DOUBLE_PLAYER_SPEED(player);
12969     }
12970
12971     PlayLevelSoundAction(x, y, ACTION_PASSING);
12972   }
12973   else if (player_can_move_or_snap && IS_DIGGABLE(element))
12974   {
12975     RemoveField(x, y);
12976
12977     if (mode != DF_SNAP)
12978     {
12979       GfxElement[x][y] = GFX_ELEMENT(element);
12980       player->is_digging = TRUE;
12981     }
12982
12983     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12984
12985     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12986                                         player->index_bit, dig_side);
12987
12988     if (mode == DF_SNAP)
12989     {
12990 #if USE_NEW_SNAP_DELAY
12991       if (level.block_snap_field)
12992         setFieldForSnapping(x, y, element, move_direction);
12993       else
12994         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12995 #else
12996       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
12997 #endif
12998
12999       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13000                                           player->index_bit, dig_side);
13001     }
13002   }
13003   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13004   {
13005     RemoveField(x, y);
13006
13007     if (is_player && mode != DF_SNAP)
13008     {
13009       GfxElement[x][y] = element;
13010       player->is_collecting = TRUE;
13011     }
13012
13013     if (element == EL_SPEED_PILL)
13014     {
13015       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13016     }
13017     else if (element == EL_EXTRA_TIME && level.time > 0)
13018     {
13019       TimeLeft += level.extra_time;
13020       DrawGameValue_Time(TimeLeft);
13021     }
13022     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13023     {
13024       player->shield_normal_time_left += level.shield_normal_time;
13025       if (element == EL_SHIELD_DEADLY)
13026         player->shield_deadly_time_left += level.shield_deadly_time;
13027     }
13028     else if (element == EL_DYNAMITE ||
13029              element == EL_EM_DYNAMITE ||
13030              element == EL_SP_DISK_RED)
13031     {
13032       if (player->inventory_size < MAX_INVENTORY_SIZE)
13033         player->inventory_element[player->inventory_size++] = element;
13034
13035       DrawGameDoorValues();
13036     }
13037     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13038     {
13039       player->dynabomb_count++;
13040       player->dynabombs_left++;
13041     }
13042     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13043     {
13044       player->dynabomb_size++;
13045     }
13046     else if (element == EL_DYNABOMB_INCREASE_POWER)
13047     {
13048       player->dynabomb_xl = TRUE;
13049     }
13050     else if (IS_KEY(element))
13051     {
13052       player->key[KEY_NR(element)] = TRUE;
13053
13054       DrawGameDoorValues();
13055     }
13056     else if (element == EL_DC_KEY_WHITE)
13057     {
13058       player->num_white_keys++;
13059
13060       /* display white keys? */
13061       /* DrawGameDoorValues(); */
13062     }
13063     else if (IS_ENVELOPE(element))
13064     {
13065       player->show_envelope = element;
13066     }
13067     else if (element == EL_EMC_LENSES)
13068     {
13069       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13070
13071       RedrawAllInvisibleElementsForLenses();
13072     }
13073     else if (element == EL_EMC_MAGNIFIER)
13074     {
13075       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13076
13077       RedrawAllInvisibleElementsForMagnifier();
13078     }
13079     else if (IS_DROPPABLE(element) ||
13080              IS_THROWABLE(element))     /* can be collected and dropped */
13081     {
13082       int i;
13083
13084       if (collect_count == 0)
13085         player->inventory_infinite_element = element;
13086       else
13087         for (i = 0; i < collect_count; i++)
13088           if (player->inventory_size < MAX_INVENTORY_SIZE)
13089             player->inventory_element[player->inventory_size++] = element;
13090
13091       DrawGameDoorValues();
13092     }
13093     else if (collect_count > 0)
13094     {
13095       local_player->gems_still_needed -= collect_count;
13096       if (local_player->gems_still_needed < 0)
13097         local_player->gems_still_needed = 0;
13098
13099       DrawGameValue_Emeralds(local_player->gems_still_needed);
13100     }
13101
13102     RaiseScoreElement(element);
13103     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13104
13105     if (is_player)
13106       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13107                                           player->index_bit, dig_side);
13108
13109     if (mode == DF_SNAP)
13110     {
13111 #if USE_NEW_SNAP_DELAY
13112       if (level.block_snap_field)
13113         setFieldForSnapping(x, y, element, move_direction);
13114       else
13115         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13116 #else
13117       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13118 #endif
13119
13120       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13121                                           player->index_bit, dig_side);
13122     }
13123   }
13124   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13125   {
13126     if (mode == DF_SNAP && element != EL_BD_ROCK)
13127       return MP_NO_ACTION;
13128
13129     if (CAN_FALL(element) && dy)
13130       return MP_NO_ACTION;
13131
13132     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13133         !(element == EL_SPRING && level.use_spring_bug))
13134       return MP_NO_ACTION;
13135
13136     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13137         ((move_direction & MV_VERTICAL &&
13138           ((element_info[element].move_pattern & MV_LEFT &&
13139             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13140            (element_info[element].move_pattern & MV_RIGHT &&
13141             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13142          (move_direction & MV_HORIZONTAL &&
13143           ((element_info[element].move_pattern & MV_UP &&
13144             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13145            (element_info[element].move_pattern & MV_DOWN &&
13146             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13147       return MP_NO_ACTION;
13148
13149     /* do not push elements already moving away faster than player */
13150     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13151         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13152       return MP_NO_ACTION;
13153
13154     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13155     {
13156       if (player->push_delay_value == -1 || !player_was_pushing)
13157         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13158     }
13159     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13160     {
13161       if (player->push_delay_value == -1)
13162         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13163     }
13164     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13165     {
13166       if (!player->is_pushing)
13167         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13168     }
13169
13170     player->is_pushing = TRUE;
13171     player->is_active = TRUE;
13172
13173     if (!(IN_LEV_FIELD(nextx, nexty) &&
13174           (IS_FREE(nextx, nexty) ||
13175            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13176             IS_SB_ELEMENT(element)))))
13177       return MP_NO_ACTION;
13178
13179     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13180       return MP_NO_ACTION;
13181
13182     if (player->push_delay == -1)       /* new pushing; restart delay */
13183       player->push_delay = 0;
13184
13185     if (player->push_delay < player->push_delay_value &&
13186         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13187         element != EL_SPRING && element != EL_BALLOON)
13188     {
13189       /* make sure that there is no move delay before next try to push */
13190       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13191         player->move_delay = 0;
13192
13193       return MP_NO_ACTION;
13194     }
13195
13196     if (IS_SB_ELEMENT(element))
13197     {
13198       if (element == EL_SOKOBAN_FIELD_FULL)
13199       {
13200         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13201         local_player->sokobanfields_still_needed++;
13202       }
13203
13204       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13205       {
13206         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13207         local_player->sokobanfields_still_needed--;
13208       }
13209
13210       Feld[x][y] = EL_SOKOBAN_OBJECT;
13211
13212       if (Back[x][y] == Back[nextx][nexty])
13213         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13214       else if (Back[x][y] != 0)
13215         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13216                                     ACTION_EMPTYING);
13217       else
13218         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13219                                     ACTION_FILLING);
13220
13221       if (local_player->sokobanfields_still_needed == 0 &&
13222           game.emulation == EMU_SOKOBAN)
13223       {
13224         PlayerWins(player);
13225
13226         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13227       }
13228     }
13229     else
13230       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13231
13232     InitMovingField(x, y, move_direction);
13233     GfxAction[x][y] = ACTION_PUSHING;
13234
13235     if (mode == DF_SNAP)
13236       ContinueMoving(x, y);
13237     else
13238       MovPos[x][y] = (dx != 0 ? dx : dy);
13239
13240     Pushed[x][y] = TRUE;
13241     Pushed[nextx][nexty] = TRUE;
13242
13243     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13244       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13245     else
13246       player->push_delay_value = -1;    /* get new value later */
13247
13248     /* check for element change _after_ element has been pushed */
13249     if (game.use_change_when_pushing_bug)
13250     {
13251       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13252                                  player->index_bit, dig_side);
13253       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13254                                           player->index_bit, dig_side);
13255     }
13256   }
13257   else if (IS_SWITCHABLE(element))
13258   {
13259     if (PLAYER_SWITCHING(player, x, y))
13260     {
13261       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13262                                           player->index_bit, dig_side);
13263
13264       return MP_ACTION;
13265     }
13266
13267     player->is_switching = TRUE;
13268     player->switch_x = x;
13269     player->switch_y = y;
13270
13271     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13272
13273     if (element == EL_ROBOT_WHEEL)
13274     {
13275       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13276       ZX = x;
13277       ZY = y;
13278
13279       DrawLevelField(x, y);
13280     }
13281     else if (element == EL_SP_TERMINAL)
13282     {
13283       int xx, yy;
13284
13285       SCAN_PLAYFIELD(xx, yy)
13286       {
13287         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13288           Bang(xx, yy);
13289         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13290           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13291       }
13292     }
13293     else if (IS_BELT_SWITCH(element))
13294     {
13295       ToggleBeltSwitch(x, y);
13296     }
13297     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13298              element == EL_SWITCHGATE_SWITCH_DOWN ||
13299              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13300              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13301     {
13302       ToggleSwitchgateSwitch(x, y);
13303     }
13304     else if (element == EL_LIGHT_SWITCH ||
13305              element == EL_LIGHT_SWITCH_ACTIVE)
13306     {
13307       ToggleLightSwitch(x, y);
13308     }
13309     else if (element == EL_TIMEGATE_SWITCH ||
13310              element == EL_DC_TIMEGATE_SWITCH)
13311     {
13312       ActivateTimegateSwitch(x, y);
13313     }
13314     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13315              element == EL_BALLOON_SWITCH_RIGHT ||
13316              element == EL_BALLOON_SWITCH_UP    ||
13317              element == EL_BALLOON_SWITCH_DOWN  ||
13318              element == EL_BALLOON_SWITCH_NONE  ||
13319              element == EL_BALLOON_SWITCH_ANY)
13320     {
13321       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13322                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13323                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13324                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13325                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13326                              move_direction);
13327     }
13328     else if (element == EL_LAMP)
13329     {
13330       Feld[x][y] = EL_LAMP_ACTIVE;
13331       local_player->lights_still_needed--;
13332
13333       ResetGfxAnimation(x, y);
13334       DrawLevelField(x, y);
13335     }
13336     else if (element == EL_TIME_ORB_FULL)
13337     {
13338       Feld[x][y] = EL_TIME_ORB_EMPTY;
13339
13340       if (level.time > 0 || level.use_time_orb_bug)
13341       {
13342         TimeLeft += level.time_orb_time;
13343         DrawGameValue_Time(TimeLeft);
13344       }
13345
13346       ResetGfxAnimation(x, y);
13347       DrawLevelField(x, y);
13348     }
13349     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13350              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13351     {
13352       int xx, yy;
13353
13354       game.ball_state = !game.ball_state;
13355
13356       SCAN_PLAYFIELD(xx, yy)
13357       {
13358         int e = Feld[xx][yy];
13359
13360         if (game.ball_state)
13361         {
13362           if (e == EL_EMC_MAGIC_BALL)
13363             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13364           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13365             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13366         }
13367         else
13368         {
13369           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13370             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13371           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13372             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13373         }
13374       }
13375     }
13376
13377     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13378                                         player->index_bit, dig_side);
13379
13380     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13381                                         player->index_bit, dig_side);
13382
13383     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13384                                         player->index_bit, dig_side);
13385
13386     return MP_ACTION;
13387   }
13388   else
13389   {
13390     if (!PLAYER_SWITCHING(player, x, y))
13391     {
13392       player->is_switching = TRUE;
13393       player->switch_x = x;
13394       player->switch_y = y;
13395
13396       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13397                                  player->index_bit, dig_side);
13398       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13399                                           player->index_bit, dig_side);
13400
13401       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13402                                  player->index_bit, dig_side);
13403       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13404                                           player->index_bit, dig_side);
13405     }
13406
13407     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13408                                player->index_bit, dig_side);
13409     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13410                                         player->index_bit, dig_side);
13411
13412     return MP_NO_ACTION;
13413   }
13414
13415   player->push_delay = -1;
13416
13417   if (is_player)                /* function can also be called by EL_PENGUIN */
13418   {
13419     if (Feld[x][y] != element)          /* really digged/collected something */
13420     {
13421       player->is_collecting = !player->is_digging;
13422       player->is_active = TRUE;
13423     }
13424   }
13425
13426   return MP_MOVING;
13427 }
13428
13429 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13430 {
13431   int jx = player->jx, jy = player->jy;
13432   int x = jx + dx, y = jy + dy;
13433   int snap_direction = (dx == -1 ? MV_LEFT  :
13434                         dx == +1 ? MV_RIGHT :
13435                         dy == -1 ? MV_UP    :
13436                         dy == +1 ? MV_DOWN  : MV_NONE);
13437   boolean can_continue_snapping = (level.continuous_snapping &&
13438                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13439
13440   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13441     return FALSE;
13442
13443   if (!player->active || !IN_LEV_FIELD(x, y))
13444     return FALSE;
13445
13446   if (dx && dy)
13447     return FALSE;
13448
13449   if (!dx && !dy)
13450   {
13451     if (player->MovPos == 0)
13452       player->is_pushing = FALSE;
13453
13454     player->is_snapping = FALSE;
13455
13456     if (player->MovPos == 0)
13457     {
13458       player->is_moving = FALSE;
13459       player->is_digging = FALSE;
13460       player->is_collecting = FALSE;
13461     }
13462
13463     return FALSE;
13464   }
13465
13466 #if USE_NEW_CONTINUOUS_SNAPPING
13467   /* prevent snapping with already pressed snap key when not allowed */
13468   if (player->is_snapping && !can_continue_snapping)
13469     return FALSE;
13470 #else
13471   if (player->is_snapping)
13472     return FALSE;
13473 #endif
13474
13475   player->MovDir = snap_direction;
13476
13477   if (player->MovPos == 0)
13478   {
13479     player->is_moving = FALSE;
13480     player->is_digging = FALSE;
13481     player->is_collecting = FALSE;
13482   }
13483
13484   player->is_dropping = FALSE;
13485   player->is_dropping_pressed = FALSE;
13486   player->drop_pressed_delay = 0;
13487
13488   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13489     return FALSE;
13490
13491   player->is_snapping = TRUE;
13492   player->is_active = TRUE;
13493
13494   if (player->MovPos == 0)
13495   {
13496     player->is_moving = FALSE;
13497     player->is_digging = FALSE;
13498     player->is_collecting = FALSE;
13499   }
13500
13501   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13502     DrawLevelField(player->last_jx, player->last_jy);
13503
13504   DrawLevelField(x, y);
13505
13506   return TRUE;
13507 }
13508
13509 boolean DropElement(struct PlayerInfo *player)
13510 {
13511   int old_element, new_element;
13512   int dropx = player->jx, dropy = player->jy;
13513   int drop_direction = player->MovDir;
13514   int drop_side = drop_direction;
13515   int drop_element = (player->inventory_size > 0 ?
13516                       player->inventory_element[player->inventory_size - 1] :
13517                       player->inventory_infinite_element != EL_UNDEFINED ?
13518                       player->inventory_infinite_element :
13519                       player->dynabombs_left > 0 ?
13520                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
13521                       EL_UNDEFINED);
13522
13523   player->is_dropping_pressed = TRUE;
13524
13525   /* do not drop an element on top of another element; when holding drop key
13526      pressed without moving, dropped element must move away before the next
13527      element can be dropped (this is especially important if the next element
13528      is dynamite, which can be placed on background for historical reasons) */
13529   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13530     return MP_ACTION;
13531
13532   if (IS_THROWABLE(drop_element))
13533   {
13534     dropx += GET_DX_FROM_DIR(drop_direction);
13535     dropy += GET_DY_FROM_DIR(drop_direction);
13536
13537     if (!IN_LEV_FIELD(dropx, dropy))
13538       return FALSE;
13539   }
13540
13541   old_element = Feld[dropx][dropy];     /* old element at dropping position */
13542   new_element = drop_element;           /* default: no change when dropping */
13543
13544   /* check if player is active, not moving and ready to drop */
13545   if (!player->active || player->MovPos || player->drop_delay > 0)
13546     return FALSE;
13547
13548   /* check if player has anything that can be dropped */
13549   if (new_element == EL_UNDEFINED)
13550     return FALSE;
13551
13552   /* check if drop key was pressed long enough for EM style dynamite */
13553   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13554     return FALSE;
13555
13556   /* check if anything can be dropped at the current position */
13557   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13558     return FALSE;
13559
13560   /* collected custom elements can only be dropped on empty fields */
13561   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13562     return FALSE;
13563
13564   if (old_element != EL_EMPTY)
13565     Back[dropx][dropy] = old_element;   /* store old element on this field */
13566
13567   ResetGfxAnimation(dropx, dropy);
13568   ResetRandomAnimationValue(dropx, dropy);
13569
13570   if (player->inventory_size > 0 ||
13571       player->inventory_infinite_element != EL_UNDEFINED)
13572   {
13573     if (player->inventory_size > 0)
13574     {
13575       player->inventory_size--;
13576
13577       DrawGameDoorValues();
13578
13579       if (new_element == EL_DYNAMITE)
13580         new_element = EL_DYNAMITE_ACTIVE;
13581       else if (new_element == EL_EM_DYNAMITE)
13582         new_element = EL_EM_DYNAMITE_ACTIVE;
13583       else if (new_element == EL_SP_DISK_RED)
13584         new_element = EL_SP_DISK_RED_ACTIVE;
13585     }
13586
13587     Feld[dropx][dropy] = new_element;
13588
13589     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13590       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13591                           el2img(Feld[dropx][dropy]), 0);
13592
13593     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13594
13595     /* needed if previous element just changed to "empty" in the last frame */
13596     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
13597
13598     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13599                                player->index_bit, drop_side);
13600     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13601                                         CE_PLAYER_DROPS_X,
13602                                         player->index_bit, drop_side);
13603
13604     TestIfElementTouchesCustomElement(dropx, dropy);
13605   }
13606   else          /* player is dropping a dyna bomb */
13607   {
13608     player->dynabombs_left--;
13609
13610     Feld[dropx][dropy] = new_element;
13611
13612     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13613       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13614                           el2img(Feld[dropx][dropy]), 0);
13615
13616     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13617   }
13618
13619   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
13620     InitField_WithBug1(dropx, dropy, FALSE);
13621
13622   new_element = Feld[dropx][dropy];     /* element might have changed */
13623
13624   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
13625       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
13626   {
13627     int move_direction, nextx, nexty;
13628
13629     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
13630       MovDir[dropx][dropy] = drop_direction;
13631
13632     move_direction = MovDir[dropx][dropy];
13633     nextx = dropx + GET_DX_FROM_DIR(move_direction);
13634     nexty = dropy + GET_DY_FROM_DIR(move_direction);
13635
13636     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
13637
13638 #if USE_FIX_IMPACT_COLLISION
13639     /* do not cause impact style collision by dropping elements that can fall */
13640     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13641 #else
13642     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13643 #endif
13644   }
13645
13646   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
13647   player->is_dropping = TRUE;
13648
13649   player->drop_pressed_delay = 0;
13650   player->is_dropping_pressed = FALSE;
13651
13652   player->drop_x = dropx;
13653   player->drop_y = dropy;
13654
13655   return TRUE;
13656 }
13657
13658 /* ------------------------------------------------------------------------- */
13659 /* game sound playing functions                                              */
13660 /* ------------------------------------------------------------------------- */
13661
13662 static int *loop_sound_frame = NULL;
13663 static int *loop_sound_volume = NULL;
13664
13665 void InitPlayLevelSound()
13666 {
13667   int num_sounds = getSoundListSize();
13668
13669   checked_free(loop_sound_frame);
13670   checked_free(loop_sound_volume);
13671
13672   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
13673   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
13674 }
13675
13676 static void PlayLevelSound(int x, int y, int nr)
13677 {
13678   int sx = SCREENX(x), sy = SCREENY(y);
13679   int volume, stereo_position;
13680   int max_distance = 8;
13681   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
13682
13683   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
13684       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
13685     return;
13686
13687   if (!IN_LEV_FIELD(x, y) ||
13688       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
13689       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
13690     return;
13691
13692   volume = SOUND_MAX_VOLUME;
13693
13694   if (!IN_SCR_FIELD(sx, sy))
13695   {
13696     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
13697     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
13698
13699     volume -= volume * (dx > dy ? dx : dy) / max_distance;
13700   }
13701
13702   stereo_position = (SOUND_MAX_LEFT +
13703                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
13704                      (SCR_FIELDX + 2 * max_distance));
13705
13706   if (IS_LOOP_SOUND(nr))
13707   {
13708     /* This assures that quieter loop sounds do not overwrite louder ones,
13709        while restarting sound volume comparison with each new game frame. */
13710
13711     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
13712       return;
13713
13714     loop_sound_volume[nr] = volume;
13715     loop_sound_frame[nr] = FrameCounter;
13716   }
13717
13718   PlaySoundExt(nr, volume, stereo_position, type);
13719 }
13720
13721 static void PlayLevelSoundNearest(int x, int y, int sound_action)
13722 {
13723   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
13724                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
13725                  y < LEVELY(BY1) ? LEVELY(BY1) :
13726                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
13727                  sound_action);
13728 }
13729
13730 static void PlayLevelSoundAction(int x, int y, int action)
13731 {
13732   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
13733 }
13734
13735 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
13736 {
13737   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13738
13739   if (sound_effect != SND_UNDEFINED)
13740     PlayLevelSound(x, y, sound_effect);
13741 }
13742
13743 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
13744                                               int action)
13745 {
13746   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13747
13748   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13749     PlayLevelSound(x, y, sound_effect);
13750 }
13751
13752 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
13753 {
13754   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13755
13756   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13757     PlayLevelSound(x, y, sound_effect);
13758 }
13759
13760 static void StopLevelSoundActionIfLoop(int x, int y, int action)
13761 {
13762   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13763
13764   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13765     StopSound(sound_effect);
13766 }
13767
13768 static void PlayLevelMusic()
13769 {
13770   if (levelset.music[level_nr] != MUS_UNDEFINED)
13771     PlayMusic(levelset.music[level_nr]);        /* from config file */
13772   else
13773     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
13774 }
13775
13776 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
13777 {
13778   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13779   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
13780   int x = xx - 1 - offset;
13781   int y = yy - 1 - offset;
13782
13783   switch (sample)
13784   {
13785     case SAMPLE_blank:
13786       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13787       break;
13788
13789     case SAMPLE_roll:
13790       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13791       break;
13792
13793     case SAMPLE_stone:
13794       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13795       break;
13796
13797     case SAMPLE_nut:
13798       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13799       break;
13800
13801     case SAMPLE_crack:
13802       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13803       break;
13804
13805     case SAMPLE_bug:
13806       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13807       break;
13808
13809     case SAMPLE_tank:
13810       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13811       break;
13812
13813     case SAMPLE_android_clone:
13814       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13815       break;
13816
13817     case SAMPLE_android_move:
13818       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13819       break;
13820
13821     case SAMPLE_spring:
13822       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13823       break;
13824
13825     case SAMPLE_slurp:
13826       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
13827       break;
13828
13829     case SAMPLE_eater:
13830       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13831       break;
13832
13833     case SAMPLE_eater_eat:
13834       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13835       break;
13836
13837     case SAMPLE_alien:
13838       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13839       break;
13840
13841     case SAMPLE_collect:
13842       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13843       break;
13844
13845     case SAMPLE_diamond:
13846       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13847       break;
13848
13849     case SAMPLE_squash:
13850       /* !!! CHECK THIS !!! */
13851 #if 1
13852       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13853 #else
13854       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13855 #endif
13856       break;
13857
13858     case SAMPLE_wonderfall:
13859       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13860       break;
13861
13862     case SAMPLE_drip:
13863       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13864       break;
13865
13866     case SAMPLE_push:
13867       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13868       break;
13869
13870     case SAMPLE_dirt:
13871       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13872       break;
13873
13874     case SAMPLE_acid:
13875       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13876       break;
13877
13878     case SAMPLE_ball:
13879       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13880       break;
13881
13882     case SAMPLE_grow:
13883       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13884       break;
13885
13886     case SAMPLE_wonder:
13887       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13888       break;
13889
13890     case SAMPLE_door:
13891       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13892       break;
13893
13894     case SAMPLE_exit_open:
13895       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13896       break;
13897
13898     case SAMPLE_exit_leave:
13899       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13900       break;
13901
13902     case SAMPLE_dynamite:
13903       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13904       break;
13905
13906     case SAMPLE_tick:
13907       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13908       break;
13909
13910     case SAMPLE_press:
13911       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13912       break;
13913
13914     case SAMPLE_wheel:
13915       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13916       break;
13917
13918     case SAMPLE_boom:
13919       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13920       break;
13921
13922     case SAMPLE_die:
13923       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13924       break;
13925
13926     case SAMPLE_time:
13927       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13928       break;
13929
13930     default:
13931       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13932       break;
13933   }
13934 }
13935
13936 #if 0
13937 void ChangeTime(int value)
13938 {
13939   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
13940
13941   *time += value;
13942
13943   /* EMC game engine uses value from time counter of RND game engine */
13944   level.native_em_level->lev->time = *time;
13945
13946   DrawGameValue_Time(*time);
13947 }
13948
13949 void RaiseScore(int value)
13950 {
13951   /* EMC game engine and RND game engine have separate score counters */
13952   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
13953                 &level.native_em_level->lev->score : &local_player->score);
13954
13955   *score += value;
13956
13957   DrawGameValue_Score(*score);
13958 }
13959 #endif
13960
13961 void RaiseScore(int value)
13962 {
13963   local_player->score += value;
13964
13965   DrawGameValue_Score(local_player->score);
13966 }
13967
13968 void RaiseScoreElement(int element)
13969 {
13970   switch (element)
13971   {
13972     case EL_EMERALD:
13973     case EL_BD_DIAMOND:
13974     case EL_EMERALD_YELLOW:
13975     case EL_EMERALD_RED:
13976     case EL_EMERALD_PURPLE:
13977     case EL_SP_INFOTRON:
13978       RaiseScore(level.score[SC_EMERALD]);
13979       break;
13980     case EL_DIAMOND:
13981       RaiseScore(level.score[SC_DIAMOND]);
13982       break;
13983     case EL_CRYSTAL:
13984       RaiseScore(level.score[SC_CRYSTAL]);
13985       break;
13986     case EL_PEARL:
13987       RaiseScore(level.score[SC_PEARL]);
13988       break;
13989     case EL_BUG:
13990     case EL_BD_BUTTERFLY:
13991     case EL_SP_ELECTRON:
13992       RaiseScore(level.score[SC_BUG]);
13993       break;
13994     case EL_SPACESHIP:
13995     case EL_BD_FIREFLY:
13996     case EL_SP_SNIKSNAK:
13997       RaiseScore(level.score[SC_SPACESHIP]);
13998       break;
13999     case EL_YAMYAM:
14000     case EL_DARK_YAMYAM:
14001       RaiseScore(level.score[SC_YAMYAM]);
14002       break;
14003     case EL_ROBOT:
14004       RaiseScore(level.score[SC_ROBOT]);
14005       break;
14006     case EL_PACMAN:
14007       RaiseScore(level.score[SC_PACMAN]);
14008       break;
14009     case EL_NUT:
14010       RaiseScore(level.score[SC_NUT]);
14011       break;
14012     case EL_DYNAMITE:
14013     case EL_EM_DYNAMITE:
14014     case EL_SP_DISK_RED:
14015     case EL_DYNABOMB_INCREASE_NUMBER:
14016     case EL_DYNABOMB_INCREASE_SIZE:
14017     case EL_DYNABOMB_INCREASE_POWER:
14018       RaiseScore(level.score[SC_DYNAMITE]);
14019       break;
14020     case EL_SHIELD_NORMAL:
14021     case EL_SHIELD_DEADLY:
14022       RaiseScore(level.score[SC_SHIELD]);
14023       break;
14024     case EL_EXTRA_TIME:
14025       RaiseScore(level.extra_time_score);
14026       break;
14027     case EL_KEY_1:
14028     case EL_KEY_2:
14029     case EL_KEY_3:
14030     case EL_KEY_4:
14031     case EL_EM_KEY_1:
14032     case EL_EM_KEY_2:
14033     case EL_EM_KEY_3:
14034     case EL_EM_KEY_4:
14035     case EL_EMC_KEY_5:
14036     case EL_EMC_KEY_6:
14037     case EL_EMC_KEY_7:
14038     case EL_EMC_KEY_8:
14039     case EL_DC_KEY_WHITE:
14040       RaiseScore(level.score[SC_KEY]);
14041       break;
14042     default:
14043       RaiseScore(element_info[element].collect_score);
14044       break;
14045   }
14046 }
14047
14048 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14049 {
14050   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14051   {
14052 #if defined(NETWORK_AVALIABLE)
14053     if (options.network)
14054       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14055     else
14056 #endif
14057     {
14058       if (quick_quit)
14059       {
14060         game_status = GAME_MODE_MAIN;
14061
14062         DrawMainMenu();
14063       }
14064       else
14065       {
14066         FadeOut(REDRAW_FIELD);
14067
14068         game_status = GAME_MODE_MAIN;
14069
14070         DrawAndFadeInMainMenu(REDRAW_FIELD);
14071       }
14072     }
14073   }
14074   else          /* continue playing the game */
14075   {
14076     if (tape.playing && tape.deactivate_display)
14077       TapeDeactivateDisplayOff(TRUE);
14078
14079     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14080
14081     if (tape.playing && tape.deactivate_display)
14082       TapeDeactivateDisplayOn();
14083   }
14084 }
14085
14086 void RequestQuitGame(boolean ask_if_really_quit)
14087 {
14088   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14089   boolean skip_request = AllPlayersGone || quick_quit;
14090
14091   RequestQuitGameExt(skip_request, quick_quit,
14092                      "Do you really want to quit the game ?");
14093 }
14094
14095
14096 /* ------------------------------------------------------------------------- */
14097 /* random generator functions                                                */
14098 /* ------------------------------------------------------------------------- */
14099
14100 unsigned int InitEngineRandom_RND(long seed)
14101 {
14102   game.num_random_calls = 0;
14103
14104 #if 0
14105   unsigned int rnd_seed = InitEngineRandom(seed);
14106
14107   printf("::: START RND: %d\n", rnd_seed);
14108
14109   return rnd_seed;
14110 #else
14111
14112   return InitEngineRandom(seed);
14113
14114 #endif
14115
14116 }
14117
14118 unsigned int RND(int max)
14119 {
14120   if (max > 0)
14121   {
14122     game.num_random_calls++;
14123
14124     return GetEngineRandom(max);
14125   }
14126
14127   return 0;
14128 }
14129
14130
14131 /* ------------------------------------------------------------------------- */
14132 /* game engine snapshot handling functions                                   */
14133 /* ------------------------------------------------------------------------- */
14134
14135 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
14136
14137 struct EngineSnapshotInfo
14138 {
14139   /* runtime values for custom element collect score */
14140   int collect_score[NUM_CUSTOM_ELEMENTS];
14141
14142   /* runtime values for group element choice position */
14143   int choice_pos[NUM_GROUP_ELEMENTS];
14144
14145   /* runtime values for belt position animations */
14146   int belt_graphic[4 * NUM_BELT_PARTS];
14147   int belt_anim_mode[4 * NUM_BELT_PARTS];
14148 };
14149
14150 struct EngineSnapshotNodeInfo
14151 {
14152   void *buffer_orig;
14153   void *buffer_copy;
14154   int size;
14155 };
14156
14157 static struct EngineSnapshotInfo engine_snapshot_rnd;
14158 static ListNode *engine_snapshot_list = NULL;
14159 static char *snapshot_level_identifier = NULL;
14160 static int snapshot_level_nr = -1;
14161
14162 void FreeEngineSnapshot()
14163 {
14164   while (engine_snapshot_list != NULL)
14165     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14166                        checked_free);
14167
14168   setString(&snapshot_level_identifier, NULL);
14169   snapshot_level_nr = -1;
14170 }
14171
14172 static void SaveEngineSnapshotValues_RND()
14173 {
14174   static int belt_base_active_element[4] =
14175   {
14176     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14177     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14178     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14179     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14180   };
14181   int i, j;
14182
14183   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14184   {
14185     int element = EL_CUSTOM_START + i;
14186
14187     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14188   }
14189
14190   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14191   {
14192     int element = EL_GROUP_START + i;
14193
14194     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14195   }
14196
14197   for (i = 0; i < 4; i++)
14198   {
14199     for (j = 0; j < NUM_BELT_PARTS; j++)
14200     {
14201       int element = belt_base_active_element[i] + j;
14202       int graphic = el2img(element);
14203       int anim_mode = graphic_info[graphic].anim_mode;
14204
14205       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14206       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14207     }
14208   }
14209 }
14210
14211 static void LoadEngineSnapshotValues_RND()
14212 {
14213   unsigned long num_random_calls = game.num_random_calls;
14214   int i, j;
14215
14216   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14217   {
14218     int element = EL_CUSTOM_START + i;
14219
14220     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14221   }
14222
14223   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14224   {
14225     int element = EL_GROUP_START + i;
14226
14227     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14228   }
14229
14230   for (i = 0; i < 4; i++)
14231   {
14232     for (j = 0; j < NUM_BELT_PARTS; j++)
14233     {
14234       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14235       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14236
14237       graphic_info[graphic].anim_mode = anim_mode;
14238     }
14239   }
14240
14241   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14242   {
14243     InitRND(tape.random_seed);
14244     for (i = 0; i < num_random_calls; i++)
14245       RND(1);
14246   }
14247
14248   if (game.num_random_calls != num_random_calls)
14249   {
14250     Error(ERR_INFO, "number of random calls out of sync");
14251     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14252     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14253     Error(ERR_EXIT, "this should not happen -- please debug");
14254   }
14255 }
14256
14257 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14258 {
14259   struct EngineSnapshotNodeInfo *bi =
14260     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14261
14262   bi->buffer_orig = buffer;
14263   bi->buffer_copy = checked_malloc(size);
14264   bi->size = size;
14265
14266   memcpy(bi->buffer_copy, buffer, size);
14267
14268   addNodeToList(&engine_snapshot_list, NULL, bi);
14269 }
14270
14271 void SaveEngineSnapshot()
14272 {
14273   FreeEngineSnapshot();         /* free previous snapshot, if needed */
14274
14275   if (level_editor_test_game)   /* do not save snapshots from editor */
14276     return;
14277
14278   /* copy some special values to a structure better suited for the snapshot */
14279
14280   SaveEngineSnapshotValues_RND();
14281   SaveEngineSnapshotValues_EM();
14282
14283   /* save values stored in special snapshot structure */
14284
14285   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14286   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14287
14288   /* save further RND engine values */
14289
14290   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14291   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14292   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14293
14294   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14295   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14296   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14297   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14298
14299   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14300   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14301   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14302   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14303   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14304
14305   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14306   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14307   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14308
14309   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14310
14311   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14312
14313   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14314   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14315
14316   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14317   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14318   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14319   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14320   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14321   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14322   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14323   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14324   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14325   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14326   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14327   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14328   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14329   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14330   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14331   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14332   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
14333   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
14334
14335   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14336   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14337
14338   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14339   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14340   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14341
14342   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14343   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14344
14345   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14346   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14347   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14348   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14349   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14350
14351   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14352   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14353
14354   /* save level identification information */
14355
14356   setString(&snapshot_level_identifier, leveldir_current->identifier);
14357   snapshot_level_nr = level_nr;
14358
14359 #if 0
14360   ListNode *node = engine_snapshot_list;
14361   int num_bytes = 0;
14362
14363   while (node != NULL)
14364   {
14365     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14366
14367     node = node->next;
14368   }
14369
14370   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14371 #endif
14372 }
14373
14374 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
14375 {
14376   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
14377 }
14378
14379 void LoadEngineSnapshot()
14380 {
14381   ListNode *node = engine_snapshot_list;
14382
14383   if (engine_snapshot_list == NULL)
14384     return;
14385
14386   while (node != NULL)
14387   {
14388     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
14389
14390     node = node->next;
14391   }
14392
14393   /* restore special values from snapshot structure */
14394
14395   LoadEngineSnapshotValues_RND();
14396   LoadEngineSnapshotValues_EM();
14397 }
14398
14399 boolean CheckEngineSnapshot()
14400 {
14401   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14402           snapshot_level_nr == level_nr);
14403 }
14404
14405
14406 /* ---------- new game button stuff ---------------------------------------- */
14407
14408 /* graphic position values for game buttons */
14409 #define GAME_BUTTON_XSIZE       30
14410 #define GAME_BUTTON_YSIZE       30
14411 #define GAME_BUTTON_XPOS        5
14412 #define GAME_BUTTON_YPOS        215
14413 #define SOUND_BUTTON_XPOS       5
14414 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
14415
14416 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14417 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14418 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14419 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14420 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14421 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14422
14423 static struct
14424 {
14425   int x, y;
14426   int gadget_id;
14427   char *infotext;
14428 } gamebutton_info[NUM_GAME_BUTTONS] =
14429 {
14430   {
14431     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
14432     GAME_CTRL_ID_STOP,
14433     "stop game"
14434   },
14435   {
14436     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
14437     GAME_CTRL_ID_PAUSE,
14438     "pause game"
14439   },
14440   {
14441     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
14442     GAME_CTRL_ID_PLAY,
14443     "play game"
14444   },
14445   {
14446     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
14447     SOUND_CTRL_ID_MUSIC,
14448     "background music on/off"
14449   },
14450   {
14451     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
14452     SOUND_CTRL_ID_LOOPS,
14453     "sound loops on/off"
14454   },
14455   {
14456     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
14457     SOUND_CTRL_ID_SIMPLE,
14458     "normal sounds on/off"
14459   }
14460 };
14461
14462 void CreateGameButtons()
14463 {
14464   int i;
14465
14466   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14467   {
14468     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
14469     struct GadgetInfo *gi;
14470     int button_type;
14471     boolean checked;
14472     unsigned long event_mask;
14473     int gd_xoffset, gd_yoffset;
14474     int gd_x1, gd_x2, gd_y1, gd_y2;
14475     int id = i;
14476
14477     gd_xoffset = gamebutton_info[i].x;
14478     gd_yoffset = gamebutton_info[i].y;
14479     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
14480     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
14481
14482     if (id == GAME_CTRL_ID_STOP ||
14483         id == GAME_CTRL_ID_PAUSE ||
14484         id == GAME_CTRL_ID_PLAY)
14485     {
14486       button_type = GD_TYPE_NORMAL_BUTTON;
14487       checked = FALSE;
14488       event_mask = GD_EVENT_RELEASED;
14489       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14490       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14491     }
14492     else
14493     {
14494       button_type = GD_TYPE_CHECK_BUTTON;
14495       checked =
14496         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14497          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14498          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14499       event_mask = GD_EVENT_PRESSED;
14500       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
14501       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14502     }
14503
14504     gi = CreateGadget(GDI_CUSTOM_ID, id,
14505                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14506                       GDI_X, DX + gd_xoffset,
14507                       GDI_Y, DY + gd_yoffset,
14508                       GDI_WIDTH, GAME_BUTTON_XSIZE,
14509                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
14510                       GDI_TYPE, button_type,
14511                       GDI_STATE, GD_BUTTON_UNPRESSED,
14512                       GDI_CHECKED, checked,
14513                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
14514                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
14515                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
14516                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
14517                       GDI_EVENT_MASK, event_mask,
14518                       GDI_CALLBACK_ACTION, HandleGameButtons,
14519                       GDI_END);
14520
14521     if (gi == NULL)
14522       Error(ERR_EXIT, "cannot create gadget");
14523
14524     game_gadget[id] = gi;
14525   }
14526 }
14527
14528 void FreeGameButtons()
14529 {
14530   int i;
14531
14532   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14533     FreeGadget(game_gadget[i]);
14534 }
14535
14536 static void MapGameButtons()
14537 {
14538   int i;
14539
14540   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14541     MapGadget(game_gadget[i]);
14542 }
14543
14544 void UnmapGameButtons()
14545 {
14546   int i;
14547
14548   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14549     UnmapGadget(game_gadget[i]);
14550 }
14551
14552 static void HandleGameButtons(struct GadgetInfo *gi)
14553 {
14554   int id = gi->custom_id;
14555
14556   if (game_status != GAME_MODE_PLAYING)
14557     return;
14558
14559   switch (id)
14560   {
14561     case GAME_CTRL_ID_STOP:
14562       if (tape.playing)
14563         TapeStop();
14564       else
14565         RequestQuitGame(TRUE);
14566       break;
14567
14568     case GAME_CTRL_ID_PAUSE:
14569       if (options.network)
14570       {
14571 #if defined(NETWORK_AVALIABLE)
14572         if (tape.pausing)
14573           SendToServer_ContinuePlaying();
14574         else
14575           SendToServer_PausePlaying();
14576 #endif
14577       }
14578       else
14579         TapeTogglePause(TAPE_TOGGLE_MANUAL);
14580       break;
14581
14582     case GAME_CTRL_ID_PLAY:
14583       if (tape.pausing)
14584       {
14585 #if defined(NETWORK_AVALIABLE)
14586         if (options.network)
14587           SendToServer_ContinuePlaying();
14588         else
14589 #endif
14590         {
14591           tape.pausing = FALSE;
14592           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
14593         }
14594       }
14595       break;
14596
14597     case SOUND_CTRL_ID_MUSIC:
14598       if (setup.sound_music)
14599       { 
14600         setup.sound_music = FALSE;
14601         FadeMusic();
14602       }
14603       else if (audio.music_available)
14604       { 
14605         setup.sound = setup.sound_music = TRUE;
14606
14607         SetAudioMode(setup.sound);
14608
14609         PlayLevelMusic();
14610       }
14611       break;
14612
14613     case SOUND_CTRL_ID_LOOPS:
14614       if (setup.sound_loops)
14615         setup.sound_loops = FALSE;
14616       else if (audio.loops_available)
14617       {
14618         setup.sound = setup.sound_loops = TRUE;
14619         SetAudioMode(setup.sound);
14620       }
14621       break;
14622
14623     case SOUND_CTRL_ID_SIMPLE:
14624       if (setup.sound_simple)
14625         setup.sound_simple = FALSE;
14626       else if (audio.sound_available)
14627       {
14628         setup.sound = setup.sound_simple = TRUE;
14629         SetAudioMode(setup.sound);
14630       }
14631       break;
14632
14633     default:
14634       break;
14635   }
14636 }