rnd-20070330-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61
62 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
63
64
65 /* for DigField() */
66 #define DF_NO_PUSH              0
67 #define DF_DIG                  1
68 #define DF_SNAP                 2
69
70 /* for MovePlayer() */
71 #define MP_NO_ACTION            0
72 #define MP_MOVING               1
73 #define MP_ACTION               2
74 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
75
76 /* for ScrollPlayer() */
77 #define SCROLL_INIT             0
78 #define SCROLL_GO_ON            1
79
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START          0
82 #define EX_TYPE_NONE            0
83 #define EX_TYPE_NORMAL          (1 << 0)
84 #define EX_TYPE_CENTER          (1 << 1)
85 #define EX_TYPE_BORDER          (1 << 2)
86 #define EX_TYPE_CROSS           (1 << 3)
87 #define EX_TYPE_DYNA            (1 << 4)
88 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
89
90 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
91 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
92 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
93 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
94
95 /* special positions in the game control window (relative to control window) */
96 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
97 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
98 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
99 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
100 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
101 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
102 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
103 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
104 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
105 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
106 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
107 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
108 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
109 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
110 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
111 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
112
113 /* special positions in the game control window (relative to main window) */
114 #define DX_LEVEL1               (DX + XX_LEVEL1)
115 #define DX_LEVEL2               (DX + XX_LEVEL2)
116 #define DX_LEVEL                (DX + XX_LEVEL)
117 #define DY_LEVEL                (DY + YY_LEVEL)
118 #define DX_EMERALDS             (DX + XX_EMERALDS)
119 #define DY_EMERALDS             (DY + YY_EMERALDS)
120 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
121 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
122 #define DX_KEYS                 (DX + XX_KEYS)
123 #define DY_KEYS                 (DY + YY_KEYS)
124 #define DX_SCORE                (DX + XX_SCORE)
125 #define DY_SCORE                (DY + YY_SCORE)
126 #define DX_TIME1                (DX + XX_TIME1)
127 #define DX_TIME2                (DX + XX_TIME2)
128 #define DX_TIME                 (DX + XX_TIME)
129 #define DY_TIME                 (DY + YY_TIME)
130
131 #if 1
132 /* game panel display and control definitions */
133
134 #define GAME_CONTROL_LEVEL_NUMBER               0
135 #define GAME_CONTROL_GEMS                       1
136 #define GAME_CONTROL_INVENTORY                  2
137 #define GAME_CONTROL_KEY_1                      3
138 #define GAME_CONTROL_KEY_2                      4
139 #define GAME_CONTROL_KEY_3                      5
140 #define GAME_CONTROL_KEY_4                      6
141 #define GAME_CONTROL_KEY_5                      7
142 #define GAME_CONTROL_KEY_6                      8
143 #define GAME_CONTROL_KEY_7                      9
144 #define GAME_CONTROL_KEY_8                      10
145 #define GAME_CONTROL_KEY_WHITE                  11
146 #define GAME_CONTROL_KEY_WHITE_COUNT            12
147 #define GAME_CONTROL_SCORE                      13
148 #define GAME_CONTROL_TIME                       14
149 #define GAME_CONTROL_TIME_HH                    15
150 #define GAME_CONTROL_TIME_MM                    16
151 #define GAME_CONTROL_TIME_SS                    17
152 #define GAME_CONTROL_DROP_NEXT_1                18
153 #define GAME_CONTROL_DROP_NEXT_2                19
154 #define GAME_CONTROL_DROP_NEXT_3                20
155 #define GAME_CONTROL_DROP_NEXT_4                21
156 #define GAME_CONTROL_DROP_NEXT_5                22
157 #define GAME_CONTROL_DROP_NEXT_6                23
158 #define GAME_CONTROL_DROP_NEXT_7                24
159 #define GAME_CONTROL_DROP_NEXT_8                25
160 #define GAME_CONTROL_SHIELD_NORMAL              26
161 #define GAME_CONTROL_SHIELD_NORMAL_TIME         27
162 #define GAME_CONTROL_SHIELD_DEADLY              28
163 #define GAME_CONTROL_SHIELD_DEADLY_TIME         29
164 #define GAME_CONTROL_EXIT                       30
165 #define GAME_CONTROL_EM_EXIT                    31
166 #define GAME_CONTROL_SP_EXIT                    32
167 #define GAME_CONTROL_STEEL_EXIT                 33
168 #define GAME_CONTROL_EM_STEEL_EXIT              34
169 #define GAME_CONTROL_EMC_MAGIC_BALL             35
170 #define GAME_CONTROL_EMC_MAGIC_BALL_SWITCH      36
171 #define GAME_CONTROL_LIGHT_SWITCH               37
172 #define GAME_CONTROL_LIGHT_SWITCH_TIME          38
173 #define GAME_CONTROL_TIMEGATE_SWITCH            39
174 #define GAME_CONTROL_TIMEGATE_SWITCH_TIME       40
175 #define GAME_CONTROL_SWITCHGATE_SWITCH          41
176 #define GAME_CONTROL_EMC_LENSES                 42
177 #define GAME_CONTROL_EMC_LENSES_TIME            43
178 #define GAME_CONTROL_EMC_MAGNIFIER              44
179 #define GAME_CONTROL_EMC_MAGNIFIER_TIME         45
180 #define GAME_CONTROL_BALLOON_SWITCH             46
181 #define GAME_CONTROL_DYNABOMB_NUMBER            47
182 #define GAME_CONTROL_DYNABOMB_SIZE              48
183 #define GAME_CONTROL_DYNABOMB_POWER             49
184 #define GAME_CONTROL_PENGUINS                   50
185 #define GAME_CONTROL_SOKOBAN_OBJECTS            51
186 #define GAME_CONTROL_SOKOBAN_FIELDS             52
187 #define GAME_CONTROL_ROBOT_WHEEL                53
188 #define GAME_CONTROL_CONVEYOR_BELT_1            54
189 #define GAME_CONTROL_CONVEYOR_BELT_1_SWITCH     55
190 #define GAME_CONTROL_CONVEYOR_BELT_2            56
191 #define GAME_CONTROL_CONVEYOR_BELT_2_SWITCH     57
192 #define GAME_CONTROL_CONVEYOR_BELT_3            58
193 #define GAME_CONTROL_CONVEYOR_BELT_3_SWITCH     59
194 #define GAME_CONTROL_CONVEYOR_BELT_4            60
195 #define GAME_CONTROL_CONVEYOR_BELT_4_SWITCH     61
196 #define GAME_CONTROL_MAGIC_WALL                 62
197 #define GAME_CONTROL_MAGIC_WALL_TIME            63
198 #define GAME_CONTROL_BD_MAGIC_WALL              64
199 #define GAME_CONTROL_DC_MAGIC_WALL              65
200 #define GAME_CONTROL_PLAYER_NAME                66
201 #define GAME_CONTROL_LEVEL_NAME                 67
202 #define GAME_CONTROL_LEVEL_AUTHOR               68
203
204 #define NUM_GAME_CONTROLS                       69
205
206 int game_control_value[NUM_GAME_CONTROLS];
207 int last_game_control_value[NUM_GAME_CONTROLS];
208
209 struct GameControlInfo
210 {
211   int nr;
212
213   struct TextPosInfo *pos;
214   int type;
215 };
216
217 static struct GameControlInfo game_controls[] =
218 {
219   {
220     GAME_CONTROL_LEVEL_NUMBER,
221     &game.panel.level_number,
222     TYPE_INTEGER,
223   },
224   {
225     GAME_CONTROL_GEMS,
226     &game.panel.gems,
227     TYPE_INTEGER,
228   },
229   {
230     GAME_CONTROL_INVENTORY,
231     &game.panel.inventory,
232     TYPE_INTEGER,
233   },
234   {
235     GAME_CONTROL_KEY_1,
236     &game.panel.key[0],
237     TYPE_ELEMENT,
238   },
239   {
240     GAME_CONTROL_KEY_2,
241     &game.panel.key[1],
242     TYPE_ELEMENT,
243   },
244   {
245     GAME_CONTROL_KEY_3,
246     &game.panel.key[2],
247     TYPE_ELEMENT,
248   },
249   {
250     GAME_CONTROL_KEY_4,
251     &game.panel.key[3],
252     TYPE_ELEMENT,
253   },
254   {
255     GAME_CONTROL_KEY_5,
256     &game.panel.key[4],
257     TYPE_ELEMENT,
258   },
259   {
260     GAME_CONTROL_KEY_6,
261     &game.panel.key[5],
262     TYPE_ELEMENT,
263   },
264   {
265     GAME_CONTROL_KEY_7,
266     &game.panel.key[6],
267     TYPE_ELEMENT,
268   },
269   {
270     GAME_CONTROL_KEY_8,
271     &game.panel.key[7],
272     TYPE_ELEMENT,
273   },
274   {
275     GAME_CONTROL_KEY_WHITE,
276     &game.panel.key_white,
277     TYPE_ELEMENT,
278   },
279   {
280     GAME_CONTROL_KEY_WHITE_COUNT,
281     &game.panel.key_white_count,
282     TYPE_INTEGER,
283   },
284   {
285     GAME_CONTROL_SCORE,
286     &game.panel.score,
287     TYPE_INTEGER,
288   },
289   {
290     GAME_CONTROL_TIME,
291     &game.panel.time,
292     TYPE_INTEGER,
293   },
294   {
295     GAME_CONTROL_TIME_HH,
296     &game.panel.time_hh,
297     TYPE_INTEGER,
298   },
299   {
300     GAME_CONTROL_TIME_MM,
301     &game.panel.time_mm,
302     TYPE_INTEGER,
303   },
304   {
305     GAME_CONTROL_TIME_SS,
306     &game.panel.time_ss,
307     TYPE_INTEGER,
308   },
309   {
310     GAME_CONTROL_DROP_NEXT_1,
311     &game.panel.drop_next_1,
312     TYPE_ELEMENT,
313   },
314   {
315     GAME_CONTROL_DROP_NEXT_2,
316     &game.panel.drop_next_2,
317     TYPE_ELEMENT,
318   },
319   {
320     GAME_CONTROL_DROP_NEXT_3,
321     &game.panel.drop_next_3,
322     TYPE_ELEMENT,
323   },
324   {
325     GAME_CONTROL_DROP_NEXT_4,
326     &game.panel.drop_next_4,
327     TYPE_ELEMENT,
328   },
329   {
330     GAME_CONTROL_DROP_NEXT_5,
331     &game.panel.drop_next_5,
332     TYPE_ELEMENT,
333   },
334   {
335     GAME_CONTROL_DROP_NEXT_6,
336     &game.panel.drop_next_6,
337     TYPE_ELEMENT,
338   },
339   {
340     GAME_CONTROL_DROP_NEXT_7,
341     &game.panel.drop_next_7,
342     TYPE_ELEMENT,
343   },
344   {
345     GAME_CONTROL_DROP_NEXT_8,
346     &game.panel.drop_next_8,
347     TYPE_ELEMENT,
348   },
349   {
350     GAME_CONTROL_SHIELD_NORMAL,
351     &game.panel.shield_normal,
352     TYPE_ELEMENT,
353   },
354   {
355     GAME_CONTROL_SHIELD_NORMAL_TIME,
356     &game.panel.shield_normal_time,
357     TYPE_INTEGER,
358   },
359   {
360     GAME_CONTROL_SHIELD_DEADLY,
361     &game.panel.shield_deadly,
362     TYPE_ELEMENT,
363   },
364   {
365     GAME_CONTROL_SHIELD_DEADLY_TIME,
366     &game.panel.shield_deadly_time,
367     TYPE_INTEGER,
368   },
369   {
370     GAME_CONTROL_EXIT,
371     &game.panel.exit,
372     TYPE_ELEMENT,
373   },
374   {
375     GAME_CONTROL_EM_EXIT,
376     &game.panel.em_exit,
377     TYPE_ELEMENT,
378   },
379   {
380     GAME_CONTROL_SP_EXIT,
381     &game.panel.sp_exit,
382     TYPE_ELEMENT,
383   },
384   {
385     GAME_CONTROL_STEEL_EXIT,
386     &game.panel.steel_exit,
387     TYPE_ELEMENT,
388   },
389   {
390     GAME_CONTROL_EM_STEEL_EXIT,
391     &game.panel.em_steel_exit,
392     TYPE_ELEMENT,
393   },
394   {
395     GAME_CONTROL_EMC_MAGIC_BALL,
396     &game.panel.emc_magic_ball,
397     TYPE_ELEMENT,
398   },
399   {
400     GAME_CONTROL_EMC_MAGIC_BALL_SWITCH,
401     &game.panel.emc_magic_ball_switch,
402     TYPE_ELEMENT,
403   },
404   {
405     GAME_CONTROL_LIGHT_SWITCH,
406     &game.panel.light_switch,
407     TYPE_ELEMENT,
408   },
409   {
410     GAME_CONTROL_LIGHT_SWITCH_TIME,
411     &game.panel.light_switch_time,
412     TYPE_INTEGER,
413   },
414   {
415     GAME_CONTROL_TIMEGATE_SWITCH,
416     &game.panel.timegate_switch,
417     TYPE_ELEMENT,
418   },
419   {
420     GAME_CONTROL_TIMEGATE_SWITCH_TIME,
421     &game.panel.timegate_switch_time,
422     TYPE_INTEGER,
423   },
424   {
425     GAME_CONTROL_SWITCHGATE_SWITCH,
426     &game.panel.switchgate_switch,
427     TYPE_ELEMENT,
428   },
429   {
430     GAME_CONTROL_EMC_LENSES,
431     &game.panel.emc_lenses,
432     TYPE_ELEMENT,
433   },
434   {
435     GAME_CONTROL_EMC_LENSES_TIME,
436     &game.panel.emc_lenses_time,
437     TYPE_INTEGER,
438   },
439   {
440     GAME_CONTROL_EMC_MAGNIFIER,
441     &game.panel.emc_magnifier,
442     TYPE_ELEMENT,
443   },
444   {
445     GAME_CONTROL_EMC_MAGNIFIER_TIME,
446     &game.panel.emc_magnifier_time,
447     TYPE_INTEGER,
448   },
449   {
450     GAME_CONTROL_BALLOON_SWITCH,
451     &game.panel.balloon_switch,
452     TYPE_ELEMENT,
453   },
454   {
455     GAME_CONTROL_DYNABOMB_NUMBER,
456     &game.panel.dynabomb_number,
457     TYPE_INTEGER,
458   },
459   {
460     GAME_CONTROL_DYNABOMB_SIZE,
461     &game.panel.dynabomb_size,
462     TYPE_INTEGER,
463   },
464   {
465     GAME_CONTROL_DYNABOMB_POWER,
466     &game.panel.dynabomb_power,
467     TYPE_ELEMENT,
468   },
469   {
470     GAME_CONTROL_PENGUINS,
471     &game.panel.penguins,
472     TYPE_INTEGER,
473   },
474   {
475     GAME_CONTROL_SOKOBAN_OBJECTS,
476     &game.panel.sokoban_objects,
477     TYPE_INTEGER,
478   },
479   {
480     GAME_CONTROL_SOKOBAN_FIELDS,
481     &game.panel.sokoban_fields,
482     TYPE_INTEGER,
483   },
484   {
485     GAME_CONTROL_ROBOT_WHEEL,
486     &game.panel.robot_wheel,
487     TYPE_ELEMENT,
488   },
489   {
490     GAME_CONTROL_CONVEYOR_BELT_1,
491     &game.panel.conveyor_belt_1,
492     TYPE_ELEMENT,
493   },
494   {
495     GAME_CONTROL_CONVEYOR_BELT_1_SWITCH,
496     &game.panel.conveyor_belt_1_switch,
497     TYPE_ELEMENT,
498   },
499   {
500     GAME_CONTROL_CONVEYOR_BELT_2,
501     &game.panel.conveyor_belt_2,
502     TYPE_ELEMENT,
503   },
504   {
505     GAME_CONTROL_CONVEYOR_BELT_2_SWITCH,
506     &game.panel.conveyor_belt_2_switch,
507     TYPE_ELEMENT,
508   },
509   {
510     GAME_CONTROL_CONVEYOR_BELT_3,
511     &game.panel.conveyor_belt_3,
512     TYPE_ELEMENT,
513   },
514   {
515     GAME_CONTROL_CONVEYOR_BELT_3_SWITCH,
516     &game.panel.conveyor_belt_3_switch,
517     TYPE_ELEMENT,
518   },
519   {
520     GAME_CONTROL_CONVEYOR_BELT_4,
521     &game.panel.conveyor_belt_4,
522     TYPE_ELEMENT,
523   },
524   {
525     GAME_CONTROL_CONVEYOR_BELT_4_SWITCH,
526     &game.panel.conveyor_belt_4_switch,
527     TYPE_ELEMENT,
528   },
529   {
530     GAME_CONTROL_MAGIC_WALL,
531     &game.panel.magic_wall,
532     TYPE_ELEMENT,
533   },
534   {
535     GAME_CONTROL_MAGIC_WALL_TIME,
536     &game.panel.magic_wall_time,
537     TYPE_INTEGER,
538   },
539   {
540     GAME_CONTROL_BD_MAGIC_WALL,
541     &game.panel.bd_magic_wall,
542     TYPE_ELEMENT,
543   },
544   {
545     GAME_CONTROL_DC_MAGIC_WALL,
546     &game.panel.dc_magic_wall,
547     TYPE_ELEMENT,
548   },
549   {
550     GAME_CONTROL_PLAYER_NAME,
551     &game.panel.player_name,
552     TYPE_STRING,
553   },
554   {
555     GAME_CONTROL_LEVEL_NAME,
556     &game.panel.level_name,
557     TYPE_STRING,
558   },
559   {
560     GAME_CONTROL_LEVEL_AUTHOR,
561     &game.panel.level_author,
562     TYPE_STRING,
563   },
564
565   {
566     -1,
567     NULL,
568     -1,
569   }
570 };
571 #endif
572
573
574 /* values for delayed check of falling and moving elements and for collision */
575 #define CHECK_DELAY_MOVING      3
576 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
577 #define CHECK_DELAY_COLLISION   2
578 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
579
580 /* values for initial player move delay (initial delay counter value) */
581 #define INITIAL_MOVE_DELAY_OFF  -1
582 #define INITIAL_MOVE_DELAY_ON   0
583
584 /* values for player movement speed (which is in fact a delay value) */
585 #define MOVE_DELAY_MIN_SPEED    32
586 #define MOVE_DELAY_NORMAL_SPEED 8
587 #define MOVE_DELAY_HIGH_SPEED   4
588 #define MOVE_DELAY_MAX_SPEED    1
589
590 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
591 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
592
593 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
594 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
595
596 /* values for other actions */
597 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
598 #define MOVE_STEPSIZE_MIN       (1)
599 #define MOVE_STEPSIZE_MAX       (TILEX)
600
601 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
602 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
603
604 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
605
606 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
607                                  RND(element_info[e].push_delay_random))
608 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
609                                  RND(element_info[e].drop_delay_random))
610 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
611                                  RND(element_info[e].move_delay_random))
612 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
613                                     (element_info[e].move_delay_random))
614 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
615                                  RND(element_info[e].ce_value_random_initial))
616 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
617 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
618                                  RND((c)->delay_random * (c)->delay_frames))
619 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
620                                  RND((c)->delay_random))
621
622
623 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
624          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
625
626 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
627         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
628          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
629          (be) + (e) - EL_SELF)
630
631 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
632         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
633          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
634          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
635          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
636          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
637          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
638          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
639          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
640          (e))
641
642 #define CAN_GROW_INTO(e)                                                \
643         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
644
645 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
646                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
647                                         (condition)))
648
649 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
650                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
651                                         (CAN_MOVE_INTO_ACID(e) &&       \
652                                          Feld[x][y] == EL_ACID) ||      \
653                                         (condition)))
654
655 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
656                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
657                                         (CAN_MOVE_INTO_ACID(e) &&       \
658                                          Feld[x][y] == EL_ACID) ||      \
659                                         (condition)))
660
661 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
662                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
663                                         (condition) ||                  \
664                                         (CAN_MOVE_INTO_ACID(e) &&       \
665                                          Feld[x][y] == EL_ACID) ||      \
666                                         (DONT_COLLIDE_WITH(e) &&        \
667                                          IS_PLAYER(x, y) &&             \
668                                          !PLAYER_ENEMY_PROTECTED(x, y))))
669
670 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
671         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
672
673 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
674         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
675
676 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
677         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
678
679 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
680         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
681                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
682
683 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
684         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
685
686 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
687         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
688
689 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
690         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
691
692 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
693         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
694
695 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
696         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
697
698 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
699         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
700                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
701                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
702                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
703                                                  IS_FOOD_PENGUIN(Feld[x][y])))
704 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
705         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
706
707 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
708         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
709
710 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
711         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
712
713 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
714         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
715                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
716
717 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
718
719 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
720                 (!IS_PLAYER(x, y) &&                                    \
721                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
722
723 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
724         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
725
726 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
727 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
728
729 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
730 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
731 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
732 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
733
734 /* game button identifiers */
735 #define GAME_CTRL_ID_STOP               0
736 #define GAME_CTRL_ID_PAUSE              1
737 #define GAME_CTRL_ID_PLAY               2
738 #define SOUND_CTRL_ID_MUSIC             3
739 #define SOUND_CTRL_ID_LOOPS             4
740 #define SOUND_CTRL_ID_SIMPLE            5
741
742 #define NUM_GAME_BUTTONS                6
743
744
745 /* forward declaration for internal use */
746
747 static void CreateField(int, int, int);
748
749 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
750 static void AdvanceFrameAndPlayerCounters(int);
751
752 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
753 static boolean MovePlayer(struct PlayerInfo *, int, int);
754 static void ScrollPlayer(struct PlayerInfo *, int);
755 static void ScrollScreen(struct PlayerInfo *, int);
756
757 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
758
759 static void InitBeltMovement(void);
760 static void CloseAllOpenTimegates(void);
761 static void CheckGravityMovement(struct PlayerInfo *);
762 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
763 static void KillPlayerUnlessEnemyProtected(int, int);
764 static void KillPlayerUnlessExplosionProtected(int, int);
765
766 static void TestIfPlayerTouchesCustomElement(int, int);
767 static void TestIfElementTouchesCustomElement(int, int);
768 static void TestIfElementHitsCustomElement(int, int, int);
769 #if 0
770 static void TestIfElementSmashesCustomElement(int, int, int);
771 #endif
772
773 static void HandleElementChange(int, int, int);
774 static void ExecuteCustomElementAction(int, int, int, int);
775 static boolean ChangeElement(int, int, int, int);
776
777 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
778 #define CheckTriggeredElementChange(x, y, e, ev)                        \
779         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
780 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
781         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
782 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
783         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
784 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
785         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
786
787 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
788 #define CheckElementChange(x, y, e, te, ev)                             \
789         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
790 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
791         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
792 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
793         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
794
795 static void PlayLevelSound(int, int, int);
796 static void PlayLevelSoundNearest(int, int, int);
797 static void PlayLevelSoundAction(int, int, int);
798 static void PlayLevelSoundElementAction(int, int, int, int);
799 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
800 static void PlayLevelSoundActionIfLoop(int, int, int);
801 static void StopLevelSoundActionIfLoop(int, int, int);
802 static void PlayLevelMusic();
803
804 static void MapGameButtons();
805 static void HandleGameButtons(struct GadgetInfo *);
806
807 int AmoebeNachbarNr(int, int);
808 void AmoebeUmwandeln(int, int);
809 void ContinueMoving(int, int);
810 void Bang(int, int);
811 void InitMovDir(int, int);
812 void InitAmoebaNr(int, int);
813 int NewHiScore(void);
814
815 void TestIfGoodThingHitsBadThing(int, int, int);
816 void TestIfBadThingHitsGoodThing(int, int, int);
817 void TestIfPlayerTouchesBadThing(int, int);
818 void TestIfPlayerRunsIntoBadThing(int, int, int);
819 void TestIfBadThingTouchesPlayer(int, int);
820 void TestIfBadThingRunsIntoPlayer(int, int, int);
821 void TestIfFriendTouchesBadThing(int, int);
822 void TestIfBadThingTouchesFriend(int, int);
823 void TestIfBadThingTouchesOtherBadThing(int, int);
824
825 void KillPlayer(struct PlayerInfo *);
826 void BuryPlayer(struct PlayerInfo *);
827 void RemovePlayer(struct PlayerInfo *);
828
829 boolean SnapField(struct PlayerInfo *, int, int);
830 boolean DropElement(struct PlayerInfo *);
831
832 static int getInvisibleActiveFromInvisibleElement(int);
833 static int getInvisibleFromInvisibleActiveElement(int);
834
835 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
836
837 /* for detection of endless loops, caused by custom element programming */
838 /* (using maximal playfield width x 10 is just a rough approximation) */
839 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
840
841 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
842 {                                                                       \
843   if (recursion_loop_detected)                                          \
844     return (rc);                                                        \
845                                                                         \
846   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
847   {                                                                     \
848     recursion_loop_detected = TRUE;                                     \
849     recursion_loop_element = (e);                                       \
850   }                                                                     \
851                                                                         \
852   recursion_loop_depth++;                                               \
853 }
854
855 #define RECURSION_LOOP_DETECTION_END()                                  \
856 {                                                                       \
857   recursion_loop_depth--;                                               \
858 }
859
860 static int recursion_loop_depth;
861 static boolean recursion_loop_detected;
862 static boolean recursion_loop_element;
863
864
865 /* ------------------------------------------------------------------------- */
866 /* definition of elements that automatically change to other elements after  */
867 /* a specified time, eventually calling a function when changing             */
868 /* ------------------------------------------------------------------------- */
869
870 /* forward declaration for changer functions */
871 static void InitBuggyBase(int, int);
872 static void WarnBuggyBase(int, int);
873
874 static void InitTrap(int, int);
875 static void ActivateTrap(int, int);
876 static void ChangeActiveTrap(int, int);
877
878 static void InitRobotWheel(int, int);
879 static void RunRobotWheel(int, int);
880 static void StopRobotWheel(int, int);
881
882 static void InitTimegateWheel(int, int);
883 static void RunTimegateWheel(int, int);
884
885 static void InitMagicBallDelay(int, int);
886 static void ActivateMagicBall(int, int);
887
888 struct ChangingElementInfo
889 {
890   int element;
891   int target_element;
892   int change_delay;
893   void (*pre_change_function)(int x, int y);
894   void (*change_function)(int x, int y);
895   void (*post_change_function)(int x, int y);
896 };
897
898 static struct ChangingElementInfo change_delay_list[] =
899 {
900   {
901     EL_NUT_BREAKING,
902     EL_EMERALD,
903     6,
904     NULL,
905     NULL,
906     NULL
907   },
908   {
909     EL_PEARL_BREAKING,
910     EL_EMPTY,
911     8,
912     NULL,
913     NULL,
914     NULL
915   },
916   {
917     EL_EXIT_OPENING,
918     EL_EXIT_OPEN,
919     29,
920     NULL,
921     NULL,
922     NULL
923   },
924   {
925     EL_EXIT_CLOSING,
926     EL_EXIT_CLOSED,
927     29,
928     NULL,
929     NULL,
930     NULL
931   },
932   {
933     EL_STEEL_EXIT_OPENING,
934     EL_STEEL_EXIT_OPEN,
935     29,
936     NULL,
937     NULL,
938     NULL
939   },
940   {
941     EL_STEEL_EXIT_CLOSING,
942     EL_STEEL_EXIT_CLOSED,
943     29,
944     NULL,
945     NULL,
946     NULL
947   },
948   {
949     EL_EM_EXIT_OPENING,
950     EL_EM_EXIT_OPEN,
951     29,
952     NULL,
953     NULL,
954     NULL
955   },
956   {
957     EL_EM_EXIT_CLOSING,
958 #if 1
959     EL_EMPTY,
960 #else
961     EL_EM_EXIT_CLOSED,
962 #endif
963     29,
964     NULL,
965     NULL,
966     NULL
967   },
968   {
969     EL_EM_STEEL_EXIT_OPENING,
970     EL_EM_STEEL_EXIT_OPEN,
971     29,
972     NULL,
973     NULL,
974     NULL
975   },
976   {
977     EL_EM_STEEL_EXIT_CLOSING,
978 #if 1
979     EL_STEELWALL,
980 #else
981     EL_EM_STEEL_EXIT_CLOSED,
982 #endif
983     29,
984     NULL,
985     NULL,
986     NULL
987   },
988   {
989     EL_SP_EXIT_OPENING,
990     EL_SP_EXIT_OPEN,
991     29,
992     NULL,
993     NULL,
994     NULL
995   },
996   {
997     EL_SP_EXIT_CLOSING,
998     EL_SP_EXIT_CLOSED,
999     29,
1000     NULL,
1001     NULL,
1002     NULL
1003   },
1004   {
1005     EL_SWITCHGATE_OPENING,
1006     EL_SWITCHGATE_OPEN,
1007     29,
1008     NULL,
1009     NULL,
1010     NULL
1011   },
1012   {
1013     EL_SWITCHGATE_CLOSING,
1014     EL_SWITCHGATE_CLOSED,
1015     29,
1016     NULL,
1017     NULL,
1018     NULL
1019   },
1020   {
1021     EL_TIMEGATE_OPENING,
1022     EL_TIMEGATE_OPEN,
1023     29,
1024     NULL,
1025     NULL,
1026     NULL
1027   },
1028   {
1029     EL_TIMEGATE_CLOSING,
1030     EL_TIMEGATE_CLOSED,
1031     29,
1032     NULL,
1033     NULL,
1034     NULL
1035   },
1036
1037   {
1038     EL_ACID_SPLASH_LEFT,
1039     EL_EMPTY,
1040     8,
1041     NULL,
1042     NULL,
1043     NULL
1044   },
1045   {
1046     EL_ACID_SPLASH_RIGHT,
1047     EL_EMPTY,
1048     8,
1049     NULL,
1050     NULL,
1051     NULL
1052   },
1053   {
1054     EL_SP_BUGGY_BASE,
1055     EL_SP_BUGGY_BASE_ACTIVATING,
1056     0,
1057     InitBuggyBase,
1058     NULL,
1059     NULL
1060   },
1061   {
1062     EL_SP_BUGGY_BASE_ACTIVATING,
1063     EL_SP_BUGGY_BASE_ACTIVE,
1064     0,
1065     InitBuggyBase,
1066     NULL,
1067     NULL
1068   },
1069   {
1070     EL_SP_BUGGY_BASE_ACTIVE,
1071     EL_SP_BUGGY_BASE,
1072     0,
1073     InitBuggyBase,
1074     WarnBuggyBase,
1075     NULL
1076   },
1077   {
1078     EL_TRAP,
1079     EL_TRAP_ACTIVE,
1080     0,
1081     InitTrap,
1082     NULL,
1083     ActivateTrap
1084   },
1085   {
1086     EL_TRAP_ACTIVE,
1087     EL_TRAP,
1088     31,
1089     NULL,
1090     ChangeActiveTrap,
1091     NULL
1092   },
1093   {
1094     EL_ROBOT_WHEEL_ACTIVE,
1095     EL_ROBOT_WHEEL,
1096     0,
1097     InitRobotWheel,
1098     RunRobotWheel,
1099     StopRobotWheel
1100   },
1101   {
1102     EL_TIMEGATE_SWITCH_ACTIVE,
1103     EL_TIMEGATE_SWITCH,
1104     0,
1105     InitTimegateWheel,
1106     RunTimegateWheel,
1107     NULL
1108   },
1109   {
1110     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1111     EL_DC_TIMEGATE_SWITCH,
1112     0,
1113     InitTimegateWheel,
1114     RunTimegateWheel,
1115     NULL
1116   },
1117   {
1118     EL_EMC_MAGIC_BALL_ACTIVE,
1119     EL_EMC_MAGIC_BALL_ACTIVE,
1120     0,
1121     InitMagicBallDelay,
1122     NULL,
1123     ActivateMagicBall
1124   },
1125   {
1126     EL_EMC_SPRING_BUMPER_ACTIVE,
1127     EL_EMC_SPRING_BUMPER,
1128     8,
1129     NULL,
1130     NULL,
1131     NULL
1132   },
1133   {
1134     EL_DIAGONAL_SHRINKING,
1135     EL_UNDEFINED,
1136     0,
1137     NULL,
1138     NULL,
1139     NULL
1140   },
1141   {
1142     EL_DIAGONAL_GROWING,
1143     EL_UNDEFINED,
1144     0,
1145     NULL,
1146     NULL,
1147     NULL,
1148   },
1149
1150   {
1151     EL_UNDEFINED,
1152     EL_UNDEFINED,
1153     -1,
1154     NULL,
1155     NULL,
1156     NULL
1157   }
1158 };
1159
1160 struct
1161 {
1162   int element;
1163   int push_delay_fixed, push_delay_random;
1164 }
1165 push_delay_list[] =
1166 {
1167   { EL_SPRING,                  0, 0 },
1168   { EL_BALLOON,                 0, 0 },
1169
1170   { EL_SOKOBAN_OBJECT,          2, 0 },
1171   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1172   { EL_SATELLITE,               2, 0 },
1173   { EL_SP_DISK_YELLOW,          2, 0 },
1174
1175   { EL_UNDEFINED,               0, 0 },
1176 };
1177
1178 struct
1179 {
1180   int element;
1181   int move_stepsize;
1182 }
1183 move_stepsize_list[] =
1184 {
1185   { EL_AMOEBA_DROP,             2 },
1186   { EL_AMOEBA_DROPPING,         2 },
1187   { EL_QUICKSAND_FILLING,       1 },
1188   { EL_QUICKSAND_EMPTYING,      1 },
1189   { EL_QUICKSAND_FAST_FILLING,  2 },
1190   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1191   { EL_MAGIC_WALL_FILLING,      2 },
1192   { EL_MAGIC_WALL_EMPTYING,     2 },
1193   { EL_BD_MAGIC_WALL_FILLING,   2 },
1194   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1195   { EL_DC_MAGIC_WALL_FILLING,   2 },
1196   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1197
1198   { EL_UNDEFINED,               0 },
1199 };
1200
1201 struct
1202 {
1203   int element;
1204   int count;
1205 }
1206 collect_count_list[] =
1207 {
1208   { EL_EMERALD,                 1 },
1209   { EL_BD_DIAMOND,              1 },
1210   { EL_EMERALD_YELLOW,          1 },
1211   { EL_EMERALD_RED,             1 },
1212   { EL_EMERALD_PURPLE,          1 },
1213   { EL_DIAMOND,                 3 },
1214   { EL_SP_INFOTRON,             1 },
1215   { EL_PEARL,                   5 },
1216   { EL_CRYSTAL,                 8 },
1217
1218   { EL_UNDEFINED,               0 },
1219 };
1220
1221 struct
1222 {
1223   int element;
1224   int direction;
1225 }
1226 access_direction_list[] =
1227 {
1228   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1229   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1230   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1231   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1232   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1233   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1234   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1235   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1236   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1237   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1238   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1239
1240   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1241   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1242   { EL_SP_PORT_UP,                                                   MV_DOWN },
1243   { EL_SP_PORT_DOWN,                                         MV_UP           },
1244   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1245   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1246   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1247   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1248   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1249   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1250   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1251   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1252   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1253   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1254   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1255   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1256   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1257   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1258   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1259
1260   { EL_UNDEFINED,                       MV_NONE                              }
1261 };
1262
1263 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1264
1265 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1266 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1267 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1268                                  IS_JUST_CHANGING(x, y))
1269
1270 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1271
1272 /* static variables for playfield scan mode (scanning forward or backward) */
1273 static int playfield_scan_start_x = 0;
1274 static int playfield_scan_start_y = 0;
1275 static int playfield_scan_delta_x = 1;
1276 static int playfield_scan_delta_y = 1;
1277
1278 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1279                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1280                                      (y) += playfield_scan_delta_y)     \
1281                                 for ((x) = playfield_scan_start_x;      \
1282                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1283                                      (x) += playfield_scan_delta_x)
1284
1285 #ifdef DEBUG
1286 void DEBUG_SetMaximumDynamite()
1287 {
1288   int i;
1289
1290   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1291     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1292       local_player->inventory_element[local_player->inventory_size++] =
1293         EL_DYNAMITE;
1294 }
1295 #endif
1296
1297 static void InitPlayfieldScanModeVars()
1298 {
1299   if (game.use_reverse_scan_direction)
1300   {
1301     playfield_scan_start_x = lev_fieldx - 1;
1302     playfield_scan_start_y = lev_fieldy - 1;
1303
1304     playfield_scan_delta_x = -1;
1305     playfield_scan_delta_y = -1;
1306   }
1307   else
1308   {
1309     playfield_scan_start_x = 0;
1310     playfield_scan_start_y = 0;
1311
1312     playfield_scan_delta_x = 1;
1313     playfield_scan_delta_y = 1;
1314   }
1315 }
1316
1317 static void InitPlayfieldScanMode(int mode)
1318 {
1319   game.use_reverse_scan_direction =
1320     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1321
1322   InitPlayfieldScanModeVars();
1323 }
1324
1325 static int get_move_delay_from_stepsize(int move_stepsize)
1326 {
1327   move_stepsize =
1328     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1329
1330   /* make sure that stepsize value is always a power of 2 */
1331   move_stepsize = (1 << log_2(move_stepsize));
1332
1333   return TILEX / move_stepsize;
1334 }
1335
1336 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1337                                boolean init_game)
1338 {
1339   int player_nr = player->index_nr;
1340   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1341   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1342
1343   /* do no immediately change move delay -- the player might just be moving */
1344   player->move_delay_value_next = move_delay;
1345
1346   /* information if player can move must be set separately */
1347   player->cannot_move = cannot_move;
1348
1349   if (init_game)
1350   {
1351     player->move_delay       = game.initial_move_delay[player_nr];
1352     player->move_delay_value = game.initial_move_delay_value[player_nr];
1353
1354     player->move_delay_value_next = -1;
1355
1356     player->move_delay_reset_counter = 0;
1357   }
1358 }
1359
1360 void GetPlayerConfig()
1361 {
1362   GameFrameDelay = setup.game_frame_delay;
1363
1364   if (!audio.sound_available)
1365     setup.sound_simple = FALSE;
1366
1367   if (!audio.loops_available)
1368     setup.sound_loops = FALSE;
1369
1370   if (!audio.music_available)
1371     setup.sound_music = FALSE;
1372
1373   if (!video.fullscreen_available)
1374     setup.fullscreen = FALSE;
1375
1376   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1377
1378   SetAudioMode(setup.sound);
1379   InitJoysticks();
1380 }
1381
1382 int GetElementFromGroupElement(int element)
1383 {
1384   if (IS_GROUP_ELEMENT(element))
1385   {
1386     struct ElementGroupInfo *group = element_info[element].group;
1387     int last_anim_random_frame = gfx.anim_random_frame;
1388     int element_pos;
1389
1390     if (group->choice_mode == ANIM_RANDOM)
1391       gfx.anim_random_frame = RND(group->num_elements_resolved);
1392
1393     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1394                                     group->choice_mode, 0,
1395                                     group->choice_pos);
1396
1397     if (group->choice_mode == ANIM_RANDOM)
1398       gfx.anim_random_frame = last_anim_random_frame;
1399
1400     group->choice_pos++;
1401
1402     element = group->element_resolved[element_pos];
1403   }
1404
1405   return element;
1406 }
1407
1408 static void InitPlayerField(int x, int y, int element, boolean init_game)
1409 {
1410   if (element == EL_SP_MURPHY)
1411   {
1412     if (init_game)
1413     {
1414       if (stored_player[0].present)
1415       {
1416         Feld[x][y] = EL_SP_MURPHY_CLONE;
1417
1418         return;
1419       }
1420       else
1421       {
1422         stored_player[0].use_murphy = TRUE;
1423
1424         if (!level.use_artwork_element[0])
1425           stored_player[0].artwork_element = EL_SP_MURPHY;
1426       }
1427
1428       Feld[x][y] = EL_PLAYER_1;
1429     }
1430   }
1431
1432   if (init_game)
1433   {
1434     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1435     int jx = player->jx, jy = player->jy;
1436
1437     player->present = TRUE;
1438
1439     player->block_last_field = (element == EL_SP_MURPHY ?
1440                                 level.sp_block_last_field :
1441                                 level.block_last_field);
1442
1443     /* ---------- initialize player's last field block delay --------------- */
1444
1445     /* always start with reliable default value (no adjustment needed) */
1446     player->block_delay_adjustment = 0;
1447
1448     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1449     if (player->block_last_field && element == EL_SP_MURPHY)
1450       player->block_delay_adjustment = 1;
1451
1452     /* special case 2: in game engines before 3.1.1, blocking was different */
1453     if (game.use_block_last_field_bug)
1454       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1455
1456     if (!options.network || player->connected)
1457     {
1458       player->active = TRUE;
1459
1460       /* remove potentially duplicate players */
1461       if (StorePlayer[jx][jy] == Feld[x][y])
1462         StorePlayer[jx][jy] = 0;
1463
1464       StorePlayer[x][y] = Feld[x][y];
1465
1466       if (options.debug)
1467       {
1468         printf("Player %d activated.\n", player->element_nr);
1469         printf("[Local player is %d and currently %s.]\n",
1470                local_player->element_nr,
1471                local_player->active ? "active" : "not active");
1472       }
1473     }
1474
1475     Feld[x][y] = EL_EMPTY;
1476
1477     player->jx = player->last_jx = x;
1478     player->jy = player->last_jy = y;
1479   }
1480 }
1481
1482 static void InitField(int x, int y, boolean init_game)
1483 {
1484   int element = Feld[x][y];
1485
1486   switch (element)
1487   {
1488     case EL_SP_MURPHY:
1489     case EL_PLAYER_1:
1490     case EL_PLAYER_2:
1491     case EL_PLAYER_3:
1492     case EL_PLAYER_4:
1493       InitPlayerField(x, y, element, init_game);
1494       break;
1495
1496     case EL_SOKOBAN_FIELD_PLAYER:
1497       element = Feld[x][y] = EL_PLAYER_1;
1498       InitField(x, y, init_game);
1499
1500       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1501       InitField(x, y, init_game);
1502       break;
1503
1504     case EL_SOKOBAN_FIELD_EMPTY:
1505       local_player->sokobanfields_still_needed++;
1506       break;
1507
1508     case EL_STONEBLOCK:
1509       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1510         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1511       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1512         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1513       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1514         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1515       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1516         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1517       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1518         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1519       break;
1520
1521     case EL_BUG:
1522     case EL_BUG_RIGHT:
1523     case EL_BUG_UP:
1524     case EL_BUG_LEFT:
1525     case EL_BUG_DOWN:
1526     case EL_SPACESHIP:
1527     case EL_SPACESHIP_RIGHT:
1528     case EL_SPACESHIP_UP:
1529     case EL_SPACESHIP_LEFT:
1530     case EL_SPACESHIP_DOWN:
1531     case EL_BD_BUTTERFLY:
1532     case EL_BD_BUTTERFLY_RIGHT:
1533     case EL_BD_BUTTERFLY_UP:
1534     case EL_BD_BUTTERFLY_LEFT:
1535     case EL_BD_BUTTERFLY_DOWN:
1536     case EL_BD_FIREFLY:
1537     case EL_BD_FIREFLY_RIGHT:
1538     case EL_BD_FIREFLY_UP:
1539     case EL_BD_FIREFLY_LEFT:
1540     case EL_BD_FIREFLY_DOWN:
1541     case EL_PACMAN_RIGHT:
1542     case EL_PACMAN_UP:
1543     case EL_PACMAN_LEFT:
1544     case EL_PACMAN_DOWN:
1545     case EL_YAMYAM:
1546     case EL_YAMYAM_LEFT:
1547     case EL_YAMYAM_RIGHT:
1548     case EL_YAMYAM_UP:
1549     case EL_YAMYAM_DOWN:
1550     case EL_DARK_YAMYAM:
1551     case EL_ROBOT:
1552     case EL_PACMAN:
1553     case EL_SP_SNIKSNAK:
1554     case EL_SP_ELECTRON:
1555     case EL_MOLE:
1556     case EL_MOLE_LEFT:
1557     case EL_MOLE_RIGHT:
1558     case EL_MOLE_UP:
1559     case EL_MOLE_DOWN:
1560       InitMovDir(x, y);
1561       break;
1562
1563     case EL_AMOEBA_FULL:
1564     case EL_BD_AMOEBA:
1565       InitAmoebaNr(x, y);
1566       break;
1567
1568     case EL_AMOEBA_DROP:
1569       if (y == lev_fieldy - 1)
1570       {
1571         Feld[x][y] = EL_AMOEBA_GROWING;
1572         Store[x][y] = EL_AMOEBA_WET;
1573       }
1574       break;
1575
1576     case EL_DYNAMITE_ACTIVE:
1577     case EL_SP_DISK_RED_ACTIVE:
1578     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1579     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1580     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1581     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1582       MovDelay[x][y] = 96;
1583       break;
1584
1585     case EL_EM_DYNAMITE_ACTIVE:
1586       MovDelay[x][y] = 32;
1587       break;
1588
1589     case EL_LAMP:
1590       local_player->lights_still_needed++;
1591       break;
1592
1593     case EL_PENGUIN:
1594       local_player->friends_still_needed++;
1595       break;
1596
1597     case EL_PIG:
1598     case EL_DRAGON:
1599       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1600       break;
1601
1602     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1603     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1604     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1605     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1606     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1607     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1608     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1609     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1610     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1611     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1612     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1613     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1614       if (init_game)
1615       {
1616         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1617         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1618         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1619
1620         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1621         {
1622           game.belt_dir[belt_nr] = belt_dir;
1623           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1624         }
1625         else    /* more than one switch -- set it like the first switch */
1626         {
1627           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1628         }
1629       }
1630       break;
1631
1632 #if !USE_BOTH_SWITCHGATE_SWITCHES
1633     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1634       if (init_game)
1635         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1636       break;
1637
1638     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1639       if (init_game)
1640         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1641       break;
1642 #endif
1643
1644     case EL_LIGHT_SWITCH_ACTIVE:
1645       if (init_game)
1646         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1647       break;
1648
1649     case EL_INVISIBLE_STEELWALL:
1650     case EL_INVISIBLE_WALL:
1651     case EL_INVISIBLE_SAND:
1652       if (game.light_time_left > 0 ||
1653           game.lenses_time_left > 0)
1654         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1655       break;
1656
1657     case EL_EMC_MAGIC_BALL:
1658       if (game.ball_state)
1659         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1660       break;
1661
1662     case EL_EMC_MAGIC_BALL_SWITCH:
1663       if (game.ball_state)
1664         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1665       break;
1666
1667     default:
1668       if (IS_CUSTOM_ELEMENT(element))
1669       {
1670         if (CAN_MOVE(element))
1671           InitMovDir(x, y);
1672
1673 #if USE_NEW_CUSTOM_VALUE
1674         if (!element_info[element].use_last_ce_value || init_game)
1675           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1676 #endif
1677       }
1678       else if (IS_GROUP_ELEMENT(element))
1679       {
1680         Feld[x][y] = GetElementFromGroupElement(element);
1681
1682         InitField(x, y, init_game);
1683       }
1684
1685       break;
1686   }
1687
1688   if (!init_game)
1689     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1690 }
1691
1692 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1693 {
1694   InitField(x, y, init_game);
1695
1696   /* not needed to call InitMovDir() -- already done by InitField()! */
1697   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1698       CAN_MOVE(Feld[x][y]))
1699     InitMovDir(x, y);
1700 }
1701
1702 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1703 {
1704   int old_element = Feld[x][y];
1705
1706   InitField(x, y, init_game);
1707
1708   /* not needed to call InitMovDir() -- already done by InitField()! */
1709   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1710       CAN_MOVE(old_element) &&
1711       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1712     InitMovDir(x, y);
1713
1714   /* this case is in fact a combination of not less than three bugs:
1715      first, it calls InitMovDir() for elements that can move, although this is
1716      already done by InitField(); then, it checks the element that was at this
1717      field _before_ the call to InitField() (which can change it); lastly, it
1718      was not called for "mole with direction" elements, which were treated as
1719      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1720   */
1721 }
1722
1723 #if 1
1724
1725 void InitGameControlValues()
1726 {
1727   int i;
1728
1729   for (i = 0; i < NUM_GAME_CONTROLS; i++)
1730     game_control_value[i] = last_game_control_value[i] = -1;
1731
1732   for (i = 0; game_controls[i].nr != -1; i++)
1733   {
1734     int nr = game_controls[i].nr;
1735     int type = game_controls[i].type;
1736     struct TextPosInfo *pos = game_controls[i].pos;
1737
1738     game_control_value[nr] = last_game_control_value[nr] = -1;
1739
1740     /* determine panel value width for later calculation of alignment */
1741     if (type == TYPE_INTEGER || type == TYPE_STRING)
1742       pos->width = pos->chars * getFontWidth(pos->font);
1743     else if (type == TYPE_ELEMENT)
1744       pos->width = MINI_TILESIZE;
1745   }
1746 }
1747
1748 void UpdateGameControlValues()
1749 {
1750   int i, j;
1751
1752   game_control_value[GAME_CONTROL_LEVEL_NUMBER] = level_nr;
1753   game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
1754
1755   game_control_value[GAME_CONTROL_INVENTORY] = 0;
1756   for (i = 0; i < MAX_NUM_KEYS; i++)
1757     game_control_value[GAME_CONTROL_KEY_1 + i] = 0;
1758   game_control_value[GAME_CONTROL_KEY_WHITE] = 0;
1759   game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] = 0;
1760
1761   if (game.centered_player_nr == -1)
1762   {
1763     for (i = 0; i < MAX_PLAYERS; i++)
1764     {
1765       for (j = 0; j < MAX_NUM_KEYS; j++)
1766         if (stored_player[i].key[j])
1767           game_control_value[GAME_CONTROL_KEY_1 + j] = 1;
1768
1769       game_control_value[GAME_CONTROL_INVENTORY] +=
1770         stored_player[i].inventory_size;
1771
1772       if (stored_player[i].num_white_keys > 0)
1773         game_control_value[GAME_CONTROL_KEY_WHITE] = 1;
1774
1775       game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
1776         stored_player[i].num_white_keys;
1777     }
1778   }
1779   else
1780   {
1781     int player_nr = game.centered_player_nr;
1782
1783     for (i = 0; i < MAX_NUM_KEYS; i++)
1784       if (stored_player[player_nr].key[i])
1785         game_control_value[GAME_CONTROL_KEY_1 + i] = 1;
1786
1787     game_control_value[GAME_CONTROL_INVENTORY] +=
1788       stored_player[player_nr].inventory_size;
1789
1790     if (stored_player[player_nr].num_white_keys > 0)
1791       game_control_value[GAME_CONTROL_KEY_WHITE] = 1;
1792
1793     game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
1794       stored_player[player_nr].num_white_keys;
1795   }
1796
1797   game_control_value[GAME_CONTROL_SCORE] = (local_player->LevelSolved ?
1798                                             local_player->score_final :
1799                                             local_player->score);
1800
1801   game_control_value[GAME_CONTROL_TIME] = (level.time == 0 ?
1802                                            TimePlayed :
1803                                            TimeLeft);
1804
1805   game_control_value[GAME_CONTROL_TIME_HH] = TapeTime / 3600;
1806   game_control_value[GAME_CONTROL_TIME_MM] = (TapeTime / 60) % 60;
1807   game_control_value[GAME_CONTROL_TIME_SS] = TapeTime % 60;
1808
1809   for (i = 0; i < 8; i++)
1810     game_control_value[GAME_CONTROL_DROP_NEXT_1 + i] = EL_UNDEFINED;
1811
1812   game_control_value[GAME_CONTROL_SHIELD_NORMAL] =
1813     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
1814      EL_EMPTY);
1815   game_control_value[GAME_CONTROL_SHIELD_NORMAL_TIME] =
1816     local_player->shield_normal_time_left;
1817   game_control_value[GAME_CONTROL_SHIELD_DEADLY] =
1818     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
1819      EL_EMPTY);
1820   game_control_value[GAME_CONTROL_SHIELD_DEADLY_TIME] =
1821     local_player->shield_deadly_time_left;
1822
1823   if (local_player->gems_still_needed > 0 ||
1824       local_player->sokobanfields_still_needed > 0 ||
1825       local_player->lights_still_needed > 0)
1826   {
1827     game_control_value[GAME_CONTROL_EXIT]          = EL_EXIT_CLOSED;
1828     game_control_value[GAME_CONTROL_EM_EXIT]       = EL_EM_EXIT_CLOSED;
1829     game_control_value[GAME_CONTROL_SP_EXIT]       = EL_SP_EXIT_CLOSED;
1830     game_control_value[GAME_CONTROL_STEEL_EXIT]    = EL_STEEL_EXIT_CLOSED;
1831     game_control_value[GAME_CONTROL_EM_STEEL_EXIT] = EL_EM_STEEL_EXIT_CLOSED;
1832   }
1833   else
1834   {
1835     game_control_value[GAME_CONTROL_EXIT]          = EL_EXIT_OPEN;
1836     game_control_value[GAME_CONTROL_EM_EXIT]       = EL_EM_EXIT_OPEN;
1837     game_control_value[GAME_CONTROL_SP_EXIT]       = EL_SP_EXIT_OPEN;
1838     game_control_value[GAME_CONTROL_STEEL_EXIT]    = EL_STEEL_EXIT_OPEN;
1839     game_control_value[GAME_CONTROL_EM_STEEL_EXIT] = EL_EM_STEEL_EXIT_OPEN;
1840   }
1841
1842   game_control_value[GAME_CONTROL_EMC_MAGIC_BALL] = EL_UNDEFINED;
1843   game_control_value[GAME_CONTROL_EMC_MAGIC_BALL_SWITCH] = EL_UNDEFINED;
1844
1845   game_control_value[GAME_CONTROL_LIGHT_SWITCH] =
1846     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
1847   game_control_value[GAME_CONTROL_LIGHT_SWITCH_TIME] = game.light_time_left;
1848
1849   game_control_value[GAME_CONTROL_TIMEGATE_SWITCH] =
1850     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
1851   game_control_value[GAME_CONTROL_TIMEGATE_SWITCH_TIME] =
1852     game.timegate_time_left;
1853
1854   game_control_value[GAME_CONTROL_SWITCHGATE_SWITCH] = EL_UNDEFINED;
1855
1856   game_control_value[GAME_CONTROL_EMC_LENSES] =
1857     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
1858   game_control_value[GAME_CONTROL_EMC_LENSES_TIME] = game.lenses_time_left;
1859
1860   game_control_value[GAME_CONTROL_EMC_MAGNIFIER] =
1861     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
1862   game_control_value[GAME_CONTROL_EMC_MAGNIFIER_TIME] = game.magnify_time_left;
1863
1864   game_control_value[GAME_CONTROL_BALLOON_SWITCH] =
1865     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
1866      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
1867      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
1868      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
1869      EL_BALLOON_SWITCH_NONE);
1870
1871   game_control_value[GAME_CONTROL_DYNABOMB_NUMBER] =
1872     local_player->dynabomb_count;
1873   game_control_value[GAME_CONTROL_DYNABOMB_SIZE] =
1874     local_player->dynabomb_size;
1875   game_control_value[GAME_CONTROL_DYNABOMB_POWER] =
1876     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
1877
1878   game_control_value[GAME_CONTROL_PENGUINS] =
1879     local_player->friends_still_needed;
1880
1881   game_control_value[GAME_CONTROL_SOKOBAN_OBJECTS] =
1882     local_player->sokobanfields_still_needed;
1883   game_control_value[GAME_CONTROL_SOKOBAN_FIELDS] =
1884     local_player->sokobanfields_still_needed;
1885
1886   game_control_value[GAME_CONTROL_ROBOT_WHEEL] = EL_UNDEFINED;
1887
1888   game_control_value[GAME_CONTROL_CONVEYOR_BELT_1] = EL_UNDEFINED;
1889   game_control_value[GAME_CONTROL_CONVEYOR_BELT_1_SWITCH] = EL_UNDEFINED;
1890   game_control_value[GAME_CONTROL_CONVEYOR_BELT_2] = EL_UNDEFINED;
1891   game_control_value[GAME_CONTROL_CONVEYOR_BELT_2_SWITCH] = EL_UNDEFINED;
1892   game_control_value[GAME_CONTROL_CONVEYOR_BELT_3] = EL_UNDEFINED;
1893   game_control_value[GAME_CONTROL_CONVEYOR_BELT_3_SWITCH] = EL_UNDEFINED;
1894   game_control_value[GAME_CONTROL_CONVEYOR_BELT_4] = EL_UNDEFINED;
1895   game_control_value[GAME_CONTROL_CONVEYOR_BELT_4_SWITCH] = EL_UNDEFINED;
1896
1897   game_control_value[GAME_CONTROL_MAGIC_WALL] = EL_UNDEFINED;
1898   game_control_value[GAME_CONTROL_MAGIC_WALL_TIME] =
1899     game.magic_wall_time_left;
1900   game_control_value[GAME_CONTROL_BD_MAGIC_WALL] = EL_UNDEFINED;
1901   game_control_value[GAME_CONTROL_DC_MAGIC_WALL] = EL_UNDEFINED;
1902
1903   game_control_value[GAME_CONTROL_PLAYER_NAME] = 0;
1904   game_control_value[GAME_CONTROL_LEVEL_NAME] = 0;
1905   game_control_value[GAME_CONTROL_LEVEL_AUTHOR] = 0;
1906 }
1907
1908 void DisplayGameControlValues()
1909 {
1910   int i;
1911
1912   for (i = 0; game_controls[i].nr != -1; i++)
1913   {
1914     int nr = game_controls[i].nr;
1915     int type = game_controls[i].type;
1916     struct TextPosInfo *pos = game_controls[i].pos;
1917     int value = game_control_value[nr];
1918     int last_value = last_game_control_value[nr];
1919     int chars = pos->chars;
1920     int font = pos->font;
1921
1922     if (value == last_value)
1923       continue;
1924
1925     last_game_control_value[nr] = value;
1926
1927 #if 0
1928     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
1929 #endif
1930
1931     if (PANEL_DEACTIVATED(pos))
1932       continue;
1933
1934     if (type == TYPE_INTEGER)
1935     {
1936       if (nr == GAME_CONTROL_LEVEL_NUMBER || nr == GAME_CONTROL_TIME)
1937       {
1938         boolean use_dynamic_chars = (pos->chars == -1 ? TRUE : FALSE);
1939
1940         if (use_dynamic_chars)          /* use dynamic number of chars */
1941         {
1942           int value_change = (nr == GAME_CONTROL_LEVEL_NUMBER ? 100 : 1000);
1943           int chars1 = (nr == GAME_CONTROL_LEVEL_NUMBER ? 2 : 3);
1944           int chars2 = chars1 + 1;
1945           int font1 = pos->font;
1946           int font2 = pos->font_alt;
1947
1948           chars = (value < value_change ? chars1 : chars2);
1949           font  = (value < value_change ? font1  : font2);
1950
1951           /* clear background if value just changed its size (dynamic chars) */
1952           if ((last_value < value_change) != (value < value_change))
1953           {
1954             int width1 = chars1 * getFontWidth(font1);
1955             int width2 = chars2 * getFontWidth(font2);
1956             int max_width = MAX(width1, width2);
1957             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
1958
1959             pos->width = max_width;
1960
1961             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
1962                                        max_width, max_height);
1963           }
1964         }
1965
1966         pos->width = chars * getFontWidth(font);
1967       }
1968
1969       DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font);
1970     }
1971     else if (type == TYPE_ELEMENT)
1972     {
1973       if (nr >= GAME_CONTROL_KEY_1 && nr <= GAME_CONTROL_KEY_8)
1974       {
1975         int key_nr = nr - GAME_CONTROL_KEY_1;
1976         int src_x = DOOR_GFX_PAGEX5 + 18 + (key_nr % STD_NUM_KEYS) * MINI_TILEX;
1977         int src_y = DOOR_GFX_PAGEY1 + 123;
1978         int dst_x = PANEL_XPOS(pos);
1979         int dst_y = PANEL_YPOS(pos);
1980         int element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1981                        level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1982                        EL_EM_KEY_1 : EL_KEY_1) + key_nr;
1983         int graphic = el2edimg(element);
1984
1985         if (value)
1986           DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
1987         else
1988           BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1989                      MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1990       }
1991       else if (value != EL_UNDEFINED)
1992       {
1993         int graphic = el2edimg(value);
1994         int dst_x = PANEL_XPOS(pos);
1995         int dst_y = PANEL_YPOS(pos);
1996
1997         DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
1998       }
1999     }
2000     else if (type == TYPE_STRING)
2001     {
2002       char *s = (nr == GAME_CONTROL_PLAYER_NAME  ? setup.player_name :
2003                  nr == GAME_CONTROL_LEVEL_NAME   ? level.name :
2004                  nr == GAME_CONTROL_LEVEL_AUTHOR ? level.author : NULL);
2005
2006       if (s != NULL)
2007       {
2008         char *s_cut = getStringCopyN(s, pos->chars);
2009
2010         DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), s_cut, pos->font);
2011
2012         free(s_cut);
2013       }
2014     }
2015
2016     redraw_mask |= REDRAW_DOOR_1;
2017   }
2018 }
2019
2020 void DrawGameValue_Emeralds(int value)
2021 {
2022   struct TextPosInfo *pos = &game.panel.gems;
2023 #if 1
2024   int font_nr = pos->font;
2025 #else
2026   int font_nr = FONT_TEXT_2;
2027 #endif
2028   int font_width = getFontWidth(font_nr);
2029   int chars = pos->chars;
2030
2031 #if 1
2032   return;       /* !!! USE NEW STUFF !!! */
2033 #endif
2034
2035   if (PANEL_DEACTIVATED(pos))
2036     return;
2037
2038   pos->width = chars * font_width;
2039
2040   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2041 }
2042
2043 void DrawGameValue_Dynamite(int value)
2044 {
2045   struct TextPosInfo *pos = &game.panel.inventory;
2046 #if 1
2047   int font_nr = pos->font;
2048 #else
2049   int font_nr = FONT_TEXT_2;
2050 #endif
2051   int font_width = getFontWidth(font_nr);
2052   int chars = pos->chars;
2053
2054 #if 1
2055   return;       /* !!! USE NEW STUFF !!! */
2056 #endif
2057
2058   if (PANEL_DEACTIVATED(pos))
2059     return;
2060
2061   pos->width = chars * font_width;
2062
2063   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2064 }
2065
2066 void DrawGameValue_Score(int value)
2067 {
2068   struct TextPosInfo *pos = &game.panel.score;
2069 #if 1
2070   int font_nr = pos->font;
2071 #else
2072   int font_nr = FONT_TEXT_2;
2073 #endif
2074   int font_width = getFontWidth(font_nr);
2075   int chars = pos->chars;
2076
2077 #if 1
2078   return;       /* !!! USE NEW STUFF !!! */
2079 #endif
2080
2081   if (PANEL_DEACTIVATED(pos))
2082     return;
2083
2084   pos->width = chars * font_width;
2085
2086   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2087 }
2088
2089 void DrawGameValue_Time(int value)
2090 {
2091   struct TextPosInfo *pos = &game.panel.time;
2092   static int last_value = -1;
2093   int chars1 = 3;
2094   int chars2 = 4;
2095   int chars = pos->chars;
2096 #if 1
2097   int font1_nr = pos->font;
2098   int font2_nr = pos->font_alt;
2099 #else
2100   int font1_nr = FONT_TEXT_2;
2101   int font2_nr = FONT_TEXT_1;
2102 #endif
2103   int font_nr = font1_nr;
2104   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2105
2106 #if 1
2107   return;       /* !!! USE NEW STUFF !!! */
2108 #endif
2109
2110   if (PANEL_DEACTIVATED(pos))
2111     return;
2112
2113   if (use_dynamic_chars)                /* use dynamic number of chars */
2114   {
2115     chars   = (value < 1000 ? chars1   : chars2);
2116     font_nr = (value < 1000 ? font1_nr : font2_nr);
2117   }
2118
2119   /* clear background if value just changed its size (dynamic chars only) */
2120   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2121   {
2122     int width1 = chars1 * getFontWidth(font1_nr);
2123     int width2 = chars2 * getFontWidth(font2_nr);
2124     int max_width = MAX(width1, width2);
2125     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2126
2127     pos->width = max_width;
2128
2129     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2130                                max_width, max_height);
2131   }
2132
2133   pos->width = chars * getFontWidth(font_nr);
2134
2135   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2136
2137   last_value = value;
2138 }
2139
2140 void DrawGameValue_Level(int value)
2141 {
2142   struct TextPosInfo *pos = &game.panel.level_number;
2143   int chars1 = 2;
2144   int chars2 = 3;
2145   int chars = pos->chars;
2146 #if 1
2147   int font1_nr = pos->font;
2148   int font2_nr = pos->font_alt;
2149 #else
2150   int font1_nr = FONT_TEXT_2;
2151   int font2_nr = FONT_TEXT_1;
2152 #endif
2153   int font_nr = font1_nr;
2154   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2155
2156 #if 1
2157   return;       /* !!! USE NEW STUFF !!! */
2158 #endif
2159
2160   if (PANEL_DEACTIVATED(pos))
2161     return;
2162
2163   if (use_dynamic_chars)                /* use dynamic number of chars */
2164   {
2165     chars   = (level_nr < 100 ? chars1   : chars2);
2166     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2167   }
2168
2169   pos->width = chars * getFontWidth(font_nr);
2170
2171   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2172 }
2173
2174 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2175 {
2176 #if 0
2177   struct TextPosInfo *pos = &game.panel.keys;
2178 #endif
2179 #if 0
2180   int base_key_graphic = EL_KEY_1;
2181 #endif
2182   int i;
2183
2184 #if 1
2185   return;       /* !!! USE NEW STUFF !!! */
2186 #endif
2187
2188 #if 0
2189   if (PANEL_DEACTIVATED(pos))
2190     return;
2191 #endif
2192
2193 #if 0
2194   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2195     base_key_graphic = EL_EM_KEY_1;
2196 #endif
2197
2198 #if 0
2199   pos->width = 4 * MINI_TILEX;
2200 #endif
2201
2202 #if 1
2203   for (i = 0; i < MAX_NUM_KEYS; i++)
2204 #else
2205   /* currently only 4 of 8 possible keys are displayed */
2206   for (i = 0; i < STD_NUM_KEYS; i++)
2207 #endif
2208   {
2209 #if 1
2210     struct TextPosInfo *pos = &game.panel.key[i];
2211 #endif
2212     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2213     int src_y = DOOR_GFX_PAGEY1 + 123;
2214 #if 1
2215     int dst_x = PANEL_XPOS(pos);
2216     int dst_y = PANEL_YPOS(pos);
2217 #else
2218     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2219     int dst_y = PANEL_YPOS(pos);
2220 #endif
2221
2222 #if 1
2223     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2224                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2225                    EL_KEY_1) + i;
2226     int graphic = el2edimg(element);
2227 #endif
2228
2229 #if 1
2230     if (PANEL_DEACTIVATED(pos))
2231       continue;
2232 #endif
2233
2234 #if 0
2235     /* masked blit with tiles from half-size scaled bitmap does not work yet
2236        (no mask bitmap created for these sizes after loading and scaling) --
2237        solution: load without creating mask, scale, then create final mask */
2238
2239     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2240                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2241
2242     if (key[i])
2243     {
2244 #if 0
2245       int graphic = el2edimg(base_key_graphic + i);
2246 #endif
2247       Bitmap *src_bitmap;
2248       int src_x, src_y;
2249
2250       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2251
2252       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2253                     dst_x - src_x, dst_y - src_y);
2254       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2255                        dst_x, dst_y);
2256     }
2257 #else
2258 #if 1
2259     if (key[i])
2260       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2261     else
2262       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2263                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2264 #else
2265     if (key[i])
2266       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2267     else
2268       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2269                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2270 #endif
2271 #endif
2272   }
2273 }
2274
2275 #else
2276
2277 void DrawGameValue_Emeralds(int value)
2278 {
2279   int font_nr = FONT_TEXT_2;
2280   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2281
2282   if (PANEL_DEACTIVATED(game.panel.gems))
2283     return;
2284
2285   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2286 }
2287
2288 void DrawGameValue_Dynamite(int value)
2289 {
2290   int font_nr = FONT_TEXT_2;
2291   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2292
2293   if (PANEL_DEACTIVATED(game.panel.inventory))
2294     return;
2295
2296   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2297 }
2298
2299 void DrawGameValue_Score(int value)
2300 {
2301   int font_nr = FONT_TEXT_2;
2302   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2303
2304   if (PANEL_DEACTIVATED(game.panel.score))
2305     return;
2306
2307   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2308 }
2309
2310 void DrawGameValue_Time(int value)
2311 {
2312   int font1_nr = FONT_TEXT_2;
2313 #if 1
2314   int font2_nr = FONT_TEXT_1;
2315 #else
2316   int font2_nr = FONT_LEVEL_NUMBER;
2317 #endif
2318   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2319   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2320
2321   if (PANEL_DEACTIVATED(game.panel.time))
2322     return;
2323
2324   /* clear background if value just changed its size */
2325   if (value == 999 || value == 1000)
2326     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2327
2328   if (value < 1000)
2329     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2330   else
2331     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2332 }
2333
2334 void DrawGameValue_Level(int value)
2335 {
2336   int font1_nr = FONT_TEXT_2;
2337 #if 1
2338   int font2_nr = FONT_TEXT_1;
2339 #else
2340   int font2_nr = FONT_LEVEL_NUMBER;
2341 #endif
2342
2343   if (PANEL_DEACTIVATED(game.panel.level))
2344     return;
2345
2346   if (level_nr < 100)
2347     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2348   else
2349     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2350 }
2351
2352 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2353 {
2354   int base_key_graphic = EL_KEY_1;
2355   int i;
2356
2357   if (PANEL_DEACTIVATED(game.panel.keys))
2358     return;
2359
2360   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2361     base_key_graphic = EL_EM_KEY_1;
2362
2363   /* currently only 4 of 8 possible keys are displayed */
2364   for (i = 0; i < STD_NUM_KEYS; i++)
2365   {
2366     int x = XX_KEYS + i * MINI_TILEX;
2367     int y = YY_KEYS;
2368
2369     if (key[i])
2370       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2371     else
2372       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2373                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2374   }
2375 }
2376
2377 #endif
2378
2379 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2380                        int key_bits)
2381 {
2382   int key[MAX_NUM_KEYS];
2383   int i;
2384
2385   /* prevent EM engine from updating time/score values parallel to GameWon() */
2386   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2387       local_player->LevelSolved)
2388     return;
2389
2390   for (i = 0; i < MAX_NUM_KEYS; i++)
2391     key[i] = key_bits & (1 << i);
2392
2393   DrawGameValue_Level(level_nr);
2394
2395   DrawGameValue_Emeralds(emeralds);
2396   DrawGameValue_Dynamite(dynamite);
2397   DrawGameValue_Score(score);
2398   DrawGameValue_Time(time);
2399
2400   DrawGameValue_Keys(key);
2401 }
2402
2403 void DrawGameDoorValues()
2404 {
2405   UpdateGameControlValues();
2406   DisplayGameControlValues();
2407 }
2408
2409 void DrawGameDoorValues_OLD()
2410 {
2411   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2412   int dynamite_value = 0;
2413   int score_value = (local_player->LevelSolved ? local_player->score_final :
2414                      local_player->score);
2415   int gems_value = local_player->gems_still_needed;
2416   int key_bits = 0;
2417   int i, j;
2418
2419   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2420   {
2421     DrawGameDoorValues_EM();
2422
2423     return;
2424   }
2425
2426   if (game.centered_player_nr == -1)
2427   {
2428     for (i = 0; i < MAX_PLAYERS; i++)
2429     {
2430       for (j = 0; j < MAX_NUM_KEYS; j++)
2431         if (stored_player[i].key[j])
2432           key_bits |= (1 << j);
2433
2434       dynamite_value += stored_player[i].inventory_size;
2435     }
2436   }
2437   else
2438   {
2439     int player_nr = game.centered_player_nr;
2440
2441     for (i = 0; i < MAX_NUM_KEYS; i++)
2442       if (stored_player[player_nr].key[i])
2443         key_bits |= (1 << i);
2444
2445     dynamite_value = stored_player[player_nr].inventory_size;
2446   }
2447
2448   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2449                     key_bits);
2450 }
2451
2452
2453 /*
2454   =============================================================================
2455   InitGameEngine()
2456   -----------------------------------------------------------------------------
2457   initialize game engine due to level / tape version number
2458   =============================================================================
2459 */
2460
2461 static void InitGameEngine()
2462 {
2463   int i, j, k, l, x, y;
2464
2465   /* set game engine from tape file when re-playing, else from level file */
2466   game.engine_version = (tape.playing ? tape.engine_version :
2467                          level.game_version);
2468
2469   /* ---------------------------------------------------------------------- */
2470   /* set flags for bugs and changes according to active game engine version */
2471   /* ---------------------------------------------------------------------- */
2472
2473   /*
2474     Summary of bugfix/change:
2475     Fixed handling for custom elements that change when pushed by the player.
2476
2477     Fixed/changed in version:
2478     3.1.0
2479
2480     Description:
2481     Before 3.1.0, custom elements that "change when pushing" changed directly
2482     after the player started pushing them (until then handled in "DigField()").
2483     Since 3.1.0, these custom elements are not changed until the "pushing"
2484     move of the element is finished (now handled in "ContinueMoving()").
2485
2486     Affected levels/tapes:
2487     The first condition is generally needed for all levels/tapes before version
2488     3.1.0, which might use the old behaviour before it was changed; known tapes
2489     that are affected are some tapes from the level set "Walpurgis Gardens" by
2490     Jamie Cullen.
2491     The second condition is an exception from the above case and is needed for
2492     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2493     above (including some development versions of 3.1.0), but before it was
2494     known that this change would break tapes like the above and was fixed in
2495     3.1.1, so that the changed behaviour was active although the engine version
2496     while recording maybe was before 3.1.0. There is at least one tape that is
2497     affected by this exception, which is the tape for the one-level set "Bug
2498     Machine" by Juergen Bonhagen.
2499   */
2500
2501   game.use_change_when_pushing_bug =
2502     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2503      !(tape.playing &&
2504        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2505        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2506
2507   /*
2508     Summary of bugfix/change:
2509     Fixed handling for blocking the field the player leaves when moving.
2510
2511     Fixed/changed in version:
2512     3.1.1
2513
2514     Description:
2515     Before 3.1.1, when "block last field when moving" was enabled, the field
2516     the player is leaving when moving was blocked for the time of the move,
2517     and was directly unblocked afterwards. This resulted in the last field
2518     being blocked for exactly one less than the number of frames of one player
2519     move. Additionally, even when blocking was disabled, the last field was
2520     blocked for exactly one frame.
2521     Since 3.1.1, due to changes in player movement handling, the last field
2522     is not blocked at all when blocking is disabled. When blocking is enabled,
2523     the last field is blocked for exactly the number of frames of one player
2524     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2525     last field is blocked for exactly one more than the number of frames of
2526     one player move.
2527
2528     Affected levels/tapes:
2529     (!!! yet to be determined -- probably many !!!)
2530   */
2531
2532   game.use_block_last_field_bug =
2533     (game.engine_version < VERSION_IDENT(3,1,1,0));
2534
2535   /*
2536     Summary of bugfix/change:
2537     Changed behaviour of CE changes with multiple changes per single frame.
2538
2539     Fixed/changed in version:
2540     3.2.0-6
2541
2542     Description:
2543     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
2544     This resulted in race conditions where CEs seem to behave strange in some
2545     situations (where triggered CE changes were just skipped because there was
2546     already a CE change on that tile in the playfield in that engine frame).
2547     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
2548     (The number of changes per frame must be limited in any case, because else
2549     it is easily possible to define CE changes that would result in an infinite
2550     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
2551     should be set large enough so that it would only be reached in cases where
2552     the corresponding CE change conditions run into a loop. Therefore, it seems
2553     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
2554     maximal number of change pages for custom elements.)
2555
2556     Affected levels/tapes:
2557     Probably many.
2558   */
2559
2560 #if USE_ONLY_ONE_CHANGE_PER_FRAME
2561   game.max_num_changes_per_frame = 1;
2562 #else
2563   game.max_num_changes_per_frame =
2564     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
2565 #endif
2566
2567   /* ---------------------------------------------------------------------- */
2568
2569   /* default scan direction: scan playfield from top/left to bottom/right */
2570   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2571
2572   /* dynamically adjust element properties according to game engine version */
2573   InitElementPropertiesEngine(game.engine_version);
2574
2575 #if 0
2576   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2577   printf("          tape version == %06d [%s] [file: %06d]\n",
2578          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2579          tape.file_version);
2580   printf("       => game.engine_version == %06d\n", game.engine_version);
2581 #endif
2582
2583   /* ---------- initialize player's initial move delay --------------------- */
2584
2585   /* dynamically adjust player properties according to level information */
2586   for (i = 0; i < MAX_PLAYERS; i++)
2587     game.initial_move_delay_value[i] =
2588       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2589
2590   /* dynamically adjust player properties according to game engine version */
2591   for (i = 0; i < MAX_PLAYERS; i++)
2592     game.initial_move_delay[i] =
2593       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2594        game.initial_move_delay_value[i] : 0);
2595
2596   /* ---------- initialize player's initial push delay --------------------- */
2597
2598   /* dynamically adjust player properties according to game engine version */
2599   game.initial_push_delay_value =
2600     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2601
2602   /* ---------- initialize changing elements ------------------------------- */
2603
2604   /* initialize changing elements information */
2605   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2606   {
2607     struct ElementInfo *ei = &element_info[i];
2608
2609     /* this pointer might have been changed in the level editor */
2610     ei->change = &ei->change_page[0];
2611
2612     if (!IS_CUSTOM_ELEMENT(i))
2613     {
2614       ei->change->target_element = EL_EMPTY_SPACE;
2615       ei->change->delay_fixed = 0;
2616       ei->change->delay_random = 0;
2617       ei->change->delay_frames = 1;
2618     }
2619
2620     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2621     {
2622       ei->has_change_event[j] = FALSE;
2623
2624       ei->event_page_nr[j] = 0;
2625       ei->event_page[j] = &ei->change_page[0];
2626     }
2627   }
2628
2629   /* add changing elements from pre-defined list */
2630   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2631   {
2632     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2633     struct ElementInfo *ei = &element_info[ch_delay->element];
2634
2635     ei->change->target_element       = ch_delay->target_element;
2636     ei->change->delay_fixed          = ch_delay->change_delay;
2637
2638     ei->change->pre_change_function  = ch_delay->pre_change_function;
2639     ei->change->change_function      = ch_delay->change_function;
2640     ei->change->post_change_function = ch_delay->post_change_function;
2641
2642     ei->change->can_change = TRUE;
2643     ei->change->can_change_or_has_action = TRUE;
2644
2645     ei->has_change_event[CE_DELAY] = TRUE;
2646
2647     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2648     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2649   }
2650
2651   /* ---------- initialize internal run-time variables ------------- */
2652
2653   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2654   {
2655     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2656
2657     for (j = 0; j < ei->num_change_pages; j++)
2658     {
2659       ei->change_page[j].can_change_or_has_action =
2660         (ei->change_page[j].can_change |
2661          ei->change_page[j].has_action);
2662     }
2663   }
2664
2665   /* add change events from custom element configuration */
2666   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2667   {
2668     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2669
2670     for (j = 0; j < ei->num_change_pages; j++)
2671     {
2672       if (!ei->change_page[j].can_change_or_has_action)
2673         continue;
2674
2675       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2676       {
2677         /* only add event page for the first page found with this event */
2678         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2679         {
2680           ei->has_change_event[k] = TRUE;
2681
2682           ei->event_page_nr[k] = j;
2683           ei->event_page[k] = &ei->change_page[j];
2684         }
2685       }
2686     }
2687   }
2688
2689   /* ---------- initialize run-time trigger player and element ------------- */
2690
2691   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2692   {
2693     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2694
2695     for (j = 0; j < ei->num_change_pages; j++)
2696     {
2697       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2698       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
2699       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2700       ei->change_page[j].actual_trigger_ce_value = 0;
2701       ei->change_page[j].actual_trigger_ce_score = 0;
2702     }
2703   }
2704
2705   /* ---------- initialize trigger events ---------------------------------- */
2706
2707   /* initialize trigger events information */
2708   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2709     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2710       trigger_events[i][j] = FALSE;
2711
2712   /* add trigger events from element change event properties */
2713   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2714   {
2715     struct ElementInfo *ei = &element_info[i];
2716
2717     for (j = 0; j < ei->num_change_pages; j++)
2718     {
2719       if (!ei->change_page[j].can_change_or_has_action)
2720         continue;
2721
2722       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2723       {
2724         int trigger_element = ei->change_page[j].trigger_element;
2725
2726         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2727         {
2728           if (ei->change_page[j].has_event[k])
2729           {
2730             if (IS_GROUP_ELEMENT(trigger_element))
2731             {
2732               struct ElementGroupInfo *group =
2733                 element_info[trigger_element].group;
2734
2735               for (l = 0; l < group->num_elements_resolved; l++)
2736                 trigger_events[group->element_resolved[l]][k] = TRUE;
2737             }
2738             else if (trigger_element == EL_ANY_ELEMENT)
2739               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2740                 trigger_events[l][k] = TRUE;
2741             else
2742               trigger_events[trigger_element][k] = TRUE;
2743           }
2744         }
2745       }
2746     }
2747   }
2748
2749   /* ---------- initialize push delay -------------------------------------- */
2750
2751   /* initialize push delay values to default */
2752   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2753   {
2754     if (!IS_CUSTOM_ELEMENT(i))
2755     {
2756       /* set default push delay values (corrected since version 3.0.7-1) */
2757       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2758       {
2759         element_info[i].push_delay_fixed = 2;
2760         element_info[i].push_delay_random = 8;
2761       }
2762       else
2763       {
2764         element_info[i].push_delay_fixed = 8;
2765         element_info[i].push_delay_random = 8;
2766       }
2767     }
2768   }
2769
2770   /* set push delay value for certain elements from pre-defined list */
2771   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2772   {
2773     int e = push_delay_list[i].element;
2774
2775     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2776     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2777   }
2778
2779   /* set push delay value for Supaplex elements for newer engine versions */
2780   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2781   {
2782     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2783     {
2784       if (IS_SP_ELEMENT(i))
2785       {
2786         /* set SP push delay to just enough to push under a falling zonk */
2787         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2788
2789         element_info[i].push_delay_fixed  = delay;
2790         element_info[i].push_delay_random = 0;
2791       }
2792     }
2793   }
2794
2795   /* ---------- initialize move stepsize ----------------------------------- */
2796
2797   /* initialize move stepsize values to default */
2798   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2799     if (!IS_CUSTOM_ELEMENT(i))
2800       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2801
2802   /* set move stepsize value for certain elements from pre-defined list */
2803   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2804   {
2805     int e = move_stepsize_list[i].element;
2806
2807     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2808   }
2809
2810   /* ---------- initialize collect score ----------------------------------- */
2811
2812   /* initialize collect score values for custom elements from initial value */
2813   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2814     if (IS_CUSTOM_ELEMENT(i))
2815       element_info[i].collect_score = element_info[i].collect_score_initial;
2816
2817   /* ---------- initialize collect count ----------------------------------- */
2818
2819   /* initialize collect count values for non-custom elements */
2820   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2821     if (!IS_CUSTOM_ELEMENT(i))
2822       element_info[i].collect_count_initial = 0;
2823
2824   /* add collect count values for all elements from pre-defined list */
2825   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2826     element_info[collect_count_list[i].element].collect_count_initial =
2827       collect_count_list[i].count;
2828
2829   /* ---------- initialize access direction -------------------------------- */
2830
2831   /* initialize access direction values to default (access from every side) */
2832   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2833     if (!IS_CUSTOM_ELEMENT(i))
2834       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2835
2836   /* set access direction value for certain elements from pre-defined list */
2837   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2838     element_info[access_direction_list[i].element].access_direction =
2839       access_direction_list[i].direction;
2840
2841   /* ---------- initialize explosion content ------------------------------- */
2842   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2843   {
2844     if (IS_CUSTOM_ELEMENT(i))
2845       continue;
2846
2847     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2848     {
2849       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2850
2851       element_info[i].content.e[x][y] =
2852         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2853          i == EL_PLAYER_2 ? EL_EMERALD_RED :
2854          i == EL_PLAYER_3 ? EL_EMERALD :
2855          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2856          i == EL_MOLE ? EL_EMERALD_RED :
2857          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2858          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2859          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2860          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2861          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2862          i == EL_WALL_EMERALD ? EL_EMERALD :
2863          i == EL_WALL_DIAMOND ? EL_DIAMOND :
2864          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2865          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2866          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2867          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2868          i == EL_WALL_PEARL ? EL_PEARL :
2869          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2870          EL_EMPTY);
2871     }
2872   }
2873
2874   /* ---------- initialize recursion detection ------------------------------ */
2875   recursion_loop_depth = 0;
2876   recursion_loop_detected = FALSE;
2877   recursion_loop_element = EL_UNDEFINED;
2878 }
2879
2880 int get_num_special_action(int element, int action_first, int action_last)
2881 {
2882   int num_special_action = 0;
2883   int i, j;
2884
2885   for (i = action_first; i <= action_last; i++)
2886   {
2887     boolean found = FALSE;
2888
2889     for (j = 0; j < NUM_DIRECTIONS; j++)
2890       if (el_act_dir2img(element, i, j) !=
2891           el_act_dir2img(element, ACTION_DEFAULT, j))
2892         found = TRUE;
2893
2894     if (found)
2895       num_special_action++;
2896     else
2897       break;
2898   }
2899
2900   return num_special_action;
2901 }
2902
2903
2904 /*
2905   =============================================================================
2906   InitGame()
2907   -----------------------------------------------------------------------------
2908   initialize and start new game
2909   =============================================================================
2910 */
2911
2912 void InitGame()
2913 {
2914   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
2915   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
2916   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
2917 #if 0
2918   boolean do_fading = (game_status == GAME_MODE_MAIN);
2919 #endif
2920   int i, j, x, y;
2921
2922   game_status = GAME_MODE_PLAYING;
2923
2924   InitGameEngine();
2925   InitGameControlValues();
2926
2927   /* don't play tapes over network */
2928   network_playing = (options.network && !tape.playing);
2929
2930   for (i = 0; i < MAX_PLAYERS; i++)
2931   {
2932     struct PlayerInfo *player = &stored_player[i];
2933
2934     player->index_nr = i;
2935     player->index_bit = (1 << i);
2936     player->element_nr = EL_PLAYER_1 + i;
2937
2938     player->present = FALSE;
2939     player->active = FALSE;
2940     player->killed = FALSE;
2941
2942     player->action = 0;
2943     player->effective_action = 0;
2944     player->programmed_action = 0;
2945
2946     player->score = 0;
2947     player->score_final = 0;
2948
2949     player->gems_still_needed = level.gems_needed;
2950     player->sokobanfields_still_needed = 0;
2951     player->lights_still_needed = 0;
2952     player->friends_still_needed = 0;
2953
2954     for (j = 0; j < MAX_NUM_KEYS; j++)
2955       player->key[j] = FALSE;
2956
2957     player->num_white_keys = 0;
2958
2959     player->dynabomb_count = 0;
2960     player->dynabomb_size = 1;
2961     player->dynabombs_left = 0;
2962     player->dynabomb_xl = FALSE;
2963
2964     player->MovDir = MV_NONE;
2965     player->MovPos = 0;
2966     player->GfxPos = 0;
2967     player->GfxDir = MV_NONE;
2968     player->GfxAction = ACTION_DEFAULT;
2969     player->Frame = 0;
2970     player->StepFrame = 0;
2971
2972     player->use_murphy = FALSE;
2973     player->artwork_element =
2974       (level.use_artwork_element[i] ? level.artwork_element[i] :
2975        player->element_nr);
2976
2977     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
2978     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2979
2980     player->gravity = level.initial_player_gravity[i];
2981
2982     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2983
2984     player->actual_frame_counter = 0;
2985
2986     player->step_counter = 0;
2987
2988     player->last_move_dir = MV_NONE;
2989
2990     player->is_active = FALSE;
2991
2992     player->is_waiting = FALSE;
2993     player->is_moving = FALSE;
2994     player->is_auto_moving = FALSE;
2995     player->is_digging = FALSE;
2996     player->is_snapping = FALSE;
2997     player->is_collecting = FALSE;
2998     player->is_pushing = FALSE;
2999     player->is_switching = FALSE;
3000     player->is_dropping = FALSE;
3001     player->is_dropping_pressed = FALSE;
3002
3003     player->is_bored = FALSE;
3004     player->is_sleeping = FALSE;
3005
3006     player->frame_counter_bored = -1;
3007     player->frame_counter_sleeping = -1;
3008
3009     player->anim_delay_counter = 0;
3010     player->post_delay_counter = 0;
3011
3012     player->dir_waiting = MV_NONE;
3013     player->action_waiting = ACTION_DEFAULT;
3014     player->last_action_waiting = ACTION_DEFAULT;
3015     player->special_action_bored = ACTION_DEFAULT;
3016     player->special_action_sleeping = ACTION_DEFAULT;
3017
3018     player->switch_x = -1;
3019     player->switch_y = -1;
3020
3021     player->drop_x = -1;
3022     player->drop_y = -1;
3023
3024     player->show_envelope = 0;
3025
3026     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3027
3028     player->push_delay       = -1;      /* initialized when pushing starts */
3029     player->push_delay_value = game.initial_push_delay_value;
3030
3031     player->drop_delay = 0;
3032     player->drop_pressed_delay = 0;
3033
3034     player->last_jx = -1;
3035     player->last_jy = -1;
3036     player->jx = -1;
3037     player->jy = -1;
3038
3039     player->shield_normal_time_left = 0;
3040     player->shield_deadly_time_left = 0;
3041
3042     player->inventory_infinite_element = EL_UNDEFINED;
3043     player->inventory_size = 0;
3044
3045     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3046     SnapField(player, 0, 0);
3047
3048     player->LevelSolved = FALSE;
3049     player->GameOver = FALSE;
3050
3051     player->LevelSolved_GameWon = FALSE;
3052     player->LevelSolved_GameEnd = FALSE;
3053     player->LevelSolved_PanelOff = FALSE;
3054     player->LevelSolved_SaveTape = FALSE;
3055     player->LevelSolved_SaveScore = FALSE;
3056   }
3057
3058   network_player_action_received = FALSE;
3059
3060 #if defined(NETWORK_AVALIABLE)
3061   /* initial null action */
3062   if (network_playing)
3063     SendToServer_MovePlayer(MV_NONE);
3064 #endif
3065
3066   ZX = ZY = -1;
3067   ExitX = ExitY = -1;
3068
3069   FrameCounter = 0;
3070   TimeFrames = 0;
3071   TimePlayed = 0;
3072   TimeLeft = level.time;
3073   TapeTime = 0;
3074
3075   ScreenMovDir = MV_NONE;
3076   ScreenMovPos = 0;
3077   ScreenGfxPos = 0;
3078
3079   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3080
3081   AllPlayersGone = FALSE;
3082
3083   game.yamyam_content_nr = 0;
3084   game.magic_wall_active = FALSE;
3085   game.magic_wall_time_left = 0;
3086   game.light_time_left = 0;
3087   game.timegate_time_left = 0;
3088   game.switchgate_pos = 0;
3089   game.wind_direction = level.wind_direction_initial;
3090
3091 #if !USE_PLAYER_GRAVITY
3092   game.gravity = FALSE;
3093   game.explosions_delayed = TRUE;
3094 #endif
3095
3096   game.lenses_time_left = 0;
3097   game.magnify_time_left = 0;
3098
3099   game.ball_state = level.ball_state_initial;
3100   game.ball_content_nr = 0;
3101
3102   game.envelope_active = FALSE;
3103
3104   /* set focus to local player for network games, else to all players */
3105   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3106   game.centered_player_nr_next = game.centered_player_nr;
3107   game.set_centered_player = FALSE;
3108
3109   if (network_playing && tape.recording)
3110   {
3111     /* store client dependent player focus when recording network games */
3112     tape.centered_player_nr_next = game.centered_player_nr_next;
3113     tape.set_centered_player = TRUE;
3114   }
3115
3116   for (i = 0; i < NUM_BELTS; i++)
3117   {
3118     game.belt_dir[i] = MV_NONE;
3119     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3120   }
3121
3122   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3123     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3124
3125   SCAN_PLAYFIELD(x, y)
3126   {
3127     Feld[x][y] = level.field[x][y];
3128     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3129     ChangeDelay[x][y] = 0;
3130     ChangePage[x][y] = -1;
3131 #if USE_NEW_CUSTOM_VALUE
3132     CustomValue[x][y] = 0;              /* initialized in InitField() */
3133 #endif
3134     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3135     AmoebaNr[x][y] = 0;
3136     WasJustMoving[x][y] = 0;
3137     WasJustFalling[x][y] = 0;
3138     CheckCollision[x][y] = 0;
3139     CheckImpact[x][y] = 0;
3140     Stop[x][y] = FALSE;
3141     Pushed[x][y] = FALSE;
3142
3143     ChangeCount[x][y] = 0;
3144     ChangeEvent[x][y] = -1;
3145
3146     ExplodePhase[x][y] = 0;
3147     ExplodeDelay[x][y] = 0;
3148     ExplodeField[x][y] = EX_TYPE_NONE;
3149
3150     RunnerVisit[x][y] = 0;
3151     PlayerVisit[x][y] = 0;
3152
3153     GfxFrame[x][y] = 0;
3154     GfxRandom[x][y] = INIT_GFX_RANDOM();
3155     GfxElement[x][y] = EL_UNDEFINED;
3156     GfxAction[x][y] = ACTION_DEFAULT;
3157     GfxDir[x][y] = MV_NONE;
3158   }
3159
3160   SCAN_PLAYFIELD(x, y)
3161   {
3162     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3163       emulate_bd = FALSE;
3164     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3165       emulate_sb = FALSE;
3166     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3167       emulate_sp = FALSE;
3168
3169     InitField(x, y, TRUE);
3170   }
3171
3172   InitBeltMovement();
3173
3174   for (i = 0; i < MAX_PLAYERS; i++)
3175   {
3176     struct PlayerInfo *player = &stored_player[i];
3177
3178     /* set number of special actions for bored and sleeping animation */
3179     player->num_special_action_bored =
3180       get_num_special_action(player->artwork_element,
3181                              ACTION_BORING_1, ACTION_BORING_LAST);
3182     player->num_special_action_sleeping =
3183       get_num_special_action(player->artwork_element,
3184                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3185   }
3186
3187   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3188                     emulate_sb ? EMU_SOKOBAN :
3189                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3190
3191 #if USE_NEW_ALL_SLIPPERY
3192   /* initialize type of slippery elements */
3193   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3194   {
3195     if (!IS_CUSTOM_ELEMENT(i))
3196     {
3197       /* default: elements slip down either to the left or right randomly */
3198       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3199
3200       /* SP style elements prefer to slip down on the left side */
3201       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3202         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3203
3204       /* BD style elements prefer to slip down on the left side */
3205       if (game.emulation == EMU_BOULDERDASH)
3206         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3207     }
3208   }
3209 #endif
3210
3211   /* initialize explosion and ignition delay */
3212   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3213   {
3214     if (!IS_CUSTOM_ELEMENT(i))
3215     {
3216       int num_phase = 8;
3217       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3218                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3219                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3220       int last_phase = (num_phase + 1) * delay;
3221       int half_phase = (num_phase / 2) * delay;
3222
3223       element_info[i].explosion_delay = last_phase - 1;
3224       element_info[i].ignition_delay = half_phase;
3225
3226       if (i == EL_BLACK_ORB)
3227         element_info[i].ignition_delay = 1;
3228     }
3229
3230 #if 0
3231     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3232       element_info[i].explosion_delay = 1;
3233
3234     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3235       element_info[i].ignition_delay = 1;
3236 #endif
3237   }
3238
3239   /* correct non-moving belts to start moving left */
3240   for (i = 0; i < NUM_BELTS; i++)
3241     if (game.belt_dir[i] == MV_NONE)
3242       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3243
3244   /* check if any connected player was not found in playfield */
3245   for (i = 0; i < MAX_PLAYERS; i++)
3246   {
3247     struct PlayerInfo *player = &stored_player[i];
3248
3249     if (player->connected && !player->present)
3250     {
3251       for (j = 0; j < MAX_PLAYERS; j++)
3252       {
3253         struct PlayerInfo *some_player = &stored_player[j];
3254         int jx = some_player->jx, jy = some_player->jy;
3255
3256         /* assign first free player found that is present in the playfield */
3257         if (some_player->present && !some_player->connected)
3258         {
3259           player->present = TRUE;
3260           player->active = TRUE;
3261
3262           some_player->present = FALSE;
3263           some_player->active = FALSE;
3264
3265           player->artwork_element = some_player->artwork_element;
3266
3267           player->block_last_field       = some_player->block_last_field;
3268           player->block_delay_adjustment = some_player->block_delay_adjustment;
3269
3270           StorePlayer[jx][jy] = player->element_nr;
3271           player->jx = player->last_jx = jx;
3272           player->jy = player->last_jy = jy;
3273
3274           break;
3275         }
3276       }
3277     }
3278   }
3279
3280   if (tape.playing)
3281   {
3282     /* when playing a tape, eliminate all players who do not participate */
3283
3284     for (i = 0; i < MAX_PLAYERS; i++)
3285     {
3286       if (stored_player[i].active && !tape.player_participates[i])
3287       {
3288         struct PlayerInfo *player = &stored_player[i];
3289         int jx = player->jx, jy = player->jy;
3290
3291         player->active = FALSE;
3292         StorePlayer[jx][jy] = 0;
3293         Feld[jx][jy] = EL_EMPTY;
3294       }
3295     }
3296   }
3297   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
3298   {
3299     /* when in single player mode, eliminate all but the first active player */
3300
3301     for (i = 0; i < MAX_PLAYERS; i++)
3302     {
3303       if (stored_player[i].active)
3304       {
3305         for (j = i + 1; j < MAX_PLAYERS; j++)
3306         {
3307           if (stored_player[j].active)
3308           {
3309             struct PlayerInfo *player = &stored_player[j];
3310             int jx = player->jx, jy = player->jy;
3311
3312             player->active = FALSE;
3313             player->present = FALSE;
3314
3315             StorePlayer[jx][jy] = 0;
3316             Feld[jx][jy] = EL_EMPTY;
3317           }
3318         }
3319       }
3320     }
3321   }
3322
3323   /* when recording the game, store which players take part in the game */
3324   if (tape.recording)
3325   {
3326     for (i = 0; i < MAX_PLAYERS; i++)
3327       if (stored_player[i].active)
3328         tape.player_participates[i] = TRUE;
3329   }
3330
3331   if (options.debug)
3332   {
3333     for (i = 0; i < MAX_PLAYERS; i++)
3334     {
3335       struct PlayerInfo *player = &stored_player[i];
3336
3337       printf("Player %d: present == %d, connected == %d, active == %d.\n",
3338              i+1,
3339              player->present,
3340              player->connected,
3341              player->active);
3342       if (local_player == player)
3343         printf("Player  %d is local player.\n", i+1);
3344     }
3345   }
3346
3347   if (BorderElement == EL_EMPTY)
3348   {
3349     SBX_Left = 0;
3350     SBX_Right = lev_fieldx - SCR_FIELDX;
3351     SBY_Upper = 0;
3352     SBY_Lower = lev_fieldy - SCR_FIELDY;
3353   }
3354   else
3355   {
3356     SBX_Left = -1;
3357     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3358     SBY_Upper = -1;
3359     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3360   }
3361
3362   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3363     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3364
3365   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3366     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3367
3368   /* if local player not found, look for custom element that might create
3369      the player (make some assumptions about the right custom element) */
3370   if (!local_player->present)
3371   {
3372     int start_x = 0, start_y = 0;
3373     int found_rating = 0;
3374     int found_element = EL_UNDEFINED;
3375     int player_nr = local_player->index_nr;
3376
3377     SCAN_PLAYFIELD(x, y)
3378     {
3379       int element = Feld[x][y];
3380       int content;
3381       int xx, yy;
3382       boolean is_player;
3383
3384       if (level.use_start_element[player_nr] &&
3385           level.start_element[player_nr] == element &&
3386           found_rating < 4)
3387       {
3388         start_x = x;
3389         start_y = y;
3390
3391         found_rating = 4;
3392         found_element = element;
3393       }
3394
3395       if (!IS_CUSTOM_ELEMENT(element))
3396         continue;
3397
3398       if (CAN_CHANGE(element))
3399       {
3400         for (i = 0; i < element_info[element].num_change_pages; i++)
3401         {
3402           /* check for player created from custom element as single target */
3403           content = element_info[element].change_page[i].target_element;
3404           is_player = ELEM_IS_PLAYER(content);
3405
3406           if (is_player && (found_rating < 3 ||
3407                             (found_rating == 3 && element < found_element)))
3408           {
3409             start_x = x;
3410             start_y = y;
3411
3412             found_rating = 3;
3413             found_element = element;
3414           }
3415         }
3416       }
3417
3418       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3419       {
3420         /* check for player created from custom element as explosion content */
3421         content = element_info[element].content.e[xx][yy];
3422         is_player = ELEM_IS_PLAYER(content);
3423
3424         if (is_player && (found_rating < 2 ||
3425                           (found_rating == 2 && element < found_element)))
3426         {
3427           start_x = x + xx - 1;
3428           start_y = y + yy - 1;
3429
3430           found_rating = 2;
3431           found_element = element;
3432         }
3433
3434         if (!CAN_CHANGE(element))
3435           continue;
3436
3437         for (i = 0; i < element_info[element].num_change_pages; i++)
3438         {
3439           /* check for player created from custom element as extended target */
3440           content =
3441             element_info[element].change_page[i].target_content.e[xx][yy];
3442
3443           is_player = ELEM_IS_PLAYER(content);
3444
3445           if (is_player && (found_rating < 1 ||
3446                             (found_rating == 1 && element < found_element)))
3447           {
3448             start_x = x + xx - 1;
3449             start_y = y + yy - 1;
3450
3451             found_rating = 1;
3452             found_element = element;
3453           }
3454         }
3455       }
3456     }
3457
3458     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3459                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3460                 start_x - MIDPOSX);
3461
3462     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3463                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3464                 start_y - MIDPOSY);
3465   }
3466   else
3467   {
3468     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3469                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3470                 local_player->jx - MIDPOSX);
3471
3472     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3473                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3474                 local_player->jy - MIDPOSY);
3475   }
3476
3477   StopAnimation();
3478
3479   if (!game.restart_level)
3480     CloseDoor(DOOR_CLOSE_1);
3481
3482 #if 1
3483   if (level_editor_test_game)
3484     FadeSkipNextFadeIn();
3485   else
3486     FadeSetStartItem();
3487 #else
3488   if (level_editor_test_game)
3489     fading = fading_none;
3490   else
3491     fading = menu.destination;
3492 #endif
3493
3494 #if 1
3495   FadeOut(REDRAW_FIELD);
3496 #else
3497   if (do_fading)
3498     FadeOut(REDRAW_FIELD);
3499 #endif
3500
3501   /* !!! FIX THIS (START) !!! */
3502   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3503   {
3504     InitGameEngine_EM();
3505
3506     /* blit playfield from scroll buffer to normal back buffer for fading in */
3507     BlitScreenToBitmap_EM(backbuffer);
3508   }
3509   else
3510   {
3511     DrawLevel();
3512     DrawAllPlayers();
3513
3514     /* after drawing the level, correct some elements */
3515     if (game.timegate_time_left == 0)
3516       CloseAllOpenTimegates();
3517
3518     /* blit playfield from scroll buffer to normal back buffer for fading in */
3519     if (setup.soft_scrolling)
3520       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3521
3522     redraw_mask |= REDRAW_FROM_BACKBUFFER;
3523   }
3524   /* !!! FIX THIS (END) !!! */
3525
3526 #if 1
3527   FadeIn(REDRAW_FIELD);
3528 #else
3529   if (do_fading)
3530     FadeIn(REDRAW_FIELD);
3531
3532   BackToFront();
3533 #endif
3534
3535   if (!game.restart_level)
3536   {
3537     /* copy default game door content to main double buffer */
3538     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3539                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3540   }
3541
3542   SetPanelBackground();
3543   SetDrawBackgroundMask(REDRAW_DOOR_1);
3544
3545   DrawGameDoorValues();
3546
3547   if (!game.restart_level)
3548   {
3549     UnmapGameButtons();
3550     UnmapTapeButtons();
3551     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3552     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3553     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3554     MapGameButtons();
3555     MapTapeButtons();
3556
3557     /* copy actual game door content to door double buffer for OpenDoor() */
3558     BlitBitmap(drawto, bitmap_db_door,
3559                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3560
3561     OpenDoor(DOOR_OPEN_ALL);
3562
3563     PlaySound(SND_GAME_STARTING);
3564
3565     if (setup.sound_music)
3566       PlayLevelMusic();
3567
3568     KeyboardAutoRepeatOffUnlessAutoplay();
3569
3570     if (options.debug)
3571     {
3572       for (i = 0; i < MAX_PLAYERS; i++)
3573         printf("Player %d %sactive.\n",
3574                i + 1, (stored_player[i].active ? "" : "not "));
3575     }
3576   }
3577
3578 #if 1
3579   UnmapAllGadgets();
3580
3581   MapGameButtons();
3582   MapTapeButtons();
3583 #endif
3584
3585   game.restart_level = FALSE;
3586 }
3587
3588 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3589 {
3590   /* this is used for non-R'n'D game engines to update certain engine values */
3591
3592   /* needed to determine if sounds are played within the visible screen area */
3593   scroll_x = actual_scroll_x;
3594   scroll_y = actual_scroll_y;
3595 }
3596
3597 void InitMovDir(int x, int y)
3598 {
3599   int i, element = Feld[x][y];
3600   static int xy[4][2] =
3601   {
3602     {  0, +1 },
3603     { +1,  0 },
3604     {  0, -1 },
3605     { -1,  0 }
3606   };
3607   static int direction[3][4] =
3608   {
3609     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
3610     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
3611     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
3612   };
3613
3614   switch (element)
3615   {
3616     case EL_BUG_RIGHT:
3617     case EL_BUG_UP:
3618     case EL_BUG_LEFT:
3619     case EL_BUG_DOWN:
3620       Feld[x][y] = EL_BUG;
3621       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3622       break;
3623
3624     case EL_SPACESHIP_RIGHT:
3625     case EL_SPACESHIP_UP:
3626     case EL_SPACESHIP_LEFT:
3627     case EL_SPACESHIP_DOWN:
3628       Feld[x][y] = EL_SPACESHIP;
3629       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3630       break;
3631
3632     case EL_BD_BUTTERFLY_RIGHT:
3633     case EL_BD_BUTTERFLY_UP:
3634     case EL_BD_BUTTERFLY_LEFT:
3635     case EL_BD_BUTTERFLY_DOWN:
3636       Feld[x][y] = EL_BD_BUTTERFLY;
3637       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3638       break;
3639
3640     case EL_BD_FIREFLY_RIGHT:
3641     case EL_BD_FIREFLY_UP:
3642     case EL_BD_FIREFLY_LEFT:
3643     case EL_BD_FIREFLY_DOWN:
3644       Feld[x][y] = EL_BD_FIREFLY;
3645       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3646       break;
3647
3648     case EL_PACMAN_RIGHT:
3649     case EL_PACMAN_UP:
3650     case EL_PACMAN_LEFT:
3651     case EL_PACMAN_DOWN:
3652       Feld[x][y] = EL_PACMAN;
3653       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3654       break;
3655
3656     case EL_YAMYAM_LEFT:
3657     case EL_YAMYAM_RIGHT:
3658     case EL_YAMYAM_UP:
3659     case EL_YAMYAM_DOWN:
3660       Feld[x][y] = EL_YAMYAM;
3661       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3662       break;
3663
3664     case EL_SP_SNIKSNAK:
3665       MovDir[x][y] = MV_UP;
3666       break;
3667
3668     case EL_SP_ELECTRON:
3669       MovDir[x][y] = MV_LEFT;
3670       break;
3671
3672     case EL_MOLE_LEFT:
3673     case EL_MOLE_RIGHT:
3674     case EL_MOLE_UP:
3675     case EL_MOLE_DOWN:
3676       Feld[x][y] = EL_MOLE;
3677       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3678       break;
3679
3680     default:
3681       if (IS_CUSTOM_ELEMENT(element))
3682       {
3683         struct ElementInfo *ei = &element_info[element];
3684         int move_direction_initial = ei->move_direction_initial;
3685         int move_pattern = ei->move_pattern;
3686
3687         if (move_direction_initial == MV_START_PREVIOUS)
3688         {
3689           if (MovDir[x][y] != MV_NONE)
3690             return;
3691
3692           move_direction_initial = MV_START_AUTOMATIC;
3693         }
3694
3695         if (move_direction_initial == MV_START_RANDOM)
3696           MovDir[x][y] = 1 << RND(4);
3697         else if (move_direction_initial & MV_ANY_DIRECTION)
3698           MovDir[x][y] = move_direction_initial;
3699         else if (move_pattern == MV_ALL_DIRECTIONS ||
3700                  move_pattern == MV_TURNING_LEFT ||
3701                  move_pattern == MV_TURNING_RIGHT ||
3702                  move_pattern == MV_TURNING_LEFT_RIGHT ||
3703                  move_pattern == MV_TURNING_RIGHT_LEFT ||
3704                  move_pattern == MV_TURNING_RANDOM)
3705           MovDir[x][y] = 1 << RND(4);
3706         else if (move_pattern == MV_HORIZONTAL)
3707           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3708         else if (move_pattern == MV_VERTICAL)
3709           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3710         else if (move_pattern & MV_ANY_DIRECTION)
3711           MovDir[x][y] = element_info[element].move_pattern;
3712         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
3713                  move_pattern == MV_ALONG_RIGHT_SIDE)
3714         {
3715           /* use random direction as default start direction */
3716           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3717             MovDir[x][y] = 1 << RND(4);
3718
3719           for (i = 0; i < NUM_DIRECTIONS; i++)
3720           {
3721             int x1 = x + xy[i][0];
3722             int y1 = y + xy[i][1];
3723
3724             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3725             {
3726               if (move_pattern == MV_ALONG_RIGHT_SIDE)
3727                 MovDir[x][y] = direction[0][i];
3728               else
3729                 MovDir[x][y] = direction[1][i];
3730
3731               break;
3732             }
3733           }
3734         }                
3735       }
3736       else
3737       {
3738         MovDir[x][y] = 1 << RND(4);
3739
3740         if (element != EL_BUG &&
3741             element != EL_SPACESHIP &&
3742             element != EL_BD_BUTTERFLY &&
3743             element != EL_BD_FIREFLY)
3744           break;
3745
3746         for (i = 0; i < NUM_DIRECTIONS; i++)
3747         {
3748           int x1 = x + xy[i][0];
3749           int y1 = y + xy[i][1];
3750
3751           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3752           {
3753             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3754             {
3755               MovDir[x][y] = direction[0][i];
3756               break;
3757             }
3758             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3759                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3760             {
3761               MovDir[x][y] = direction[1][i];
3762               break;
3763             }
3764           }
3765         }
3766       }
3767       break;
3768   }
3769
3770   GfxDir[x][y] = MovDir[x][y];
3771 }
3772
3773 void InitAmoebaNr(int x, int y)
3774 {
3775   int i;
3776   int group_nr = AmoebeNachbarNr(x, y);
3777
3778   if (group_nr == 0)
3779   {
3780     for (i = 1; i < MAX_NUM_AMOEBA; i++)
3781     {
3782       if (AmoebaCnt[i] == 0)
3783       {
3784         group_nr = i;
3785         break;
3786       }
3787     }
3788   }
3789
3790   AmoebaNr[x][y] = group_nr;
3791   AmoebaCnt[group_nr]++;
3792   AmoebaCnt2[group_nr]++;
3793 }
3794
3795 static void PlayerWins(struct PlayerInfo *player)
3796 {
3797   player->LevelSolved = TRUE;
3798   player->GameOver = TRUE;
3799
3800   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
3801                          level.native_em_level->lev->score : player->score);
3802 }
3803
3804 void GameWon()
3805 {
3806   static int time, time_final;
3807   static int score, score_final;
3808   static int game_over_delay_1 = 0;
3809   static int game_over_delay_2 = 0;
3810   int game_over_delay_value_1 = 50;
3811   int game_over_delay_value_2 = 50;
3812
3813   if (!local_player->LevelSolved_GameWon)
3814   {
3815     int i;
3816
3817     /* do not start end game actions before the player stops moving (to exit) */
3818     if (local_player->MovPos)
3819       return;
3820
3821     local_player->LevelSolved_GameWon = TRUE;
3822     local_player->LevelSolved_SaveTape = tape.recording;
3823     local_player->LevelSolved_SaveScore = !tape.playing;
3824
3825     if (tape.auto_play)         /* tape might already be stopped here */
3826       tape.auto_play_level_solved = TRUE;
3827
3828 #if 1
3829     TapeStop();
3830 #endif
3831
3832     game_over_delay_1 = game_over_delay_value_1;
3833     game_over_delay_2 = game_over_delay_value_2;
3834
3835     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3836     score = score_final = local_player->score_final;
3837
3838     if (TimeLeft > 0)
3839     {
3840       time_final = 0;
3841       score_final += TimeLeft * level.score[SC_TIME_BONUS];
3842     }
3843     else if (level.time == 0 && TimePlayed < 999)
3844     {
3845       time_final = 999;
3846       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3847     }
3848
3849     local_player->score_final = score_final;
3850
3851     if (level_editor_test_game)
3852     {
3853       time = time_final;
3854       score = score_final;
3855
3856 #if 1
3857       game_control_value[GAME_CONTROL_TIME] = time;
3858       game_control_value[GAME_CONTROL_SCORE] = score;
3859
3860       DisplayGameControlValues();
3861 #else
3862       DrawGameValue_Time(time);
3863       DrawGameValue_Score(score);
3864 #endif
3865     }
3866
3867     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3868     {
3869       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
3870       {
3871         /* close exit door after last player */
3872         if ((AllPlayersGone &&
3873              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3874               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3875               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3876             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3877             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3878         {
3879           int element = Feld[ExitX][ExitY];
3880
3881 #if 0
3882           if (element == EL_EM_EXIT_OPEN ||
3883               element == EL_EM_STEEL_EXIT_OPEN)
3884           {
3885             Bang(ExitX, ExitY);
3886           }
3887           else
3888 #endif
3889           {
3890             Feld[ExitX][ExitY] =
3891               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
3892                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
3893                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
3894                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
3895                EL_EM_STEEL_EXIT_CLOSING);
3896
3897             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3898           }
3899         }
3900
3901         /* player disappears */
3902         DrawLevelField(ExitX, ExitY);
3903       }
3904
3905       for (i = 0; i < MAX_PLAYERS; i++)
3906       {
3907         struct PlayerInfo *player = &stored_player[i];
3908
3909         if (player->present)
3910         {
3911           RemovePlayer(player);
3912
3913           /* player disappears */
3914           DrawLevelField(player->jx, player->jy);
3915         }
3916       }
3917     }
3918
3919     PlaySound(SND_GAME_WINNING);
3920   }
3921
3922   if (game_over_delay_1 > 0)
3923   {
3924     game_over_delay_1--;
3925
3926     return;
3927   }
3928
3929   if (time != time_final)
3930   {
3931     int time_to_go = ABS(time_final - time);
3932     int time_count_dir = (time < time_final ? +1 : -1);
3933     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
3934
3935     time  += time_count_steps * time_count_dir;
3936     score += time_count_steps * level.score[SC_TIME_BONUS];
3937
3938 #if 1
3939     game_control_value[GAME_CONTROL_TIME] = time;
3940     game_control_value[GAME_CONTROL_SCORE] = score;
3941
3942     DisplayGameControlValues();
3943 #else
3944     DrawGameValue_Time(time);
3945     DrawGameValue_Score(score);
3946 #endif
3947
3948     if (time == time_final)
3949       StopSound(SND_GAME_LEVELTIME_BONUS);
3950     else if (setup.sound_loops)
3951       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
3952     else
3953       PlaySound(SND_GAME_LEVELTIME_BONUS);
3954
3955     return;
3956   }
3957
3958   local_player->LevelSolved_PanelOff = TRUE;
3959
3960   if (game_over_delay_2 > 0)
3961   {
3962     game_over_delay_2--;
3963
3964     return;
3965   }
3966
3967 #if 1
3968   GameEnd();
3969 #endif
3970 }
3971
3972 void GameEnd()
3973 {
3974   int hi_pos;
3975   boolean raise_level = FALSE;
3976
3977   local_player->LevelSolved_GameEnd = TRUE;
3978
3979   CloseDoor(DOOR_CLOSE_1);
3980
3981   if (local_player->LevelSolved_SaveTape)
3982   {
3983 #if 0
3984     TapeStop();
3985 #endif
3986
3987 #if 1
3988     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
3989 #else
3990     SaveTape(tape.level_nr);            /* ask to save tape */
3991 #endif
3992   }
3993
3994   if (level_editor_test_game)
3995   {
3996     game_status = GAME_MODE_MAIN;
3997
3998 #if 1
3999     DrawAndFadeInMainMenu(REDRAW_FIELD);
4000 #else
4001     DrawMainMenu();
4002 #endif
4003
4004     return;
4005   }
4006
4007   if (!local_player->LevelSolved_SaveScore)
4008   {
4009 #if 1
4010     FadeOut(REDRAW_FIELD);
4011 #endif
4012
4013     game_status = GAME_MODE_MAIN;
4014
4015     DrawAndFadeInMainMenu(REDRAW_FIELD);
4016
4017     return;
4018   }
4019
4020   if (level_nr == leveldir_current->handicap_level)
4021   {
4022     leveldir_current->handicap_level++;
4023     SaveLevelSetup_SeriesInfo();
4024   }
4025
4026   if (level_nr < leveldir_current->last_level)
4027     raise_level = TRUE;                 /* advance to next level */
4028
4029   if ((hi_pos = NewHiScore()) >= 0) 
4030   {
4031     game_status = GAME_MODE_SCORES;
4032
4033     DrawHallOfFame(hi_pos);
4034
4035     if (raise_level)
4036     {
4037       level_nr++;
4038       TapeErase();
4039     }
4040   }
4041   else
4042   {
4043 #if 1
4044     FadeOut(REDRAW_FIELD);
4045 #endif
4046
4047     game_status = GAME_MODE_MAIN;
4048
4049     if (raise_level)
4050     {
4051       level_nr++;
4052       TapeErase();
4053     }
4054
4055     DrawAndFadeInMainMenu(REDRAW_FIELD);
4056   }
4057 }
4058
4059 int NewHiScore()
4060 {
4061   int k, l;
4062   int position = -1;
4063
4064   LoadScore(level_nr);
4065
4066   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4067       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4068     return -1;
4069
4070   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4071   {
4072     if (local_player->score_final > highscore[k].Score)
4073     {
4074       /* player has made it to the hall of fame */
4075
4076       if (k < MAX_SCORE_ENTRIES - 1)
4077       {
4078         int m = MAX_SCORE_ENTRIES - 1;
4079
4080 #ifdef ONE_PER_NAME
4081         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4082           if (strEqual(setup.player_name, highscore[l].Name))
4083             m = l;
4084         if (m == k)     /* player's new highscore overwrites his old one */
4085           goto put_into_list;
4086 #endif
4087
4088         for (l = m; l > k; l--)
4089         {
4090           strcpy(highscore[l].Name, highscore[l - 1].Name);
4091           highscore[l].Score = highscore[l - 1].Score;
4092         }
4093       }
4094
4095 #ifdef ONE_PER_NAME
4096       put_into_list:
4097 #endif
4098       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4099       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4100       highscore[k].Score = local_player->score_final; 
4101       position = k;
4102       break;
4103     }
4104
4105 #ifdef ONE_PER_NAME
4106     else if (!strncmp(setup.player_name, highscore[k].Name,
4107                       MAX_PLAYER_NAME_LEN))
4108       break;    /* player already there with a higher score */
4109 #endif
4110
4111   }
4112
4113   if (position >= 0) 
4114     SaveScore(level_nr);
4115
4116   return position;
4117 }
4118
4119 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4120 {
4121   int element = Feld[x][y];
4122   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4123   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4124   int horiz_move = (dx != 0);
4125   int sign = (horiz_move ? dx : dy);
4126   int step = sign * element_info[element].move_stepsize;
4127
4128   /* special values for move stepsize for spring and things on conveyor belt */
4129   if (horiz_move)
4130   {
4131     if (CAN_FALL(element) &&
4132         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4133       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4134     else if (element == EL_SPRING)
4135       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4136   }
4137
4138   return step;
4139 }
4140
4141 inline static int getElementMoveStepsize(int x, int y)
4142 {
4143   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4144 }
4145
4146 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4147 {
4148   if (player->GfxAction != action || player->GfxDir != dir)
4149   {
4150 #if 0
4151     printf("Player frame reset! (%d => %d, %d => %d)\n",
4152            player->GfxAction, action, player->GfxDir, dir);
4153 #endif
4154
4155     player->GfxAction = action;
4156     player->GfxDir = dir;
4157     player->Frame = 0;
4158     player->StepFrame = 0;
4159   }
4160 }
4161
4162 #if USE_GFX_RESET_GFX_ANIMATION
4163 static void ResetGfxFrame(int x, int y, boolean redraw)
4164 {
4165   int element = Feld[x][y];
4166   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4167   int last_gfx_frame = GfxFrame[x][y];
4168
4169   if (graphic_info[graphic].anim_global_sync)
4170     GfxFrame[x][y] = FrameCounter;
4171   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4172     GfxFrame[x][y] = CustomValue[x][y];
4173   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4174     GfxFrame[x][y] = element_info[element].collect_score;
4175   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4176     GfxFrame[x][y] = ChangeDelay[x][y];
4177
4178   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4179     DrawLevelGraphicAnimation(x, y, graphic);
4180 }
4181 #endif
4182
4183 static void ResetGfxAnimation(int x, int y)
4184 {
4185   GfxAction[x][y] = ACTION_DEFAULT;
4186   GfxDir[x][y] = MovDir[x][y];
4187   GfxFrame[x][y] = 0;
4188
4189 #if USE_GFX_RESET_GFX_ANIMATION
4190   ResetGfxFrame(x, y, FALSE);
4191 #endif
4192 }
4193
4194 static void ResetRandomAnimationValue(int x, int y)
4195 {
4196   GfxRandom[x][y] = INIT_GFX_RANDOM();
4197 }
4198
4199 void InitMovingField(int x, int y, int direction)
4200 {
4201   int element = Feld[x][y];
4202   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4203   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4204   int newx = x + dx;
4205   int newy = y + dy;
4206   boolean is_moving_before, is_moving_after;
4207 #if 0
4208   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4209 #endif
4210
4211   /* check if element was/is moving or being moved before/after mode change */
4212 #if 1
4213 #if 1
4214   is_moving_before = (WasJustMoving[x][y] != 0);
4215 #else
4216   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4217   is_moving_before = WasJustMoving[x][y];
4218 #endif
4219 #else
4220   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4221 #endif
4222   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4223
4224   /* reset animation only for moving elements which change direction of moving
4225      or which just started or stopped moving
4226      (else CEs with property "can move" / "not moving" are reset each frame) */
4227 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4228 #if 1
4229   if (is_moving_before != is_moving_after ||
4230       direction != MovDir[x][y])
4231     ResetGfxAnimation(x, y);
4232 #else
4233   if ((is_moving_before || is_moving_after) && !continues_moving)
4234     ResetGfxAnimation(x, y);
4235 #endif
4236 #else
4237   if (!continues_moving)
4238     ResetGfxAnimation(x, y);
4239 #endif
4240
4241   MovDir[x][y] = direction;
4242   GfxDir[x][y] = direction;
4243
4244 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4245   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4246                      direction == MV_DOWN && CAN_FALL(element) ?
4247                      ACTION_FALLING : ACTION_MOVING);
4248 #else
4249   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4250                      ACTION_FALLING : ACTION_MOVING);
4251 #endif
4252
4253   /* this is needed for CEs with property "can move" / "not moving" */
4254
4255   if (is_moving_after)
4256   {
4257     if (Feld[newx][newy] == EL_EMPTY)
4258       Feld[newx][newy] = EL_BLOCKED;
4259
4260     MovDir[newx][newy] = MovDir[x][y];
4261
4262 #if USE_NEW_CUSTOM_VALUE
4263     CustomValue[newx][newy] = CustomValue[x][y];
4264 #endif
4265
4266     GfxFrame[newx][newy] = GfxFrame[x][y];
4267     GfxRandom[newx][newy] = GfxRandom[x][y];
4268     GfxAction[newx][newy] = GfxAction[x][y];
4269     GfxDir[newx][newy] = GfxDir[x][y];
4270   }
4271 }
4272
4273 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4274 {
4275   int direction = MovDir[x][y];
4276   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4277   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4278
4279   *goes_to_x = newx;
4280   *goes_to_y = newy;
4281 }
4282
4283 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4284 {
4285   int oldx = x, oldy = y;
4286   int direction = MovDir[x][y];
4287
4288   if (direction == MV_LEFT)
4289     oldx++;
4290   else if (direction == MV_RIGHT)
4291     oldx--;
4292   else if (direction == MV_UP)
4293     oldy++;
4294   else if (direction == MV_DOWN)
4295     oldy--;
4296
4297   *comes_from_x = oldx;
4298   *comes_from_y = oldy;
4299 }
4300
4301 int MovingOrBlocked2Element(int x, int y)
4302 {
4303   int element = Feld[x][y];
4304
4305   if (element == EL_BLOCKED)
4306   {
4307     int oldx, oldy;
4308
4309     Blocked2Moving(x, y, &oldx, &oldy);
4310     return Feld[oldx][oldy];
4311   }
4312   else
4313     return element;
4314 }
4315
4316 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4317 {
4318   /* like MovingOrBlocked2Element(), but if element is moving
4319      and (x,y) is the field the moving element is just leaving,
4320      return EL_BLOCKED instead of the element value */
4321   int element = Feld[x][y];
4322
4323   if (IS_MOVING(x, y))
4324   {
4325     if (element == EL_BLOCKED)
4326     {
4327       int oldx, oldy;
4328
4329       Blocked2Moving(x, y, &oldx, &oldy);
4330       return Feld[oldx][oldy];
4331     }
4332     else
4333       return EL_BLOCKED;
4334   }
4335   else
4336     return element;
4337 }
4338
4339 static void RemoveField(int x, int y)
4340 {
4341   Feld[x][y] = EL_EMPTY;
4342
4343   MovPos[x][y] = 0;
4344   MovDir[x][y] = 0;
4345   MovDelay[x][y] = 0;
4346
4347 #if USE_NEW_CUSTOM_VALUE
4348   CustomValue[x][y] = 0;
4349 #endif
4350
4351   AmoebaNr[x][y] = 0;
4352   ChangeDelay[x][y] = 0;
4353   ChangePage[x][y] = -1;
4354   Pushed[x][y] = FALSE;
4355
4356 #if 0
4357   ExplodeField[x][y] = EX_TYPE_NONE;
4358 #endif
4359
4360   GfxElement[x][y] = EL_UNDEFINED;
4361   GfxAction[x][y] = ACTION_DEFAULT;
4362   GfxDir[x][y] = MV_NONE;
4363 }
4364
4365 void RemoveMovingField(int x, int y)
4366 {
4367   int oldx = x, oldy = y, newx = x, newy = y;
4368   int element = Feld[x][y];
4369   int next_element = EL_UNDEFINED;
4370
4371   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4372     return;
4373
4374   if (IS_MOVING(x, y))
4375   {
4376     Moving2Blocked(x, y, &newx, &newy);
4377
4378     if (Feld[newx][newy] != EL_BLOCKED)
4379     {
4380       /* element is moving, but target field is not free (blocked), but
4381          already occupied by something different (example: acid pool);
4382          in this case, only remove the moving field, but not the target */
4383
4384       RemoveField(oldx, oldy);
4385
4386       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4387
4388       DrawLevelField(oldx, oldy);
4389
4390       return;
4391     }
4392   }
4393   else if (element == EL_BLOCKED)
4394   {
4395     Blocked2Moving(x, y, &oldx, &oldy);
4396     if (!IS_MOVING(oldx, oldy))
4397       return;
4398   }
4399
4400   if (element == EL_BLOCKED &&
4401       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4402        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4403        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4404        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4405        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4406        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4407     next_element = get_next_element(Feld[oldx][oldy]);
4408
4409   RemoveField(oldx, oldy);
4410   RemoveField(newx, newy);
4411
4412   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4413
4414   if (next_element != EL_UNDEFINED)
4415     Feld[oldx][oldy] = next_element;
4416
4417   DrawLevelField(oldx, oldy);
4418   DrawLevelField(newx, newy);
4419 }
4420
4421 void DrawDynamite(int x, int y)
4422 {
4423   int sx = SCREENX(x), sy = SCREENY(y);
4424   int graphic = el2img(Feld[x][y]);
4425   int frame;
4426
4427   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4428     return;
4429
4430   if (IS_WALKABLE_INSIDE(Back[x][y]))
4431     return;
4432
4433   if (Back[x][y])
4434     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4435   else if (Store[x][y])
4436     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4437
4438   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4439
4440   if (Back[x][y] || Store[x][y])
4441     DrawGraphicThruMask(sx, sy, graphic, frame);
4442   else
4443     DrawGraphic(sx, sy, graphic, frame);
4444 }
4445
4446 void CheckDynamite(int x, int y)
4447 {
4448   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4449   {
4450     MovDelay[x][y]--;
4451
4452     if (MovDelay[x][y] != 0)
4453     {
4454       DrawDynamite(x, y);
4455       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4456
4457       return;
4458     }
4459   }
4460
4461   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4462
4463   Bang(x, y);
4464 }
4465
4466 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4467 {
4468   boolean num_checked_players = 0;
4469   int i;
4470
4471   for (i = 0; i < MAX_PLAYERS; i++)
4472   {
4473     if (stored_player[i].active)
4474     {
4475       int sx = stored_player[i].jx;
4476       int sy = stored_player[i].jy;
4477
4478       if (num_checked_players == 0)
4479       {
4480         *sx1 = *sx2 = sx;
4481         *sy1 = *sy2 = sy;
4482       }
4483       else
4484       {
4485         *sx1 = MIN(*sx1, sx);
4486         *sy1 = MIN(*sy1, sy);
4487         *sx2 = MAX(*sx2, sx);
4488         *sy2 = MAX(*sy2, sy);
4489       }
4490
4491       num_checked_players++;
4492     }
4493   }
4494 }
4495
4496 static boolean checkIfAllPlayersFitToScreen_RND()
4497 {
4498   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4499
4500   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4501
4502   return (sx2 - sx1 < SCR_FIELDX &&
4503           sy2 - sy1 < SCR_FIELDY);
4504 }
4505
4506 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4507 {
4508   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4509
4510   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4511
4512   *sx = (sx1 + sx2) / 2;
4513   *sy = (sy1 + sy2) / 2;
4514 }
4515
4516 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4517                         boolean center_screen, boolean quick_relocation)
4518 {
4519   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4520   boolean no_delay = (tape.warp_forward);
4521   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4522   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4523
4524   if (quick_relocation)
4525   {
4526     int offset = (setup.scroll_delay ? setup.scroll_delay_value : 0);
4527
4528     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4529     {
4530       if (!level.shifted_relocation || center_screen)
4531       {
4532         /* quick relocation (without scrolling), with centering of screen */
4533
4534         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4535                     x > SBX_Right + MIDPOSX ? SBX_Right :
4536                     x - MIDPOSX);
4537
4538         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4539                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4540                     y - MIDPOSY);
4541       }
4542       else
4543       {
4544         /* quick relocation (without scrolling), but do not center screen */
4545
4546         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4547                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4548                                old_x - MIDPOSX);
4549
4550         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4551                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4552                                old_y - MIDPOSY);
4553
4554         int offset_x = x + (scroll_x - center_scroll_x);
4555         int offset_y = y + (scroll_y - center_scroll_y);
4556
4557         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4558                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4559                     offset_x - MIDPOSX);
4560
4561         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4562                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4563                     offset_y - MIDPOSY);
4564       }
4565     }
4566     else
4567     {
4568       /* quick relocation (without scrolling), inside visible screen area */
4569
4570       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
4571           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4572         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4573
4574       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
4575           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4576         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4577
4578       /* don't scroll over playfield boundaries */
4579       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4580         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4581
4582       /* don't scroll over playfield boundaries */
4583       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4584         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4585     }
4586
4587     RedrawPlayfield(TRUE, 0,0,0,0);
4588   }
4589   else
4590   {
4591 #if 1
4592     int scroll_xx, scroll_yy;
4593
4594     if (!level.shifted_relocation || center_screen)
4595     {
4596       /* visible relocation (with scrolling), with centering of screen */
4597
4598       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4599                    x > SBX_Right + MIDPOSX ? SBX_Right :
4600                    x - MIDPOSX);
4601
4602       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4603                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
4604                    y - MIDPOSY);
4605     }
4606     else
4607     {
4608       /* visible relocation (with scrolling), but do not center screen */
4609
4610       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4611                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
4612                              old_x - MIDPOSX);
4613
4614       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4615                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4616                              old_y - MIDPOSY);
4617
4618       int offset_x = x + (scroll_x - center_scroll_x);
4619       int offset_y = y + (scroll_y - center_scroll_y);
4620
4621       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4622                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4623                    offset_x - MIDPOSX);
4624
4625       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4626                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4627                    offset_y - MIDPOSY);
4628     }
4629
4630 #else
4631
4632     /* visible relocation (with scrolling), with centering of screen */
4633
4634     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4635                      x > SBX_Right + MIDPOSX ? SBX_Right :
4636                      x - MIDPOSX);
4637
4638     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4639                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
4640                      y - MIDPOSY);
4641 #endif
4642
4643     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
4644
4645     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4646     {
4647       int dx = 0, dy = 0;
4648       int fx = FX, fy = FY;
4649
4650       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4651       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4652
4653       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
4654         break;
4655
4656       scroll_x -= dx;
4657       scroll_y -= dy;
4658
4659       fx += dx * TILEX / 2;
4660       fy += dy * TILEY / 2;
4661
4662       ScrollLevel(dx, dy);
4663       DrawAllPlayers();
4664
4665       /* scroll in two steps of half tile size to make things smoother */
4666       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4667       FlushDisplay();
4668       Delay(wait_delay_value);
4669
4670       /* scroll second step to align at full tile size */
4671       BackToFront();
4672       Delay(wait_delay_value);
4673     }
4674
4675     DrawAllPlayers();
4676     BackToFront();
4677     Delay(wait_delay_value);
4678   }
4679 }
4680
4681 void RelocatePlayer(int jx, int jy, int el_player_raw)
4682 {
4683   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4684   int player_nr = GET_PLAYER_NR(el_player);
4685   struct PlayerInfo *player = &stored_player[player_nr];
4686   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4687   boolean no_delay = (tape.warp_forward);
4688   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4689   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4690   int old_jx = player->jx;
4691   int old_jy = player->jy;
4692   int old_element = Feld[old_jx][old_jy];
4693   int element = Feld[jx][jy];
4694   boolean player_relocated = (old_jx != jx || old_jy != jy);
4695
4696   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4697   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
4698   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4699   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
4700   int leave_side_horiz = move_dir_horiz;
4701   int leave_side_vert  = move_dir_vert;
4702   int enter_side = enter_side_horiz | enter_side_vert;
4703   int leave_side = leave_side_horiz | leave_side_vert;
4704
4705   if (player->GameOver)         /* do not reanimate dead player */
4706     return;
4707
4708   if (!player_relocated)        /* no need to relocate the player */
4709     return;
4710
4711   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
4712   {
4713     RemoveField(jx, jy);        /* temporarily remove newly placed player */
4714     DrawLevelField(jx, jy);
4715   }
4716
4717   if (player->present)
4718   {
4719     while (player->MovPos)
4720     {
4721       ScrollPlayer(player, SCROLL_GO_ON);
4722       ScrollScreen(NULL, SCROLL_GO_ON);
4723
4724       AdvanceFrameAndPlayerCounters(player->index_nr);
4725
4726       DrawPlayer(player);
4727
4728       BackToFront();
4729       Delay(wait_delay_value);
4730     }
4731
4732     DrawPlayer(player);         /* needed here only to cleanup last field */
4733     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
4734
4735     player->is_moving = FALSE;
4736   }
4737
4738   if (IS_CUSTOM_ELEMENT(old_element))
4739     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4740                                CE_LEFT_BY_PLAYER,
4741                                player->index_bit, leave_side);
4742
4743   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4744                                       CE_PLAYER_LEAVES_X,
4745                                       player->index_bit, leave_side);
4746
4747   Feld[jx][jy] = el_player;
4748   InitPlayerField(jx, jy, el_player, TRUE);
4749
4750   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4751   {
4752     Feld[jx][jy] = element;
4753     InitField(jx, jy, FALSE);
4754   }
4755
4756   /* only visually relocate centered player */
4757   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4758                      FALSE, level.instant_relocation);
4759
4760   TestIfPlayerTouchesBadThing(jx, jy);
4761   TestIfPlayerTouchesCustomElement(jx, jy);
4762
4763   if (IS_CUSTOM_ELEMENT(element))
4764     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4765                                player->index_bit, enter_side);
4766
4767   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4768                                       player->index_bit, enter_side);
4769 }
4770
4771 void Explode(int ex, int ey, int phase, int mode)
4772 {
4773   int x, y;
4774   int last_phase;
4775   int border_element;
4776
4777   /* !!! eliminate this variable !!! */
4778   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4779
4780   if (game.explosions_delayed)
4781   {
4782     ExplodeField[ex][ey] = mode;
4783     return;
4784   }
4785
4786   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
4787   {
4788     int center_element = Feld[ex][ey];
4789     int artwork_element, explosion_element;     /* set these values later */
4790
4791 #if 0
4792     /* --- This is only really needed (and now handled) in "Impact()". --- */
4793     /* do not explode moving elements that left the explode field in time */
4794     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4795         center_element == EL_EMPTY &&
4796         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4797       return;
4798 #endif
4799
4800 #if 0
4801     /* !!! at this place, the center element may be EL_BLOCKED !!! */
4802     if (mode == EX_TYPE_NORMAL ||
4803         mode == EX_TYPE_CENTER ||
4804         mode == EX_TYPE_CROSS)
4805       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4806 #endif
4807
4808     /* remove things displayed in background while burning dynamite */
4809     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4810       Back[ex][ey] = 0;
4811
4812     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4813     {
4814       /* put moving element to center field (and let it explode there) */
4815       center_element = MovingOrBlocked2Element(ex, ey);
4816       RemoveMovingField(ex, ey);
4817       Feld[ex][ey] = center_element;
4818     }
4819
4820     /* now "center_element" is finally determined -- set related values now */
4821     artwork_element = center_element;           /* for custom player artwork */
4822     explosion_element = center_element;         /* for custom player artwork */
4823
4824     if (IS_PLAYER(ex, ey))
4825     {
4826       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4827
4828       artwork_element = stored_player[player_nr].artwork_element;
4829
4830       if (level.use_explosion_element[player_nr])
4831       {
4832         explosion_element = level.explosion_element[player_nr];
4833         artwork_element = explosion_element;
4834       }
4835     }
4836
4837 #if 1
4838     if (mode == EX_TYPE_NORMAL ||
4839         mode == EX_TYPE_CENTER ||
4840         mode == EX_TYPE_CROSS)
4841       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4842 #endif
4843
4844     last_phase = element_info[explosion_element].explosion_delay + 1;
4845
4846     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4847     {
4848       int xx = x - ex + 1;
4849       int yy = y - ey + 1;
4850       int element;
4851
4852       if (!IN_LEV_FIELD(x, y) ||
4853           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4854           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
4855         continue;
4856
4857       element = Feld[x][y];
4858
4859       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4860       {
4861         element = MovingOrBlocked2Element(x, y);
4862
4863         if (!IS_EXPLOSION_PROOF(element))
4864           RemoveMovingField(x, y);
4865       }
4866
4867       /* indestructible elements can only explode in center (but not flames) */
4868       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4869                                            mode == EX_TYPE_BORDER)) ||
4870           element == EL_FLAMES)
4871         continue;
4872
4873       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4874          behaviour, for example when touching a yamyam that explodes to rocks
4875          with active deadly shield, a rock is created under the player !!! */
4876       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4877 #if 0
4878       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4879           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
4880            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
4881 #else
4882       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
4883 #endif
4884       {
4885         if (IS_ACTIVE_BOMB(element))
4886         {
4887           /* re-activate things under the bomb like gate or penguin */
4888           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
4889           Back[x][y] = 0;
4890         }
4891
4892         continue;
4893       }
4894
4895       /* save walkable background elements while explosion on same tile */
4896       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
4897           (x != ex || y != ey || mode == EX_TYPE_BORDER))
4898         Back[x][y] = element;
4899
4900       /* ignite explodable elements reached by other explosion */
4901       if (element == EL_EXPLOSION)
4902         element = Store2[x][y];
4903
4904       if (AmoebaNr[x][y] &&
4905           (element == EL_AMOEBA_FULL ||
4906            element == EL_BD_AMOEBA ||
4907            element == EL_AMOEBA_GROWING))
4908       {
4909         AmoebaCnt[AmoebaNr[x][y]]--;
4910         AmoebaCnt2[AmoebaNr[x][y]]--;
4911       }
4912
4913       RemoveField(x, y);
4914
4915       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
4916       {
4917         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
4918
4919         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
4920
4921         if (PLAYERINFO(ex, ey)->use_murphy)
4922           Store[x][y] = EL_EMPTY;
4923       }
4924
4925       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
4926          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
4927       else if (ELEM_IS_PLAYER(center_element))
4928         Store[x][y] = EL_EMPTY;
4929       else if (center_element == EL_YAMYAM)
4930         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4931       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4932         Store[x][y] = element_info[center_element].content.e[xx][yy];
4933 #if 1
4934       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4935          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4936          otherwise) -- FIX THIS !!! */
4937       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4938         Store[x][y] = element_info[element].content.e[1][1];
4939 #else
4940       else if (!CAN_EXPLODE(element))
4941         Store[x][y] = element_info[element].content.e[1][1];
4942 #endif
4943       else
4944         Store[x][y] = EL_EMPTY;
4945
4946       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4947           center_element == EL_AMOEBA_TO_DIAMOND)
4948         Store2[x][y] = element;
4949
4950       Feld[x][y] = EL_EXPLOSION;
4951       GfxElement[x][y] = artwork_element;
4952
4953       ExplodePhase[x][y] = 1;
4954       ExplodeDelay[x][y] = last_phase;
4955
4956       Stop[x][y] = TRUE;
4957     }
4958
4959     if (center_element == EL_YAMYAM)
4960       game.yamyam_content_nr =
4961         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4962
4963     return;
4964   }
4965
4966   if (Stop[ex][ey])
4967     return;
4968
4969   x = ex;
4970   y = ey;
4971
4972   if (phase == 1)
4973     GfxFrame[x][y] = 0;         /* restart explosion animation */
4974
4975   last_phase = ExplodeDelay[x][y];
4976
4977   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4978
4979 #ifdef DEBUG
4980
4981   /* activate this even in non-DEBUG version until cause for crash in
4982      getGraphicAnimationFrame() (see below) is found and eliminated */
4983
4984 #endif
4985 #if 1
4986
4987 #if 1
4988   /* this can happen if the player leaves an explosion just in time */
4989   if (GfxElement[x][y] == EL_UNDEFINED)
4990     GfxElement[x][y] = EL_EMPTY;
4991 #else
4992   if (GfxElement[x][y] == EL_UNDEFINED)
4993   {
4994     printf("\n\n");
4995     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4996     printf("Explode(): This should never happen!\n");
4997     printf("\n\n");
4998
4999     GfxElement[x][y] = EL_EMPTY;
5000   }
5001 #endif
5002
5003 #endif
5004
5005   border_element = Store2[x][y];
5006   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5007     border_element = StorePlayer[x][y];
5008
5009   if (phase == element_info[border_element].ignition_delay ||
5010       phase == last_phase)
5011   {
5012     boolean border_explosion = FALSE;
5013
5014     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5015         !PLAYER_EXPLOSION_PROTECTED(x, y))
5016     {
5017       KillPlayerUnlessExplosionProtected(x, y);
5018       border_explosion = TRUE;
5019     }
5020     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5021     {
5022       Feld[x][y] = Store2[x][y];
5023       Store2[x][y] = 0;
5024       Bang(x, y);
5025       border_explosion = TRUE;
5026     }
5027     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5028     {
5029       AmoebeUmwandeln(x, y);
5030       Store2[x][y] = 0;
5031       border_explosion = TRUE;
5032     }
5033
5034     /* if an element just explodes due to another explosion (chain-reaction),
5035        do not immediately end the new explosion when it was the last frame of
5036        the explosion (as it would be done in the following "if"-statement!) */
5037     if (border_explosion && phase == last_phase)
5038       return;
5039   }
5040
5041   if (phase == last_phase)
5042   {
5043     int element;
5044
5045     element = Feld[x][y] = Store[x][y];
5046     Store[x][y] = Store2[x][y] = 0;
5047     GfxElement[x][y] = EL_UNDEFINED;
5048
5049     /* player can escape from explosions and might therefore be still alive */
5050     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5051         element <= EL_PLAYER_IS_EXPLODING_4)
5052     {
5053       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5054       int explosion_element = EL_PLAYER_1 + player_nr;
5055       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5056       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5057
5058       if (level.use_explosion_element[player_nr])
5059         explosion_element = level.explosion_element[player_nr];
5060
5061       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5062                     element_info[explosion_element].content.e[xx][yy]);
5063     }
5064
5065     /* restore probably existing indestructible background element */
5066     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5067       element = Feld[x][y] = Back[x][y];
5068     Back[x][y] = 0;
5069
5070     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5071     GfxDir[x][y] = MV_NONE;
5072     ChangeDelay[x][y] = 0;
5073     ChangePage[x][y] = -1;
5074
5075 #if USE_NEW_CUSTOM_VALUE
5076     CustomValue[x][y] = 0;
5077 #endif
5078
5079     InitField_WithBug2(x, y, FALSE);
5080
5081     DrawLevelField(x, y);
5082
5083     TestIfElementTouchesCustomElement(x, y);
5084
5085     if (GFX_CRUMBLED(element))
5086       DrawLevelFieldCrumbledSandNeighbours(x, y);
5087
5088     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5089       StorePlayer[x][y] = 0;
5090
5091     if (ELEM_IS_PLAYER(element))
5092       RelocatePlayer(x, y, element);
5093   }
5094   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5095   {
5096     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5097     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5098
5099     if (phase == delay)
5100       DrawLevelFieldCrumbledSand(x, y);
5101
5102     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5103     {
5104       DrawLevelElement(x, y, Back[x][y]);
5105       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5106     }
5107     else if (IS_WALKABLE_UNDER(Back[x][y]))
5108     {
5109       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5110       DrawLevelElementThruMask(x, y, Back[x][y]);
5111     }
5112     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5113       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5114   }
5115 }
5116
5117 void DynaExplode(int ex, int ey)
5118 {
5119   int i, j;
5120   int dynabomb_element = Feld[ex][ey];
5121   int dynabomb_size = 1;
5122   boolean dynabomb_xl = FALSE;
5123   struct PlayerInfo *player;
5124   static int xy[4][2] =
5125   {
5126     { 0, -1 },
5127     { -1, 0 },
5128     { +1, 0 },
5129     { 0, +1 }
5130   };
5131
5132   if (IS_ACTIVE_BOMB(dynabomb_element))
5133   {
5134     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5135     dynabomb_size = player->dynabomb_size;
5136     dynabomb_xl = player->dynabomb_xl;
5137     player->dynabombs_left++;
5138   }
5139
5140   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5141
5142   for (i = 0; i < NUM_DIRECTIONS; i++)
5143   {
5144     for (j = 1; j <= dynabomb_size; j++)
5145     {
5146       int x = ex + j * xy[i][0];
5147       int y = ey + j * xy[i][1];
5148       int element;
5149
5150       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5151         break;
5152
5153       element = Feld[x][y];
5154
5155       /* do not restart explosions of fields with active bombs */
5156       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5157         continue;
5158
5159       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5160
5161       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5162           !IS_DIGGABLE(element) && !dynabomb_xl)
5163         break;
5164     }
5165   }
5166 }
5167
5168 void Bang(int x, int y)
5169 {
5170   int element = MovingOrBlocked2Element(x, y);
5171   int explosion_type = EX_TYPE_NORMAL;
5172
5173   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5174   {
5175     struct PlayerInfo *player = PLAYERINFO(x, y);
5176
5177     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5178                             player->element_nr);
5179
5180     if (level.use_explosion_element[player->index_nr])
5181     {
5182       int explosion_element = level.explosion_element[player->index_nr];
5183
5184       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5185         explosion_type = EX_TYPE_CROSS;
5186       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5187         explosion_type = EX_TYPE_CENTER;
5188     }
5189   }
5190
5191   switch (element)
5192   {
5193     case EL_BUG:
5194     case EL_SPACESHIP:
5195     case EL_BD_BUTTERFLY:
5196     case EL_BD_FIREFLY:
5197     case EL_YAMYAM:
5198     case EL_DARK_YAMYAM:
5199     case EL_ROBOT:
5200     case EL_PACMAN:
5201     case EL_MOLE:
5202       RaiseScoreElement(element);
5203       break;
5204
5205     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5206     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5207     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5208     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5209     case EL_DYNABOMB_INCREASE_NUMBER:
5210     case EL_DYNABOMB_INCREASE_SIZE:
5211     case EL_DYNABOMB_INCREASE_POWER:
5212       explosion_type = EX_TYPE_DYNA;
5213       break;
5214
5215     case EL_DC_LANDMINE:
5216 #if 0
5217     case EL_EM_EXIT_OPEN:
5218     case EL_EM_STEEL_EXIT_OPEN:
5219 #endif
5220       explosion_type = EX_TYPE_CENTER;
5221       break;
5222
5223     case EL_PENGUIN:
5224     case EL_LAMP:
5225     case EL_LAMP_ACTIVE:
5226     case EL_AMOEBA_TO_DIAMOND:
5227       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5228         explosion_type = EX_TYPE_CENTER;
5229       break;
5230
5231     default:
5232       if (element_info[element].explosion_type == EXPLODES_CROSS)
5233         explosion_type = EX_TYPE_CROSS;
5234       else if (element_info[element].explosion_type == EXPLODES_1X1)
5235         explosion_type = EX_TYPE_CENTER;
5236       break;
5237   }
5238
5239   if (explosion_type == EX_TYPE_DYNA)
5240     DynaExplode(x, y);
5241   else
5242     Explode(x, y, EX_PHASE_START, explosion_type);
5243
5244   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5245 }
5246
5247 void SplashAcid(int x, int y)
5248 {
5249   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5250       (!IN_LEV_FIELD(x - 1, y - 2) ||
5251        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5252     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5253
5254   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5255       (!IN_LEV_FIELD(x + 1, y - 2) ||
5256        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5257     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5258
5259   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5260 }
5261
5262 static void InitBeltMovement()
5263 {
5264   static int belt_base_element[4] =
5265   {
5266     EL_CONVEYOR_BELT_1_LEFT,
5267     EL_CONVEYOR_BELT_2_LEFT,
5268     EL_CONVEYOR_BELT_3_LEFT,
5269     EL_CONVEYOR_BELT_4_LEFT
5270   };
5271   static int belt_base_active_element[4] =
5272   {
5273     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5274     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5275     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5276     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5277   };
5278
5279   int x, y, i, j;
5280
5281   /* set frame order for belt animation graphic according to belt direction */
5282   for (i = 0; i < NUM_BELTS; i++)
5283   {
5284     int belt_nr = i;
5285
5286     for (j = 0; j < NUM_BELT_PARTS; j++)
5287     {
5288       int element = belt_base_active_element[belt_nr] + j;
5289       int graphic = el2img(element);
5290
5291       if (game.belt_dir[i] == MV_LEFT)
5292         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5293       else
5294         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
5295     }
5296   }
5297
5298   SCAN_PLAYFIELD(x, y)
5299   {
5300     int element = Feld[x][y];
5301
5302     for (i = 0; i < NUM_BELTS; i++)
5303     {
5304       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5305       {
5306         int e_belt_nr = getBeltNrFromBeltElement(element);
5307         int belt_nr = i;
5308
5309         if (e_belt_nr == belt_nr)
5310         {
5311           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5312
5313           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5314         }
5315       }
5316     }
5317   }
5318 }
5319
5320 static void ToggleBeltSwitch(int x, int y)
5321 {
5322   static int belt_base_element[4] =
5323   {
5324     EL_CONVEYOR_BELT_1_LEFT,
5325     EL_CONVEYOR_BELT_2_LEFT,
5326     EL_CONVEYOR_BELT_3_LEFT,
5327     EL_CONVEYOR_BELT_4_LEFT
5328   };
5329   static int belt_base_active_element[4] =
5330   {
5331     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5332     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5333     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5334     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5335   };
5336   static int belt_base_switch_element[4] =
5337   {
5338     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5339     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5340     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5341     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5342   };
5343   static int belt_move_dir[4] =
5344   {
5345     MV_LEFT,
5346     MV_NONE,
5347     MV_RIGHT,
5348     MV_NONE,
5349   };
5350
5351   int element = Feld[x][y];
5352   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5353   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5354   int belt_dir = belt_move_dir[belt_dir_nr];
5355   int xx, yy, i;
5356
5357   if (!IS_BELT_SWITCH(element))
5358     return;
5359
5360   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5361   game.belt_dir[belt_nr] = belt_dir;
5362
5363   if (belt_dir_nr == 3)
5364     belt_dir_nr = 1;
5365
5366   /* set frame order for belt animation graphic according to belt direction */
5367   for (i = 0; i < NUM_BELT_PARTS; i++)
5368   {
5369     int element = belt_base_active_element[belt_nr] + i;
5370     int graphic = el2img(element);
5371
5372     if (belt_dir == MV_LEFT)
5373       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5374     else
5375       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
5376   }
5377
5378   SCAN_PLAYFIELD(xx, yy)
5379   {
5380     int element = Feld[xx][yy];
5381
5382     if (IS_BELT_SWITCH(element))
5383     {
5384       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5385
5386       if (e_belt_nr == belt_nr)
5387       {
5388         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5389         DrawLevelField(xx, yy);
5390       }
5391     }
5392     else if (IS_BELT(element) && belt_dir != MV_NONE)
5393     {
5394       int e_belt_nr = getBeltNrFromBeltElement(element);
5395
5396       if (e_belt_nr == belt_nr)
5397       {
5398         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5399
5400         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5401         DrawLevelField(xx, yy);
5402       }
5403     }
5404     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5405     {
5406       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5407
5408       if (e_belt_nr == belt_nr)
5409       {
5410         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5411
5412         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5413         DrawLevelField(xx, yy);
5414       }
5415     }
5416   }
5417 }
5418
5419 static void ToggleSwitchgateSwitch(int x, int y)
5420 {
5421   int xx, yy;
5422
5423   game.switchgate_pos = !game.switchgate_pos;
5424
5425   SCAN_PLAYFIELD(xx, yy)
5426   {
5427     int element = Feld[xx][yy];
5428
5429 #if !USE_BOTH_SWITCHGATE_SWITCHES
5430     if (element == EL_SWITCHGATE_SWITCH_UP ||
5431         element == EL_SWITCHGATE_SWITCH_DOWN)
5432     {
5433       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5434       DrawLevelField(xx, yy);
5435     }
5436     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5437              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5438     {
5439       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5440       DrawLevelField(xx, yy);
5441     }
5442 #else
5443     if (element == EL_SWITCHGATE_SWITCH_UP)
5444     {
5445       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5446       DrawLevelField(xx, yy);
5447     }
5448     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5449     {
5450       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5451       DrawLevelField(xx, yy);
5452     }
5453     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5454     {
5455       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5456       DrawLevelField(xx, yy);
5457     }
5458     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5459     {
5460       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5461       DrawLevelField(xx, yy);
5462     }
5463 #endif
5464     else if (element == EL_SWITCHGATE_OPEN ||
5465              element == EL_SWITCHGATE_OPENING)
5466     {
5467       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5468
5469       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5470     }
5471     else if (element == EL_SWITCHGATE_CLOSED ||
5472              element == EL_SWITCHGATE_CLOSING)
5473     {
5474       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5475
5476       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5477     }
5478   }
5479 }
5480
5481 static int getInvisibleActiveFromInvisibleElement(int element)
5482 {
5483   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5484           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5485           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5486           element);
5487 }
5488
5489 static int getInvisibleFromInvisibleActiveElement(int element)
5490 {
5491   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5492           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5493           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5494           element);
5495 }
5496
5497 static void RedrawAllLightSwitchesAndInvisibleElements()
5498 {
5499   int x, y;
5500
5501   SCAN_PLAYFIELD(x, y)
5502   {
5503     int element = Feld[x][y];
5504
5505     if (element == EL_LIGHT_SWITCH &&
5506         game.light_time_left > 0)
5507     {
5508       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5509       DrawLevelField(x, y);
5510     }
5511     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5512              game.light_time_left == 0)
5513     {
5514       Feld[x][y] = EL_LIGHT_SWITCH;
5515       DrawLevelField(x, y);
5516     }
5517     else if (element == EL_EMC_DRIPPER &&
5518              game.light_time_left > 0)
5519     {
5520       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5521       DrawLevelField(x, y);
5522     }
5523     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5524              game.light_time_left == 0)
5525     {
5526       Feld[x][y] = EL_EMC_DRIPPER;
5527       DrawLevelField(x, y);
5528     }
5529     else if (element == EL_INVISIBLE_STEELWALL ||
5530              element == EL_INVISIBLE_WALL ||
5531              element == EL_INVISIBLE_SAND)
5532     {
5533       if (game.light_time_left > 0)
5534         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5535
5536       DrawLevelField(x, y);
5537
5538       /* uncrumble neighbour fields, if needed */
5539       if (element == EL_INVISIBLE_SAND)
5540         DrawLevelFieldCrumbledSandNeighbours(x, y);
5541     }
5542     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5543              element == EL_INVISIBLE_WALL_ACTIVE ||
5544              element == EL_INVISIBLE_SAND_ACTIVE)
5545     {
5546       if (game.light_time_left == 0)
5547         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5548
5549       DrawLevelField(x, y);
5550
5551       /* re-crumble neighbour fields, if needed */
5552       if (element == EL_INVISIBLE_SAND)
5553         DrawLevelFieldCrumbledSandNeighbours(x, y);
5554     }
5555   }
5556 }
5557
5558 static void RedrawAllInvisibleElementsForLenses()
5559 {
5560   int x, y;
5561
5562   SCAN_PLAYFIELD(x, y)
5563   {
5564     int element = Feld[x][y];
5565
5566     if (element == EL_EMC_DRIPPER &&
5567         game.lenses_time_left > 0)
5568     {
5569       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5570       DrawLevelField(x, y);
5571     }
5572     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5573              game.lenses_time_left == 0)
5574     {
5575       Feld[x][y] = EL_EMC_DRIPPER;
5576       DrawLevelField(x, y);
5577     }
5578     else if (element == EL_INVISIBLE_STEELWALL ||
5579              element == EL_INVISIBLE_WALL ||
5580              element == EL_INVISIBLE_SAND)
5581     {
5582       if (game.lenses_time_left > 0)
5583         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5584
5585       DrawLevelField(x, y);
5586
5587       /* uncrumble neighbour fields, if needed */
5588       if (element == EL_INVISIBLE_SAND)
5589         DrawLevelFieldCrumbledSandNeighbours(x, y);
5590     }
5591     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5592              element == EL_INVISIBLE_WALL_ACTIVE ||
5593              element == EL_INVISIBLE_SAND_ACTIVE)
5594     {
5595       if (game.lenses_time_left == 0)
5596         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5597
5598       DrawLevelField(x, y);
5599
5600       /* re-crumble neighbour fields, if needed */
5601       if (element == EL_INVISIBLE_SAND)
5602         DrawLevelFieldCrumbledSandNeighbours(x, y);
5603     }
5604   }
5605 }
5606
5607 static void RedrawAllInvisibleElementsForMagnifier()
5608 {
5609   int x, y;
5610
5611   SCAN_PLAYFIELD(x, y)
5612   {
5613     int element = Feld[x][y];
5614
5615     if (element == EL_EMC_FAKE_GRASS &&
5616         game.magnify_time_left > 0)
5617     {
5618       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5619       DrawLevelField(x, y);
5620     }
5621     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5622              game.magnify_time_left == 0)
5623     {
5624       Feld[x][y] = EL_EMC_FAKE_GRASS;
5625       DrawLevelField(x, y);
5626     }
5627     else if (IS_GATE_GRAY(element) &&
5628              game.magnify_time_left > 0)
5629     {
5630       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5631                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5632                     IS_EM_GATE_GRAY(element) ?
5633                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5634                     IS_EMC_GATE_GRAY(element) ?
5635                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5636                     element);
5637       DrawLevelField(x, y);
5638     }
5639     else if (IS_GATE_GRAY_ACTIVE(element) &&
5640              game.magnify_time_left == 0)
5641     {
5642       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5643                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5644                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5645                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5646                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5647                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5648                     element);
5649       DrawLevelField(x, y);
5650     }
5651   }
5652 }
5653
5654 static void ToggleLightSwitch(int x, int y)
5655 {
5656   int element = Feld[x][y];
5657
5658   game.light_time_left =
5659     (element == EL_LIGHT_SWITCH ?
5660      level.time_light * FRAMES_PER_SECOND : 0);
5661
5662   RedrawAllLightSwitchesAndInvisibleElements();
5663 }
5664
5665 static void ActivateTimegateSwitch(int x, int y)
5666 {
5667   int xx, yy;
5668
5669   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5670
5671   SCAN_PLAYFIELD(xx, yy)
5672   {
5673     int element = Feld[xx][yy];
5674
5675     if (element == EL_TIMEGATE_CLOSED ||
5676         element == EL_TIMEGATE_CLOSING)
5677     {
5678       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5679       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5680     }
5681
5682     /*
5683     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5684     {
5685       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5686       DrawLevelField(xx, yy);
5687     }
5688     */
5689
5690   }
5691
5692 #if 1
5693   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5694                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5695 #else
5696   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5697 #endif
5698 }
5699
5700 void Impact(int x, int y)
5701 {
5702   boolean last_line = (y == lev_fieldy - 1);
5703   boolean object_hit = FALSE;
5704   boolean impact = (last_line || object_hit);
5705   int element = Feld[x][y];
5706   int smashed = EL_STEELWALL;
5707
5708   if (!last_line)       /* check if element below was hit */
5709   {
5710     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5711       return;
5712
5713     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5714                                          MovDir[x][y + 1] != MV_DOWN ||
5715                                          MovPos[x][y + 1] <= TILEY / 2));
5716
5717     /* do not smash moving elements that left the smashed field in time */
5718     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5719         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5720       object_hit = FALSE;
5721
5722 #if USE_QUICKSAND_IMPACT_BUGFIX
5723     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5724     {
5725       RemoveMovingField(x, y + 1);
5726       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5727       Feld[x][y + 2] = EL_ROCK;
5728       DrawLevelField(x, y + 2);
5729
5730       object_hit = TRUE;
5731     }
5732
5733     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5734     {
5735       RemoveMovingField(x, y + 1);
5736       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5737       Feld[x][y + 2] = EL_ROCK;
5738       DrawLevelField(x, y + 2);
5739
5740       object_hit = TRUE;
5741     }
5742 #endif
5743
5744     if (object_hit)
5745       smashed = MovingOrBlocked2Element(x, y + 1);
5746
5747     impact = (last_line || object_hit);
5748   }
5749
5750   if (!last_line && smashed == EL_ACID) /* element falls into acid */
5751   {
5752     SplashAcid(x, y + 1);
5753     return;
5754   }
5755
5756   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5757   /* only reset graphic animation if graphic really changes after impact */
5758   if (impact &&
5759       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5760   {
5761     ResetGfxAnimation(x, y);
5762     DrawLevelField(x, y);
5763   }
5764
5765   if (impact && CAN_EXPLODE_IMPACT(element))
5766   {
5767     Bang(x, y);
5768     return;
5769   }
5770   else if (impact && element == EL_PEARL &&
5771            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5772   {
5773     ResetGfxAnimation(x, y);
5774
5775     Feld[x][y] = EL_PEARL_BREAKING;
5776     PlayLevelSound(x, y, SND_PEARL_BREAKING);
5777     return;
5778   }
5779   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5780   {
5781     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5782
5783     return;
5784   }
5785
5786   if (impact && element == EL_AMOEBA_DROP)
5787   {
5788     if (object_hit && IS_PLAYER(x, y + 1))
5789       KillPlayerUnlessEnemyProtected(x, y + 1);
5790     else if (object_hit && smashed == EL_PENGUIN)
5791       Bang(x, y + 1);
5792     else
5793     {
5794       Feld[x][y] = EL_AMOEBA_GROWING;
5795       Store[x][y] = EL_AMOEBA_WET;
5796
5797       ResetRandomAnimationValue(x, y);
5798     }
5799     return;
5800   }
5801
5802   if (object_hit)               /* check which object was hit */
5803   {
5804     if ((CAN_PASS_MAGIC_WALL(element) && 
5805          (smashed == EL_MAGIC_WALL ||
5806           smashed == EL_BD_MAGIC_WALL)) ||
5807         (CAN_PASS_DC_MAGIC_WALL(element) &&
5808          smashed == EL_DC_MAGIC_WALL))
5809     {
5810       int xx, yy;
5811       int activated_magic_wall =
5812         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5813          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5814          EL_DC_MAGIC_WALL_ACTIVE);
5815
5816       /* activate magic wall / mill */
5817       SCAN_PLAYFIELD(xx, yy)
5818       {
5819         if (Feld[xx][yy] == smashed)
5820           Feld[xx][yy] = activated_magic_wall;
5821       }
5822
5823       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5824       game.magic_wall_active = TRUE;
5825
5826       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5827                             SND_MAGIC_WALL_ACTIVATING :
5828                             smashed == EL_BD_MAGIC_WALL ?
5829                             SND_BD_MAGIC_WALL_ACTIVATING :
5830                             SND_DC_MAGIC_WALL_ACTIVATING));
5831     }
5832
5833     if (IS_PLAYER(x, y + 1))
5834     {
5835       if (CAN_SMASH_PLAYER(element))
5836       {
5837         KillPlayerUnlessEnemyProtected(x, y + 1);
5838         return;
5839       }
5840     }
5841     else if (smashed == EL_PENGUIN)
5842     {
5843       if (CAN_SMASH_PLAYER(element))
5844       {
5845         Bang(x, y + 1);
5846         return;
5847       }
5848     }
5849     else if (element == EL_BD_DIAMOND)
5850     {
5851       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5852       {
5853         Bang(x, y + 1);
5854         return;
5855       }
5856     }
5857     else if (((element == EL_SP_INFOTRON ||
5858                element == EL_SP_ZONK) &&
5859               (smashed == EL_SP_SNIKSNAK ||
5860                smashed == EL_SP_ELECTRON ||
5861                smashed == EL_SP_DISK_ORANGE)) ||
5862              (element == EL_SP_INFOTRON &&
5863               smashed == EL_SP_DISK_YELLOW))
5864     {
5865       Bang(x, y + 1);
5866       return;
5867     }
5868     else if (CAN_SMASH_EVERYTHING(element))
5869     {
5870       if (IS_CLASSIC_ENEMY(smashed) ||
5871           CAN_EXPLODE_SMASHED(smashed))
5872       {
5873         Bang(x, y + 1);
5874         return;
5875       }
5876       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5877       {
5878         if (smashed == EL_LAMP ||
5879             smashed == EL_LAMP_ACTIVE)
5880         {
5881           Bang(x, y + 1);
5882           return;
5883         }
5884         else if (smashed == EL_NUT)
5885         {
5886           Feld[x][y + 1] = EL_NUT_BREAKING;
5887           PlayLevelSound(x, y, SND_NUT_BREAKING);
5888           RaiseScoreElement(EL_NUT);
5889           return;
5890         }
5891         else if (smashed == EL_PEARL)
5892         {
5893           ResetGfxAnimation(x, y);
5894
5895           Feld[x][y + 1] = EL_PEARL_BREAKING;
5896           PlayLevelSound(x, y, SND_PEARL_BREAKING);
5897           return;
5898         }
5899         else if (smashed == EL_DIAMOND)
5900         {
5901           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
5902           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
5903           return;
5904         }
5905         else if (IS_BELT_SWITCH(smashed))
5906         {
5907           ToggleBeltSwitch(x, y + 1);
5908         }
5909         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
5910                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
5911                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
5912                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
5913         {
5914           ToggleSwitchgateSwitch(x, y + 1);
5915         }
5916         else if (smashed == EL_LIGHT_SWITCH ||
5917                  smashed == EL_LIGHT_SWITCH_ACTIVE)
5918         {
5919           ToggleLightSwitch(x, y + 1);
5920         }
5921         else
5922         {
5923 #if 0
5924           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
5925 #endif
5926
5927           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5928
5929           CheckElementChangeBySide(x, y + 1, smashed, element,
5930                                    CE_SWITCHED, CH_SIDE_TOP);
5931           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5932                                             CH_SIDE_TOP);
5933         }
5934       }
5935       else
5936       {
5937         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5938       }
5939     }
5940   }
5941
5942   /* play sound of magic wall / mill */
5943   if (!last_line &&
5944       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5945        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
5946        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
5947   {
5948     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5949       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5950     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5951       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5952     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5953       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5954
5955     return;
5956   }
5957
5958   /* play sound of object that hits the ground */
5959   if (last_line || object_hit)
5960     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5961 }
5962
5963 inline static void TurnRoundExt(int x, int y)
5964 {
5965   static struct
5966   {
5967     int dx, dy;
5968   } move_xy[] =
5969   {
5970     {  0,  0 },
5971     { -1,  0 },
5972     { +1,  0 },
5973     {  0,  0 },
5974     {  0, -1 },
5975     {  0,  0 }, { 0, 0 }, { 0, 0 },
5976     {  0, +1 }
5977   };
5978   static struct
5979   {
5980     int left, right, back;
5981   } turn[] =
5982   {
5983     { 0,        0,              0        },
5984     { MV_DOWN,  MV_UP,          MV_RIGHT },
5985     { MV_UP,    MV_DOWN,        MV_LEFT  },
5986     { 0,        0,              0        },
5987     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
5988     { 0,        0,              0        },
5989     { 0,        0,              0        },
5990     { 0,        0,              0        },
5991     { MV_RIGHT, MV_LEFT,        MV_UP    }
5992   };
5993
5994   int element = Feld[x][y];
5995   int move_pattern = element_info[element].move_pattern;
5996
5997   int old_move_dir = MovDir[x][y];
5998   int left_dir  = turn[old_move_dir].left;
5999   int right_dir = turn[old_move_dir].right;
6000   int back_dir  = turn[old_move_dir].back;
6001
6002   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6003   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6004   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6005   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6006
6007   int left_x  = x + left_dx,  left_y  = y + left_dy;
6008   int right_x = x + right_dx, right_y = y + right_dy;
6009   int move_x  = x + move_dx,  move_y  = y + move_dy;
6010
6011   int xx, yy;
6012
6013   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6014   {
6015     TestIfBadThingTouchesOtherBadThing(x, y);
6016
6017     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6018       MovDir[x][y] = right_dir;
6019     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6020       MovDir[x][y] = left_dir;
6021
6022     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6023       MovDelay[x][y] = 9;
6024     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6025       MovDelay[x][y] = 1;
6026   }
6027   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6028   {
6029     TestIfBadThingTouchesOtherBadThing(x, y);
6030
6031     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6032       MovDir[x][y] = left_dir;
6033     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6034       MovDir[x][y] = right_dir;
6035
6036     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6037       MovDelay[x][y] = 9;
6038     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6039       MovDelay[x][y] = 1;
6040   }
6041   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6042   {
6043     TestIfBadThingTouchesOtherBadThing(x, y);
6044
6045     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6046       MovDir[x][y] = left_dir;
6047     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6048       MovDir[x][y] = right_dir;
6049
6050     if (MovDir[x][y] != old_move_dir)
6051       MovDelay[x][y] = 9;
6052   }
6053   else if (element == EL_YAMYAM)
6054   {
6055     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6056     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6057
6058     if (can_turn_left && can_turn_right)
6059       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6060     else if (can_turn_left)
6061       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6062     else if (can_turn_right)
6063       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6064     else
6065       MovDir[x][y] = back_dir;
6066
6067     MovDelay[x][y] = 16 + 16 * RND(3);
6068   }
6069   else if (element == EL_DARK_YAMYAM)
6070   {
6071     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6072                                                          left_x, left_y);
6073     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6074                                                          right_x, right_y);
6075
6076     if (can_turn_left && can_turn_right)
6077       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6078     else if (can_turn_left)
6079       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6080     else if (can_turn_right)
6081       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6082     else
6083       MovDir[x][y] = back_dir;
6084
6085     MovDelay[x][y] = 16 + 16 * RND(3);
6086   }
6087   else if (element == EL_PACMAN)
6088   {
6089     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6090     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6091
6092     if (can_turn_left && can_turn_right)
6093       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6094     else if (can_turn_left)
6095       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6096     else if (can_turn_right)
6097       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6098     else
6099       MovDir[x][y] = back_dir;
6100
6101     MovDelay[x][y] = 6 + RND(40);
6102   }
6103   else if (element == EL_PIG)
6104   {
6105     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6106     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6107     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6108     boolean should_turn_left, should_turn_right, should_move_on;
6109     int rnd_value = 24;
6110     int rnd = RND(rnd_value);
6111
6112     should_turn_left = (can_turn_left &&
6113                         (!can_move_on ||
6114                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6115                                                    y + back_dy + left_dy)));
6116     should_turn_right = (can_turn_right &&
6117                          (!can_move_on ||
6118                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6119                                                     y + back_dy + right_dy)));
6120     should_move_on = (can_move_on &&
6121                       (!can_turn_left ||
6122                        !can_turn_right ||
6123                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6124                                                  y + move_dy + left_dy) ||
6125                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6126                                                  y + move_dy + right_dy)));
6127
6128     if (should_turn_left || should_turn_right || should_move_on)
6129     {
6130       if (should_turn_left && should_turn_right && should_move_on)
6131         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6132                         rnd < 2 * rnd_value / 3 ? right_dir :
6133                         old_move_dir);
6134       else if (should_turn_left && should_turn_right)
6135         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6136       else if (should_turn_left && should_move_on)
6137         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6138       else if (should_turn_right && should_move_on)
6139         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6140       else if (should_turn_left)
6141         MovDir[x][y] = left_dir;
6142       else if (should_turn_right)
6143         MovDir[x][y] = right_dir;
6144       else if (should_move_on)
6145         MovDir[x][y] = old_move_dir;
6146     }
6147     else if (can_move_on && rnd > rnd_value / 8)
6148       MovDir[x][y] = old_move_dir;
6149     else if (can_turn_left && can_turn_right)
6150       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6151     else if (can_turn_left && rnd > rnd_value / 8)
6152       MovDir[x][y] = left_dir;
6153     else if (can_turn_right && rnd > rnd_value/8)
6154       MovDir[x][y] = right_dir;
6155     else
6156       MovDir[x][y] = back_dir;
6157
6158     xx = x + move_xy[MovDir[x][y]].dx;
6159     yy = y + move_xy[MovDir[x][y]].dy;
6160
6161     if (!IN_LEV_FIELD(xx, yy) ||
6162         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6163       MovDir[x][y] = old_move_dir;
6164
6165     MovDelay[x][y] = 0;
6166   }
6167   else if (element == EL_DRAGON)
6168   {
6169     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6170     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6171     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6172     int rnd_value = 24;
6173     int rnd = RND(rnd_value);
6174
6175     if (can_move_on && rnd > rnd_value / 8)
6176       MovDir[x][y] = old_move_dir;
6177     else if (can_turn_left && can_turn_right)
6178       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6179     else if (can_turn_left && rnd > rnd_value / 8)
6180       MovDir[x][y] = left_dir;
6181     else if (can_turn_right && rnd > rnd_value / 8)
6182       MovDir[x][y] = right_dir;
6183     else
6184       MovDir[x][y] = back_dir;
6185
6186     xx = x + move_xy[MovDir[x][y]].dx;
6187     yy = y + move_xy[MovDir[x][y]].dy;
6188
6189     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6190       MovDir[x][y] = old_move_dir;
6191
6192     MovDelay[x][y] = 0;
6193   }
6194   else if (element == EL_MOLE)
6195   {
6196     boolean can_move_on =
6197       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6198                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6199                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6200     if (!can_move_on)
6201     {
6202       boolean can_turn_left =
6203         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6204                               IS_AMOEBOID(Feld[left_x][left_y])));
6205
6206       boolean can_turn_right =
6207         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6208                               IS_AMOEBOID(Feld[right_x][right_y])));
6209
6210       if (can_turn_left && can_turn_right)
6211         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6212       else if (can_turn_left)
6213         MovDir[x][y] = left_dir;
6214       else
6215         MovDir[x][y] = right_dir;
6216     }
6217
6218     if (MovDir[x][y] != old_move_dir)
6219       MovDelay[x][y] = 9;
6220   }
6221   else if (element == EL_BALLOON)
6222   {
6223     MovDir[x][y] = game.wind_direction;
6224     MovDelay[x][y] = 0;
6225   }
6226   else if (element == EL_SPRING)
6227   {
6228 #if USE_NEW_SPRING_BUMPER
6229     if (MovDir[x][y] & MV_HORIZONTAL)
6230     {
6231       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6232           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6233       {
6234         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6235         ResetGfxAnimation(move_x, move_y);
6236         DrawLevelField(move_x, move_y);
6237
6238         MovDir[x][y] = back_dir;
6239       }
6240       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6241                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6242         MovDir[x][y] = MV_NONE;
6243     }
6244 #else
6245     if (MovDir[x][y] & MV_HORIZONTAL &&
6246         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6247          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6248       MovDir[x][y] = MV_NONE;
6249 #endif
6250
6251     MovDelay[x][y] = 0;
6252   }
6253   else if (element == EL_ROBOT ||
6254            element == EL_SATELLITE ||
6255            element == EL_PENGUIN ||
6256            element == EL_EMC_ANDROID)
6257   {
6258     int attr_x = -1, attr_y = -1;
6259
6260     if (AllPlayersGone)
6261     {
6262       attr_x = ExitX;
6263       attr_y = ExitY;
6264     }
6265     else
6266     {
6267       int i;
6268
6269       for (i = 0; i < MAX_PLAYERS; i++)
6270       {
6271         struct PlayerInfo *player = &stored_player[i];
6272         int jx = player->jx, jy = player->jy;
6273
6274         if (!player->active)
6275           continue;
6276
6277         if (attr_x == -1 ||
6278             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6279         {
6280           attr_x = jx;
6281           attr_y = jy;
6282         }
6283       }
6284     }
6285
6286     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6287         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6288          game.engine_version < VERSION_IDENT(3,1,0,0)))
6289     {
6290       attr_x = ZX;
6291       attr_y = ZY;
6292     }
6293
6294     if (element == EL_PENGUIN)
6295     {
6296       int i;
6297       static int xy[4][2] =
6298       {
6299         { 0, -1 },
6300         { -1, 0 },
6301         { +1, 0 },
6302         { 0, +1 }
6303       };
6304
6305       for (i = 0; i < NUM_DIRECTIONS; i++)
6306       {
6307         int ex = x + xy[i][0];
6308         int ey = y + xy[i][1];
6309
6310         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6311                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6312                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6313                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6314         {
6315           attr_x = ex;
6316           attr_y = ey;
6317           break;
6318         }
6319       }
6320     }
6321
6322     MovDir[x][y] = MV_NONE;
6323     if (attr_x < x)
6324       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6325     else if (attr_x > x)
6326       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6327     if (attr_y < y)
6328       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6329     else if (attr_y > y)
6330       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6331
6332     if (element == EL_ROBOT)
6333     {
6334       int newx, newy;
6335
6336       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6337         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6338       Moving2Blocked(x, y, &newx, &newy);
6339
6340       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6341         MovDelay[x][y] = 8 + 8 * !RND(3);
6342       else
6343         MovDelay[x][y] = 16;
6344     }
6345     else if (element == EL_PENGUIN)
6346     {
6347       int newx, newy;
6348
6349       MovDelay[x][y] = 1;
6350
6351       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6352       {
6353         boolean first_horiz = RND(2);
6354         int new_move_dir = MovDir[x][y];
6355
6356         MovDir[x][y] =
6357           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6358         Moving2Blocked(x, y, &newx, &newy);
6359
6360         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6361           return;
6362
6363         MovDir[x][y] =
6364           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6365         Moving2Blocked(x, y, &newx, &newy);
6366
6367         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6368           return;
6369
6370         MovDir[x][y] = old_move_dir;
6371         return;
6372       }
6373     }
6374     else if (element == EL_SATELLITE)
6375     {
6376       int newx, newy;
6377
6378       MovDelay[x][y] = 1;
6379
6380       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6381       {
6382         boolean first_horiz = RND(2);
6383         int new_move_dir = MovDir[x][y];
6384
6385         MovDir[x][y] =
6386           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6387         Moving2Blocked(x, y, &newx, &newy);
6388
6389         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6390           return;
6391
6392         MovDir[x][y] =
6393           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6394         Moving2Blocked(x, y, &newx, &newy);
6395
6396         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6397           return;
6398
6399         MovDir[x][y] = old_move_dir;
6400         return;
6401       }
6402     }
6403     else if (element == EL_EMC_ANDROID)
6404     {
6405       static int check_pos[16] =
6406       {
6407         -1,             /*  0 => (invalid)          */
6408         7,              /*  1 => MV_LEFT            */
6409         3,              /*  2 => MV_RIGHT           */
6410         -1,             /*  3 => (invalid)          */
6411         1,              /*  4 =>            MV_UP   */
6412         0,              /*  5 => MV_LEFT  | MV_UP   */
6413         2,              /*  6 => MV_RIGHT | MV_UP   */
6414         -1,             /*  7 => (invalid)          */
6415         5,              /*  8 =>            MV_DOWN */
6416         6,              /*  9 => MV_LEFT  | MV_DOWN */
6417         4,              /* 10 => MV_RIGHT | MV_DOWN */
6418         -1,             /* 11 => (invalid)          */
6419         -1,             /* 12 => (invalid)          */
6420         -1,             /* 13 => (invalid)          */
6421         -1,             /* 14 => (invalid)          */
6422         -1,             /* 15 => (invalid)          */
6423       };
6424       static struct
6425       {
6426         int dx, dy;
6427         int dir;
6428       } check_xy[8] =
6429       {
6430         { -1, -1,       MV_LEFT  | MV_UP   },
6431         {  0, -1,                  MV_UP   },
6432         { +1, -1,       MV_RIGHT | MV_UP   },
6433         { +1,  0,       MV_RIGHT           },
6434         { +1, +1,       MV_RIGHT | MV_DOWN },
6435         {  0, +1,                  MV_DOWN },
6436         { -1, +1,       MV_LEFT  | MV_DOWN },
6437         { -1,  0,       MV_LEFT            },
6438       };
6439       int start_pos, check_order;
6440       boolean can_clone = FALSE;
6441       int i;
6442
6443       /* check if there is any free field around current position */
6444       for (i = 0; i < 8; i++)
6445       {
6446         int newx = x + check_xy[i].dx;
6447         int newy = y + check_xy[i].dy;
6448
6449         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6450         {
6451           can_clone = TRUE;
6452
6453           break;
6454         }
6455       }
6456
6457       if (can_clone)            /* randomly find an element to clone */
6458       {
6459         can_clone = FALSE;
6460
6461         start_pos = check_pos[RND(8)];
6462         check_order = (RND(2) ? -1 : +1);
6463
6464         for (i = 0; i < 8; i++)
6465         {
6466           int pos_raw = start_pos + i * check_order;
6467           int pos = (pos_raw + 8) % 8;
6468           int newx = x + check_xy[pos].dx;
6469           int newy = y + check_xy[pos].dy;
6470
6471           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6472           {
6473             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6474             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6475
6476             Store[x][y] = Feld[newx][newy];
6477
6478             can_clone = TRUE;
6479
6480             break;
6481           }
6482         }
6483       }
6484
6485       if (can_clone)            /* randomly find a direction to move */
6486       {
6487         can_clone = FALSE;
6488
6489         start_pos = check_pos[RND(8)];
6490         check_order = (RND(2) ? -1 : +1);
6491
6492         for (i = 0; i < 8; i++)
6493         {
6494           int pos_raw = start_pos + i * check_order;
6495           int pos = (pos_raw + 8) % 8;
6496           int newx = x + check_xy[pos].dx;
6497           int newy = y + check_xy[pos].dy;
6498           int new_move_dir = check_xy[pos].dir;
6499
6500           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6501           {
6502             MovDir[x][y] = new_move_dir;
6503             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6504
6505             can_clone = TRUE;
6506
6507             break;
6508           }
6509         }
6510       }
6511
6512       if (can_clone)            /* cloning and moving successful */
6513         return;
6514
6515       /* cannot clone -- try to move towards player */
6516
6517       start_pos = check_pos[MovDir[x][y] & 0x0f];
6518       check_order = (RND(2) ? -1 : +1);
6519
6520       for (i = 0; i < 3; i++)
6521       {
6522         /* first check start_pos, then previous/next or (next/previous) pos */
6523         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6524         int pos = (pos_raw + 8) % 8;
6525         int newx = x + check_xy[pos].dx;
6526         int newy = y + check_xy[pos].dy;
6527         int new_move_dir = check_xy[pos].dir;
6528
6529         if (IS_PLAYER(newx, newy))
6530           break;
6531
6532         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6533         {
6534           MovDir[x][y] = new_move_dir;
6535           MovDelay[x][y] = level.android_move_time * 8 + 1;
6536
6537           break;
6538         }
6539       }
6540     }
6541   }
6542   else if (move_pattern == MV_TURNING_LEFT ||
6543            move_pattern == MV_TURNING_RIGHT ||
6544            move_pattern == MV_TURNING_LEFT_RIGHT ||
6545            move_pattern == MV_TURNING_RIGHT_LEFT ||
6546            move_pattern == MV_TURNING_RANDOM ||
6547            move_pattern == MV_ALL_DIRECTIONS)
6548   {
6549     boolean can_turn_left =
6550       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6551     boolean can_turn_right =
6552       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6553
6554     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6555       return;
6556
6557     if (move_pattern == MV_TURNING_LEFT)
6558       MovDir[x][y] = left_dir;
6559     else if (move_pattern == MV_TURNING_RIGHT)
6560       MovDir[x][y] = right_dir;
6561     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6562       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6563     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6564       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6565     else if (move_pattern == MV_TURNING_RANDOM)
6566       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6567                       can_turn_right && !can_turn_left ? right_dir :
6568                       RND(2) ? left_dir : right_dir);
6569     else if (can_turn_left && can_turn_right)
6570       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6571     else if (can_turn_left)
6572       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6573     else if (can_turn_right)
6574       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6575     else
6576       MovDir[x][y] = back_dir;
6577
6578     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6579   }
6580   else if (move_pattern == MV_HORIZONTAL ||
6581            move_pattern == MV_VERTICAL)
6582   {
6583     if (move_pattern & old_move_dir)
6584       MovDir[x][y] = back_dir;
6585     else if (move_pattern == MV_HORIZONTAL)
6586       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6587     else if (move_pattern == MV_VERTICAL)
6588       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6589
6590     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6591   }
6592   else if (move_pattern & MV_ANY_DIRECTION)
6593   {
6594     MovDir[x][y] = move_pattern;
6595     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6596   }
6597   else if (move_pattern & MV_WIND_DIRECTION)
6598   {
6599     MovDir[x][y] = game.wind_direction;
6600     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6601   }
6602   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6603   {
6604     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6605       MovDir[x][y] = left_dir;
6606     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6607       MovDir[x][y] = right_dir;
6608
6609     if (MovDir[x][y] != old_move_dir)
6610       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6611   }
6612   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6613   {
6614     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6615       MovDir[x][y] = right_dir;
6616     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6617       MovDir[x][y] = left_dir;
6618
6619     if (MovDir[x][y] != old_move_dir)
6620       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6621   }
6622   else if (move_pattern == MV_TOWARDS_PLAYER ||
6623            move_pattern == MV_AWAY_FROM_PLAYER)
6624   {
6625     int attr_x = -1, attr_y = -1;
6626     int newx, newy;
6627     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6628
6629     if (AllPlayersGone)
6630     {
6631       attr_x = ExitX;
6632       attr_y = ExitY;
6633     }
6634     else
6635     {
6636       int i;
6637
6638       for (i = 0; i < MAX_PLAYERS; i++)
6639       {
6640         struct PlayerInfo *player = &stored_player[i];
6641         int jx = player->jx, jy = player->jy;
6642
6643         if (!player->active)
6644           continue;
6645
6646         if (attr_x == -1 ||
6647             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6648         {
6649           attr_x = jx;
6650           attr_y = jy;
6651         }
6652       }
6653     }
6654
6655     MovDir[x][y] = MV_NONE;
6656     if (attr_x < x)
6657       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6658     else if (attr_x > x)
6659       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6660     if (attr_y < y)
6661       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6662     else if (attr_y > y)
6663       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6664
6665     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6666
6667     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6668     {
6669       boolean first_horiz = RND(2);
6670       int new_move_dir = MovDir[x][y];
6671
6672       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6673       {
6674         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6675         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6676
6677         return;
6678       }
6679
6680       MovDir[x][y] =
6681         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6682       Moving2Blocked(x, y, &newx, &newy);
6683
6684       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6685         return;
6686
6687       MovDir[x][y] =
6688         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6689       Moving2Blocked(x, y, &newx, &newy);
6690
6691       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6692         return;
6693
6694       MovDir[x][y] = old_move_dir;
6695     }
6696   }
6697   else if (move_pattern == MV_WHEN_PUSHED ||
6698            move_pattern == MV_WHEN_DROPPED)
6699   {
6700     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6701       MovDir[x][y] = MV_NONE;
6702
6703     MovDelay[x][y] = 0;
6704   }
6705   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6706   {
6707     static int test_xy[7][2] =
6708     {
6709       { 0, -1 },
6710       { -1, 0 },
6711       { +1, 0 },
6712       { 0, +1 },
6713       { 0, -1 },
6714       { -1, 0 },
6715       { +1, 0 },
6716     };
6717     static int test_dir[7] =
6718     {
6719       MV_UP,
6720       MV_LEFT,
6721       MV_RIGHT,
6722       MV_DOWN,
6723       MV_UP,
6724       MV_LEFT,
6725       MV_RIGHT,
6726     };
6727     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6728     int move_preference = -1000000;     /* start with very low preference */
6729     int new_move_dir = MV_NONE;
6730     int start_test = RND(4);
6731     int i;
6732
6733     for (i = 0; i < NUM_DIRECTIONS; i++)
6734     {
6735       int move_dir = test_dir[start_test + i];
6736       int move_dir_preference;
6737
6738       xx = x + test_xy[start_test + i][0];
6739       yy = y + test_xy[start_test + i][1];
6740
6741       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6742           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6743       {
6744         new_move_dir = move_dir;
6745
6746         break;
6747       }
6748
6749       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6750         continue;
6751
6752       move_dir_preference = -1 * RunnerVisit[xx][yy];
6753       if (hunter_mode && PlayerVisit[xx][yy] > 0)
6754         move_dir_preference = PlayerVisit[xx][yy];
6755
6756       if (move_dir_preference > move_preference)
6757       {
6758         /* prefer field that has not been visited for the longest time */
6759         move_preference = move_dir_preference;
6760         new_move_dir = move_dir;
6761       }
6762       else if (move_dir_preference == move_preference &&
6763                move_dir == old_move_dir)
6764       {
6765         /* prefer last direction when all directions are preferred equally */
6766         move_preference = move_dir_preference;
6767         new_move_dir = move_dir;
6768       }
6769     }
6770
6771     MovDir[x][y] = new_move_dir;
6772     if (old_move_dir != new_move_dir)
6773       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6774   }
6775 }
6776
6777 static void TurnRound(int x, int y)
6778 {
6779   int direction = MovDir[x][y];
6780
6781   TurnRoundExt(x, y);
6782
6783   GfxDir[x][y] = MovDir[x][y];
6784
6785   if (direction != MovDir[x][y])
6786     GfxFrame[x][y] = 0;
6787
6788   if (MovDelay[x][y])
6789     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6790
6791   ResetGfxFrame(x, y, FALSE);
6792 }
6793
6794 static boolean JustBeingPushed(int x, int y)
6795 {
6796   int i;
6797
6798   for (i = 0; i < MAX_PLAYERS; i++)
6799   {
6800     struct PlayerInfo *player = &stored_player[i];
6801
6802     if (player->active && player->is_pushing && player->MovPos)
6803     {
6804       int next_jx = player->jx + (player->jx - player->last_jx);
6805       int next_jy = player->jy + (player->jy - player->last_jy);
6806
6807       if (x == next_jx && y == next_jy)
6808         return TRUE;
6809     }
6810   }
6811
6812   return FALSE;
6813 }
6814
6815 void StartMoving(int x, int y)
6816 {
6817   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
6818   int element = Feld[x][y];
6819
6820   if (Stop[x][y])
6821     return;
6822
6823   if (MovDelay[x][y] == 0)
6824     GfxAction[x][y] = ACTION_DEFAULT;
6825
6826   if (CAN_FALL(element) && y < lev_fieldy - 1)
6827   {
6828     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
6829         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6830       if (JustBeingPushed(x, y))
6831         return;
6832
6833     if (element == EL_QUICKSAND_FULL)
6834     {
6835       if (IS_FREE(x, y + 1))
6836       {
6837         InitMovingField(x, y, MV_DOWN);
6838         started_moving = TRUE;
6839
6840         Feld[x][y] = EL_QUICKSAND_EMPTYING;
6841 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6842         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6843           Store[x][y] = EL_ROCK;
6844 #else
6845         Store[x][y] = EL_ROCK;
6846 #endif
6847
6848         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6849       }
6850       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6851       {
6852         if (!MovDelay[x][y])
6853           MovDelay[x][y] = TILEY + 1;
6854
6855         if (MovDelay[x][y])
6856         {
6857           MovDelay[x][y]--;
6858           if (MovDelay[x][y])
6859             return;
6860         }
6861
6862         Feld[x][y] = EL_QUICKSAND_EMPTY;
6863         Feld[x][y + 1] = EL_QUICKSAND_FULL;
6864         Store[x][y + 1] = Store[x][y];
6865         Store[x][y] = 0;
6866
6867         PlayLevelSoundAction(x, y, ACTION_FILLING);
6868       }
6869     }
6870     else if (element == EL_QUICKSAND_FAST_FULL)
6871     {
6872       if (IS_FREE(x, y + 1))
6873       {
6874         InitMovingField(x, y, MV_DOWN);
6875         started_moving = TRUE;
6876
6877         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
6878 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6879         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6880           Store[x][y] = EL_ROCK;
6881 #else
6882         Store[x][y] = EL_ROCK;
6883 #endif
6884
6885         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6886       }
6887       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6888       {
6889         if (!MovDelay[x][y])
6890           MovDelay[x][y] = TILEY + 1;
6891
6892         if (MovDelay[x][y])
6893         {
6894           MovDelay[x][y]--;
6895           if (MovDelay[x][y])
6896             return;
6897         }
6898
6899         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
6900         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
6901         Store[x][y + 1] = Store[x][y];
6902         Store[x][y] = 0;
6903
6904         PlayLevelSoundAction(x, y, ACTION_FILLING);
6905       }
6906     }
6907     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6908              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6909     {
6910       InitMovingField(x, y, MV_DOWN);
6911       started_moving = TRUE;
6912
6913       Feld[x][y] = EL_QUICKSAND_FILLING;
6914       Store[x][y] = element;
6915
6916       PlayLevelSoundAction(x, y, ACTION_FILLING);
6917     }
6918     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6919              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6920     {
6921       InitMovingField(x, y, MV_DOWN);
6922       started_moving = TRUE;
6923
6924       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
6925       Store[x][y] = element;
6926
6927       PlayLevelSoundAction(x, y, ACTION_FILLING);
6928     }
6929     else if (element == EL_MAGIC_WALL_FULL)
6930     {
6931       if (IS_FREE(x, y + 1))
6932       {
6933         InitMovingField(x, y, MV_DOWN);
6934         started_moving = TRUE;
6935
6936         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6937         Store[x][y] = EL_CHANGED(Store[x][y]);
6938       }
6939       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6940       {
6941         if (!MovDelay[x][y])
6942           MovDelay[x][y] = TILEY/4 + 1;
6943
6944         if (MovDelay[x][y])
6945         {
6946           MovDelay[x][y]--;
6947           if (MovDelay[x][y])
6948             return;
6949         }
6950
6951         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6952         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6953         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6954         Store[x][y] = 0;
6955       }
6956     }
6957     else if (element == EL_BD_MAGIC_WALL_FULL)
6958     {
6959       if (IS_FREE(x, y + 1))
6960       {
6961         InitMovingField(x, y, MV_DOWN);
6962         started_moving = TRUE;
6963
6964         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6965         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6966       }
6967       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6968       {
6969         if (!MovDelay[x][y])
6970           MovDelay[x][y] = TILEY/4 + 1;
6971
6972         if (MovDelay[x][y])
6973         {
6974           MovDelay[x][y]--;
6975           if (MovDelay[x][y])
6976             return;
6977         }
6978
6979         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6980         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6981         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6982         Store[x][y] = 0;
6983       }
6984     }
6985     else if (element == EL_DC_MAGIC_WALL_FULL)
6986     {
6987       if (IS_FREE(x, y + 1))
6988       {
6989         InitMovingField(x, y, MV_DOWN);
6990         started_moving = TRUE;
6991
6992         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
6993         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
6994       }
6995       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6996       {
6997         if (!MovDelay[x][y])
6998           MovDelay[x][y] = TILEY/4 + 1;
6999
7000         if (MovDelay[x][y])
7001         {
7002           MovDelay[x][y]--;
7003           if (MovDelay[x][y])
7004             return;
7005         }
7006
7007         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7008         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7009         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7010         Store[x][y] = 0;
7011       }
7012     }
7013     else if ((CAN_PASS_MAGIC_WALL(element) &&
7014               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7015                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7016              (CAN_PASS_DC_MAGIC_WALL(element) &&
7017               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7018
7019     {
7020       InitMovingField(x, y, MV_DOWN);
7021       started_moving = TRUE;
7022
7023       Feld[x][y] =
7024         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7025          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7026          EL_DC_MAGIC_WALL_FILLING);
7027       Store[x][y] = element;
7028     }
7029     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7030     {
7031       SplashAcid(x, y + 1);
7032
7033       InitMovingField(x, y, MV_DOWN);
7034       started_moving = TRUE;
7035
7036       Store[x][y] = EL_ACID;
7037     }
7038     else if (
7039 #if USE_FIX_IMPACT_COLLISION
7040              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7041               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7042 #else
7043              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7044               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7045 #endif
7046              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7047               CAN_FALL(element) && WasJustFalling[x][y] &&
7048               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7049
7050              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7051               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7052               (Feld[x][y + 1] == EL_BLOCKED)))
7053     {
7054       /* this is needed for a special case not covered by calling "Impact()"
7055          from "ContinueMoving()": if an element moves to a tile directly below
7056          another element which was just falling on that tile (which was empty
7057          in the previous frame), the falling element above would just stop
7058          instead of smashing the element below (in previous version, the above
7059          element was just checked for "moving" instead of "falling", resulting
7060          in incorrect smashes caused by horizontal movement of the above
7061          element; also, the case of the player being the element to smash was
7062          simply not covered here... :-/ ) */
7063
7064       CheckCollision[x][y] = 0;
7065       CheckImpact[x][y] = 0;
7066
7067       Impact(x, y);
7068     }
7069     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7070     {
7071       if (MovDir[x][y] == MV_NONE)
7072       {
7073         InitMovingField(x, y, MV_DOWN);
7074         started_moving = TRUE;
7075       }
7076     }
7077     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7078     {
7079       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7080         MovDir[x][y] = MV_DOWN;
7081
7082       InitMovingField(x, y, MV_DOWN);
7083       started_moving = TRUE;
7084     }
7085     else if (element == EL_AMOEBA_DROP)
7086     {
7087       Feld[x][y] = EL_AMOEBA_GROWING;
7088       Store[x][y] = EL_AMOEBA_WET;
7089     }
7090     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7091               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7092              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7093              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7094     {
7095       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7096                                 (IS_FREE(x - 1, y + 1) ||
7097                                  Feld[x - 1][y + 1] == EL_ACID));
7098       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7099                                 (IS_FREE(x + 1, y + 1) ||
7100                                  Feld[x + 1][y + 1] == EL_ACID));
7101       boolean can_fall_any  = (can_fall_left || can_fall_right);
7102       boolean can_fall_both = (can_fall_left && can_fall_right);
7103       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7104
7105 #if USE_NEW_ALL_SLIPPERY
7106       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7107       {
7108         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7109           can_fall_right = FALSE;
7110         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7111           can_fall_left = FALSE;
7112         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7113           can_fall_right = FALSE;
7114         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7115           can_fall_left = FALSE;
7116
7117         can_fall_any  = (can_fall_left || can_fall_right);
7118         can_fall_both = FALSE;
7119       }
7120 #else
7121       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7122       {
7123         if (slippery_type == SLIPPERY_ONLY_LEFT)
7124           can_fall_right = FALSE;
7125         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7126           can_fall_left = FALSE;
7127         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7128           can_fall_right = FALSE;
7129         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7130           can_fall_left = FALSE;
7131
7132         can_fall_any  = (can_fall_left || can_fall_right);
7133         can_fall_both = (can_fall_left && can_fall_right);
7134       }
7135 #endif
7136
7137 #if USE_NEW_ALL_SLIPPERY
7138 #else
7139 #if USE_NEW_SP_SLIPPERY
7140       /* !!! better use the same properties as for custom elements here !!! */
7141       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7142                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7143       {
7144         can_fall_right = FALSE;         /* slip down on left side */
7145         can_fall_both = FALSE;
7146       }
7147 #endif
7148 #endif
7149
7150 #if USE_NEW_ALL_SLIPPERY
7151       if (can_fall_both)
7152       {
7153         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7154           can_fall_right = FALSE;       /* slip down on left side */
7155         else
7156           can_fall_left = !(can_fall_right = RND(2));
7157
7158         can_fall_both = FALSE;
7159       }
7160 #else
7161       if (can_fall_both)
7162       {
7163         if (game.emulation == EMU_BOULDERDASH ||
7164             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7165           can_fall_right = FALSE;       /* slip down on left side */
7166         else
7167           can_fall_left = !(can_fall_right = RND(2));
7168
7169         can_fall_both = FALSE;
7170       }
7171 #endif
7172
7173       if (can_fall_any)
7174       {
7175         /* if not determined otherwise, prefer left side for slipping down */
7176         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7177         started_moving = TRUE;
7178       }
7179     }
7180 #if 0
7181     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7182 #else
7183     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7184 #endif
7185     {
7186       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7187       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7188       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7189       int belt_dir = game.belt_dir[belt_nr];
7190
7191       if ((belt_dir == MV_LEFT  && left_is_free) ||
7192           (belt_dir == MV_RIGHT && right_is_free))
7193       {
7194         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7195
7196         InitMovingField(x, y, belt_dir);
7197         started_moving = TRUE;
7198
7199         Pushed[x][y] = TRUE;
7200         Pushed[nextx][y] = TRUE;
7201
7202         GfxAction[x][y] = ACTION_DEFAULT;
7203       }
7204       else
7205       {
7206         MovDir[x][y] = 0;       /* if element was moving, stop it */
7207       }
7208     }
7209   }
7210
7211   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7212 #if 0
7213   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7214 #else
7215   if (CAN_MOVE(element) && !started_moving)
7216 #endif
7217   {
7218     int move_pattern = element_info[element].move_pattern;
7219     int newx, newy;
7220
7221 #if 0
7222 #if DEBUG
7223     if (MovDir[x][y] == MV_NONE)
7224     {
7225       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7226              x, y, element, element_info[element].token_name);
7227       printf("StartMoving(): This should never happen!\n");
7228     }
7229 #endif
7230 #endif
7231
7232     Moving2Blocked(x, y, &newx, &newy);
7233
7234     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7235       return;
7236
7237     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7238         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7239     {
7240       WasJustMoving[x][y] = 0;
7241       CheckCollision[x][y] = 0;
7242
7243       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7244
7245       if (Feld[x][y] != element)        /* element has changed */
7246         return;
7247     }
7248
7249     if (!MovDelay[x][y])        /* start new movement phase */
7250     {
7251       /* all objects that can change their move direction after each step
7252          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7253
7254       if (element != EL_YAMYAM &&
7255           element != EL_DARK_YAMYAM &&
7256           element != EL_PACMAN &&
7257           !(move_pattern & MV_ANY_DIRECTION) &&
7258           move_pattern != MV_TURNING_LEFT &&
7259           move_pattern != MV_TURNING_RIGHT &&
7260           move_pattern != MV_TURNING_LEFT_RIGHT &&
7261           move_pattern != MV_TURNING_RIGHT_LEFT &&
7262           move_pattern != MV_TURNING_RANDOM)
7263       {
7264         TurnRound(x, y);
7265
7266         if (MovDelay[x][y] && (element == EL_BUG ||
7267                                element == EL_SPACESHIP ||
7268                                element == EL_SP_SNIKSNAK ||
7269                                element == EL_SP_ELECTRON ||
7270                                element == EL_MOLE))
7271           DrawLevelField(x, y);
7272       }
7273     }
7274
7275     if (MovDelay[x][y])         /* wait some time before next movement */
7276     {
7277       MovDelay[x][y]--;
7278
7279       if (element == EL_ROBOT ||
7280           element == EL_YAMYAM ||
7281           element == EL_DARK_YAMYAM)
7282       {
7283         DrawLevelElementAnimationIfNeeded(x, y, element);
7284         PlayLevelSoundAction(x, y, ACTION_WAITING);
7285       }
7286       else if (element == EL_SP_ELECTRON)
7287         DrawLevelElementAnimationIfNeeded(x, y, element);
7288       else if (element == EL_DRAGON)
7289       {
7290         int i;
7291         int dir = MovDir[x][y];
7292         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7293         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7294         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7295                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7296                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7297                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7298         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7299
7300         GfxAction[x][y] = ACTION_ATTACKING;
7301
7302         if (IS_PLAYER(x, y))
7303           DrawPlayerField(x, y);
7304         else
7305           DrawLevelField(x, y);
7306
7307         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7308
7309         for (i = 1; i <= 3; i++)
7310         {
7311           int xx = x + i * dx;
7312           int yy = y + i * dy;
7313           int sx = SCREENX(xx);
7314           int sy = SCREENY(yy);
7315           int flame_graphic = graphic + (i - 1);
7316
7317           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7318             break;
7319
7320           if (MovDelay[x][y])
7321           {
7322             int flamed = MovingOrBlocked2Element(xx, yy);
7323
7324             /* !!! */
7325 #if 0
7326             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7327               Bang(xx, yy);
7328             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
7329               RemoveMovingField(xx, yy);
7330             else
7331               RemoveField(xx, yy);
7332 #else
7333             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7334               Bang(xx, yy);
7335             else
7336               RemoveMovingField(xx, yy);
7337 #endif
7338
7339             ChangeDelay[xx][yy] = 0;
7340
7341             Feld[xx][yy] = EL_FLAMES;
7342
7343             if (IN_SCR_FIELD(sx, sy))
7344             {
7345               DrawLevelFieldCrumbledSand(xx, yy);
7346               DrawGraphic(sx, sy, flame_graphic, frame);
7347             }
7348           }
7349           else
7350           {
7351             if (Feld[xx][yy] == EL_FLAMES)
7352               Feld[xx][yy] = EL_EMPTY;
7353             DrawLevelField(xx, yy);
7354           }
7355         }
7356       }
7357
7358       if (MovDelay[x][y])       /* element still has to wait some time */
7359       {
7360         PlayLevelSoundAction(x, y, ACTION_WAITING);
7361
7362         return;
7363       }
7364     }
7365
7366     /* now make next step */
7367
7368     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7369
7370     if (DONT_COLLIDE_WITH(element) &&
7371         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7372         !PLAYER_ENEMY_PROTECTED(newx, newy))
7373     {
7374       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7375
7376       return;
7377     }
7378
7379     else if (CAN_MOVE_INTO_ACID(element) &&
7380              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7381              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7382              (MovDir[x][y] == MV_DOWN ||
7383               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7384     {
7385       SplashAcid(newx, newy);
7386       Store[x][y] = EL_ACID;
7387     }
7388     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7389     {
7390       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7391           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7392           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7393           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7394       {
7395         RemoveField(x, y);
7396         DrawLevelField(x, y);
7397
7398         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7399         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7400           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7401
7402         local_player->friends_still_needed--;
7403         if (!local_player->friends_still_needed &&
7404             !local_player->GameOver && AllPlayersGone)
7405           PlayerWins(local_player);
7406
7407         return;
7408       }
7409       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7410       {
7411         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7412           DrawLevelField(newx, newy);
7413         else
7414           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7415       }
7416       else if (!IS_FREE(newx, newy))
7417       {
7418         GfxAction[x][y] = ACTION_WAITING;
7419
7420         if (IS_PLAYER(x, y))
7421           DrawPlayerField(x, y);
7422         else
7423           DrawLevelField(x, y);
7424
7425         return;
7426       }
7427     }
7428     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7429     {
7430       if (IS_FOOD_PIG(Feld[newx][newy]))
7431       {
7432         if (IS_MOVING(newx, newy))
7433           RemoveMovingField(newx, newy);
7434         else
7435         {
7436           Feld[newx][newy] = EL_EMPTY;
7437           DrawLevelField(newx, newy);
7438         }
7439
7440         PlayLevelSound(x, y, SND_PIG_DIGGING);
7441       }
7442       else if (!IS_FREE(newx, newy))
7443       {
7444         if (IS_PLAYER(x, y))
7445           DrawPlayerField(x, y);
7446         else
7447           DrawLevelField(x, y);
7448
7449         return;
7450       }
7451     }
7452     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7453     {
7454       if (Store[x][y] != EL_EMPTY)
7455       {
7456         boolean can_clone = FALSE;
7457         int xx, yy;
7458
7459         /* check if element to clone is still there */
7460         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7461         {
7462           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7463           {
7464             can_clone = TRUE;
7465
7466             break;
7467           }
7468         }
7469
7470         /* cannot clone or target field not free anymore -- do not clone */
7471         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7472           Store[x][y] = EL_EMPTY;
7473       }
7474
7475       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7476       {
7477         if (IS_MV_DIAGONAL(MovDir[x][y]))
7478         {
7479           int diagonal_move_dir = MovDir[x][y];
7480           int stored = Store[x][y];
7481           int change_delay = 8;
7482           int graphic;
7483
7484           /* android is moving diagonally */
7485
7486           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7487
7488           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7489           GfxElement[x][y] = EL_EMC_ANDROID;
7490           GfxAction[x][y] = ACTION_SHRINKING;
7491           GfxDir[x][y] = diagonal_move_dir;
7492           ChangeDelay[x][y] = change_delay;
7493
7494           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7495                                    GfxDir[x][y]);
7496
7497           DrawLevelGraphicAnimation(x, y, graphic);
7498           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7499
7500           if (Feld[newx][newy] == EL_ACID)
7501           {
7502             SplashAcid(newx, newy);
7503
7504             return;
7505           }
7506
7507           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7508
7509           Store[newx][newy] = EL_EMC_ANDROID;
7510           GfxElement[newx][newy] = EL_EMC_ANDROID;
7511           GfxAction[newx][newy] = ACTION_GROWING;
7512           GfxDir[newx][newy] = diagonal_move_dir;
7513           ChangeDelay[newx][newy] = change_delay;
7514
7515           graphic = el_act_dir2img(GfxElement[newx][newy],
7516                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7517
7518           DrawLevelGraphicAnimation(newx, newy, graphic);
7519           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7520
7521           return;
7522         }
7523         else
7524         {
7525           Feld[newx][newy] = EL_EMPTY;
7526           DrawLevelField(newx, newy);
7527
7528           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7529         }
7530       }
7531       else if (!IS_FREE(newx, newy))
7532       {
7533 #if 0
7534         if (IS_PLAYER(x, y))
7535           DrawPlayerField(x, y);
7536         else
7537           DrawLevelField(x, y);
7538 #endif
7539
7540         return;
7541       }
7542     }
7543     else if (IS_CUSTOM_ELEMENT(element) &&
7544              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7545     {
7546       int new_element = Feld[newx][newy];
7547
7548       if (!IS_FREE(newx, newy))
7549       {
7550         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7551                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7552                       ACTION_BREAKING);
7553
7554         /* no element can dig solid indestructible elements */
7555         if (IS_INDESTRUCTIBLE(new_element) &&
7556             !IS_DIGGABLE(new_element) &&
7557             !IS_COLLECTIBLE(new_element))
7558           return;
7559
7560         if (AmoebaNr[newx][newy] &&
7561             (new_element == EL_AMOEBA_FULL ||
7562              new_element == EL_BD_AMOEBA ||
7563              new_element == EL_AMOEBA_GROWING))
7564         {
7565           AmoebaCnt[AmoebaNr[newx][newy]]--;
7566           AmoebaCnt2[AmoebaNr[newx][newy]]--;
7567         }
7568
7569         if (IS_MOVING(newx, newy))
7570           RemoveMovingField(newx, newy);
7571         else
7572         {
7573           RemoveField(newx, newy);
7574           DrawLevelField(newx, newy);
7575         }
7576
7577         /* if digged element was about to explode, prevent the explosion */
7578         ExplodeField[newx][newy] = EX_TYPE_NONE;
7579
7580         PlayLevelSoundAction(x, y, action);
7581       }
7582
7583       Store[newx][newy] = EL_EMPTY;
7584 #if 1
7585       /* this makes it possible to leave the removed element again */
7586       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7587         Store[newx][newy] = new_element;
7588 #else
7589       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7590       {
7591         int move_leave_element = element_info[element].move_leave_element;
7592
7593         /* this makes it possible to leave the removed element again */
7594         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7595                              new_element : move_leave_element);
7596       }
7597 #endif
7598
7599       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7600       {
7601         RunnerVisit[x][y] = FrameCounter;
7602         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7603       }
7604     }
7605     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7606     {
7607       if (!IS_FREE(newx, newy))
7608       {
7609         if (IS_PLAYER(x, y))
7610           DrawPlayerField(x, y);
7611         else
7612           DrawLevelField(x, y);
7613
7614         return;
7615       }
7616       else
7617       {
7618         boolean wanna_flame = !RND(10);
7619         int dx = newx - x, dy = newy - y;
7620         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7621         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7622         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7623                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7624         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7625                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7626
7627         if ((wanna_flame ||
7628              IS_CLASSIC_ENEMY(element1) ||
7629              IS_CLASSIC_ENEMY(element2)) &&
7630             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7631             element1 != EL_FLAMES && element2 != EL_FLAMES)
7632         {
7633           ResetGfxAnimation(x, y);
7634           GfxAction[x][y] = ACTION_ATTACKING;
7635
7636           if (IS_PLAYER(x, y))
7637             DrawPlayerField(x, y);
7638           else
7639             DrawLevelField(x, y);
7640
7641           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7642
7643           MovDelay[x][y] = 50;
7644
7645           /* !!! */
7646 #if 0
7647           RemoveField(newx, newy);
7648 #endif
7649           Feld[newx][newy] = EL_FLAMES;
7650           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7651           {
7652 #if 0
7653             RemoveField(newx1, newy1);
7654 #endif
7655             Feld[newx1][newy1] = EL_FLAMES;
7656           }
7657           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7658           {
7659 #if 0
7660             RemoveField(newx2, newy2);
7661 #endif
7662             Feld[newx2][newy2] = EL_FLAMES;
7663           }
7664
7665           return;
7666         }
7667       }
7668     }
7669     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7670              Feld[newx][newy] == EL_DIAMOND)
7671     {
7672       if (IS_MOVING(newx, newy))
7673         RemoveMovingField(newx, newy);
7674       else
7675       {
7676         Feld[newx][newy] = EL_EMPTY;
7677         DrawLevelField(newx, newy);
7678       }
7679
7680       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7681     }
7682     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7683              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7684     {
7685       if (AmoebaNr[newx][newy])
7686       {
7687         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7688         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7689             Feld[newx][newy] == EL_BD_AMOEBA)
7690           AmoebaCnt[AmoebaNr[newx][newy]]--;
7691       }
7692
7693 #if 0
7694       /* !!! test !!! */
7695       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7696       {
7697         RemoveMovingField(newx, newy);
7698       }
7699 #else
7700       if (IS_MOVING(newx, newy))
7701       {
7702         RemoveMovingField(newx, newy);
7703       }
7704 #endif
7705       else
7706       {
7707         Feld[newx][newy] = EL_EMPTY;
7708         DrawLevelField(newx, newy);
7709       }
7710
7711       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7712     }
7713     else if ((element == EL_PACMAN || element == EL_MOLE)
7714              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7715     {
7716       if (AmoebaNr[newx][newy])
7717       {
7718         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7719         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7720             Feld[newx][newy] == EL_BD_AMOEBA)
7721           AmoebaCnt[AmoebaNr[newx][newy]]--;
7722       }
7723
7724       if (element == EL_MOLE)
7725       {
7726         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7727         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7728
7729         ResetGfxAnimation(x, y);
7730         GfxAction[x][y] = ACTION_DIGGING;
7731         DrawLevelField(x, y);
7732
7733         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7734
7735         return;                         /* wait for shrinking amoeba */
7736       }
7737       else      /* element == EL_PACMAN */
7738       {
7739         Feld[newx][newy] = EL_EMPTY;
7740         DrawLevelField(newx, newy);
7741         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7742       }
7743     }
7744     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7745              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7746               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7747     {
7748       /* wait for shrinking amoeba to completely disappear */
7749       return;
7750     }
7751     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7752     {
7753       /* object was running against a wall */
7754
7755       TurnRound(x, y);
7756
7757 #if 0
7758       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7759       if (move_pattern & MV_ANY_DIRECTION &&
7760           move_pattern == MovDir[x][y])
7761       {
7762         int blocking_element =
7763           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7764
7765         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7766                                  MovDir[x][y]);
7767
7768         element = Feld[x][y];   /* element might have changed */
7769       }
7770 #endif
7771
7772       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7773         DrawLevelElementAnimation(x, y, element);
7774
7775       if (DONT_TOUCH(element))
7776         TestIfBadThingTouchesPlayer(x, y);
7777
7778       return;
7779     }
7780
7781     InitMovingField(x, y, MovDir[x][y]);
7782
7783     PlayLevelSoundAction(x, y, ACTION_MOVING);
7784   }
7785
7786   if (MovDir[x][y])
7787     ContinueMoving(x, y);
7788 }
7789
7790 void ContinueMoving(int x, int y)
7791 {
7792   int element = Feld[x][y];
7793   struct ElementInfo *ei = &element_info[element];
7794   int direction = MovDir[x][y];
7795   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7796   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7797   int newx = x + dx, newy = y + dy;
7798   int stored = Store[x][y];
7799   int stored_new = Store[newx][newy];
7800   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7801   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7802   boolean last_line = (newy == lev_fieldy - 1);
7803
7804   MovPos[x][y] += getElementMoveStepsize(x, y);
7805
7806   if (pushed_by_player) /* special case: moving object pushed by player */
7807     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7808
7809   if (ABS(MovPos[x][y]) < TILEX)
7810   {
7811 #if 0
7812     int ee = Feld[x][y];
7813     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7814     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7815
7816     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7817            x, y, ABS(MovPos[x][y]),
7818            ee, gg, ff,
7819            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
7820 #endif
7821
7822     DrawLevelField(x, y);
7823
7824     return;     /* element is still moving */
7825   }
7826
7827   /* element reached destination field */
7828
7829   Feld[x][y] = EL_EMPTY;
7830   Feld[newx][newy] = element;
7831   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
7832
7833   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
7834   {
7835     element = Feld[newx][newy] = EL_ACID;
7836   }
7837   else if (element == EL_MOLE)
7838   {
7839     Feld[x][y] = EL_SAND;
7840
7841     DrawLevelFieldCrumbledSandNeighbours(x, y);
7842   }
7843   else if (element == EL_QUICKSAND_FILLING)
7844   {
7845     element = Feld[newx][newy] = get_next_element(element);
7846     Store[newx][newy] = Store[x][y];
7847   }
7848   else if (element == EL_QUICKSAND_EMPTYING)
7849   {
7850     Feld[x][y] = get_next_element(element);
7851     element = Feld[newx][newy] = Store[x][y];
7852   }
7853   else if (element == EL_QUICKSAND_FAST_FILLING)
7854   {
7855     element = Feld[newx][newy] = get_next_element(element);
7856     Store[newx][newy] = Store[x][y];
7857   }
7858   else if (element == EL_QUICKSAND_FAST_EMPTYING)
7859   {
7860     Feld[x][y] = get_next_element(element);
7861     element = Feld[newx][newy] = Store[x][y];
7862   }
7863   else if (element == EL_MAGIC_WALL_FILLING)
7864   {
7865     element = Feld[newx][newy] = get_next_element(element);
7866     if (!game.magic_wall_active)
7867       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7868     Store[newx][newy] = Store[x][y];
7869   }
7870   else if (element == EL_MAGIC_WALL_EMPTYING)
7871   {
7872     Feld[x][y] = get_next_element(element);
7873     if (!game.magic_wall_active)
7874       Feld[x][y] = EL_MAGIC_WALL_DEAD;
7875     element = Feld[newx][newy] = Store[x][y];
7876
7877 #if USE_NEW_CUSTOM_VALUE
7878     InitField(newx, newy, FALSE);
7879 #endif
7880   }
7881   else if (element == EL_BD_MAGIC_WALL_FILLING)
7882   {
7883     element = Feld[newx][newy] = get_next_element(element);
7884     if (!game.magic_wall_active)
7885       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
7886     Store[newx][newy] = Store[x][y];
7887   }
7888   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
7889   {
7890     Feld[x][y] = get_next_element(element);
7891     if (!game.magic_wall_active)
7892       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7893     element = Feld[newx][newy] = Store[x][y];
7894
7895 #if USE_NEW_CUSTOM_VALUE
7896     InitField(newx, newy, FALSE);
7897 #endif
7898   }
7899   else if (element == EL_DC_MAGIC_WALL_FILLING)
7900   {
7901     element = Feld[newx][newy] = get_next_element(element);
7902     if (!game.magic_wall_active)
7903       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
7904     Store[newx][newy] = Store[x][y];
7905   }
7906   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
7907   {
7908     Feld[x][y] = get_next_element(element);
7909     if (!game.magic_wall_active)
7910       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
7911     element = Feld[newx][newy] = Store[x][y];
7912
7913 #if USE_NEW_CUSTOM_VALUE
7914     InitField(newx, newy, FALSE);
7915 #endif
7916   }
7917   else if (element == EL_AMOEBA_DROPPING)
7918   {
7919     Feld[x][y] = get_next_element(element);
7920     element = Feld[newx][newy] = Store[x][y];
7921   }
7922   else if (element == EL_SOKOBAN_OBJECT)
7923   {
7924     if (Back[x][y])
7925       Feld[x][y] = Back[x][y];
7926
7927     if (Back[newx][newy])
7928       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
7929
7930     Back[x][y] = Back[newx][newy] = 0;
7931   }
7932
7933   Store[x][y] = EL_EMPTY;
7934   MovPos[x][y] = 0;
7935   MovDir[x][y] = 0;
7936   MovDelay[x][y] = 0;
7937
7938   MovDelay[newx][newy] = 0;
7939
7940   if (CAN_CHANGE_OR_HAS_ACTION(element))
7941   {
7942     /* copy element change control values to new field */
7943     ChangeDelay[newx][newy] = ChangeDelay[x][y];
7944     ChangePage[newx][newy]  = ChangePage[x][y];
7945     ChangeCount[newx][newy] = ChangeCount[x][y];
7946     ChangeEvent[newx][newy] = ChangeEvent[x][y];
7947   }
7948
7949 #if USE_NEW_CUSTOM_VALUE
7950     CustomValue[newx][newy] = CustomValue[x][y];
7951 #endif
7952
7953   ChangeDelay[x][y] = 0;
7954   ChangePage[x][y] = -1;
7955   ChangeCount[x][y] = 0;
7956   ChangeEvent[x][y] = -1;
7957
7958 #if USE_NEW_CUSTOM_VALUE
7959   CustomValue[x][y] = 0;
7960 #endif
7961
7962   /* copy animation control values to new field */
7963   GfxFrame[newx][newy]  = GfxFrame[x][y];
7964   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
7965   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
7966   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
7967
7968   Pushed[x][y] = Pushed[newx][newy] = FALSE;
7969
7970   /* some elements can leave other elements behind after moving */
7971 #if 1
7972   if (ei->move_leave_element != EL_EMPTY &&
7973       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7974       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7975 #else
7976   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7977       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7978       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7979 #endif
7980   {
7981     int move_leave_element = ei->move_leave_element;
7982
7983 #if 1
7984 #if 1
7985     /* this makes it possible to leave the removed element again */
7986     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7987       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7988 #else
7989     /* this makes it possible to leave the removed element again */
7990     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7991       move_leave_element = stored;
7992 #endif
7993 #else
7994     /* this makes it possible to leave the removed element again */
7995     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7996         ei->move_leave_element == EL_TRIGGER_ELEMENT)
7997       move_leave_element = stored;
7998 #endif
7999
8000     Feld[x][y] = move_leave_element;
8001
8002     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8003       MovDir[x][y] = direction;
8004
8005     InitField(x, y, FALSE);
8006
8007     if (GFX_CRUMBLED(Feld[x][y]))
8008       DrawLevelFieldCrumbledSandNeighbours(x, y);
8009
8010     if (ELEM_IS_PLAYER(move_leave_element))
8011       RelocatePlayer(x, y, move_leave_element);
8012   }
8013
8014   /* do this after checking for left-behind element */
8015   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8016
8017   if (!CAN_MOVE(element) ||
8018       (CAN_FALL(element) && direction == MV_DOWN &&
8019        (element == EL_SPRING ||
8020         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8021         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8022     GfxDir[x][y] = MovDir[newx][newy] = 0;
8023
8024   DrawLevelField(x, y);
8025   DrawLevelField(newx, newy);
8026
8027   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8028
8029   /* prevent pushed element from moving on in pushed direction */
8030   if (pushed_by_player && CAN_MOVE(element) &&
8031       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8032       !(element_info[element].move_pattern & direction))
8033     TurnRound(newx, newy);
8034
8035   /* prevent elements on conveyor belt from moving on in last direction */
8036   if (pushed_by_conveyor && CAN_FALL(element) &&
8037       direction & MV_HORIZONTAL)
8038     MovDir[newx][newy] = 0;
8039
8040   if (!pushed_by_player)
8041   {
8042     int nextx = newx + dx, nexty = newy + dy;
8043     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8044
8045     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8046
8047     if (CAN_FALL(element) && direction == MV_DOWN)
8048       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8049
8050     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8051       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8052
8053 #if USE_FIX_IMPACT_COLLISION
8054     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8055       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8056 #endif
8057   }
8058
8059   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8060   {
8061     TestIfBadThingTouchesPlayer(newx, newy);
8062     TestIfBadThingTouchesFriend(newx, newy);
8063
8064     if (!IS_CUSTOM_ELEMENT(element))
8065       TestIfBadThingTouchesOtherBadThing(newx, newy);
8066   }
8067   else if (element == EL_PENGUIN)
8068     TestIfFriendTouchesBadThing(newx, newy);
8069
8070   /* give the player one last chance (one more frame) to move away */
8071   if (CAN_FALL(element) && direction == MV_DOWN &&
8072       (last_line || (!IS_FREE(x, newy + 1) &&
8073                      (!IS_PLAYER(x, newy + 1) ||
8074                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8075     Impact(x, newy);
8076
8077   if (pushed_by_player && !game.use_change_when_pushing_bug)
8078   {
8079     int push_side = MV_DIR_OPPOSITE(direction);
8080     struct PlayerInfo *player = PLAYERINFO(x, y);
8081
8082     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8083                                player->index_bit, push_side);
8084     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8085                                         player->index_bit, push_side);
8086   }
8087
8088   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8089     MovDelay[newx][newy] = 1;
8090
8091   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8092
8093   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8094
8095 #if 0
8096   if (ChangePage[newx][newy] != -1)             /* delayed change */
8097   {
8098     int page = ChangePage[newx][newy];
8099     struct ElementChangeInfo *change = &ei->change_page[page];
8100
8101     ChangePage[newx][newy] = -1;
8102
8103     if (change->can_change)
8104     {
8105       if (ChangeElement(newx, newy, element, page))
8106       {
8107         if (change->post_change_function)
8108           change->post_change_function(newx, newy);
8109       }
8110     }
8111
8112     if (change->has_action)
8113       ExecuteCustomElementAction(newx, newy, element, page);
8114   }
8115 #endif
8116
8117   TestIfElementHitsCustomElement(newx, newy, direction);
8118   TestIfPlayerTouchesCustomElement(newx, newy);
8119   TestIfElementTouchesCustomElement(newx, newy);
8120
8121   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8122       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8123     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8124                              MV_DIR_OPPOSITE(direction));
8125 }
8126
8127 int AmoebeNachbarNr(int ax, int ay)
8128 {
8129   int i;
8130   int element = Feld[ax][ay];
8131   int group_nr = 0;
8132   static int xy[4][2] =
8133   {
8134     { 0, -1 },
8135     { -1, 0 },
8136     { +1, 0 },
8137     { 0, +1 }
8138   };
8139
8140   for (i = 0; i < NUM_DIRECTIONS; i++)
8141   {
8142     int x = ax + xy[i][0];
8143     int y = ay + xy[i][1];
8144
8145     if (!IN_LEV_FIELD(x, y))
8146       continue;
8147
8148     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8149       group_nr = AmoebaNr[x][y];
8150   }
8151
8152   return group_nr;
8153 }
8154
8155 void AmoebenVereinigen(int ax, int ay)
8156 {
8157   int i, x, y, xx, yy;
8158   int new_group_nr = AmoebaNr[ax][ay];
8159   static int xy[4][2] =
8160   {
8161     { 0, -1 },
8162     { -1, 0 },
8163     { +1, 0 },
8164     { 0, +1 }
8165   };
8166
8167   if (new_group_nr == 0)
8168     return;
8169
8170   for (i = 0; i < NUM_DIRECTIONS; i++)
8171   {
8172     x = ax + xy[i][0];
8173     y = ay + xy[i][1];
8174
8175     if (!IN_LEV_FIELD(x, y))
8176       continue;
8177
8178     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8179          Feld[x][y] == EL_BD_AMOEBA ||
8180          Feld[x][y] == EL_AMOEBA_DEAD) &&
8181         AmoebaNr[x][y] != new_group_nr)
8182     {
8183       int old_group_nr = AmoebaNr[x][y];
8184
8185       if (old_group_nr == 0)
8186         return;
8187
8188       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8189       AmoebaCnt[old_group_nr] = 0;
8190       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8191       AmoebaCnt2[old_group_nr] = 0;
8192
8193       SCAN_PLAYFIELD(xx, yy)
8194       {
8195         if (AmoebaNr[xx][yy] == old_group_nr)
8196           AmoebaNr[xx][yy] = new_group_nr;
8197       }
8198     }
8199   }
8200 }
8201
8202 void AmoebeUmwandeln(int ax, int ay)
8203 {
8204   int i, x, y;
8205
8206   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8207   {
8208     int group_nr = AmoebaNr[ax][ay];
8209
8210 #ifdef DEBUG
8211     if (group_nr == 0)
8212     {
8213       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8214       printf("AmoebeUmwandeln(): This should never happen!\n");
8215       return;
8216     }
8217 #endif
8218
8219     SCAN_PLAYFIELD(x, y)
8220     {
8221       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8222       {
8223         AmoebaNr[x][y] = 0;
8224         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8225       }
8226     }
8227
8228     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8229                             SND_AMOEBA_TURNING_TO_GEM :
8230                             SND_AMOEBA_TURNING_TO_ROCK));
8231     Bang(ax, ay);
8232   }
8233   else
8234   {
8235     static int xy[4][2] =
8236     {
8237       { 0, -1 },
8238       { -1, 0 },
8239       { +1, 0 },
8240       { 0, +1 }
8241     };
8242
8243     for (i = 0; i < NUM_DIRECTIONS; i++)
8244     {
8245       x = ax + xy[i][0];
8246       y = ay + xy[i][1];
8247
8248       if (!IN_LEV_FIELD(x, y))
8249         continue;
8250
8251       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8252       {
8253         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8254                               SND_AMOEBA_TURNING_TO_GEM :
8255                               SND_AMOEBA_TURNING_TO_ROCK));
8256         Bang(x, y);
8257       }
8258     }
8259   }
8260 }
8261
8262 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8263 {
8264   int x, y;
8265   int group_nr = AmoebaNr[ax][ay];
8266   boolean done = FALSE;
8267
8268 #ifdef DEBUG
8269   if (group_nr == 0)
8270   {
8271     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8272     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8273     return;
8274   }
8275 #endif
8276
8277   SCAN_PLAYFIELD(x, y)
8278   {
8279     if (AmoebaNr[x][y] == group_nr &&
8280         (Feld[x][y] == EL_AMOEBA_DEAD ||
8281          Feld[x][y] == EL_BD_AMOEBA ||
8282          Feld[x][y] == EL_AMOEBA_GROWING))
8283     {
8284       AmoebaNr[x][y] = 0;
8285       Feld[x][y] = new_element;
8286       InitField(x, y, FALSE);
8287       DrawLevelField(x, y);
8288       done = TRUE;
8289     }
8290   }
8291
8292   if (done)
8293     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8294                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8295                             SND_BD_AMOEBA_TURNING_TO_GEM));
8296 }
8297
8298 void AmoebeWaechst(int x, int y)
8299 {
8300   static unsigned long sound_delay = 0;
8301   static unsigned long sound_delay_value = 0;
8302
8303   if (!MovDelay[x][y])          /* start new growing cycle */
8304   {
8305     MovDelay[x][y] = 7;
8306
8307     if (DelayReached(&sound_delay, sound_delay_value))
8308     {
8309       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8310       sound_delay_value = 30;
8311     }
8312   }
8313
8314   if (MovDelay[x][y])           /* wait some time before growing bigger */
8315   {
8316     MovDelay[x][y]--;
8317     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8318     {
8319       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8320                                            6 - MovDelay[x][y]);
8321
8322       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8323     }
8324
8325     if (!MovDelay[x][y])
8326     {
8327       Feld[x][y] = Store[x][y];
8328       Store[x][y] = 0;
8329       DrawLevelField(x, y);
8330     }
8331   }
8332 }
8333
8334 void AmoebaDisappearing(int x, int y)
8335 {
8336   static unsigned long sound_delay = 0;
8337   static unsigned long sound_delay_value = 0;
8338
8339   if (!MovDelay[x][y])          /* start new shrinking cycle */
8340   {
8341     MovDelay[x][y] = 7;
8342
8343     if (DelayReached(&sound_delay, sound_delay_value))
8344       sound_delay_value = 30;
8345   }
8346
8347   if (MovDelay[x][y])           /* wait some time before shrinking */
8348   {
8349     MovDelay[x][y]--;
8350     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8351     {
8352       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8353                                            6 - MovDelay[x][y]);
8354
8355       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8356     }
8357
8358     if (!MovDelay[x][y])
8359     {
8360       Feld[x][y] = EL_EMPTY;
8361       DrawLevelField(x, y);
8362
8363       /* don't let mole enter this field in this cycle;
8364          (give priority to objects falling to this field from above) */
8365       Stop[x][y] = TRUE;
8366     }
8367   }
8368 }
8369
8370 void AmoebeAbleger(int ax, int ay)
8371 {
8372   int i;
8373   int element = Feld[ax][ay];
8374   int graphic = el2img(element);
8375   int newax = ax, neway = ay;
8376   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8377   static int xy[4][2] =
8378   {
8379     { 0, -1 },
8380     { -1, 0 },
8381     { +1, 0 },
8382     { 0, +1 }
8383   };
8384
8385   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8386   {
8387     Feld[ax][ay] = EL_AMOEBA_DEAD;
8388     DrawLevelField(ax, ay);
8389     return;
8390   }
8391
8392   if (IS_ANIMATED(graphic))
8393     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8394
8395   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8396     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8397
8398   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8399   {
8400     MovDelay[ax][ay]--;
8401     if (MovDelay[ax][ay])
8402       return;
8403   }
8404
8405   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8406   {
8407     int start = RND(4);
8408     int x = ax + xy[start][0];
8409     int y = ay + xy[start][1];
8410
8411     if (!IN_LEV_FIELD(x, y))
8412       return;
8413
8414     if (IS_FREE(x, y) ||
8415         CAN_GROW_INTO(Feld[x][y]) ||
8416         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8417         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8418     {
8419       newax = x;
8420       neway = y;
8421     }
8422
8423     if (newax == ax && neway == ay)
8424       return;
8425   }
8426   else                          /* normal or "filled" (BD style) amoeba */
8427   {
8428     int start = RND(4);
8429     boolean waiting_for_player = FALSE;
8430
8431     for (i = 0; i < NUM_DIRECTIONS; i++)
8432     {
8433       int j = (start + i) % 4;
8434       int x = ax + xy[j][0];
8435       int y = ay + xy[j][1];
8436
8437       if (!IN_LEV_FIELD(x, y))
8438         continue;
8439
8440       if (IS_FREE(x, y) ||
8441           CAN_GROW_INTO(Feld[x][y]) ||
8442           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8443           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8444       {
8445         newax = x;
8446         neway = y;
8447         break;
8448       }
8449       else if (IS_PLAYER(x, y))
8450         waiting_for_player = TRUE;
8451     }
8452
8453     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8454     {
8455       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8456       {
8457         Feld[ax][ay] = EL_AMOEBA_DEAD;
8458         DrawLevelField(ax, ay);
8459         AmoebaCnt[AmoebaNr[ax][ay]]--;
8460
8461         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8462         {
8463           if (element == EL_AMOEBA_FULL)
8464             AmoebeUmwandeln(ax, ay);
8465           else if (element == EL_BD_AMOEBA)
8466             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8467         }
8468       }
8469       return;
8470     }
8471     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8472     {
8473       /* amoeba gets larger by growing in some direction */
8474
8475       int new_group_nr = AmoebaNr[ax][ay];
8476
8477 #ifdef DEBUG
8478   if (new_group_nr == 0)
8479   {
8480     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8481     printf("AmoebeAbleger(): This should never happen!\n");
8482     return;
8483   }
8484 #endif
8485
8486       AmoebaNr[newax][neway] = new_group_nr;
8487       AmoebaCnt[new_group_nr]++;
8488       AmoebaCnt2[new_group_nr]++;
8489
8490       /* if amoeba touches other amoeba(s) after growing, unify them */
8491       AmoebenVereinigen(newax, neway);
8492
8493       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8494       {
8495         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8496         return;
8497       }
8498     }
8499   }
8500
8501   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8502       (neway == lev_fieldy - 1 && newax != ax))
8503   {
8504     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8505     Store[newax][neway] = element;
8506   }
8507   else if (neway == ay || element == EL_EMC_DRIPPER)
8508   {
8509     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8510
8511     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8512   }
8513   else
8514   {
8515     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8516     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8517     Store[ax][ay] = EL_AMOEBA_DROP;
8518     ContinueMoving(ax, ay);
8519     return;
8520   }
8521
8522   DrawLevelField(newax, neway);
8523 }
8524
8525 void Life(int ax, int ay)
8526 {
8527   int x1, y1, x2, y2;
8528   int life_time = 40;
8529   int element = Feld[ax][ay];
8530   int graphic = el2img(element);
8531   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8532                          level.biomaze);
8533   boolean changed = FALSE;
8534
8535   if (IS_ANIMATED(graphic))
8536     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8537
8538   if (Stop[ax][ay])
8539     return;
8540
8541   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8542     MovDelay[ax][ay] = life_time;
8543
8544   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8545   {
8546     MovDelay[ax][ay]--;
8547     if (MovDelay[ax][ay])
8548       return;
8549   }
8550
8551   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8552   {
8553     int xx = ax+x1, yy = ay+y1;
8554     int nachbarn = 0;
8555
8556     if (!IN_LEV_FIELD(xx, yy))
8557       continue;
8558
8559     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8560     {
8561       int x = xx+x2, y = yy+y2;
8562
8563       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8564         continue;
8565
8566       if (((Feld[x][y] == element ||
8567             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8568            !Stop[x][y]) ||
8569           (IS_FREE(x, y) && Stop[x][y]))
8570         nachbarn++;
8571     }
8572
8573     if (xx == ax && yy == ay)           /* field in the middle */
8574     {
8575       if (nachbarn < life_parameter[0] ||
8576           nachbarn > life_parameter[1])
8577       {
8578         Feld[xx][yy] = EL_EMPTY;
8579         if (!Stop[xx][yy])
8580           DrawLevelField(xx, yy);
8581         Stop[xx][yy] = TRUE;
8582         changed = TRUE;
8583       }
8584     }
8585     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8586     {                                   /* free border field */
8587       if (nachbarn >= life_parameter[2] &&
8588           nachbarn <= life_parameter[3])
8589       {
8590         Feld[xx][yy] = element;
8591         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8592         if (!Stop[xx][yy])
8593           DrawLevelField(xx, yy);
8594         Stop[xx][yy] = TRUE;
8595         changed = TRUE;
8596       }
8597     }
8598   }
8599
8600   if (changed)
8601     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8602                    SND_GAME_OF_LIFE_GROWING);
8603 }
8604
8605 static void InitRobotWheel(int x, int y)
8606 {
8607   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8608 }
8609
8610 static void RunRobotWheel(int x, int y)
8611 {
8612   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8613 }
8614
8615 static void StopRobotWheel(int x, int y)
8616 {
8617   if (ZX == x && ZY == y)
8618     ZX = ZY = -1;
8619 }
8620
8621 static void InitTimegateWheel(int x, int y)
8622 {
8623   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8624 }
8625
8626 static void RunTimegateWheel(int x, int y)
8627 {
8628   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8629 }
8630
8631 static void InitMagicBallDelay(int x, int y)
8632 {
8633 #if 1
8634   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8635 #else
8636   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8637 #endif
8638 }
8639
8640 static void ActivateMagicBall(int bx, int by)
8641 {
8642   int x, y;
8643
8644   if (level.ball_random)
8645   {
8646     int pos_border = RND(8);    /* select one of the eight border elements */
8647     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8648     int xx = pos_content % 3;
8649     int yy = pos_content / 3;
8650
8651     x = bx - 1 + xx;
8652     y = by - 1 + yy;
8653
8654     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8655       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8656   }
8657   else
8658   {
8659     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8660     {
8661       int xx = x - bx + 1;
8662       int yy = y - by + 1;
8663
8664       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8665         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8666     }
8667   }
8668
8669   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8670 }
8671
8672 void CheckExit(int x, int y)
8673 {
8674   if (local_player->gems_still_needed > 0 ||
8675       local_player->sokobanfields_still_needed > 0 ||
8676       local_player->lights_still_needed > 0)
8677   {
8678     int element = Feld[x][y];
8679     int graphic = el2img(element);
8680
8681     if (IS_ANIMATED(graphic))
8682       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8683
8684     return;
8685   }
8686
8687   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8688     return;
8689
8690   Feld[x][y] = EL_EXIT_OPENING;
8691
8692   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8693 }
8694
8695 void CheckExitEM(int x, int y)
8696 {
8697   if (local_player->gems_still_needed > 0 ||
8698       local_player->sokobanfields_still_needed > 0 ||
8699       local_player->lights_still_needed > 0)
8700   {
8701     int element = Feld[x][y];
8702     int graphic = el2img(element);
8703
8704     if (IS_ANIMATED(graphic))
8705       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8706
8707     return;
8708   }
8709
8710   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8711     return;
8712
8713   Feld[x][y] = EL_EM_EXIT_OPENING;
8714
8715   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8716 }
8717
8718 void CheckExitSteel(int x, int y)
8719 {
8720   if (local_player->gems_still_needed > 0 ||
8721       local_player->sokobanfields_still_needed > 0 ||
8722       local_player->lights_still_needed > 0)
8723   {
8724     int element = Feld[x][y];
8725     int graphic = el2img(element);
8726
8727     if (IS_ANIMATED(graphic))
8728       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8729
8730     return;
8731   }
8732
8733   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8734     return;
8735
8736   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8737
8738   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8739 }
8740
8741 void CheckExitSteelEM(int x, int y)
8742 {
8743   if (local_player->gems_still_needed > 0 ||
8744       local_player->sokobanfields_still_needed > 0 ||
8745       local_player->lights_still_needed > 0)
8746   {
8747     int element = Feld[x][y];
8748     int graphic = el2img(element);
8749
8750     if (IS_ANIMATED(graphic))
8751       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8752
8753     return;
8754   }
8755
8756   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8757     return;
8758
8759   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8760
8761   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8762 }
8763
8764 void CheckExitSP(int x, int y)
8765 {
8766   if (local_player->gems_still_needed > 0)
8767   {
8768     int element = Feld[x][y];
8769     int graphic = el2img(element);
8770
8771     if (IS_ANIMATED(graphic))
8772       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8773
8774     return;
8775   }
8776
8777   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8778     return;
8779
8780   Feld[x][y] = EL_SP_EXIT_OPENING;
8781
8782   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8783 }
8784
8785 static void CloseAllOpenTimegates()
8786 {
8787   int x, y;
8788
8789   SCAN_PLAYFIELD(x, y)
8790   {
8791     int element = Feld[x][y];
8792
8793     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8794     {
8795       Feld[x][y] = EL_TIMEGATE_CLOSING;
8796
8797       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8798     }
8799   }
8800 }
8801
8802 void DrawTwinkleOnField(int x, int y)
8803 {
8804   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8805     return;
8806
8807   if (Feld[x][y] == EL_BD_DIAMOND)
8808     return;
8809
8810   if (MovDelay[x][y] == 0)      /* next animation frame */
8811     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8812
8813   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8814   {
8815     MovDelay[x][y]--;
8816
8817     if (setup.direct_draw && MovDelay[x][y])
8818       SetDrawtoField(DRAW_BUFFERED);
8819
8820     DrawLevelElementAnimation(x, y, Feld[x][y]);
8821
8822     if (MovDelay[x][y] != 0)
8823     {
8824       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8825                                            10 - MovDelay[x][y]);
8826
8827       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8828
8829       if (setup.direct_draw)
8830       {
8831         int dest_x, dest_y;
8832
8833         dest_x = FX + SCREENX(x) * TILEX;
8834         dest_y = FY + SCREENY(y) * TILEY;
8835
8836         BlitBitmap(drawto_field, window,
8837                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8838         SetDrawtoField(DRAW_DIRECT);
8839       }
8840     }
8841   }
8842 }
8843
8844 void MauerWaechst(int x, int y)
8845 {
8846   int delay = 6;
8847
8848   if (!MovDelay[x][y])          /* next animation frame */
8849     MovDelay[x][y] = 3 * delay;
8850
8851   if (MovDelay[x][y])           /* wait some time before next frame */
8852   {
8853     MovDelay[x][y]--;
8854
8855     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8856     {
8857       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8858       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8859
8860       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8861     }
8862
8863     if (!MovDelay[x][y])
8864     {
8865       if (MovDir[x][y] == MV_LEFT)
8866       {
8867         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8868           DrawLevelField(x - 1, y);
8869       }
8870       else if (MovDir[x][y] == MV_RIGHT)
8871       {
8872         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8873           DrawLevelField(x + 1, y);
8874       }
8875       else if (MovDir[x][y] == MV_UP)
8876       {
8877         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8878           DrawLevelField(x, y - 1);
8879       }
8880       else
8881       {
8882         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8883           DrawLevelField(x, y + 1);
8884       }
8885
8886       Feld[x][y] = Store[x][y];
8887       Store[x][y] = 0;
8888       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8889       DrawLevelField(x, y);
8890     }
8891   }
8892 }
8893
8894 void MauerAbleger(int ax, int ay)
8895 {
8896   int element = Feld[ax][ay];
8897   int graphic = el2img(element);
8898   boolean oben_frei = FALSE, unten_frei = FALSE;
8899   boolean links_frei = FALSE, rechts_frei = FALSE;
8900   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8901   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8902   boolean new_wall = FALSE;
8903
8904   if (IS_ANIMATED(graphic))
8905     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8906
8907   if (!MovDelay[ax][ay])        /* start building new wall */
8908     MovDelay[ax][ay] = 6;
8909
8910   if (MovDelay[ax][ay])         /* wait some time before building new wall */
8911   {
8912     MovDelay[ax][ay]--;
8913     if (MovDelay[ax][ay])
8914       return;
8915   }
8916
8917   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8918     oben_frei = TRUE;
8919   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8920     unten_frei = TRUE;
8921   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8922     links_frei = TRUE;
8923   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8924     rechts_frei = TRUE;
8925
8926   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
8927       element == EL_EXPANDABLE_WALL_ANY)
8928   {
8929     if (oben_frei)
8930     {
8931       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
8932       Store[ax][ay-1] = element;
8933       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8934       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8935         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8936                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
8937       new_wall = TRUE;
8938     }
8939     if (unten_frei)
8940     {
8941       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
8942       Store[ax][ay+1] = element;
8943       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8944       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8945         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8946                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8947       new_wall = TRUE;
8948     }
8949   }
8950
8951   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8952       element == EL_EXPANDABLE_WALL_ANY ||
8953       element == EL_EXPANDABLE_WALL ||
8954       element == EL_BD_EXPANDABLE_WALL)
8955   {
8956     if (links_frei)
8957     {
8958       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8959       Store[ax-1][ay] = element;
8960       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8961       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8962         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8963                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8964       new_wall = TRUE;
8965     }
8966
8967     if (rechts_frei)
8968     {
8969       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8970       Store[ax+1][ay] = element;
8971       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8972       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8973         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8974                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8975       new_wall = TRUE;
8976     }
8977   }
8978
8979   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8980     DrawLevelField(ax, ay);
8981
8982   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8983     oben_massiv = TRUE;
8984   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8985     unten_massiv = TRUE;
8986   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8987     links_massiv = TRUE;
8988   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8989     rechts_massiv = TRUE;
8990
8991   if (((oben_massiv && unten_massiv) ||
8992        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8993        element == EL_EXPANDABLE_WALL) &&
8994       ((links_massiv && rechts_massiv) ||
8995        element == EL_EXPANDABLE_WALL_VERTICAL))
8996     Feld[ax][ay] = EL_WALL;
8997
8998   if (new_wall)
8999     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9000 }
9001
9002 void MauerAblegerStahl(int ax, int ay)
9003 {
9004   int element = Feld[ax][ay];
9005   int graphic = el2img(element);
9006   boolean oben_frei = FALSE, unten_frei = FALSE;
9007   boolean links_frei = FALSE, rechts_frei = FALSE;
9008   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9009   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9010   boolean new_wall = FALSE;
9011
9012   if (IS_ANIMATED(graphic))
9013     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9014
9015   if (!MovDelay[ax][ay])        /* start building new wall */
9016     MovDelay[ax][ay] = 6;
9017
9018   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9019   {
9020     MovDelay[ax][ay]--;
9021     if (MovDelay[ax][ay])
9022       return;
9023   }
9024
9025   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9026     oben_frei = TRUE;
9027   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9028     unten_frei = TRUE;
9029   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9030     links_frei = TRUE;
9031   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9032     rechts_frei = TRUE;
9033
9034   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9035       element == EL_EXPANDABLE_STEELWALL_ANY)
9036   {
9037     if (oben_frei)
9038     {
9039       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9040       Store[ax][ay-1] = element;
9041       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9042       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9043         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9044                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9045       new_wall = TRUE;
9046     }
9047     if (unten_frei)
9048     {
9049       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9050       Store[ax][ay+1] = element;
9051       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9052       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9053         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9054                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9055       new_wall = TRUE;
9056     }
9057   }
9058
9059   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9060       element == EL_EXPANDABLE_STEELWALL_ANY)
9061   {
9062     if (links_frei)
9063     {
9064       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9065       Store[ax-1][ay] = element;
9066       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9067       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9068         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9069                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9070       new_wall = TRUE;
9071     }
9072
9073     if (rechts_frei)
9074     {
9075       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9076       Store[ax+1][ay] = element;
9077       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9078       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9079         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9080                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9081       new_wall = TRUE;
9082     }
9083   }
9084
9085   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9086     oben_massiv = TRUE;
9087   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9088     unten_massiv = TRUE;
9089   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9090     links_massiv = TRUE;
9091   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9092     rechts_massiv = TRUE;
9093
9094   if (((oben_massiv && unten_massiv) ||
9095        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9096       ((links_massiv && rechts_massiv) ||
9097        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9098     Feld[ax][ay] = EL_WALL;
9099
9100   if (new_wall)
9101     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9102 }
9103
9104 void CheckForDragon(int x, int y)
9105 {
9106   int i, j;
9107   boolean dragon_found = FALSE;
9108   static int xy[4][2] =
9109   {
9110     { 0, -1 },
9111     { -1, 0 },
9112     { +1, 0 },
9113     { 0, +1 }
9114   };
9115
9116   for (i = 0; i < NUM_DIRECTIONS; i++)
9117   {
9118     for (j = 0; j < 4; j++)
9119     {
9120       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9121
9122       if (IN_LEV_FIELD(xx, yy) &&
9123           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9124       {
9125         if (Feld[xx][yy] == EL_DRAGON)
9126           dragon_found = TRUE;
9127       }
9128       else
9129         break;
9130     }
9131   }
9132
9133   if (!dragon_found)
9134   {
9135     for (i = 0; i < NUM_DIRECTIONS; i++)
9136     {
9137       for (j = 0; j < 3; j++)
9138       {
9139         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9140   
9141         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9142         {
9143           Feld[xx][yy] = EL_EMPTY;
9144           DrawLevelField(xx, yy);
9145         }
9146         else
9147           break;
9148       }
9149     }
9150   }
9151 }
9152
9153 static void InitBuggyBase(int x, int y)
9154 {
9155   int element = Feld[x][y];
9156   int activating_delay = FRAMES_PER_SECOND / 4;
9157
9158   ChangeDelay[x][y] =
9159     (element == EL_SP_BUGGY_BASE ?
9160      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9161      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9162      activating_delay :
9163      element == EL_SP_BUGGY_BASE_ACTIVE ?
9164      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9165 }
9166
9167 static void WarnBuggyBase(int x, int y)
9168 {
9169   int i;
9170   static int xy[4][2] =
9171   {
9172     { 0, -1 },
9173     { -1, 0 },
9174     { +1, 0 },
9175     { 0, +1 }
9176   };
9177
9178   for (i = 0; i < NUM_DIRECTIONS; i++)
9179   {
9180     int xx = x + xy[i][0];
9181     int yy = y + xy[i][1];
9182
9183     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9184     {
9185       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9186
9187       break;
9188     }
9189   }
9190 }
9191
9192 static void InitTrap(int x, int y)
9193 {
9194   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9195 }
9196
9197 static void ActivateTrap(int x, int y)
9198 {
9199   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9200 }
9201
9202 static void ChangeActiveTrap(int x, int y)
9203 {
9204   int graphic = IMG_TRAP_ACTIVE;
9205
9206   /* if new animation frame was drawn, correct crumbled sand border */
9207   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9208     DrawLevelFieldCrumbledSand(x, y);
9209 }
9210
9211 static int getSpecialActionElement(int element, int number, int base_element)
9212 {
9213   return (element != EL_EMPTY ? element :
9214           number != -1 ? base_element + number - 1 :
9215           EL_EMPTY);
9216 }
9217
9218 static int getModifiedActionNumber(int value_old, int operator, int operand,
9219                                    int value_min, int value_max)
9220 {
9221   int value_new = (operator == CA_MODE_SET      ? operand :
9222                    operator == CA_MODE_ADD      ? value_old + operand :
9223                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9224                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9225                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9226                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9227                    value_old);
9228
9229   return (value_new < value_min ? value_min :
9230           value_new > value_max ? value_max :
9231           value_new);
9232 }
9233
9234 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9235 {
9236   struct ElementInfo *ei = &element_info[element];
9237   struct ElementChangeInfo *change = &ei->change_page[page];
9238   int target_element = change->target_element;
9239   int action_type = change->action_type;
9240   int action_mode = change->action_mode;
9241   int action_arg = change->action_arg;
9242   int i;
9243
9244   if (!change->has_action)
9245     return;
9246
9247   /* ---------- determine action paramater values -------------------------- */
9248
9249   int level_time_value =
9250     (level.time > 0 ? TimeLeft :
9251      TimePlayed);
9252
9253   int action_arg_element =
9254     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9255      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9256      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9257      EL_EMPTY);
9258
9259   int action_arg_direction =
9260     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9261      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9262      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9263      change->actual_trigger_side :
9264      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9265      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9266      MV_NONE);
9267
9268   int action_arg_number_min =
9269     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9270      CA_ARG_MIN);
9271
9272   int action_arg_number_max =
9273     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9274      action_type == CA_SET_LEVEL_GEMS ? 999 :
9275      action_type == CA_SET_LEVEL_TIME ? 9999 :
9276      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9277      action_type == CA_SET_CE_VALUE ? 9999 :
9278      action_type == CA_SET_CE_SCORE ? 9999 :
9279      CA_ARG_MAX);
9280
9281   int action_arg_number_reset =
9282     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9283      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9284      action_type == CA_SET_LEVEL_TIME ? level.time :
9285      action_type == CA_SET_LEVEL_SCORE ? 0 :
9286 #if USE_NEW_CUSTOM_VALUE
9287      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9288 #else
9289      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9290 #endif
9291      action_type == CA_SET_CE_SCORE ? 0 :
9292      0);
9293
9294   int action_arg_number =
9295     (action_arg <= CA_ARG_MAX ? action_arg :
9296      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9297      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9298      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9299      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9300      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9301      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9302 #if USE_NEW_CUSTOM_VALUE
9303      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9304 #else
9305      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9306 #endif
9307      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9308      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9309      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9310      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9311      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9312      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9313      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9314      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9315      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9316      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9317      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9318      -1);
9319
9320   int action_arg_number_old =
9321     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9322      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9323      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9324      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9325      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9326      0);
9327
9328   int action_arg_number_new =
9329     getModifiedActionNumber(action_arg_number_old,
9330                             action_mode, action_arg_number,
9331                             action_arg_number_min, action_arg_number_max);
9332
9333   int trigger_player_bits =
9334     (change->actual_trigger_player >= EL_PLAYER_1 &&
9335      change->actual_trigger_player <= EL_PLAYER_4 ?
9336      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9337      PLAYER_BITS_ANY);
9338
9339   int action_arg_player_bits =
9340     (action_arg >= CA_ARG_PLAYER_1 &&
9341      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9342      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9343      PLAYER_BITS_ANY);
9344
9345   /* ---------- execute action  -------------------------------------------- */
9346
9347   switch (action_type)
9348   {
9349     case CA_NO_ACTION:
9350     {
9351       return;
9352     }
9353
9354     /* ---------- level actions  ------------------------------------------- */
9355
9356     case CA_RESTART_LEVEL:
9357     {
9358       game.restart_level = TRUE;
9359
9360       break;
9361     }
9362
9363     case CA_SHOW_ENVELOPE:
9364     {
9365       int element = getSpecialActionElement(action_arg_element,
9366                                             action_arg_number, EL_ENVELOPE_1);
9367
9368       if (IS_ENVELOPE(element))
9369         local_player->show_envelope = element;
9370
9371       break;
9372     }
9373
9374     case CA_SET_LEVEL_TIME:
9375     {
9376       if (level.time > 0)       /* only modify limited time value */
9377       {
9378         TimeLeft = action_arg_number_new;
9379
9380 #if 1
9381         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
9382
9383         DisplayGameControlValues();
9384 #else
9385         DrawGameValue_Time(TimeLeft);
9386 #endif
9387
9388         if (!TimeLeft && setup.time_limit)
9389           for (i = 0; i < MAX_PLAYERS; i++)
9390             KillPlayer(&stored_player[i]);
9391       }
9392
9393       break;
9394     }
9395
9396     case CA_SET_LEVEL_SCORE:
9397     {
9398       local_player->score = action_arg_number_new;
9399
9400 #if 1
9401       game_control_value[GAME_CONTROL_SCORE] = local_player->score;
9402
9403       DisplayGameControlValues();
9404 #else
9405       DrawGameValue_Score(local_player->score);
9406 #endif
9407
9408       break;
9409     }
9410
9411     case CA_SET_LEVEL_GEMS:
9412     {
9413       local_player->gems_still_needed = action_arg_number_new;
9414
9415 #if 1
9416       game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
9417
9418       DisplayGameControlValues();
9419 #else
9420       DrawGameValue_Emeralds(local_player->gems_still_needed);
9421 #endif
9422
9423       break;
9424     }
9425
9426 #if !USE_PLAYER_GRAVITY
9427     case CA_SET_LEVEL_GRAVITY:
9428     {
9429       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
9430                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
9431                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9432                       game.gravity);
9433       break;
9434     }
9435 #endif
9436
9437     case CA_SET_LEVEL_WIND:
9438     {
9439       game.wind_direction = action_arg_direction;
9440
9441       break;
9442     }
9443
9444     /* ---------- player actions  ------------------------------------------ */
9445
9446     case CA_MOVE_PLAYER:
9447     {
9448       /* automatically move to the next field in specified direction */
9449       for (i = 0; i < MAX_PLAYERS; i++)
9450         if (trigger_player_bits & (1 << i))
9451           stored_player[i].programmed_action = action_arg_direction;
9452
9453       break;
9454     }
9455
9456     case CA_EXIT_PLAYER:
9457     {
9458       for (i = 0; i < MAX_PLAYERS; i++)
9459         if (action_arg_player_bits & (1 << i))
9460           PlayerWins(&stored_player[i]);
9461
9462       break;
9463     }
9464
9465     case CA_KILL_PLAYER:
9466     {
9467       for (i = 0; i < MAX_PLAYERS; i++)
9468         if (action_arg_player_bits & (1 << i))
9469           KillPlayer(&stored_player[i]);
9470
9471       break;
9472     }
9473
9474     case CA_SET_PLAYER_KEYS:
9475     {
9476       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9477       int element = getSpecialActionElement(action_arg_element,
9478                                             action_arg_number, EL_KEY_1);
9479
9480       if (IS_KEY(element))
9481       {
9482         for (i = 0; i < MAX_PLAYERS; i++)
9483         {
9484           if (trigger_player_bits & (1 << i))
9485           {
9486             stored_player[i].key[KEY_NR(element)] = key_state;
9487
9488             DrawGameDoorValues();
9489           }
9490         }
9491       }
9492
9493       break;
9494     }
9495
9496     case CA_SET_PLAYER_SPEED:
9497     {
9498       for (i = 0; i < MAX_PLAYERS; i++)
9499       {
9500         if (trigger_player_bits & (1 << i))
9501         {
9502           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9503
9504           if (action_arg == CA_ARG_SPEED_FASTER &&
9505               stored_player[i].cannot_move)
9506           {
9507             action_arg_number = STEPSIZE_VERY_SLOW;
9508           }
9509           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9510                    action_arg == CA_ARG_SPEED_FASTER)
9511           {
9512             action_arg_number = 2;
9513             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9514                            CA_MODE_MULTIPLY);
9515           }
9516           else if (action_arg == CA_ARG_NUMBER_RESET)
9517           {
9518             action_arg_number = level.initial_player_stepsize[i];
9519           }
9520
9521           move_stepsize =
9522             getModifiedActionNumber(move_stepsize,
9523                                     action_mode,
9524                                     action_arg_number,
9525                                     action_arg_number_min,
9526                                     action_arg_number_max);
9527
9528           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9529         }
9530       }
9531
9532       break;
9533     }
9534
9535     case CA_SET_PLAYER_SHIELD:
9536     {
9537       for (i = 0; i < MAX_PLAYERS; i++)
9538       {
9539         if (trigger_player_bits & (1 << i))
9540         {
9541           if (action_arg == CA_ARG_SHIELD_OFF)
9542           {
9543             stored_player[i].shield_normal_time_left = 0;
9544             stored_player[i].shield_deadly_time_left = 0;
9545           }
9546           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9547           {
9548             stored_player[i].shield_normal_time_left = 999999;
9549           }
9550           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9551           {
9552             stored_player[i].shield_normal_time_left = 999999;
9553             stored_player[i].shield_deadly_time_left = 999999;
9554           }
9555         }
9556       }
9557
9558       break;
9559     }
9560
9561 #if USE_PLAYER_GRAVITY
9562     case CA_SET_PLAYER_GRAVITY:
9563     {
9564       for (i = 0; i < MAX_PLAYERS; i++)
9565       {
9566         if (trigger_player_bits & (1 << i))
9567         {
9568           stored_player[i].gravity =
9569             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9570              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9571              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9572              stored_player[i].gravity);
9573         }
9574       }
9575
9576       break;
9577     }
9578 #endif
9579
9580     case CA_SET_PLAYER_ARTWORK:
9581     {
9582       for (i = 0; i < MAX_PLAYERS; i++)
9583       {
9584         if (trigger_player_bits & (1 << i))
9585         {
9586           int artwork_element = action_arg_element;
9587
9588           if (action_arg == CA_ARG_ELEMENT_RESET)
9589             artwork_element =
9590               (level.use_artwork_element[i] ? level.artwork_element[i] :
9591                stored_player[i].element_nr);
9592
9593 #if USE_GFX_RESET_PLAYER_ARTWORK
9594           if (stored_player[i].artwork_element != artwork_element)
9595             stored_player[i].Frame = 0;
9596 #endif
9597
9598           stored_player[i].artwork_element = artwork_element;
9599
9600           SetPlayerWaiting(&stored_player[i], FALSE);
9601
9602           /* set number of special actions for bored and sleeping animation */
9603           stored_player[i].num_special_action_bored =
9604             get_num_special_action(artwork_element,
9605                                    ACTION_BORING_1, ACTION_BORING_LAST);
9606           stored_player[i].num_special_action_sleeping =
9607             get_num_special_action(artwork_element,
9608                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9609         }
9610       }
9611
9612       break;
9613     }
9614
9615     /* ---------- CE actions  ---------------------------------------------- */
9616
9617     case CA_SET_CE_VALUE:
9618     {
9619 #if USE_NEW_CUSTOM_VALUE
9620       int last_ce_value = CustomValue[x][y];
9621
9622       CustomValue[x][y] = action_arg_number_new;
9623
9624       if (CustomValue[x][y] != last_ce_value)
9625       {
9626         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9627         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9628
9629         if (CustomValue[x][y] == 0)
9630         {
9631           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9632           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9633         }
9634       }
9635 #endif
9636
9637       break;
9638     }
9639
9640     case CA_SET_CE_SCORE:
9641     {
9642 #if USE_NEW_CUSTOM_VALUE
9643       int last_ce_score = ei->collect_score;
9644
9645       ei->collect_score = action_arg_number_new;
9646
9647       if (ei->collect_score != last_ce_score)
9648       {
9649         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9650         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9651
9652         if (ei->collect_score == 0)
9653         {
9654           int xx, yy;
9655
9656           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9657           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9658
9659           /*
9660             This is a very special case that seems to be a mixture between
9661             CheckElementChange() and CheckTriggeredElementChange(): while
9662             the first one only affects single elements that are triggered
9663             directly, the second one affects multiple elements in the playfield
9664             that are triggered indirectly by another element. This is a third
9665             case: Changing the CE score always affects multiple identical CEs,
9666             so every affected CE must be checked, not only the single CE for
9667             which the CE score was changed in the first place (as every instance
9668             of that CE shares the same CE score, and therefore also can change)!
9669           */
9670           SCAN_PLAYFIELD(xx, yy)
9671           {
9672             if (Feld[xx][yy] == element)
9673               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9674                                  CE_SCORE_GETS_ZERO);
9675           }
9676         }
9677       }
9678 #endif
9679
9680       break;
9681     }
9682
9683     /* ---------- engine actions  ------------------------------------------ */
9684
9685     case CA_SET_ENGINE_SCAN_MODE:
9686     {
9687       InitPlayfieldScanMode(action_arg);
9688
9689       break;
9690     }
9691
9692     default:
9693       break;
9694   }
9695 }
9696
9697 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9698 {
9699   int old_element = Feld[x][y];
9700   int new_element = GetElementFromGroupElement(element);
9701   int previous_move_direction = MovDir[x][y];
9702 #if USE_NEW_CUSTOM_VALUE
9703   int last_ce_value = CustomValue[x][y];
9704 #endif
9705   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9706   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9707   boolean add_player_onto_element = (new_element_is_player &&
9708 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9709                                      /* this breaks SnakeBite when a snake is
9710                                         halfway through a door that closes */
9711                                      /* NOW FIXED AT LEVEL INIT IN files.c */
9712                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9713 #endif
9714                                      IS_WALKABLE(old_element));
9715
9716 #if 0
9717   /* check if element under the player changes from accessible to unaccessible
9718      (needed for special case of dropping element which then changes) */
9719   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9720       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9721   {
9722     Bang(x, y);
9723
9724     return;
9725   }
9726 #endif
9727
9728   if (!add_player_onto_element)
9729   {
9730     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9731       RemoveMovingField(x, y);
9732     else
9733       RemoveField(x, y);
9734
9735     Feld[x][y] = new_element;
9736
9737 #if !USE_GFX_RESET_GFX_ANIMATION
9738     ResetGfxAnimation(x, y);
9739     ResetRandomAnimationValue(x, y);
9740 #endif
9741
9742     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9743       MovDir[x][y] = previous_move_direction;
9744
9745 #if USE_NEW_CUSTOM_VALUE
9746     if (element_info[new_element].use_last_ce_value)
9747       CustomValue[x][y] = last_ce_value;
9748 #endif
9749
9750     InitField_WithBug1(x, y, FALSE);
9751
9752     new_element = Feld[x][y];   /* element may have changed */
9753
9754 #if USE_GFX_RESET_GFX_ANIMATION
9755     ResetGfxAnimation(x, y);
9756     ResetRandomAnimationValue(x, y);
9757 #endif
9758
9759     DrawLevelField(x, y);
9760
9761     if (GFX_CRUMBLED(new_element))
9762       DrawLevelFieldCrumbledSandNeighbours(x, y);
9763   }
9764
9765 #if 1
9766   /* check if element under the player changes from accessible to unaccessible
9767      (needed for special case of dropping element which then changes) */
9768   /* (must be checked after creating new element for walkable group elements) */
9769 #if USE_FIX_KILLED_BY_NON_WALKABLE
9770   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9771       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9772   {
9773     Bang(x, y);
9774
9775     return;
9776   }
9777 #else
9778   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9779       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9780   {
9781     Bang(x, y);
9782
9783     return;
9784   }
9785 #endif
9786 #endif
9787
9788   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9789   if (new_element_is_player)
9790     RelocatePlayer(x, y, new_element);
9791
9792   if (is_change)
9793     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9794
9795   TestIfBadThingTouchesPlayer(x, y);
9796   TestIfPlayerTouchesCustomElement(x, y);
9797   TestIfElementTouchesCustomElement(x, y);
9798 }
9799
9800 static void CreateField(int x, int y, int element)
9801 {
9802   CreateFieldExt(x, y, element, FALSE);
9803 }
9804
9805 static void CreateElementFromChange(int x, int y, int element)
9806 {
9807   element = GET_VALID_RUNTIME_ELEMENT(element);
9808
9809 #if USE_STOP_CHANGED_ELEMENTS
9810   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9811   {
9812     int old_element = Feld[x][y];
9813
9814     /* prevent changed element from moving in same engine frame
9815        unless both old and new element can either fall or move */
9816     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9817         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9818       Stop[x][y] = TRUE;
9819   }
9820 #endif
9821
9822   CreateFieldExt(x, y, element, TRUE);
9823 }
9824
9825 static boolean ChangeElement(int x, int y, int element, int page)
9826 {
9827   struct ElementInfo *ei = &element_info[element];
9828   struct ElementChangeInfo *change = &ei->change_page[page];
9829   int ce_value = CustomValue[x][y];
9830   int ce_score = ei->collect_score;
9831   int target_element;
9832   int old_element = Feld[x][y];
9833
9834   /* always use default change event to prevent running into a loop */
9835   if (ChangeEvent[x][y] == -1)
9836     ChangeEvent[x][y] = CE_DELAY;
9837
9838   if (ChangeEvent[x][y] == CE_DELAY)
9839   {
9840     /* reset actual trigger element, trigger player and action element */
9841     change->actual_trigger_element = EL_EMPTY;
9842     change->actual_trigger_player = EL_PLAYER_1;
9843     change->actual_trigger_side = CH_SIDE_NONE;
9844     change->actual_trigger_ce_value = 0;
9845     change->actual_trigger_ce_score = 0;
9846   }
9847
9848   /* do not change elements more than a specified maximum number of changes */
9849   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
9850     return FALSE;
9851
9852   ChangeCount[x][y]++;          /* count number of changes in the same frame */
9853
9854   if (change->explode)
9855   {
9856     Bang(x, y);
9857
9858     return TRUE;
9859   }
9860
9861   if (change->use_target_content)
9862   {
9863     boolean complete_replace = TRUE;
9864     boolean can_replace[3][3];
9865     int xx, yy;
9866
9867     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9868     {
9869       boolean is_empty;
9870       boolean is_walkable;
9871       boolean is_diggable;
9872       boolean is_collectible;
9873       boolean is_removable;
9874       boolean is_destructible;
9875       int ex = x + xx - 1;
9876       int ey = y + yy - 1;
9877       int content_element = change->target_content.e[xx][yy];
9878       int e;
9879
9880       can_replace[xx][yy] = TRUE;
9881
9882       if (ex == x && ey == y)   /* do not check changing element itself */
9883         continue;
9884
9885       if (content_element == EL_EMPTY_SPACE)
9886       {
9887         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
9888
9889         continue;
9890       }
9891
9892       if (!IN_LEV_FIELD(ex, ey))
9893       {
9894         can_replace[xx][yy] = FALSE;
9895         complete_replace = FALSE;
9896
9897         continue;
9898       }
9899
9900       e = Feld[ex][ey];
9901
9902       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9903         e = MovingOrBlocked2Element(ex, ey);
9904
9905       is_empty = (IS_FREE(ex, ey) ||
9906                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
9907
9908       is_walkable     = (is_empty || IS_WALKABLE(e));
9909       is_diggable     = (is_empty || IS_DIGGABLE(e));
9910       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
9911       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
9912       is_removable    = (is_diggable || is_collectible);
9913
9914       can_replace[xx][yy] =
9915         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
9916           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
9917           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
9918           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
9919           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
9920           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
9921          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
9922
9923       if (!can_replace[xx][yy])
9924         complete_replace = FALSE;
9925     }
9926
9927     if (!change->only_if_complete || complete_replace)
9928     {
9929       boolean something_has_changed = FALSE;
9930
9931       if (change->only_if_complete && change->use_random_replace &&
9932           RND(100) < change->random_percentage)
9933         return FALSE;
9934
9935       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9936       {
9937         int ex = x + xx - 1;
9938         int ey = y + yy - 1;
9939         int content_element;
9940
9941         if (can_replace[xx][yy] && (!change->use_random_replace ||
9942                                     RND(100) < change->random_percentage))
9943         {
9944           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9945             RemoveMovingField(ex, ey);
9946
9947           ChangeEvent[ex][ey] = ChangeEvent[x][y];
9948
9949           content_element = change->target_content.e[xx][yy];
9950           target_element = GET_TARGET_ELEMENT(element, content_element, change,
9951                                               ce_value, ce_score);
9952
9953           CreateElementFromChange(ex, ey, target_element);
9954
9955           something_has_changed = TRUE;
9956
9957           /* for symmetry reasons, freeze newly created border elements */
9958           if (ex != x || ey != y)
9959             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
9960         }
9961       }
9962
9963       if (something_has_changed)
9964       {
9965         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9966         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9967       }
9968     }
9969   }
9970   else
9971   {
9972     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9973                                         ce_value, ce_score);
9974
9975     if (element == EL_DIAGONAL_GROWING ||
9976         element == EL_DIAGONAL_SHRINKING)
9977     {
9978       target_element = Store[x][y];
9979
9980       Store[x][y] = EL_EMPTY;
9981     }
9982
9983     CreateElementFromChange(x, y, target_element);
9984
9985     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9986     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9987   }
9988
9989   /* this uses direct change before indirect change */
9990   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9991
9992   return TRUE;
9993 }
9994
9995 #if USE_NEW_DELAYED_ACTION
9996
9997 static void HandleElementChange(int x, int y, int page)
9998 {
9999   int element = MovingOrBlocked2Element(x, y);
10000   struct ElementInfo *ei = &element_info[element];
10001   struct ElementChangeInfo *change = &ei->change_page[page];
10002
10003 #ifdef DEBUG
10004   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10005       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10006   {
10007     printf("\n\n");
10008     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10009            x, y, element, element_info[element].token_name);
10010     printf("HandleElementChange(): This should never happen!\n");
10011     printf("\n\n");
10012   }
10013 #endif
10014
10015   /* this can happen with classic bombs on walkable, changing elements */
10016   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10017   {
10018 #if 0
10019     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10020       ChangeDelay[x][y] = 0;
10021 #endif
10022
10023     return;
10024   }
10025
10026   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10027   {
10028     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10029
10030     if (change->can_change)
10031     {
10032 #if 1
10033       /* !!! not clear why graphic animation should be reset at all here !!! */
10034       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10035 #if USE_GFX_RESET_WHEN_NOT_MOVING
10036       /* when a custom element is about to change (for example by change delay),
10037          do not reset graphic animation when the custom element is moving */
10038       if (!IS_MOVING(x, y))
10039 #endif
10040       {
10041         ResetGfxAnimation(x, y);
10042         ResetRandomAnimationValue(x, y);
10043       }
10044 #endif
10045
10046       if (change->pre_change_function)
10047         change->pre_change_function(x, y);
10048     }
10049   }
10050
10051   ChangeDelay[x][y]--;
10052
10053   if (ChangeDelay[x][y] != 0)           /* continue element change */
10054   {
10055     if (change->can_change)
10056     {
10057       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10058
10059       if (IS_ANIMATED(graphic))
10060         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10061
10062       if (change->change_function)
10063         change->change_function(x, y);
10064     }
10065   }
10066   else                                  /* finish element change */
10067   {
10068     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10069     {
10070       page = ChangePage[x][y];
10071       ChangePage[x][y] = -1;
10072
10073       change = &ei->change_page[page];
10074     }
10075
10076     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10077     {
10078       ChangeDelay[x][y] = 1;            /* try change after next move step */
10079       ChangePage[x][y] = page;          /* remember page to use for change */
10080
10081       return;
10082     }
10083
10084     if (change->can_change)
10085     {
10086       if (ChangeElement(x, y, element, page))
10087       {
10088         if (change->post_change_function)
10089           change->post_change_function(x, y);
10090       }
10091     }
10092
10093     if (change->has_action)
10094       ExecuteCustomElementAction(x, y, element, page);
10095   }
10096 }
10097
10098 #else
10099
10100 static void HandleElementChange(int x, int y, int page)
10101 {
10102   int element = MovingOrBlocked2Element(x, y);
10103   struct ElementInfo *ei = &element_info[element];
10104   struct ElementChangeInfo *change = &ei->change_page[page];
10105
10106 #ifdef DEBUG
10107   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10108   {
10109     printf("\n\n");
10110     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10111            x, y, element, element_info[element].token_name);
10112     printf("HandleElementChange(): This should never happen!\n");
10113     printf("\n\n");
10114   }
10115 #endif
10116
10117   /* this can happen with classic bombs on walkable, changing elements */
10118   if (!CAN_CHANGE(element))
10119   {
10120 #if 0
10121     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10122       ChangeDelay[x][y] = 0;
10123 #endif
10124
10125     return;
10126   }
10127
10128   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10129   {
10130     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10131
10132     ResetGfxAnimation(x, y);
10133     ResetRandomAnimationValue(x, y);
10134
10135     if (change->pre_change_function)
10136       change->pre_change_function(x, y);
10137   }
10138
10139   ChangeDelay[x][y]--;
10140
10141   if (ChangeDelay[x][y] != 0)           /* continue element change */
10142   {
10143     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10144
10145     if (IS_ANIMATED(graphic))
10146       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10147
10148     if (change->change_function)
10149       change->change_function(x, y);
10150   }
10151   else                                  /* finish element change */
10152   {
10153     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10154     {
10155       page = ChangePage[x][y];
10156       ChangePage[x][y] = -1;
10157
10158       change = &ei->change_page[page];
10159     }
10160
10161     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10162     {
10163       ChangeDelay[x][y] = 1;            /* try change after next move step */
10164       ChangePage[x][y] = page;          /* remember page to use for change */
10165
10166       return;
10167     }
10168
10169     if (ChangeElement(x, y, element, page))
10170     {
10171       if (change->post_change_function)
10172         change->post_change_function(x, y);
10173     }
10174   }
10175 }
10176
10177 #endif
10178
10179 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10180                                               int trigger_element,
10181                                               int trigger_event,
10182                                               int trigger_player,
10183                                               int trigger_side,
10184                                               int trigger_page)
10185 {
10186   boolean change_done_any = FALSE;
10187   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10188   int i;
10189
10190   if (!(trigger_events[trigger_element][trigger_event]))
10191     return FALSE;
10192
10193 #if 0
10194   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10195          trigger_event, recursion_loop_depth, recursion_loop_detected,
10196          recursion_loop_element, EL_NAME(recursion_loop_element));
10197 #endif
10198
10199   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10200
10201   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10202   {
10203     int element = EL_CUSTOM_START + i;
10204     boolean change_done = FALSE;
10205     int p;
10206
10207     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10208         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10209       continue;
10210
10211     for (p = 0; p < element_info[element].num_change_pages; p++)
10212     {
10213       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10214
10215       if (change->can_change_or_has_action &&
10216           change->has_event[trigger_event] &&
10217           change->trigger_side & trigger_side &&
10218           change->trigger_player & trigger_player &&
10219           change->trigger_page & trigger_page_bits &&
10220           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10221       {
10222         change->actual_trigger_element = trigger_element;
10223         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10224         change->actual_trigger_side = trigger_side;
10225         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10226         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10227
10228         if ((change->can_change && !change_done) || change->has_action)
10229         {
10230           int x, y;
10231
10232           SCAN_PLAYFIELD(x, y)
10233           {
10234             if (Feld[x][y] == element)
10235             {
10236               if (change->can_change && !change_done)
10237               {
10238                 ChangeDelay[x][y] = 1;
10239                 ChangeEvent[x][y] = trigger_event;
10240
10241                 HandleElementChange(x, y, p);
10242               }
10243 #if USE_NEW_DELAYED_ACTION
10244               else if (change->has_action)
10245               {
10246                 ExecuteCustomElementAction(x, y, element, p);
10247                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10248               }
10249 #else
10250               if (change->has_action)
10251               {
10252                 ExecuteCustomElementAction(x, y, element, p);
10253                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10254               }
10255 #endif
10256             }
10257           }
10258
10259           if (change->can_change)
10260           {
10261             change_done = TRUE;
10262             change_done_any = TRUE;
10263           }
10264         }
10265       }
10266     }
10267   }
10268
10269   RECURSION_LOOP_DETECTION_END();
10270
10271   return change_done_any;
10272 }
10273
10274 static boolean CheckElementChangeExt(int x, int y,
10275                                      int element,
10276                                      int trigger_element,
10277                                      int trigger_event,
10278                                      int trigger_player,
10279                                      int trigger_side)
10280 {
10281   boolean change_done = FALSE;
10282   int p;
10283
10284   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10285       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10286     return FALSE;
10287
10288   if (Feld[x][y] == EL_BLOCKED)
10289   {
10290     Blocked2Moving(x, y, &x, &y);
10291     element = Feld[x][y];
10292   }
10293
10294 #if 0
10295   /* check if element has already changed */
10296   if (Feld[x][y] != element)
10297     return FALSE;
10298 #else
10299   /* check if element has already changed or is about to change after moving */
10300   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10301        Feld[x][y] != element) ||
10302
10303       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10304        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10305         ChangePage[x][y] != -1)))
10306     return FALSE;
10307 #endif
10308
10309 #if 0
10310   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10311          trigger_event, recursion_loop_depth, recursion_loop_detected,
10312          recursion_loop_element, EL_NAME(recursion_loop_element));
10313 #endif
10314
10315   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10316
10317   for (p = 0; p < element_info[element].num_change_pages; p++)
10318   {
10319     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10320
10321     /* check trigger element for all events where the element that is checked
10322        for changing interacts with a directly adjacent element -- this is
10323        different to element changes that affect other elements to change on the
10324        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10325     boolean check_trigger_element =
10326       (trigger_event == CE_TOUCHING_X ||
10327        trigger_event == CE_HITTING_X ||
10328        trigger_event == CE_HIT_BY_X ||
10329 #if 1
10330        /* this one was forgotten until 3.2.3 */
10331        trigger_event == CE_DIGGING_X);
10332 #endif
10333
10334     if (change->can_change_or_has_action &&
10335         change->has_event[trigger_event] &&
10336         change->trigger_side & trigger_side &&
10337         change->trigger_player & trigger_player &&
10338         (!check_trigger_element ||
10339          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10340     {
10341       change->actual_trigger_element = trigger_element;
10342       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10343       change->actual_trigger_side = trigger_side;
10344       change->actual_trigger_ce_value = CustomValue[x][y];
10345       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10346
10347       /* special case: trigger element not at (x,y) position for some events */
10348       if (check_trigger_element)
10349       {
10350         static struct
10351         {
10352           int dx, dy;
10353         } move_xy[] =
10354           {
10355             {  0,  0 },
10356             { -1,  0 },
10357             { +1,  0 },
10358             {  0,  0 },
10359             {  0, -1 },
10360             {  0,  0 }, { 0, 0 }, { 0, 0 },
10361             {  0, +1 }
10362           };
10363
10364         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10365         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10366
10367         change->actual_trigger_ce_value = CustomValue[xx][yy];
10368         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10369       }
10370
10371       if (change->can_change && !change_done)
10372       {
10373         ChangeDelay[x][y] = 1;
10374         ChangeEvent[x][y] = trigger_event;
10375
10376         HandleElementChange(x, y, p);
10377
10378         change_done = TRUE;
10379       }
10380 #if USE_NEW_DELAYED_ACTION
10381       else if (change->has_action)
10382       {
10383         ExecuteCustomElementAction(x, y, element, p);
10384         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10385       }
10386 #else
10387       if (change->has_action)
10388       {
10389         ExecuteCustomElementAction(x, y, element, p);
10390         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10391       }
10392 #endif
10393     }
10394   }
10395
10396   RECURSION_LOOP_DETECTION_END();
10397
10398   return change_done;
10399 }
10400
10401 static void PlayPlayerSound(struct PlayerInfo *player)
10402 {
10403   int jx = player->jx, jy = player->jy;
10404   int sound_element = player->artwork_element;
10405   int last_action = player->last_action_waiting;
10406   int action = player->action_waiting;
10407
10408   if (player->is_waiting)
10409   {
10410     if (action != last_action)
10411       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10412     else
10413       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10414   }
10415   else
10416   {
10417     if (action != last_action)
10418       StopSound(element_info[sound_element].sound[last_action]);
10419
10420     if (last_action == ACTION_SLEEPING)
10421       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10422   }
10423 }
10424
10425 static void PlayAllPlayersSound()
10426 {
10427   int i;
10428
10429   for (i = 0; i < MAX_PLAYERS; i++)
10430     if (stored_player[i].active)
10431       PlayPlayerSound(&stored_player[i]);
10432 }
10433
10434 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10435 {
10436   boolean last_waiting = player->is_waiting;
10437   int move_dir = player->MovDir;
10438
10439   player->dir_waiting = move_dir;
10440   player->last_action_waiting = player->action_waiting;
10441
10442   if (is_waiting)
10443   {
10444     if (!last_waiting)          /* not waiting -> waiting */
10445     {
10446       player->is_waiting = TRUE;
10447
10448       player->frame_counter_bored =
10449         FrameCounter +
10450         game.player_boring_delay_fixed +
10451         GetSimpleRandom(game.player_boring_delay_random);
10452       player->frame_counter_sleeping =
10453         FrameCounter +
10454         game.player_sleeping_delay_fixed +
10455         GetSimpleRandom(game.player_sleeping_delay_random);
10456
10457       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10458     }
10459
10460     if (game.player_sleeping_delay_fixed +
10461         game.player_sleeping_delay_random > 0 &&
10462         player->anim_delay_counter == 0 &&
10463         player->post_delay_counter == 0 &&
10464         FrameCounter >= player->frame_counter_sleeping)
10465       player->is_sleeping = TRUE;
10466     else if (game.player_boring_delay_fixed +
10467              game.player_boring_delay_random > 0 &&
10468              FrameCounter >= player->frame_counter_bored)
10469       player->is_bored = TRUE;
10470
10471     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10472                               player->is_bored ? ACTION_BORING :
10473                               ACTION_WAITING);
10474
10475     if (player->is_sleeping && player->use_murphy)
10476     {
10477       /* special case for sleeping Murphy when leaning against non-free tile */
10478
10479       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10480           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10481            !IS_MOVING(player->jx - 1, player->jy)))
10482         move_dir = MV_LEFT;
10483       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10484                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10485                 !IS_MOVING(player->jx + 1, player->jy)))
10486         move_dir = MV_RIGHT;
10487       else
10488         player->is_sleeping = FALSE;
10489
10490       player->dir_waiting = move_dir;
10491     }
10492
10493     if (player->is_sleeping)
10494     {
10495       if (player->num_special_action_sleeping > 0)
10496       {
10497         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10498         {
10499           int last_special_action = player->special_action_sleeping;
10500           int num_special_action = player->num_special_action_sleeping;
10501           int special_action =
10502             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10503              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10504              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10505              last_special_action + 1 : ACTION_SLEEPING);
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_sleeping = special_action;
10517         }
10518
10519         if (player->anim_delay_counter > 0)
10520         {
10521           player->action_waiting = player->special_action_sleeping;
10522           player->anim_delay_counter--;
10523         }
10524         else if (player->post_delay_counter > 0)
10525         {
10526           player->post_delay_counter--;
10527         }
10528       }
10529     }
10530     else if (player->is_bored)
10531     {
10532       if (player->num_special_action_bored > 0)
10533       {
10534         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10535         {
10536           int special_action =
10537             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10538           int special_graphic =
10539             el_act_dir2img(player->artwork_element, special_action, move_dir);
10540
10541           player->anim_delay_counter =
10542             graphic_info[special_graphic].anim_delay_fixed +
10543             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10544           player->post_delay_counter =
10545             graphic_info[special_graphic].post_delay_fixed +
10546             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10547
10548           player->special_action_bored = special_action;
10549         }
10550
10551         if (player->anim_delay_counter > 0)
10552         {
10553           player->action_waiting = player->special_action_bored;
10554           player->anim_delay_counter--;
10555         }
10556         else if (player->post_delay_counter > 0)
10557         {
10558           player->post_delay_counter--;
10559         }
10560       }
10561     }
10562   }
10563   else if (last_waiting)        /* waiting -> not waiting */
10564   {
10565     player->is_waiting = FALSE;
10566     player->is_bored = FALSE;
10567     player->is_sleeping = FALSE;
10568
10569     player->frame_counter_bored = -1;
10570     player->frame_counter_sleeping = -1;
10571
10572     player->anim_delay_counter = 0;
10573     player->post_delay_counter = 0;
10574
10575     player->dir_waiting = player->MovDir;
10576     player->action_waiting = ACTION_DEFAULT;
10577
10578     player->special_action_bored = ACTION_DEFAULT;
10579     player->special_action_sleeping = ACTION_DEFAULT;
10580   }
10581 }
10582
10583 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10584 {
10585   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10586   int left      = player_action & JOY_LEFT;
10587   int right     = player_action & JOY_RIGHT;
10588   int up        = player_action & JOY_UP;
10589   int down      = player_action & JOY_DOWN;
10590   int button1   = player_action & JOY_BUTTON_1;
10591   int button2   = player_action & JOY_BUTTON_2;
10592   int dx        = (left ? -1 : right ? 1 : 0);
10593   int dy        = (up   ? -1 : down  ? 1 : 0);
10594
10595   if (!player->active || tape.pausing)
10596     return 0;
10597
10598   if (player_action)
10599   {
10600     if (button1)
10601       snapped = SnapField(player, dx, dy);
10602     else
10603     {
10604       if (button2)
10605         dropped = DropElement(player);
10606
10607       moved = MovePlayer(player, dx, dy);
10608     }
10609
10610     if (tape.single_step && tape.recording && !tape.pausing)
10611     {
10612       if (button1 || (dropped && !moved))
10613       {
10614         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10615         SnapField(player, 0, 0);                /* stop snapping */
10616       }
10617     }
10618
10619     SetPlayerWaiting(player, FALSE);
10620
10621     return player_action;
10622   }
10623   else
10624   {
10625     /* no actions for this player (no input at player's configured device) */
10626
10627     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10628     SnapField(player, 0, 0);
10629     CheckGravityMovementWhenNotMoving(player);
10630
10631     if (player->MovPos == 0)
10632       SetPlayerWaiting(player, TRUE);
10633
10634     if (player->MovPos == 0)    /* needed for tape.playing */
10635       player->is_moving = FALSE;
10636
10637     player->is_dropping = FALSE;
10638     player->is_dropping_pressed = FALSE;
10639     player->drop_pressed_delay = 0;
10640
10641     return 0;
10642   }
10643 }
10644
10645 static void CheckLevelTime()
10646 {
10647   int i;
10648
10649   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10650   {
10651     if (level.native_em_level->lev->home == 0)  /* all players at home */
10652     {
10653       PlayerWins(local_player);
10654
10655       AllPlayersGone = TRUE;
10656
10657       level.native_em_level->lev->home = -1;
10658     }
10659
10660     if (level.native_em_level->ply[0]->alive == 0 &&
10661         level.native_em_level->ply[1]->alive == 0 &&
10662         level.native_em_level->ply[2]->alive == 0 &&
10663         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10664       AllPlayersGone = TRUE;
10665   }
10666
10667   if (TimeFrames >= FRAMES_PER_SECOND)
10668   {
10669     TimeFrames = 0;
10670     TapeTime++;
10671
10672     for (i = 0; i < MAX_PLAYERS; i++)
10673     {
10674       struct PlayerInfo *player = &stored_player[i];
10675
10676       if (SHIELD_ON(player))
10677       {
10678         player->shield_normal_time_left--;
10679
10680         if (player->shield_deadly_time_left > 0)
10681           player->shield_deadly_time_left--;
10682       }
10683     }
10684
10685     if (!local_player->LevelSolved && !level.use_step_counter)
10686     {
10687       TimePlayed++;
10688
10689       if (TimeLeft > 0)
10690       {
10691         TimeLeft--;
10692
10693         if (TimeLeft <= 10 && setup.time_limit)
10694           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10695
10696 #if 1
10697         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
10698
10699         DisplayGameControlValues();
10700 #else
10701         DrawGameValue_Time(TimeLeft);
10702 #endif
10703
10704         if (!TimeLeft && setup.time_limit)
10705         {
10706           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10707             level.native_em_level->lev->killed_out_of_time = TRUE;
10708           else
10709             for (i = 0; i < MAX_PLAYERS; i++)
10710               KillPlayer(&stored_player[i]);
10711         }
10712       }
10713 #if 1
10714       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10715       {
10716         game_control_value[GAME_CONTROL_TIME] = TimePlayed;
10717
10718         DisplayGameControlValues();
10719       }
10720 #else
10721       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10722         DrawGameValue_Time(TimePlayed);
10723 #endif
10724
10725       level.native_em_level->lev->time =
10726         (level.time == 0 ? TimePlayed : TimeLeft);
10727     }
10728
10729     if (tape.recording || tape.playing)
10730       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10731   }
10732
10733   DrawGameDoorValues();
10734 }
10735
10736 void AdvanceFrameAndPlayerCounters(int player_nr)
10737 {
10738   int i;
10739
10740   /* advance frame counters (global frame counter and time frame counter) */
10741   FrameCounter++;
10742   TimeFrames++;
10743
10744   /* advance player counters (counters for move delay, move animation etc.) */
10745   for (i = 0; i < MAX_PLAYERS; i++)
10746   {
10747     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10748     int move_delay_value = stored_player[i].move_delay_value;
10749     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10750
10751     if (!advance_player_counters)       /* not all players may be affected */
10752       continue;
10753
10754 #if USE_NEW_PLAYER_ANIM
10755     if (move_frames == 0)       /* less than one move per game frame */
10756     {
10757       int stepsize = TILEX / move_delay_value;
10758       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10759       int count = (stored_player[i].is_moving ?
10760                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10761
10762       if (count % delay == 0)
10763         move_frames = 1;
10764     }
10765 #endif
10766
10767     stored_player[i].Frame += move_frames;
10768
10769     if (stored_player[i].MovPos != 0)
10770       stored_player[i].StepFrame += move_frames;
10771
10772     if (stored_player[i].move_delay > 0)
10773       stored_player[i].move_delay--;
10774
10775     /* due to bugs in previous versions, counter must count up, not down */
10776     if (stored_player[i].push_delay != -1)
10777       stored_player[i].push_delay++;
10778
10779     if (stored_player[i].drop_delay > 0)
10780       stored_player[i].drop_delay--;
10781
10782     if (stored_player[i].is_dropping_pressed)
10783       stored_player[i].drop_pressed_delay++;
10784   }
10785 }
10786
10787 void StartGameActions(boolean init_network_game, boolean record_tape,
10788                       long random_seed)
10789 {
10790   unsigned long new_random_seed = InitRND(random_seed);
10791
10792   if (record_tape)
10793     TapeStartRecording(new_random_seed);
10794
10795 #if defined(NETWORK_AVALIABLE)
10796   if (init_network_game)
10797   {
10798     SendToServer_StartPlaying();
10799
10800     return;
10801   }
10802 #endif
10803
10804   InitGame();
10805 }
10806
10807 void GameActions()
10808 {
10809   static unsigned long game_frame_delay = 0;
10810   unsigned long game_frame_delay_value;
10811   byte *recorded_player_action;
10812   byte summarized_player_action = 0;
10813   byte tape_action[MAX_PLAYERS];
10814   int i;
10815
10816   /* detect endless loops, caused by custom element programming */
10817   if (recursion_loop_detected && recursion_loop_depth == 0)
10818   {
10819     char *message = getStringCat3("Internal Error ! Element ",
10820                                   EL_NAME(recursion_loop_element),
10821                                   " caused endless loop ! Quit the game ?");
10822
10823     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10824           EL_NAME(recursion_loop_element));
10825
10826     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10827
10828     recursion_loop_detected = FALSE;    /* if game should be continued */
10829
10830     free(message);
10831
10832     return;
10833   }
10834
10835   if (game.restart_level)
10836     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
10837
10838   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10839   {
10840     if (level.native_em_level->lev->home == 0)  /* all players at home */
10841     {
10842       PlayerWins(local_player);
10843
10844       AllPlayersGone = TRUE;
10845
10846       level.native_em_level->lev->home = -1;
10847     }
10848
10849     if (level.native_em_level->ply[0]->alive == 0 &&
10850         level.native_em_level->ply[1]->alive == 0 &&
10851         level.native_em_level->ply[2]->alive == 0 &&
10852         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10853       AllPlayersGone = TRUE;
10854   }
10855
10856   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10857     GameWon();
10858
10859   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10860     TapeStop();
10861
10862   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
10863     return;
10864
10865   game_frame_delay_value =
10866     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10867
10868   if (tape.playing && tape.warp_forward && !tape.pausing)
10869     game_frame_delay_value = 0;
10870
10871   /* ---------- main game synchronization point ---------- */
10872
10873   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10874
10875   if (network_playing && !network_player_action_received)
10876   {
10877     /* try to get network player actions in time */
10878
10879 #if defined(NETWORK_AVALIABLE)
10880     /* last chance to get network player actions without main loop delay */
10881     HandleNetworking();
10882 #endif
10883
10884     /* game was quit by network peer */
10885     if (game_status != GAME_MODE_PLAYING)
10886       return;
10887
10888     if (!network_player_action_received)
10889       return;           /* failed to get network player actions in time */
10890
10891     /* do not yet reset "network_player_action_received" (for tape.pausing) */
10892   }
10893
10894   if (tape.pausing)
10895     return;
10896
10897   /* at this point we know that we really continue executing the game */
10898
10899   network_player_action_received = FALSE;
10900
10901   /* when playing tape, read previously recorded player input from tape data */
10902   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
10903
10904 #if 1
10905   /* TapePlayAction() may return NULL when toggling to "pause before death" */
10906   if (tape.pausing)
10907     return;
10908 #endif
10909
10910   if (tape.set_centered_player)
10911   {
10912     game.centered_player_nr_next = tape.centered_player_nr_next;
10913     game.set_centered_player = TRUE;
10914   }
10915
10916   for (i = 0; i < MAX_PLAYERS; i++)
10917   {
10918     summarized_player_action |= stored_player[i].action;
10919
10920     if (!network_playing)
10921       stored_player[i].effective_action = stored_player[i].action;
10922   }
10923
10924 #if defined(NETWORK_AVALIABLE)
10925   if (network_playing)
10926     SendToServer_MovePlayer(summarized_player_action);
10927 #endif
10928
10929   if (!options.network && !setup.team_mode)
10930     local_player->effective_action = summarized_player_action;
10931
10932   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
10933   {
10934     for (i = 0; i < MAX_PLAYERS; i++)
10935       stored_player[i].effective_action =
10936         (i == game.centered_player_nr ? summarized_player_action : 0);
10937   }
10938
10939   if (recorded_player_action != NULL)
10940     for (i = 0; i < MAX_PLAYERS; i++)
10941       stored_player[i].effective_action = recorded_player_action[i];
10942
10943   for (i = 0; i < MAX_PLAYERS; i++)
10944   {
10945     tape_action[i] = stored_player[i].effective_action;
10946
10947     /* (this can only happen in the R'n'D game engine) */
10948     if (tape.recording && tape_action[i] && !tape.player_participates[i])
10949       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
10950   }
10951
10952   /* only record actions from input devices, but not programmed actions */
10953   if (tape.recording)
10954     TapeRecordAction(tape_action);
10955
10956   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10957   {
10958     GameActions_EM_Main();
10959   }
10960   else
10961   {
10962     GameActions_RND();
10963   }
10964 }
10965
10966 void GameActions_EM_Main()
10967 {
10968   byte effective_action[MAX_PLAYERS];
10969   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
10970   int i;
10971
10972   for (i = 0; i < MAX_PLAYERS; i++)
10973     effective_action[i] = stored_player[i].effective_action;
10974
10975   GameActions_EM(effective_action, warp_mode);
10976
10977   CheckLevelTime();
10978
10979   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
10980 }
10981
10982 void GameActions_RND()
10983 {
10984   int magic_wall_x = 0, magic_wall_y = 0;
10985   int i, x, y, element, graphic;
10986
10987   InitPlayfieldScanModeVars();
10988
10989 #if USE_ONE_MORE_CHANGE_PER_FRAME
10990   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10991   {
10992     SCAN_PLAYFIELD(x, y)
10993     {
10994       ChangeCount[x][y] = 0;
10995       ChangeEvent[x][y] = -1;
10996     }
10997   }
10998 #endif
10999
11000   if (game.set_centered_player)
11001   {
11002     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11003
11004     /* switching to "all players" only possible if all players fit to screen */
11005     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11006     {
11007       game.centered_player_nr_next = game.centered_player_nr;
11008       game.set_centered_player = FALSE;
11009     }
11010
11011     /* do not switch focus to non-existing (or non-active) player */
11012     if (game.centered_player_nr_next >= 0 &&
11013         !stored_player[game.centered_player_nr_next].active)
11014     {
11015       game.centered_player_nr_next = game.centered_player_nr;
11016       game.set_centered_player = FALSE;
11017     }
11018   }
11019
11020   if (game.set_centered_player &&
11021       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11022   {
11023     int sx, sy;
11024
11025     if (game.centered_player_nr_next == -1)
11026     {
11027       setScreenCenteredToAllPlayers(&sx, &sy);
11028     }
11029     else
11030     {
11031       sx = stored_player[game.centered_player_nr_next].jx;
11032       sy = stored_player[game.centered_player_nr_next].jy;
11033     }
11034
11035     game.centered_player_nr = game.centered_player_nr_next;
11036     game.set_centered_player = FALSE;
11037
11038     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11039     DrawGameDoorValues();
11040   }
11041
11042   for (i = 0; i < MAX_PLAYERS; i++)
11043   {
11044     int actual_player_action = stored_player[i].effective_action;
11045
11046 #if 1
11047     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11048        - rnd_equinox_tetrachloride 048
11049        - rnd_equinox_tetrachloride_ii 096
11050        - rnd_emanuel_schmieg 002
11051        - doctor_sloan_ww 001, 020
11052     */
11053     if (stored_player[i].MovPos == 0)
11054       CheckGravityMovement(&stored_player[i]);
11055 #endif
11056
11057     /* overwrite programmed action with tape action */
11058     if (stored_player[i].programmed_action)
11059       actual_player_action = stored_player[i].programmed_action;
11060
11061     PlayerActions(&stored_player[i], actual_player_action);
11062
11063     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11064   }
11065
11066   ScrollScreen(NULL, SCROLL_GO_ON);
11067
11068   /* for backwards compatibility, the following code emulates a fixed bug that
11069      occured when pushing elements (causing elements that just made their last
11070      pushing step to already (if possible) make their first falling step in the
11071      same game frame, which is bad); this code is also needed to use the famous
11072      "spring push bug" which is used in older levels and might be wanted to be
11073      used also in newer levels, but in this case the buggy pushing code is only
11074      affecting the "spring" element and no other elements */
11075
11076   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11077   {
11078     for (i = 0; i < MAX_PLAYERS; i++)
11079     {
11080       struct PlayerInfo *player = &stored_player[i];
11081       int x = player->jx;
11082       int y = player->jy;
11083
11084       if (player->active && player->is_pushing && player->is_moving &&
11085           IS_MOVING(x, y) &&
11086           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11087            Feld[x][y] == EL_SPRING))
11088       {
11089         ContinueMoving(x, y);
11090
11091         /* continue moving after pushing (this is actually a bug) */
11092         if (!IS_MOVING(x, y))
11093           Stop[x][y] = FALSE;
11094       }
11095     }
11096   }
11097
11098 #if 0
11099   debug_print_timestamp(0, "start main loop profiling");
11100 #endif
11101
11102   SCAN_PLAYFIELD(x, y)
11103   {
11104     ChangeCount[x][y] = 0;
11105     ChangeEvent[x][y] = -1;
11106
11107     /* this must be handled before main playfield loop */
11108     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11109     {
11110       MovDelay[x][y]--;
11111       if (MovDelay[x][y] <= 0)
11112         RemoveField(x, y);
11113     }
11114
11115 #if USE_NEW_SNAP_DELAY
11116     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11117     {
11118       MovDelay[x][y]--;
11119       if (MovDelay[x][y] <= 0)
11120       {
11121         RemoveField(x, y);
11122         DrawLevelField(x, y);
11123
11124         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11125       }
11126     }
11127 #endif
11128
11129 #if DEBUG
11130     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11131     {
11132       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11133       printf("GameActions(): This should never happen!\n");
11134
11135       ChangePage[x][y] = -1;
11136     }
11137 #endif
11138
11139     Stop[x][y] = FALSE;
11140     if (WasJustMoving[x][y] > 0)
11141       WasJustMoving[x][y]--;
11142     if (WasJustFalling[x][y] > 0)
11143       WasJustFalling[x][y]--;
11144     if (CheckCollision[x][y] > 0)
11145       CheckCollision[x][y]--;
11146     if (CheckImpact[x][y] > 0)
11147       CheckImpact[x][y]--;
11148
11149     GfxFrame[x][y]++;
11150
11151     /* reset finished pushing action (not done in ContinueMoving() to allow
11152        continuous pushing animation for elements with zero push delay) */
11153     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11154     {
11155       ResetGfxAnimation(x, y);
11156       DrawLevelField(x, y);
11157     }
11158
11159 #if DEBUG
11160     if (IS_BLOCKED(x, y))
11161     {
11162       int oldx, oldy;
11163
11164       Blocked2Moving(x, y, &oldx, &oldy);
11165       if (!IS_MOVING(oldx, oldy))
11166       {
11167         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11168         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11169         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11170         printf("GameActions(): This should never happen!\n");
11171       }
11172     }
11173 #endif
11174   }
11175
11176 #if 0
11177   debug_print_timestamp(0, "- time for pre-main loop:");
11178 #endif
11179
11180 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
11181   SCAN_PLAYFIELD(x, y)
11182   {
11183     element = Feld[x][y];
11184     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11185
11186 #if 1
11187     {
11188 #if 1
11189       int element2 = element;
11190       int graphic2 = graphic;
11191 #else
11192       int element2 = Feld[x][y];
11193       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11194 #endif
11195       int last_gfx_frame = GfxFrame[x][y];
11196
11197       if (graphic_info[graphic2].anim_global_sync)
11198         GfxFrame[x][y] = FrameCounter;
11199       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11200         GfxFrame[x][y] = CustomValue[x][y];
11201       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11202         GfxFrame[x][y] = element_info[element2].collect_score;
11203       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11204         GfxFrame[x][y] = ChangeDelay[x][y];
11205
11206       if (redraw && GfxFrame[x][y] != last_gfx_frame)
11207         DrawLevelGraphicAnimation(x, y, graphic2);
11208     }
11209 #else
11210     ResetGfxFrame(x, y, TRUE);
11211 #endif
11212
11213 #if 1
11214     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11215         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11216       ResetRandomAnimationValue(x, y);
11217 #endif
11218
11219 #if 1
11220     SetRandomAnimationValue(x, y);
11221 #endif
11222
11223 #if 1
11224     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11225 #endif
11226   }
11227 #endif  // -------------------- !!! TEST ONLY !!! --------------------
11228
11229 #if 0
11230   debug_print_timestamp(0, "- time for TEST loop:     -->");
11231 #endif
11232
11233   SCAN_PLAYFIELD(x, y)
11234   {
11235     element = Feld[x][y];
11236     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11237
11238     ResetGfxFrame(x, y, TRUE);
11239
11240     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11241         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11242       ResetRandomAnimationValue(x, y);
11243
11244     SetRandomAnimationValue(x, y);
11245
11246     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11247
11248     if (IS_INACTIVE(element))
11249     {
11250       if (IS_ANIMATED(graphic))
11251         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11252
11253       continue;
11254     }
11255
11256     /* this may take place after moving, so 'element' may have changed */
11257     if (IS_CHANGING(x, y) &&
11258         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11259     {
11260       int page = element_info[element].event_page_nr[CE_DELAY];
11261
11262 #if 1
11263       HandleElementChange(x, y, page);
11264 #else
11265       if (CAN_CHANGE(element))
11266         HandleElementChange(x, y, page);
11267
11268       if (HAS_ACTION(element))
11269         ExecuteCustomElementAction(x, y, element, page);
11270 #endif
11271
11272       element = Feld[x][y];
11273       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11274     }
11275
11276 #if 0   // ---------------------------------------------------------------------
11277
11278     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11279     {
11280       StartMoving(x, y);
11281
11282       element = Feld[x][y];
11283       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11284
11285       if (IS_ANIMATED(graphic) &&
11286           !IS_MOVING(x, y) &&
11287           !Stop[x][y])
11288         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11289
11290       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11291         DrawTwinkleOnField(x, y);
11292     }
11293     else if (IS_MOVING(x, y))
11294       ContinueMoving(x, y);
11295     else
11296     {
11297       switch (element)
11298       {
11299         case EL_ACID:
11300         case EL_EXIT_OPEN:
11301         case EL_EM_EXIT_OPEN:
11302         case EL_SP_EXIT_OPEN:
11303         case EL_STEEL_EXIT_OPEN:
11304         case EL_EM_STEEL_EXIT_OPEN:
11305         case EL_SP_TERMINAL:
11306         case EL_SP_TERMINAL_ACTIVE:
11307         case EL_EXTRA_TIME:
11308         case EL_SHIELD_NORMAL:
11309         case EL_SHIELD_DEADLY:
11310           if (IS_ANIMATED(graphic))
11311             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11312           break;
11313
11314         case EL_DYNAMITE_ACTIVE:
11315         case EL_EM_DYNAMITE_ACTIVE:
11316         case EL_DYNABOMB_PLAYER_1_ACTIVE:
11317         case EL_DYNABOMB_PLAYER_2_ACTIVE:
11318         case EL_DYNABOMB_PLAYER_3_ACTIVE:
11319         case EL_DYNABOMB_PLAYER_4_ACTIVE:
11320         case EL_SP_DISK_RED_ACTIVE:
11321           CheckDynamite(x, y);
11322           break;
11323
11324         case EL_AMOEBA_GROWING:
11325           AmoebeWaechst(x, y);
11326           break;
11327
11328         case EL_AMOEBA_SHRINKING:
11329           AmoebaDisappearing(x, y);
11330           break;
11331
11332 #if !USE_NEW_AMOEBA_CODE
11333         case EL_AMOEBA_WET:
11334         case EL_AMOEBA_DRY:
11335         case EL_AMOEBA_FULL:
11336         case EL_BD_AMOEBA:
11337         case EL_EMC_DRIPPER:
11338           AmoebeAbleger(x, y);
11339           break;
11340 #endif
11341
11342         case EL_GAME_OF_LIFE:
11343         case EL_BIOMAZE:
11344           Life(x, y);
11345           break;
11346
11347         case EL_EXIT_CLOSED:
11348           CheckExit(x, y);
11349           break;
11350
11351         case EL_EM_EXIT_CLOSED:
11352           CheckExitEM(x, y);
11353           break;
11354
11355         case EL_STEEL_EXIT_CLOSED:
11356           CheckExitSteel(x, y);
11357           break;
11358
11359         case EL_EM_STEEL_EXIT_CLOSED:
11360           CheckExitSteelEM(x, y);
11361           break;
11362
11363         case EL_SP_EXIT_CLOSED:
11364           CheckExitSP(x, y);
11365           break;
11366
11367         case EL_EXPANDABLE_WALL_GROWING:
11368         case EL_EXPANDABLE_STEELWALL_GROWING:
11369           MauerWaechst(x, y);
11370           break;
11371
11372         case EL_EXPANDABLE_WALL:
11373         case EL_EXPANDABLE_WALL_HORIZONTAL:
11374         case EL_EXPANDABLE_WALL_VERTICAL:
11375         case EL_EXPANDABLE_WALL_ANY:
11376         case EL_BD_EXPANDABLE_WALL:
11377           MauerAbleger(x, y);
11378           break;
11379
11380         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
11381         case EL_EXPANDABLE_STEELWALL_VERTICAL:
11382         case EL_EXPANDABLE_STEELWALL_ANY:
11383           MauerAblegerStahl(x, y);
11384           break;
11385
11386         case EL_FLAMES:
11387           CheckForDragon(x, y);
11388           break;
11389
11390         case EL_EXPLOSION:
11391           break;
11392
11393         case EL_ELEMENT_SNAPPING:
11394         case EL_DIAGONAL_SHRINKING:
11395         case EL_DIAGONAL_GROWING:
11396         {
11397           graphic =
11398             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11399
11400           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11401           break;
11402         }
11403
11404         default:
11405           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11406             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11407           break;
11408       }
11409     }
11410
11411 #else   // ---------------------------------------------------------------------
11412
11413     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11414     {
11415       StartMoving(x, y);
11416
11417       element = Feld[x][y];
11418       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11419
11420       if (IS_ANIMATED(graphic) &&
11421           !IS_MOVING(x, y) &&
11422           !Stop[x][y])
11423         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11424
11425       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11426         DrawTwinkleOnField(x, y);
11427     }
11428     else if ((element == EL_ACID ||
11429               element == EL_EXIT_OPEN ||
11430               element == EL_EM_EXIT_OPEN ||
11431               element == EL_SP_EXIT_OPEN ||
11432               element == EL_STEEL_EXIT_OPEN ||
11433               element == EL_EM_STEEL_EXIT_OPEN ||
11434               element == EL_SP_TERMINAL ||
11435               element == EL_SP_TERMINAL_ACTIVE ||
11436               element == EL_EXTRA_TIME ||
11437               element == EL_SHIELD_NORMAL ||
11438               element == EL_SHIELD_DEADLY) &&
11439              IS_ANIMATED(graphic))
11440       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11441     else if (IS_MOVING(x, y))
11442       ContinueMoving(x, y);
11443     else if (IS_ACTIVE_BOMB(element))
11444       CheckDynamite(x, y);
11445     else if (element == EL_AMOEBA_GROWING)
11446       AmoebeWaechst(x, y);
11447     else if (element == EL_AMOEBA_SHRINKING)
11448       AmoebaDisappearing(x, y);
11449
11450 #if !USE_NEW_AMOEBA_CODE
11451     else if (IS_AMOEBALIVE(element))
11452       AmoebeAbleger(x, y);
11453 #endif
11454
11455     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11456       Life(x, y);
11457     else if (element == EL_EXIT_CLOSED)
11458       CheckExit(x, y);
11459     else if (element == EL_EM_EXIT_CLOSED)
11460       CheckExitEM(x, y);
11461     else if (element == EL_STEEL_EXIT_CLOSED)
11462       CheckExitSteel(x, y);
11463     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11464       CheckExitSteelEM(x, y);
11465     else if (element == EL_SP_EXIT_CLOSED)
11466       CheckExitSP(x, y);
11467     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11468              element == EL_EXPANDABLE_STEELWALL_GROWING)
11469       MauerWaechst(x, y);
11470     else if (element == EL_EXPANDABLE_WALL ||
11471              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11472              element == EL_EXPANDABLE_WALL_VERTICAL ||
11473              element == EL_EXPANDABLE_WALL_ANY ||
11474              element == EL_BD_EXPANDABLE_WALL)
11475       MauerAbleger(x, y);
11476     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11477              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11478              element == EL_EXPANDABLE_STEELWALL_ANY)
11479       MauerAblegerStahl(x, y);
11480     else if (element == EL_FLAMES)
11481       CheckForDragon(x, y);
11482     else if (element == EL_EXPLOSION)
11483       ; /* drawing of correct explosion animation is handled separately */
11484     else if (element == EL_ELEMENT_SNAPPING ||
11485              element == EL_DIAGONAL_SHRINKING ||
11486              element == EL_DIAGONAL_GROWING)
11487     {
11488       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11489
11490       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11491     }
11492     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11493       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11494
11495 #endif  // ---------------------------------------------------------------------
11496
11497     if (IS_BELT_ACTIVE(element))
11498       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11499
11500     if (game.magic_wall_active)
11501     {
11502       int jx = local_player->jx, jy = local_player->jy;
11503
11504       /* play the element sound at the position nearest to the player */
11505       if ((element == EL_MAGIC_WALL_FULL ||
11506            element == EL_MAGIC_WALL_ACTIVE ||
11507            element == EL_MAGIC_WALL_EMPTYING ||
11508            element == EL_BD_MAGIC_WALL_FULL ||
11509            element == EL_BD_MAGIC_WALL_ACTIVE ||
11510            element == EL_BD_MAGIC_WALL_EMPTYING ||
11511            element == EL_DC_MAGIC_WALL_FULL ||
11512            element == EL_DC_MAGIC_WALL_ACTIVE ||
11513            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11514           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11515       {
11516         magic_wall_x = x;
11517         magic_wall_y = y;
11518       }
11519     }
11520   }
11521
11522 #if 0
11523   debug_print_timestamp(0, "- time for MAIN loop:     -->");
11524 #endif
11525
11526 #if USE_NEW_AMOEBA_CODE
11527   /* new experimental amoeba growth stuff */
11528   if (!(FrameCounter % 8))
11529   {
11530     static unsigned long random = 1684108901;
11531
11532     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11533     {
11534       x = RND(lev_fieldx);
11535       y = RND(lev_fieldy);
11536       element = Feld[x][y];
11537
11538       if (!IS_PLAYER(x,y) &&
11539           (element == EL_EMPTY ||
11540            CAN_GROW_INTO(element) ||
11541            element == EL_QUICKSAND_EMPTY ||
11542            element == EL_QUICKSAND_FAST_EMPTY ||
11543            element == EL_ACID_SPLASH_LEFT ||
11544            element == EL_ACID_SPLASH_RIGHT))
11545       {
11546         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11547             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11548             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11549             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11550           Feld[x][y] = EL_AMOEBA_DROP;
11551       }
11552
11553       random = random * 129 + 1;
11554     }
11555   }
11556 #endif
11557
11558 #if 0
11559   if (game.explosions_delayed)
11560 #endif
11561   {
11562     game.explosions_delayed = FALSE;
11563
11564     SCAN_PLAYFIELD(x, y)
11565     {
11566       element = Feld[x][y];
11567
11568       if (ExplodeField[x][y])
11569         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11570       else if (element == EL_EXPLOSION)
11571         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11572
11573       ExplodeField[x][y] = EX_TYPE_NONE;
11574     }
11575
11576     game.explosions_delayed = TRUE;
11577   }
11578
11579   if (game.magic_wall_active)
11580   {
11581     if (!(game.magic_wall_time_left % 4))
11582     {
11583       int element = Feld[magic_wall_x][magic_wall_y];
11584
11585       if (element == EL_BD_MAGIC_WALL_FULL ||
11586           element == EL_BD_MAGIC_WALL_ACTIVE ||
11587           element == EL_BD_MAGIC_WALL_EMPTYING)
11588         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11589       else if (element == EL_DC_MAGIC_WALL_FULL ||
11590                element == EL_DC_MAGIC_WALL_ACTIVE ||
11591                element == EL_DC_MAGIC_WALL_EMPTYING)
11592         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11593       else
11594         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11595     }
11596
11597     if (game.magic_wall_time_left > 0)
11598     {
11599       game.magic_wall_time_left--;
11600       if (!game.magic_wall_time_left)
11601       {
11602         SCAN_PLAYFIELD(x, y)
11603         {
11604           element = Feld[x][y];
11605
11606           if (element == EL_MAGIC_WALL_ACTIVE ||
11607               element == EL_MAGIC_WALL_FULL)
11608           {
11609             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11610             DrawLevelField(x, y);
11611           }
11612           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11613                    element == EL_BD_MAGIC_WALL_FULL)
11614           {
11615             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11616             DrawLevelField(x, y);
11617           }
11618           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11619                    element == EL_DC_MAGIC_WALL_FULL)
11620           {
11621             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11622             DrawLevelField(x, y);
11623           }
11624         }
11625
11626         game.magic_wall_active = FALSE;
11627       }
11628     }
11629   }
11630
11631   if (game.light_time_left > 0)
11632   {
11633     game.light_time_left--;
11634
11635     if (game.light_time_left == 0)
11636       RedrawAllLightSwitchesAndInvisibleElements();
11637   }
11638
11639   if (game.timegate_time_left > 0)
11640   {
11641     game.timegate_time_left--;
11642
11643     if (game.timegate_time_left == 0)
11644       CloseAllOpenTimegates();
11645   }
11646
11647   if (game.lenses_time_left > 0)
11648   {
11649     game.lenses_time_left--;
11650
11651     if (game.lenses_time_left == 0)
11652       RedrawAllInvisibleElementsForLenses();
11653   }
11654
11655   if (game.magnify_time_left > 0)
11656   {
11657     game.magnify_time_left--;
11658
11659     if (game.magnify_time_left == 0)
11660       RedrawAllInvisibleElementsForMagnifier();
11661   }
11662
11663   for (i = 0; i < MAX_PLAYERS; i++)
11664   {
11665     struct PlayerInfo *player = &stored_player[i];
11666
11667     if (SHIELD_ON(player))
11668     {
11669       if (player->shield_deadly_time_left)
11670         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11671       else if (player->shield_normal_time_left)
11672         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11673     }
11674   }
11675
11676   CheckLevelTime();
11677
11678   DrawAllPlayers();
11679   PlayAllPlayersSound();
11680
11681   if (options.debug)                    /* calculate frames per second */
11682   {
11683     static unsigned long fps_counter = 0;
11684     static int fps_frames = 0;
11685     unsigned long fps_delay_ms = Counter() - fps_counter;
11686
11687     fps_frames++;
11688
11689     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11690     {
11691       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11692
11693       fps_frames = 0;
11694       fps_counter = Counter();
11695     }
11696
11697     redraw_mask |= REDRAW_FPS;
11698   }
11699
11700   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11701
11702   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11703   {
11704     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11705
11706     local_player->show_envelope = 0;
11707   }
11708
11709 #if 0
11710   debug_print_timestamp(0, "stop main loop profiling ");
11711   printf("----------------------------------------------------------\n");
11712 #endif
11713
11714   /* use random number generator in every frame to make it less predictable */
11715   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11716     RND(1);
11717 }
11718
11719 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11720 {
11721   int min_x = x, min_y = y, max_x = x, max_y = y;
11722   int i;
11723
11724   for (i = 0; i < MAX_PLAYERS; i++)
11725   {
11726     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11727
11728     if (!stored_player[i].active || &stored_player[i] == player)
11729       continue;
11730
11731     min_x = MIN(min_x, jx);
11732     min_y = MIN(min_y, jy);
11733     max_x = MAX(max_x, jx);
11734     max_y = MAX(max_y, jy);
11735   }
11736
11737   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11738 }
11739
11740 static boolean AllPlayersInVisibleScreen()
11741 {
11742   int i;
11743
11744   for (i = 0; i < MAX_PLAYERS; i++)
11745   {
11746     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11747
11748     if (!stored_player[i].active)
11749       continue;
11750
11751     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11752       return FALSE;
11753   }
11754
11755   return TRUE;
11756 }
11757
11758 void ScrollLevel(int dx, int dy)
11759 {
11760 #if 1
11761   static Bitmap *bitmap_db_field2 = NULL;
11762   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11763   int x, y;
11764 #else
11765   int i, x, y;
11766 #endif
11767
11768 #if 0
11769   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11770   /* only horizontal XOR vertical scroll direction allowed */
11771   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11772     return;
11773 #endif
11774
11775 #if 1
11776   if (bitmap_db_field2 == NULL)
11777     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11778
11779   /* needed when blitting directly to same bitmap -- should not be needed with
11780      recent SDL libraries, but apparently does not work in 1.2.11 directly */
11781   BlitBitmap(drawto_field, bitmap_db_field2,
11782              FX + TILEX * (dx == -1) - softscroll_offset,
11783              FY + TILEY * (dy == -1) - softscroll_offset,
11784              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11785              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11786              FX + TILEX * (dx == 1) - softscroll_offset,
11787              FY + TILEY * (dy == 1) - softscroll_offset);
11788   BlitBitmap(bitmap_db_field2, drawto_field,
11789              FX + TILEX * (dx == 1) - softscroll_offset,
11790              FY + TILEY * (dy == 1) - softscroll_offset,
11791              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11792              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11793              FX + TILEX * (dx == 1) - softscroll_offset,
11794              FY + TILEY * (dy == 1) - softscroll_offset);
11795
11796 #else
11797
11798 #if 1
11799   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11800   int xsize = (BX2 - BX1 + 1);
11801   int ysize = (BY2 - BY1 + 1);
11802   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11803   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11804   int step  = (start < end ? +1 : -1);
11805
11806   for (i = start; i != end; i += step)
11807   {
11808     BlitBitmap(drawto_field, drawto_field,
11809                FX + TILEX * (dx != 0 ? i + step : 0),
11810                FY + TILEY * (dy != 0 ? i + step : 0),
11811                TILEX * (dx != 0 ? 1 : xsize),
11812                TILEY * (dy != 0 ? 1 : ysize),
11813                FX + TILEX * (dx != 0 ? i : 0),
11814                FY + TILEY * (dy != 0 ? i : 0));
11815   }
11816
11817 #else
11818
11819   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11820
11821   BlitBitmap(drawto_field, drawto_field,
11822              FX + TILEX * (dx == -1) - softscroll_offset,
11823              FY + TILEY * (dy == -1) - softscroll_offset,
11824              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11825              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11826              FX + TILEX * (dx == 1) - softscroll_offset,
11827              FY + TILEY * (dy == 1) - softscroll_offset);
11828 #endif
11829 #endif
11830
11831   if (dx != 0)
11832   {
11833     x = (dx == 1 ? BX1 : BX2);
11834     for (y = BY1; y <= BY2; y++)
11835       DrawScreenField(x, y);
11836   }
11837
11838   if (dy != 0)
11839   {
11840     y = (dy == 1 ? BY1 : BY2);
11841     for (x = BX1; x <= BX2; x++)
11842       DrawScreenField(x, y);
11843   }
11844
11845   redraw_mask |= REDRAW_FIELD;
11846 }
11847
11848 static boolean canFallDown(struct PlayerInfo *player)
11849 {
11850   int jx = player->jx, jy = player->jy;
11851
11852   return (IN_LEV_FIELD(jx, jy + 1) &&
11853           (IS_FREE(jx, jy + 1) ||
11854            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11855           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11856           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11857 }
11858
11859 static boolean canPassField(int x, int y, int move_dir)
11860 {
11861   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11862   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11863   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11864   int nextx = x + dx;
11865   int nexty = y + dy;
11866   int element = Feld[x][y];
11867
11868   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11869           !CAN_MOVE(element) &&
11870           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11871           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11872           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11873 }
11874
11875 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11876 {
11877   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11878   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11879   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11880   int newx = x + dx;
11881   int newy = y + dy;
11882
11883   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11884           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11885           (IS_DIGGABLE(Feld[newx][newy]) ||
11886            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11887            canPassField(newx, newy, move_dir)));
11888 }
11889
11890 static void CheckGravityMovement(struct PlayerInfo *player)
11891 {
11892 #if USE_PLAYER_GRAVITY
11893   if (player->gravity && !player->programmed_action)
11894 #else
11895   if (game.gravity && !player->programmed_action)
11896 #endif
11897   {
11898     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11899     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11900     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11901     int jx = player->jx, jy = player->jy;
11902     boolean player_is_moving_to_valid_field =
11903       (!player_is_snapping &&
11904        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11905         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11906     boolean player_can_fall_down = canFallDown(player);
11907
11908     if (player_can_fall_down &&
11909         !player_is_moving_to_valid_field)
11910       player->programmed_action = MV_DOWN;
11911   }
11912 }
11913
11914 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11915 {
11916   return CheckGravityMovement(player);
11917
11918 #if USE_PLAYER_GRAVITY
11919   if (player->gravity && !player->programmed_action)
11920 #else
11921   if (game.gravity && !player->programmed_action)
11922 #endif
11923   {
11924     int jx = player->jx, jy = player->jy;
11925     boolean field_under_player_is_free =
11926       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11927     boolean player_is_standing_on_valid_field =
11928       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11929        (IS_WALKABLE(Feld[jx][jy]) &&
11930         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11931
11932     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11933       player->programmed_action = MV_DOWN;
11934   }
11935 }
11936
11937 /*
11938   MovePlayerOneStep()
11939   -----------------------------------------------------------------------------
11940   dx, dy:               direction (non-diagonal) to try to move the player to
11941   real_dx, real_dy:     direction as read from input device (can be diagonal)
11942 */
11943
11944 boolean MovePlayerOneStep(struct PlayerInfo *player,
11945                           int dx, int dy, int real_dx, int real_dy)
11946 {
11947   int jx = player->jx, jy = player->jy;
11948   int new_jx = jx + dx, new_jy = jy + dy;
11949 #if !USE_FIXED_DONT_RUN_INTO
11950   int element;
11951 #endif
11952   int can_move;
11953   boolean player_can_move = !player->cannot_move;
11954
11955   if (!player->active || (!dx && !dy))
11956     return MP_NO_ACTION;
11957
11958   player->MovDir = (dx < 0 ? MV_LEFT :
11959                     dx > 0 ? MV_RIGHT :
11960                     dy < 0 ? MV_UP :
11961                     dy > 0 ? MV_DOWN :  MV_NONE);
11962
11963   if (!IN_LEV_FIELD(new_jx, new_jy))
11964     return MP_NO_ACTION;
11965
11966   if (!player_can_move)
11967   {
11968     if (player->MovPos == 0)
11969     {
11970       player->is_moving = FALSE;
11971       player->is_digging = FALSE;
11972       player->is_collecting = FALSE;
11973       player->is_snapping = FALSE;
11974       player->is_pushing = FALSE;
11975     }
11976   }
11977
11978 #if 1
11979   if (!options.network && game.centered_player_nr == -1 &&
11980       !AllPlayersInSight(player, new_jx, new_jy))
11981     return MP_NO_ACTION;
11982 #else
11983   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
11984     return MP_NO_ACTION;
11985 #endif
11986
11987 #if !USE_FIXED_DONT_RUN_INTO
11988   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
11989
11990   /* (moved to DigField()) */
11991   if (player_can_move && DONT_RUN_INTO(element))
11992   {
11993     if (element == EL_ACID && dx == 0 && dy == 1)
11994     {
11995       SplashAcid(new_jx, new_jy);
11996       Feld[jx][jy] = EL_PLAYER_1;
11997       InitMovingField(jx, jy, MV_DOWN);
11998       Store[jx][jy] = EL_ACID;
11999       ContinueMoving(jx, jy);
12000       BuryPlayer(player);
12001     }
12002     else
12003       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12004
12005     return MP_MOVING;
12006   }
12007 #endif
12008
12009   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12010   if (can_move != MP_MOVING)
12011     return can_move;
12012
12013   /* check if DigField() has caused relocation of the player */
12014   if (player->jx != jx || player->jy != jy)
12015     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12016
12017   StorePlayer[jx][jy] = 0;
12018   player->last_jx = jx;
12019   player->last_jy = jy;
12020   player->jx = new_jx;
12021   player->jy = new_jy;
12022   StorePlayer[new_jx][new_jy] = player->element_nr;
12023
12024   if (player->move_delay_value_next != -1)
12025   {
12026     player->move_delay_value = player->move_delay_value_next;
12027     player->move_delay_value_next = -1;
12028   }
12029
12030   player->MovPos =
12031     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12032
12033   player->step_counter++;
12034
12035   PlayerVisit[jx][jy] = FrameCounter;
12036
12037 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12038   player->is_moving = TRUE;
12039 #endif
12040
12041 #if 1
12042   /* should better be called in MovePlayer(), but this breaks some tapes */
12043   ScrollPlayer(player, SCROLL_INIT);
12044 #endif
12045
12046   return MP_MOVING;
12047 }
12048
12049 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12050 {
12051   int jx = player->jx, jy = player->jy;
12052   int old_jx = jx, old_jy = jy;
12053   int moved = MP_NO_ACTION;
12054
12055   if (!player->active)
12056     return FALSE;
12057
12058   if (!dx && !dy)
12059   {
12060     if (player->MovPos == 0)
12061     {
12062       player->is_moving = FALSE;
12063       player->is_digging = FALSE;
12064       player->is_collecting = FALSE;
12065       player->is_snapping = FALSE;
12066       player->is_pushing = FALSE;
12067     }
12068
12069     return FALSE;
12070   }
12071
12072   if (player->move_delay > 0)
12073     return FALSE;
12074
12075   player->move_delay = -1;              /* set to "uninitialized" value */
12076
12077   /* store if player is automatically moved to next field */
12078   player->is_auto_moving = (player->programmed_action != MV_NONE);
12079
12080   /* remove the last programmed player action */
12081   player->programmed_action = 0;
12082
12083   if (player->MovPos)
12084   {
12085     /* should only happen if pre-1.2 tape recordings are played */
12086     /* this is only for backward compatibility */
12087
12088     int original_move_delay_value = player->move_delay_value;
12089
12090 #if DEBUG
12091     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12092            tape.counter);
12093 #endif
12094
12095     /* scroll remaining steps with finest movement resolution */
12096     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12097
12098     while (player->MovPos)
12099     {
12100       ScrollPlayer(player, SCROLL_GO_ON);
12101       ScrollScreen(NULL, SCROLL_GO_ON);
12102
12103       AdvanceFrameAndPlayerCounters(player->index_nr);
12104
12105       DrawAllPlayers();
12106       BackToFront();
12107     }
12108
12109     player->move_delay_value = original_move_delay_value;
12110   }
12111
12112   player->is_active = FALSE;
12113
12114   if (player->last_move_dir & MV_HORIZONTAL)
12115   {
12116     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12117       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12118   }
12119   else
12120   {
12121     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12122       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12123   }
12124
12125 #if USE_FIXED_BORDER_RUNNING_GFX
12126   if (!moved && !player->is_active)
12127   {
12128     player->is_moving = FALSE;
12129     player->is_digging = FALSE;
12130     player->is_collecting = FALSE;
12131     player->is_snapping = FALSE;
12132     player->is_pushing = FALSE;
12133   }
12134 #endif
12135
12136   jx = player->jx;
12137   jy = player->jy;
12138
12139 #if 1
12140   if (moved & MP_MOVING && !ScreenMovPos &&
12141       (player->index_nr == game.centered_player_nr ||
12142        game.centered_player_nr == -1))
12143 #else
12144   if (moved & MP_MOVING && !ScreenMovPos &&
12145       (player == local_player || !options.network))
12146 #endif
12147   {
12148     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12149     int offset = (setup.scroll_delay ? setup.scroll_delay_value : 0);
12150
12151     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12152     {
12153       /* actual player has left the screen -- scroll in that direction */
12154       if (jx != old_jx)         /* player has moved horizontally */
12155         scroll_x += (jx - old_jx);
12156       else                      /* player has moved vertically */
12157         scroll_y += (jy - old_jy);
12158     }
12159     else
12160     {
12161       if (jx != old_jx)         /* player has moved horizontally */
12162       {
12163         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12164             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12165           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12166
12167         /* don't scroll over playfield boundaries */
12168         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12169           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12170
12171         /* don't scroll more than one field at a time */
12172         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12173
12174         /* don't scroll against the player's moving direction */
12175         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12176             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12177           scroll_x = old_scroll_x;
12178       }
12179       else                      /* player has moved vertically */
12180       {
12181         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12182             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12183           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12184
12185         /* don't scroll over playfield boundaries */
12186         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12187           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12188
12189         /* don't scroll more than one field at a time */
12190         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12191
12192         /* don't scroll against the player's moving direction */
12193         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12194             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12195           scroll_y = old_scroll_y;
12196       }
12197     }
12198
12199     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12200     {
12201 #if 1
12202       if (!options.network && game.centered_player_nr == -1 &&
12203           !AllPlayersInVisibleScreen())
12204       {
12205         scroll_x = old_scroll_x;
12206         scroll_y = old_scroll_y;
12207       }
12208       else
12209 #else
12210       if (!options.network && !AllPlayersInVisibleScreen())
12211       {
12212         scroll_x = old_scroll_x;
12213         scroll_y = old_scroll_y;
12214       }
12215       else
12216 #endif
12217       {
12218         ScrollScreen(player, SCROLL_INIT);
12219         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12220       }
12221     }
12222   }
12223
12224   player->StepFrame = 0;
12225
12226   if (moved & MP_MOVING)
12227   {
12228     if (old_jx != jx && old_jy == jy)
12229       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12230     else if (old_jx == jx && old_jy != jy)
12231       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12232
12233     DrawLevelField(jx, jy);     /* for "crumbled sand" */
12234
12235     player->last_move_dir = player->MovDir;
12236     player->is_moving = TRUE;
12237     player->is_snapping = FALSE;
12238     player->is_switching = FALSE;
12239     player->is_dropping = FALSE;
12240     player->is_dropping_pressed = FALSE;
12241     player->drop_pressed_delay = 0;
12242
12243 #if 0
12244     /* should better be called here than above, but this breaks some tapes */
12245     ScrollPlayer(player, SCROLL_INIT);
12246 #endif
12247   }
12248   else
12249   {
12250     CheckGravityMovementWhenNotMoving(player);
12251
12252     player->is_moving = FALSE;
12253
12254     /* at this point, the player is allowed to move, but cannot move right now
12255        (e.g. because of something blocking the way) -- ensure that the player
12256        is also allowed to move in the next frame (in old versions before 3.1.1,
12257        the player was forced to wait again for eight frames before next try) */
12258
12259     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12260       player->move_delay = 0;   /* allow direct movement in the next frame */
12261   }
12262
12263   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12264     player->move_delay = player->move_delay_value;
12265
12266   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12267   {
12268     TestIfPlayerTouchesBadThing(jx, jy);
12269     TestIfPlayerTouchesCustomElement(jx, jy);
12270   }
12271
12272   if (!player->active)
12273     RemovePlayer(player);
12274
12275   return moved;
12276 }
12277
12278 void ScrollPlayer(struct PlayerInfo *player, int mode)
12279 {
12280   int jx = player->jx, jy = player->jy;
12281   int last_jx = player->last_jx, last_jy = player->last_jy;
12282   int move_stepsize = TILEX / player->move_delay_value;
12283
12284 #if USE_NEW_PLAYER_SPEED
12285   if (!player->active)
12286     return;
12287
12288   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12289     return;
12290 #else
12291   if (!player->active || player->MovPos == 0)
12292     return;
12293 #endif
12294
12295   if (mode == SCROLL_INIT)
12296   {
12297     player->actual_frame_counter = FrameCounter;
12298     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12299
12300     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12301         Feld[last_jx][last_jy] == EL_EMPTY)
12302     {
12303       int last_field_block_delay = 0;   /* start with no blocking at all */
12304       int block_delay_adjustment = player->block_delay_adjustment;
12305
12306       /* if player blocks last field, add delay for exactly one move */
12307       if (player->block_last_field)
12308       {
12309         last_field_block_delay += player->move_delay_value;
12310
12311         /* when blocking enabled, prevent moving up despite gravity */
12312 #if USE_PLAYER_GRAVITY
12313         if (player->gravity && player->MovDir == MV_UP)
12314           block_delay_adjustment = -1;
12315 #else
12316         if (game.gravity && player->MovDir == MV_UP)
12317           block_delay_adjustment = -1;
12318 #endif
12319       }
12320
12321       /* add block delay adjustment (also possible when not blocking) */
12322       last_field_block_delay += block_delay_adjustment;
12323
12324       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12325       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12326     }
12327
12328 #if USE_NEW_PLAYER_SPEED
12329     if (player->MovPos != 0)    /* player has not yet reached destination */
12330       return;
12331 #else
12332     return;
12333 #endif
12334   }
12335   else if (!FrameReached(&player->actual_frame_counter, 1))
12336     return;
12337
12338 #if USE_NEW_PLAYER_SPEED
12339   if (player->MovPos != 0)
12340   {
12341     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12342     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12343
12344     /* before DrawPlayer() to draw correct player graphic for this case */
12345     if (player->MovPos == 0)
12346       CheckGravityMovement(player);
12347   }
12348 #else
12349   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12350   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12351
12352   /* before DrawPlayer() to draw correct player graphic for this case */
12353   if (player->MovPos == 0)
12354     CheckGravityMovement(player);
12355 #endif
12356
12357   if (player->MovPos == 0)      /* player reached destination field */
12358   {
12359     if (player->move_delay_reset_counter > 0)
12360     {
12361       player->move_delay_reset_counter--;
12362
12363       if (player->move_delay_reset_counter == 0)
12364       {
12365         /* continue with normal speed after quickly moving through gate */
12366         HALVE_PLAYER_SPEED(player);
12367
12368         /* be able to make the next move without delay */
12369         player->move_delay = 0;
12370       }
12371     }
12372
12373     player->last_jx = jx;
12374     player->last_jy = jy;
12375
12376     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12377         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12378         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12379         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12380         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12381         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12382     {
12383       DrawPlayer(player);       /* needed here only to cleanup last field */
12384       RemovePlayer(player);
12385
12386       if (local_player->friends_still_needed == 0 ||
12387           IS_SP_ELEMENT(Feld[jx][jy]))
12388         PlayerWins(player);
12389     }
12390
12391     /* this breaks one level: "machine", level 000 */
12392     {
12393       int move_direction = player->MovDir;
12394       int enter_side = MV_DIR_OPPOSITE(move_direction);
12395       int leave_side = move_direction;
12396       int old_jx = last_jx;
12397       int old_jy = last_jy;
12398       int old_element = Feld[old_jx][old_jy];
12399       int new_element = Feld[jx][jy];
12400
12401       if (IS_CUSTOM_ELEMENT(old_element))
12402         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12403                                    CE_LEFT_BY_PLAYER,
12404                                    player->index_bit, leave_side);
12405
12406       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12407                                           CE_PLAYER_LEAVES_X,
12408                                           player->index_bit, leave_side);
12409
12410       if (IS_CUSTOM_ELEMENT(new_element))
12411         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12412                                    player->index_bit, enter_side);
12413
12414       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12415                                           CE_PLAYER_ENTERS_X,
12416                                           player->index_bit, enter_side);
12417
12418       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12419                                         CE_MOVE_OF_X, move_direction);
12420     }
12421
12422     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12423     {
12424       TestIfPlayerTouchesBadThing(jx, jy);
12425       TestIfPlayerTouchesCustomElement(jx, jy);
12426
12427       /* needed because pushed element has not yet reached its destination,
12428          so it would trigger a change event at its previous field location */
12429       if (!player->is_pushing)
12430         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12431
12432       if (!player->active)
12433         RemovePlayer(player);
12434     }
12435
12436     if (!local_player->LevelSolved && level.use_step_counter)
12437     {
12438       int i;
12439
12440       TimePlayed++;
12441
12442       if (TimeLeft > 0)
12443       {
12444         TimeLeft--;
12445
12446         if (TimeLeft <= 10 && setup.time_limit)
12447           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12448
12449 #if 1
12450         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
12451
12452         DisplayGameControlValues();
12453 #else
12454         DrawGameValue_Time(TimeLeft);
12455 #endif
12456
12457         if (!TimeLeft && setup.time_limit)
12458           for (i = 0; i < MAX_PLAYERS; i++)
12459             KillPlayer(&stored_player[i]);
12460       }
12461 #if 1
12462       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12463       {
12464         game_control_value[GAME_CONTROL_TIME] = TimePlayed;
12465
12466         DisplayGameControlValues();
12467       }
12468 #else
12469       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12470         DrawGameValue_Time(TimePlayed);
12471 #endif
12472     }
12473
12474     if (tape.single_step && tape.recording && !tape.pausing &&
12475         !player->programmed_action)
12476       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12477   }
12478 }
12479
12480 void ScrollScreen(struct PlayerInfo *player, int mode)
12481 {
12482   static unsigned long screen_frame_counter = 0;
12483
12484   if (mode == SCROLL_INIT)
12485   {
12486     /* set scrolling step size according to actual player's moving speed */
12487     ScrollStepSize = TILEX / player->move_delay_value;
12488
12489     screen_frame_counter = FrameCounter;
12490     ScreenMovDir = player->MovDir;
12491     ScreenMovPos = player->MovPos;
12492     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12493     return;
12494   }
12495   else if (!FrameReached(&screen_frame_counter, 1))
12496     return;
12497
12498   if (ScreenMovPos)
12499   {
12500     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12501     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12502     redraw_mask |= REDRAW_FIELD;
12503   }
12504   else
12505     ScreenMovDir = MV_NONE;
12506 }
12507
12508 void TestIfPlayerTouchesCustomElement(int x, int y)
12509 {
12510   static int xy[4][2] =
12511   {
12512     { 0, -1 },
12513     { -1, 0 },
12514     { +1, 0 },
12515     { 0, +1 }
12516   };
12517   static int trigger_sides[4][2] =
12518   {
12519     /* center side       border side */
12520     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12521     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12522     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12523     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12524   };
12525   static int touch_dir[4] =
12526   {
12527     MV_LEFT | MV_RIGHT,
12528     MV_UP   | MV_DOWN,
12529     MV_UP   | MV_DOWN,
12530     MV_LEFT | MV_RIGHT
12531   };
12532   int center_element = Feld[x][y];      /* should always be non-moving! */
12533   int i;
12534
12535   for (i = 0; i < NUM_DIRECTIONS; i++)
12536   {
12537     int xx = x + xy[i][0];
12538     int yy = y + xy[i][1];
12539     int center_side = trigger_sides[i][0];
12540     int border_side = trigger_sides[i][1];
12541     int border_element;
12542
12543     if (!IN_LEV_FIELD(xx, yy))
12544       continue;
12545
12546     if (IS_PLAYER(x, y))
12547     {
12548       struct PlayerInfo *player = PLAYERINFO(x, y);
12549
12550       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12551         border_element = Feld[xx][yy];          /* may be moving! */
12552       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12553         border_element = Feld[xx][yy];
12554       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12555         border_element = MovingOrBlocked2Element(xx, yy);
12556       else
12557         continue;               /* center and border element do not touch */
12558
12559       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12560                                  player->index_bit, border_side);
12561       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12562                                           CE_PLAYER_TOUCHES_X,
12563                                           player->index_bit, border_side);
12564     }
12565     else if (IS_PLAYER(xx, yy))
12566     {
12567       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12568
12569       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12570       {
12571         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12572           continue;             /* center and border element do not touch */
12573       }
12574
12575       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12576                                  player->index_bit, center_side);
12577       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12578                                           CE_PLAYER_TOUCHES_X,
12579                                           player->index_bit, center_side);
12580       break;
12581     }
12582   }
12583 }
12584
12585 #if USE_ELEMENT_TOUCHING_BUGFIX
12586
12587 void TestIfElementTouchesCustomElement(int x, int y)
12588 {
12589   static int xy[4][2] =
12590   {
12591     { 0, -1 },
12592     { -1, 0 },
12593     { +1, 0 },
12594     { 0, +1 }
12595   };
12596   static int trigger_sides[4][2] =
12597   {
12598     /* center side      border side */
12599     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12600     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12601     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12602     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12603   };
12604   static int touch_dir[4] =
12605   {
12606     MV_LEFT | MV_RIGHT,
12607     MV_UP   | MV_DOWN,
12608     MV_UP   | MV_DOWN,
12609     MV_LEFT | MV_RIGHT
12610   };
12611   boolean change_center_element = FALSE;
12612   int center_element = Feld[x][y];      /* should always be non-moving! */
12613   int border_element_old[NUM_DIRECTIONS];
12614   int i;
12615
12616   for (i = 0; i < NUM_DIRECTIONS; i++)
12617   {
12618     int xx = x + xy[i][0];
12619     int yy = y + xy[i][1];
12620     int border_element;
12621
12622     border_element_old[i] = -1;
12623
12624     if (!IN_LEV_FIELD(xx, yy))
12625       continue;
12626
12627     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12628       border_element = Feld[xx][yy];    /* may be moving! */
12629     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12630       border_element = Feld[xx][yy];
12631     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12632       border_element = MovingOrBlocked2Element(xx, yy);
12633     else
12634       continue;                 /* center and border element do not touch */
12635
12636     border_element_old[i] = border_element;
12637   }
12638
12639   for (i = 0; i < NUM_DIRECTIONS; i++)
12640   {
12641     int xx = x + xy[i][0];
12642     int yy = y + xy[i][1];
12643     int center_side = trigger_sides[i][0];
12644     int border_element = border_element_old[i];
12645
12646     if (border_element == -1)
12647       continue;
12648
12649     /* check for change of border element */
12650     CheckElementChangeBySide(xx, yy, border_element, center_element,
12651                              CE_TOUCHING_X, center_side);
12652   }
12653
12654   for (i = 0; i < NUM_DIRECTIONS; i++)
12655   {
12656     int border_side = trigger_sides[i][1];
12657     int border_element = border_element_old[i];
12658
12659     if (border_element == -1)
12660       continue;
12661
12662     /* check for change of center element (but change it only once) */
12663     if (!change_center_element)
12664       change_center_element =
12665         CheckElementChangeBySide(x, y, center_element, border_element,
12666                                  CE_TOUCHING_X, border_side);
12667   }
12668 }
12669
12670 #else
12671
12672 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12673 {
12674   static int xy[4][2] =
12675   {
12676     { 0, -1 },
12677     { -1, 0 },
12678     { +1, 0 },
12679     { 0, +1 }
12680   };
12681   static int trigger_sides[4][2] =
12682   {
12683     /* center side      border side */
12684     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12685     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12686     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12687     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12688   };
12689   static int touch_dir[4] =
12690   {
12691     MV_LEFT | MV_RIGHT,
12692     MV_UP   | MV_DOWN,
12693     MV_UP   | MV_DOWN,
12694     MV_LEFT | MV_RIGHT
12695   };
12696   boolean change_center_element = FALSE;
12697   int center_element = Feld[x][y];      /* should always be non-moving! */
12698   int i;
12699
12700   for (i = 0; i < NUM_DIRECTIONS; i++)
12701   {
12702     int xx = x + xy[i][0];
12703     int yy = y + xy[i][1];
12704     int center_side = trigger_sides[i][0];
12705     int border_side = trigger_sides[i][1];
12706     int border_element;
12707
12708     if (!IN_LEV_FIELD(xx, yy))
12709       continue;
12710
12711     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12712       border_element = Feld[xx][yy];    /* may be moving! */
12713     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12714       border_element = Feld[xx][yy];
12715     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12716       border_element = MovingOrBlocked2Element(xx, yy);
12717     else
12718       continue;                 /* center and border element do not touch */
12719
12720     /* check for change of center element (but change it only once) */
12721     if (!change_center_element)
12722       change_center_element =
12723         CheckElementChangeBySide(x, y, center_element, border_element,
12724                                  CE_TOUCHING_X, border_side);
12725
12726     /* check for change of border element */
12727     CheckElementChangeBySide(xx, yy, border_element, center_element,
12728                              CE_TOUCHING_X, center_side);
12729   }
12730 }
12731
12732 #endif
12733
12734 void TestIfElementHitsCustomElement(int x, int y, int direction)
12735 {
12736   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12737   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12738   int hitx = x + dx, hity = y + dy;
12739   int hitting_element = Feld[x][y];
12740   int touched_element;
12741
12742   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12743     return;
12744
12745   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12746                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12747
12748   if (IN_LEV_FIELD(hitx, hity))
12749   {
12750     int opposite_direction = MV_DIR_OPPOSITE(direction);
12751     int hitting_side = direction;
12752     int touched_side = opposite_direction;
12753     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12754                           MovDir[hitx][hity] != direction ||
12755                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12756
12757     object_hit = TRUE;
12758
12759     if (object_hit)
12760     {
12761       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12762                                CE_HITTING_X, touched_side);
12763
12764       CheckElementChangeBySide(hitx, hity, touched_element,
12765                                hitting_element, CE_HIT_BY_X, hitting_side);
12766
12767       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12768                                CE_HIT_BY_SOMETHING, opposite_direction);
12769     }
12770   }
12771
12772   /* "hitting something" is also true when hitting the playfield border */
12773   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12774                            CE_HITTING_SOMETHING, direction);
12775 }
12776
12777 #if 0
12778 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12779 {
12780   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12781   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12782   int hitx = x + dx, hity = y + dy;
12783   int hitting_element = Feld[x][y];
12784   int touched_element;
12785 #if 0
12786   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12787                         !IS_FREE(hitx, hity) &&
12788                         (!IS_MOVING(hitx, hity) ||
12789                          MovDir[hitx][hity] != direction ||
12790                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
12791 #endif
12792
12793   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12794     return;
12795
12796 #if 0
12797   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12798     return;
12799 #endif
12800
12801   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12802                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12803
12804   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12805                            EP_CAN_SMASH_EVERYTHING, direction);
12806
12807   if (IN_LEV_FIELD(hitx, hity))
12808   {
12809     int opposite_direction = MV_DIR_OPPOSITE(direction);
12810     int hitting_side = direction;
12811     int touched_side = opposite_direction;
12812 #if 0
12813     int touched_element = MovingOrBlocked2Element(hitx, hity);
12814 #endif
12815 #if 1
12816     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12817                           MovDir[hitx][hity] != direction ||
12818                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12819
12820     object_hit = TRUE;
12821 #endif
12822
12823     if (object_hit)
12824     {
12825       int i;
12826
12827       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12828                                CE_SMASHED_BY_SOMETHING, opposite_direction);
12829
12830       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12831                                CE_OTHER_IS_SMASHING, touched_side);
12832
12833       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12834                                CE_OTHER_GETS_SMASHED, hitting_side);
12835     }
12836   }
12837 }
12838 #endif
12839
12840 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12841 {
12842   int i, kill_x = -1, kill_y = -1;
12843
12844   int bad_element = -1;
12845   static int test_xy[4][2] =
12846   {
12847     { 0, -1 },
12848     { -1, 0 },
12849     { +1, 0 },
12850     { 0, +1 }
12851   };
12852   static int test_dir[4] =
12853   {
12854     MV_UP,
12855     MV_LEFT,
12856     MV_RIGHT,
12857     MV_DOWN
12858   };
12859
12860   for (i = 0; i < NUM_DIRECTIONS; i++)
12861   {
12862     int test_x, test_y, test_move_dir, test_element;
12863
12864     test_x = good_x + test_xy[i][0];
12865     test_y = good_y + test_xy[i][1];
12866
12867     if (!IN_LEV_FIELD(test_x, test_y))
12868       continue;
12869
12870     test_move_dir =
12871       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12872
12873     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12874
12875     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12876        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12877     */
12878     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12879         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12880     {
12881       kill_x = test_x;
12882       kill_y = test_y;
12883       bad_element = test_element;
12884
12885       break;
12886     }
12887   }
12888
12889   if (kill_x != -1 || kill_y != -1)
12890   {
12891     if (IS_PLAYER(good_x, good_y))
12892     {
12893       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12894
12895       if (player->shield_deadly_time_left > 0 &&
12896           !IS_INDESTRUCTIBLE(bad_element))
12897         Bang(kill_x, kill_y);
12898       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12899         KillPlayer(player);
12900     }
12901     else
12902       Bang(good_x, good_y);
12903   }
12904 }
12905
12906 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12907 {
12908   int i, kill_x = -1, kill_y = -1;
12909   int bad_element = Feld[bad_x][bad_y];
12910   static int test_xy[4][2] =
12911   {
12912     { 0, -1 },
12913     { -1, 0 },
12914     { +1, 0 },
12915     { 0, +1 }
12916   };
12917   static int touch_dir[4] =
12918   {
12919     MV_LEFT | MV_RIGHT,
12920     MV_UP   | MV_DOWN,
12921     MV_UP   | MV_DOWN,
12922     MV_LEFT | MV_RIGHT
12923   };
12924   static int test_dir[4] =
12925   {
12926     MV_UP,
12927     MV_LEFT,
12928     MV_RIGHT,
12929     MV_DOWN
12930   };
12931
12932   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12933     return;
12934
12935   for (i = 0; i < NUM_DIRECTIONS; i++)
12936   {
12937     int test_x, test_y, test_move_dir, test_element;
12938
12939     test_x = bad_x + test_xy[i][0];
12940     test_y = bad_y + test_xy[i][1];
12941     if (!IN_LEV_FIELD(test_x, test_y))
12942       continue;
12943
12944     test_move_dir =
12945       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12946
12947     test_element = Feld[test_x][test_y];
12948
12949     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12950        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12951     */
12952     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12953         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12954     {
12955       /* good thing is player or penguin that does not move away */
12956       if (IS_PLAYER(test_x, test_y))
12957       {
12958         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12959
12960         if (bad_element == EL_ROBOT && player->is_moving)
12961           continue;     /* robot does not kill player if he is moving */
12962
12963         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12964         {
12965           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12966             continue;           /* center and border element do not touch */
12967         }
12968
12969         kill_x = test_x;
12970         kill_y = test_y;
12971         break;
12972       }
12973       else if (test_element == EL_PENGUIN)
12974       {
12975         kill_x = test_x;
12976         kill_y = test_y;
12977         break;
12978       }
12979     }
12980   }
12981
12982   if (kill_x != -1 || kill_y != -1)
12983   {
12984     if (IS_PLAYER(kill_x, kill_y))
12985     {
12986       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12987
12988       if (player->shield_deadly_time_left > 0 &&
12989           !IS_INDESTRUCTIBLE(bad_element))
12990         Bang(bad_x, bad_y);
12991       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12992         KillPlayer(player);
12993     }
12994     else
12995       Bang(kill_x, kill_y);
12996   }
12997 }
12998
12999 void TestIfPlayerTouchesBadThing(int x, int y)
13000 {
13001   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13002 }
13003
13004 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13005 {
13006   TestIfGoodThingHitsBadThing(x, y, move_dir);
13007 }
13008
13009 void TestIfBadThingTouchesPlayer(int x, int y)
13010 {
13011   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13012 }
13013
13014 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13015 {
13016   TestIfBadThingHitsGoodThing(x, y, move_dir);
13017 }
13018
13019 void TestIfFriendTouchesBadThing(int x, int y)
13020 {
13021   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13022 }
13023
13024 void TestIfBadThingTouchesFriend(int x, int y)
13025 {
13026   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13027 }
13028
13029 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13030 {
13031   int i, kill_x = bad_x, kill_y = bad_y;
13032   static int xy[4][2] =
13033   {
13034     { 0, -1 },
13035     { -1, 0 },
13036     { +1, 0 },
13037     { 0, +1 }
13038   };
13039
13040   for (i = 0; i < NUM_DIRECTIONS; i++)
13041   {
13042     int x, y, element;
13043
13044     x = bad_x + xy[i][0];
13045     y = bad_y + xy[i][1];
13046     if (!IN_LEV_FIELD(x, y))
13047       continue;
13048
13049     element = Feld[x][y];
13050     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13051         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13052     {
13053       kill_x = x;
13054       kill_y = y;
13055       break;
13056     }
13057   }
13058
13059   if (kill_x != bad_x || kill_y != bad_y)
13060     Bang(bad_x, bad_y);
13061 }
13062
13063 void KillPlayer(struct PlayerInfo *player)
13064 {
13065   int jx = player->jx, jy = player->jy;
13066
13067   if (!player->active)
13068     return;
13069
13070   /* the following code was introduced to prevent an infinite loop when calling
13071      -> Bang()
13072      -> CheckTriggeredElementChangeExt()
13073      -> ExecuteCustomElementAction()
13074      -> KillPlayer()
13075      -> (infinitely repeating the above sequence of function calls)
13076      which occurs when killing the player while having a CE with the setting
13077      "kill player X when explosion of <player X>"; the solution using a new
13078      field "player->killed" was chosen for backwards compatibility, although
13079      clever use of the fields "player->active" etc. would probably also work */
13080 #if 1
13081   if (player->killed)
13082     return;
13083 #endif
13084
13085   player->killed = TRUE;
13086
13087   /* remove accessible field at the player's position */
13088   Feld[jx][jy] = EL_EMPTY;
13089
13090   /* deactivate shield (else Bang()/Explode() would not work right) */
13091   player->shield_normal_time_left = 0;
13092   player->shield_deadly_time_left = 0;
13093
13094   Bang(jx, jy);
13095   BuryPlayer(player);
13096 }
13097
13098 static void KillPlayerUnlessEnemyProtected(int x, int y)
13099 {
13100   if (!PLAYER_ENEMY_PROTECTED(x, y))
13101     KillPlayer(PLAYERINFO(x, y));
13102 }
13103
13104 static void KillPlayerUnlessExplosionProtected(int x, int y)
13105 {
13106   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13107     KillPlayer(PLAYERINFO(x, y));
13108 }
13109
13110 void BuryPlayer(struct PlayerInfo *player)
13111 {
13112   int jx = player->jx, jy = player->jy;
13113
13114   if (!player->active)
13115     return;
13116
13117   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13118   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13119
13120   player->GameOver = TRUE;
13121   RemovePlayer(player);
13122 }
13123
13124 void RemovePlayer(struct PlayerInfo *player)
13125 {
13126   int jx = player->jx, jy = player->jy;
13127   int i, found = FALSE;
13128
13129   player->present = FALSE;
13130   player->active = FALSE;
13131
13132   if (!ExplodeField[jx][jy])
13133     StorePlayer[jx][jy] = 0;
13134
13135   if (player->is_moving)
13136     DrawLevelField(player->last_jx, player->last_jy);
13137
13138   for (i = 0; i < MAX_PLAYERS; i++)
13139     if (stored_player[i].active)
13140       found = TRUE;
13141
13142   if (!found)
13143     AllPlayersGone = TRUE;
13144
13145   ExitX = ZX = jx;
13146   ExitY = ZY = jy;
13147 }
13148
13149 #if USE_NEW_SNAP_DELAY
13150 static void setFieldForSnapping(int x, int y, int element, int direction)
13151 {
13152   struct ElementInfo *ei = &element_info[element];
13153   int direction_bit = MV_DIR_TO_BIT(direction);
13154   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13155   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13156                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13157
13158   Feld[x][y] = EL_ELEMENT_SNAPPING;
13159   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13160
13161   ResetGfxAnimation(x, y);
13162
13163   GfxElement[x][y] = element;
13164   GfxAction[x][y] = action;
13165   GfxDir[x][y] = direction;
13166   GfxFrame[x][y] = -1;
13167 }
13168 #endif
13169
13170 /*
13171   =============================================================================
13172   checkDiagonalPushing()
13173   -----------------------------------------------------------------------------
13174   check if diagonal input device direction results in pushing of object
13175   (by checking if the alternative direction is walkable, diggable, ...)
13176   =============================================================================
13177 */
13178
13179 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13180                                     int x, int y, int real_dx, int real_dy)
13181 {
13182   int jx, jy, dx, dy, xx, yy;
13183
13184   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13185     return TRUE;
13186
13187   /* diagonal direction: check alternative direction */
13188   jx = player->jx;
13189   jy = player->jy;
13190   dx = x - jx;
13191   dy = y - jy;
13192   xx = jx + (dx == 0 ? real_dx : 0);
13193   yy = jy + (dy == 0 ? real_dy : 0);
13194
13195   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13196 }
13197
13198 /*
13199   =============================================================================
13200   DigField()
13201   -----------------------------------------------------------------------------
13202   x, y:                 field next to player (non-diagonal) to try to dig to
13203   real_dx, real_dy:     direction as read from input device (can be diagonal)
13204   =============================================================================
13205 */
13206
13207 int DigField(struct PlayerInfo *player,
13208              int oldx, int oldy, int x, int y,
13209              int real_dx, int real_dy, int mode)
13210 {
13211   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13212   boolean player_was_pushing = player->is_pushing;
13213   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13214   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13215   int jx = oldx, jy = oldy;
13216   int dx = x - jx, dy = y - jy;
13217   int nextx = x + dx, nexty = y + dy;
13218   int move_direction = (dx == -1 ? MV_LEFT  :
13219                         dx == +1 ? MV_RIGHT :
13220                         dy == -1 ? MV_UP    :
13221                         dy == +1 ? MV_DOWN  : MV_NONE);
13222   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13223   int dig_side = MV_DIR_OPPOSITE(move_direction);
13224   int old_element = Feld[jx][jy];
13225 #if USE_FIXED_DONT_RUN_INTO
13226   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13227 #else
13228   int element;
13229 #endif
13230   int collect_count;
13231
13232   if (is_player)                /* function can also be called by EL_PENGUIN */
13233   {
13234     if (player->MovPos == 0)
13235     {
13236       player->is_digging = FALSE;
13237       player->is_collecting = FALSE;
13238     }
13239
13240     if (player->MovPos == 0)    /* last pushing move finished */
13241       player->is_pushing = FALSE;
13242
13243     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13244     {
13245       player->is_switching = FALSE;
13246       player->push_delay = -1;
13247
13248       return MP_NO_ACTION;
13249     }
13250   }
13251
13252 #if !USE_FIXED_DONT_RUN_INTO
13253   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13254     return MP_NO_ACTION;
13255 #endif
13256
13257   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13258     old_element = Back[jx][jy];
13259
13260   /* in case of element dropped at player position, check background */
13261   else if (Back[jx][jy] != EL_EMPTY &&
13262            game.engine_version >= VERSION_IDENT(2,2,0,0))
13263     old_element = Back[jx][jy];
13264
13265   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13266     return MP_NO_ACTION;        /* field has no opening in this direction */
13267
13268   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13269     return MP_NO_ACTION;        /* field has no opening in this direction */
13270
13271 #if USE_FIXED_DONT_RUN_INTO
13272   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13273   {
13274     SplashAcid(x, y);
13275
13276     Feld[jx][jy] = player->artwork_element;
13277     InitMovingField(jx, jy, MV_DOWN);
13278     Store[jx][jy] = EL_ACID;
13279     ContinueMoving(jx, jy);
13280     BuryPlayer(player);
13281
13282     return MP_DONT_RUN_INTO;
13283   }
13284 #endif
13285
13286 #if USE_FIXED_DONT_RUN_INTO
13287   if (player_can_move && DONT_RUN_INTO(element))
13288   {
13289     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13290
13291     return MP_DONT_RUN_INTO;
13292   }
13293 #endif
13294
13295 #if USE_FIXED_DONT_RUN_INTO
13296   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13297     return MP_NO_ACTION;
13298 #endif
13299
13300 #if !USE_FIXED_DONT_RUN_INTO
13301   element = Feld[x][y];
13302 #endif
13303
13304   collect_count = element_info[element].collect_count_initial;
13305
13306   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13307     return MP_NO_ACTION;
13308
13309   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13310     player_can_move = player_can_move_or_snap;
13311
13312   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13313       game.engine_version >= VERSION_IDENT(2,2,0,0))
13314   {
13315     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13316                                player->index_bit, dig_side);
13317     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13318                                         player->index_bit, dig_side);
13319
13320     if (element == EL_DC_LANDMINE)
13321       Bang(x, y);
13322
13323     if (Feld[x][y] != element)          /* field changed by snapping */
13324       return MP_ACTION;
13325
13326     return MP_NO_ACTION;
13327   }
13328
13329 #if USE_PLAYER_GRAVITY
13330   if (player->gravity && is_player && !player->is_auto_moving &&
13331       canFallDown(player) && move_direction != MV_DOWN &&
13332       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13333     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13334 #else
13335   if (game.gravity && is_player && !player->is_auto_moving &&
13336       canFallDown(player) && move_direction != MV_DOWN &&
13337       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13338     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13339 #endif
13340
13341   if (player_can_move &&
13342       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13343   {
13344     int sound_element = SND_ELEMENT(element);
13345     int sound_action = ACTION_WALKING;
13346
13347     if (IS_RND_GATE(element))
13348     {
13349       if (!player->key[RND_GATE_NR(element)])
13350         return MP_NO_ACTION;
13351     }
13352     else if (IS_RND_GATE_GRAY(element))
13353     {
13354       if (!player->key[RND_GATE_GRAY_NR(element)])
13355         return MP_NO_ACTION;
13356     }
13357     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13358     {
13359       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13360         return MP_NO_ACTION;
13361     }
13362     else if (element == EL_EXIT_OPEN ||
13363              element == EL_EM_EXIT_OPEN ||
13364              element == EL_STEEL_EXIT_OPEN ||
13365              element == EL_EM_STEEL_EXIT_OPEN ||
13366              element == EL_SP_EXIT_OPEN ||
13367              element == EL_SP_EXIT_OPENING)
13368     {
13369       sound_action = ACTION_PASSING;    /* player is passing exit */
13370     }
13371     else if (element == EL_EMPTY)
13372     {
13373       sound_action = ACTION_MOVING;             /* nothing to walk on */
13374     }
13375
13376     /* play sound from background or player, whatever is available */
13377     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13378       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13379     else
13380       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13381   }
13382   else if (player_can_move &&
13383            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13384   {
13385     if (!ACCESS_FROM(element, opposite_direction))
13386       return MP_NO_ACTION;      /* field not accessible from this direction */
13387
13388     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13389       return MP_NO_ACTION;
13390
13391     if (IS_EM_GATE(element))
13392     {
13393       if (!player->key[EM_GATE_NR(element)])
13394         return MP_NO_ACTION;
13395     }
13396     else if (IS_EM_GATE_GRAY(element))
13397     {
13398       if (!player->key[EM_GATE_GRAY_NR(element)])
13399         return MP_NO_ACTION;
13400     }
13401     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13402     {
13403       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13404         return MP_NO_ACTION;
13405     }
13406     else if (IS_EMC_GATE(element))
13407     {
13408       if (!player->key[EMC_GATE_NR(element)])
13409         return MP_NO_ACTION;
13410     }
13411     else if (IS_EMC_GATE_GRAY(element))
13412     {
13413       if (!player->key[EMC_GATE_GRAY_NR(element)])
13414         return MP_NO_ACTION;
13415     }
13416     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13417     {
13418       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13419         return MP_NO_ACTION;
13420     }
13421     else if (element == EL_DC_GATE_WHITE ||
13422              element == EL_DC_GATE_WHITE_GRAY ||
13423              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13424     {
13425       if (player->num_white_keys == 0)
13426         return MP_NO_ACTION;
13427
13428       player->num_white_keys--;
13429     }
13430     else if (IS_SP_PORT(element))
13431     {
13432       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13433           element == EL_SP_GRAVITY_PORT_RIGHT ||
13434           element == EL_SP_GRAVITY_PORT_UP ||
13435           element == EL_SP_GRAVITY_PORT_DOWN)
13436 #if USE_PLAYER_GRAVITY
13437         player->gravity = !player->gravity;
13438 #else
13439         game.gravity = !game.gravity;
13440 #endif
13441       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13442                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13443                element == EL_SP_GRAVITY_ON_PORT_UP ||
13444                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13445 #if USE_PLAYER_GRAVITY
13446         player->gravity = TRUE;
13447 #else
13448         game.gravity = TRUE;
13449 #endif
13450       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13451                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13452                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13453                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13454 #if USE_PLAYER_GRAVITY
13455         player->gravity = FALSE;
13456 #else
13457         game.gravity = FALSE;
13458 #endif
13459     }
13460
13461     /* automatically move to the next field with double speed */
13462     player->programmed_action = move_direction;
13463
13464     if (player->move_delay_reset_counter == 0)
13465     {
13466       player->move_delay_reset_counter = 2;     /* two double speed steps */
13467
13468       DOUBLE_PLAYER_SPEED(player);
13469     }
13470
13471     PlayLevelSoundAction(x, y, ACTION_PASSING);
13472   }
13473   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13474   {
13475     RemoveField(x, y);
13476
13477     if (mode != DF_SNAP)
13478     {
13479       GfxElement[x][y] = GFX_ELEMENT(element);
13480       player->is_digging = TRUE;
13481     }
13482
13483     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13484
13485     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13486                                         player->index_bit, dig_side);
13487
13488     if (mode == DF_SNAP)
13489     {
13490 #if USE_NEW_SNAP_DELAY
13491       if (level.block_snap_field)
13492         setFieldForSnapping(x, y, element, move_direction);
13493       else
13494         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13495 #else
13496       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13497 #endif
13498
13499       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13500                                           player->index_bit, dig_side);
13501     }
13502   }
13503   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13504   {
13505     RemoveField(x, y);
13506
13507     if (is_player && mode != DF_SNAP)
13508     {
13509       GfxElement[x][y] = element;
13510       player->is_collecting = TRUE;
13511     }
13512
13513     if (element == EL_SPEED_PILL)
13514     {
13515       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13516     }
13517     else if (element == EL_EXTRA_TIME && level.time > 0)
13518     {
13519       TimeLeft += level.extra_time;
13520
13521 #if 1
13522       game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13523
13524       DisplayGameControlValues();
13525 #else
13526       DrawGameValue_Time(TimeLeft);
13527 #endif
13528     }
13529     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13530     {
13531       player->shield_normal_time_left += level.shield_normal_time;
13532       if (element == EL_SHIELD_DEADLY)
13533         player->shield_deadly_time_left += level.shield_deadly_time;
13534     }
13535     else if (element == EL_DYNAMITE ||
13536              element == EL_EM_DYNAMITE ||
13537              element == EL_SP_DISK_RED)
13538     {
13539       if (player->inventory_size < MAX_INVENTORY_SIZE)
13540         player->inventory_element[player->inventory_size++] = element;
13541
13542       DrawGameDoorValues();
13543     }
13544     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13545     {
13546       player->dynabomb_count++;
13547       player->dynabombs_left++;
13548     }
13549     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13550     {
13551       player->dynabomb_size++;
13552     }
13553     else if (element == EL_DYNABOMB_INCREASE_POWER)
13554     {
13555       player->dynabomb_xl = TRUE;
13556     }
13557     else if (IS_KEY(element))
13558     {
13559       player->key[KEY_NR(element)] = TRUE;
13560
13561       DrawGameDoorValues();
13562     }
13563     else if (element == EL_DC_KEY_WHITE)
13564     {
13565       player->num_white_keys++;
13566
13567       /* display white keys? */
13568       /* DrawGameDoorValues(); */
13569     }
13570     else if (IS_ENVELOPE(element))
13571     {
13572       player->show_envelope = element;
13573     }
13574     else if (element == EL_EMC_LENSES)
13575     {
13576       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13577
13578       RedrawAllInvisibleElementsForLenses();
13579     }
13580     else if (element == EL_EMC_MAGNIFIER)
13581     {
13582       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13583
13584       RedrawAllInvisibleElementsForMagnifier();
13585     }
13586     else if (IS_DROPPABLE(element) ||
13587              IS_THROWABLE(element))     /* can be collected and dropped */
13588     {
13589       int i;
13590
13591       if (collect_count == 0)
13592         player->inventory_infinite_element = element;
13593       else
13594         for (i = 0; i < collect_count; i++)
13595           if (player->inventory_size < MAX_INVENTORY_SIZE)
13596             player->inventory_element[player->inventory_size++] = element;
13597
13598       DrawGameDoorValues();
13599     }
13600     else if (collect_count > 0)
13601     {
13602       local_player->gems_still_needed -= collect_count;
13603       if (local_player->gems_still_needed < 0)
13604         local_player->gems_still_needed = 0;
13605
13606 #if 1
13607       game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
13608
13609       DisplayGameControlValues();
13610 #else
13611       DrawGameValue_Emeralds(local_player->gems_still_needed);
13612 #endif
13613     }
13614
13615     RaiseScoreElement(element);
13616     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13617
13618     if (is_player)
13619       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13620                                           player->index_bit, dig_side);
13621
13622     if (mode == DF_SNAP)
13623     {
13624 #if USE_NEW_SNAP_DELAY
13625       if (level.block_snap_field)
13626         setFieldForSnapping(x, y, element, move_direction);
13627       else
13628         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13629 #else
13630       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13631 #endif
13632
13633       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13634                                           player->index_bit, dig_side);
13635     }
13636   }
13637   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13638   {
13639     if (mode == DF_SNAP && element != EL_BD_ROCK)
13640       return MP_NO_ACTION;
13641
13642     if (CAN_FALL(element) && dy)
13643       return MP_NO_ACTION;
13644
13645     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13646         !(element == EL_SPRING && level.use_spring_bug))
13647       return MP_NO_ACTION;
13648
13649     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13650         ((move_direction & MV_VERTICAL &&
13651           ((element_info[element].move_pattern & MV_LEFT &&
13652             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13653            (element_info[element].move_pattern & MV_RIGHT &&
13654             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13655          (move_direction & MV_HORIZONTAL &&
13656           ((element_info[element].move_pattern & MV_UP &&
13657             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13658            (element_info[element].move_pattern & MV_DOWN &&
13659             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13660       return MP_NO_ACTION;
13661
13662     /* do not push elements already moving away faster than player */
13663     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13664         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13665       return MP_NO_ACTION;
13666
13667     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13668     {
13669       if (player->push_delay_value == -1 || !player_was_pushing)
13670         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13671     }
13672     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13673     {
13674       if (player->push_delay_value == -1)
13675         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13676     }
13677     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13678     {
13679       if (!player->is_pushing)
13680         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13681     }
13682
13683     player->is_pushing = TRUE;
13684     player->is_active = TRUE;
13685
13686     if (!(IN_LEV_FIELD(nextx, nexty) &&
13687           (IS_FREE(nextx, nexty) ||
13688            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13689             IS_SB_ELEMENT(element)))))
13690       return MP_NO_ACTION;
13691
13692     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13693       return MP_NO_ACTION;
13694
13695     if (player->push_delay == -1)       /* new pushing; restart delay */
13696       player->push_delay = 0;
13697
13698     if (player->push_delay < player->push_delay_value &&
13699         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13700         element != EL_SPRING && element != EL_BALLOON)
13701     {
13702       /* make sure that there is no move delay before next try to push */
13703       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13704         player->move_delay = 0;
13705
13706       return MP_NO_ACTION;
13707     }
13708
13709     if (IS_SB_ELEMENT(element))
13710     {
13711       if (element == EL_SOKOBAN_FIELD_FULL)
13712       {
13713         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13714         local_player->sokobanfields_still_needed++;
13715       }
13716
13717       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13718       {
13719         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13720         local_player->sokobanfields_still_needed--;
13721       }
13722
13723       Feld[x][y] = EL_SOKOBAN_OBJECT;
13724
13725       if (Back[x][y] == Back[nextx][nexty])
13726         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13727       else if (Back[x][y] != 0)
13728         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13729                                     ACTION_EMPTYING);
13730       else
13731         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13732                                     ACTION_FILLING);
13733
13734       if (local_player->sokobanfields_still_needed == 0 &&
13735           game.emulation == EMU_SOKOBAN)
13736       {
13737         PlayerWins(player);
13738
13739         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13740       }
13741     }
13742     else
13743       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13744
13745     InitMovingField(x, y, move_direction);
13746     GfxAction[x][y] = ACTION_PUSHING;
13747
13748     if (mode == DF_SNAP)
13749       ContinueMoving(x, y);
13750     else
13751       MovPos[x][y] = (dx != 0 ? dx : dy);
13752
13753     Pushed[x][y] = TRUE;
13754     Pushed[nextx][nexty] = TRUE;
13755
13756     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13757       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13758     else
13759       player->push_delay_value = -1;    /* get new value later */
13760
13761     /* check for element change _after_ element has been pushed */
13762     if (game.use_change_when_pushing_bug)
13763     {
13764       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13765                                  player->index_bit, dig_side);
13766       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13767                                           player->index_bit, dig_side);
13768     }
13769   }
13770   else if (IS_SWITCHABLE(element))
13771   {
13772     if (PLAYER_SWITCHING(player, x, y))
13773     {
13774       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13775                                           player->index_bit, dig_side);
13776
13777       return MP_ACTION;
13778     }
13779
13780     player->is_switching = TRUE;
13781     player->switch_x = x;
13782     player->switch_y = y;
13783
13784     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13785
13786     if (element == EL_ROBOT_WHEEL)
13787     {
13788       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13789       ZX = x;
13790       ZY = y;
13791
13792       DrawLevelField(x, y);
13793     }
13794     else if (element == EL_SP_TERMINAL)
13795     {
13796       int xx, yy;
13797
13798       SCAN_PLAYFIELD(xx, yy)
13799       {
13800         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13801           Bang(xx, yy);
13802         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13803           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13804       }
13805     }
13806     else if (IS_BELT_SWITCH(element))
13807     {
13808       ToggleBeltSwitch(x, y);
13809     }
13810     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13811              element == EL_SWITCHGATE_SWITCH_DOWN ||
13812              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13813              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13814     {
13815       ToggleSwitchgateSwitch(x, y);
13816     }
13817     else if (element == EL_LIGHT_SWITCH ||
13818              element == EL_LIGHT_SWITCH_ACTIVE)
13819     {
13820       ToggleLightSwitch(x, y);
13821     }
13822     else if (element == EL_TIMEGATE_SWITCH ||
13823              element == EL_DC_TIMEGATE_SWITCH)
13824     {
13825       ActivateTimegateSwitch(x, y);
13826     }
13827     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13828              element == EL_BALLOON_SWITCH_RIGHT ||
13829              element == EL_BALLOON_SWITCH_UP    ||
13830              element == EL_BALLOON_SWITCH_DOWN  ||
13831              element == EL_BALLOON_SWITCH_NONE  ||
13832              element == EL_BALLOON_SWITCH_ANY)
13833     {
13834       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13835                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13836                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13837                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13838                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13839                              move_direction);
13840     }
13841     else if (element == EL_LAMP)
13842     {
13843       Feld[x][y] = EL_LAMP_ACTIVE;
13844       local_player->lights_still_needed--;
13845
13846       ResetGfxAnimation(x, y);
13847       DrawLevelField(x, y);
13848     }
13849     else if (element == EL_TIME_ORB_FULL)
13850     {
13851       Feld[x][y] = EL_TIME_ORB_EMPTY;
13852
13853       if (level.time > 0 || level.use_time_orb_bug)
13854       {
13855         TimeLeft += level.time_orb_time;
13856
13857 #if 1
13858         game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13859
13860         DisplayGameControlValues();
13861 #else
13862         DrawGameValue_Time(TimeLeft);
13863 #endif
13864       }
13865
13866       ResetGfxAnimation(x, y);
13867       DrawLevelField(x, y);
13868     }
13869     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13870              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13871     {
13872       int xx, yy;
13873
13874       game.ball_state = !game.ball_state;
13875
13876       SCAN_PLAYFIELD(xx, yy)
13877       {
13878         int e = Feld[xx][yy];
13879
13880         if (game.ball_state)
13881         {
13882           if (e == EL_EMC_MAGIC_BALL)
13883             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13884           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13885             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13886         }
13887         else
13888         {
13889           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13890             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13891           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13892             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13893         }
13894       }
13895     }
13896
13897     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13898                                         player->index_bit, dig_side);
13899
13900     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13901                                         player->index_bit, dig_side);
13902
13903     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13904                                         player->index_bit, dig_side);
13905
13906     return MP_ACTION;
13907   }
13908   else
13909   {
13910     if (!PLAYER_SWITCHING(player, x, y))
13911     {
13912       player->is_switching = TRUE;
13913       player->switch_x = x;
13914       player->switch_y = y;
13915
13916       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13917                                  player->index_bit, dig_side);
13918       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13919                                           player->index_bit, dig_side);
13920
13921       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13922                                  player->index_bit, dig_side);
13923       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13924                                           player->index_bit, dig_side);
13925     }
13926
13927     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13928                                player->index_bit, dig_side);
13929     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13930                                         player->index_bit, dig_side);
13931
13932     return MP_NO_ACTION;
13933   }
13934
13935   player->push_delay = -1;
13936
13937   if (is_player)                /* function can also be called by EL_PENGUIN */
13938   {
13939     if (Feld[x][y] != element)          /* really digged/collected something */
13940     {
13941       player->is_collecting = !player->is_digging;
13942       player->is_active = TRUE;
13943     }
13944   }
13945
13946   return MP_MOVING;
13947 }
13948
13949 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13950 {
13951   int jx = player->jx, jy = player->jy;
13952   int x = jx + dx, y = jy + dy;
13953   int snap_direction = (dx == -1 ? MV_LEFT  :
13954                         dx == +1 ? MV_RIGHT :
13955                         dy == -1 ? MV_UP    :
13956                         dy == +1 ? MV_DOWN  : MV_NONE);
13957   boolean can_continue_snapping = (level.continuous_snapping &&
13958                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13959
13960   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13961     return FALSE;
13962
13963   if (!player->active || !IN_LEV_FIELD(x, y))
13964     return FALSE;
13965
13966   if (dx && dy)
13967     return FALSE;
13968
13969   if (!dx && !dy)
13970   {
13971     if (player->MovPos == 0)
13972       player->is_pushing = FALSE;
13973
13974     player->is_snapping = FALSE;
13975
13976     if (player->MovPos == 0)
13977     {
13978       player->is_moving = FALSE;
13979       player->is_digging = FALSE;
13980       player->is_collecting = FALSE;
13981     }
13982
13983     return FALSE;
13984   }
13985
13986 #if USE_NEW_CONTINUOUS_SNAPPING
13987   /* prevent snapping with already pressed snap key when not allowed */
13988   if (player->is_snapping && !can_continue_snapping)
13989     return FALSE;
13990 #else
13991   if (player->is_snapping)
13992     return FALSE;
13993 #endif
13994
13995   player->MovDir = snap_direction;
13996
13997   if (player->MovPos == 0)
13998   {
13999     player->is_moving = FALSE;
14000     player->is_digging = FALSE;
14001     player->is_collecting = FALSE;
14002   }
14003
14004   player->is_dropping = FALSE;
14005   player->is_dropping_pressed = FALSE;
14006   player->drop_pressed_delay = 0;
14007
14008   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14009     return FALSE;
14010
14011   player->is_snapping = TRUE;
14012   player->is_active = TRUE;
14013
14014   if (player->MovPos == 0)
14015   {
14016     player->is_moving = FALSE;
14017     player->is_digging = FALSE;
14018     player->is_collecting = FALSE;
14019   }
14020
14021   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14022     DrawLevelField(player->last_jx, player->last_jy);
14023
14024   DrawLevelField(x, y);
14025
14026   return TRUE;
14027 }
14028
14029 boolean DropElement(struct PlayerInfo *player)
14030 {
14031   int old_element, new_element;
14032   int dropx = player->jx, dropy = player->jy;
14033   int drop_direction = player->MovDir;
14034   int drop_side = drop_direction;
14035   int drop_element = (player->inventory_size > 0 ?
14036                       player->inventory_element[player->inventory_size - 1] :
14037                       player->inventory_infinite_element != EL_UNDEFINED ?
14038                       player->inventory_infinite_element :
14039                       player->dynabombs_left > 0 ?
14040                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14041                       EL_UNDEFINED);
14042
14043   player->is_dropping_pressed = TRUE;
14044
14045   /* do not drop an element on top of another element; when holding drop key
14046      pressed without moving, dropped element must move away before the next
14047      element can be dropped (this is especially important if the next element
14048      is dynamite, which can be placed on background for historical reasons) */
14049   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14050     return MP_ACTION;
14051
14052   if (IS_THROWABLE(drop_element))
14053   {
14054     dropx += GET_DX_FROM_DIR(drop_direction);
14055     dropy += GET_DY_FROM_DIR(drop_direction);
14056
14057     if (!IN_LEV_FIELD(dropx, dropy))
14058       return FALSE;
14059   }
14060
14061   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14062   new_element = drop_element;           /* default: no change when dropping */
14063
14064   /* check if player is active, not moving and ready to drop */
14065   if (!player->active || player->MovPos || player->drop_delay > 0)
14066     return FALSE;
14067
14068   /* check if player has anything that can be dropped */
14069   if (new_element == EL_UNDEFINED)
14070     return FALSE;
14071
14072   /* check if drop key was pressed long enough for EM style dynamite */
14073   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14074     return FALSE;
14075
14076   /* check if anything can be dropped at the current position */
14077   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14078     return FALSE;
14079
14080   /* collected custom elements can only be dropped on empty fields */
14081   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14082     return FALSE;
14083
14084   if (old_element != EL_EMPTY)
14085     Back[dropx][dropy] = old_element;   /* store old element on this field */
14086
14087   ResetGfxAnimation(dropx, dropy);
14088   ResetRandomAnimationValue(dropx, dropy);
14089
14090   if (player->inventory_size > 0 ||
14091       player->inventory_infinite_element != EL_UNDEFINED)
14092   {
14093     if (player->inventory_size > 0)
14094     {
14095       player->inventory_size--;
14096
14097       DrawGameDoorValues();
14098
14099       if (new_element == EL_DYNAMITE)
14100         new_element = EL_DYNAMITE_ACTIVE;
14101       else if (new_element == EL_EM_DYNAMITE)
14102         new_element = EL_EM_DYNAMITE_ACTIVE;
14103       else if (new_element == EL_SP_DISK_RED)
14104         new_element = EL_SP_DISK_RED_ACTIVE;
14105     }
14106
14107     Feld[dropx][dropy] = new_element;
14108
14109     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14110       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14111                           el2img(Feld[dropx][dropy]), 0);
14112
14113     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14114
14115     /* needed if previous element just changed to "empty" in the last frame */
14116     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14117
14118     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14119                                player->index_bit, drop_side);
14120     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14121                                         CE_PLAYER_DROPS_X,
14122                                         player->index_bit, drop_side);
14123
14124     TestIfElementTouchesCustomElement(dropx, dropy);
14125   }
14126   else          /* player is dropping a dyna bomb */
14127   {
14128     player->dynabombs_left--;
14129
14130     Feld[dropx][dropy] = new_element;
14131
14132     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14133       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14134                           el2img(Feld[dropx][dropy]), 0);
14135
14136     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14137   }
14138
14139   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14140     InitField_WithBug1(dropx, dropy, FALSE);
14141
14142   new_element = Feld[dropx][dropy];     /* element might have changed */
14143
14144   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14145       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14146   {
14147     int move_direction, nextx, nexty;
14148
14149     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14150       MovDir[dropx][dropy] = drop_direction;
14151
14152     move_direction = MovDir[dropx][dropy];
14153     nextx = dropx + GET_DX_FROM_DIR(move_direction);
14154     nexty = dropy + GET_DY_FROM_DIR(move_direction);
14155
14156     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14157
14158 #if USE_FIX_IMPACT_COLLISION
14159     /* do not cause impact style collision by dropping elements that can fall */
14160     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14161 #else
14162     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14163 #endif
14164   }
14165
14166   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14167   player->is_dropping = TRUE;
14168
14169   player->drop_pressed_delay = 0;
14170   player->is_dropping_pressed = FALSE;
14171
14172   player->drop_x = dropx;
14173   player->drop_y = dropy;
14174
14175   return TRUE;
14176 }
14177
14178 /* ------------------------------------------------------------------------- */
14179 /* game sound playing functions                                              */
14180 /* ------------------------------------------------------------------------- */
14181
14182 static int *loop_sound_frame = NULL;
14183 static int *loop_sound_volume = NULL;
14184
14185 void InitPlayLevelSound()
14186 {
14187   int num_sounds = getSoundListSize();
14188
14189   checked_free(loop_sound_frame);
14190   checked_free(loop_sound_volume);
14191
14192   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14193   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14194 }
14195
14196 static void PlayLevelSound(int x, int y, int nr)
14197 {
14198   int sx = SCREENX(x), sy = SCREENY(y);
14199   int volume, stereo_position;
14200   int max_distance = 8;
14201   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14202
14203   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14204       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14205     return;
14206
14207   if (!IN_LEV_FIELD(x, y) ||
14208       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14209       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14210     return;
14211
14212   volume = SOUND_MAX_VOLUME;
14213
14214   if (!IN_SCR_FIELD(sx, sy))
14215   {
14216     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14217     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14218
14219     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14220   }
14221
14222   stereo_position = (SOUND_MAX_LEFT +
14223                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14224                      (SCR_FIELDX + 2 * max_distance));
14225
14226   if (IS_LOOP_SOUND(nr))
14227   {
14228     /* This assures that quieter loop sounds do not overwrite louder ones,
14229        while restarting sound volume comparison with each new game frame. */
14230
14231     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14232       return;
14233
14234     loop_sound_volume[nr] = volume;
14235     loop_sound_frame[nr] = FrameCounter;
14236   }
14237
14238   PlaySoundExt(nr, volume, stereo_position, type);
14239 }
14240
14241 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14242 {
14243   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14244                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14245                  y < LEVELY(BY1) ? LEVELY(BY1) :
14246                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14247                  sound_action);
14248 }
14249
14250 static void PlayLevelSoundAction(int x, int y, int action)
14251 {
14252   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14253 }
14254
14255 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14256 {
14257   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14258
14259   if (sound_effect != SND_UNDEFINED)
14260     PlayLevelSound(x, y, sound_effect);
14261 }
14262
14263 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14264                                               int action)
14265 {
14266   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14267
14268   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14269     PlayLevelSound(x, y, sound_effect);
14270 }
14271
14272 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14273 {
14274   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14275
14276   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14277     PlayLevelSound(x, y, sound_effect);
14278 }
14279
14280 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14281 {
14282   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14283
14284   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14285     StopSound(sound_effect);
14286 }
14287
14288 static void PlayLevelMusic()
14289 {
14290   if (levelset.music[level_nr] != MUS_UNDEFINED)
14291     PlayMusic(levelset.music[level_nr]);        /* from config file */
14292   else
14293     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14294 }
14295
14296 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14297 {
14298   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14299   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14300   int x = xx - 1 - offset;
14301   int y = yy - 1 - offset;
14302
14303   switch (sample)
14304   {
14305     case SAMPLE_blank:
14306       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14307       break;
14308
14309     case SAMPLE_roll:
14310       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14311       break;
14312
14313     case SAMPLE_stone:
14314       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14315       break;
14316
14317     case SAMPLE_nut:
14318       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14319       break;
14320
14321     case SAMPLE_crack:
14322       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14323       break;
14324
14325     case SAMPLE_bug:
14326       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14327       break;
14328
14329     case SAMPLE_tank:
14330       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14331       break;
14332
14333     case SAMPLE_android_clone:
14334       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14335       break;
14336
14337     case SAMPLE_android_move:
14338       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14339       break;
14340
14341     case SAMPLE_spring:
14342       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14343       break;
14344
14345     case SAMPLE_slurp:
14346       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14347       break;
14348
14349     case SAMPLE_eater:
14350       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14351       break;
14352
14353     case SAMPLE_eater_eat:
14354       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14355       break;
14356
14357     case SAMPLE_alien:
14358       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14359       break;
14360
14361     case SAMPLE_collect:
14362       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14363       break;
14364
14365     case SAMPLE_diamond:
14366       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14367       break;
14368
14369     case SAMPLE_squash:
14370       /* !!! CHECK THIS !!! */
14371 #if 1
14372       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14373 #else
14374       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14375 #endif
14376       break;
14377
14378     case SAMPLE_wonderfall:
14379       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14380       break;
14381
14382     case SAMPLE_drip:
14383       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14384       break;
14385
14386     case SAMPLE_push:
14387       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14388       break;
14389
14390     case SAMPLE_dirt:
14391       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14392       break;
14393
14394     case SAMPLE_acid:
14395       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14396       break;
14397
14398     case SAMPLE_ball:
14399       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14400       break;
14401
14402     case SAMPLE_grow:
14403       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14404       break;
14405
14406     case SAMPLE_wonder:
14407       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14408       break;
14409
14410     case SAMPLE_door:
14411       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14412       break;
14413
14414     case SAMPLE_exit_open:
14415       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14416       break;
14417
14418     case SAMPLE_exit_leave:
14419       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14420       break;
14421
14422     case SAMPLE_dynamite:
14423       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14424       break;
14425
14426     case SAMPLE_tick:
14427       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14428       break;
14429
14430     case SAMPLE_press:
14431       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14432       break;
14433
14434     case SAMPLE_wheel:
14435       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14436       break;
14437
14438     case SAMPLE_boom:
14439       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14440       break;
14441
14442     case SAMPLE_die:
14443       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14444       break;
14445
14446     case SAMPLE_time:
14447       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14448       break;
14449
14450     default:
14451       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14452       break;
14453   }
14454 }
14455
14456 #if 0
14457 void ChangeTime(int value)
14458 {
14459   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
14460
14461   *time += value;
14462
14463   /* EMC game engine uses value from time counter of RND game engine */
14464   level.native_em_level->lev->time = *time;
14465
14466   DrawGameValue_Time(*time);
14467 }
14468
14469 void RaiseScore(int value)
14470 {
14471   /* EMC game engine and RND game engine have separate score counters */
14472   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
14473                 &level.native_em_level->lev->score : &local_player->score);
14474
14475   *score += value;
14476
14477   DrawGameValue_Score(*score);
14478 }
14479 #endif
14480
14481 void RaiseScore(int value)
14482 {
14483   local_player->score += value;
14484
14485 #if 1
14486   game_control_value[GAME_CONTROL_SCORE] = local_player->score;
14487
14488   DisplayGameControlValues();
14489 #else
14490   DrawGameValue_Score(local_player->score);
14491 #endif
14492 }
14493
14494 void RaiseScoreElement(int element)
14495 {
14496   switch (element)
14497   {
14498     case EL_EMERALD:
14499     case EL_BD_DIAMOND:
14500     case EL_EMERALD_YELLOW:
14501     case EL_EMERALD_RED:
14502     case EL_EMERALD_PURPLE:
14503     case EL_SP_INFOTRON:
14504       RaiseScore(level.score[SC_EMERALD]);
14505       break;
14506     case EL_DIAMOND:
14507       RaiseScore(level.score[SC_DIAMOND]);
14508       break;
14509     case EL_CRYSTAL:
14510       RaiseScore(level.score[SC_CRYSTAL]);
14511       break;
14512     case EL_PEARL:
14513       RaiseScore(level.score[SC_PEARL]);
14514       break;
14515     case EL_BUG:
14516     case EL_BD_BUTTERFLY:
14517     case EL_SP_ELECTRON:
14518       RaiseScore(level.score[SC_BUG]);
14519       break;
14520     case EL_SPACESHIP:
14521     case EL_BD_FIREFLY:
14522     case EL_SP_SNIKSNAK:
14523       RaiseScore(level.score[SC_SPACESHIP]);
14524       break;
14525     case EL_YAMYAM:
14526     case EL_DARK_YAMYAM:
14527       RaiseScore(level.score[SC_YAMYAM]);
14528       break;
14529     case EL_ROBOT:
14530       RaiseScore(level.score[SC_ROBOT]);
14531       break;
14532     case EL_PACMAN:
14533       RaiseScore(level.score[SC_PACMAN]);
14534       break;
14535     case EL_NUT:
14536       RaiseScore(level.score[SC_NUT]);
14537       break;
14538     case EL_DYNAMITE:
14539     case EL_EM_DYNAMITE:
14540     case EL_SP_DISK_RED:
14541     case EL_DYNABOMB_INCREASE_NUMBER:
14542     case EL_DYNABOMB_INCREASE_SIZE:
14543     case EL_DYNABOMB_INCREASE_POWER:
14544       RaiseScore(level.score[SC_DYNAMITE]);
14545       break;
14546     case EL_SHIELD_NORMAL:
14547     case EL_SHIELD_DEADLY:
14548       RaiseScore(level.score[SC_SHIELD]);
14549       break;
14550     case EL_EXTRA_TIME:
14551       RaiseScore(level.extra_time_score);
14552       break;
14553     case EL_KEY_1:
14554     case EL_KEY_2:
14555     case EL_KEY_3:
14556     case EL_KEY_4:
14557     case EL_EM_KEY_1:
14558     case EL_EM_KEY_2:
14559     case EL_EM_KEY_3:
14560     case EL_EM_KEY_4:
14561     case EL_EMC_KEY_5:
14562     case EL_EMC_KEY_6:
14563     case EL_EMC_KEY_7:
14564     case EL_EMC_KEY_8:
14565     case EL_DC_KEY_WHITE:
14566       RaiseScore(level.score[SC_KEY]);
14567       break;
14568     default:
14569       RaiseScore(element_info[element].collect_score);
14570       break;
14571   }
14572 }
14573
14574 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14575 {
14576   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14577   {
14578 #if defined(NETWORK_AVALIABLE)
14579     if (options.network)
14580       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14581     else
14582 #endif
14583     {
14584       if (quick_quit)
14585       {
14586 #if 1
14587
14588 #if 1
14589         FadeSkipNextFadeIn();
14590 #else
14591         fading = fading_none;
14592 #endif
14593
14594 #else
14595         OpenDoor(DOOR_CLOSE_1);
14596 #endif
14597
14598         game_status = GAME_MODE_MAIN;
14599
14600 #if 1
14601         DrawAndFadeInMainMenu(REDRAW_FIELD);
14602 #else
14603         DrawMainMenu();
14604 #endif
14605       }
14606       else
14607       {
14608 #if 0
14609         FadeOut(REDRAW_FIELD);
14610 #endif
14611
14612         game_status = GAME_MODE_MAIN;
14613
14614         DrawAndFadeInMainMenu(REDRAW_FIELD);
14615       }
14616     }
14617   }
14618   else          /* continue playing the game */
14619   {
14620     if (tape.playing && tape.deactivate_display)
14621       TapeDeactivateDisplayOff(TRUE);
14622
14623     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14624
14625     if (tape.playing && tape.deactivate_display)
14626       TapeDeactivateDisplayOn();
14627   }
14628 }
14629
14630 void RequestQuitGame(boolean ask_if_really_quit)
14631 {
14632   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14633   boolean skip_request = AllPlayersGone || quick_quit;
14634
14635   RequestQuitGameExt(skip_request, quick_quit,
14636                      "Do you really want to quit the game ?");
14637 }
14638
14639
14640 /* ------------------------------------------------------------------------- */
14641 /* random generator functions                                                */
14642 /* ------------------------------------------------------------------------- */
14643
14644 unsigned int InitEngineRandom_RND(long seed)
14645 {
14646   game.num_random_calls = 0;
14647
14648 #if 0
14649   unsigned int rnd_seed = InitEngineRandom(seed);
14650
14651   printf("::: START RND: %d\n", rnd_seed);
14652
14653   return rnd_seed;
14654 #else
14655
14656   return InitEngineRandom(seed);
14657
14658 #endif
14659
14660 }
14661
14662 unsigned int RND(int max)
14663 {
14664   if (max > 0)
14665   {
14666     game.num_random_calls++;
14667
14668     return GetEngineRandom(max);
14669   }
14670
14671   return 0;
14672 }
14673
14674
14675 /* ------------------------------------------------------------------------- */
14676 /* game engine snapshot handling functions                                   */
14677 /* ------------------------------------------------------------------------- */
14678
14679 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
14680
14681 struct EngineSnapshotInfo
14682 {
14683   /* runtime values for custom element collect score */
14684   int collect_score[NUM_CUSTOM_ELEMENTS];
14685
14686   /* runtime values for group element choice position */
14687   int choice_pos[NUM_GROUP_ELEMENTS];
14688
14689   /* runtime values for belt position animations */
14690   int belt_graphic[4 * NUM_BELT_PARTS];
14691   int belt_anim_mode[4 * NUM_BELT_PARTS];
14692 };
14693
14694 struct EngineSnapshotNodeInfo
14695 {
14696   void *buffer_orig;
14697   void *buffer_copy;
14698   int size;
14699 };
14700
14701 static struct EngineSnapshotInfo engine_snapshot_rnd;
14702 static ListNode *engine_snapshot_list = NULL;
14703 static char *snapshot_level_identifier = NULL;
14704 static int snapshot_level_nr = -1;
14705
14706 void FreeEngineSnapshot()
14707 {
14708   while (engine_snapshot_list != NULL)
14709     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14710                        checked_free);
14711
14712   setString(&snapshot_level_identifier, NULL);
14713   snapshot_level_nr = -1;
14714 }
14715
14716 static void SaveEngineSnapshotValues_RND()
14717 {
14718   static int belt_base_active_element[4] =
14719   {
14720     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14721     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14722     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14723     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14724   };
14725   int i, j;
14726
14727   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14728   {
14729     int element = EL_CUSTOM_START + i;
14730
14731     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14732   }
14733
14734   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14735   {
14736     int element = EL_GROUP_START + i;
14737
14738     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14739   }
14740
14741   for (i = 0; i < 4; i++)
14742   {
14743     for (j = 0; j < NUM_BELT_PARTS; j++)
14744     {
14745       int element = belt_base_active_element[i] + j;
14746       int graphic = el2img(element);
14747       int anim_mode = graphic_info[graphic].anim_mode;
14748
14749       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14750       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14751     }
14752   }
14753 }
14754
14755 static void LoadEngineSnapshotValues_RND()
14756 {
14757   unsigned long num_random_calls = game.num_random_calls;
14758   int i, j;
14759
14760   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14761   {
14762     int element = EL_CUSTOM_START + i;
14763
14764     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14765   }
14766
14767   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14768   {
14769     int element = EL_GROUP_START + i;
14770
14771     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14772   }
14773
14774   for (i = 0; i < 4; i++)
14775   {
14776     for (j = 0; j < NUM_BELT_PARTS; j++)
14777     {
14778       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14779       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14780
14781       graphic_info[graphic].anim_mode = anim_mode;
14782     }
14783   }
14784
14785   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14786   {
14787     InitRND(tape.random_seed);
14788     for (i = 0; i < num_random_calls; i++)
14789       RND(1);
14790   }
14791
14792   if (game.num_random_calls != num_random_calls)
14793   {
14794     Error(ERR_INFO, "number of random calls out of sync");
14795     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14796     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14797     Error(ERR_EXIT, "this should not happen -- please debug");
14798   }
14799 }
14800
14801 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14802 {
14803   struct EngineSnapshotNodeInfo *bi =
14804     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14805
14806   bi->buffer_orig = buffer;
14807   bi->buffer_copy = checked_malloc(size);
14808   bi->size = size;
14809
14810   memcpy(bi->buffer_copy, buffer, size);
14811
14812   addNodeToList(&engine_snapshot_list, NULL, bi);
14813 }
14814
14815 void SaveEngineSnapshot()
14816 {
14817   FreeEngineSnapshot();         /* free previous snapshot, if needed */
14818
14819   if (level_editor_test_game)   /* do not save snapshots from editor */
14820     return;
14821
14822   /* copy some special values to a structure better suited for the snapshot */
14823
14824   SaveEngineSnapshotValues_RND();
14825   SaveEngineSnapshotValues_EM();
14826
14827   /* save values stored in special snapshot structure */
14828
14829   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14830   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14831
14832   /* save further RND engine values */
14833
14834   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14835   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14836   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14837
14838   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14839   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14840   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14841   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14842
14843   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14844   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14845   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14846   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14847   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14848
14849   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14850   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14851   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14852
14853   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14854
14855   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14856
14857   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14858   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14859
14860   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14861   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14862   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14863   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14864   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14865   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14866   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14867   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14868   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14869   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14870   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14871   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14872   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14873   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14874   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14875   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14876   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
14877   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
14878
14879   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14880   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14881
14882   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14883   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14884   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14885
14886   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14887   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14888
14889   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14890   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14891   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14892   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14893   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14894
14895   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14896   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14897
14898   /* save level identification information */
14899
14900   setString(&snapshot_level_identifier, leveldir_current->identifier);
14901   snapshot_level_nr = level_nr;
14902
14903 #if 0
14904   ListNode *node = engine_snapshot_list;
14905   int num_bytes = 0;
14906
14907   while (node != NULL)
14908   {
14909     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14910
14911     node = node->next;
14912   }
14913
14914   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14915 #endif
14916 }
14917
14918 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
14919 {
14920   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
14921 }
14922
14923 void LoadEngineSnapshot()
14924 {
14925   ListNode *node = engine_snapshot_list;
14926
14927   if (engine_snapshot_list == NULL)
14928     return;
14929
14930   while (node != NULL)
14931   {
14932     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
14933
14934     node = node->next;
14935   }
14936
14937   /* restore special values from snapshot structure */
14938
14939   LoadEngineSnapshotValues_RND();
14940   LoadEngineSnapshotValues_EM();
14941 }
14942
14943 boolean CheckEngineSnapshot()
14944 {
14945   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14946           snapshot_level_nr == level_nr);
14947 }
14948
14949
14950 /* ---------- new game button stuff ---------------------------------------- */
14951
14952 /* graphic position values for game buttons */
14953 #define GAME_BUTTON_XSIZE       30
14954 #define GAME_BUTTON_YSIZE       30
14955 #define GAME_BUTTON_XPOS        5
14956 #define GAME_BUTTON_YPOS        215
14957 #define SOUND_BUTTON_XPOS       5
14958 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
14959
14960 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14961 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14962 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14963 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14964 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14965 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14966
14967 static struct
14968 {
14969   int *x, *y;
14970   int gd_x, gd_y;
14971   int gadget_id;
14972   char *infotext;
14973 } gamebutton_info[NUM_GAME_BUTTONS] =
14974 {
14975 #if 1
14976   {
14977     &game.button.stop.x,        &game.button.stop.y,
14978     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
14979     GAME_CTRL_ID_STOP,
14980     "stop game"
14981   },
14982   {
14983     &game.button.pause.x,       &game.button.pause.y,
14984     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
14985     GAME_CTRL_ID_PAUSE,
14986     "pause game"
14987   },
14988   {
14989     &game.button.play.x,        &game.button.play.y,
14990     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
14991     GAME_CTRL_ID_PLAY,
14992     "play game"
14993   },
14994   {
14995     &game.button.sound_music.x, &game.button.sound_music.y,
14996     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
14997     SOUND_CTRL_ID_MUSIC,
14998     "background music on/off"
14999   },
15000   {
15001     &game.button.sound_loops.x, &game.button.sound_loops.y,
15002     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15003     SOUND_CTRL_ID_LOOPS,
15004     "sound loops on/off"
15005   },
15006   {
15007     &game.button.sound_simple.x,&game.button.sound_simple.y,
15008     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15009     SOUND_CTRL_ID_SIMPLE,
15010     "normal sounds on/off"
15011   }
15012 #else
15013   {
15014     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15015     GAME_CTRL_ID_STOP,
15016     "stop game"
15017   },
15018   {
15019     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15020     GAME_CTRL_ID_PAUSE,
15021     "pause game"
15022   },
15023   {
15024     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15025     GAME_CTRL_ID_PLAY,
15026     "play game"
15027   },
15028   {
15029     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15030     SOUND_CTRL_ID_MUSIC,
15031     "background music on/off"
15032   },
15033   {
15034     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15035     SOUND_CTRL_ID_LOOPS,
15036     "sound loops on/off"
15037   },
15038   {
15039     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15040     SOUND_CTRL_ID_SIMPLE,
15041     "normal sounds on/off"
15042   }
15043 #endif
15044 };
15045
15046 void CreateGameButtons()
15047 {
15048   int i;
15049
15050   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15051   {
15052     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15053     struct GadgetInfo *gi;
15054     int button_type;
15055     boolean checked;
15056     unsigned long event_mask;
15057     int x, y;
15058     int gd_xoffset, gd_yoffset;
15059     int gd_x1, gd_x2, gd_y1, gd_y2;
15060     int id = i;
15061
15062     x = DX + *gamebutton_info[i].x;
15063     y = DY + *gamebutton_info[i].y;
15064     gd_xoffset = gamebutton_info[i].gd_x;
15065     gd_yoffset = gamebutton_info[i].gd_y;
15066     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15067     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15068
15069     if (id == GAME_CTRL_ID_STOP ||
15070         id == GAME_CTRL_ID_PAUSE ||
15071         id == GAME_CTRL_ID_PLAY)
15072     {
15073       button_type = GD_TYPE_NORMAL_BUTTON;
15074       checked = FALSE;
15075       event_mask = GD_EVENT_RELEASED;
15076       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15077       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15078     }
15079     else
15080     {
15081       button_type = GD_TYPE_CHECK_BUTTON;
15082       checked =
15083         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15084          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15085          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15086       event_mask = GD_EVENT_PRESSED;
15087       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
15088       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15089     }
15090
15091     gi = CreateGadget(GDI_CUSTOM_ID, id,
15092                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15093 #if 1
15094                       GDI_X, x,
15095                       GDI_Y, y,
15096 #else
15097                       GDI_X, DX + gd_xoffset,
15098                       GDI_Y, DY + gd_yoffset,
15099 #endif
15100                       GDI_WIDTH, GAME_BUTTON_XSIZE,
15101                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
15102                       GDI_TYPE, button_type,
15103                       GDI_STATE, GD_BUTTON_UNPRESSED,
15104                       GDI_CHECKED, checked,
15105                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15106                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15107                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15108                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15109                       GDI_EVENT_MASK, event_mask,
15110                       GDI_CALLBACK_ACTION, HandleGameButtons,
15111                       GDI_END);
15112
15113     if (gi == NULL)
15114       Error(ERR_EXIT, "cannot create gadget");
15115
15116     game_gadget[id] = gi;
15117   }
15118 }
15119
15120 void FreeGameButtons()
15121 {
15122   int i;
15123
15124   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15125     FreeGadget(game_gadget[i]);
15126 }
15127
15128 static void MapGameButtons()
15129 {
15130   int i;
15131
15132   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15133     MapGadget(game_gadget[i]);
15134 }
15135
15136 void UnmapGameButtons()
15137 {
15138   int i;
15139
15140   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15141     UnmapGadget(game_gadget[i]);
15142 }
15143
15144 static void HandleGameButtons(struct GadgetInfo *gi)
15145 {
15146   int id = gi->custom_id;
15147
15148   if (game_status != GAME_MODE_PLAYING)
15149     return;
15150
15151   switch (id)
15152   {
15153     case GAME_CTRL_ID_STOP:
15154       if (tape.playing)
15155         TapeStop();
15156       else
15157         RequestQuitGame(TRUE);
15158       break;
15159
15160     case GAME_CTRL_ID_PAUSE:
15161       if (options.network)
15162       {
15163 #if defined(NETWORK_AVALIABLE)
15164         if (tape.pausing)
15165           SendToServer_ContinuePlaying();
15166         else
15167           SendToServer_PausePlaying();
15168 #endif
15169       }
15170       else
15171         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15172       break;
15173
15174     case GAME_CTRL_ID_PLAY:
15175       if (tape.pausing)
15176       {
15177 #if defined(NETWORK_AVALIABLE)
15178         if (options.network)
15179           SendToServer_ContinuePlaying();
15180         else
15181 #endif
15182         {
15183           tape.pausing = FALSE;
15184           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15185         }
15186       }
15187       break;
15188
15189     case SOUND_CTRL_ID_MUSIC:
15190       if (setup.sound_music)
15191       { 
15192         setup.sound_music = FALSE;
15193         FadeMusic();
15194       }
15195       else if (audio.music_available)
15196       { 
15197         setup.sound = setup.sound_music = TRUE;
15198
15199         SetAudioMode(setup.sound);
15200
15201         PlayLevelMusic();
15202       }
15203       break;
15204
15205     case SOUND_CTRL_ID_LOOPS:
15206       if (setup.sound_loops)
15207         setup.sound_loops = FALSE;
15208       else if (audio.loops_available)
15209       {
15210         setup.sound = setup.sound_loops = TRUE;
15211         SetAudioMode(setup.sound);
15212       }
15213       break;
15214
15215     case SOUND_CTRL_ID_SIMPLE:
15216       if (setup.sound_simple)
15217         setup.sound_simple = FALSE;
15218       else if (audio.sound_available)
15219       {
15220         setup.sound = setup.sound_simple = TRUE;
15221         SetAudioMode(setup.sound);
15222       }
15223       break;
15224
15225     default:
15226       break;
15227   }
15228 }