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