rnd-20070327-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61
62 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
63
64
65 /* for DigField() */
66 #define DF_NO_PUSH              0
67 #define DF_DIG                  1
68 #define DF_SNAP                 2
69
70 /* for MovePlayer() */
71 #define MP_NO_ACTION            0
72 #define MP_MOVING               1
73 #define MP_ACTION               2
74 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
75
76 /* for ScrollPlayer() */
77 #define SCROLL_INIT             0
78 #define SCROLL_GO_ON            1
79
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START          0
82 #define EX_TYPE_NONE            0
83 #define EX_TYPE_NORMAL          (1 << 0)
84 #define EX_TYPE_CENTER          (1 << 1)
85 #define EX_TYPE_BORDER          (1 << 2)
86 #define EX_TYPE_CROSS           (1 << 3)
87 #define EX_TYPE_DYNA            (1 << 4)
88 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
89
90 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
91 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
92 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
93 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
94
95 /* special positions in the game control window (relative to control window) */
96 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
97 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
98 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
99 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
100 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
101 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
102 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
103 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
104 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
105 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
106 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
107 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
108 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
109 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
110 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
111 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
112
113 /* special positions in the game control window (relative to main window) */
114 #define DX_LEVEL1               (DX + XX_LEVEL1)
115 #define DX_LEVEL2               (DX + XX_LEVEL2)
116 #define DX_LEVEL                (DX + XX_LEVEL)
117 #define DY_LEVEL                (DY + YY_LEVEL)
118 #define DX_EMERALDS             (DX + XX_EMERALDS)
119 #define DY_EMERALDS             (DY + YY_EMERALDS)
120 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
121 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
122 #define DX_KEYS                 (DX + XX_KEYS)
123 #define DY_KEYS                 (DY + YY_KEYS)
124 #define DX_SCORE                (DX + XX_SCORE)
125 #define DY_SCORE                (DY + YY_SCORE)
126 #define DX_TIME1                (DX + XX_TIME1)
127 #define DX_TIME2                (DX + XX_TIME2)
128 #define DX_TIME                 (DX + XX_TIME)
129 #define DY_TIME                 (DY + YY_TIME)
130
131 #if 1
132 /* game panel display and control definitions */
133
134 #define GAME_CONTROL_LEVEL_NUMBER               0
135 #define GAME_CONTROL_GEMS                       1
136 #define GAME_CONTROL_INVENTORY                  2
137 #define GAME_CONTROL_KEY_1                      3
138 #define GAME_CONTROL_KEY_2                      4
139 #define GAME_CONTROL_KEY_3                      5
140 #define GAME_CONTROL_KEY_4                      6
141 #define GAME_CONTROL_KEY_5                      7
142 #define GAME_CONTROL_KEY_6                      8
143 #define GAME_CONTROL_KEY_7                      9
144 #define GAME_CONTROL_KEY_8                      10
145 #define GAME_CONTROL_KEY_WHITE                  11
146 #define GAME_CONTROL_KEY_WHITE_COUNT            12
147 #define GAME_CONTROL_SCORE                      13
148 #define GAME_CONTROL_TIME                       14
149 #define GAME_CONTROL_TIME_HH                    15
150 #define GAME_CONTROL_TIME_MM                    16
151 #define GAME_CONTROL_TIME_SS                    17
152 #define GAME_CONTROL_DROP_NEXT_1                18
153 #define GAME_CONTROL_DROP_NEXT_2                19
154 #define GAME_CONTROL_DROP_NEXT_3                20
155 #define GAME_CONTROL_DROP_NEXT_4                21
156 #define GAME_CONTROL_DROP_NEXT_5                22
157 #define GAME_CONTROL_DROP_NEXT_6                23
158 #define GAME_CONTROL_DROP_NEXT_7                24
159 #define GAME_CONTROL_DROP_NEXT_8                25
160 #define GAME_CONTROL_SHIELD_NORMAL              26
161 #define GAME_CONTROL_SHIELD_NORMAL_TIME         27
162 #define GAME_CONTROL_SHIELD_DEADLY              28
163 #define GAME_CONTROL_SHIELD_DEADLY_TIME         29
164 #define GAME_CONTROL_EXIT                       30
165 #define GAME_CONTROL_EM_EXIT                    31
166 #define GAME_CONTROL_SP_EXIT                    32
167 #define GAME_CONTROL_STEEL_EXIT                 33
168 #define GAME_CONTROL_EM_STEEL_EXIT              34
169 #define GAME_CONTROL_EMC_MAGIC_BALL             35
170 #define GAME_CONTROL_EMC_MAGIC_BALL_SWITCH      36
171 #define GAME_CONTROL_EMC_MAGIC_BALL_TIME        37
172 #define GAME_CONTROL_LIGHT_SWITCH               38
173 #define GAME_CONTROL_LIGHT_SWITCH_TIME          39
174 #define GAME_CONTROL_TIMEGATE_SWITCH            40
175 #define GAME_CONTROL_TIMEGATE_SWITCH_TIME       41
176 #define GAME_CONTROL_SWITCHGATE_SWITCH          42
177 #define GAME_CONTROL_EMC_LENSES                 43
178 #define GAME_CONTROL_EMC_LENSES_TIME            44
179 #define GAME_CONTROL_EMC_MAGNIFIER              45
180 #define GAME_CONTROL_EMC_MAGNIFIER_TIME         46
181 #define GAME_CONTROL_BALLOON_SWITCH             47
182 #define GAME_CONTROL_DYNABOMB_NUMBER            48
183 #define GAME_CONTROL_DYNABOMB_SIZE              49
184 #define GAME_CONTROL_DYNABOMB_POWER             50
185 #define GAME_CONTROL_PENGUINS                   51
186 #define GAME_CONTROL_SOKOBAN_OBJECTS            52
187 #define GAME_CONTROL_SOKOBAN_FIELDS             53
188 #define GAME_CONTROL_ROBOT_WHEEL                54
189 #define GAME_CONTROL_CONVEYOR_BELT_1            55
190 #define GAME_CONTROL_CONVEYOR_BELT_1_SWITCH     56
191 #define GAME_CONTROL_CONVEYOR_BELT_2            57
192 #define GAME_CONTROL_CONVEYOR_BELT_2_SWITCH     58
193 #define GAME_CONTROL_CONVEYOR_BELT_3            59
194 #define GAME_CONTROL_CONVEYOR_BELT_3_SWITCH     60
195 #define GAME_CONTROL_CONVEYOR_BELT_4            61
196 #define GAME_CONTROL_CONVEYOR_BELT_4_SWITCH     62
197 #define GAME_CONTROL_MAGIC_WALL                 63
198 #define GAME_CONTROL_MAGIC_WALL_TIME            64
199 #define GAME_CONTROL_BD_MAGIC_WALL              65
200 #define GAME_CONTROL_DC_MAGIC_WALL              66
201 #define GAME_CONTROL_PLAYER_NAME                67
202 #define GAME_CONTROL_LEVEL_NAME                 68
203 #define GAME_CONTROL_LEVEL_AUTHOR               69
204
205 #define NUM_GAME_CONTROLS                       70
206
207 int game_control_value[NUM_GAME_CONTROLS];
208 int last_game_control_value[NUM_GAME_CONTROLS];
209
210 struct GameControlInfo
211 {
212   int nr;
213
214   struct TextPosInfo *pos;
215   int type;
216 };
217
218 static struct GameControlInfo game_controls[] =
219 {
220   {
221     GAME_CONTROL_LEVEL_NUMBER,
222     &game.panel.level_number,
223     TYPE_INTEGER,
224   },
225   {
226     GAME_CONTROL_GEMS,
227     &game.panel.gems,
228     TYPE_INTEGER,
229   },
230   {
231     GAME_CONTROL_INVENTORY,
232     &game.panel.inventory,
233     TYPE_INTEGER,
234   },
235   {
236     GAME_CONTROL_KEY_1,
237     &game.panel.key[0],
238     TYPE_ELEMENT,
239   },
240   {
241     GAME_CONTROL_KEY_2,
242     &game.panel.key[1],
243     TYPE_ELEMENT,
244   },
245   {
246     GAME_CONTROL_KEY_3,
247     &game.panel.key[2],
248     TYPE_ELEMENT,
249   },
250   {
251     GAME_CONTROL_KEY_4,
252     &game.panel.key[3],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_CONTROL_KEY_5,
257     &game.panel.key[4],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_CONTROL_KEY_6,
262     &game.panel.key[5],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_CONTROL_KEY_7,
267     &game.panel.key[6],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_CONTROL_KEY_8,
272     &game.panel.key[7],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_CONTROL_KEY_WHITE,
277     &game.panel.key_white,
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_CONTROL_KEY_WHITE_COUNT,
282     &game.panel.key_white_count,
283     TYPE_INTEGER,
284   },
285   {
286     GAME_CONTROL_SCORE,
287     &game.panel.score,
288     TYPE_INTEGER,
289   },
290   {
291     GAME_CONTROL_TIME,
292     &game.panel.time,
293     TYPE_INTEGER,
294   },
295   {
296     GAME_CONTROL_TIME_HH,
297     &game.panel.time_hh,
298     TYPE_INTEGER,
299   },
300   {
301     GAME_CONTROL_TIME_MM,
302     &game.panel.time_mm,
303     TYPE_INTEGER,
304   },
305   {
306     GAME_CONTROL_TIME_SS,
307     &game.panel.time_ss,
308     TYPE_INTEGER,
309   },
310   {
311     GAME_CONTROL_DROP_NEXT_1,
312     &game.panel.drop_next_1,
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_CONTROL_DROP_NEXT_2,
317     &game.panel.drop_next_2,
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_CONTROL_DROP_NEXT_3,
322     &game.panel.drop_next_3,
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_CONTROL_DROP_NEXT_4,
327     &game.panel.drop_next_4,
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_CONTROL_DROP_NEXT_5,
332     &game.panel.drop_next_5,
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_CONTROL_DROP_NEXT_6,
337     &game.panel.drop_next_6,
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_CONTROL_DROP_NEXT_7,
342     &game.panel.drop_next_7,
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_CONTROL_DROP_NEXT_8,
347     &game.panel.drop_next_8,
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_CONTROL_SHIELD_NORMAL,
352     &game.panel.shield_normal,
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_CONTROL_SHIELD_NORMAL_TIME,
357     &game.panel.shield_normal_time,
358     TYPE_INTEGER,
359   },
360   {
361     GAME_CONTROL_SHIELD_DEADLY,
362     &game.panel.shield_deadly,
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_CONTROL_SHIELD_DEADLY_TIME,
367     &game.panel.shield_deadly_time,
368     TYPE_INTEGER,
369   },
370   {
371     GAME_CONTROL_EXIT,
372     &game.panel.exit,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_CONTROL_EM_EXIT,
377     &game.panel.em_exit,
378     TYPE_ELEMENT,
379   },
380   {
381     GAME_CONTROL_SP_EXIT,
382     &game.panel.sp_exit,
383     TYPE_ELEMENT,
384   },
385   {
386     GAME_CONTROL_STEEL_EXIT,
387     &game.panel.steel_exit,
388     TYPE_ELEMENT,
389   },
390   {
391     GAME_CONTROL_EM_STEEL_EXIT,
392     &game.panel.em_steel_exit,
393     TYPE_ELEMENT,
394   },
395   {
396     GAME_CONTROL_EMC_MAGIC_BALL,
397     &game.panel.emc_magic_ball,
398     TYPE_ELEMENT,
399   },
400   {
401     GAME_CONTROL_EMC_MAGIC_BALL_SWITCH,
402     &game.panel.emc_magic_ball_switch,
403     TYPE_ELEMENT,
404   },
405   {
406     GAME_CONTROL_EMC_MAGIC_BALL_TIME,
407     &game.panel.emc_magic_ball_time,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_CONTROL_LIGHT_SWITCH,
412     &game.panel.light_switch,
413     TYPE_ELEMENT,
414   },
415   {
416     GAME_CONTROL_LIGHT_SWITCH_TIME,
417     &game.panel.light_switch_time,
418     TYPE_INTEGER,
419   },
420   {
421     GAME_CONTROL_TIMEGATE_SWITCH,
422     &game.panel.timegate_switch,
423     TYPE_ELEMENT,
424   },
425   {
426     GAME_CONTROL_TIMEGATE_SWITCH_TIME,
427     &game.panel.timegate_switch_time,
428     TYPE_INTEGER,
429   },
430   {
431     GAME_CONTROL_SWITCHGATE_SWITCH,
432     &game.panel.switchgate_switch,
433     TYPE_ELEMENT,
434   },
435   {
436     GAME_CONTROL_EMC_LENSES,
437     &game.panel.emc_lenses,
438     TYPE_ELEMENT,
439   },
440   {
441     GAME_CONTROL_EMC_LENSES_TIME,
442     &game.panel.emc_lenses_time,
443     TYPE_INTEGER,
444   },
445   {
446     GAME_CONTROL_EMC_MAGNIFIER,
447     &game.panel.emc_magnifier,
448     TYPE_ELEMENT,
449   },
450   {
451     GAME_CONTROL_EMC_MAGNIFIER_TIME,
452     &game.panel.emc_magnifier_time,
453     TYPE_INTEGER,
454   },
455   {
456     GAME_CONTROL_BALLOON_SWITCH,
457     &game.panel.balloon_switch,
458     TYPE_ELEMENT,
459   },
460   {
461     GAME_CONTROL_DYNABOMB_NUMBER,
462     &game.panel.dynabomb_number,
463     TYPE_INTEGER,
464   },
465   {
466     GAME_CONTROL_DYNABOMB_SIZE,
467     &game.panel.dynabomb_size,
468     TYPE_INTEGER,
469   },
470   {
471     GAME_CONTROL_DYNABOMB_POWER,
472     &game.panel.dynabomb_power,
473     TYPE_ELEMENT,
474   },
475   {
476     GAME_CONTROL_PENGUINS,
477     &game.panel.penguins,
478     TYPE_INTEGER,
479   },
480   {
481     GAME_CONTROL_SOKOBAN_OBJECTS,
482     &game.panel.sokoban_objects,
483     TYPE_INTEGER,
484   },
485   {
486     GAME_CONTROL_SOKOBAN_FIELDS,
487     &game.panel.sokoban_fields,
488     TYPE_INTEGER,
489   },
490   {
491     GAME_CONTROL_ROBOT_WHEEL,
492     &game.panel.robot_wheel,
493     TYPE_ELEMENT,
494   },
495   {
496     GAME_CONTROL_CONVEYOR_BELT_1,
497     &game.panel.conveyor_belt_1,
498     TYPE_ELEMENT,
499   },
500   {
501     GAME_CONTROL_CONVEYOR_BELT_1_SWITCH,
502     &game.panel.conveyor_belt_1_switch,
503     TYPE_ELEMENT,
504   },
505   {
506     GAME_CONTROL_CONVEYOR_BELT_2,
507     &game.panel.conveyor_belt_2,
508     TYPE_ELEMENT,
509   },
510   {
511     GAME_CONTROL_CONVEYOR_BELT_2_SWITCH,
512     &game.panel.conveyor_belt_2_switch,
513     TYPE_ELEMENT,
514   },
515   {
516     GAME_CONTROL_CONVEYOR_BELT_3,
517     &game.panel.conveyor_belt_3,
518     TYPE_ELEMENT,
519   },
520   {
521     GAME_CONTROL_CONVEYOR_BELT_3_SWITCH,
522     &game.panel.conveyor_belt_3_switch,
523     TYPE_ELEMENT,
524   },
525   {
526     GAME_CONTROL_CONVEYOR_BELT_4,
527     &game.panel.conveyor_belt_4,
528     TYPE_ELEMENT,
529   },
530   {
531     GAME_CONTROL_CONVEYOR_BELT_4_SWITCH,
532     &game.panel.conveyor_belt_4_switch,
533     TYPE_ELEMENT,
534   },
535   {
536     GAME_CONTROL_MAGIC_WALL,
537     &game.panel.magic_wall,
538     TYPE_ELEMENT,
539   },
540   {
541     GAME_CONTROL_MAGIC_WALL_TIME,
542     &game.panel.magic_wall_time,
543     TYPE_INTEGER,
544   },
545   {
546     GAME_CONTROL_BD_MAGIC_WALL,
547     &game.panel.bd_magic_wall,
548     TYPE_ELEMENT,
549   },
550   {
551     GAME_CONTROL_DC_MAGIC_WALL,
552     &game.panel.dc_magic_wall,
553     TYPE_ELEMENT,
554   },
555   {
556     GAME_CONTROL_PLAYER_NAME,
557     &game.panel.player_name,
558     TYPE_STRING,
559   },
560   {
561     GAME_CONTROL_LEVEL_NAME,
562     &game.panel.level_name,
563     TYPE_STRING,
564   },
565   {
566     GAME_CONTROL_LEVEL_AUTHOR,
567     &game.panel.level_author,
568     TYPE_STRING,
569   },
570
571   {
572     -1,
573     NULL,
574     -1,
575   }
576 };
577 #endif
578
579
580 /* values for delayed check of falling and moving elements and for collision */
581 #define CHECK_DELAY_MOVING      3
582 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
583 #define CHECK_DELAY_COLLISION   2
584 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
585
586 /* values for initial player move delay (initial delay counter value) */
587 #define INITIAL_MOVE_DELAY_OFF  -1
588 #define INITIAL_MOVE_DELAY_ON   0
589
590 /* values for player movement speed (which is in fact a delay value) */
591 #define MOVE_DELAY_MIN_SPEED    32
592 #define MOVE_DELAY_NORMAL_SPEED 8
593 #define MOVE_DELAY_HIGH_SPEED   4
594 #define MOVE_DELAY_MAX_SPEED    1
595
596 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
597 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
598
599 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
600 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
601
602 /* values for other actions */
603 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
604 #define MOVE_STEPSIZE_MIN       (1)
605 #define MOVE_STEPSIZE_MAX       (TILEX)
606
607 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
608 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
609
610 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
611
612 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
613                                  RND(element_info[e].push_delay_random))
614 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
615                                  RND(element_info[e].drop_delay_random))
616 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
617                                  RND(element_info[e].move_delay_random))
618 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
619                                     (element_info[e].move_delay_random))
620 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
621                                  RND(element_info[e].ce_value_random_initial))
622 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
623 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
624                                  RND((c)->delay_random * (c)->delay_frames))
625 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
626                                  RND((c)->delay_random))
627
628
629 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
630          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
631
632 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
633         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
634          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
635          (be) + (e) - EL_SELF)
636
637 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
638         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
639          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
640          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
641          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
642          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
643          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
644          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
645          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
646          (e))
647
648 #define CAN_GROW_INTO(e)                                                \
649         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
650
651 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
652                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
653                                         (condition)))
654
655 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
656                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
657                                         (CAN_MOVE_INTO_ACID(e) &&       \
658                                          Feld[x][y] == EL_ACID) ||      \
659                                         (condition)))
660
661 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
662                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
663                                         (CAN_MOVE_INTO_ACID(e) &&       \
664                                          Feld[x][y] == EL_ACID) ||      \
665                                         (condition)))
666
667 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
668                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
669                                         (condition) ||                  \
670                                         (CAN_MOVE_INTO_ACID(e) &&       \
671                                          Feld[x][y] == EL_ACID) ||      \
672                                         (DONT_COLLIDE_WITH(e) &&        \
673                                          IS_PLAYER(x, y) &&             \
674                                          !PLAYER_ENEMY_PROTECTED(x, y))))
675
676 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
677         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
678
679 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
680         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
681
682 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
683         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
684
685 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
686         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
687                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
688
689 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
690         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
691
692 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
693         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
694
695 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
696         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
697
698 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
699         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
700
701 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
702         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
703
704 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
705         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
706                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
707                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
708                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
709                                                  IS_FOOD_PENGUIN(Feld[x][y])))
710 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
711         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
712
713 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
714         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
715
716 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
717         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
718
719 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
720         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
721                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
722
723 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
724
725 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
726                 (!IS_PLAYER(x, y) &&                                    \
727                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
728
729 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
730         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
731
732 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
733 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
734
735 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
736 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
737 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
738 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
739
740 /* game button identifiers */
741 #define GAME_CTRL_ID_STOP               0
742 #define GAME_CTRL_ID_PAUSE              1
743 #define GAME_CTRL_ID_PLAY               2
744 #define SOUND_CTRL_ID_MUSIC             3
745 #define SOUND_CTRL_ID_LOOPS             4
746 #define SOUND_CTRL_ID_SIMPLE            5
747
748 #define NUM_GAME_BUTTONS                6
749
750
751 /* forward declaration for internal use */
752
753 static void CreateField(int, int, int);
754
755 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
756 static void AdvanceFrameAndPlayerCounters(int);
757
758 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
759 static boolean MovePlayer(struct PlayerInfo *, int, int);
760 static void ScrollPlayer(struct PlayerInfo *, int);
761 static void ScrollScreen(struct PlayerInfo *, int);
762
763 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
764
765 static void InitBeltMovement(void);
766 static void CloseAllOpenTimegates(void);
767 static void CheckGravityMovement(struct PlayerInfo *);
768 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
769 static void KillPlayerUnlessEnemyProtected(int, int);
770 static void KillPlayerUnlessExplosionProtected(int, int);
771
772 static void TestIfPlayerTouchesCustomElement(int, int);
773 static void TestIfElementTouchesCustomElement(int, int);
774 static void TestIfElementHitsCustomElement(int, int, int);
775 #if 0
776 static void TestIfElementSmashesCustomElement(int, int, int);
777 #endif
778
779 static void HandleElementChange(int, int, int);
780 static void ExecuteCustomElementAction(int, int, int, int);
781 static boolean ChangeElement(int, int, int, int);
782
783 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
784 #define CheckTriggeredElementChange(x, y, e, ev)                        \
785         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
786 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
787         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
788 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
789         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
790 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
791         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
792
793 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
794 #define CheckElementChange(x, y, e, te, ev)                             \
795         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
796 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
797         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
798 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
799         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
800
801 static void PlayLevelSound(int, int, int);
802 static void PlayLevelSoundNearest(int, int, int);
803 static void PlayLevelSoundAction(int, int, int);
804 static void PlayLevelSoundElementAction(int, int, int, int);
805 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
806 static void PlayLevelSoundActionIfLoop(int, int, int);
807 static void StopLevelSoundActionIfLoop(int, int, int);
808 static void PlayLevelMusic();
809
810 static void MapGameButtons();
811 static void HandleGameButtons(struct GadgetInfo *);
812
813 int AmoebeNachbarNr(int, int);
814 void AmoebeUmwandeln(int, int);
815 void ContinueMoving(int, int);
816 void Bang(int, int);
817 void InitMovDir(int, int);
818 void InitAmoebaNr(int, int);
819 int NewHiScore(void);
820
821 void TestIfGoodThingHitsBadThing(int, int, int);
822 void TestIfBadThingHitsGoodThing(int, int, int);
823 void TestIfPlayerTouchesBadThing(int, int);
824 void TestIfPlayerRunsIntoBadThing(int, int, int);
825 void TestIfBadThingTouchesPlayer(int, int);
826 void TestIfBadThingRunsIntoPlayer(int, int, int);
827 void TestIfFriendTouchesBadThing(int, int);
828 void TestIfBadThingTouchesFriend(int, int);
829 void TestIfBadThingTouchesOtherBadThing(int, int);
830
831 void KillPlayer(struct PlayerInfo *);
832 void BuryPlayer(struct PlayerInfo *);
833 void RemovePlayer(struct PlayerInfo *);
834
835 boolean SnapField(struct PlayerInfo *, int, int);
836 boolean DropElement(struct PlayerInfo *);
837
838 static int getInvisibleActiveFromInvisibleElement(int);
839 static int getInvisibleFromInvisibleActiveElement(int);
840
841 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
842
843 /* for detection of endless loops, caused by custom element programming */
844 /* (using maximal playfield width x 10 is just a rough approximation) */
845 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
846
847 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
848 {                                                                       \
849   if (recursion_loop_detected)                                          \
850     return (rc);                                                        \
851                                                                         \
852   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
853   {                                                                     \
854     recursion_loop_detected = TRUE;                                     \
855     recursion_loop_element = (e);                                       \
856   }                                                                     \
857                                                                         \
858   recursion_loop_depth++;                                               \
859 }
860
861 #define RECURSION_LOOP_DETECTION_END()                                  \
862 {                                                                       \
863   recursion_loop_depth--;                                               \
864 }
865
866 static int recursion_loop_depth;
867 static boolean recursion_loop_detected;
868 static boolean recursion_loop_element;
869
870
871 /* ------------------------------------------------------------------------- */
872 /* definition of elements that automatically change to other elements after  */
873 /* a specified time, eventually calling a function when changing             */
874 /* ------------------------------------------------------------------------- */
875
876 /* forward declaration for changer functions */
877 static void InitBuggyBase(int, int);
878 static void WarnBuggyBase(int, int);
879
880 static void InitTrap(int, int);
881 static void ActivateTrap(int, int);
882 static void ChangeActiveTrap(int, int);
883
884 static void InitRobotWheel(int, int);
885 static void RunRobotWheel(int, int);
886 static void StopRobotWheel(int, int);
887
888 static void InitTimegateWheel(int, int);
889 static void RunTimegateWheel(int, int);
890
891 static void InitMagicBallDelay(int, int);
892 static void ActivateMagicBall(int, int);
893
894 struct ChangingElementInfo
895 {
896   int element;
897   int target_element;
898   int change_delay;
899   void (*pre_change_function)(int x, int y);
900   void (*change_function)(int x, int y);
901   void (*post_change_function)(int x, int y);
902 };
903
904 static struct ChangingElementInfo change_delay_list[] =
905 {
906   {
907     EL_NUT_BREAKING,
908     EL_EMERALD,
909     6,
910     NULL,
911     NULL,
912     NULL
913   },
914   {
915     EL_PEARL_BREAKING,
916     EL_EMPTY,
917     8,
918     NULL,
919     NULL,
920     NULL
921   },
922   {
923     EL_EXIT_OPENING,
924     EL_EXIT_OPEN,
925     29,
926     NULL,
927     NULL,
928     NULL
929   },
930   {
931     EL_EXIT_CLOSING,
932     EL_EXIT_CLOSED,
933     29,
934     NULL,
935     NULL,
936     NULL
937   },
938   {
939     EL_STEEL_EXIT_OPENING,
940     EL_STEEL_EXIT_OPEN,
941     29,
942     NULL,
943     NULL,
944     NULL
945   },
946   {
947     EL_STEEL_EXIT_CLOSING,
948     EL_STEEL_EXIT_CLOSED,
949     29,
950     NULL,
951     NULL,
952     NULL
953   },
954   {
955     EL_EM_EXIT_OPENING,
956     EL_EM_EXIT_OPEN,
957     29,
958     NULL,
959     NULL,
960     NULL
961   },
962   {
963     EL_EM_EXIT_CLOSING,
964 #if 1
965     EL_EMPTY,
966 #else
967     EL_EM_EXIT_CLOSED,
968 #endif
969     29,
970     NULL,
971     NULL,
972     NULL
973   },
974   {
975     EL_EM_STEEL_EXIT_OPENING,
976     EL_EM_STEEL_EXIT_OPEN,
977     29,
978     NULL,
979     NULL,
980     NULL
981   },
982   {
983     EL_EM_STEEL_EXIT_CLOSING,
984 #if 1
985     EL_STEELWALL,
986 #else
987     EL_EM_STEEL_EXIT_CLOSED,
988 #endif
989     29,
990     NULL,
991     NULL,
992     NULL
993   },
994   {
995     EL_SP_EXIT_OPENING,
996     EL_SP_EXIT_OPEN,
997     29,
998     NULL,
999     NULL,
1000     NULL
1001   },
1002   {
1003     EL_SP_EXIT_CLOSING,
1004     EL_SP_EXIT_CLOSED,
1005     29,
1006     NULL,
1007     NULL,
1008     NULL
1009   },
1010   {
1011     EL_SWITCHGATE_OPENING,
1012     EL_SWITCHGATE_OPEN,
1013     29,
1014     NULL,
1015     NULL,
1016     NULL
1017   },
1018   {
1019     EL_SWITCHGATE_CLOSING,
1020     EL_SWITCHGATE_CLOSED,
1021     29,
1022     NULL,
1023     NULL,
1024     NULL
1025   },
1026   {
1027     EL_TIMEGATE_OPENING,
1028     EL_TIMEGATE_OPEN,
1029     29,
1030     NULL,
1031     NULL,
1032     NULL
1033   },
1034   {
1035     EL_TIMEGATE_CLOSING,
1036     EL_TIMEGATE_CLOSED,
1037     29,
1038     NULL,
1039     NULL,
1040     NULL
1041   },
1042
1043   {
1044     EL_ACID_SPLASH_LEFT,
1045     EL_EMPTY,
1046     8,
1047     NULL,
1048     NULL,
1049     NULL
1050   },
1051   {
1052     EL_ACID_SPLASH_RIGHT,
1053     EL_EMPTY,
1054     8,
1055     NULL,
1056     NULL,
1057     NULL
1058   },
1059   {
1060     EL_SP_BUGGY_BASE,
1061     EL_SP_BUGGY_BASE_ACTIVATING,
1062     0,
1063     InitBuggyBase,
1064     NULL,
1065     NULL
1066   },
1067   {
1068     EL_SP_BUGGY_BASE_ACTIVATING,
1069     EL_SP_BUGGY_BASE_ACTIVE,
1070     0,
1071     InitBuggyBase,
1072     NULL,
1073     NULL
1074   },
1075   {
1076     EL_SP_BUGGY_BASE_ACTIVE,
1077     EL_SP_BUGGY_BASE,
1078     0,
1079     InitBuggyBase,
1080     WarnBuggyBase,
1081     NULL
1082   },
1083   {
1084     EL_TRAP,
1085     EL_TRAP_ACTIVE,
1086     0,
1087     InitTrap,
1088     NULL,
1089     ActivateTrap
1090   },
1091   {
1092     EL_TRAP_ACTIVE,
1093     EL_TRAP,
1094     31,
1095     NULL,
1096     ChangeActiveTrap,
1097     NULL
1098   },
1099   {
1100     EL_ROBOT_WHEEL_ACTIVE,
1101     EL_ROBOT_WHEEL,
1102     0,
1103     InitRobotWheel,
1104     RunRobotWheel,
1105     StopRobotWheel
1106   },
1107   {
1108     EL_TIMEGATE_SWITCH_ACTIVE,
1109     EL_TIMEGATE_SWITCH,
1110     0,
1111     InitTimegateWheel,
1112     RunTimegateWheel,
1113     NULL
1114   },
1115   {
1116     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1117     EL_DC_TIMEGATE_SWITCH,
1118     0,
1119     InitTimegateWheel,
1120     RunTimegateWheel,
1121     NULL
1122   },
1123   {
1124     EL_EMC_MAGIC_BALL_ACTIVE,
1125     EL_EMC_MAGIC_BALL_ACTIVE,
1126     0,
1127     InitMagicBallDelay,
1128     NULL,
1129     ActivateMagicBall
1130   },
1131   {
1132     EL_EMC_SPRING_BUMPER_ACTIVE,
1133     EL_EMC_SPRING_BUMPER,
1134     8,
1135     NULL,
1136     NULL,
1137     NULL
1138   },
1139   {
1140     EL_DIAGONAL_SHRINKING,
1141     EL_UNDEFINED,
1142     0,
1143     NULL,
1144     NULL,
1145     NULL
1146   },
1147   {
1148     EL_DIAGONAL_GROWING,
1149     EL_UNDEFINED,
1150     0,
1151     NULL,
1152     NULL,
1153     NULL,
1154   },
1155
1156   {
1157     EL_UNDEFINED,
1158     EL_UNDEFINED,
1159     -1,
1160     NULL,
1161     NULL,
1162     NULL
1163   }
1164 };
1165
1166 struct
1167 {
1168   int element;
1169   int push_delay_fixed, push_delay_random;
1170 }
1171 push_delay_list[] =
1172 {
1173   { EL_SPRING,                  0, 0 },
1174   { EL_BALLOON,                 0, 0 },
1175
1176   { EL_SOKOBAN_OBJECT,          2, 0 },
1177   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1178   { EL_SATELLITE,               2, 0 },
1179   { EL_SP_DISK_YELLOW,          2, 0 },
1180
1181   { EL_UNDEFINED,               0, 0 },
1182 };
1183
1184 struct
1185 {
1186   int element;
1187   int move_stepsize;
1188 }
1189 move_stepsize_list[] =
1190 {
1191   { EL_AMOEBA_DROP,             2 },
1192   { EL_AMOEBA_DROPPING,         2 },
1193   { EL_QUICKSAND_FILLING,       1 },
1194   { EL_QUICKSAND_EMPTYING,      1 },
1195   { EL_QUICKSAND_FAST_FILLING,  2 },
1196   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1197   { EL_MAGIC_WALL_FILLING,      2 },
1198   { EL_MAGIC_WALL_EMPTYING,     2 },
1199   { EL_BD_MAGIC_WALL_FILLING,   2 },
1200   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1201   { EL_DC_MAGIC_WALL_FILLING,   2 },
1202   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1203
1204   { EL_UNDEFINED,               0 },
1205 };
1206
1207 struct
1208 {
1209   int element;
1210   int count;
1211 }
1212 collect_count_list[] =
1213 {
1214   { EL_EMERALD,                 1 },
1215   { EL_BD_DIAMOND,              1 },
1216   { EL_EMERALD_YELLOW,          1 },
1217   { EL_EMERALD_RED,             1 },
1218   { EL_EMERALD_PURPLE,          1 },
1219   { EL_DIAMOND,                 3 },
1220   { EL_SP_INFOTRON,             1 },
1221   { EL_PEARL,                   5 },
1222   { EL_CRYSTAL,                 8 },
1223
1224   { EL_UNDEFINED,               0 },
1225 };
1226
1227 struct
1228 {
1229   int element;
1230   int direction;
1231 }
1232 access_direction_list[] =
1233 {
1234   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1235   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1236   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1237   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1238   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1239   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1240   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1241   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1242   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1243   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1244   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1245
1246   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1247   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1248   { EL_SP_PORT_UP,                                                   MV_DOWN },
1249   { EL_SP_PORT_DOWN,                                         MV_UP           },
1250   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1251   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1252   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1253   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1254   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1255   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1256   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1257   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1258   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1259   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1260   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1261   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1262   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1263   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1264   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1265
1266   { EL_UNDEFINED,                       MV_NONE                              }
1267 };
1268
1269 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1270
1271 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1272 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1273 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1274                                  IS_JUST_CHANGING(x, y))
1275
1276 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1277
1278 /* static variables for playfield scan mode (scanning forward or backward) */
1279 static int playfield_scan_start_x = 0;
1280 static int playfield_scan_start_y = 0;
1281 static int playfield_scan_delta_x = 1;
1282 static int playfield_scan_delta_y = 1;
1283
1284 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1285                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1286                                      (y) += playfield_scan_delta_y)     \
1287                                 for ((x) = playfield_scan_start_x;      \
1288                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1289                                      (x) += playfield_scan_delta_x)
1290
1291 #ifdef DEBUG
1292 void DEBUG_SetMaximumDynamite()
1293 {
1294   int i;
1295
1296   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1297     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1298       local_player->inventory_element[local_player->inventory_size++] =
1299         EL_DYNAMITE;
1300 }
1301 #endif
1302
1303 static void InitPlayfieldScanModeVars()
1304 {
1305   if (game.use_reverse_scan_direction)
1306   {
1307     playfield_scan_start_x = lev_fieldx - 1;
1308     playfield_scan_start_y = lev_fieldy - 1;
1309
1310     playfield_scan_delta_x = -1;
1311     playfield_scan_delta_y = -1;
1312   }
1313   else
1314   {
1315     playfield_scan_start_x = 0;
1316     playfield_scan_start_y = 0;
1317
1318     playfield_scan_delta_x = 1;
1319     playfield_scan_delta_y = 1;
1320   }
1321 }
1322
1323 static void InitPlayfieldScanMode(int mode)
1324 {
1325   game.use_reverse_scan_direction =
1326     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1327
1328   InitPlayfieldScanModeVars();
1329 }
1330
1331 static int get_move_delay_from_stepsize(int move_stepsize)
1332 {
1333   move_stepsize =
1334     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1335
1336   /* make sure that stepsize value is always a power of 2 */
1337   move_stepsize = (1 << log_2(move_stepsize));
1338
1339   return TILEX / move_stepsize;
1340 }
1341
1342 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1343                                boolean init_game)
1344 {
1345   int player_nr = player->index_nr;
1346   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1347   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1348
1349   /* do no immediately change move delay -- the player might just be moving */
1350   player->move_delay_value_next = move_delay;
1351
1352   /* information if player can move must be set separately */
1353   player->cannot_move = cannot_move;
1354
1355   if (init_game)
1356   {
1357     player->move_delay       = game.initial_move_delay[player_nr];
1358     player->move_delay_value = game.initial_move_delay_value[player_nr];
1359
1360     player->move_delay_value_next = -1;
1361
1362     player->move_delay_reset_counter = 0;
1363   }
1364 }
1365
1366 void GetPlayerConfig()
1367 {
1368   GameFrameDelay = setup.game_frame_delay;
1369
1370   if (!audio.sound_available)
1371     setup.sound_simple = FALSE;
1372
1373   if (!audio.loops_available)
1374     setup.sound_loops = FALSE;
1375
1376   if (!audio.music_available)
1377     setup.sound_music = FALSE;
1378
1379   if (!video.fullscreen_available)
1380     setup.fullscreen = FALSE;
1381
1382   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1383
1384   SetAudioMode(setup.sound);
1385   InitJoysticks();
1386 }
1387
1388 int GetElementFromGroupElement(int element)
1389 {
1390   if (IS_GROUP_ELEMENT(element))
1391   {
1392     struct ElementGroupInfo *group = element_info[element].group;
1393     int last_anim_random_frame = gfx.anim_random_frame;
1394     int element_pos;
1395
1396     if (group->choice_mode == ANIM_RANDOM)
1397       gfx.anim_random_frame = RND(group->num_elements_resolved);
1398
1399     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1400                                     group->choice_mode, 0,
1401                                     group->choice_pos);
1402
1403     if (group->choice_mode == ANIM_RANDOM)
1404       gfx.anim_random_frame = last_anim_random_frame;
1405
1406     group->choice_pos++;
1407
1408     element = group->element_resolved[element_pos];
1409   }
1410
1411   return element;
1412 }
1413
1414 static void InitPlayerField(int x, int y, int element, boolean init_game)
1415 {
1416   if (element == EL_SP_MURPHY)
1417   {
1418     if (init_game)
1419     {
1420       if (stored_player[0].present)
1421       {
1422         Feld[x][y] = EL_SP_MURPHY_CLONE;
1423
1424         return;
1425       }
1426       else
1427       {
1428         stored_player[0].use_murphy = TRUE;
1429
1430         if (!level.use_artwork_element[0])
1431           stored_player[0].artwork_element = EL_SP_MURPHY;
1432       }
1433
1434       Feld[x][y] = EL_PLAYER_1;
1435     }
1436   }
1437
1438   if (init_game)
1439   {
1440     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1441     int jx = player->jx, jy = player->jy;
1442
1443     player->present = TRUE;
1444
1445     player->block_last_field = (element == EL_SP_MURPHY ?
1446                                 level.sp_block_last_field :
1447                                 level.block_last_field);
1448
1449     /* ---------- initialize player's last field block delay --------------- */
1450
1451     /* always start with reliable default value (no adjustment needed) */
1452     player->block_delay_adjustment = 0;
1453
1454     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1455     if (player->block_last_field && element == EL_SP_MURPHY)
1456       player->block_delay_adjustment = 1;
1457
1458     /* special case 2: in game engines before 3.1.1, blocking was different */
1459     if (game.use_block_last_field_bug)
1460       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1461
1462     if (!options.network || player->connected)
1463     {
1464       player->active = TRUE;
1465
1466       /* remove potentially duplicate players */
1467       if (StorePlayer[jx][jy] == Feld[x][y])
1468         StorePlayer[jx][jy] = 0;
1469
1470       StorePlayer[x][y] = Feld[x][y];
1471
1472       if (options.debug)
1473       {
1474         printf("Player %d activated.\n", player->element_nr);
1475         printf("[Local player is %d and currently %s.]\n",
1476                local_player->element_nr,
1477                local_player->active ? "active" : "not active");
1478       }
1479     }
1480
1481     Feld[x][y] = EL_EMPTY;
1482
1483     player->jx = player->last_jx = x;
1484     player->jy = player->last_jy = y;
1485   }
1486 }
1487
1488 static void InitField(int x, int y, boolean init_game)
1489 {
1490   int element = Feld[x][y];
1491
1492   switch (element)
1493   {
1494     case EL_SP_MURPHY:
1495     case EL_PLAYER_1:
1496     case EL_PLAYER_2:
1497     case EL_PLAYER_3:
1498     case EL_PLAYER_4:
1499       InitPlayerField(x, y, element, init_game);
1500       break;
1501
1502     case EL_SOKOBAN_FIELD_PLAYER:
1503       element = Feld[x][y] = EL_PLAYER_1;
1504       InitField(x, y, init_game);
1505
1506       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1507       InitField(x, y, init_game);
1508       break;
1509
1510     case EL_SOKOBAN_FIELD_EMPTY:
1511       local_player->sokobanfields_still_needed++;
1512       break;
1513
1514     case EL_STONEBLOCK:
1515       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1516         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1517       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1518         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1519       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1520         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1521       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1522         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1523       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1524         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1525       break;
1526
1527     case EL_BUG:
1528     case EL_BUG_RIGHT:
1529     case EL_BUG_UP:
1530     case EL_BUG_LEFT:
1531     case EL_BUG_DOWN:
1532     case EL_SPACESHIP:
1533     case EL_SPACESHIP_RIGHT:
1534     case EL_SPACESHIP_UP:
1535     case EL_SPACESHIP_LEFT:
1536     case EL_SPACESHIP_DOWN:
1537     case EL_BD_BUTTERFLY:
1538     case EL_BD_BUTTERFLY_RIGHT:
1539     case EL_BD_BUTTERFLY_UP:
1540     case EL_BD_BUTTERFLY_LEFT:
1541     case EL_BD_BUTTERFLY_DOWN:
1542     case EL_BD_FIREFLY:
1543     case EL_BD_FIREFLY_RIGHT:
1544     case EL_BD_FIREFLY_UP:
1545     case EL_BD_FIREFLY_LEFT:
1546     case EL_BD_FIREFLY_DOWN:
1547     case EL_PACMAN_RIGHT:
1548     case EL_PACMAN_UP:
1549     case EL_PACMAN_LEFT:
1550     case EL_PACMAN_DOWN:
1551     case EL_YAMYAM:
1552     case EL_YAMYAM_LEFT:
1553     case EL_YAMYAM_RIGHT:
1554     case EL_YAMYAM_UP:
1555     case EL_YAMYAM_DOWN:
1556     case EL_DARK_YAMYAM:
1557     case EL_ROBOT:
1558     case EL_PACMAN:
1559     case EL_SP_SNIKSNAK:
1560     case EL_SP_ELECTRON:
1561     case EL_MOLE:
1562     case EL_MOLE_LEFT:
1563     case EL_MOLE_RIGHT:
1564     case EL_MOLE_UP:
1565     case EL_MOLE_DOWN:
1566       InitMovDir(x, y);
1567       break;
1568
1569     case EL_AMOEBA_FULL:
1570     case EL_BD_AMOEBA:
1571       InitAmoebaNr(x, y);
1572       break;
1573
1574     case EL_AMOEBA_DROP:
1575       if (y == lev_fieldy - 1)
1576       {
1577         Feld[x][y] = EL_AMOEBA_GROWING;
1578         Store[x][y] = EL_AMOEBA_WET;
1579       }
1580       break;
1581
1582     case EL_DYNAMITE_ACTIVE:
1583     case EL_SP_DISK_RED_ACTIVE:
1584     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1585     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1586     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1587     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1588       MovDelay[x][y] = 96;
1589       break;
1590
1591     case EL_EM_DYNAMITE_ACTIVE:
1592       MovDelay[x][y] = 32;
1593       break;
1594
1595     case EL_LAMP:
1596       local_player->lights_still_needed++;
1597       break;
1598
1599     case EL_PENGUIN:
1600       local_player->friends_still_needed++;
1601       break;
1602
1603     case EL_PIG:
1604     case EL_DRAGON:
1605       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1606       break;
1607
1608     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1609     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1610     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1611     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1612     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1613     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1614     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1615     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1616     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1617     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1618     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1619     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1620       if (init_game)
1621       {
1622         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1623         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1624         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1625
1626         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1627         {
1628           game.belt_dir[belt_nr] = belt_dir;
1629           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1630         }
1631         else    /* more than one switch -- set it like the first switch */
1632         {
1633           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1634         }
1635       }
1636       break;
1637
1638 #if !USE_BOTH_SWITCHGATE_SWITCHES
1639     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1640       if (init_game)
1641         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1642       break;
1643
1644     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1645       if (init_game)
1646         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1647       break;
1648 #endif
1649
1650     case EL_LIGHT_SWITCH_ACTIVE:
1651       if (init_game)
1652         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1653       break;
1654
1655     case EL_INVISIBLE_STEELWALL:
1656     case EL_INVISIBLE_WALL:
1657     case EL_INVISIBLE_SAND:
1658       if (game.light_time_left > 0 ||
1659           game.lenses_time_left > 0)
1660         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1661       break;
1662
1663     case EL_EMC_MAGIC_BALL:
1664       if (game.ball_state)
1665         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1666       break;
1667
1668     case EL_EMC_MAGIC_BALL_SWITCH:
1669       if (game.ball_state)
1670         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1671       break;
1672
1673     default:
1674       if (IS_CUSTOM_ELEMENT(element))
1675       {
1676         if (CAN_MOVE(element))
1677           InitMovDir(x, y);
1678
1679 #if USE_NEW_CUSTOM_VALUE
1680         if (!element_info[element].use_last_ce_value || init_game)
1681           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1682 #endif
1683       }
1684       else if (IS_GROUP_ELEMENT(element))
1685       {
1686         Feld[x][y] = GetElementFromGroupElement(element);
1687
1688         InitField(x, y, init_game);
1689       }
1690
1691       break;
1692   }
1693
1694   if (!init_game)
1695     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1696 }
1697
1698 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1699 {
1700   InitField(x, y, init_game);
1701
1702   /* not needed to call InitMovDir() -- already done by InitField()! */
1703   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1704       CAN_MOVE(Feld[x][y]))
1705     InitMovDir(x, y);
1706 }
1707
1708 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1709 {
1710   int old_element = Feld[x][y];
1711
1712   InitField(x, y, init_game);
1713
1714   /* not needed to call InitMovDir() -- already done by InitField()! */
1715   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1716       CAN_MOVE(old_element) &&
1717       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1718     InitMovDir(x, y);
1719
1720   /* this case is in fact a combination of not less than three bugs:
1721      first, it calls InitMovDir() for elements that can move, although this is
1722      already done by InitField(); then, it checks the element that was at this
1723      field _before_ the call to InitField() (which can change it); lastly, it
1724      was not called for "mole with direction" elements, which were treated as
1725      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1726   */
1727 }
1728
1729 #if 1
1730
1731 void InitGameControlValues()
1732 {
1733   int i;
1734
1735   for (i = 0; i < NUM_GAME_CONTROLS; i++)
1736     game_control_value[i] = last_game_control_value[i] = -1;
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     game_control_value[nr] = last_game_control_value[nr] = -1;
1745
1746     /* determine panel value width for later calculation of alignment */
1747     if (type == TYPE_INTEGER || type == TYPE_STRING)
1748       pos->width = pos->chars * getFontWidth(pos->font);
1749   }
1750 }
1751
1752 void UpdateGameControlValues()
1753 {
1754   int i, j;
1755
1756   game_control_value[GAME_CONTROL_LEVEL_NUMBER] = level_nr;
1757   game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
1758
1759   game_control_value[GAME_CONTROL_INVENTORY] = 0;
1760   for (i = 0; i < MAX_NUM_KEYS; i++)
1761     game_control_value[GAME_CONTROL_KEY_1 + i] = 0;
1762   game_control_value[GAME_CONTROL_KEY_WHITE] = 0;
1763   game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] = 0;
1764
1765   if (game.centered_player_nr == -1)
1766   {
1767     for (i = 0; i < MAX_PLAYERS; i++)
1768     {
1769       for (j = 0; j < MAX_NUM_KEYS; j++)
1770         if (stored_player[i].key[j])
1771           game_control_value[GAME_CONTROL_KEY_1 + j] = 1;
1772
1773       game_control_value[GAME_CONTROL_INVENTORY] +=
1774         stored_player[i].inventory_size;
1775
1776       if (stored_player[i].num_white_keys > 0)
1777         game_control_value[GAME_CONTROL_KEY_WHITE] = 1;
1778
1779       game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
1780         stored_player[i].num_white_keys;
1781     }
1782   }
1783   else
1784   {
1785     int player_nr = game.centered_player_nr;
1786
1787     for (i = 0; i < MAX_NUM_KEYS; i++)
1788       if (stored_player[player_nr].key[i])
1789         game_control_value[GAME_CONTROL_KEY_1 + i] = 1;
1790
1791     game_control_value[GAME_CONTROL_INVENTORY] +=
1792       stored_player[player_nr].inventory_size;
1793
1794     if (stored_player[player_nr].num_white_keys > 0)
1795       game_control_value[GAME_CONTROL_KEY_WHITE] = 1;
1796
1797     game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
1798       stored_player[player_nr].num_white_keys;
1799   }
1800
1801   game_control_value[GAME_CONTROL_SCORE] = (local_player->LevelSolved ?
1802                                             local_player->score_final :
1803                                             local_player->score);
1804
1805   game_control_value[GAME_CONTROL_TIME] = (level.time == 0 ?
1806                                            TimePlayed :
1807                                            TimeLeft);
1808
1809   game_control_value[GAME_CONTROL_TIME_HH] = TapeTime / 3600;
1810   game_control_value[GAME_CONTROL_TIME_MM] = (TapeTime / 60) % 60;
1811   game_control_value[GAME_CONTROL_TIME_SS] = TapeTime % 60;
1812
1813   for (i = 0; i < 8; i++)
1814     game_control_value[GAME_CONTROL_DROP_NEXT_1 + i] = 0;
1815
1816   game_control_value[GAME_CONTROL_SHIELD_NORMAL] =
1817     (local_player->shield_normal_time_left > 0 ? 1 : 0);
1818   game_control_value[GAME_CONTROL_SHIELD_NORMAL_TIME] =
1819     local_player->shield_normal_time_left;
1820   game_control_value[GAME_CONTROL_SHIELD_DEADLY] =
1821     (local_player->shield_deadly_time_left > 0 ? 1 : 0);
1822   game_control_value[GAME_CONTROL_SHIELD_DEADLY_TIME] =
1823     local_player->shield_deadly_time_left;
1824
1825   game_control_value[GAME_CONTROL_EXIT] = 0;
1826   game_control_value[GAME_CONTROL_EM_EXIT] = 0;
1827   game_control_value[GAME_CONTROL_SP_EXIT] = 0;
1828   game_control_value[GAME_CONTROL_STEEL_EXIT] = 0;
1829   game_control_value[GAME_CONTROL_EM_STEEL_EXIT] = 0;
1830
1831   game_control_value[GAME_CONTROL_EMC_MAGIC_BALL] = 0;
1832   game_control_value[GAME_CONTROL_EMC_MAGIC_BALL_SWITCH] = 0;
1833   game_control_value[GAME_CONTROL_EMC_MAGIC_BALL_TIME] = 0;
1834
1835   game_control_value[GAME_CONTROL_LIGHT_SWITCH] = 0;
1836   game_control_value[GAME_CONTROL_LIGHT_SWITCH_TIME] = game.light_time_left;
1837
1838   game_control_value[GAME_CONTROL_TIMEGATE_SWITCH] = 0;
1839   game_control_value[GAME_CONTROL_TIMEGATE_SWITCH_TIME] =
1840     game.timegate_time_left;
1841
1842   game_control_value[GAME_CONTROL_SWITCHGATE_SWITCH] = 0;
1843
1844   game_control_value[GAME_CONTROL_EMC_LENSES] = 0;
1845   game_control_value[GAME_CONTROL_EMC_LENSES_TIME] = game.lenses_time_left;
1846
1847   game_control_value[GAME_CONTROL_EMC_MAGNIFIER] = 0;
1848   game_control_value[GAME_CONTROL_EMC_MAGNIFIER_TIME] = game.magnify_time_left;
1849
1850   game_control_value[GAME_CONTROL_BALLOON_SWITCH] = 0;
1851
1852   game_control_value[GAME_CONTROL_DYNABOMB_NUMBER] =
1853     local_player->dynabomb_count;
1854   game_control_value[GAME_CONTROL_DYNABOMB_SIZE] =
1855     local_player->dynabomb_size;
1856   game_control_value[GAME_CONTROL_DYNABOMB_POWER] =
1857     local_player->dynabomb_xl;
1858
1859   game_control_value[GAME_CONTROL_PENGUINS] =
1860     local_player->friends_still_needed;
1861
1862   game_control_value[GAME_CONTROL_SOKOBAN_OBJECTS] =
1863     local_player->sokobanfields_still_needed;
1864   game_control_value[GAME_CONTROL_SOKOBAN_FIELDS] =
1865     local_player->sokobanfields_still_needed;
1866
1867   game_control_value[GAME_CONTROL_ROBOT_WHEEL] = 0;
1868
1869   game_control_value[GAME_CONTROL_CONVEYOR_BELT_1] = 0;
1870   game_control_value[GAME_CONTROL_CONVEYOR_BELT_1_SWITCH] = 0;
1871   game_control_value[GAME_CONTROL_CONVEYOR_BELT_2] = 0;
1872   game_control_value[GAME_CONTROL_CONVEYOR_BELT_2_SWITCH] = 0;
1873   game_control_value[GAME_CONTROL_CONVEYOR_BELT_3] = 0;
1874   game_control_value[GAME_CONTROL_CONVEYOR_BELT_3_SWITCH] = 0;
1875   game_control_value[GAME_CONTROL_CONVEYOR_BELT_4] = 0;
1876   game_control_value[GAME_CONTROL_CONVEYOR_BELT_4_SWITCH] = 0;
1877
1878   game_control_value[GAME_CONTROL_MAGIC_WALL] = 0;
1879   game_control_value[GAME_CONTROL_MAGIC_WALL_TIME] =
1880     game.magic_wall_time_left;
1881   game_control_value[GAME_CONTROL_BD_MAGIC_WALL] = 0;
1882   game_control_value[GAME_CONTROL_DC_MAGIC_WALL] = 0;
1883
1884   game_control_value[GAME_CONTROL_PLAYER_NAME] = 0;
1885   game_control_value[GAME_CONTROL_LEVEL_NAME] = 0;
1886   game_control_value[GAME_CONTROL_LEVEL_AUTHOR] = 0;
1887 }
1888
1889 void DisplayGameControlValues()
1890 {
1891   int i;
1892
1893   for (i = 0; game_controls[i].nr != -1; i++)
1894   {
1895     int nr = game_controls[i].nr;
1896     int type = game_controls[i].type;
1897     struct TextPosInfo *pos = game_controls[i].pos;
1898     int value = game_control_value[nr];
1899     int last_value = last_game_control_value[nr];
1900     int chars = pos->chars;
1901     int font = pos->font;
1902
1903     if (value == last_value)
1904       continue;
1905
1906     last_game_control_value[nr] = value;
1907
1908 #if 0
1909     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
1910 #endif
1911
1912     if (PANEL_DEACTIVATED(pos))
1913       continue;
1914
1915     if (type == TYPE_INTEGER)
1916     {
1917       if (nr == GAME_CONTROL_LEVEL_NUMBER || nr == GAME_CONTROL_TIME)
1918       {
1919         boolean use_dynamic_chars = (pos->chars == -1 ? TRUE : FALSE);
1920
1921         if (use_dynamic_chars)          /* use dynamic number of chars */
1922         {
1923           int value_change = (nr == GAME_CONTROL_LEVEL_NUMBER ? 100 : 1000);
1924           int chars1 = (nr == GAME_CONTROL_LEVEL_NUMBER ? 2 : 3);
1925           int chars2 = chars1 + 1;
1926           int font1 = pos->font;
1927           int font2 = pos->font_alt;
1928
1929           chars = (value < value_change ? chars1 : chars2);
1930           font  = (value < value_change ? font1  : font2);
1931
1932           /* clear background if value just changed its size (dynamic chars) */
1933           if ((last_value < value_change) != (value < value_change))
1934           {
1935             int width1 = chars1 * getFontWidth(font1);
1936             int width2 = chars2 * getFontWidth(font2);
1937             int max_width = MAX(width1, width2);
1938             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
1939
1940             pos->width = max_width;
1941
1942             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
1943                                        max_width, max_height);
1944           }
1945         }
1946
1947         pos->width = chars * getFontWidth(font);
1948       }
1949
1950       DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font);
1951     }
1952     else if (type == TYPE_ELEMENT)
1953     {
1954       if (nr >= GAME_CONTROL_KEY_1 && nr <= GAME_CONTROL_KEY_8)
1955       {
1956         int key_nr = nr - GAME_CONTROL_KEY_1;
1957         int src_x = DOOR_GFX_PAGEX5 + 18 + (key_nr % STD_NUM_KEYS) * MINI_TILEX;
1958         int src_y = DOOR_GFX_PAGEY1 + 123;
1959         int dst_x = PANEL_XPOS(pos);
1960         int dst_y = PANEL_YPOS(pos);
1961         int element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1962                        level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1963                        EL_EM_KEY_1 : EL_KEY_1) + key_nr;
1964         int graphic = el2edimg(element);
1965
1966         if (value)
1967           DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
1968         else
1969           BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1970                      MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1971       }
1972     }
1973     else if (type == TYPE_STRING)
1974     {
1975       char *s = (nr == GAME_CONTROL_PLAYER_NAME  ? setup.player_name :
1976                  nr == GAME_CONTROL_LEVEL_NAME   ? level.name :
1977                  nr == GAME_CONTROL_LEVEL_AUTHOR ? level.author : NULL);
1978
1979       if (s != NULL)
1980       {
1981         char *s_cut = getStringCopyN(s, pos->chars);
1982
1983         DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), s_cut, pos->font);
1984
1985         free(s_cut);
1986       }
1987     }
1988   }
1989 }
1990
1991 void DrawGameValue_Emeralds(int value)
1992 {
1993   struct TextPosInfo *pos = &game.panel.gems;
1994 #if 1
1995   int font_nr = pos->font;
1996 #else
1997   int font_nr = FONT_TEXT_2;
1998 #endif
1999   int font_width = getFontWidth(font_nr);
2000   int chars = pos->chars;
2001
2002 #if 1
2003   return;       /* !!! USE NEW STUFF !!! */
2004 #endif
2005
2006   if (PANEL_DEACTIVATED(pos))
2007     return;
2008
2009   pos->width = chars * font_width;
2010
2011   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2012 }
2013
2014 void DrawGameValue_Dynamite(int value)
2015 {
2016   struct TextPosInfo *pos = &game.panel.inventory;
2017 #if 1
2018   int font_nr = pos->font;
2019 #else
2020   int font_nr = FONT_TEXT_2;
2021 #endif
2022   int font_width = getFontWidth(font_nr);
2023   int chars = pos->chars;
2024
2025 #if 1
2026   return;       /* !!! USE NEW STUFF !!! */
2027 #endif
2028
2029   if (PANEL_DEACTIVATED(pos))
2030     return;
2031
2032   pos->width = chars * font_width;
2033
2034   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2035 }
2036
2037 void DrawGameValue_Score(int value)
2038 {
2039   struct TextPosInfo *pos = &game.panel.score;
2040 #if 1
2041   int font_nr = pos->font;
2042 #else
2043   int font_nr = FONT_TEXT_2;
2044 #endif
2045   int font_width = getFontWidth(font_nr);
2046   int chars = pos->chars;
2047
2048 #if 1
2049   return;       /* !!! USE NEW STUFF !!! */
2050 #endif
2051
2052   if (PANEL_DEACTIVATED(pos))
2053     return;
2054
2055   pos->width = chars * font_width;
2056
2057   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2058 }
2059
2060 void DrawGameValue_Time(int value)
2061 {
2062   struct TextPosInfo *pos = &game.panel.time;
2063   static int last_value = -1;
2064   int chars1 = 3;
2065   int chars2 = 4;
2066   int chars = pos->chars;
2067 #if 1
2068   int font1_nr = pos->font;
2069   int font2_nr = pos->font_alt;
2070 #else
2071   int font1_nr = FONT_TEXT_2;
2072   int font2_nr = FONT_TEXT_1;
2073 #endif
2074   int font_nr = font1_nr;
2075   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2076
2077 #if 1
2078   return;       /* !!! USE NEW STUFF !!! */
2079 #endif
2080
2081   if (PANEL_DEACTIVATED(pos))
2082     return;
2083
2084   if (use_dynamic_chars)                /* use dynamic number of chars */
2085   {
2086     chars   = (value < 1000 ? chars1   : chars2);
2087     font_nr = (value < 1000 ? font1_nr : font2_nr);
2088   }
2089
2090   /* clear background if value just changed its size (dynamic chars only) */
2091   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2092   {
2093     int width1 = chars1 * getFontWidth(font1_nr);
2094     int width2 = chars2 * getFontWidth(font2_nr);
2095     int max_width = MAX(width1, width2);
2096     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2097
2098     pos->width = max_width;
2099
2100     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2101                                max_width, max_height);
2102   }
2103
2104   pos->width = chars * getFontWidth(font_nr);
2105
2106   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2107
2108   last_value = value;
2109 }
2110
2111 void DrawGameValue_Level(int value)
2112 {
2113   struct TextPosInfo *pos = &game.panel.level_number;
2114   int chars1 = 2;
2115   int chars2 = 3;
2116   int chars = pos->chars;
2117 #if 1
2118   int font1_nr = pos->font;
2119   int font2_nr = pos->font_alt;
2120 #else
2121   int font1_nr = FONT_TEXT_2;
2122   int font2_nr = FONT_TEXT_1;
2123 #endif
2124   int font_nr = font1_nr;
2125   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2126
2127 #if 1
2128   return;       /* !!! USE NEW STUFF !!! */
2129 #endif
2130
2131   if (PANEL_DEACTIVATED(pos))
2132     return;
2133
2134   if (use_dynamic_chars)                /* use dynamic number of chars */
2135   {
2136     chars   = (level_nr < 100 ? chars1   : chars2);
2137     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2138   }
2139
2140   pos->width = chars * getFontWidth(font_nr);
2141
2142   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2143 }
2144
2145 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2146 {
2147 #if 0
2148   struct TextPosInfo *pos = &game.panel.keys;
2149 #endif
2150 #if 0
2151   int base_key_graphic = EL_KEY_1;
2152 #endif
2153   int i;
2154
2155 #if 1
2156   return;       /* !!! USE NEW STUFF !!! */
2157 #endif
2158
2159 #if 0
2160   if (PANEL_DEACTIVATED(pos))
2161     return;
2162 #endif
2163
2164 #if 0
2165   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2166     base_key_graphic = EL_EM_KEY_1;
2167 #endif
2168
2169 #if 0
2170   pos->width = 4 * MINI_TILEX;
2171 #endif
2172
2173 #if 1
2174   for (i = 0; i < MAX_NUM_KEYS; i++)
2175 #else
2176   /* currently only 4 of 8 possible keys are displayed */
2177   for (i = 0; i < STD_NUM_KEYS; i++)
2178 #endif
2179   {
2180 #if 1
2181     struct TextPosInfo *pos = &game.panel.key[i];
2182 #endif
2183     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2184     int src_y = DOOR_GFX_PAGEY1 + 123;
2185 #if 1
2186     int dst_x = PANEL_XPOS(pos);
2187     int dst_y = PANEL_YPOS(pos);
2188 #else
2189     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2190     int dst_y = PANEL_YPOS(pos);
2191 #endif
2192
2193 #if 1
2194     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2195                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2196                    EL_KEY_1) + i;
2197     int graphic = el2edimg(element);
2198 #endif
2199
2200 #if 1
2201     if (PANEL_DEACTIVATED(pos))
2202       continue;
2203 #endif
2204
2205 #if 0
2206     /* masked blit with tiles from half-size scaled bitmap does not work yet
2207        (no mask bitmap created for these sizes after loading and scaling) --
2208        solution: load without creating mask, scale, then create final mask */
2209
2210     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2211                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2212
2213     if (key[i])
2214     {
2215 #if 0
2216       int graphic = el2edimg(base_key_graphic + i);
2217 #endif
2218       Bitmap *src_bitmap;
2219       int src_x, src_y;
2220
2221       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2222
2223       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2224                     dst_x - src_x, dst_y - src_y);
2225       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2226                        dst_x, dst_y);
2227     }
2228 #else
2229 #if 1
2230     if (key[i])
2231       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2232     else
2233       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2234                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2235 #else
2236     if (key[i])
2237       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2238     else
2239       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2240                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2241 #endif
2242 #endif
2243   }
2244 }
2245
2246 #else
2247
2248 void DrawGameValue_Emeralds(int value)
2249 {
2250   int font_nr = FONT_TEXT_2;
2251   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2252
2253   if (PANEL_DEACTIVATED(game.panel.gems))
2254     return;
2255
2256   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2257 }
2258
2259 void DrawGameValue_Dynamite(int value)
2260 {
2261   int font_nr = FONT_TEXT_2;
2262   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2263
2264   if (PANEL_DEACTIVATED(game.panel.inventory))
2265     return;
2266
2267   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2268 }
2269
2270 void DrawGameValue_Score(int value)
2271 {
2272   int font_nr = FONT_TEXT_2;
2273   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2274
2275   if (PANEL_DEACTIVATED(game.panel.score))
2276     return;
2277
2278   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2279 }
2280
2281 void DrawGameValue_Time(int value)
2282 {
2283   int font1_nr = FONT_TEXT_2;
2284 #if 1
2285   int font2_nr = FONT_TEXT_1;
2286 #else
2287   int font2_nr = FONT_LEVEL_NUMBER;
2288 #endif
2289   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2290   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2291
2292   if (PANEL_DEACTIVATED(game.panel.time))
2293     return;
2294
2295   /* clear background if value just changed its size */
2296   if (value == 999 || value == 1000)
2297     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2298
2299   if (value < 1000)
2300     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2301   else
2302     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2303 }
2304
2305 void DrawGameValue_Level(int value)
2306 {
2307   int font1_nr = FONT_TEXT_2;
2308 #if 1
2309   int font2_nr = FONT_TEXT_1;
2310 #else
2311   int font2_nr = FONT_LEVEL_NUMBER;
2312 #endif
2313
2314   if (PANEL_DEACTIVATED(game.panel.level))
2315     return;
2316
2317   if (level_nr < 100)
2318     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2319   else
2320     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2321 }
2322
2323 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2324 {
2325   int base_key_graphic = EL_KEY_1;
2326   int i;
2327
2328   if (PANEL_DEACTIVATED(game.panel.keys))
2329     return;
2330
2331   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2332     base_key_graphic = EL_EM_KEY_1;
2333
2334   /* currently only 4 of 8 possible keys are displayed */
2335   for (i = 0; i < STD_NUM_KEYS; i++)
2336   {
2337     int x = XX_KEYS + i * MINI_TILEX;
2338     int y = YY_KEYS;
2339
2340     if (key[i])
2341       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2342     else
2343       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2344                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2345   }
2346 }
2347
2348 #endif
2349
2350 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2351                        int key_bits)
2352 {
2353   int key[MAX_NUM_KEYS];
2354   int i;
2355
2356   /* prevent EM engine from updating time/score values parallel to GameWon() */
2357   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2358       local_player->LevelSolved)
2359     return;
2360
2361   for (i = 0; i < MAX_NUM_KEYS; i++)
2362     key[i] = key_bits & (1 << i);
2363
2364   DrawGameValue_Level(level_nr);
2365
2366   DrawGameValue_Emeralds(emeralds);
2367   DrawGameValue_Dynamite(dynamite);
2368   DrawGameValue_Score(score);
2369   DrawGameValue_Time(time);
2370
2371   DrawGameValue_Keys(key);
2372 }
2373
2374 void DrawGameDoorValues()
2375 {
2376   UpdateGameControlValues();
2377   DisplayGameControlValues();
2378 }
2379
2380 void DrawGameDoorValues_OLD()
2381 {
2382   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2383   int dynamite_value = 0;
2384   int score_value = (local_player->LevelSolved ? local_player->score_final :
2385                      local_player->score);
2386   int gems_value = local_player->gems_still_needed;
2387   int key_bits = 0;
2388   int i, j;
2389
2390   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2391   {
2392     DrawGameDoorValues_EM();
2393
2394     return;
2395   }
2396
2397   if (game.centered_player_nr == -1)
2398   {
2399     for (i = 0; i < MAX_PLAYERS; i++)
2400     {
2401       for (j = 0; j < MAX_NUM_KEYS; j++)
2402         if (stored_player[i].key[j])
2403           key_bits |= (1 << j);
2404
2405       dynamite_value += stored_player[i].inventory_size;
2406     }
2407   }
2408   else
2409   {
2410     int player_nr = game.centered_player_nr;
2411
2412     for (i = 0; i < MAX_NUM_KEYS; i++)
2413       if (stored_player[player_nr].key[i])
2414         key_bits |= (1 << i);
2415
2416     dynamite_value = stored_player[player_nr].inventory_size;
2417   }
2418
2419   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2420                     key_bits);
2421 }
2422
2423
2424 /*
2425   =============================================================================
2426   InitGameEngine()
2427   -----------------------------------------------------------------------------
2428   initialize game engine due to level / tape version number
2429   =============================================================================
2430 */
2431
2432 static void InitGameEngine()
2433 {
2434   int i, j, k, l, x, y;
2435
2436   /* set game engine from tape file when re-playing, else from level file */
2437   game.engine_version = (tape.playing ? tape.engine_version :
2438                          level.game_version);
2439
2440   /* ---------------------------------------------------------------------- */
2441   /* set flags for bugs and changes according to active game engine version */
2442   /* ---------------------------------------------------------------------- */
2443
2444   /*
2445     Summary of bugfix/change:
2446     Fixed handling for custom elements that change when pushed by the player.
2447
2448     Fixed/changed in version:
2449     3.1.0
2450
2451     Description:
2452     Before 3.1.0, custom elements that "change when pushing" changed directly
2453     after the player started pushing them (until then handled in "DigField()").
2454     Since 3.1.0, these custom elements are not changed until the "pushing"
2455     move of the element is finished (now handled in "ContinueMoving()").
2456
2457     Affected levels/tapes:
2458     The first condition is generally needed for all levels/tapes before version
2459     3.1.0, which might use the old behaviour before it was changed; known tapes
2460     that are affected are some tapes from the level set "Walpurgis Gardens" by
2461     Jamie Cullen.
2462     The second condition is an exception from the above case and is needed for
2463     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2464     above (including some development versions of 3.1.0), but before it was
2465     known that this change would break tapes like the above and was fixed in
2466     3.1.1, so that the changed behaviour was active although the engine version
2467     while recording maybe was before 3.1.0. There is at least one tape that is
2468     affected by this exception, which is the tape for the one-level set "Bug
2469     Machine" by Juergen Bonhagen.
2470   */
2471
2472   game.use_change_when_pushing_bug =
2473     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2474      !(tape.playing &&
2475        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2476        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2477
2478   /*
2479     Summary of bugfix/change:
2480     Fixed handling for blocking the field the player leaves when moving.
2481
2482     Fixed/changed in version:
2483     3.1.1
2484
2485     Description:
2486     Before 3.1.1, when "block last field when moving" was enabled, the field
2487     the player is leaving when moving was blocked for the time of the move,
2488     and was directly unblocked afterwards. This resulted in the last field
2489     being blocked for exactly one less than the number of frames of one player
2490     move. Additionally, even when blocking was disabled, the last field was
2491     blocked for exactly one frame.
2492     Since 3.1.1, due to changes in player movement handling, the last field
2493     is not blocked at all when blocking is disabled. When blocking is enabled,
2494     the last field is blocked for exactly the number of frames of one player
2495     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2496     last field is blocked for exactly one more than the number of frames of
2497     one player move.
2498
2499     Affected levels/tapes:
2500     (!!! yet to be determined -- probably many !!!)
2501   */
2502
2503   game.use_block_last_field_bug =
2504     (game.engine_version < VERSION_IDENT(3,1,1,0));
2505
2506   /*
2507     Summary of bugfix/change:
2508     Changed behaviour of CE changes with multiple changes per single frame.
2509
2510     Fixed/changed in version:
2511     3.2.0-6
2512
2513     Description:
2514     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
2515     This resulted in race conditions where CEs seem to behave strange in some
2516     situations (where triggered CE changes were just skipped because there was
2517     already a CE change on that tile in the playfield in that engine frame).
2518     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
2519     (The number of changes per frame must be limited in any case, because else
2520     it is easily possible to define CE changes that would result in an infinite
2521     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
2522     should be set large enough so that it would only be reached in cases where
2523     the corresponding CE change conditions run into a loop. Therefore, it seems
2524     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
2525     maximal number of change pages for custom elements.)
2526
2527     Affected levels/tapes:
2528     Probably many.
2529   */
2530
2531 #if USE_ONLY_ONE_CHANGE_PER_FRAME
2532   game.max_num_changes_per_frame = 1;
2533 #else
2534   game.max_num_changes_per_frame =
2535     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
2536 #endif
2537
2538   /* ---------------------------------------------------------------------- */
2539
2540   /* default scan direction: scan playfield from top/left to bottom/right */
2541   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2542
2543   /* dynamically adjust element properties according to game engine version */
2544   InitElementPropertiesEngine(game.engine_version);
2545
2546 #if 0
2547   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2548   printf("          tape version == %06d [%s] [file: %06d]\n",
2549          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2550          tape.file_version);
2551   printf("       => game.engine_version == %06d\n", game.engine_version);
2552 #endif
2553
2554   /* ---------- initialize player's initial move delay --------------------- */
2555
2556   /* dynamically adjust player properties according to level information */
2557   for (i = 0; i < MAX_PLAYERS; i++)
2558     game.initial_move_delay_value[i] =
2559       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2560
2561   /* dynamically adjust player properties according to game engine version */
2562   for (i = 0; i < MAX_PLAYERS; i++)
2563     game.initial_move_delay[i] =
2564       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2565        game.initial_move_delay_value[i] : 0);
2566
2567   /* ---------- initialize player's initial push delay --------------------- */
2568
2569   /* dynamically adjust player properties according to game engine version */
2570   game.initial_push_delay_value =
2571     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2572
2573   /* ---------- initialize changing elements ------------------------------- */
2574
2575   /* initialize changing elements information */
2576   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2577   {
2578     struct ElementInfo *ei = &element_info[i];
2579
2580     /* this pointer might have been changed in the level editor */
2581     ei->change = &ei->change_page[0];
2582
2583     if (!IS_CUSTOM_ELEMENT(i))
2584     {
2585       ei->change->target_element = EL_EMPTY_SPACE;
2586       ei->change->delay_fixed = 0;
2587       ei->change->delay_random = 0;
2588       ei->change->delay_frames = 1;
2589     }
2590
2591     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2592     {
2593       ei->has_change_event[j] = FALSE;
2594
2595       ei->event_page_nr[j] = 0;
2596       ei->event_page[j] = &ei->change_page[0];
2597     }
2598   }
2599
2600   /* add changing elements from pre-defined list */
2601   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2602   {
2603     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2604     struct ElementInfo *ei = &element_info[ch_delay->element];
2605
2606     ei->change->target_element       = ch_delay->target_element;
2607     ei->change->delay_fixed          = ch_delay->change_delay;
2608
2609     ei->change->pre_change_function  = ch_delay->pre_change_function;
2610     ei->change->change_function      = ch_delay->change_function;
2611     ei->change->post_change_function = ch_delay->post_change_function;
2612
2613     ei->change->can_change = TRUE;
2614     ei->change->can_change_or_has_action = TRUE;
2615
2616     ei->has_change_event[CE_DELAY] = TRUE;
2617
2618     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2619     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2620   }
2621
2622   /* ---------- initialize internal run-time variables ------------- */
2623
2624   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2625   {
2626     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2627
2628     for (j = 0; j < ei->num_change_pages; j++)
2629     {
2630       ei->change_page[j].can_change_or_has_action =
2631         (ei->change_page[j].can_change |
2632          ei->change_page[j].has_action);
2633     }
2634   }
2635
2636   /* add change events from custom element configuration */
2637   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2638   {
2639     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2640
2641     for (j = 0; j < ei->num_change_pages; j++)
2642     {
2643       if (!ei->change_page[j].can_change_or_has_action)
2644         continue;
2645
2646       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2647       {
2648         /* only add event page for the first page found with this event */
2649         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2650         {
2651           ei->has_change_event[k] = TRUE;
2652
2653           ei->event_page_nr[k] = j;
2654           ei->event_page[k] = &ei->change_page[j];
2655         }
2656       }
2657     }
2658   }
2659
2660   /* ---------- initialize run-time trigger player and element ------------- */
2661
2662   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2663   {
2664     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2665
2666     for (j = 0; j < ei->num_change_pages; j++)
2667     {
2668       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2669       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
2670       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2671       ei->change_page[j].actual_trigger_ce_value = 0;
2672       ei->change_page[j].actual_trigger_ce_score = 0;
2673     }
2674   }
2675
2676   /* ---------- initialize trigger events ---------------------------------- */
2677
2678   /* initialize trigger events information */
2679   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2680     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2681       trigger_events[i][j] = FALSE;
2682
2683   /* add trigger events from element change event properties */
2684   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2685   {
2686     struct ElementInfo *ei = &element_info[i];
2687
2688     for (j = 0; j < ei->num_change_pages; j++)
2689     {
2690       if (!ei->change_page[j].can_change_or_has_action)
2691         continue;
2692
2693       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2694       {
2695         int trigger_element = ei->change_page[j].trigger_element;
2696
2697         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2698         {
2699           if (ei->change_page[j].has_event[k])
2700           {
2701             if (IS_GROUP_ELEMENT(trigger_element))
2702             {
2703               struct ElementGroupInfo *group =
2704                 element_info[trigger_element].group;
2705
2706               for (l = 0; l < group->num_elements_resolved; l++)
2707                 trigger_events[group->element_resolved[l]][k] = TRUE;
2708             }
2709             else if (trigger_element == EL_ANY_ELEMENT)
2710               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2711                 trigger_events[l][k] = TRUE;
2712             else
2713               trigger_events[trigger_element][k] = TRUE;
2714           }
2715         }
2716       }
2717     }
2718   }
2719
2720   /* ---------- initialize push delay -------------------------------------- */
2721
2722   /* initialize push delay values to default */
2723   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2724   {
2725     if (!IS_CUSTOM_ELEMENT(i))
2726     {
2727       /* set default push delay values (corrected since version 3.0.7-1) */
2728       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2729       {
2730         element_info[i].push_delay_fixed = 2;
2731         element_info[i].push_delay_random = 8;
2732       }
2733       else
2734       {
2735         element_info[i].push_delay_fixed = 8;
2736         element_info[i].push_delay_random = 8;
2737       }
2738     }
2739   }
2740
2741   /* set push delay value for certain elements from pre-defined list */
2742   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2743   {
2744     int e = push_delay_list[i].element;
2745
2746     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2747     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2748   }
2749
2750   /* set push delay value for Supaplex elements for newer engine versions */
2751   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2752   {
2753     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2754     {
2755       if (IS_SP_ELEMENT(i))
2756       {
2757         /* set SP push delay to just enough to push under a falling zonk */
2758         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2759
2760         element_info[i].push_delay_fixed  = delay;
2761         element_info[i].push_delay_random = 0;
2762       }
2763     }
2764   }
2765
2766   /* ---------- initialize move stepsize ----------------------------------- */
2767
2768   /* initialize move stepsize values to default */
2769   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2770     if (!IS_CUSTOM_ELEMENT(i))
2771       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2772
2773   /* set move stepsize value for certain elements from pre-defined list */
2774   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2775   {
2776     int e = move_stepsize_list[i].element;
2777
2778     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2779   }
2780
2781   /* ---------- initialize collect score ----------------------------------- */
2782
2783   /* initialize collect score values for custom elements from initial value */
2784   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2785     if (IS_CUSTOM_ELEMENT(i))
2786       element_info[i].collect_score = element_info[i].collect_score_initial;
2787
2788   /* ---------- initialize collect count ----------------------------------- */
2789
2790   /* initialize collect count values for non-custom elements */
2791   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2792     if (!IS_CUSTOM_ELEMENT(i))
2793       element_info[i].collect_count_initial = 0;
2794
2795   /* add collect count values for all elements from pre-defined list */
2796   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2797     element_info[collect_count_list[i].element].collect_count_initial =
2798       collect_count_list[i].count;
2799
2800   /* ---------- initialize access direction -------------------------------- */
2801
2802   /* initialize access direction values to default (access from every side) */
2803   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2804     if (!IS_CUSTOM_ELEMENT(i))
2805       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2806
2807   /* set access direction value for certain elements from pre-defined list */
2808   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2809     element_info[access_direction_list[i].element].access_direction =
2810       access_direction_list[i].direction;
2811
2812   /* ---------- initialize explosion content ------------------------------- */
2813   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2814   {
2815     if (IS_CUSTOM_ELEMENT(i))
2816       continue;
2817
2818     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2819     {
2820       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2821
2822       element_info[i].content.e[x][y] =
2823         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2824          i == EL_PLAYER_2 ? EL_EMERALD_RED :
2825          i == EL_PLAYER_3 ? EL_EMERALD :
2826          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2827          i == EL_MOLE ? EL_EMERALD_RED :
2828          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2829          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2830          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2831          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2832          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2833          i == EL_WALL_EMERALD ? EL_EMERALD :
2834          i == EL_WALL_DIAMOND ? EL_DIAMOND :
2835          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2836          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2837          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2838          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2839          i == EL_WALL_PEARL ? EL_PEARL :
2840          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2841          EL_EMPTY);
2842     }
2843   }
2844
2845   /* ---------- initialize recursion detection ------------------------------ */
2846   recursion_loop_depth = 0;
2847   recursion_loop_detected = FALSE;
2848   recursion_loop_element = EL_UNDEFINED;
2849 }
2850
2851 int get_num_special_action(int element, int action_first, int action_last)
2852 {
2853   int num_special_action = 0;
2854   int i, j;
2855
2856   for (i = action_first; i <= action_last; i++)
2857   {
2858     boolean found = FALSE;
2859
2860     for (j = 0; j < NUM_DIRECTIONS; j++)
2861       if (el_act_dir2img(element, i, j) !=
2862           el_act_dir2img(element, ACTION_DEFAULT, j))
2863         found = TRUE;
2864
2865     if (found)
2866       num_special_action++;
2867     else
2868       break;
2869   }
2870
2871   return num_special_action;
2872 }
2873
2874
2875 /*
2876   =============================================================================
2877   InitGame()
2878   -----------------------------------------------------------------------------
2879   initialize and start new game
2880   =============================================================================
2881 */
2882
2883 void InitGame()
2884 {
2885   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
2886   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
2887   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
2888 #if 0
2889   boolean do_fading = (game_status == GAME_MODE_MAIN);
2890 #endif
2891   int i, j, x, y;
2892
2893   game_status = GAME_MODE_PLAYING;
2894
2895   InitGameEngine();
2896   InitGameControlValues();
2897
2898   /* don't play tapes over network */
2899   network_playing = (options.network && !tape.playing);
2900
2901   for (i = 0; i < MAX_PLAYERS; i++)
2902   {
2903     struct PlayerInfo *player = &stored_player[i];
2904
2905     player->index_nr = i;
2906     player->index_bit = (1 << i);
2907     player->element_nr = EL_PLAYER_1 + i;
2908
2909     player->present = FALSE;
2910     player->active = FALSE;
2911     player->killed = FALSE;
2912
2913     player->action = 0;
2914     player->effective_action = 0;
2915     player->programmed_action = 0;
2916
2917     player->score = 0;
2918     player->score_final = 0;
2919
2920     player->gems_still_needed = level.gems_needed;
2921     player->sokobanfields_still_needed = 0;
2922     player->lights_still_needed = 0;
2923     player->friends_still_needed = 0;
2924
2925     for (j = 0; j < MAX_NUM_KEYS; j++)
2926       player->key[j] = FALSE;
2927
2928     player->num_white_keys = 0;
2929
2930     player->dynabomb_count = 0;
2931     player->dynabomb_size = 1;
2932     player->dynabombs_left = 0;
2933     player->dynabomb_xl = FALSE;
2934
2935     player->MovDir = MV_NONE;
2936     player->MovPos = 0;
2937     player->GfxPos = 0;
2938     player->GfxDir = MV_NONE;
2939     player->GfxAction = ACTION_DEFAULT;
2940     player->Frame = 0;
2941     player->StepFrame = 0;
2942
2943     player->use_murphy = FALSE;
2944     player->artwork_element =
2945       (level.use_artwork_element[i] ? level.artwork_element[i] :
2946        player->element_nr);
2947
2948     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
2949     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2950
2951     player->gravity = level.initial_player_gravity[i];
2952
2953     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2954
2955     player->actual_frame_counter = 0;
2956
2957     player->step_counter = 0;
2958
2959     player->last_move_dir = MV_NONE;
2960
2961     player->is_active = FALSE;
2962
2963     player->is_waiting = FALSE;
2964     player->is_moving = FALSE;
2965     player->is_auto_moving = FALSE;
2966     player->is_digging = FALSE;
2967     player->is_snapping = FALSE;
2968     player->is_collecting = FALSE;
2969     player->is_pushing = FALSE;
2970     player->is_switching = FALSE;
2971     player->is_dropping = FALSE;
2972     player->is_dropping_pressed = FALSE;
2973
2974     player->is_bored = FALSE;
2975     player->is_sleeping = FALSE;
2976
2977     player->frame_counter_bored = -1;
2978     player->frame_counter_sleeping = -1;
2979
2980     player->anim_delay_counter = 0;
2981     player->post_delay_counter = 0;
2982
2983     player->dir_waiting = MV_NONE;
2984     player->action_waiting = ACTION_DEFAULT;
2985     player->last_action_waiting = ACTION_DEFAULT;
2986     player->special_action_bored = ACTION_DEFAULT;
2987     player->special_action_sleeping = ACTION_DEFAULT;
2988
2989     player->switch_x = -1;
2990     player->switch_y = -1;
2991
2992     player->drop_x = -1;
2993     player->drop_y = -1;
2994
2995     player->show_envelope = 0;
2996
2997     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2998
2999     player->push_delay       = -1;      /* initialized when pushing starts */
3000     player->push_delay_value = game.initial_push_delay_value;
3001
3002     player->drop_delay = 0;
3003     player->drop_pressed_delay = 0;
3004
3005     player->last_jx = -1;
3006     player->last_jy = -1;
3007     player->jx = -1;
3008     player->jy = -1;
3009
3010     player->shield_normal_time_left = 0;
3011     player->shield_deadly_time_left = 0;
3012
3013     player->inventory_infinite_element = EL_UNDEFINED;
3014     player->inventory_size = 0;
3015
3016     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3017     SnapField(player, 0, 0);
3018
3019     player->LevelSolved = FALSE;
3020     player->GameOver = FALSE;
3021
3022     player->LevelSolved_GameWon = FALSE;
3023     player->LevelSolved_GameEnd = FALSE;
3024     player->LevelSolved_PanelOff = FALSE;
3025     player->LevelSolved_SaveTape = FALSE;
3026     player->LevelSolved_SaveScore = FALSE;
3027   }
3028
3029   network_player_action_received = FALSE;
3030
3031 #if defined(NETWORK_AVALIABLE)
3032   /* initial null action */
3033   if (network_playing)
3034     SendToServer_MovePlayer(MV_NONE);
3035 #endif
3036
3037   ZX = ZY = -1;
3038   ExitX = ExitY = -1;
3039
3040   FrameCounter = 0;
3041   TimeFrames = 0;
3042   TimePlayed = 0;
3043   TimeLeft = level.time;
3044   TapeTime = 0;
3045
3046   ScreenMovDir = MV_NONE;
3047   ScreenMovPos = 0;
3048   ScreenGfxPos = 0;
3049
3050   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3051
3052   AllPlayersGone = FALSE;
3053
3054   game.yamyam_content_nr = 0;
3055   game.magic_wall_active = FALSE;
3056   game.magic_wall_time_left = 0;
3057   game.light_time_left = 0;
3058   game.timegate_time_left = 0;
3059   game.switchgate_pos = 0;
3060   game.wind_direction = level.wind_direction_initial;
3061
3062 #if !USE_PLAYER_GRAVITY
3063   game.gravity = FALSE;
3064   game.explosions_delayed = TRUE;
3065 #endif
3066
3067   game.lenses_time_left = 0;
3068   game.magnify_time_left = 0;
3069
3070   game.ball_state = level.ball_state_initial;
3071   game.ball_content_nr = 0;
3072
3073   game.envelope_active = FALSE;
3074
3075   /* set focus to local player for network games, else to all players */
3076   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3077   game.centered_player_nr_next = game.centered_player_nr;
3078   game.set_centered_player = FALSE;
3079
3080   if (network_playing && tape.recording)
3081   {
3082     /* store client dependent player focus when recording network games */
3083     tape.centered_player_nr_next = game.centered_player_nr_next;
3084     tape.set_centered_player = TRUE;
3085   }
3086
3087   for (i = 0; i < NUM_BELTS; i++)
3088   {
3089     game.belt_dir[i] = MV_NONE;
3090     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3091   }
3092
3093   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3094     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3095
3096   SCAN_PLAYFIELD(x, y)
3097   {
3098     Feld[x][y] = level.field[x][y];
3099     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3100     ChangeDelay[x][y] = 0;
3101     ChangePage[x][y] = -1;
3102 #if USE_NEW_CUSTOM_VALUE
3103     CustomValue[x][y] = 0;              /* initialized in InitField() */
3104 #endif
3105     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3106     AmoebaNr[x][y] = 0;
3107     WasJustMoving[x][y] = 0;
3108     WasJustFalling[x][y] = 0;
3109     CheckCollision[x][y] = 0;
3110     CheckImpact[x][y] = 0;
3111     Stop[x][y] = FALSE;
3112     Pushed[x][y] = FALSE;
3113
3114     ChangeCount[x][y] = 0;
3115     ChangeEvent[x][y] = -1;
3116
3117     ExplodePhase[x][y] = 0;
3118     ExplodeDelay[x][y] = 0;
3119     ExplodeField[x][y] = EX_TYPE_NONE;
3120
3121     RunnerVisit[x][y] = 0;
3122     PlayerVisit[x][y] = 0;
3123
3124     GfxFrame[x][y] = 0;
3125     GfxRandom[x][y] = INIT_GFX_RANDOM();
3126     GfxElement[x][y] = EL_UNDEFINED;
3127     GfxAction[x][y] = ACTION_DEFAULT;
3128     GfxDir[x][y] = MV_NONE;
3129   }
3130
3131   SCAN_PLAYFIELD(x, y)
3132   {
3133     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3134       emulate_bd = FALSE;
3135     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3136       emulate_sb = FALSE;
3137     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3138       emulate_sp = FALSE;
3139
3140     InitField(x, y, TRUE);
3141   }
3142
3143   InitBeltMovement();
3144
3145   for (i = 0; i < MAX_PLAYERS; i++)
3146   {
3147     struct PlayerInfo *player = &stored_player[i];
3148
3149     /* set number of special actions for bored and sleeping animation */
3150     player->num_special_action_bored =
3151       get_num_special_action(player->artwork_element,
3152                              ACTION_BORING_1, ACTION_BORING_LAST);
3153     player->num_special_action_sleeping =
3154       get_num_special_action(player->artwork_element,
3155                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3156   }
3157
3158   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3159                     emulate_sb ? EMU_SOKOBAN :
3160                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3161
3162 #if USE_NEW_ALL_SLIPPERY
3163   /* initialize type of slippery elements */
3164   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3165   {
3166     if (!IS_CUSTOM_ELEMENT(i))
3167     {
3168       /* default: elements slip down either to the left or right randomly */
3169       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3170
3171       /* SP style elements prefer to slip down on the left side */
3172       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3173         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3174
3175       /* BD style elements prefer to slip down on the left side */
3176       if (game.emulation == EMU_BOULDERDASH)
3177         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3178     }
3179   }
3180 #endif
3181
3182   /* initialize explosion and ignition delay */
3183   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3184   {
3185     if (!IS_CUSTOM_ELEMENT(i))
3186     {
3187       int num_phase = 8;
3188       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3189                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3190                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3191       int last_phase = (num_phase + 1) * delay;
3192       int half_phase = (num_phase / 2) * delay;
3193
3194       element_info[i].explosion_delay = last_phase - 1;
3195       element_info[i].ignition_delay = half_phase;
3196
3197       if (i == EL_BLACK_ORB)
3198         element_info[i].ignition_delay = 1;
3199     }
3200
3201 #if 0
3202     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3203       element_info[i].explosion_delay = 1;
3204
3205     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3206       element_info[i].ignition_delay = 1;
3207 #endif
3208   }
3209
3210   /* correct non-moving belts to start moving left */
3211   for (i = 0; i < NUM_BELTS; i++)
3212     if (game.belt_dir[i] == MV_NONE)
3213       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3214
3215   /* check if any connected player was not found in playfield */
3216   for (i = 0; i < MAX_PLAYERS; i++)
3217   {
3218     struct PlayerInfo *player = &stored_player[i];
3219
3220     if (player->connected && !player->present)
3221     {
3222       for (j = 0; j < MAX_PLAYERS; j++)
3223       {
3224         struct PlayerInfo *some_player = &stored_player[j];
3225         int jx = some_player->jx, jy = some_player->jy;
3226
3227         /* assign first free player found that is present in the playfield */
3228         if (some_player->present && !some_player->connected)
3229         {
3230           player->present = TRUE;
3231           player->active = TRUE;
3232
3233           some_player->present = FALSE;
3234           some_player->active = FALSE;
3235
3236           player->artwork_element = some_player->artwork_element;
3237
3238           player->block_last_field       = some_player->block_last_field;
3239           player->block_delay_adjustment = some_player->block_delay_adjustment;
3240
3241           StorePlayer[jx][jy] = player->element_nr;
3242           player->jx = player->last_jx = jx;
3243           player->jy = player->last_jy = jy;
3244
3245           break;
3246         }
3247       }
3248     }
3249   }
3250
3251   if (tape.playing)
3252   {
3253     /* when playing a tape, eliminate all players who do not participate */
3254
3255     for (i = 0; i < MAX_PLAYERS; i++)
3256     {
3257       if (stored_player[i].active && !tape.player_participates[i])
3258       {
3259         struct PlayerInfo *player = &stored_player[i];
3260         int jx = player->jx, jy = player->jy;
3261
3262         player->active = FALSE;
3263         StorePlayer[jx][jy] = 0;
3264         Feld[jx][jy] = EL_EMPTY;
3265       }
3266     }
3267   }
3268   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
3269   {
3270     /* when in single player mode, eliminate all but the first active player */
3271
3272     for (i = 0; i < MAX_PLAYERS; i++)
3273     {
3274       if (stored_player[i].active)
3275       {
3276         for (j = i + 1; j < MAX_PLAYERS; j++)
3277         {
3278           if (stored_player[j].active)
3279           {
3280             struct PlayerInfo *player = &stored_player[j];
3281             int jx = player->jx, jy = player->jy;
3282
3283             player->active = FALSE;
3284             player->present = FALSE;
3285
3286             StorePlayer[jx][jy] = 0;
3287             Feld[jx][jy] = EL_EMPTY;
3288           }
3289         }
3290       }
3291     }
3292   }
3293
3294   /* when recording the game, store which players take part in the game */
3295   if (tape.recording)
3296   {
3297     for (i = 0; i < MAX_PLAYERS; i++)
3298       if (stored_player[i].active)
3299         tape.player_participates[i] = TRUE;
3300   }
3301
3302   if (options.debug)
3303   {
3304     for (i = 0; i < MAX_PLAYERS; i++)
3305     {
3306       struct PlayerInfo *player = &stored_player[i];
3307
3308       printf("Player %d: present == %d, connected == %d, active == %d.\n",
3309              i+1,
3310              player->present,
3311              player->connected,
3312              player->active);
3313       if (local_player == player)
3314         printf("Player  %d is local player.\n", i+1);
3315     }
3316   }
3317
3318   if (BorderElement == EL_EMPTY)
3319   {
3320     SBX_Left = 0;
3321     SBX_Right = lev_fieldx - SCR_FIELDX;
3322     SBY_Upper = 0;
3323     SBY_Lower = lev_fieldy - SCR_FIELDY;
3324   }
3325   else
3326   {
3327     SBX_Left = -1;
3328     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3329     SBY_Upper = -1;
3330     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3331   }
3332
3333   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3334     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3335
3336   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3337     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3338
3339   /* if local player not found, look for custom element that might create
3340      the player (make some assumptions about the right custom element) */
3341   if (!local_player->present)
3342   {
3343     int start_x = 0, start_y = 0;
3344     int found_rating = 0;
3345     int found_element = EL_UNDEFINED;
3346     int player_nr = local_player->index_nr;
3347
3348     SCAN_PLAYFIELD(x, y)
3349     {
3350       int element = Feld[x][y];
3351       int content;
3352       int xx, yy;
3353       boolean is_player;
3354
3355       if (level.use_start_element[player_nr] &&
3356           level.start_element[player_nr] == element &&
3357           found_rating < 4)
3358       {
3359         start_x = x;
3360         start_y = y;
3361
3362         found_rating = 4;
3363         found_element = element;
3364       }
3365
3366       if (!IS_CUSTOM_ELEMENT(element))
3367         continue;
3368
3369       if (CAN_CHANGE(element))
3370       {
3371         for (i = 0; i < element_info[element].num_change_pages; i++)
3372         {
3373           /* check for player created from custom element as single target */
3374           content = element_info[element].change_page[i].target_element;
3375           is_player = ELEM_IS_PLAYER(content);
3376
3377           if (is_player && (found_rating < 3 || element < found_element))
3378           {
3379             start_x = x;
3380             start_y = y;
3381
3382             found_rating = 3;
3383             found_element = element;
3384           }
3385         }
3386       }
3387
3388       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3389       {
3390         /* check for player created from custom element as explosion content */
3391         content = element_info[element].content.e[xx][yy];
3392         is_player = ELEM_IS_PLAYER(content);
3393
3394         if (is_player && (found_rating < 2 || element < found_element))
3395         {
3396           start_x = x + xx - 1;
3397           start_y = y + yy - 1;
3398
3399           found_rating = 2;
3400           found_element = element;
3401         }
3402
3403         if (!CAN_CHANGE(element))
3404           continue;
3405
3406         for (i = 0; i < element_info[element].num_change_pages; i++)
3407         {
3408           /* check for player created from custom element as extended target */
3409           content =
3410             element_info[element].change_page[i].target_content.e[xx][yy];
3411
3412           is_player = ELEM_IS_PLAYER(content);
3413
3414           if (is_player && (found_rating < 1 || element < found_element))
3415           {
3416             start_x = x + xx - 1;
3417             start_y = y + yy - 1;
3418
3419             found_rating = 1;
3420             found_element = element;
3421           }
3422         }
3423       }
3424     }
3425
3426     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3427                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3428                 start_x - MIDPOSX);
3429
3430     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3431                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3432                 start_y - MIDPOSY);
3433   }
3434   else
3435   {
3436     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3437                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3438                 local_player->jx - MIDPOSX);
3439
3440     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3441                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3442                 local_player->jy - MIDPOSY);
3443   }
3444
3445   StopAnimation();
3446
3447   if (!game.restart_level)
3448     CloseDoor(DOOR_CLOSE_1);
3449
3450 #if 1
3451   if (level_editor_test_game)
3452     FadeSkipNextFadeIn();
3453   else
3454     FadeSetStartItem();
3455 #else
3456   if (level_editor_test_game)
3457     fading = fading_none;
3458   else
3459     fading = menu.destination;
3460 #endif
3461
3462 #if 1
3463   FadeOut(REDRAW_FIELD);
3464 #else
3465   if (do_fading)
3466     FadeOut(REDRAW_FIELD);
3467 #endif
3468
3469   /* !!! FIX THIS (START) !!! */
3470   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3471   {
3472     InitGameEngine_EM();
3473
3474     /* blit playfield from scroll buffer to normal back buffer for fading in */
3475     BlitScreenToBitmap_EM(backbuffer);
3476   }
3477   else
3478   {
3479     DrawLevel();
3480     DrawAllPlayers();
3481
3482     /* after drawing the level, correct some elements */
3483     if (game.timegate_time_left == 0)
3484       CloseAllOpenTimegates();
3485
3486     /* blit playfield from scroll buffer to normal back buffer for fading in */
3487     if (setup.soft_scrolling)
3488       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3489
3490     redraw_mask |= REDRAW_FROM_BACKBUFFER;
3491   }
3492   /* !!! FIX THIS (END) !!! */
3493
3494 #if 1
3495   FadeIn(REDRAW_FIELD);
3496 #else
3497   if (do_fading)
3498     FadeIn(REDRAW_FIELD);
3499
3500   BackToFront();
3501 #endif
3502
3503   if (!game.restart_level)
3504   {
3505     /* copy default game door content to main double buffer */
3506     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3507                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3508   }
3509
3510   SetPanelBackground();
3511   SetDrawBackgroundMask(REDRAW_DOOR_1);
3512
3513   DrawGameDoorValues();
3514
3515   if (!game.restart_level)
3516   {
3517     UnmapGameButtons();
3518     UnmapTapeButtons();
3519     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3520     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3521     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3522     MapGameButtons();
3523     MapTapeButtons();
3524
3525     /* copy actual game door content to door double buffer for OpenDoor() */
3526     BlitBitmap(drawto, bitmap_db_door,
3527                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3528
3529     OpenDoor(DOOR_OPEN_ALL);
3530
3531     PlaySound(SND_GAME_STARTING);
3532
3533     if (setup.sound_music)
3534       PlayLevelMusic();
3535
3536     KeyboardAutoRepeatOffUnlessAutoplay();
3537
3538     if (options.debug)
3539     {
3540       for (i = 0; i < MAX_PLAYERS; i++)
3541         printf("Player %d %sactive.\n",
3542                i + 1, (stored_player[i].active ? "" : "not "));
3543     }
3544   }
3545
3546 #if 1
3547   UnmapAllGadgets();
3548
3549   MapGameButtons();
3550   MapTapeButtons();
3551 #endif
3552
3553   game.restart_level = FALSE;
3554 }
3555
3556 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3557 {
3558   /* this is used for non-R'n'D game engines to update certain engine values */
3559
3560   /* needed to determine if sounds are played within the visible screen area */
3561   scroll_x = actual_scroll_x;
3562   scroll_y = actual_scroll_y;
3563 }
3564
3565 void InitMovDir(int x, int y)
3566 {
3567   int i, element = Feld[x][y];
3568   static int xy[4][2] =
3569   {
3570     {  0, +1 },
3571     { +1,  0 },
3572     {  0, -1 },
3573     { -1,  0 }
3574   };
3575   static int direction[3][4] =
3576   {
3577     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
3578     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
3579     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
3580   };
3581
3582   switch (element)
3583   {
3584     case EL_BUG_RIGHT:
3585     case EL_BUG_UP:
3586     case EL_BUG_LEFT:
3587     case EL_BUG_DOWN:
3588       Feld[x][y] = EL_BUG;
3589       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3590       break;
3591
3592     case EL_SPACESHIP_RIGHT:
3593     case EL_SPACESHIP_UP:
3594     case EL_SPACESHIP_LEFT:
3595     case EL_SPACESHIP_DOWN:
3596       Feld[x][y] = EL_SPACESHIP;
3597       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3598       break;
3599
3600     case EL_BD_BUTTERFLY_RIGHT:
3601     case EL_BD_BUTTERFLY_UP:
3602     case EL_BD_BUTTERFLY_LEFT:
3603     case EL_BD_BUTTERFLY_DOWN:
3604       Feld[x][y] = EL_BD_BUTTERFLY;
3605       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3606       break;
3607
3608     case EL_BD_FIREFLY_RIGHT:
3609     case EL_BD_FIREFLY_UP:
3610     case EL_BD_FIREFLY_LEFT:
3611     case EL_BD_FIREFLY_DOWN:
3612       Feld[x][y] = EL_BD_FIREFLY;
3613       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3614       break;
3615
3616     case EL_PACMAN_RIGHT:
3617     case EL_PACMAN_UP:
3618     case EL_PACMAN_LEFT:
3619     case EL_PACMAN_DOWN:
3620       Feld[x][y] = EL_PACMAN;
3621       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3622       break;
3623
3624     case EL_YAMYAM_LEFT:
3625     case EL_YAMYAM_RIGHT:
3626     case EL_YAMYAM_UP:
3627     case EL_YAMYAM_DOWN:
3628       Feld[x][y] = EL_YAMYAM;
3629       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3630       break;
3631
3632     case EL_SP_SNIKSNAK:
3633       MovDir[x][y] = MV_UP;
3634       break;
3635
3636     case EL_SP_ELECTRON:
3637       MovDir[x][y] = MV_LEFT;
3638       break;
3639
3640     case EL_MOLE_LEFT:
3641     case EL_MOLE_RIGHT:
3642     case EL_MOLE_UP:
3643     case EL_MOLE_DOWN:
3644       Feld[x][y] = EL_MOLE;
3645       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3646       break;
3647
3648     default:
3649       if (IS_CUSTOM_ELEMENT(element))
3650       {
3651         struct ElementInfo *ei = &element_info[element];
3652         int move_direction_initial = ei->move_direction_initial;
3653         int move_pattern = ei->move_pattern;
3654
3655         if (move_direction_initial == MV_START_PREVIOUS)
3656         {
3657           if (MovDir[x][y] != MV_NONE)
3658             return;
3659
3660           move_direction_initial = MV_START_AUTOMATIC;
3661         }
3662
3663         if (move_direction_initial == MV_START_RANDOM)
3664           MovDir[x][y] = 1 << RND(4);
3665         else if (move_direction_initial & MV_ANY_DIRECTION)
3666           MovDir[x][y] = move_direction_initial;
3667         else if (move_pattern == MV_ALL_DIRECTIONS ||
3668                  move_pattern == MV_TURNING_LEFT ||
3669                  move_pattern == MV_TURNING_RIGHT ||
3670                  move_pattern == MV_TURNING_LEFT_RIGHT ||
3671                  move_pattern == MV_TURNING_RIGHT_LEFT ||
3672                  move_pattern == MV_TURNING_RANDOM)
3673           MovDir[x][y] = 1 << RND(4);
3674         else if (move_pattern == MV_HORIZONTAL)
3675           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3676         else if (move_pattern == MV_VERTICAL)
3677           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3678         else if (move_pattern & MV_ANY_DIRECTION)
3679           MovDir[x][y] = element_info[element].move_pattern;
3680         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
3681                  move_pattern == MV_ALONG_RIGHT_SIDE)
3682         {
3683           /* use random direction as default start direction */
3684           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3685             MovDir[x][y] = 1 << RND(4);
3686
3687           for (i = 0; i < NUM_DIRECTIONS; i++)
3688           {
3689             int x1 = x + xy[i][0];
3690             int y1 = y + xy[i][1];
3691
3692             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3693             {
3694               if (move_pattern == MV_ALONG_RIGHT_SIDE)
3695                 MovDir[x][y] = direction[0][i];
3696               else
3697                 MovDir[x][y] = direction[1][i];
3698
3699               break;
3700             }
3701           }
3702         }                
3703       }
3704       else
3705       {
3706         MovDir[x][y] = 1 << RND(4);
3707
3708         if (element != EL_BUG &&
3709             element != EL_SPACESHIP &&
3710             element != EL_BD_BUTTERFLY &&
3711             element != EL_BD_FIREFLY)
3712           break;
3713
3714         for (i = 0; i < NUM_DIRECTIONS; i++)
3715         {
3716           int x1 = x + xy[i][0];
3717           int y1 = y + xy[i][1];
3718
3719           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3720           {
3721             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3722             {
3723               MovDir[x][y] = direction[0][i];
3724               break;
3725             }
3726             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3727                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3728             {
3729               MovDir[x][y] = direction[1][i];
3730               break;
3731             }
3732           }
3733         }
3734       }
3735       break;
3736   }
3737
3738   GfxDir[x][y] = MovDir[x][y];
3739 }
3740
3741 void InitAmoebaNr(int x, int y)
3742 {
3743   int i;
3744   int group_nr = AmoebeNachbarNr(x, y);
3745
3746   if (group_nr == 0)
3747   {
3748     for (i = 1; i < MAX_NUM_AMOEBA; i++)
3749     {
3750       if (AmoebaCnt[i] == 0)
3751       {
3752         group_nr = i;
3753         break;
3754       }
3755     }
3756   }
3757
3758   AmoebaNr[x][y] = group_nr;
3759   AmoebaCnt[group_nr]++;
3760   AmoebaCnt2[group_nr]++;
3761 }
3762
3763 static void PlayerWins(struct PlayerInfo *player)
3764 {
3765   player->LevelSolved = TRUE;
3766   player->GameOver = TRUE;
3767
3768   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
3769                          level.native_em_level->lev->score : player->score);
3770 }
3771
3772 void GameWon()
3773 {
3774   static int time, time_final;
3775   static int score, score_final;
3776   static int game_over_delay_1 = 0;
3777   static int game_over_delay_2 = 0;
3778   int game_over_delay_value_1 = 50;
3779   int game_over_delay_value_2 = 50;
3780
3781   if (!local_player->LevelSolved_GameWon)
3782   {
3783     int i;
3784
3785     /* do not start end game actions before the player stops moving (to exit) */
3786     if (local_player->MovPos)
3787       return;
3788
3789     local_player->LevelSolved_GameWon = TRUE;
3790     local_player->LevelSolved_SaveTape = tape.recording;
3791     local_player->LevelSolved_SaveScore = !tape.playing;
3792
3793     if (tape.auto_play)         /* tape might already be stopped here */
3794       tape.auto_play_level_solved = TRUE;
3795
3796 #if 1
3797     TapeStop();
3798 #endif
3799
3800     game_over_delay_1 = game_over_delay_value_1;
3801     game_over_delay_2 = game_over_delay_value_2;
3802
3803     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3804     score = score_final = local_player->score_final;
3805
3806     if (TimeLeft > 0)
3807     {
3808       time_final = 0;
3809       score_final += TimeLeft * level.score[SC_TIME_BONUS];
3810     }
3811     else if (level.time == 0 && TimePlayed < 999)
3812     {
3813       time_final = 999;
3814       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3815     }
3816
3817     local_player->score_final = score_final;
3818
3819     if (level_editor_test_game)
3820     {
3821       time = time_final;
3822       score = score_final;
3823
3824 #if 1
3825       game_control_value[GAME_CONTROL_TIME] = time;
3826       game_control_value[GAME_CONTROL_SCORE] = score;
3827
3828       DisplayGameControlValues();
3829 #else
3830       DrawGameValue_Time(time);
3831       DrawGameValue_Score(score);
3832 #endif
3833     }
3834
3835     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3836     {
3837       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
3838       {
3839         /* close exit door after last player */
3840         if ((AllPlayersGone &&
3841              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3842               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3843               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3844             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3845             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3846         {
3847           int element = Feld[ExitX][ExitY];
3848
3849 #if 0
3850           if (element == EL_EM_EXIT_OPEN ||
3851               element == EL_EM_STEEL_EXIT_OPEN)
3852           {
3853             Bang(ExitX, ExitY);
3854           }
3855           else
3856 #endif
3857           {
3858             Feld[ExitX][ExitY] =
3859               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
3860                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
3861                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
3862                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
3863                EL_EM_STEEL_EXIT_CLOSING);
3864
3865             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3866           }
3867         }
3868
3869         /* player disappears */
3870         DrawLevelField(ExitX, ExitY);
3871       }
3872
3873       for (i = 0; i < MAX_PLAYERS; i++)
3874       {
3875         struct PlayerInfo *player = &stored_player[i];
3876
3877         if (player->present)
3878         {
3879           RemovePlayer(player);
3880
3881           /* player disappears */
3882           DrawLevelField(player->jx, player->jy);
3883         }
3884       }
3885     }
3886
3887     PlaySound(SND_GAME_WINNING);
3888   }
3889
3890   if (game_over_delay_1 > 0)
3891   {
3892     game_over_delay_1--;
3893
3894     return;
3895   }
3896
3897   if (time != time_final)
3898   {
3899     int time_to_go = ABS(time_final - time);
3900     int time_count_dir = (time < time_final ? +1 : -1);
3901     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
3902
3903     time  += time_count_steps * time_count_dir;
3904     score += time_count_steps * level.score[SC_TIME_BONUS];
3905
3906 #if 1
3907     game_control_value[GAME_CONTROL_TIME] = time;
3908     game_control_value[GAME_CONTROL_SCORE] = score;
3909
3910     DisplayGameControlValues();
3911 #else
3912     DrawGameValue_Time(time);
3913     DrawGameValue_Score(score);
3914 #endif
3915
3916     if (time == time_final)
3917       StopSound(SND_GAME_LEVELTIME_BONUS);
3918     else if (setup.sound_loops)
3919       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
3920     else
3921       PlaySound(SND_GAME_LEVELTIME_BONUS);
3922
3923     return;
3924   }
3925
3926   local_player->LevelSolved_PanelOff = TRUE;
3927
3928   if (game_over_delay_2 > 0)
3929   {
3930     game_over_delay_2--;
3931
3932     return;
3933   }
3934
3935 #if 1
3936   GameEnd();
3937 #endif
3938 }
3939
3940 void GameEnd()
3941 {
3942   int hi_pos;
3943   boolean raise_level = FALSE;
3944
3945   local_player->LevelSolved_GameEnd = TRUE;
3946
3947   CloseDoor(DOOR_CLOSE_1);
3948
3949   if (local_player->LevelSolved_SaveTape)
3950   {
3951 #if 0
3952     TapeStop();
3953 #endif
3954
3955 #if 1
3956     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
3957 #else
3958     SaveTape(tape.level_nr);            /* ask to save tape */
3959 #endif
3960   }
3961
3962   if (level_editor_test_game)
3963   {
3964     game_status = GAME_MODE_MAIN;
3965
3966 #if 1
3967     DrawAndFadeInMainMenu(REDRAW_FIELD);
3968 #else
3969     DrawMainMenu();
3970 #endif
3971
3972     return;
3973   }
3974
3975   if (!local_player->LevelSolved_SaveScore)
3976   {
3977 #if 1
3978     FadeOut(REDRAW_FIELD);
3979 #endif
3980
3981     game_status = GAME_MODE_MAIN;
3982
3983     DrawAndFadeInMainMenu(REDRAW_FIELD);
3984
3985     return;
3986   }
3987
3988   if (level_nr == leveldir_current->handicap_level)
3989   {
3990     leveldir_current->handicap_level++;
3991     SaveLevelSetup_SeriesInfo();
3992   }
3993
3994   if (level_nr < leveldir_current->last_level)
3995     raise_level = TRUE;                 /* advance to next level */
3996
3997   if ((hi_pos = NewHiScore()) >= 0) 
3998   {
3999     game_status = GAME_MODE_SCORES;
4000
4001     DrawHallOfFame(hi_pos);
4002
4003     if (raise_level)
4004     {
4005       level_nr++;
4006       TapeErase();
4007     }
4008   }
4009   else
4010   {
4011 #if 1
4012     FadeOut(REDRAW_FIELD);
4013 #endif
4014
4015     game_status = GAME_MODE_MAIN;
4016
4017     if (raise_level)
4018     {
4019       level_nr++;
4020       TapeErase();
4021     }
4022
4023     DrawAndFadeInMainMenu(REDRAW_FIELD);
4024   }
4025 }
4026
4027 int NewHiScore()
4028 {
4029   int k, l;
4030   int position = -1;
4031
4032   LoadScore(level_nr);
4033
4034   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4035       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4036     return -1;
4037
4038   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4039   {
4040     if (local_player->score_final > highscore[k].Score)
4041     {
4042       /* player has made it to the hall of fame */
4043
4044       if (k < MAX_SCORE_ENTRIES - 1)
4045       {
4046         int m = MAX_SCORE_ENTRIES - 1;
4047
4048 #ifdef ONE_PER_NAME
4049         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4050           if (strEqual(setup.player_name, highscore[l].Name))
4051             m = l;
4052         if (m == k)     /* player's new highscore overwrites his old one */
4053           goto put_into_list;
4054 #endif
4055
4056         for (l = m; l > k; l--)
4057         {
4058           strcpy(highscore[l].Name, highscore[l - 1].Name);
4059           highscore[l].Score = highscore[l - 1].Score;
4060         }
4061       }
4062
4063 #ifdef ONE_PER_NAME
4064       put_into_list:
4065 #endif
4066       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4067       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4068       highscore[k].Score = local_player->score_final; 
4069       position = k;
4070       break;
4071     }
4072
4073 #ifdef ONE_PER_NAME
4074     else if (!strncmp(setup.player_name, highscore[k].Name,
4075                       MAX_PLAYER_NAME_LEN))
4076       break;    /* player already there with a higher score */
4077 #endif
4078
4079   }
4080
4081   if (position >= 0) 
4082     SaveScore(level_nr);
4083
4084   return position;
4085 }
4086
4087 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4088 {
4089   int element = Feld[x][y];
4090   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4091   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4092   int horiz_move = (dx != 0);
4093   int sign = (horiz_move ? dx : dy);
4094   int step = sign * element_info[element].move_stepsize;
4095
4096   /* special values for move stepsize for spring and things on conveyor belt */
4097   if (horiz_move)
4098   {
4099     if (CAN_FALL(element) &&
4100         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4101       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4102     else if (element == EL_SPRING)
4103       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4104   }
4105
4106   return step;
4107 }
4108
4109 inline static int getElementMoveStepsize(int x, int y)
4110 {
4111   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4112 }
4113
4114 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4115 {
4116   if (player->GfxAction != action || player->GfxDir != dir)
4117   {
4118 #if 0
4119     printf("Player frame reset! (%d => %d, %d => %d)\n",
4120            player->GfxAction, action, player->GfxDir, dir);
4121 #endif
4122
4123     player->GfxAction = action;
4124     player->GfxDir = dir;
4125     player->Frame = 0;
4126     player->StepFrame = 0;
4127   }
4128 }
4129
4130 #if USE_GFX_RESET_GFX_ANIMATION
4131 static void ResetGfxFrame(int x, int y, boolean redraw)
4132 {
4133   int element = Feld[x][y];
4134   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4135   int last_gfx_frame = GfxFrame[x][y];
4136
4137   if (graphic_info[graphic].anim_global_sync)
4138     GfxFrame[x][y] = FrameCounter;
4139   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4140     GfxFrame[x][y] = CustomValue[x][y];
4141   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4142     GfxFrame[x][y] = element_info[element].collect_score;
4143   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4144     GfxFrame[x][y] = ChangeDelay[x][y];
4145
4146   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4147     DrawLevelGraphicAnimation(x, y, graphic);
4148 }
4149 #endif
4150
4151 static void ResetGfxAnimation(int x, int y)
4152 {
4153   GfxAction[x][y] = ACTION_DEFAULT;
4154   GfxDir[x][y] = MovDir[x][y];
4155   GfxFrame[x][y] = 0;
4156
4157 #if USE_GFX_RESET_GFX_ANIMATION
4158   ResetGfxFrame(x, y, FALSE);
4159 #endif
4160 }
4161
4162 static void ResetRandomAnimationValue(int x, int y)
4163 {
4164   GfxRandom[x][y] = INIT_GFX_RANDOM();
4165 }
4166
4167 void InitMovingField(int x, int y, int direction)
4168 {
4169   int element = Feld[x][y];
4170   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4171   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4172   int newx = x + dx;
4173   int newy = y + dy;
4174   boolean is_moving_before, is_moving_after;
4175 #if 0
4176   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4177 #endif
4178
4179   /* check if element was/is moving or being moved before/after mode change */
4180 #if 1
4181 #if 1
4182   is_moving_before = (WasJustMoving[x][y] != 0);
4183 #else
4184   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4185   is_moving_before = WasJustMoving[x][y];
4186 #endif
4187 #else
4188   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4189 #endif
4190   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4191
4192   /* reset animation only for moving elements which change direction of moving
4193      or which just started or stopped moving
4194      (else CEs with property "can move" / "not moving" are reset each frame) */
4195 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4196 #if 1
4197   if (is_moving_before != is_moving_after ||
4198       direction != MovDir[x][y])
4199     ResetGfxAnimation(x, y);
4200 #else
4201   if ((is_moving_before || is_moving_after) && !continues_moving)
4202     ResetGfxAnimation(x, y);
4203 #endif
4204 #else
4205   if (!continues_moving)
4206     ResetGfxAnimation(x, y);
4207 #endif
4208
4209   MovDir[x][y] = direction;
4210   GfxDir[x][y] = direction;
4211
4212 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4213   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4214                      direction == MV_DOWN && CAN_FALL(element) ?
4215                      ACTION_FALLING : ACTION_MOVING);
4216 #else
4217   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4218                      ACTION_FALLING : ACTION_MOVING);
4219 #endif
4220
4221   /* this is needed for CEs with property "can move" / "not moving" */
4222
4223   if (is_moving_after)
4224   {
4225     if (Feld[newx][newy] == EL_EMPTY)
4226       Feld[newx][newy] = EL_BLOCKED;
4227
4228     MovDir[newx][newy] = MovDir[x][y];
4229
4230 #if USE_NEW_CUSTOM_VALUE
4231     CustomValue[newx][newy] = CustomValue[x][y];
4232 #endif
4233
4234     GfxFrame[newx][newy] = GfxFrame[x][y];
4235     GfxRandom[newx][newy] = GfxRandom[x][y];
4236     GfxAction[newx][newy] = GfxAction[x][y];
4237     GfxDir[newx][newy] = GfxDir[x][y];
4238   }
4239 }
4240
4241 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4242 {
4243   int direction = MovDir[x][y];
4244   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4245   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4246
4247   *goes_to_x = newx;
4248   *goes_to_y = newy;
4249 }
4250
4251 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4252 {
4253   int oldx = x, oldy = y;
4254   int direction = MovDir[x][y];
4255
4256   if (direction == MV_LEFT)
4257     oldx++;
4258   else if (direction == MV_RIGHT)
4259     oldx--;
4260   else if (direction == MV_UP)
4261     oldy++;
4262   else if (direction == MV_DOWN)
4263     oldy--;
4264
4265   *comes_from_x = oldx;
4266   *comes_from_y = oldy;
4267 }
4268
4269 int MovingOrBlocked2Element(int x, int y)
4270 {
4271   int element = Feld[x][y];
4272
4273   if (element == EL_BLOCKED)
4274   {
4275     int oldx, oldy;
4276
4277     Blocked2Moving(x, y, &oldx, &oldy);
4278     return Feld[oldx][oldy];
4279   }
4280   else
4281     return element;
4282 }
4283
4284 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4285 {
4286   /* like MovingOrBlocked2Element(), but if element is moving
4287      and (x,y) is the field the moving element is just leaving,
4288      return EL_BLOCKED instead of the element value */
4289   int element = Feld[x][y];
4290
4291   if (IS_MOVING(x, y))
4292   {
4293     if (element == EL_BLOCKED)
4294     {
4295       int oldx, oldy;
4296
4297       Blocked2Moving(x, y, &oldx, &oldy);
4298       return Feld[oldx][oldy];
4299     }
4300     else
4301       return EL_BLOCKED;
4302   }
4303   else
4304     return element;
4305 }
4306
4307 static void RemoveField(int x, int y)
4308 {
4309   Feld[x][y] = EL_EMPTY;
4310
4311   MovPos[x][y] = 0;
4312   MovDir[x][y] = 0;
4313   MovDelay[x][y] = 0;
4314
4315 #if USE_NEW_CUSTOM_VALUE
4316   CustomValue[x][y] = 0;
4317 #endif
4318
4319   AmoebaNr[x][y] = 0;
4320   ChangeDelay[x][y] = 0;
4321   ChangePage[x][y] = -1;
4322   Pushed[x][y] = FALSE;
4323
4324 #if 0
4325   ExplodeField[x][y] = EX_TYPE_NONE;
4326 #endif
4327
4328   GfxElement[x][y] = EL_UNDEFINED;
4329   GfxAction[x][y] = ACTION_DEFAULT;
4330   GfxDir[x][y] = MV_NONE;
4331 }
4332
4333 void RemoveMovingField(int x, int y)
4334 {
4335   int oldx = x, oldy = y, newx = x, newy = y;
4336   int element = Feld[x][y];
4337   int next_element = EL_UNDEFINED;
4338
4339   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4340     return;
4341
4342   if (IS_MOVING(x, y))
4343   {
4344     Moving2Blocked(x, y, &newx, &newy);
4345
4346     if (Feld[newx][newy] != EL_BLOCKED)
4347     {
4348       /* element is moving, but target field is not free (blocked), but
4349          already occupied by something different (example: acid pool);
4350          in this case, only remove the moving field, but not the target */
4351
4352       RemoveField(oldx, oldy);
4353
4354       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4355
4356       DrawLevelField(oldx, oldy);
4357
4358       return;
4359     }
4360   }
4361   else if (element == EL_BLOCKED)
4362   {
4363     Blocked2Moving(x, y, &oldx, &oldy);
4364     if (!IS_MOVING(oldx, oldy))
4365       return;
4366   }
4367
4368   if (element == EL_BLOCKED &&
4369       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4370        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4371        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4372        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4373        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4374        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4375     next_element = get_next_element(Feld[oldx][oldy]);
4376
4377   RemoveField(oldx, oldy);
4378   RemoveField(newx, newy);
4379
4380   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4381
4382   if (next_element != EL_UNDEFINED)
4383     Feld[oldx][oldy] = next_element;
4384
4385   DrawLevelField(oldx, oldy);
4386   DrawLevelField(newx, newy);
4387 }
4388
4389 void DrawDynamite(int x, int y)
4390 {
4391   int sx = SCREENX(x), sy = SCREENY(y);
4392   int graphic = el2img(Feld[x][y]);
4393   int frame;
4394
4395   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4396     return;
4397
4398   if (IS_WALKABLE_INSIDE(Back[x][y]))
4399     return;
4400
4401   if (Back[x][y])
4402     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4403   else if (Store[x][y])
4404     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4405
4406   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4407
4408   if (Back[x][y] || Store[x][y])
4409     DrawGraphicThruMask(sx, sy, graphic, frame);
4410   else
4411     DrawGraphic(sx, sy, graphic, frame);
4412 }
4413
4414 void CheckDynamite(int x, int y)
4415 {
4416   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4417   {
4418     MovDelay[x][y]--;
4419
4420     if (MovDelay[x][y] != 0)
4421     {
4422       DrawDynamite(x, y);
4423       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4424
4425       return;
4426     }
4427   }
4428
4429   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4430
4431   Bang(x, y);
4432 }
4433
4434 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4435 {
4436   boolean num_checked_players = 0;
4437   int i;
4438
4439   for (i = 0; i < MAX_PLAYERS; i++)
4440   {
4441     if (stored_player[i].active)
4442     {
4443       int sx = stored_player[i].jx;
4444       int sy = stored_player[i].jy;
4445
4446       if (num_checked_players == 0)
4447       {
4448         *sx1 = *sx2 = sx;
4449         *sy1 = *sy2 = sy;
4450       }
4451       else
4452       {
4453         *sx1 = MIN(*sx1, sx);
4454         *sy1 = MIN(*sy1, sy);
4455         *sx2 = MAX(*sx2, sx);
4456         *sy2 = MAX(*sy2, sy);
4457       }
4458
4459       num_checked_players++;
4460     }
4461   }
4462 }
4463
4464 static boolean checkIfAllPlayersFitToScreen_RND()
4465 {
4466   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4467
4468   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4469
4470   return (sx2 - sx1 < SCR_FIELDX &&
4471           sy2 - sy1 < SCR_FIELDY);
4472 }
4473
4474 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4475 {
4476   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4477
4478   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4479
4480   *sx = (sx1 + sx2) / 2;
4481   *sy = (sy1 + sy2) / 2;
4482 }
4483
4484 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4485                         boolean center_screen, boolean quick_relocation)
4486 {
4487   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4488   boolean no_delay = (tape.warp_forward);
4489   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4490   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4491
4492   if (quick_relocation)
4493   {
4494     int offset = (setup.scroll_delay ? 3 : 0);
4495
4496     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4497     {
4498       if (!level.shifted_relocation || center_screen)
4499       {
4500         /* quick relocation (without scrolling), with centering of screen */
4501
4502         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4503                     x > SBX_Right + MIDPOSX ? SBX_Right :
4504                     x - MIDPOSX);
4505
4506         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4507                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4508                     y - MIDPOSY);
4509       }
4510       else
4511       {
4512         /* quick relocation (without scrolling), but do not center screen */
4513
4514         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4515                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4516                                old_x - MIDPOSX);
4517
4518         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4519                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4520                                old_y - MIDPOSY);
4521
4522         int offset_x = x + (scroll_x - center_scroll_x);
4523         int offset_y = y + (scroll_y - center_scroll_y);
4524
4525         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4526                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4527                     offset_x - MIDPOSX);
4528
4529         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4530                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4531                     offset_y - MIDPOSY);
4532       }
4533     }
4534     else
4535     {
4536       /* quick relocation (without scrolling), inside visible screen area */
4537
4538       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
4539           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4540         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4541
4542       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
4543           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4544         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4545
4546       /* don't scroll over playfield boundaries */
4547       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4548         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4549
4550       /* don't scroll over playfield boundaries */
4551       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4552         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4553     }
4554
4555     RedrawPlayfield(TRUE, 0,0,0,0);
4556   }
4557   else
4558   {
4559 #if 1
4560     int scroll_xx, scroll_yy;
4561
4562     if (!level.shifted_relocation || center_screen)
4563     {
4564       /* visible relocation (with scrolling), with centering of screen */
4565
4566       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4567                    x > SBX_Right + MIDPOSX ? SBX_Right :
4568                    x - MIDPOSX);
4569
4570       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4571                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
4572                    y - MIDPOSY);
4573     }
4574     else
4575     {
4576       /* visible relocation (with scrolling), but do not center screen */
4577
4578       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4579                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
4580                              old_x - MIDPOSX);
4581
4582       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4583                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4584                              old_y - MIDPOSY);
4585
4586       int offset_x = x + (scroll_x - center_scroll_x);
4587       int offset_y = y + (scroll_y - center_scroll_y);
4588
4589       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4590                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4591                    offset_x - MIDPOSX);
4592
4593       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4594                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4595                    offset_y - MIDPOSY);
4596     }
4597
4598 #else
4599
4600     /* visible relocation (with scrolling), with centering of screen */
4601
4602     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4603                      x > SBX_Right + MIDPOSX ? SBX_Right :
4604                      x - MIDPOSX);
4605
4606     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4607                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
4608                      y - MIDPOSY);
4609 #endif
4610
4611     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
4612
4613     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4614     {
4615       int dx = 0, dy = 0;
4616       int fx = FX, fy = FY;
4617
4618       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4619       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4620
4621       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
4622         break;
4623
4624       scroll_x -= dx;
4625       scroll_y -= dy;
4626
4627       fx += dx * TILEX / 2;
4628       fy += dy * TILEY / 2;
4629
4630       ScrollLevel(dx, dy);
4631       DrawAllPlayers();
4632
4633       /* scroll in two steps of half tile size to make things smoother */
4634       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4635       FlushDisplay();
4636       Delay(wait_delay_value);
4637
4638       /* scroll second step to align at full tile size */
4639       BackToFront();
4640       Delay(wait_delay_value);
4641     }
4642
4643     DrawAllPlayers();
4644     BackToFront();
4645     Delay(wait_delay_value);
4646   }
4647 }
4648
4649 void RelocatePlayer(int jx, int jy, int el_player_raw)
4650 {
4651   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4652   int player_nr = GET_PLAYER_NR(el_player);
4653   struct PlayerInfo *player = &stored_player[player_nr];
4654   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4655   boolean no_delay = (tape.warp_forward);
4656   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4657   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4658   int old_jx = player->jx;
4659   int old_jy = player->jy;
4660   int old_element = Feld[old_jx][old_jy];
4661   int element = Feld[jx][jy];
4662   boolean player_relocated = (old_jx != jx || old_jy != jy);
4663
4664   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4665   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
4666   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4667   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
4668   int leave_side_horiz = move_dir_horiz;
4669   int leave_side_vert  = move_dir_vert;
4670   int enter_side = enter_side_horiz | enter_side_vert;
4671   int leave_side = leave_side_horiz | leave_side_vert;
4672
4673   if (player->GameOver)         /* do not reanimate dead player */
4674     return;
4675
4676   if (!player_relocated)        /* no need to relocate the player */
4677     return;
4678
4679   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
4680   {
4681     RemoveField(jx, jy);        /* temporarily remove newly placed player */
4682     DrawLevelField(jx, jy);
4683   }
4684
4685   if (player->present)
4686   {
4687     while (player->MovPos)
4688     {
4689       ScrollPlayer(player, SCROLL_GO_ON);
4690       ScrollScreen(NULL, SCROLL_GO_ON);
4691
4692       AdvanceFrameAndPlayerCounters(player->index_nr);
4693
4694       DrawPlayer(player);
4695
4696       BackToFront();
4697       Delay(wait_delay_value);
4698     }
4699
4700     DrawPlayer(player);         /* needed here only to cleanup last field */
4701     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
4702
4703     player->is_moving = FALSE;
4704   }
4705
4706   if (IS_CUSTOM_ELEMENT(old_element))
4707     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4708                                CE_LEFT_BY_PLAYER,
4709                                player->index_bit, leave_side);
4710
4711   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4712                                       CE_PLAYER_LEAVES_X,
4713                                       player->index_bit, leave_side);
4714
4715   Feld[jx][jy] = el_player;
4716   InitPlayerField(jx, jy, el_player, TRUE);
4717
4718   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4719   {
4720     Feld[jx][jy] = element;
4721     InitField(jx, jy, FALSE);
4722   }
4723
4724   /* only visually relocate centered player */
4725   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4726                      FALSE, level.instant_relocation);
4727
4728   TestIfPlayerTouchesBadThing(jx, jy);
4729   TestIfPlayerTouchesCustomElement(jx, jy);
4730
4731   if (IS_CUSTOM_ELEMENT(element))
4732     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4733                                player->index_bit, enter_side);
4734
4735   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4736                                       player->index_bit, enter_side);
4737 }
4738
4739 void Explode(int ex, int ey, int phase, int mode)
4740 {
4741   int x, y;
4742   int last_phase;
4743   int border_element;
4744
4745   /* !!! eliminate this variable !!! */
4746   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4747
4748   if (game.explosions_delayed)
4749   {
4750     ExplodeField[ex][ey] = mode;
4751     return;
4752   }
4753
4754   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
4755   {
4756     int center_element = Feld[ex][ey];
4757     int artwork_element, explosion_element;     /* set these values later */
4758
4759 #if 0
4760     /* --- This is only really needed (and now handled) in "Impact()". --- */
4761     /* do not explode moving elements that left the explode field in time */
4762     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4763         center_element == EL_EMPTY &&
4764         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4765       return;
4766 #endif
4767
4768 #if 0
4769     /* !!! at this place, the center element may be EL_BLOCKED !!! */
4770     if (mode == EX_TYPE_NORMAL ||
4771         mode == EX_TYPE_CENTER ||
4772         mode == EX_TYPE_CROSS)
4773       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4774 #endif
4775
4776     /* remove things displayed in background while burning dynamite */
4777     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4778       Back[ex][ey] = 0;
4779
4780     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4781     {
4782       /* put moving element to center field (and let it explode there) */
4783       center_element = MovingOrBlocked2Element(ex, ey);
4784       RemoveMovingField(ex, ey);
4785       Feld[ex][ey] = center_element;
4786     }
4787
4788     /* now "center_element" is finally determined -- set related values now */
4789     artwork_element = center_element;           /* for custom player artwork */
4790     explosion_element = center_element;         /* for custom player artwork */
4791
4792     if (IS_PLAYER(ex, ey))
4793     {
4794       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4795
4796       artwork_element = stored_player[player_nr].artwork_element;
4797
4798       if (level.use_explosion_element[player_nr])
4799       {
4800         explosion_element = level.explosion_element[player_nr];
4801         artwork_element = explosion_element;
4802       }
4803     }
4804
4805 #if 1
4806     if (mode == EX_TYPE_NORMAL ||
4807         mode == EX_TYPE_CENTER ||
4808         mode == EX_TYPE_CROSS)
4809       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4810 #endif
4811
4812     last_phase = element_info[explosion_element].explosion_delay + 1;
4813
4814     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4815     {
4816       int xx = x - ex + 1;
4817       int yy = y - ey + 1;
4818       int element;
4819
4820       if (!IN_LEV_FIELD(x, y) ||
4821           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4822           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
4823         continue;
4824
4825       element = Feld[x][y];
4826
4827       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4828       {
4829         element = MovingOrBlocked2Element(x, y);
4830
4831         if (!IS_EXPLOSION_PROOF(element))
4832           RemoveMovingField(x, y);
4833       }
4834
4835       /* indestructible elements can only explode in center (but not flames) */
4836       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4837                                            mode == EX_TYPE_BORDER)) ||
4838           element == EL_FLAMES)
4839         continue;
4840
4841       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4842          behaviour, for example when touching a yamyam that explodes to rocks
4843          with active deadly shield, a rock is created under the player !!! */
4844       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4845 #if 0
4846       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4847           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
4848            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
4849 #else
4850       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
4851 #endif
4852       {
4853         if (IS_ACTIVE_BOMB(element))
4854         {
4855           /* re-activate things under the bomb like gate or penguin */
4856           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
4857           Back[x][y] = 0;
4858         }
4859
4860         continue;
4861       }
4862
4863       /* save walkable background elements while explosion on same tile */
4864       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
4865           (x != ex || y != ey || mode == EX_TYPE_BORDER))
4866         Back[x][y] = element;
4867
4868       /* ignite explodable elements reached by other explosion */
4869       if (element == EL_EXPLOSION)
4870         element = Store2[x][y];
4871
4872       if (AmoebaNr[x][y] &&
4873           (element == EL_AMOEBA_FULL ||
4874            element == EL_BD_AMOEBA ||
4875            element == EL_AMOEBA_GROWING))
4876       {
4877         AmoebaCnt[AmoebaNr[x][y]]--;
4878         AmoebaCnt2[AmoebaNr[x][y]]--;
4879       }
4880
4881       RemoveField(x, y);
4882
4883       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
4884       {
4885         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
4886
4887         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
4888
4889         if (PLAYERINFO(ex, ey)->use_murphy)
4890           Store[x][y] = EL_EMPTY;
4891       }
4892
4893       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
4894          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
4895       else if (ELEM_IS_PLAYER(center_element))
4896         Store[x][y] = EL_EMPTY;
4897       else if (center_element == EL_YAMYAM)
4898         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4899       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4900         Store[x][y] = element_info[center_element].content.e[xx][yy];
4901 #if 1
4902       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4903          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4904          otherwise) -- FIX THIS !!! */
4905       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4906         Store[x][y] = element_info[element].content.e[1][1];
4907 #else
4908       else if (!CAN_EXPLODE(element))
4909         Store[x][y] = element_info[element].content.e[1][1];
4910 #endif
4911       else
4912         Store[x][y] = EL_EMPTY;
4913
4914       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4915           center_element == EL_AMOEBA_TO_DIAMOND)
4916         Store2[x][y] = element;
4917
4918       Feld[x][y] = EL_EXPLOSION;
4919       GfxElement[x][y] = artwork_element;
4920
4921       ExplodePhase[x][y] = 1;
4922       ExplodeDelay[x][y] = last_phase;
4923
4924       Stop[x][y] = TRUE;
4925     }
4926
4927     if (center_element == EL_YAMYAM)
4928       game.yamyam_content_nr =
4929         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4930
4931     return;
4932   }
4933
4934   if (Stop[ex][ey])
4935     return;
4936
4937   x = ex;
4938   y = ey;
4939
4940   if (phase == 1)
4941     GfxFrame[x][y] = 0;         /* restart explosion animation */
4942
4943   last_phase = ExplodeDelay[x][y];
4944
4945   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4946
4947 #ifdef DEBUG
4948
4949   /* activate this even in non-DEBUG version until cause for crash in
4950      getGraphicAnimationFrame() (see below) is found and eliminated */
4951
4952 #endif
4953 #if 1
4954
4955 #if 1
4956   /* this can happen if the player leaves an explosion just in time */
4957   if (GfxElement[x][y] == EL_UNDEFINED)
4958     GfxElement[x][y] = EL_EMPTY;
4959 #else
4960   if (GfxElement[x][y] == EL_UNDEFINED)
4961   {
4962     printf("\n\n");
4963     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4964     printf("Explode(): This should never happen!\n");
4965     printf("\n\n");
4966
4967     GfxElement[x][y] = EL_EMPTY;
4968   }
4969 #endif
4970
4971 #endif
4972
4973   border_element = Store2[x][y];
4974   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4975     border_element = StorePlayer[x][y];
4976
4977   if (phase == element_info[border_element].ignition_delay ||
4978       phase == last_phase)
4979   {
4980     boolean border_explosion = FALSE;
4981
4982     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4983         !PLAYER_EXPLOSION_PROTECTED(x, y))
4984     {
4985       KillPlayerUnlessExplosionProtected(x, y);
4986       border_explosion = TRUE;
4987     }
4988     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4989     {
4990       Feld[x][y] = Store2[x][y];
4991       Store2[x][y] = 0;
4992       Bang(x, y);
4993       border_explosion = TRUE;
4994     }
4995     else if (border_element == EL_AMOEBA_TO_DIAMOND)
4996     {
4997       AmoebeUmwandeln(x, y);
4998       Store2[x][y] = 0;
4999       border_explosion = TRUE;
5000     }
5001
5002     /* if an element just explodes due to another explosion (chain-reaction),
5003        do not immediately end the new explosion when it was the last frame of
5004        the explosion (as it would be done in the following "if"-statement!) */
5005     if (border_explosion && phase == last_phase)
5006       return;
5007   }
5008
5009   if (phase == last_phase)
5010   {
5011     int element;
5012
5013     element = Feld[x][y] = Store[x][y];
5014     Store[x][y] = Store2[x][y] = 0;
5015     GfxElement[x][y] = EL_UNDEFINED;
5016
5017     /* player can escape from explosions and might therefore be still alive */
5018     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5019         element <= EL_PLAYER_IS_EXPLODING_4)
5020     {
5021       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5022       int explosion_element = EL_PLAYER_1 + player_nr;
5023       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5024       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5025
5026       if (level.use_explosion_element[player_nr])
5027         explosion_element = level.explosion_element[player_nr];
5028
5029       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5030                     element_info[explosion_element].content.e[xx][yy]);
5031     }
5032
5033     /* restore probably existing indestructible background element */
5034     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5035       element = Feld[x][y] = Back[x][y];
5036     Back[x][y] = 0;
5037
5038     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5039     GfxDir[x][y] = MV_NONE;
5040     ChangeDelay[x][y] = 0;
5041     ChangePage[x][y] = -1;
5042
5043 #if USE_NEW_CUSTOM_VALUE
5044     CustomValue[x][y] = 0;
5045 #endif
5046
5047     InitField_WithBug2(x, y, FALSE);
5048
5049     DrawLevelField(x, y);
5050
5051     TestIfElementTouchesCustomElement(x, y);
5052
5053     if (GFX_CRUMBLED(element))
5054       DrawLevelFieldCrumbledSandNeighbours(x, y);
5055
5056     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5057       StorePlayer[x][y] = 0;
5058
5059     if (ELEM_IS_PLAYER(element))
5060       RelocatePlayer(x, y, element);
5061   }
5062   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5063   {
5064     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5065     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5066
5067     if (phase == delay)
5068       DrawLevelFieldCrumbledSand(x, y);
5069
5070     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5071     {
5072       DrawLevelElement(x, y, Back[x][y]);
5073       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5074     }
5075     else if (IS_WALKABLE_UNDER(Back[x][y]))
5076     {
5077       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5078       DrawLevelElementThruMask(x, y, Back[x][y]);
5079     }
5080     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5081       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5082   }
5083 }
5084
5085 void DynaExplode(int ex, int ey)
5086 {
5087   int i, j;
5088   int dynabomb_element = Feld[ex][ey];
5089   int dynabomb_size = 1;
5090   boolean dynabomb_xl = FALSE;
5091   struct PlayerInfo *player;
5092   static int xy[4][2] =
5093   {
5094     { 0, -1 },
5095     { -1, 0 },
5096     { +1, 0 },
5097     { 0, +1 }
5098   };
5099
5100   if (IS_ACTIVE_BOMB(dynabomb_element))
5101   {
5102     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5103     dynabomb_size = player->dynabomb_size;
5104     dynabomb_xl = player->dynabomb_xl;
5105     player->dynabombs_left++;
5106   }
5107
5108   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5109
5110   for (i = 0; i < NUM_DIRECTIONS; i++)
5111   {
5112     for (j = 1; j <= dynabomb_size; j++)
5113     {
5114       int x = ex + j * xy[i][0];
5115       int y = ey + j * xy[i][1];
5116       int element;
5117
5118       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5119         break;
5120
5121       element = Feld[x][y];
5122
5123       /* do not restart explosions of fields with active bombs */
5124       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5125         continue;
5126
5127       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5128
5129       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5130           !IS_DIGGABLE(element) && !dynabomb_xl)
5131         break;
5132     }
5133   }
5134 }
5135
5136 void Bang(int x, int y)
5137 {
5138   int element = MovingOrBlocked2Element(x, y);
5139   int explosion_type = EX_TYPE_NORMAL;
5140
5141   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5142   {
5143     struct PlayerInfo *player = PLAYERINFO(x, y);
5144
5145     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5146                             player->element_nr);
5147
5148     if (level.use_explosion_element[player->index_nr])
5149     {
5150       int explosion_element = level.explosion_element[player->index_nr];
5151
5152       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5153         explosion_type = EX_TYPE_CROSS;
5154       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5155         explosion_type = EX_TYPE_CENTER;
5156     }
5157   }
5158
5159   switch (element)
5160   {
5161     case EL_BUG:
5162     case EL_SPACESHIP:
5163     case EL_BD_BUTTERFLY:
5164     case EL_BD_FIREFLY:
5165     case EL_YAMYAM:
5166     case EL_DARK_YAMYAM:
5167     case EL_ROBOT:
5168     case EL_PACMAN:
5169     case EL_MOLE:
5170       RaiseScoreElement(element);
5171       break;
5172
5173     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5174     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5175     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5176     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5177     case EL_DYNABOMB_INCREASE_NUMBER:
5178     case EL_DYNABOMB_INCREASE_SIZE:
5179     case EL_DYNABOMB_INCREASE_POWER:
5180       explosion_type = EX_TYPE_DYNA;
5181       break;
5182
5183     case EL_DC_LANDMINE:
5184 #if 0
5185     case EL_EM_EXIT_OPEN:
5186     case EL_EM_STEEL_EXIT_OPEN:
5187 #endif
5188       explosion_type = EX_TYPE_CENTER;
5189       break;
5190
5191     case EL_PENGUIN:
5192     case EL_LAMP:
5193     case EL_LAMP_ACTIVE:
5194     case EL_AMOEBA_TO_DIAMOND:
5195       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5196         explosion_type = EX_TYPE_CENTER;
5197       break;
5198
5199     default:
5200       if (element_info[element].explosion_type == EXPLODES_CROSS)
5201         explosion_type = EX_TYPE_CROSS;
5202       else if (element_info[element].explosion_type == EXPLODES_1X1)
5203         explosion_type = EX_TYPE_CENTER;
5204       break;
5205   }
5206
5207   if (explosion_type == EX_TYPE_DYNA)
5208     DynaExplode(x, y);
5209   else
5210     Explode(x, y, EX_PHASE_START, explosion_type);
5211
5212   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5213 }
5214
5215 void SplashAcid(int x, int y)
5216 {
5217   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5218       (!IN_LEV_FIELD(x - 1, y - 2) ||
5219        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5220     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5221
5222   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5223       (!IN_LEV_FIELD(x + 1, y - 2) ||
5224        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5225     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5226
5227   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5228 }
5229
5230 static void InitBeltMovement()
5231 {
5232   static int belt_base_element[4] =
5233   {
5234     EL_CONVEYOR_BELT_1_LEFT,
5235     EL_CONVEYOR_BELT_2_LEFT,
5236     EL_CONVEYOR_BELT_3_LEFT,
5237     EL_CONVEYOR_BELT_4_LEFT
5238   };
5239   static int belt_base_active_element[4] =
5240   {
5241     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5242     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5243     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5244     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5245   };
5246
5247   int x, y, i, j;
5248
5249   /* set frame order for belt animation graphic according to belt direction */
5250   for (i = 0; i < NUM_BELTS; i++)
5251   {
5252     int belt_nr = i;
5253
5254     for (j = 0; j < NUM_BELT_PARTS; j++)
5255     {
5256       int element = belt_base_active_element[belt_nr] + j;
5257       int graphic = el2img(element);
5258
5259       if (game.belt_dir[i] == MV_LEFT)
5260         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5261       else
5262         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
5263     }
5264   }
5265
5266   SCAN_PLAYFIELD(x, y)
5267   {
5268     int element = Feld[x][y];
5269
5270     for (i = 0; i < NUM_BELTS; i++)
5271     {
5272       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5273       {
5274         int e_belt_nr = getBeltNrFromBeltElement(element);
5275         int belt_nr = i;
5276
5277         if (e_belt_nr == belt_nr)
5278         {
5279           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5280
5281           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5282         }
5283       }
5284     }
5285   }
5286 }
5287
5288 static void ToggleBeltSwitch(int x, int y)
5289 {
5290   static int belt_base_element[4] =
5291   {
5292     EL_CONVEYOR_BELT_1_LEFT,
5293     EL_CONVEYOR_BELT_2_LEFT,
5294     EL_CONVEYOR_BELT_3_LEFT,
5295     EL_CONVEYOR_BELT_4_LEFT
5296   };
5297   static int belt_base_active_element[4] =
5298   {
5299     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5300     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5301     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5302     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5303   };
5304   static int belt_base_switch_element[4] =
5305   {
5306     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5307     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5308     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5309     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5310   };
5311   static int belt_move_dir[4] =
5312   {
5313     MV_LEFT,
5314     MV_NONE,
5315     MV_RIGHT,
5316     MV_NONE,
5317   };
5318
5319   int element = Feld[x][y];
5320   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5321   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5322   int belt_dir = belt_move_dir[belt_dir_nr];
5323   int xx, yy, i;
5324
5325   if (!IS_BELT_SWITCH(element))
5326     return;
5327
5328   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5329   game.belt_dir[belt_nr] = belt_dir;
5330
5331   if (belt_dir_nr == 3)
5332     belt_dir_nr = 1;
5333
5334   /* set frame order for belt animation graphic according to belt direction */
5335   for (i = 0; i < NUM_BELT_PARTS; i++)
5336   {
5337     int element = belt_base_active_element[belt_nr] + i;
5338     int graphic = el2img(element);
5339
5340     if (belt_dir == MV_LEFT)
5341       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5342     else
5343       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
5344   }
5345
5346   SCAN_PLAYFIELD(xx, yy)
5347   {
5348     int element = Feld[xx][yy];
5349
5350     if (IS_BELT_SWITCH(element))
5351     {
5352       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5353
5354       if (e_belt_nr == belt_nr)
5355       {
5356         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5357         DrawLevelField(xx, yy);
5358       }
5359     }
5360     else if (IS_BELT(element) && belt_dir != MV_NONE)
5361     {
5362       int e_belt_nr = getBeltNrFromBeltElement(element);
5363
5364       if (e_belt_nr == belt_nr)
5365       {
5366         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5367
5368         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5369         DrawLevelField(xx, yy);
5370       }
5371     }
5372     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5373     {
5374       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5375
5376       if (e_belt_nr == belt_nr)
5377       {
5378         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5379
5380         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5381         DrawLevelField(xx, yy);
5382       }
5383     }
5384   }
5385 }
5386
5387 static void ToggleSwitchgateSwitch(int x, int y)
5388 {
5389   int xx, yy;
5390
5391   game.switchgate_pos = !game.switchgate_pos;
5392
5393   SCAN_PLAYFIELD(xx, yy)
5394   {
5395     int element = Feld[xx][yy];
5396
5397 #if !USE_BOTH_SWITCHGATE_SWITCHES
5398     if (element == EL_SWITCHGATE_SWITCH_UP ||
5399         element == EL_SWITCHGATE_SWITCH_DOWN)
5400     {
5401       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5402       DrawLevelField(xx, yy);
5403     }
5404     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5405              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5406     {
5407       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5408       DrawLevelField(xx, yy);
5409     }
5410 #else
5411     if (element == EL_SWITCHGATE_SWITCH_UP)
5412     {
5413       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5414       DrawLevelField(xx, yy);
5415     }
5416     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5417     {
5418       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5419       DrawLevelField(xx, yy);
5420     }
5421     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5422     {
5423       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5424       DrawLevelField(xx, yy);
5425     }
5426     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5427     {
5428       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5429       DrawLevelField(xx, yy);
5430     }
5431 #endif
5432     else if (element == EL_SWITCHGATE_OPEN ||
5433              element == EL_SWITCHGATE_OPENING)
5434     {
5435       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5436
5437       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5438     }
5439     else if (element == EL_SWITCHGATE_CLOSED ||
5440              element == EL_SWITCHGATE_CLOSING)
5441     {
5442       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5443
5444       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5445     }
5446   }
5447 }
5448
5449 static int getInvisibleActiveFromInvisibleElement(int element)
5450 {
5451   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5452           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5453           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5454           element);
5455 }
5456
5457 static int getInvisibleFromInvisibleActiveElement(int element)
5458 {
5459   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5460           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5461           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5462           element);
5463 }
5464
5465 static void RedrawAllLightSwitchesAndInvisibleElements()
5466 {
5467   int x, y;
5468
5469   SCAN_PLAYFIELD(x, y)
5470   {
5471     int element = Feld[x][y];
5472
5473     if (element == EL_LIGHT_SWITCH &&
5474         game.light_time_left > 0)
5475     {
5476       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5477       DrawLevelField(x, y);
5478     }
5479     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5480              game.light_time_left == 0)
5481     {
5482       Feld[x][y] = EL_LIGHT_SWITCH;
5483       DrawLevelField(x, y);
5484     }
5485     else if (element == EL_EMC_DRIPPER &&
5486              game.light_time_left > 0)
5487     {
5488       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5489       DrawLevelField(x, y);
5490     }
5491     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5492              game.light_time_left == 0)
5493     {
5494       Feld[x][y] = EL_EMC_DRIPPER;
5495       DrawLevelField(x, y);
5496     }
5497     else if (element == EL_INVISIBLE_STEELWALL ||
5498              element == EL_INVISIBLE_WALL ||
5499              element == EL_INVISIBLE_SAND)
5500     {
5501       if (game.light_time_left > 0)
5502         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5503
5504       DrawLevelField(x, y);
5505
5506       /* uncrumble neighbour fields, if needed */
5507       if (element == EL_INVISIBLE_SAND)
5508         DrawLevelFieldCrumbledSandNeighbours(x, y);
5509     }
5510     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5511              element == EL_INVISIBLE_WALL_ACTIVE ||
5512              element == EL_INVISIBLE_SAND_ACTIVE)
5513     {
5514       if (game.light_time_left == 0)
5515         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5516
5517       DrawLevelField(x, y);
5518
5519       /* re-crumble neighbour fields, if needed */
5520       if (element == EL_INVISIBLE_SAND)
5521         DrawLevelFieldCrumbledSandNeighbours(x, y);
5522     }
5523   }
5524 }
5525
5526 static void RedrawAllInvisibleElementsForLenses()
5527 {
5528   int x, y;
5529
5530   SCAN_PLAYFIELD(x, y)
5531   {
5532     int element = Feld[x][y];
5533
5534     if (element == EL_EMC_DRIPPER &&
5535         game.lenses_time_left > 0)
5536     {
5537       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5538       DrawLevelField(x, y);
5539     }
5540     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5541              game.lenses_time_left == 0)
5542     {
5543       Feld[x][y] = EL_EMC_DRIPPER;
5544       DrawLevelField(x, y);
5545     }
5546     else if (element == EL_INVISIBLE_STEELWALL ||
5547              element == EL_INVISIBLE_WALL ||
5548              element == EL_INVISIBLE_SAND)
5549     {
5550       if (game.lenses_time_left > 0)
5551         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5552
5553       DrawLevelField(x, y);
5554
5555       /* uncrumble neighbour fields, if needed */
5556       if (element == EL_INVISIBLE_SAND)
5557         DrawLevelFieldCrumbledSandNeighbours(x, y);
5558     }
5559     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5560              element == EL_INVISIBLE_WALL_ACTIVE ||
5561              element == EL_INVISIBLE_SAND_ACTIVE)
5562     {
5563       if (game.lenses_time_left == 0)
5564         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5565
5566       DrawLevelField(x, y);
5567
5568       /* re-crumble neighbour fields, if needed */
5569       if (element == EL_INVISIBLE_SAND)
5570         DrawLevelFieldCrumbledSandNeighbours(x, y);
5571     }
5572   }
5573 }
5574
5575 static void RedrawAllInvisibleElementsForMagnifier()
5576 {
5577   int x, y;
5578
5579   SCAN_PLAYFIELD(x, y)
5580   {
5581     int element = Feld[x][y];
5582
5583     if (element == EL_EMC_FAKE_GRASS &&
5584         game.magnify_time_left > 0)
5585     {
5586       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5587       DrawLevelField(x, y);
5588     }
5589     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5590              game.magnify_time_left == 0)
5591     {
5592       Feld[x][y] = EL_EMC_FAKE_GRASS;
5593       DrawLevelField(x, y);
5594     }
5595     else if (IS_GATE_GRAY(element) &&
5596              game.magnify_time_left > 0)
5597     {
5598       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5599                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5600                     IS_EM_GATE_GRAY(element) ?
5601                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5602                     IS_EMC_GATE_GRAY(element) ?
5603                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5604                     element);
5605       DrawLevelField(x, y);
5606     }
5607     else if (IS_GATE_GRAY_ACTIVE(element) &&
5608              game.magnify_time_left == 0)
5609     {
5610       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5611                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5612                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5613                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5614                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5615                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5616                     element);
5617       DrawLevelField(x, y);
5618     }
5619   }
5620 }
5621
5622 static void ToggleLightSwitch(int x, int y)
5623 {
5624   int element = Feld[x][y];
5625
5626   game.light_time_left =
5627     (element == EL_LIGHT_SWITCH ?
5628      level.time_light * FRAMES_PER_SECOND : 0);
5629
5630   RedrawAllLightSwitchesAndInvisibleElements();
5631 }
5632
5633 static void ActivateTimegateSwitch(int x, int y)
5634 {
5635   int xx, yy;
5636
5637   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5638
5639   SCAN_PLAYFIELD(xx, yy)
5640   {
5641     int element = Feld[xx][yy];
5642
5643     if (element == EL_TIMEGATE_CLOSED ||
5644         element == EL_TIMEGATE_CLOSING)
5645     {
5646       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5647       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5648     }
5649
5650     /*
5651     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5652     {
5653       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5654       DrawLevelField(xx, yy);
5655     }
5656     */
5657
5658   }
5659
5660 #if 1
5661   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5662                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5663 #else
5664   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5665 #endif
5666 }
5667
5668 void Impact(int x, int y)
5669 {
5670   boolean last_line = (y == lev_fieldy - 1);
5671   boolean object_hit = FALSE;
5672   boolean impact = (last_line || object_hit);
5673   int element = Feld[x][y];
5674   int smashed = EL_STEELWALL;
5675
5676   if (!last_line)       /* check if element below was hit */
5677   {
5678     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5679       return;
5680
5681     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5682                                          MovDir[x][y + 1] != MV_DOWN ||
5683                                          MovPos[x][y + 1] <= TILEY / 2));
5684
5685     /* do not smash moving elements that left the smashed field in time */
5686     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5687         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5688       object_hit = FALSE;
5689
5690 #if USE_QUICKSAND_IMPACT_BUGFIX
5691     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5692     {
5693       RemoveMovingField(x, y + 1);
5694       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5695       Feld[x][y + 2] = EL_ROCK;
5696       DrawLevelField(x, y + 2);
5697
5698       object_hit = TRUE;
5699     }
5700
5701     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5702     {
5703       RemoveMovingField(x, y + 1);
5704       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5705       Feld[x][y + 2] = EL_ROCK;
5706       DrawLevelField(x, y + 2);
5707
5708       object_hit = TRUE;
5709     }
5710 #endif
5711
5712     if (object_hit)
5713       smashed = MovingOrBlocked2Element(x, y + 1);
5714
5715     impact = (last_line || object_hit);
5716   }
5717
5718   if (!last_line && smashed == EL_ACID) /* element falls into acid */
5719   {
5720     SplashAcid(x, y + 1);
5721     return;
5722   }
5723
5724   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5725   /* only reset graphic animation if graphic really changes after impact */
5726   if (impact &&
5727       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5728   {
5729     ResetGfxAnimation(x, y);
5730     DrawLevelField(x, y);
5731   }
5732
5733   if (impact && CAN_EXPLODE_IMPACT(element))
5734   {
5735     Bang(x, y);
5736     return;
5737   }
5738   else if (impact && element == EL_PEARL &&
5739            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5740   {
5741     ResetGfxAnimation(x, y);
5742
5743     Feld[x][y] = EL_PEARL_BREAKING;
5744     PlayLevelSound(x, y, SND_PEARL_BREAKING);
5745     return;
5746   }
5747   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5748   {
5749     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5750
5751     return;
5752   }
5753
5754   if (impact && element == EL_AMOEBA_DROP)
5755   {
5756     if (object_hit && IS_PLAYER(x, y + 1))
5757       KillPlayerUnlessEnemyProtected(x, y + 1);
5758     else if (object_hit && smashed == EL_PENGUIN)
5759       Bang(x, y + 1);
5760     else
5761     {
5762       Feld[x][y] = EL_AMOEBA_GROWING;
5763       Store[x][y] = EL_AMOEBA_WET;
5764
5765       ResetRandomAnimationValue(x, y);
5766     }
5767     return;
5768   }
5769
5770   if (object_hit)               /* check which object was hit */
5771   {
5772     if ((CAN_PASS_MAGIC_WALL(element) && 
5773          (smashed == EL_MAGIC_WALL ||
5774           smashed == EL_BD_MAGIC_WALL)) ||
5775         (CAN_PASS_DC_MAGIC_WALL(element) &&
5776          smashed == EL_DC_MAGIC_WALL))
5777     {
5778       int xx, yy;
5779       int activated_magic_wall =
5780         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5781          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5782          EL_DC_MAGIC_WALL_ACTIVE);
5783
5784       /* activate magic wall / mill */
5785       SCAN_PLAYFIELD(xx, yy)
5786       {
5787         if (Feld[xx][yy] == smashed)
5788           Feld[xx][yy] = activated_magic_wall;
5789       }
5790
5791       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5792       game.magic_wall_active = TRUE;
5793
5794       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5795                             SND_MAGIC_WALL_ACTIVATING :
5796                             smashed == EL_BD_MAGIC_WALL ?
5797                             SND_BD_MAGIC_WALL_ACTIVATING :
5798                             SND_DC_MAGIC_WALL_ACTIVATING));
5799     }
5800
5801     if (IS_PLAYER(x, y + 1))
5802     {
5803       if (CAN_SMASH_PLAYER(element))
5804       {
5805         KillPlayerUnlessEnemyProtected(x, y + 1);
5806         return;
5807       }
5808     }
5809     else if (smashed == EL_PENGUIN)
5810     {
5811       if (CAN_SMASH_PLAYER(element))
5812       {
5813         Bang(x, y + 1);
5814         return;
5815       }
5816     }
5817     else if (element == EL_BD_DIAMOND)
5818     {
5819       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5820       {
5821         Bang(x, y + 1);
5822         return;
5823       }
5824     }
5825     else if (((element == EL_SP_INFOTRON ||
5826                element == EL_SP_ZONK) &&
5827               (smashed == EL_SP_SNIKSNAK ||
5828                smashed == EL_SP_ELECTRON ||
5829                smashed == EL_SP_DISK_ORANGE)) ||
5830              (element == EL_SP_INFOTRON &&
5831               smashed == EL_SP_DISK_YELLOW))
5832     {
5833       Bang(x, y + 1);
5834       return;
5835     }
5836     else if (CAN_SMASH_EVERYTHING(element))
5837     {
5838       if (IS_CLASSIC_ENEMY(smashed) ||
5839           CAN_EXPLODE_SMASHED(smashed))
5840       {
5841         Bang(x, y + 1);
5842         return;
5843       }
5844       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5845       {
5846         if (smashed == EL_LAMP ||
5847             smashed == EL_LAMP_ACTIVE)
5848         {
5849           Bang(x, y + 1);
5850           return;
5851         }
5852         else if (smashed == EL_NUT)
5853         {
5854           Feld[x][y + 1] = EL_NUT_BREAKING;
5855           PlayLevelSound(x, y, SND_NUT_BREAKING);
5856           RaiseScoreElement(EL_NUT);
5857           return;
5858         }
5859         else if (smashed == EL_PEARL)
5860         {
5861           ResetGfxAnimation(x, y);
5862
5863           Feld[x][y + 1] = EL_PEARL_BREAKING;
5864           PlayLevelSound(x, y, SND_PEARL_BREAKING);
5865           return;
5866         }
5867         else if (smashed == EL_DIAMOND)
5868         {
5869           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
5870           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
5871           return;
5872         }
5873         else if (IS_BELT_SWITCH(smashed))
5874         {
5875           ToggleBeltSwitch(x, y + 1);
5876         }
5877         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
5878                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
5879                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
5880                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
5881         {
5882           ToggleSwitchgateSwitch(x, y + 1);
5883         }
5884         else if (smashed == EL_LIGHT_SWITCH ||
5885                  smashed == EL_LIGHT_SWITCH_ACTIVE)
5886         {
5887           ToggleLightSwitch(x, y + 1);
5888         }
5889         else
5890         {
5891 #if 0
5892           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
5893 #endif
5894
5895           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5896
5897           CheckElementChangeBySide(x, y + 1, smashed, element,
5898                                    CE_SWITCHED, CH_SIDE_TOP);
5899           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5900                                             CH_SIDE_TOP);
5901         }
5902       }
5903       else
5904       {
5905         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5906       }
5907     }
5908   }
5909
5910   /* play sound of magic wall / mill */
5911   if (!last_line &&
5912       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5913        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
5914        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
5915   {
5916     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5917       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5918     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5919       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5920     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5921       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5922
5923     return;
5924   }
5925
5926   /* play sound of object that hits the ground */
5927   if (last_line || object_hit)
5928     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5929 }
5930
5931 inline static void TurnRoundExt(int x, int y)
5932 {
5933   static struct
5934   {
5935     int dx, dy;
5936   } move_xy[] =
5937   {
5938     {  0,  0 },
5939     { -1,  0 },
5940     { +1,  0 },
5941     {  0,  0 },
5942     {  0, -1 },
5943     {  0,  0 }, { 0, 0 }, { 0, 0 },
5944     {  0, +1 }
5945   };
5946   static struct
5947   {
5948     int left, right, back;
5949   } turn[] =
5950   {
5951     { 0,        0,              0        },
5952     { MV_DOWN,  MV_UP,          MV_RIGHT },
5953     { MV_UP,    MV_DOWN,        MV_LEFT  },
5954     { 0,        0,              0        },
5955     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
5956     { 0,        0,              0        },
5957     { 0,        0,              0        },
5958     { 0,        0,              0        },
5959     { MV_RIGHT, MV_LEFT,        MV_UP    }
5960   };
5961
5962   int element = Feld[x][y];
5963   int move_pattern = element_info[element].move_pattern;
5964
5965   int old_move_dir = MovDir[x][y];
5966   int left_dir  = turn[old_move_dir].left;
5967   int right_dir = turn[old_move_dir].right;
5968   int back_dir  = turn[old_move_dir].back;
5969
5970   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
5971   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
5972   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
5973   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
5974
5975   int left_x  = x + left_dx,  left_y  = y + left_dy;
5976   int right_x = x + right_dx, right_y = y + right_dy;
5977   int move_x  = x + move_dx,  move_y  = y + move_dy;
5978
5979   int xx, yy;
5980
5981   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5982   {
5983     TestIfBadThingTouchesOtherBadThing(x, y);
5984
5985     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5986       MovDir[x][y] = right_dir;
5987     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5988       MovDir[x][y] = left_dir;
5989
5990     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5991       MovDelay[x][y] = 9;
5992     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
5993       MovDelay[x][y] = 1;
5994   }
5995   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5996   {
5997     TestIfBadThingTouchesOtherBadThing(x, y);
5998
5999     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6000       MovDir[x][y] = left_dir;
6001     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6002       MovDir[x][y] = right_dir;
6003
6004     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6005       MovDelay[x][y] = 9;
6006     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6007       MovDelay[x][y] = 1;
6008   }
6009   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6010   {
6011     TestIfBadThingTouchesOtherBadThing(x, y);
6012
6013     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6014       MovDir[x][y] = left_dir;
6015     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6016       MovDir[x][y] = right_dir;
6017
6018     if (MovDir[x][y] != old_move_dir)
6019       MovDelay[x][y] = 9;
6020   }
6021   else if (element == EL_YAMYAM)
6022   {
6023     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6024     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6025
6026     if (can_turn_left && can_turn_right)
6027       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6028     else if (can_turn_left)
6029       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6030     else if (can_turn_right)
6031       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6032     else
6033       MovDir[x][y] = back_dir;
6034
6035     MovDelay[x][y] = 16 + 16 * RND(3);
6036   }
6037   else if (element == EL_DARK_YAMYAM)
6038   {
6039     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6040                                                          left_x, left_y);
6041     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6042                                                          right_x, right_y);
6043
6044     if (can_turn_left && can_turn_right)
6045       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6046     else if (can_turn_left)
6047       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6048     else if (can_turn_right)
6049       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6050     else
6051       MovDir[x][y] = back_dir;
6052
6053     MovDelay[x][y] = 16 + 16 * RND(3);
6054   }
6055   else if (element == EL_PACMAN)
6056   {
6057     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6058     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6059
6060     if (can_turn_left && can_turn_right)
6061       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6062     else if (can_turn_left)
6063       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6064     else if (can_turn_right)
6065       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6066     else
6067       MovDir[x][y] = back_dir;
6068
6069     MovDelay[x][y] = 6 + RND(40);
6070   }
6071   else if (element == EL_PIG)
6072   {
6073     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6074     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6075     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6076     boolean should_turn_left, should_turn_right, should_move_on;
6077     int rnd_value = 24;
6078     int rnd = RND(rnd_value);
6079
6080     should_turn_left = (can_turn_left &&
6081                         (!can_move_on ||
6082                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6083                                                    y + back_dy + left_dy)));
6084     should_turn_right = (can_turn_right &&
6085                          (!can_move_on ||
6086                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6087                                                     y + back_dy + right_dy)));
6088     should_move_on = (can_move_on &&
6089                       (!can_turn_left ||
6090                        !can_turn_right ||
6091                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6092                                                  y + move_dy + left_dy) ||
6093                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6094                                                  y + move_dy + right_dy)));
6095
6096     if (should_turn_left || should_turn_right || should_move_on)
6097     {
6098       if (should_turn_left && should_turn_right && should_move_on)
6099         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6100                         rnd < 2 * rnd_value / 3 ? right_dir :
6101                         old_move_dir);
6102       else if (should_turn_left && should_turn_right)
6103         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6104       else if (should_turn_left && should_move_on)
6105         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6106       else if (should_turn_right && should_move_on)
6107         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6108       else if (should_turn_left)
6109         MovDir[x][y] = left_dir;
6110       else if (should_turn_right)
6111         MovDir[x][y] = right_dir;
6112       else if (should_move_on)
6113         MovDir[x][y] = old_move_dir;
6114     }
6115     else if (can_move_on && rnd > rnd_value / 8)
6116       MovDir[x][y] = old_move_dir;
6117     else if (can_turn_left && can_turn_right)
6118       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6119     else if (can_turn_left && rnd > rnd_value / 8)
6120       MovDir[x][y] = left_dir;
6121     else if (can_turn_right && rnd > rnd_value/8)
6122       MovDir[x][y] = right_dir;
6123     else
6124       MovDir[x][y] = back_dir;
6125
6126     xx = x + move_xy[MovDir[x][y]].dx;
6127     yy = y + move_xy[MovDir[x][y]].dy;
6128
6129     if (!IN_LEV_FIELD(xx, yy) ||
6130         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6131       MovDir[x][y] = old_move_dir;
6132
6133     MovDelay[x][y] = 0;
6134   }
6135   else if (element == EL_DRAGON)
6136   {
6137     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6138     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6139     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6140     int rnd_value = 24;
6141     int rnd = RND(rnd_value);
6142
6143     if (can_move_on && rnd > rnd_value / 8)
6144       MovDir[x][y] = old_move_dir;
6145     else if (can_turn_left && can_turn_right)
6146       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6147     else if (can_turn_left && rnd > rnd_value / 8)
6148       MovDir[x][y] = left_dir;
6149     else if (can_turn_right && rnd > rnd_value / 8)
6150       MovDir[x][y] = right_dir;
6151     else
6152       MovDir[x][y] = back_dir;
6153
6154     xx = x + move_xy[MovDir[x][y]].dx;
6155     yy = y + move_xy[MovDir[x][y]].dy;
6156
6157     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6158       MovDir[x][y] = old_move_dir;
6159
6160     MovDelay[x][y] = 0;
6161   }
6162   else if (element == EL_MOLE)
6163   {
6164     boolean can_move_on =
6165       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6166                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6167                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6168     if (!can_move_on)
6169     {
6170       boolean can_turn_left =
6171         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6172                               IS_AMOEBOID(Feld[left_x][left_y])));
6173
6174       boolean can_turn_right =
6175         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6176                               IS_AMOEBOID(Feld[right_x][right_y])));
6177
6178       if (can_turn_left && can_turn_right)
6179         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6180       else if (can_turn_left)
6181         MovDir[x][y] = left_dir;
6182       else
6183         MovDir[x][y] = right_dir;
6184     }
6185
6186     if (MovDir[x][y] != old_move_dir)
6187       MovDelay[x][y] = 9;
6188   }
6189   else if (element == EL_BALLOON)
6190   {
6191     MovDir[x][y] = game.wind_direction;
6192     MovDelay[x][y] = 0;
6193   }
6194   else if (element == EL_SPRING)
6195   {
6196 #if USE_NEW_SPRING_BUMPER
6197     if (MovDir[x][y] & MV_HORIZONTAL)
6198     {
6199       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6200           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6201       {
6202         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6203         ResetGfxAnimation(move_x, move_y);
6204         DrawLevelField(move_x, move_y);
6205
6206         MovDir[x][y] = back_dir;
6207       }
6208       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6209                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6210         MovDir[x][y] = MV_NONE;
6211     }
6212 #else
6213     if (MovDir[x][y] & MV_HORIZONTAL &&
6214         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6215          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6216       MovDir[x][y] = MV_NONE;
6217 #endif
6218
6219     MovDelay[x][y] = 0;
6220   }
6221   else if (element == EL_ROBOT ||
6222            element == EL_SATELLITE ||
6223            element == EL_PENGUIN ||
6224            element == EL_EMC_ANDROID)
6225   {
6226     int attr_x = -1, attr_y = -1;
6227
6228     if (AllPlayersGone)
6229     {
6230       attr_x = ExitX;
6231       attr_y = ExitY;
6232     }
6233     else
6234     {
6235       int i;
6236
6237       for (i = 0; i < MAX_PLAYERS; i++)
6238       {
6239         struct PlayerInfo *player = &stored_player[i];
6240         int jx = player->jx, jy = player->jy;
6241
6242         if (!player->active)
6243           continue;
6244
6245         if (attr_x == -1 ||
6246             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6247         {
6248           attr_x = jx;
6249           attr_y = jy;
6250         }
6251       }
6252     }
6253
6254     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6255         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6256          game.engine_version < VERSION_IDENT(3,1,0,0)))
6257     {
6258       attr_x = ZX;
6259       attr_y = ZY;
6260     }
6261
6262     if (element == EL_PENGUIN)
6263     {
6264       int i;
6265       static int xy[4][2] =
6266       {
6267         { 0, -1 },
6268         { -1, 0 },
6269         { +1, 0 },
6270         { 0, +1 }
6271       };
6272
6273       for (i = 0; i < NUM_DIRECTIONS; i++)
6274       {
6275         int ex = x + xy[i][0];
6276         int ey = y + xy[i][1];
6277
6278         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6279                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6280                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6281                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6282         {
6283           attr_x = ex;
6284           attr_y = ey;
6285           break;
6286         }
6287       }
6288     }
6289
6290     MovDir[x][y] = MV_NONE;
6291     if (attr_x < x)
6292       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6293     else if (attr_x > x)
6294       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6295     if (attr_y < y)
6296       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6297     else if (attr_y > y)
6298       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6299
6300     if (element == EL_ROBOT)
6301     {
6302       int newx, newy;
6303
6304       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6305         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6306       Moving2Blocked(x, y, &newx, &newy);
6307
6308       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6309         MovDelay[x][y] = 8 + 8 * !RND(3);
6310       else
6311         MovDelay[x][y] = 16;
6312     }
6313     else if (element == EL_PENGUIN)
6314     {
6315       int newx, newy;
6316
6317       MovDelay[x][y] = 1;
6318
6319       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6320       {
6321         boolean first_horiz = RND(2);
6322         int new_move_dir = MovDir[x][y];
6323
6324         MovDir[x][y] =
6325           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6326         Moving2Blocked(x, y, &newx, &newy);
6327
6328         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6329           return;
6330
6331         MovDir[x][y] =
6332           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6333         Moving2Blocked(x, y, &newx, &newy);
6334
6335         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6336           return;
6337
6338         MovDir[x][y] = old_move_dir;
6339         return;
6340       }
6341     }
6342     else if (element == EL_SATELLITE)
6343     {
6344       int newx, newy;
6345
6346       MovDelay[x][y] = 1;
6347
6348       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6349       {
6350         boolean first_horiz = RND(2);
6351         int new_move_dir = MovDir[x][y];
6352
6353         MovDir[x][y] =
6354           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6355         Moving2Blocked(x, y, &newx, &newy);
6356
6357         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6358           return;
6359
6360         MovDir[x][y] =
6361           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6362         Moving2Blocked(x, y, &newx, &newy);
6363
6364         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6365           return;
6366
6367         MovDir[x][y] = old_move_dir;
6368         return;
6369       }
6370     }
6371     else if (element == EL_EMC_ANDROID)
6372     {
6373       static int check_pos[16] =
6374       {
6375         -1,             /*  0 => (invalid)          */
6376         7,              /*  1 => MV_LEFT            */
6377         3,              /*  2 => MV_RIGHT           */
6378         -1,             /*  3 => (invalid)          */
6379         1,              /*  4 =>            MV_UP   */
6380         0,              /*  5 => MV_LEFT  | MV_UP   */
6381         2,              /*  6 => MV_RIGHT | MV_UP   */
6382         -1,             /*  7 => (invalid)          */
6383         5,              /*  8 =>            MV_DOWN */
6384         6,              /*  9 => MV_LEFT  | MV_DOWN */
6385         4,              /* 10 => MV_RIGHT | MV_DOWN */
6386         -1,             /* 11 => (invalid)          */
6387         -1,             /* 12 => (invalid)          */
6388         -1,             /* 13 => (invalid)          */
6389         -1,             /* 14 => (invalid)          */
6390         -1,             /* 15 => (invalid)          */
6391       };
6392       static struct
6393       {
6394         int dx, dy;
6395         int dir;
6396       } check_xy[8] =
6397       {
6398         { -1, -1,       MV_LEFT  | MV_UP   },
6399         {  0, -1,                  MV_UP   },
6400         { +1, -1,       MV_RIGHT | MV_UP   },
6401         { +1,  0,       MV_RIGHT           },
6402         { +1, +1,       MV_RIGHT | MV_DOWN },
6403         {  0, +1,                  MV_DOWN },
6404         { -1, +1,       MV_LEFT  | MV_DOWN },
6405         { -1,  0,       MV_LEFT            },
6406       };
6407       int start_pos, check_order;
6408       boolean can_clone = FALSE;
6409       int i;
6410
6411       /* check if there is any free field around current position */
6412       for (i = 0; i < 8; i++)
6413       {
6414         int newx = x + check_xy[i].dx;
6415         int newy = y + check_xy[i].dy;
6416
6417         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6418         {
6419           can_clone = TRUE;
6420
6421           break;
6422         }
6423       }
6424
6425       if (can_clone)            /* randomly find an element to clone */
6426       {
6427         can_clone = FALSE;
6428
6429         start_pos = check_pos[RND(8)];
6430         check_order = (RND(2) ? -1 : +1);
6431
6432         for (i = 0; i < 8; i++)
6433         {
6434           int pos_raw = start_pos + i * check_order;
6435           int pos = (pos_raw + 8) % 8;
6436           int newx = x + check_xy[pos].dx;
6437           int newy = y + check_xy[pos].dy;
6438
6439           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6440           {
6441             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6442             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6443
6444             Store[x][y] = Feld[newx][newy];
6445
6446             can_clone = TRUE;
6447
6448             break;
6449           }
6450         }
6451       }
6452
6453       if (can_clone)            /* randomly find a direction to move */
6454       {
6455         can_clone = FALSE;
6456
6457         start_pos = check_pos[RND(8)];
6458         check_order = (RND(2) ? -1 : +1);
6459
6460         for (i = 0; i < 8; i++)
6461         {
6462           int pos_raw = start_pos + i * check_order;
6463           int pos = (pos_raw + 8) % 8;
6464           int newx = x + check_xy[pos].dx;
6465           int newy = y + check_xy[pos].dy;
6466           int new_move_dir = check_xy[pos].dir;
6467
6468           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6469           {
6470             MovDir[x][y] = new_move_dir;
6471             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6472
6473             can_clone = TRUE;
6474
6475             break;
6476           }
6477         }
6478       }
6479
6480       if (can_clone)            /* cloning and moving successful */
6481         return;
6482
6483       /* cannot clone -- try to move towards player */
6484
6485       start_pos = check_pos[MovDir[x][y] & 0x0f];
6486       check_order = (RND(2) ? -1 : +1);
6487
6488       for (i = 0; i < 3; i++)
6489       {
6490         /* first check start_pos, then previous/next or (next/previous) pos */
6491         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6492         int pos = (pos_raw + 8) % 8;
6493         int newx = x + check_xy[pos].dx;
6494         int newy = y + check_xy[pos].dy;
6495         int new_move_dir = check_xy[pos].dir;
6496
6497         if (IS_PLAYER(newx, newy))
6498           break;
6499
6500         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6501         {
6502           MovDir[x][y] = new_move_dir;
6503           MovDelay[x][y] = level.android_move_time * 8 + 1;
6504
6505           break;
6506         }
6507       }
6508     }
6509   }
6510   else if (move_pattern == MV_TURNING_LEFT ||
6511            move_pattern == MV_TURNING_RIGHT ||
6512            move_pattern == MV_TURNING_LEFT_RIGHT ||
6513            move_pattern == MV_TURNING_RIGHT_LEFT ||
6514            move_pattern == MV_TURNING_RANDOM ||
6515            move_pattern == MV_ALL_DIRECTIONS)
6516   {
6517     boolean can_turn_left =
6518       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6519     boolean can_turn_right =
6520       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6521
6522     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6523       return;
6524
6525     if (move_pattern == MV_TURNING_LEFT)
6526       MovDir[x][y] = left_dir;
6527     else if (move_pattern == MV_TURNING_RIGHT)
6528       MovDir[x][y] = right_dir;
6529     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6530       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6531     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6532       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6533     else if (move_pattern == MV_TURNING_RANDOM)
6534       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6535                       can_turn_right && !can_turn_left ? right_dir :
6536                       RND(2) ? left_dir : right_dir);
6537     else if (can_turn_left && can_turn_right)
6538       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6539     else if (can_turn_left)
6540       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6541     else if (can_turn_right)
6542       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6543     else
6544       MovDir[x][y] = back_dir;
6545
6546     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6547   }
6548   else if (move_pattern == MV_HORIZONTAL ||
6549            move_pattern == MV_VERTICAL)
6550   {
6551     if (move_pattern & old_move_dir)
6552       MovDir[x][y] = back_dir;
6553     else if (move_pattern == MV_HORIZONTAL)
6554       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6555     else if (move_pattern == MV_VERTICAL)
6556       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6557
6558     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6559   }
6560   else if (move_pattern & MV_ANY_DIRECTION)
6561   {
6562     MovDir[x][y] = move_pattern;
6563     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6564   }
6565   else if (move_pattern & MV_WIND_DIRECTION)
6566   {
6567     MovDir[x][y] = game.wind_direction;
6568     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6569   }
6570   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6571   {
6572     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6573       MovDir[x][y] = left_dir;
6574     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6575       MovDir[x][y] = right_dir;
6576
6577     if (MovDir[x][y] != old_move_dir)
6578       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6579   }
6580   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6581   {
6582     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6583       MovDir[x][y] = right_dir;
6584     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6585       MovDir[x][y] = left_dir;
6586
6587     if (MovDir[x][y] != old_move_dir)
6588       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6589   }
6590   else if (move_pattern == MV_TOWARDS_PLAYER ||
6591            move_pattern == MV_AWAY_FROM_PLAYER)
6592   {
6593     int attr_x = -1, attr_y = -1;
6594     int newx, newy;
6595     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6596
6597     if (AllPlayersGone)
6598     {
6599       attr_x = ExitX;
6600       attr_y = ExitY;
6601     }
6602     else
6603     {
6604       int i;
6605
6606       for (i = 0; i < MAX_PLAYERS; i++)
6607       {
6608         struct PlayerInfo *player = &stored_player[i];
6609         int jx = player->jx, jy = player->jy;
6610
6611         if (!player->active)
6612           continue;
6613
6614         if (attr_x == -1 ||
6615             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6616         {
6617           attr_x = jx;
6618           attr_y = jy;
6619         }
6620       }
6621     }
6622
6623     MovDir[x][y] = MV_NONE;
6624     if (attr_x < x)
6625       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6626     else if (attr_x > x)
6627       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6628     if (attr_y < y)
6629       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6630     else if (attr_y > y)
6631       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6632
6633     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6634
6635     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6636     {
6637       boolean first_horiz = RND(2);
6638       int new_move_dir = MovDir[x][y];
6639
6640       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6641       {
6642         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6643         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6644
6645         return;
6646       }
6647
6648       MovDir[x][y] =
6649         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6650       Moving2Blocked(x, y, &newx, &newy);
6651
6652       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6653         return;
6654
6655       MovDir[x][y] =
6656         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6657       Moving2Blocked(x, y, &newx, &newy);
6658
6659       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6660         return;
6661
6662       MovDir[x][y] = old_move_dir;
6663     }
6664   }
6665   else if (move_pattern == MV_WHEN_PUSHED ||
6666            move_pattern == MV_WHEN_DROPPED)
6667   {
6668     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6669       MovDir[x][y] = MV_NONE;
6670
6671     MovDelay[x][y] = 0;
6672   }
6673   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6674   {
6675     static int test_xy[7][2] =
6676     {
6677       { 0, -1 },
6678       { -1, 0 },
6679       { +1, 0 },
6680       { 0, +1 },
6681       { 0, -1 },
6682       { -1, 0 },
6683       { +1, 0 },
6684     };
6685     static int test_dir[7] =
6686     {
6687       MV_UP,
6688       MV_LEFT,
6689       MV_RIGHT,
6690       MV_DOWN,
6691       MV_UP,
6692       MV_LEFT,
6693       MV_RIGHT,
6694     };
6695     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6696     int move_preference = -1000000;     /* start with very low preference */
6697     int new_move_dir = MV_NONE;
6698     int start_test = RND(4);
6699     int i;
6700
6701     for (i = 0; i < NUM_DIRECTIONS; i++)
6702     {
6703       int move_dir = test_dir[start_test + i];
6704       int move_dir_preference;
6705
6706       xx = x + test_xy[start_test + i][0];
6707       yy = y + test_xy[start_test + i][1];
6708
6709       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6710           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6711       {
6712         new_move_dir = move_dir;
6713
6714         break;
6715       }
6716
6717       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6718         continue;
6719
6720       move_dir_preference = -1 * RunnerVisit[xx][yy];
6721       if (hunter_mode && PlayerVisit[xx][yy] > 0)
6722         move_dir_preference = PlayerVisit[xx][yy];
6723
6724       if (move_dir_preference > move_preference)
6725       {
6726         /* prefer field that has not been visited for the longest time */
6727         move_preference = move_dir_preference;
6728         new_move_dir = move_dir;
6729       }
6730       else if (move_dir_preference == move_preference &&
6731                move_dir == old_move_dir)
6732       {
6733         /* prefer last direction when all directions are preferred equally */
6734         move_preference = move_dir_preference;
6735         new_move_dir = move_dir;
6736       }
6737     }
6738
6739     MovDir[x][y] = new_move_dir;
6740     if (old_move_dir != new_move_dir)
6741       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6742   }
6743 }
6744
6745 static void TurnRound(int x, int y)
6746 {
6747   int direction = MovDir[x][y];
6748
6749   TurnRoundExt(x, y);
6750
6751   GfxDir[x][y] = MovDir[x][y];
6752
6753   if (direction != MovDir[x][y])
6754     GfxFrame[x][y] = 0;
6755
6756   if (MovDelay[x][y])
6757     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6758
6759   ResetGfxFrame(x, y, FALSE);
6760 }
6761
6762 static boolean JustBeingPushed(int x, int y)
6763 {
6764   int i;
6765
6766   for (i = 0; i < MAX_PLAYERS; i++)
6767   {
6768     struct PlayerInfo *player = &stored_player[i];
6769
6770     if (player->active && player->is_pushing && player->MovPos)
6771     {
6772       int next_jx = player->jx + (player->jx - player->last_jx);
6773       int next_jy = player->jy + (player->jy - player->last_jy);
6774
6775       if (x == next_jx && y == next_jy)
6776         return TRUE;
6777     }
6778   }
6779
6780   return FALSE;
6781 }
6782
6783 void StartMoving(int x, int y)
6784 {
6785   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
6786   int element = Feld[x][y];
6787
6788   if (Stop[x][y])
6789     return;
6790
6791   if (MovDelay[x][y] == 0)
6792     GfxAction[x][y] = ACTION_DEFAULT;
6793
6794   if (CAN_FALL(element) && y < lev_fieldy - 1)
6795   {
6796     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
6797         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6798       if (JustBeingPushed(x, y))
6799         return;
6800
6801     if (element == EL_QUICKSAND_FULL)
6802     {
6803       if (IS_FREE(x, y + 1))
6804       {
6805         InitMovingField(x, y, MV_DOWN);
6806         started_moving = TRUE;
6807
6808         Feld[x][y] = EL_QUICKSAND_EMPTYING;
6809 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6810         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6811           Store[x][y] = EL_ROCK;
6812 #else
6813         Store[x][y] = EL_ROCK;
6814 #endif
6815
6816         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6817       }
6818       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6819       {
6820         if (!MovDelay[x][y])
6821           MovDelay[x][y] = TILEY + 1;
6822
6823         if (MovDelay[x][y])
6824         {
6825           MovDelay[x][y]--;
6826           if (MovDelay[x][y])
6827             return;
6828         }
6829
6830         Feld[x][y] = EL_QUICKSAND_EMPTY;
6831         Feld[x][y + 1] = EL_QUICKSAND_FULL;
6832         Store[x][y + 1] = Store[x][y];
6833         Store[x][y] = 0;
6834
6835         PlayLevelSoundAction(x, y, ACTION_FILLING);
6836       }
6837     }
6838     else if (element == EL_QUICKSAND_FAST_FULL)
6839     {
6840       if (IS_FREE(x, y + 1))
6841       {
6842         InitMovingField(x, y, MV_DOWN);
6843         started_moving = TRUE;
6844
6845         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
6846 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6847         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6848           Store[x][y] = EL_ROCK;
6849 #else
6850         Store[x][y] = EL_ROCK;
6851 #endif
6852
6853         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6854       }
6855       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6856       {
6857         if (!MovDelay[x][y])
6858           MovDelay[x][y] = TILEY + 1;
6859
6860         if (MovDelay[x][y])
6861         {
6862           MovDelay[x][y]--;
6863           if (MovDelay[x][y])
6864             return;
6865         }
6866
6867         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
6868         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
6869         Store[x][y + 1] = Store[x][y];
6870         Store[x][y] = 0;
6871
6872         PlayLevelSoundAction(x, y, ACTION_FILLING);
6873       }
6874     }
6875     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6876              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6877     {
6878       InitMovingField(x, y, MV_DOWN);
6879       started_moving = TRUE;
6880
6881       Feld[x][y] = EL_QUICKSAND_FILLING;
6882       Store[x][y] = element;
6883
6884       PlayLevelSoundAction(x, y, ACTION_FILLING);
6885     }
6886     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6887              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6888     {
6889       InitMovingField(x, y, MV_DOWN);
6890       started_moving = TRUE;
6891
6892       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
6893       Store[x][y] = element;
6894
6895       PlayLevelSoundAction(x, y, ACTION_FILLING);
6896     }
6897     else if (element == EL_MAGIC_WALL_FULL)
6898     {
6899       if (IS_FREE(x, y + 1))
6900       {
6901         InitMovingField(x, y, MV_DOWN);
6902         started_moving = TRUE;
6903
6904         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6905         Store[x][y] = EL_CHANGED(Store[x][y]);
6906       }
6907       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6908       {
6909         if (!MovDelay[x][y])
6910           MovDelay[x][y] = TILEY/4 + 1;
6911
6912         if (MovDelay[x][y])
6913         {
6914           MovDelay[x][y]--;
6915           if (MovDelay[x][y])
6916             return;
6917         }
6918
6919         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6920         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6921         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6922         Store[x][y] = 0;
6923       }
6924     }
6925     else if (element == EL_BD_MAGIC_WALL_FULL)
6926     {
6927       if (IS_FREE(x, y + 1))
6928       {
6929         InitMovingField(x, y, MV_DOWN);
6930         started_moving = TRUE;
6931
6932         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6933         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6934       }
6935       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6936       {
6937         if (!MovDelay[x][y])
6938           MovDelay[x][y] = TILEY/4 + 1;
6939
6940         if (MovDelay[x][y])
6941         {
6942           MovDelay[x][y]--;
6943           if (MovDelay[x][y])
6944             return;
6945         }
6946
6947         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6948         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6949         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6950         Store[x][y] = 0;
6951       }
6952     }
6953     else if (element == EL_DC_MAGIC_WALL_FULL)
6954     {
6955       if (IS_FREE(x, y + 1))
6956       {
6957         InitMovingField(x, y, MV_DOWN);
6958         started_moving = TRUE;
6959
6960         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
6961         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
6962       }
6963       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6964       {
6965         if (!MovDelay[x][y])
6966           MovDelay[x][y] = TILEY/4 + 1;
6967
6968         if (MovDelay[x][y])
6969         {
6970           MovDelay[x][y]--;
6971           if (MovDelay[x][y])
6972             return;
6973         }
6974
6975         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
6976         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
6977         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
6978         Store[x][y] = 0;
6979       }
6980     }
6981     else if ((CAN_PASS_MAGIC_WALL(element) &&
6982               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6983                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
6984              (CAN_PASS_DC_MAGIC_WALL(element) &&
6985               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
6986
6987     {
6988       InitMovingField(x, y, MV_DOWN);
6989       started_moving = TRUE;
6990
6991       Feld[x][y] =
6992         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6993          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
6994          EL_DC_MAGIC_WALL_FILLING);
6995       Store[x][y] = element;
6996     }
6997     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6998     {
6999       SplashAcid(x, y + 1);
7000
7001       InitMovingField(x, y, MV_DOWN);
7002       started_moving = TRUE;
7003
7004       Store[x][y] = EL_ACID;
7005     }
7006     else if (
7007 #if USE_FIX_IMPACT_COLLISION
7008              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7009               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7010 #else
7011              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7012               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7013 #endif
7014              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7015               CAN_FALL(element) && WasJustFalling[x][y] &&
7016               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7017
7018              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7019               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7020               (Feld[x][y + 1] == EL_BLOCKED)))
7021     {
7022       /* this is needed for a special case not covered by calling "Impact()"
7023          from "ContinueMoving()": if an element moves to a tile directly below
7024          another element which was just falling on that tile (which was empty
7025          in the previous frame), the falling element above would just stop
7026          instead of smashing the element below (in previous version, the above
7027          element was just checked for "moving" instead of "falling", resulting
7028          in incorrect smashes caused by horizontal movement of the above
7029          element; also, the case of the player being the element to smash was
7030          simply not covered here... :-/ ) */
7031
7032       CheckCollision[x][y] = 0;
7033       CheckImpact[x][y] = 0;
7034
7035       Impact(x, y);
7036     }
7037     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7038     {
7039       if (MovDir[x][y] == MV_NONE)
7040       {
7041         InitMovingField(x, y, MV_DOWN);
7042         started_moving = TRUE;
7043       }
7044     }
7045     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7046     {
7047       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7048         MovDir[x][y] = MV_DOWN;
7049
7050       InitMovingField(x, y, MV_DOWN);
7051       started_moving = TRUE;
7052     }
7053     else if (element == EL_AMOEBA_DROP)
7054     {
7055       Feld[x][y] = EL_AMOEBA_GROWING;
7056       Store[x][y] = EL_AMOEBA_WET;
7057     }
7058     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7059               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7060              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7061              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7062     {
7063       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7064                                 (IS_FREE(x - 1, y + 1) ||
7065                                  Feld[x - 1][y + 1] == EL_ACID));
7066       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7067                                 (IS_FREE(x + 1, y + 1) ||
7068                                  Feld[x + 1][y + 1] == EL_ACID));
7069       boolean can_fall_any  = (can_fall_left || can_fall_right);
7070       boolean can_fall_both = (can_fall_left && can_fall_right);
7071       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7072
7073 #if USE_NEW_ALL_SLIPPERY
7074       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7075       {
7076         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7077           can_fall_right = FALSE;
7078         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7079           can_fall_left = FALSE;
7080         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7081           can_fall_right = FALSE;
7082         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7083           can_fall_left = FALSE;
7084
7085         can_fall_any  = (can_fall_left || can_fall_right);
7086         can_fall_both = FALSE;
7087       }
7088 #else
7089       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7090       {
7091         if (slippery_type == SLIPPERY_ONLY_LEFT)
7092           can_fall_right = FALSE;
7093         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7094           can_fall_left = FALSE;
7095         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7096           can_fall_right = FALSE;
7097         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7098           can_fall_left = FALSE;
7099
7100         can_fall_any  = (can_fall_left || can_fall_right);
7101         can_fall_both = (can_fall_left && can_fall_right);
7102       }
7103 #endif
7104
7105 #if USE_NEW_ALL_SLIPPERY
7106 #else
7107 #if USE_NEW_SP_SLIPPERY
7108       /* !!! better use the same properties as for custom elements here !!! */
7109       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7110                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7111       {
7112         can_fall_right = FALSE;         /* slip down on left side */
7113         can_fall_both = FALSE;
7114       }
7115 #endif
7116 #endif
7117
7118 #if USE_NEW_ALL_SLIPPERY
7119       if (can_fall_both)
7120       {
7121         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7122           can_fall_right = FALSE;       /* slip down on left side */
7123         else
7124           can_fall_left = !(can_fall_right = RND(2));
7125
7126         can_fall_both = FALSE;
7127       }
7128 #else
7129       if (can_fall_both)
7130       {
7131         if (game.emulation == EMU_BOULDERDASH ||
7132             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7133           can_fall_right = FALSE;       /* slip down on left side */
7134         else
7135           can_fall_left = !(can_fall_right = RND(2));
7136
7137         can_fall_both = FALSE;
7138       }
7139 #endif
7140
7141       if (can_fall_any)
7142       {
7143         /* if not determined otherwise, prefer left side for slipping down */
7144         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7145         started_moving = TRUE;
7146       }
7147     }
7148 #if 0
7149     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7150 #else
7151     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7152 #endif
7153     {
7154       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7155       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7156       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7157       int belt_dir = game.belt_dir[belt_nr];
7158
7159       if ((belt_dir == MV_LEFT  && left_is_free) ||
7160           (belt_dir == MV_RIGHT && right_is_free))
7161       {
7162         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7163
7164         InitMovingField(x, y, belt_dir);
7165         started_moving = TRUE;
7166
7167         Pushed[x][y] = TRUE;
7168         Pushed[nextx][y] = TRUE;
7169
7170         GfxAction[x][y] = ACTION_DEFAULT;
7171       }
7172       else
7173       {
7174         MovDir[x][y] = 0;       /* if element was moving, stop it */
7175       }
7176     }
7177   }
7178
7179   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7180 #if 0
7181   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7182 #else
7183   if (CAN_MOVE(element) && !started_moving)
7184 #endif
7185   {
7186     int move_pattern = element_info[element].move_pattern;
7187     int newx, newy;
7188
7189 #if 0
7190 #if DEBUG
7191     if (MovDir[x][y] == MV_NONE)
7192     {
7193       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7194              x, y, element, element_info[element].token_name);
7195       printf("StartMoving(): This should never happen!\n");
7196     }
7197 #endif
7198 #endif
7199
7200     Moving2Blocked(x, y, &newx, &newy);
7201
7202     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7203       return;
7204
7205     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7206         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7207     {
7208       WasJustMoving[x][y] = 0;
7209       CheckCollision[x][y] = 0;
7210
7211       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7212
7213       if (Feld[x][y] != element)        /* element has changed */
7214         return;
7215     }
7216
7217     if (!MovDelay[x][y])        /* start new movement phase */
7218     {
7219       /* all objects that can change their move direction after each step
7220          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7221
7222       if (element != EL_YAMYAM &&
7223           element != EL_DARK_YAMYAM &&
7224           element != EL_PACMAN &&
7225           !(move_pattern & MV_ANY_DIRECTION) &&
7226           move_pattern != MV_TURNING_LEFT &&
7227           move_pattern != MV_TURNING_RIGHT &&
7228           move_pattern != MV_TURNING_LEFT_RIGHT &&
7229           move_pattern != MV_TURNING_RIGHT_LEFT &&
7230           move_pattern != MV_TURNING_RANDOM)
7231       {
7232         TurnRound(x, y);
7233
7234         if (MovDelay[x][y] && (element == EL_BUG ||
7235                                element == EL_SPACESHIP ||
7236                                element == EL_SP_SNIKSNAK ||
7237                                element == EL_SP_ELECTRON ||
7238                                element == EL_MOLE))
7239           DrawLevelField(x, y);
7240       }
7241     }
7242
7243     if (MovDelay[x][y])         /* wait some time before next movement */
7244     {
7245       MovDelay[x][y]--;
7246
7247       if (element == EL_ROBOT ||
7248           element == EL_YAMYAM ||
7249           element == EL_DARK_YAMYAM)
7250       {
7251         DrawLevelElementAnimationIfNeeded(x, y, element);
7252         PlayLevelSoundAction(x, y, ACTION_WAITING);
7253       }
7254       else if (element == EL_SP_ELECTRON)
7255         DrawLevelElementAnimationIfNeeded(x, y, element);
7256       else if (element == EL_DRAGON)
7257       {
7258         int i;
7259         int dir = MovDir[x][y];
7260         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7261         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7262         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7263                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7264                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7265                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7266         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7267
7268         GfxAction[x][y] = ACTION_ATTACKING;
7269
7270         if (IS_PLAYER(x, y))
7271           DrawPlayerField(x, y);
7272         else
7273           DrawLevelField(x, y);
7274
7275         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7276
7277         for (i = 1; i <= 3; i++)
7278         {
7279           int xx = x + i * dx;
7280           int yy = y + i * dy;
7281           int sx = SCREENX(xx);
7282           int sy = SCREENY(yy);
7283           int flame_graphic = graphic + (i - 1);
7284
7285           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7286             break;
7287
7288           if (MovDelay[x][y])
7289           {
7290             int flamed = MovingOrBlocked2Element(xx, yy);
7291
7292             /* !!! */
7293 #if 0
7294             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7295               Bang(xx, yy);
7296             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
7297               RemoveMovingField(xx, yy);
7298             else
7299               RemoveField(xx, yy);
7300 #else
7301             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7302               Bang(xx, yy);
7303             else
7304               RemoveMovingField(xx, yy);
7305 #endif
7306
7307             ChangeDelay[xx][yy] = 0;
7308
7309             Feld[xx][yy] = EL_FLAMES;
7310
7311             if (IN_SCR_FIELD(sx, sy))
7312             {
7313               DrawLevelFieldCrumbledSand(xx, yy);
7314               DrawGraphic(sx, sy, flame_graphic, frame);
7315             }
7316           }
7317           else
7318           {
7319             if (Feld[xx][yy] == EL_FLAMES)
7320               Feld[xx][yy] = EL_EMPTY;
7321             DrawLevelField(xx, yy);
7322           }
7323         }
7324       }
7325
7326       if (MovDelay[x][y])       /* element still has to wait some time */
7327       {
7328         PlayLevelSoundAction(x, y, ACTION_WAITING);
7329
7330         return;
7331       }
7332     }
7333
7334     /* now make next step */
7335
7336     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7337
7338     if (DONT_COLLIDE_WITH(element) &&
7339         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7340         !PLAYER_ENEMY_PROTECTED(newx, newy))
7341     {
7342       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7343
7344       return;
7345     }
7346
7347     else if (CAN_MOVE_INTO_ACID(element) &&
7348              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7349              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7350              (MovDir[x][y] == MV_DOWN ||
7351               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7352     {
7353       SplashAcid(newx, newy);
7354       Store[x][y] = EL_ACID;
7355     }
7356     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7357     {
7358       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7359           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7360           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7361           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7362       {
7363         RemoveField(x, y);
7364         DrawLevelField(x, y);
7365
7366         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7367         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7368           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7369
7370         local_player->friends_still_needed--;
7371         if (!local_player->friends_still_needed &&
7372             !local_player->GameOver && AllPlayersGone)
7373           PlayerWins(local_player);
7374
7375         return;
7376       }
7377       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7378       {
7379         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7380           DrawLevelField(newx, newy);
7381         else
7382           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7383       }
7384       else if (!IS_FREE(newx, newy))
7385       {
7386         GfxAction[x][y] = ACTION_WAITING;
7387
7388         if (IS_PLAYER(x, y))
7389           DrawPlayerField(x, y);
7390         else
7391           DrawLevelField(x, y);
7392
7393         return;
7394       }
7395     }
7396     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7397     {
7398       if (IS_FOOD_PIG(Feld[newx][newy]))
7399       {
7400         if (IS_MOVING(newx, newy))
7401           RemoveMovingField(newx, newy);
7402         else
7403         {
7404           Feld[newx][newy] = EL_EMPTY;
7405           DrawLevelField(newx, newy);
7406         }
7407
7408         PlayLevelSound(x, y, SND_PIG_DIGGING);
7409       }
7410       else if (!IS_FREE(newx, newy))
7411       {
7412         if (IS_PLAYER(x, y))
7413           DrawPlayerField(x, y);
7414         else
7415           DrawLevelField(x, y);
7416
7417         return;
7418       }
7419     }
7420     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7421     {
7422       if (Store[x][y] != EL_EMPTY)
7423       {
7424         boolean can_clone = FALSE;
7425         int xx, yy;
7426
7427         /* check if element to clone is still there */
7428         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7429         {
7430           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7431           {
7432             can_clone = TRUE;
7433
7434             break;
7435           }
7436         }
7437
7438         /* cannot clone or target field not free anymore -- do not clone */
7439         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7440           Store[x][y] = EL_EMPTY;
7441       }
7442
7443       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7444       {
7445         if (IS_MV_DIAGONAL(MovDir[x][y]))
7446         {
7447           int diagonal_move_dir = MovDir[x][y];
7448           int stored = Store[x][y];
7449           int change_delay = 8;
7450           int graphic;
7451
7452           /* android is moving diagonally */
7453
7454           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7455
7456           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7457           GfxElement[x][y] = EL_EMC_ANDROID;
7458           GfxAction[x][y] = ACTION_SHRINKING;
7459           GfxDir[x][y] = diagonal_move_dir;
7460           ChangeDelay[x][y] = change_delay;
7461
7462           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7463                                    GfxDir[x][y]);
7464
7465           DrawLevelGraphicAnimation(x, y, graphic);
7466           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7467
7468           if (Feld[newx][newy] == EL_ACID)
7469           {
7470             SplashAcid(newx, newy);
7471
7472             return;
7473           }
7474
7475           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7476
7477           Store[newx][newy] = EL_EMC_ANDROID;
7478           GfxElement[newx][newy] = EL_EMC_ANDROID;
7479           GfxAction[newx][newy] = ACTION_GROWING;
7480           GfxDir[newx][newy] = diagonal_move_dir;
7481           ChangeDelay[newx][newy] = change_delay;
7482
7483           graphic = el_act_dir2img(GfxElement[newx][newy],
7484                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7485
7486           DrawLevelGraphicAnimation(newx, newy, graphic);
7487           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7488
7489           return;
7490         }
7491         else
7492         {
7493           Feld[newx][newy] = EL_EMPTY;
7494           DrawLevelField(newx, newy);
7495
7496           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7497         }
7498       }
7499       else if (!IS_FREE(newx, newy))
7500       {
7501 #if 0
7502         if (IS_PLAYER(x, y))
7503           DrawPlayerField(x, y);
7504         else
7505           DrawLevelField(x, y);
7506 #endif
7507
7508         return;
7509       }
7510     }
7511     else if (IS_CUSTOM_ELEMENT(element) &&
7512              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7513     {
7514       int new_element = Feld[newx][newy];
7515
7516       if (!IS_FREE(newx, newy))
7517       {
7518         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7519                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7520                       ACTION_BREAKING);
7521
7522         /* no element can dig solid indestructible elements */
7523         if (IS_INDESTRUCTIBLE(new_element) &&
7524             !IS_DIGGABLE(new_element) &&
7525             !IS_COLLECTIBLE(new_element))
7526           return;
7527
7528         if (AmoebaNr[newx][newy] &&
7529             (new_element == EL_AMOEBA_FULL ||
7530              new_element == EL_BD_AMOEBA ||
7531              new_element == EL_AMOEBA_GROWING))
7532         {
7533           AmoebaCnt[AmoebaNr[newx][newy]]--;
7534           AmoebaCnt2[AmoebaNr[newx][newy]]--;
7535         }
7536
7537         if (IS_MOVING(newx, newy))
7538           RemoveMovingField(newx, newy);
7539         else
7540         {
7541           RemoveField(newx, newy);
7542           DrawLevelField(newx, newy);
7543         }
7544
7545         /* if digged element was about to explode, prevent the explosion */
7546         ExplodeField[newx][newy] = EX_TYPE_NONE;
7547
7548         PlayLevelSoundAction(x, y, action);
7549       }
7550
7551       Store[newx][newy] = EL_EMPTY;
7552 #if 1
7553       /* this makes it possible to leave the removed element again */
7554       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7555         Store[newx][newy] = new_element;
7556 #else
7557       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7558       {
7559         int move_leave_element = element_info[element].move_leave_element;
7560
7561         /* this makes it possible to leave the removed element again */
7562         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7563                              new_element : move_leave_element);
7564       }
7565 #endif
7566
7567       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7568       {
7569         RunnerVisit[x][y] = FrameCounter;
7570         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7571       }
7572     }
7573     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7574     {
7575       if (!IS_FREE(newx, newy))
7576       {
7577         if (IS_PLAYER(x, y))
7578           DrawPlayerField(x, y);
7579         else
7580           DrawLevelField(x, y);
7581
7582         return;
7583       }
7584       else
7585       {
7586         boolean wanna_flame = !RND(10);
7587         int dx = newx - x, dy = newy - y;
7588         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7589         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7590         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7591                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7592         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7593                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7594
7595         if ((wanna_flame ||
7596              IS_CLASSIC_ENEMY(element1) ||
7597              IS_CLASSIC_ENEMY(element2)) &&
7598             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7599             element1 != EL_FLAMES && element2 != EL_FLAMES)
7600         {
7601           ResetGfxAnimation(x, y);
7602           GfxAction[x][y] = ACTION_ATTACKING;
7603
7604           if (IS_PLAYER(x, y))
7605             DrawPlayerField(x, y);
7606           else
7607             DrawLevelField(x, y);
7608
7609           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7610
7611           MovDelay[x][y] = 50;
7612
7613           /* !!! */
7614 #if 0
7615           RemoveField(newx, newy);
7616 #endif
7617           Feld[newx][newy] = EL_FLAMES;
7618           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7619           {
7620 #if 0
7621             RemoveField(newx1, newy1);
7622 #endif
7623             Feld[newx1][newy1] = EL_FLAMES;
7624           }
7625           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7626           {
7627 #if 0
7628             RemoveField(newx2, newy2);
7629 #endif
7630             Feld[newx2][newy2] = EL_FLAMES;
7631           }
7632
7633           return;
7634         }
7635       }
7636     }
7637     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7638              Feld[newx][newy] == EL_DIAMOND)
7639     {
7640       if (IS_MOVING(newx, newy))
7641         RemoveMovingField(newx, newy);
7642       else
7643       {
7644         Feld[newx][newy] = EL_EMPTY;
7645         DrawLevelField(newx, newy);
7646       }
7647
7648       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7649     }
7650     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7651              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7652     {
7653       if (AmoebaNr[newx][newy])
7654       {
7655         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7656         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7657             Feld[newx][newy] == EL_BD_AMOEBA)
7658           AmoebaCnt[AmoebaNr[newx][newy]]--;
7659       }
7660
7661 #if 0
7662       /* !!! test !!! */
7663       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7664       {
7665         RemoveMovingField(newx, newy);
7666       }
7667 #else
7668       if (IS_MOVING(newx, newy))
7669       {
7670         RemoveMovingField(newx, newy);
7671       }
7672 #endif
7673       else
7674       {
7675         Feld[newx][newy] = EL_EMPTY;
7676         DrawLevelField(newx, newy);
7677       }
7678
7679       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7680     }
7681     else if ((element == EL_PACMAN || element == EL_MOLE)
7682              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7683     {
7684       if (AmoebaNr[newx][newy])
7685       {
7686         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7687         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7688             Feld[newx][newy] == EL_BD_AMOEBA)
7689           AmoebaCnt[AmoebaNr[newx][newy]]--;
7690       }
7691
7692       if (element == EL_MOLE)
7693       {
7694         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7695         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7696
7697         ResetGfxAnimation(x, y);
7698         GfxAction[x][y] = ACTION_DIGGING;
7699         DrawLevelField(x, y);
7700
7701         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7702
7703         return;                         /* wait for shrinking amoeba */
7704       }
7705       else      /* element == EL_PACMAN */
7706       {
7707         Feld[newx][newy] = EL_EMPTY;
7708         DrawLevelField(newx, newy);
7709         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7710       }
7711     }
7712     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7713              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7714               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7715     {
7716       /* wait for shrinking amoeba to completely disappear */
7717       return;
7718     }
7719     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7720     {
7721       /* object was running against a wall */
7722
7723       TurnRound(x, y);
7724
7725 #if 0
7726       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7727       if (move_pattern & MV_ANY_DIRECTION &&
7728           move_pattern == MovDir[x][y])
7729       {
7730         int blocking_element =
7731           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7732
7733         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7734                                  MovDir[x][y]);
7735
7736         element = Feld[x][y];   /* element might have changed */
7737       }
7738 #endif
7739
7740       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7741         DrawLevelElementAnimation(x, y, element);
7742
7743       if (DONT_TOUCH(element))
7744         TestIfBadThingTouchesPlayer(x, y);
7745
7746       return;
7747     }
7748
7749     InitMovingField(x, y, MovDir[x][y]);
7750
7751     PlayLevelSoundAction(x, y, ACTION_MOVING);
7752   }
7753
7754   if (MovDir[x][y])
7755     ContinueMoving(x, y);
7756 }
7757
7758 void ContinueMoving(int x, int y)
7759 {
7760   int element = Feld[x][y];
7761   struct ElementInfo *ei = &element_info[element];
7762   int direction = MovDir[x][y];
7763   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7764   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7765   int newx = x + dx, newy = y + dy;
7766   int stored = Store[x][y];
7767   int stored_new = Store[newx][newy];
7768   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7769   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7770   boolean last_line = (newy == lev_fieldy - 1);
7771
7772   MovPos[x][y] += getElementMoveStepsize(x, y);
7773
7774   if (pushed_by_player) /* special case: moving object pushed by player */
7775     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7776
7777   if (ABS(MovPos[x][y]) < TILEX)
7778   {
7779 #if 0
7780     int ee = Feld[x][y];
7781     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7782     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7783
7784     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7785            x, y, ABS(MovPos[x][y]),
7786            ee, gg, ff,
7787            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
7788 #endif
7789
7790     DrawLevelField(x, y);
7791
7792     return;     /* element is still moving */
7793   }
7794
7795   /* element reached destination field */
7796
7797   Feld[x][y] = EL_EMPTY;
7798   Feld[newx][newy] = element;
7799   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
7800
7801   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
7802   {
7803     element = Feld[newx][newy] = EL_ACID;
7804   }
7805   else if (element == EL_MOLE)
7806   {
7807     Feld[x][y] = EL_SAND;
7808
7809     DrawLevelFieldCrumbledSandNeighbours(x, y);
7810   }
7811   else if (element == EL_QUICKSAND_FILLING)
7812   {
7813     element = Feld[newx][newy] = get_next_element(element);
7814     Store[newx][newy] = Store[x][y];
7815   }
7816   else if (element == EL_QUICKSAND_EMPTYING)
7817   {
7818     Feld[x][y] = get_next_element(element);
7819     element = Feld[newx][newy] = Store[x][y];
7820   }
7821   else if (element == EL_QUICKSAND_FAST_FILLING)
7822   {
7823     element = Feld[newx][newy] = get_next_element(element);
7824     Store[newx][newy] = Store[x][y];
7825   }
7826   else if (element == EL_QUICKSAND_FAST_EMPTYING)
7827   {
7828     Feld[x][y] = get_next_element(element);
7829     element = Feld[newx][newy] = Store[x][y];
7830   }
7831   else if (element == EL_MAGIC_WALL_FILLING)
7832   {
7833     element = Feld[newx][newy] = get_next_element(element);
7834     if (!game.magic_wall_active)
7835       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7836     Store[newx][newy] = Store[x][y];
7837   }
7838   else if (element == EL_MAGIC_WALL_EMPTYING)
7839   {
7840     Feld[x][y] = get_next_element(element);
7841     if (!game.magic_wall_active)
7842       Feld[x][y] = EL_MAGIC_WALL_DEAD;
7843     element = Feld[newx][newy] = Store[x][y];
7844
7845 #if USE_NEW_CUSTOM_VALUE
7846     InitField(newx, newy, FALSE);
7847 #endif
7848   }
7849   else if (element == EL_BD_MAGIC_WALL_FILLING)
7850   {
7851     element = Feld[newx][newy] = get_next_element(element);
7852     if (!game.magic_wall_active)
7853       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
7854     Store[newx][newy] = Store[x][y];
7855   }
7856   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
7857   {
7858     Feld[x][y] = get_next_element(element);
7859     if (!game.magic_wall_active)
7860       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7861     element = Feld[newx][newy] = Store[x][y];
7862
7863 #if USE_NEW_CUSTOM_VALUE
7864     InitField(newx, newy, FALSE);
7865 #endif
7866   }
7867   else if (element == EL_DC_MAGIC_WALL_FILLING)
7868   {
7869     element = Feld[newx][newy] = get_next_element(element);
7870     if (!game.magic_wall_active)
7871       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
7872     Store[newx][newy] = Store[x][y];
7873   }
7874   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
7875   {
7876     Feld[x][y] = get_next_element(element);
7877     if (!game.magic_wall_active)
7878       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
7879     element = Feld[newx][newy] = Store[x][y];
7880
7881 #if USE_NEW_CUSTOM_VALUE
7882     InitField(newx, newy, FALSE);
7883 #endif
7884   }
7885   else if (element == EL_AMOEBA_DROPPING)
7886   {
7887     Feld[x][y] = get_next_element(element);
7888     element = Feld[newx][newy] = Store[x][y];
7889   }
7890   else if (element == EL_SOKOBAN_OBJECT)
7891   {
7892     if (Back[x][y])
7893       Feld[x][y] = Back[x][y];
7894
7895     if (Back[newx][newy])
7896       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
7897
7898     Back[x][y] = Back[newx][newy] = 0;
7899   }
7900
7901   Store[x][y] = EL_EMPTY;
7902   MovPos[x][y] = 0;
7903   MovDir[x][y] = 0;
7904   MovDelay[x][y] = 0;
7905
7906   MovDelay[newx][newy] = 0;
7907
7908   if (CAN_CHANGE_OR_HAS_ACTION(element))
7909   {
7910     /* copy element change control values to new field */
7911     ChangeDelay[newx][newy] = ChangeDelay[x][y];
7912     ChangePage[newx][newy]  = ChangePage[x][y];
7913     ChangeCount[newx][newy] = ChangeCount[x][y];
7914     ChangeEvent[newx][newy] = ChangeEvent[x][y];
7915   }
7916
7917 #if USE_NEW_CUSTOM_VALUE
7918     CustomValue[newx][newy] = CustomValue[x][y];
7919 #endif
7920
7921   ChangeDelay[x][y] = 0;
7922   ChangePage[x][y] = -1;
7923   ChangeCount[x][y] = 0;
7924   ChangeEvent[x][y] = -1;
7925
7926 #if USE_NEW_CUSTOM_VALUE
7927   CustomValue[x][y] = 0;
7928 #endif
7929
7930   /* copy animation control values to new field */
7931   GfxFrame[newx][newy]  = GfxFrame[x][y];
7932   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
7933   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
7934   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
7935
7936   Pushed[x][y] = Pushed[newx][newy] = FALSE;
7937
7938   /* some elements can leave other elements behind after moving */
7939 #if 1
7940   if (ei->move_leave_element != EL_EMPTY &&
7941       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7942       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7943 #else
7944   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7945       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7946       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7947 #endif
7948   {
7949     int move_leave_element = ei->move_leave_element;
7950
7951 #if 1
7952 #if 1
7953     /* this makes it possible to leave the removed element again */
7954     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7955       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7956 #else
7957     /* this makes it possible to leave the removed element again */
7958     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7959       move_leave_element = stored;
7960 #endif
7961 #else
7962     /* this makes it possible to leave the removed element again */
7963     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7964         ei->move_leave_element == EL_TRIGGER_ELEMENT)
7965       move_leave_element = stored;
7966 #endif
7967
7968     Feld[x][y] = move_leave_element;
7969
7970     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7971       MovDir[x][y] = direction;
7972
7973     InitField(x, y, FALSE);
7974
7975     if (GFX_CRUMBLED(Feld[x][y]))
7976       DrawLevelFieldCrumbledSandNeighbours(x, y);
7977
7978     if (ELEM_IS_PLAYER(move_leave_element))
7979       RelocatePlayer(x, y, move_leave_element);
7980   }
7981
7982   /* do this after checking for left-behind element */
7983   ResetGfxAnimation(x, y);      /* reset animation values for old field */
7984
7985   if (!CAN_MOVE(element) ||
7986       (CAN_FALL(element) && direction == MV_DOWN &&
7987        (element == EL_SPRING ||
7988         element_info[element].move_pattern == MV_WHEN_PUSHED ||
7989         element_info[element].move_pattern == MV_WHEN_DROPPED)))
7990     GfxDir[x][y] = MovDir[newx][newy] = 0;
7991
7992   DrawLevelField(x, y);
7993   DrawLevelField(newx, newy);
7994
7995   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
7996
7997   /* prevent pushed element from moving on in pushed direction */
7998   if (pushed_by_player && CAN_MOVE(element) &&
7999       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8000       !(element_info[element].move_pattern & direction))
8001     TurnRound(newx, newy);
8002
8003   /* prevent elements on conveyor belt from moving on in last direction */
8004   if (pushed_by_conveyor && CAN_FALL(element) &&
8005       direction & MV_HORIZONTAL)
8006     MovDir[newx][newy] = 0;
8007
8008   if (!pushed_by_player)
8009   {
8010     int nextx = newx + dx, nexty = newy + dy;
8011     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8012
8013     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8014
8015     if (CAN_FALL(element) && direction == MV_DOWN)
8016       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8017
8018     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8019       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8020
8021 #if USE_FIX_IMPACT_COLLISION
8022     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8023       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8024 #endif
8025   }
8026
8027   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8028   {
8029     TestIfBadThingTouchesPlayer(newx, newy);
8030     TestIfBadThingTouchesFriend(newx, newy);
8031
8032     if (!IS_CUSTOM_ELEMENT(element))
8033       TestIfBadThingTouchesOtherBadThing(newx, newy);
8034   }
8035   else if (element == EL_PENGUIN)
8036     TestIfFriendTouchesBadThing(newx, newy);
8037
8038   /* give the player one last chance (one more frame) to move away */
8039   if (CAN_FALL(element) && direction == MV_DOWN &&
8040       (last_line || (!IS_FREE(x, newy + 1) &&
8041                      (!IS_PLAYER(x, newy + 1) ||
8042                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8043     Impact(x, newy);
8044
8045   if (pushed_by_player && !game.use_change_when_pushing_bug)
8046   {
8047     int push_side = MV_DIR_OPPOSITE(direction);
8048     struct PlayerInfo *player = PLAYERINFO(x, y);
8049
8050     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8051                                player->index_bit, push_side);
8052     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8053                                         player->index_bit, push_side);
8054   }
8055
8056   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8057     MovDelay[newx][newy] = 1;
8058
8059   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8060
8061   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8062
8063 #if 0
8064   if (ChangePage[newx][newy] != -1)             /* delayed change */
8065   {
8066     int page = ChangePage[newx][newy];
8067     struct ElementChangeInfo *change = &ei->change_page[page];
8068
8069     ChangePage[newx][newy] = -1;
8070
8071     if (change->can_change)
8072     {
8073       if (ChangeElement(newx, newy, element, page))
8074       {
8075         if (change->post_change_function)
8076           change->post_change_function(newx, newy);
8077       }
8078     }
8079
8080     if (change->has_action)
8081       ExecuteCustomElementAction(newx, newy, element, page);
8082   }
8083 #endif
8084
8085   TestIfElementHitsCustomElement(newx, newy, direction);
8086   TestIfPlayerTouchesCustomElement(newx, newy);
8087   TestIfElementTouchesCustomElement(newx, newy);
8088
8089   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8090       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8091     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8092                              MV_DIR_OPPOSITE(direction));
8093 }
8094
8095 int AmoebeNachbarNr(int ax, int ay)
8096 {
8097   int i;
8098   int element = Feld[ax][ay];
8099   int group_nr = 0;
8100   static int xy[4][2] =
8101   {
8102     { 0, -1 },
8103     { -1, 0 },
8104     { +1, 0 },
8105     { 0, +1 }
8106   };
8107
8108   for (i = 0; i < NUM_DIRECTIONS; i++)
8109   {
8110     int x = ax + xy[i][0];
8111     int y = ay + xy[i][1];
8112
8113     if (!IN_LEV_FIELD(x, y))
8114       continue;
8115
8116     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8117       group_nr = AmoebaNr[x][y];
8118   }
8119
8120   return group_nr;
8121 }
8122
8123 void AmoebenVereinigen(int ax, int ay)
8124 {
8125   int i, x, y, xx, yy;
8126   int new_group_nr = AmoebaNr[ax][ay];
8127   static int xy[4][2] =
8128   {
8129     { 0, -1 },
8130     { -1, 0 },
8131     { +1, 0 },
8132     { 0, +1 }
8133   };
8134
8135   if (new_group_nr == 0)
8136     return;
8137
8138   for (i = 0; i < NUM_DIRECTIONS; i++)
8139   {
8140     x = ax + xy[i][0];
8141     y = ay + xy[i][1];
8142
8143     if (!IN_LEV_FIELD(x, y))
8144       continue;
8145
8146     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8147          Feld[x][y] == EL_BD_AMOEBA ||
8148          Feld[x][y] == EL_AMOEBA_DEAD) &&
8149         AmoebaNr[x][y] != new_group_nr)
8150     {
8151       int old_group_nr = AmoebaNr[x][y];
8152
8153       if (old_group_nr == 0)
8154         return;
8155
8156       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8157       AmoebaCnt[old_group_nr] = 0;
8158       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8159       AmoebaCnt2[old_group_nr] = 0;
8160
8161       SCAN_PLAYFIELD(xx, yy)
8162       {
8163         if (AmoebaNr[xx][yy] == old_group_nr)
8164           AmoebaNr[xx][yy] = new_group_nr;
8165       }
8166     }
8167   }
8168 }
8169
8170 void AmoebeUmwandeln(int ax, int ay)
8171 {
8172   int i, x, y;
8173
8174   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8175   {
8176     int group_nr = AmoebaNr[ax][ay];
8177
8178 #ifdef DEBUG
8179     if (group_nr == 0)
8180     {
8181       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8182       printf("AmoebeUmwandeln(): This should never happen!\n");
8183       return;
8184     }
8185 #endif
8186
8187     SCAN_PLAYFIELD(x, y)
8188     {
8189       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8190       {
8191         AmoebaNr[x][y] = 0;
8192         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8193       }
8194     }
8195
8196     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8197                             SND_AMOEBA_TURNING_TO_GEM :
8198                             SND_AMOEBA_TURNING_TO_ROCK));
8199     Bang(ax, ay);
8200   }
8201   else
8202   {
8203     static int xy[4][2] =
8204     {
8205       { 0, -1 },
8206       { -1, 0 },
8207       { +1, 0 },
8208       { 0, +1 }
8209     };
8210
8211     for (i = 0; i < NUM_DIRECTIONS; i++)
8212     {
8213       x = ax + xy[i][0];
8214       y = ay + xy[i][1];
8215
8216       if (!IN_LEV_FIELD(x, y))
8217         continue;
8218
8219       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8220       {
8221         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8222                               SND_AMOEBA_TURNING_TO_GEM :
8223                               SND_AMOEBA_TURNING_TO_ROCK));
8224         Bang(x, y);
8225       }
8226     }
8227   }
8228 }
8229
8230 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8231 {
8232   int x, y;
8233   int group_nr = AmoebaNr[ax][ay];
8234   boolean done = FALSE;
8235
8236 #ifdef DEBUG
8237   if (group_nr == 0)
8238   {
8239     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8240     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8241     return;
8242   }
8243 #endif
8244
8245   SCAN_PLAYFIELD(x, y)
8246   {
8247     if (AmoebaNr[x][y] == group_nr &&
8248         (Feld[x][y] == EL_AMOEBA_DEAD ||
8249          Feld[x][y] == EL_BD_AMOEBA ||
8250          Feld[x][y] == EL_AMOEBA_GROWING))
8251     {
8252       AmoebaNr[x][y] = 0;
8253       Feld[x][y] = new_element;
8254       InitField(x, y, FALSE);
8255       DrawLevelField(x, y);
8256       done = TRUE;
8257     }
8258   }
8259
8260   if (done)
8261     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8262                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8263                             SND_BD_AMOEBA_TURNING_TO_GEM));
8264 }
8265
8266 void AmoebeWaechst(int x, int y)
8267 {
8268   static unsigned long sound_delay = 0;
8269   static unsigned long sound_delay_value = 0;
8270
8271   if (!MovDelay[x][y])          /* start new growing cycle */
8272   {
8273     MovDelay[x][y] = 7;
8274
8275     if (DelayReached(&sound_delay, sound_delay_value))
8276     {
8277       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8278       sound_delay_value = 30;
8279     }
8280   }
8281
8282   if (MovDelay[x][y])           /* wait some time before growing bigger */
8283   {
8284     MovDelay[x][y]--;
8285     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8286     {
8287       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8288                                            6 - MovDelay[x][y]);
8289
8290       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8291     }
8292
8293     if (!MovDelay[x][y])
8294     {
8295       Feld[x][y] = Store[x][y];
8296       Store[x][y] = 0;
8297       DrawLevelField(x, y);
8298     }
8299   }
8300 }
8301
8302 void AmoebaDisappearing(int x, int y)
8303 {
8304   static unsigned long sound_delay = 0;
8305   static unsigned long sound_delay_value = 0;
8306
8307   if (!MovDelay[x][y])          /* start new shrinking cycle */
8308   {
8309     MovDelay[x][y] = 7;
8310
8311     if (DelayReached(&sound_delay, sound_delay_value))
8312       sound_delay_value = 30;
8313   }
8314
8315   if (MovDelay[x][y])           /* wait some time before shrinking */
8316   {
8317     MovDelay[x][y]--;
8318     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8319     {
8320       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8321                                            6 - MovDelay[x][y]);
8322
8323       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8324     }
8325
8326     if (!MovDelay[x][y])
8327     {
8328       Feld[x][y] = EL_EMPTY;
8329       DrawLevelField(x, y);
8330
8331       /* don't let mole enter this field in this cycle;
8332          (give priority to objects falling to this field from above) */
8333       Stop[x][y] = TRUE;
8334     }
8335   }
8336 }
8337
8338 void AmoebeAbleger(int ax, int ay)
8339 {
8340   int i;
8341   int element = Feld[ax][ay];
8342   int graphic = el2img(element);
8343   int newax = ax, neway = ay;
8344   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8345   static int xy[4][2] =
8346   {
8347     { 0, -1 },
8348     { -1, 0 },
8349     { +1, 0 },
8350     { 0, +1 }
8351   };
8352
8353   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8354   {
8355     Feld[ax][ay] = EL_AMOEBA_DEAD;
8356     DrawLevelField(ax, ay);
8357     return;
8358   }
8359
8360   if (IS_ANIMATED(graphic))
8361     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8362
8363   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8364     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8365
8366   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8367   {
8368     MovDelay[ax][ay]--;
8369     if (MovDelay[ax][ay])
8370       return;
8371   }
8372
8373   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8374   {
8375     int start = RND(4);
8376     int x = ax + xy[start][0];
8377     int y = ay + xy[start][1];
8378
8379     if (!IN_LEV_FIELD(x, y))
8380       return;
8381
8382     if (IS_FREE(x, y) ||
8383         CAN_GROW_INTO(Feld[x][y]) ||
8384         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8385         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8386     {
8387       newax = x;
8388       neway = y;
8389     }
8390
8391     if (newax == ax && neway == ay)
8392       return;
8393   }
8394   else                          /* normal or "filled" (BD style) amoeba */
8395   {
8396     int start = RND(4);
8397     boolean waiting_for_player = FALSE;
8398
8399     for (i = 0; i < NUM_DIRECTIONS; i++)
8400     {
8401       int j = (start + i) % 4;
8402       int x = ax + xy[j][0];
8403       int y = ay + xy[j][1];
8404
8405       if (!IN_LEV_FIELD(x, y))
8406         continue;
8407
8408       if (IS_FREE(x, y) ||
8409           CAN_GROW_INTO(Feld[x][y]) ||
8410           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8411           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8412       {
8413         newax = x;
8414         neway = y;
8415         break;
8416       }
8417       else if (IS_PLAYER(x, y))
8418         waiting_for_player = TRUE;
8419     }
8420
8421     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8422     {
8423       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8424       {
8425         Feld[ax][ay] = EL_AMOEBA_DEAD;
8426         DrawLevelField(ax, ay);
8427         AmoebaCnt[AmoebaNr[ax][ay]]--;
8428
8429         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8430         {
8431           if (element == EL_AMOEBA_FULL)
8432             AmoebeUmwandeln(ax, ay);
8433           else if (element == EL_BD_AMOEBA)
8434             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8435         }
8436       }
8437       return;
8438     }
8439     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8440     {
8441       /* amoeba gets larger by growing in some direction */
8442
8443       int new_group_nr = AmoebaNr[ax][ay];
8444
8445 #ifdef DEBUG
8446   if (new_group_nr == 0)
8447   {
8448     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8449     printf("AmoebeAbleger(): This should never happen!\n");
8450     return;
8451   }
8452 #endif
8453
8454       AmoebaNr[newax][neway] = new_group_nr;
8455       AmoebaCnt[new_group_nr]++;
8456       AmoebaCnt2[new_group_nr]++;
8457
8458       /* if amoeba touches other amoeba(s) after growing, unify them */
8459       AmoebenVereinigen(newax, neway);
8460
8461       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8462       {
8463         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8464         return;
8465       }
8466     }
8467   }
8468
8469   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8470       (neway == lev_fieldy - 1 && newax != ax))
8471   {
8472     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8473     Store[newax][neway] = element;
8474   }
8475   else if (neway == ay || element == EL_EMC_DRIPPER)
8476   {
8477     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8478
8479     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8480   }
8481   else
8482   {
8483     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8484     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8485     Store[ax][ay] = EL_AMOEBA_DROP;
8486     ContinueMoving(ax, ay);
8487     return;
8488   }
8489
8490   DrawLevelField(newax, neway);
8491 }
8492
8493 void Life(int ax, int ay)
8494 {
8495   int x1, y1, x2, y2;
8496   int life_time = 40;
8497   int element = Feld[ax][ay];
8498   int graphic = el2img(element);
8499   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8500                          level.biomaze);
8501   boolean changed = FALSE;
8502
8503   if (IS_ANIMATED(graphic))
8504     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8505
8506   if (Stop[ax][ay])
8507     return;
8508
8509   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8510     MovDelay[ax][ay] = life_time;
8511
8512   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8513   {
8514     MovDelay[ax][ay]--;
8515     if (MovDelay[ax][ay])
8516       return;
8517   }
8518
8519   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8520   {
8521     int xx = ax+x1, yy = ay+y1;
8522     int nachbarn = 0;
8523
8524     if (!IN_LEV_FIELD(xx, yy))
8525       continue;
8526
8527     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8528     {
8529       int x = xx+x2, y = yy+y2;
8530
8531       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8532         continue;
8533
8534       if (((Feld[x][y] == element ||
8535             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8536            !Stop[x][y]) ||
8537           (IS_FREE(x, y) && Stop[x][y]))
8538         nachbarn++;
8539     }
8540
8541     if (xx == ax && yy == ay)           /* field in the middle */
8542     {
8543       if (nachbarn < life_parameter[0] ||
8544           nachbarn > life_parameter[1])
8545       {
8546         Feld[xx][yy] = EL_EMPTY;
8547         if (!Stop[xx][yy])
8548           DrawLevelField(xx, yy);
8549         Stop[xx][yy] = TRUE;
8550         changed = TRUE;
8551       }
8552     }
8553     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8554     {                                   /* free border field */
8555       if (nachbarn >= life_parameter[2] &&
8556           nachbarn <= life_parameter[3])
8557       {
8558         Feld[xx][yy] = element;
8559         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8560         if (!Stop[xx][yy])
8561           DrawLevelField(xx, yy);
8562         Stop[xx][yy] = TRUE;
8563         changed = TRUE;
8564       }
8565     }
8566   }
8567
8568   if (changed)
8569     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8570                    SND_GAME_OF_LIFE_GROWING);
8571 }
8572
8573 static void InitRobotWheel(int x, int y)
8574 {
8575   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8576 }
8577
8578 static void RunRobotWheel(int x, int y)
8579 {
8580   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8581 }
8582
8583 static void StopRobotWheel(int x, int y)
8584 {
8585   if (ZX == x && ZY == y)
8586     ZX = ZY = -1;
8587 }
8588
8589 static void InitTimegateWheel(int x, int y)
8590 {
8591   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8592 }
8593
8594 static void RunTimegateWheel(int x, int y)
8595 {
8596   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8597 }
8598
8599 static void InitMagicBallDelay(int x, int y)
8600 {
8601 #if 1
8602   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8603 #else
8604   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8605 #endif
8606 }
8607
8608 static void ActivateMagicBall(int bx, int by)
8609 {
8610   int x, y;
8611
8612   if (level.ball_random)
8613   {
8614     int pos_border = RND(8);    /* select one of the eight border elements */
8615     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8616     int xx = pos_content % 3;
8617     int yy = pos_content / 3;
8618
8619     x = bx - 1 + xx;
8620     y = by - 1 + yy;
8621
8622     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8623       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8624   }
8625   else
8626   {
8627     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8628     {
8629       int xx = x - bx + 1;
8630       int yy = y - by + 1;
8631
8632       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8633         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8634     }
8635   }
8636
8637   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8638 }
8639
8640 void CheckExit(int x, int y)
8641 {
8642   if (local_player->gems_still_needed > 0 ||
8643       local_player->sokobanfields_still_needed > 0 ||
8644       local_player->lights_still_needed > 0)
8645   {
8646     int element = Feld[x][y];
8647     int graphic = el2img(element);
8648
8649     if (IS_ANIMATED(graphic))
8650       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8651
8652     return;
8653   }
8654
8655   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8656     return;
8657
8658   Feld[x][y] = EL_EXIT_OPENING;
8659
8660   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8661 }
8662
8663 void CheckExitEM(int x, int y)
8664 {
8665   if (local_player->gems_still_needed > 0 ||
8666       local_player->sokobanfields_still_needed > 0 ||
8667       local_player->lights_still_needed > 0)
8668   {
8669     int element = Feld[x][y];
8670     int graphic = el2img(element);
8671
8672     if (IS_ANIMATED(graphic))
8673       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8674
8675     return;
8676   }
8677
8678   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8679     return;
8680
8681   Feld[x][y] = EL_EM_EXIT_OPENING;
8682
8683   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8684 }
8685
8686 void CheckExitSteel(int x, int y)
8687 {
8688   if (local_player->gems_still_needed > 0 ||
8689       local_player->sokobanfields_still_needed > 0 ||
8690       local_player->lights_still_needed > 0)
8691   {
8692     int element = Feld[x][y];
8693     int graphic = el2img(element);
8694
8695     if (IS_ANIMATED(graphic))
8696       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8697
8698     return;
8699   }
8700
8701   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8702     return;
8703
8704   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8705
8706   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8707 }
8708
8709 void CheckExitSteelEM(int x, int y)
8710 {
8711   if (local_player->gems_still_needed > 0 ||
8712       local_player->sokobanfields_still_needed > 0 ||
8713       local_player->lights_still_needed > 0)
8714   {
8715     int element = Feld[x][y];
8716     int graphic = el2img(element);
8717
8718     if (IS_ANIMATED(graphic))
8719       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8720
8721     return;
8722   }
8723
8724   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8725     return;
8726
8727   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8728
8729   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8730 }
8731
8732 void CheckExitSP(int x, int y)
8733 {
8734   if (local_player->gems_still_needed > 0)
8735   {
8736     int element = Feld[x][y];
8737     int graphic = el2img(element);
8738
8739     if (IS_ANIMATED(graphic))
8740       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8741
8742     return;
8743   }
8744
8745   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8746     return;
8747
8748   Feld[x][y] = EL_SP_EXIT_OPENING;
8749
8750   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8751 }
8752
8753 static void CloseAllOpenTimegates()
8754 {
8755   int x, y;
8756
8757   SCAN_PLAYFIELD(x, y)
8758   {
8759     int element = Feld[x][y];
8760
8761     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8762     {
8763       Feld[x][y] = EL_TIMEGATE_CLOSING;
8764
8765       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8766     }
8767   }
8768 }
8769
8770 void DrawTwinkleOnField(int x, int y)
8771 {
8772   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8773     return;
8774
8775   if (Feld[x][y] == EL_BD_DIAMOND)
8776     return;
8777
8778   if (MovDelay[x][y] == 0)      /* next animation frame */
8779     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8780
8781   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8782   {
8783     MovDelay[x][y]--;
8784
8785     if (setup.direct_draw && MovDelay[x][y])
8786       SetDrawtoField(DRAW_BUFFERED);
8787
8788     DrawLevelElementAnimation(x, y, Feld[x][y]);
8789
8790     if (MovDelay[x][y] != 0)
8791     {
8792       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8793                                            10 - MovDelay[x][y]);
8794
8795       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8796
8797       if (setup.direct_draw)
8798       {
8799         int dest_x, dest_y;
8800
8801         dest_x = FX + SCREENX(x) * TILEX;
8802         dest_y = FY + SCREENY(y) * TILEY;
8803
8804         BlitBitmap(drawto_field, window,
8805                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8806         SetDrawtoField(DRAW_DIRECT);
8807       }
8808     }
8809   }
8810 }
8811
8812 void MauerWaechst(int x, int y)
8813 {
8814   int delay = 6;
8815
8816   if (!MovDelay[x][y])          /* next animation frame */
8817     MovDelay[x][y] = 3 * delay;
8818
8819   if (MovDelay[x][y])           /* wait some time before next frame */
8820   {
8821     MovDelay[x][y]--;
8822
8823     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8824     {
8825       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8826       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8827
8828       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8829     }
8830
8831     if (!MovDelay[x][y])
8832     {
8833       if (MovDir[x][y] == MV_LEFT)
8834       {
8835         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8836           DrawLevelField(x - 1, y);
8837       }
8838       else if (MovDir[x][y] == MV_RIGHT)
8839       {
8840         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8841           DrawLevelField(x + 1, y);
8842       }
8843       else if (MovDir[x][y] == MV_UP)
8844       {
8845         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8846           DrawLevelField(x, y - 1);
8847       }
8848       else
8849       {
8850         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8851           DrawLevelField(x, y + 1);
8852       }
8853
8854       Feld[x][y] = Store[x][y];
8855       Store[x][y] = 0;
8856       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8857       DrawLevelField(x, y);
8858     }
8859   }
8860 }
8861
8862 void MauerAbleger(int ax, int ay)
8863 {
8864   int element = Feld[ax][ay];
8865   int graphic = el2img(element);
8866   boolean oben_frei = FALSE, unten_frei = FALSE;
8867   boolean links_frei = FALSE, rechts_frei = FALSE;
8868   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8869   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8870   boolean new_wall = FALSE;
8871
8872   if (IS_ANIMATED(graphic))
8873     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8874
8875   if (!MovDelay[ax][ay])        /* start building new wall */
8876     MovDelay[ax][ay] = 6;
8877
8878   if (MovDelay[ax][ay])         /* wait some time before building new wall */
8879   {
8880     MovDelay[ax][ay]--;
8881     if (MovDelay[ax][ay])
8882       return;
8883   }
8884
8885   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8886     oben_frei = TRUE;
8887   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8888     unten_frei = TRUE;
8889   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8890     links_frei = TRUE;
8891   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8892     rechts_frei = TRUE;
8893
8894   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
8895       element == EL_EXPANDABLE_WALL_ANY)
8896   {
8897     if (oben_frei)
8898     {
8899       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
8900       Store[ax][ay-1] = element;
8901       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8902       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8903         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8904                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
8905       new_wall = TRUE;
8906     }
8907     if (unten_frei)
8908     {
8909       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
8910       Store[ax][ay+1] = element;
8911       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8912       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8913         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8914                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8915       new_wall = TRUE;
8916     }
8917   }
8918
8919   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8920       element == EL_EXPANDABLE_WALL_ANY ||
8921       element == EL_EXPANDABLE_WALL ||
8922       element == EL_BD_EXPANDABLE_WALL)
8923   {
8924     if (links_frei)
8925     {
8926       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8927       Store[ax-1][ay] = element;
8928       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8929       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8930         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8931                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8932       new_wall = TRUE;
8933     }
8934
8935     if (rechts_frei)
8936     {
8937       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8938       Store[ax+1][ay] = element;
8939       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8940       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8941         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8942                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8943       new_wall = TRUE;
8944     }
8945   }
8946
8947   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8948     DrawLevelField(ax, ay);
8949
8950   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8951     oben_massiv = TRUE;
8952   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8953     unten_massiv = TRUE;
8954   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8955     links_massiv = TRUE;
8956   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8957     rechts_massiv = TRUE;
8958
8959   if (((oben_massiv && unten_massiv) ||
8960        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8961        element == EL_EXPANDABLE_WALL) &&
8962       ((links_massiv && rechts_massiv) ||
8963        element == EL_EXPANDABLE_WALL_VERTICAL))
8964     Feld[ax][ay] = EL_WALL;
8965
8966   if (new_wall)
8967     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8968 }
8969
8970 void MauerAblegerStahl(int ax, int ay)
8971 {
8972   int element = Feld[ax][ay];
8973   int graphic = el2img(element);
8974   boolean oben_frei = FALSE, unten_frei = FALSE;
8975   boolean links_frei = FALSE, rechts_frei = FALSE;
8976   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8977   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8978   boolean new_wall = FALSE;
8979
8980   if (IS_ANIMATED(graphic))
8981     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8982
8983   if (!MovDelay[ax][ay])        /* start building new wall */
8984     MovDelay[ax][ay] = 6;
8985
8986   if (MovDelay[ax][ay])         /* wait some time before building new wall */
8987   {
8988     MovDelay[ax][ay]--;
8989     if (MovDelay[ax][ay])
8990       return;
8991   }
8992
8993   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8994     oben_frei = TRUE;
8995   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8996     unten_frei = TRUE;
8997   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8998     links_frei = TRUE;
8999   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9000     rechts_frei = TRUE;
9001
9002   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9003       element == EL_EXPANDABLE_STEELWALL_ANY)
9004   {
9005     if (oben_frei)
9006     {
9007       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9008       Store[ax][ay-1] = element;
9009       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9010       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9011         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9012                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9013       new_wall = TRUE;
9014     }
9015     if (unten_frei)
9016     {
9017       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9018       Store[ax][ay+1] = element;
9019       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9020       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9021         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9022                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9023       new_wall = TRUE;
9024     }
9025   }
9026
9027   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9028       element == EL_EXPANDABLE_STEELWALL_ANY)
9029   {
9030     if (links_frei)
9031     {
9032       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9033       Store[ax-1][ay] = element;
9034       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9035       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9036         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9037                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9038       new_wall = TRUE;
9039     }
9040
9041     if (rechts_frei)
9042     {
9043       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9044       Store[ax+1][ay] = element;
9045       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9046       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9047         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9048                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9049       new_wall = TRUE;
9050     }
9051   }
9052
9053   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9054     oben_massiv = TRUE;
9055   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9056     unten_massiv = TRUE;
9057   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9058     links_massiv = TRUE;
9059   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9060     rechts_massiv = TRUE;
9061
9062   if (((oben_massiv && unten_massiv) ||
9063        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9064       ((links_massiv && rechts_massiv) ||
9065        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9066     Feld[ax][ay] = EL_WALL;
9067
9068   if (new_wall)
9069     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9070 }
9071
9072 void CheckForDragon(int x, int y)
9073 {
9074   int i, j;
9075   boolean dragon_found = FALSE;
9076   static int xy[4][2] =
9077   {
9078     { 0, -1 },
9079     { -1, 0 },
9080     { +1, 0 },
9081     { 0, +1 }
9082   };
9083
9084   for (i = 0; i < NUM_DIRECTIONS; i++)
9085   {
9086     for (j = 0; j < 4; j++)
9087     {
9088       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9089
9090       if (IN_LEV_FIELD(xx, yy) &&
9091           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9092       {
9093         if (Feld[xx][yy] == EL_DRAGON)
9094           dragon_found = TRUE;
9095       }
9096       else
9097         break;
9098     }
9099   }
9100
9101   if (!dragon_found)
9102   {
9103     for (i = 0; i < NUM_DIRECTIONS; i++)
9104     {
9105       for (j = 0; j < 3; j++)
9106       {
9107         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9108   
9109         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9110         {
9111           Feld[xx][yy] = EL_EMPTY;
9112           DrawLevelField(xx, yy);
9113         }
9114         else
9115           break;
9116       }
9117     }
9118   }
9119 }
9120
9121 static void InitBuggyBase(int x, int y)
9122 {
9123   int element = Feld[x][y];
9124   int activating_delay = FRAMES_PER_SECOND / 4;
9125
9126   ChangeDelay[x][y] =
9127     (element == EL_SP_BUGGY_BASE ?
9128      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9129      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9130      activating_delay :
9131      element == EL_SP_BUGGY_BASE_ACTIVE ?
9132      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9133 }
9134
9135 static void WarnBuggyBase(int x, int y)
9136 {
9137   int i;
9138   static int xy[4][2] =
9139   {
9140     { 0, -1 },
9141     { -1, 0 },
9142     { +1, 0 },
9143     { 0, +1 }
9144   };
9145
9146   for (i = 0; i < NUM_DIRECTIONS; i++)
9147   {
9148     int xx = x + xy[i][0];
9149     int yy = y + xy[i][1];
9150
9151     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9152     {
9153       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9154
9155       break;
9156     }
9157   }
9158 }
9159
9160 static void InitTrap(int x, int y)
9161 {
9162   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9163 }
9164
9165 static void ActivateTrap(int x, int y)
9166 {
9167   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9168 }
9169
9170 static void ChangeActiveTrap(int x, int y)
9171 {
9172   int graphic = IMG_TRAP_ACTIVE;
9173
9174   /* if new animation frame was drawn, correct crumbled sand border */
9175   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9176     DrawLevelFieldCrumbledSand(x, y);
9177 }
9178
9179 static int getSpecialActionElement(int element, int number, int base_element)
9180 {
9181   return (element != EL_EMPTY ? element :
9182           number != -1 ? base_element + number - 1 :
9183           EL_EMPTY);
9184 }
9185
9186 static int getModifiedActionNumber(int value_old, int operator, int operand,
9187                                    int value_min, int value_max)
9188 {
9189   int value_new = (operator == CA_MODE_SET      ? operand :
9190                    operator == CA_MODE_ADD      ? value_old + operand :
9191                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9192                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9193                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9194                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9195                    value_old);
9196
9197   return (value_new < value_min ? value_min :
9198           value_new > value_max ? value_max :
9199           value_new);
9200 }
9201
9202 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9203 {
9204   struct ElementInfo *ei = &element_info[element];
9205   struct ElementChangeInfo *change = &ei->change_page[page];
9206   int target_element = change->target_element;
9207   int action_type = change->action_type;
9208   int action_mode = change->action_mode;
9209   int action_arg = change->action_arg;
9210   int i;
9211
9212   if (!change->has_action)
9213     return;
9214
9215   /* ---------- determine action paramater values -------------------------- */
9216
9217   int level_time_value =
9218     (level.time > 0 ? TimeLeft :
9219      TimePlayed);
9220
9221   int action_arg_element =
9222     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9223      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9224      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9225      EL_EMPTY);
9226
9227   int action_arg_direction =
9228     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9229      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9230      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9231      change->actual_trigger_side :
9232      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9233      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9234      MV_NONE);
9235
9236   int action_arg_number_min =
9237     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9238      CA_ARG_MIN);
9239
9240   int action_arg_number_max =
9241     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9242      action_type == CA_SET_LEVEL_GEMS ? 999 :
9243      action_type == CA_SET_LEVEL_TIME ? 9999 :
9244      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9245      action_type == CA_SET_CE_VALUE ? 9999 :
9246      action_type == CA_SET_CE_SCORE ? 9999 :
9247      CA_ARG_MAX);
9248
9249   int action_arg_number_reset =
9250     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9251      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9252      action_type == CA_SET_LEVEL_TIME ? level.time :
9253      action_type == CA_SET_LEVEL_SCORE ? 0 :
9254 #if USE_NEW_CUSTOM_VALUE
9255      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9256 #else
9257      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9258 #endif
9259      action_type == CA_SET_CE_SCORE ? 0 :
9260      0);
9261
9262   int action_arg_number =
9263     (action_arg <= CA_ARG_MAX ? action_arg :
9264      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9265      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9266      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9267      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9268      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9269      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9270 #if USE_NEW_CUSTOM_VALUE
9271      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9272 #else
9273      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9274 #endif
9275      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9276      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9277      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9278      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9279      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9280      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9281      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9282      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9283      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9284      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9285      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9286      -1);
9287
9288   int action_arg_number_old =
9289     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9290      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9291      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9292      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9293      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9294      0);
9295
9296   int action_arg_number_new =
9297     getModifiedActionNumber(action_arg_number_old,
9298                             action_mode, action_arg_number,
9299                             action_arg_number_min, action_arg_number_max);
9300
9301   int trigger_player_bits =
9302     (change->actual_trigger_player >= EL_PLAYER_1 &&
9303      change->actual_trigger_player <= EL_PLAYER_4 ?
9304      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9305      PLAYER_BITS_ANY);
9306
9307   int action_arg_player_bits =
9308     (action_arg >= CA_ARG_PLAYER_1 &&
9309      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9310      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9311      PLAYER_BITS_ANY);
9312
9313   /* ---------- execute action  -------------------------------------------- */
9314
9315   switch (action_type)
9316   {
9317     case CA_NO_ACTION:
9318     {
9319       return;
9320     }
9321
9322     /* ---------- level actions  ------------------------------------------- */
9323
9324     case CA_RESTART_LEVEL:
9325     {
9326       game.restart_level = TRUE;
9327
9328       break;
9329     }
9330
9331     case CA_SHOW_ENVELOPE:
9332     {
9333       int element = getSpecialActionElement(action_arg_element,
9334                                             action_arg_number, EL_ENVELOPE_1);
9335
9336       if (IS_ENVELOPE(element))
9337         local_player->show_envelope = element;
9338
9339       break;
9340     }
9341
9342     case CA_SET_LEVEL_TIME:
9343     {
9344       if (level.time > 0)       /* only modify limited time value */
9345       {
9346         TimeLeft = action_arg_number_new;
9347
9348 #if 1
9349         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
9350
9351         DisplayGameControlValues();
9352 #else
9353         DrawGameValue_Time(TimeLeft);
9354 #endif
9355
9356         if (!TimeLeft && setup.time_limit)
9357           for (i = 0; i < MAX_PLAYERS; i++)
9358             KillPlayer(&stored_player[i]);
9359       }
9360
9361       break;
9362     }
9363
9364     case CA_SET_LEVEL_SCORE:
9365     {
9366       local_player->score = action_arg_number_new;
9367
9368 #if 1
9369       game_control_value[GAME_CONTROL_SCORE] = local_player->score;
9370
9371       DisplayGameControlValues();
9372 #else
9373       DrawGameValue_Score(local_player->score);
9374 #endif
9375
9376       break;
9377     }
9378
9379     case CA_SET_LEVEL_GEMS:
9380     {
9381       local_player->gems_still_needed = action_arg_number_new;
9382
9383 #if 1
9384       game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
9385
9386       DisplayGameControlValues();
9387 #else
9388       DrawGameValue_Emeralds(local_player->gems_still_needed);
9389 #endif
9390
9391       break;
9392     }
9393
9394 #if !USE_PLAYER_GRAVITY
9395     case CA_SET_LEVEL_GRAVITY:
9396     {
9397       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
9398                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
9399                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9400                       game.gravity);
9401       break;
9402     }
9403 #endif
9404
9405     case CA_SET_LEVEL_WIND:
9406     {
9407       game.wind_direction = action_arg_direction;
9408
9409       break;
9410     }
9411
9412     /* ---------- player actions  ------------------------------------------ */
9413
9414     case CA_MOVE_PLAYER:
9415     {
9416       /* automatically move to the next field in specified direction */
9417       for (i = 0; i < MAX_PLAYERS; i++)
9418         if (trigger_player_bits & (1 << i))
9419           stored_player[i].programmed_action = action_arg_direction;
9420
9421       break;
9422     }
9423
9424     case CA_EXIT_PLAYER:
9425     {
9426       for (i = 0; i < MAX_PLAYERS; i++)
9427         if (action_arg_player_bits & (1 << i))
9428           PlayerWins(&stored_player[i]);
9429
9430       break;
9431     }
9432
9433     case CA_KILL_PLAYER:
9434     {
9435       for (i = 0; i < MAX_PLAYERS; i++)
9436         if (action_arg_player_bits & (1 << i))
9437           KillPlayer(&stored_player[i]);
9438
9439       break;
9440     }
9441
9442     case CA_SET_PLAYER_KEYS:
9443     {
9444       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9445       int element = getSpecialActionElement(action_arg_element,
9446                                             action_arg_number, EL_KEY_1);
9447
9448       if (IS_KEY(element))
9449       {
9450         for (i = 0; i < MAX_PLAYERS; i++)
9451         {
9452           if (trigger_player_bits & (1 << i))
9453           {
9454             stored_player[i].key[KEY_NR(element)] = key_state;
9455
9456             DrawGameDoorValues();
9457           }
9458         }
9459       }
9460
9461       break;
9462     }
9463
9464     case CA_SET_PLAYER_SPEED:
9465     {
9466       for (i = 0; i < MAX_PLAYERS; i++)
9467       {
9468         if (trigger_player_bits & (1 << i))
9469         {
9470           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9471
9472           if (action_arg == CA_ARG_SPEED_FASTER &&
9473               stored_player[i].cannot_move)
9474           {
9475             action_arg_number = STEPSIZE_VERY_SLOW;
9476           }
9477           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9478                    action_arg == CA_ARG_SPEED_FASTER)
9479           {
9480             action_arg_number = 2;
9481             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9482                            CA_MODE_MULTIPLY);
9483           }
9484           else if (action_arg == CA_ARG_NUMBER_RESET)
9485           {
9486             action_arg_number = level.initial_player_stepsize[i];
9487           }
9488
9489           move_stepsize =
9490             getModifiedActionNumber(move_stepsize,
9491                                     action_mode,
9492                                     action_arg_number,
9493                                     action_arg_number_min,
9494                                     action_arg_number_max);
9495
9496           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9497         }
9498       }
9499
9500       break;
9501     }
9502
9503     case CA_SET_PLAYER_SHIELD:
9504     {
9505       for (i = 0; i < MAX_PLAYERS; i++)
9506       {
9507         if (trigger_player_bits & (1 << i))
9508         {
9509           if (action_arg == CA_ARG_SHIELD_OFF)
9510           {
9511             stored_player[i].shield_normal_time_left = 0;
9512             stored_player[i].shield_deadly_time_left = 0;
9513           }
9514           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9515           {
9516             stored_player[i].shield_normal_time_left = 999999;
9517           }
9518           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9519           {
9520             stored_player[i].shield_normal_time_left = 999999;
9521             stored_player[i].shield_deadly_time_left = 999999;
9522           }
9523         }
9524       }
9525
9526       break;
9527     }
9528
9529 #if USE_PLAYER_GRAVITY
9530     case CA_SET_PLAYER_GRAVITY:
9531     {
9532       for (i = 0; i < MAX_PLAYERS; i++)
9533       {
9534         if (trigger_player_bits & (1 << i))
9535         {
9536           stored_player[i].gravity =
9537             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9538              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9539              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9540              stored_player[i].gravity);
9541         }
9542       }
9543
9544       break;
9545     }
9546 #endif
9547
9548     case CA_SET_PLAYER_ARTWORK:
9549     {
9550       for (i = 0; i < MAX_PLAYERS; i++)
9551       {
9552         if (trigger_player_bits & (1 << i))
9553         {
9554           int artwork_element = action_arg_element;
9555
9556           if (action_arg == CA_ARG_ELEMENT_RESET)
9557             artwork_element =
9558               (level.use_artwork_element[i] ? level.artwork_element[i] :
9559                stored_player[i].element_nr);
9560
9561 #if USE_GFX_RESET_PLAYER_ARTWORK
9562           if (stored_player[i].artwork_element != artwork_element)
9563             stored_player[i].Frame = 0;
9564 #endif
9565
9566           stored_player[i].artwork_element = artwork_element;
9567
9568           SetPlayerWaiting(&stored_player[i], FALSE);
9569
9570           /* set number of special actions for bored and sleeping animation */
9571           stored_player[i].num_special_action_bored =
9572             get_num_special_action(artwork_element,
9573                                    ACTION_BORING_1, ACTION_BORING_LAST);
9574           stored_player[i].num_special_action_sleeping =
9575             get_num_special_action(artwork_element,
9576                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9577         }
9578       }
9579
9580       break;
9581     }
9582
9583     /* ---------- CE actions  ---------------------------------------------- */
9584
9585     case CA_SET_CE_VALUE:
9586     {
9587 #if USE_NEW_CUSTOM_VALUE
9588       int last_ce_value = CustomValue[x][y];
9589
9590       CustomValue[x][y] = action_arg_number_new;
9591
9592       if (CustomValue[x][y] != last_ce_value)
9593       {
9594         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9595         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9596
9597         if (CustomValue[x][y] == 0)
9598         {
9599           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9600           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9601         }
9602       }
9603 #endif
9604
9605       break;
9606     }
9607
9608     case CA_SET_CE_SCORE:
9609     {
9610 #if USE_NEW_CUSTOM_VALUE
9611       int last_ce_score = ei->collect_score;
9612
9613       ei->collect_score = action_arg_number_new;
9614
9615       if (ei->collect_score != last_ce_score)
9616       {
9617         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9618         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9619
9620         if (ei->collect_score == 0)
9621         {
9622           int xx, yy;
9623
9624           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9625           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9626
9627           /*
9628             This is a very special case that seems to be a mixture between
9629             CheckElementChange() and CheckTriggeredElementChange(): while
9630             the first one only affects single elements that are triggered
9631             directly, the second one affects multiple elements in the playfield
9632             that are triggered indirectly by another element. This is a third
9633             case: Changing the CE score always affects multiple identical CEs,
9634             so every affected CE must be checked, not only the single CE for
9635             which the CE score was changed in the first place (as every instance
9636             of that CE shares the same CE score, and therefore also can change)!
9637           */
9638           SCAN_PLAYFIELD(xx, yy)
9639           {
9640             if (Feld[xx][yy] == element)
9641               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9642                                  CE_SCORE_GETS_ZERO);
9643           }
9644         }
9645       }
9646 #endif
9647
9648       break;
9649     }
9650
9651     /* ---------- engine actions  ------------------------------------------ */
9652
9653     case CA_SET_ENGINE_SCAN_MODE:
9654     {
9655       InitPlayfieldScanMode(action_arg);
9656
9657       break;
9658     }
9659
9660     default:
9661       break;
9662   }
9663 }
9664
9665 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9666 {
9667   int old_element = Feld[x][y];
9668   int new_element = GetElementFromGroupElement(element);
9669   int previous_move_direction = MovDir[x][y];
9670 #if USE_NEW_CUSTOM_VALUE
9671   int last_ce_value = CustomValue[x][y];
9672 #endif
9673   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9674   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9675   boolean add_player_onto_element = (new_element_is_player &&
9676 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9677                                      /* this breaks SnakeBite when a snake is
9678                                         halfway through a door that closes */
9679                                      /* NOW FIXED AT LEVEL INIT IN files.c */
9680                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9681 #endif
9682                                      IS_WALKABLE(old_element));
9683
9684 #if 0
9685   /* check if element under the player changes from accessible to unaccessible
9686      (needed for special case of dropping element which then changes) */
9687   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9688       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9689   {
9690     Bang(x, y);
9691
9692     return;
9693   }
9694 #endif
9695
9696   if (!add_player_onto_element)
9697   {
9698     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9699       RemoveMovingField(x, y);
9700     else
9701       RemoveField(x, y);
9702
9703     Feld[x][y] = new_element;
9704
9705 #if !USE_GFX_RESET_GFX_ANIMATION
9706     ResetGfxAnimation(x, y);
9707     ResetRandomAnimationValue(x, y);
9708 #endif
9709
9710     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9711       MovDir[x][y] = previous_move_direction;
9712
9713 #if USE_NEW_CUSTOM_VALUE
9714     if (element_info[new_element].use_last_ce_value)
9715       CustomValue[x][y] = last_ce_value;
9716 #endif
9717
9718     InitField_WithBug1(x, y, FALSE);
9719
9720     new_element = Feld[x][y];   /* element may have changed */
9721
9722 #if USE_GFX_RESET_GFX_ANIMATION
9723     ResetGfxAnimation(x, y);
9724     ResetRandomAnimationValue(x, y);
9725 #endif
9726
9727     DrawLevelField(x, y);
9728
9729     if (GFX_CRUMBLED(new_element))
9730       DrawLevelFieldCrumbledSandNeighbours(x, y);
9731   }
9732
9733 #if 1
9734   /* check if element under the player changes from accessible to unaccessible
9735      (needed for special case of dropping element which then changes) */
9736   /* (must be checked after creating new element for walkable group elements) */
9737 #if USE_FIX_KILLED_BY_NON_WALKABLE
9738   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9739       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9740   {
9741     Bang(x, y);
9742
9743     return;
9744   }
9745 #else
9746   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9747       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9748   {
9749     Bang(x, y);
9750
9751     return;
9752   }
9753 #endif
9754 #endif
9755
9756   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9757   if (new_element_is_player)
9758     RelocatePlayer(x, y, new_element);
9759
9760   if (is_change)
9761     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9762
9763   TestIfBadThingTouchesPlayer(x, y);
9764   TestIfPlayerTouchesCustomElement(x, y);
9765   TestIfElementTouchesCustomElement(x, y);
9766 }
9767
9768 static void CreateField(int x, int y, int element)
9769 {
9770   CreateFieldExt(x, y, element, FALSE);
9771 }
9772
9773 static void CreateElementFromChange(int x, int y, int element)
9774 {
9775   element = GET_VALID_RUNTIME_ELEMENT(element);
9776
9777 #if USE_STOP_CHANGED_ELEMENTS
9778   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9779   {
9780     int old_element = Feld[x][y];
9781
9782     /* prevent changed element from moving in same engine frame
9783        unless both old and new element can either fall or move */
9784     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9785         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9786       Stop[x][y] = TRUE;
9787   }
9788 #endif
9789
9790   CreateFieldExt(x, y, element, TRUE);
9791 }
9792
9793 static boolean ChangeElement(int x, int y, int element, int page)
9794 {
9795   struct ElementInfo *ei = &element_info[element];
9796   struct ElementChangeInfo *change = &ei->change_page[page];
9797   int ce_value = CustomValue[x][y];
9798   int ce_score = ei->collect_score;
9799   int target_element;
9800   int old_element = Feld[x][y];
9801
9802   /* always use default change event to prevent running into a loop */
9803   if (ChangeEvent[x][y] == -1)
9804     ChangeEvent[x][y] = CE_DELAY;
9805
9806   if (ChangeEvent[x][y] == CE_DELAY)
9807   {
9808     /* reset actual trigger element, trigger player and action element */
9809     change->actual_trigger_element = EL_EMPTY;
9810     change->actual_trigger_player = EL_PLAYER_1;
9811     change->actual_trigger_side = CH_SIDE_NONE;
9812     change->actual_trigger_ce_value = 0;
9813     change->actual_trigger_ce_score = 0;
9814   }
9815
9816   /* do not change elements more than a specified maximum number of changes */
9817   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
9818     return FALSE;
9819
9820   ChangeCount[x][y]++;          /* count number of changes in the same frame */
9821
9822   if (change->explode)
9823   {
9824     Bang(x, y);
9825
9826     return TRUE;
9827   }
9828
9829   if (change->use_target_content)
9830   {
9831     boolean complete_replace = TRUE;
9832     boolean can_replace[3][3];
9833     int xx, yy;
9834
9835     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9836     {
9837       boolean is_empty;
9838       boolean is_walkable;
9839       boolean is_diggable;
9840       boolean is_collectible;
9841       boolean is_removable;
9842       boolean is_destructible;
9843       int ex = x + xx - 1;
9844       int ey = y + yy - 1;
9845       int content_element = change->target_content.e[xx][yy];
9846       int e;
9847
9848       can_replace[xx][yy] = TRUE;
9849
9850       if (ex == x && ey == y)   /* do not check changing element itself */
9851         continue;
9852
9853       if (content_element == EL_EMPTY_SPACE)
9854       {
9855         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
9856
9857         continue;
9858       }
9859
9860       if (!IN_LEV_FIELD(ex, ey))
9861       {
9862         can_replace[xx][yy] = FALSE;
9863         complete_replace = FALSE;
9864
9865         continue;
9866       }
9867
9868       e = Feld[ex][ey];
9869
9870       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9871         e = MovingOrBlocked2Element(ex, ey);
9872
9873       is_empty = (IS_FREE(ex, ey) ||
9874                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
9875
9876       is_walkable     = (is_empty || IS_WALKABLE(e));
9877       is_diggable     = (is_empty || IS_DIGGABLE(e));
9878       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
9879       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
9880       is_removable    = (is_diggable || is_collectible);
9881
9882       can_replace[xx][yy] =
9883         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
9884           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
9885           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
9886           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
9887           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
9888           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
9889          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
9890
9891       if (!can_replace[xx][yy])
9892         complete_replace = FALSE;
9893     }
9894
9895     if (!change->only_if_complete || complete_replace)
9896     {
9897       boolean something_has_changed = FALSE;
9898
9899       if (change->only_if_complete && change->use_random_replace &&
9900           RND(100) < change->random_percentage)
9901         return FALSE;
9902
9903       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9904       {
9905         int ex = x + xx - 1;
9906         int ey = y + yy - 1;
9907         int content_element;
9908
9909         if (can_replace[xx][yy] && (!change->use_random_replace ||
9910                                     RND(100) < change->random_percentage))
9911         {
9912           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9913             RemoveMovingField(ex, ey);
9914
9915           ChangeEvent[ex][ey] = ChangeEvent[x][y];
9916
9917           content_element = change->target_content.e[xx][yy];
9918           target_element = GET_TARGET_ELEMENT(element, content_element, change,
9919                                               ce_value, ce_score);
9920
9921           CreateElementFromChange(ex, ey, target_element);
9922
9923           something_has_changed = TRUE;
9924
9925           /* for symmetry reasons, freeze newly created border elements */
9926           if (ex != x || ey != y)
9927             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
9928         }
9929       }
9930
9931       if (something_has_changed)
9932       {
9933         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9934         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9935       }
9936     }
9937   }
9938   else
9939   {
9940     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9941                                         ce_value, ce_score);
9942
9943     if (element == EL_DIAGONAL_GROWING ||
9944         element == EL_DIAGONAL_SHRINKING)
9945     {
9946       target_element = Store[x][y];
9947
9948       Store[x][y] = EL_EMPTY;
9949     }
9950
9951     CreateElementFromChange(x, y, target_element);
9952
9953     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9954     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9955   }
9956
9957   /* this uses direct change before indirect change */
9958   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9959
9960   return TRUE;
9961 }
9962
9963 #if USE_NEW_DELAYED_ACTION
9964
9965 static void HandleElementChange(int x, int y, int page)
9966 {
9967   int element = MovingOrBlocked2Element(x, y);
9968   struct ElementInfo *ei = &element_info[element];
9969   struct ElementChangeInfo *change = &ei->change_page[page];
9970
9971 #ifdef DEBUG
9972   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
9973       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
9974   {
9975     printf("\n\n");
9976     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9977            x, y, element, element_info[element].token_name);
9978     printf("HandleElementChange(): This should never happen!\n");
9979     printf("\n\n");
9980   }
9981 #endif
9982
9983   /* this can happen with classic bombs on walkable, changing elements */
9984   if (!CAN_CHANGE_OR_HAS_ACTION(element))
9985   {
9986 #if 0
9987     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9988       ChangeDelay[x][y] = 0;
9989 #endif
9990
9991     return;
9992   }
9993
9994   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9995   {
9996     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9997
9998     if (change->can_change)
9999     {
10000 #if 1
10001       /* !!! not clear why graphic animation should be reset at all here !!! */
10002       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10003 #if USE_GFX_RESET_WHEN_NOT_MOVING
10004       /* when a custom element is about to change (for example by change delay),
10005          do not reset graphic animation when the custom element is moving */
10006       if (!IS_MOVING(x, y))
10007 #endif
10008       {
10009         ResetGfxAnimation(x, y);
10010         ResetRandomAnimationValue(x, y);
10011       }
10012 #endif
10013
10014       if (change->pre_change_function)
10015         change->pre_change_function(x, y);
10016     }
10017   }
10018
10019   ChangeDelay[x][y]--;
10020
10021   if (ChangeDelay[x][y] != 0)           /* continue element change */
10022   {
10023     if (change->can_change)
10024     {
10025       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10026
10027       if (IS_ANIMATED(graphic))
10028         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10029
10030       if (change->change_function)
10031         change->change_function(x, y);
10032     }
10033   }
10034   else                                  /* finish element change */
10035   {
10036     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10037     {
10038       page = ChangePage[x][y];
10039       ChangePage[x][y] = -1;
10040
10041       change = &ei->change_page[page];
10042     }
10043
10044     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10045     {
10046       ChangeDelay[x][y] = 1;            /* try change after next move step */
10047       ChangePage[x][y] = page;          /* remember page to use for change */
10048
10049       return;
10050     }
10051
10052     if (change->can_change)
10053     {
10054       if (ChangeElement(x, y, element, page))
10055       {
10056         if (change->post_change_function)
10057           change->post_change_function(x, y);
10058       }
10059     }
10060
10061     if (change->has_action)
10062       ExecuteCustomElementAction(x, y, element, page);
10063   }
10064 }
10065
10066 #else
10067
10068 static void HandleElementChange(int x, int y, int page)
10069 {
10070   int element = MovingOrBlocked2Element(x, y);
10071   struct ElementInfo *ei = &element_info[element];
10072   struct ElementChangeInfo *change = &ei->change_page[page];
10073
10074 #ifdef DEBUG
10075   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10076   {
10077     printf("\n\n");
10078     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10079            x, y, element, element_info[element].token_name);
10080     printf("HandleElementChange(): This should never happen!\n");
10081     printf("\n\n");
10082   }
10083 #endif
10084
10085   /* this can happen with classic bombs on walkable, changing elements */
10086   if (!CAN_CHANGE(element))
10087   {
10088 #if 0
10089     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10090       ChangeDelay[x][y] = 0;
10091 #endif
10092
10093     return;
10094   }
10095
10096   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10097   {
10098     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10099
10100     ResetGfxAnimation(x, y);
10101     ResetRandomAnimationValue(x, y);
10102
10103     if (change->pre_change_function)
10104       change->pre_change_function(x, y);
10105   }
10106
10107   ChangeDelay[x][y]--;
10108
10109   if (ChangeDelay[x][y] != 0)           /* continue element change */
10110   {
10111     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10112
10113     if (IS_ANIMATED(graphic))
10114       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10115
10116     if (change->change_function)
10117       change->change_function(x, y);
10118   }
10119   else                                  /* finish element change */
10120   {
10121     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10122     {
10123       page = ChangePage[x][y];
10124       ChangePage[x][y] = -1;
10125
10126       change = &ei->change_page[page];
10127     }
10128
10129     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10130     {
10131       ChangeDelay[x][y] = 1;            /* try change after next move step */
10132       ChangePage[x][y] = page;          /* remember page to use for change */
10133
10134       return;
10135     }
10136
10137     if (ChangeElement(x, y, element, page))
10138     {
10139       if (change->post_change_function)
10140         change->post_change_function(x, y);
10141     }
10142   }
10143 }
10144
10145 #endif
10146
10147 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10148                                               int trigger_element,
10149                                               int trigger_event,
10150                                               int trigger_player,
10151                                               int trigger_side,
10152                                               int trigger_page)
10153 {
10154   boolean change_done_any = FALSE;
10155   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10156   int i;
10157
10158   if (!(trigger_events[trigger_element][trigger_event]))
10159     return FALSE;
10160
10161 #if 0
10162   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10163          trigger_event, recursion_loop_depth, recursion_loop_detected,
10164          recursion_loop_element, EL_NAME(recursion_loop_element));
10165 #endif
10166
10167   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10168
10169   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10170   {
10171     int element = EL_CUSTOM_START + i;
10172     boolean change_done = FALSE;
10173     int p;
10174
10175     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10176         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10177       continue;
10178
10179     for (p = 0; p < element_info[element].num_change_pages; p++)
10180     {
10181       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10182
10183       if (change->can_change_or_has_action &&
10184           change->has_event[trigger_event] &&
10185           change->trigger_side & trigger_side &&
10186           change->trigger_player & trigger_player &&
10187           change->trigger_page & trigger_page_bits &&
10188           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10189       {
10190         change->actual_trigger_element = trigger_element;
10191         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10192         change->actual_trigger_side = trigger_side;
10193         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10194         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10195
10196         if ((change->can_change && !change_done) || change->has_action)
10197         {
10198           int x, y;
10199
10200           SCAN_PLAYFIELD(x, y)
10201           {
10202             if (Feld[x][y] == element)
10203             {
10204               if (change->can_change && !change_done)
10205               {
10206                 ChangeDelay[x][y] = 1;
10207                 ChangeEvent[x][y] = trigger_event;
10208
10209                 HandleElementChange(x, y, p);
10210               }
10211 #if USE_NEW_DELAYED_ACTION
10212               else if (change->has_action)
10213               {
10214                 ExecuteCustomElementAction(x, y, element, p);
10215                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10216               }
10217 #else
10218               if (change->has_action)
10219               {
10220                 ExecuteCustomElementAction(x, y, element, p);
10221                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10222               }
10223 #endif
10224             }
10225           }
10226
10227           if (change->can_change)
10228           {
10229             change_done = TRUE;
10230             change_done_any = TRUE;
10231           }
10232         }
10233       }
10234     }
10235   }
10236
10237   RECURSION_LOOP_DETECTION_END();
10238
10239   return change_done_any;
10240 }
10241
10242 static boolean CheckElementChangeExt(int x, int y,
10243                                      int element,
10244                                      int trigger_element,
10245                                      int trigger_event,
10246                                      int trigger_player,
10247                                      int trigger_side)
10248 {
10249   boolean change_done = FALSE;
10250   int p;
10251
10252   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10253       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10254     return FALSE;
10255
10256   if (Feld[x][y] == EL_BLOCKED)
10257   {
10258     Blocked2Moving(x, y, &x, &y);
10259     element = Feld[x][y];
10260   }
10261
10262 #if 0
10263   /* check if element has already changed */
10264   if (Feld[x][y] != element)
10265     return FALSE;
10266 #else
10267   /* check if element has already changed or is about to change after moving */
10268   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10269        Feld[x][y] != element) ||
10270
10271       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10272        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10273         ChangePage[x][y] != -1)))
10274     return FALSE;
10275 #endif
10276
10277 #if 0
10278   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10279          trigger_event, recursion_loop_depth, recursion_loop_detected,
10280          recursion_loop_element, EL_NAME(recursion_loop_element));
10281 #endif
10282
10283   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10284
10285   for (p = 0; p < element_info[element].num_change_pages; p++)
10286   {
10287     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10288
10289     /* check trigger element for all events where the element that is checked
10290        for changing interacts with a directly adjacent element -- this is
10291        different to element changes that affect other elements to change on the
10292        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10293     boolean check_trigger_element =
10294       (trigger_event == CE_TOUCHING_X ||
10295        trigger_event == CE_HITTING_X ||
10296        trigger_event == CE_HIT_BY_X ||
10297 #if 1
10298        /* this one was forgotten until 3.2.3 */
10299        trigger_event == CE_DIGGING_X);
10300 #endif
10301
10302     if (change->can_change_or_has_action &&
10303         change->has_event[trigger_event] &&
10304         change->trigger_side & trigger_side &&
10305         change->trigger_player & trigger_player &&
10306         (!check_trigger_element ||
10307          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10308     {
10309       change->actual_trigger_element = trigger_element;
10310       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10311       change->actual_trigger_side = trigger_side;
10312       change->actual_trigger_ce_value = CustomValue[x][y];
10313       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10314
10315       /* special case: trigger element not at (x,y) position for some events */
10316       if (check_trigger_element)
10317       {
10318         static struct
10319         {
10320           int dx, dy;
10321         } move_xy[] =
10322           {
10323             {  0,  0 },
10324             { -1,  0 },
10325             { +1,  0 },
10326             {  0,  0 },
10327             {  0, -1 },
10328             {  0,  0 }, { 0, 0 }, { 0, 0 },
10329             {  0, +1 }
10330           };
10331
10332         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10333         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10334
10335         change->actual_trigger_ce_value = CustomValue[xx][yy];
10336         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10337       }
10338
10339       if (change->can_change && !change_done)
10340       {
10341         ChangeDelay[x][y] = 1;
10342         ChangeEvent[x][y] = trigger_event;
10343
10344         HandleElementChange(x, y, p);
10345
10346         change_done = TRUE;
10347       }
10348 #if USE_NEW_DELAYED_ACTION
10349       else if (change->has_action)
10350       {
10351         ExecuteCustomElementAction(x, y, element, p);
10352         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10353       }
10354 #else
10355       if (change->has_action)
10356       {
10357         ExecuteCustomElementAction(x, y, element, p);
10358         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10359       }
10360 #endif
10361     }
10362   }
10363
10364   RECURSION_LOOP_DETECTION_END();
10365
10366   return change_done;
10367 }
10368
10369 static void PlayPlayerSound(struct PlayerInfo *player)
10370 {
10371   int jx = player->jx, jy = player->jy;
10372   int sound_element = player->artwork_element;
10373   int last_action = player->last_action_waiting;
10374   int action = player->action_waiting;
10375
10376   if (player->is_waiting)
10377   {
10378     if (action != last_action)
10379       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10380     else
10381       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10382   }
10383   else
10384   {
10385     if (action != last_action)
10386       StopSound(element_info[sound_element].sound[last_action]);
10387
10388     if (last_action == ACTION_SLEEPING)
10389       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10390   }
10391 }
10392
10393 static void PlayAllPlayersSound()
10394 {
10395   int i;
10396
10397   for (i = 0; i < MAX_PLAYERS; i++)
10398     if (stored_player[i].active)
10399       PlayPlayerSound(&stored_player[i]);
10400 }
10401
10402 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10403 {
10404   boolean last_waiting = player->is_waiting;
10405   int move_dir = player->MovDir;
10406
10407   player->dir_waiting = move_dir;
10408   player->last_action_waiting = player->action_waiting;
10409
10410   if (is_waiting)
10411   {
10412     if (!last_waiting)          /* not waiting -> waiting */
10413     {
10414       player->is_waiting = TRUE;
10415
10416       player->frame_counter_bored =
10417         FrameCounter +
10418         game.player_boring_delay_fixed +
10419         GetSimpleRandom(game.player_boring_delay_random);
10420       player->frame_counter_sleeping =
10421         FrameCounter +
10422         game.player_sleeping_delay_fixed +
10423         GetSimpleRandom(game.player_sleeping_delay_random);
10424
10425       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10426     }
10427
10428     if (game.player_sleeping_delay_fixed +
10429         game.player_sleeping_delay_random > 0 &&
10430         player->anim_delay_counter == 0 &&
10431         player->post_delay_counter == 0 &&
10432         FrameCounter >= player->frame_counter_sleeping)
10433       player->is_sleeping = TRUE;
10434     else if (game.player_boring_delay_fixed +
10435              game.player_boring_delay_random > 0 &&
10436              FrameCounter >= player->frame_counter_bored)
10437       player->is_bored = TRUE;
10438
10439     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10440                               player->is_bored ? ACTION_BORING :
10441                               ACTION_WAITING);
10442
10443     if (player->is_sleeping && player->use_murphy)
10444     {
10445       /* special case for sleeping Murphy when leaning against non-free tile */
10446
10447       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10448           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10449            !IS_MOVING(player->jx - 1, player->jy)))
10450         move_dir = MV_LEFT;
10451       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10452                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10453                 !IS_MOVING(player->jx + 1, player->jy)))
10454         move_dir = MV_RIGHT;
10455       else
10456         player->is_sleeping = FALSE;
10457
10458       player->dir_waiting = move_dir;
10459     }
10460
10461     if (player->is_sleeping)
10462     {
10463       if (player->num_special_action_sleeping > 0)
10464       {
10465         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10466         {
10467           int last_special_action = player->special_action_sleeping;
10468           int num_special_action = player->num_special_action_sleeping;
10469           int special_action =
10470             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10471              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10472              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10473              last_special_action + 1 : ACTION_SLEEPING);
10474           int special_graphic =
10475             el_act_dir2img(player->artwork_element, special_action, move_dir);
10476
10477           player->anim_delay_counter =
10478             graphic_info[special_graphic].anim_delay_fixed +
10479             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10480           player->post_delay_counter =
10481             graphic_info[special_graphic].post_delay_fixed +
10482             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10483
10484           player->special_action_sleeping = special_action;
10485         }
10486
10487         if (player->anim_delay_counter > 0)
10488         {
10489           player->action_waiting = player->special_action_sleeping;
10490           player->anim_delay_counter--;
10491         }
10492         else if (player->post_delay_counter > 0)
10493         {
10494           player->post_delay_counter--;
10495         }
10496       }
10497     }
10498     else if (player->is_bored)
10499     {
10500       if (player->num_special_action_bored > 0)
10501       {
10502         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10503         {
10504           int special_action =
10505             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10506           int special_graphic =
10507             el_act_dir2img(player->artwork_element, special_action, move_dir);
10508
10509           player->anim_delay_counter =
10510             graphic_info[special_graphic].anim_delay_fixed +
10511             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10512           player->post_delay_counter =
10513             graphic_info[special_graphic].post_delay_fixed +
10514             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10515
10516           player->special_action_bored = special_action;
10517         }
10518
10519         if (player->anim_delay_counter > 0)
10520         {
10521           player->action_waiting = player->special_action_bored;
10522           player->anim_delay_counter--;
10523         }
10524         else if (player->post_delay_counter > 0)
10525         {
10526           player->post_delay_counter--;
10527         }
10528       }
10529     }
10530   }
10531   else if (last_waiting)        /* waiting -> not waiting */
10532   {
10533     player->is_waiting = FALSE;
10534     player->is_bored = FALSE;
10535     player->is_sleeping = FALSE;
10536
10537     player->frame_counter_bored = -1;
10538     player->frame_counter_sleeping = -1;
10539
10540     player->anim_delay_counter = 0;
10541     player->post_delay_counter = 0;
10542
10543     player->dir_waiting = player->MovDir;
10544     player->action_waiting = ACTION_DEFAULT;
10545
10546     player->special_action_bored = ACTION_DEFAULT;
10547     player->special_action_sleeping = ACTION_DEFAULT;
10548   }
10549 }
10550
10551 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10552 {
10553   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10554   int left      = player_action & JOY_LEFT;
10555   int right     = player_action & JOY_RIGHT;
10556   int up        = player_action & JOY_UP;
10557   int down      = player_action & JOY_DOWN;
10558   int button1   = player_action & JOY_BUTTON_1;
10559   int button2   = player_action & JOY_BUTTON_2;
10560   int dx        = (left ? -1 : right ? 1 : 0);
10561   int dy        = (up   ? -1 : down  ? 1 : 0);
10562
10563   if (!player->active || tape.pausing)
10564     return 0;
10565
10566   if (player_action)
10567   {
10568     if (button1)
10569       snapped = SnapField(player, dx, dy);
10570     else
10571     {
10572       if (button2)
10573         dropped = DropElement(player);
10574
10575       moved = MovePlayer(player, dx, dy);
10576     }
10577
10578     if (tape.single_step && tape.recording && !tape.pausing)
10579     {
10580       if (button1 || (dropped && !moved))
10581       {
10582         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10583         SnapField(player, 0, 0);                /* stop snapping */
10584       }
10585     }
10586
10587     SetPlayerWaiting(player, FALSE);
10588
10589     return player_action;
10590   }
10591   else
10592   {
10593     /* no actions for this player (no input at player's configured device) */
10594
10595     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10596     SnapField(player, 0, 0);
10597     CheckGravityMovementWhenNotMoving(player);
10598
10599     if (player->MovPos == 0)
10600       SetPlayerWaiting(player, TRUE);
10601
10602     if (player->MovPos == 0)    /* needed for tape.playing */
10603       player->is_moving = FALSE;
10604
10605     player->is_dropping = FALSE;
10606     player->is_dropping_pressed = FALSE;
10607     player->drop_pressed_delay = 0;
10608
10609     return 0;
10610   }
10611 }
10612
10613 static void CheckLevelTime()
10614 {
10615   int i;
10616
10617   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10618   {
10619     if (level.native_em_level->lev->home == 0)  /* all players at home */
10620     {
10621       PlayerWins(local_player);
10622
10623       AllPlayersGone = TRUE;
10624
10625       level.native_em_level->lev->home = -1;
10626     }
10627
10628     if (level.native_em_level->ply[0]->alive == 0 &&
10629         level.native_em_level->ply[1]->alive == 0 &&
10630         level.native_em_level->ply[2]->alive == 0 &&
10631         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10632       AllPlayersGone = TRUE;
10633   }
10634
10635   if (TimeFrames >= FRAMES_PER_SECOND)
10636   {
10637     TimeFrames = 0;
10638     TapeTime++;
10639
10640     for (i = 0; i < MAX_PLAYERS; i++)
10641     {
10642       struct PlayerInfo *player = &stored_player[i];
10643
10644       if (SHIELD_ON(player))
10645       {
10646         player->shield_normal_time_left--;
10647
10648         if (player->shield_deadly_time_left > 0)
10649           player->shield_deadly_time_left--;
10650       }
10651     }
10652
10653     if (!local_player->LevelSolved && !level.use_step_counter)
10654     {
10655       TimePlayed++;
10656
10657       if (TimeLeft > 0)
10658       {
10659         TimeLeft--;
10660
10661         if (TimeLeft <= 10 && setup.time_limit)
10662           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10663
10664 #if 1
10665         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
10666
10667         DisplayGameControlValues();
10668 #else
10669         DrawGameValue_Time(TimeLeft);
10670 #endif
10671
10672         if (!TimeLeft && setup.time_limit)
10673         {
10674           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10675             level.native_em_level->lev->killed_out_of_time = TRUE;
10676           else
10677             for (i = 0; i < MAX_PLAYERS; i++)
10678               KillPlayer(&stored_player[i]);
10679         }
10680       }
10681 #if 1
10682       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10683       {
10684         game_control_value[GAME_CONTROL_TIME] = TimePlayed;
10685
10686         DisplayGameControlValues();
10687       }
10688 #else
10689       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10690         DrawGameValue_Time(TimePlayed);
10691 #endif
10692
10693       level.native_em_level->lev->time =
10694         (level.time == 0 ? TimePlayed : TimeLeft);
10695     }
10696
10697     if (tape.recording || tape.playing)
10698       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10699   }
10700
10701   DrawGameDoorValues();
10702 }
10703
10704 void AdvanceFrameAndPlayerCounters(int player_nr)
10705 {
10706   int i;
10707
10708   /* advance frame counters (global frame counter and time frame counter) */
10709   FrameCounter++;
10710   TimeFrames++;
10711
10712   /* advance player counters (counters for move delay, move animation etc.) */
10713   for (i = 0; i < MAX_PLAYERS; i++)
10714   {
10715     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10716     int move_delay_value = stored_player[i].move_delay_value;
10717     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10718
10719     if (!advance_player_counters)       /* not all players may be affected */
10720       continue;
10721
10722 #if USE_NEW_PLAYER_ANIM
10723     if (move_frames == 0)       /* less than one move per game frame */
10724     {
10725       int stepsize = TILEX / move_delay_value;
10726       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10727       int count = (stored_player[i].is_moving ?
10728                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10729
10730       if (count % delay == 0)
10731         move_frames = 1;
10732     }
10733 #endif
10734
10735     stored_player[i].Frame += move_frames;
10736
10737     if (stored_player[i].MovPos != 0)
10738       stored_player[i].StepFrame += move_frames;
10739
10740     if (stored_player[i].move_delay > 0)
10741       stored_player[i].move_delay--;
10742
10743     /* due to bugs in previous versions, counter must count up, not down */
10744     if (stored_player[i].push_delay != -1)
10745       stored_player[i].push_delay++;
10746
10747     if (stored_player[i].drop_delay > 0)
10748       stored_player[i].drop_delay--;
10749
10750     if (stored_player[i].is_dropping_pressed)
10751       stored_player[i].drop_pressed_delay++;
10752   }
10753 }
10754
10755 void StartGameActions(boolean init_network_game, boolean record_tape,
10756                       long random_seed)
10757 {
10758   unsigned long new_random_seed = InitRND(random_seed);
10759
10760   if (record_tape)
10761     TapeStartRecording(new_random_seed);
10762
10763 #if defined(NETWORK_AVALIABLE)
10764   if (init_network_game)
10765   {
10766     SendToServer_StartPlaying();
10767
10768     return;
10769   }
10770 #endif
10771
10772   InitGame();
10773 }
10774
10775 void GameActions()
10776 {
10777   static unsigned long game_frame_delay = 0;
10778   unsigned long game_frame_delay_value;
10779   byte *recorded_player_action;
10780   byte summarized_player_action = 0;
10781   byte tape_action[MAX_PLAYERS];
10782   int i;
10783
10784   /* detect endless loops, caused by custom element programming */
10785   if (recursion_loop_detected && recursion_loop_depth == 0)
10786   {
10787     char *message = getStringCat3("Internal Error ! Element ",
10788                                   EL_NAME(recursion_loop_element),
10789                                   " caused endless loop ! Quit the game ?");
10790
10791     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10792           EL_NAME(recursion_loop_element));
10793
10794     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10795
10796     recursion_loop_detected = FALSE;    /* if game should be continued */
10797
10798     free(message);
10799
10800     return;
10801   }
10802
10803   if (game.restart_level)
10804     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
10805
10806   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10807   {
10808     if (level.native_em_level->lev->home == 0)  /* all players at home */
10809     {
10810       PlayerWins(local_player);
10811
10812       AllPlayersGone = TRUE;
10813
10814       level.native_em_level->lev->home = -1;
10815     }
10816
10817     if (level.native_em_level->ply[0]->alive == 0 &&
10818         level.native_em_level->ply[1]->alive == 0 &&
10819         level.native_em_level->ply[2]->alive == 0 &&
10820         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10821       AllPlayersGone = TRUE;
10822   }
10823
10824   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10825     GameWon();
10826
10827   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10828     TapeStop();
10829
10830   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
10831     return;
10832
10833   game_frame_delay_value =
10834     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10835
10836   if (tape.playing && tape.warp_forward && !tape.pausing)
10837     game_frame_delay_value = 0;
10838
10839   /* ---------- main game synchronization point ---------- */
10840
10841   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10842
10843   if (network_playing && !network_player_action_received)
10844   {
10845     /* try to get network player actions in time */
10846
10847 #if defined(NETWORK_AVALIABLE)
10848     /* last chance to get network player actions without main loop delay */
10849     HandleNetworking();
10850 #endif
10851
10852     /* game was quit by network peer */
10853     if (game_status != GAME_MODE_PLAYING)
10854       return;
10855
10856     if (!network_player_action_received)
10857       return;           /* failed to get network player actions in time */
10858
10859     /* do not yet reset "network_player_action_received" (for tape.pausing) */
10860   }
10861
10862   if (tape.pausing)
10863     return;
10864
10865   /* at this point we know that we really continue executing the game */
10866
10867   network_player_action_received = FALSE;
10868
10869   /* when playing tape, read previously recorded player input from tape data */
10870   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
10871
10872 #if 1
10873   /* TapePlayAction() may return NULL when toggling to "pause before death" */
10874   if (tape.pausing)
10875     return;
10876 #endif
10877
10878   if (tape.set_centered_player)
10879   {
10880     game.centered_player_nr_next = tape.centered_player_nr_next;
10881     game.set_centered_player = TRUE;
10882   }
10883
10884   for (i = 0; i < MAX_PLAYERS; i++)
10885   {
10886     summarized_player_action |= stored_player[i].action;
10887
10888     if (!network_playing)
10889       stored_player[i].effective_action = stored_player[i].action;
10890   }
10891
10892 #if defined(NETWORK_AVALIABLE)
10893   if (network_playing)
10894     SendToServer_MovePlayer(summarized_player_action);
10895 #endif
10896
10897   if (!options.network && !setup.team_mode)
10898     local_player->effective_action = summarized_player_action;
10899
10900   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
10901   {
10902     for (i = 0; i < MAX_PLAYERS; i++)
10903       stored_player[i].effective_action =
10904         (i == game.centered_player_nr ? summarized_player_action : 0);
10905   }
10906
10907   if (recorded_player_action != NULL)
10908     for (i = 0; i < MAX_PLAYERS; i++)
10909       stored_player[i].effective_action = recorded_player_action[i];
10910
10911   for (i = 0; i < MAX_PLAYERS; i++)
10912   {
10913     tape_action[i] = stored_player[i].effective_action;
10914
10915     /* (this can only happen in the R'n'D game engine) */
10916     if (tape.recording && tape_action[i] && !tape.player_participates[i])
10917       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
10918   }
10919
10920   /* only record actions from input devices, but not programmed actions */
10921   if (tape.recording)
10922     TapeRecordAction(tape_action);
10923
10924   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10925   {
10926     GameActions_EM_Main();
10927   }
10928   else
10929   {
10930     GameActions_RND();
10931   }
10932 }
10933
10934 void GameActions_EM_Main()
10935 {
10936   byte effective_action[MAX_PLAYERS];
10937   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
10938   int i;
10939
10940   for (i = 0; i < MAX_PLAYERS; i++)
10941     effective_action[i] = stored_player[i].effective_action;
10942
10943   GameActions_EM(effective_action, warp_mode);
10944
10945   CheckLevelTime();
10946
10947   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
10948 }
10949
10950 void GameActions_RND()
10951 {
10952   int magic_wall_x = 0, magic_wall_y = 0;
10953   int i, x, y, element, graphic;
10954
10955   InitPlayfieldScanModeVars();
10956
10957 #if USE_ONE_MORE_CHANGE_PER_FRAME
10958   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10959   {
10960     SCAN_PLAYFIELD(x, y)
10961     {
10962       ChangeCount[x][y] = 0;
10963       ChangeEvent[x][y] = -1;
10964     }
10965   }
10966 #endif
10967
10968   if (game.set_centered_player)
10969   {
10970     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
10971
10972     /* switching to "all players" only possible if all players fit to screen */
10973     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
10974     {
10975       game.centered_player_nr_next = game.centered_player_nr;
10976       game.set_centered_player = FALSE;
10977     }
10978
10979     /* do not switch focus to non-existing (or non-active) player */
10980     if (game.centered_player_nr_next >= 0 &&
10981         !stored_player[game.centered_player_nr_next].active)
10982     {
10983       game.centered_player_nr_next = game.centered_player_nr;
10984       game.set_centered_player = FALSE;
10985     }
10986   }
10987
10988   if (game.set_centered_player &&
10989       ScreenMovPos == 0)        /* screen currently aligned at tile position */
10990   {
10991     int sx, sy;
10992
10993     if (game.centered_player_nr_next == -1)
10994     {
10995       setScreenCenteredToAllPlayers(&sx, &sy);
10996     }
10997     else
10998     {
10999       sx = stored_player[game.centered_player_nr_next].jx;
11000       sy = stored_player[game.centered_player_nr_next].jy;
11001     }
11002
11003     game.centered_player_nr = game.centered_player_nr_next;
11004     game.set_centered_player = FALSE;
11005
11006     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11007     DrawGameDoorValues();
11008   }
11009
11010   for (i = 0; i < MAX_PLAYERS; i++)
11011   {
11012     int actual_player_action = stored_player[i].effective_action;
11013
11014 #if 1
11015     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11016        - rnd_equinox_tetrachloride 048
11017        - rnd_equinox_tetrachloride_ii 096
11018        - rnd_emanuel_schmieg 002
11019        - doctor_sloan_ww 001, 020
11020     */
11021     if (stored_player[i].MovPos == 0)
11022       CheckGravityMovement(&stored_player[i]);
11023 #endif
11024
11025     /* overwrite programmed action with tape action */
11026     if (stored_player[i].programmed_action)
11027       actual_player_action = stored_player[i].programmed_action;
11028
11029     PlayerActions(&stored_player[i], actual_player_action);
11030
11031     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11032   }
11033
11034   ScrollScreen(NULL, SCROLL_GO_ON);
11035
11036   /* for backwards compatibility, the following code emulates a fixed bug that
11037      occured when pushing elements (causing elements that just made their last
11038      pushing step to already (if possible) make their first falling step in the
11039      same game frame, which is bad); this code is also needed to use the famous
11040      "spring push bug" which is used in older levels and might be wanted to be
11041      used also in newer levels, but in this case the buggy pushing code is only
11042      affecting the "spring" element and no other elements */
11043
11044   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11045   {
11046     for (i = 0; i < MAX_PLAYERS; i++)
11047     {
11048       struct PlayerInfo *player = &stored_player[i];
11049       int x = player->jx;
11050       int y = player->jy;
11051
11052       if (player->active && player->is_pushing && player->is_moving &&
11053           IS_MOVING(x, y) &&
11054           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11055            Feld[x][y] == EL_SPRING))
11056       {
11057         ContinueMoving(x, y);
11058
11059         /* continue moving after pushing (this is actually a bug) */
11060         if (!IS_MOVING(x, y))
11061           Stop[x][y] = FALSE;
11062       }
11063     }
11064   }
11065
11066 #if 0
11067   debug_print_timestamp(0, "start main loop profiling");
11068 #endif
11069
11070   SCAN_PLAYFIELD(x, y)
11071   {
11072     ChangeCount[x][y] = 0;
11073     ChangeEvent[x][y] = -1;
11074
11075     /* this must be handled before main playfield loop */
11076     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11077     {
11078       MovDelay[x][y]--;
11079       if (MovDelay[x][y] <= 0)
11080         RemoveField(x, y);
11081     }
11082
11083 #if USE_NEW_SNAP_DELAY
11084     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11085     {
11086       MovDelay[x][y]--;
11087       if (MovDelay[x][y] <= 0)
11088       {
11089         RemoveField(x, y);
11090         DrawLevelField(x, y);
11091
11092         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11093       }
11094     }
11095 #endif
11096
11097 #if DEBUG
11098     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11099     {
11100       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11101       printf("GameActions(): This should never happen!\n");
11102
11103       ChangePage[x][y] = -1;
11104     }
11105 #endif
11106
11107     Stop[x][y] = FALSE;
11108     if (WasJustMoving[x][y] > 0)
11109       WasJustMoving[x][y]--;
11110     if (WasJustFalling[x][y] > 0)
11111       WasJustFalling[x][y]--;
11112     if (CheckCollision[x][y] > 0)
11113       CheckCollision[x][y]--;
11114     if (CheckImpact[x][y] > 0)
11115       CheckImpact[x][y]--;
11116
11117     GfxFrame[x][y]++;
11118
11119     /* reset finished pushing action (not done in ContinueMoving() to allow
11120        continuous pushing animation for elements with zero push delay) */
11121     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11122     {
11123       ResetGfxAnimation(x, y);
11124       DrawLevelField(x, y);
11125     }
11126
11127 #if DEBUG
11128     if (IS_BLOCKED(x, y))
11129     {
11130       int oldx, oldy;
11131
11132       Blocked2Moving(x, y, &oldx, &oldy);
11133       if (!IS_MOVING(oldx, oldy))
11134       {
11135         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11136         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11137         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11138         printf("GameActions(): This should never happen!\n");
11139       }
11140     }
11141 #endif
11142   }
11143
11144 #if 0
11145   debug_print_timestamp(0, "- time for pre-main loop:");
11146 #endif
11147
11148 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
11149   SCAN_PLAYFIELD(x, y)
11150   {
11151     element = Feld[x][y];
11152     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11153
11154 #if 1
11155     {
11156 #if 1
11157       int element2 = element;
11158       int graphic2 = graphic;
11159 #else
11160       int element2 = Feld[x][y];
11161       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11162 #endif
11163       int last_gfx_frame = GfxFrame[x][y];
11164
11165       if (graphic_info[graphic2].anim_global_sync)
11166         GfxFrame[x][y] = FrameCounter;
11167       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11168         GfxFrame[x][y] = CustomValue[x][y];
11169       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11170         GfxFrame[x][y] = element_info[element2].collect_score;
11171       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11172         GfxFrame[x][y] = ChangeDelay[x][y];
11173
11174       if (redraw && GfxFrame[x][y] != last_gfx_frame)
11175         DrawLevelGraphicAnimation(x, y, graphic2);
11176     }
11177 #else
11178     ResetGfxFrame(x, y, TRUE);
11179 #endif
11180
11181 #if 1
11182     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11183         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11184       ResetRandomAnimationValue(x, y);
11185 #endif
11186
11187 #if 1
11188     SetRandomAnimationValue(x, y);
11189 #endif
11190
11191 #if 1
11192     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11193 #endif
11194   }
11195 #endif  // -------------------- !!! TEST ONLY !!! --------------------
11196
11197 #if 0
11198   debug_print_timestamp(0, "- time for TEST loop:     -->");
11199 #endif
11200
11201   SCAN_PLAYFIELD(x, y)
11202   {
11203     element = Feld[x][y];
11204     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11205
11206     ResetGfxFrame(x, y, TRUE);
11207
11208     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11209         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11210       ResetRandomAnimationValue(x, y);
11211
11212     SetRandomAnimationValue(x, y);
11213
11214     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11215
11216     if (IS_INACTIVE(element))
11217     {
11218       if (IS_ANIMATED(graphic))
11219         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11220
11221       continue;
11222     }
11223
11224     /* this may take place after moving, so 'element' may have changed */
11225     if (IS_CHANGING(x, y) &&
11226         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11227     {
11228       int page = element_info[element].event_page_nr[CE_DELAY];
11229
11230 #if 1
11231       HandleElementChange(x, y, page);
11232 #else
11233       if (CAN_CHANGE(element))
11234         HandleElementChange(x, y, page);
11235
11236       if (HAS_ACTION(element))
11237         ExecuteCustomElementAction(x, y, element, page);
11238 #endif
11239
11240       element = Feld[x][y];
11241       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11242     }
11243
11244 #if 0   // ---------------------------------------------------------------------
11245
11246     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11247     {
11248       StartMoving(x, y);
11249
11250       element = Feld[x][y];
11251       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11252
11253       if (IS_ANIMATED(graphic) &&
11254           !IS_MOVING(x, y) &&
11255           !Stop[x][y])
11256         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11257
11258       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11259         DrawTwinkleOnField(x, y);
11260     }
11261     else if (IS_MOVING(x, y))
11262       ContinueMoving(x, y);
11263     else
11264     {
11265       switch (element)
11266       {
11267         case EL_ACID:
11268         case EL_EXIT_OPEN:
11269         case EL_EM_EXIT_OPEN:
11270         case EL_SP_EXIT_OPEN:
11271         case EL_STEEL_EXIT_OPEN:
11272         case EL_EM_STEEL_EXIT_OPEN:
11273         case EL_SP_TERMINAL:
11274         case EL_SP_TERMINAL_ACTIVE:
11275         case EL_EXTRA_TIME:
11276         case EL_SHIELD_NORMAL:
11277         case EL_SHIELD_DEADLY:
11278           if (IS_ANIMATED(graphic))
11279             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11280           break;
11281
11282         case EL_DYNAMITE_ACTIVE:
11283         case EL_EM_DYNAMITE_ACTIVE:
11284         case EL_DYNABOMB_PLAYER_1_ACTIVE:
11285         case EL_DYNABOMB_PLAYER_2_ACTIVE:
11286         case EL_DYNABOMB_PLAYER_3_ACTIVE:
11287         case EL_DYNABOMB_PLAYER_4_ACTIVE:
11288         case EL_SP_DISK_RED_ACTIVE:
11289           CheckDynamite(x, y);
11290           break;
11291
11292         case EL_AMOEBA_GROWING:
11293           AmoebeWaechst(x, y);
11294           break;
11295
11296         case EL_AMOEBA_SHRINKING:
11297           AmoebaDisappearing(x, y);
11298           break;
11299
11300 #if !USE_NEW_AMOEBA_CODE
11301         case EL_AMOEBA_WET:
11302         case EL_AMOEBA_DRY:
11303         case EL_AMOEBA_FULL:
11304         case EL_BD_AMOEBA:
11305         case EL_EMC_DRIPPER:
11306           AmoebeAbleger(x, y);
11307           break;
11308 #endif
11309
11310         case EL_GAME_OF_LIFE:
11311         case EL_BIOMAZE:
11312           Life(x, y);
11313           break;
11314
11315         case EL_EXIT_CLOSED:
11316           CheckExit(x, y);
11317           break;
11318
11319         case EL_EM_EXIT_CLOSED:
11320           CheckExitEM(x, y);
11321           break;
11322
11323         case EL_STEEL_EXIT_CLOSED:
11324           CheckExitSteel(x, y);
11325           break;
11326
11327         case EL_EM_STEEL_EXIT_CLOSED:
11328           CheckExitSteelEM(x, y);
11329           break;
11330
11331         case EL_SP_EXIT_CLOSED:
11332           CheckExitSP(x, y);
11333           break;
11334
11335         case EL_EXPANDABLE_WALL_GROWING:
11336         case EL_EXPANDABLE_STEELWALL_GROWING:
11337           MauerWaechst(x, y);
11338           break;
11339
11340         case EL_EXPANDABLE_WALL:
11341         case EL_EXPANDABLE_WALL_HORIZONTAL:
11342         case EL_EXPANDABLE_WALL_VERTICAL:
11343         case EL_EXPANDABLE_WALL_ANY:
11344         case EL_BD_EXPANDABLE_WALL:
11345           MauerAbleger(x, y);
11346           break;
11347
11348         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
11349         case EL_EXPANDABLE_STEELWALL_VERTICAL:
11350         case EL_EXPANDABLE_STEELWALL_ANY:
11351           MauerAblegerStahl(x, y);
11352           break;
11353
11354         case EL_FLAMES:
11355           CheckForDragon(x, y);
11356           break;
11357
11358         case EL_EXPLOSION:
11359           break;
11360
11361         case EL_ELEMENT_SNAPPING:
11362         case EL_DIAGONAL_SHRINKING:
11363         case EL_DIAGONAL_GROWING:
11364         {
11365           graphic =
11366             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11367
11368           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11369           break;
11370         }
11371
11372         default:
11373           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11374             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11375           break;
11376       }
11377     }
11378
11379 #else   // ---------------------------------------------------------------------
11380
11381     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11382     {
11383       StartMoving(x, y);
11384
11385       element = Feld[x][y];
11386       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11387
11388       if (IS_ANIMATED(graphic) &&
11389           !IS_MOVING(x, y) &&
11390           !Stop[x][y])
11391         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11392
11393       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11394         DrawTwinkleOnField(x, y);
11395     }
11396     else if ((element == EL_ACID ||
11397               element == EL_EXIT_OPEN ||
11398               element == EL_EM_EXIT_OPEN ||
11399               element == EL_SP_EXIT_OPEN ||
11400               element == EL_STEEL_EXIT_OPEN ||
11401               element == EL_EM_STEEL_EXIT_OPEN ||
11402               element == EL_SP_TERMINAL ||
11403               element == EL_SP_TERMINAL_ACTIVE ||
11404               element == EL_EXTRA_TIME ||
11405               element == EL_SHIELD_NORMAL ||
11406               element == EL_SHIELD_DEADLY) &&
11407              IS_ANIMATED(graphic))
11408       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11409     else if (IS_MOVING(x, y))
11410       ContinueMoving(x, y);
11411     else if (IS_ACTIVE_BOMB(element))
11412       CheckDynamite(x, y);
11413     else if (element == EL_AMOEBA_GROWING)
11414       AmoebeWaechst(x, y);
11415     else if (element == EL_AMOEBA_SHRINKING)
11416       AmoebaDisappearing(x, y);
11417
11418 #if !USE_NEW_AMOEBA_CODE
11419     else if (IS_AMOEBALIVE(element))
11420       AmoebeAbleger(x, y);
11421 #endif
11422
11423     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11424       Life(x, y);
11425     else if (element == EL_EXIT_CLOSED)
11426       CheckExit(x, y);
11427     else if (element == EL_EM_EXIT_CLOSED)
11428       CheckExitEM(x, y);
11429     else if (element == EL_STEEL_EXIT_CLOSED)
11430       CheckExitSteel(x, y);
11431     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11432       CheckExitSteelEM(x, y);
11433     else if (element == EL_SP_EXIT_CLOSED)
11434       CheckExitSP(x, y);
11435     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11436              element == EL_EXPANDABLE_STEELWALL_GROWING)
11437       MauerWaechst(x, y);
11438     else if (element == EL_EXPANDABLE_WALL ||
11439              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11440              element == EL_EXPANDABLE_WALL_VERTICAL ||
11441              element == EL_EXPANDABLE_WALL_ANY ||
11442              element == EL_BD_EXPANDABLE_WALL)
11443       MauerAbleger(x, y);
11444     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11445              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11446              element == EL_EXPANDABLE_STEELWALL_ANY)
11447       MauerAblegerStahl(x, y);
11448     else if (element == EL_FLAMES)
11449       CheckForDragon(x, y);
11450     else if (element == EL_EXPLOSION)
11451       ; /* drawing of correct explosion animation is handled separately */
11452     else if (element == EL_ELEMENT_SNAPPING ||
11453              element == EL_DIAGONAL_SHRINKING ||
11454              element == EL_DIAGONAL_GROWING)
11455     {
11456       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11457
11458       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11459     }
11460     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11461       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11462
11463 #endif  // ---------------------------------------------------------------------
11464
11465     if (IS_BELT_ACTIVE(element))
11466       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11467
11468     if (game.magic_wall_active)
11469     {
11470       int jx = local_player->jx, jy = local_player->jy;
11471
11472       /* play the element sound at the position nearest to the player */
11473       if ((element == EL_MAGIC_WALL_FULL ||
11474            element == EL_MAGIC_WALL_ACTIVE ||
11475            element == EL_MAGIC_WALL_EMPTYING ||
11476            element == EL_BD_MAGIC_WALL_FULL ||
11477            element == EL_BD_MAGIC_WALL_ACTIVE ||
11478            element == EL_BD_MAGIC_WALL_EMPTYING ||
11479            element == EL_DC_MAGIC_WALL_FULL ||
11480            element == EL_DC_MAGIC_WALL_ACTIVE ||
11481            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11482           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11483       {
11484         magic_wall_x = x;
11485         magic_wall_y = y;
11486       }
11487     }
11488   }
11489
11490 #if 0
11491   debug_print_timestamp(0, "- time for MAIN loop:     -->");
11492 #endif
11493
11494 #if USE_NEW_AMOEBA_CODE
11495   /* new experimental amoeba growth stuff */
11496   if (!(FrameCounter % 8))
11497   {
11498     static unsigned long random = 1684108901;
11499
11500     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11501     {
11502       x = RND(lev_fieldx);
11503       y = RND(lev_fieldy);
11504       element = Feld[x][y];
11505
11506       if (!IS_PLAYER(x,y) &&
11507           (element == EL_EMPTY ||
11508            CAN_GROW_INTO(element) ||
11509            element == EL_QUICKSAND_EMPTY ||
11510            element == EL_QUICKSAND_FAST_EMPTY ||
11511            element == EL_ACID_SPLASH_LEFT ||
11512            element == EL_ACID_SPLASH_RIGHT))
11513       {
11514         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11515             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11516             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11517             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11518           Feld[x][y] = EL_AMOEBA_DROP;
11519       }
11520
11521       random = random * 129 + 1;
11522     }
11523   }
11524 #endif
11525
11526 #if 0
11527   if (game.explosions_delayed)
11528 #endif
11529   {
11530     game.explosions_delayed = FALSE;
11531
11532     SCAN_PLAYFIELD(x, y)
11533     {
11534       element = Feld[x][y];
11535
11536       if (ExplodeField[x][y])
11537         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11538       else if (element == EL_EXPLOSION)
11539         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11540
11541       ExplodeField[x][y] = EX_TYPE_NONE;
11542     }
11543
11544     game.explosions_delayed = TRUE;
11545   }
11546
11547   if (game.magic_wall_active)
11548   {
11549     if (!(game.magic_wall_time_left % 4))
11550     {
11551       int element = Feld[magic_wall_x][magic_wall_y];
11552
11553       if (element == EL_BD_MAGIC_WALL_FULL ||
11554           element == EL_BD_MAGIC_WALL_ACTIVE ||
11555           element == EL_BD_MAGIC_WALL_EMPTYING)
11556         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11557       else if (element == EL_DC_MAGIC_WALL_FULL ||
11558                element == EL_DC_MAGIC_WALL_ACTIVE ||
11559                element == EL_DC_MAGIC_WALL_EMPTYING)
11560         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11561       else
11562         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11563     }
11564
11565     if (game.magic_wall_time_left > 0)
11566     {
11567       game.magic_wall_time_left--;
11568       if (!game.magic_wall_time_left)
11569       {
11570         SCAN_PLAYFIELD(x, y)
11571         {
11572           element = Feld[x][y];
11573
11574           if (element == EL_MAGIC_WALL_ACTIVE ||
11575               element == EL_MAGIC_WALL_FULL)
11576           {
11577             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11578             DrawLevelField(x, y);
11579           }
11580           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11581                    element == EL_BD_MAGIC_WALL_FULL)
11582           {
11583             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11584             DrawLevelField(x, y);
11585           }
11586           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11587                    element == EL_DC_MAGIC_WALL_FULL)
11588           {
11589             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11590             DrawLevelField(x, y);
11591           }
11592         }
11593
11594         game.magic_wall_active = FALSE;
11595       }
11596     }
11597   }
11598
11599   if (game.light_time_left > 0)
11600   {
11601     game.light_time_left--;
11602
11603     if (game.light_time_left == 0)
11604       RedrawAllLightSwitchesAndInvisibleElements();
11605   }
11606
11607   if (game.timegate_time_left > 0)
11608   {
11609     game.timegate_time_left--;
11610
11611     if (game.timegate_time_left == 0)
11612       CloseAllOpenTimegates();
11613   }
11614
11615   if (game.lenses_time_left > 0)
11616   {
11617     game.lenses_time_left--;
11618
11619     if (game.lenses_time_left == 0)
11620       RedrawAllInvisibleElementsForLenses();
11621   }
11622
11623   if (game.magnify_time_left > 0)
11624   {
11625     game.magnify_time_left--;
11626
11627     if (game.magnify_time_left == 0)
11628       RedrawAllInvisibleElementsForMagnifier();
11629   }
11630
11631   for (i = 0; i < MAX_PLAYERS; i++)
11632   {
11633     struct PlayerInfo *player = &stored_player[i];
11634
11635     if (SHIELD_ON(player))
11636     {
11637       if (player->shield_deadly_time_left)
11638         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11639       else if (player->shield_normal_time_left)
11640         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11641     }
11642   }
11643
11644   CheckLevelTime();
11645
11646   DrawAllPlayers();
11647   PlayAllPlayersSound();
11648
11649   if (options.debug)                    /* calculate frames per second */
11650   {
11651     static unsigned long fps_counter = 0;
11652     static int fps_frames = 0;
11653     unsigned long fps_delay_ms = Counter() - fps_counter;
11654
11655     fps_frames++;
11656
11657     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11658     {
11659       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11660
11661       fps_frames = 0;
11662       fps_counter = Counter();
11663     }
11664
11665     redraw_mask |= REDRAW_FPS;
11666   }
11667
11668   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11669
11670   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11671   {
11672     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11673
11674     local_player->show_envelope = 0;
11675   }
11676
11677 #if 0
11678   debug_print_timestamp(0, "stop main loop profiling ");
11679   printf("----------------------------------------------------------\n");
11680 #endif
11681
11682   /* use random number generator in every frame to make it less predictable */
11683   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11684     RND(1);
11685 }
11686
11687 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11688 {
11689   int min_x = x, min_y = y, max_x = x, max_y = y;
11690   int i;
11691
11692   for (i = 0; i < MAX_PLAYERS; i++)
11693   {
11694     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11695
11696     if (!stored_player[i].active || &stored_player[i] == player)
11697       continue;
11698
11699     min_x = MIN(min_x, jx);
11700     min_y = MIN(min_y, jy);
11701     max_x = MAX(max_x, jx);
11702     max_y = MAX(max_y, jy);
11703   }
11704
11705   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11706 }
11707
11708 static boolean AllPlayersInVisibleScreen()
11709 {
11710   int i;
11711
11712   for (i = 0; i < MAX_PLAYERS; i++)
11713   {
11714     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11715
11716     if (!stored_player[i].active)
11717       continue;
11718
11719     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11720       return FALSE;
11721   }
11722
11723   return TRUE;
11724 }
11725
11726 void ScrollLevel(int dx, int dy)
11727 {
11728 #if 1
11729   static Bitmap *bitmap_db_field2 = NULL;
11730   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11731   int x, y;
11732 #else
11733   int i, x, y;
11734 #endif
11735
11736 #if 0
11737   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11738   /* only horizontal XOR vertical scroll direction allowed */
11739   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11740     return;
11741 #endif
11742
11743 #if 1
11744   if (bitmap_db_field2 == NULL)
11745     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11746
11747   /* needed when blitting directly to same bitmap -- should not be needed with
11748      recent SDL libraries, but apparently does not work in 1.2.11 directly */
11749   BlitBitmap(drawto_field, bitmap_db_field2,
11750              FX + TILEX * (dx == -1) - softscroll_offset,
11751              FY + TILEY * (dy == -1) - softscroll_offset,
11752              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11753              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11754              FX + TILEX * (dx == 1) - softscroll_offset,
11755              FY + TILEY * (dy == 1) - softscroll_offset);
11756   BlitBitmap(bitmap_db_field2, drawto_field,
11757              FX + TILEX * (dx == 1) - softscroll_offset,
11758              FY + TILEY * (dy == 1) - softscroll_offset,
11759              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11760              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11761              FX + TILEX * (dx == 1) - softscroll_offset,
11762              FY + TILEY * (dy == 1) - softscroll_offset);
11763
11764 #else
11765
11766 #if 1
11767   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11768   int xsize = (BX2 - BX1 + 1);
11769   int ysize = (BY2 - BY1 + 1);
11770   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11771   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11772   int step  = (start < end ? +1 : -1);
11773
11774   for (i = start; i != end; i += step)
11775   {
11776     BlitBitmap(drawto_field, drawto_field,
11777                FX + TILEX * (dx != 0 ? i + step : 0),
11778                FY + TILEY * (dy != 0 ? i + step : 0),
11779                TILEX * (dx != 0 ? 1 : xsize),
11780                TILEY * (dy != 0 ? 1 : ysize),
11781                FX + TILEX * (dx != 0 ? i : 0),
11782                FY + TILEY * (dy != 0 ? i : 0));
11783   }
11784
11785 #else
11786
11787   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11788
11789   BlitBitmap(drawto_field, drawto_field,
11790              FX + TILEX * (dx == -1) - softscroll_offset,
11791              FY + TILEY * (dy == -1) - softscroll_offset,
11792              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11793              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11794              FX + TILEX * (dx == 1) - softscroll_offset,
11795              FY + TILEY * (dy == 1) - softscroll_offset);
11796 #endif
11797 #endif
11798
11799   if (dx != 0)
11800   {
11801     x = (dx == 1 ? BX1 : BX2);
11802     for (y = BY1; y <= BY2; y++)
11803       DrawScreenField(x, y);
11804   }
11805
11806   if (dy != 0)
11807   {
11808     y = (dy == 1 ? BY1 : BY2);
11809     for (x = BX1; x <= BX2; x++)
11810       DrawScreenField(x, y);
11811   }
11812
11813   redraw_mask |= REDRAW_FIELD;
11814 }
11815
11816 static boolean canFallDown(struct PlayerInfo *player)
11817 {
11818   int jx = player->jx, jy = player->jy;
11819
11820   return (IN_LEV_FIELD(jx, jy + 1) &&
11821           (IS_FREE(jx, jy + 1) ||
11822            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11823           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11824           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11825 }
11826
11827 static boolean canPassField(int x, int y, int move_dir)
11828 {
11829   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11830   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11831   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11832   int nextx = x + dx;
11833   int nexty = y + dy;
11834   int element = Feld[x][y];
11835
11836   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11837           !CAN_MOVE(element) &&
11838           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11839           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11840           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11841 }
11842
11843 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11844 {
11845   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11846   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11847   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11848   int newx = x + dx;
11849   int newy = y + dy;
11850
11851   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11852           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11853           (IS_DIGGABLE(Feld[newx][newy]) ||
11854            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11855            canPassField(newx, newy, move_dir)));
11856 }
11857
11858 static void CheckGravityMovement(struct PlayerInfo *player)
11859 {
11860 #if USE_PLAYER_GRAVITY
11861   if (player->gravity && !player->programmed_action)
11862 #else
11863   if (game.gravity && !player->programmed_action)
11864 #endif
11865   {
11866     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11867     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11868     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11869     int jx = player->jx, jy = player->jy;
11870     boolean player_is_moving_to_valid_field =
11871       (!player_is_snapping &&
11872        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11873         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11874     boolean player_can_fall_down = canFallDown(player);
11875
11876     if (player_can_fall_down &&
11877         !player_is_moving_to_valid_field)
11878       player->programmed_action = MV_DOWN;
11879   }
11880 }
11881
11882 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11883 {
11884   return CheckGravityMovement(player);
11885
11886 #if USE_PLAYER_GRAVITY
11887   if (player->gravity && !player->programmed_action)
11888 #else
11889   if (game.gravity && !player->programmed_action)
11890 #endif
11891   {
11892     int jx = player->jx, jy = player->jy;
11893     boolean field_under_player_is_free =
11894       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11895     boolean player_is_standing_on_valid_field =
11896       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11897        (IS_WALKABLE(Feld[jx][jy]) &&
11898         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11899
11900     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11901       player->programmed_action = MV_DOWN;
11902   }
11903 }
11904
11905 /*
11906   MovePlayerOneStep()
11907   -----------------------------------------------------------------------------
11908   dx, dy:               direction (non-diagonal) to try to move the player to
11909   real_dx, real_dy:     direction as read from input device (can be diagonal)
11910 */
11911
11912 boolean MovePlayerOneStep(struct PlayerInfo *player,
11913                           int dx, int dy, int real_dx, int real_dy)
11914 {
11915   int jx = player->jx, jy = player->jy;
11916   int new_jx = jx + dx, new_jy = jy + dy;
11917 #if !USE_FIXED_DONT_RUN_INTO
11918   int element;
11919 #endif
11920   int can_move;
11921   boolean player_can_move = !player->cannot_move;
11922
11923   if (!player->active || (!dx && !dy))
11924     return MP_NO_ACTION;
11925
11926   player->MovDir = (dx < 0 ? MV_LEFT :
11927                     dx > 0 ? MV_RIGHT :
11928                     dy < 0 ? MV_UP :
11929                     dy > 0 ? MV_DOWN :  MV_NONE);
11930
11931   if (!IN_LEV_FIELD(new_jx, new_jy))
11932     return MP_NO_ACTION;
11933
11934   if (!player_can_move)
11935   {
11936     if (player->MovPos == 0)
11937     {
11938       player->is_moving = FALSE;
11939       player->is_digging = FALSE;
11940       player->is_collecting = FALSE;
11941       player->is_snapping = FALSE;
11942       player->is_pushing = FALSE;
11943     }
11944   }
11945
11946 #if 1
11947   if (!options.network && game.centered_player_nr == -1 &&
11948       !AllPlayersInSight(player, new_jx, new_jy))
11949     return MP_NO_ACTION;
11950 #else
11951   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
11952     return MP_NO_ACTION;
11953 #endif
11954
11955 #if !USE_FIXED_DONT_RUN_INTO
11956   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
11957
11958   /* (moved to DigField()) */
11959   if (player_can_move && DONT_RUN_INTO(element))
11960   {
11961     if (element == EL_ACID && dx == 0 && dy == 1)
11962     {
11963       SplashAcid(new_jx, new_jy);
11964       Feld[jx][jy] = EL_PLAYER_1;
11965       InitMovingField(jx, jy, MV_DOWN);
11966       Store[jx][jy] = EL_ACID;
11967       ContinueMoving(jx, jy);
11968       BuryPlayer(player);
11969     }
11970     else
11971       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11972
11973     return MP_MOVING;
11974   }
11975 #endif
11976
11977   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11978   if (can_move != MP_MOVING)
11979     return can_move;
11980
11981   /* check if DigField() has caused relocation of the player */
11982   if (player->jx != jx || player->jy != jy)
11983     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11984
11985   StorePlayer[jx][jy] = 0;
11986   player->last_jx = jx;
11987   player->last_jy = jy;
11988   player->jx = new_jx;
11989   player->jy = new_jy;
11990   StorePlayer[new_jx][new_jy] = player->element_nr;
11991
11992   if (player->move_delay_value_next != -1)
11993   {
11994     player->move_delay_value = player->move_delay_value_next;
11995     player->move_delay_value_next = -1;
11996   }
11997
11998   player->MovPos =
11999     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12000
12001   player->step_counter++;
12002
12003   PlayerVisit[jx][jy] = FrameCounter;
12004
12005 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12006   player->is_moving = TRUE;
12007 #endif
12008
12009 #if 1
12010   /* should better be called in MovePlayer(), but this breaks some tapes */
12011   ScrollPlayer(player, SCROLL_INIT);
12012 #endif
12013
12014   return MP_MOVING;
12015 }
12016
12017 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12018 {
12019   int jx = player->jx, jy = player->jy;
12020   int old_jx = jx, old_jy = jy;
12021   int moved = MP_NO_ACTION;
12022
12023   if (!player->active)
12024     return FALSE;
12025
12026   if (!dx && !dy)
12027   {
12028     if (player->MovPos == 0)
12029     {
12030       player->is_moving = FALSE;
12031       player->is_digging = FALSE;
12032       player->is_collecting = FALSE;
12033       player->is_snapping = FALSE;
12034       player->is_pushing = FALSE;
12035     }
12036
12037     return FALSE;
12038   }
12039
12040   if (player->move_delay > 0)
12041     return FALSE;
12042
12043   player->move_delay = -1;              /* set to "uninitialized" value */
12044
12045   /* store if player is automatically moved to next field */
12046   player->is_auto_moving = (player->programmed_action != MV_NONE);
12047
12048   /* remove the last programmed player action */
12049   player->programmed_action = 0;
12050
12051   if (player->MovPos)
12052   {
12053     /* should only happen if pre-1.2 tape recordings are played */
12054     /* this is only for backward compatibility */
12055
12056     int original_move_delay_value = player->move_delay_value;
12057
12058 #if DEBUG
12059     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12060            tape.counter);
12061 #endif
12062
12063     /* scroll remaining steps with finest movement resolution */
12064     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12065
12066     while (player->MovPos)
12067     {
12068       ScrollPlayer(player, SCROLL_GO_ON);
12069       ScrollScreen(NULL, SCROLL_GO_ON);
12070
12071       AdvanceFrameAndPlayerCounters(player->index_nr);
12072
12073       DrawAllPlayers();
12074       BackToFront();
12075     }
12076
12077     player->move_delay_value = original_move_delay_value;
12078   }
12079
12080   player->is_active = FALSE;
12081
12082   if (player->last_move_dir & MV_HORIZONTAL)
12083   {
12084     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12085       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12086   }
12087   else
12088   {
12089     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12090       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12091   }
12092
12093 #if USE_FIXED_BORDER_RUNNING_GFX
12094   if (!moved && !player->is_active)
12095   {
12096     player->is_moving = FALSE;
12097     player->is_digging = FALSE;
12098     player->is_collecting = FALSE;
12099     player->is_snapping = FALSE;
12100     player->is_pushing = FALSE;
12101   }
12102 #endif
12103
12104   jx = player->jx;
12105   jy = player->jy;
12106
12107 #if 1
12108   if (moved & MP_MOVING && !ScreenMovPos &&
12109       (player->index_nr == game.centered_player_nr ||
12110        game.centered_player_nr == -1))
12111 #else
12112   if (moved & MP_MOVING && !ScreenMovPos &&
12113       (player == local_player || !options.network))
12114 #endif
12115   {
12116     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12117     int offset = (setup.scroll_delay ? 3 : 0);
12118
12119     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12120     {
12121       /* actual player has left the screen -- scroll in that direction */
12122       if (jx != old_jx)         /* player has moved horizontally */
12123         scroll_x += (jx - old_jx);
12124       else                      /* player has moved vertically */
12125         scroll_y += (jy - old_jy);
12126     }
12127     else
12128     {
12129       if (jx != old_jx)         /* player has moved horizontally */
12130       {
12131         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12132             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12133           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12134
12135         /* don't scroll over playfield boundaries */
12136         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12137           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12138
12139         /* don't scroll more than one field at a time */
12140         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12141
12142         /* don't scroll against the player's moving direction */
12143         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12144             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12145           scroll_x = old_scroll_x;
12146       }
12147       else                      /* player has moved vertically */
12148       {
12149         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12150             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12151           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12152
12153         /* don't scroll over playfield boundaries */
12154         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12155           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12156
12157         /* don't scroll more than one field at a time */
12158         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12159
12160         /* don't scroll against the player's moving direction */
12161         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12162             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12163           scroll_y = old_scroll_y;
12164       }
12165     }
12166
12167     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12168     {
12169 #if 1
12170       if (!options.network && game.centered_player_nr == -1 &&
12171           !AllPlayersInVisibleScreen())
12172       {
12173         scroll_x = old_scroll_x;
12174         scroll_y = old_scroll_y;
12175       }
12176       else
12177 #else
12178       if (!options.network && !AllPlayersInVisibleScreen())
12179       {
12180         scroll_x = old_scroll_x;
12181         scroll_y = old_scroll_y;
12182       }
12183       else
12184 #endif
12185       {
12186         ScrollScreen(player, SCROLL_INIT);
12187         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12188       }
12189     }
12190   }
12191
12192   player->StepFrame = 0;
12193
12194   if (moved & MP_MOVING)
12195   {
12196     if (old_jx != jx && old_jy == jy)
12197       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12198     else if (old_jx == jx && old_jy != jy)
12199       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12200
12201     DrawLevelField(jx, jy);     /* for "crumbled sand" */
12202
12203     player->last_move_dir = player->MovDir;
12204     player->is_moving = TRUE;
12205     player->is_snapping = FALSE;
12206     player->is_switching = FALSE;
12207     player->is_dropping = FALSE;
12208     player->is_dropping_pressed = FALSE;
12209     player->drop_pressed_delay = 0;
12210
12211 #if 0
12212     /* should better be called here than above, but this breaks some tapes */
12213     ScrollPlayer(player, SCROLL_INIT);
12214 #endif
12215   }
12216   else
12217   {
12218     CheckGravityMovementWhenNotMoving(player);
12219
12220     player->is_moving = FALSE;
12221
12222     /* at this point, the player is allowed to move, but cannot move right now
12223        (e.g. because of something blocking the way) -- ensure that the player
12224        is also allowed to move in the next frame (in old versions before 3.1.1,
12225        the player was forced to wait again for eight frames before next try) */
12226
12227     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12228       player->move_delay = 0;   /* allow direct movement in the next frame */
12229   }
12230
12231   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12232     player->move_delay = player->move_delay_value;
12233
12234   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12235   {
12236     TestIfPlayerTouchesBadThing(jx, jy);
12237     TestIfPlayerTouchesCustomElement(jx, jy);
12238   }
12239
12240   if (!player->active)
12241     RemovePlayer(player);
12242
12243   return moved;
12244 }
12245
12246 void ScrollPlayer(struct PlayerInfo *player, int mode)
12247 {
12248   int jx = player->jx, jy = player->jy;
12249   int last_jx = player->last_jx, last_jy = player->last_jy;
12250   int move_stepsize = TILEX / player->move_delay_value;
12251
12252 #if USE_NEW_PLAYER_SPEED
12253   if (!player->active)
12254     return;
12255
12256   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12257     return;
12258 #else
12259   if (!player->active || player->MovPos == 0)
12260     return;
12261 #endif
12262
12263   if (mode == SCROLL_INIT)
12264   {
12265     player->actual_frame_counter = FrameCounter;
12266     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12267
12268     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12269         Feld[last_jx][last_jy] == EL_EMPTY)
12270     {
12271       int last_field_block_delay = 0;   /* start with no blocking at all */
12272       int block_delay_adjustment = player->block_delay_adjustment;
12273
12274       /* if player blocks last field, add delay for exactly one move */
12275       if (player->block_last_field)
12276       {
12277         last_field_block_delay += player->move_delay_value;
12278
12279         /* when blocking enabled, prevent moving up despite gravity */
12280 #if USE_PLAYER_GRAVITY
12281         if (player->gravity && player->MovDir == MV_UP)
12282           block_delay_adjustment = -1;
12283 #else
12284         if (game.gravity && player->MovDir == MV_UP)
12285           block_delay_adjustment = -1;
12286 #endif
12287       }
12288
12289       /* add block delay adjustment (also possible when not blocking) */
12290       last_field_block_delay += block_delay_adjustment;
12291
12292       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12293       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12294     }
12295
12296 #if USE_NEW_PLAYER_SPEED
12297     if (player->MovPos != 0)    /* player has not yet reached destination */
12298       return;
12299 #else
12300     return;
12301 #endif
12302   }
12303   else if (!FrameReached(&player->actual_frame_counter, 1))
12304     return;
12305
12306 #if USE_NEW_PLAYER_SPEED
12307   if (player->MovPos != 0)
12308   {
12309     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12310     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12311
12312     /* before DrawPlayer() to draw correct player graphic for this case */
12313     if (player->MovPos == 0)
12314       CheckGravityMovement(player);
12315   }
12316 #else
12317   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12318   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12319
12320   /* before DrawPlayer() to draw correct player graphic for this case */
12321   if (player->MovPos == 0)
12322     CheckGravityMovement(player);
12323 #endif
12324
12325   if (player->MovPos == 0)      /* player reached destination field */
12326   {
12327     if (player->move_delay_reset_counter > 0)
12328     {
12329       player->move_delay_reset_counter--;
12330
12331       if (player->move_delay_reset_counter == 0)
12332       {
12333         /* continue with normal speed after quickly moving through gate */
12334         HALVE_PLAYER_SPEED(player);
12335
12336         /* be able to make the next move without delay */
12337         player->move_delay = 0;
12338       }
12339     }
12340
12341     player->last_jx = jx;
12342     player->last_jy = jy;
12343
12344     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12345         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12346         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12347         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12348         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12349         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12350     {
12351       DrawPlayer(player);       /* needed here only to cleanup last field */
12352       RemovePlayer(player);
12353
12354       if (local_player->friends_still_needed == 0 ||
12355           IS_SP_ELEMENT(Feld[jx][jy]))
12356         PlayerWins(player);
12357     }
12358
12359     /* this breaks one level: "machine", level 000 */
12360     {
12361       int move_direction = player->MovDir;
12362       int enter_side = MV_DIR_OPPOSITE(move_direction);
12363       int leave_side = move_direction;
12364       int old_jx = last_jx;
12365       int old_jy = last_jy;
12366       int old_element = Feld[old_jx][old_jy];
12367       int new_element = Feld[jx][jy];
12368
12369       if (IS_CUSTOM_ELEMENT(old_element))
12370         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12371                                    CE_LEFT_BY_PLAYER,
12372                                    player->index_bit, leave_side);
12373
12374       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12375                                           CE_PLAYER_LEAVES_X,
12376                                           player->index_bit, leave_side);
12377
12378       if (IS_CUSTOM_ELEMENT(new_element))
12379         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12380                                    player->index_bit, enter_side);
12381
12382       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12383                                           CE_PLAYER_ENTERS_X,
12384                                           player->index_bit, enter_side);
12385
12386       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12387                                         CE_MOVE_OF_X, move_direction);
12388     }
12389
12390     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12391     {
12392       TestIfPlayerTouchesBadThing(jx, jy);
12393       TestIfPlayerTouchesCustomElement(jx, jy);
12394
12395       /* needed because pushed element has not yet reached its destination,
12396          so it would trigger a change event at its previous field location */
12397       if (!player->is_pushing)
12398         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12399
12400       if (!player->active)
12401         RemovePlayer(player);
12402     }
12403
12404     if (!local_player->LevelSolved && level.use_step_counter)
12405     {
12406       int i;
12407
12408       TimePlayed++;
12409
12410       if (TimeLeft > 0)
12411       {
12412         TimeLeft--;
12413
12414         if (TimeLeft <= 10 && setup.time_limit)
12415           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12416
12417 #if 1
12418         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
12419
12420         DisplayGameControlValues();
12421 #else
12422         DrawGameValue_Time(TimeLeft);
12423 #endif
12424
12425         if (!TimeLeft && setup.time_limit)
12426           for (i = 0; i < MAX_PLAYERS; i++)
12427             KillPlayer(&stored_player[i]);
12428       }
12429 #if 1
12430       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12431       {
12432         game_control_value[GAME_CONTROL_TIME] = TimePlayed;
12433
12434         DisplayGameControlValues();
12435       }
12436 #else
12437       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12438         DrawGameValue_Time(TimePlayed);
12439 #endif
12440     }
12441
12442     if (tape.single_step && tape.recording && !tape.pausing &&
12443         !player->programmed_action)
12444       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12445   }
12446 }
12447
12448 void ScrollScreen(struct PlayerInfo *player, int mode)
12449 {
12450   static unsigned long screen_frame_counter = 0;
12451
12452   if (mode == SCROLL_INIT)
12453   {
12454     /* set scrolling step size according to actual player's moving speed */
12455     ScrollStepSize = TILEX / player->move_delay_value;
12456
12457     screen_frame_counter = FrameCounter;
12458     ScreenMovDir = player->MovDir;
12459     ScreenMovPos = player->MovPos;
12460     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12461     return;
12462   }
12463   else if (!FrameReached(&screen_frame_counter, 1))
12464     return;
12465
12466   if (ScreenMovPos)
12467   {
12468     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12469     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12470     redraw_mask |= REDRAW_FIELD;
12471   }
12472   else
12473     ScreenMovDir = MV_NONE;
12474 }
12475
12476 void TestIfPlayerTouchesCustomElement(int x, int y)
12477 {
12478   static int xy[4][2] =
12479   {
12480     { 0, -1 },
12481     { -1, 0 },
12482     { +1, 0 },
12483     { 0, +1 }
12484   };
12485   static int trigger_sides[4][2] =
12486   {
12487     /* center side       border side */
12488     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12489     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12490     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12491     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12492   };
12493   static int touch_dir[4] =
12494   {
12495     MV_LEFT | MV_RIGHT,
12496     MV_UP   | MV_DOWN,
12497     MV_UP   | MV_DOWN,
12498     MV_LEFT | MV_RIGHT
12499   };
12500   int center_element = Feld[x][y];      /* should always be non-moving! */
12501   int i;
12502
12503   for (i = 0; i < NUM_DIRECTIONS; i++)
12504   {
12505     int xx = x + xy[i][0];
12506     int yy = y + xy[i][1];
12507     int center_side = trigger_sides[i][0];
12508     int border_side = trigger_sides[i][1];
12509     int border_element;
12510
12511     if (!IN_LEV_FIELD(xx, yy))
12512       continue;
12513
12514     if (IS_PLAYER(x, y))
12515     {
12516       struct PlayerInfo *player = PLAYERINFO(x, y);
12517
12518       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12519         border_element = Feld[xx][yy];          /* may be moving! */
12520       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12521         border_element = Feld[xx][yy];
12522       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12523         border_element = MovingOrBlocked2Element(xx, yy);
12524       else
12525         continue;               /* center and border element do not touch */
12526
12527       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12528                                  player->index_bit, border_side);
12529       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12530                                           CE_PLAYER_TOUCHES_X,
12531                                           player->index_bit, border_side);
12532     }
12533     else if (IS_PLAYER(xx, yy))
12534     {
12535       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12536
12537       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12538       {
12539         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12540           continue;             /* center and border element do not touch */
12541       }
12542
12543       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12544                                  player->index_bit, center_side);
12545       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12546                                           CE_PLAYER_TOUCHES_X,
12547                                           player->index_bit, center_side);
12548       break;
12549     }
12550   }
12551 }
12552
12553 #if USE_ELEMENT_TOUCHING_BUGFIX
12554
12555 void TestIfElementTouchesCustomElement(int x, int y)
12556 {
12557   static int xy[4][2] =
12558   {
12559     { 0, -1 },
12560     { -1, 0 },
12561     { +1, 0 },
12562     { 0, +1 }
12563   };
12564   static int trigger_sides[4][2] =
12565   {
12566     /* center side      border side */
12567     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12568     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12569     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12570     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12571   };
12572   static int touch_dir[4] =
12573   {
12574     MV_LEFT | MV_RIGHT,
12575     MV_UP   | MV_DOWN,
12576     MV_UP   | MV_DOWN,
12577     MV_LEFT | MV_RIGHT
12578   };
12579   boolean change_center_element = FALSE;
12580   int center_element = Feld[x][y];      /* should always be non-moving! */
12581   int border_element_old[NUM_DIRECTIONS];
12582   int i;
12583
12584   for (i = 0; i < NUM_DIRECTIONS; i++)
12585   {
12586     int xx = x + xy[i][0];
12587     int yy = y + xy[i][1];
12588     int border_element;
12589
12590     border_element_old[i] = -1;
12591
12592     if (!IN_LEV_FIELD(xx, yy))
12593       continue;
12594
12595     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12596       border_element = Feld[xx][yy];    /* may be moving! */
12597     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12598       border_element = Feld[xx][yy];
12599     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12600       border_element = MovingOrBlocked2Element(xx, yy);
12601     else
12602       continue;                 /* center and border element do not touch */
12603
12604     border_element_old[i] = border_element;
12605   }
12606
12607   for (i = 0; i < NUM_DIRECTIONS; i++)
12608   {
12609     int xx = x + xy[i][0];
12610     int yy = y + xy[i][1];
12611     int center_side = trigger_sides[i][0];
12612     int border_element = border_element_old[i];
12613
12614     if (border_element == -1)
12615       continue;
12616
12617     /* check for change of border element */
12618     CheckElementChangeBySide(xx, yy, border_element, center_element,
12619                              CE_TOUCHING_X, center_side);
12620   }
12621
12622   for (i = 0; i < NUM_DIRECTIONS; i++)
12623   {
12624     int border_side = trigger_sides[i][1];
12625     int border_element = border_element_old[i];
12626
12627     if (border_element == -1)
12628       continue;
12629
12630     /* check for change of center element (but change it only once) */
12631     if (!change_center_element)
12632       change_center_element =
12633         CheckElementChangeBySide(x, y, center_element, border_element,
12634                                  CE_TOUCHING_X, border_side);
12635   }
12636 }
12637
12638 #else
12639
12640 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12641 {
12642   static int xy[4][2] =
12643   {
12644     { 0, -1 },
12645     { -1, 0 },
12646     { +1, 0 },
12647     { 0, +1 }
12648   };
12649   static int trigger_sides[4][2] =
12650   {
12651     /* center side      border side */
12652     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12653     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12654     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12655     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12656   };
12657   static int touch_dir[4] =
12658   {
12659     MV_LEFT | MV_RIGHT,
12660     MV_UP   | MV_DOWN,
12661     MV_UP   | MV_DOWN,
12662     MV_LEFT | MV_RIGHT
12663   };
12664   boolean change_center_element = FALSE;
12665   int center_element = Feld[x][y];      /* should always be non-moving! */
12666   int i;
12667
12668   for (i = 0; i < NUM_DIRECTIONS; i++)
12669   {
12670     int xx = x + xy[i][0];
12671     int yy = y + xy[i][1];
12672     int center_side = trigger_sides[i][0];
12673     int border_side = trigger_sides[i][1];
12674     int border_element;
12675
12676     if (!IN_LEV_FIELD(xx, yy))
12677       continue;
12678
12679     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12680       border_element = Feld[xx][yy];    /* may be moving! */
12681     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12682       border_element = Feld[xx][yy];
12683     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12684       border_element = MovingOrBlocked2Element(xx, yy);
12685     else
12686       continue;                 /* center and border element do not touch */
12687
12688     /* check for change of center element (but change it only once) */
12689     if (!change_center_element)
12690       change_center_element =
12691         CheckElementChangeBySide(x, y, center_element, border_element,
12692                                  CE_TOUCHING_X, border_side);
12693
12694     /* check for change of border element */
12695     CheckElementChangeBySide(xx, yy, border_element, center_element,
12696                              CE_TOUCHING_X, center_side);
12697   }
12698 }
12699
12700 #endif
12701
12702 void TestIfElementHitsCustomElement(int x, int y, int direction)
12703 {
12704   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12705   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12706   int hitx = x + dx, hity = y + dy;
12707   int hitting_element = Feld[x][y];
12708   int touched_element;
12709
12710   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12711     return;
12712
12713   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12714                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12715
12716   if (IN_LEV_FIELD(hitx, hity))
12717   {
12718     int opposite_direction = MV_DIR_OPPOSITE(direction);
12719     int hitting_side = direction;
12720     int touched_side = opposite_direction;
12721     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12722                           MovDir[hitx][hity] != direction ||
12723                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12724
12725     object_hit = TRUE;
12726
12727     if (object_hit)
12728     {
12729       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12730                                CE_HITTING_X, touched_side);
12731
12732       CheckElementChangeBySide(hitx, hity, touched_element,
12733                                hitting_element, CE_HIT_BY_X, hitting_side);
12734
12735       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12736                                CE_HIT_BY_SOMETHING, opposite_direction);
12737     }
12738   }
12739
12740   /* "hitting something" is also true when hitting the playfield border */
12741   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12742                            CE_HITTING_SOMETHING, direction);
12743 }
12744
12745 #if 0
12746 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12747 {
12748   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12749   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12750   int hitx = x + dx, hity = y + dy;
12751   int hitting_element = Feld[x][y];
12752   int touched_element;
12753 #if 0
12754   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12755                         !IS_FREE(hitx, hity) &&
12756                         (!IS_MOVING(hitx, hity) ||
12757                          MovDir[hitx][hity] != direction ||
12758                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
12759 #endif
12760
12761   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12762     return;
12763
12764 #if 0
12765   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12766     return;
12767 #endif
12768
12769   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12770                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12771
12772   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12773                            EP_CAN_SMASH_EVERYTHING, direction);
12774
12775   if (IN_LEV_FIELD(hitx, hity))
12776   {
12777     int opposite_direction = MV_DIR_OPPOSITE(direction);
12778     int hitting_side = direction;
12779     int touched_side = opposite_direction;
12780 #if 0
12781     int touched_element = MovingOrBlocked2Element(hitx, hity);
12782 #endif
12783 #if 1
12784     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12785                           MovDir[hitx][hity] != direction ||
12786                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12787
12788     object_hit = TRUE;
12789 #endif
12790
12791     if (object_hit)
12792     {
12793       int i;
12794
12795       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12796                                CE_SMASHED_BY_SOMETHING, opposite_direction);
12797
12798       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12799                                CE_OTHER_IS_SMASHING, touched_side);
12800
12801       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12802                                CE_OTHER_GETS_SMASHED, hitting_side);
12803     }
12804   }
12805 }
12806 #endif
12807
12808 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12809 {
12810   int i, kill_x = -1, kill_y = -1;
12811
12812   int bad_element = -1;
12813   static int test_xy[4][2] =
12814   {
12815     { 0, -1 },
12816     { -1, 0 },
12817     { +1, 0 },
12818     { 0, +1 }
12819   };
12820   static int test_dir[4] =
12821   {
12822     MV_UP,
12823     MV_LEFT,
12824     MV_RIGHT,
12825     MV_DOWN
12826   };
12827
12828   for (i = 0; i < NUM_DIRECTIONS; i++)
12829   {
12830     int test_x, test_y, test_move_dir, test_element;
12831
12832     test_x = good_x + test_xy[i][0];
12833     test_y = good_y + test_xy[i][1];
12834
12835     if (!IN_LEV_FIELD(test_x, test_y))
12836       continue;
12837
12838     test_move_dir =
12839       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12840
12841     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12842
12843     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12844        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12845     */
12846     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12847         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12848     {
12849       kill_x = test_x;
12850       kill_y = test_y;
12851       bad_element = test_element;
12852
12853       break;
12854     }
12855   }
12856
12857   if (kill_x != -1 || kill_y != -1)
12858   {
12859     if (IS_PLAYER(good_x, good_y))
12860     {
12861       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12862
12863       if (player->shield_deadly_time_left > 0 &&
12864           !IS_INDESTRUCTIBLE(bad_element))
12865         Bang(kill_x, kill_y);
12866       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12867         KillPlayer(player);
12868     }
12869     else
12870       Bang(good_x, good_y);
12871   }
12872 }
12873
12874 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12875 {
12876   int i, kill_x = -1, kill_y = -1;
12877   int bad_element = Feld[bad_x][bad_y];
12878   static int test_xy[4][2] =
12879   {
12880     { 0, -1 },
12881     { -1, 0 },
12882     { +1, 0 },
12883     { 0, +1 }
12884   };
12885   static int touch_dir[4] =
12886   {
12887     MV_LEFT | MV_RIGHT,
12888     MV_UP   | MV_DOWN,
12889     MV_UP   | MV_DOWN,
12890     MV_LEFT | MV_RIGHT
12891   };
12892   static int test_dir[4] =
12893   {
12894     MV_UP,
12895     MV_LEFT,
12896     MV_RIGHT,
12897     MV_DOWN
12898   };
12899
12900   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12901     return;
12902
12903   for (i = 0; i < NUM_DIRECTIONS; i++)
12904   {
12905     int test_x, test_y, test_move_dir, test_element;
12906
12907     test_x = bad_x + test_xy[i][0];
12908     test_y = bad_y + test_xy[i][1];
12909     if (!IN_LEV_FIELD(test_x, test_y))
12910       continue;
12911
12912     test_move_dir =
12913       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12914
12915     test_element = Feld[test_x][test_y];
12916
12917     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12918        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12919     */
12920     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12921         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12922     {
12923       /* good thing is player or penguin that does not move away */
12924       if (IS_PLAYER(test_x, test_y))
12925       {
12926         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12927
12928         if (bad_element == EL_ROBOT && player->is_moving)
12929           continue;     /* robot does not kill player if he is moving */
12930
12931         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12932         {
12933           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12934             continue;           /* center and border element do not touch */
12935         }
12936
12937         kill_x = test_x;
12938         kill_y = test_y;
12939         break;
12940       }
12941       else if (test_element == EL_PENGUIN)
12942       {
12943         kill_x = test_x;
12944         kill_y = test_y;
12945         break;
12946       }
12947     }
12948   }
12949
12950   if (kill_x != -1 || kill_y != -1)
12951   {
12952     if (IS_PLAYER(kill_x, kill_y))
12953     {
12954       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12955
12956       if (player->shield_deadly_time_left > 0 &&
12957           !IS_INDESTRUCTIBLE(bad_element))
12958         Bang(bad_x, bad_y);
12959       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12960         KillPlayer(player);
12961     }
12962     else
12963       Bang(kill_x, kill_y);
12964   }
12965 }
12966
12967 void TestIfPlayerTouchesBadThing(int x, int y)
12968 {
12969   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12970 }
12971
12972 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12973 {
12974   TestIfGoodThingHitsBadThing(x, y, move_dir);
12975 }
12976
12977 void TestIfBadThingTouchesPlayer(int x, int y)
12978 {
12979   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12980 }
12981
12982 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12983 {
12984   TestIfBadThingHitsGoodThing(x, y, move_dir);
12985 }
12986
12987 void TestIfFriendTouchesBadThing(int x, int y)
12988 {
12989   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12990 }
12991
12992 void TestIfBadThingTouchesFriend(int x, int y)
12993 {
12994   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12995 }
12996
12997 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12998 {
12999   int i, kill_x = bad_x, kill_y = bad_y;
13000   static int xy[4][2] =
13001   {
13002     { 0, -1 },
13003     { -1, 0 },
13004     { +1, 0 },
13005     { 0, +1 }
13006   };
13007
13008   for (i = 0; i < NUM_DIRECTIONS; i++)
13009   {
13010     int x, y, element;
13011
13012     x = bad_x + xy[i][0];
13013     y = bad_y + xy[i][1];
13014     if (!IN_LEV_FIELD(x, y))
13015       continue;
13016
13017     element = Feld[x][y];
13018     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13019         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13020     {
13021       kill_x = x;
13022       kill_y = y;
13023       break;
13024     }
13025   }
13026
13027   if (kill_x != bad_x || kill_y != bad_y)
13028     Bang(bad_x, bad_y);
13029 }
13030
13031 void KillPlayer(struct PlayerInfo *player)
13032 {
13033   int jx = player->jx, jy = player->jy;
13034
13035   if (!player->active)
13036     return;
13037
13038   /* the following code was introduced to prevent an infinite loop when calling
13039      -> Bang()
13040      -> CheckTriggeredElementChangeExt()
13041      -> ExecuteCustomElementAction()
13042      -> KillPlayer()
13043      -> (infinitely repeating the above sequence of function calls)
13044      which occurs when killing the player while having a CE with the setting
13045      "kill player X when explosion of <player X>"; the solution using a new
13046      field "player->killed" was chosen for backwards compatibility, although
13047      clever use of the fields "player->active" etc. would probably also work */
13048 #if 1
13049   if (player->killed)
13050     return;
13051 #endif
13052
13053   player->killed = TRUE;
13054
13055   /* remove accessible field at the player's position */
13056   Feld[jx][jy] = EL_EMPTY;
13057
13058   /* deactivate shield (else Bang()/Explode() would not work right) */
13059   player->shield_normal_time_left = 0;
13060   player->shield_deadly_time_left = 0;
13061
13062   Bang(jx, jy);
13063   BuryPlayer(player);
13064 }
13065
13066 static void KillPlayerUnlessEnemyProtected(int x, int y)
13067 {
13068   if (!PLAYER_ENEMY_PROTECTED(x, y))
13069     KillPlayer(PLAYERINFO(x, y));
13070 }
13071
13072 static void KillPlayerUnlessExplosionProtected(int x, int y)
13073 {
13074   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13075     KillPlayer(PLAYERINFO(x, y));
13076 }
13077
13078 void BuryPlayer(struct PlayerInfo *player)
13079 {
13080   int jx = player->jx, jy = player->jy;
13081
13082   if (!player->active)
13083     return;
13084
13085   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13086   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13087
13088   player->GameOver = TRUE;
13089   RemovePlayer(player);
13090 }
13091
13092 void RemovePlayer(struct PlayerInfo *player)
13093 {
13094   int jx = player->jx, jy = player->jy;
13095   int i, found = FALSE;
13096
13097   player->present = FALSE;
13098   player->active = FALSE;
13099
13100   if (!ExplodeField[jx][jy])
13101     StorePlayer[jx][jy] = 0;
13102
13103   if (player->is_moving)
13104     DrawLevelField(player->last_jx, player->last_jy);
13105
13106   for (i = 0; i < MAX_PLAYERS; i++)
13107     if (stored_player[i].active)
13108       found = TRUE;
13109
13110   if (!found)
13111     AllPlayersGone = TRUE;
13112
13113   ExitX = ZX = jx;
13114   ExitY = ZY = jy;
13115 }
13116
13117 #if USE_NEW_SNAP_DELAY
13118 static void setFieldForSnapping(int x, int y, int element, int direction)
13119 {
13120   struct ElementInfo *ei = &element_info[element];
13121   int direction_bit = MV_DIR_TO_BIT(direction);
13122   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13123   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13124                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13125
13126   Feld[x][y] = EL_ELEMENT_SNAPPING;
13127   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13128
13129   ResetGfxAnimation(x, y);
13130
13131   GfxElement[x][y] = element;
13132   GfxAction[x][y] = action;
13133   GfxDir[x][y] = direction;
13134   GfxFrame[x][y] = -1;
13135 }
13136 #endif
13137
13138 /*
13139   =============================================================================
13140   checkDiagonalPushing()
13141   -----------------------------------------------------------------------------
13142   check if diagonal input device direction results in pushing of object
13143   (by checking if the alternative direction is walkable, diggable, ...)
13144   =============================================================================
13145 */
13146
13147 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13148                                     int x, int y, int real_dx, int real_dy)
13149 {
13150   int jx, jy, dx, dy, xx, yy;
13151
13152   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13153     return TRUE;
13154
13155   /* diagonal direction: check alternative direction */
13156   jx = player->jx;
13157   jy = player->jy;
13158   dx = x - jx;
13159   dy = y - jy;
13160   xx = jx + (dx == 0 ? real_dx : 0);
13161   yy = jy + (dy == 0 ? real_dy : 0);
13162
13163   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13164 }
13165
13166 /*
13167   =============================================================================
13168   DigField()
13169   -----------------------------------------------------------------------------
13170   x, y:                 field next to player (non-diagonal) to try to dig to
13171   real_dx, real_dy:     direction as read from input device (can be diagonal)
13172   =============================================================================
13173 */
13174
13175 int DigField(struct PlayerInfo *player,
13176              int oldx, int oldy, int x, int y,
13177              int real_dx, int real_dy, int mode)
13178 {
13179   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13180   boolean player_was_pushing = player->is_pushing;
13181   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13182   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13183   int jx = oldx, jy = oldy;
13184   int dx = x - jx, dy = y - jy;
13185   int nextx = x + dx, nexty = y + dy;
13186   int move_direction = (dx == -1 ? MV_LEFT  :
13187                         dx == +1 ? MV_RIGHT :
13188                         dy == -1 ? MV_UP    :
13189                         dy == +1 ? MV_DOWN  : MV_NONE);
13190   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13191   int dig_side = MV_DIR_OPPOSITE(move_direction);
13192   int old_element = Feld[jx][jy];
13193 #if USE_FIXED_DONT_RUN_INTO
13194   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13195 #else
13196   int element;
13197 #endif
13198   int collect_count;
13199
13200   if (is_player)                /* function can also be called by EL_PENGUIN */
13201   {
13202     if (player->MovPos == 0)
13203     {
13204       player->is_digging = FALSE;
13205       player->is_collecting = FALSE;
13206     }
13207
13208     if (player->MovPos == 0)    /* last pushing move finished */
13209       player->is_pushing = FALSE;
13210
13211     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13212     {
13213       player->is_switching = FALSE;
13214       player->push_delay = -1;
13215
13216       return MP_NO_ACTION;
13217     }
13218   }
13219
13220 #if !USE_FIXED_DONT_RUN_INTO
13221   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13222     return MP_NO_ACTION;
13223 #endif
13224
13225   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13226     old_element = Back[jx][jy];
13227
13228   /* in case of element dropped at player position, check background */
13229   else if (Back[jx][jy] != EL_EMPTY &&
13230            game.engine_version >= VERSION_IDENT(2,2,0,0))
13231     old_element = Back[jx][jy];
13232
13233   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13234     return MP_NO_ACTION;        /* field has no opening in this direction */
13235
13236   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13237     return MP_NO_ACTION;        /* field has no opening in this direction */
13238
13239 #if USE_FIXED_DONT_RUN_INTO
13240   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13241   {
13242     SplashAcid(x, y);
13243
13244     Feld[jx][jy] = player->artwork_element;
13245     InitMovingField(jx, jy, MV_DOWN);
13246     Store[jx][jy] = EL_ACID;
13247     ContinueMoving(jx, jy);
13248     BuryPlayer(player);
13249
13250     return MP_DONT_RUN_INTO;
13251   }
13252 #endif
13253
13254 #if USE_FIXED_DONT_RUN_INTO
13255   if (player_can_move && DONT_RUN_INTO(element))
13256   {
13257     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13258
13259     return MP_DONT_RUN_INTO;
13260   }
13261 #endif
13262
13263 #if USE_FIXED_DONT_RUN_INTO
13264   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13265     return MP_NO_ACTION;
13266 #endif
13267
13268 #if !USE_FIXED_DONT_RUN_INTO
13269   element = Feld[x][y];
13270 #endif
13271
13272   collect_count = element_info[element].collect_count_initial;
13273
13274   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13275     return MP_NO_ACTION;
13276
13277   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13278     player_can_move = player_can_move_or_snap;
13279
13280   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13281       game.engine_version >= VERSION_IDENT(2,2,0,0))
13282   {
13283     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13284                                player->index_bit, dig_side);
13285     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13286                                         player->index_bit, dig_side);
13287
13288     if (element == EL_DC_LANDMINE)
13289       Bang(x, y);
13290
13291     if (Feld[x][y] != element)          /* field changed by snapping */
13292       return MP_ACTION;
13293
13294     return MP_NO_ACTION;
13295   }
13296
13297 #if USE_PLAYER_GRAVITY
13298   if (player->gravity && is_player && !player->is_auto_moving &&
13299       canFallDown(player) && move_direction != MV_DOWN &&
13300       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13301     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13302 #else
13303   if (game.gravity && is_player && !player->is_auto_moving &&
13304       canFallDown(player) && move_direction != MV_DOWN &&
13305       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13306     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13307 #endif
13308
13309   if (player_can_move &&
13310       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13311   {
13312     int sound_element = SND_ELEMENT(element);
13313     int sound_action = ACTION_WALKING;
13314
13315     if (IS_RND_GATE(element))
13316     {
13317       if (!player->key[RND_GATE_NR(element)])
13318         return MP_NO_ACTION;
13319     }
13320     else if (IS_RND_GATE_GRAY(element))
13321     {
13322       if (!player->key[RND_GATE_GRAY_NR(element)])
13323         return MP_NO_ACTION;
13324     }
13325     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13326     {
13327       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13328         return MP_NO_ACTION;
13329     }
13330     else if (element == EL_EXIT_OPEN ||
13331              element == EL_EM_EXIT_OPEN ||
13332              element == EL_STEEL_EXIT_OPEN ||
13333              element == EL_EM_STEEL_EXIT_OPEN ||
13334              element == EL_SP_EXIT_OPEN ||
13335              element == EL_SP_EXIT_OPENING)
13336     {
13337       sound_action = ACTION_PASSING;    /* player is passing exit */
13338     }
13339     else if (element == EL_EMPTY)
13340     {
13341       sound_action = ACTION_MOVING;             /* nothing to walk on */
13342     }
13343
13344     /* play sound from background or player, whatever is available */
13345     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13346       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13347     else
13348       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13349   }
13350   else if (player_can_move &&
13351            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13352   {
13353     if (!ACCESS_FROM(element, opposite_direction))
13354       return MP_NO_ACTION;      /* field not accessible from this direction */
13355
13356     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13357       return MP_NO_ACTION;
13358
13359     if (IS_EM_GATE(element))
13360     {
13361       if (!player->key[EM_GATE_NR(element)])
13362         return MP_NO_ACTION;
13363     }
13364     else if (IS_EM_GATE_GRAY(element))
13365     {
13366       if (!player->key[EM_GATE_GRAY_NR(element)])
13367         return MP_NO_ACTION;
13368     }
13369     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13370     {
13371       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13372         return MP_NO_ACTION;
13373     }
13374     else if (IS_EMC_GATE(element))
13375     {
13376       if (!player->key[EMC_GATE_NR(element)])
13377         return MP_NO_ACTION;
13378     }
13379     else if (IS_EMC_GATE_GRAY(element))
13380     {
13381       if (!player->key[EMC_GATE_GRAY_NR(element)])
13382         return MP_NO_ACTION;
13383     }
13384     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13385     {
13386       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13387         return MP_NO_ACTION;
13388     }
13389     else if (element == EL_DC_GATE_WHITE ||
13390              element == EL_DC_GATE_WHITE_GRAY ||
13391              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13392     {
13393       if (player->num_white_keys == 0)
13394         return MP_NO_ACTION;
13395
13396       player->num_white_keys--;
13397     }
13398     else if (IS_SP_PORT(element))
13399     {
13400       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13401           element == EL_SP_GRAVITY_PORT_RIGHT ||
13402           element == EL_SP_GRAVITY_PORT_UP ||
13403           element == EL_SP_GRAVITY_PORT_DOWN)
13404 #if USE_PLAYER_GRAVITY
13405         player->gravity = !player->gravity;
13406 #else
13407         game.gravity = !game.gravity;
13408 #endif
13409       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13410                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13411                element == EL_SP_GRAVITY_ON_PORT_UP ||
13412                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13413 #if USE_PLAYER_GRAVITY
13414         player->gravity = TRUE;
13415 #else
13416         game.gravity = TRUE;
13417 #endif
13418       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13419                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13420                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13421                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13422 #if USE_PLAYER_GRAVITY
13423         player->gravity = FALSE;
13424 #else
13425         game.gravity = FALSE;
13426 #endif
13427     }
13428
13429     /* automatically move to the next field with double speed */
13430     player->programmed_action = move_direction;
13431
13432     if (player->move_delay_reset_counter == 0)
13433     {
13434       player->move_delay_reset_counter = 2;     /* two double speed steps */
13435
13436       DOUBLE_PLAYER_SPEED(player);
13437     }
13438
13439     PlayLevelSoundAction(x, y, ACTION_PASSING);
13440   }
13441   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13442   {
13443     RemoveField(x, y);
13444
13445     if (mode != DF_SNAP)
13446     {
13447       GfxElement[x][y] = GFX_ELEMENT(element);
13448       player->is_digging = TRUE;
13449     }
13450
13451     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13452
13453     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13454                                         player->index_bit, dig_side);
13455
13456     if (mode == DF_SNAP)
13457     {
13458 #if USE_NEW_SNAP_DELAY
13459       if (level.block_snap_field)
13460         setFieldForSnapping(x, y, element, move_direction);
13461       else
13462         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13463 #else
13464       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13465 #endif
13466
13467       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13468                                           player->index_bit, dig_side);
13469     }
13470   }
13471   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13472   {
13473     RemoveField(x, y);
13474
13475     if (is_player && mode != DF_SNAP)
13476     {
13477       GfxElement[x][y] = element;
13478       player->is_collecting = TRUE;
13479     }
13480
13481     if (element == EL_SPEED_PILL)
13482     {
13483       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13484     }
13485     else if (element == EL_EXTRA_TIME && level.time > 0)
13486     {
13487       TimeLeft += level.extra_time;
13488
13489 #if 1
13490       game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13491
13492       DisplayGameControlValues();
13493 #else
13494       DrawGameValue_Time(TimeLeft);
13495 #endif
13496     }
13497     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13498     {
13499       player->shield_normal_time_left += level.shield_normal_time;
13500       if (element == EL_SHIELD_DEADLY)
13501         player->shield_deadly_time_left += level.shield_deadly_time;
13502     }
13503     else if (element == EL_DYNAMITE ||
13504              element == EL_EM_DYNAMITE ||
13505              element == EL_SP_DISK_RED)
13506     {
13507       if (player->inventory_size < MAX_INVENTORY_SIZE)
13508         player->inventory_element[player->inventory_size++] = element;
13509
13510       DrawGameDoorValues();
13511     }
13512     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13513     {
13514       player->dynabomb_count++;
13515       player->dynabombs_left++;
13516     }
13517     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13518     {
13519       player->dynabomb_size++;
13520     }
13521     else if (element == EL_DYNABOMB_INCREASE_POWER)
13522     {
13523       player->dynabomb_xl = TRUE;
13524     }
13525     else if (IS_KEY(element))
13526     {
13527       player->key[KEY_NR(element)] = TRUE;
13528
13529       DrawGameDoorValues();
13530     }
13531     else if (element == EL_DC_KEY_WHITE)
13532     {
13533       player->num_white_keys++;
13534
13535       /* display white keys? */
13536       /* DrawGameDoorValues(); */
13537     }
13538     else if (IS_ENVELOPE(element))
13539     {
13540       player->show_envelope = element;
13541     }
13542     else if (element == EL_EMC_LENSES)
13543     {
13544       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13545
13546       RedrawAllInvisibleElementsForLenses();
13547     }
13548     else if (element == EL_EMC_MAGNIFIER)
13549     {
13550       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13551
13552       RedrawAllInvisibleElementsForMagnifier();
13553     }
13554     else if (IS_DROPPABLE(element) ||
13555              IS_THROWABLE(element))     /* can be collected and dropped */
13556     {
13557       int i;
13558
13559       if (collect_count == 0)
13560         player->inventory_infinite_element = element;
13561       else
13562         for (i = 0; i < collect_count; i++)
13563           if (player->inventory_size < MAX_INVENTORY_SIZE)
13564             player->inventory_element[player->inventory_size++] = element;
13565
13566       DrawGameDoorValues();
13567     }
13568     else if (collect_count > 0)
13569     {
13570       local_player->gems_still_needed -= collect_count;
13571       if (local_player->gems_still_needed < 0)
13572         local_player->gems_still_needed = 0;
13573
13574 #if 1
13575       game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
13576
13577       DisplayGameControlValues();
13578 #else
13579       DrawGameValue_Emeralds(local_player->gems_still_needed);
13580 #endif
13581     }
13582
13583     RaiseScoreElement(element);
13584     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13585
13586     if (is_player)
13587       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13588                                           player->index_bit, dig_side);
13589
13590     if (mode == DF_SNAP)
13591     {
13592 #if USE_NEW_SNAP_DELAY
13593       if (level.block_snap_field)
13594         setFieldForSnapping(x, y, element, move_direction);
13595       else
13596         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13597 #else
13598       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13599 #endif
13600
13601       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13602                                           player->index_bit, dig_side);
13603     }
13604   }
13605   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13606   {
13607     if (mode == DF_SNAP && element != EL_BD_ROCK)
13608       return MP_NO_ACTION;
13609
13610     if (CAN_FALL(element) && dy)
13611       return MP_NO_ACTION;
13612
13613     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13614         !(element == EL_SPRING && level.use_spring_bug))
13615       return MP_NO_ACTION;
13616
13617     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13618         ((move_direction & MV_VERTICAL &&
13619           ((element_info[element].move_pattern & MV_LEFT &&
13620             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13621            (element_info[element].move_pattern & MV_RIGHT &&
13622             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13623          (move_direction & MV_HORIZONTAL &&
13624           ((element_info[element].move_pattern & MV_UP &&
13625             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13626            (element_info[element].move_pattern & MV_DOWN &&
13627             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13628       return MP_NO_ACTION;
13629
13630     /* do not push elements already moving away faster than player */
13631     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13632         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13633       return MP_NO_ACTION;
13634
13635     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13636     {
13637       if (player->push_delay_value == -1 || !player_was_pushing)
13638         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13639     }
13640     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13641     {
13642       if (player->push_delay_value == -1)
13643         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13644     }
13645     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13646     {
13647       if (!player->is_pushing)
13648         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13649     }
13650
13651     player->is_pushing = TRUE;
13652     player->is_active = TRUE;
13653
13654     if (!(IN_LEV_FIELD(nextx, nexty) &&
13655           (IS_FREE(nextx, nexty) ||
13656            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13657             IS_SB_ELEMENT(element)))))
13658       return MP_NO_ACTION;
13659
13660     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13661       return MP_NO_ACTION;
13662
13663     if (player->push_delay == -1)       /* new pushing; restart delay */
13664       player->push_delay = 0;
13665
13666     if (player->push_delay < player->push_delay_value &&
13667         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13668         element != EL_SPRING && element != EL_BALLOON)
13669     {
13670       /* make sure that there is no move delay before next try to push */
13671       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13672         player->move_delay = 0;
13673
13674       return MP_NO_ACTION;
13675     }
13676
13677     if (IS_SB_ELEMENT(element))
13678     {
13679       if (element == EL_SOKOBAN_FIELD_FULL)
13680       {
13681         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13682         local_player->sokobanfields_still_needed++;
13683       }
13684
13685       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13686       {
13687         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13688         local_player->sokobanfields_still_needed--;
13689       }
13690
13691       Feld[x][y] = EL_SOKOBAN_OBJECT;
13692
13693       if (Back[x][y] == Back[nextx][nexty])
13694         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13695       else if (Back[x][y] != 0)
13696         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13697                                     ACTION_EMPTYING);
13698       else
13699         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13700                                     ACTION_FILLING);
13701
13702       if (local_player->sokobanfields_still_needed == 0 &&
13703           game.emulation == EMU_SOKOBAN)
13704       {
13705         PlayerWins(player);
13706
13707         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13708       }
13709     }
13710     else
13711       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13712
13713     InitMovingField(x, y, move_direction);
13714     GfxAction[x][y] = ACTION_PUSHING;
13715
13716     if (mode == DF_SNAP)
13717       ContinueMoving(x, y);
13718     else
13719       MovPos[x][y] = (dx != 0 ? dx : dy);
13720
13721     Pushed[x][y] = TRUE;
13722     Pushed[nextx][nexty] = TRUE;
13723
13724     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13725       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13726     else
13727       player->push_delay_value = -1;    /* get new value later */
13728
13729     /* check for element change _after_ element has been pushed */
13730     if (game.use_change_when_pushing_bug)
13731     {
13732       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13733                                  player->index_bit, dig_side);
13734       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13735                                           player->index_bit, dig_side);
13736     }
13737   }
13738   else if (IS_SWITCHABLE(element))
13739   {
13740     if (PLAYER_SWITCHING(player, x, y))
13741     {
13742       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13743                                           player->index_bit, dig_side);
13744
13745       return MP_ACTION;
13746     }
13747
13748     player->is_switching = TRUE;
13749     player->switch_x = x;
13750     player->switch_y = y;
13751
13752     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13753
13754     if (element == EL_ROBOT_WHEEL)
13755     {
13756       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13757       ZX = x;
13758       ZY = y;
13759
13760       DrawLevelField(x, y);
13761     }
13762     else if (element == EL_SP_TERMINAL)
13763     {
13764       int xx, yy;
13765
13766       SCAN_PLAYFIELD(xx, yy)
13767       {
13768         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13769           Bang(xx, yy);
13770         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13771           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13772       }
13773     }
13774     else if (IS_BELT_SWITCH(element))
13775     {
13776       ToggleBeltSwitch(x, y);
13777     }
13778     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13779              element == EL_SWITCHGATE_SWITCH_DOWN ||
13780              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13781              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13782     {
13783       ToggleSwitchgateSwitch(x, y);
13784     }
13785     else if (element == EL_LIGHT_SWITCH ||
13786              element == EL_LIGHT_SWITCH_ACTIVE)
13787     {
13788       ToggleLightSwitch(x, y);
13789     }
13790     else if (element == EL_TIMEGATE_SWITCH ||
13791              element == EL_DC_TIMEGATE_SWITCH)
13792     {
13793       ActivateTimegateSwitch(x, y);
13794     }
13795     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13796              element == EL_BALLOON_SWITCH_RIGHT ||
13797              element == EL_BALLOON_SWITCH_UP    ||
13798              element == EL_BALLOON_SWITCH_DOWN  ||
13799              element == EL_BALLOON_SWITCH_NONE  ||
13800              element == EL_BALLOON_SWITCH_ANY)
13801     {
13802       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13803                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13804                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13805                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13806                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13807                              move_direction);
13808     }
13809     else if (element == EL_LAMP)
13810     {
13811       Feld[x][y] = EL_LAMP_ACTIVE;
13812       local_player->lights_still_needed--;
13813
13814       ResetGfxAnimation(x, y);
13815       DrawLevelField(x, y);
13816     }
13817     else if (element == EL_TIME_ORB_FULL)
13818     {
13819       Feld[x][y] = EL_TIME_ORB_EMPTY;
13820
13821       if (level.time > 0 || level.use_time_orb_bug)
13822       {
13823         TimeLeft += level.time_orb_time;
13824
13825 #if 1
13826         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13827
13828         DisplayGameControlValues();
13829 #else
13830         DrawGameValue_Time(TimeLeft);
13831 #endif
13832       }
13833
13834       ResetGfxAnimation(x, y);
13835       DrawLevelField(x, y);
13836     }
13837     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13838              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13839     {
13840       int xx, yy;
13841
13842       game.ball_state = !game.ball_state;
13843
13844       SCAN_PLAYFIELD(xx, yy)
13845       {
13846         int e = Feld[xx][yy];
13847
13848         if (game.ball_state)
13849         {
13850           if (e == EL_EMC_MAGIC_BALL)
13851             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13852           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13853             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13854         }
13855         else
13856         {
13857           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13858             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13859           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13860             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13861         }
13862       }
13863     }
13864
13865     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13866                                         player->index_bit, dig_side);
13867
13868     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13869                                         player->index_bit, dig_side);
13870
13871     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13872                                         player->index_bit, dig_side);
13873
13874     return MP_ACTION;
13875   }
13876   else
13877   {
13878     if (!PLAYER_SWITCHING(player, x, y))
13879     {
13880       player->is_switching = TRUE;
13881       player->switch_x = x;
13882       player->switch_y = y;
13883
13884       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13885                                  player->index_bit, dig_side);
13886       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13887                                           player->index_bit, dig_side);
13888
13889       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13890                                  player->index_bit, dig_side);
13891       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13892                                           player->index_bit, dig_side);
13893     }
13894
13895     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13896                                player->index_bit, dig_side);
13897     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13898                                         player->index_bit, dig_side);
13899
13900     return MP_NO_ACTION;
13901   }
13902
13903   player->push_delay = -1;
13904
13905   if (is_player)                /* function can also be called by EL_PENGUIN */
13906   {
13907     if (Feld[x][y] != element)          /* really digged/collected something */
13908     {
13909       player->is_collecting = !player->is_digging;
13910       player->is_active = TRUE;
13911     }
13912   }
13913
13914   return MP_MOVING;
13915 }
13916
13917 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13918 {
13919   int jx = player->jx, jy = player->jy;
13920   int x = jx + dx, y = jy + dy;
13921   int snap_direction = (dx == -1 ? MV_LEFT  :
13922                         dx == +1 ? MV_RIGHT :
13923                         dy == -1 ? MV_UP    :
13924                         dy == +1 ? MV_DOWN  : MV_NONE);
13925   boolean can_continue_snapping = (level.continuous_snapping &&
13926                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13927
13928   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13929     return FALSE;
13930
13931   if (!player->active || !IN_LEV_FIELD(x, y))
13932     return FALSE;
13933
13934   if (dx && dy)
13935     return FALSE;
13936
13937   if (!dx && !dy)
13938   {
13939     if (player->MovPos == 0)
13940       player->is_pushing = FALSE;
13941
13942     player->is_snapping = FALSE;
13943
13944     if (player->MovPos == 0)
13945     {
13946       player->is_moving = FALSE;
13947       player->is_digging = FALSE;
13948       player->is_collecting = FALSE;
13949     }
13950
13951     return FALSE;
13952   }
13953
13954 #if USE_NEW_CONTINUOUS_SNAPPING
13955   /* prevent snapping with already pressed snap key when not allowed */
13956   if (player->is_snapping && !can_continue_snapping)
13957     return FALSE;
13958 #else
13959   if (player->is_snapping)
13960     return FALSE;
13961 #endif
13962
13963   player->MovDir = snap_direction;
13964
13965   if (player->MovPos == 0)
13966   {
13967     player->is_moving = FALSE;
13968     player->is_digging = FALSE;
13969     player->is_collecting = FALSE;
13970   }
13971
13972   player->is_dropping = FALSE;
13973   player->is_dropping_pressed = FALSE;
13974   player->drop_pressed_delay = 0;
13975
13976   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13977     return FALSE;
13978
13979   player->is_snapping = TRUE;
13980   player->is_active = TRUE;
13981
13982   if (player->MovPos == 0)
13983   {
13984     player->is_moving = FALSE;
13985     player->is_digging = FALSE;
13986     player->is_collecting = FALSE;
13987   }
13988
13989   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13990     DrawLevelField(player->last_jx, player->last_jy);
13991
13992   DrawLevelField(x, y);
13993
13994   return TRUE;
13995 }
13996
13997 boolean DropElement(struct PlayerInfo *player)
13998 {
13999   int old_element, new_element;
14000   int dropx = player->jx, dropy = player->jy;
14001   int drop_direction = player->MovDir;
14002   int drop_side = drop_direction;
14003   int drop_element = (player->inventory_size > 0 ?
14004                       player->inventory_element[player->inventory_size - 1] :
14005                       player->inventory_infinite_element != EL_UNDEFINED ?
14006                       player->inventory_infinite_element :
14007                       player->dynabombs_left > 0 ?
14008                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14009                       EL_UNDEFINED);
14010
14011   player->is_dropping_pressed = TRUE;
14012
14013   /* do not drop an element on top of another element; when holding drop key
14014      pressed without moving, dropped element must move away before the next
14015      element can be dropped (this is especially important if the next element
14016      is dynamite, which can be placed on background for historical reasons) */
14017   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14018     return MP_ACTION;
14019
14020   if (IS_THROWABLE(drop_element))
14021   {
14022     dropx += GET_DX_FROM_DIR(drop_direction);
14023     dropy += GET_DY_FROM_DIR(drop_direction);
14024
14025     if (!IN_LEV_FIELD(dropx, dropy))
14026       return FALSE;
14027   }
14028
14029   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14030   new_element = drop_element;           /* default: no change when dropping */
14031
14032   /* check if player is active, not moving and ready to drop */
14033   if (!player->active || player->MovPos || player->drop_delay > 0)
14034     return FALSE;
14035
14036   /* check if player has anything that can be dropped */
14037   if (new_element == EL_UNDEFINED)
14038     return FALSE;
14039
14040   /* check if drop key was pressed long enough for EM style dynamite */
14041   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14042     return FALSE;
14043
14044   /* check if anything can be dropped at the current position */
14045   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14046     return FALSE;
14047
14048   /* collected custom elements can only be dropped on empty fields */
14049   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14050     return FALSE;
14051
14052   if (old_element != EL_EMPTY)
14053     Back[dropx][dropy] = old_element;   /* store old element on this field */
14054
14055   ResetGfxAnimation(dropx, dropy);
14056   ResetRandomAnimationValue(dropx, dropy);
14057
14058   if (player->inventory_size > 0 ||
14059       player->inventory_infinite_element != EL_UNDEFINED)
14060   {
14061     if (player->inventory_size > 0)
14062     {
14063       player->inventory_size--;
14064
14065       DrawGameDoorValues();
14066
14067       if (new_element == EL_DYNAMITE)
14068         new_element = EL_DYNAMITE_ACTIVE;
14069       else if (new_element == EL_EM_DYNAMITE)
14070         new_element = EL_EM_DYNAMITE_ACTIVE;
14071       else if (new_element == EL_SP_DISK_RED)
14072         new_element = EL_SP_DISK_RED_ACTIVE;
14073     }
14074
14075     Feld[dropx][dropy] = new_element;
14076
14077     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14078       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14079                           el2img(Feld[dropx][dropy]), 0);
14080
14081     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14082
14083     /* needed if previous element just changed to "empty" in the last frame */
14084     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14085
14086     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14087                                player->index_bit, drop_side);
14088     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14089                                         CE_PLAYER_DROPS_X,
14090                                         player->index_bit, drop_side);
14091
14092     TestIfElementTouchesCustomElement(dropx, dropy);
14093   }
14094   else          /* player is dropping a dyna bomb */
14095   {
14096     player->dynabombs_left--;
14097
14098     Feld[dropx][dropy] = new_element;
14099
14100     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14101       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14102                           el2img(Feld[dropx][dropy]), 0);
14103
14104     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14105   }
14106
14107   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14108     InitField_WithBug1(dropx, dropy, FALSE);
14109
14110   new_element = Feld[dropx][dropy];     /* element might have changed */
14111
14112   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14113       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14114   {
14115     int move_direction, nextx, nexty;
14116
14117     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14118       MovDir[dropx][dropy] = drop_direction;
14119
14120     move_direction = MovDir[dropx][dropy];
14121     nextx = dropx + GET_DX_FROM_DIR(move_direction);
14122     nexty = dropy + GET_DY_FROM_DIR(move_direction);
14123
14124     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14125
14126 #if USE_FIX_IMPACT_COLLISION
14127     /* do not cause impact style collision by dropping elements that can fall */
14128     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14129 #else
14130     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14131 #endif
14132   }
14133
14134   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14135   player->is_dropping = TRUE;
14136
14137   player->drop_pressed_delay = 0;
14138   player->is_dropping_pressed = FALSE;
14139
14140   player->drop_x = dropx;
14141   player->drop_y = dropy;
14142
14143   return TRUE;
14144 }
14145
14146 /* ------------------------------------------------------------------------- */
14147 /* game sound playing functions                                              */
14148 /* ------------------------------------------------------------------------- */
14149
14150 static int *loop_sound_frame = NULL;
14151 static int *loop_sound_volume = NULL;
14152
14153 void InitPlayLevelSound()
14154 {
14155   int num_sounds = getSoundListSize();
14156
14157   checked_free(loop_sound_frame);
14158   checked_free(loop_sound_volume);
14159
14160   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14161   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14162 }
14163
14164 static void PlayLevelSound(int x, int y, int nr)
14165 {
14166   int sx = SCREENX(x), sy = SCREENY(y);
14167   int volume, stereo_position;
14168   int max_distance = 8;
14169   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14170
14171   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14172       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14173     return;
14174
14175   if (!IN_LEV_FIELD(x, y) ||
14176       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14177       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14178     return;
14179
14180   volume = SOUND_MAX_VOLUME;
14181
14182   if (!IN_SCR_FIELD(sx, sy))
14183   {
14184     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14185     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14186
14187     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14188   }
14189
14190   stereo_position = (SOUND_MAX_LEFT +
14191                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14192                      (SCR_FIELDX + 2 * max_distance));
14193
14194   if (IS_LOOP_SOUND(nr))
14195   {
14196     /* This assures that quieter loop sounds do not overwrite louder ones,
14197        while restarting sound volume comparison with each new game frame. */
14198
14199     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14200       return;
14201
14202     loop_sound_volume[nr] = volume;
14203     loop_sound_frame[nr] = FrameCounter;
14204   }
14205
14206   PlaySoundExt(nr, volume, stereo_position, type);
14207 }
14208
14209 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14210 {
14211   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14212                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14213                  y < LEVELY(BY1) ? LEVELY(BY1) :
14214                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14215                  sound_action);
14216 }
14217
14218 static void PlayLevelSoundAction(int x, int y, int action)
14219 {
14220   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14221 }
14222
14223 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14224 {
14225   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14226
14227   if (sound_effect != SND_UNDEFINED)
14228     PlayLevelSound(x, y, sound_effect);
14229 }
14230
14231 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14232                                               int action)
14233 {
14234   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14235
14236   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14237     PlayLevelSound(x, y, sound_effect);
14238 }
14239
14240 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14241 {
14242   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14243
14244   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14245     PlayLevelSound(x, y, sound_effect);
14246 }
14247
14248 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14249 {
14250   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14251
14252   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14253     StopSound(sound_effect);
14254 }
14255
14256 static void PlayLevelMusic()
14257 {
14258   if (levelset.music[level_nr] != MUS_UNDEFINED)
14259     PlayMusic(levelset.music[level_nr]);        /* from config file */
14260   else
14261     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14262 }
14263
14264 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14265 {
14266   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14267   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14268   int x = xx - 1 - offset;
14269   int y = yy - 1 - offset;
14270
14271   switch (sample)
14272   {
14273     case SAMPLE_blank:
14274       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14275       break;
14276
14277     case SAMPLE_roll:
14278       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14279       break;
14280
14281     case SAMPLE_stone:
14282       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14283       break;
14284
14285     case SAMPLE_nut:
14286       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14287       break;
14288
14289     case SAMPLE_crack:
14290       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14291       break;
14292
14293     case SAMPLE_bug:
14294       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14295       break;
14296
14297     case SAMPLE_tank:
14298       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14299       break;
14300
14301     case SAMPLE_android_clone:
14302       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14303       break;
14304
14305     case SAMPLE_android_move:
14306       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14307       break;
14308
14309     case SAMPLE_spring:
14310       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14311       break;
14312
14313     case SAMPLE_slurp:
14314       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14315       break;
14316
14317     case SAMPLE_eater:
14318       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14319       break;
14320
14321     case SAMPLE_eater_eat:
14322       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14323       break;
14324
14325     case SAMPLE_alien:
14326       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14327       break;
14328
14329     case SAMPLE_collect:
14330       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14331       break;
14332
14333     case SAMPLE_diamond:
14334       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14335       break;
14336
14337     case SAMPLE_squash:
14338       /* !!! CHECK THIS !!! */
14339 #if 1
14340       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14341 #else
14342       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14343 #endif
14344       break;
14345
14346     case SAMPLE_wonderfall:
14347       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14348       break;
14349
14350     case SAMPLE_drip:
14351       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14352       break;
14353
14354     case SAMPLE_push:
14355       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14356       break;
14357
14358     case SAMPLE_dirt:
14359       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14360       break;
14361
14362     case SAMPLE_acid:
14363       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14364       break;
14365
14366     case SAMPLE_ball:
14367       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14368       break;
14369
14370     case SAMPLE_grow:
14371       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14372       break;
14373
14374     case SAMPLE_wonder:
14375       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14376       break;
14377
14378     case SAMPLE_door:
14379       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14380       break;
14381
14382     case SAMPLE_exit_open:
14383       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14384       break;
14385
14386     case SAMPLE_exit_leave:
14387       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14388       break;
14389
14390     case SAMPLE_dynamite:
14391       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14392       break;
14393
14394     case SAMPLE_tick:
14395       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14396       break;
14397
14398     case SAMPLE_press:
14399       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14400       break;
14401
14402     case SAMPLE_wheel:
14403       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14404       break;
14405
14406     case SAMPLE_boom:
14407       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14408       break;
14409
14410     case SAMPLE_die:
14411       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14412       break;
14413
14414     case SAMPLE_time:
14415       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14416       break;
14417
14418     default:
14419       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14420       break;
14421   }
14422 }
14423
14424 #if 0
14425 void ChangeTime(int value)
14426 {
14427   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
14428
14429   *time += value;
14430
14431   /* EMC game engine uses value from time counter of RND game engine */
14432   level.native_em_level->lev->time = *time;
14433
14434   DrawGameValue_Time(*time);
14435 }
14436
14437 void RaiseScore(int value)
14438 {
14439   /* EMC game engine and RND game engine have separate score counters */
14440   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
14441                 &level.native_em_level->lev->score : &local_player->score);
14442
14443   *score += value;
14444
14445   DrawGameValue_Score(*score);
14446 }
14447 #endif
14448
14449 void RaiseScore(int value)
14450 {
14451   local_player->score += value;
14452
14453 #if 1
14454   game_control_value[GAME_CONTROL_SCORE] = local_player->score;
14455
14456   DisplayGameControlValues();
14457 #else
14458   DrawGameValue_Score(local_player->score);
14459 #endif
14460 }
14461
14462 void RaiseScoreElement(int element)
14463 {
14464   switch (element)
14465   {
14466     case EL_EMERALD:
14467     case EL_BD_DIAMOND:
14468     case EL_EMERALD_YELLOW:
14469     case EL_EMERALD_RED:
14470     case EL_EMERALD_PURPLE:
14471     case EL_SP_INFOTRON:
14472       RaiseScore(level.score[SC_EMERALD]);
14473       break;
14474     case EL_DIAMOND:
14475       RaiseScore(level.score[SC_DIAMOND]);
14476       break;
14477     case EL_CRYSTAL:
14478       RaiseScore(level.score[SC_CRYSTAL]);
14479       break;
14480     case EL_PEARL:
14481       RaiseScore(level.score[SC_PEARL]);
14482       break;
14483     case EL_BUG:
14484     case EL_BD_BUTTERFLY:
14485     case EL_SP_ELECTRON:
14486       RaiseScore(level.score[SC_BUG]);
14487       break;
14488     case EL_SPACESHIP:
14489     case EL_BD_FIREFLY:
14490     case EL_SP_SNIKSNAK:
14491       RaiseScore(level.score[SC_SPACESHIP]);
14492       break;
14493     case EL_YAMYAM:
14494     case EL_DARK_YAMYAM:
14495       RaiseScore(level.score[SC_YAMYAM]);
14496       break;
14497     case EL_ROBOT:
14498       RaiseScore(level.score[SC_ROBOT]);
14499       break;
14500     case EL_PACMAN:
14501       RaiseScore(level.score[SC_PACMAN]);
14502       break;
14503     case EL_NUT:
14504       RaiseScore(level.score[SC_NUT]);
14505       break;
14506     case EL_DYNAMITE:
14507     case EL_EM_DYNAMITE:
14508     case EL_SP_DISK_RED:
14509     case EL_DYNABOMB_INCREASE_NUMBER:
14510     case EL_DYNABOMB_INCREASE_SIZE:
14511     case EL_DYNABOMB_INCREASE_POWER:
14512       RaiseScore(level.score[SC_DYNAMITE]);
14513       break;
14514     case EL_SHIELD_NORMAL:
14515     case EL_SHIELD_DEADLY:
14516       RaiseScore(level.score[SC_SHIELD]);
14517       break;
14518     case EL_EXTRA_TIME:
14519       RaiseScore(level.extra_time_score);
14520       break;
14521     case EL_KEY_1:
14522     case EL_KEY_2:
14523     case EL_KEY_3:
14524     case EL_KEY_4:
14525     case EL_EM_KEY_1:
14526     case EL_EM_KEY_2:
14527     case EL_EM_KEY_3:
14528     case EL_EM_KEY_4:
14529     case EL_EMC_KEY_5:
14530     case EL_EMC_KEY_6:
14531     case EL_EMC_KEY_7:
14532     case EL_EMC_KEY_8:
14533     case EL_DC_KEY_WHITE:
14534       RaiseScore(level.score[SC_KEY]);
14535       break;
14536     default:
14537       RaiseScore(element_info[element].collect_score);
14538       break;
14539   }
14540 }
14541
14542 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14543 {
14544   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14545   {
14546 #if defined(NETWORK_AVALIABLE)
14547     if (options.network)
14548       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14549     else
14550 #endif
14551     {
14552       if (quick_quit)
14553       {
14554 #if 1
14555
14556 #if 1
14557         FadeSkipNextFadeIn();
14558 #else
14559         fading = fading_none;
14560 #endif
14561
14562 #else
14563         OpenDoor(DOOR_CLOSE_1);
14564 #endif
14565
14566         game_status = GAME_MODE_MAIN;
14567
14568 #if 1
14569         DrawAndFadeInMainMenu(REDRAW_FIELD);
14570 #else
14571         DrawMainMenu();
14572 #endif
14573       }
14574       else
14575       {
14576 #if 0
14577         FadeOut(REDRAW_FIELD);
14578 #endif
14579
14580         game_status = GAME_MODE_MAIN;
14581
14582         DrawAndFadeInMainMenu(REDRAW_FIELD);
14583       }
14584     }
14585   }
14586   else          /* continue playing the game */
14587   {
14588     if (tape.playing && tape.deactivate_display)
14589       TapeDeactivateDisplayOff(TRUE);
14590
14591     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14592
14593     if (tape.playing && tape.deactivate_display)
14594       TapeDeactivateDisplayOn();
14595   }
14596 }
14597
14598 void RequestQuitGame(boolean ask_if_really_quit)
14599 {
14600   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14601   boolean skip_request = AllPlayersGone || quick_quit;
14602
14603   RequestQuitGameExt(skip_request, quick_quit,
14604                      "Do you really want to quit the game ?");
14605 }
14606
14607
14608 /* ------------------------------------------------------------------------- */
14609 /* random generator functions                                                */
14610 /* ------------------------------------------------------------------------- */
14611
14612 unsigned int InitEngineRandom_RND(long seed)
14613 {
14614   game.num_random_calls = 0;
14615
14616 #if 0
14617   unsigned int rnd_seed = InitEngineRandom(seed);
14618
14619   printf("::: START RND: %d\n", rnd_seed);
14620
14621   return rnd_seed;
14622 #else
14623
14624   return InitEngineRandom(seed);
14625
14626 #endif
14627
14628 }
14629
14630 unsigned int RND(int max)
14631 {
14632   if (max > 0)
14633   {
14634     game.num_random_calls++;
14635
14636     return GetEngineRandom(max);
14637   }
14638
14639   return 0;
14640 }
14641
14642
14643 /* ------------------------------------------------------------------------- */
14644 /* game engine snapshot handling functions                                   */
14645 /* ------------------------------------------------------------------------- */
14646
14647 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
14648
14649 struct EngineSnapshotInfo
14650 {
14651   /* runtime values for custom element collect score */
14652   int collect_score[NUM_CUSTOM_ELEMENTS];
14653
14654   /* runtime values for group element choice position */
14655   int choice_pos[NUM_GROUP_ELEMENTS];
14656
14657   /* runtime values for belt position animations */
14658   int belt_graphic[4 * NUM_BELT_PARTS];
14659   int belt_anim_mode[4 * NUM_BELT_PARTS];
14660 };
14661
14662 struct EngineSnapshotNodeInfo
14663 {
14664   void *buffer_orig;
14665   void *buffer_copy;
14666   int size;
14667 };
14668
14669 static struct EngineSnapshotInfo engine_snapshot_rnd;
14670 static ListNode *engine_snapshot_list = NULL;
14671 static char *snapshot_level_identifier = NULL;
14672 static int snapshot_level_nr = -1;
14673
14674 void FreeEngineSnapshot()
14675 {
14676   while (engine_snapshot_list != NULL)
14677     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14678                        checked_free);
14679
14680   setString(&snapshot_level_identifier, NULL);
14681   snapshot_level_nr = -1;
14682 }
14683
14684 static void SaveEngineSnapshotValues_RND()
14685 {
14686   static int belt_base_active_element[4] =
14687   {
14688     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14689     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14690     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14691     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14692   };
14693   int i, j;
14694
14695   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14696   {
14697     int element = EL_CUSTOM_START + i;
14698
14699     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14700   }
14701
14702   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14703   {
14704     int element = EL_GROUP_START + i;
14705
14706     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14707   }
14708
14709   for (i = 0; i < 4; i++)
14710   {
14711     for (j = 0; j < NUM_BELT_PARTS; j++)
14712     {
14713       int element = belt_base_active_element[i] + j;
14714       int graphic = el2img(element);
14715       int anim_mode = graphic_info[graphic].anim_mode;
14716
14717       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14718       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14719     }
14720   }
14721 }
14722
14723 static void LoadEngineSnapshotValues_RND()
14724 {
14725   unsigned long num_random_calls = game.num_random_calls;
14726   int i, j;
14727
14728   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14729   {
14730     int element = EL_CUSTOM_START + i;
14731
14732     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14733   }
14734
14735   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14736   {
14737     int element = EL_GROUP_START + i;
14738
14739     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14740   }
14741
14742   for (i = 0; i < 4; i++)
14743   {
14744     for (j = 0; j < NUM_BELT_PARTS; j++)
14745     {
14746       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14747       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14748
14749       graphic_info[graphic].anim_mode = anim_mode;
14750     }
14751   }
14752
14753   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14754   {
14755     InitRND(tape.random_seed);
14756     for (i = 0; i < num_random_calls; i++)
14757       RND(1);
14758   }
14759
14760   if (game.num_random_calls != num_random_calls)
14761   {
14762     Error(ERR_INFO, "number of random calls out of sync");
14763     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14764     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14765     Error(ERR_EXIT, "this should not happen -- please debug");
14766   }
14767 }
14768
14769 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14770 {
14771   struct EngineSnapshotNodeInfo *bi =
14772     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14773
14774   bi->buffer_orig = buffer;
14775   bi->buffer_copy = checked_malloc(size);
14776   bi->size = size;
14777
14778   memcpy(bi->buffer_copy, buffer, size);
14779
14780   addNodeToList(&engine_snapshot_list, NULL, bi);
14781 }
14782
14783 void SaveEngineSnapshot()
14784 {
14785   FreeEngineSnapshot();         /* free previous snapshot, if needed */
14786
14787   if (level_editor_test_game)   /* do not save snapshots from editor */
14788     return;
14789
14790   /* copy some special values to a structure better suited for the snapshot */
14791
14792   SaveEngineSnapshotValues_RND();
14793   SaveEngineSnapshotValues_EM();
14794
14795   /* save values stored in special snapshot structure */
14796
14797   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14798   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14799
14800   /* save further RND engine values */
14801
14802   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14803   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14804   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14805
14806   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14807   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14808   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14809   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14810
14811   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14812   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14813   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14814   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14815   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14816
14817   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14818   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14819   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14820
14821   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14822
14823   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14824
14825   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14826   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14827
14828   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14829   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14830   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14831   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14832   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14833   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14834   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14835   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14836   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14837   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14838   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14839   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14840   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14841   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14842   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14843   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14844   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
14845   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
14846
14847   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14848   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14849
14850   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14851   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14852   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14853
14854   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14855   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14856
14857   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14858   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14859   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14860   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14861   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14862
14863   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14864   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14865
14866   /* save level identification information */
14867
14868   setString(&snapshot_level_identifier, leveldir_current->identifier);
14869   snapshot_level_nr = level_nr;
14870
14871 #if 0
14872   ListNode *node = engine_snapshot_list;
14873   int num_bytes = 0;
14874
14875   while (node != NULL)
14876   {
14877     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14878
14879     node = node->next;
14880   }
14881
14882   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14883 #endif
14884 }
14885
14886 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
14887 {
14888   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
14889 }
14890
14891 void LoadEngineSnapshot()
14892 {
14893   ListNode *node = engine_snapshot_list;
14894
14895   if (engine_snapshot_list == NULL)
14896     return;
14897
14898   while (node != NULL)
14899   {
14900     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
14901
14902     node = node->next;
14903   }
14904
14905   /* restore special values from snapshot structure */
14906
14907   LoadEngineSnapshotValues_RND();
14908   LoadEngineSnapshotValues_EM();
14909 }
14910
14911 boolean CheckEngineSnapshot()
14912 {
14913   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14914           snapshot_level_nr == level_nr);
14915 }
14916
14917
14918 /* ---------- new game button stuff ---------------------------------------- */
14919
14920 /* graphic position values for game buttons */
14921 #define GAME_BUTTON_XSIZE       30
14922 #define GAME_BUTTON_YSIZE       30
14923 #define GAME_BUTTON_XPOS        5
14924 #define GAME_BUTTON_YPOS        215
14925 #define SOUND_BUTTON_XPOS       5
14926 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
14927
14928 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14929 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14930 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14931 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14932 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14933 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14934
14935 static struct
14936 {
14937   int *x, *y;
14938   int gd_x, gd_y;
14939   int gadget_id;
14940   char *infotext;
14941 } gamebutton_info[NUM_GAME_BUTTONS] =
14942 {
14943 #if 1
14944   {
14945     &game.button.stop.x,        &game.button.stop.y,
14946     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
14947     GAME_CTRL_ID_STOP,
14948     "stop game"
14949   },
14950   {
14951     &game.button.pause.x,       &game.button.pause.y,
14952     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
14953     GAME_CTRL_ID_PAUSE,
14954     "pause game"
14955   },
14956   {
14957     &game.button.play.x,        &game.button.play.y,
14958     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
14959     GAME_CTRL_ID_PLAY,
14960     "play game"
14961   },
14962   {
14963     &game.button.sound_music.x, &game.button.sound_music.y,
14964     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
14965     SOUND_CTRL_ID_MUSIC,
14966     "background music on/off"
14967   },
14968   {
14969     &game.button.sound_loops.x, &game.button.sound_loops.y,
14970     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
14971     SOUND_CTRL_ID_LOOPS,
14972     "sound loops on/off"
14973   },
14974   {
14975     &game.button.sound_simple.x,&game.button.sound_simple.y,
14976     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
14977     SOUND_CTRL_ID_SIMPLE,
14978     "normal sounds on/off"
14979   }
14980 #else
14981   {
14982     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
14983     GAME_CTRL_ID_STOP,
14984     "stop game"
14985   },
14986   {
14987     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
14988     GAME_CTRL_ID_PAUSE,
14989     "pause game"
14990   },
14991   {
14992     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
14993     GAME_CTRL_ID_PLAY,
14994     "play game"
14995   },
14996   {
14997     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
14998     SOUND_CTRL_ID_MUSIC,
14999     "background music on/off"
15000   },
15001   {
15002     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15003     SOUND_CTRL_ID_LOOPS,
15004     "sound loops on/off"
15005   },
15006   {
15007     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15008     SOUND_CTRL_ID_SIMPLE,
15009     "normal sounds on/off"
15010   }
15011 #endif
15012 };
15013
15014 void CreateGameButtons()
15015 {
15016   int i;
15017
15018   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15019   {
15020     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15021     struct GadgetInfo *gi;
15022     int button_type;
15023     boolean checked;
15024     unsigned long event_mask;
15025     int x, y;
15026     int gd_xoffset, gd_yoffset;
15027     int gd_x1, gd_x2, gd_y1, gd_y2;
15028     int id = i;
15029
15030     x = DX + *gamebutton_info[i].x;
15031     y = DY + *gamebutton_info[i].y;
15032     gd_xoffset = gamebutton_info[i].gd_x;
15033     gd_yoffset = gamebutton_info[i].gd_y;
15034     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15035     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15036
15037     if (id == GAME_CTRL_ID_STOP ||
15038         id == GAME_CTRL_ID_PAUSE ||
15039         id == GAME_CTRL_ID_PLAY)
15040     {
15041       button_type = GD_TYPE_NORMAL_BUTTON;
15042       checked = FALSE;
15043       event_mask = GD_EVENT_RELEASED;
15044       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15045       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15046     }
15047     else
15048     {
15049       button_type = GD_TYPE_CHECK_BUTTON;
15050       checked =
15051         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15052          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15053          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15054       event_mask = GD_EVENT_PRESSED;
15055       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
15056       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15057     }
15058
15059     gi = CreateGadget(GDI_CUSTOM_ID, id,
15060                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15061 #if 1
15062                       GDI_X, x,
15063                       GDI_Y, y,
15064 #else
15065                       GDI_X, DX + gd_xoffset,
15066                       GDI_Y, DY + gd_yoffset,
15067 #endif
15068                       GDI_WIDTH, GAME_BUTTON_XSIZE,
15069                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
15070                       GDI_TYPE, button_type,
15071                       GDI_STATE, GD_BUTTON_UNPRESSED,
15072                       GDI_CHECKED, checked,
15073                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15074                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15075                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15076                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15077                       GDI_EVENT_MASK, event_mask,
15078                       GDI_CALLBACK_ACTION, HandleGameButtons,
15079                       GDI_END);
15080
15081     if (gi == NULL)
15082       Error(ERR_EXIT, "cannot create gadget");
15083
15084     game_gadget[id] = gi;
15085   }
15086 }
15087
15088 void FreeGameButtons()
15089 {
15090   int i;
15091
15092   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15093     FreeGadget(game_gadget[i]);
15094 }
15095
15096 static void MapGameButtons()
15097 {
15098   int i;
15099
15100   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15101     MapGadget(game_gadget[i]);
15102 }
15103
15104 void UnmapGameButtons()
15105 {
15106   int i;
15107
15108   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15109     UnmapGadget(game_gadget[i]);
15110 }
15111
15112 static void HandleGameButtons(struct GadgetInfo *gi)
15113 {
15114   int id = gi->custom_id;
15115
15116   if (game_status != GAME_MODE_PLAYING)
15117     return;
15118
15119   switch (id)
15120   {
15121     case GAME_CTRL_ID_STOP:
15122       if (tape.playing)
15123         TapeStop();
15124       else
15125         RequestQuitGame(TRUE);
15126       break;
15127
15128     case GAME_CTRL_ID_PAUSE:
15129       if (options.network)
15130       {
15131 #if defined(NETWORK_AVALIABLE)
15132         if (tape.pausing)
15133           SendToServer_ContinuePlaying();
15134         else
15135           SendToServer_PausePlaying();
15136 #endif
15137       }
15138       else
15139         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15140       break;
15141
15142     case GAME_CTRL_ID_PLAY:
15143       if (tape.pausing)
15144       {
15145 #if defined(NETWORK_AVALIABLE)
15146         if (options.network)
15147           SendToServer_ContinuePlaying();
15148         else
15149 #endif
15150         {
15151           tape.pausing = FALSE;
15152           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15153         }
15154       }
15155       break;
15156
15157     case SOUND_CTRL_ID_MUSIC:
15158       if (setup.sound_music)
15159       { 
15160         setup.sound_music = FALSE;
15161         FadeMusic();
15162       }
15163       else if (audio.music_available)
15164       { 
15165         setup.sound = setup.sound_music = TRUE;
15166
15167         SetAudioMode(setup.sound);
15168
15169         PlayLevelMusic();
15170       }
15171       break;
15172
15173     case SOUND_CTRL_ID_LOOPS:
15174       if (setup.sound_loops)
15175         setup.sound_loops = FALSE;
15176       else if (audio.loops_available)
15177       {
15178         setup.sound = setup.sound_loops = TRUE;
15179         SetAudioMode(setup.sound);
15180       }
15181       break;
15182
15183     case SOUND_CTRL_ID_SIMPLE:
15184       if (setup.sound_simple)
15185         setup.sound_simple = FALSE;
15186       else if (audio.sound_available)
15187       {
15188         setup.sound = setup.sound_simple = TRUE;
15189         SetAudioMode(setup.sound);
15190       }
15191       break;
15192
15193     default:
15194       break;
15195   }
15196 }