rnd-20070331-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61
62 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
63
64
65 /* for DigField() */
66 #define DF_NO_PUSH              0
67 #define DF_DIG                  1
68 #define DF_SNAP                 2
69
70 /* for MovePlayer() */
71 #define MP_NO_ACTION            0
72 #define MP_MOVING               1
73 #define MP_ACTION               2
74 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
75
76 /* for ScrollPlayer() */
77 #define SCROLL_INIT             0
78 #define SCROLL_GO_ON            1
79
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START          0
82 #define EX_TYPE_NONE            0
83 #define EX_TYPE_NORMAL          (1 << 0)
84 #define EX_TYPE_CENTER          (1 << 1)
85 #define EX_TYPE_BORDER          (1 << 2)
86 #define EX_TYPE_CROSS           (1 << 3)
87 #define EX_TYPE_DYNA            (1 << 4)
88 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
89
90 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
91 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
92 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
93 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
94
95 /* special positions in the game control window (relative to control window) */
96 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
97 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
98 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
99 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
100 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
101 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
102 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
103 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
104 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
105 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
106 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
107 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
108 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
109 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
110 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
111 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
112
113 /* special positions in the game control window (relative to main window) */
114 #define DX_LEVEL1               (DX + XX_LEVEL1)
115 #define DX_LEVEL2               (DX + XX_LEVEL2)
116 #define DX_LEVEL                (DX + XX_LEVEL)
117 #define DY_LEVEL                (DY + YY_LEVEL)
118 #define DX_EMERALDS             (DX + XX_EMERALDS)
119 #define DY_EMERALDS             (DY + YY_EMERALDS)
120 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
121 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
122 #define DX_KEYS                 (DX + XX_KEYS)
123 #define DY_KEYS                 (DY + YY_KEYS)
124 #define DX_SCORE                (DX + XX_SCORE)
125 #define DY_SCORE                (DY + YY_SCORE)
126 #define DX_TIME1                (DX + XX_TIME1)
127 #define DX_TIME2                (DX + XX_TIME2)
128 #define DX_TIME                 (DX + XX_TIME)
129 #define DY_TIME                 (DY + YY_TIME)
130
131 #if 1
132 /* game panel display and control definitions */
133
134 #define GAME_CONTROL_LEVEL_NUMBER               0
135 #define GAME_CONTROL_GEMS                       1
136 #define GAME_CONTROL_INVENTORY                  2
137 #define GAME_CONTROL_KEY_1                      3
138 #define GAME_CONTROL_KEY_2                      4
139 #define GAME_CONTROL_KEY_3                      5
140 #define GAME_CONTROL_KEY_4                      6
141 #define GAME_CONTROL_KEY_5                      7
142 #define GAME_CONTROL_KEY_6                      8
143 #define GAME_CONTROL_KEY_7                      9
144 #define GAME_CONTROL_KEY_8                      10
145 #define GAME_CONTROL_KEY_WHITE                  11
146 #define GAME_CONTROL_KEY_WHITE_COUNT            12
147 #define GAME_CONTROL_SCORE                      13
148 #define GAME_CONTROL_TIME                       14
149 #define GAME_CONTROL_TIME_HH                    15
150 #define GAME_CONTROL_TIME_MM                    16
151 #define GAME_CONTROL_TIME_SS                    17
152 #define GAME_CONTROL_DROP_NEXT_1                18
153 #define GAME_CONTROL_DROP_NEXT_2                19
154 #define GAME_CONTROL_DROP_NEXT_3                20
155 #define GAME_CONTROL_DROP_NEXT_4                21
156 #define GAME_CONTROL_DROP_NEXT_5                22
157 #define GAME_CONTROL_DROP_NEXT_6                23
158 #define GAME_CONTROL_DROP_NEXT_7                24
159 #define GAME_CONTROL_DROP_NEXT_8                25
160 #define GAME_CONTROL_SHIELD_NORMAL              26
161 #define GAME_CONTROL_SHIELD_NORMAL_TIME         27
162 #define GAME_CONTROL_SHIELD_DEADLY              28
163 #define GAME_CONTROL_SHIELD_DEADLY_TIME         29
164 #define GAME_CONTROL_EXIT                       30
165 #define GAME_CONTROL_EM_EXIT                    31
166 #define GAME_CONTROL_SP_EXIT                    32
167 #define GAME_CONTROL_STEEL_EXIT                 33
168 #define GAME_CONTROL_EM_STEEL_EXIT              34
169 #define GAME_CONTROL_EMC_MAGIC_BALL             35
170 #define GAME_CONTROL_EMC_MAGIC_BALL_SWITCH      36
171 #define GAME_CONTROL_LIGHT_SWITCH               37
172 #define GAME_CONTROL_LIGHT_SWITCH_TIME          38
173 #define GAME_CONTROL_TIMEGATE_SWITCH            39
174 #define GAME_CONTROL_TIMEGATE_SWITCH_TIME       40
175 #define GAME_CONTROL_SWITCHGATE_SWITCH          41
176 #define GAME_CONTROL_EMC_LENSES                 42
177 #define GAME_CONTROL_EMC_LENSES_TIME            43
178 #define GAME_CONTROL_EMC_MAGNIFIER              44
179 #define GAME_CONTROL_EMC_MAGNIFIER_TIME         45
180 #define GAME_CONTROL_BALLOON_SWITCH             46
181 #define GAME_CONTROL_DYNABOMB_NUMBER            47
182 #define GAME_CONTROL_DYNABOMB_SIZE              48
183 #define GAME_CONTROL_DYNABOMB_POWER             49
184 #define GAME_CONTROL_PENGUINS                   50
185 #define GAME_CONTROL_SOKOBAN_OBJECTS            51
186 #define GAME_CONTROL_SOKOBAN_FIELDS             52
187 #define GAME_CONTROL_ROBOT_WHEEL                53
188 #define GAME_CONTROL_CONVEYOR_BELT_1            54
189 #define GAME_CONTROL_CONVEYOR_BELT_1_SWITCH     55
190 #define GAME_CONTROL_CONVEYOR_BELT_2            56
191 #define GAME_CONTROL_CONVEYOR_BELT_2_SWITCH     57
192 #define GAME_CONTROL_CONVEYOR_BELT_3            58
193 #define GAME_CONTROL_CONVEYOR_BELT_3_SWITCH     59
194 #define GAME_CONTROL_CONVEYOR_BELT_4            60
195 #define GAME_CONTROL_CONVEYOR_BELT_4_SWITCH     61
196 #define GAME_CONTROL_MAGIC_WALL                 62
197 #define GAME_CONTROL_MAGIC_WALL_TIME            63
198 #define GAME_CONTROL_BD_MAGIC_WALL              64
199 #define GAME_CONTROL_DC_MAGIC_WALL              65
200 #define GAME_CONTROL_PLAYER_NAME                66
201 #define GAME_CONTROL_LEVEL_NAME                 67
202 #define GAME_CONTROL_LEVEL_AUTHOR               68
203
204 #define NUM_GAME_CONTROLS                       69
205
206 int game_control_value[NUM_GAME_CONTROLS];
207 int last_game_control_value[NUM_GAME_CONTROLS];
208
209 struct GameControlInfo
210 {
211   int nr;
212
213   struct TextPosInfo *pos;
214   int type;
215 };
216
217 static struct GameControlInfo game_controls[] =
218 {
219   {
220     GAME_CONTROL_LEVEL_NUMBER,
221     &game.panel.level_number,
222     TYPE_INTEGER,
223   },
224   {
225     GAME_CONTROL_GEMS,
226     &game.panel.gems,
227     TYPE_INTEGER,
228   },
229   {
230     GAME_CONTROL_INVENTORY,
231     &game.panel.inventory,
232     TYPE_INTEGER,
233   },
234   {
235     GAME_CONTROL_KEY_1,
236     &game.panel.key[0],
237     TYPE_ELEMENT,
238   },
239   {
240     GAME_CONTROL_KEY_2,
241     &game.panel.key[1],
242     TYPE_ELEMENT,
243   },
244   {
245     GAME_CONTROL_KEY_3,
246     &game.panel.key[2],
247     TYPE_ELEMENT,
248   },
249   {
250     GAME_CONTROL_KEY_4,
251     &game.panel.key[3],
252     TYPE_ELEMENT,
253   },
254   {
255     GAME_CONTROL_KEY_5,
256     &game.panel.key[4],
257     TYPE_ELEMENT,
258   },
259   {
260     GAME_CONTROL_KEY_6,
261     &game.panel.key[5],
262     TYPE_ELEMENT,
263   },
264   {
265     GAME_CONTROL_KEY_7,
266     &game.panel.key[6],
267     TYPE_ELEMENT,
268   },
269   {
270     GAME_CONTROL_KEY_8,
271     &game.panel.key[7],
272     TYPE_ELEMENT,
273   },
274   {
275     GAME_CONTROL_KEY_WHITE,
276     &game.panel.key_white,
277     TYPE_ELEMENT,
278   },
279   {
280     GAME_CONTROL_KEY_WHITE_COUNT,
281     &game.panel.key_white_count,
282     TYPE_INTEGER,
283   },
284   {
285     GAME_CONTROL_SCORE,
286     &game.panel.score,
287     TYPE_INTEGER,
288   },
289   {
290     GAME_CONTROL_TIME,
291     &game.panel.time,
292     TYPE_INTEGER,
293   },
294   {
295     GAME_CONTROL_TIME_HH,
296     &game.panel.time_hh,
297     TYPE_INTEGER,
298   },
299   {
300     GAME_CONTROL_TIME_MM,
301     &game.panel.time_mm,
302     TYPE_INTEGER,
303   },
304   {
305     GAME_CONTROL_TIME_SS,
306     &game.panel.time_ss,
307     TYPE_INTEGER,
308   },
309   {
310     GAME_CONTROL_DROP_NEXT_1,
311     &game.panel.drop_next_1,
312     TYPE_ELEMENT,
313   },
314   {
315     GAME_CONTROL_DROP_NEXT_2,
316     &game.panel.drop_next_2,
317     TYPE_ELEMENT,
318   },
319   {
320     GAME_CONTROL_DROP_NEXT_3,
321     &game.panel.drop_next_3,
322     TYPE_ELEMENT,
323   },
324   {
325     GAME_CONTROL_DROP_NEXT_4,
326     &game.panel.drop_next_4,
327     TYPE_ELEMENT,
328   },
329   {
330     GAME_CONTROL_DROP_NEXT_5,
331     &game.panel.drop_next_5,
332     TYPE_ELEMENT,
333   },
334   {
335     GAME_CONTROL_DROP_NEXT_6,
336     &game.panel.drop_next_6,
337     TYPE_ELEMENT,
338   },
339   {
340     GAME_CONTROL_DROP_NEXT_7,
341     &game.panel.drop_next_7,
342     TYPE_ELEMENT,
343   },
344   {
345     GAME_CONTROL_DROP_NEXT_8,
346     &game.panel.drop_next_8,
347     TYPE_ELEMENT,
348   },
349   {
350     GAME_CONTROL_SHIELD_NORMAL,
351     &game.panel.shield_normal,
352     TYPE_ELEMENT,
353   },
354   {
355     GAME_CONTROL_SHIELD_NORMAL_TIME,
356     &game.panel.shield_normal_time,
357     TYPE_INTEGER,
358   },
359   {
360     GAME_CONTROL_SHIELD_DEADLY,
361     &game.panel.shield_deadly,
362     TYPE_ELEMENT,
363   },
364   {
365     GAME_CONTROL_SHIELD_DEADLY_TIME,
366     &game.panel.shield_deadly_time,
367     TYPE_INTEGER,
368   },
369   {
370     GAME_CONTROL_EXIT,
371     &game.panel.exit,
372     TYPE_ELEMENT,
373   },
374   {
375     GAME_CONTROL_EM_EXIT,
376     &game.panel.em_exit,
377     TYPE_ELEMENT,
378   },
379   {
380     GAME_CONTROL_SP_EXIT,
381     &game.panel.sp_exit,
382     TYPE_ELEMENT,
383   },
384   {
385     GAME_CONTROL_STEEL_EXIT,
386     &game.panel.steel_exit,
387     TYPE_ELEMENT,
388   },
389   {
390     GAME_CONTROL_EM_STEEL_EXIT,
391     &game.panel.em_steel_exit,
392     TYPE_ELEMENT,
393   },
394   {
395     GAME_CONTROL_EMC_MAGIC_BALL,
396     &game.panel.emc_magic_ball,
397     TYPE_ELEMENT,
398   },
399   {
400     GAME_CONTROL_EMC_MAGIC_BALL_SWITCH,
401     &game.panel.emc_magic_ball_switch,
402     TYPE_ELEMENT,
403   },
404   {
405     GAME_CONTROL_LIGHT_SWITCH,
406     &game.panel.light_switch,
407     TYPE_ELEMENT,
408   },
409   {
410     GAME_CONTROL_LIGHT_SWITCH_TIME,
411     &game.panel.light_switch_time,
412     TYPE_INTEGER,
413   },
414   {
415     GAME_CONTROL_TIMEGATE_SWITCH,
416     &game.panel.timegate_switch,
417     TYPE_ELEMENT,
418   },
419   {
420     GAME_CONTROL_TIMEGATE_SWITCH_TIME,
421     &game.panel.timegate_switch_time,
422     TYPE_INTEGER,
423   },
424   {
425     GAME_CONTROL_SWITCHGATE_SWITCH,
426     &game.panel.switchgate_switch,
427     TYPE_ELEMENT,
428   },
429   {
430     GAME_CONTROL_EMC_LENSES,
431     &game.panel.emc_lenses,
432     TYPE_ELEMENT,
433   },
434   {
435     GAME_CONTROL_EMC_LENSES_TIME,
436     &game.panel.emc_lenses_time,
437     TYPE_INTEGER,
438   },
439   {
440     GAME_CONTROL_EMC_MAGNIFIER,
441     &game.panel.emc_magnifier,
442     TYPE_ELEMENT,
443   },
444   {
445     GAME_CONTROL_EMC_MAGNIFIER_TIME,
446     &game.panel.emc_magnifier_time,
447     TYPE_INTEGER,
448   },
449   {
450     GAME_CONTROL_BALLOON_SWITCH,
451     &game.panel.balloon_switch,
452     TYPE_ELEMENT,
453   },
454   {
455     GAME_CONTROL_DYNABOMB_NUMBER,
456     &game.panel.dynabomb_number,
457     TYPE_INTEGER,
458   },
459   {
460     GAME_CONTROL_DYNABOMB_SIZE,
461     &game.panel.dynabomb_size,
462     TYPE_INTEGER,
463   },
464   {
465     GAME_CONTROL_DYNABOMB_POWER,
466     &game.panel.dynabomb_power,
467     TYPE_ELEMENT,
468   },
469   {
470     GAME_CONTROL_PENGUINS,
471     &game.panel.penguins,
472     TYPE_INTEGER,
473   },
474   {
475     GAME_CONTROL_SOKOBAN_OBJECTS,
476     &game.panel.sokoban_objects,
477     TYPE_INTEGER,
478   },
479   {
480     GAME_CONTROL_SOKOBAN_FIELDS,
481     &game.panel.sokoban_fields,
482     TYPE_INTEGER,
483   },
484   {
485     GAME_CONTROL_ROBOT_WHEEL,
486     &game.panel.robot_wheel,
487     TYPE_ELEMENT,
488   },
489   {
490     GAME_CONTROL_CONVEYOR_BELT_1,
491     &game.panel.conveyor_belt_1,
492     TYPE_ELEMENT,
493   },
494   {
495     GAME_CONTROL_CONVEYOR_BELT_1_SWITCH,
496     &game.panel.conveyor_belt_1_switch,
497     TYPE_ELEMENT,
498   },
499   {
500     GAME_CONTROL_CONVEYOR_BELT_2,
501     &game.panel.conveyor_belt_2,
502     TYPE_ELEMENT,
503   },
504   {
505     GAME_CONTROL_CONVEYOR_BELT_2_SWITCH,
506     &game.panel.conveyor_belt_2_switch,
507     TYPE_ELEMENT,
508   },
509   {
510     GAME_CONTROL_CONVEYOR_BELT_3,
511     &game.panel.conveyor_belt_3,
512     TYPE_ELEMENT,
513   },
514   {
515     GAME_CONTROL_CONVEYOR_BELT_3_SWITCH,
516     &game.panel.conveyor_belt_3_switch,
517     TYPE_ELEMENT,
518   },
519   {
520     GAME_CONTROL_CONVEYOR_BELT_4,
521     &game.panel.conveyor_belt_4,
522     TYPE_ELEMENT,
523   },
524   {
525     GAME_CONTROL_CONVEYOR_BELT_4_SWITCH,
526     &game.panel.conveyor_belt_4_switch,
527     TYPE_ELEMENT,
528   },
529   {
530     GAME_CONTROL_MAGIC_WALL,
531     &game.panel.magic_wall,
532     TYPE_ELEMENT,
533   },
534   {
535     GAME_CONTROL_MAGIC_WALL_TIME,
536     &game.panel.magic_wall_time,
537     TYPE_INTEGER,
538   },
539   {
540     GAME_CONTROL_BD_MAGIC_WALL,
541     &game.panel.bd_magic_wall,
542     TYPE_ELEMENT,
543   },
544   {
545     GAME_CONTROL_DC_MAGIC_WALL,
546     &game.panel.dc_magic_wall,
547     TYPE_ELEMENT,
548   },
549   {
550     GAME_CONTROL_PLAYER_NAME,
551     &game.panel.player_name,
552     TYPE_STRING,
553   },
554   {
555     GAME_CONTROL_LEVEL_NAME,
556     &game.panel.level_name,
557     TYPE_STRING,
558   },
559   {
560     GAME_CONTROL_LEVEL_AUTHOR,
561     &game.panel.level_author,
562     TYPE_STRING,
563   },
564
565   {
566     -1,
567     NULL,
568     -1,
569   }
570 };
571 #endif
572
573
574 /* values for delayed check of falling and moving elements and for collision */
575 #define CHECK_DELAY_MOVING      3
576 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
577 #define CHECK_DELAY_COLLISION   2
578 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
579
580 /* values for initial player move delay (initial delay counter value) */
581 #define INITIAL_MOVE_DELAY_OFF  -1
582 #define INITIAL_MOVE_DELAY_ON   0
583
584 /* values for player movement speed (which is in fact a delay value) */
585 #define MOVE_DELAY_MIN_SPEED    32
586 #define MOVE_DELAY_NORMAL_SPEED 8
587 #define MOVE_DELAY_HIGH_SPEED   4
588 #define MOVE_DELAY_MAX_SPEED    1
589
590 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
591 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
592
593 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
594 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
595
596 /* values for other actions */
597 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
598 #define MOVE_STEPSIZE_MIN       (1)
599 #define MOVE_STEPSIZE_MAX       (TILEX)
600
601 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
602 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
603
604 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
605
606 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
607                                  RND(element_info[e].push_delay_random))
608 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
609                                  RND(element_info[e].drop_delay_random))
610 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
611                                  RND(element_info[e].move_delay_random))
612 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
613                                     (element_info[e].move_delay_random))
614 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
615                                  RND(element_info[e].ce_value_random_initial))
616 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
617 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
618                                  RND((c)->delay_random * (c)->delay_frames))
619 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
620                                  RND((c)->delay_random))
621
622
623 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
624          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
625
626 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
627         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
628          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
629          (be) + (e) - EL_SELF)
630
631 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
632         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
633          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
634          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
635          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
636          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
637          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
638          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
639          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
640          (e))
641
642 #define CAN_GROW_INTO(e)                                                \
643         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
644
645 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
646                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
647                                         (condition)))
648
649 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
650                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
651                                         (CAN_MOVE_INTO_ACID(e) &&       \
652                                          Feld[x][y] == EL_ACID) ||      \
653                                         (condition)))
654
655 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
656                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
657                                         (CAN_MOVE_INTO_ACID(e) &&       \
658                                          Feld[x][y] == EL_ACID) ||      \
659                                         (condition)))
660
661 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
662                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
663                                         (condition) ||                  \
664                                         (CAN_MOVE_INTO_ACID(e) &&       \
665                                          Feld[x][y] == EL_ACID) ||      \
666                                         (DONT_COLLIDE_WITH(e) &&        \
667                                          IS_PLAYER(x, y) &&             \
668                                          !PLAYER_ENEMY_PROTECTED(x, y))))
669
670 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
671         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
672
673 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
674         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
675
676 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
677         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
678
679 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
680         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
681                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
682
683 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
684         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
685
686 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
687         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
688
689 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
690         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
691
692 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
693         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
694
695 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
696         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
697
698 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
699         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
700                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
701                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
702                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
703                                                  IS_FOOD_PENGUIN(Feld[x][y])))
704 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
705         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
706
707 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
708         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
709
710 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
711         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
712
713 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
714         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
715                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
716
717 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
718
719 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
720                 (!IS_PLAYER(x, y) &&                                    \
721                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
722
723 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
724         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
725
726 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
727 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
728
729 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
730 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
731 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
732 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
733
734 /* game button identifiers */
735 #define GAME_CTRL_ID_STOP               0
736 #define GAME_CTRL_ID_PAUSE              1
737 #define GAME_CTRL_ID_PLAY               2
738 #define SOUND_CTRL_ID_MUSIC             3
739 #define SOUND_CTRL_ID_LOOPS             4
740 #define SOUND_CTRL_ID_SIMPLE            5
741
742 #define NUM_GAME_BUTTONS                6
743
744
745 /* forward declaration for internal use */
746
747 static void CreateField(int, int, int);
748
749 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
750 static void AdvanceFrameAndPlayerCounters(int);
751
752 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
753 static boolean MovePlayer(struct PlayerInfo *, int, int);
754 static void ScrollPlayer(struct PlayerInfo *, int);
755 static void ScrollScreen(struct PlayerInfo *, int);
756
757 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
758
759 static void InitBeltMovement(void);
760 static void CloseAllOpenTimegates(void);
761 static void CheckGravityMovement(struct PlayerInfo *);
762 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
763 static void KillPlayerUnlessEnemyProtected(int, int);
764 static void KillPlayerUnlessExplosionProtected(int, int);
765
766 static void TestIfPlayerTouchesCustomElement(int, int);
767 static void TestIfElementTouchesCustomElement(int, int);
768 static void TestIfElementHitsCustomElement(int, int, int);
769 #if 0
770 static void TestIfElementSmashesCustomElement(int, int, int);
771 #endif
772
773 static void HandleElementChange(int, int, int);
774 static void ExecuteCustomElementAction(int, int, int, int);
775 static boolean ChangeElement(int, int, int, int);
776
777 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
778 #define CheckTriggeredElementChange(x, y, e, ev)                        \
779         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
780 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
781         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
782 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
783         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
784 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
785         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
786
787 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
788 #define CheckElementChange(x, y, e, te, ev)                             \
789         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
790 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
791         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
792 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
793         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
794
795 static void PlayLevelSound(int, int, int);
796 static void PlayLevelSoundNearest(int, int, int);
797 static void PlayLevelSoundAction(int, int, int);
798 static void PlayLevelSoundElementAction(int, int, int, int);
799 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
800 static void PlayLevelSoundActionIfLoop(int, int, int);
801 static void StopLevelSoundActionIfLoop(int, int, int);
802 static void PlayLevelMusic();
803
804 static void MapGameButtons();
805 static void HandleGameButtons(struct GadgetInfo *);
806
807 int AmoebeNachbarNr(int, int);
808 void AmoebeUmwandeln(int, int);
809 void ContinueMoving(int, int);
810 void Bang(int, int);
811 void InitMovDir(int, int);
812 void InitAmoebaNr(int, int);
813 int NewHiScore(void);
814
815 void TestIfGoodThingHitsBadThing(int, int, int);
816 void TestIfBadThingHitsGoodThing(int, int, int);
817 void TestIfPlayerTouchesBadThing(int, int);
818 void TestIfPlayerRunsIntoBadThing(int, int, int);
819 void TestIfBadThingTouchesPlayer(int, int);
820 void TestIfBadThingRunsIntoPlayer(int, int, int);
821 void TestIfFriendTouchesBadThing(int, int);
822 void TestIfBadThingTouchesFriend(int, int);
823 void TestIfBadThingTouchesOtherBadThing(int, int);
824
825 void KillPlayer(struct PlayerInfo *);
826 void BuryPlayer(struct PlayerInfo *);
827 void RemovePlayer(struct PlayerInfo *);
828
829 boolean SnapField(struct PlayerInfo *, int, int);
830 boolean DropElement(struct PlayerInfo *);
831
832 static int getInvisibleActiveFromInvisibleElement(int);
833 static int getInvisibleFromInvisibleActiveElement(int);
834
835 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
836
837 /* for detection of endless loops, caused by custom element programming */
838 /* (using maximal playfield width x 10 is just a rough approximation) */
839 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
840
841 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
842 {                                                                       \
843   if (recursion_loop_detected)                                          \
844     return (rc);                                                        \
845                                                                         \
846   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
847   {                                                                     \
848     recursion_loop_detected = TRUE;                                     \
849     recursion_loop_element = (e);                                       \
850   }                                                                     \
851                                                                         \
852   recursion_loop_depth++;                                               \
853 }
854
855 #define RECURSION_LOOP_DETECTION_END()                                  \
856 {                                                                       \
857   recursion_loop_depth--;                                               \
858 }
859
860 static int recursion_loop_depth;
861 static boolean recursion_loop_detected;
862 static boolean recursion_loop_element;
863
864
865 /* ------------------------------------------------------------------------- */
866 /* definition of elements that automatically change to other elements after  */
867 /* a specified time, eventually calling a function when changing             */
868 /* ------------------------------------------------------------------------- */
869
870 /* forward declaration for changer functions */
871 static void InitBuggyBase(int, int);
872 static void WarnBuggyBase(int, int);
873
874 static void InitTrap(int, int);
875 static void ActivateTrap(int, int);
876 static void ChangeActiveTrap(int, int);
877
878 static void InitRobotWheel(int, int);
879 static void RunRobotWheel(int, int);
880 static void StopRobotWheel(int, int);
881
882 static void InitTimegateWheel(int, int);
883 static void RunTimegateWheel(int, int);
884
885 static void InitMagicBallDelay(int, int);
886 static void ActivateMagicBall(int, int);
887
888 struct ChangingElementInfo
889 {
890   int element;
891   int target_element;
892   int change_delay;
893   void (*pre_change_function)(int x, int y);
894   void (*change_function)(int x, int y);
895   void (*post_change_function)(int x, int y);
896 };
897
898 static struct ChangingElementInfo change_delay_list[] =
899 {
900   {
901     EL_NUT_BREAKING,
902     EL_EMERALD,
903     6,
904     NULL,
905     NULL,
906     NULL
907   },
908   {
909     EL_PEARL_BREAKING,
910     EL_EMPTY,
911     8,
912     NULL,
913     NULL,
914     NULL
915   },
916   {
917     EL_EXIT_OPENING,
918     EL_EXIT_OPEN,
919     29,
920     NULL,
921     NULL,
922     NULL
923   },
924   {
925     EL_EXIT_CLOSING,
926     EL_EXIT_CLOSED,
927     29,
928     NULL,
929     NULL,
930     NULL
931   },
932   {
933     EL_STEEL_EXIT_OPENING,
934     EL_STEEL_EXIT_OPEN,
935     29,
936     NULL,
937     NULL,
938     NULL
939   },
940   {
941     EL_STEEL_EXIT_CLOSING,
942     EL_STEEL_EXIT_CLOSED,
943     29,
944     NULL,
945     NULL,
946     NULL
947   },
948   {
949     EL_EM_EXIT_OPENING,
950     EL_EM_EXIT_OPEN,
951     29,
952     NULL,
953     NULL,
954     NULL
955   },
956   {
957     EL_EM_EXIT_CLOSING,
958 #if 1
959     EL_EMPTY,
960 #else
961     EL_EM_EXIT_CLOSED,
962 #endif
963     29,
964     NULL,
965     NULL,
966     NULL
967   },
968   {
969     EL_EM_STEEL_EXIT_OPENING,
970     EL_EM_STEEL_EXIT_OPEN,
971     29,
972     NULL,
973     NULL,
974     NULL
975   },
976   {
977     EL_EM_STEEL_EXIT_CLOSING,
978 #if 1
979     EL_STEELWALL,
980 #else
981     EL_EM_STEEL_EXIT_CLOSED,
982 #endif
983     29,
984     NULL,
985     NULL,
986     NULL
987   },
988   {
989     EL_SP_EXIT_OPENING,
990     EL_SP_EXIT_OPEN,
991     29,
992     NULL,
993     NULL,
994     NULL
995   },
996   {
997     EL_SP_EXIT_CLOSING,
998     EL_SP_EXIT_CLOSED,
999     29,
1000     NULL,
1001     NULL,
1002     NULL
1003   },
1004   {
1005     EL_SWITCHGATE_OPENING,
1006     EL_SWITCHGATE_OPEN,
1007     29,
1008     NULL,
1009     NULL,
1010     NULL
1011   },
1012   {
1013     EL_SWITCHGATE_CLOSING,
1014     EL_SWITCHGATE_CLOSED,
1015     29,
1016     NULL,
1017     NULL,
1018     NULL
1019   },
1020   {
1021     EL_TIMEGATE_OPENING,
1022     EL_TIMEGATE_OPEN,
1023     29,
1024     NULL,
1025     NULL,
1026     NULL
1027   },
1028   {
1029     EL_TIMEGATE_CLOSING,
1030     EL_TIMEGATE_CLOSED,
1031     29,
1032     NULL,
1033     NULL,
1034     NULL
1035   },
1036
1037   {
1038     EL_ACID_SPLASH_LEFT,
1039     EL_EMPTY,
1040     8,
1041     NULL,
1042     NULL,
1043     NULL
1044   },
1045   {
1046     EL_ACID_SPLASH_RIGHT,
1047     EL_EMPTY,
1048     8,
1049     NULL,
1050     NULL,
1051     NULL
1052   },
1053   {
1054     EL_SP_BUGGY_BASE,
1055     EL_SP_BUGGY_BASE_ACTIVATING,
1056     0,
1057     InitBuggyBase,
1058     NULL,
1059     NULL
1060   },
1061   {
1062     EL_SP_BUGGY_BASE_ACTIVATING,
1063     EL_SP_BUGGY_BASE_ACTIVE,
1064     0,
1065     InitBuggyBase,
1066     NULL,
1067     NULL
1068   },
1069   {
1070     EL_SP_BUGGY_BASE_ACTIVE,
1071     EL_SP_BUGGY_BASE,
1072     0,
1073     InitBuggyBase,
1074     WarnBuggyBase,
1075     NULL
1076   },
1077   {
1078     EL_TRAP,
1079     EL_TRAP_ACTIVE,
1080     0,
1081     InitTrap,
1082     NULL,
1083     ActivateTrap
1084   },
1085   {
1086     EL_TRAP_ACTIVE,
1087     EL_TRAP,
1088     31,
1089     NULL,
1090     ChangeActiveTrap,
1091     NULL
1092   },
1093   {
1094     EL_ROBOT_WHEEL_ACTIVE,
1095     EL_ROBOT_WHEEL,
1096     0,
1097     InitRobotWheel,
1098     RunRobotWheel,
1099     StopRobotWheel
1100   },
1101   {
1102     EL_TIMEGATE_SWITCH_ACTIVE,
1103     EL_TIMEGATE_SWITCH,
1104     0,
1105     InitTimegateWheel,
1106     RunTimegateWheel,
1107     NULL
1108   },
1109   {
1110     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1111     EL_DC_TIMEGATE_SWITCH,
1112     0,
1113     InitTimegateWheel,
1114     RunTimegateWheel,
1115     NULL
1116   },
1117   {
1118     EL_EMC_MAGIC_BALL_ACTIVE,
1119     EL_EMC_MAGIC_BALL_ACTIVE,
1120     0,
1121     InitMagicBallDelay,
1122     NULL,
1123     ActivateMagicBall
1124   },
1125   {
1126     EL_EMC_SPRING_BUMPER_ACTIVE,
1127     EL_EMC_SPRING_BUMPER,
1128     8,
1129     NULL,
1130     NULL,
1131     NULL
1132   },
1133   {
1134     EL_DIAGONAL_SHRINKING,
1135     EL_UNDEFINED,
1136     0,
1137     NULL,
1138     NULL,
1139     NULL
1140   },
1141   {
1142     EL_DIAGONAL_GROWING,
1143     EL_UNDEFINED,
1144     0,
1145     NULL,
1146     NULL,
1147     NULL,
1148   },
1149
1150   {
1151     EL_UNDEFINED,
1152     EL_UNDEFINED,
1153     -1,
1154     NULL,
1155     NULL,
1156     NULL
1157   }
1158 };
1159
1160 struct
1161 {
1162   int element;
1163   int push_delay_fixed, push_delay_random;
1164 }
1165 push_delay_list[] =
1166 {
1167   { EL_SPRING,                  0, 0 },
1168   { EL_BALLOON,                 0, 0 },
1169
1170   { EL_SOKOBAN_OBJECT,          2, 0 },
1171   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1172   { EL_SATELLITE,               2, 0 },
1173   { EL_SP_DISK_YELLOW,          2, 0 },
1174
1175   { EL_UNDEFINED,               0, 0 },
1176 };
1177
1178 struct
1179 {
1180   int element;
1181   int move_stepsize;
1182 }
1183 move_stepsize_list[] =
1184 {
1185   { EL_AMOEBA_DROP,             2 },
1186   { EL_AMOEBA_DROPPING,         2 },
1187   { EL_QUICKSAND_FILLING,       1 },
1188   { EL_QUICKSAND_EMPTYING,      1 },
1189   { EL_QUICKSAND_FAST_FILLING,  2 },
1190   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1191   { EL_MAGIC_WALL_FILLING,      2 },
1192   { EL_MAGIC_WALL_EMPTYING,     2 },
1193   { EL_BD_MAGIC_WALL_FILLING,   2 },
1194   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1195   { EL_DC_MAGIC_WALL_FILLING,   2 },
1196   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1197
1198   { EL_UNDEFINED,               0 },
1199 };
1200
1201 struct
1202 {
1203   int element;
1204   int count;
1205 }
1206 collect_count_list[] =
1207 {
1208   { EL_EMERALD,                 1 },
1209   { EL_BD_DIAMOND,              1 },
1210   { EL_EMERALD_YELLOW,          1 },
1211   { EL_EMERALD_RED,             1 },
1212   { EL_EMERALD_PURPLE,          1 },
1213   { EL_DIAMOND,                 3 },
1214   { EL_SP_INFOTRON,             1 },
1215   { EL_PEARL,                   5 },
1216   { EL_CRYSTAL,                 8 },
1217
1218   { EL_UNDEFINED,               0 },
1219 };
1220
1221 struct
1222 {
1223   int element;
1224   int direction;
1225 }
1226 access_direction_list[] =
1227 {
1228   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1229   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1230   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1231   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1232   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1233   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1234   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1235   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1236   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1237   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1238   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1239
1240   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1241   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1242   { EL_SP_PORT_UP,                                                   MV_DOWN },
1243   { EL_SP_PORT_DOWN,                                         MV_UP           },
1244   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1245   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1246   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1247   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1248   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1249   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1250   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1251   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1252   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1253   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1254   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1255   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1256   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1257   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1258   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1259
1260   { EL_UNDEFINED,                       MV_NONE                              }
1261 };
1262
1263 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1264
1265 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1266 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1267 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1268                                  IS_JUST_CHANGING(x, y))
1269
1270 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1271
1272 /* static variables for playfield scan mode (scanning forward or backward) */
1273 static int playfield_scan_start_x = 0;
1274 static int playfield_scan_start_y = 0;
1275 static int playfield_scan_delta_x = 1;
1276 static int playfield_scan_delta_y = 1;
1277
1278 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1279                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1280                                      (y) += playfield_scan_delta_y)     \
1281                                 for ((x) = playfield_scan_start_x;      \
1282                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1283                                      (x) += playfield_scan_delta_x)
1284
1285 #ifdef DEBUG
1286 void DEBUG_SetMaximumDynamite()
1287 {
1288   int i;
1289
1290   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1291     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1292       local_player->inventory_element[local_player->inventory_size++] =
1293         EL_DYNAMITE;
1294 }
1295 #endif
1296
1297 static void InitPlayfieldScanModeVars()
1298 {
1299   if (game.use_reverse_scan_direction)
1300   {
1301     playfield_scan_start_x = lev_fieldx - 1;
1302     playfield_scan_start_y = lev_fieldy - 1;
1303
1304     playfield_scan_delta_x = -1;
1305     playfield_scan_delta_y = -1;
1306   }
1307   else
1308   {
1309     playfield_scan_start_x = 0;
1310     playfield_scan_start_y = 0;
1311
1312     playfield_scan_delta_x = 1;
1313     playfield_scan_delta_y = 1;
1314   }
1315 }
1316
1317 static void InitPlayfieldScanMode(int mode)
1318 {
1319   game.use_reverse_scan_direction =
1320     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1321
1322   InitPlayfieldScanModeVars();
1323 }
1324
1325 static int get_move_delay_from_stepsize(int move_stepsize)
1326 {
1327   move_stepsize =
1328     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1329
1330   /* make sure that stepsize value is always a power of 2 */
1331   move_stepsize = (1 << log_2(move_stepsize));
1332
1333   return TILEX / move_stepsize;
1334 }
1335
1336 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1337                                boolean init_game)
1338 {
1339   int player_nr = player->index_nr;
1340   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1341   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1342
1343   /* do no immediately change move delay -- the player might just be moving */
1344   player->move_delay_value_next = move_delay;
1345
1346   /* information if player can move must be set separately */
1347   player->cannot_move = cannot_move;
1348
1349   if (init_game)
1350   {
1351     player->move_delay       = game.initial_move_delay[player_nr];
1352     player->move_delay_value = game.initial_move_delay_value[player_nr];
1353
1354     player->move_delay_value_next = -1;
1355
1356     player->move_delay_reset_counter = 0;
1357   }
1358 }
1359
1360 void GetPlayerConfig()
1361 {
1362   GameFrameDelay = setup.game_frame_delay;
1363
1364   if (!audio.sound_available)
1365     setup.sound_simple = FALSE;
1366
1367   if (!audio.loops_available)
1368     setup.sound_loops = FALSE;
1369
1370   if (!audio.music_available)
1371     setup.sound_music = FALSE;
1372
1373   if (!video.fullscreen_available)
1374     setup.fullscreen = FALSE;
1375
1376   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1377
1378   SetAudioMode(setup.sound);
1379   InitJoysticks();
1380 }
1381
1382 int GetElementFromGroupElement(int element)
1383 {
1384   if (IS_GROUP_ELEMENT(element))
1385   {
1386     struct ElementGroupInfo *group = element_info[element].group;
1387     int last_anim_random_frame = gfx.anim_random_frame;
1388     int element_pos;
1389
1390     if (group->choice_mode == ANIM_RANDOM)
1391       gfx.anim_random_frame = RND(group->num_elements_resolved);
1392
1393     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1394                                     group->choice_mode, 0,
1395                                     group->choice_pos);
1396
1397     if (group->choice_mode == ANIM_RANDOM)
1398       gfx.anim_random_frame = last_anim_random_frame;
1399
1400     group->choice_pos++;
1401
1402     element = group->element_resolved[element_pos];
1403   }
1404
1405   return element;
1406 }
1407
1408 static void InitPlayerField(int x, int y, int element, boolean init_game)
1409 {
1410   if (element == EL_SP_MURPHY)
1411   {
1412     if (init_game)
1413     {
1414       if (stored_player[0].present)
1415       {
1416         Feld[x][y] = EL_SP_MURPHY_CLONE;
1417
1418         return;
1419       }
1420       else
1421       {
1422         stored_player[0].use_murphy = TRUE;
1423
1424         if (!level.use_artwork_element[0])
1425           stored_player[0].artwork_element = EL_SP_MURPHY;
1426       }
1427
1428       Feld[x][y] = EL_PLAYER_1;
1429     }
1430   }
1431
1432   if (init_game)
1433   {
1434     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1435     int jx = player->jx, jy = player->jy;
1436
1437     player->present = TRUE;
1438
1439     player->block_last_field = (element == EL_SP_MURPHY ?
1440                                 level.sp_block_last_field :
1441                                 level.block_last_field);
1442
1443     /* ---------- initialize player's last field block delay --------------- */
1444
1445     /* always start with reliable default value (no adjustment needed) */
1446     player->block_delay_adjustment = 0;
1447
1448     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1449     if (player->block_last_field && element == EL_SP_MURPHY)
1450       player->block_delay_adjustment = 1;
1451
1452     /* special case 2: in game engines before 3.1.1, blocking was different */
1453     if (game.use_block_last_field_bug)
1454       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1455
1456     if (!options.network || player->connected)
1457     {
1458       player->active = TRUE;
1459
1460       /* remove potentially duplicate players */
1461       if (StorePlayer[jx][jy] == Feld[x][y])
1462         StorePlayer[jx][jy] = 0;
1463
1464       StorePlayer[x][y] = Feld[x][y];
1465
1466       if (options.debug)
1467       {
1468         printf("Player %d activated.\n", player->element_nr);
1469         printf("[Local player is %d and currently %s.]\n",
1470                local_player->element_nr,
1471                local_player->active ? "active" : "not active");
1472       }
1473     }
1474
1475     Feld[x][y] = EL_EMPTY;
1476
1477     player->jx = player->last_jx = x;
1478     player->jy = player->last_jy = y;
1479   }
1480 }
1481
1482 static void InitField(int x, int y, boolean init_game)
1483 {
1484   int element = Feld[x][y];
1485
1486   switch (element)
1487   {
1488     case EL_SP_MURPHY:
1489     case EL_PLAYER_1:
1490     case EL_PLAYER_2:
1491     case EL_PLAYER_3:
1492     case EL_PLAYER_4:
1493       InitPlayerField(x, y, element, init_game);
1494       break;
1495
1496     case EL_SOKOBAN_FIELD_PLAYER:
1497       element = Feld[x][y] = EL_PLAYER_1;
1498       InitField(x, y, init_game);
1499
1500       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1501       InitField(x, y, init_game);
1502       break;
1503
1504     case EL_SOKOBAN_FIELD_EMPTY:
1505       local_player->sokobanfields_still_needed++;
1506       break;
1507
1508     case EL_STONEBLOCK:
1509       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1510         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1511       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1512         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1513       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1514         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1515       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1516         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1517       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1518         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1519       break;
1520
1521     case EL_BUG:
1522     case EL_BUG_RIGHT:
1523     case EL_BUG_UP:
1524     case EL_BUG_LEFT:
1525     case EL_BUG_DOWN:
1526     case EL_SPACESHIP:
1527     case EL_SPACESHIP_RIGHT:
1528     case EL_SPACESHIP_UP:
1529     case EL_SPACESHIP_LEFT:
1530     case EL_SPACESHIP_DOWN:
1531     case EL_BD_BUTTERFLY:
1532     case EL_BD_BUTTERFLY_RIGHT:
1533     case EL_BD_BUTTERFLY_UP:
1534     case EL_BD_BUTTERFLY_LEFT:
1535     case EL_BD_BUTTERFLY_DOWN:
1536     case EL_BD_FIREFLY:
1537     case EL_BD_FIREFLY_RIGHT:
1538     case EL_BD_FIREFLY_UP:
1539     case EL_BD_FIREFLY_LEFT:
1540     case EL_BD_FIREFLY_DOWN:
1541     case EL_PACMAN_RIGHT:
1542     case EL_PACMAN_UP:
1543     case EL_PACMAN_LEFT:
1544     case EL_PACMAN_DOWN:
1545     case EL_YAMYAM:
1546     case EL_YAMYAM_LEFT:
1547     case EL_YAMYAM_RIGHT:
1548     case EL_YAMYAM_UP:
1549     case EL_YAMYAM_DOWN:
1550     case EL_DARK_YAMYAM:
1551     case EL_ROBOT:
1552     case EL_PACMAN:
1553     case EL_SP_SNIKSNAK:
1554     case EL_SP_ELECTRON:
1555     case EL_MOLE:
1556     case EL_MOLE_LEFT:
1557     case EL_MOLE_RIGHT:
1558     case EL_MOLE_UP:
1559     case EL_MOLE_DOWN:
1560       InitMovDir(x, y);
1561       break;
1562
1563     case EL_AMOEBA_FULL:
1564     case EL_BD_AMOEBA:
1565       InitAmoebaNr(x, y);
1566       break;
1567
1568     case EL_AMOEBA_DROP:
1569       if (y == lev_fieldy - 1)
1570       {
1571         Feld[x][y] = EL_AMOEBA_GROWING;
1572         Store[x][y] = EL_AMOEBA_WET;
1573       }
1574       break;
1575
1576     case EL_DYNAMITE_ACTIVE:
1577     case EL_SP_DISK_RED_ACTIVE:
1578     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1579     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1580     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1581     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1582       MovDelay[x][y] = 96;
1583       break;
1584
1585     case EL_EM_DYNAMITE_ACTIVE:
1586       MovDelay[x][y] = 32;
1587       break;
1588
1589     case EL_LAMP:
1590       local_player->lights_still_needed++;
1591       break;
1592
1593     case EL_PENGUIN:
1594       local_player->friends_still_needed++;
1595       break;
1596
1597     case EL_PIG:
1598     case EL_DRAGON:
1599       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1600       break;
1601
1602     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1603     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1604     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1605     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1606     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1607     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1608     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1609     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1610     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1611     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1612     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1613     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1614       if (init_game)
1615       {
1616         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1617         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1618         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1619
1620         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1621         {
1622           game.belt_dir[belt_nr] = belt_dir;
1623           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1624         }
1625         else    /* more than one switch -- set it like the first switch */
1626         {
1627           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1628         }
1629       }
1630       break;
1631
1632 #if !USE_BOTH_SWITCHGATE_SWITCHES
1633     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1634       if (init_game)
1635         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1636       break;
1637
1638     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1639       if (init_game)
1640         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1641       break;
1642 #endif
1643
1644     case EL_LIGHT_SWITCH_ACTIVE:
1645       if (init_game)
1646         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1647       break;
1648
1649     case EL_INVISIBLE_STEELWALL:
1650     case EL_INVISIBLE_WALL:
1651     case EL_INVISIBLE_SAND:
1652       if (game.light_time_left > 0 ||
1653           game.lenses_time_left > 0)
1654         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1655       break;
1656
1657     case EL_EMC_MAGIC_BALL:
1658       if (game.ball_state)
1659         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1660       break;
1661
1662     case EL_EMC_MAGIC_BALL_SWITCH:
1663       if (game.ball_state)
1664         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1665       break;
1666
1667     default:
1668       if (IS_CUSTOM_ELEMENT(element))
1669       {
1670         if (CAN_MOVE(element))
1671           InitMovDir(x, y);
1672
1673 #if USE_NEW_CUSTOM_VALUE
1674         if (!element_info[element].use_last_ce_value || init_game)
1675           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1676 #endif
1677       }
1678       else if (IS_GROUP_ELEMENT(element))
1679       {
1680         Feld[x][y] = GetElementFromGroupElement(element);
1681
1682         InitField(x, y, init_game);
1683       }
1684
1685       break;
1686   }
1687
1688   if (!init_game)
1689     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1690 }
1691
1692 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1693 {
1694   InitField(x, y, init_game);
1695
1696   /* not needed to call InitMovDir() -- already done by InitField()! */
1697   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1698       CAN_MOVE(Feld[x][y]))
1699     InitMovDir(x, y);
1700 }
1701
1702 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1703 {
1704   int old_element = Feld[x][y];
1705
1706   InitField(x, y, init_game);
1707
1708   /* not needed to call InitMovDir() -- already done by InitField()! */
1709   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1710       CAN_MOVE(old_element) &&
1711       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1712     InitMovDir(x, y);
1713
1714   /* this case is in fact a combination of not less than three bugs:
1715      first, it calls InitMovDir() for elements that can move, although this is
1716      already done by InitField(); then, it checks the element that was at this
1717      field _before_ the call to InitField() (which can change it); lastly, it
1718      was not called for "mole with direction" elements, which were treated as
1719      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1720   */
1721 }
1722
1723 #if 1
1724
1725 void InitGameControlValues()
1726 {
1727   int i;
1728
1729   for (i = 0; i < NUM_GAME_CONTROLS; i++)
1730     game_control_value[i] = last_game_control_value[i] = -1;
1731
1732   for (i = 0; game_controls[i].nr != -1; i++)
1733   {
1734     int nr = game_controls[i].nr;
1735     int type = game_controls[i].type;
1736     struct TextPosInfo *pos = game_controls[i].pos;
1737
1738     game_control_value[nr] = last_game_control_value[nr] = -1;
1739
1740     /* determine panel value width for later calculation of alignment */
1741     if (type == TYPE_INTEGER || type == TYPE_STRING)
1742       pos->width = pos->chars * getFontWidth(pos->font);
1743     else if (type == TYPE_ELEMENT)
1744       pos->width = MINI_TILESIZE;
1745   }
1746 }
1747
1748 void UpdateGameControlValues()
1749 {
1750   int i, j;
1751
1752   game_control_value[GAME_CONTROL_LEVEL_NUMBER] = level_nr;
1753   game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
1754
1755   game_control_value[GAME_CONTROL_INVENTORY] = 0;
1756   for (i = 0; i < MAX_NUM_KEYS; i++)
1757     game_control_value[GAME_CONTROL_KEY_1 + i] = 0;
1758   game_control_value[GAME_CONTROL_KEY_WHITE] = 0;
1759   game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] = 0;
1760
1761   if (game.centered_player_nr == -1)
1762   {
1763     for (i = 0; i < MAX_PLAYERS; i++)
1764     {
1765       for (j = 0; j < MAX_NUM_KEYS; j++)
1766         if (stored_player[i].key[j])
1767           game_control_value[GAME_CONTROL_KEY_1 + j] = 1;
1768
1769       game_control_value[GAME_CONTROL_INVENTORY] +=
1770         stored_player[i].inventory_size;
1771
1772       if (stored_player[i].num_white_keys > 0)
1773         game_control_value[GAME_CONTROL_KEY_WHITE] = 1;
1774
1775       game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
1776         stored_player[i].num_white_keys;
1777     }
1778   }
1779   else
1780   {
1781     int player_nr = game.centered_player_nr;
1782
1783     for (i = 0; i < MAX_NUM_KEYS; i++)
1784       if (stored_player[player_nr].key[i])
1785         game_control_value[GAME_CONTROL_KEY_1 + i] = 1;
1786
1787     game_control_value[GAME_CONTROL_INVENTORY] +=
1788       stored_player[player_nr].inventory_size;
1789
1790     if (stored_player[player_nr].num_white_keys > 0)
1791       game_control_value[GAME_CONTROL_KEY_WHITE] = 1;
1792
1793     game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
1794       stored_player[player_nr].num_white_keys;
1795   }
1796
1797   game_control_value[GAME_CONTROL_SCORE] = (local_player->LevelSolved ?
1798                                             local_player->score_final :
1799                                             local_player->score);
1800
1801   game_control_value[GAME_CONTROL_TIME] = (level.time == 0 ?
1802                                            TimePlayed :
1803                                            TimeLeft);
1804
1805   game_control_value[GAME_CONTROL_TIME_HH] = TapeTime / 3600;
1806   game_control_value[GAME_CONTROL_TIME_MM] = (TapeTime / 60) % 60;
1807   game_control_value[GAME_CONTROL_TIME_SS] = TapeTime % 60;
1808
1809   for (i = 0; i < 8; i++)
1810     game_control_value[GAME_CONTROL_DROP_NEXT_1 + i] = EL_UNDEFINED;
1811
1812   game_control_value[GAME_CONTROL_SHIELD_NORMAL] =
1813     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
1814      EL_EMPTY);
1815   game_control_value[GAME_CONTROL_SHIELD_NORMAL_TIME] =
1816     local_player->shield_normal_time_left;
1817   game_control_value[GAME_CONTROL_SHIELD_DEADLY] =
1818     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
1819      EL_EMPTY);
1820   game_control_value[GAME_CONTROL_SHIELD_DEADLY_TIME] =
1821     local_player->shield_deadly_time_left;
1822
1823   if (local_player->gems_still_needed > 0 ||
1824       local_player->sokobanfields_still_needed > 0 ||
1825       local_player->lights_still_needed > 0)
1826   {
1827     game_control_value[GAME_CONTROL_EXIT]          = EL_EXIT_CLOSED;
1828     game_control_value[GAME_CONTROL_EM_EXIT]       = EL_EM_EXIT_CLOSED;
1829     game_control_value[GAME_CONTROL_SP_EXIT]       = EL_SP_EXIT_CLOSED;
1830     game_control_value[GAME_CONTROL_STEEL_EXIT]    = EL_STEEL_EXIT_CLOSED;
1831     game_control_value[GAME_CONTROL_EM_STEEL_EXIT] = EL_EM_STEEL_EXIT_CLOSED;
1832   }
1833   else
1834   {
1835     game_control_value[GAME_CONTROL_EXIT]          = EL_EXIT_OPEN;
1836     game_control_value[GAME_CONTROL_EM_EXIT]       = EL_EM_EXIT_OPEN;
1837     game_control_value[GAME_CONTROL_SP_EXIT]       = EL_SP_EXIT_OPEN;
1838     game_control_value[GAME_CONTROL_STEEL_EXIT]    = EL_STEEL_EXIT_OPEN;
1839     game_control_value[GAME_CONTROL_EM_STEEL_EXIT] = EL_EM_STEEL_EXIT_OPEN;
1840   }
1841
1842   game_control_value[GAME_CONTROL_EMC_MAGIC_BALL] = EL_UNDEFINED;
1843   game_control_value[GAME_CONTROL_EMC_MAGIC_BALL_SWITCH] = EL_UNDEFINED;
1844
1845   game_control_value[GAME_CONTROL_LIGHT_SWITCH] =
1846     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
1847   game_control_value[GAME_CONTROL_LIGHT_SWITCH_TIME] = game.light_time_left;
1848
1849   game_control_value[GAME_CONTROL_TIMEGATE_SWITCH] =
1850     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
1851   game_control_value[GAME_CONTROL_TIMEGATE_SWITCH_TIME] =
1852     game.timegate_time_left;
1853
1854   game_control_value[GAME_CONTROL_SWITCHGATE_SWITCH] = EL_UNDEFINED;
1855
1856   game_control_value[GAME_CONTROL_EMC_LENSES] =
1857     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
1858   game_control_value[GAME_CONTROL_EMC_LENSES_TIME] = game.lenses_time_left;
1859
1860   game_control_value[GAME_CONTROL_EMC_MAGNIFIER] =
1861     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
1862   game_control_value[GAME_CONTROL_EMC_MAGNIFIER_TIME] = game.magnify_time_left;
1863
1864   game_control_value[GAME_CONTROL_BALLOON_SWITCH] =
1865     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
1866      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
1867      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
1868      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
1869      EL_BALLOON_SWITCH_NONE);
1870
1871   game_control_value[GAME_CONTROL_DYNABOMB_NUMBER] =
1872     local_player->dynabomb_count;
1873   game_control_value[GAME_CONTROL_DYNABOMB_SIZE] =
1874     local_player->dynabomb_size;
1875   game_control_value[GAME_CONTROL_DYNABOMB_POWER] =
1876     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
1877
1878   game_control_value[GAME_CONTROL_PENGUINS] =
1879     local_player->friends_still_needed;
1880
1881   game_control_value[GAME_CONTROL_SOKOBAN_OBJECTS] =
1882     local_player->sokobanfields_still_needed;
1883   game_control_value[GAME_CONTROL_SOKOBAN_FIELDS] =
1884     local_player->sokobanfields_still_needed;
1885
1886   game_control_value[GAME_CONTROL_ROBOT_WHEEL] = EL_UNDEFINED;
1887
1888   game_control_value[GAME_CONTROL_CONVEYOR_BELT_1] = EL_UNDEFINED;
1889   game_control_value[GAME_CONTROL_CONVEYOR_BELT_1_SWITCH] = EL_UNDEFINED;
1890   game_control_value[GAME_CONTROL_CONVEYOR_BELT_2] = EL_UNDEFINED;
1891   game_control_value[GAME_CONTROL_CONVEYOR_BELT_2_SWITCH] = EL_UNDEFINED;
1892   game_control_value[GAME_CONTROL_CONVEYOR_BELT_3] = EL_UNDEFINED;
1893   game_control_value[GAME_CONTROL_CONVEYOR_BELT_3_SWITCH] = EL_UNDEFINED;
1894   game_control_value[GAME_CONTROL_CONVEYOR_BELT_4] = EL_UNDEFINED;
1895   game_control_value[GAME_CONTROL_CONVEYOR_BELT_4_SWITCH] = EL_UNDEFINED;
1896
1897   game_control_value[GAME_CONTROL_MAGIC_WALL] = EL_UNDEFINED;
1898   game_control_value[GAME_CONTROL_MAGIC_WALL_TIME] =
1899     game.magic_wall_time_left;
1900   game_control_value[GAME_CONTROL_BD_MAGIC_WALL] = EL_UNDEFINED;
1901   game_control_value[GAME_CONTROL_DC_MAGIC_WALL] = EL_UNDEFINED;
1902
1903   game_control_value[GAME_CONTROL_PLAYER_NAME] = 0;
1904   game_control_value[GAME_CONTROL_LEVEL_NAME] = 0;
1905   game_control_value[GAME_CONTROL_LEVEL_AUTHOR] = 0;
1906 }
1907
1908 void DisplayGameControlValues()
1909 {
1910   int i;
1911
1912   for (i = 0; game_controls[i].nr != -1; i++)
1913   {
1914     int nr = game_controls[i].nr;
1915     int type = game_controls[i].type;
1916     struct TextPosInfo *pos = game_controls[i].pos;
1917     int value = game_control_value[nr];
1918     int last_value = last_game_control_value[nr];
1919     int chars = pos->chars;
1920     int font = pos->font;
1921
1922     if (value == last_value)
1923       continue;
1924
1925     last_game_control_value[nr] = value;
1926
1927 #if 0
1928     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
1929 #endif
1930
1931     if (PANEL_DEACTIVATED(pos))
1932       continue;
1933
1934     if (type == TYPE_INTEGER)
1935     {
1936       if (nr == GAME_CONTROL_LEVEL_NUMBER || nr == GAME_CONTROL_TIME)
1937       {
1938         boolean use_dynamic_chars = (pos->chars == -1 ? TRUE : FALSE);
1939
1940         if (use_dynamic_chars)          /* use dynamic number of chars */
1941         {
1942           int value_change = (nr == GAME_CONTROL_LEVEL_NUMBER ? 100 : 1000);
1943           int chars1 = (nr == GAME_CONTROL_LEVEL_NUMBER ? 2 : 3);
1944           int chars2 = chars1 + 1;
1945           int font1 = pos->font;
1946           int font2 = pos->font_alt;
1947
1948           chars = (value < value_change ? chars1 : chars2);
1949           font  = (value < value_change ? font1  : font2);
1950
1951           /* clear background if value just changed its size (dynamic chars) */
1952           if ((last_value < value_change) != (value < value_change))
1953           {
1954             int width1 = chars1 * getFontWidth(font1);
1955             int width2 = chars2 * getFontWidth(font2);
1956             int max_width = MAX(width1, width2);
1957             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
1958
1959             pos->width = max_width;
1960
1961             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
1962                                        max_width, max_height);
1963           }
1964         }
1965
1966         pos->width = chars * getFontWidth(font);
1967       }
1968
1969       DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font);
1970     }
1971     else if (type == TYPE_ELEMENT)
1972     {
1973       if (nr >= GAME_CONTROL_KEY_1 && nr <= GAME_CONTROL_KEY_8)
1974       {
1975         int key_nr = nr - GAME_CONTROL_KEY_1;
1976         int src_x = DOOR_GFX_PAGEX5 + 18 + (key_nr % STD_NUM_KEYS) * MINI_TILEX;
1977         int src_y = DOOR_GFX_PAGEY1 + 123;
1978         int dst_x = PANEL_XPOS(pos);
1979         int dst_y = PANEL_YPOS(pos);
1980         int element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1981                        level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1982                        EL_EM_KEY_1 : EL_KEY_1) + key_nr;
1983         int graphic = el2edimg(element);
1984
1985         if (value)
1986           DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
1987         else
1988           BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1989                      MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1990       }
1991       else if (value != EL_UNDEFINED)
1992       {
1993         int graphic = el2edimg(value);
1994         int dst_x = PANEL_XPOS(pos);
1995         int dst_y = PANEL_YPOS(pos);
1996
1997         DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
1998       }
1999     }
2000     else if (type == TYPE_STRING)
2001     {
2002       char *s = (nr == GAME_CONTROL_PLAYER_NAME  ? setup.player_name :
2003                  nr == GAME_CONTROL_LEVEL_NAME   ? level.name :
2004                  nr == GAME_CONTROL_LEVEL_AUTHOR ? level.author : NULL);
2005
2006       if (s != NULL)
2007       {
2008         char *s_cut = getStringCopyN(s, pos->chars);
2009
2010         DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), s_cut, pos->font);
2011
2012         free(s_cut);
2013       }
2014     }
2015
2016     redraw_mask |= REDRAW_DOOR_1;
2017   }
2018 }
2019
2020 void DrawGameValue_Emeralds(int value)
2021 {
2022   struct TextPosInfo *pos = &game.panel.gems;
2023 #if 1
2024   int font_nr = pos->font;
2025 #else
2026   int font_nr = FONT_TEXT_2;
2027 #endif
2028   int font_width = getFontWidth(font_nr);
2029   int chars = pos->chars;
2030
2031 #if 1
2032   return;       /* !!! USE NEW STUFF !!! */
2033 #endif
2034
2035   if (PANEL_DEACTIVATED(pos))
2036     return;
2037
2038   pos->width = chars * font_width;
2039
2040   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2041 }
2042
2043 void DrawGameValue_Dynamite(int value)
2044 {
2045   struct TextPosInfo *pos = &game.panel.inventory;
2046 #if 1
2047   int font_nr = pos->font;
2048 #else
2049   int font_nr = FONT_TEXT_2;
2050 #endif
2051   int font_width = getFontWidth(font_nr);
2052   int chars = pos->chars;
2053
2054 #if 1
2055   return;       /* !!! USE NEW STUFF !!! */
2056 #endif
2057
2058   if (PANEL_DEACTIVATED(pos))
2059     return;
2060
2061   pos->width = chars * font_width;
2062
2063   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2064 }
2065
2066 void DrawGameValue_Score(int value)
2067 {
2068   struct TextPosInfo *pos = &game.panel.score;
2069 #if 1
2070   int font_nr = pos->font;
2071 #else
2072   int font_nr = FONT_TEXT_2;
2073 #endif
2074   int font_width = getFontWidth(font_nr);
2075   int chars = pos->chars;
2076
2077 #if 1
2078   return;       /* !!! USE NEW STUFF !!! */
2079 #endif
2080
2081   if (PANEL_DEACTIVATED(pos))
2082     return;
2083
2084   pos->width = chars * font_width;
2085
2086   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2087 }
2088
2089 void DrawGameValue_Time(int value)
2090 {
2091   struct TextPosInfo *pos = &game.panel.time;
2092   static int last_value = -1;
2093   int chars1 = 3;
2094   int chars2 = 4;
2095   int chars = pos->chars;
2096 #if 1
2097   int font1_nr = pos->font;
2098   int font2_nr = pos->font_alt;
2099 #else
2100   int font1_nr = FONT_TEXT_2;
2101   int font2_nr = FONT_TEXT_1;
2102 #endif
2103   int font_nr = font1_nr;
2104   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2105
2106 #if 1
2107   return;       /* !!! USE NEW STUFF !!! */
2108 #endif
2109
2110   if (PANEL_DEACTIVATED(pos))
2111     return;
2112
2113   if (use_dynamic_chars)                /* use dynamic number of chars */
2114   {
2115     chars   = (value < 1000 ? chars1   : chars2);
2116     font_nr = (value < 1000 ? font1_nr : font2_nr);
2117   }
2118
2119   /* clear background if value just changed its size (dynamic chars only) */
2120   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2121   {
2122     int width1 = chars1 * getFontWidth(font1_nr);
2123     int width2 = chars2 * getFontWidth(font2_nr);
2124     int max_width = MAX(width1, width2);
2125     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2126
2127     pos->width = max_width;
2128
2129     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2130                                max_width, max_height);
2131   }
2132
2133   pos->width = chars * getFontWidth(font_nr);
2134
2135   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2136
2137   last_value = value;
2138 }
2139
2140 void DrawGameValue_Level(int value)
2141 {
2142   struct TextPosInfo *pos = &game.panel.level_number;
2143   int chars1 = 2;
2144   int chars2 = 3;
2145   int chars = pos->chars;
2146 #if 1
2147   int font1_nr = pos->font;
2148   int font2_nr = pos->font_alt;
2149 #else
2150   int font1_nr = FONT_TEXT_2;
2151   int font2_nr = FONT_TEXT_1;
2152 #endif
2153   int font_nr = font1_nr;
2154   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2155
2156 #if 1
2157   return;       /* !!! USE NEW STUFF !!! */
2158 #endif
2159
2160   if (PANEL_DEACTIVATED(pos))
2161     return;
2162
2163   if (use_dynamic_chars)                /* use dynamic number of chars */
2164   {
2165     chars   = (level_nr < 100 ? chars1   : chars2);
2166     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2167   }
2168
2169   pos->width = chars * getFontWidth(font_nr);
2170
2171   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2172 }
2173
2174 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2175 {
2176 #if 0
2177   struct TextPosInfo *pos = &game.panel.keys;
2178 #endif
2179 #if 0
2180   int base_key_graphic = EL_KEY_1;
2181 #endif
2182   int i;
2183
2184 #if 1
2185   return;       /* !!! USE NEW STUFF !!! */
2186 #endif
2187
2188 #if 0
2189   if (PANEL_DEACTIVATED(pos))
2190     return;
2191 #endif
2192
2193 #if 0
2194   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2195     base_key_graphic = EL_EM_KEY_1;
2196 #endif
2197
2198 #if 0
2199   pos->width = 4 * MINI_TILEX;
2200 #endif
2201
2202 #if 1
2203   for (i = 0; i < MAX_NUM_KEYS; i++)
2204 #else
2205   /* currently only 4 of 8 possible keys are displayed */
2206   for (i = 0; i < STD_NUM_KEYS; i++)
2207 #endif
2208   {
2209 #if 1
2210     struct TextPosInfo *pos = &game.panel.key[i];
2211 #endif
2212     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2213     int src_y = DOOR_GFX_PAGEY1 + 123;
2214 #if 1
2215     int dst_x = PANEL_XPOS(pos);
2216     int dst_y = PANEL_YPOS(pos);
2217 #else
2218     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2219     int dst_y = PANEL_YPOS(pos);
2220 #endif
2221
2222 #if 1
2223     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2224                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2225                    EL_KEY_1) + i;
2226     int graphic = el2edimg(element);
2227 #endif
2228
2229 #if 1
2230     if (PANEL_DEACTIVATED(pos))
2231       continue;
2232 #endif
2233
2234 #if 0
2235     /* masked blit with tiles from half-size scaled bitmap does not work yet
2236        (no mask bitmap created for these sizes after loading and scaling) --
2237        solution: load without creating mask, scale, then create final mask */
2238
2239     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2240                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2241
2242     if (key[i])
2243     {
2244 #if 0
2245       int graphic = el2edimg(base_key_graphic + i);
2246 #endif
2247       Bitmap *src_bitmap;
2248       int src_x, src_y;
2249
2250       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2251
2252       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2253                     dst_x - src_x, dst_y - src_y);
2254       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2255                        dst_x, dst_y);
2256     }
2257 #else
2258 #if 1
2259     if (key[i])
2260       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2261     else
2262       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2263                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2264 #else
2265     if (key[i])
2266       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2267     else
2268       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2269                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2270 #endif
2271 #endif
2272   }
2273 }
2274
2275 #else
2276
2277 void DrawGameValue_Emeralds(int value)
2278 {
2279   int font_nr = FONT_TEXT_2;
2280   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2281
2282   if (PANEL_DEACTIVATED(game.panel.gems))
2283     return;
2284
2285   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2286 }
2287
2288 void DrawGameValue_Dynamite(int value)
2289 {
2290   int font_nr = FONT_TEXT_2;
2291   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2292
2293   if (PANEL_DEACTIVATED(game.panel.inventory))
2294     return;
2295
2296   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2297 }
2298
2299 void DrawGameValue_Score(int value)
2300 {
2301   int font_nr = FONT_TEXT_2;
2302   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2303
2304   if (PANEL_DEACTIVATED(game.panel.score))
2305     return;
2306
2307   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2308 }
2309
2310 void DrawGameValue_Time(int value)
2311 {
2312   int font1_nr = FONT_TEXT_2;
2313 #if 1
2314   int font2_nr = FONT_TEXT_1;
2315 #else
2316   int font2_nr = FONT_LEVEL_NUMBER;
2317 #endif
2318   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2319   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2320
2321   if (PANEL_DEACTIVATED(game.panel.time))
2322     return;
2323
2324   /* clear background if value just changed its size */
2325   if (value == 999 || value == 1000)
2326     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2327
2328   if (value < 1000)
2329     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2330   else
2331     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2332 }
2333
2334 void DrawGameValue_Level(int value)
2335 {
2336   int font1_nr = FONT_TEXT_2;
2337 #if 1
2338   int font2_nr = FONT_TEXT_1;
2339 #else
2340   int font2_nr = FONT_LEVEL_NUMBER;
2341 #endif
2342
2343   if (PANEL_DEACTIVATED(game.panel.level))
2344     return;
2345
2346   if (level_nr < 100)
2347     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2348   else
2349     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2350 }
2351
2352 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2353 {
2354   int base_key_graphic = EL_KEY_1;
2355   int i;
2356
2357   if (PANEL_DEACTIVATED(game.panel.keys))
2358     return;
2359
2360   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2361     base_key_graphic = EL_EM_KEY_1;
2362
2363   /* currently only 4 of 8 possible keys are displayed */
2364   for (i = 0; i < STD_NUM_KEYS; i++)
2365   {
2366     int x = XX_KEYS + i * MINI_TILEX;
2367     int y = YY_KEYS;
2368
2369     if (key[i])
2370       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2371     else
2372       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2373                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2374   }
2375 }
2376
2377 #endif
2378
2379 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2380                        int key_bits)
2381 {
2382   int key[MAX_NUM_KEYS];
2383   int i;
2384
2385   /* prevent EM engine from updating time/score values parallel to GameWon() */
2386   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2387       local_player->LevelSolved)
2388     return;
2389
2390   for (i = 0; i < MAX_NUM_KEYS; i++)
2391     key[i] = key_bits & (1 << i);
2392
2393   DrawGameValue_Level(level_nr);
2394
2395   DrawGameValue_Emeralds(emeralds);
2396   DrawGameValue_Dynamite(dynamite);
2397   DrawGameValue_Score(score);
2398   DrawGameValue_Time(time);
2399
2400   DrawGameValue_Keys(key);
2401 }
2402
2403 void DrawGameDoorValues()
2404 {
2405   UpdateGameControlValues();
2406   DisplayGameControlValues();
2407 }
2408
2409 void DrawGameDoorValues_OLD()
2410 {
2411   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2412   int dynamite_value = 0;
2413   int score_value = (local_player->LevelSolved ? local_player->score_final :
2414                      local_player->score);
2415   int gems_value = local_player->gems_still_needed;
2416   int key_bits = 0;
2417   int i, j;
2418
2419   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2420   {
2421     DrawGameDoorValues_EM();
2422
2423     return;
2424   }
2425
2426   if (game.centered_player_nr == -1)
2427   {
2428     for (i = 0; i < MAX_PLAYERS; i++)
2429     {
2430       for (j = 0; j < MAX_NUM_KEYS; j++)
2431         if (stored_player[i].key[j])
2432           key_bits |= (1 << j);
2433
2434       dynamite_value += stored_player[i].inventory_size;
2435     }
2436   }
2437   else
2438   {
2439     int player_nr = game.centered_player_nr;
2440
2441     for (i = 0; i < MAX_NUM_KEYS; i++)
2442       if (stored_player[player_nr].key[i])
2443         key_bits |= (1 << i);
2444
2445     dynamite_value = stored_player[player_nr].inventory_size;
2446   }
2447
2448   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2449                     key_bits);
2450 }
2451
2452
2453 /*
2454   =============================================================================
2455   InitGameEngine()
2456   -----------------------------------------------------------------------------
2457   initialize game engine due to level / tape version number
2458   =============================================================================
2459 */
2460
2461 static void InitGameEngine()
2462 {
2463   int i, j, k, l, x, y;
2464
2465   /* set game engine from tape file when re-playing, else from level file */
2466   game.engine_version = (tape.playing ? tape.engine_version :
2467                          level.game_version);
2468
2469   /* ---------------------------------------------------------------------- */
2470   /* set flags for bugs and changes according to active game engine version */
2471   /* ---------------------------------------------------------------------- */
2472
2473   /*
2474     Summary of bugfix/change:
2475     Fixed handling for custom elements that change when pushed by the player.
2476
2477     Fixed/changed in version:
2478     3.1.0
2479
2480     Description:
2481     Before 3.1.0, custom elements that "change when pushing" changed directly
2482     after the player started pushing them (until then handled in "DigField()").
2483     Since 3.1.0, these custom elements are not changed until the "pushing"
2484     move of the element is finished (now handled in "ContinueMoving()").
2485
2486     Affected levels/tapes:
2487     The first condition is generally needed for all levels/tapes before version
2488     3.1.0, which might use the old behaviour before it was changed; known tapes
2489     that are affected are some tapes from the level set "Walpurgis Gardens" by
2490     Jamie Cullen.
2491     The second condition is an exception from the above case and is needed for
2492     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2493     above (including some development versions of 3.1.0), but before it was
2494     known that this change would break tapes like the above and was fixed in
2495     3.1.1, so that the changed behaviour was active although the engine version
2496     while recording maybe was before 3.1.0. There is at least one tape that is
2497     affected by this exception, which is the tape for the one-level set "Bug
2498     Machine" by Juergen Bonhagen.
2499   */
2500
2501   game.use_change_when_pushing_bug =
2502     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2503      !(tape.playing &&
2504        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2505        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2506
2507   /*
2508     Summary of bugfix/change:
2509     Fixed handling for blocking the field the player leaves when moving.
2510
2511     Fixed/changed in version:
2512     3.1.1
2513
2514     Description:
2515     Before 3.1.1, when "block last field when moving" was enabled, the field
2516     the player is leaving when moving was blocked for the time of the move,
2517     and was directly unblocked afterwards. This resulted in the last field
2518     being blocked for exactly one less than the number of frames of one player
2519     move. Additionally, even when blocking was disabled, the last field was
2520     blocked for exactly one frame.
2521     Since 3.1.1, due to changes in player movement handling, the last field
2522     is not blocked at all when blocking is disabled. When blocking is enabled,
2523     the last field is blocked for exactly the number of frames of one player
2524     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2525     last field is blocked for exactly one more than the number of frames of
2526     one player move.
2527
2528     Affected levels/tapes:
2529     (!!! yet to be determined -- probably many !!!)
2530   */
2531
2532   game.use_block_last_field_bug =
2533     (game.engine_version < VERSION_IDENT(3,1,1,0));
2534
2535   /*
2536     Summary of bugfix/change:
2537     Changed behaviour of CE changes with multiple changes per single frame.
2538
2539     Fixed/changed in version:
2540     3.2.0-6
2541
2542     Description:
2543     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
2544     This resulted in race conditions where CEs seem to behave strange in some
2545     situations (where triggered CE changes were just skipped because there was
2546     already a CE change on that tile in the playfield in that engine frame).
2547     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
2548     (The number of changes per frame must be limited in any case, because else
2549     it is easily possible to define CE changes that would result in an infinite
2550     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
2551     should be set large enough so that it would only be reached in cases where
2552     the corresponding CE change conditions run into a loop. Therefore, it seems
2553     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
2554     maximal number of change pages for custom elements.)
2555
2556     Affected levels/tapes:
2557     Probably many.
2558   */
2559
2560 #if USE_ONLY_ONE_CHANGE_PER_FRAME
2561   game.max_num_changes_per_frame = 1;
2562 #else
2563   game.max_num_changes_per_frame =
2564     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
2565 #endif
2566
2567   /* ---------------------------------------------------------------------- */
2568
2569   /* default scan direction: scan playfield from top/left to bottom/right */
2570   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2571
2572   /* dynamically adjust element properties according to game engine version */
2573   InitElementPropertiesEngine(game.engine_version);
2574
2575 #if 0
2576   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2577   printf("          tape version == %06d [%s] [file: %06d]\n",
2578          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2579          tape.file_version);
2580   printf("       => game.engine_version == %06d\n", game.engine_version);
2581 #endif
2582
2583   /* ---------- initialize player's initial move delay --------------------- */
2584
2585   /* dynamically adjust player properties according to level information */
2586   for (i = 0; i < MAX_PLAYERS; i++)
2587     game.initial_move_delay_value[i] =
2588       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2589
2590   /* dynamically adjust player properties according to game engine version */
2591   for (i = 0; i < MAX_PLAYERS; i++)
2592     game.initial_move_delay[i] =
2593       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2594        game.initial_move_delay_value[i] : 0);
2595
2596   /* ---------- initialize player's initial push delay --------------------- */
2597
2598   /* dynamically adjust player properties according to game engine version */
2599   game.initial_push_delay_value =
2600     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2601
2602   /* ---------- initialize changing elements ------------------------------- */
2603
2604   /* initialize changing elements information */
2605   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2606   {
2607     struct ElementInfo *ei = &element_info[i];
2608
2609     /* this pointer might have been changed in the level editor */
2610     ei->change = &ei->change_page[0];
2611
2612     if (!IS_CUSTOM_ELEMENT(i))
2613     {
2614       ei->change->target_element = EL_EMPTY_SPACE;
2615       ei->change->delay_fixed = 0;
2616       ei->change->delay_random = 0;
2617       ei->change->delay_frames = 1;
2618     }
2619
2620     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2621     {
2622       ei->has_change_event[j] = FALSE;
2623
2624       ei->event_page_nr[j] = 0;
2625       ei->event_page[j] = &ei->change_page[0];
2626     }
2627   }
2628
2629   /* add changing elements from pre-defined list */
2630   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2631   {
2632     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2633     struct ElementInfo *ei = &element_info[ch_delay->element];
2634
2635     ei->change->target_element       = ch_delay->target_element;
2636     ei->change->delay_fixed          = ch_delay->change_delay;
2637
2638     ei->change->pre_change_function  = ch_delay->pre_change_function;
2639     ei->change->change_function      = ch_delay->change_function;
2640     ei->change->post_change_function = ch_delay->post_change_function;
2641
2642     ei->change->can_change = TRUE;
2643     ei->change->can_change_or_has_action = TRUE;
2644
2645     ei->has_change_event[CE_DELAY] = TRUE;
2646
2647     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2648     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2649   }
2650
2651   /* ---------- initialize internal run-time variables ------------- */
2652
2653   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2654   {
2655     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2656
2657     for (j = 0; j < ei->num_change_pages; j++)
2658     {
2659       ei->change_page[j].can_change_or_has_action =
2660         (ei->change_page[j].can_change |
2661          ei->change_page[j].has_action);
2662     }
2663   }
2664
2665   /* add change events from custom element configuration */
2666   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2667   {
2668     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2669
2670     for (j = 0; j < ei->num_change_pages; j++)
2671     {
2672       if (!ei->change_page[j].can_change_or_has_action)
2673         continue;
2674
2675       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2676       {
2677         /* only add event page for the first page found with this event */
2678         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2679         {
2680           ei->has_change_event[k] = TRUE;
2681
2682           ei->event_page_nr[k] = j;
2683           ei->event_page[k] = &ei->change_page[j];
2684         }
2685       }
2686     }
2687   }
2688
2689   /* ---------- initialize run-time trigger player and element ------------- */
2690
2691   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2692   {
2693     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2694
2695     for (j = 0; j < ei->num_change_pages; j++)
2696     {
2697       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2698       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
2699       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2700       ei->change_page[j].actual_trigger_ce_value = 0;
2701       ei->change_page[j].actual_trigger_ce_score = 0;
2702     }
2703   }
2704
2705   /* ---------- initialize trigger events ---------------------------------- */
2706
2707   /* initialize trigger events information */
2708   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2709     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2710       trigger_events[i][j] = FALSE;
2711
2712   /* add trigger events from element change event properties */
2713   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2714   {
2715     struct ElementInfo *ei = &element_info[i];
2716
2717     for (j = 0; j < ei->num_change_pages; j++)
2718     {
2719       if (!ei->change_page[j].can_change_or_has_action)
2720         continue;
2721
2722       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2723       {
2724         int trigger_element = ei->change_page[j].trigger_element;
2725
2726         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2727         {
2728           if (ei->change_page[j].has_event[k])
2729           {
2730             if (IS_GROUP_ELEMENT(trigger_element))
2731             {
2732               struct ElementGroupInfo *group =
2733                 element_info[trigger_element].group;
2734
2735               for (l = 0; l < group->num_elements_resolved; l++)
2736                 trigger_events[group->element_resolved[l]][k] = TRUE;
2737             }
2738             else if (trigger_element == EL_ANY_ELEMENT)
2739               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2740                 trigger_events[l][k] = TRUE;
2741             else
2742               trigger_events[trigger_element][k] = TRUE;
2743           }
2744         }
2745       }
2746     }
2747   }
2748
2749   /* ---------- initialize push delay -------------------------------------- */
2750
2751   /* initialize push delay values to default */
2752   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2753   {
2754     if (!IS_CUSTOM_ELEMENT(i))
2755     {
2756       /* set default push delay values (corrected since version 3.0.7-1) */
2757       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2758       {
2759         element_info[i].push_delay_fixed = 2;
2760         element_info[i].push_delay_random = 8;
2761       }
2762       else
2763       {
2764         element_info[i].push_delay_fixed = 8;
2765         element_info[i].push_delay_random = 8;
2766       }
2767     }
2768   }
2769
2770   /* set push delay value for certain elements from pre-defined list */
2771   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2772   {
2773     int e = push_delay_list[i].element;
2774
2775     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2776     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2777   }
2778
2779   /* set push delay value for Supaplex elements for newer engine versions */
2780   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2781   {
2782     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2783     {
2784       if (IS_SP_ELEMENT(i))
2785       {
2786         /* set SP push delay to just enough to push under a falling zonk */
2787         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2788
2789         element_info[i].push_delay_fixed  = delay;
2790         element_info[i].push_delay_random = 0;
2791       }
2792     }
2793   }
2794
2795   /* ---------- initialize move stepsize ----------------------------------- */
2796
2797   /* initialize move stepsize values to default */
2798   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2799     if (!IS_CUSTOM_ELEMENT(i))
2800       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2801
2802   /* set move stepsize value for certain elements from pre-defined list */
2803   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2804   {
2805     int e = move_stepsize_list[i].element;
2806
2807     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2808   }
2809
2810   /* ---------- initialize collect score ----------------------------------- */
2811
2812   /* initialize collect score values for custom elements from initial value */
2813   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2814     if (IS_CUSTOM_ELEMENT(i))
2815       element_info[i].collect_score = element_info[i].collect_score_initial;
2816
2817   /* ---------- initialize collect count ----------------------------------- */
2818
2819   /* initialize collect count values for non-custom elements */
2820   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2821     if (!IS_CUSTOM_ELEMENT(i))
2822       element_info[i].collect_count_initial = 0;
2823
2824   /* add collect count values for all elements from pre-defined list */
2825   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2826     element_info[collect_count_list[i].element].collect_count_initial =
2827       collect_count_list[i].count;
2828
2829   /* ---------- initialize access direction -------------------------------- */
2830
2831   /* initialize access direction values to default (access from every side) */
2832   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2833     if (!IS_CUSTOM_ELEMENT(i))
2834       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2835
2836   /* set access direction value for certain elements from pre-defined list */
2837   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2838     element_info[access_direction_list[i].element].access_direction =
2839       access_direction_list[i].direction;
2840
2841   /* ---------- initialize explosion content ------------------------------- */
2842   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2843   {
2844     if (IS_CUSTOM_ELEMENT(i))
2845       continue;
2846
2847     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2848     {
2849       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2850
2851       element_info[i].content.e[x][y] =
2852         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2853          i == EL_PLAYER_2 ? EL_EMERALD_RED :
2854          i == EL_PLAYER_3 ? EL_EMERALD :
2855          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2856          i == EL_MOLE ? EL_EMERALD_RED :
2857          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2858          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2859          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2860          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2861          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2862          i == EL_WALL_EMERALD ? EL_EMERALD :
2863          i == EL_WALL_DIAMOND ? EL_DIAMOND :
2864          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2865          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2866          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2867          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2868          i == EL_WALL_PEARL ? EL_PEARL :
2869          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2870          EL_EMPTY);
2871     }
2872   }
2873
2874   /* ---------- initialize recursion detection ------------------------------ */
2875   recursion_loop_depth = 0;
2876   recursion_loop_detected = FALSE;
2877   recursion_loop_element = EL_UNDEFINED;
2878
2879   /* ---------- initialize graphics engine ---------------------------------- */
2880   game.scroll_delay_value =
2881     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
2882      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
2883   game.scroll_delay_value =
2884     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
2885 }
2886
2887 int get_num_special_action(int element, int action_first, int action_last)
2888 {
2889   int num_special_action = 0;
2890   int i, j;
2891
2892   for (i = action_first; i <= action_last; i++)
2893   {
2894     boolean found = FALSE;
2895
2896     for (j = 0; j < NUM_DIRECTIONS; j++)
2897       if (el_act_dir2img(element, i, j) !=
2898           el_act_dir2img(element, ACTION_DEFAULT, j))
2899         found = TRUE;
2900
2901     if (found)
2902       num_special_action++;
2903     else
2904       break;
2905   }
2906
2907   return num_special_action;
2908 }
2909
2910
2911 /*
2912   =============================================================================
2913   InitGame()
2914   -----------------------------------------------------------------------------
2915   initialize and start new game
2916   =============================================================================
2917 */
2918
2919 void InitGame()
2920 {
2921   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
2922   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
2923   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
2924 #if 0
2925   boolean do_fading = (game_status == GAME_MODE_MAIN);
2926 #endif
2927   int i, j, x, y;
2928
2929   game_status = GAME_MODE_PLAYING;
2930
2931   InitGameEngine();
2932   InitGameControlValues();
2933
2934   /* don't play tapes over network */
2935   network_playing = (options.network && !tape.playing);
2936
2937   for (i = 0; i < MAX_PLAYERS; i++)
2938   {
2939     struct PlayerInfo *player = &stored_player[i];
2940
2941     player->index_nr = i;
2942     player->index_bit = (1 << i);
2943     player->element_nr = EL_PLAYER_1 + i;
2944
2945     player->present = FALSE;
2946     player->active = FALSE;
2947     player->killed = FALSE;
2948
2949     player->action = 0;
2950     player->effective_action = 0;
2951     player->programmed_action = 0;
2952
2953     player->score = 0;
2954     player->score_final = 0;
2955
2956     player->gems_still_needed = level.gems_needed;
2957     player->sokobanfields_still_needed = 0;
2958     player->lights_still_needed = 0;
2959     player->friends_still_needed = 0;
2960
2961     for (j = 0; j < MAX_NUM_KEYS; j++)
2962       player->key[j] = FALSE;
2963
2964     player->num_white_keys = 0;
2965
2966     player->dynabomb_count = 0;
2967     player->dynabomb_size = 1;
2968     player->dynabombs_left = 0;
2969     player->dynabomb_xl = FALSE;
2970
2971     player->MovDir = MV_NONE;
2972     player->MovPos = 0;
2973     player->GfxPos = 0;
2974     player->GfxDir = MV_NONE;
2975     player->GfxAction = ACTION_DEFAULT;
2976     player->Frame = 0;
2977     player->StepFrame = 0;
2978
2979     player->use_murphy = FALSE;
2980     player->artwork_element =
2981       (level.use_artwork_element[i] ? level.artwork_element[i] :
2982        player->element_nr);
2983
2984     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
2985     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2986
2987     player->gravity = level.initial_player_gravity[i];
2988
2989     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2990
2991     player->actual_frame_counter = 0;
2992
2993     player->step_counter = 0;
2994
2995     player->last_move_dir = MV_NONE;
2996
2997     player->is_active = FALSE;
2998
2999     player->is_waiting = FALSE;
3000     player->is_moving = FALSE;
3001     player->is_auto_moving = FALSE;
3002     player->is_digging = FALSE;
3003     player->is_snapping = FALSE;
3004     player->is_collecting = FALSE;
3005     player->is_pushing = FALSE;
3006     player->is_switching = FALSE;
3007     player->is_dropping = FALSE;
3008     player->is_dropping_pressed = FALSE;
3009
3010     player->is_bored = FALSE;
3011     player->is_sleeping = FALSE;
3012
3013     player->frame_counter_bored = -1;
3014     player->frame_counter_sleeping = -1;
3015
3016     player->anim_delay_counter = 0;
3017     player->post_delay_counter = 0;
3018
3019     player->dir_waiting = MV_NONE;
3020     player->action_waiting = ACTION_DEFAULT;
3021     player->last_action_waiting = ACTION_DEFAULT;
3022     player->special_action_bored = ACTION_DEFAULT;
3023     player->special_action_sleeping = ACTION_DEFAULT;
3024
3025     player->switch_x = -1;
3026     player->switch_y = -1;
3027
3028     player->drop_x = -1;
3029     player->drop_y = -1;
3030
3031     player->show_envelope = 0;
3032
3033     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3034
3035     player->push_delay       = -1;      /* initialized when pushing starts */
3036     player->push_delay_value = game.initial_push_delay_value;
3037
3038     player->drop_delay = 0;
3039     player->drop_pressed_delay = 0;
3040
3041     player->last_jx = -1;
3042     player->last_jy = -1;
3043     player->jx = -1;
3044     player->jy = -1;
3045
3046     player->shield_normal_time_left = 0;
3047     player->shield_deadly_time_left = 0;
3048
3049     player->inventory_infinite_element = EL_UNDEFINED;
3050     player->inventory_size = 0;
3051
3052     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3053     SnapField(player, 0, 0);
3054
3055     player->LevelSolved = FALSE;
3056     player->GameOver = FALSE;
3057
3058     player->LevelSolved_GameWon = FALSE;
3059     player->LevelSolved_GameEnd = FALSE;
3060     player->LevelSolved_PanelOff = FALSE;
3061     player->LevelSolved_SaveTape = FALSE;
3062     player->LevelSolved_SaveScore = FALSE;
3063   }
3064
3065   network_player_action_received = FALSE;
3066
3067 #if defined(NETWORK_AVALIABLE)
3068   /* initial null action */
3069   if (network_playing)
3070     SendToServer_MovePlayer(MV_NONE);
3071 #endif
3072
3073   ZX = ZY = -1;
3074   ExitX = ExitY = -1;
3075
3076   FrameCounter = 0;
3077   TimeFrames = 0;
3078   TimePlayed = 0;
3079   TimeLeft = level.time;
3080   TapeTime = 0;
3081
3082   ScreenMovDir = MV_NONE;
3083   ScreenMovPos = 0;
3084   ScreenGfxPos = 0;
3085
3086   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3087
3088   AllPlayersGone = FALSE;
3089
3090   game.yamyam_content_nr = 0;
3091   game.magic_wall_active = FALSE;
3092   game.magic_wall_time_left = 0;
3093   game.light_time_left = 0;
3094   game.timegate_time_left = 0;
3095   game.switchgate_pos = 0;
3096   game.wind_direction = level.wind_direction_initial;
3097
3098 #if !USE_PLAYER_GRAVITY
3099   game.gravity = FALSE;
3100   game.explosions_delayed = TRUE;
3101 #endif
3102
3103   game.lenses_time_left = 0;
3104   game.magnify_time_left = 0;
3105
3106   game.ball_state = level.ball_state_initial;
3107   game.ball_content_nr = 0;
3108
3109   game.envelope_active = FALSE;
3110
3111   /* set focus to local player for network games, else to all players */
3112   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3113   game.centered_player_nr_next = game.centered_player_nr;
3114   game.set_centered_player = FALSE;
3115
3116   if (network_playing && tape.recording)
3117   {
3118     /* store client dependent player focus when recording network games */
3119     tape.centered_player_nr_next = game.centered_player_nr_next;
3120     tape.set_centered_player = TRUE;
3121   }
3122
3123   for (i = 0; i < NUM_BELTS; i++)
3124   {
3125     game.belt_dir[i] = MV_NONE;
3126     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3127   }
3128
3129   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3130     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3131
3132   SCAN_PLAYFIELD(x, y)
3133   {
3134     Feld[x][y] = level.field[x][y];
3135     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3136     ChangeDelay[x][y] = 0;
3137     ChangePage[x][y] = -1;
3138 #if USE_NEW_CUSTOM_VALUE
3139     CustomValue[x][y] = 0;              /* initialized in InitField() */
3140 #endif
3141     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3142     AmoebaNr[x][y] = 0;
3143     WasJustMoving[x][y] = 0;
3144     WasJustFalling[x][y] = 0;
3145     CheckCollision[x][y] = 0;
3146     CheckImpact[x][y] = 0;
3147     Stop[x][y] = FALSE;
3148     Pushed[x][y] = FALSE;
3149
3150     ChangeCount[x][y] = 0;
3151     ChangeEvent[x][y] = -1;
3152
3153     ExplodePhase[x][y] = 0;
3154     ExplodeDelay[x][y] = 0;
3155     ExplodeField[x][y] = EX_TYPE_NONE;
3156
3157     RunnerVisit[x][y] = 0;
3158     PlayerVisit[x][y] = 0;
3159
3160     GfxFrame[x][y] = 0;
3161     GfxRandom[x][y] = INIT_GFX_RANDOM();
3162     GfxElement[x][y] = EL_UNDEFINED;
3163     GfxAction[x][y] = ACTION_DEFAULT;
3164     GfxDir[x][y] = MV_NONE;
3165   }
3166
3167   SCAN_PLAYFIELD(x, y)
3168   {
3169     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3170       emulate_bd = FALSE;
3171     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3172       emulate_sb = FALSE;
3173     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3174       emulate_sp = FALSE;
3175
3176     InitField(x, y, TRUE);
3177   }
3178
3179   InitBeltMovement();
3180
3181   for (i = 0; i < MAX_PLAYERS; i++)
3182   {
3183     struct PlayerInfo *player = &stored_player[i];
3184
3185     /* set number of special actions for bored and sleeping animation */
3186     player->num_special_action_bored =
3187       get_num_special_action(player->artwork_element,
3188                              ACTION_BORING_1, ACTION_BORING_LAST);
3189     player->num_special_action_sleeping =
3190       get_num_special_action(player->artwork_element,
3191                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3192   }
3193
3194   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3195                     emulate_sb ? EMU_SOKOBAN :
3196                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3197
3198 #if USE_NEW_ALL_SLIPPERY
3199   /* initialize type of slippery elements */
3200   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3201   {
3202     if (!IS_CUSTOM_ELEMENT(i))
3203     {
3204       /* default: elements slip down either to the left or right randomly */
3205       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3206
3207       /* SP style elements prefer to slip down on the left side */
3208       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3209         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3210
3211       /* BD style elements prefer to slip down on the left side */
3212       if (game.emulation == EMU_BOULDERDASH)
3213         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3214     }
3215   }
3216 #endif
3217
3218   /* initialize explosion and ignition delay */
3219   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3220   {
3221     if (!IS_CUSTOM_ELEMENT(i))
3222     {
3223       int num_phase = 8;
3224       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3225                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3226                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3227       int last_phase = (num_phase + 1) * delay;
3228       int half_phase = (num_phase / 2) * delay;
3229
3230       element_info[i].explosion_delay = last_phase - 1;
3231       element_info[i].ignition_delay = half_phase;
3232
3233       if (i == EL_BLACK_ORB)
3234         element_info[i].ignition_delay = 1;
3235     }
3236
3237 #if 0
3238     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3239       element_info[i].explosion_delay = 1;
3240
3241     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3242       element_info[i].ignition_delay = 1;
3243 #endif
3244   }
3245
3246   /* correct non-moving belts to start moving left */
3247   for (i = 0; i < NUM_BELTS; i++)
3248     if (game.belt_dir[i] == MV_NONE)
3249       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3250
3251   /* check if any connected player was not found in playfield */
3252   for (i = 0; i < MAX_PLAYERS; i++)
3253   {
3254     struct PlayerInfo *player = &stored_player[i];
3255
3256     if (player->connected && !player->present)
3257     {
3258       for (j = 0; j < MAX_PLAYERS; j++)
3259       {
3260         struct PlayerInfo *some_player = &stored_player[j];
3261         int jx = some_player->jx, jy = some_player->jy;
3262
3263         /* assign first free player found that is present in the playfield */
3264         if (some_player->present && !some_player->connected)
3265         {
3266           player->present = TRUE;
3267           player->active = TRUE;
3268
3269           some_player->present = FALSE;
3270           some_player->active = FALSE;
3271
3272           player->artwork_element = some_player->artwork_element;
3273
3274           player->block_last_field       = some_player->block_last_field;
3275           player->block_delay_adjustment = some_player->block_delay_adjustment;
3276
3277           StorePlayer[jx][jy] = player->element_nr;
3278           player->jx = player->last_jx = jx;
3279           player->jy = player->last_jy = jy;
3280
3281           break;
3282         }
3283       }
3284     }
3285   }
3286
3287   if (tape.playing)
3288   {
3289     /* when playing a tape, eliminate all players who do not participate */
3290
3291     for (i = 0; i < MAX_PLAYERS; i++)
3292     {
3293       if (stored_player[i].active && !tape.player_participates[i])
3294       {
3295         struct PlayerInfo *player = &stored_player[i];
3296         int jx = player->jx, jy = player->jy;
3297
3298         player->active = FALSE;
3299         StorePlayer[jx][jy] = 0;
3300         Feld[jx][jy] = EL_EMPTY;
3301       }
3302     }
3303   }
3304   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
3305   {
3306     /* when in single player mode, eliminate all but the first active player */
3307
3308     for (i = 0; i < MAX_PLAYERS; i++)
3309     {
3310       if (stored_player[i].active)
3311       {
3312         for (j = i + 1; j < MAX_PLAYERS; j++)
3313         {
3314           if (stored_player[j].active)
3315           {
3316             struct PlayerInfo *player = &stored_player[j];
3317             int jx = player->jx, jy = player->jy;
3318
3319             player->active = FALSE;
3320             player->present = FALSE;
3321
3322             StorePlayer[jx][jy] = 0;
3323             Feld[jx][jy] = EL_EMPTY;
3324           }
3325         }
3326       }
3327     }
3328   }
3329
3330   /* when recording the game, store which players take part in the game */
3331   if (tape.recording)
3332   {
3333     for (i = 0; i < MAX_PLAYERS; i++)
3334       if (stored_player[i].active)
3335         tape.player_participates[i] = TRUE;
3336   }
3337
3338   if (options.debug)
3339   {
3340     for (i = 0; i < MAX_PLAYERS; i++)
3341     {
3342       struct PlayerInfo *player = &stored_player[i];
3343
3344       printf("Player %d: present == %d, connected == %d, active == %d.\n",
3345              i+1,
3346              player->present,
3347              player->connected,
3348              player->active);
3349       if (local_player == player)
3350         printf("Player  %d is local player.\n", i+1);
3351     }
3352   }
3353
3354   if (BorderElement == EL_EMPTY)
3355   {
3356     SBX_Left = 0;
3357     SBX_Right = lev_fieldx - SCR_FIELDX;
3358     SBY_Upper = 0;
3359     SBY_Lower = lev_fieldy - SCR_FIELDY;
3360   }
3361   else
3362   {
3363     SBX_Left = -1;
3364     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3365     SBY_Upper = -1;
3366     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3367   }
3368
3369   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3370     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3371
3372   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3373     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3374
3375   /* if local player not found, look for custom element that might create
3376      the player (make some assumptions about the right custom element) */
3377   if (!local_player->present)
3378   {
3379     int start_x = 0, start_y = 0;
3380     int found_rating = 0;
3381     int found_element = EL_UNDEFINED;
3382     int player_nr = local_player->index_nr;
3383
3384     SCAN_PLAYFIELD(x, y)
3385     {
3386       int element = Feld[x][y];
3387       int content;
3388       int xx, yy;
3389       boolean is_player;
3390
3391       if (level.use_start_element[player_nr] &&
3392           level.start_element[player_nr] == element &&
3393           found_rating < 4)
3394       {
3395         start_x = x;
3396         start_y = y;
3397
3398         found_rating = 4;
3399         found_element = element;
3400       }
3401
3402       if (!IS_CUSTOM_ELEMENT(element))
3403         continue;
3404
3405       if (CAN_CHANGE(element))
3406       {
3407         for (i = 0; i < element_info[element].num_change_pages; i++)
3408         {
3409           /* check for player created from custom element as single target */
3410           content = element_info[element].change_page[i].target_element;
3411           is_player = ELEM_IS_PLAYER(content);
3412
3413           if (is_player && (found_rating < 3 ||
3414                             (found_rating == 3 && element < found_element)))
3415           {
3416             start_x = x;
3417             start_y = y;
3418
3419             found_rating = 3;
3420             found_element = element;
3421           }
3422         }
3423       }
3424
3425       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3426       {
3427         /* check for player created from custom element as explosion content */
3428         content = element_info[element].content.e[xx][yy];
3429         is_player = ELEM_IS_PLAYER(content);
3430
3431         if (is_player && (found_rating < 2 ||
3432                           (found_rating == 2 && element < found_element)))
3433         {
3434           start_x = x + xx - 1;
3435           start_y = y + yy - 1;
3436
3437           found_rating = 2;
3438           found_element = element;
3439         }
3440
3441         if (!CAN_CHANGE(element))
3442           continue;
3443
3444         for (i = 0; i < element_info[element].num_change_pages; i++)
3445         {
3446           /* check for player created from custom element as extended target */
3447           content =
3448             element_info[element].change_page[i].target_content.e[xx][yy];
3449
3450           is_player = ELEM_IS_PLAYER(content);
3451
3452           if (is_player && (found_rating < 1 ||
3453                             (found_rating == 1 && element < found_element)))
3454           {
3455             start_x = x + xx - 1;
3456             start_y = y + yy - 1;
3457
3458             found_rating = 1;
3459             found_element = element;
3460           }
3461         }
3462       }
3463     }
3464
3465     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3466                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3467                 start_x - MIDPOSX);
3468
3469     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3470                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3471                 start_y - MIDPOSY);
3472   }
3473   else
3474   {
3475     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3476                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3477                 local_player->jx - MIDPOSX);
3478
3479     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3480                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3481                 local_player->jy - MIDPOSY);
3482   }
3483
3484   StopAnimation();
3485
3486   if (!game.restart_level)
3487     CloseDoor(DOOR_CLOSE_1);
3488
3489 #if 1
3490   if (level_editor_test_game)
3491     FadeSkipNextFadeIn();
3492   else
3493     FadeSetStartItem();
3494 #else
3495   if (level_editor_test_game)
3496     fading = fading_none;
3497   else
3498     fading = menu.destination;
3499 #endif
3500
3501 #if 1
3502   FadeOut(REDRAW_FIELD);
3503 #else
3504   if (do_fading)
3505     FadeOut(REDRAW_FIELD);
3506 #endif
3507
3508   /* !!! FIX THIS (START) !!! */
3509   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3510   {
3511     InitGameEngine_EM();
3512
3513     /* blit playfield from scroll buffer to normal back buffer for fading in */
3514     BlitScreenToBitmap_EM(backbuffer);
3515   }
3516   else
3517   {
3518     DrawLevel();
3519     DrawAllPlayers();
3520
3521     /* after drawing the level, correct some elements */
3522     if (game.timegate_time_left == 0)
3523       CloseAllOpenTimegates();
3524
3525     /* blit playfield from scroll buffer to normal back buffer for fading in */
3526     if (setup.soft_scrolling)
3527       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3528
3529     redraw_mask |= REDRAW_FROM_BACKBUFFER;
3530   }
3531   /* !!! FIX THIS (END) !!! */
3532
3533 #if 1
3534   FadeIn(REDRAW_FIELD);
3535 #else
3536   if (do_fading)
3537     FadeIn(REDRAW_FIELD);
3538
3539   BackToFront();
3540 #endif
3541
3542   if (!game.restart_level)
3543   {
3544     /* copy default game door content to main double buffer */
3545     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3546                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3547   }
3548
3549   SetPanelBackground();
3550   SetDrawBackgroundMask(REDRAW_DOOR_1);
3551
3552   DrawGameDoorValues();
3553
3554   if (!game.restart_level)
3555   {
3556     UnmapGameButtons();
3557     UnmapTapeButtons();
3558     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3559     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3560     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3561     MapGameButtons();
3562     MapTapeButtons();
3563
3564     /* copy actual game door content to door double buffer for OpenDoor() */
3565     BlitBitmap(drawto, bitmap_db_door,
3566                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3567
3568     OpenDoor(DOOR_OPEN_ALL);
3569
3570     PlaySound(SND_GAME_STARTING);
3571
3572     if (setup.sound_music)
3573       PlayLevelMusic();
3574
3575     KeyboardAutoRepeatOffUnlessAutoplay();
3576
3577     if (options.debug)
3578     {
3579       for (i = 0; i < MAX_PLAYERS; i++)
3580         printf("Player %d %sactive.\n",
3581                i + 1, (stored_player[i].active ? "" : "not "));
3582     }
3583   }
3584
3585 #if 1
3586   UnmapAllGadgets();
3587
3588   MapGameButtons();
3589   MapTapeButtons();
3590 #endif
3591
3592   game.restart_level = FALSE;
3593 }
3594
3595 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3596 {
3597   /* this is used for non-R'n'D game engines to update certain engine values */
3598
3599   /* needed to determine if sounds are played within the visible screen area */
3600   scroll_x = actual_scroll_x;
3601   scroll_y = actual_scroll_y;
3602 }
3603
3604 void InitMovDir(int x, int y)
3605 {
3606   int i, element = Feld[x][y];
3607   static int xy[4][2] =
3608   {
3609     {  0, +1 },
3610     { +1,  0 },
3611     {  0, -1 },
3612     { -1,  0 }
3613   };
3614   static int direction[3][4] =
3615   {
3616     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
3617     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
3618     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
3619   };
3620
3621   switch (element)
3622   {
3623     case EL_BUG_RIGHT:
3624     case EL_BUG_UP:
3625     case EL_BUG_LEFT:
3626     case EL_BUG_DOWN:
3627       Feld[x][y] = EL_BUG;
3628       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3629       break;
3630
3631     case EL_SPACESHIP_RIGHT:
3632     case EL_SPACESHIP_UP:
3633     case EL_SPACESHIP_LEFT:
3634     case EL_SPACESHIP_DOWN:
3635       Feld[x][y] = EL_SPACESHIP;
3636       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3637       break;
3638
3639     case EL_BD_BUTTERFLY_RIGHT:
3640     case EL_BD_BUTTERFLY_UP:
3641     case EL_BD_BUTTERFLY_LEFT:
3642     case EL_BD_BUTTERFLY_DOWN:
3643       Feld[x][y] = EL_BD_BUTTERFLY;
3644       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3645       break;
3646
3647     case EL_BD_FIREFLY_RIGHT:
3648     case EL_BD_FIREFLY_UP:
3649     case EL_BD_FIREFLY_LEFT:
3650     case EL_BD_FIREFLY_DOWN:
3651       Feld[x][y] = EL_BD_FIREFLY;
3652       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3653       break;
3654
3655     case EL_PACMAN_RIGHT:
3656     case EL_PACMAN_UP:
3657     case EL_PACMAN_LEFT:
3658     case EL_PACMAN_DOWN:
3659       Feld[x][y] = EL_PACMAN;
3660       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3661       break;
3662
3663     case EL_YAMYAM_LEFT:
3664     case EL_YAMYAM_RIGHT:
3665     case EL_YAMYAM_UP:
3666     case EL_YAMYAM_DOWN:
3667       Feld[x][y] = EL_YAMYAM;
3668       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3669       break;
3670
3671     case EL_SP_SNIKSNAK:
3672       MovDir[x][y] = MV_UP;
3673       break;
3674
3675     case EL_SP_ELECTRON:
3676       MovDir[x][y] = MV_LEFT;
3677       break;
3678
3679     case EL_MOLE_LEFT:
3680     case EL_MOLE_RIGHT:
3681     case EL_MOLE_UP:
3682     case EL_MOLE_DOWN:
3683       Feld[x][y] = EL_MOLE;
3684       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3685       break;
3686
3687     default:
3688       if (IS_CUSTOM_ELEMENT(element))
3689       {
3690         struct ElementInfo *ei = &element_info[element];
3691         int move_direction_initial = ei->move_direction_initial;
3692         int move_pattern = ei->move_pattern;
3693
3694         if (move_direction_initial == MV_START_PREVIOUS)
3695         {
3696           if (MovDir[x][y] != MV_NONE)
3697             return;
3698
3699           move_direction_initial = MV_START_AUTOMATIC;
3700         }
3701
3702         if (move_direction_initial == MV_START_RANDOM)
3703           MovDir[x][y] = 1 << RND(4);
3704         else if (move_direction_initial & MV_ANY_DIRECTION)
3705           MovDir[x][y] = move_direction_initial;
3706         else if (move_pattern == MV_ALL_DIRECTIONS ||
3707                  move_pattern == MV_TURNING_LEFT ||
3708                  move_pattern == MV_TURNING_RIGHT ||
3709                  move_pattern == MV_TURNING_LEFT_RIGHT ||
3710                  move_pattern == MV_TURNING_RIGHT_LEFT ||
3711                  move_pattern == MV_TURNING_RANDOM)
3712           MovDir[x][y] = 1 << RND(4);
3713         else if (move_pattern == MV_HORIZONTAL)
3714           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3715         else if (move_pattern == MV_VERTICAL)
3716           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3717         else if (move_pattern & MV_ANY_DIRECTION)
3718           MovDir[x][y] = element_info[element].move_pattern;
3719         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
3720                  move_pattern == MV_ALONG_RIGHT_SIDE)
3721         {
3722           /* use random direction as default start direction */
3723           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3724             MovDir[x][y] = 1 << RND(4);
3725
3726           for (i = 0; i < NUM_DIRECTIONS; i++)
3727           {
3728             int x1 = x + xy[i][0];
3729             int y1 = y + xy[i][1];
3730
3731             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3732             {
3733               if (move_pattern == MV_ALONG_RIGHT_SIDE)
3734                 MovDir[x][y] = direction[0][i];
3735               else
3736                 MovDir[x][y] = direction[1][i];
3737
3738               break;
3739             }
3740           }
3741         }                
3742       }
3743       else
3744       {
3745         MovDir[x][y] = 1 << RND(4);
3746
3747         if (element != EL_BUG &&
3748             element != EL_SPACESHIP &&
3749             element != EL_BD_BUTTERFLY &&
3750             element != EL_BD_FIREFLY)
3751           break;
3752
3753         for (i = 0; i < NUM_DIRECTIONS; i++)
3754         {
3755           int x1 = x + xy[i][0];
3756           int y1 = y + xy[i][1];
3757
3758           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3759           {
3760             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3761             {
3762               MovDir[x][y] = direction[0][i];
3763               break;
3764             }
3765             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3766                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3767             {
3768               MovDir[x][y] = direction[1][i];
3769               break;
3770             }
3771           }
3772         }
3773       }
3774       break;
3775   }
3776
3777   GfxDir[x][y] = MovDir[x][y];
3778 }
3779
3780 void InitAmoebaNr(int x, int y)
3781 {
3782   int i;
3783   int group_nr = AmoebeNachbarNr(x, y);
3784
3785   if (group_nr == 0)
3786   {
3787     for (i = 1; i < MAX_NUM_AMOEBA; i++)
3788     {
3789       if (AmoebaCnt[i] == 0)
3790       {
3791         group_nr = i;
3792         break;
3793       }
3794     }
3795   }
3796
3797   AmoebaNr[x][y] = group_nr;
3798   AmoebaCnt[group_nr]++;
3799   AmoebaCnt2[group_nr]++;
3800 }
3801
3802 static void PlayerWins(struct PlayerInfo *player)
3803 {
3804   player->LevelSolved = TRUE;
3805   player->GameOver = TRUE;
3806
3807   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
3808                          level.native_em_level->lev->score : player->score);
3809 }
3810
3811 void GameWon()
3812 {
3813   static int time, time_final;
3814   static int score, score_final;
3815   static int game_over_delay_1 = 0;
3816   static int game_over_delay_2 = 0;
3817   int game_over_delay_value_1 = 50;
3818   int game_over_delay_value_2 = 50;
3819
3820   if (!local_player->LevelSolved_GameWon)
3821   {
3822     int i;
3823
3824     /* do not start end game actions before the player stops moving (to exit) */
3825     if (local_player->MovPos)
3826       return;
3827
3828     local_player->LevelSolved_GameWon = TRUE;
3829     local_player->LevelSolved_SaveTape = tape.recording;
3830     local_player->LevelSolved_SaveScore = !tape.playing;
3831
3832     if (tape.auto_play)         /* tape might already be stopped here */
3833       tape.auto_play_level_solved = TRUE;
3834
3835 #if 1
3836     TapeStop();
3837 #endif
3838
3839     game_over_delay_1 = game_over_delay_value_1;
3840     game_over_delay_2 = game_over_delay_value_2;
3841
3842     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3843     score = score_final = local_player->score_final;
3844
3845     if (TimeLeft > 0)
3846     {
3847       time_final = 0;
3848       score_final += TimeLeft * level.score[SC_TIME_BONUS];
3849     }
3850     else if (level.time == 0 && TimePlayed < 999)
3851     {
3852       time_final = 999;
3853       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3854     }
3855
3856     local_player->score_final = score_final;
3857
3858     if (level_editor_test_game)
3859     {
3860       time = time_final;
3861       score = score_final;
3862
3863 #if 1
3864       game_control_value[GAME_CONTROL_TIME] = time;
3865       game_control_value[GAME_CONTROL_SCORE] = score;
3866
3867       DisplayGameControlValues();
3868 #else
3869       DrawGameValue_Time(time);
3870       DrawGameValue_Score(score);
3871 #endif
3872     }
3873
3874     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3875     {
3876       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
3877       {
3878         /* close exit door after last player */
3879         if ((AllPlayersGone &&
3880              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3881               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3882               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3883             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3884             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3885         {
3886           int element = Feld[ExitX][ExitY];
3887
3888 #if 0
3889           if (element == EL_EM_EXIT_OPEN ||
3890               element == EL_EM_STEEL_EXIT_OPEN)
3891           {
3892             Bang(ExitX, ExitY);
3893           }
3894           else
3895 #endif
3896           {
3897             Feld[ExitX][ExitY] =
3898               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
3899                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
3900                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
3901                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
3902                EL_EM_STEEL_EXIT_CLOSING);
3903
3904             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3905           }
3906         }
3907
3908         /* player disappears */
3909         DrawLevelField(ExitX, ExitY);
3910       }
3911
3912       for (i = 0; i < MAX_PLAYERS; i++)
3913       {
3914         struct PlayerInfo *player = &stored_player[i];
3915
3916         if (player->present)
3917         {
3918           RemovePlayer(player);
3919
3920           /* player disappears */
3921           DrawLevelField(player->jx, player->jy);
3922         }
3923       }
3924     }
3925
3926     PlaySound(SND_GAME_WINNING);
3927   }
3928
3929   if (game_over_delay_1 > 0)
3930   {
3931     game_over_delay_1--;
3932
3933     return;
3934   }
3935
3936   if (time != time_final)
3937   {
3938     int time_to_go = ABS(time_final - time);
3939     int time_count_dir = (time < time_final ? +1 : -1);
3940     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
3941
3942     time  += time_count_steps * time_count_dir;
3943     score += time_count_steps * level.score[SC_TIME_BONUS];
3944
3945 #if 1
3946     game_control_value[GAME_CONTROL_TIME] = time;
3947     game_control_value[GAME_CONTROL_SCORE] = score;
3948
3949     DisplayGameControlValues();
3950 #else
3951     DrawGameValue_Time(time);
3952     DrawGameValue_Score(score);
3953 #endif
3954
3955     if (time == time_final)
3956       StopSound(SND_GAME_LEVELTIME_BONUS);
3957     else if (setup.sound_loops)
3958       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
3959     else
3960       PlaySound(SND_GAME_LEVELTIME_BONUS);
3961
3962     return;
3963   }
3964
3965   local_player->LevelSolved_PanelOff = TRUE;
3966
3967   if (game_over_delay_2 > 0)
3968   {
3969     game_over_delay_2--;
3970
3971     return;
3972   }
3973
3974 #if 1
3975   GameEnd();
3976 #endif
3977 }
3978
3979 void GameEnd()
3980 {
3981   int hi_pos;
3982   boolean raise_level = FALSE;
3983
3984   local_player->LevelSolved_GameEnd = TRUE;
3985
3986   CloseDoor(DOOR_CLOSE_1);
3987
3988   if (local_player->LevelSolved_SaveTape)
3989   {
3990 #if 0
3991     TapeStop();
3992 #endif
3993
3994 #if 1
3995     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
3996 #else
3997     SaveTape(tape.level_nr);            /* ask to save tape */
3998 #endif
3999   }
4000
4001   if (level_editor_test_game)
4002   {
4003     game_status = GAME_MODE_MAIN;
4004
4005 #if 1
4006     DrawAndFadeInMainMenu(REDRAW_FIELD);
4007 #else
4008     DrawMainMenu();
4009 #endif
4010
4011     return;
4012   }
4013
4014   if (!local_player->LevelSolved_SaveScore)
4015   {
4016 #if 1
4017     FadeOut(REDRAW_FIELD);
4018 #endif
4019
4020     game_status = GAME_MODE_MAIN;
4021
4022     DrawAndFadeInMainMenu(REDRAW_FIELD);
4023
4024     return;
4025   }
4026
4027   if (level_nr == leveldir_current->handicap_level)
4028   {
4029     leveldir_current->handicap_level++;
4030     SaveLevelSetup_SeriesInfo();
4031   }
4032
4033   if (level_nr < leveldir_current->last_level)
4034     raise_level = TRUE;                 /* advance to next level */
4035
4036   if ((hi_pos = NewHiScore()) >= 0) 
4037   {
4038     game_status = GAME_MODE_SCORES;
4039
4040     DrawHallOfFame(hi_pos);
4041
4042     if (raise_level)
4043     {
4044       level_nr++;
4045       TapeErase();
4046     }
4047   }
4048   else
4049   {
4050 #if 1
4051     FadeOut(REDRAW_FIELD);
4052 #endif
4053
4054     game_status = GAME_MODE_MAIN;
4055
4056     if (raise_level)
4057     {
4058       level_nr++;
4059       TapeErase();
4060     }
4061
4062     DrawAndFadeInMainMenu(REDRAW_FIELD);
4063   }
4064 }
4065
4066 int NewHiScore()
4067 {
4068   int k, l;
4069   int position = -1;
4070
4071   LoadScore(level_nr);
4072
4073   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4074       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4075     return -1;
4076
4077   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4078   {
4079     if (local_player->score_final > highscore[k].Score)
4080     {
4081       /* player has made it to the hall of fame */
4082
4083       if (k < MAX_SCORE_ENTRIES - 1)
4084       {
4085         int m = MAX_SCORE_ENTRIES - 1;
4086
4087 #ifdef ONE_PER_NAME
4088         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4089           if (strEqual(setup.player_name, highscore[l].Name))
4090             m = l;
4091         if (m == k)     /* player's new highscore overwrites his old one */
4092           goto put_into_list;
4093 #endif
4094
4095         for (l = m; l > k; l--)
4096         {
4097           strcpy(highscore[l].Name, highscore[l - 1].Name);
4098           highscore[l].Score = highscore[l - 1].Score;
4099         }
4100       }
4101
4102 #ifdef ONE_PER_NAME
4103       put_into_list:
4104 #endif
4105       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4106       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4107       highscore[k].Score = local_player->score_final; 
4108       position = k;
4109       break;
4110     }
4111
4112 #ifdef ONE_PER_NAME
4113     else if (!strncmp(setup.player_name, highscore[k].Name,
4114                       MAX_PLAYER_NAME_LEN))
4115       break;    /* player already there with a higher score */
4116 #endif
4117
4118   }
4119
4120   if (position >= 0) 
4121     SaveScore(level_nr);
4122
4123   return position;
4124 }
4125
4126 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4127 {
4128   int element = Feld[x][y];
4129   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4130   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4131   int horiz_move = (dx != 0);
4132   int sign = (horiz_move ? dx : dy);
4133   int step = sign * element_info[element].move_stepsize;
4134
4135   /* special values for move stepsize for spring and things on conveyor belt */
4136   if (horiz_move)
4137   {
4138     if (CAN_FALL(element) &&
4139         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4140       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4141     else if (element == EL_SPRING)
4142       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4143   }
4144
4145   return step;
4146 }
4147
4148 inline static int getElementMoveStepsize(int x, int y)
4149 {
4150   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4151 }
4152
4153 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4154 {
4155   if (player->GfxAction != action || player->GfxDir != dir)
4156   {
4157 #if 0
4158     printf("Player frame reset! (%d => %d, %d => %d)\n",
4159            player->GfxAction, action, player->GfxDir, dir);
4160 #endif
4161
4162     player->GfxAction = action;
4163     player->GfxDir = dir;
4164     player->Frame = 0;
4165     player->StepFrame = 0;
4166   }
4167 }
4168
4169 #if USE_GFX_RESET_GFX_ANIMATION
4170 static void ResetGfxFrame(int x, int y, boolean redraw)
4171 {
4172   int element = Feld[x][y];
4173   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4174   int last_gfx_frame = GfxFrame[x][y];
4175
4176   if (graphic_info[graphic].anim_global_sync)
4177     GfxFrame[x][y] = FrameCounter;
4178   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4179     GfxFrame[x][y] = CustomValue[x][y];
4180   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4181     GfxFrame[x][y] = element_info[element].collect_score;
4182   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4183     GfxFrame[x][y] = ChangeDelay[x][y];
4184
4185   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4186     DrawLevelGraphicAnimation(x, y, graphic);
4187 }
4188 #endif
4189
4190 static void ResetGfxAnimation(int x, int y)
4191 {
4192   GfxAction[x][y] = ACTION_DEFAULT;
4193   GfxDir[x][y] = MovDir[x][y];
4194   GfxFrame[x][y] = 0;
4195
4196 #if USE_GFX_RESET_GFX_ANIMATION
4197   ResetGfxFrame(x, y, FALSE);
4198 #endif
4199 }
4200
4201 static void ResetRandomAnimationValue(int x, int y)
4202 {
4203   GfxRandom[x][y] = INIT_GFX_RANDOM();
4204 }
4205
4206 void InitMovingField(int x, int y, int direction)
4207 {
4208   int element = Feld[x][y];
4209   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4210   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4211   int newx = x + dx;
4212   int newy = y + dy;
4213   boolean is_moving_before, is_moving_after;
4214 #if 0
4215   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4216 #endif
4217
4218   /* check if element was/is moving or being moved before/after mode change */
4219 #if 1
4220 #if 1
4221   is_moving_before = (WasJustMoving[x][y] != 0);
4222 #else
4223   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4224   is_moving_before = WasJustMoving[x][y];
4225 #endif
4226 #else
4227   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4228 #endif
4229   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4230
4231   /* reset animation only for moving elements which change direction of moving
4232      or which just started or stopped moving
4233      (else CEs with property "can move" / "not moving" are reset each frame) */
4234 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4235 #if 1
4236   if (is_moving_before != is_moving_after ||
4237       direction != MovDir[x][y])
4238     ResetGfxAnimation(x, y);
4239 #else
4240   if ((is_moving_before || is_moving_after) && !continues_moving)
4241     ResetGfxAnimation(x, y);
4242 #endif
4243 #else
4244   if (!continues_moving)
4245     ResetGfxAnimation(x, y);
4246 #endif
4247
4248   MovDir[x][y] = direction;
4249   GfxDir[x][y] = direction;
4250
4251 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4252   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4253                      direction == MV_DOWN && CAN_FALL(element) ?
4254                      ACTION_FALLING : ACTION_MOVING);
4255 #else
4256   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4257                      ACTION_FALLING : ACTION_MOVING);
4258 #endif
4259
4260   /* this is needed for CEs with property "can move" / "not moving" */
4261
4262   if (is_moving_after)
4263   {
4264     if (Feld[newx][newy] == EL_EMPTY)
4265       Feld[newx][newy] = EL_BLOCKED;
4266
4267     MovDir[newx][newy] = MovDir[x][y];
4268
4269 #if USE_NEW_CUSTOM_VALUE
4270     CustomValue[newx][newy] = CustomValue[x][y];
4271 #endif
4272
4273     GfxFrame[newx][newy] = GfxFrame[x][y];
4274     GfxRandom[newx][newy] = GfxRandom[x][y];
4275     GfxAction[newx][newy] = GfxAction[x][y];
4276     GfxDir[newx][newy] = GfxDir[x][y];
4277   }
4278 }
4279
4280 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4281 {
4282   int direction = MovDir[x][y];
4283   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4284   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4285
4286   *goes_to_x = newx;
4287   *goes_to_y = newy;
4288 }
4289
4290 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4291 {
4292   int oldx = x, oldy = y;
4293   int direction = MovDir[x][y];
4294
4295   if (direction == MV_LEFT)
4296     oldx++;
4297   else if (direction == MV_RIGHT)
4298     oldx--;
4299   else if (direction == MV_UP)
4300     oldy++;
4301   else if (direction == MV_DOWN)
4302     oldy--;
4303
4304   *comes_from_x = oldx;
4305   *comes_from_y = oldy;
4306 }
4307
4308 int MovingOrBlocked2Element(int x, int y)
4309 {
4310   int element = Feld[x][y];
4311
4312   if (element == EL_BLOCKED)
4313   {
4314     int oldx, oldy;
4315
4316     Blocked2Moving(x, y, &oldx, &oldy);
4317     return Feld[oldx][oldy];
4318   }
4319   else
4320     return element;
4321 }
4322
4323 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4324 {
4325   /* like MovingOrBlocked2Element(), but if element is moving
4326      and (x,y) is the field the moving element is just leaving,
4327      return EL_BLOCKED instead of the element value */
4328   int element = Feld[x][y];
4329
4330   if (IS_MOVING(x, y))
4331   {
4332     if (element == EL_BLOCKED)
4333     {
4334       int oldx, oldy;
4335
4336       Blocked2Moving(x, y, &oldx, &oldy);
4337       return Feld[oldx][oldy];
4338     }
4339     else
4340       return EL_BLOCKED;
4341   }
4342   else
4343     return element;
4344 }
4345
4346 static void RemoveField(int x, int y)
4347 {
4348   Feld[x][y] = EL_EMPTY;
4349
4350   MovPos[x][y] = 0;
4351   MovDir[x][y] = 0;
4352   MovDelay[x][y] = 0;
4353
4354 #if USE_NEW_CUSTOM_VALUE
4355   CustomValue[x][y] = 0;
4356 #endif
4357
4358   AmoebaNr[x][y] = 0;
4359   ChangeDelay[x][y] = 0;
4360   ChangePage[x][y] = -1;
4361   Pushed[x][y] = FALSE;
4362
4363 #if 0
4364   ExplodeField[x][y] = EX_TYPE_NONE;
4365 #endif
4366
4367   GfxElement[x][y] = EL_UNDEFINED;
4368   GfxAction[x][y] = ACTION_DEFAULT;
4369   GfxDir[x][y] = MV_NONE;
4370 }
4371
4372 void RemoveMovingField(int x, int y)
4373 {
4374   int oldx = x, oldy = y, newx = x, newy = y;
4375   int element = Feld[x][y];
4376   int next_element = EL_UNDEFINED;
4377
4378   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4379     return;
4380
4381   if (IS_MOVING(x, y))
4382   {
4383     Moving2Blocked(x, y, &newx, &newy);
4384
4385     if (Feld[newx][newy] != EL_BLOCKED)
4386     {
4387       /* element is moving, but target field is not free (blocked), but
4388          already occupied by something different (example: acid pool);
4389          in this case, only remove the moving field, but not the target */
4390
4391       RemoveField(oldx, oldy);
4392
4393       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4394
4395       DrawLevelField(oldx, oldy);
4396
4397       return;
4398     }
4399   }
4400   else if (element == EL_BLOCKED)
4401   {
4402     Blocked2Moving(x, y, &oldx, &oldy);
4403     if (!IS_MOVING(oldx, oldy))
4404       return;
4405   }
4406
4407   if (element == EL_BLOCKED &&
4408       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4409        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4410        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4411        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4412        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4413        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4414     next_element = get_next_element(Feld[oldx][oldy]);
4415
4416   RemoveField(oldx, oldy);
4417   RemoveField(newx, newy);
4418
4419   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4420
4421   if (next_element != EL_UNDEFINED)
4422     Feld[oldx][oldy] = next_element;
4423
4424   DrawLevelField(oldx, oldy);
4425   DrawLevelField(newx, newy);
4426 }
4427
4428 void DrawDynamite(int x, int y)
4429 {
4430   int sx = SCREENX(x), sy = SCREENY(y);
4431   int graphic = el2img(Feld[x][y]);
4432   int frame;
4433
4434   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4435     return;
4436
4437   if (IS_WALKABLE_INSIDE(Back[x][y]))
4438     return;
4439
4440   if (Back[x][y])
4441     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4442   else if (Store[x][y])
4443     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4444
4445   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4446
4447   if (Back[x][y] || Store[x][y])
4448     DrawGraphicThruMask(sx, sy, graphic, frame);
4449   else
4450     DrawGraphic(sx, sy, graphic, frame);
4451 }
4452
4453 void CheckDynamite(int x, int y)
4454 {
4455   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4456   {
4457     MovDelay[x][y]--;
4458
4459     if (MovDelay[x][y] != 0)
4460     {
4461       DrawDynamite(x, y);
4462       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4463
4464       return;
4465     }
4466   }
4467
4468   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4469
4470   Bang(x, y);
4471 }
4472
4473 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4474 {
4475   boolean num_checked_players = 0;
4476   int i;
4477
4478   for (i = 0; i < MAX_PLAYERS; i++)
4479   {
4480     if (stored_player[i].active)
4481     {
4482       int sx = stored_player[i].jx;
4483       int sy = stored_player[i].jy;
4484
4485       if (num_checked_players == 0)
4486       {
4487         *sx1 = *sx2 = sx;
4488         *sy1 = *sy2 = sy;
4489       }
4490       else
4491       {
4492         *sx1 = MIN(*sx1, sx);
4493         *sy1 = MIN(*sy1, sy);
4494         *sx2 = MAX(*sx2, sx);
4495         *sy2 = MAX(*sy2, sy);
4496       }
4497
4498       num_checked_players++;
4499     }
4500   }
4501 }
4502
4503 static boolean checkIfAllPlayersFitToScreen_RND()
4504 {
4505   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4506
4507   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4508
4509   return (sx2 - sx1 < SCR_FIELDX &&
4510           sy2 - sy1 < SCR_FIELDY);
4511 }
4512
4513 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4514 {
4515   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4516
4517   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4518
4519   *sx = (sx1 + sx2) / 2;
4520   *sy = (sy1 + sy2) / 2;
4521 }
4522
4523 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4524                         boolean center_screen, boolean quick_relocation)
4525 {
4526   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4527   boolean no_delay = (tape.warp_forward);
4528   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4529   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4530
4531   if (quick_relocation)
4532   {
4533     int offset = game.scroll_delay_value;
4534
4535     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4536     {
4537       if (!level.shifted_relocation || center_screen)
4538       {
4539         /* quick relocation (without scrolling), with centering of screen */
4540
4541         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4542                     x > SBX_Right + MIDPOSX ? SBX_Right :
4543                     x - MIDPOSX);
4544
4545         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4546                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4547                     y - MIDPOSY);
4548       }
4549       else
4550       {
4551         /* quick relocation (without scrolling), but do not center screen */
4552
4553         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4554                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4555                                old_x - MIDPOSX);
4556
4557         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4558                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4559                                old_y - MIDPOSY);
4560
4561         int offset_x = x + (scroll_x - center_scroll_x);
4562         int offset_y = y + (scroll_y - center_scroll_y);
4563
4564         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4565                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4566                     offset_x - MIDPOSX);
4567
4568         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4569                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4570                     offset_y - MIDPOSY);
4571       }
4572     }
4573     else
4574     {
4575       /* quick relocation (without scrolling), inside visible screen area */
4576
4577       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
4578           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4579         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4580
4581       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
4582           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4583         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4584
4585       /* don't scroll over playfield boundaries */
4586       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4587         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4588
4589       /* don't scroll over playfield boundaries */
4590       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4591         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4592     }
4593
4594     RedrawPlayfield(TRUE, 0,0,0,0);
4595   }
4596   else
4597   {
4598 #if 1
4599     int scroll_xx, scroll_yy;
4600
4601     if (!level.shifted_relocation || center_screen)
4602     {
4603       /* visible relocation (with scrolling), with centering of screen */
4604
4605       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4606                    x > SBX_Right + MIDPOSX ? SBX_Right :
4607                    x - MIDPOSX);
4608
4609       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4610                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
4611                    y - MIDPOSY);
4612     }
4613     else
4614     {
4615       /* visible relocation (with scrolling), but do not center screen */
4616
4617       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4618                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
4619                              old_x - MIDPOSX);
4620
4621       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4622                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4623                              old_y - MIDPOSY);
4624
4625       int offset_x = x + (scroll_x - center_scroll_x);
4626       int offset_y = y + (scroll_y - center_scroll_y);
4627
4628       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4629                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4630                    offset_x - MIDPOSX);
4631
4632       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4633                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4634                    offset_y - MIDPOSY);
4635     }
4636
4637 #else
4638
4639     /* visible relocation (with scrolling), with centering of screen */
4640
4641     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4642                      x > SBX_Right + MIDPOSX ? SBX_Right :
4643                      x - MIDPOSX);
4644
4645     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4646                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
4647                      y - MIDPOSY);
4648 #endif
4649
4650     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
4651
4652     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4653     {
4654       int dx = 0, dy = 0;
4655       int fx = FX, fy = FY;
4656
4657       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4658       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4659
4660       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
4661         break;
4662
4663       scroll_x -= dx;
4664       scroll_y -= dy;
4665
4666       fx += dx * TILEX / 2;
4667       fy += dy * TILEY / 2;
4668
4669       ScrollLevel(dx, dy);
4670       DrawAllPlayers();
4671
4672       /* scroll in two steps of half tile size to make things smoother */
4673       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4674       FlushDisplay();
4675       Delay(wait_delay_value);
4676
4677       /* scroll second step to align at full tile size */
4678       BackToFront();
4679       Delay(wait_delay_value);
4680     }
4681
4682     DrawAllPlayers();
4683     BackToFront();
4684     Delay(wait_delay_value);
4685   }
4686 }
4687
4688 void RelocatePlayer(int jx, int jy, int el_player_raw)
4689 {
4690   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4691   int player_nr = GET_PLAYER_NR(el_player);
4692   struct PlayerInfo *player = &stored_player[player_nr];
4693   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4694   boolean no_delay = (tape.warp_forward);
4695   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4696   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4697   int old_jx = player->jx;
4698   int old_jy = player->jy;
4699   int old_element = Feld[old_jx][old_jy];
4700   int element = Feld[jx][jy];
4701   boolean player_relocated = (old_jx != jx || old_jy != jy);
4702
4703   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4704   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
4705   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4706   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
4707   int leave_side_horiz = move_dir_horiz;
4708   int leave_side_vert  = move_dir_vert;
4709   int enter_side = enter_side_horiz | enter_side_vert;
4710   int leave_side = leave_side_horiz | leave_side_vert;
4711
4712   if (player->GameOver)         /* do not reanimate dead player */
4713     return;
4714
4715   if (!player_relocated)        /* no need to relocate the player */
4716     return;
4717
4718   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
4719   {
4720     RemoveField(jx, jy);        /* temporarily remove newly placed player */
4721     DrawLevelField(jx, jy);
4722   }
4723
4724   if (player->present)
4725   {
4726     while (player->MovPos)
4727     {
4728       ScrollPlayer(player, SCROLL_GO_ON);
4729       ScrollScreen(NULL, SCROLL_GO_ON);
4730
4731       AdvanceFrameAndPlayerCounters(player->index_nr);
4732
4733       DrawPlayer(player);
4734
4735       BackToFront();
4736       Delay(wait_delay_value);
4737     }
4738
4739     DrawPlayer(player);         /* needed here only to cleanup last field */
4740     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
4741
4742     player->is_moving = FALSE;
4743   }
4744
4745   if (IS_CUSTOM_ELEMENT(old_element))
4746     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4747                                CE_LEFT_BY_PLAYER,
4748                                player->index_bit, leave_side);
4749
4750   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4751                                       CE_PLAYER_LEAVES_X,
4752                                       player->index_bit, leave_side);
4753
4754   Feld[jx][jy] = el_player;
4755   InitPlayerField(jx, jy, el_player, TRUE);
4756
4757   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4758   {
4759     Feld[jx][jy] = element;
4760     InitField(jx, jy, FALSE);
4761   }
4762
4763   /* only visually relocate centered player */
4764   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4765                      FALSE, level.instant_relocation);
4766
4767   TestIfPlayerTouchesBadThing(jx, jy);
4768   TestIfPlayerTouchesCustomElement(jx, jy);
4769
4770   if (IS_CUSTOM_ELEMENT(element))
4771     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4772                                player->index_bit, enter_side);
4773
4774   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4775                                       player->index_bit, enter_side);
4776 }
4777
4778 void Explode(int ex, int ey, int phase, int mode)
4779 {
4780   int x, y;
4781   int last_phase;
4782   int border_element;
4783
4784   /* !!! eliminate this variable !!! */
4785   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4786
4787   if (game.explosions_delayed)
4788   {
4789     ExplodeField[ex][ey] = mode;
4790     return;
4791   }
4792
4793   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
4794   {
4795     int center_element = Feld[ex][ey];
4796     int artwork_element, explosion_element;     /* set these values later */
4797
4798 #if 0
4799     /* --- This is only really needed (and now handled) in "Impact()". --- */
4800     /* do not explode moving elements that left the explode field in time */
4801     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4802         center_element == EL_EMPTY &&
4803         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4804       return;
4805 #endif
4806
4807 #if 0
4808     /* !!! at this place, the center element may be EL_BLOCKED !!! */
4809     if (mode == EX_TYPE_NORMAL ||
4810         mode == EX_TYPE_CENTER ||
4811         mode == EX_TYPE_CROSS)
4812       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4813 #endif
4814
4815     /* remove things displayed in background while burning dynamite */
4816     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4817       Back[ex][ey] = 0;
4818
4819     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4820     {
4821       /* put moving element to center field (and let it explode there) */
4822       center_element = MovingOrBlocked2Element(ex, ey);
4823       RemoveMovingField(ex, ey);
4824       Feld[ex][ey] = center_element;
4825     }
4826
4827     /* now "center_element" is finally determined -- set related values now */
4828     artwork_element = center_element;           /* for custom player artwork */
4829     explosion_element = center_element;         /* for custom player artwork */
4830
4831     if (IS_PLAYER(ex, ey))
4832     {
4833       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4834
4835       artwork_element = stored_player[player_nr].artwork_element;
4836
4837       if (level.use_explosion_element[player_nr])
4838       {
4839         explosion_element = level.explosion_element[player_nr];
4840         artwork_element = explosion_element;
4841       }
4842     }
4843
4844 #if 1
4845     if (mode == EX_TYPE_NORMAL ||
4846         mode == EX_TYPE_CENTER ||
4847         mode == EX_TYPE_CROSS)
4848       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4849 #endif
4850
4851     last_phase = element_info[explosion_element].explosion_delay + 1;
4852
4853     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4854     {
4855       int xx = x - ex + 1;
4856       int yy = y - ey + 1;
4857       int element;
4858
4859       if (!IN_LEV_FIELD(x, y) ||
4860           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4861           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
4862         continue;
4863
4864       element = Feld[x][y];
4865
4866       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4867       {
4868         element = MovingOrBlocked2Element(x, y);
4869
4870         if (!IS_EXPLOSION_PROOF(element))
4871           RemoveMovingField(x, y);
4872       }
4873
4874       /* indestructible elements can only explode in center (but not flames) */
4875       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4876                                            mode == EX_TYPE_BORDER)) ||
4877           element == EL_FLAMES)
4878         continue;
4879
4880       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4881          behaviour, for example when touching a yamyam that explodes to rocks
4882          with active deadly shield, a rock is created under the player !!! */
4883       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4884 #if 0
4885       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4886           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
4887            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
4888 #else
4889       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
4890 #endif
4891       {
4892         if (IS_ACTIVE_BOMB(element))
4893         {
4894           /* re-activate things under the bomb like gate or penguin */
4895           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
4896           Back[x][y] = 0;
4897         }
4898
4899         continue;
4900       }
4901
4902       /* save walkable background elements while explosion on same tile */
4903       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
4904           (x != ex || y != ey || mode == EX_TYPE_BORDER))
4905         Back[x][y] = element;
4906
4907       /* ignite explodable elements reached by other explosion */
4908       if (element == EL_EXPLOSION)
4909         element = Store2[x][y];
4910
4911       if (AmoebaNr[x][y] &&
4912           (element == EL_AMOEBA_FULL ||
4913            element == EL_BD_AMOEBA ||
4914            element == EL_AMOEBA_GROWING))
4915       {
4916         AmoebaCnt[AmoebaNr[x][y]]--;
4917         AmoebaCnt2[AmoebaNr[x][y]]--;
4918       }
4919
4920       RemoveField(x, y);
4921
4922       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
4923       {
4924         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
4925
4926         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
4927
4928         if (PLAYERINFO(ex, ey)->use_murphy)
4929           Store[x][y] = EL_EMPTY;
4930       }
4931
4932       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
4933          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
4934       else if (ELEM_IS_PLAYER(center_element))
4935         Store[x][y] = EL_EMPTY;
4936       else if (center_element == EL_YAMYAM)
4937         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4938       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4939         Store[x][y] = element_info[center_element].content.e[xx][yy];
4940 #if 1
4941       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4942          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4943          otherwise) -- FIX THIS !!! */
4944       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4945         Store[x][y] = element_info[element].content.e[1][1];
4946 #else
4947       else if (!CAN_EXPLODE(element))
4948         Store[x][y] = element_info[element].content.e[1][1];
4949 #endif
4950       else
4951         Store[x][y] = EL_EMPTY;
4952
4953       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4954           center_element == EL_AMOEBA_TO_DIAMOND)
4955         Store2[x][y] = element;
4956
4957       Feld[x][y] = EL_EXPLOSION;
4958       GfxElement[x][y] = artwork_element;
4959
4960       ExplodePhase[x][y] = 1;
4961       ExplodeDelay[x][y] = last_phase;
4962
4963       Stop[x][y] = TRUE;
4964     }
4965
4966     if (center_element == EL_YAMYAM)
4967       game.yamyam_content_nr =
4968         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4969
4970     return;
4971   }
4972
4973   if (Stop[ex][ey])
4974     return;
4975
4976   x = ex;
4977   y = ey;
4978
4979   if (phase == 1)
4980     GfxFrame[x][y] = 0;         /* restart explosion animation */
4981
4982   last_phase = ExplodeDelay[x][y];
4983
4984   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4985
4986 #ifdef DEBUG
4987
4988   /* activate this even in non-DEBUG version until cause for crash in
4989      getGraphicAnimationFrame() (see below) is found and eliminated */
4990
4991 #endif
4992 #if 1
4993
4994 #if 1
4995   /* this can happen if the player leaves an explosion just in time */
4996   if (GfxElement[x][y] == EL_UNDEFINED)
4997     GfxElement[x][y] = EL_EMPTY;
4998 #else
4999   if (GfxElement[x][y] == EL_UNDEFINED)
5000   {
5001     printf("\n\n");
5002     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5003     printf("Explode(): This should never happen!\n");
5004     printf("\n\n");
5005
5006     GfxElement[x][y] = EL_EMPTY;
5007   }
5008 #endif
5009
5010 #endif
5011
5012   border_element = Store2[x][y];
5013   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5014     border_element = StorePlayer[x][y];
5015
5016   if (phase == element_info[border_element].ignition_delay ||
5017       phase == last_phase)
5018   {
5019     boolean border_explosion = FALSE;
5020
5021     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5022         !PLAYER_EXPLOSION_PROTECTED(x, y))
5023     {
5024       KillPlayerUnlessExplosionProtected(x, y);
5025       border_explosion = TRUE;
5026     }
5027     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5028     {
5029       Feld[x][y] = Store2[x][y];
5030       Store2[x][y] = 0;
5031       Bang(x, y);
5032       border_explosion = TRUE;
5033     }
5034     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5035     {
5036       AmoebeUmwandeln(x, y);
5037       Store2[x][y] = 0;
5038       border_explosion = TRUE;
5039     }
5040
5041     /* if an element just explodes due to another explosion (chain-reaction),
5042        do not immediately end the new explosion when it was the last frame of
5043        the explosion (as it would be done in the following "if"-statement!) */
5044     if (border_explosion && phase == last_phase)
5045       return;
5046   }
5047
5048   if (phase == last_phase)
5049   {
5050     int element;
5051
5052     element = Feld[x][y] = Store[x][y];
5053     Store[x][y] = Store2[x][y] = 0;
5054     GfxElement[x][y] = EL_UNDEFINED;
5055
5056     /* player can escape from explosions and might therefore be still alive */
5057     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5058         element <= EL_PLAYER_IS_EXPLODING_4)
5059     {
5060       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5061       int explosion_element = EL_PLAYER_1 + player_nr;
5062       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5063       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5064
5065       if (level.use_explosion_element[player_nr])
5066         explosion_element = level.explosion_element[player_nr];
5067
5068       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5069                     element_info[explosion_element].content.e[xx][yy]);
5070     }
5071
5072     /* restore probably existing indestructible background element */
5073     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5074       element = Feld[x][y] = Back[x][y];
5075     Back[x][y] = 0;
5076
5077     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5078     GfxDir[x][y] = MV_NONE;
5079     ChangeDelay[x][y] = 0;
5080     ChangePage[x][y] = -1;
5081
5082 #if USE_NEW_CUSTOM_VALUE
5083     CustomValue[x][y] = 0;
5084 #endif
5085
5086     InitField_WithBug2(x, y, FALSE);
5087
5088     DrawLevelField(x, y);
5089
5090     TestIfElementTouchesCustomElement(x, y);
5091
5092     if (GFX_CRUMBLED(element))
5093       DrawLevelFieldCrumbledSandNeighbours(x, y);
5094
5095     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5096       StorePlayer[x][y] = 0;
5097
5098     if (ELEM_IS_PLAYER(element))
5099       RelocatePlayer(x, y, element);
5100   }
5101   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5102   {
5103     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5104     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5105
5106     if (phase == delay)
5107       DrawLevelFieldCrumbledSand(x, y);
5108
5109     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5110     {
5111       DrawLevelElement(x, y, Back[x][y]);
5112       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5113     }
5114     else if (IS_WALKABLE_UNDER(Back[x][y]))
5115     {
5116       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5117       DrawLevelElementThruMask(x, y, Back[x][y]);
5118     }
5119     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5120       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5121   }
5122 }
5123
5124 void DynaExplode(int ex, int ey)
5125 {
5126   int i, j;
5127   int dynabomb_element = Feld[ex][ey];
5128   int dynabomb_size = 1;
5129   boolean dynabomb_xl = FALSE;
5130   struct PlayerInfo *player;
5131   static int xy[4][2] =
5132   {
5133     { 0, -1 },
5134     { -1, 0 },
5135     { +1, 0 },
5136     { 0, +1 }
5137   };
5138
5139   if (IS_ACTIVE_BOMB(dynabomb_element))
5140   {
5141     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5142     dynabomb_size = player->dynabomb_size;
5143     dynabomb_xl = player->dynabomb_xl;
5144     player->dynabombs_left++;
5145   }
5146
5147   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5148
5149   for (i = 0; i < NUM_DIRECTIONS; i++)
5150   {
5151     for (j = 1; j <= dynabomb_size; j++)
5152     {
5153       int x = ex + j * xy[i][0];
5154       int y = ey + j * xy[i][1];
5155       int element;
5156
5157       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5158         break;
5159
5160       element = Feld[x][y];
5161
5162       /* do not restart explosions of fields with active bombs */
5163       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5164         continue;
5165
5166       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5167
5168       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5169           !IS_DIGGABLE(element) && !dynabomb_xl)
5170         break;
5171     }
5172   }
5173 }
5174
5175 void Bang(int x, int y)
5176 {
5177   int element = MovingOrBlocked2Element(x, y);
5178   int explosion_type = EX_TYPE_NORMAL;
5179
5180   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5181   {
5182     struct PlayerInfo *player = PLAYERINFO(x, y);
5183
5184     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5185                             player->element_nr);
5186
5187     if (level.use_explosion_element[player->index_nr])
5188     {
5189       int explosion_element = level.explosion_element[player->index_nr];
5190
5191       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5192         explosion_type = EX_TYPE_CROSS;
5193       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5194         explosion_type = EX_TYPE_CENTER;
5195     }
5196   }
5197
5198   switch (element)
5199   {
5200     case EL_BUG:
5201     case EL_SPACESHIP:
5202     case EL_BD_BUTTERFLY:
5203     case EL_BD_FIREFLY:
5204     case EL_YAMYAM:
5205     case EL_DARK_YAMYAM:
5206     case EL_ROBOT:
5207     case EL_PACMAN:
5208     case EL_MOLE:
5209       RaiseScoreElement(element);
5210       break;
5211
5212     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5213     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5214     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5215     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5216     case EL_DYNABOMB_INCREASE_NUMBER:
5217     case EL_DYNABOMB_INCREASE_SIZE:
5218     case EL_DYNABOMB_INCREASE_POWER:
5219       explosion_type = EX_TYPE_DYNA;
5220       break;
5221
5222     case EL_DC_LANDMINE:
5223 #if 0
5224     case EL_EM_EXIT_OPEN:
5225     case EL_EM_STEEL_EXIT_OPEN:
5226 #endif
5227       explosion_type = EX_TYPE_CENTER;
5228       break;
5229
5230     case EL_PENGUIN:
5231     case EL_LAMP:
5232     case EL_LAMP_ACTIVE:
5233     case EL_AMOEBA_TO_DIAMOND:
5234       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5235         explosion_type = EX_TYPE_CENTER;
5236       break;
5237
5238     default:
5239       if (element_info[element].explosion_type == EXPLODES_CROSS)
5240         explosion_type = EX_TYPE_CROSS;
5241       else if (element_info[element].explosion_type == EXPLODES_1X1)
5242         explosion_type = EX_TYPE_CENTER;
5243       break;
5244   }
5245
5246   if (explosion_type == EX_TYPE_DYNA)
5247     DynaExplode(x, y);
5248   else
5249     Explode(x, y, EX_PHASE_START, explosion_type);
5250
5251   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5252 }
5253
5254 void SplashAcid(int x, int y)
5255 {
5256   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5257       (!IN_LEV_FIELD(x - 1, y - 2) ||
5258        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5259     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5260
5261   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5262       (!IN_LEV_FIELD(x + 1, y - 2) ||
5263        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5264     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5265
5266   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5267 }
5268
5269 static void InitBeltMovement()
5270 {
5271   static int belt_base_element[4] =
5272   {
5273     EL_CONVEYOR_BELT_1_LEFT,
5274     EL_CONVEYOR_BELT_2_LEFT,
5275     EL_CONVEYOR_BELT_3_LEFT,
5276     EL_CONVEYOR_BELT_4_LEFT
5277   };
5278   static int belt_base_active_element[4] =
5279   {
5280     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5281     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5282     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5283     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5284   };
5285
5286   int x, y, i, j;
5287
5288   /* set frame order for belt animation graphic according to belt direction */
5289   for (i = 0; i < NUM_BELTS; i++)
5290   {
5291     int belt_nr = i;
5292
5293     for (j = 0; j < NUM_BELT_PARTS; j++)
5294     {
5295       int element = belt_base_active_element[belt_nr] + j;
5296       int graphic = el2img(element);
5297
5298       if (game.belt_dir[i] == MV_LEFT)
5299         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5300       else
5301         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
5302     }
5303   }
5304
5305   SCAN_PLAYFIELD(x, y)
5306   {
5307     int element = Feld[x][y];
5308
5309     for (i = 0; i < NUM_BELTS; i++)
5310     {
5311       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5312       {
5313         int e_belt_nr = getBeltNrFromBeltElement(element);
5314         int belt_nr = i;
5315
5316         if (e_belt_nr == belt_nr)
5317         {
5318           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5319
5320           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5321         }
5322       }
5323     }
5324   }
5325 }
5326
5327 static void ToggleBeltSwitch(int x, int y)
5328 {
5329   static int belt_base_element[4] =
5330   {
5331     EL_CONVEYOR_BELT_1_LEFT,
5332     EL_CONVEYOR_BELT_2_LEFT,
5333     EL_CONVEYOR_BELT_3_LEFT,
5334     EL_CONVEYOR_BELT_4_LEFT
5335   };
5336   static int belt_base_active_element[4] =
5337   {
5338     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5339     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5340     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5341     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5342   };
5343   static int belt_base_switch_element[4] =
5344   {
5345     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5346     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5347     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5348     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5349   };
5350   static int belt_move_dir[4] =
5351   {
5352     MV_LEFT,
5353     MV_NONE,
5354     MV_RIGHT,
5355     MV_NONE,
5356   };
5357
5358   int element = Feld[x][y];
5359   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5360   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5361   int belt_dir = belt_move_dir[belt_dir_nr];
5362   int xx, yy, i;
5363
5364   if (!IS_BELT_SWITCH(element))
5365     return;
5366
5367   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5368   game.belt_dir[belt_nr] = belt_dir;
5369
5370   if (belt_dir_nr == 3)
5371     belt_dir_nr = 1;
5372
5373   /* set frame order for belt animation graphic according to belt direction */
5374   for (i = 0; i < NUM_BELT_PARTS; i++)
5375   {
5376     int element = belt_base_active_element[belt_nr] + i;
5377     int graphic = el2img(element);
5378
5379     if (belt_dir == MV_LEFT)
5380       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5381     else
5382       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
5383   }
5384
5385   SCAN_PLAYFIELD(xx, yy)
5386   {
5387     int element = Feld[xx][yy];
5388
5389     if (IS_BELT_SWITCH(element))
5390     {
5391       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5392
5393       if (e_belt_nr == belt_nr)
5394       {
5395         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5396         DrawLevelField(xx, yy);
5397       }
5398     }
5399     else if (IS_BELT(element) && belt_dir != MV_NONE)
5400     {
5401       int e_belt_nr = getBeltNrFromBeltElement(element);
5402
5403       if (e_belt_nr == belt_nr)
5404       {
5405         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5406
5407         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5408         DrawLevelField(xx, yy);
5409       }
5410     }
5411     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5412     {
5413       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5414
5415       if (e_belt_nr == belt_nr)
5416       {
5417         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5418
5419         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5420         DrawLevelField(xx, yy);
5421       }
5422     }
5423   }
5424 }
5425
5426 static void ToggleSwitchgateSwitch(int x, int y)
5427 {
5428   int xx, yy;
5429
5430   game.switchgate_pos = !game.switchgate_pos;
5431
5432   SCAN_PLAYFIELD(xx, yy)
5433   {
5434     int element = Feld[xx][yy];
5435
5436 #if !USE_BOTH_SWITCHGATE_SWITCHES
5437     if (element == EL_SWITCHGATE_SWITCH_UP ||
5438         element == EL_SWITCHGATE_SWITCH_DOWN)
5439     {
5440       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5441       DrawLevelField(xx, yy);
5442     }
5443     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5444              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5445     {
5446       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5447       DrawLevelField(xx, yy);
5448     }
5449 #else
5450     if (element == EL_SWITCHGATE_SWITCH_UP)
5451     {
5452       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5453       DrawLevelField(xx, yy);
5454     }
5455     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5456     {
5457       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5458       DrawLevelField(xx, yy);
5459     }
5460     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5461     {
5462       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5463       DrawLevelField(xx, yy);
5464     }
5465     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5466     {
5467       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5468       DrawLevelField(xx, yy);
5469     }
5470 #endif
5471     else if (element == EL_SWITCHGATE_OPEN ||
5472              element == EL_SWITCHGATE_OPENING)
5473     {
5474       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5475
5476       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5477     }
5478     else if (element == EL_SWITCHGATE_CLOSED ||
5479              element == EL_SWITCHGATE_CLOSING)
5480     {
5481       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5482
5483       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5484     }
5485   }
5486 }
5487
5488 static int getInvisibleActiveFromInvisibleElement(int element)
5489 {
5490   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5491           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5492           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5493           element);
5494 }
5495
5496 static int getInvisibleFromInvisibleActiveElement(int element)
5497 {
5498   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5499           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5500           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5501           element);
5502 }
5503
5504 static void RedrawAllLightSwitchesAndInvisibleElements()
5505 {
5506   int x, y;
5507
5508   SCAN_PLAYFIELD(x, y)
5509   {
5510     int element = Feld[x][y];
5511
5512     if (element == EL_LIGHT_SWITCH &&
5513         game.light_time_left > 0)
5514     {
5515       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5516       DrawLevelField(x, y);
5517     }
5518     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5519              game.light_time_left == 0)
5520     {
5521       Feld[x][y] = EL_LIGHT_SWITCH;
5522       DrawLevelField(x, y);
5523     }
5524     else if (element == EL_EMC_DRIPPER &&
5525              game.light_time_left > 0)
5526     {
5527       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5528       DrawLevelField(x, y);
5529     }
5530     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5531              game.light_time_left == 0)
5532     {
5533       Feld[x][y] = EL_EMC_DRIPPER;
5534       DrawLevelField(x, y);
5535     }
5536     else if (element == EL_INVISIBLE_STEELWALL ||
5537              element == EL_INVISIBLE_WALL ||
5538              element == EL_INVISIBLE_SAND)
5539     {
5540       if (game.light_time_left > 0)
5541         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5542
5543       DrawLevelField(x, y);
5544
5545       /* uncrumble neighbour fields, if needed */
5546       if (element == EL_INVISIBLE_SAND)
5547         DrawLevelFieldCrumbledSandNeighbours(x, y);
5548     }
5549     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5550              element == EL_INVISIBLE_WALL_ACTIVE ||
5551              element == EL_INVISIBLE_SAND_ACTIVE)
5552     {
5553       if (game.light_time_left == 0)
5554         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5555
5556       DrawLevelField(x, y);
5557
5558       /* re-crumble neighbour fields, if needed */
5559       if (element == EL_INVISIBLE_SAND)
5560         DrawLevelFieldCrumbledSandNeighbours(x, y);
5561     }
5562   }
5563 }
5564
5565 static void RedrawAllInvisibleElementsForLenses()
5566 {
5567   int x, y;
5568
5569   SCAN_PLAYFIELD(x, y)
5570   {
5571     int element = Feld[x][y];
5572
5573     if (element == EL_EMC_DRIPPER &&
5574         game.lenses_time_left > 0)
5575     {
5576       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5577       DrawLevelField(x, y);
5578     }
5579     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5580              game.lenses_time_left == 0)
5581     {
5582       Feld[x][y] = EL_EMC_DRIPPER;
5583       DrawLevelField(x, y);
5584     }
5585     else if (element == EL_INVISIBLE_STEELWALL ||
5586              element == EL_INVISIBLE_WALL ||
5587              element == EL_INVISIBLE_SAND)
5588     {
5589       if (game.lenses_time_left > 0)
5590         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5591
5592       DrawLevelField(x, y);
5593
5594       /* uncrumble neighbour fields, if needed */
5595       if (element == EL_INVISIBLE_SAND)
5596         DrawLevelFieldCrumbledSandNeighbours(x, y);
5597     }
5598     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5599              element == EL_INVISIBLE_WALL_ACTIVE ||
5600              element == EL_INVISIBLE_SAND_ACTIVE)
5601     {
5602       if (game.lenses_time_left == 0)
5603         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5604
5605       DrawLevelField(x, y);
5606
5607       /* re-crumble neighbour fields, if needed */
5608       if (element == EL_INVISIBLE_SAND)
5609         DrawLevelFieldCrumbledSandNeighbours(x, y);
5610     }
5611   }
5612 }
5613
5614 static void RedrawAllInvisibleElementsForMagnifier()
5615 {
5616   int x, y;
5617
5618   SCAN_PLAYFIELD(x, y)
5619   {
5620     int element = Feld[x][y];
5621
5622     if (element == EL_EMC_FAKE_GRASS &&
5623         game.magnify_time_left > 0)
5624     {
5625       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5626       DrawLevelField(x, y);
5627     }
5628     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5629              game.magnify_time_left == 0)
5630     {
5631       Feld[x][y] = EL_EMC_FAKE_GRASS;
5632       DrawLevelField(x, y);
5633     }
5634     else if (IS_GATE_GRAY(element) &&
5635              game.magnify_time_left > 0)
5636     {
5637       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5638                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5639                     IS_EM_GATE_GRAY(element) ?
5640                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5641                     IS_EMC_GATE_GRAY(element) ?
5642                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5643                     element);
5644       DrawLevelField(x, y);
5645     }
5646     else if (IS_GATE_GRAY_ACTIVE(element) &&
5647              game.magnify_time_left == 0)
5648     {
5649       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5650                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5651                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5652                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5653                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5654                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5655                     element);
5656       DrawLevelField(x, y);
5657     }
5658   }
5659 }
5660
5661 static void ToggleLightSwitch(int x, int y)
5662 {
5663   int element = Feld[x][y];
5664
5665   game.light_time_left =
5666     (element == EL_LIGHT_SWITCH ?
5667      level.time_light * FRAMES_PER_SECOND : 0);
5668
5669   RedrawAllLightSwitchesAndInvisibleElements();
5670 }
5671
5672 static void ActivateTimegateSwitch(int x, int y)
5673 {
5674   int xx, yy;
5675
5676   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5677
5678   SCAN_PLAYFIELD(xx, yy)
5679   {
5680     int element = Feld[xx][yy];
5681
5682     if (element == EL_TIMEGATE_CLOSED ||
5683         element == EL_TIMEGATE_CLOSING)
5684     {
5685       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5686       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5687     }
5688
5689     /*
5690     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5691     {
5692       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5693       DrawLevelField(xx, yy);
5694     }
5695     */
5696
5697   }
5698
5699 #if 1
5700   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5701                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5702 #else
5703   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5704 #endif
5705 }
5706
5707 void Impact(int x, int y)
5708 {
5709   boolean last_line = (y == lev_fieldy - 1);
5710   boolean object_hit = FALSE;
5711   boolean impact = (last_line || object_hit);
5712   int element = Feld[x][y];
5713   int smashed = EL_STEELWALL;
5714
5715   if (!last_line)       /* check if element below was hit */
5716   {
5717     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5718       return;
5719
5720     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5721                                          MovDir[x][y + 1] != MV_DOWN ||
5722                                          MovPos[x][y + 1] <= TILEY / 2));
5723
5724     /* do not smash moving elements that left the smashed field in time */
5725     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5726         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5727       object_hit = FALSE;
5728
5729 #if USE_QUICKSAND_IMPACT_BUGFIX
5730     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5731     {
5732       RemoveMovingField(x, y + 1);
5733       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5734       Feld[x][y + 2] = EL_ROCK;
5735       DrawLevelField(x, y + 2);
5736
5737       object_hit = TRUE;
5738     }
5739
5740     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5741     {
5742       RemoveMovingField(x, y + 1);
5743       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5744       Feld[x][y + 2] = EL_ROCK;
5745       DrawLevelField(x, y + 2);
5746
5747       object_hit = TRUE;
5748     }
5749 #endif
5750
5751     if (object_hit)
5752       smashed = MovingOrBlocked2Element(x, y + 1);
5753
5754     impact = (last_line || object_hit);
5755   }
5756
5757   if (!last_line && smashed == EL_ACID) /* element falls into acid */
5758   {
5759     SplashAcid(x, y + 1);
5760     return;
5761   }
5762
5763   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5764   /* only reset graphic animation if graphic really changes after impact */
5765   if (impact &&
5766       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5767   {
5768     ResetGfxAnimation(x, y);
5769     DrawLevelField(x, y);
5770   }
5771
5772   if (impact && CAN_EXPLODE_IMPACT(element))
5773   {
5774     Bang(x, y);
5775     return;
5776   }
5777   else if (impact && element == EL_PEARL &&
5778            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5779   {
5780     ResetGfxAnimation(x, y);
5781
5782     Feld[x][y] = EL_PEARL_BREAKING;
5783     PlayLevelSound(x, y, SND_PEARL_BREAKING);
5784     return;
5785   }
5786   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5787   {
5788     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5789
5790     return;
5791   }
5792
5793   if (impact && element == EL_AMOEBA_DROP)
5794   {
5795     if (object_hit && IS_PLAYER(x, y + 1))
5796       KillPlayerUnlessEnemyProtected(x, y + 1);
5797     else if (object_hit && smashed == EL_PENGUIN)
5798       Bang(x, y + 1);
5799     else
5800     {
5801       Feld[x][y] = EL_AMOEBA_GROWING;
5802       Store[x][y] = EL_AMOEBA_WET;
5803
5804       ResetRandomAnimationValue(x, y);
5805     }
5806     return;
5807   }
5808
5809   if (object_hit)               /* check which object was hit */
5810   {
5811     if ((CAN_PASS_MAGIC_WALL(element) && 
5812          (smashed == EL_MAGIC_WALL ||
5813           smashed == EL_BD_MAGIC_WALL)) ||
5814         (CAN_PASS_DC_MAGIC_WALL(element) &&
5815          smashed == EL_DC_MAGIC_WALL))
5816     {
5817       int xx, yy;
5818       int activated_magic_wall =
5819         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5820          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5821          EL_DC_MAGIC_WALL_ACTIVE);
5822
5823       /* activate magic wall / mill */
5824       SCAN_PLAYFIELD(xx, yy)
5825       {
5826         if (Feld[xx][yy] == smashed)
5827           Feld[xx][yy] = activated_magic_wall;
5828       }
5829
5830       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5831       game.magic_wall_active = TRUE;
5832
5833       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5834                             SND_MAGIC_WALL_ACTIVATING :
5835                             smashed == EL_BD_MAGIC_WALL ?
5836                             SND_BD_MAGIC_WALL_ACTIVATING :
5837                             SND_DC_MAGIC_WALL_ACTIVATING));
5838     }
5839
5840     if (IS_PLAYER(x, y + 1))
5841     {
5842       if (CAN_SMASH_PLAYER(element))
5843       {
5844         KillPlayerUnlessEnemyProtected(x, y + 1);
5845         return;
5846       }
5847     }
5848     else if (smashed == EL_PENGUIN)
5849     {
5850       if (CAN_SMASH_PLAYER(element))
5851       {
5852         Bang(x, y + 1);
5853         return;
5854       }
5855     }
5856     else if (element == EL_BD_DIAMOND)
5857     {
5858       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5859       {
5860         Bang(x, y + 1);
5861         return;
5862       }
5863     }
5864     else if (((element == EL_SP_INFOTRON ||
5865                element == EL_SP_ZONK) &&
5866               (smashed == EL_SP_SNIKSNAK ||
5867                smashed == EL_SP_ELECTRON ||
5868                smashed == EL_SP_DISK_ORANGE)) ||
5869              (element == EL_SP_INFOTRON &&
5870               smashed == EL_SP_DISK_YELLOW))
5871     {
5872       Bang(x, y + 1);
5873       return;
5874     }
5875     else if (CAN_SMASH_EVERYTHING(element))
5876     {
5877       if (IS_CLASSIC_ENEMY(smashed) ||
5878           CAN_EXPLODE_SMASHED(smashed))
5879       {
5880         Bang(x, y + 1);
5881         return;
5882       }
5883       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5884       {
5885         if (smashed == EL_LAMP ||
5886             smashed == EL_LAMP_ACTIVE)
5887         {
5888           Bang(x, y + 1);
5889           return;
5890         }
5891         else if (smashed == EL_NUT)
5892         {
5893           Feld[x][y + 1] = EL_NUT_BREAKING;
5894           PlayLevelSound(x, y, SND_NUT_BREAKING);
5895           RaiseScoreElement(EL_NUT);
5896           return;
5897         }
5898         else if (smashed == EL_PEARL)
5899         {
5900           ResetGfxAnimation(x, y);
5901
5902           Feld[x][y + 1] = EL_PEARL_BREAKING;
5903           PlayLevelSound(x, y, SND_PEARL_BREAKING);
5904           return;
5905         }
5906         else if (smashed == EL_DIAMOND)
5907         {
5908           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
5909           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
5910           return;
5911         }
5912         else if (IS_BELT_SWITCH(smashed))
5913         {
5914           ToggleBeltSwitch(x, y + 1);
5915         }
5916         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
5917                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
5918                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
5919                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
5920         {
5921           ToggleSwitchgateSwitch(x, y + 1);
5922         }
5923         else if (smashed == EL_LIGHT_SWITCH ||
5924                  smashed == EL_LIGHT_SWITCH_ACTIVE)
5925         {
5926           ToggleLightSwitch(x, y + 1);
5927         }
5928         else
5929         {
5930 #if 0
5931           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
5932 #endif
5933
5934           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5935
5936           CheckElementChangeBySide(x, y + 1, smashed, element,
5937                                    CE_SWITCHED, CH_SIDE_TOP);
5938           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5939                                             CH_SIDE_TOP);
5940         }
5941       }
5942       else
5943       {
5944         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5945       }
5946     }
5947   }
5948
5949   /* play sound of magic wall / mill */
5950   if (!last_line &&
5951       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5952        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
5953        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
5954   {
5955     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5956       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5957     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5958       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5959     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5960       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5961
5962     return;
5963   }
5964
5965   /* play sound of object that hits the ground */
5966   if (last_line || object_hit)
5967     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5968 }
5969
5970 inline static void TurnRoundExt(int x, int y)
5971 {
5972   static struct
5973   {
5974     int dx, dy;
5975   } move_xy[] =
5976   {
5977     {  0,  0 },
5978     { -1,  0 },
5979     { +1,  0 },
5980     {  0,  0 },
5981     {  0, -1 },
5982     {  0,  0 }, { 0, 0 }, { 0, 0 },
5983     {  0, +1 }
5984   };
5985   static struct
5986   {
5987     int left, right, back;
5988   } turn[] =
5989   {
5990     { 0,        0,              0        },
5991     { MV_DOWN,  MV_UP,          MV_RIGHT },
5992     { MV_UP,    MV_DOWN,        MV_LEFT  },
5993     { 0,        0,              0        },
5994     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
5995     { 0,        0,              0        },
5996     { 0,        0,              0        },
5997     { 0,        0,              0        },
5998     { MV_RIGHT, MV_LEFT,        MV_UP    }
5999   };
6000
6001   int element = Feld[x][y];
6002   int move_pattern = element_info[element].move_pattern;
6003
6004   int old_move_dir = MovDir[x][y];
6005   int left_dir  = turn[old_move_dir].left;
6006   int right_dir = turn[old_move_dir].right;
6007   int back_dir  = turn[old_move_dir].back;
6008
6009   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6010   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6011   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6012   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6013
6014   int left_x  = x + left_dx,  left_y  = y + left_dy;
6015   int right_x = x + right_dx, right_y = y + right_dy;
6016   int move_x  = x + move_dx,  move_y  = y + move_dy;
6017
6018   int xx, yy;
6019
6020   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6021   {
6022     TestIfBadThingTouchesOtherBadThing(x, y);
6023
6024     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6025       MovDir[x][y] = right_dir;
6026     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6027       MovDir[x][y] = left_dir;
6028
6029     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6030       MovDelay[x][y] = 9;
6031     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6032       MovDelay[x][y] = 1;
6033   }
6034   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6035   {
6036     TestIfBadThingTouchesOtherBadThing(x, y);
6037
6038     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6039       MovDir[x][y] = left_dir;
6040     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6041       MovDir[x][y] = right_dir;
6042
6043     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6044       MovDelay[x][y] = 9;
6045     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6046       MovDelay[x][y] = 1;
6047   }
6048   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6049   {
6050     TestIfBadThingTouchesOtherBadThing(x, y);
6051
6052     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6053       MovDir[x][y] = left_dir;
6054     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6055       MovDir[x][y] = right_dir;
6056
6057     if (MovDir[x][y] != old_move_dir)
6058       MovDelay[x][y] = 9;
6059   }
6060   else if (element == EL_YAMYAM)
6061   {
6062     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6063     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6064
6065     if (can_turn_left && can_turn_right)
6066       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6067     else if (can_turn_left)
6068       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6069     else if (can_turn_right)
6070       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6071     else
6072       MovDir[x][y] = back_dir;
6073
6074     MovDelay[x][y] = 16 + 16 * RND(3);
6075   }
6076   else if (element == EL_DARK_YAMYAM)
6077   {
6078     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6079                                                          left_x, left_y);
6080     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6081                                                          right_x, right_y);
6082
6083     if (can_turn_left && can_turn_right)
6084       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6085     else if (can_turn_left)
6086       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6087     else if (can_turn_right)
6088       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6089     else
6090       MovDir[x][y] = back_dir;
6091
6092     MovDelay[x][y] = 16 + 16 * RND(3);
6093   }
6094   else if (element == EL_PACMAN)
6095   {
6096     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6097     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6098
6099     if (can_turn_left && can_turn_right)
6100       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6101     else if (can_turn_left)
6102       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6103     else if (can_turn_right)
6104       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6105     else
6106       MovDir[x][y] = back_dir;
6107
6108     MovDelay[x][y] = 6 + RND(40);
6109   }
6110   else if (element == EL_PIG)
6111   {
6112     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6113     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6114     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6115     boolean should_turn_left, should_turn_right, should_move_on;
6116     int rnd_value = 24;
6117     int rnd = RND(rnd_value);
6118
6119     should_turn_left = (can_turn_left &&
6120                         (!can_move_on ||
6121                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6122                                                    y + back_dy + left_dy)));
6123     should_turn_right = (can_turn_right &&
6124                          (!can_move_on ||
6125                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6126                                                     y + back_dy + right_dy)));
6127     should_move_on = (can_move_on &&
6128                       (!can_turn_left ||
6129                        !can_turn_right ||
6130                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6131                                                  y + move_dy + left_dy) ||
6132                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6133                                                  y + move_dy + right_dy)));
6134
6135     if (should_turn_left || should_turn_right || should_move_on)
6136     {
6137       if (should_turn_left && should_turn_right && should_move_on)
6138         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6139                         rnd < 2 * rnd_value / 3 ? right_dir :
6140                         old_move_dir);
6141       else if (should_turn_left && should_turn_right)
6142         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6143       else if (should_turn_left && should_move_on)
6144         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6145       else if (should_turn_right && should_move_on)
6146         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6147       else if (should_turn_left)
6148         MovDir[x][y] = left_dir;
6149       else if (should_turn_right)
6150         MovDir[x][y] = right_dir;
6151       else if (should_move_on)
6152         MovDir[x][y] = old_move_dir;
6153     }
6154     else if (can_move_on && rnd > rnd_value / 8)
6155       MovDir[x][y] = old_move_dir;
6156     else if (can_turn_left && can_turn_right)
6157       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6158     else if (can_turn_left && rnd > rnd_value / 8)
6159       MovDir[x][y] = left_dir;
6160     else if (can_turn_right && rnd > rnd_value/8)
6161       MovDir[x][y] = right_dir;
6162     else
6163       MovDir[x][y] = back_dir;
6164
6165     xx = x + move_xy[MovDir[x][y]].dx;
6166     yy = y + move_xy[MovDir[x][y]].dy;
6167
6168     if (!IN_LEV_FIELD(xx, yy) ||
6169         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6170       MovDir[x][y] = old_move_dir;
6171
6172     MovDelay[x][y] = 0;
6173   }
6174   else if (element == EL_DRAGON)
6175   {
6176     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6177     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6178     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6179     int rnd_value = 24;
6180     int rnd = RND(rnd_value);
6181
6182     if (can_move_on && rnd > rnd_value / 8)
6183       MovDir[x][y] = old_move_dir;
6184     else if (can_turn_left && can_turn_right)
6185       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6186     else if (can_turn_left && rnd > rnd_value / 8)
6187       MovDir[x][y] = left_dir;
6188     else if (can_turn_right && rnd > rnd_value / 8)
6189       MovDir[x][y] = right_dir;
6190     else
6191       MovDir[x][y] = back_dir;
6192
6193     xx = x + move_xy[MovDir[x][y]].dx;
6194     yy = y + move_xy[MovDir[x][y]].dy;
6195
6196     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6197       MovDir[x][y] = old_move_dir;
6198
6199     MovDelay[x][y] = 0;
6200   }
6201   else if (element == EL_MOLE)
6202   {
6203     boolean can_move_on =
6204       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6205                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6206                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6207     if (!can_move_on)
6208     {
6209       boolean can_turn_left =
6210         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6211                               IS_AMOEBOID(Feld[left_x][left_y])));
6212
6213       boolean can_turn_right =
6214         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6215                               IS_AMOEBOID(Feld[right_x][right_y])));
6216
6217       if (can_turn_left && can_turn_right)
6218         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6219       else if (can_turn_left)
6220         MovDir[x][y] = left_dir;
6221       else
6222         MovDir[x][y] = right_dir;
6223     }
6224
6225     if (MovDir[x][y] != old_move_dir)
6226       MovDelay[x][y] = 9;
6227   }
6228   else if (element == EL_BALLOON)
6229   {
6230     MovDir[x][y] = game.wind_direction;
6231     MovDelay[x][y] = 0;
6232   }
6233   else if (element == EL_SPRING)
6234   {
6235 #if USE_NEW_SPRING_BUMPER
6236     if (MovDir[x][y] & MV_HORIZONTAL)
6237     {
6238       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6239           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6240       {
6241         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6242         ResetGfxAnimation(move_x, move_y);
6243         DrawLevelField(move_x, move_y);
6244
6245         MovDir[x][y] = back_dir;
6246       }
6247       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6248                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6249         MovDir[x][y] = MV_NONE;
6250     }
6251 #else
6252     if (MovDir[x][y] & MV_HORIZONTAL &&
6253         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6254          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6255       MovDir[x][y] = MV_NONE;
6256 #endif
6257
6258     MovDelay[x][y] = 0;
6259   }
6260   else if (element == EL_ROBOT ||
6261            element == EL_SATELLITE ||
6262            element == EL_PENGUIN ||
6263            element == EL_EMC_ANDROID)
6264   {
6265     int attr_x = -1, attr_y = -1;
6266
6267     if (AllPlayersGone)
6268     {
6269       attr_x = ExitX;
6270       attr_y = ExitY;
6271     }
6272     else
6273     {
6274       int i;
6275
6276       for (i = 0; i < MAX_PLAYERS; i++)
6277       {
6278         struct PlayerInfo *player = &stored_player[i];
6279         int jx = player->jx, jy = player->jy;
6280
6281         if (!player->active)
6282           continue;
6283
6284         if (attr_x == -1 ||
6285             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6286         {
6287           attr_x = jx;
6288           attr_y = jy;
6289         }
6290       }
6291     }
6292
6293     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6294         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6295          game.engine_version < VERSION_IDENT(3,1,0,0)))
6296     {
6297       attr_x = ZX;
6298       attr_y = ZY;
6299     }
6300
6301     if (element == EL_PENGUIN)
6302     {
6303       int i;
6304       static int xy[4][2] =
6305       {
6306         { 0, -1 },
6307         { -1, 0 },
6308         { +1, 0 },
6309         { 0, +1 }
6310       };
6311
6312       for (i = 0; i < NUM_DIRECTIONS; i++)
6313       {
6314         int ex = x + xy[i][0];
6315         int ey = y + xy[i][1];
6316
6317         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6318                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6319                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6320                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6321         {
6322           attr_x = ex;
6323           attr_y = ey;
6324           break;
6325         }
6326       }
6327     }
6328
6329     MovDir[x][y] = MV_NONE;
6330     if (attr_x < x)
6331       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6332     else if (attr_x > x)
6333       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6334     if (attr_y < y)
6335       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6336     else if (attr_y > y)
6337       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6338
6339     if (element == EL_ROBOT)
6340     {
6341       int newx, newy;
6342
6343       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6344         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6345       Moving2Blocked(x, y, &newx, &newy);
6346
6347       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6348         MovDelay[x][y] = 8 + 8 * !RND(3);
6349       else
6350         MovDelay[x][y] = 16;
6351     }
6352     else if (element == EL_PENGUIN)
6353     {
6354       int newx, newy;
6355
6356       MovDelay[x][y] = 1;
6357
6358       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6359       {
6360         boolean first_horiz = RND(2);
6361         int new_move_dir = MovDir[x][y];
6362
6363         MovDir[x][y] =
6364           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6365         Moving2Blocked(x, y, &newx, &newy);
6366
6367         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6368           return;
6369
6370         MovDir[x][y] =
6371           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6372         Moving2Blocked(x, y, &newx, &newy);
6373
6374         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6375           return;
6376
6377         MovDir[x][y] = old_move_dir;
6378         return;
6379       }
6380     }
6381     else if (element == EL_SATELLITE)
6382     {
6383       int newx, newy;
6384
6385       MovDelay[x][y] = 1;
6386
6387       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6388       {
6389         boolean first_horiz = RND(2);
6390         int new_move_dir = MovDir[x][y];
6391
6392         MovDir[x][y] =
6393           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6394         Moving2Blocked(x, y, &newx, &newy);
6395
6396         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6397           return;
6398
6399         MovDir[x][y] =
6400           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6401         Moving2Blocked(x, y, &newx, &newy);
6402
6403         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6404           return;
6405
6406         MovDir[x][y] = old_move_dir;
6407         return;
6408       }
6409     }
6410     else if (element == EL_EMC_ANDROID)
6411     {
6412       static int check_pos[16] =
6413       {
6414         -1,             /*  0 => (invalid)          */
6415         7,              /*  1 => MV_LEFT            */
6416         3,              /*  2 => MV_RIGHT           */
6417         -1,             /*  3 => (invalid)          */
6418         1,              /*  4 =>            MV_UP   */
6419         0,              /*  5 => MV_LEFT  | MV_UP   */
6420         2,              /*  6 => MV_RIGHT | MV_UP   */
6421         -1,             /*  7 => (invalid)          */
6422         5,              /*  8 =>            MV_DOWN */
6423         6,              /*  9 => MV_LEFT  | MV_DOWN */
6424         4,              /* 10 => MV_RIGHT | MV_DOWN */
6425         -1,             /* 11 => (invalid)          */
6426         -1,             /* 12 => (invalid)          */
6427         -1,             /* 13 => (invalid)          */
6428         -1,             /* 14 => (invalid)          */
6429         -1,             /* 15 => (invalid)          */
6430       };
6431       static struct
6432       {
6433         int dx, dy;
6434         int dir;
6435       } check_xy[8] =
6436       {
6437         { -1, -1,       MV_LEFT  | MV_UP   },
6438         {  0, -1,                  MV_UP   },
6439         { +1, -1,       MV_RIGHT | MV_UP   },
6440         { +1,  0,       MV_RIGHT           },
6441         { +1, +1,       MV_RIGHT | MV_DOWN },
6442         {  0, +1,                  MV_DOWN },
6443         { -1, +1,       MV_LEFT  | MV_DOWN },
6444         { -1,  0,       MV_LEFT            },
6445       };
6446       int start_pos, check_order;
6447       boolean can_clone = FALSE;
6448       int i;
6449
6450       /* check if there is any free field around current position */
6451       for (i = 0; i < 8; i++)
6452       {
6453         int newx = x + check_xy[i].dx;
6454         int newy = y + check_xy[i].dy;
6455
6456         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6457         {
6458           can_clone = TRUE;
6459
6460           break;
6461         }
6462       }
6463
6464       if (can_clone)            /* randomly find an element to clone */
6465       {
6466         can_clone = FALSE;
6467
6468         start_pos = check_pos[RND(8)];
6469         check_order = (RND(2) ? -1 : +1);
6470
6471         for (i = 0; i < 8; i++)
6472         {
6473           int pos_raw = start_pos + i * check_order;
6474           int pos = (pos_raw + 8) % 8;
6475           int newx = x + check_xy[pos].dx;
6476           int newy = y + check_xy[pos].dy;
6477
6478           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6479           {
6480             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6481             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6482
6483             Store[x][y] = Feld[newx][newy];
6484
6485             can_clone = TRUE;
6486
6487             break;
6488           }
6489         }
6490       }
6491
6492       if (can_clone)            /* randomly find a direction to move */
6493       {
6494         can_clone = FALSE;
6495
6496         start_pos = check_pos[RND(8)];
6497         check_order = (RND(2) ? -1 : +1);
6498
6499         for (i = 0; i < 8; i++)
6500         {
6501           int pos_raw = start_pos + i * check_order;
6502           int pos = (pos_raw + 8) % 8;
6503           int newx = x + check_xy[pos].dx;
6504           int newy = y + check_xy[pos].dy;
6505           int new_move_dir = check_xy[pos].dir;
6506
6507           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6508           {
6509             MovDir[x][y] = new_move_dir;
6510             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6511
6512             can_clone = TRUE;
6513
6514             break;
6515           }
6516         }
6517       }
6518
6519       if (can_clone)            /* cloning and moving successful */
6520         return;
6521
6522       /* cannot clone -- try to move towards player */
6523
6524       start_pos = check_pos[MovDir[x][y] & 0x0f];
6525       check_order = (RND(2) ? -1 : +1);
6526
6527       for (i = 0; i < 3; i++)
6528       {
6529         /* first check start_pos, then previous/next or (next/previous) pos */
6530         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6531         int pos = (pos_raw + 8) % 8;
6532         int newx = x + check_xy[pos].dx;
6533         int newy = y + check_xy[pos].dy;
6534         int new_move_dir = check_xy[pos].dir;
6535
6536         if (IS_PLAYER(newx, newy))
6537           break;
6538
6539         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6540         {
6541           MovDir[x][y] = new_move_dir;
6542           MovDelay[x][y] = level.android_move_time * 8 + 1;
6543
6544           break;
6545         }
6546       }
6547     }
6548   }
6549   else if (move_pattern == MV_TURNING_LEFT ||
6550            move_pattern == MV_TURNING_RIGHT ||
6551            move_pattern == MV_TURNING_LEFT_RIGHT ||
6552            move_pattern == MV_TURNING_RIGHT_LEFT ||
6553            move_pattern == MV_TURNING_RANDOM ||
6554            move_pattern == MV_ALL_DIRECTIONS)
6555   {
6556     boolean can_turn_left =
6557       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6558     boolean can_turn_right =
6559       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6560
6561     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6562       return;
6563
6564     if (move_pattern == MV_TURNING_LEFT)
6565       MovDir[x][y] = left_dir;
6566     else if (move_pattern == MV_TURNING_RIGHT)
6567       MovDir[x][y] = right_dir;
6568     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6569       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6570     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6571       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6572     else if (move_pattern == MV_TURNING_RANDOM)
6573       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6574                       can_turn_right && !can_turn_left ? right_dir :
6575                       RND(2) ? left_dir : right_dir);
6576     else if (can_turn_left && can_turn_right)
6577       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6578     else if (can_turn_left)
6579       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6580     else if (can_turn_right)
6581       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6582     else
6583       MovDir[x][y] = back_dir;
6584
6585     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6586   }
6587   else if (move_pattern == MV_HORIZONTAL ||
6588            move_pattern == MV_VERTICAL)
6589   {
6590     if (move_pattern & old_move_dir)
6591       MovDir[x][y] = back_dir;
6592     else if (move_pattern == MV_HORIZONTAL)
6593       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6594     else if (move_pattern == MV_VERTICAL)
6595       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6596
6597     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6598   }
6599   else if (move_pattern & MV_ANY_DIRECTION)
6600   {
6601     MovDir[x][y] = move_pattern;
6602     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6603   }
6604   else if (move_pattern & MV_WIND_DIRECTION)
6605   {
6606     MovDir[x][y] = game.wind_direction;
6607     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6608   }
6609   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6610   {
6611     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6612       MovDir[x][y] = left_dir;
6613     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6614       MovDir[x][y] = right_dir;
6615
6616     if (MovDir[x][y] != old_move_dir)
6617       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6618   }
6619   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6620   {
6621     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6622       MovDir[x][y] = right_dir;
6623     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6624       MovDir[x][y] = left_dir;
6625
6626     if (MovDir[x][y] != old_move_dir)
6627       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6628   }
6629   else if (move_pattern == MV_TOWARDS_PLAYER ||
6630            move_pattern == MV_AWAY_FROM_PLAYER)
6631   {
6632     int attr_x = -1, attr_y = -1;
6633     int newx, newy;
6634     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6635
6636     if (AllPlayersGone)
6637     {
6638       attr_x = ExitX;
6639       attr_y = ExitY;
6640     }
6641     else
6642     {
6643       int i;
6644
6645       for (i = 0; i < MAX_PLAYERS; i++)
6646       {
6647         struct PlayerInfo *player = &stored_player[i];
6648         int jx = player->jx, jy = player->jy;
6649
6650         if (!player->active)
6651           continue;
6652
6653         if (attr_x == -1 ||
6654             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6655         {
6656           attr_x = jx;
6657           attr_y = jy;
6658         }
6659       }
6660     }
6661
6662     MovDir[x][y] = MV_NONE;
6663     if (attr_x < x)
6664       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6665     else if (attr_x > x)
6666       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6667     if (attr_y < y)
6668       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6669     else if (attr_y > y)
6670       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6671
6672     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6673
6674     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6675     {
6676       boolean first_horiz = RND(2);
6677       int new_move_dir = MovDir[x][y];
6678
6679       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6680       {
6681         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6682         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6683
6684         return;
6685       }
6686
6687       MovDir[x][y] =
6688         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6689       Moving2Blocked(x, y, &newx, &newy);
6690
6691       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6692         return;
6693
6694       MovDir[x][y] =
6695         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6696       Moving2Blocked(x, y, &newx, &newy);
6697
6698       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6699         return;
6700
6701       MovDir[x][y] = old_move_dir;
6702     }
6703   }
6704   else if (move_pattern == MV_WHEN_PUSHED ||
6705            move_pattern == MV_WHEN_DROPPED)
6706   {
6707     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6708       MovDir[x][y] = MV_NONE;
6709
6710     MovDelay[x][y] = 0;
6711   }
6712   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6713   {
6714     static int test_xy[7][2] =
6715     {
6716       { 0, -1 },
6717       { -1, 0 },
6718       { +1, 0 },
6719       { 0, +1 },
6720       { 0, -1 },
6721       { -1, 0 },
6722       { +1, 0 },
6723     };
6724     static int test_dir[7] =
6725     {
6726       MV_UP,
6727       MV_LEFT,
6728       MV_RIGHT,
6729       MV_DOWN,
6730       MV_UP,
6731       MV_LEFT,
6732       MV_RIGHT,
6733     };
6734     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6735     int move_preference = -1000000;     /* start with very low preference */
6736     int new_move_dir = MV_NONE;
6737     int start_test = RND(4);
6738     int i;
6739
6740     for (i = 0; i < NUM_DIRECTIONS; i++)
6741     {
6742       int move_dir = test_dir[start_test + i];
6743       int move_dir_preference;
6744
6745       xx = x + test_xy[start_test + i][0];
6746       yy = y + test_xy[start_test + i][1];
6747
6748       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6749           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6750       {
6751         new_move_dir = move_dir;
6752
6753         break;
6754       }
6755
6756       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6757         continue;
6758
6759       move_dir_preference = -1 * RunnerVisit[xx][yy];
6760       if (hunter_mode && PlayerVisit[xx][yy] > 0)
6761         move_dir_preference = PlayerVisit[xx][yy];
6762
6763       if (move_dir_preference > move_preference)
6764       {
6765         /* prefer field that has not been visited for the longest time */
6766         move_preference = move_dir_preference;
6767         new_move_dir = move_dir;
6768       }
6769       else if (move_dir_preference == move_preference &&
6770                move_dir == old_move_dir)
6771       {
6772         /* prefer last direction when all directions are preferred equally */
6773         move_preference = move_dir_preference;
6774         new_move_dir = move_dir;
6775       }
6776     }
6777
6778     MovDir[x][y] = new_move_dir;
6779     if (old_move_dir != new_move_dir)
6780       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6781   }
6782 }
6783
6784 static void TurnRound(int x, int y)
6785 {
6786   int direction = MovDir[x][y];
6787
6788   TurnRoundExt(x, y);
6789
6790   GfxDir[x][y] = MovDir[x][y];
6791
6792   if (direction != MovDir[x][y])
6793     GfxFrame[x][y] = 0;
6794
6795   if (MovDelay[x][y])
6796     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6797
6798   ResetGfxFrame(x, y, FALSE);
6799 }
6800
6801 static boolean JustBeingPushed(int x, int y)
6802 {
6803   int i;
6804
6805   for (i = 0; i < MAX_PLAYERS; i++)
6806   {
6807     struct PlayerInfo *player = &stored_player[i];
6808
6809     if (player->active && player->is_pushing && player->MovPos)
6810     {
6811       int next_jx = player->jx + (player->jx - player->last_jx);
6812       int next_jy = player->jy + (player->jy - player->last_jy);
6813
6814       if (x == next_jx && y == next_jy)
6815         return TRUE;
6816     }
6817   }
6818
6819   return FALSE;
6820 }
6821
6822 void StartMoving(int x, int y)
6823 {
6824   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
6825   int element = Feld[x][y];
6826
6827   if (Stop[x][y])
6828     return;
6829
6830   if (MovDelay[x][y] == 0)
6831     GfxAction[x][y] = ACTION_DEFAULT;
6832
6833   if (CAN_FALL(element) && y < lev_fieldy - 1)
6834   {
6835     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
6836         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6837       if (JustBeingPushed(x, y))
6838         return;
6839
6840     if (element == EL_QUICKSAND_FULL)
6841     {
6842       if (IS_FREE(x, y + 1))
6843       {
6844         InitMovingField(x, y, MV_DOWN);
6845         started_moving = TRUE;
6846
6847         Feld[x][y] = EL_QUICKSAND_EMPTYING;
6848 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6849         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6850           Store[x][y] = EL_ROCK;
6851 #else
6852         Store[x][y] = EL_ROCK;
6853 #endif
6854
6855         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6856       }
6857       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6858       {
6859         if (!MovDelay[x][y])
6860           MovDelay[x][y] = TILEY + 1;
6861
6862         if (MovDelay[x][y])
6863         {
6864           MovDelay[x][y]--;
6865           if (MovDelay[x][y])
6866             return;
6867         }
6868
6869         Feld[x][y] = EL_QUICKSAND_EMPTY;
6870         Feld[x][y + 1] = EL_QUICKSAND_FULL;
6871         Store[x][y + 1] = Store[x][y];
6872         Store[x][y] = 0;
6873
6874         PlayLevelSoundAction(x, y, ACTION_FILLING);
6875       }
6876     }
6877     else if (element == EL_QUICKSAND_FAST_FULL)
6878     {
6879       if (IS_FREE(x, y + 1))
6880       {
6881         InitMovingField(x, y, MV_DOWN);
6882         started_moving = TRUE;
6883
6884         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
6885 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6886         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6887           Store[x][y] = EL_ROCK;
6888 #else
6889         Store[x][y] = EL_ROCK;
6890 #endif
6891
6892         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6893       }
6894       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6895       {
6896         if (!MovDelay[x][y])
6897           MovDelay[x][y] = TILEY + 1;
6898
6899         if (MovDelay[x][y])
6900         {
6901           MovDelay[x][y]--;
6902           if (MovDelay[x][y])
6903             return;
6904         }
6905
6906         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
6907         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
6908         Store[x][y + 1] = Store[x][y];
6909         Store[x][y] = 0;
6910
6911         PlayLevelSoundAction(x, y, ACTION_FILLING);
6912       }
6913     }
6914     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6915              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6916     {
6917       InitMovingField(x, y, MV_DOWN);
6918       started_moving = TRUE;
6919
6920       Feld[x][y] = EL_QUICKSAND_FILLING;
6921       Store[x][y] = element;
6922
6923       PlayLevelSoundAction(x, y, ACTION_FILLING);
6924     }
6925     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6926              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6927     {
6928       InitMovingField(x, y, MV_DOWN);
6929       started_moving = TRUE;
6930
6931       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
6932       Store[x][y] = element;
6933
6934       PlayLevelSoundAction(x, y, ACTION_FILLING);
6935     }
6936     else if (element == EL_MAGIC_WALL_FULL)
6937     {
6938       if (IS_FREE(x, y + 1))
6939       {
6940         InitMovingField(x, y, MV_DOWN);
6941         started_moving = TRUE;
6942
6943         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6944         Store[x][y] = EL_CHANGED(Store[x][y]);
6945       }
6946       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6947       {
6948         if (!MovDelay[x][y])
6949           MovDelay[x][y] = TILEY/4 + 1;
6950
6951         if (MovDelay[x][y])
6952         {
6953           MovDelay[x][y]--;
6954           if (MovDelay[x][y])
6955             return;
6956         }
6957
6958         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6959         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6960         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6961         Store[x][y] = 0;
6962       }
6963     }
6964     else if (element == EL_BD_MAGIC_WALL_FULL)
6965     {
6966       if (IS_FREE(x, y + 1))
6967       {
6968         InitMovingField(x, y, MV_DOWN);
6969         started_moving = TRUE;
6970
6971         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6972         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6973       }
6974       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6975       {
6976         if (!MovDelay[x][y])
6977           MovDelay[x][y] = TILEY/4 + 1;
6978
6979         if (MovDelay[x][y])
6980         {
6981           MovDelay[x][y]--;
6982           if (MovDelay[x][y])
6983             return;
6984         }
6985
6986         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6987         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6988         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6989         Store[x][y] = 0;
6990       }
6991     }
6992     else if (element == EL_DC_MAGIC_WALL_FULL)
6993     {
6994       if (IS_FREE(x, y + 1))
6995       {
6996         InitMovingField(x, y, MV_DOWN);
6997         started_moving = TRUE;
6998
6999         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7000         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7001       }
7002       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7003       {
7004         if (!MovDelay[x][y])
7005           MovDelay[x][y] = TILEY/4 + 1;
7006
7007         if (MovDelay[x][y])
7008         {
7009           MovDelay[x][y]--;
7010           if (MovDelay[x][y])
7011             return;
7012         }
7013
7014         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7015         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7016         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7017         Store[x][y] = 0;
7018       }
7019     }
7020     else if ((CAN_PASS_MAGIC_WALL(element) &&
7021               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7022                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7023              (CAN_PASS_DC_MAGIC_WALL(element) &&
7024               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7025
7026     {
7027       InitMovingField(x, y, MV_DOWN);
7028       started_moving = TRUE;
7029
7030       Feld[x][y] =
7031         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7032          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7033          EL_DC_MAGIC_WALL_FILLING);
7034       Store[x][y] = element;
7035     }
7036     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7037     {
7038       SplashAcid(x, y + 1);
7039
7040       InitMovingField(x, y, MV_DOWN);
7041       started_moving = TRUE;
7042
7043       Store[x][y] = EL_ACID;
7044     }
7045     else if (
7046 #if USE_FIX_IMPACT_COLLISION
7047              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7048               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7049 #else
7050              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7051               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7052 #endif
7053              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7054               CAN_FALL(element) && WasJustFalling[x][y] &&
7055               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7056
7057              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7058               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7059               (Feld[x][y + 1] == EL_BLOCKED)))
7060     {
7061       /* this is needed for a special case not covered by calling "Impact()"
7062          from "ContinueMoving()": if an element moves to a tile directly below
7063          another element which was just falling on that tile (which was empty
7064          in the previous frame), the falling element above would just stop
7065          instead of smashing the element below (in previous version, the above
7066          element was just checked for "moving" instead of "falling", resulting
7067          in incorrect smashes caused by horizontal movement of the above
7068          element; also, the case of the player being the element to smash was
7069          simply not covered here... :-/ ) */
7070
7071       CheckCollision[x][y] = 0;
7072       CheckImpact[x][y] = 0;
7073
7074       Impact(x, y);
7075     }
7076     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7077     {
7078       if (MovDir[x][y] == MV_NONE)
7079       {
7080         InitMovingField(x, y, MV_DOWN);
7081         started_moving = TRUE;
7082       }
7083     }
7084     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7085     {
7086       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7087         MovDir[x][y] = MV_DOWN;
7088
7089       InitMovingField(x, y, MV_DOWN);
7090       started_moving = TRUE;
7091     }
7092     else if (element == EL_AMOEBA_DROP)
7093     {
7094       Feld[x][y] = EL_AMOEBA_GROWING;
7095       Store[x][y] = EL_AMOEBA_WET;
7096     }
7097     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7098               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7099              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7100              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7101     {
7102       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7103                                 (IS_FREE(x - 1, y + 1) ||
7104                                  Feld[x - 1][y + 1] == EL_ACID));
7105       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7106                                 (IS_FREE(x + 1, y + 1) ||
7107                                  Feld[x + 1][y + 1] == EL_ACID));
7108       boolean can_fall_any  = (can_fall_left || can_fall_right);
7109       boolean can_fall_both = (can_fall_left && can_fall_right);
7110       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7111
7112 #if USE_NEW_ALL_SLIPPERY
7113       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7114       {
7115         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7116           can_fall_right = FALSE;
7117         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7118           can_fall_left = FALSE;
7119         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7120           can_fall_right = FALSE;
7121         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7122           can_fall_left = FALSE;
7123
7124         can_fall_any  = (can_fall_left || can_fall_right);
7125         can_fall_both = FALSE;
7126       }
7127 #else
7128       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7129       {
7130         if (slippery_type == SLIPPERY_ONLY_LEFT)
7131           can_fall_right = FALSE;
7132         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7133           can_fall_left = FALSE;
7134         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7135           can_fall_right = FALSE;
7136         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7137           can_fall_left = FALSE;
7138
7139         can_fall_any  = (can_fall_left || can_fall_right);
7140         can_fall_both = (can_fall_left && can_fall_right);
7141       }
7142 #endif
7143
7144 #if USE_NEW_ALL_SLIPPERY
7145 #else
7146 #if USE_NEW_SP_SLIPPERY
7147       /* !!! better use the same properties as for custom elements here !!! */
7148       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7149                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7150       {
7151         can_fall_right = FALSE;         /* slip down on left side */
7152         can_fall_both = FALSE;
7153       }
7154 #endif
7155 #endif
7156
7157 #if USE_NEW_ALL_SLIPPERY
7158       if (can_fall_both)
7159       {
7160         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7161           can_fall_right = FALSE;       /* slip down on left side */
7162         else
7163           can_fall_left = !(can_fall_right = RND(2));
7164
7165         can_fall_both = FALSE;
7166       }
7167 #else
7168       if (can_fall_both)
7169       {
7170         if (game.emulation == EMU_BOULDERDASH ||
7171             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7172           can_fall_right = FALSE;       /* slip down on left side */
7173         else
7174           can_fall_left = !(can_fall_right = RND(2));
7175
7176         can_fall_both = FALSE;
7177       }
7178 #endif
7179
7180       if (can_fall_any)
7181       {
7182         /* if not determined otherwise, prefer left side for slipping down */
7183         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7184         started_moving = TRUE;
7185       }
7186     }
7187 #if 0
7188     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7189 #else
7190     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7191 #endif
7192     {
7193       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7194       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7195       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7196       int belt_dir = game.belt_dir[belt_nr];
7197
7198       if ((belt_dir == MV_LEFT  && left_is_free) ||
7199           (belt_dir == MV_RIGHT && right_is_free))
7200       {
7201         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7202
7203         InitMovingField(x, y, belt_dir);
7204         started_moving = TRUE;
7205
7206         Pushed[x][y] = TRUE;
7207         Pushed[nextx][y] = TRUE;
7208
7209         GfxAction[x][y] = ACTION_DEFAULT;
7210       }
7211       else
7212       {
7213         MovDir[x][y] = 0;       /* if element was moving, stop it */
7214       }
7215     }
7216   }
7217
7218   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7219 #if 0
7220   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7221 #else
7222   if (CAN_MOVE(element) && !started_moving)
7223 #endif
7224   {
7225     int move_pattern = element_info[element].move_pattern;
7226     int newx, newy;
7227
7228 #if 0
7229 #if DEBUG
7230     if (MovDir[x][y] == MV_NONE)
7231     {
7232       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7233              x, y, element, element_info[element].token_name);
7234       printf("StartMoving(): This should never happen!\n");
7235     }
7236 #endif
7237 #endif
7238
7239     Moving2Blocked(x, y, &newx, &newy);
7240
7241     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7242       return;
7243
7244     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7245         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7246     {
7247       WasJustMoving[x][y] = 0;
7248       CheckCollision[x][y] = 0;
7249
7250       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7251
7252       if (Feld[x][y] != element)        /* element has changed */
7253         return;
7254     }
7255
7256     if (!MovDelay[x][y])        /* start new movement phase */
7257     {
7258       /* all objects that can change their move direction after each step
7259          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7260
7261       if (element != EL_YAMYAM &&
7262           element != EL_DARK_YAMYAM &&
7263           element != EL_PACMAN &&
7264           !(move_pattern & MV_ANY_DIRECTION) &&
7265           move_pattern != MV_TURNING_LEFT &&
7266           move_pattern != MV_TURNING_RIGHT &&
7267           move_pattern != MV_TURNING_LEFT_RIGHT &&
7268           move_pattern != MV_TURNING_RIGHT_LEFT &&
7269           move_pattern != MV_TURNING_RANDOM)
7270       {
7271         TurnRound(x, y);
7272
7273         if (MovDelay[x][y] && (element == EL_BUG ||
7274                                element == EL_SPACESHIP ||
7275                                element == EL_SP_SNIKSNAK ||
7276                                element == EL_SP_ELECTRON ||
7277                                element == EL_MOLE))
7278           DrawLevelField(x, y);
7279       }
7280     }
7281
7282     if (MovDelay[x][y])         /* wait some time before next movement */
7283     {
7284       MovDelay[x][y]--;
7285
7286       if (element == EL_ROBOT ||
7287           element == EL_YAMYAM ||
7288           element == EL_DARK_YAMYAM)
7289       {
7290         DrawLevelElementAnimationIfNeeded(x, y, element);
7291         PlayLevelSoundAction(x, y, ACTION_WAITING);
7292       }
7293       else if (element == EL_SP_ELECTRON)
7294         DrawLevelElementAnimationIfNeeded(x, y, element);
7295       else if (element == EL_DRAGON)
7296       {
7297         int i;
7298         int dir = MovDir[x][y];
7299         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7300         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7301         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7302                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7303                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7304                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7305         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7306
7307         GfxAction[x][y] = ACTION_ATTACKING;
7308
7309         if (IS_PLAYER(x, y))
7310           DrawPlayerField(x, y);
7311         else
7312           DrawLevelField(x, y);
7313
7314         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7315
7316         for (i = 1; i <= 3; i++)
7317         {
7318           int xx = x + i * dx;
7319           int yy = y + i * dy;
7320           int sx = SCREENX(xx);
7321           int sy = SCREENY(yy);
7322           int flame_graphic = graphic + (i - 1);
7323
7324           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7325             break;
7326
7327           if (MovDelay[x][y])
7328           {
7329             int flamed = MovingOrBlocked2Element(xx, yy);
7330
7331             /* !!! */
7332 #if 0
7333             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7334               Bang(xx, yy);
7335             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
7336               RemoveMovingField(xx, yy);
7337             else
7338               RemoveField(xx, yy);
7339 #else
7340             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7341               Bang(xx, yy);
7342             else
7343               RemoveMovingField(xx, yy);
7344 #endif
7345
7346             ChangeDelay[xx][yy] = 0;
7347
7348             Feld[xx][yy] = EL_FLAMES;
7349
7350             if (IN_SCR_FIELD(sx, sy))
7351             {
7352               DrawLevelFieldCrumbledSand(xx, yy);
7353               DrawGraphic(sx, sy, flame_graphic, frame);
7354             }
7355           }
7356           else
7357           {
7358             if (Feld[xx][yy] == EL_FLAMES)
7359               Feld[xx][yy] = EL_EMPTY;
7360             DrawLevelField(xx, yy);
7361           }
7362         }
7363       }
7364
7365       if (MovDelay[x][y])       /* element still has to wait some time */
7366       {
7367         PlayLevelSoundAction(x, y, ACTION_WAITING);
7368
7369         return;
7370       }
7371     }
7372
7373     /* now make next step */
7374
7375     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7376
7377     if (DONT_COLLIDE_WITH(element) &&
7378         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7379         !PLAYER_ENEMY_PROTECTED(newx, newy))
7380     {
7381       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7382
7383       return;
7384     }
7385
7386     else if (CAN_MOVE_INTO_ACID(element) &&
7387              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7388              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7389              (MovDir[x][y] == MV_DOWN ||
7390               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7391     {
7392       SplashAcid(newx, newy);
7393       Store[x][y] = EL_ACID;
7394     }
7395     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7396     {
7397       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7398           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7399           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7400           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7401       {
7402         RemoveField(x, y);
7403         DrawLevelField(x, y);
7404
7405         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7406         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7407           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7408
7409         local_player->friends_still_needed--;
7410         if (!local_player->friends_still_needed &&
7411             !local_player->GameOver && AllPlayersGone)
7412           PlayerWins(local_player);
7413
7414         return;
7415       }
7416       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7417       {
7418         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7419           DrawLevelField(newx, newy);
7420         else
7421           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7422       }
7423       else if (!IS_FREE(newx, newy))
7424       {
7425         GfxAction[x][y] = ACTION_WAITING;
7426
7427         if (IS_PLAYER(x, y))
7428           DrawPlayerField(x, y);
7429         else
7430           DrawLevelField(x, y);
7431
7432         return;
7433       }
7434     }
7435     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7436     {
7437       if (IS_FOOD_PIG(Feld[newx][newy]))
7438       {
7439         if (IS_MOVING(newx, newy))
7440           RemoveMovingField(newx, newy);
7441         else
7442         {
7443           Feld[newx][newy] = EL_EMPTY;
7444           DrawLevelField(newx, newy);
7445         }
7446
7447         PlayLevelSound(x, y, SND_PIG_DIGGING);
7448       }
7449       else if (!IS_FREE(newx, newy))
7450       {
7451         if (IS_PLAYER(x, y))
7452           DrawPlayerField(x, y);
7453         else
7454           DrawLevelField(x, y);
7455
7456         return;
7457       }
7458     }
7459     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7460     {
7461       if (Store[x][y] != EL_EMPTY)
7462       {
7463         boolean can_clone = FALSE;
7464         int xx, yy;
7465
7466         /* check if element to clone is still there */
7467         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7468         {
7469           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7470           {
7471             can_clone = TRUE;
7472
7473             break;
7474           }
7475         }
7476
7477         /* cannot clone or target field not free anymore -- do not clone */
7478         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7479           Store[x][y] = EL_EMPTY;
7480       }
7481
7482       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7483       {
7484         if (IS_MV_DIAGONAL(MovDir[x][y]))
7485         {
7486           int diagonal_move_dir = MovDir[x][y];
7487           int stored = Store[x][y];
7488           int change_delay = 8;
7489           int graphic;
7490
7491           /* android is moving diagonally */
7492
7493           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7494
7495           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7496           GfxElement[x][y] = EL_EMC_ANDROID;
7497           GfxAction[x][y] = ACTION_SHRINKING;
7498           GfxDir[x][y] = diagonal_move_dir;
7499           ChangeDelay[x][y] = change_delay;
7500
7501           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7502                                    GfxDir[x][y]);
7503
7504           DrawLevelGraphicAnimation(x, y, graphic);
7505           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7506
7507           if (Feld[newx][newy] == EL_ACID)
7508           {
7509             SplashAcid(newx, newy);
7510
7511             return;
7512           }
7513
7514           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7515
7516           Store[newx][newy] = EL_EMC_ANDROID;
7517           GfxElement[newx][newy] = EL_EMC_ANDROID;
7518           GfxAction[newx][newy] = ACTION_GROWING;
7519           GfxDir[newx][newy] = diagonal_move_dir;
7520           ChangeDelay[newx][newy] = change_delay;
7521
7522           graphic = el_act_dir2img(GfxElement[newx][newy],
7523                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7524
7525           DrawLevelGraphicAnimation(newx, newy, graphic);
7526           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7527
7528           return;
7529         }
7530         else
7531         {
7532           Feld[newx][newy] = EL_EMPTY;
7533           DrawLevelField(newx, newy);
7534
7535           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7536         }
7537       }
7538       else if (!IS_FREE(newx, newy))
7539       {
7540 #if 0
7541         if (IS_PLAYER(x, y))
7542           DrawPlayerField(x, y);
7543         else
7544           DrawLevelField(x, y);
7545 #endif
7546
7547         return;
7548       }
7549     }
7550     else if (IS_CUSTOM_ELEMENT(element) &&
7551              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7552     {
7553       int new_element = Feld[newx][newy];
7554
7555       if (!IS_FREE(newx, newy))
7556       {
7557         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7558                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7559                       ACTION_BREAKING);
7560
7561         /* no element can dig solid indestructible elements */
7562         if (IS_INDESTRUCTIBLE(new_element) &&
7563             !IS_DIGGABLE(new_element) &&
7564             !IS_COLLECTIBLE(new_element))
7565           return;
7566
7567         if (AmoebaNr[newx][newy] &&
7568             (new_element == EL_AMOEBA_FULL ||
7569              new_element == EL_BD_AMOEBA ||
7570              new_element == EL_AMOEBA_GROWING))
7571         {
7572           AmoebaCnt[AmoebaNr[newx][newy]]--;
7573           AmoebaCnt2[AmoebaNr[newx][newy]]--;
7574         }
7575
7576         if (IS_MOVING(newx, newy))
7577           RemoveMovingField(newx, newy);
7578         else
7579         {
7580           RemoveField(newx, newy);
7581           DrawLevelField(newx, newy);
7582         }
7583
7584         /* if digged element was about to explode, prevent the explosion */
7585         ExplodeField[newx][newy] = EX_TYPE_NONE;
7586
7587         PlayLevelSoundAction(x, y, action);
7588       }
7589
7590       Store[newx][newy] = EL_EMPTY;
7591 #if 1
7592       /* this makes it possible to leave the removed element again */
7593       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7594         Store[newx][newy] = new_element;
7595 #else
7596       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7597       {
7598         int move_leave_element = element_info[element].move_leave_element;
7599
7600         /* this makes it possible to leave the removed element again */
7601         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7602                              new_element : move_leave_element);
7603       }
7604 #endif
7605
7606       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7607       {
7608         RunnerVisit[x][y] = FrameCounter;
7609         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7610       }
7611     }
7612     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7613     {
7614       if (!IS_FREE(newx, newy))
7615       {
7616         if (IS_PLAYER(x, y))
7617           DrawPlayerField(x, y);
7618         else
7619           DrawLevelField(x, y);
7620
7621         return;
7622       }
7623       else
7624       {
7625         boolean wanna_flame = !RND(10);
7626         int dx = newx - x, dy = newy - y;
7627         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7628         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7629         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7630                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7631         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7632                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7633
7634         if ((wanna_flame ||
7635              IS_CLASSIC_ENEMY(element1) ||
7636              IS_CLASSIC_ENEMY(element2)) &&
7637             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7638             element1 != EL_FLAMES && element2 != EL_FLAMES)
7639         {
7640           ResetGfxAnimation(x, y);
7641           GfxAction[x][y] = ACTION_ATTACKING;
7642
7643           if (IS_PLAYER(x, y))
7644             DrawPlayerField(x, y);
7645           else
7646             DrawLevelField(x, y);
7647
7648           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7649
7650           MovDelay[x][y] = 50;
7651
7652           /* !!! */
7653 #if 0
7654           RemoveField(newx, newy);
7655 #endif
7656           Feld[newx][newy] = EL_FLAMES;
7657           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7658           {
7659 #if 0
7660             RemoveField(newx1, newy1);
7661 #endif
7662             Feld[newx1][newy1] = EL_FLAMES;
7663           }
7664           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7665           {
7666 #if 0
7667             RemoveField(newx2, newy2);
7668 #endif
7669             Feld[newx2][newy2] = EL_FLAMES;
7670           }
7671
7672           return;
7673         }
7674       }
7675     }
7676     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7677              Feld[newx][newy] == EL_DIAMOND)
7678     {
7679       if (IS_MOVING(newx, newy))
7680         RemoveMovingField(newx, newy);
7681       else
7682       {
7683         Feld[newx][newy] = EL_EMPTY;
7684         DrawLevelField(newx, newy);
7685       }
7686
7687       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7688     }
7689     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7690              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7691     {
7692       if (AmoebaNr[newx][newy])
7693       {
7694         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7695         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7696             Feld[newx][newy] == EL_BD_AMOEBA)
7697           AmoebaCnt[AmoebaNr[newx][newy]]--;
7698       }
7699
7700 #if 0
7701       /* !!! test !!! */
7702       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7703       {
7704         RemoveMovingField(newx, newy);
7705       }
7706 #else
7707       if (IS_MOVING(newx, newy))
7708       {
7709         RemoveMovingField(newx, newy);
7710       }
7711 #endif
7712       else
7713       {
7714         Feld[newx][newy] = EL_EMPTY;
7715         DrawLevelField(newx, newy);
7716       }
7717
7718       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7719     }
7720     else if ((element == EL_PACMAN || element == EL_MOLE)
7721              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7722     {
7723       if (AmoebaNr[newx][newy])
7724       {
7725         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7726         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7727             Feld[newx][newy] == EL_BD_AMOEBA)
7728           AmoebaCnt[AmoebaNr[newx][newy]]--;
7729       }
7730
7731       if (element == EL_MOLE)
7732       {
7733         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7734         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7735
7736         ResetGfxAnimation(x, y);
7737         GfxAction[x][y] = ACTION_DIGGING;
7738         DrawLevelField(x, y);
7739
7740         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7741
7742         return;                         /* wait for shrinking amoeba */
7743       }
7744       else      /* element == EL_PACMAN */
7745       {
7746         Feld[newx][newy] = EL_EMPTY;
7747         DrawLevelField(newx, newy);
7748         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7749       }
7750     }
7751     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7752              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7753               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7754     {
7755       /* wait for shrinking amoeba to completely disappear */
7756       return;
7757     }
7758     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7759     {
7760       /* object was running against a wall */
7761
7762       TurnRound(x, y);
7763
7764 #if 0
7765       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7766       if (move_pattern & MV_ANY_DIRECTION &&
7767           move_pattern == MovDir[x][y])
7768       {
7769         int blocking_element =
7770           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7771
7772         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7773                                  MovDir[x][y]);
7774
7775         element = Feld[x][y];   /* element might have changed */
7776       }
7777 #endif
7778
7779       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7780         DrawLevelElementAnimation(x, y, element);
7781
7782       if (DONT_TOUCH(element))
7783         TestIfBadThingTouchesPlayer(x, y);
7784
7785       return;
7786     }
7787
7788     InitMovingField(x, y, MovDir[x][y]);
7789
7790     PlayLevelSoundAction(x, y, ACTION_MOVING);
7791   }
7792
7793   if (MovDir[x][y])
7794     ContinueMoving(x, y);
7795 }
7796
7797 void ContinueMoving(int x, int y)
7798 {
7799   int element = Feld[x][y];
7800   struct ElementInfo *ei = &element_info[element];
7801   int direction = MovDir[x][y];
7802   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7803   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7804   int newx = x + dx, newy = y + dy;
7805   int stored = Store[x][y];
7806   int stored_new = Store[newx][newy];
7807   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7808   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7809   boolean last_line = (newy == lev_fieldy - 1);
7810
7811   MovPos[x][y] += getElementMoveStepsize(x, y);
7812
7813   if (pushed_by_player) /* special case: moving object pushed by player */
7814     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7815
7816   if (ABS(MovPos[x][y]) < TILEX)
7817   {
7818 #if 0
7819     int ee = Feld[x][y];
7820     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7821     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7822
7823     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7824            x, y, ABS(MovPos[x][y]),
7825            ee, gg, ff,
7826            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
7827 #endif
7828
7829     DrawLevelField(x, y);
7830
7831     return;     /* element is still moving */
7832   }
7833
7834   /* element reached destination field */
7835
7836   Feld[x][y] = EL_EMPTY;
7837   Feld[newx][newy] = element;
7838   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
7839
7840   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
7841   {
7842     element = Feld[newx][newy] = EL_ACID;
7843   }
7844   else if (element == EL_MOLE)
7845   {
7846     Feld[x][y] = EL_SAND;
7847
7848     DrawLevelFieldCrumbledSandNeighbours(x, y);
7849   }
7850   else if (element == EL_QUICKSAND_FILLING)
7851   {
7852     element = Feld[newx][newy] = get_next_element(element);
7853     Store[newx][newy] = Store[x][y];
7854   }
7855   else if (element == EL_QUICKSAND_EMPTYING)
7856   {
7857     Feld[x][y] = get_next_element(element);
7858     element = Feld[newx][newy] = Store[x][y];
7859   }
7860   else if (element == EL_QUICKSAND_FAST_FILLING)
7861   {
7862     element = Feld[newx][newy] = get_next_element(element);
7863     Store[newx][newy] = Store[x][y];
7864   }
7865   else if (element == EL_QUICKSAND_FAST_EMPTYING)
7866   {
7867     Feld[x][y] = get_next_element(element);
7868     element = Feld[newx][newy] = Store[x][y];
7869   }
7870   else if (element == EL_MAGIC_WALL_FILLING)
7871   {
7872     element = Feld[newx][newy] = get_next_element(element);
7873     if (!game.magic_wall_active)
7874       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7875     Store[newx][newy] = Store[x][y];
7876   }
7877   else if (element == EL_MAGIC_WALL_EMPTYING)
7878   {
7879     Feld[x][y] = get_next_element(element);
7880     if (!game.magic_wall_active)
7881       Feld[x][y] = EL_MAGIC_WALL_DEAD;
7882     element = Feld[newx][newy] = Store[x][y];
7883
7884 #if USE_NEW_CUSTOM_VALUE
7885     InitField(newx, newy, FALSE);
7886 #endif
7887   }
7888   else if (element == EL_BD_MAGIC_WALL_FILLING)
7889   {
7890     element = Feld[newx][newy] = get_next_element(element);
7891     if (!game.magic_wall_active)
7892       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
7893     Store[newx][newy] = Store[x][y];
7894   }
7895   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
7896   {
7897     Feld[x][y] = get_next_element(element);
7898     if (!game.magic_wall_active)
7899       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7900     element = Feld[newx][newy] = Store[x][y];
7901
7902 #if USE_NEW_CUSTOM_VALUE
7903     InitField(newx, newy, FALSE);
7904 #endif
7905   }
7906   else if (element == EL_DC_MAGIC_WALL_FILLING)
7907   {
7908     element = Feld[newx][newy] = get_next_element(element);
7909     if (!game.magic_wall_active)
7910       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
7911     Store[newx][newy] = Store[x][y];
7912   }
7913   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
7914   {
7915     Feld[x][y] = get_next_element(element);
7916     if (!game.magic_wall_active)
7917       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
7918     element = Feld[newx][newy] = Store[x][y];
7919
7920 #if USE_NEW_CUSTOM_VALUE
7921     InitField(newx, newy, FALSE);
7922 #endif
7923   }
7924   else if (element == EL_AMOEBA_DROPPING)
7925   {
7926     Feld[x][y] = get_next_element(element);
7927     element = Feld[newx][newy] = Store[x][y];
7928   }
7929   else if (element == EL_SOKOBAN_OBJECT)
7930   {
7931     if (Back[x][y])
7932       Feld[x][y] = Back[x][y];
7933
7934     if (Back[newx][newy])
7935       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
7936
7937     Back[x][y] = Back[newx][newy] = 0;
7938   }
7939
7940   Store[x][y] = EL_EMPTY;
7941   MovPos[x][y] = 0;
7942   MovDir[x][y] = 0;
7943   MovDelay[x][y] = 0;
7944
7945   MovDelay[newx][newy] = 0;
7946
7947   if (CAN_CHANGE_OR_HAS_ACTION(element))
7948   {
7949     /* copy element change control values to new field */
7950     ChangeDelay[newx][newy] = ChangeDelay[x][y];
7951     ChangePage[newx][newy]  = ChangePage[x][y];
7952     ChangeCount[newx][newy] = ChangeCount[x][y];
7953     ChangeEvent[newx][newy] = ChangeEvent[x][y];
7954   }
7955
7956 #if USE_NEW_CUSTOM_VALUE
7957     CustomValue[newx][newy] = CustomValue[x][y];
7958 #endif
7959
7960   ChangeDelay[x][y] = 0;
7961   ChangePage[x][y] = -1;
7962   ChangeCount[x][y] = 0;
7963   ChangeEvent[x][y] = -1;
7964
7965 #if USE_NEW_CUSTOM_VALUE
7966   CustomValue[x][y] = 0;
7967 #endif
7968
7969   /* copy animation control values to new field */
7970   GfxFrame[newx][newy]  = GfxFrame[x][y];
7971   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
7972   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
7973   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
7974
7975   Pushed[x][y] = Pushed[newx][newy] = FALSE;
7976
7977   /* some elements can leave other elements behind after moving */
7978 #if 1
7979   if (ei->move_leave_element != EL_EMPTY &&
7980       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7981       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7982 #else
7983   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7984       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7985       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7986 #endif
7987   {
7988     int move_leave_element = ei->move_leave_element;
7989
7990 #if 1
7991 #if 1
7992     /* this makes it possible to leave the removed element again */
7993     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7994       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7995 #else
7996     /* this makes it possible to leave the removed element again */
7997     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7998       move_leave_element = stored;
7999 #endif
8000 #else
8001     /* this makes it possible to leave the removed element again */
8002     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8003         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8004       move_leave_element = stored;
8005 #endif
8006
8007     Feld[x][y] = move_leave_element;
8008
8009     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8010       MovDir[x][y] = direction;
8011
8012     InitField(x, y, FALSE);
8013
8014     if (GFX_CRUMBLED(Feld[x][y]))
8015       DrawLevelFieldCrumbledSandNeighbours(x, y);
8016
8017     if (ELEM_IS_PLAYER(move_leave_element))
8018       RelocatePlayer(x, y, move_leave_element);
8019   }
8020
8021   /* do this after checking for left-behind element */
8022   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8023
8024   if (!CAN_MOVE(element) ||
8025       (CAN_FALL(element) && direction == MV_DOWN &&
8026        (element == EL_SPRING ||
8027         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8028         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8029     GfxDir[x][y] = MovDir[newx][newy] = 0;
8030
8031   DrawLevelField(x, y);
8032   DrawLevelField(newx, newy);
8033
8034   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8035
8036   /* prevent pushed element from moving on in pushed direction */
8037   if (pushed_by_player && CAN_MOVE(element) &&
8038       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8039       !(element_info[element].move_pattern & direction))
8040     TurnRound(newx, newy);
8041
8042   /* prevent elements on conveyor belt from moving on in last direction */
8043   if (pushed_by_conveyor && CAN_FALL(element) &&
8044       direction & MV_HORIZONTAL)
8045     MovDir[newx][newy] = 0;
8046
8047   if (!pushed_by_player)
8048   {
8049     int nextx = newx + dx, nexty = newy + dy;
8050     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8051
8052     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8053
8054     if (CAN_FALL(element) && direction == MV_DOWN)
8055       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8056
8057     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8058       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8059
8060 #if USE_FIX_IMPACT_COLLISION
8061     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8062       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8063 #endif
8064   }
8065
8066   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8067   {
8068     TestIfBadThingTouchesPlayer(newx, newy);
8069     TestIfBadThingTouchesFriend(newx, newy);
8070
8071     if (!IS_CUSTOM_ELEMENT(element))
8072       TestIfBadThingTouchesOtherBadThing(newx, newy);
8073   }
8074   else if (element == EL_PENGUIN)
8075     TestIfFriendTouchesBadThing(newx, newy);
8076
8077   /* give the player one last chance (one more frame) to move away */
8078   if (CAN_FALL(element) && direction == MV_DOWN &&
8079       (last_line || (!IS_FREE(x, newy + 1) &&
8080                      (!IS_PLAYER(x, newy + 1) ||
8081                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8082     Impact(x, newy);
8083
8084   if (pushed_by_player && !game.use_change_when_pushing_bug)
8085   {
8086     int push_side = MV_DIR_OPPOSITE(direction);
8087     struct PlayerInfo *player = PLAYERINFO(x, y);
8088
8089     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8090                                player->index_bit, push_side);
8091     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8092                                         player->index_bit, push_side);
8093   }
8094
8095   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8096     MovDelay[newx][newy] = 1;
8097
8098   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8099
8100   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8101
8102 #if 0
8103   if (ChangePage[newx][newy] != -1)             /* delayed change */
8104   {
8105     int page = ChangePage[newx][newy];
8106     struct ElementChangeInfo *change = &ei->change_page[page];
8107
8108     ChangePage[newx][newy] = -1;
8109
8110     if (change->can_change)
8111     {
8112       if (ChangeElement(newx, newy, element, page))
8113       {
8114         if (change->post_change_function)
8115           change->post_change_function(newx, newy);
8116       }
8117     }
8118
8119     if (change->has_action)
8120       ExecuteCustomElementAction(newx, newy, element, page);
8121   }
8122 #endif
8123
8124   TestIfElementHitsCustomElement(newx, newy, direction);
8125   TestIfPlayerTouchesCustomElement(newx, newy);
8126   TestIfElementTouchesCustomElement(newx, newy);
8127
8128   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8129       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8130     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8131                              MV_DIR_OPPOSITE(direction));
8132 }
8133
8134 int AmoebeNachbarNr(int ax, int ay)
8135 {
8136   int i;
8137   int element = Feld[ax][ay];
8138   int group_nr = 0;
8139   static int xy[4][2] =
8140   {
8141     { 0, -1 },
8142     { -1, 0 },
8143     { +1, 0 },
8144     { 0, +1 }
8145   };
8146
8147   for (i = 0; i < NUM_DIRECTIONS; i++)
8148   {
8149     int x = ax + xy[i][0];
8150     int y = ay + xy[i][1];
8151
8152     if (!IN_LEV_FIELD(x, y))
8153       continue;
8154
8155     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8156       group_nr = AmoebaNr[x][y];
8157   }
8158
8159   return group_nr;
8160 }
8161
8162 void AmoebenVereinigen(int ax, int ay)
8163 {
8164   int i, x, y, xx, yy;
8165   int new_group_nr = AmoebaNr[ax][ay];
8166   static int xy[4][2] =
8167   {
8168     { 0, -1 },
8169     { -1, 0 },
8170     { +1, 0 },
8171     { 0, +1 }
8172   };
8173
8174   if (new_group_nr == 0)
8175     return;
8176
8177   for (i = 0; i < NUM_DIRECTIONS; i++)
8178   {
8179     x = ax + xy[i][0];
8180     y = ay + xy[i][1];
8181
8182     if (!IN_LEV_FIELD(x, y))
8183       continue;
8184
8185     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8186          Feld[x][y] == EL_BD_AMOEBA ||
8187          Feld[x][y] == EL_AMOEBA_DEAD) &&
8188         AmoebaNr[x][y] != new_group_nr)
8189     {
8190       int old_group_nr = AmoebaNr[x][y];
8191
8192       if (old_group_nr == 0)
8193         return;
8194
8195       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8196       AmoebaCnt[old_group_nr] = 0;
8197       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8198       AmoebaCnt2[old_group_nr] = 0;
8199
8200       SCAN_PLAYFIELD(xx, yy)
8201       {
8202         if (AmoebaNr[xx][yy] == old_group_nr)
8203           AmoebaNr[xx][yy] = new_group_nr;
8204       }
8205     }
8206   }
8207 }
8208
8209 void AmoebeUmwandeln(int ax, int ay)
8210 {
8211   int i, x, y;
8212
8213   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8214   {
8215     int group_nr = AmoebaNr[ax][ay];
8216
8217 #ifdef DEBUG
8218     if (group_nr == 0)
8219     {
8220       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8221       printf("AmoebeUmwandeln(): This should never happen!\n");
8222       return;
8223     }
8224 #endif
8225
8226     SCAN_PLAYFIELD(x, y)
8227     {
8228       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8229       {
8230         AmoebaNr[x][y] = 0;
8231         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8232       }
8233     }
8234
8235     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8236                             SND_AMOEBA_TURNING_TO_GEM :
8237                             SND_AMOEBA_TURNING_TO_ROCK));
8238     Bang(ax, ay);
8239   }
8240   else
8241   {
8242     static int xy[4][2] =
8243     {
8244       { 0, -1 },
8245       { -1, 0 },
8246       { +1, 0 },
8247       { 0, +1 }
8248     };
8249
8250     for (i = 0; i < NUM_DIRECTIONS; i++)
8251     {
8252       x = ax + xy[i][0];
8253       y = ay + xy[i][1];
8254
8255       if (!IN_LEV_FIELD(x, y))
8256         continue;
8257
8258       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8259       {
8260         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8261                               SND_AMOEBA_TURNING_TO_GEM :
8262                               SND_AMOEBA_TURNING_TO_ROCK));
8263         Bang(x, y);
8264       }
8265     }
8266   }
8267 }
8268
8269 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8270 {
8271   int x, y;
8272   int group_nr = AmoebaNr[ax][ay];
8273   boolean done = FALSE;
8274
8275 #ifdef DEBUG
8276   if (group_nr == 0)
8277   {
8278     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8279     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8280     return;
8281   }
8282 #endif
8283
8284   SCAN_PLAYFIELD(x, y)
8285   {
8286     if (AmoebaNr[x][y] == group_nr &&
8287         (Feld[x][y] == EL_AMOEBA_DEAD ||
8288          Feld[x][y] == EL_BD_AMOEBA ||
8289          Feld[x][y] == EL_AMOEBA_GROWING))
8290     {
8291       AmoebaNr[x][y] = 0;
8292       Feld[x][y] = new_element;
8293       InitField(x, y, FALSE);
8294       DrawLevelField(x, y);
8295       done = TRUE;
8296     }
8297   }
8298
8299   if (done)
8300     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8301                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8302                             SND_BD_AMOEBA_TURNING_TO_GEM));
8303 }
8304
8305 void AmoebeWaechst(int x, int y)
8306 {
8307   static unsigned long sound_delay = 0;
8308   static unsigned long sound_delay_value = 0;
8309
8310   if (!MovDelay[x][y])          /* start new growing cycle */
8311   {
8312     MovDelay[x][y] = 7;
8313
8314     if (DelayReached(&sound_delay, sound_delay_value))
8315     {
8316       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8317       sound_delay_value = 30;
8318     }
8319   }
8320
8321   if (MovDelay[x][y])           /* wait some time before growing bigger */
8322   {
8323     MovDelay[x][y]--;
8324     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8325     {
8326       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8327                                            6 - MovDelay[x][y]);
8328
8329       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8330     }
8331
8332     if (!MovDelay[x][y])
8333     {
8334       Feld[x][y] = Store[x][y];
8335       Store[x][y] = 0;
8336       DrawLevelField(x, y);
8337     }
8338   }
8339 }
8340
8341 void AmoebaDisappearing(int x, int y)
8342 {
8343   static unsigned long sound_delay = 0;
8344   static unsigned long sound_delay_value = 0;
8345
8346   if (!MovDelay[x][y])          /* start new shrinking cycle */
8347   {
8348     MovDelay[x][y] = 7;
8349
8350     if (DelayReached(&sound_delay, sound_delay_value))
8351       sound_delay_value = 30;
8352   }
8353
8354   if (MovDelay[x][y])           /* wait some time before shrinking */
8355   {
8356     MovDelay[x][y]--;
8357     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8358     {
8359       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8360                                            6 - MovDelay[x][y]);
8361
8362       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8363     }
8364
8365     if (!MovDelay[x][y])
8366     {
8367       Feld[x][y] = EL_EMPTY;
8368       DrawLevelField(x, y);
8369
8370       /* don't let mole enter this field in this cycle;
8371          (give priority to objects falling to this field from above) */
8372       Stop[x][y] = TRUE;
8373     }
8374   }
8375 }
8376
8377 void AmoebeAbleger(int ax, int ay)
8378 {
8379   int i;
8380   int element = Feld[ax][ay];
8381   int graphic = el2img(element);
8382   int newax = ax, neway = ay;
8383   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8384   static int xy[4][2] =
8385   {
8386     { 0, -1 },
8387     { -1, 0 },
8388     { +1, 0 },
8389     { 0, +1 }
8390   };
8391
8392   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8393   {
8394     Feld[ax][ay] = EL_AMOEBA_DEAD;
8395     DrawLevelField(ax, ay);
8396     return;
8397   }
8398
8399   if (IS_ANIMATED(graphic))
8400     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8401
8402   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8403     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8404
8405   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8406   {
8407     MovDelay[ax][ay]--;
8408     if (MovDelay[ax][ay])
8409       return;
8410   }
8411
8412   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8413   {
8414     int start = RND(4);
8415     int x = ax + xy[start][0];
8416     int y = ay + xy[start][1];
8417
8418     if (!IN_LEV_FIELD(x, y))
8419       return;
8420
8421     if (IS_FREE(x, y) ||
8422         CAN_GROW_INTO(Feld[x][y]) ||
8423         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8424         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8425     {
8426       newax = x;
8427       neway = y;
8428     }
8429
8430     if (newax == ax && neway == ay)
8431       return;
8432   }
8433   else                          /* normal or "filled" (BD style) amoeba */
8434   {
8435     int start = RND(4);
8436     boolean waiting_for_player = FALSE;
8437
8438     for (i = 0; i < NUM_DIRECTIONS; i++)
8439     {
8440       int j = (start + i) % 4;
8441       int x = ax + xy[j][0];
8442       int y = ay + xy[j][1];
8443
8444       if (!IN_LEV_FIELD(x, y))
8445         continue;
8446
8447       if (IS_FREE(x, y) ||
8448           CAN_GROW_INTO(Feld[x][y]) ||
8449           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8450           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8451       {
8452         newax = x;
8453         neway = y;
8454         break;
8455       }
8456       else if (IS_PLAYER(x, y))
8457         waiting_for_player = TRUE;
8458     }
8459
8460     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8461     {
8462       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8463       {
8464         Feld[ax][ay] = EL_AMOEBA_DEAD;
8465         DrawLevelField(ax, ay);
8466         AmoebaCnt[AmoebaNr[ax][ay]]--;
8467
8468         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8469         {
8470           if (element == EL_AMOEBA_FULL)
8471             AmoebeUmwandeln(ax, ay);
8472           else if (element == EL_BD_AMOEBA)
8473             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8474         }
8475       }
8476       return;
8477     }
8478     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8479     {
8480       /* amoeba gets larger by growing in some direction */
8481
8482       int new_group_nr = AmoebaNr[ax][ay];
8483
8484 #ifdef DEBUG
8485   if (new_group_nr == 0)
8486   {
8487     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8488     printf("AmoebeAbleger(): This should never happen!\n");
8489     return;
8490   }
8491 #endif
8492
8493       AmoebaNr[newax][neway] = new_group_nr;
8494       AmoebaCnt[new_group_nr]++;
8495       AmoebaCnt2[new_group_nr]++;
8496
8497       /* if amoeba touches other amoeba(s) after growing, unify them */
8498       AmoebenVereinigen(newax, neway);
8499
8500       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8501       {
8502         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8503         return;
8504       }
8505     }
8506   }
8507
8508   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8509       (neway == lev_fieldy - 1 && newax != ax))
8510   {
8511     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8512     Store[newax][neway] = element;
8513   }
8514   else if (neway == ay || element == EL_EMC_DRIPPER)
8515   {
8516     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8517
8518     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8519   }
8520   else
8521   {
8522     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8523     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8524     Store[ax][ay] = EL_AMOEBA_DROP;
8525     ContinueMoving(ax, ay);
8526     return;
8527   }
8528
8529   DrawLevelField(newax, neway);
8530 }
8531
8532 void Life(int ax, int ay)
8533 {
8534   int x1, y1, x2, y2;
8535   int life_time = 40;
8536   int element = Feld[ax][ay];
8537   int graphic = el2img(element);
8538   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8539                          level.biomaze);
8540   boolean changed = FALSE;
8541
8542   if (IS_ANIMATED(graphic))
8543     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8544
8545   if (Stop[ax][ay])
8546     return;
8547
8548   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8549     MovDelay[ax][ay] = life_time;
8550
8551   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8552   {
8553     MovDelay[ax][ay]--;
8554     if (MovDelay[ax][ay])
8555       return;
8556   }
8557
8558   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8559   {
8560     int xx = ax+x1, yy = ay+y1;
8561     int nachbarn = 0;
8562
8563     if (!IN_LEV_FIELD(xx, yy))
8564       continue;
8565
8566     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8567     {
8568       int x = xx+x2, y = yy+y2;
8569
8570       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8571         continue;
8572
8573       if (((Feld[x][y] == element ||
8574             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8575            !Stop[x][y]) ||
8576           (IS_FREE(x, y) && Stop[x][y]))
8577         nachbarn++;
8578     }
8579
8580     if (xx == ax && yy == ay)           /* field in the middle */
8581     {
8582       if (nachbarn < life_parameter[0] ||
8583           nachbarn > life_parameter[1])
8584       {
8585         Feld[xx][yy] = EL_EMPTY;
8586         if (!Stop[xx][yy])
8587           DrawLevelField(xx, yy);
8588         Stop[xx][yy] = TRUE;
8589         changed = TRUE;
8590       }
8591     }
8592     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8593     {                                   /* free border field */
8594       if (nachbarn >= life_parameter[2] &&
8595           nachbarn <= life_parameter[3])
8596       {
8597         Feld[xx][yy] = element;
8598         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8599         if (!Stop[xx][yy])
8600           DrawLevelField(xx, yy);
8601         Stop[xx][yy] = TRUE;
8602         changed = TRUE;
8603       }
8604     }
8605   }
8606
8607   if (changed)
8608     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8609                    SND_GAME_OF_LIFE_GROWING);
8610 }
8611
8612 static void InitRobotWheel(int x, int y)
8613 {
8614   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8615 }
8616
8617 static void RunRobotWheel(int x, int y)
8618 {
8619   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8620 }
8621
8622 static void StopRobotWheel(int x, int y)
8623 {
8624   if (ZX == x && ZY == y)
8625     ZX = ZY = -1;
8626 }
8627
8628 static void InitTimegateWheel(int x, int y)
8629 {
8630   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8631 }
8632
8633 static void RunTimegateWheel(int x, int y)
8634 {
8635   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8636 }
8637
8638 static void InitMagicBallDelay(int x, int y)
8639 {
8640 #if 1
8641   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8642 #else
8643   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8644 #endif
8645 }
8646
8647 static void ActivateMagicBall(int bx, int by)
8648 {
8649   int x, y;
8650
8651   if (level.ball_random)
8652   {
8653     int pos_border = RND(8);    /* select one of the eight border elements */
8654     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8655     int xx = pos_content % 3;
8656     int yy = pos_content / 3;
8657
8658     x = bx - 1 + xx;
8659     y = by - 1 + yy;
8660
8661     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8662       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8663   }
8664   else
8665   {
8666     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8667     {
8668       int xx = x - bx + 1;
8669       int yy = y - by + 1;
8670
8671       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8672         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8673     }
8674   }
8675
8676   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8677 }
8678
8679 void CheckExit(int x, int y)
8680 {
8681   if (local_player->gems_still_needed > 0 ||
8682       local_player->sokobanfields_still_needed > 0 ||
8683       local_player->lights_still_needed > 0)
8684   {
8685     int element = Feld[x][y];
8686     int graphic = el2img(element);
8687
8688     if (IS_ANIMATED(graphic))
8689       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8690
8691     return;
8692   }
8693
8694   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8695     return;
8696
8697   Feld[x][y] = EL_EXIT_OPENING;
8698
8699   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8700 }
8701
8702 void CheckExitEM(int x, int y)
8703 {
8704   if (local_player->gems_still_needed > 0 ||
8705       local_player->sokobanfields_still_needed > 0 ||
8706       local_player->lights_still_needed > 0)
8707   {
8708     int element = Feld[x][y];
8709     int graphic = el2img(element);
8710
8711     if (IS_ANIMATED(graphic))
8712       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8713
8714     return;
8715   }
8716
8717   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8718     return;
8719
8720   Feld[x][y] = EL_EM_EXIT_OPENING;
8721
8722   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8723 }
8724
8725 void CheckExitSteel(int x, int y)
8726 {
8727   if (local_player->gems_still_needed > 0 ||
8728       local_player->sokobanfields_still_needed > 0 ||
8729       local_player->lights_still_needed > 0)
8730   {
8731     int element = Feld[x][y];
8732     int graphic = el2img(element);
8733
8734     if (IS_ANIMATED(graphic))
8735       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8736
8737     return;
8738   }
8739
8740   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8741     return;
8742
8743   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8744
8745   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8746 }
8747
8748 void CheckExitSteelEM(int x, int y)
8749 {
8750   if (local_player->gems_still_needed > 0 ||
8751       local_player->sokobanfields_still_needed > 0 ||
8752       local_player->lights_still_needed > 0)
8753   {
8754     int element = Feld[x][y];
8755     int graphic = el2img(element);
8756
8757     if (IS_ANIMATED(graphic))
8758       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8759
8760     return;
8761   }
8762
8763   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8764     return;
8765
8766   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8767
8768   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8769 }
8770
8771 void CheckExitSP(int x, int y)
8772 {
8773   if (local_player->gems_still_needed > 0)
8774   {
8775     int element = Feld[x][y];
8776     int graphic = el2img(element);
8777
8778     if (IS_ANIMATED(graphic))
8779       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8780
8781     return;
8782   }
8783
8784   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8785     return;
8786
8787   Feld[x][y] = EL_SP_EXIT_OPENING;
8788
8789   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8790 }
8791
8792 static void CloseAllOpenTimegates()
8793 {
8794   int x, y;
8795
8796   SCAN_PLAYFIELD(x, y)
8797   {
8798     int element = Feld[x][y];
8799
8800     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8801     {
8802       Feld[x][y] = EL_TIMEGATE_CLOSING;
8803
8804       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8805     }
8806   }
8807 }
8808
8809 void DrawTwinkleOnField(int x, int y)
8810 {
8811   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8812     return;
8813
8814   if (Feld[x][y] == EL_BD_DIAMOND)
8815     return;
8816
8817   if (MovDelay[x][y] == 0)      /* next animation frame */
8818     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8819
8820   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8821   {
8822     MovDelay[x][y]--;
8823
8824     if (setup.direct_draw && MovDelay[x][y])
8825       SetDrawtoField(DRAW_BUFFERED);
8826
8827     DrawLevelElementAnimation(x, y, Feld[x][y]);
8828
8829     if (MovDelay[x][y] != 0)
8830     {
8831       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8832                                            10 - MovDelay[x][y]);
8833
8834       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8835
8836       if (setup.direct_draw)
8837       {
8838         int dest_x, dest_y;
8839
8840         dest_x = FX + SCREENX(x) * TILEX;
8841         dest_y = FY + SCREENY(y) * TILEY;
8842
8843         BlitBitmap(drawto_field, window,
8844                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8845         SetDrawtoField(DRAW_DIRECT);
8846       }
8847     }
8848   }
8849 }
8850
8851 void MauerWaechst(int x, int y)
8852 {
8853   int delay = 6;
8854
8855   if (!MovDelay[x][y])          /* next animation frame */
8856     MovDelay[x][y] = 3 * delay;
8857
8858   if (MovDelay[x][y])           /* wait some time before next frame */
8859   {
8860     MovDelay[x][y]--;
8861
8862     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8863     {
8864       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8865       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8866
8867       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8868     }
8869
8870     if (!MovDelay[x][y])
8871     {
8872       if (MovDir[x][y] == MV_LEFT)
8873       {
8874         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8875           DrawLevelField(x - 1, y);
8876       }
8877       else if (MovDir[x][y] == MV_RIGHT)
8878       {
8879         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8880           DrawLevelField(x + 1, y);
8881       }
8882       else if (MovDir[x][y] == MV_UP)
8883       {
8884         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8885           DrawLevelField(x, y - 1);
8886       }
8887       else
8888       {
8889         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8890           DrawLevelField(x, y + 1);
8891       }
8892
8893       Feld[x][y] = Store[x][y];
8894       Store[x][y] = 0;
8895       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8896       DrawLevelField(x, y);
8897     }
8898   }
8899 }
8900
8901 void MauerAbleger(int ax, int ay)
8902 {
8903   int element = Feld[ax][ay];
8904   int graphic = el2img(element);
8905   boolean oben_frei = FALSE, unten_frei = FALSE;
8906   boolean links_frei = FALSE, rechts_frei = FALSE;
8907   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8908   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8909   boolean new_wall = FALSE;
8910
8911   if (IS_ANIMATED(graphic))
8912     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8913
8914   if (!MovDelay[ax][ay])        /* start building new wall */
8915     MovDelay[ax][ay] = 6;
8916
8917   if (MovDelay[ax][ay])         /* wait some time before building new wall */
8918   {
8919     MovDelay[ax][ay]--;
8920     if (MovDelay[ax][ay])
8921       return;
8922   }
8923
8924   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8925     oben_frei = TRUE;
8926   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8927     unten_frei = TRUE;
8928   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8929     links_frei = TRUE;
8930   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8931     rechts_frei = TRUE;
8932
8933   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
8934       element == EL_EXPANDABLE_WALL_ANY)
8935   {
8936     if (oben_frei)
8937     {
8938       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
8939       Store[ax][ay-1] = element;
8940       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8941       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8942         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8943                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
8944       new_wall = TRUE;
8945     }
8946     if (unten_frei)
8947     {
8948       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
8949       Store[ax][ay+1] = element;
8950       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8951       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8952         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8953                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8954       new_wall = TRUE;
8955     }
8956   }
8957
8958   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8959       element == EL_EXPANDABLE_WALL_ANY ||
8960       element == EL_EXPANDABLE_WALL ||
8961       element == EL_BD_EXPANDABLE_WALL)
8962   {
8963     if (links_frei)
8964     {
8965       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8966       Store[ax-1][ay] = element;
8967       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8968       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8969         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8970                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8971       new_wall = TRUE;
8972     }
8973
8974     if (rechts_frei)
8975     {
8976       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8977       Store[ax+1][ay] = element;
8978       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8979       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8980         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8981                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8982       new_wall = TRUE;
8983     }
8984   }
8985
8986   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8987     DrawLevelField(ax, ay);
8988
8989   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8990     oben_massiv = TRUE;
8991   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8992     unten_massiv = TRUE;
8993   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8994     links_massiv = TRUE;
8995   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8996     rechts_massiv = TRUE;
8997
8998   if (((oben_massiv && unten_massiv) ||
8999        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9000        element == EL_EXPANDABLE_WALL) &&
9001       ((links_massiv && rechts_massiv) ||
9002        element == EL_EXPANDABLE_WALL_VERTICAL))
9003     Feld[ax][ay] = EL_WALL;
9004
9005   if (new_wall)
9006     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9007 }
9008
9009 void MauerAblegerStahl(int ax, int ay)
9010 {
9011   int element = Feld[ax][ay];
9012   int graphic = el2img(element);
9013   boolean oben_frei = FALSE, unten_frei = FALSE;
9014   boolean links_frei = FALSE, rechts_frei = FALSE;
9015   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9016   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9017   boolean new_wall = FALSE;
9018
9019   if (IS_ANIMATED(graphic))
9020     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9021
9022   if (!MovDelay[ax][ay])        /* start building new wall */
9023     MovDelay[ax][ay] = 6;
9024
9025   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9026   {
9027     MovDelay[ax][ay]--;
9028     if (MovDelay[ax][ay])
9029       return;
9030   }
9031
9032   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9033     oben_frei = TRUE;
9034   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9035     unten_frei = TRUE;
9036   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9037     links_frei = TRUE;
9038   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9039     rechts_frei = TRUE;
9040
9041   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9042       element == EL_EXPANDABLE_STEELWALL_ANY)
9043   {
9044     if (oben_frei)
9045     {
9046       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9047       Store[ax][ay-1] = element;
9048       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9049       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9050         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9051                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9052       new_wall = TRUE;
9053     }
9054     if (unten_frei)
9055     {
9056       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9057       Store[ax][ay+1] = element;
9058       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9059       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9060         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9061                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9062       new_wall = TRUE;
9063     }
9064   }
9065
9066   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9067       element == EL_EXPANDABLE_STEELWALL_ANY)
9068   {
9069     if (links_frei)
9070     {
9071       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9072       Store[ax-1][ay] = element;
9073       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9074       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9075         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9076                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9077       new_wall = TRUE;
9078     }
9079
9080     if (rechts_frei)
9081     {
9082       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9083       Store[ax+1][ay] = element;
9084       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9085       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9086         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9087                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9088       new_wall = TRUE;
9089     }
9090   }
9091
9092   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9093     oben_massiv = TRUE;
9094   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9095     unten_massiv = TRUE;
9096   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9097     links_massiv = TRUE;
9098   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9099     rechts_massiv = TRUE;
9100
9101   if (((oben_massiv && unten_massiv) ||
9102        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9103       ((links_massiv && rechts_massiv) ||
9104        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9105     Feld[ax][ay] = EL_WALL;
9106
9107   if (new_wall)
9108     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9109 }
9110
9111 void CheckForDragon(int x, int y)
9112 {
9113   int i, j;
9114   boolean dragon_found = FALSE;
9115   static int xy[4][2] =
9116   {
9117     { 0, -1 },
9118     { -1, 0 },
9119     { +1, 0 },
9120     { 0, +1 }
9121   };
9122
9123   for (i = 0; i < NUM_DIRECTIONS; i++)
9124   {
9125     for (j = 0; j < 4; j++)
9126     {
9127       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9128
9129       if (IN_LEV_FIELD(xx, yy) &&
9130           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9131       {
9132         if (Feld[xx][yy] == EL_DRAGON)
9133           dragon_found = TRUE;
9134       }
9135       else
9136         break;
9137     }
9138   }
9139
9140   if (!dragon_found)
9141   {
9142     for (i = 0; i < NUM_DIRECTIONS; i++)
9143     {
9144       for (j = 0; j < 3; j++)
9145       {
9146         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9147   
9148         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9149         {
9150           Feld[xx][yy] = EL_EMPTY;
9151           DrawLevelField(xx, yy);
9152         }
9153         else
9154           break;
9155       }
9156     }
9157   }
9158 }
9159
9160 static void InitBuggyBase(int x, int y)
9161 {
9162   int element = Feld[x][y];
9163   int activating_delay = FRAMES_PER_SECOND / 4;
9164
9165   ChangeDelay[x][y] =
9166     (element == EL_SP_BUGGY_BASE ?
9167      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9168      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9169      activating_delay :
9170      element == EL_SP_BUGGY_BASE_ACTIVE ?
9171      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9172 }
9173
9174 static void WarnBuggyBase(int x, int y)
9175 {
9176   int i;
9177   static int xy[4][2] =
9178   {
9179     { 0, -1 },
9180     { -1, 0 },
9181     { +1, 0 },
9182     { 0, +1 }
9183   };
9184
9185   for (i = 0; i < NUM_DIRECTIONS; i++)
9186   {
9187     int xx = x + xy[i][0];
9188     int yy = y + xy[i][1];
9189
9190     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9191     {
9192       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9193
9194       break;
9195     }
9196   }
9197 }
9198
9199 static void InitTrap(int x, int y)
9200 {
9201   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9202 }
9203
9204 static void ActivateTrap(int x, int y)
9205 {
9206   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9207 }
9208
9209 static void ChangeActiveTrap(int x, int y)
9210 {
9211   int graphic = IMG_TRAP_ACTIVE;
9212
9213   /* if new animation frame was drawn, correct crumbled sand border */
9214   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9215     DrawLevelFieldCrumbledSand(x, y);
9216 }
9217
9218 static int getSpecialActionElement(int element, int number, int base_element)
9219 {
9220   return (element != EL_EMPTY ? element :
9221           number != -1 ? base_element + number - 1 :
9222           EL_EMPTY);
9223 }
9224
9225 static int getModifiedActionNumber(int value_old, int operator, int operand,
9226                                    int value_min, int value_max)
9227 {
9228   int value_new = (operator == CA_MODE_SET      ? operand :
9229                    operator == CA_MODE_ADD      ? value_old + operand :
9230                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9231                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9232                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9233                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9234                    value_old);
9235
9236   return (value_new < value_min ? value_min :
9237           value_new > value_max ? value_max :
9238           value_new);
9239 }
9240
9241 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9242 {
9243   struct ElementInfo *ei = &element_info[element];
9244   struct ElementChangeInfo *change = &ei->change_page[page];
9245   int target_element = change->target_element;
9246   int action_type = change->action_type;
9247   int action_mode = change->action_mode;
9248   int action_arg = change->action_arg;
9249   int i;
9250
9251   if (!change->has_action)
9252     return;
9253
9254   /* ---------- determine action paramater values -------------------------- */
9255
9256   int level_time_value =
9257     (level.time > 0 ? TimeLeft :
9258      TimePlayed);
9259
9260   int action_arg_element =
9261     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9262      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9263      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9264      EL_EMPTY);
9265
9266   int action_arg_direction =
9267     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9268      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9269      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9270      change->actual_trigger_side :
9271      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9272      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9273      MV_NONE);
9274
9275   int action_arg_number_min =
9276     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9277      CA_ARG_MIN);
9278
9279   int action_arg_number_max =
9280     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9281      action_type == CA_SET_LEVEL_GEMS ? 999 :
9282      action_type == CA_SET_LEVEL_TIME ? 9999 :
9283      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9284      action_type == CA_SET_CE_VALUE ? 9999 :
9285      action_type == CA_SET_CE_SCORE ? 9999 :
9286      CA_ARG_MAX);
9287
9288   int action_arg_number_reset =
9289     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9290      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9291      action_type == CA_SET_LEVEL_TIME ? level.time :
9292      action_type == CA_SET_LEVEL_SCORE ? 0 :
9293 #if USE_NEW_CUSTOM_VALUE
9294      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9295 #else
9296      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9297 #endif
9298      action_type == CA_SET_CE_SCORE ? 0 :
9299      0);
9300
9301   int action_arg_number =
9302     (action_arg <= CA_ARG_MAX ? action_arg :
9303      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9304      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9305      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9306      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9307      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9308      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9309 #if USE_NEW_CUSTOM_VALUE
9310      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9311 #else
9312      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9313 #endif
9314      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9315      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9316      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9317      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9318      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9319      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9320      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9321      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9322      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9323      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9324      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9325      -1);
9326
9327   int action_arg_number_old =
9328     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9329      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9330      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9331      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9332      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9333      0);
9334
9335   int action_arg_number_new =
9336     getModifiedActionNumber(action_arg_number_old,
9337                             action_mode, action_arg_number,
9338                             action_arg_number_min, action_arg_number_max);
9339
9340   int trigger_player_bits =
9341     (change->actual_trigger_player >= EL_PLAYER_1 &&
9342      change->actual_trigger_player <= EL_PLAYER_4 ?
9343      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9344      PLAYER_BITS_ANY);
9345
9346   int action_arg_player_bits =
9347     (action_arg >= CA_ARG_PLAYER_1 &&
9348      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9349      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9350      PLAYER_BITS_ANY);
9351
9352   /* ---------- execute action  -------------------------------------------- */
9353
9354   switch (action_type)
9355   {
9356     case CA_NO_ACTION:
9357     {
9358       return;
9359     }
9360
9361     /* ---------- level actions  ------------------------------------------- */
9362
9363     case CA_RESTART_LEVEL:
9364     {
9365       game.restart_level = TRUE;
9366
9367       break;
9368     }
9369
9370     case CA_SHOW_ENVELOPE:
9371     {
9372       int element = getSpecialActionElement(action_arg_element,
9373                                             action_arg_number, EL_ENVELOPE_1);
9374
9375       if (IS_ENVELOPE(element))
9376         local_player->show_envelope = element;
9377
9378       break;
9379     }
9380
9381     case CA_SET_LEVEL_TIME:
9382     {
9383       if (level.time > 0)       /* only modify limited time value */
9384       {
9385         TimeLeft = action_arg_number_new;
9386
9387 #if 1
9388         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
9389
9390         DisplayGameControlValues();
9391 #else
9392         DrawGameValue_Time(TimeLeft);
9393 #endif
9394
9395         if (!TimeLeft && setup.time_limit)
9396           for (i = 0; i < MAX_PLAYERS; i++)
9397             KillPlayer(&stored_player[i]);
9398       }
9399
9400       break;
9401     }
9402
9403     case CA_SET_LEVEL_SCORE:
9404     {
9405       local_player->score = action_arg_number_new;
9406
9407 #if 1
9408       game_control_value[GAME_CONTROL_SCORE] = local_player->score;
9409
9410       DisplayGameControlValues();
9411 #else
9412       DrawGameValue_Score(local_player->score);
9413 #endif
9414
9415       break;
9416     }
9417
9418     case CA_SET_LEVEL_GEMS:
9419     {
9420       local_player->gems_still_needed = action_arg_number_new;
9421
9422 #if 1
9423       game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
9424
9425       DisplayGameControlValues();
9426 #else
9427       DrawGameValue_Emeralds(local_player->gems_still_needed);
9428 #endif
9429
9430       break;
9431     }
9432
9433 #if !USE_PLAYER_GRAVITY
9434     case CA_SET_LEVEL_GRAVITY:
9435     {
9436       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
9437                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
9438                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9439                       game.gravity);
9440       break;
9441     }
9442 #endif
9443
9444     case CA_SET_LEVEL_WIND:
9445     {
9446       game.wind_direction = action_arg_direction;
9447
9448       break;
9449     }
9450
9451     /* ---------- player actions  ------------------------------------------ */
9452
9453     case CA_MOVE_PLAYER:
9454     {
9455       /* automatically move to the next field in specified direction */
9456       for (i = 0; i < MAX_PLAYERS; i++)
9457         if (trigger_player_bits & (1 << i))
9458           stored_player[i].programmed_action = action_arg_direction;
9459
9460       break;
9461     }
9462
9463     case CA_EXIT_PLAYER:
9464     {
9465       for (i = 0; i < MAX_PLAYERS; i++)
9466         if (action_arg_player_bits & (1 << i))
9467           PlayerWins(&stored_player[i]);
9468
9469       break;
9470     }
9471
9472     case CA_KILL_PLAYER:
9473     {
9474       for (i = 0; i < MAX_PLAYERS; i++)
9475         if (action_arg_player_bits & (1 << i))
9476           KillPlayer(&stored_player[i]);
9477
9478       break;
9479     }
9480
9481     case CA_SET_PLAYER_KEYS:
9482     {
9483       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9484       int element = getSpecialActionElement(action_arg_element,
9485                                             action_arg_number, EL_KEY_1);
9486
9487       if (IS_KEY(element))
9488       {
9489         for (i = 0; i < MAX_PLAYERS; i++)
9490         {
9491           if (trigger_player_bits & (1 << i))
9492           {
9493             stored_player[i].key[KEY_NR(element)] = key_state;
9494
9495             DrawGameDoorValues();
9496           }
9497         }
9498       }
9499
9500       break;
9501     }
9502
9503     case CA_SET_PLAYER_SPEED:
9504     {
9505       for (i = 0; i < MAX_PLAYERS; i++)
9506       {
9507         if (trigger_player_bits & (1 << i))
9508         {
9509           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9510
9511           if (action_arg == CA_ARG_SPEED_FASTER &&
9512               stored_player[i].cannot_move)
9513           {
9514             action_arg_number = STEPSIZE_VERY_SLOW;
9515           }
9516           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9517                    action_arg == CA_ARG_SPEED_FASTER)
9518           {
9519             action_arg_number = 2;
9520             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9521                            CA_MODE_MULTIPLY);
9522           }
9523           else if (action_arg == CA_ARG_NUMBER_RESET)
9524           {
9525             action_arg_number = level.initial_player_stepsize[i];
9526           }
9527
9528           move_stepsize =
9529             getModifiedActionNumber(move_stepsize,
9530                                     action_mode,
9531                                     action_arg_number,
9532                                     action_arg_number_min,
9533                                     action_arg_number_max);
9534
9535           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9536         }
9537       }
9538
9539       break;
9540     }
9541
9542     case CA_SET_PLAYER_SHIELD:
9543     {
9544       for (i = 0; i < MAX_PLAYERS; i++)
9545       {
9546         if (trigger_player_bits & (1 << i))
9547         {
9548           if (action_arg == CA_ARG_SHIELD_OFF)
9549           {
9550             stored_player[i].shield_normal_time_left = 0;
9551             stored_player[i].shield_deadly_time_left = 0;
9552           }
9553           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9554           {
9555             stored_player[i].shield_normal_time_left = 999999;
9556           }
9557           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9558           {
9559             stored_player[i].shield_normal_time_left = 999999;
9560             stored_player[i].shield_deadly_time_left = 999999;
9561           }
9562         }
9563       }
9564
9565       break;
9566     }
9567
9568 #if USE_PLAYER_GRAVITY
9569     case CA_SET_PLAYER_GRAVITY:
9570     {
9571       for (i = 0; i < MAX_PLAYERS; i++)
9572       {
9573         if (trigger_player_bits & (1 << i))
9574         {
9575           stored_player[i].gravity =
9576             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9577              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9578              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9579              stored_player[i].gravity);
9580         }
9581       }
9582
9583       break;
9584     }
9585 #endif
9586
9587     case CA_SET_PLAYER_ARTWORK:
9588     {
9589       for (i = 0; i < MAX_PLAYERS; i++)
9590       {
9591         if (trigger_player_bits & (1 << i))
9592         {
9593           int artwork_element = action_arg_element;
9594
9595           if (action_arg == CA_ARG_ELEMENT_RESET)
9596             artwork_element =
9597               (level.use_artwork_element[i] ? level.artwork_element[i] :
9598                stored_player[i].element_nr);
9599
9600 #if USE_GFX_RESET_PLAYER_ARTWORK
9601           if (stored_player[i].artwork_element != artwork_element)
9602             stored_player[i].Frame = 0;
9603 #endif
9604
9605           stored_player[i].artwork_element = artwork_element;
9606
9607           SetPlayerWaiting(&stored_player[i], FALSE);
9608
9609           /* set number of special actions for bored and sleeping animation */
9610           stored_player[i].num_special_action_bored =
9611             get_num_special_action(artwork_element,
9612                                    ACTION_BORING_1, ACTION_BORING_LAST);
9613           stored_player[i].num_special_action_sleeping =
9614             get_num_special_action(artwork_element,
9615                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9616         }
9617       }
9618
9619       break;
9620     }
9621
9622     /* ---------- CE actions  ---------------------------------------------- */
9623
9624     case CA_SET_CE_VALUE:
9625     {
9626 #if USE_NEW_CUSTOM_VALUE
9627       int last_ce_value = CustomValue[x][y];
9628
9629       CustomValue[x][y] = action_arg_number_new;
9630
9631       if (CustomValue[x][y] != last_ce_value)
9632       {
9633         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9634         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9635
9636         if (CustomValue[x][y] == 0)
9637         {
9638           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9639           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9640         }
9641       }
9642 #endif
9643
9644       break;
9645     }
9646
9647     case CA_SET_CE_SCORE:
9648     {
9649 #if USE_NEW_CUSTOM_VALUE
9650       int last_ce_score = ei->collect_score;
9651
9652       ei->collect_score = action_arg_number_new;
9653
9654       if (ei->collect_score != last_ce_score)
9655       {
9656         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9657         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9658
9659         if (ei->collect_score == 0)
9660         {
9661           int xx, yy;
9662
9663           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9664           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9665
9666           /*
9667             This is a very special case that seems to be a mixture between
9668             CheckElementChange() and CheckTriggeredElementChange(): while
9669             the first one only affects single elements that are triggered
9670             directly, the second one affects multiple elements in the playfield
9671             that are triggered indirectly by another element. This is a third
9672             case: Changing the CE score always affects multiple identical CEs,
9673             so every affected CE must be checked, not only the single CE for
9674             which the CE score was changed in the first place (as every instance
9675             of that CE shares the same CE score, and therefore also can change)!
9676           */
9677           SCAN_PLAYFIELD(xx, yy)
9678           {
9679             if (Feld[xx][yy] == element)
9680               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9681                                  CE_SCORE_GETS_ZERO);
9682           }
9683         }
9684       }
9685 #endif
9686
9687       break;
9688     }
9689
9690     /* ---------- engine actions  ------------------------------------------ */
9691
9692     case CA_SET_ENGINE_SCAN_MODE:
9693     {
9694       InitPlayfieldScanMode(action_arg);
9695
9696       break;
9697     }
9698
9699     default:
9700       break;
9701   }
9702 }
9703
9704 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9705 {
9706   int old_element = Feld[x][y];
9707   int new_element = GetElementFromGroupElement(element);
9708   int previous_move_direction = MovDir[x][y];
9709 #if USE_NEW_CUSTOM_VALUE
9710   int last_ce_value = CustomValue[x][y];
9711 #endif
9712   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9713   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9714   boolean add_player_onto_element = (new_element_is_player &&
9715 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9716                                      /* this breaks SnakeBite when a snake is
9717                                         halfway through a door that closes */
9718                                      /* NOW FIXED AT LEVEL INIT IN files.c */
9719                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9720 #endif
9721                                      IS_WALKABLE(old_element));
9722
9723 #if 0
9724   /* check if element under the player changes from accessible to unaccessible
9725      (needed for special case of dropping element which then changes) */
9726   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9727       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9728   {
9729     Bang(x, y);
9730
9731     return;
9732   }
9733 #endif
9734
9735   if (!add_player_onto_element)
9736   {
9737     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9738       RemoveMovingField(x, y);
9739     else
9740       RemoveField(x, y);
9741
9742     Feld[x][y] = new_element;
9743
9744 #if !USE_GFX_RESET_GFX_ANIMATION
9745     ResetGfxAnimation(x, y);
9746     ResetRandomAnimationValue(x, y);
9747 #endif
9748
9749     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9750       MovDir[x][y] = previous_move_direction;
9751
9752 #if USE_NEW_CUSTOM_VALUE
9753     if (element_info[new_element].use_last_ce_value)
9754       CustomValue[x][y] = last_ce_value;
9755 #endif
9756
9757     InitField_WithBug1(x, y, FALSE);
9758
9759     new_element = Feld[x][y];   /* element may have changed */
9760
9761 #if USE_GFX_RESET_GFX_ANIMATION
9762     ResetGfxAnimation(x, y);
9763     ResetRandomAnimationValue(x, y);
9764 #endif
9765
9766     DrawLevelField(x, y);
9767
9768     if (GFX_CRUMBLED(new_element))
9769       DrawLevelFieldCrumbledSandNeighbours(x, y);
9770   }
9771
9772 #if 1
9773   /* check if element under the player changes from accessible to unaccessible
9774      (needed for special case of dropping element which then changes) */
9775   /* (must be checked after creating new element for walkable group elements) */
9776 #if USE_FIX_KILLED_BY_NON_WALKABLE
9777   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9778       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9779   {
9780     Bang(x, y);
9781
9782     return;
9783   }
9784 #else
9785   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9786       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9787   {
9788     Bang(x, y);
9789
9790     return;
9791   }
9792 #endif
9793 #endif
9794
9795   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9796   if (new_element_is_player)
9797     RelocatePlayer(x, y, new_element);
9798
9799   if (is_change)
9800     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9801
9802   TestIfBadThingTouchesPlayer(x, y);
9803   TestIfPlayerTouchesCustomElement(x, y);
9804   TestIfElementTouchesCustomElement(x, y);
9805 }
9806
9807 static void CreateField(int x, int y, int element)
9808 {
9809   CreateFieldExt(x, y, element, FALSE);
9810 }
9811
9812 static void CreateElementFromChange(int x, int y, int element)
9813 {
9814   element = GET_VALID_RUNTIME_ELEMENT(element);
9815
9816 #if USE_STOP_CHANGED_ELEMENTS
9817   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9818   {
9819     int old_element = Feld[x][y];
9820
9821     /* prevent changed element from moving in same engine frame
9822        unless both old and new element can either fall or move */
9823     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9824         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9825       Stop[x][y] = TRUE;
9826   }
9827 #endif
9828
9829   CreateFieldExt(x, y, element, TRUE);
9830 }
9831
9832 static boolean ChangeElement(int x, int y, int element, int page)
9833 {
9834   struct ElementInfo *ei = &element_info[element];
9835   struct ElementChangeInfo *change = &ei->change_page[page];
9836   int ce_value = CustomValue[x][y];
9837   int ce_score = ei->collect_score;
9838   int target_element;
9839   int old_element = Feld[x][y];
9840
9841   /* always use default change event to prevent running into a loop */
9842   if (ChangeEvent[x][y] == -1)
9843     ChangeEvent[x][y] = CE_DELAY;
9844
9845   if (ChangeEvent[x][y] == CE_DELAY)
9846   {
9847     /* reset actual trigger element, trigger player and action element */
9848     change->actual_trigger_element = EL_EMPTY;
9849     change->actual_trigger_player = EL_PLAYER_1;
9850     change->actual_trigger_side = CH_SIDE_NONE;
9851     change->actual_trigger_ce_value = 0;
9852     change->actual_trigger_ce_score = 0;
9853   }
9854
9855   /* do not change elements more than a specified maximum number of changes */
9856   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
9857     return FALSE;
9858
9859   ChangeCount[x][y]++;          /* count number of changes in the same frame */
9860
9861   if (change->explode)
9862   {
9863     Bang(x, y);
9864
9865     return TRUE;
9866   }
9867
9868   if (change->use_target_content)
9869   {
9870     boolean complete_replace = TRUE;
9871     boolean can_replace[3][3];
9872     int xx, yy;
9873
9874     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9875     {
9876       boolean is_empty;
9877       boolean is_walkable;
9878       boolean is_diggable;
9879       boolean is_collectible;
9880       boolean is_removable;
9881       boolean is_destructible;
9882       int ex = x + xx - 1;
9883       int ey = y + yy - 1;
9884       int content_element = change->target_content.e[xx][yy];
9885       int e;
9886
9887       can_replace[xx][yy] = TRUE;
9888
9889       if (ex == x && ey == y)   /* do not check changing element itself */
9890         continue;
9891
9892       if (content_element == EL_EMPTY_SPACE)
9893       {
9894         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
9895
9896         continue;
9897       }
9898
9899       if (!IN_LEV_FIELD(ex, ey))
9900       {
9901         can_replace[xx][yy] = FALSE;
9902         complete_replace = FALSE;
9903
9904         continue;
9905       }
9906
9907       e = Feld[ex][ey];
9908
9909       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9910         e = MovingOrBlocked2Element(ex, ey);
9911
9912       is_empty = (IS_FREE(ex, ey) ||
9913                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
9914
9915       is_walkable     = (is_empty || IS_WALKABLE(e));
9916       is_diggable     = (is_empty || IS_DIGGABLE(e));
9917       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
9918       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
9919       is_removable    = (is_diggable || is_collectible);
9920
9921       can_replace[xx][yy] =
9922         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
9923           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
9924           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
9925           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
9926           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
9927           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
9928          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
9929
9930       if (!can_replace[xx][yy])
9931         complete_replace = FALSE;
9932     }
9933
9934     if (!change->only_if_complete || complete_replace)
9935     {
9936       boolean something_has_changed = FALSE;
9937
9938       if (change->only_if_complete && change->use_random_replace &&
9939           RND(100) < change->random_percentage)
9940         return FALSE;
9941
9942       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9943       {
9944         int ex = x + xx - 1;
9945         int ey = y + yy - 1;
9946         int content_element;
9947
9948         if (can_replace[xx][yy] && (!change->use_random_replace ||
9949                                     RND(100) < change->random_percentage))
9950         {
9951           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9952             RemoveMovingField(ex, ey);
9953
9954           ChangeEvent[ex][ey] = ChangeEvent[x][y];
9955
9956           content_element = change->target_content.e[xx][yy];
9957           target_element = GET_TARGET_ELEMENT(element, content_element, change,
9958                                               ce_value, ce_score);
9959
9960           CreateElementFromChange(ex, ey, target_element);
9961
9962           something_has_changed = TRUE;
9963
9964           /* for symmetry reasons, freeze newly created border elements */
9965           if (ex != x || ey != y)
9966             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
9967         }
9968       }
9969
9970       if (something_has_changed)
9971       {
9972         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9973         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9974       }
9975     }
9976   }
9977   else
9978   {
9979     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9980                                         ce_value, ce_score);
9981
9982     if (element == EL_DIAGONAL_GROWING ||
9983         element == EL_DIAGONAL_SHRINKING)
9984     {
9985       target_element = Store[x][y];
9986
9987       Store[x][y] = EL_EMPTY;
9988     }
9989
9990     CreateElementFromChange(x, y, target_element);
9991
9992     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9993     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9994   }
9995
9996   /* this uses direct change before indirect change */
9997   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9998
9999   return TRUE;
10000 }
10001
10002 #if USE_NEW_DELAYED_ACTION
10003
10004 static void HandleElementChange(int x, int y, int page)
10005 {
10006   int element = MovingOrBlocked2Element(x, y);
10007   struct ElementInfo *ei = &element_info[element];
10008   struct ElementChangeInfo *change = &ei->change_page[page];
10009
10010 #ifdef DEBUG
10011   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10012       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10013   {
10014     printf("\n\n");
10015     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10016            x, y, element, element_info[element].token_name);
10017     printf("HandleElementChange(): This should never happen!\n");
10018     printf("\n\n");
10019   }
10020 #endif
10021
10022   /* this can happen with classic bombs on walkable, changing elements */
10023   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10024   {
10025 #if 0
10026     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10027       ChangeDelay[x][y] = 0;
10028 #endif
10029
10030     return;
10031   }
10032
10033   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10034   {
10035     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10036
10037     if (change->can_change)
10038     {
10039 #if 1
10040       /* !!! not clear why graphic animation should be reset at all here !!! */
10041       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10042 #if USE_GFX_RESET_WHEN_NOT_MOVING
10043       /* when a custom element is about to change (for example by change delay),
10044          do not reset graphic animation when the custom element is moving */
10045       if (!IS_MOVING(x, y))
10046 #endif
10047       {
10048         ResetGfxAnimation(x, y);
10049         ResetRandomAnimationValue(x, y);
10050       }
10051 #endif
10052
10053       if (change->pre_change_function)
10054         change->pre_change_function(x, y);
10055     }
10056   }
10057
10058   ChangeDelay[x][y]--;
10059
10060   if (ChangeDelay[x][y] != 0)           /* continue element change */
10061   {
10062     if (change->can_change)
10063     {
10064       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10065
10066       if (IS_ANIMATED(graphic))
10067         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10068
10069       if (change->change_function)
10070         change->change_function(x, y);
10071     }
10072   }
10073   else                                  /* finish element change */
10074   {
10075     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10076     {
10077       page = ChangePage[x][y];
10078       ChangePage[x][y] = -1;
10079
10080       change = &ei->change_page[page];
10081     }
10082
10083     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10084     {
10085       ChangeDelay[x][y] = 1;            /* try change after next move step */
10086       ChangePage[x][y] = page;          /* remember page to use for change */
10087
10088       return;
10089     }
10090
10091     if (change->can_change)
10092     {
10093       if (ChangeElement(x, y, element, page))
10094       {
10095         if (change->post_change_function)
10096           change->post_change_function(x, y);
10097       }
10098     }
10099
10100     if (change->has_action)
10101       ExecuteCustomElementAction(x, y, element, page);
10102   }
10103 }
10104
10105 #else
10106
10107 static void HandleElementChange(int x, int y, int page)
10108 {
10109   int element = MovingOrBlocked2Element(x, y);
10110   struct ElementInfo *ei = &element_info[element];
10111   struct ElementChangeInfo *change = &ei->change_page[page];
10112
10113 #ifdef DEBUG
10114   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10115   {
10116     printf("\n\n");
10117     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10118            x, y, element, element_info[element].token_name);
10119     printf("HandleElementChange(): This should never happen!\n");
10120     printf("\n\n");
10121   }
10122 #endif
10123
10124   /* this can happen with classic bombs on walkable, changing elements */
10125   if (!CAN_CHANGE(element))
10126   {
10127 #if 0
10128     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10129       ChangeDelay[x][y] = 0;
10130 #endif
10131
10132     return;
10133   }
10134
10135   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10136   {
10137     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10138
10139     ResetGfxAnimation(x, y);
10140     ResetRandomAnimationValue(x, y);
10141
10142     if (change->pre_change_function)
10143       change->pre_change_function(x, y);
10144   }
10145
10146   ChangeDelay[x][y]--;
10147
10148   if (ChangeDelay[x][y] != 0)           /* continue element change */
10149   {
10150     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10151
10152     if (IS_ANIMATED(graphic))
10153       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10154
10155     if (change->change_function)
10156       change->change_function(x, y);
10157   }
10158   else                                  /* finish element change */
10159   {
10160     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10161     {
10162       page = ChangePage[x][y];
10163       ChangePage[x][y] = -1;
10164
10165       change = &ei->change_page[page];
10166     }
10167
10168     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10169     {
10170       ChangeDelay[x][y] = 1;            /* try change after next move step */
10171       ChangePage[x][y] = page;          /* remember page to use for change */
10172
10173       return;
10174     }
10175
10176     if (ChangeElement(x, y, element, page))
10177     {
10178       if (change->post_change_function)
10179         change->post_change_function(x, y);
10180     }
10181   }
10182 }
10183
10184 #endif
10185
10186 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10187                                               int trigger_element,
10188                                               int trigger_event,
10189                                               int trigger_player,
10190                                               int trigger_side,
10191                                               int trigger_page)
10192 {
10193   boolean change_done_any = FALSE;
10194   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10195   int i;
10196
10197   if (!(trigger_events[trigger_element][trigger_event]))
10198     return FALSE;
10199
10200 #if 0
10201   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10202          trigger_event, recursion_loop_depth, recursion_loop_detected,
10203          recursion_loop_element, EL_NAME(recursion_loop_element));
10204 #endif
10205
10206   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10207
10208   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10209   {
10210     int element = EL_CUSTOM_START + i;
10211     boolean change_done = FALSE;
10212     int p;
10213
10214     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10215         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10216       continue;
10217
10218     for (p = 0; p < element_info[element].num_change_pages; p++)
10219     {
10220       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10221
10222       if (change->can_change_or_has_action &&
10223           change->has_event[trigger_event] &&
10224           change->trigger_side & trigger_side &&
10225           change->trigger_player & trigger_player &&
10226           change->trigger_page & trigger_page_bits &&
10227           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10228       {
10229         change->actual_trigger_element = trigger_element;
10230         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10231         change->actual_trigger_side = trigger_side;
10232         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10233         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10234
10235         if ((change->can_change && !change_done) || change->has_action)
10236         {
10237           int x, y;
10238
10239           SCAN_PLAYFIELD(x, y)
10240           {
10241             if (Feld[x][y] == element)
10242             {
10243               if (change->can_change && !change_done)
10244               {
10245                 ChangeDelay[x][y] = 1;
10246                 ChangeEvent[x][y] = trigger_event;
10247
10248                 HandleElementChange(x, y, p);
10249               }
10250 #if USE_NEW_DELAYED_ACTION
10251               else if (change->has_action)
10252               {
10253                 ExecuteCustomElementAction(x, y, element, p);
10254                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10255               }
10256 #else
10257               if (change->has_action)
10258               {
10259                 ExecuteCustomElementAction(x, y, element, p);
10260                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10261               }
10262 #endif
10263             }
10264           }
10265
10266           if (change->can_change)
10267           {
10268             change_done = TRUE;
10269             change_done_any = TRUE;
10270           }
10271         }
10272       }
10273     }
10274   }
10275
10276   RECURSION_LOOP_DETECTION_END();
10277
10278   return change_done_any;
10279 }
10280
10281 static boolean CheckElementChangeExt(int x, int y,
10282                                      int element,
10283                                      int trigger_element,
10284                                      int trigger_event,
10285                                      int trigger_player,
10286                                      int trigger_side)
10287 {
10288   boolean change_done = FALSE;
10289   int p;
10290
10291   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10292       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10293     return FALSE;
10294
10295   if (Feld[x][y] == EL_BLOCKED)
10296   {
10297     Blocked2Moving(x, y, &x, &y);
10298     element = Feld[x][y];
10299   }
10300
10301 #if 0
10302   /* check if element has already changed */
10303   if (Feld[x][y] != element)
10304     return FALSE;
10305 #else
10306   /* check if element has already changed or is about to change after moving */
10307   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10308        Feld[x][y] != element) ||
10309
10310       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10311        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10312         ChangePage[x][y] != -1)))
10313     return FALSE;
10314 #endif
10315
10316 #if 0
10317   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10318          trigger_event, recursion_loop_depth, recursion_loop_detected,
10319          recursion_loop_element, EL_NAME(recursion_loop_element));
10320 #endif
10321
10322   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10323
10324   for (p = 0; p < element_info[element].num_change_pages; p++)
10325   {
10326     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10327
10328     /* check trigger element for all events where the element that is checked
10329        for changing interacts with a directly adjacent element -- this is
10330        different to element changes that affect other elements to change on the
10331        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10332     boolean check_trigger_element =
10333       (trigger_event == CE_TOUCHING_X ||
10334        trigger_event == CE_HITTING_X ||
10335        trigger_event == CE_HIT_BY_X ||
10336 #if 1
10337        /* this one was forgotten until 3.2.3 */
10338        trigger_event == CE_DIGGING_X);
10339 #endif
10340
10341     if (change->can_change_or_has_action &&
10342         change->has_event[trigger_event] &&
10343         change->trigger_side & trigger_side &&
10344         change->trigger_player & trigger_player &&
10345         (!check_trigger_element ||
10346          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10347     {
10348       change->actual_trigger_element = trigger_element;
10349       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10350       change->actual_trigger_side = trigger_side;
10351       change->actual_trigger_ce_value = CustomValue[x][y];
10352       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10353
10354       /* special case: trigger element not at (x,y) position for some events */
10355       if (check_trigger_element)
10356       {
10357         static struct
10358         {
10359           int dx, dy;
10360         } move_xy[] =
10361           {
10362             {  0,  0 },
10363             { -1,  0 },
10364             { +1,  0 },
10365             {  0,  0 },
10366             {  0, -1 },
10367             {  0,  0 }, { 0, 0 }, { 0, 0 },
10368             {  0, +1 }
10369           };
10370
10371         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10372         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10373
10374         change->actual_trigger_ce_value = CustomValue[xx][yy];
10375         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10376       }
10377
10378       if (change->can_change && !change_done)
10379       {
10380         ChangeDelay[x][y] = 1;
10381         ChangeEvent[x][y] = trigger_event;
10382
10383         HandleElementChange(x, y, p);
10384
10385         change_done = TRUE;
10386       }
10387 #if USE_NEW_DELAYED_ACTION
10388       else if (change->has_action)
10389       {
10390         ExecuteCustomElementAction(x, y, element, p);
10391         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10392       }
10393 #else
10394       if (change->has_action)
10395       {
10396         ExecuteCustomElementAction(x, y, element, p);
10397         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10398       }
10399 #endif
10400     }
10401   }
10402
10403   RECURSION_LOOP_DETECTION_END();
10404
10405   return change_done;
10406 }
10407
10408 static void PlayPlayerSound(struct PlayerInfo *player)
10409 {
10410   int jx = player->jx, jy = player->jy;
10411   int sound_element = player->artwork_element;
10412   int last_action = player->last_action_waiting;
10413   int action = player->action_waiting;
10414
10415   if (player->is_waiting)
10416   {
10417     if (action != last_action)
10418       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10419     else
10420       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10421   }
10422   else
10423   {
10424     if (action != last_action)
10425       StopSound(element_info[sound_element].sound[last_action]);
10426
10427     if (last_action == ACTION_SLEEPING)
10428       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10429   }
10430 }
10431
10432 static void PlayAllPlayersSound()
10433 {
10434   int i;
10435
10436   for (i = 0; i < MAX_PLAYERS; i++)
10437     if (stored_player[i].active)
10438       PlayPlayerSound(&stored_player[i]);
10439 }
10440
10441 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10442 {
10443   boolean last_waiting = player->is_waiting;
10444   int move_dir = player->MovDir;
10445
10446   player->dir_waiting = move_dir;
10447   player->last_action_waiting = player->action_waiting;
10448
10449   if (is_waiting)
10450   {
10451     if (!last_waiting)          /* not waiting -> waiting */
10452     {
10453       player->is_waiting = TRUE;
10454
10455       player->frame_counter_bored =
10456         FrameCounter +
10457         game.player_boring_delay_fixed +
10458         GetSimpleRandom(game.player_boring_delay_random);
10459       player->frame_counter_sleeping =
10460         FrameCounter +
10461         game.player_sleeping_delay_fixed +
10462         GetSimpleRandom(game.player_sleeping_delay_random);
10463
10464       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10465     }
10466
10467     if (game.player_sleeping_delay_fixed +
10468         game.player_sleeping_delay_random > 0 &&
10469         player->anim_delay_counter == 0 &&
10470         player->post_delay_counter == 0 &&
10471         FrameCounter >= player->frame_counter_sleeping)
10472       player->is_sleeping = TRUE;
10473     else if (game.player_boring_delay_fixed +
10474              game.player_boring_delay_random > 0 &&
10475              FrameCounter >= player->frame_counter_bored)
10476       player->is_bored = TRUE;
10477
10478     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10479                               player->is_bored ? ACTION_BORING :
10480                               ACTION_WAITING);
10481
10482     if (player->is_sleeping && player->use_murphy)
10483     {
10484       /* special case for sleeping Murphy when leaning against non-free tile */
10485
10486       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10487           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10488            !IS_MOVING(player->jx - 1, player->jy)))
10489         move_dir = MV_LEFT;
10490       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10491                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10492                 !IS_MOVING(player->jx + 1, player->jy)))
10493         move_dir = MV_RIGHT;
10494       else
10495         player->is_sleeping = FALSE;
10496
10497       player->dir_waiting = move_dir;
10498     }
10499
10500     if (player->is_sleeping)
10501     {
10502       if (player->num_special_action_sleeping > 0)
10503       {
10504         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10505         {
10506           int last_special_action = player->special_action_sleeping;
10507           int num_special_action = player->num_special_action_sleeping;
10508           int special_action =
10509             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10510              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10511              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10512              last_special_action + 1 : ACTION_SLEEPING);
10513           int special_graphic =
10514             el_act_dir2img(player->artwork_element, special_action, move_dir);
10515
10516           player->anim_delay_counter =
10517             graphic_info[special_graphic].anim_delay_fixed +
10518             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10519           player->post_delay_counter =
10520             graphic_info[special_graphic].post_delay_fixed +
10521             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10522
10523           player->special_action_sleeping = special_action;
10524         }
10525
10526         if (player->anim_delay_counter > 0)
10527         {
10528           player->action_waiting = player->special_action_sleeping;
10529           player->anim_delay_counter--;
10530         }
10531         else if (player->post_delay_counter > 0)
10532         {
10533           player->post_delay_counter--;
10534         }
10535       }
10536     }
10537     else if (player->is_bored)
10538     {
10539       if (player->num_special_action_bored > 0)
10540       {
10541         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10542         {
10543           int special_action =
10544             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10545           int special_graphic =
10546             el_act_dir2img(player->artwork_element, special_action, move_dir);
10547
10548           player->anim_delay_counter =
10549             graphic_info[special_graphic].anim_delay_fixed +
10550             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10551           player->post_delay_counter =
10552             graphic_info[special_graphic].post_delay_fixed +
10553             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10554
10555           player->special_action_bored = special_action;
10556         }
10557
10558         if (player->anim_delay_counter > 0)
10559         {
10560           player->action_waiting = player->special_action_bored;
10561           player->anim_delay_counter--;
10562         }
10563         else if (player->post_delay_counter > 0)
10564         {
10565           player->post_delay_counter--;
10566         }
10567       }
10568     }
10569   }
10570   else if (last_waiting)        /* waiting -> not waiting */
10571   {
10572     player->is_waiting = FALSE;
10573     player->is_bored = FALSE;
10574     player->is_sleeping = FALSE;
10575
10576     player->frame_counter_bored = -1;
10577     player->frame_counter_sleeping = -1;
10578
10579     player->anim_delay_counter = 0;
10580     player->post_delay_counter = 0;
10581
10582     player->dir_waiting = player->MovDir;
10583     player->action_waiting = ACTION_DEFAULT;
10584
10585     player->special_action_bored = ACTION_DEFAULT;
10586     player->special_action_sleeping = ACTION_DEFAULT;
10587   }
10588 }
10589
10590 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10591 {
10592   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10593   int left      = player_action & JOY_LEFT;
10594   int right     = player_action & JOY_RIGHT;
10595   int up        = player_action & JOY_UP;
10596   int down      = player_action & JOY_DOWN;
10597   int button1   = player_action & JOY_BUTTON_1;
10598   int button2   = player_action & JOY_BUTTON_2;
10599   int dx        = (left ? -1 : right ? 1 : 0);
10600   int dy        = (up   ? -1 : down  ? 1 : 0);
10601
10602   if (!player->active || tape.pausing)
10603     return 0;
10604
10605   if (player_action)
10606   {
10607     if (button1)
10608       snapped = SnapField(player, dx, dy);
10609     else
10610     {
10611       if (button2)
10612         dropped = DropElement(player);
10613
10614       moved = MovePlayer(player, dx, dy);
10615     }
10616
10617     if (tape.single_step && tape.recording && !tape.pausing)
10618     {
10619       if (button1 || (dropped && !moved))
10620       {
10621         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10622         SnapField(player, 0, 0);                /* stop snapping */
10623       }
10624     }
10625
10626     SetPlayerWaiting(player, FALSE);
10627
10628     return player_action;
10629   }
10630   else
10631   {
10632     /* no actions for this player (no input at player's configured device) */
10633
10634     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10635     SnapField(player, 0, 0);
10636     CheckGravityMovementWhenNotMoving(player);
10637
10638     if (player->MovPos == 0)
10639       SetPlayerWaiting(player, TRUE);
10640
10641     if (player->MovPos == 0)    /* needed for tape.playing */
10642       player->is_moving = FALSE;
10643
10644     player->is_dropping = FALSE;
10645     player->is_dropping_pressed = FALSE;
10646     player->drop_pressed_delay = 0;
10647
10648     return 0;
10649   }
10650 }
10651
10652 static void CheckLevelTime()
10653 {
10654   int i;
10655
10656   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10657   {
10658     if (level.native_em_level->lev->home == 0)  /* all players at home */
10659     {
10660       PlayerWins(local_player);
10661
10662       AllPlayersGone = TRUE;
10663
10664       level.native_em_level->lev->home = -1;
10665     }
10666
10667     if (level.native_em_level->ply[0]->alive == 0 &&
10668         level.native_em_level->ply[1]->alive == 0 &&
10669         level.native_em_level->ply[2]->alive == 0 &&
10670         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10671       AllPlayersGone = TRUE;
10672   }
10673
10674   if (TimeFrames >= FRAMES_PER_SECOND)
10675   {
10676     TimeFrames = 0;
10677     TapeTime++;
10678
10679     for (i = 0; i < MAX_PLAYERS; i++)
10680     {
10681       struct PlayerInfo *player = &stored_player[i];
10682
10683       if (SHIELD_ON(player))
10684       {
10685         player->shield_normal_time_left--;
10686
10687         if (player->shield_deadly_time_left > 0)
10688           player->shield_deadly_time_left--;
10689       }
10690     }
10691
10692     if (!local_player->LevelSolved && !level.use_step_counter)
10693     {
10694       TimePlayed++;
10695
10696       if (TimeLeft > 0)
10697       {
10698         TimeLeft--;
10699
10700         if (TimeLeft <= 10 && setup.time_limit)
10701           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10702
10703 #if 1
10704         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
10705
10706         DisplayGameControlValues();
10707 #else
10708         DrawGameValue_Time(TimeLeft);
10709 #endif
10710
10711         if (!TimeLeft && setup.time_limit)
10712         {
10713           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10714             level.native_em_level->lev->killed_out_of_time = TRUE;
10715           else
10716             for (i = 0; i < MAX_PLAYERS; i++)
10717               KillPlayer(&stored_player[i]);
10718         }
10719       }
10720 #if 1
10721       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10722       {
10723         game_control_value[GAME_CONTROL_TIME] = TimePlayed;
10724
10725         DisplayGameControlValues();
10726       }
10727 #else
10728       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10729         DrawGameValue_Time(TimePlayed);
10730 #endif
10731
10732       level.native_em_level->lev->time =
10733         (level.time == 0 ? TimePlayed : TimeLeft);
10734     }
10735
10736     if (tape.recording || tape.playing)
10737       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10738   }
10739
10740   DrawGameDoorValues();
10741 }
10742
10743 void AdvanceFrameAndPlayerCounters(int player_nr)
10744 {
10745   int i;
10746
10747   /* advance frame counters (global frame counter and time frame counter) */
10748   FrameCounter++;
10749   TimeFrames++;
10750
10751   /* advance player counters (counters for move delay, move animation etc.) */
10752   for (i = 0; i < MAX_PLAYERS; i++)
10753   {
10754     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10755     int move_delay_value = stored_player[i].move_delay_value;
10756     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10757
10758     if (!advance_player_counters)       /* not all players may be affected */
10759       continue;
10760
10761 #if USE_NEW_PLAYER_ANIM
10762     if (move_frames == 0)       /* less than one move per game frame */
10763     {
10764       int stepsize = TILEX / move_delay_value;
10765       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10766       int count = (stored_player[i].is_moving ?
10767                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10768
10769       if (count % delay == 0)
10770         move_frames = 1;
10771     }
10772 #endif
10773
10774     stored_player[i].Frame += move_frames;
10775
10776     if (stored_player[i].MovPos != 0)
10777       stored_player[i].StepFrame += move_frames;
10778
10779     if (stored_player[i].move_delay > 0)
10780       stored_player[i].move_delay--;
10781
10782     /* due to bugs in previous versions, counter must count up, not down */
10783     if (stored_player[i].push_delay != -1)
10784       stored_player[i].push_delay++;
10785
10786     if (stored_player[i].drop_delay > 0)
10787       stored_player[i].drop_delay--;
10788
10789     if (stored_player[i].is_dropping_pressed)
10790       stored_player[i].drop_pressed_delay++;
10791   }
10792 }
10793
10794 void StartGameActions(boolean init_network_game, boolean record_tape,
10795                       long random_seed)
10796 {
10797   unsigned long new_random_seed = InitRND(random_seed);
10798
10799   if (record_tape)
10800     TapeStartRecording(new_random_seed);
10801
10802 #if defined(NETWORK_AVALIABLE)
10803   if (init_network_game)
10804   {
10805     SendToServer_StartPlaying();
10806
10807     return;
10808   }
10809 #endif
10810
10811   InitGame();
10812 }
10813
10814 void GameActions()
10815 {
10816   static unsigned long game_frame_delay = 0;
10817   unsigned long game_frame_delay_value;
10818   byte *recorded_player_action;
10819   byte summarized_player_action = 0;
10820   byte tape_action[MAX_PLAYERS];
10821   int i;
10822
10823   /* detect endless loops, caused by custom element programming */
10824   if (recursion_loop_detected && recursion_loop_depth == 0)
10825   {
10826     char *message = getStringCat3("Internal Error ! Element ",
10827                                   EL_NAME(recursion_loop_element),
10828                                   " caused endless loop ! Quit the game ?");
10829
10830     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10831           EL_NAME(recursion_loop_element));
10832
10833     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10834
10835     recursion_loop_detected = FALSE;    /* if game should be continued */
10836
10837     free(message);
10838
10839     return;
10840   }
10841
10842   if (game.restart_level)
10843     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
10844
10845   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10846   {
10847     if (level.native_em_level->lev->home == 0)  /* all players at home */
10848     {
10849       PlayerWins(local_player);
10850
10851       AllPlayersGone = TRUE;
10852
10853       level.native_em_level->lev->home = -1;
10854     }
10855
10856     if (level.native_em_level->ply[0]->alive == 0 &&
10857         level.native_em_level->ply[1]->alive == 0 &&
10858         level.native_em_level->ply[2]->alive == 0 &&
10859         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10860       AllPlayersGone = TRUE;
10861   }
10862
10863   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10864     GameWon();
10865
10866   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10867     TapeStop();
10868
10869   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
10870     return;
10871
10872   game_frame_delay_value =
10873     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10874
10875   if (tape.playing && tape.warp_forward && !tape.pausing)
10876     game_frame_delay_value = 0;
10877
10878   /* ---------- main game synchronization point ---------- */
10879
10880   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10881
10882   if (network_playing && !network_player_action_received)
10883   {
10884     /* try to get network player actions in time */
10885
10886 #if defined(NETWORK_AVALIABLE)
10887     /* last chance to get network player actions without main loop delay */
10888     HandleNetworking();
10889 #endif
10890
10891     /* game was quit by network peer */
10892     if (game_status != GAME_MODE_PLAYING)
10893       return;
10894
10895     if (!network_player_action_received)
10896       return;           /* failed to get network player actions in time */
10897
10898     /* do not yet reset "network_player_action_received" (for tape.pausing) */
10899   }
10900
10901   if (tape.pausing)
10902     return;
10903
10904   /* at this point we know that we really continue executing the game */
10905
10906   network_player_action_received = FALSE;
10907
10908   /* when playing tape, read previously recorded player input from tape data */
10909   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
10910
10911 #if 1
10912   /* TapePlayAction() may return NULL when toggling to "pause before death" */
10913   if (tape.pausing)
10914     return;
10915 #endif
10916
10917   if (tape.set_centered_player)
10918   {
10919     game.centered_player_nr_next = tape.centered_player_nr_next;
10920     game.set_centered_player = TRUE;
10921   }
10922
10923   for (i = 0; i < MAX_PLAYERS; i++)
10924   {
10925     summarized_player_action |= stored_player[i].action;
10926
10927     if (!network_playing)
10928       stored_player[i].effective_action = stored_player[i].action;
10929   }
10930
10931 #if defined(NETWORK_AVALIABLE)
10932   if (network_playing)
10933     SendToServer_MovePlayer(summarized_player_action);
10934 #endif
10935
10936   if (!options.network && !setup.team_mode)
10937     local_player->effective_action = summarized_player_action;
10938
10939   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
10940   {
10941     for (i = 0; i < MAX_PLAYERS; i++)
10942       stored_player[i].effective_action =
10943         (i == game.centered_player_nr ? summarized_player_action : 0);
10944   }
10945
10946   if (recorded_player_action != NULL)
10947     for (i = 0; i < MAX_PLAYERS; i++)
10948       stored_player[i].effective_action = recorded_player_action[i];
10949
10950   for (i = 0; i < MAX_PLAYERS; i++)
10951   {
10952     tape_action[i] = stored_player[i].effective_action;
10953
10954     /* (this can only happen in the R'n'D game engine) */
10955     if (tape.recording && tape_action[i] && !tape.player_participates[i])
10956       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
10957   }
10958
10959   /* only record actions from input devices, but not programmed actions */
10960   if (tape.recording)
10961     TapeRecordAction(tape_action);
10962
10963   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10964   {
10965     GameActions_EM_Main();
10966   }
10967   else
10968   {
10969     GameActions_RND();
10970   }
10971 }
10972
10973 void GameActions_EM_Main()
10974 {
10975   byte effective_action[MAX_PLAYERS];
10976   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
10977   int i;
10978
10979   for (i = 0; i < MAX_PLAYERS; i++)
10980     effective_action[i] = stored_player[i].effective_action;
10981
10982   GameActions_EM(effective_action, warp_mode);
10983
10984   CheckLevelTime();
10985
10986   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
10987 }
10988
10989 void GameActions_RND()
10990 {
10991   int magic_wall_x = 0, magic_wall_y = 0;
10992   int i, x, y, element, graphic;
10993
10994   InitPlayfieldScanModeVars();
10995
10996 #if USE_ONE_MORE_CHANGE_PER_FRAME
10997   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10998   {
10999     SCAN_PLAYFIELD(x, y)
11000     {
11001       ChangeCount[x][y] = 0;
11002       ChangeEvent[x][y] = -1;
11003     }
11004   }
11005 #endif
11006
11007   if (game.set_centered_player)
11008   {
11009     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11010
11011     /* switching to "all players" only possible if all players fit to screen */
11012     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11013     {
11014       game.centered_player_nr_next = game.centered_player_nr;
11015       game.set_centered_player = FALSE;
11016     }
11017
11018     /* do not switch focus to non-existing (or non-active) player */
11019     if (game.centered_player_nr_next >= 0 &&
11020         !stored_player[game.centered_player_nr_next].active)
11021     {
11022       game.centered_player_nr_next = game.centered_player_nr;
11023       game.set_centered_player = FALSE;
11024     }
11025   }
11026
11027   if (game.set_centered_player &&
11028       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11029   {
11030     int sx, sy;
11031
11032     if (game.centered_player_nr_next == -1)
11033     {
11034       setScreenCenteredToAllPlayers(&sx, &sy);
11035     }
11036     else
11037     {
11038       sx = stored_player[game.centered_player_nr_next].jx;
11039       sy = stored_player[game.centered_player_nr_next].jy;
11040     }
11041
11042     game.centered_player_nr = game.centered_player_nr_next;
11043     game.set_centered_player = FALSE;
11044
11045     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11046     DrawGameDoorValues();
11047   }
11048
11049   for (i = 0; i < MAX_PLAYERS; i++)
11050   {
11051     int actual_player_action = stored_player[i].effective_action;
11052
11053 #if 1
11054     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11055        - rnd_equinox_tetrachloride 048
11056        - rnd_equinox_tetrachloride_ii 096
11057        - rnd_emanuel_schmieg 002
11058        - doctor_sloan_ww 001, 020
11059     */
11060     if (stored_player[i].MovPos == 0)
11061       CheckGravityMovement(&stored_player[i]);
11062 #endif
11063
11064     /* overwrite programmed action with tape action */
11065     if (stored_player[i].programmed_action)
11066       actual_player_action = stored_player[i].programmed_action;
11067
11068     PlayerActions(&stored_player[i], actual_player_action);
11069
11070     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11071   }
11072
11073   ScrollScreen(NULL, SCROLL_GO_ON);
11074
11075   /* for backwards compatibility, the following code emulates a fixed bug that
11076      occured when pushing elements (causing elements that just made their last
11077      pushing step to already (if possible) make their first falling step in the
11078      same game frame, which is bad); this code is also needed to use the famous
11079      "spring push bug" which is used in older levels and might be wanted to be
11080      used also in newer levels, but in this case the buggy pushing code is only
11081      affecting the "spring" element and no other elements */
11082
11083   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11084   {
11085     for (i = 0; i < MAX_PLAYERS; i++)
11086     {
11087       struct PlayerInfo *player = &stored_player[i];
11088       int x = player->jx;
11089       int y = player->jy;
11090
11091       if (player->active && player->is_pushing && player->is_moving &&
11092           IS_MOVING(x, y) &&
11093           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11094            Feld[x][y] == EL_SPRING))
11095       {
11096         ContinueMoving(x, y);
11097
11098         /* continue moving after pushing (this is actually a bug) */
11099         if (!IS_MOVING(x, y))
11100           Stop[x][y] = FALSE;
11101       }
11102     }
11103   }
11104
11105 #if 0
11106   debug_print_timestamp(0, "start main loop profiling");
11107 #endif
11108
11109   SCAN_PLAYFIELD(x, y)
11110   {
11111     ChangeCount[x][y] = 0;
11112     ChangeEvent[x][y] = -1;
11113
11114     /* this must be handled before main playfield loop */
11115     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11116     {
11117       MovDelay[x][y]--;
11118       if (MovDelay[x][y] <= 0)
11119         RemoveField(x, y);
11120     }
11121
11122 #if USE_NEW_SNAP_DELAY
11123     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11124     {
11125       MovDelay[x][y]--;
11126       if (MovDelay[x][y] <= 0)
11127       {
11128         RemoveField(x, y);
11129         DrawLevelField(x, y);
11130
11131         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11132       }
11133     }
11134 #endif
11135
11136 #if DEBUG
11137     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11138     {
11139       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11140       printf("GameActions(): This should never happen!\n");
11141
11142       ChangePage[x][y] = -1;
11143     }
11144 #endif
11145
11146     Stop[x][y] = FALSE;
11147     if (WasJustMoving[x][y] > 0)
11148       WasJustMoving[x][y]--;
11149     if (WasJustFalling[x][y] > 0)
11150       WasJustFalling[x][y]--;
11151     if (CheckCollision[x][y] > 0)
11152       CheckCollision[x][y]--;
11153     if (CheckImpact[x][y] > 0)
11154       CheckImpact[x][y]--;
11155
11156     GfxFrame[x][y]++;
11157
11158     /* reset finished pushing action (not done in ContinueMoving() to allow
11159        continuous pushing animation for elements with zero push delay) */
11160     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11161     {
11162       ResetGfxAnimation(x, y);
11163       DrawLevelField(x, y);
11164     }
11165
11166 #if DEBUG
11167     if (IS_BLOCKED(x, y))
11168     {
11169       int oldx, oldy;
11170
11171       Blocked2Moving(x, y, &oldx, &oldy);
11172       if (!IS_MOVING(oldx, oldy))
11173       {
11174         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11175         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11176         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11177         printf("GameActions(): This should never happen!\n");
11178       }
11179     }
11180 #endif
11181   }
11182
11183 #if 0
11184   debug_print_timestamp(0, "- time for pre-main loop:");
11185 #endif
11186
11187 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
11188   SCAN_PLAYFIELD(x, y)
11189   {
11190     element = Feld[x][y];
11191     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11192
11193 #if 1
11194     {
11195 #if 1
11196       int element2 = element;
11197       int graphic2 = graphic;
11198 #else
11199       int element2 = Feld[x][y];
11200       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11201 #endif
11202       int last_gfx_frame = GfxFrame[x][y];
11203
11204       if (graphic_info[graphic2].anim_global_sync)
11205         GfxFrame[x][y] = FrameCounter;
11206       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11207         GfxFrame[x][y] = CustomValue[x][y];
11208       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11209         GfxFrame[x][y] = element_info[element2].collect_score;
11210       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11211         GfxFrame[x][y] = ChangeDelay[x][y];
11212
11213       if (redraw && GfxFrame[x][y] != last_gfx_frame)
11214         DrawLevelGraphicAnimation(x, y, graphic2);
11215     }
11216 #else
11217     ResetGfxFrame(x, y, TRUE);
11218 #endif
11219
11220 #if 1
11221     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11222         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11223       ResetRandomAnimationValue(x, y);
11224 #endif
11225
11226 #if 1
11227     SetRandomAnimationValue(x, y);
11228 #endif
11229
11230 #if 1
11231     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11232 #endif
11233   }
11234 #endif  // -------------------- !!! TEST ONLY !!! --------------------
11235
11236 #if 0
11237   debug_print_timestamp(0, "- time for TEST loop:     -->");
11238 #endif
11239
11240   SCAN_PLAYFIELD(x, y)
11241   {
11242     element = Feld[x][y];
11243     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11244
11245     ResetGfxFrame(x, y, TRUE);
11246
11247     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11248         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11249       ResetRandomAnimationValue(x, y);
11250
11251     SetRandomAnimationValue(x, y);
11252
11253     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11254
11255     if (IS_INACTIVE(element))
11256     {
11257       if (IS_ANIMATED(graphic))
11258         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11259
11260       continue;
11261     }
11262
11263     /* this may take place after moving, so 'element' may have changed */
11264     if (IS_CHANGING(x, y) &&
11265         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11266     {
11267       int page = element_info[element].event_page_nr[CE_DELAY];
11268
11269 #if 1
11270       HandleElementChange(x, y, page);
11271 #else
11272       if (CAN_CHANGE(element))
11273         HandleElementChange(x, y, page);
11274
11275       if (HAS_ACTION(element))
11276         ExecuteCustomElementAction(x, y, element, page);
11277 #endif
11278
11279       element = Feld[x][y];
11280       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11281     }
11282
11283 #if 0   // ---------------------------------------------------------------------
11284
11285     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11286     {
11287       StartMoving(x, y);
11288
11289       element = Feld[x][y];
11290       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11291
11292       if (IS_ANIMATED(graphic) &&
11293           !IS_MOVING(x, y) &&
11294           !Stop[x][y])
11295         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11296
11297       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11298         DrawTwinkleOnField(x, y);
11299     }
11300     else if (IS_MOVING(x, y))
11301       ContinueMoving(x, y);
11302     else
11303     {
11304       switch (element)
11305       {
11306         case EL_ACID:
11307         case EL_EXIT_OPEN:
11308         case EL_EM_EXIT_OPEN:
11309         case EL_SP_EXIT_OPEN:
11310         case EL_STEEL_EXIT_OPEN:
11311         case EL_EM_STEEL_EXIT_OPEN:
11312         case EL_SP_TERMINAL:
11313         case EL_SP_TERMINAL_ACTIVE:
11314         case EL_EXTRA_TIME:
11315         case EL_SHIELD_NORMAL:
11316         case EL_SHIELD_DEADLY:
11317           if (IS_ANIMATED(graphic))
11318             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11319           break;
11320
11321         case EL_DYNAMITE_ACTIVE:
11322         case EL_EM_DYNAMITE_ACTIVE:
11323         case EL_DYNABOMB_PLAYER_1_ACTIVE:
11324         case EL_DYNABOMB_PLAYER_2_ACTIVE:
11325         case EL_DYNABOMB_PLAYER_3_ACTIVE:
11326         case EL_DYNABOMB_PLAYER_4_ACTIVE:
11327         case EL_SP_DISK_RED_ACTIVE:
11328           CheckDynamite(x, y);
11329           break;
11330
11331         case EL_AMOEBA_GROWING:
11332           AmoebeWaechst(x, y);
11333           break;
11334
11335         case EL_AMOEBA_SHRINKING:
11336           AmoebaDisappearing(x, y);
11337           break;
11338
11339 #if !USE_NEW_AMOEBA_CODE
11340         case EL_AMOEBA_WET:
11341         case EL_AMOEBA_DRY:
11342         case EL_AMOEBA_FULL:
11343         case EL_BD_AMOEBA:
11344         case EL_EMC_DRIPPER:
11345           AmoebeAbleger(x, y);
11346           break;
11347 #endif
11348
11349         case EL_GAME_OF_LIFE:
11350         case EL_BIOMAZE:
11351           Life(x, y);
11352           break;
11353
11354         case EL_EXIT_CLOSED:
11355           CheckExit(x, y);
11356           break;
11357
11358         case EL_EM_EXIT_CLOSED:
11359           CheckExitEM(x, y);
11360           break;
11361
11362         case EL_STEEL_EXIT_CLOSED:
11363           CheckExitSteel(x, y);
11364           break;
11365
11366         case EL_EM_STEEL_EXIT_CLOSED:
11367           CheckExitSteelEM(x, y);
11368           break;
11369
11370         case EL_SP_EXIT_CLOSED:
11371           CheckExitSP(x, y);
11372           break;
11373
11374         case EL_EXPANDABLE_WALL_GROWING:
11375         case EL_EXPANDABLE_STEELWALL_GROWING:
11376           MauerWaechst(x, y);
11377           break;
11378
11379         case EL_EXPANDABLE_WALL:
11380         case EL_EXPANDABLE_WALL_HORIZONTAL:
11381         case EL_EXPANDABLE_WALL_VERTICAL:
11382         case EL_EXPANDABLE_WALL_ANY:
11383         case EL_BD_EXPANDABLE_WALL:
11384           MauerAbleger(x, y);
11385           break;
11386
11387         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
11388         case EL_EXPANDABLE_STEELWALL_VERTICAL:
11389         case EL_EXPANDABLE_STEELWALL_ANY:
11390           MauerAblegerStahl(x, y);
11391           break;
11392
11393         case EL_FLAMES:
11394           CheckForDragon(x, y);
11395           break;
11396
11397         case EL_EXPLOSION:
11398           break;
11399
11400         case EL_ELEMENT_SNAPPING:
11401         case EL_DIAGONAL_SHRINKING:
11402         case EL_DIAGONAL_GROWING:
11403         {
11404           graphic =
11405             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11406
11407           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11408           break;
11409         }
11410
11411         default:
11412           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11413             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11414           break;
11415       }
11416     }
11417
11418 #else   // ---------------------------------------------------------------------
11419
11420     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11421     {
11422       StartMoving(x, y);
11423
11424       element = Feld[x][y];
11425       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11426
11427       if (IS_ANIMATED(graphic) &&
11428           !IS_MOVING(x, y) &&
11429           !Stop[x][y])
11430         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11431
11432       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11433         DrawTwinkleOnField(x, y);
11434     }
11435     else if ((element == EL_ACID ||
11436               element == EL_EXIT_OPEN ||
11437               element == EL_EM_EXIT_OPEN ||
11438               element == EL_SP_EXIT_OPEN ||
11439               element == EL_STEEL_EXIT_OPEN ||
11440               element == EL_EM_STEEL_EXIT_OPEN ||
11441               element == EL_SP_TERMINAL ||
11442               element == EL_SP_TERMINAL_ACTIVE ||
11443               element == EL_EXTRA_TIME ||
11444               element == EL_SHIELD_NORMAL ||
11445               element == EL_SHIELD_DEADLY) &&
11446              IS_ANIMATED(graphic))
11447       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11448     else if (IS_MOVING(x, y))
11449       ContinueMoving(x, y);
11450     else if (IS_ACTIVE_BOMB(element))
11451       CheckDynamite(x, y);
11452     else if (element == EL_AMOEBA_GROWING)
11453       AmoebeWaechst(x, y);
11454     else if (element == EL_AMOEBA_SHRINKING)
11455       AmoebaDisappearing(x, y);
11456
11457 #if !USE_NEW_AMOEBA_CODE
11458     else if (IS_AMOEBALIVE(element))
11459       AmoebeAbleger(x, y);
11460 #endif
11461
11462     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11463       Life(x, y);
11464     else if (element == EL_EXIT_CLOSED)
11465       CheckExit(x, y);
11466     else if (element == EL_EM_EXIT_CLOSED)
11467       CheckExitEM(x, y);
11468     else if (element == EL_STEEL_EXIT_CLOSED)
11469       CheckExitSteel(x, y);
11470     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11471       CheckExitSteelEM(x, y);
11472     else if (element == EL_SP_EXIT_CLOSED)
11473       CheckExitSP(x, y);
11474     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11475              element == EL_EXPANDABLE_STEELWALL_GROWING)
11476       MauerWaechst(x, y);
11477     else if (element == EL_EXPANDABLE_WALL ||
11478              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11479              element == EL_EXPANDABLE_WALL_VERTICAL ||
11480              element == EL_EXPANDABLE_WALL_ANY ||
11481              element == EL_BD_EXPANDABLE_WALL)
11482       MauerAbleger(x, y);
11483     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11484              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11485              element == EL_EXPANDABLE_STEELWALL_ANY)
11486       MauerAblegerStahl(x, y);
11487     else if (element == EL_FLAMES)
11488       CheckForDragon(x, y);
11489     else if (element == EL_EXPLOSION)
11490       ; /* drawing of correct explosion animation is handled separately */
11491     else if (element == EL_ELEMENT_SNAPPING ||
11492              element == EL_DIAGONAL_SHRINKING ||
11493              element == EL_DIAGONAL_GROWING)
11494     {
11495       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11496
11497       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11498     }
11499     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11500       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11501
11502 #endif  // ---------------------------------------------------------------------
11503
11504     if (IS_BELT_ACTIVE(element))
11505       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11506
11507     if (game.magic_wall_active)
11508     {
11509       int jx = local_player->jx, jy = local_player->jy;
11510
11511       /* play the element sound at the position nearest to the player */
11512       if ((element == EL_MAGIC_WALL_FULL ||
11513            element == EL_MAGIC_WALL_ACTIVE ||
11514            element == EL_MAGIC_WALL_EMPTYING ||
11515            element == EL_BD_MAGIC_WALL_FULL ||
11516            element == EL_BD_MAGIC_WALL_ACTIVE ||
11517            element == EL_BD_MAGIC_WALL_EMPTYING ||
11518            element == EL_DC_MAGIC_WALL_FULL ||
11519            element == EL_DC_MAGIC_WALL_ACTIVE ||
11520            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11521           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11522       {
11523         magic_wall_x = x;
11524         magic_wall_y = y;
11525       }
11526     }
11527   }
11528
11529 #if 0
11530   debug_print_timestamp(0, "- time for MAIN loop:     -->");
11531 #endif
11532
11533 #if USE_NEW_AMOEBA_CODE
11534   /* new experimental amoeba growth stuff */
11535   if (!(FrameCounter % 8))
11536   {
11537     static unsigned long random = 1684108901;
11538
11539     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11540     {
11541       x = RND(lev_fieldx);
11542       y = RND(lev_fieldy);
11543       element = Feld[x][y];
11544
11545       if (!IS_PLAYER(x,y) &&
11546           (element == EL_EMPTY ||
11547            CAN_GROW_INTO(element) ||
11548            element == EL_QUICKSAND_EMPTY ||
11549            element == EL_QUICKSAND_FAST_EMPTY ||
11550            element == EL_ACID_SPLASH_LEFT ||
11551            element == EL_ACID_SPLASH_RIGHT))
11552       {
11553         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11554             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11555             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11556             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11557           Feld[x][y] = EL_AMOEBA_DROP;
11558       }
11559
11560       random = random * 129 + 1;
11561     }
11562   }
11563 #endif
11564
11565 #if 0
11566   if (game.explosions_delayed)
11567 #endif
11568   {
11569     game.explosions_delayed = FALSE;
11570
11571     SCAN_PLAYFIELD(x, y)
11572     {
11573       element = Feld[x][y];
11574
11575       if (ExplodeField[x][y])
11576         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11577       else if (element == EL_EXPLOSION)
11578         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11579
11580       ExplodeField[x][y] = EX_TYPE_NONE;
11581     }
11582
11583     game.explosions_delayed = TRUE;
11584   }
11585
11586   if (game.magic_wall_active)
11587   {
11588     if (!(game.magic_wall_time_left % 4))
11589     {
11590       int element = Feld[magic_wall_x][magic_wall_y];
11591
11592       if (element == EL_BD_MAGIC_WALL_FULL ||
11593           element == EL_BD_MAGIC_WALL_ACTIVE ||
11594           element == EL_BD_MAGIC_WALL_EMPTYING)
11595         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11596       else if (element == EL_DC_MAGIC_WALL_FULL ||
11597                element == EL_DC_MAGIC_WALL_ACTIVE ||
11598                element == EL_DC_MAGIC_WALL_EMPTYING)
11599         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11600       else
11601         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11602     }
11603
11604     if (game.magic_wall_time_left > 0)
11605     {
11606       game.magic_wall_time_left--;
11607       if (!game.magic_wall_time_left)
11608       {
11609         SCAN_PLAYFIELD(x, y)
11610         {
11611           element = Feld[x][y];
11612
11613           if (element == EL_MAGIC_WALL_ACTIVE ||
11614               element == EL_MAGIC_WALL_FULL)
11615           {
11616             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11617             DrawLevelField(x, y);
11618           }
11619           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11620                    element == EL_BD_MAGIC_WALL_FULL)
11621           {
11622             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11623             DrawLevelField(x, y);
11624           }
11625           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11626                    element == EL_DC_MAGIC_WALL_FULL)
11627           {
11628             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11629             DrawLevelField(x, y);
11630           }
11631         }
11632
11633         game.magic_wall_active = FALSE;
11634       }
11635     }
11636   }
11637
11638   if (game.light_time_left > 0)
11639   {
11640     game.light_time_left--;
11641
11642     if (game.light_time_left == 0)
11643       RedrawAllLightSwitchesAndInvisibleElements();
11644   }
11645
11646   if (game.timegate_time_left > 0)
11647   {
11648     game.timegate_time_left--;
11649
11650     if (game.timegate_time_left == 0)
11651       CloseAllOpenTimegates();
11652   }
11653
11654   if (game.lenses_time_left > 0)
11655   {
11656     game.lenses_time_left--;
11657
11658     if (game.lenses_time_left == 0)
11659       RedrawAllInvisibleElementsForLenses();
11660   }
11661
11662   if (game.magnify_time_left > 0)
11663   {
11664     game.magnify_time_left--;
11665
11666     if (game.magnify_time_left == 0)
11667       RedrawAllInvisibleElementsForMagnifier();
11668   }
11669
11670   for (i = 0; i < MAX_PLAYERS; i++)
11671   {
11672     struct PlayerInfo *player = &stored_player[i];
11673
11674     if (SHIELD_ON(player))
11675     {
11676       if (player->shield_deadly_time_left)
11677         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11678       else if (player->shield_normal_time_left)
11679         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11680     }
11681   }
11682
11683   CheckLevelTime();
11684
11685   DrawAllPlayers();
11686   PlayAllPlayersSound();
11687
11688   if (options.debug)                    /* calculate frames per second */
11689   {
11690     static unsigned long fps_counter = 0;
11691     static int fps_frames = 0;
11692     unsigned long fps_delay_ms = Counter() - fps_counter;
11693
11694     fps_frames++;
11695
11696     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11697     {
11698       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11699
11700       fps_frames = 0;
11701       fps_counter = Counter();
11702     }
11703
11704     redraw_mask |= REDRAW_FPS;
11705   }
11706
11707   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11708
11709   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11710   {
11711     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11712
11713     local_player->show_envelope = 0;
11714   }
11715
11716 #if 0
11717   debug_print_timestamp(0, "stop main loop profiling ");
11718   printf("----------------------------------------------------------\n");
11719 #endif
11720
11721   /* use random number generator in every frame to make it less predictable */
11722   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11723     RND(1);
11724 }
11725
11726 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11727 {
11728   int min_x = x, min_y = y, max_x = x, max_y = y;
11729   int i;
11730
11731   for (i = 0; i < MAX_PLAYERS; i++)
11732   {
11733     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11734
11735     if (!stored_player[i].active || &stored_player[i] == player)
11736       continue;
11737
11738     min_x = MIN(min_x, jx);
11739     min_y = MIN(min_y, jy);
11740     max_x = MAX(max_x, jx);
11741     max_y = MAX(max_y, jy);
11742   }
11743
11744   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11745 }
11746
11747 static boolean AllPlayersInVisibleScreen()
11748 {
11749   int i;
11750
11751   for (i = 0; i < MAX_PLAYERS; i++)
11752   {
11753     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11754
11755     if (!stored_player[i].active)
11756       continue;
11757
11758     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11759       return FALSE;
11760   }
11761
11762   return TRUE;
11763 }
11764
11765 void ScrollLevel(int dx, int dy)
11766 {
11767 #if 1
11768   static Bitmap *bitmap_db_field2 = NULL;
11769   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11770   int x, y;
11771 #else
11772   int i, x, y;
11773 #endif
11774
11775 #if 0
11776   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11777   /* only horizontal XOR vertical scroll direction allowed */
11778   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11779     return;
11780 #endif
11781
11782 #if 1
11783   if (bitmap_db_field2 == NULL)
11784     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11785
11786   /* needed when blitting directly to same bitmap -- should not be needed with
11787      recent SDL libraries, but apparently does not work in 1.2.11 directly */
11788   BlitBitmap(drawto_field, bitmap_db_field2,
11789              FX + TILEX * (dx == -1) - softscroll_offset,
11790              FY + TILEY * (dy == -1) - softscroll_offset,
11791              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11792              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11793              FX + TILEX * (dx == 1) - softscroll_offset,
11794              FY + TILEY * (dy == 1) - softscroll_offset);
11795   BlitBitmap(bitmap_db_field2, drawto_field,
11796              FX + TILEX * (dx == 1) - softscroll_offset,
11797              FY + TILEY * (dy == 1) - softscroll_offset,
11798              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11799              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11800              FX + TILEX * (dx == 1) - softscroll_offset,
11801              FY + TILEY * (dy == 1) - softscroll_offset);
11802
11803 #else
11804
11805 #if 1
11806   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11807   int xsize = (BX2 - BX1 + 1);
11808   int ysize = (BY2 - BY1 + 1);
11809   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11810   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11811   int step  = (start < end ? +1 : -1);
11812
11813   for (i = start; i != end; i += step)
11814   {
11815     BlitBitmap(drawto_field, drawto_field,
11816                FX + TILEX * (dx != 0 ? i + step : 0),
11817                FY + TILEY * (dy != 0 ? i + step : 0),
11818                TILEX * (dx != 0 ? 1 : xsize),
11819                TILEY * (dy != 0 ? 1 : ysize),
11820                FX + TILEX * (dx != 0 ? i : 0),
11821                FY + TILEY * (dy != 0 ? i : 0));
11822   }
11823
11824 #else
11825
11826   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11827
11828   BlitBitmap(drawto_field, drawto_field,
11829              FX + TILEX * (dx == -1) - softscroll_offset,
11830              FY + TILEY * (dy == -1) - softscroll_offset,
11831              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11832              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11833              FX + TILEX * (dx == 1) - softscroll_offset,
11834              FY + TILEY * (dy == 1) - softscroll_offset);
11835 #endif
11836 #endif
11837
11838   if (dx != 0)
11839   {
11840     x = (dx == 1 ? BX1 : BX2);
11841     for (y = BY1; y <= BY2; y++)
11842       DrawScreenField(x, y);
11843   }
11844
11845   if (dy != 0)
11846   {
11847     y = (dy == 1 ? BY1 : BY2);
11848     for (x = BX1; x <= BX2; x++)
11849       DrawScreenField(x, y);
11850   }
11851
11852   redraw_mask |= REDRAW_FIELD;
11853 }
11854
11855 static boolean canFallDown(struct PlayerInfo *player)
11856 {
11857   int jx = player->jx, jy = player->jy;
11858
11859   return (IN_LEV_FIELD(jx, jy + 1) &&
11860           (IS_FREE(jx, jy + 1) ||
11861            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11862           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11863           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11864 }
11865
11866 static boolean canPassField(int x, int y, int move_dir)
11867 {
11868   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11869   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11870   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11871   int nextx = x + dx;
11872   int nexty = y + dy;
11873   int element = Feld[x][y];
11874
11875   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11876           !CAN_MOVE(element) &&
11877           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11878           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11879           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11880 }
11881
11882 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11883 {
11884   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11885   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11886   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11887   int newx = x + dx;
11888   int newy = y + dy;
11889
11890   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11891           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11892           (IS_DIGGABLE(Feld[newx][newy]) ||
11893            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11894            canPassField(newx, newy, move_dir)));
11895 }
11896
11897 static void CheckGravityMovement(struct PlayerInfo *player)
11898 {
11899 #if USE_PLAYER_GRAVITY
11900   if (player->gravity && !player->programmed_action)
11901 #else
11902   if (game.gravity && !player->programmed_action)
11903 #endif
11904   {
11905     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11906     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11907     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11908     int jx = player->jx, jy = player->jy;
11909     boolean player_is_moving_to_valid_field =
11910       (!player_is_snapping &&
11911        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11912         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11913     boolean player_can_fall_down = canFallDown(player);
11914
11915     if (player_can_fall_down &&
11916         !player_is_moving_to_valid_field)
11917       player->programmed_action = MV_DOWN;
11918   }
11919 }
11920
11921 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11922 {
11923   return CheckGravityMovement(player);
11924
11925 #if USE_PLAYER_GRAVITY
11926   if (player->gravity && !player->programmed_action)
11927 #else
11928   if (game.gravity && !player->programmed_action)
11929 #endif
11930   {
11931     int jx = player->jx, jy = player->jy;
11932     boolean field_under_player_is_free =
11933       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11934     boolean player_is_standing_on_valid_field =
11935       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11936        (IS_WALKABLE(Feld[jx][jy]) &&
11937         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11938
11939     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11940       player->programmed_action = MV_DOWN;
11941   }
11942 }
11943
11944 /*
11945   MovePlayerOneStep()
11946   -----------------------------------------------------------------------------
11947   dx, dy:               direction (non-diagonal) to try to move the player to
11948   real_dx, real_dy:     direction as read from input device (can be diagonal)
11949 */
11950
11951 boolean MovePlayerOneStep(struct PlayerInfo *player,
11952                           int dx, int dy, int real_dx, int real_dy)
11953 {
11954   int jx = player->jx, jy = player->jy;
11955   int new_jx = jx + dx, new_jy = jy + dy;
11956 #if !USE_FIXED_DONT_RUN_INTO
11957   int element;
11958 #endif
11959   int can_move;
11960   boolean player_can_move = !player->cannot_move;
11961
11962   if (!player->active || (!dx && !dy))
11963     return MP_NO_ACTION;
11964
11965   player->MovDir = (dx < 0 ? MV_LEFT :
11966                     dx > 0 ? MV_RIGHT :
11967                     dy < 0 ? MV_UP :
11968                     dy > 0 ? MV_DOWN :  MV_NONE);
11969
11970   if (!IN_LEV_FIELD(new_jx, new_jy))
11971     return MP_NO_ACTION;
11972
11973   if (!player_can_move)
11974   {
11975     if (player->MovPos == 0)
11976     {
11977       player->is_moving = FALSE;
11978       player->is_digging = FALSE;
11979       player->is_collecting = FALSE;
11980       player->is_snapping = FALSE;
11981       player->is_pushing = FALSE;
11982     }
11983   }
11984
11985 #if 1
11986   if (!options.network && game.centered_player_nr == -1 &&
11987       !AllPlayersInSight(player, new_jx, new_jy))
11988     return MP_NO_ACTION;
11989 #else
11990   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
11991     return MP_NO_ACTION;
11992 #endif
11993
11994 #if !USE_FIXED_DONT_RUN_INTO
11995   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
11996
11997   /* (moved to DigField()) */
11998   if (player_can_move && DONT_RUN_INTO(element))
11999   {
12000     if (element == EL_ACID && dx == 0 && dy == 1)
12001     {
12002       SplashAcid(new_jx, new_jy);
12003       Feld[jx][jy] = EL_PLAYER_1;
12004       InitMovingField(jx, jy, MV_DOWN);
12005       Store[jx][jy] = EL_ACID;
12006       ContinueMoving(jx, jy);
12007       BuryPlayer(player);
12008     }
12009     else
12010       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12011
12012     return MP_MOVING;
12013   }
12014 #endif
12015
12016   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12017   if (can_move != MP_MOVING)
12018     return can_move;
12019
12020   /* check if DigField() has caused relocation of the player */
12021   if (player->jx != jx || player->jy != jy)
12022     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12023
12024   StorePlayer[jx][jy] = 0;
12025   player->last_jx = jx;
12026   player->last_jy = jy;
12027   player->jx = new_jx;
12028   player->jy = new_jy;
12029   StorePlayer[new_jx][new_jy] = player->element_nr;
12030
12031   if (player->move_delay_value_next != -1)
12032   {
12033     player->move_delay_value = player->move_delay_value_next;
12034     player->move_delay_value_next = -1;
12035   }
12036
12037   player->MovPos =
12038     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12039
12040   player->step_counter++;
12041
12042   PlayerVisit[jx][jy] = FrameCounter;
12043
12044 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12045   player->is_moving = TRUE;
12046 #endif
12047
12048 #if 1
12049   /* should better be called in MovePlayer(), but this breaks some tapes */
12050   ScrollPlayer(player, SCROLL_INIT);
12051 #endif
12052
12053   return MP_MOVING;
12054 }
12055
12056 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12057 {
12058   int jx = player->jx, jy = player->jy;
12059   int old_jx = jx, old_jy = jy;
12060   int moved = MP_NO_ACTION;
12061
12062   if (!player->active)
12063     return FALSE;
12064
12065   if (!dx && !dy)
12066   {
12067     if (player->MovPos == 0)
12068     {
12069       player->is_moving = FALSE;
12070       player->is_digging = FALSE;
12071       player->is_collecting = FALSE;
12072       player->is_snapping = FALSE;
12073       player->is_pushing = FALSE;
12074     }
12075
12076     return FALSE;
12077   }
12078
12079   if (player->move_delay > 0)
12080     return FALSE;
12081
12082   player->move_delay = -1;              /* set to "uninitialized" value */
12083
12084   /* store if player is automatically moved to next field */
12085   player->is_auto_moving = (player->programmed_action != MV_NONE);
12086
12087   /* remove the last programmed player action */
12088   player->programmed_action = 0;
12089
12090   if (player->MovPos)
12091   {
12092     /* should only happen if pre-1.2 tape recordings are played */
12093     /* this is only for backward compatibility */
12094
12095     int original_move_delay_value = player->move_delay_value;
12096
12097 #if DEBUG
12098     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12099            tape.counter);
12100 #endif
12101
12102     /* scroll remaining steps with finest movement resolution */
12103     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12104
12105     while (player->MovPos)
12106     {
12107       ScrollPlayer(player, SCROLL_GO_ON);
12108       ScrollScreen(NULL, SCROLL_GO_ON);
12109
12110       AdvanceFrameAndPlayerCounters(player->index_nr);
12111
12112       DrawAllPlayers();
12113       BackToFront();
12114     }
12115
12116     player->move_delay_value = original_move_delay_value;
12117   }
12118
12119   player->is_active = FALSE;
12120
12121   if (player->last_move_dir & MV_HORIZONTAL)
12122   {
12123     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12124       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12125   }
12126   else
12127   {
12128     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12129       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12130   }
12131
12132 #if USE_FIXED_BORDER_RUNNING_GFX
12133   if (!moved && !player->is_active)
12134   {
12135     player->is_moving = FALSE;
12136     player->is_digging = FALSE;
12137     player->is_collecting = FALSE;
12138     player->is_snapping = FALSE;
12139     player->is_pushing = FALSE;
12140   }
12141 #endif
12142
12143   jx = player->jx;
12144   jy = player->jy;
12145
12146 #if 1
12147   if (moved & MP_MOVING && !ScreenMovPos &&
12148       (player->index_nr == game.centered_player_nr ||
12149        game.centered_player_nr == -1))
12150 #else
12151   if (moved & MP_MOVING && !ScreenMovPos &&
12152       (player == local_player || !options.network))
12153 #endif
12154   {
12155     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12156     int offset = game.scroll_delay_value;
12157
12158     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12159     {
12160       /* actual player has left the screen -- scroll in that direction */
12161       if (jx != old_jx)         /* player has moved horizontally */
12162         scroll_x += (jx - old_jx);
12163       else                      /* player has moved vertically */
12164         scroll_y += (jy - old_jy);
12165     }
12166     else
12167     {
12168       if (jx != old_jx)         /* player has moved horizontally */
12169       {
12170         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12171             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12172           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12173
12174         /* don't scroll over playfield boundaries */
12175         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12176           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12177
12178         /* don't scroll more than one field at a time */
12179         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12180
12181         /* don't scroll against the player's moving direction */
12182         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12183             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12184           scroll_x = old_scroll_x;
12185       }
12186       else                      /* player has moved vertically */
12187       {
12188         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12189             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12190           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12191
12192         /* don't scroll over playfield boundaries */
12193         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12194           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12195
12196         /* don't scroll more than one field at a time */
12197         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12198
12199         /* don't scroll against the player's moving direction */
12200         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12201             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12202           scroll_y = old_scroll_y;
12203       }
12204     }
12205
12206     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12207     {
12208 #if 1
12209       if (!options.network && game.centered_player_nr == -1 &&
12210           !AllPlayersInVisibleScreen())
12211       {
12212         scroll_x = old_scroll_x;
12213         scroll_y = old_scroll_y;
12214       }
12215       else
12216 #else
12217       if (!options.network && !AllPlayersInVisibleScreen())
12218       {
12219         scroll_x = old_scroll_x;
12220         scroll_y = old_scroll_y;
12221       }
12222       else
12223 #endif
12224       {
12225         ScrollScreen(player, SCROLL_INIT);
12226         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12227       }
12228     }
12229   }
12230
12231   player->StepFrame = 0;
12232
12233   if (moved & MP_MOVING)
12234   {
12235     if (old_jx != jx && old_jy == jy)
12236       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12237     else if (old_jx == jx && old_jy != jy)
12238       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12239
12240     DrawLevelField(jx, jy);     /* for "crumbled sand" */
12241
12242     player->last_move_dir = player->MovDir;
12243     player->is_moving = TRUE;
12244     player->is_snapping = FALSE;
12245     player->is_switching = FALSE;
12246     player->is_dropping = FALSE;
12247     player->is_dropping_pressed = FALSE;
12248     player->drop_pressed_delay = 0;
12249
12250 #if 0
12251     /* should better be called here than above, but this breaks some tapes */
12252     ScrollPlayer(player, SCROLL_INIT);
12253 #endif
12254   }
12255   else
12256   {
12257     CheckGravityMovementWhenNotMoving(player);
12258
12259     player->is_moving = FALSE;
12260
12261     /* at this point, the player is allowed to move, but cannot move right now
12262        (e.g. because of something blocking the way) -- ensure that the player
12263        is also allowed to move in the next frame (in old versions before 3.1.1,
12264        the player was forced to wait again for eight frames before next try) */
12265
12266     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12267       player->move_delay = 0;   /* allow direct movement in the next frame */
12268   }
12269
12270   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12271     player->move_delay = player->move_delay_value;
12272
12273   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12274   {
12275     TestIfPlayerTouchesBadThing(jx, jy);
12276     TestIfPlayerTouchesCustomElement(jx, jy);
12277   }
12278
12279   if (!player->active)
12280     RemovePlayer(player);
12281
12282   return moved;
12283 }
12284
12285 void ScrollPlayer(struct PlayerInfo *player, int mode)
12286 {
12287   int jx = player->jx, jy = player->jy;
12288   int last_jx = player->last_jx, last_jy = player->last_jy;
12289   int move_stepsize = TILEX / player->move_delay_value;
12290
12291 #if USE_NEW_PLAYER_SPEED
12292   if (!player->active)
12293     return;
12294
12295   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12296     return;
12297 #else
12298   if (!player->active || player->MovPos == 0)
12299     return;
12300 #endif
12301
12302   if (mode == SCROLL_INIT)
12303   {
12304     player->actual_frame_counter = FrameCounter;
12305     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12306
12307     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12308         Feld[last_jx][last_jy] == EL_EMPTY)
12309     {
12310       int last_field_block_delay = 0;   /* start with no blocking at all */
12311       int block_delay_adjustment = player->block_delay_adjustment;
12312
12313       /* if player blocks last field, add delay for exactly one move */
12314       if (player->block_last_field)
12315       {
12316         last_field_block_delay += player->move_delay_value;
12317
12318         /* when blocking enabled, prevent moving up despite gravity */
12319 #if USE_PLAYER_GRAVITY
12320         if (player->gravity && player->MovDir == MV_UP)
12321           block_delay_adjustment = -1;
12322 #else
12323         if (game.gravity && player->MovDir == MV_UP)
12324           block_delay_adjustment = -1;
12325 #endif
12326       }
12327
12328       /* add block delay adjustment (also possible when not blocking) */
12329       last_field_block_delay += block_delay_adjustment;
12330
12331       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12332       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12333     }
12334
12335 #if USE_NEW_PLAYER_SPEED
12336     if (player->MovPos != 0)    /* player has not yet reached destination */
12337       return;
12338 #else
12339     return;
12340 #endif
12341   }
12342   else if (!FrameReached(&player->actual_frame_counter, 1))
12343     return;
12344
12345 #if USE_NEW_PLAYER_SPEED
12346   if (player->MovPos != 0)
12347   {
12348     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12349     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12350
12351     /* before DrawPlayer() to draw correct player graphic for this case */
12352     if (player->MovPos == 0)
12353       CheckGravityMovement(player);
12354   }
12355 #else
12356   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12357   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12358
12359   /* before DrawPlayer() to draw correct player graphic for this case */
12360   if (player->MovPos == 0)
12361     CheckGravityMovement(player);
12362 #endif
12363
12364   if (player->MovPos == 0)      /* player reached destination field */
12365   {
12366     if (player->move_delay_reset_counter > 0)
12367     {
12368       player->move_delay_reset_counter--;
12369
12370       if (player->move_delay_reset_counter == 0)
12371       {
12372         /* continue with normal speed after quickly moving through gate */
12373         HALVE_PLAYER_SPEED(player);
12374
12375         /* be able to make the next move without delay */
12376         player->move_delay = 0;
12377       }
12378     }
12379
12380     player->last_jx = jx;
12381     player->last_jy = jy;
12382
12383     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12384         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12385         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12386         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12387         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12388         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12389     {
12390       DrawPlayer(player);       /* needed here only to cleanup last field */
12391       RemovePlayer(player);
12392
12393       if (local_player->friends_still_needed == 0 ||
12394           IS_SP_ELEMENT(Feld[jx][jy]))
12395         PlayerWins(player);
12396     }
12397
12398     /* this breaks one level: "machine", level 000 */
12399     {
12400       int move_direction = player->MovDir;
12401       int enter_side = MV_DIR_OPPOSITE(move_direction);
12402       int leave_side = move_direction;
12403       int old_jx = last_jx;
12404       int old_jy = last_jy;
12405       int old_element = Feld[old_jx][old_jy];
12406       int new_element = Feld[jx][jy];
12407
12408       if (IS_CUSTOM_ELEMENT(old_element))
12409         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12410                                    CE_LEFT_BY_PLAYER,
12411                                    player->index_bit, leave_side);
12412
12413       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12414                                           CE_PLAYER_LEAVES_X,
12415                                           player->index_bit, leave_side);
12416
12417       if (IS_CUSTOM_ELEMENT(new_element))
12418         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12419                                    player->index_bit, enter_side);
12420
12421       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12422                                           CE_PLAYER_ENTERS_X,
12423                                           player->index_bit, enter_side);
12424
12425       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12426                                         CE_MOVE_OF_X, move_direction);
12427     }
12428
12429     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12430     {
12431       TestIfPlayerTouchesBadThing(jx, jy);
12432       TestIfPlayerTouchesCustomElement(jx, jy);
12433
12434       /* needed because pushed element has not yet reached its destination,
12435          so it would trigger a change event at its previous field location */
12436       if (!player->is_pushing)
12437         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12438
12439       if (!player->active)
12440         RemovePlayer(player);
12441     }
12442
12443     if (!local_player->LevelSolved && level.use_step_counter)
12444     {
12445       int i;
12446
12447       TimePlayed++;
12448
12449       if (TimeLeft > 0)
12450       {
12451         TimeLeft--;
12452
12453         if (TimeLeft <= 10 && setup.time_limit)
12454           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12455
12456 #if 1
12457         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
12458
12459         DisplayGameControlValues();
12460 #else
12461         DrawGameValue_Time(TimeLeft);
12462 #endif
12463
12464         if (!TimeLeft && setup.time_limit)
12465           for (i = 0; i < MAX_PLAYERS; i++)
12466             KillPlayer(&stored_player[i]);
12467       }
12468 #if 1
12469       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12470       {
12471         game_control_value[GAME_CONTROL_TIME] = TimePlayed;
12472
12473         DisplayGameControlValues();
12474       }
12475 #else
12476       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12477         DrawGameValue_Time(TimePlayed);
12478 #endif
12479     }
12480
12481     if (tape.single_step && tape.recording && !tape.pausing &&
12482         !player->programmed_action)
12483       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12484   }
12485 }
12486
12487 void ScrollScreen(struct PlayerInfo *player, int mode)
12488 {
12489   static unsigned long screen_frame_counter = 0;
12490
12491   if (mode == SCROLL_INIT)
12492   {
12493     /* set scrolling step size according to actual player's moving speed */
12494     ScrollStepSize = TILEX / player->move_delay_value;
12495
12496     screen_frame_counter = FrameCounter;
12497     ScreenMovDir = player->MovDir;
12498     ScreenMovPos = player->MovPos;
12499     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12500     return;
12501   }
12502   else if (!FrameReached(&screen_frame_counter, 1))
12503     return;
12504
12505   if (ScreenMovPos)
12506   {
12507     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12508     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12509     redraw_mask |= REDRAW_FIELD;
12510   }
12511   else
12512     ScreenMovDir = MV_NONE;
12513 }
12514
12515 void TestIfPlayerTouchesCustomElement(int x, int y)
12516 {
12517   static int xy[4][2] =
12518   {
12519     { 0, -1 },
12520     { -1, 0 },
12521     { +1, 0 },
12522     { 0, +1 }
12523   };
12524   static int trigger_sides[4][2] =
12525   {
12526     /* center side       border side */
12527     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12528     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12529     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12530     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12531   };
12532   static int touch_dir[4] =
12533   {
12534     MV_LEFT | MV_RIGHT,
12535     MV_UP   | MV_DOWN,
12536     MV_UP   | MV_DOWN,
12537     MV_LEFT | MV_RIGHT
12538   };
12539   int center_element = Feld[x][y];      /* should always be non-moving! */
12540   int i;
12541
12542   for (i = 0; i < NUM_DIRECTIONS; i++)
12543   {
12544     int xx = x + xy[i][0];
12545     int yy = y + xy[i][1];
12546     int center_side = trigger_sides[i][0];
12547     int border_side = trigger_sides[i][1];
12548     int border_element;
12549
12550     if (!IN_LEV_FIELD(xx, yy))
12551       continue;
12552
12553     if (IS_PLAYER(x, y))
12554     {
12555       struct PlayerInfo *player = PLAYERINFO(x, y);
12556
12557       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12558         border_element = Feld[xx][yy];          /* may be moving! */
12559       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12560         border_element = Feld[xx][yy];
12561       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12562         border_element = MovingOrBlocked2Element(xx, yy);
12563       else
12564         continue;               /* center and border element do not touch */
12565
12566       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12567                                  player->index_bit, border_side);
12568       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12569                                           CE_PLAYER_TOUCHES_X,
12570                                           player->index_bit, border_side);
12571     }
12572     else if (IS_PLAYER(xx, yy))
12573     {
12574       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12575
12576       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12577       {
12578         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12579           continue;             /* center and border element do not touch */
12580       }
12581
12582       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12583                                  player->index_bit, center_side);
12584       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12585                                           CE_PLAYER_TOUCHES_X,
12586                                           player->index_bit, center_side);
12587       break;
12588     }
12589   }
12590 }
12591
12592 #if USE_ELEMENT_TOUCHING_BUGFIX
12593
12594 void TestIfElementTouchesCustomElement(int x, int y)
12595 {
12596   static int xy[4][2] =
12597   {
12598     { 0, -1 },
12599     { -1, 0 },
12600     { +1, 0 },
12601     { 0, +1 }
12602   };
12603   static int trigger_sides[4][2] =
12604   {
12605     /* center side      border side */
12606     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12607     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12608     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12609     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12610   };
12611   static int touch_dir[4] =
12612   {
12613     MV_LEFT | MV_RIGHT,
12614     MV_UP   | MV_DOWN,
12615     MV_UP   | MV_DOWN,
12616     MV_LEFT | MV_RIGHT
12617   };
12618   boolean change_center_element = FALSE;
12619   int center_element = Feld[x][y];      /* should always be non-moving! */
12620   int border_element_old[NUM_DIRECTIONS];
12621   int i;
12622
12623   for (i = 0; i < NUM_DIRECTIONS; i++)
12624   {
12625     int xx = x + xy[i][0];
12626     int yy = y + xy[i][1];
12627     int border_element;
12628
12629     border_element_old[i] = -1;
12630
12631     if (!IN_LEV_FIELD(xx, yy))
12632       continue;
12633
12634     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12635       border_element = Feld[xx][yy];    /* may be moving! */
12636     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12637       border_element = Feld[xx][yy];
12638     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12639       border_element = MovingOrBlocked2Element(xx, yy);
12640     else
12641       continue;                 /* center and border element do not touch */
12642
12643     border_element_old[i] = border_element;
12644   }
12645
12646   for (i = 0; i < NUM_DIRECTIONS; i++)
12647   {
12648     int xx = x + xy[i][0];
12649     int yy = y + xy[i][1];
12650     int center_side = trigger_sides[i][0];
12651     int border_element = border_element_old[i];
12652
12653     if (border_element == -1)
12654       continue;
12655
12656     /* check for change of border element */
12657     CheckElementChangeBySide(xx, yy, border_element, center_element,
12658                              CE_TOUCHING_X, center_side);
12659   }
12660
12661   for (i = 0; i < NUM_DIRECTIONS; i++)
12662   {
12663     int border_side = trigger_sides[i][1];
12664     int border_element = border_element_old[i];
12665
12666     if (border_element == -1)
12667       continue;
12668
12669     /* check for change of center element (but change it only once) */
12670     if (!change_center_element)
12671       change_center_element =
12672         CheckElementChangeBySide(x, y, center_element, border_element,
12673                                  CE_TOUCHING_X, border_side);
12674   }
12675 }
12676
12677 #else
12678
12679 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12680 {
12681   static int xy[4][2] =
12682   {
12683     { 0, -1 },
12684     { -1, 0 },
12685     { +1, 0 },
12686     { 0, +1 }
12687   };
12688   static int trigger_sides[4][2] =
12689   {
12690     /* center side      border side */
12691     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12692     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12693     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12694     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12695   };
12696   static int touch_dir[4] =
12697   {
12698     MV_LEFT | MV_RIGHT,
12699     MV_UP   | MV_DOWN,
12700     MV_UP   | MV_DOWN,
12701     MV_LEFT | MV_RIGHT
12702   };
12703   boolean change_center_element = FALSE;
12704   int center_element = Feld[x][y];      /* should always be non-moving! */
12705   int i;
12706
12707   for (i = 0; i < NUM_DIRECTIONS; i++)
12708   {
12709     int xx = x + xy[i][0];
12710     int yy = y + xy[i][1];
12711     int center_side = trigger_sides[i][0];
12712     int border_side = trigger_sides[i][1];
12713     int border_element;
12714
12715     if (!IN_LEV_FIELD(xx, yy))
12716       continue;
12717
12718     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12719       border_element = Feld[xx][yy];    /* may be moving! */
12720     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12721       border_element = Feld[xx][yy];
12722     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12723       border_element = MovingOrBlocked2Element(xx, yy);
12724     else
12725       continue;                 /* center and border element do not touch */
12726
12727     /* check for change of center element (but change it only once) */
12728     if (!change_center_element)
12729       change_center_element =
12730         CheckElementChangeBySide(x, y, center_element, border_element,
12731                                  CE_TOUCHING_X, border_side);
12732
12733     /* check for change of border element */
12734     CheckElementChangeBySide(xx, yy, border_element, center_element,
12735                              CE_TOUCHING_X, center_side);
12736   }
12737 }
12738
12739 #endif
12740
12741 void TestIfElementHitsCustomElement(int x, int y, int direction)
12742 {
12743   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12744   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12745   int hitx = x + dx, hity = y + dy;
12746   int hitting_element = Feld[x][y];
12747   int touched_element;
12748
12749   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12750     return;
12751
12752   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12753                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12754
12755   if (IN_LEV_FIELD(hitx, hity))
12756   {
12757     int opposite_direction = MV_DIR_OPPOSITE(direction);
12758     int hitting_side = direction;
12759     int touched_side = opposite_direction;
12760     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12761                           MovDir[hitx][hity] != direction ||
12762                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12763
12764     object_hit = TRUE;
12765
12766     if (object_hit)
12767     {
12768       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12769                                CE_HITTING_X, touched_side);
12770
12771       CheckElementChangeBySide(hitx, hity, touched_element,
12772                                hitting_element, CE_HIT_BY_X, hitting_side);
12773
12774       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12775                                CE_HIT_BY_SOMETHING, opposite_direction);
12776     }
12777   }
12778
12779   /* "hitting something" is also true when hitting the playfield border */
12780   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12781                            CE_HITTING_SOMETHING, direction);
12782 }
12783
12784 #if 0
12785 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12786 {
12787   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12788   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12789   int hitx = x + dx, hity = y + dy;
12790   int hitting_element = Feld[x][y];
12791   int touched_element;
12792 #if 0
12793   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12794                         !IS_FREE(hitx, hity) &&
12795                         (!IS_MOVING(hitx, hity) ||
12796                          MovDir[hitx][hity] != direction ||
12797                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
12798 #endif
12799
12800   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12801     return;
12802
12803 #if 0
12804   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12805     return;
12806 #endif
12807
12808   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12809                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12810
12811   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12812                            EP_CAN_SMASH_EVERYTHING, direction);
12813
12814   if (IN_LEV_FIELD(hitx, hity))
12815   {
12816     int opposite_direction = MV_DIR_OPPOSITE(direction);
12817     int hitting_side = direction;
12818     int touched_side = opposite_direction;
12819 #if 0
12820     int touched_element = MovingOrBlocked2Element(hitx, hity);
12821 #endif
12822 #if 1
12823     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12824                           MovDir[hitx][hity] != direction ||
12825                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12826
12827     object_hit = TRUE;
12828 #endif
12829
12830     if (object_hit)
12831     {
12832       int i;
12833
12834       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12835                                CE_SMASHED_BY_SOMETHING, opposite_direction);
12836
12837       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12838                                CE_OTHER_IS_SMASHING, touched_side);
12839
12840       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12841                                CE_OTHER_GETS_SMASHED, hitting_side);
12842     }
12843   }
12844 }
12845 #endif
12846
12847 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12848 {
12849   int i, kill_x = -1, kill_y = -1;
12850
12851   int bad_element = -1;
12852   static int test_xy[4][2] =
12853   {
12854     { 0, -1 },
12855     { -1, 0 },
12856     { +1, 0 },
12857     { 0, +1 }
12858   };
12859   static int test_dir[4] =
12860   {
12861     MV_UP,
12862     MV_LEFT,
12863     MV_RIGHT,
12864     MV_DOWN
12865   };
12866
12867   for (i = 0; i < NUM_DIRECTIONS; i++)
12868   {
12869     int test_x, test_y, test_move_dir, test_element;
12870
12871     test_x = good_x + test_xy[i][0];
12872     test_y = good_y + test_xy[i][1];
12873
12874     if (!IN_LEV_FIELD(test_x, test_y))
12875       continue;
12876
12877     test_move_dir =
12878       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12879
12880     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12881
12882     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12883        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12884     */
12885     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12886         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12887     {
12888       kill_x = test_x;
12889       kill_y = test_y;
12890       bad_element = test_element;
12891
12892       break;
12893     }
12894   }
12895
12896   if (kill_x != -1 || kill_y != -1)
12897   {
12898     if (IS_PLAYER(good_x, good_y))
12899     {
12900       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12901
12902       if (player->shield_deadly_time_left > 0 &&
12903           !IS_INDESTRUCTIBLE(bad_element))
12904         Bang(kill_x, kill_y);
12905       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12906         KillPlayer(player);
12907     }
12908     else
12909       Bang(good_x, good_y);
12910   }
12911 }
12912
12913 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12914 {
12915   int i, kill_x = -1, kill_y = -1;
12916   int bad_element = Feld[bad_x][bad_y];
12917   static int test_xy[4][2] =
12918   {
12919     { 0, -1 },
12920     { -1, 0 },
12921     { +1, 0 },
12922     { 0, +1 }
12923   };
12924   static int touch_dir[4] =
12925   {
12926     MV_LEFT | MV_RIGHT,
12927     MV_UP   | MV_DOWN,
12928     MV_UP   | MV_DOWN,
12929     MV_LEFT | MV_RIGHT
12930   };
12931   static int test_dir[4] =
12932   {
12933     MV_UP,
12934     MV_LEFT,
12935     MV_RIGHT,
12936     MV_DOWN
12937   };
12938
12939   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12940     return;
12941
12942   for (i = 0; i < NUM_DIRECTIONS; i++)
12943   {
12944     int test_x, test_y, test_move_dir, test_element;
12945
12946     test_x = bad_x + test_xy[i][0];
12947     test_y = bad_y + test_xy[i][1];
12948     if (!IN_LEV_FIELD(test_x, test_y))
12949       continue;
12950
12951     test_move_dir =
12952       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12953
12954     test_element = Feld[test_x][test_y];
12955
12956     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12957        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12958     */
12959     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12960         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12961     {
12962       /* good thing is player or penguin that does not move away */
12963       if (IS_PLAYER(test_x, test_y))
12964       {
12965         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12966
12967         if (bad_element == EL_ROBOT && player->is_moving)
12968           continue;     /* robot does not kill player if he is moving */
12969
12970         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12971         {
12972           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12973             continue;           /* center and border element do not touch */
12974         }
12975
12976         kill_x = test_x;
12977         kill_y = test_y;
12978         break;
12979       }
12980       else if (test_element == EL_PENGUIN)
12981       {
12982         kill_x = test_x;
12983         kill_y = test_y;
12984         break;
12985       }
12986     }
12987   }
12988
12989   if (kill_x != -1 || kill_y != -1)
12990   {
12991     if (IS_PLAYER(kill_x, kill_y))
12992     {
12993       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12994
12995       if (player->shield_deadly_time_left > 0 &&
12996           !IS_INDESTRUCTIBLE(bad_element))
12997         Bang(bad_x, bad_y);
12998       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12999         KillPlayer(player);
13000     }
13001     else
13002       Bang(kill_x, kill_y);
13003   }
13004 }
13005
13006 void TestIfPlayerTouchesBadThing(int x, int y)
13007 {
13008   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13009 }
13010
13011 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13012 {
13013   TestIfGoodThingHitsBadThing(x, y, move_dir);
13014 }
13015
13016 void TestIfBadThingTouchesPlayer(int x, int y)
13017 {
13018   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13019 }
13020
13021 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13022 {
13023   TestIfBadThingHitsGoodThing(x, y, move_dir);
13024 }
13025
13026 void TestIfFriendTouchesBadThing(int x, int y)
13027 {
13028   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13029 }
13030
13031 void TestIfBadThingTouchesFriend(int x, int y)
13032 {
13033   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13034 }
13035
13036 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13037 {
13038   int i, kill_x = bad_x, kill_y = bad_y;
13039   static int xy[4][2] =
13040   {
13041     { 0, -1 },
13042     { -1, 0 },
13043     { +1, 0 },
13044     { 0, +1 }
13045   };
13046
13047   for (i = 0; i < NUM_DIRECTIONS; i++)
13048   {
13049     int x, y, element;
13050
13051     x = bad_x + xy[i][0];
13052     y = bad_y + xy[i][1];
13053     if (!IN_LEV_FIELD(x, y))
13054       continue;
13055
13056     element = Feld[x][y];
13057     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13058         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13059     {
13060       kill_x = x;
13061       kill_y = y;
13062       break;
13063     }
13064   }
13065
13066   if (kill_x != bad_x || kill_y != bad_y)
13067     Bang(bad_x, bad_y);
13068 }
13069
13070 void KillPlayer(struct PlayerInfo *player)
13071 {
13072   int jx = player->jx, jy = player->jy;
13073
13074   if (!player->active)
13075     return;
13076
13077   /* the following code was introduced to prevent an infinite loop when calling
13078      -> Bang()
13079      -> CheckTriggeredElementChangeExt()
13080      -> ExecuteCustomElementAction()
13081      -> KillPlayer()
13082      -> (infinitely repeating the above sequence of function calls)
13083      which occurs when killing the player while having a CE with the setting
13084      "kill player X when explosion of <player X>"; the solution using a new
13085      field "player->killed" was chosen for backwards compatibility, although
13086      clever use of the fields "player->active" etc. would probably also work */
13087 #if 1
13088   if (player->killed)
13089     return;
13090 #endif
13091
13092   player->killed = TRUE;
13093
13094   /* remove accessible field at the player's position */
13095   Feld[jx][jy] = EL_EMPTY;
13096
13097   /* deactivate shield (else Bang()/Explode() would not work right) */
13098   player->shield_normal_time_left = 0;
13099   player->shield_deadly_time_left = 0;
13100
13101   Bang(jx, jy);
13102   BuryPlayer(player);
13103 }
13104
13105 static void KillPlayerUnlessEnemyProtected(int x, int y)
13106 {
13107   if (!PLAYER_ENEMY_PROTECTED(x, y))
13108     KillPlayer(PLAYERINFO(x, y));
13109 }
13110
13111 static void KillPlayerUnlessExplosionProtected(int x, int y)
13112 {
13113   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13114     KillPlayer(PLAYERINFO(x, y));
13115 }
13116
13117 void BuryPlayer(struct PlayerInfo *player)
13118 {
13119   int jx = player->jx, jy = player->jy;
13120
13121   if (!player->active)
13122     return;
13123
13124   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13125   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13126
13127   player->GameOver = TRUE;
13128   RemovePlayer(player);
13129 }
13130
13131 void RemovePlayer(struct PlayerInfo *player)
13132 {
13133   int jx = player->jx, jy = player->jy;
13134   int i, found = FALSE;
13135
13136   player->present = FALSE;
13137   player->active = FALSE;
13138
13139   if (!ExplodeField[jx][jy])
13140     StorePlayer[jx][jy] = 0;
13141
13142   if (player->is_moving)
13143     DrawLevelField(player->last_jx, player->last_jy);
13144
13145   for (i = 0; i < MAX_PLAYERS; i++)
13146     if (stored_player[i].active)
13147       found = TRUE;
13148
13149   if (!found)
13150     AllPlayersGone = TRUE;
13151
13152   ExitX = ZX = jx;
13153   ExitY = ZY = jy;
13154 }
13155
13156 #if USE_NEW_SNAP_DELAY
13157 static void setFieldForSnapping(int x, int y, int element, int direction)
13158 {
13159   struct ElementInfo *ei = &element_info[element];
13160   int direction_bit = MV_DIR_TO_BIT(direction);
13161   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13162   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13163                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13164
13165   Feld[x][y] = EL_ELEMENT_SNAPPING;
13166   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13167
13168   ResetGfxAnimation(x, y);
13169
13170   GfxElement[x][y] = element;
13171   GfxAction[x][y] = action;
13172   GfxDir[x][y] = direction;
13173   GfxFrame[x][y] = -1;
13174 }
13175 #endif
13176
13177 /*
13178   =============================================================================
13179   checkDiagonalPushing()
13180   -----------------------------------------------------------------------------
13181   check if diagonal input device direction results in pushing of object
13182   (by checking if the alternative direction is walkable, diggable, ...)
13183   =============================================================================
13184 */
13185
13186 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13187                                     int x, int y, int real_dx, int real_dy)
13188 {
13189   int jx, jy, dx, dy, xx, yy;
13190
13191   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13192     return TRUE;
13193
13194   /* diagonal direction: check alternative direction */
13195   jx = player->jx;
13196   jy = player->jy;
13197   dx = x - jx;
13198   dy = y - jy;
13199   xx = jx + (dx == 0 ? real_dx : 0);
13200   yy = jy + (dy == 0 ? real_dy : 0);
13201
13202   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13203 }
13204
13205 /*
13206   =============================================================================
13207   DigField()
13208   -----------------------------------------------------------------------------
13209   x, y:                 field next to player (non-diagonal) to try to dig to
13210   real_dx, real_dy:     direction as read from input device (can be diagonal)
13211   =============================================================================
13212 */
13213
13214 int DigField(struct PlayerInfo *player,
13215              int oldx, int oldy, int x, int y,
13216              int real_dx, int real_dy, int mode)
13217 {
13218   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13219   boolean player_was_pushing = player->is_pushing;
13220   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13221   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13222   int jx = oldx, jy = oldy;
13223   int dx = x - jx, dy = y - jy;
13224   int nextx = x + dx, nexty = y + dy;
13225   int move_direction = (dx == -1 ? MV_LEFT  :
13226                         dx == +1 ? MV_RIGHT :
13227                         dy == -1 ? MV_UP    :
13228                         dy == +1 ? MV_DOWN  : MV_NONE);
13229   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13230   int dig_side = MV_DIR_OPPOSITE(move_direction);
13231   int old_element = Feld[jx][jy];
13232 #if USE_FIXED_DONT_RUN_INTO
13233   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13234 #else
13235   int element;
13236 #endif
13237   int collect_count;
13238
13239   if (is_player)                /* function can also be called by EL_PENGUIN */
13240   {
13241     if (player->MovPos == 0)
13242     {
13243       player->is_digging = FALSE;
13244       player->is_collecting = FALSE;
13245     }
13246
13247     if (player->MovPos == 0)    /* last pushing move finished */
13248       player->is_pushing = FALSE;
13249
13250     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13251     {
13252       player->is_switching = FALSE;
13253       player->push_delay = -1;
13254
13255       return MP_NO_ACTION;
13256     }
13257   }
13258
13259 #if !USE_FIXED_DONT_RUN_INTO
13260   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13261     return MP_NO_ACTION;
13262 #endif
13263
13264   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13265     old_element = Back[jx][jy];
13266
13267   /* in case of element dropped at player position, check background */
13268   else if (Back[jx][jy] != EL_EMPTY &&
13269            game.engine_version >= VERSION_IDENT(2,2,0,0))
13270     old_element = Back[jx][jy];
13271
13272   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13273     return MP_NO_ACTION;        /* field has no opening in this direction */
13274
13275   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13276     return MP_NO_ACTION;        /* field has no opening in this direction */
13277
13278 #if USE_FIXED_DONT_RUN_INTO
13279   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13280   {
13281     SplashAcid(x, y);
13282
13283     Feld[jx][jy] = player->artwork_element;
13284     InitMovingField(jx, jy, MV_DOWN);
13285     Store[jx][jy] = EL_ACID;
13286     ContinueMoving(jx, jy);
13287     BuryPlayer(player);
13288
13289     return MP_DONT_RUN_INTO;
13290   }
13291 #endif
13292
13293 #if USE_FIXED_DONT_RUN_INTO
13294   if (player_can_move && DONT_RUN_INTO(element))
13295   {
13296     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13297
13298     return MP_DONT_RUN_INTO;
13299   }
13300 #endif
13301
13302 #if USE_FIXED_DONT_RUN_INTO
13303   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13304     return MP_NO_ACTION;
13305 #endif
13306
13307 #if !USE_FIXED_DONT_RUN_INTO
13308   element = Feld[x][y];
13309 #endif
13310
13311   collect_count = element_info[element].collect_count_initial;
13312
13313   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13314     return MP_NO_ACTION;
13315
13316   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13317     player_can_move = player_can_move_or_snap;
13318
13319   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13320       game.engine_version >= VERSION_IDENT(2,2,0,0))
13321   {
13322     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13323                                player->index_bit, dig_side);
13324     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13325                                         player->index_bit, dig_side);
13326
13327     if (element == EL_DC_LANDMINE)
13328       Bang(x, y);
13329
13330     if (Feld[x][y] != element)          /* field changed by snapping */
13331       return MP_ACTION;
13332
13333     return MP_NO_ACTION;
13334   }
13335
13336 #if USE_PLAYER_GRAVITY
13337   if (player->gravity && is_player && !player->is_auto_moving &&
13338       canFallDown(player) && move_direction != MV_DOWN &&
13339       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13340     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13341 #else
13342   if (game.gravity && is_player && !player->is_auto_moving &&
13343       canFallDown(player) && move_direction != MV_DOWN &&
13344       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13345     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13346 #endif
13347
13348   if (player_can_move &&
13349       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13350   {
13351     int sound_element = SND_ELEMENT(element);
13352     int sound_action = ACTION_WALKING;
13353
13354     if (IS_RND_GATE(element))
13355     {
13356       if (!player->key[RND_GATE_NR(element)])
13357         return MP_NO_ACTION;
13358     }
13359     else if (IS_RND_GATE_GRAY(element))
13360     {
13361       if (!player->key[RND_GATE_GRAY_NR(element)])
13362         return MP_NO_ACTION;
13363     }
13364     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13365     {
13366       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13367         return MP_NO_ACTION;
13368     }
13369     else if (element == EL_EXIT_OPEN ||
13370              element == EL_EM_EXIT_OPEN ||
13371              element == EL_STEEL_EXIT_OPEN ||
13372              element == EL_EM_STEEL_EXIT_OPEN ||
13373              element == EL_SP_EXIT_OPEN ||
13374              element == EL_SP_EXIT_OPENING)
13375     {
13376       sound_action = ACTION_PASSING;    /* player is passing exit */
13377     }
13378     else if (element == EL_EMPTY)
13379     {
13380       sound_action = ACTION_MOVING;             /* nothing to walk on */
13381     }
13382
13383     /* play sound from background or player, whatever is available */
13384     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13385       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13386     else
13387       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13388   }
13389   else if (player_can_move &&
13390            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13391   {
13392     if (!ACCESS_FROM(element, opposite_direction))
13393       return MP_NO_ACTION;      /* field not accessible from this direction */
13394
13395     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13396       return MP_NO_ACTION;
13397
13398     if (IS_EM_GATE(element))
13399     {
13400       if (!player->key[EM_GATE_NR(element)])
13401         return MP_NO_ACTION;
13402     }
13403     else if (IS_EM_GATE_GRAY(element))
13404     {
13405       if (!player->key[EM_GATE_GRAY_NR(element)])
13406         return MP_NO_ACTION;
13407     }
13408     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13409     {
13410       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13411         return MP_NO_ACTION;
13412     }
13413     else if (IS_EMC_GATE(element))
13414     {
13415       if (!player->key[EMC_GATE_NR(element)])
13416         return MP_NO_ACTION;
13417     }
13418     else if (IS_EMC_GATE_GRAY(element))
13419     {
13420       if (!player->key[EMC_GATE_GRAY_NR(element)])
13421         return MP_NO_ACTION;
13422     }
13423     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13424     {
13425       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13426         return MP_NO_ACTION;
13427     }
13428     else if (element == EL_DC_GATE_WHITE ||
13429              element == EL_DC_GATE_WHITE_GRAY ||
13430              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13431     {
13432       if (player->num_white_keys == 0)
13433         return MP_NO_ACTION;
13434
13435       player->num_white_keys--;
13436     }
13437     else if (IS_SP_PORT(element))
13438     {
13439       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13440           element == EL_SP_GRAVITY_PORT_RIGHT ||
13441           element == EL_SP_GRAVITY_PORT_UP ||
13442           element == EL_SP_GRAVITY_PORT_DOWN)
13443 #if USE_PLAYER_GRAVITY
13444         player->gravity = !player->gravity;
13445 #else
13446         game.gravity = !game.gravity;
13447 #endif
13448       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13449                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13450                element == EL_SP_GRAVITY_ON_PORT_UP ||
13451                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13452 #if USE_PLAYER_GRAVITY
13453         player->gravity = TRUE;
13454 #else
13455         game.gravity = TRUE;
13456 #endif
13457       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13458                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13459                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13460                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13461 #if USE_PLAYER_GRAVITY
13462         player->gravity = FALSE;
13463 #else
13464         game.gravity = FALSE;
13465 #endif
13466     }
13467
13468     /* automatically move to the next field with double speed */
13469     player->programmed_action = move_direction;
13470
13471     if (player->move_delay_reset_counter == 0)
13472     {
13473       player->move_delay_reset_counter = 2;     /* two double speed steps */
13474
13475       DOUBLE_PLAYER_SPEED(player);
13476     }
13477
13478     PlayLevelSoundAction(x, y, ACTION_PASSING);
13479   }
13480   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13481   {
13482     RemoveField(x, y);
13483
13484     if (mode != DF_SNAP)
13485     {
13486       GfxElement[x][y] = GFX_ELEMENT(element);
13487       player->is_digging = TRUE;
13488     }
13489
13490     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13491
13492     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13493                                         player->index_bit, dig_side);
13494
13495     if (mode == DF_SNAP)
13496     {
13497 #if USE_NEW_SNAP_DELAY
13498       if (level.block_snap_field)
13499         setFieldForSnapping(x, y, element, move_direction);
13500       else
13501         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13502 #else
13503       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13504 #endif
13505
13506       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13507                                           player->index_bit, dig_side);
13508     }
13509   }
13510   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13511   {
13512     RemoveField(x, y);
13513
13514     if (is_player && mode != DF_SNAP)
13515     {
13516       GfxElement[x][y] = element;
13517       player->is_collecting = TRUE;
13518     }
13519
13520     if (element == EL_SPEED_PILL)
13521     {
13522       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13523     }
13524     else if (element == EL_EXTRA_TIME && level.time > 0)
13525     {
13526       TimeLeft += level.extra_time;
13527
13528 #if 1
13529       game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13530
13531       DisplayGameControlValues();
13532 #else
13533       DrawGameValue_Time(TimeLeft);
13534 #endif
13535     }
13536     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13537     {
13538       player->shield_normal_time_left += level.shield_normal_time;
13539       if (element == EL_SHIELD_DEADLY)
13540         player->shield_deadly_time_left += level.shield_deadly_time;
13541     }
13542     else if (element == EL_DYNAMITE ||
13543              element == EL_EM_DYNAMITE ||
13544              element == EL_SP_DISK_RED)
13545     {
13546       if (player->inventory_size < MAX_INVENTORY_SIZE)
13547         player->inventory_element[player->inventory_size++] = element;
13548
13549       DrawGameDoorValues();
13550     }
13551     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13552     {
13553       player->dynabomb_count++;
13554       player->dynabombs_left++;
13555     }
13556     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13557     {
13558       player->dynabomb_size++;
13559     }
13560     else if (element == EL_DYNABOMB_INCREASE_POWER)
13561     {
13562       player->dynabomb_xl = TRUE;
13563     }
13564     else if (IS_KEY(element))
13565     {
13566       player->key[KEY_NR(element)] = TRUE;
13567
13568       DrawGameDoorValues();
13569     }
13570     else if (element == EL_DC_KEY_WHITE)
13571     {
13572       player->num_white_keys++;
13573
13574       /* display white keys? */
13575       /* DrawGameDoorValues(); */
13576     }
13577     else if (IS_ENVELOPE(element))
13578     {
13579       player->show_envelope = element;
13580     }
13581     else if (element == EL_EMC_LENSES)
13582     {
13583       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13584
13585       RedrawAllInvisibleElementsForLenses();
13586     }
13587     else if (element == EL_EMC_MAGNIFIER)
13588     {
13589       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13590
13591       RedrawAllInvisibleElementsForMagnifier();
13592     }
13593     else if (IS_DROPPABLE(element) ||
13594              IS_THROWABLE(element))     /* can be collected and dropped */
13595     {
13596       int i;
13597
13598       if (collect_count == 0)
13599         player->inventory_infinite_element = element;
13600       else
13601         for (i = 0; i < collect_count; i++)
13602           if (player->inventory_size < MAX_INVENTORY_SIZE)
13603             player->inventory_element[player->inventory_size++] = element;
13604
13605       DrawGameDoorValues();
13606     }
13607     else if (collect_count > 0)
13608     {
13609       local_player->gems_still_needed -= collect_count;
13610       if (local_player->gems_still_needed < 0)
13611         local_player->gems_still_needed = 0;
13612
13613 #if 1
13614       game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
13615
13616       DisplayGameControlValues();
13617 #else
13618       DrawGameValue_Emeralds(local_player->gems_still_needed);
13619 #endif
13620     }
13621
13622     RaiseScoreElement(element);
13623     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13624
13625     if (is_player)
13626       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13627                                           player->index_bit, dig_side);
13628
13629     if (mode == DF_SNAP)
13630     {
13631 #if USE_NEW_SNAP_DELAY
13632       if (level.block_snap_field)
13633         setFieldForSnapping(x, y, element, move_direction);
13634       else
13635         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13636 #else
13637       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13638 #endif
13639
13640       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13641                                           player->index_bit, dig_side);
13642     }
13643   }
13644   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13645   {
13646     if (mode == DF_SNAP && element != EL_BD_ROCK)
13647       return MP_NO_ACTION;
13648
13649     if (CAN_FALL(element) && dy)
13650       return MP_NO_ACTION;
13651
13652     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13653         !(element == EL_SPRING && level.use_spring_bug))
13654       return MP_NO_ACTION;
13655
13656     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13657         ((move_direction & MV_VERTICAL &&
13658           ((element_info[element].move_pattern & MV_LEFT &&
13659             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13660            (element_info[element].move_pattern & MV_RIGHT &&
13661             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13662          (move_direction & MV_HORIZONTAL &&
13663           ((element_info[element].move_pattern & MV_UP &&
13664             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13665            (element_info[element].move_pattern & MV_DOWN &&
13666             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13667       return MP_NO_ACTION;
13668
13669     /* do not push elements already moving away faster than player */
13670     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13671         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13672       return MP_NO_ACTION;
13673
13674     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13675     {
13676       if (player->push_delay_value == -1 || !player_was_pushing)
13677         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13678     }
13679     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13680     {
13681       if (player->push_delay_value == -1)
13682         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13683     }
13684     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13685     {
13686       if (!player->is_pushing)
13687         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13688     }
13689
13690     player->is_pushing = TRUE;
13691     player->is_active = TRUE;
13692
13693     if (!(IN_LEV_FIELD(nextx, nexty) &&
13694           (IS_FREE(nextx, nexty) ||
13695            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13696             IS_SB_ELEMENT(element)))))
13697       return MP_NO_ACTION;
13698
13699     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13700       return MP_NO_ACTION;
13701
13702     if (player->push_delay == -1)       /* new pushing; restart delay */
13703       player->push_delay = 0;
13704
13705     if (player->push_delay < player->push_delay_value &&
13706         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13707         element != EL_SPRING && element != EL_BALLOON)
13708     {
13709       /* make sure that there is no move delay before next try to push */
13710       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13711         player->move_delay = 0;
13712
13713       return MP_NO_ACTION;
13714     }
13715
13716     if (IS_SB_ELEMENT(element))
13717     {
13718       if (element == EL_SOKOBAN_FIELD_FULL)
13719       {
13720         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13721         local_player->sokobanfields_still_needed++;
13722       }
13723
13724       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13725       {
13726         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13727         local_player->sokobanfields_still_needed--;
13728       }
13729
13730       Feld[x][y] = EL_SOKOBAN_OBJECT;
13731
13732       if (Back[x][y] == Back[nextx][nexty])
13733         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13734       else if (Back[x][y] != 0)
13735         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13736                                     ACTION_EMPTYING);
13737       else
13738         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13739                                     ACTION_FILLING);
13740
13741       if (local_player->sokobanfields_still_needed == 0 &&
13742           game.emulation == EMU_SOKOBAN)
13743       {
13744         PlayerWins(player);
13745
13746         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13747       }
13748     }
13749     else
13750       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13751
13752     InitMovingField(x, y, move_direction);
13753     GfxAction[x][y] = ACTION_PUSHING;
13754
13755     if (mode == DF_SNAP)
13756       ContinueMoving(x, y);
13757     else
13758       MovPos[x][y] = (dx != 0 ? dx : dy);
13759
13760     Pushed[x][y] = TRUE;
13761     Pushed[nextx][nexty] = TRUE;
13762
13763     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13764       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13765     else
13766       player->push_delay_value = -1;    /* get new value later */
13767
13768     /* check for element change _after_ element has been pushed */
13769     if (game.use_change_when_pushing_bug)
13770     {
13771       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13772                                  player->index_bit, dig_side);
13773       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13774                                           player->index_bit, dig_side);
13775     }
13776   }
13777   else if (IS_SWITCHABLE(element))
13778   {
13779     if (PLAYER_SWITCHING(player, x, y))
13780     {
13781       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13782                                           player->index_bit, dig_side);
13783
13784       return MP_ACTION;
13785     }
13786
13787     player->is_switching = TRUE;
13788     player->switch_x = x;
13789     player->switch_y = y;
13790
13791     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13792
13793     if (element == EL_ROBOT_WHEEL)
13794     {
13795       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13796       ZX = x;
13797       ZY = y;
13798
13799       DrawLevelField(x, y);
13800     }
13801     else if (element == EL_SP_TERMINAL)
13802     {
13803       int xx, yy;
13804
13805       SCAN_PLAYFIELD(xx, yy)
13806       {
13807         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13808           Bang(xx, yy);
13809         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13810           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13811       }
13812     }
13813     else if (IS_BELT_SWITCH(element))
13814     {
13815       ToggleBeltSwitch(x, y);
13816     }
13817     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13818              element == EL_SWITCHGATE_SWITCH_DOWN ||
13819              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13820              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13821     {
13822       ToggleSwitchgateSwitch(x, y);
13823     }
13824     else if (element == EL_LIGHT_SWITCH ||
13825              element == EL_LIGHT_SWITCH_ACTIVE)
13826     {
13827       ToggleLightSwitch(x, y);
13828     }
13829     else if (element == EL_TIMEGATE_SWITCH ||
13830              element == EL_DC_TIMEGATE_SWITCH)
13831     {
13832       ActivateTimegateSwitch(x, y);
13833     }
13834     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13835              element == EL_BALLOON_SWITCH_RIGHT ||
13836              element == EL_BALLOON_SWITCH_UP    ||
13837              element == EL_BALLOON_SWITCH_DOWN  ||
13838              element == EL_BALLOON_SWITCH_NONE  ||
13839              element == EL_BALLOON_SWITCH_ANY)
13840     {
13841       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13842                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13843                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13844                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13845                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13846                              move_direction);
13847     }
13848     else if (element == EL_LAMP)
13849     {
13850       Feld[x][y] = EL_LAMP_ACTIVE;
13851       local_player->lights_still_needed--;
13852
13853       ResetGfxAnimation(x, y);
13854       DrawLevelField(x, y);
13855     }
13856     else if (element == EL_TIME_ORB_FULL)
13857     {
13858       Feld[x][y] = EL_TIME_ORB_EMPTY;
13859
13860       if (level.time > 0 || level.use_time_orb_bug)
13861       {
13862         TimeLeft += level.time_orb_time;
13863
13864 #if 1
13865         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13866
13867         DisplayGameControlValues();
13868 #else
13869         DrawGameValue_Time(TimeLeft);
13870 #endif
13871       }
13872
13873       ResetGfxAnimation(x, y);
13874       DrawLevelField(x, y);
13875     }
13876     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13877              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13878     {
13879       int xx, yy;
13880
13881       game.ball_state = !game.ball_state;
13882
13883       SCAN_PLAYFIELD(xx, yy)
13884       {
13885         int e = Feld[xx][yy];
13886
13887         if (game.ball_state)
13888         {
13889           if (e == EL_EMC_MAGIC_BALL)
13890             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13891           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13892             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13893         }
13894         else
13895         {
13896           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13897             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13898           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13899             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13900         }
13901       }
13902     }
13903
13904     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13905                                         player->index_bit, dig_side);
13906
13907     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13908                                         player->index_bit, dig_side);
13909
13910     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13911                                         player->index_bit, dig_side);
13912
13913     return MP_ACTION;
13914   }
13915   else
13916   {
13917     if (!PLAYER_SWITCHING(player, x, y))
13918     {
13919       player->is_switching = TRUE;
13920       player->switch_x = x;
13921       player->switch_y = y;
13922
13923       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13924                                  player->index_bit, dig_side);
13925       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13926                                           player->index_bit, dig_side);
13927
13928       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13929                                  player->index_bit, dig_side);
13930       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13931                                           player->index_bit, dig_side);
13932     }
13933
13934     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13935                                player->index_bit, dig_side);
13936     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13937                                         player->index_bit, dig_side);
13938
13939     return MP_NO_ACTION;
13940   }
13941
13942   player->push_delay = -1;
13943
13944   if (is_player)                /* function can also be called by EL_PENGUIN */
13945   {
13946     if (Feld[x][y] != element)          /* really digged/collected something */
13947     {
13948       player->is_collecting = !player->is_digging;
13949       player->is_active = TRUE;
13950     }
13951   }
13952
13953   return MP_MOVING;
13954 }
13955
13956 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13957 {
13958   int jx = player->jx, jy = player->jy;
13959   int x = jx + dx, y = jy + dy;
13960   int snap_direction = (dx == -1 ? MV_LEFT  :
13961                         dx == +1 ? MV_RIGHT :
13962                         dy == -1 ? MV_UP    :
13963                         dy == +1 ? MV_DOWN  : MV_NONE);
13964   boolean can_continue_snapping = (level.continuous_snapping &&
13965                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13966
13967   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13968     return FALSE;
13969
13970   if (!player->active || !IN_LEV_FIELD(x, y))
13971     return FALSE;
13972
13973   if (dx && dy)
13974     return FALSE;
13975
13976   if (!dx && !dy)
13977   {
13978     if (player->MovPos == 0)
13979       player->is_pushing = FALSE;
13980
13981     player->is_snapping = FALSE;
13982
13983     if (player->MovPos == 0)
13984     {
13985       player->is_moving = FALSE;
13986       player->is_digging = FALSE;
13987       player->is_collecting = FALSE;
13988     }
13989
13990     return FALSE;
13991   }
13992
13993 #if USE_NEW_CONTINUOUS_SNAPPING
13994   /* prevent snapping with already pressed snap key when not allowed */
13995   if (player->is_snapping && !can_continue_snapping)
13996     return FALSE;
13997 #else
13998   if (player->is_snapping)
13999     return FALSE;
14000 #endif
14001
14002   player->MovDir = snap_direction;
14003
14004   if (player->MovPos == 0)
14005   {
14006     player->is_moving = FALSE;
14007     player->is_digging = FALSE;
14008     player->is_collecting = FALSE;
14009   }
14010
14011   player->is_dropping = FALSE;
14012   player->is_dropping_pressed = FALSE;
14013   player->drop_pressed_delay = 0;
14014
14015   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14016     return FALSE;
14017
14018   player->is_snapping = TRUE;
14019   player->is_active = TRUE;
14020
14021   if (player->MovPos == 0)
14022   {
14023     player->is_moving = FALSE;
14024     player->is_digging = FALSE;
14025     player->is_collecting = FALSE;
14026   }
14027
14028   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14029     DrawLevelField(player->last_jx, player->last_jy);
14030
14031   DrawLevelField(x, y);
14032
14033   return TRUE;
14034 }
14035
14036 boolean DropElement(struct PlayerInfo *player)
14037 {
14038   int old_element, new_element;
14039   int dropx = player->jx, dropy = player->jy;
14040   int drop_direction = player->MovDir;
14041   int drop_side = drop_direction;
14042   int drop_element = (player->inventory_size > 0 ?
14043                       player->inventory_element[player->inventory_size - 1] :
14044                       player->inventory_infinite_element != EL_UNDEFINED ?
14045                       player->inventory_infinite_element :
14046                       player->dynabombs_left > 0 ?
14047                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14048                       EL_UNDEFINED);
14049
14050   player->is_dropping_pressed = TRUE;
14051
14052   /* do not drop an element on top of another element; when holding drop key
14053      pressed without moving, dropped element must move away before the next
14054      element can be dropped (this is especially important if the next element
14055      is dynamite, which can be placed on background for historical reasons) */
14056   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14057     return MP_ACTION;
14058
14059   if (IS_THROWABLE(drop_element))
14060   {
14061     dropx += GET_DX_FROM_DIR(drop_direction);
14062     dropy += GET_DY_FROM_DIR(drop_direction);
14063
14064     if (!IN_LEV_FIELD(dropx, dropy))
14065       return FALSE;
14066   }
14067
14068   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14069   new_element = drop_element;           /* default: no change when dropping */
14070
14071   /* check if player is active, not moving and ready to drop */
14072   if (!player->active || player->MovPos || player->drop_delay > 0)
14073     return FALSE;
14074
14075   /* check if player has anything that can be dropped */
14076   if (new_element == EL_UNDEFINED)
14077     return FALSE;
14078
14079   /* check if drop key was pressed long enough for EM style dynamite */
14080   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14081     return FALSE;
14082
14083   /* check if anything can be dropped at the current position */
14084   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14085     return FALSE;
14086
14087   /* collected custom elements can only be dropped on empty fields */
14088   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14089     return FALSE;
14090
14091   if (old_element != EL_EMPTY)
14092     Back[dropx][dropy] = old_element;   /* store old element on this field */
14093
14094   ResetGfxAnimation(dropx, dropy);
14095   ResetRandomAnimationValue(dropx, dropy);
14096
14097   if (player->inventory_size > 0 ||
14098       player->inventory_infinite_element != EL_UNDEFINED)
14099   {
14100     if (player->inventory_size > 0)
14101     {
14102       player->inventory_size--;
14103
14104       DrawGameDoorValues();
14105
14106       if (new_element == EL_DYNAMITE)
14107         new_element = EL_DYNAMITE_ACTIVE;
14108       else if (new_element == EL_EM_DYNAMITE)
14109         new_element = EL_EM_DYNAMITE_ACTIVE;
14110       else if (new_element == EL_SP_DISK_RED)
14111         new_element = EL_SP_DISK_RED_ACTIVE;
14112     }
14113
14114     Feld[dropx][dropy] = new_element;
14115
14116     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14117       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14118                           el2img(Feld[dropx][dropy]), 0);
14119
14120     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14121
14122     /* needed if previous element just changed to "empty" in the last frame */
14123     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14124
14125     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14126                                player->index_bit, drop_side);
14127     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14128                                         CE_PLAYER_DROPS_X,
14129                                         player->index_bit, drop_side);
14130
14131     TestIfElementTouchesCustomElement(dropx, dropy);
14132   }
14133   else          /* player is dropping a dyna bomb */
14134   {
14135     player->dynabombs_left--;
14136
14137     Feld[dropx][dropy] = new_element;
14138
14139     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14140       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14141                           el2img(Feld[dropx][dropy]), 0);
14142
14143     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14144   }
14145
14146   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14147     InitField_WithBug1(dropx, dropy, FALSE);
14148
14149   new_element = Feld[dropx][dropy];     /* element might have changed */
14150
14151   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14152       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14153   {
14154     int move_direction, nextx, nexty;
14155
14156     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14157       MovDir[dropx][dropy] = drop_direction;
14158
14159     move_direction = MovDir[dropx][dropy];
14160     nextx = dropx + GET_DX_FROM_DIR(move_direction);
14161     nexty = dropy + GET_DY_FROM_DIR(move_direction);
14162
14163     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14164
14165 #if USE_FIX_IMPACT_COLLISION
14166     /* do not cause impact style collision by dropping elements that can fall */
14167     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14168 #else
14169     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14170 #endif
14171   }
14172
14173   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14174   player->is_dropping = TRUE;
14175
14176   player->drop_pressed_delay = 0;
14177   player->is_dropping_pressed = FALSE;
14178
14179   player->drop_x = dropx;
14180   player->drop_y = dropy;
14181
14182   return TRUE;
14183 }
14184
14185 /* ------------------------------------------------------------------------- */
14186 /* game sound playing functions                                              */
14187 /* ------------------------------------------------------------------------- */
14188
14189 static int *loop_sound_frame = NULL;
14190 static int *loop_sound_volume = NULL;
14191
14192 void InitPlayLevelSound()
14193 {
14194   int num_sounds = getSoundListSize();
14195
14196   checked_free(loop_sound_frame);
14197   checked_free(loop_sound_volume);
14198
14199   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14200   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14201 }
14202
14203 static void PlayLevelSound(int x, int y, int nr)
14204 {
14205   int sx = SCREENX(x), sy = SCREENY(y);
14206   int volume, stereo_position;
14207   int max_distance = 8;
14208   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14209
14210   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14211       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14212     return;
14213
14214   if (!IN_LEV_FIELD(x, y) ||
14215       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14216       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14217     return;
14218
14219   volume = SOUND_MAX_VOLUME;
14220
14221   if (!IN_SCR_FIELD(sx, sy))
14222   {
14223     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14224     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14225
14226     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14227   }
14228
14229   stereo_position = (SOUND_MAX_LEFT +
14230                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14231                      (SCR_FIELDX + 2 * max_distance));
14232
14233   if (IS_LOOP_SOUND(nr))
14234   {
14235     /* This assures that quieter loop sounds do not overwrite louder ones,
14236        while restarting sound volume comparison with each new game frame. */
14237
14238     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14239       return;
14240
14241     loop_sound_volume[nr] = volume;
14242     loop_sound_frame[nr] = FrameCounter;
14243   }
14244
14245   PlaySoundExt(nr, volume, stereo_position, type);
14246 }
14247
14248 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14249 {
14250   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14251                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14252                  y < LEVELY(BY1) ? LEVELY(BY1) :
14253                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14254                  sound_action);
14255 }
14256
14257 static void PlayLevelSoundAction(int x, int y, int action)
14258 {
14259   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14260 }
14261
14262 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14263 {
14264   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14265
14266   if (sound_effect != SND_UNDEFINED)
14267     PlayLevelSound(x, y, sound_effect);
14268 }
14269
14270 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14271                                               int action)
14272 {
14273   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14274
14275   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14276     PlayLevelSound(x, y, sound_effect);
14277 }
14278
14279 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14280 {
14281   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14282
14283   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14284     PlayLevelSound(x, y, sound_effect);
14285 }
14286
14287 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14288 {
14289   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14290
14291   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14292     StopSound(sound_effect);
14293 }
14294
14295 static void PlayLevelMusic()
14296 {
14297   if (levelset.music[level_nr] != MUS_UNDEFINED)
14298     PlayMusic(levelset.music[level_nr]);        /* from config file */
14299   else
14300     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14301 }
14302
14303 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14304 {
14305   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14306   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14307   int x = xx - 1 - offset;
14308   int y = yy - 1 - offset;
14309
14310   switch (sample)
14311   {
14312     case SAMPLE_blank:
14313       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14314       break;
14315
14316     case SAMPLE_roll:
14317       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14318       break;
14319
14320     case SAMPLE_stone:
14321       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14322       break;
14323
14324     case SAMPLE_nut:
14325       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14326       break;
14327
14328     case SAMPLE_crack:
14329       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14330       break;
14331
14332     case SAMPLE_bug:
14333       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14334       break;
14335
14336     case SAMPLE_tank:
14337       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14338       break;
14339
14340     case SAMPLE_android_clone:
14341       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14342       break;
14343
14344     case SAMPLE_android_move:
14345       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14346       break;
14347
14348     case SAMPLE_spring:
14349       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14350       break;
14351
14352     case SAMPLE_slurp:
14353       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14354       break;
14355
14356     case SAMPLE_eater:
14357       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14358       break;
14359
14360     case SAMPLE_eater_eat:
14361       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14362       break;
14363
14364     case SAMPLE_alien:
14365       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14366       break;
14367
14368     case SAMPLE_collect:
14369       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14370       break;
14371
14372     case SAMPLE_diamond:
14373       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14374       break;
14375
14376     case SAMPLE_squash:
14377       /* !!! CHECK THIS !!! */
14378 #if 1
14379       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14380 #else
14381       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14382 #endif
14383       break;
14384
14385     case SAMPLE_wonderfall:
14386       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14387       break;
14388
14389     case SAMPLE_drip:
14390       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14391       break;
14392
14393     case SAMPLE_push:
14394       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14395       break;
14396
14397     case SAMPLE_dirt:
14398       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14399       break;
14400
14401     case SAMPLE_acid:
14402       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14403       break;
14404
14405     case SAMPLE_ball:
14406       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14407       break;
14408
14409     case SAMPLE_grow:
14410       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14411       break;
14412
14413     case SAMPLE_wonder:
14414       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14415       break;
14416
14417     case SAMPLE_door:
14418       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14419       break;
14420
14421     case SAMPLE_exit_open:
14422       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14423       break;
14424
14425     case SAMPLE_exit_leave:
14426       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14427       break;
14428
14429     case SAMPLE_dynamite:
14430       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14431       break;
14432
14433     case SAMPLE_tick:
14434       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14435       break;
14436
14437     case SAMPLE_press:
14438       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14439       break;
14440
14441     case SAMPLE_wheel:
14442       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14443       break;
14444
14445     case SAMPLE_boom:
14446       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14447       break;
14448
14449     case SAMPLE_die:
14450       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14451       break;
14452
14453     case SAMPLE_time:
14454       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14455       break;
14456
14457     default:
14458       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14459       break;
14460   }
14461 }
14462
14463 #if 0
14464 void ChangeTime(int value)
14465 {
14466   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
14467
14468   *time += value;
14469
14470   /* EMC game engine uses value from time counter of RND game engine */
14471   level.native_em_level->lev->time = *time;
14472
14473   DrawGameValue_Time(*time);
14474 }
14475
14476 void RaiseScore(int value)
14477 {
14478   /* EMC game engine and RND game engine have separate score counters */
14479   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
14480                 &level.native_em_level->lev->score : &local_player->score);
14481
14482   *score += value;
14483
14484   DrawGameValue_Score(*score);
14485 }
14486 #endif
14487
14488 void RaiseScore(int value)
14489 {
14490   local_player->score += value;
14491
14492 #if 1
14493   game_control_value[GAME_CONTROL_SCORE] = local_player->score;
14494
14495   DisplayGameControlValues();
14496 #else
14497   DrawGameValue_Score(local_player->score);
14498 #endif
14499 }
14500
14501 void RaiseScoreElement(int element)
14502 {
14503   switch (element)
14504   {
14505     case EL_EMERALD:
14506     case EL_BD_DIAMOND:
14507     case EL_EMERALD_YELLOW:
14508     case EL_EMERALD_RED:
14509     case EL_EMERALD_PURPLE:
14510     case EL_SP_INFOTRON:
14511       RaiseScore(level.score[SC_EMERALD]);
14512       break;
14513     case EL_DIAMOND:
14514       RaiseScore(level.score[SC_DIAMOND]);
14515       break;
14516     case EL_CRYSTAL:
14517       RaiseScore(level.score[SC_CRYSTAL]);
14518       break;
14519     case EL_PEARL:
14520       RaiseScore(level.score[SC_PEARL]);
14521       break;
14522     case EL_BUG:
14523     case EL_BD_BUTTERFLY:
14524     case EL_SP_ELECTRON:
14525       RaiseScore(level.score[SC_BUG]);
14526       break;
14527     case EL_SPACESHIP:
14528     case EL_BD_FIREFLY:
14529     case EL_SP_SNIKSNAK:
14530       RaiseScore(level.score[SC_SPACESHIP]);
14531       break;
14532     case EL_YAMYAM:
14533     case EL_DARK_YAMYAM:
14534       RaiseScore(level.score[SC_YAMYAM]);
14535       break;
14536     case EL_ROBOT:
14537       RaiseScore(level.score[SC_ROBOT]);
14538       break;
14539     case EL_PACMAN:
14540       RaiseScore(level.score[SC_PACMAN]);
14541       break;
14542     case EL_NUT:
14543       RaiseScore(level.score[SC_NUT]);
14544       break;
14545     case EL_DYNAMITE:
14546     case EL_EM_DYNAMITE:
14547     case EL_SP_DISK_RED:
14548     case EL_DYNABOMB_INCREASE_NUMBER:
14549     case EL_DYNABOMB_INCREASE_SIZE:
14550     case EL_DYNABOMB_INCREASE_POWER:
14551       RaiseScore(level.score[SC_DYNAMITE]);
14552       break;
14553     case EL_SHIELD_NORMAL:
14554     case EL_SHIELD_DEADLY:
14555       RaiseScore(level.score[SC_SHIELD]);
14556       break;
14557     case EL_EXTRA_TIME:
14558       RaiseScore(level.extra_time_score);
14559       break;
14560     case EL_KEY_1:
14561     case EL_KEY_2:
14562     case EL_KEY_3:
14563     case EL_KEY_4:
14564     case EL_EM_KEY_1:
14565     case EL_EM_KEY_2:
14566     case EL_EM_KEY_3:
14567     case EL_EM_KEY_4:
14568     case EL_EMC_KEY_5:
14569     case EL_EMC_KEY_6:
14570     case EL_EMC_KEY_7:
14571     case EL_EMC_KEY_8:
14572     case EL_DC_KEY_WHITE:
14573       RaiseScore(level.score[SC_KEY]);
14574       break;
14575     default:
14576       RaiseScore(element_info[element].collect_score);
14577       break;
14578   }
14579 }
14580
14581 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14582 {
14583   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14584   {
14585 #if defined(NETWORK_AVALIABLE)
14586     if (options.network)
14587       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14588     else
14589 #endif
14590     {
14591       if (quick_quit)
14592       {
14593 #if 1
14594
14595 #if 1
14596         FadeSkipNextFadeIn();
14597 #else
14598         fading = fading_none;
14599 #endif
14600
14601 #else
14602         OpenDoor(DOOR_CLOSE_1);
14603 #endif
14604
14605         game_status = GAME_MODE_MAIN;
14606
14607 #if 1
14608         DrawAndFadeInMainMenu(REDRAW_FIELD);
14609 #else
14610         DrawMainMenu();
14611 #endif
14612       }
14613       else
14614       {
14615 #if 0
14616         FadeOut(REDRAW_FIELD);
14617 #endif
14618
14619         game_status = GAME_MODE_MAIN;
14620
14621         DrawAndFadeInMainMenu(REDRAW_FIELD);
14622       }
14623     }
14624   }
14625   else          /* continue playing the game */
14626   {
14627     if (tape.playing && tape.deactivate_display)
14628       TapeDeactivateDisplayOff(TRUE);
14629
14630     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14631
14632     if (tape.playing && tape.deactivate_display)
14633       TapeDeactivateDisplayOn();
14634   }
14635 }
14636
14637 void RequestQuitGame(boolean ask_if_really_quit)
14638 {
14639   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14640   boolean skip_request = AllPlayersGone || quick_quit;
14641
14642   RequestQuitGameExt(skip_request, quick_quit,
14643                      "Do you really want to quit the game ?");
14644 }
14645
14646
14647 /* ------------------------------------------------------------------------- */
14648 /* random generator functions                                                */
14649 /* ------------------------------------------------------------------------- */
14650
14651 unsigned int InitEngineRandom_RND(long seed)
14652 {
14653   game.num_random_calls = 0;
14654
14655 #if 0
14656   unsigned int rnd_seed = InitEngineRandom(seed);
14657
14658   printf("::: START RND: %d\n", rnd_seed);
14659
14660   return rnd_seed;
14661 #else
14662
14663   return InitEngineRandom(seed);
14664
14665 #endif
14666
14667 }
14668
14669 unsigned int RND(int max)
14670 {
14671   if (max > 0)
14672   {
14673     game.num_random_calls++;
14674
14675     return GetEngineRandom(max);
14676   }
14677
14678   return 0;
14679 }
14680
14681
14682 /* ------------------------------------------------------------------------- */
14683 /* game engine snapshot handling functions                                   */
14684 /* ------------------------------------------------------------------------- */
14685
14686 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
14687
14688 struct EngineSnapshotInfo
14689 {
14690   /* runtime values for custom element collect score */
14691   int collect_score[NUM_CUSTOM_ELEMENTS];
14692
14693   /* runtime values for group element choice position */
14694   int choice_pos[NUM_GROUP_ELEMENTS];
14695
14696   /* runtime values for belt position animations */
14697   int belt_graphic[4 * NUM_BELT_PARTS];
14698   int belt_anim_mode[4 * NUM_BELT_PARTS];
14699 };
14700
14701 struct EngineSnapshotNodeInfo
14702 {
14703   void *buffer_orig;
14704   void *buffer_copy;
14705   int size;
14706 };
14707
14708 static struct EngineSnapshotInfo engine_snapshot_rnd;
14709 static ListNode *engine_snapshot_list = NULL;
14710 static char *snapshot_level_identifier = NULL;
14711 static int snapshot_level_nr = -1;
14712
14713 void FreeEngineSnapshot()
14714 {
14715   while (engine_snapshot_list != NULL)
14716     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14717                        checked_free);
14718
14719   setString(&snapshot_level_identifier, NULL);
14720   snapshot_level_nr = -1;
14721 }
14722
14723 static void SaveEngineSnapshotValues_RND()
14724 {
14725   static int belt_base_active_element[4] =
14726   {
14727     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14728     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14729     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14730     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14731   };
14732   int i, j;
14733
14734   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14735   {
14736     int element = EL_CUSTOM_START + i;
14737
14738     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14739   }
14740
14741   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14742   {
14743     int element = EL_GROUP_START + i;
14744
14745     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14746   }
14747
14748   for (i = 0; i < 4; i++)
14749   {
14750     for (j = 0; j < NUM_BELT_PARTS; j++)
14751     {
14752       int element = belt_base_active_element[i] + j;
14753       int graphic = el2img(element);
14754       int anim_mode = graphic_info[graphic].anim_mode;
14755
14756       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14757       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14758     }
14759   }
14760 }
14761
14762 static void LoadEngineSnapshotValues_RND()
14763 {
14764   unsigned long num_random_calls = game.num_random_calls;
14765   int i, j;
14766
14767   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14768   {
14769     int element = EL_CUSTOM_START + i;
14770
14771     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14772   }
14773
14774   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14775   {
14776     int element = EL_GROUP_START + i;
14777
14778     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14779   }
14780
14781   for (i = 0; i < 4; i++)
14782   {
14783     for (j = 0; j < NUM_BELT_PARTS; j++)
14784     {
14785       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14786       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14787
14788       graphic_info[graphic].anim_mode = anim_mode;
14789     }
14790   }
14791
14792   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14793   {
14794     InitRND(tape.random_seed);
14795     for (i = 0; i < num_random_calls; i++)
14796       RND(1);
14797   }
14798
14799   if (game.num_random_calls != num_random_calls)
14800   {
14801     Error(ERR_INFO, "number of random calls out of sync");
14802     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14803     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14804     Error(ERR_EXIT, "this should not happen -- please debug");
14805   }
14806 }
14807
14808 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14809 {
14810   struct EngineSnapshotNodeInfo *bi =
14811     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14812
14813   bi->buffer_orig = buffer;
14814   bi->buffer_copy = checked_malloc(size);
14815   bi->size = size;
14816
14817   memcpy(bi->buffer_copy, buffer, size);
14818
14819   addNodeToList(&engine_snapshot_list, NULL, bi);
14820 }
14821
14822 void SaveEngineSnapshot()
14823 {
14824   FreeEngineSnapshot();         /* free previous snapshot, if needed */
14825
14826   if (level_editor_test_game)   /* do not save snapshots from editor */
14827     return;
14828
14829   /* copy some special values to a structure better suited for the snapshot */
14830
14831   SaveEngineSnapshotValues_RND();
14832   SaveEngineSnapshotValues_EM();
14833
14834   /* save values stored in special snapshot structure */
14835
14836   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14837   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14838
14839   /* save further RND engine values */
14840
14841   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14842   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14843   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14844
14845   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14846   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14847   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14848   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14849
14850   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14851   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14852   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14853   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14854   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14855
14856   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14857   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14858   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14859
14860   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14861
14862   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14863
14864   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14865   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14866
14867   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14868   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14869   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14870   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14871   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14872   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14873   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14874   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14875   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14876   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14877   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14878   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14879   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14880   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14881   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14882   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14883   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
14884   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
14885
14886   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14887   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14888
14889   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14890   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14891   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14892
14893   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14894   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14895
14896   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14897   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14898   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14899   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14900   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14901
14902   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14903   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14904
14905   /* save level identification information */
14906
14907   setString(&snapshot_level_identifier, leveldir_current->identifier);
14908   snapshot_level_nr = level_nr;
14909
14910 #if 0
14911   ListNode *node = engine_snapshot_list;
14912   int num_bytes = 0;
14913
14914   while (node != NULL)
14915   {
14916     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14917
14918     node = node->next;
14919   }
14920
14921   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14922 #endif
14923 }
14924
14925 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
14926 {
14927   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
14928 }
14929
14930 void LoadEngineSnapshot()
14931 {
14932   ListNode *node = engine_snapshot_list;
14933
14934   if (engine_snapshot_list == NULL)
14935     return;
14936
14937   while (node != NULL)
14938   {
14939     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
14940
14941     node = node->next;
14942   }
14943
14944   /* restore special values from snapshot structure */
14945
14946   LoadEngineSnapshotValues_RND();
14947   LoadEngineSnapshotValues_EM();
14948 }
14949
14950 boolean CheckEngineSnapshot()
14951 {
14952   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14953           snapshot_level_nr == level_nr);
14954 }
14955
14956
14957 /* ---------- new game button stuff ---------------------------------------- */
14958
14959 /* graphic position values for game buttons */
14960 #define GAME_BUTTON_XSIZE       30
14961 #define GAME_BUTTON_YSIZE       30
14962 #define GAME_BUTTON_XPOS        5
14963 #define GAME_BUTTON_YPOS        215
14964 #define SOUND_BUTTON_XPOS       5
14965 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
14966
14967 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14968 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14969 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14970 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14971 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14972 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14973
14974 static struct
14975 {
14976   int *x, *y;
14977   int gd_x, gd_y;
14978   int gadget_id;
14979   char *infotext;
14980 } gamebutton_info[NUM_GAME_BUTTONS] =
14981 {
14982 #if 1
14983   {
14984     &game.button.stop.x,        &game.button.stop.y,
14985     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
14986     GAME_CTRL_ID_STOP,
14987     "stop game"
14988   },
14989   {
14990     &game.button.pause.x,       &game.button.pause.y,
14991     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
14992     GAME_CTRL_ID_PAUSE,
14993     "pause game"
14994   },
14995   {
14996     &game.button.play.x,        &game.button.play.y,
14997     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
14998     GAME_CTRL_ID_PLAY,
14999     "play game"
15000   },
15001   {
15002     &game.button.sound_music.x, &game.button.sound_music.y,
15003     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15004     SOUND_CTRL_ID_MUSIC,
15005     "background music on/off"
15006   },
15007   {
15008     &game.button.sound_loops.x, &game.button.sound_loops.y,
15009     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15010     SOUND_CTRL_ID_LOOPS,
15011     "sound loops on/off"
15012   },
15013   {
15014     &game.button.sound_simple.x,&game.button.sound_simple.y,
15015     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15016     SOUND_CTRL_ID_SIMPLE,
15017     "normal sounds on/off"
15018   }
15019 #else
15020   {
15021     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15022     GAME_CTRL_ID_STOP,
15023     "stop game"
15024   },
15025   {
15026     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15027     GAME_CTRL_ID_PAUSE,
15028     "pause game"
15029   },
15030   {
15031     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15032     GAME_CTRL_ID_PLAY,
15033     "play game"
15034   },
15035   {
15036     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15037     SOUND_CTRL_ID_MUSIC,
15038     "background music on/off"
15039   },
15040   {
15041     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15042     SOUND_CTRL_ID_LOOPS,
15043     "sound loops on/off"
15044   },
15045   {
15046     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15047     SOUND_CTRL_ID_SIMPLE,
15048     "normal sounds on/off"
15049   }
15050 #endif
15051 };
15052
15053 void CreateGameButtons()
15054 {
15055   int i;
15056
15057   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15058   {
15059     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15060     struct GadgetInfo *gi;
15061     int button_type;
15062     boolean checked;
15063     unsigned long event_mask;
15064     int x, y;
15065     int gd_xoffset, gd_yoffset;
15066     int gd_x1, gd_x2, gd_y1, gd_y2;
15067     int id = i;
15068
15069     x = DX + *gamebutton_info[i].x;
15070     y = DY + *gamebutton_info[i].y;
15071     gd_xoffset = gamebutton_info[i].gd_x;
15072     gd_yoffset = gamebutton_info[i].gd_y;
15073     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15074     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15075
15076     if (id == GAME_CTRL_ID_STOP ||
15077         id == GAME_CTRL_ID_PAUSE ||
15078         id == GAME_CTRL_ID_PLAY)
15079     {
15080       button_type = GD_TYPE_NORMAL_BUTTON;
15081       checked = FALSE;
15082       event_mask = GD_EVENT_RELEASED;
15083       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15084       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15085     }
15086     else
15087     {
15088       button_type = GD_TYPE_CHECK_BUTTON;
15089       checked =
15090         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15091          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15092          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15093       event_mask = GD_EVENT_PRESSED;
15094       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
15095       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15096     }
15097
15098     gi = CreateGadget(GDI_CUSTOM_ID, id,
15099                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15100 #if 1
15101                       GDI_X, x,
15102                       GDI_Y, y,
15103 #else
15104                       GDI_X, DX + gd_xoffset,
15105                       GDI_Y, DY + gd_yoffset,
15106 #endif
15107                       GDI_WIDTH, GAME_BUTTON_XSIZE,
15108                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
15109                       GDI_TYPE, button_type,
15110                       GDI_STATE, GD_BUTTON_UNPRESSED,
15111                       GDI_CHECKED, checked,
15112                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15113                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15114                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15115                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15116                       GDI_EVENT_MASK, event_mask,
15117                       GDI_CALLBACK_ACTION, HandleGameButtons,
15118                       GDI_END);
15119
15120     if (gi == NULL)
15121       Error(ERR_EXIT, "cannot create gadget");
15122
15123     game_gadget[id] = gi;
15124   }
15125 }
15126
15127 void FreeGameButtons()
15128 {
15129   int i;
15130
15131   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15132     FreeGadget(game_gadget[i]);
15133 }
15134
15135 static void MapGameButtons()
15136 {
15137   int i;
15138
15139   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15140     MapGadget(game_gadget[i]);
15141 }
15142
15143 void UnmapGameButtons()
15144 {
15145   int i;
15146
15147   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15148     UnmapGadget(game_gadget[i]);
15149 }
15150
15151 static void HandleGameButtons(struct GadgetInfo *gi)
15152 {
15153   int id = gi->custom_id;
15154
15155   if (game_status != GAME_MODE_PLAYING)
15156     return;
15157
15158   switch (id)
15159   {
15160     case GAME_CTRL_ID_STOP:
15161       if (tape.playing)
15162         TapeStop();
15163       else
15164         RequestQuitGame(TRUE);
15165       break;
15166
15167     case GAME_CTRL_ID_PAUSE:
15168       if (options.network)
15169       {
15170 #if defined(NETWORK_AVALIABLE)
15171         if (tape.pausing)
15172           SendToServer_ContinuePlaying();
15173         else
15174           SendToServer_PausePlaying();
15175 #endif
15176       }
15177       else
15178         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15179       break;
15180
15181     case GAME_CTRL_ID_PLAY:
15182       if (tape.pausing)
15183       {
15184 #if defined(NETWORK_AVALIABLE)
15185         if (options.network)
15186           SendToServer_ContinuePlaying();
15187         else
15188 #endif
15189         {
15190           tape.pausing = FALSE;
15191           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15192         }
15193       }
15194       break;
15195
15196     case SOUND_CTRL_ID_MUSIC:
15197       if (setup.sound_music)
15198       { 
15199         setup.sound_music = FALSE;
15200         FadeMusic();
15201       }
15202       else if (audio.music_available)
15203       { 
15204         setup.sound = setup.sound_music = TRUE;
15205
15206         SetAudioMode(setup.sound);
15207
15208         PlayLevelMusic();
15209       }
15210       break;
15211
15212     case SOUND_CTRL_ID_LOOPS:
15213       if (setup.sound_loops)
15214         setup.sound_loops = FALSE;
15215       else if (audio.loops_available)
15216       {
15217         setup.sound = setup.sound_loops = TRUE;
15218         SetAudioMode(setup.sound);
15219       }
15220       break;
15221
15222     case SOUND_CTRL_ID_SIMPLE:
15223       if (setup.sound_simple)
15224         setup.sound_simple = FALSE;
15225       else if (audio.sound_available)
15226       {
15227         setup.sound = setup.sound_simple = TRUE;
15228         SetAudioMode(setup.sound);
15229       }
15230       break;
15231
15232     default:
15233       break;
15234   }
15235 }