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