3bb6aa59dde50c6492f5a797839b4fbdbc97267e
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 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 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR      FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE     FALSE
29
30 /* for DigField() */
31 #define DF_NO_PUSH              0
32 #define DF_DIG                  1
33 #define DF_SNAP                 2
34
35 /* for MovePlayer() */
36 #define MF_NO_ACTION            0
37 #define MF_MOVING               1
38 #define MF_ACTION               2
39
40 /* for ScrollPlayer() */
41 #define SCROLL_INIT             0
42 #define SCROLL_GO_ON            1
43
44 /* for Explode() */
45 #define EX_PHASE_START          0
46 #define EX_NO_EXPLOSION         0
47 #define EX_NORMAL               1
48 #define EX_CENTER               2
49 #define EX_BORDER               3
50
51 /* special positions in the game control window (relative to control window) */
52 #define XX_LEVEL                37
53 #define YY_LEVEL                20
54 #define XX_EMERALDS             29
55 #define YY_EMERALDS             54
56 #define XX_DYNAMITE             29
57 #define YY_DYNAMITE             89
58 #define XX_KEYS                 18
59 #define YY_KEYS                 123
60 #define XX_SCORE                15
61 #define YY_SCORE                159
62 #define XX_TIME                 29
63 #define YY_TIME                 194
64
65 /* special positions in the game control window (relative to main window) */
66 #define DX_LEVEL                (DX + XX_LEVEL)
67 #define DY_LEVEL                (DY + YY_LEVEL)
68 #define DX_EMERALDS             (DX + XX_EMERALDS)
69 #define DY_EMERALDS             (DY + YY_EMERALDS)
70 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
71 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
72 #define DX_KEYS                 (DX + XX_KEYS)
73 #define DY_KEYS                 (DY + YY_KEYS)
74 #define DX_SCORE                (DX + XX_SCORE)
75 #define DY_SCORE                (DY + YY_SCORE)
76 #define DX_TIME                 (DX + XX_TIME)
77 #define DY_TIME                 (DY + YY_TIME)
78
79 /* values for initial player move delay (initial delay counter value) */
80 #define INITIAL_MOVE_DELAY_OFF  -1
81 #define INITIAL_MOVE_DELAY_ON   0
82
83 /* values for player movement speed (which is in fact a delay value) */
84 #define MOVE_DELAY_NORMAL_SPEED 8
85 #define MOVE_DELAY_HIGH_SPEED   4
86
87 #define DOUBLE_MOVE_DELAY(x)    (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
88 #define HALVE_MOVE_DELAY(x)     (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
89 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY((p)->move_delay_value))
90 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
91
92 /* values for other actions */
93 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
94
95 #define INIT_GFX_RANDOM()       (SimpleRND(1000000))
96
97 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
98                                  RND(element_info[e].push_delay_random))
99 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
100                                  RND(element_info[e].move_delay_random))
101 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
102                                     (element_info[e].move_delay_random))
103
104 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition)             \
105                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
106                                         (condition) ||                  \
107                                         (DONT_COLLIDE_WITH(e) &&        \
108                                          IS_FREE_OR_PLAYER(x, y))))
109
110 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition)              \
111                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
112                                         (condition)))
113
114 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
115         ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
116
117 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y)                        \
118         ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
119
120 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y)                         \
121         ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID))
122
123 #define ENEMY_CAN_ENTER_FIELD(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
124
125 #define YAMYAM_CAN_ENTER_FIELD(x, y)                                    \
126                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
127                                         Feld[x][y] == EL_DIAMOND))
128
129 #define DARK_YAMYAM_CAN_ENTER_FIELD(x, y)                               \
130                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
131                                         IS_FOOD_DARK_YAMYAM(Feld[x][y])))
132
133 #define PACMAN_CAN_ENTER_FIELD(x, y)                                    \
134                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
135                                         IS_AMOEBOID(Feld[x][y])))
136
137 #define PIG_CAN_ENTER_FIELD(x, y)                                       \
138                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
139                                         IS_FOOD_PIG(Feld[x][y])))
140
141 #define PENGUIN_CAN_ENTER_FIELD(x, y)                                   \
142                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
143                                         IS_FOOD_PENGUIN(Feld[x][y]) ||  \
144                                         Feld[x][y] == EL_EXIT_OPEN ||   \
145                                         Feld[x][y] == EL_ACID))
146
147 #define MOLE_CAN_ENTER_FIELD(x, y, condition)                           \
148                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
149
150 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
151 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
152
153 /* game button identifiers */
154 #define GAME_CTRL_ID_STOP               0
155 #define GAME_CTRL_ID_PAUSE              1
156 #define GAME_CTRL_ID_PLAY               2
157 #define SOUND_CTRL_ID_MUSIC             3
158 #define SOUND_CTRL_ID_LOOPS             4
159 #define SOUND_CTRL_ID_SIMPLE            5
160
161 #define NUM_GAME_BUTTONS                6
162
163
164 /* forward declaration for internal use */
165
166 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
167 static boolean MovePlayer(struct PlayerInfo *, int, int);
168 static void ScrollPlayer(struct PlayerInfo *, int);
169 static void ScrollScreen(struct PlayerInfo *, int);
170
171 static void InitBeltMovement(void);
172 static void CloseAllOpenTimegates(void);
173 static void CheckGravityMovement(struct PlayerInfo *);
174 static void KillHeroUnlessProtected(int, int);
175
176 static void TestIfPlayerTouchesCustomElement(int, int);
177 static void TestIfElementTouchesCustomElement(int, int);
178
179 static void ChangeElement(int, int, int);
180 static boolean CheckTriggeredElementSideChange(int, int, int, int, int);
181 static boolean CheckTriggeredElementChange(int, int, int, int);
182 static boolean CheckElementSideChange(int, int, int, int, int, int);
183 static boolean CheckElementChange(int, int, int, int);
184
185 static void PlaySoundLevel(int, int, int);
186 static void PlaySoundLevelNearest(int, int, int);
187 static void PlaySoundLevelAction(int, int, int);
188 static void PlaySoundLevelElementAction(int, int, int, int);
189 static void PlaySoundLevelActionIfLoop(int, int, int);
190 static void StopSoundLevelActionIfLoop(int, int, int);
191
192 static void MapGameButtons();
193 static void HandleGameButtons(struct GadgetInfo *);
194
195 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
196
197
198 /* ------------------------------------------------------------------------- */
199 /* definition of elements that automatically change to other elements after  */
200 /* a specified time, eventually calling a function when changing             */
201 /* ------------------------------------------------------------------------- */
202
203 /* forward declaration for changer functions */
204 static void InitBuggyBase(int x, int y);
205 static void WarnBuggyBase(int x, int y);
206
207 static void InitTrap(int x, int y);
208 static void ActivateTrap(int x, int y);
209 static void ChangeActiveTrap(int x, int y);
210
211 static void InitRobotWheel(int x, int y);
212 static void RunRobotWheel(int x, int y);
213 static void StopRobotWheel(int x, int y);
214
215 static void InitTimegateWheel(int x, int y);
216 static void RunTimegateWheel(int x, int y);
217
218 struct ChangingElementInfo
219 {
220   int element;
221   int target_element;
222   int change_delay;
223   void (*pre_change_function)(int x, int y);
224   void (*change_function)(int x, int y);
225   void (*post_change_function)(int x, int y);
226 };
227
228 static struct ChangingElementInfo change_delay_list[] =
229 {
230   {
231     EL_NUT_BREAKING,
232     EL_EMERALD,
233     6,
234     NULL,
235     NULL,
236     NULL
237   },
238   {
239     EL_PEARL_BREAKING,
240     EL_EMPTY,
241     8,
242     NULL,
243     NULL,
244     NULL
245   },
246   {
247     EL_EXIT_OPENING,
248     EL_EXIT_OPEN,
249     29,
250     NULL,
251     NULL,
252     NULL
253   },
254   {
255     EL_EXIT_CLOSING,
256     EL_EXIT_CLOSED,
257     29,
258     NULL,
259     NULL,
260     NULL
261   },
262   {
263     EL_SP_EXIT_OPENING,
264     EL_SP_EXIT_OPEN,
265     29,
266     NULL,
267     NULL,
268     NULL
269   },
270   {
271     EL_SP_EXIT_CLOSING,
272     EL_SP_EXIT_CLOSED,
273     29,
274     NULL,
275     NULL,
276     NULL
277   },
278   {
279     EL_SWITCHGATE_OPENING,
280     EL_SWITCHGATE_OPEN,
281     29,
282     NULL,
283     NULL,
284     NULL
285   },
286   {
287     EL_SWITCHGATE_CLOSING,
288     EL_SWITCHGATE_CLOSED,
289     29,
290     NULL,
291     NULL,
292     NULL
293   },
294   {
295     EL_TIMEGATE_OPENING,
296     EL_TIMEGATE_OPEN,
297     29,
298     NULL,
299     NULL,
300     NULL
301   },
302   {
303     EL_TIMEGATE_CLOSING,
304     EL_TIMEGATE_CLOSED,
305     29,
306     NULL,
307     NULL,
308     NULL
309   },
310
311   {
312     EL_ACID_SPLASH_LEFT,
313     EL_EMPTY,
314     8,
315     NULL,
316     NULL,
317     NULL
318   },
319   {
320     EL_ACID_SPLASH_RIGHT,
321     EL_EMPTY,
322     8,
323     NULL,
324     NULL,
325     NULL
326   },
327   {
328     EL_SP_BUGGY_BASE,
329     EL_SP_BUGGY_BASE_ACTIVATING,
330     0,
331     InitBuggyBase,
332     NULL,
333     NULL
334   },
335   {
336     EL_SP_BUGGY_BASE_ACTIVATING,
337     EL_SP_BUGGY_BASE_ACTIVE,
338     0,
339     InitBuggyBase,
340     NULL,
341     NULL
342   },
343   {
344     EL_SP_BUGGY_BASE_ACTIVE,
345     EL_SP_BUGGY_BASE,
346     0,
347     InitBuggyBase,
348     WarnBuggyBase,
349     NULL
350   },
351   {
352     EL_TRAP,
353     EL_TRAP_ACTIVE,
354     0,
355     InitTrap,
356     NULL,
357     ActivateTrap
358   },
359   {
360     EL_TRAP_ACTIVE,
361     EL_TRAP,
362     31,
363     NULL,
364     ChangeActiveTrap,
365     NULL
366   },
367   {
368     EL_ROBOT_WHEEL_ACTIVE,
369     EL_ROBOT_WHEEL,
370     0,
371     InitRobotWheel,
372     RunRobotWheel,
373     StopRobotWheel
374   },
375   {
376     EL_TIMEGATE_SWITCH_ACTIVE,
377     EL_TIMEGATE_SWITCH,
378     0,
379     InitTimegateWheel,
380     RunTimegateWheel,
381     NULL
382   },
383
384   {
385     EL_UNDEFINED,
386     EL_UNDEFINED,
387     -1,
388     NULL,
389     NULL,
390     NULL
391   }
392 };
393
394 struct
395 {
396   int element;
397   int push_delay_fixed, push_delay_random;
398 }
399 push_delay_list[] =
400 {
401   { EL_SPRING,                  0, 0 },
402   { EL_BALLOON,                 0, 0 },
403
404   { EL_SOKOBAN_OBJECT,          2, 0 },
405   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
406   { EL_SATELLITE,               2, 0 },
407   { EL_SP_DISK_YELLOW,          2, 0 },
408
409   { EL_UNDEFINED,               0, 0 },
410 };
411
412 struct
413 {
414   int element;
415   int move_stepsize;
416 }
417 move_stepsize_list[] =
418 {
419   { EL_AMOEBA_DROP,             2 },
420   { EL_AMOEBA_DROPPING,         2 },
421   { EL_QUICKSAND_FILLING,       1 },
422   { EL_QUICKSAND_EMPTYING,      1 },
423   { EL_MAGIC_WALL_FILLING,      2 },
424   { EL_BD_MAGIC_WALL_FILLING,   2 },
425   { EL_MAGIC_WALL_EMPTYING,     2 },
426   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
427
428   { EL_UNDEFINED,               0 },
429 };
430
431 struct
432 {
433   int element;
434   int count;
435 }
436 collect_count_list[] =
437 {
438   { EL_EMERALD,                 1 },
439   { EL_BD_DIAMOND,              1 },
440   { EL_EMERALD_YELLOW,          1 },
441   { EL_EMERALD_RED,             1 },
442   { EL_EMERALD_PURPLE,          1 },
443   { EL_DIAMOND,                 3 },
444   { EL_SP_INFOTRON,             1 },
445   { EL_PEARL,                   5 },
446   { EL_CRYSTAL,                 8 },
447
448   { EL_UNDEFINED,               0 },
449 };
450
451 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
452
453 #define IS_AUTO_CHANGING(e)     (element_info[e].change_events & \
454                                  CH_EVENT_BIT(CE_DELAY))
455 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
456 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
457                                  IS_JUST_CHANGING(x, y))
458
459 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
460
461
462 void GetPlayerConfig()
463 {
464   if (!audio.sound_available)
465     setup.sound_simple = FALSE;
466
467   if (!audio.loops_available)
468     setup.sound_loops = FALSE;
469
470   if (!audio.music_available)
471     setup.sound_music = FALSE;
472
473   if (!video.fullscreen_available)
474     setup.fullscreen = FALSE;
475
476   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
477
478   SetAudioMode(setup.sound);
479   InitJoysticks();
480 }
481
482 static int getBeltNrFromBeltElement(int element)
483 {
484   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
485           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
486           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
487 }
488
489 static int getBeltNrFromBeltActiveElement(int element)
490 {
491   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
492           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
493           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
494 }
495
496 static int getBeltNrFromBeltSwitchElement(int element)
497 {
498   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
499           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
500           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
501 }
502
503 static int getBeltDirNrFromBeltSwitchElement(int element)
504 {
505   static int belt_base_element[4] =
506   {
507     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
508     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
509     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
510     EL_CONVEYOR_BELT_4_SWITCH_LEFT
511   };
512
513   int belt_nr = getBeltNrFromBeltSwitchElement(element);
514   int belt_dir_nr = element - belt_base_element[belt_nr];
515
516   return (belt_dir_nr % 3);
517 }
518
519 static int getBeltDirFromBeltSwitchElement(int element)
520 {
521   static int belt_move_dir[3] =
522   {
523     MV_LEFT,
524     MV_NO_MOVING,
525     MV_RIGHT
526   };
527
528   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
529
530   return belt_move_dir[belt_dir_nr];
531 }
532
533 static void InitPlayerField(int x, int y, int element, boolean init_game)
534 {
535   if (element == EL_SP_MURPHY)
536   {
537     if (init_game)
538     {
539       if (stored_player[0].present)
540       {
541         Feld[x][y] = EL_SP_MURPHY_CLONE;
542
543         return;
544       }
545       else
546       {
547         stored_player[0].use_murphy_graphic = TRUE;
548       }
549
550       Feld[x][y] = EL_PLAYER_1;
551     }
552   }
553
554   if (init_game)
555   {
556     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
557     int jx = player->jx, jy = player->jy;
558
559     player->present = TRUE;
560
561     if (!options.network || player->connected)
562     {
563       player->active = TRUE;
564
565       /* remove potentially duplicate players */
566       if (StorePlayer[jx][jy] == Feld[x][y])
567         StorePlayer[jx][jy] = 0;
568
569       StorePlayer[x][y] = Feld[x][y];
570
571       if (options.debug)
572       {
573         printf("Player %d activated.\n", player->element_nr);
574         printf("[Local player is %d and currently %s.]\n",
575                local_player->element_nr,
576                local_player->active ? "active" : "not active");
577       }
578     }
579
580     Feld[x][y] = EL_EMPTY;
581     player->jx = player->last_jx = x;
582     player->jy = player->last_jy = y;
583   }
584 }
585
586 static void InitField(int x, int y, boolean init_game)
587 {
588   int element = Feld[x][y];
589
590   switch (element)
591   {
592     case EL_SP_MURPHY:
593     case EL_PLAYER_1:
594     case EL_PLAYER_2:
595     case EL_PLAYER_3:
596     case EL_PLAYER_4:
597       InitPlayerField(x, y, element, init_game);
598       break;
599
600     case EL_STONEBLOCK:
601       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
602         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
603       else if (x > 0 && Feld[x-1][y] == EL_ACID)
604         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
605       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
606         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
607       else if (y > 0 && Feld[x][y-1] == EL_ACID)
608         Feld[x][y] = EL_ACID_POOL_BOTTOM;
609       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
610         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
611       break;
612
613     case EL_BUG_RIGHT:
614     case EL_BUG_UP:
615     case EL_BUG_LEFT:
616     case EL_BUG_DOWN:
617     case EL_BUG:
618     case EL_SPACESHIP_RIGHT:
619     case EL_SPACESHIP_UP:
620     case EL_SPACESHIP_LEFT:
621     case EL_SPACESHIP_DOWN:
622     case EL_SPACESHIP:
623     case EL_BD_BUTTERFLY_RIGHT:
624     case EL_BD_BUTTERFLY_UP:
625     case EL_BD_BUTTERFLY_LEFT:
626     case EL_BD_BUTTERFLY_DOWN:
627     case EL_BD_BUTTERFLY:
628     case EL_BD_FIREFLY_RIGHT:
629     case EL_BD_FIREFLY_UP:
630     case EL_BD_FIREFLY_LEFT:
631     case EL_BD_FIREFLY_DOWN:
632     case EL_BD_FIREFLY:
633     case EL_PACMAN_RIGHT:
634     case EL_PACMAN_UP:
635     case EL_PACMAN_LEFT:
636     case EL_PACMAN_DOWN:
637     case EL_YAMYAM:
638     case EL_DARK_YAMYAM:
639     case EL_ROBOT:
640     case EL_PACMAN:
641     case EL_SP_SNIKSNAK:
642     case EL_SP_ELECTRON:
643     case EL_MOLE_LEFT:
644     case EL_MOLE_RIGHT:
645     case EL_MOLE_UP:
646     case EL_MOLE_DOWN:
647     case EL_MOLE:
648       InitMovDir(x, y);
649       break;
650
651     case EL_AMOEBA_FULL:
652     case EL_BD_AMOEBA:
653       InitAmoebaNr(x, y);
654       break;
655
656     case EL_AMOEBA_DROP:
657       if (y == lev_fieldy - 1)
658       {
659         Feld[x][y] = EL_AMOEBA_GROWING;
660         Store[x][y] = EL_AMOEBA_WET;
661       }
662       break;
663
664     case EL_DYNAMITE_ACTIVE:
665       MovDelay[x][y] = 96;
666       break;
667
668     case EL_LAMP:
669       local_player->lights_still_needed++;
670       break;
671
672     case EL_SOKOBAN_FIELD_EMPTY:
673       local_player->sokobanfields_still_needed++;
674       break;
675
676     case EL_PENGUIN:
677       local_player->friends_still_needed++;
678       break;
679
680     case EL_PIG:
681     case EL_DRAGON:
682       MovDir[x][y] = 1 << RND(4);
683       break;
684
685 #if 0
686     case EL_SP_EMPTY:
687       Feld[x][y] = EL_EMPTY;
688       break;
689 #endif
690
691 #if 0
692     case EL_EM_KEY_1_FILE:
693       Feld[x][y] = EL_EM_KEY_1;
694       break;
695     case EL_EM_KEY_2_FILE:
696       Feld[x][y] = EL_EM_KEY_2;
697       break;
698     case EL_EM_KEY_3_FILE:
699       Feld[x][y] = EL_EM_KEY_3;
700       break;
701     case EL_EM_KEY_4_FILE:
702       Feld[x][y] = EL_EM_KEY_4;
703       break;
704 #endif
705
706     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
707     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
708     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
709     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
710     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
711     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
712     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
713     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
714     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
715     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
716     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
717     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
718       if (init_game)
719       {
720         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
721         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
722         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
723
724         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
725         {
726           game.belt_dir[belt_nr] = belt_dir;
727           game.belt_dir_nr[belt_nr] = belt_dir_nr;
728         }
729         else    /* more than one switch -- set it like the first switch */
730         {
731           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
732         }
733       }
734       break;
735
736     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
737       if (init_game)
738         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
739       break;
740
741     case EL_LIGHT_SWITCH_ACTIVE:
742       if (init_game)
743         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
744       break;
745
746     default:
747       if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
748         InitMovDir(x, y);
749       break;
750   }
751 }
752
753 void DrawGameDoorValues()
754 {
755   int i, j;
756
757   for (i=0; i<MAX_PLAYERS; i++)
758     for (j=0; j<4; j++)
759       if (stored_player[i].key[j])
760         DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
761                            el2edimg(EL_KEY_1 + j));
762
763   DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
764            int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
765   DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
766            int2str(local_player->inventory_size, 3), FONT_TEXT_2);
767   DrawText(DX + XX_SCORE, DY + YY_SCORE,
768            int2str(local_player->score, 5), FONT_TEXT_2);
769   DrawText(DX + XX_TIME, DY + YY_TIME,
770            int2str(TimeLeft, 3), FONT_TEXT_2);
771 }
772
773
774 /*
775   =============================================================================
776   InitGameEngine()
777   -----------------------------------------------------------------------------
778   initialize game engine due to level / tape version number
779   =============================================================================
780 */
781
782 static void InitGameEngine()
783 {
784   int i, j, k;
785
786   /* set game engine from tape file when re-playing, else from level file */
787   game.engine_version = (tape.playing ? tape.engine_version :
788                          level.game_version);
789
790   /* dynamically adjust element properties according to game engine version */
791   InitElementPropertiesEngine(game.engine_version);
792
793 #if 0
794   printf("level %d: level version == %06d\n", level_nr, level.game_version);
795   printf("          tape version == %06d [%s] [file: %06d]\n",
796          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
797          tape.file_version);
798   printf("       => game.engine_version == %06d\n", game.engine_version);
799 #endif
800
801   /* ---------- initialize player's initial move delay --------------------- */
802
803   /* dynamically adjust player properties according to game engine version */
804   game.initial_move_delay =
805     (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
806      INITIAL_MOVE_DELAY_OFF);
807
808   /* dynamically adjust player properties according to level information */
809   game.initial_move_delay_value =
810     (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
811
812   /* ---------- initialize player's initial push delay --------------------- */
813
814   /* dynamically adjust player properties according to game engine version */
815   game.initial_push_delay_value =
816     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
817
818   /* ---------- initialize changing elements ------------------------------- */
819
820   /* initialize changing elements information */
821   for (i=0; i < MAX_NUM_ELEMENTS; i++)
822   {
823     struct ElementInfo *ei = &element_info[i];
824
825     /* this pointer might have been changed in the level editor */
826     ei->change = &ei->change_page[0];
827
828     if (!IS_CUSTOM_ELEMENT(i))
829     {
830       ei->change->target_element = EL_EMPTY_SPACE;
831       ei->change->delay_fixed = 0;
832       ei->change->delay_random = 0;
833       ei->change->delay_frames = 1;
834     }
835
836     ei->change_events = CE_BITMASK_DEFAULT;
837     for (j=0; j < NUM_CHANGE_EVENTS; j++)
838     {
839       ei->event_page_nr[j] = 0;
840       ei->event_page[j] = &ei->change_page[0];
841     }
842   }
843
844   /* add changing elements from pre-defined list */
845   for (i=0; change_delay_list[i].element != EL_UNDEFINED; i++)
846   {
847     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
848     struct ElementInfo *ei = &element_info[ch_delay->element];
849
850     ei->change->target_element       = ch_delay->target_element;
851     ei->change->delay_fixed          = ch_delay->change_delay;
852
853     ei->change->pre_change_function  = ch_delay->pre_change_function;
854     ei->change->change_function      = ch_delay->change_function;
855     ei->change->post_change_function = ch_delay->post_change_function;
856
857     ei->change_events |= CH_EVENT_BIT(CE_DELAY);
858   }
859
860 #if 1
861   /* add change events from custom element configuration */
862   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
863   {
864     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
865
866     for (j=0; j < ei->num_change_pages; j++)
867     {
868       if (!ei->change_page[j].can_change)
869         continue;
870
871       for (k=0; k < NUM_CHANGE_EVENTS; k++)
872       {
873         /* only add event page for the first page found with this event */
874         if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
875             !(ei->change_events & CH_EVENT_BIT(k)))
876         {
877           ei->change_events |= CH_EVENT_BIT(k);
878           ei->event_page_nr[k] = j;
879           ei->event_page[k] = &ei->change_page[j];
880         }
881       }
882     }
883   }
884
885 #else
886
887   /* add change events from custom element configuration */
888   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
889   {
890     int element = EL_CUSTOM_START + i;
891
892     /* only add custom elements that change after fixed/random frame delay */
893     if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
894       element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
895   }
896 #endif
897
898   /* ---------- initialize trigger events ---------------------------------- */
899
900   /* initialize trigger events information */
901   for (i=0; i<MAX_NUM_ELEMENTS; i++)
902     trigger_events[i] = EP_BITMASK_DEFAULT;
903
904 #if 1
905   /* add trigger events from element change event properties */
906   for (i=0; i<MAX_NUM_ELEMENTS; i++)
907   {
908     struct ElementInfo *ei = &element_info[i];
909
910     for (j=0; j < ei->num_change_pages; j++)
911     {
912       if (!ei->change_page[j].can_change)
913         continue;
914
915       if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
916       {
917         int trigger_element = ei->change_page[j].trigger_element;
918
919         trigger_events[trigger_element] |= ei->change_page[j].events;
920       }
921     }
922   }
923 #else
924   /* add trigger events from element change event properties */
925   for (i=0; i<MAX_NUM_ELEMENTS; i++)
926     if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
927       trigger_events[element_info[i].change->trigger_element] |=
928         element_info[i].change->events;
929 #endif
930
931   /* ---------- initialize push delay -------------------------------------- */
932
933   /* initialize push delay values to default */
934   for (i=0; i<MAX_NUM_ELEMENTS; i++)
935   {
936     if (!IS_CUSTOM_ELEMENT(i))
937     {
938       element_info[i].push_delay_fixed  = game.default_push_delay_fixed;
939       element_info[i].push_delay_random = game.default_push_delay_random;
940     }
941   }
942
943   /* set push delay value for certain elements from pre-defined list */
944   for (i=0; push_delay_list[i].element != EL_UNDEFINED; i++)
945   {
946     int e = push_delay_list[i].element;
947
948     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
949     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
950   }
951
952   /* ---------- initialize move stepsize ----------------------------------- */
953
954   /* initialize move stepsize values to default */
955   for (i=0; i<MAX_NUM_ELEMENTS; i++)
956     if (!IS_CUSTOM_ELEMENT(i))
957       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
958
959   /* set move stepsize value for certain elements from pre-defined list */
960   for (i=0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
961   {
962     int e = move_stepsize_list[i].element;
963
964     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
965   }
966
967   /* ---------- initialize gem count --------------------------------------- */
968
969   /* initialize gem count values for each element */
970   for (i=0; i<MAX_NUM_ELEMENTS; i++)
971     if (!IS_CUSTOM_ELEMENT(i))
972       element_info[i].collect_count = 0;
973
974   /* add gem count values for all elements from pre-defined list */
975   for (i=0; collect_count_list[i].element != EL_UNDEFINED; i++)
976     element_info[collect_count_list[i].element].collect_count =
977       collect_count_list[i].count;
978 }
979
980
981 /*
982   =============================================================================
983   InitGame()
984   -----------------------------------------------------------------------------
985   initialize and start new game
986   =============================================================================
987 */
988
989 void InitGame()
990 {
991   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
992   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
993   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
994   int i, j, x, y;
995
996   InitGameEngine();
997
998 #if 0
999 #if DEBUG
1000 #if USE_NEW_AMOEBA_CODE
1001   printf("Using new amoeba code.\n");
1002 #else
1003   printf("Using old amoeba code.\n");
1004 #endif
1005 #endif
1006 #endif
1007
1008   /* don't play tapes over network */
1009   network_playing = (options.network && !tape.playing);
1010
1011   for (i=0; i<MAX_PLAYERS; i++)
1012   {
1013     struct PlayerInfo *player = &stored_player[i];
1014
1015     player->index_nr = i;
1016     player->element_nr = EL_PLAYER_1 + i;
1017
1018     player->present = FALSE;
1019     player->active = FALSE;
1020
1021     player->action = 0;
1022     player->effective_action = 0;
1023     player->programmed_action = 0;
1024
1025     player->score = 0;
1026     player->gems_still_needed = level.gems_needed;
1027     player->sokobanfields_still_needed = 0;
1028     player->lights_still_needed = 0;
1029     player->friends_still_needed = 0;
1030
1031     for (j=0; j<4; j++)
1032       player->key[j] = FALSE;
1033
1034     player->dynabomb_count = 0;
1035     player->dynabomb_size = 1;
1036     player->dynabombs_left = 0;
1037     player->dynabomb_xl = FALSE;
1038
1039     player->MovDir = MV_NO_MOVING;
1040     player->MovPos = 0;
1041     player->GfxPos = 0;
1042     player->GfxDir = MV_NO_MOVING;
1043     player->GfxAction = ACTION_DEFAULT;
1044     player->Frame = 0;
1045     player->StepFrame = 0;
1046
1047     player->use_murphy_graphic = FALSE;
1048
1049     player->actual_frame_counter = 0;
1050
1051     player->last_move_dir = MV_NO_MOVING;
1052
1053     player->is_waiting = FALSE;
1054     player->is_moving = FALSE;
1055     player->is_digging = FALSE;
1056     player->is_snapping = FALSE;
1057     player->is_collecting = FALSE;
1058     player->is_pushing = FALSE;
1059     player->is_switching = FALSE;
1060
1061     player->switch_x = -1;
1062     player->switch_y = -1;
1063
1064     player->show_envelope = 0;
1065
1066     player->move_delay       = game.initial_move_delay;
1067     player->move_delay_value = game.initial_move_delay_value;
1068
1069     player->push_delay = 0;
1070     player->push_delay_value = game.initial_push_delay_value;
1071
1072     player->last_jx = player->last_jy = 0;
1073     player->jx = player->jy = 0;
1074
1075     player->shield_normal_time_left = 0;
1076     player->shield_deadly_time_left = 0;
1077
1078     player->inventory_size = 0;
1079
1080     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1081     SnapField(player, 0, 0);
1082
1083     player->LevelSolved = FALSE;
1084     player->GameOver = FALSE;
1085   }
1086
1087   network_player_action_received = FALSE;
1088
1089 #if defined(PLATFORM_UNIX)
1090   /* initial null action */
1091   if (network_playing)
1092     SendToServer_MovePlayer(MV_NO_MOVING);
1093 #endif
1094
1095   ZX = ZY = -1;
1096
1097   FrameCounter = 0;
1098   TimeFrames = 0;
1099   TimePlayed = 0;
1100   TimeLeft = level.time;
1101
1102   ScreenMovDir = MV_NO_MOVING;
1103   ScreenMovPos = 0;
1104   ScreenGfxPos = 0;
1105
1106   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
1107
1108   AllPlayersGone = FALSE;
1109
1110   game.yamyam_content_nr = 0;
1111   game.magic_wall_active = FALSE;
1112   game.magic_wall_time_left = 0;
1113   game.light_time_left = 0;
1114   game.timegate_time_left = 0;
1115   game.switchgate_pos = 0;
1116   game.balloon_dir = MV_NO_MOVING;
1117   game.gravity = level.initial_gravity;
1118   game.explosions_delayed = TRUE;
1119
1120   game.envelope_active = FALSE;
1121
1122   for (i=0; i<4; i++)
1123   {
1124     game.belt_dir[i] = MV_NO_MOVING;
1125     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
1126   }
1127
1128   for (i=0; i<MAX_NUM_AMOEBA; i++)
1129     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1130
1131   for (x=0; x<lev_fieldx; x++)
1132   {
1133     for (y=0; y<lev_fieldy; y++)
1134     {
1135       Feld[x][y] = level.field[x][y];
1136       MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1137       ChangeDelay[x][y] = 0;
1138       ChangePage[x][y] = -1;
1139       Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1140       AmoebaNr[x][y] = 0;
1141       WasJustMoving[x][y] = 0;
1142       WasJustFalling[x][y] = 0;
1143       Stop[x][y] = FALSE;
1144       Pushed[x][y] = FALSE;
1145
1146       Changed[x][y] = CE_BITMASK_DEFAULT;
1147       ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1148
1149       ExplodePhase[x][y] = 0;
1150       ExplodeField[x][y] = EX_NO_EXPLOSION;
1151
1152       GfxFrame[x][y] = 0;
1153       GfxAction[x][y] = ACTION_DEFAULT;
1154       GfxRandom[x][y] = INIT_GFX_RANDOM();
1155       GfxElement[x][y] = EL_UNDEFINED;
1156     }
1157   }
1158
1159   for(y=0; y<lev_fieldy; y++)
1160   {
1161     for(x=0; x<lev_fieldx; x++)
1162     {
1163       if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1164         emulate_bd = FALSE;
1165       if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1166         emulate_sb = FALSE;
1167       if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1168         emulate_sp = FALSE;
1169
1170       InitField(x, y, TRUE);
1171     }
1172   }
1173
1174   InitBeltMovement();
1175
1176   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1177                     emulate_sb ? EMU_SOKOBAN :
1178                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1179
1180   /* correct non-moving belts to start moving left */
1181   for (i=0; i<4; i++)
1182     if (game.belt_dir[i] == MV_NO_MOVING)
1183       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
1184
1185   /* check if any connected player was not found in playfield */
1186   for (i=0; i<MAX_PLAYERS; i++)
1187   {
1188     struct PlayerInfo *player = &stored_player[i];
1189
1190     if (player->connected && !player->present)
1191     {
1192       for (j=0; j<MAX_PLAYERS; j++)
1193       {
1194         struct PlayerInfo *some_player = &stored_player[j];
1195         int jx = some_player->jx, jy = some_player->jy;
1196
1197         /* assign first free player found that is present in the playfield */
1198         if (some_player->present && !some_player->connected)
1199         {
1200           player->present = TRUE;
1201           player->active = TRUE;
1202           some_player->present = FALSE;
1203
1204           StorePlayer[jx][jy] = player->element_nr;
1205           player->jx = player->last_jx = jx;
1206           player->jy = player->last_jy = jy;
1207
1208           break;
1209         }
1210       }
1211     }
1212   }
1213
1214   if (tape.playing)
1215   {
1216     /* when playing a tape, eliminate all players who do not participate */
1217
1218     for (i=0; i<MAX_PLAYERS; i++)
1219     {
1220       if (stored_player[i].active && !tape.player_participates[i])
1221       {
1222         struct PlayerInfo *player = &stored_player[i];
1223         int jx = player->jx, jy = player->jy;
1224
1225         player->active = FALSE;
1226         StorePlayer[jx][jy] = 0;
1227         Feld[jx][jy] = EL_EMPTY;
1228       }
1229     }
1230   }
1231   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
1232   {
1233     /* when in single player mode, eliminate all but the first active player */
1234
1235     for (i=0; i<MAX_PLAYERS; i++)
1236     {
1237       if (stored_player[i].active)
1238       {
1239         for (j=i+1; j<MAX_PLAYERS; j++)
1240         {
1241           if (stored_player[j].active)
1242           {
1243             struct PlayerInfo *player = &stored_player[j];
1244             int jx = player->jx, jy = player->jy;
1245
1246             player->active = FALSE;
1247             StorePlayer[jx][jy] = 0;
1248             Feld[jx][jy] = EL_EMPTY;
1249           }
1250         }
1251       }
1252     }
1253   }
1254
1255   /* when recording the game, store which players take part in the game */
1256   if (tape.recording)
1257   {
1258     for (i=0; i<MAX_PLAYERS; i++)
1259       if (stored_player[i].active)
1260         tape.player_participates[i] = TRUE;
1261   }
1262
1263   if (options.debug)
1264   {
1265     for (i=0; i<MAX_PLAYERS; i++)
1266     {
1267       struct PlayerInfo *player = &stored_player[i];
1268
1269       printf("Player %d: present == %d, connected == %d, active == %d.\n",
1270              i+1,
1271              player->present,
1272              player->connected,
1273              player->active);
1274       if (local_player == player)
1275         printf("Player  %d is local player.\n", i+1);
1276     }
1277   }
1278
1279   if (BorderElement == EL_EMPTY)
1280   {
1281     SBX_Left = 0;
1282     SBX_Right = lev_fieldx - SCR_FIELDX;
1283     SBY_Upper = 0;
1284     SBY_Lower = lev_fieldy - SCR_FIELDY;
1285   }
1286   else
1287   {
1288     SBX_Left = -1;
1289     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1290     SBY_Upper = -1;
1291     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1292   }
1293
1294   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1295     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1296
1297   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1298     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1299
1300   /* if local player not found, look for custom element that might create
1301      the player (make some assumptions about the right custom element) */
1302   if (!local_player->present)
1303   {
1304     int start_x = 0, start_y = 0;
1305     int found_rating = 0;
1306     int found_element = EL_UNDEFINED;
1307
1308     for(y=0; y < lev_fieldy; y++) for(x=0; x < lev_fieldx; x++)
1309     {
1310       int element = Feld[x][y];
1311       int content;
1312       int xx, yy;
1313       boolean is_player;
1314
1315       if (!IS_CUSTOM_ELEMENT(element))
1316         continue;
1317
1318       if (CAN_CHANGE(element))
1319       {
1320         for (i=0; i < element_info[element].num_change_pages; i++)
1321         {
1322           content = element_info[element].change_page[i].target_element;
1323           is_player = ELEM_IS_PLAYER(content);
1324
1325           if (is_player && (found_rating < 3 || element < found_element))
1326           {
1327             start_x = x;
1328             start_y = y;
1329
1330             found_rating = 3;
1331             found_element = element;
1332           }
1333         }
1334       }
1335
1336       for(yy=0; yy < 3; yy++) for(xx=0; xx < 3; xx++)
1337       {
1338         content = element_info[element].content[xx][yy];
1339         is_player = ELEM_IS_PLAYER(content);
1340
1341         if (is_player && (found_rating < 2 || element < found_element))
1342         {
1343           start_x = x + xx - 1;
1344           start_y = y + yy - 1;
1345
1346           found_rating = 2;
1347           found_element = element;
1348         }
1349
1350         if (!CAN_CHANGE(element))
1351           continue;
1352
1353         for (i=0; i < element_info[element].num_change_pages; i++)
1354         {
1355           content = element_info[element].change_page[i].content[xx][yy];
1356           is_player = ELEM_IS_PLAYER(content);
1357
1358           if (is_player && (found_rating < 1 || element < found_element))
1359           {
1360             start_x = x + xx - 1;
1361             start_y = y + yy - 1;
1362
1363             found_rating = 1;
1364             found_element = element;
1365           }
1366         }
1367       }
1368     }
1369
1370     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
1371                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1372                 start_x - MIDPOSX);
1373
1374     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1375                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1376                 start_y - MIDPOSY);
1377   }
1378   else
1379   {
1380 #if 1
1381     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
1382                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1383                 local_player->jx - MIDPOSX);
1384
1385     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1386                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1387                 local_player->jy - MIDPOSY);
1388 #else
1389     scroll_x = SBX_Left;
1390     scroll_y = SBY_Upper;
1391     if (local_player->jx >= SBX_Left + MIDPOSX)
1392       scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1393                   local_player->jx - MIDPOSX :
1394                   SBX_Right);
1395     if (local_player->jy >= SBY_Upper + MIDPOSY)
1396       scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1397                   local_player->jy - MIDPOSY :
1398                   SBY_Lower);
1399 #endif
1400   }
1401
1402   CloseDoor(DOOR_CLOSE_1);
1403
1404   DrawLevel();
1405   DrawAllPlayers();
1406
1407   /* after drawing the level, correct some elements */
1408   if (game.timegate_time_left == 0)
1409     CloseAllOpenTimegates();
1410
1411   if (setup.soft_scrolling)
1412     BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1413
1414   redraw_mask |= REDRAW_FROM_BACKBUFFER;
1415   FadeToFront();
1416
1417   /* copy default game door content to main double buffer */
1418   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1419              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1420
1421   if (level_nr < 100)
1422     DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1423   else
1424   {
1425     DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1426                 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1427     BlitBitmap(drawto, drawto,
1428                DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1429                getFontWidth(FONT_LEVEL_NUMBER) * 3,
1430                getFontHeight(FONT_LEVEL_NUMBER) - 1,
1431                DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1432   }
1433
1434   DrawGameDoorValues();
1435
1436   UnmapGameButtons();
1437   UnmapTapeButtons();
1438   game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1439   game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1440   game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1441   MapGameButtons();
1442   MapTapeButtons();
1443
1444   /* copy actual game door content to door double buffer for OpenDoor() */
1445   BlitBitmap(drawto, bitmap_db_door,
1446              DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1447
1448   OpenDoor(DOOR_OPEN_ALL);
1449
1450   PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1451   if (setup.sound_music)
1452     PlayMusic(level_nr);
1453
1454   KeyboardAutoRepeatOffUnlessAutoplay();
1455
1456   if (options.debug)
1457   {
1458     for (i=0; i<4; i++)
1459       printf("Player %d %sactive.\n",
1460              i + 1, (stored_player[i].active ? "" : "not "));
1461   }
1462 }
1463
1464 void InitMovDir(int x, int y)
1465 {
1466   int i, element = Feld[x][y];
1467   static int xy[4][2] =
1468   {
1469     {  0, +1 },
1470     { +1,  0 },
1471     {  0, -1 },
1472     { -1,  0 }
1473   };
1474   static int direction[3][4] =
1475   {
1476     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
1477     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
1478     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
1479   };
1480
1481   switch(element)
1482   {
1483     case EL_BUG_RIGHT:
1484     case EL_BUG_UP:
1485     case EL_BUG_LEFT:
1486     case EL_BUG_DOWN:
1487       Feld[x][y] = EL_BUG;
1488       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1489       break;
1490
1491     case EL_SPACESHIP_RIGHT:
1492     case EL_SPACESHIP_UP:
1493     case EL_SPACESHIP_LEFT:
1494     case EL_SPACESHIP_DOWN:
1495       Feld[x][y] = EL_SPACESHIP;
1496       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1497       break;
1498
1499     case EL_BD_BUTTERFLY_RIGHT:
1500     case EL_BD_BUTTERFLY_UP:
1501     case EL_BD_BUTTERFLY_LEFT:
1502     case EL_BD_BUTTERFLY_DOWN:
1503       Feld[x][y] = EL_BD_BUTTERFLY;
1504       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1505       break;
1506
1507     case EL_BD_FIREFLY_RIGHT:
1508     case EL_BD_FIREFLY_UP:
1509     case EL_BD_FIREFLY_LEFT:
1510     case EL_BD_FIREFLY_DOWN:
1511       Feld[x][y] = EL_BD_FIREFLY;
1512       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1513       break;
1514
1515     case EL_PACMAN_RIGHT:
1516     case EL_PACMAN_UP:
1517     case EL_PACMAN_LEFT:
1518     case EL_PACMAN_DOWN:
1519       Feld[x][y] = EL_PACMAN;
1520       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1521       break;
1522
1523     case EL_SP_SNIKSNAK:
1524       MovDir[x][y] = MV_UP;
1525       break;
1526
1527     case EL_SP_ELECTRON:
1528       MovDir[x][y] = MV_LEFT;
1529       break;
1530
1531     case EL_MOLE_LEFT:
1532     case EL_MOLE_RIGHT:
1533     case EL_MOLE_UP:
1534     case EL_MOLE_DOWN:
1535       Feld[x][y] = EL_MOLE;
1536       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1537       break;
1538
1539     default:
1540       if (IS_CUSTOM_ELEMENT(element))
1541       {
1542         if (element_info[element].move_direction_initial != MV_NO_MOVING)
1543           MovDir[x][y] = element_info[element].move_direction_initial;
1544         else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
1545                  element_info[element].move_pattern == MV_TURNING_LEFT ||
1546                  element_info[element].move_pattern == MV_TURNING_RIGHT)
1547           MovDir[x][y] = 1 << RND(4);
1548         else if (element_info[element].move_pattern == MV_HORIZONTAL)
1549           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1550         else if (element_info[element].move_pattern == MV_VERTICAL)
1551           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1552         else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
1553           MovDir[x][y] = element_info[element].move_pattern;
1554         else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
1555                  element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1556         {
1557           for (i=0; i<4; i++)
1558           {
1559             int x1 = x + xy[i][0];
1560             int y1 = y + xy[i][1];
1561
1562             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1563             {
1564               if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
1565                 MovDir[x][y] = direction[0][i];
1566               else
1567                 MovDir[x][y] = direction[1][i];
1568
1569               break;
1570             }
1571           }
1572         }                
1573       }
1574       else
1575       {
1576         MovDir[x][y] = 1 << RND(4);
1577
1578         if (element != EL_BUG &&
1579             element != EL_SPACESHIP &&
1580             element != EL_BD_BUTTERFLY &&
1581             element != EL_BD_FIREFLY)
1582           break;
1583
1584         for (i=0; i<4; i++)
1585         {
1586           int x1 = x + xy[i][0];
1587           int y1 = y + xy[i][1];
1588
1589           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1590           {
1591             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1592             {
1593               MovDir[x][y] = direction[0][i];
1594               break;
1595             }
1596             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1597                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1598             {
1599               MovDir[x][y] = direction[1][i];
1600               break;
1601             }
1602           }
1603         }
1604       }
1605       break;
1606   }
1607 }
1608
1609 void InitAmoebaNr(int x, int y)
1610 {
1611   int i;
1612   int group_nr = AmoebeNachbarNr(x, y);
1613
1614   if (group_nr == 0)
1615   {
1616     for (i=1; i<MAX_NUM_AMOEBA; i++)
1617     {
1618       if (AmoebaCnt[i] == 0)
1619       {
1620         group_nr = i;
1621         break;
1622       }
1623     }
1624   }
1625
1626   AmoebaNr[x][y] = group_nr;
1627   AmoebaCnt[group_nr]++;
1628   AmoebaCnt2[group_nr]++;
1629 }
1630
1631 void GameWon()
1632 {
1633   int hi_pos;
1634   boolean raise_level = FALSE;
1635
1636   if (local_player->MovPos)
1637     return;
1638
1639 #if 1
1640   if (tape.auto_play)           /* tape might already be stopped here */
1641     tape.auto_play_level_solved = TRUE;
1642 #else
1643   if (tape.playing && tape.auto_play)
1644     tape.auto_play_level_solved = TRUE;
1645 #endif
1646
1647   local_player->LevelSolved = FALSE;
1648
1649   PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1650
1651   if (TimeLeft)
1652   {
1653     if (!tape.playing && setup.sound_loops)
1654       PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1655                    SND_CTRL_PLAY_LOOP);
1656
1657     while (TimeLeft > 0)
1658     {
1659       if (!tape.playing && !setup.sound_loops)
1660         PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1661       if (TimeLeft > 0 && !(TimeLeft % 10))
1662         RaiseScore(level.score[SC_TIME_BONUS]);
1663       if (TimeLeft > 100 && !(TimeLeft % 10))
1664         TimeLeft -= 10;
1665       else
1666         TimeLeft--;
1667       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1668       BackToFront();
1669
1670       if (!tape.playing)
1671         Delay(10);
1672     }
1673
1674     if (!tape.playing && setup.sound_loops)
1675       StopSound(SND_GAME_LEVELTIME_BONUS);
1676   }
1677   else if (level.time == 0)             /* level without time limit */
1678   {
1679     if (!tape.playing && setup.sound_loops)
1680       PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1681                    SND_CTRL_PLAY_LOOP);
1682
1683     while (TimePlayed < 999)
1684     {
1685       if (!tape.playing && !setup.sound_loops)
1686         PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1687       if (TimePlayed < 999 && !(TimePlayed % 10))
1688         RaiseScore(level.score[SC_TIME_BONUS]);
1689       if (TimePlayed < 900 && !(TimePlayed % 10))
1690         TimePlayed += 10;
1691       else
1692         TimePlayed++;
1693       DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1694       BackToFront();
1695
1696       if (!tape.playing)
1697         Delay(10);
1698     }
1699
1700     if (!tape.playing && setup.sound_loops)
1701       StopSound(SND_GAME_LEVELTIME_BONUS);
1702   }
1703
1704   /* close exit door after last player */
1705   if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
1706        Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
1707   {
1708     int element = Feld[ExitX][ExitY];
1709
1710     Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
1711                           EL_SP_EXIT_CLOSING);
1712
1713     PlaySoundLevelElementAction(ExitX, ExitY, element, ACTION_CLOSING);
1714   }
1715
1716   /* Hero disappears */
1717   DrawLevelField(ExitX, ExitY);
1718   BackToFront();
1719
1720   if (tape.playing)
1721     return;
1722
1723   CloseDoor(DOOR_CLOSE_1);
1724
1725   if (tape.recording)
1726   {
1727     TapeStop();
1728     SaveTape(tape.level_nr);            /* Ask to save tape */
1729   }
1730
1731   if (level_nr == leveldir_current->handicap_level)
1732   {
1733     leveldir_current->handicap_level++;
1734     SaveLevelSetup_SeriesInfo();
1735   }
1736
1737   if (level_editor_test_game)
1738     local_player->score = -1;   /* no highscore when playing from editor */
1739   else if (level_nr < leveldir_current->last_level)
1740     raise_level = TRUE;         /* advance to next level */
1741
1742   if ((hi_pos = NewHiScore()) >= 0) 
1743   {
1744     game_status = GAME_MODE_SCORES;
1745     DrawHallOfFame(hi_pos);
1746     if (raise_level)
1747     {
1748       level_nr++;
1749       TapeErase();
1750     }
1751   }
1752   else
1753   {
1754     game_status = GAME_MODE_MAIN;
1755     if (raise_level)
1756     {
1757       level_nr++;
1758       TapeErase();
1759     }
1760     DrawMainMenu();
1761   }
1762
1763   BackToFront();
1764 }
1765
1766 int NewHiScore()
1767 {
1768   int k, l;
1769   int position = -1;
1770
1771   LoadScore(level_nr);
1772
1773   if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1774       local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score) 
1775     return -1;
1776
1777   for (k=0; k<MAX_SCORE_ENTRIES; k++) 
1778   {
1779     if (local_player->score > highscore[k].Score)
1780     {
1781       /* player has made it to the hall of fame */
1782
1783       if (k < MAX_SCORE_ENTRIES - 1)
1784       {
1785         int m = MAX_SCORE_ENTRIES - 1;
1786
1787 #ifdef ONE_PER_NAME
1788         for (l=k; l<MAX_SCORE_ENTRIES; l++)
1789           if (!strcmp(setup.player_name, highscore[l].Name))
1790             m = l;
1791         if (m == k)     /* player's new highscore overwrites his old one */
1792           goto put_into_list;
1793 #endif
1794
1795         for (l=m; l>k; l--)
1796         {
1797           strcpy(highscore[l].Name, highscore[l - 1].Name);
1798           highscore[l].Score = highscore[l - 1].Score;
1799         }
1800       }
1801
1802 #ifdef ONE_PER_NAME
1803       put_into_list:
1804 #endif
1805       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
1806       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
1807       highscore[k].Score = local_player->score; 
1808       position = k;
1809       break;
1810     }
1811
1812 #ifdef ONE_PER_NAME
1813     else if (!strncmp(setup.player_name, highscore[k].Name,
1814                       MAX_PLAYER_NAME_LEN))
1815       break;    /* player already there with a higher score */
1816 #endif
1817
1818   }
1819
1820   if (position >= 0) 
1821     SaveScore(level_nr);
1822
1823   return position;
1824 }
1825
1826 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
1827 {
1828   if (player->GfxAction != action || player->GfxDir != dir)
1829   {
1830 #if 0
1831     printf("Player frame reset! (%d => %d, %d => %d)\n",
1832            player->GfxAction, action, player->GfxDir, dir);
1833 #endif
1834
1835     player->GfxAction = action;
1836     player->GfxDir = dir;
1837     player->Frame = 0;
1838     player->StepFrame = 0;
1839   }
1840 }
1841
1842 static void ResetRandomAnimationValue(int x, int y)
1843 {
1844   GfxRandom[x][y] = INIT_GFX_RANDOM();
1845 }
1846
1847 static void ResetGfxAnimation(int x, int y)
1848 {
1849   GfxFrame[x][y] = 0;
1850   GfxAction[x][y] = ACTION_DEFAULT;
1851 }
1852
1853 void InitMovingField(int x, int y, int direction)
1854 {
1855   int element = Feld[x][y];
1856   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1857   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
1858   int newx = x + dx;
1859   int newy = y + dy;
1860
1861   if (!WasJustMoving[x][y] || direction != MovDir[x][y])
1862     ResetGfxAnimation(x, y);
1863
1864   MovDir[newx][newy] = MovDir[x][y] = direction;
1865
1866   if (Feld[newx][newy] == EL_EMPTY)
1867     Feld[newx][newy] = EL_BLOCKED;
1868
1869   if (direction == MV_DOWN && CAN_FALL(element))
1870     GfxAction[x][y] = ACTION_FALLING;
1871   else
1872     GfxAction[x][y] = ACTION_MOVING;
1873
1874   GfxFrame[newx][newy] = GfxFrame[x][y];
1875   GfxAction[newx][newy] = GfxAction[x][y];
1876   GfxRandom[newx][newy] = GfxRandom[x][y];
1877 }
1878
1879 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
1880 {
1881   int direction = MovDir[x][y];
1882   int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1883   int newy = y + (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
1884
1885   *goes_to_x = newx;
1886   *goes_to_y = newy;
1887 }
1888
1889 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
1890 {
1891   int oldx = x, oldy = y;
1892   int direction = MovDir[x][y];
1893
1894   if (direction == MV_LEFT)
1895     oldx++;
1896   else if (direction == MV_RIGHT)
1897     oldx--;
1898   else if (direction == MV_UP)
1899     oldy++;
1900   else if (direction == MV_DOWN)
1901     oldy--;
1902
1903   *comes_from_x = oldx;
1904   *comes_from_y = oldy;
1905 }
1906
1907 int MovingOrBlocked2Element(int x, int y)
1908 {
1909   int element = Feld[x][y];
1910
1911   if (element == EL_BLOCKED)
1912   {
1913     int oldx, oldy;
1914
1915     Blocked2Moving(x, y, &oldx, &oldy);
1916     return Feld[oldx][oldy];
1917   }
1918   else
1919     return element;
1920 }
1921
1922 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
1923 {
1924   /* like MovingOrBlocked2Element(), but if element is moving
1925      and (x,y) is the field the moving element is just leaving,
1926      return EL_BLOCKED instead of the element value */
1927   int element = Feld[x][y];
1928
1929   if (IS_MOVING(x, y))
1930   {
1931     if (element == EL_BLOCKED)
1932     {
1933       int oldx, oldy;
1934
1935       Blocked2Moving(x, y, &oldx, &oldy);
1936       return Feld[oldx][oldy];
1937     }
1938     else
1939       return EL_BLOCKED;
1940   }
1941   else
1942     return element;
1943 }
1944
1945 static void RemoveField(int x, int y)
1946 {
1947   Feld[x][y] = EL_EMPTY;
1948
1949   MovPos[x][y] = 0;
1950   MovDir[x][y] = 0;
1951   MovDelay[x][y] = 0;
1952
1953   AmoebaNr[x][y] = 0;
1954   ChangeDelay[x][y] = 0;
1955   ChangePage[x][y] = -1;
1956   Pushed[x][y] = FALSE;
1957
1958   GfxElement[x][y] = EL_UNDEFINED;
1959   GfxAction[x][y] = ACTION_DEFAULT;
1960 }
1961
1962 void RemoveMovingField(int x, int y)
1963 {
1964   int oldx = x, oldy = y, newx = x, newy = y;
1965   int element = Feld[x][y];
1966   int next_element = EL_UNDEFINED;
1967
1968   if (element != EL_BLOCKED && !IS_MOVING(x, y))
1969     return;
1970
1971   if (IS_MOVING(x, y))
1972   {
1973     Moving2Blocked(x, y, &newx, &newy);
1974     if (Feld[newx][newy] != EL_BLOCKED)
1975       return;
1976   }
1977   else if (element == EL_BLOCKED)
1978   {
1979     Blocked2Moving(x, y, &oldx, &oldy);
1980     if (!IS_MOVING(oldx, oldy))
1981       return;
1982   }
1983
1984   if (element == EL_BLOCKED &&
1985       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
1986        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
1987        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
1988        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
1989     next_element = get_next_element(Feld[oldx][oldy]);
1990
1991   RemoveField(oldx, oldy);
1992   RemoveField(newx, newy);
1993
1994   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
1995
1996   if (next_element != EL_UNDEFINED)
1997     Feld[oldx][oldy] = next_element;
1998
1999   DrawLevelField(oldx, oldy);
2000   DrawLevelField(newx, newy);
2001 }
2002
2003 void DrawDynamite(int x, int y)
2004 {
2005   int sx = SCREENX(x), sy = SCREENY(y);
2006   int graphic = el2img(Feld[x][y]);
2007   int frame;
2008
2009   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2010     return;
2011
2012   if (IS_WALKABLE_INSIDE(Back[x][y]))
2013     return;
2014
2015   if (Back[x][y])
2016     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2017   else if (Store[x][y])
2018     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2019
2020   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2021
2022 #if 1
2023   if (Back[x][y] || Store[x][y])
2024     DrawGraphicThruMask(sx, sy, graphic, frame);
2025   else
2026     DrawGraphic(sx, sy, graphic, frame);
2027 #else
2028   if (game.emulation == EMU_SUPAPLEX)
2029     DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2030   else if (Store[x][y])
2031     DrawGraphicThruMask(sx, sy, graphic, frame);
2032   else
2033     DrawGraphic(sx, sy, graphic, frame);
2034 #endif
2035 }
2036
2037 void CheckDynamite(int x, int y)
2038 {
2039   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
2040   {
2041     MovDelay[x][y]--;
2042
2043     if (MovDelay[x][y] != 0)
2044     {
2045       DrawDynamite(x, y);
2046       PlaySoundLevelActionIfLoop(x, y, ACTION_ACTIVE);
2047
2048       return;
2049     }
2050   }
2051
2052 #if 1
2053   StopSoundLevelActionIfLoop(x, y, ACTION_ACTIVE);
2054 #else
2055   if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2056       Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2057     StopSound(SND_DYNAMITE_ACTIVE);
2058   else
2059     StopSound(SND_DYNABOMB_ACTIVE);
2060 #endif
2061
2062   Bang(x, y);
2063 }
2064
2065 void RelocatePlayer(int x, int y, int element)
2066 {
2067   struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2068
2069 #if 1
2070   RemoveField(x, y);            /* temporarily remove newly placed player */
2071   DrawLevelField(x, y);
2072 #endif
2073
2074   if (player->present)
2075   {
2076     while (player->MovPos)
2077     {
2078       ScrollPlayer(player, SCROLL_GO_ON);
2079       ScrollScreen(NULL, SCROLL_GO_ON);
2080       FrameCounter++;
2081
2082       DrawPlayer(player);
2083
2084       BackToFront();
2085       Delay(GAME_FRAME_DELAY);
2086     }
2087
2088     DrawPlayer(player);         /* needed here only to cleanup last field */
2089     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
2090
2091     player->is_moving = FALSE;
2092   }
2093
2094   Feld[x][y] = element;
2095   InitPlayerField(x, y, element, TRUE);
2096
2097   if (player == local_player)
2098   {
2099     int scroll_xx = -999, scroll_yy = -999;
2100
2101     while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2102     {
2103       int dx = 0, dy = 0;
2104       int fx = FX, fy = FY;
2105
2106       scroll_xx = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
2107                    local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2108                    local_player->jx - MIDPOSX);
2109
2110       scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2111                    local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2112                    local_player->jy - MIDPOSY);
2113
2114       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2115       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2116
2117       scroll_x -= dx;
2118       scroll_y -= dy;
2119
2120       fx += dx * TILEX / 2;
2121       fy += dy * TILEY / 2;
2122
2123       ScrollLevel(dx, dy);
2124       DrawAllPlayers();
2125
2126       /* scroll in two steps of half tile size to make things smoother */
2127       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2128       FlushDisplay();
2129       Delay(GAME_FRAME_DELAY);
2130
2131       /* scroll second step to align at full tile size */
2132       BackToFront();
2133       Delay(GAME_FRAME_DELAY);
2134     }
2135   }
2136 }
2137
2138 void Explode(int ex, int ey, int phase, int mode)
2139 {
2140   int x, y;
2141   int num_phase = 9;
2142   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2143   int last_phase = num_phase * delay;
2144   int half_phase = (num_phase / 2) * delay;
2145   int first_phase_after_start = EX_PHASE_START + 1;
2146
2147   if (game.explosions_delayed)
2148   {
2149     ExplodeField[ex][ey] = mode;
2150     return;
2151   }
2152
2153   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
2154   {
2155     int center_element = Feld[ex][ey];
2156
2157 #if 0
2158     /* --- This is only really needed (and now handled) in "Impact()". --- */
2159     /* do not explode moving elements that left the explode field in time */
2160     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2161         center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2162       return;
2163 #endif
2164
2165     if (mode == EX_NORMAL || mode == EX_CENTER)
2166       PlaySoundLevelAction(ex, ey, ACTION_EXPLODING);
2167
2168     /* remove things displayed in background while burning dynamite */
2169     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2170       Back[ex][ey] = 0;
2171
2172     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2173     {
2174       /* put moving element to center field (and let it explode there) */
2175       center_element = MovingOrBlocked2Element(ex, ey);
2176       RemoveMovingField(ex, ey);
2177       Feld[ex][ey] = center_element;
2178     }
2179
2180     for (y = ey - 1; y <= ey + 1; y++) for(x = ex - 1; x <= ex + 1; x++)
2181     {
2182       int xx = x - ex + 1;
2183       int yy = y - ey + 1;
2184       int element;
2185
2186       if (!IN_LEV_FIELD(x, y) ||
2187           ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2188            (x != ex || y != ey)))
2189         continue;
2190
2191       element = Feld[x][y];
2192
2193       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2194       {
2195         element = MovingOrBlocked2Element(x, y);
2196
2197         if (!IS_EXPLOSION_PROOF(element))
2198           RemoveMovingField(x, y);
2199       }
2200
2201 #if 1
2202
2203 #if 0
2204       if (IS_EXPLOSION_PROOF(element))
2205         continue;
2206 #else
2207       /* indestructible elements can only explode in center (but not flames) */
2208       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2209           element == EL_FLAMES)
2210         continue;
2211 #endif
2212
2213 #else
2214       if ((IS_INDESTRUCTIBLE(element) &&
2215            (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2216             (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2217           element == EL_FLAMES)
2218         continue;
2219 #endif
2220
2221       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2222       {
2223         if (IS_ACTIVE_BOMB(element))
2224         {
2225           /* re-activate things under the bomb like gate or penguin */
2226           Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2227           Store[x][y] = 0;
2228         }
2229
2230         continue;
2231       }
2232
2233       /* save walkable background elements while explosion on same tile */
2234 #if 0
2235       if (IS_INDESTRUCTIBLE(element))
2236         Back[x][y] = element;
2237 #else
2238       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2239         Back[x][y] = element;
2240 #endif
2241
2242       /* ignite explodable elements reached by other explosion */
2243       if (element == EL_EXPLOSION)
2244         element = Store2[x][y];
2245
2246 #if 1
2247       if (AmoebaNr[x][y] &&
2248           (element == EL_AMOEBA_FULL ||
2249            element == EL_BD_AMOEBA ||
2250            element == EL_AMOEBA_GROWING))
2251       {
2252         AmoebaCnt[AmoebaNr[x][y]]--;
2253         AmoebaCnt2[AmoebaNr[x][y]]--;
2254       }
2255
2256       RemoveField(x, y);
2257 #endif
2258
2259       if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
2260       {
2261         switch(StorePlayer[ex][ey])
2262         {
2263           case EL_PLAYER_2:
2264             Store[x][y] = EL_EMERALD_RED;
2265             break;
2266           case EL_PLAYER_3:
2267             Store[x][y] = EL_EMERALD;
2268             break;
2269           case EL_PLAYER_4:
2270             Store[x][y] = EL_EMERALD_PURPLE;
2271             break;
2272           case EL_PLAYER_1:
2273           default:
2274             Store[x][y] = EL_EMERALD_YELLOW;
2275             break;
2276         }
2277
2278         if (game.emulation == EMU_SUPAPLEX)
2279           Store[x][y] = EL_EMPTY;
2280       }
2281       else if (center_element == EL_MOLE)
2282         Store[x][y] = EL_EMERALD_RED;
2283       else if (center_element == EL_PENGUIN)
2284         Store[x][y] = EL_EMERALD_PURPLE;
2285       else if (center_element == EL_BUG)
2286         Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2287       else if (center_element == EL_BD_BUTTERFLY)
2288         Store[x][y] = EL_BD_DIAMOND;
2289       else if (center_element == EL_SP_ELECTRON)
2290         Store[x][y] = EL_SP_INFOTRON;
2291       else if (center_element == EL_AMOEBA_TO_DIAMOND)
2292         Store[x][y] = level.amoeba_content;
2293       else if (center_element == EL_YAMYAM)
2294         Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2295       else if (IS_CUSTOM_ELEMENT(center_element) &&
2296                element_info[center_element].content[xx][yy] != EL_EMPTY)
2297         Store[x][y] = element_info[center_element].content[xx][yy];
2298       else if (element == EL_WALL_EMERALD)
2299         Store[x][y] = EL_EMERALD;
2300       else if (element == EL_WALL_DIAMOND)
2301         Store[x][y] = EL_DIAMOND;
2302       else if (element == EL_WALL_BD_DIAMOND)
2303         Store[x][y] = EL_BD_DIAMOND;
2304       else if (element == EL_WALL_EMERALD_YELLOW)
2305         Store[x][y] = EL_EMERALD_YELLOW;
2306       else if (element == EL_WALL_EMERALD_RED)
2307         Store[x][y] = EL_EMERALD_RED;
2308       else if (element == EL_WALL_EMERALD_PURPLE)
2309         Store[x][y] = EL_EMERALD_PURPLE;
2310       else if (element == EL_WALL_PEARL)
2311         Store[x][y] = EL_PEARL;
2312       else if (element == EL_WALL_CRYSTAL)
2313         Store[x][y] = EL_CRYSTAL;
2314       else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2315         Store[x][y] = element_info[element].content[1][1];
2316       else
2317         Store[x][y] = EL_EMPTY;
2318
2319       if (x != ex || y != ey ||
2320           center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2321         Store2[x][y] = element;
2322
2323 #if 0
2324       if (AmoebaNr[x][y] &&
2325           (element == EL_AMOEBA_FULL ||
2326            element == EL_BD_AMOEBA ||
2327            element == EL_AMOEBA_GROWING))
2328       {
2329         AmoebaCnt[AmoebaNr[x][y]]--;
2330         AmoebaCnt2[AmoebaNr[x][y]]--;
2331       }
2332
2333 #if 1
2334       RemoveField(x, y);
2335 #else
2336       MovDir[x][y] = MovPos[x][y] = 0;
2337       AmoebaNr[x][y] = 0;
2338 #endif
2339 #endif
2340
2341       Feld[x][y] = EL_EXPLOSION;
2342 #if 1
2343       GfxElement[x][y] = center_element;
2344 #else
2345       GfxElement[x][y] = EL_UNDEFINED;
2346 #endif
2347
2348       ExplodePhase[x][y] = 1;
2349       Stop[x][y] = TRUE;
2350     }
2351
2352     if (center_element == EL_YAMYAM)
2353       game.yamyam_content_nr =
2354         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2355
2356     return;
2357   }
2358
2359   if (Stop[ex][ey])
2360     return;
2361
2362   x = ex;
2363   y = ey;
2364
2365   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2366
2367 #ifdef DEBUG
2368
2369   /* activate this even in non-DEBUG version until cause for crash in
2370      getGraphicAnimationFrame() (see below) is found and eliminated */
2371 #endif
2372 #if 1
2373
2374   if (GfxElement[x][y] == EL_UNDEFINED)
2375   {
2376     printf("\n\n");
2377     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2378     printf("Explode(): This should never happen!\n");
2379     printf("\n\n");
2380
2381     GfxElement[x][y] = EL_EMPTY;
2382   }
2383 #endif
2384
2385   if (phase == first_phase_after_start)
2386   {
2387     int element = Store2[x][y];
2388
2389     if (element == EL_BLACK_ORB)
2390     {
2391       Feld[x][y] = Store2[x][y];
2392       Store2[x][y] = 0;
2393       Bang(x, y);
2394     }
2395   }
2396   else if (phase == half_phase)
2397   {
2398     int element = Store2[x][y];
2399
2400     if (IS_PLAYER(x, y))
2401       KillHeroUnlessProtected(x, y);
2402     else if (CAN_EXPLODE_BY_FIRE(element))
2403     {
2404       Feld[x][y] = Store2[x][y];
2405       Store2[x][y] = 0;
2406       Bang(x, y);
2407     }
2408     else if (element == EL_AMOEBA_TO_DIAMOND)
2409       AmoebeUmwandeln(x, y);
2410   }
2411
2412   if (phase == last_phase)
2413   {
2414     int element;
2415
2416     element = Feld[x][y] = Store[x][y];
2417     Store[x][y] = Store2[x][y] = 0;
2418     GfxElement[x][y] = EL_UNDEFINED;
2419
2420     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2421       element = Feld[x][y] = Back[x][y];
2422     Back[x][y] = 0;
2423
2424     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2425     ChangeDelay[x][y] = 0;
2426     ChangePage[x][y] = -1;
2427
2428     InitField(x, y, FALSE);
2429     if (CAN_MOVE(element))
2430       InitMovDir(x, y);
2431     DrawLevelField(x, y);
2432
2433     TestIfElementTouchesCustomElement(x, y);
2434
2435     if (GFX_CRUMBLED(element))
2436       DrawLevelFieldCrumbledSandNeighbours(x, y);
2437
2438     if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2439       StorePlayer[x][y] = 0;
2440
2441     if (ELEM_IS_PLAYER(element))
2442       RelocatePlayer(x, y, element);
2443   }
2444   else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2445   {
2446 #if 1
2447     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2448 #else
2449     int stored = Store[x][y];
2450     int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2451                    stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2452                    IMG_SP_EXPLOSION);
2453 #endif
2454     int frame = getGraphicAnimationFrame(graphic, phase - delay);
2455
2456     if (phase == delay)
2457       DrawLevelFieldCrumbledSand(x, y);
2458
2459     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2460     {
2461       DrawLevelElement(x, y, Back[x][y]);
2462       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2463     }
2464     else if (IS_WALKABLE_UNDER(Back[x][y]))
2465     {
2466       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2467       DrawLevelElementThruMask(x, y, Back[x][y]);
2468     }
2469     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2470       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2471   }
2472 }
2473
2474 void DynaExplode(int ex, int ey)
2475 {
2476   int i, j;
2477   int dynabomb_size = 1;
2478   boolean dynabomb_xl = FALSE;
2479   struct PlayerInfo *player;
2480   static int xy[4][2] =
2481   {
2482     { 0, -1 },
2483     { -1, 0 },
2484     { +1, 0 },
2485     { 0, +1 }
2486   };
2487
2488   if (IS_ACTIVE_BOMB(Feld[ex][ey]))
2489   {
2490     player = &stored_player[Feld[ex][ey] - EL_DYNABOMB_PLAYER_1_ACTIVE];
2491     dynabomb_size = player->dynabomb_size;
2492     dynabomb_xl = player->dynabomb_xl;
2493     player->dynabombs_left++;
2494   }
2495
2496   Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2497
2498   for (i=0; i<4; i++)
2499   {
2500     for (j=1; j<=dynabomb_size; j++)
2501     {
2502       int x = ex + j * xy[i % 4][0];
2503       int y = ey + j * xy[i % 4][1];
2504       int element;
2505
2506       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2507         break;
2508
2509       element = Feld[x][y];
2510
2511       /* do not restart explosions of fields with active bombs */
2512       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2513         continue;
2514
2515       Explode(x, y, EX_PHASE_START, EX_BORDER);
2516
2517       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2518       if (element != EL_EMPTY &&
2519           element != EL_SAND &&
2520           element != EL_EXPLOSION &&
2521           !dynabomb_xl)
2522         break;
2523     }
2524   }
2525 }
2526
2527 void Bang(int x, int y)
2528 {
2529 #if 1
2530   int element = MovingOrBlocked2Element(x, y);
2531 #else
2532   int element = Feld[x][y];
2533 #endif
2534
2535 #if 1
2536   if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
2537 #else
2538   if (IS_PLAYER(x, y))
2539 #endif
2540   {
2541     struct PlayerInfo *player = PLAYERINFO(x, y);
2542
2543     element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2544                             player->element_nr);
2545   }
2546
2547 #if 0
2548 #if 1
2549   PlaySoundLevelAction(x, y, ACTION_EXPLODING);
2550 #else
2551   if (game.emulation == EMU_SUPAPLEX)
2552     PlaySoundLevel(x, y, SND_SP_ELEMENT_EXPLODING);
2553   else
2554     PlaySoundLevel(x, y, SND_ELEMENT_EXPLODING);
2555 #endif
2556 #endif
2557
2558 #if 0
2559   if (IS_PLAYER(x, y))  /* remove objects that might cause smaller explosion */
2560     element = EL_EMPTY;
2561 #endif
2562
2563   switch(element)
2564   {
2565     case EL_BUG:
2566     case EL_SPACESHIP:
2567     case EL_BD_BUTTERFLY:
2568     case EL_BD_FIREFLY:
2569     case EL_YAMYAM:
2570     case EL_DARK_YAMYAM:
2571     case EL_ROBOT:
2572     case EL_PACMAN:
2573     case EL_MOLE:
2574       RaiseScoreElement(element);
2575       Explode(x, y, EX_PHASE_START, EX_NORMAL);
2576       break;
2577     case EL_DYNABOMB_PLAYER_1_ACTIVE:
2578     case EL_DYNABOMB_PLAYER_2_ACTIVE:
2579     case EL_DYNABOMB_PLAYER_3_ACTIVE:
2580     case EL_DYNABOMB_PLAYER_4_ACTIVE:
2581     case EL_DYNABOMB_INCREASE_NUMBER:
2582     case EL_DYNABOMB_INCREASE_SIZE:
2583     case EL_DYNABOMB_INCREASE_POWER:
2584       DynaExplode(x, y);
2585       break;
2586     case EL_PENGUIN:
2587     case EL_LAMP:
2588     case EL_LAMP_ACTIVE:
2589       if (IS_PLAYER(x, y))
2590         Explode(x, y, EX_PHASE_START, EX_NORMAL);
2591       else
2592         Explode(x, y, EX_PHASE_START, EX_CENTER);
2593       break;
2594     default:
2595       if (CAN_EXPLODE_1X1(element))
2596         Explode(x, y, EX_PHASE_START, EX_CENTER);
2597       else
2598         Explode(x, y, EX_PHASE_START, EX_NORMAL);
2599       break;
2600   }
2601
2602   CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2603 }
2604
2605 void SplashAcid(int x, int y)
2606 {
2607   int element = Feld[x][y];
2608
2609   if (element != EL_ACID_SPLASH_LEFT &&
2610       element != EL_ACID_SPLASH_RIGHT)
2611   {
2612     PlaySoundLevel(x, y, SND_ACID_SPLASHING);
2613
2614     if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
2615         (!IN_LEV_FIELD(x-1, y-1) ||
2616          !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
2617       Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
2618
2619     if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
2620         (!IN_LEV_FIELD(x+1, y-1) ||
2621          !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
2622       Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
2623   }
2624 }
2625
2626 static void InitBeltMovement()
2627 {
2628   static int belt_base_element[4] =
2629   {
2630     EL_CONVEYOR_BELT_1_LEFT,
2631     EL_CONVEYOR_BELT_2_LEFT,
2632     EL_CONVEYOR_BELT_3_LEFT,
2633     EL_CONVEYOR_BELT_4_LEFT
2634   };
2635   static int belt_base_active_element[4] =
2636   {
2637     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2638     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2639     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2640     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2641   };
2642
2643   int x, y, i, j;
2644
2645   /* set frame order for belt animation graphic according to belt direction */
2646   for (i=0; i<4; i++)
2647   {
2648     int belt_nr = i;
2649
2650     for (j=0; j<3; j++)
2651     {
2652       int element = belt_base_active_element[belt_nr] + j;
2653       int graphic = el2img(element);
2654
2655       if (game.belt_dir[i] == MV_LEFT)
2656         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2657       else
2658         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
2659     }
2660   }
2661
2662   for(y=0; y<lev_fieldy; y++)
2663   {
2664     for(x=0; x<lev_fieldx; x++)
2665     {
2666       int element = Feld[x][y];
2667
2668       for (i=0; i<4; i++)
2669       {
2670         if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
2671         {
2672           int e_belt_nr = getBeltNrFromBeltElement(element);
2673           int belt_nr = i;
2674
2675           if (e_belt_nr == belt_nr)
2676           {
2677             int belt_part = Feld[x][y] - belt_base_element[belt_nr];
2678
2679             Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
2680           }
2681         }
2682       }
2683     }
2684   }
2685 }
2686
2687 static void ToggleBeltSwitch(int x, int y)
2688 {
2689   static int belt_base_element[4] =
2690   {
2691     EL_CONVEYOR_BELT_1_LEFT,
2692     EL_CONVEYOR_BELT_2_LEFT,
2693     EL_CONVEYOR_BELT_3_LEFT,
2694     EL_CONVEYOR_BELT_4_LEFT
2695   };
2696   static int belt_base_active_element[4] =
2697   {
2698     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
2699     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
2700     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
2701     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
2702   };
2703   static int belt_base_switch_element[4] =
2704   {
2705     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2706     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2707     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2708     EL_CONVEYOR_BELT_4_SWITCH_LEFT
2709   };
2710   static int belt_move_dir[4] =
2711   {
2712     MV_LEFT,
2713     MV_NO_MOVING,
2714     MV_RIGHT,
2715     MV_NO_MOVING,
2716   };
2717
2718   int element = Feld[x][y];
2719   int belt_nr = getBeltNrFromBeltSwitchElement(element);
2720   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
2721   int belt_dir = belt_move_dir[belt_dir_nr];
2722   int xx, yy, i;
2723
2724   if (!IS_BELT_SWITCH(element))
2725     return;
2726
2727   game.belt_dir_nr[belt_nr] = belt_dir_nr;
2728   game.belt_dir[belt_nr] = belt_dir;
2729
2730   if (belt_dir_nr == 3)
2731     belt_dir_nr = 1;
2732
2733   /* set frame order for belt animation graphic according to belt direction */
2734   for (i=0; i<3; i++)
2735   {
2736     int element = belt_base_active_element[belt_nr] + i;
2737     int graphic = el2img(element);
2738
2739     if (belt_dir == MV_LEFT)
2740       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
2741     else
2742       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
2743   }
2744
2745   for (yy=0; yy<lev_fieldy; yy++)
2746   {
2747     for (xx=0; xx<lev_fieldx; xx++)
2748     {
2749       int element = Feld[xx][yy];
2750
2751       if (IS_BELT_SWITCH(element))
2752       {
2753         int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
2754
2755         if (e_belt_nr == belt_nr)
2756         {
2757           Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
2758           DrawLevelField(xx, yy);
2759         }
2760       }
2761       else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
2762       {
2763         int e_belt_nr = getBeltNrFromBeltElement(element);
2764
2765         if (e_belt_nr == belt_nr)
2766         {
2767           int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
2768
2769           Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
2770           DrawLevelField(xx, yy);
2771         }
2772       }
2773       else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
2774       {
2775         int e_belt_nr = getBeltNrFromBeltActiveElement(element);
2776
2777         if (e_belt_nr == belt_nr)
2778         {
2779           int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
2780
2781           Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
2782           DrawLevelField(xx, yy);
2783         }
2784       }
2785     }
2786   }
2787 }
2788
2789 static void ToggleSwitchgateSwitch(int x, int y)
2790 {
2791   int xx, yy;
2792
2793   game.switchgate_pos = !game.switchgate_pos;
2794
2795   for (yy=0; yy<lev_fieldy; yy++)
2796   {
2797     for (xx=0; xx<lev_fieldx; xx++)
2798     {
2799       int element = Feld[xx][yy];
2800
2801       if (element == EL_SWITCHGATE_SWITCH_UP ||
2802           element == EL_SWITCHGATE_SWITCH_DOWN)
2803       {
2804         Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2805         DrawLevelField(xx, yy);
2806       }
2807       else if (element == EL_SWITCHGATE_OPEN ||
2808                element == EL_SWITCHGATE_OPENING)
2809       {
2810         Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
2811 #if 1
2812         PlaySoundLevelAction(xx, yy, ACTION_CLOSING);
2813 #else
2814         PlaySoundLevel(xx, yy, SND_SWITCHGATE_CLOSING);
2815 #endif
2816       }
2817       else if (element == EL_SWITCHGATE_CLOSED ||
2818                element == EL_SWITCHGATE_CLOSING)
2819       {
2820         Feld[xx][yy] = EL_SWITCHGATE_OPENING;
2821 #if 1
2822         PlaySoundLevelAction(xx, yy, ACTION_OPENING);
2823 #else
2824         PlaySoundLevel(xx, yy, SND_SWITCHGATE_OPENING);
2825 #endif
2826       }
2827     }
2828   }
2829 }
2830
2831 static int getInvisibleActiveFromInvisibleElement(int element)
2832 {
2833   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
2834           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
2835           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
2836           element);
2837 }
2838
2839 static int getInvisibleFromInvisibleActiveElement(int element)
2840 {
2841   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
2842           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
2843           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
2844           element);
2845 }
2846
2847 static void RedrawAllLightSwitchesAndInvisibleElements()
2848 {
2849   int x, y;
2850
2851   for (y=0; y<lev_fieldy; y++)
2852   {
2853     for (x=0; x<lev_fieldx; x++)
2854     {
2855       int element = Feld[x][y];
2856
2857       if (element == EL_LIGHT_SWITCH &&
2858           game.light_time_left > 0)
2859       {
2860         Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
2861         DrawLevelField(x, y);
2862       }
2863       else if (element == EL_LIGHT_SWITCH_ACTIVE &&
2864                game.light_time_left == 0)
2865       {
2866         Feld[x][y] = EL_LIGHT_SWITCH;
2867         DrawLevelField(x, y);
2868       }
2869       else if (element == EL_INVISIBLE_STEELWALL ||
2870                element == EL_INVISIBLE_WALL ||
2871                element == EL_INVISIBLE_SAND)
2872       {
2873         if (game.light_time_left > 0)
2874           Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
2875
2876         DrawLevelField(x, y);
2877       }
2878       else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
2879                element == EL_INVISIBLE_WALL_ACTIVE ||
2880                element == EL_INVISIBLE_SAND_ACTIVE)
2881       {
2882         if (game.light_time_left == 0)
2883           Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
2884
2885         DrawLevelField(x, y);
2886       }
2887     }
2888   }
2889 }
2890
2891 static void ToggleLightSwitch(int x, int y)
2892 {
2893   int element = Feld[x][y];
2894
2895   game.light_time_left =
2896     (element == EL_LIGHT_SWITCH ?
2897      level.time_light * FRAMES_PER_SECOND : 0);
2898
2899   RedrawAllLightSwitchesAndInvisibleElements();
2900 }
2901
2902 static void ActivateTimegateSwitch(int x, int y)
2903 {
2904   int xx, yy;
2905
2906   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
2907
2908   for (yy=0; yy<lev_fieldy; yy++)
2909   {
2910     for (xx=0; xx<lev_fieldx; xx++)
2911     {
2912       int element = Feld[xx][yy];
2913
2914       if (element == EL_TIMEGATE_CLOSED ||
2915           element == EL_TIMEGATE_CLOSING)
2916       {
2917         Feld[xx][yy] = EL_TIMEGATE_OPENING;
2918         PlaySoundLevel(xx, yy, SND_TIMEGATE_OPENING);
2919       }
2920
2921       /*
2922       else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
2923       {
2924         Feld[xx][yy] = EL_TIMEGATE_SWITCH;
2925         DrawLevelField(xx, yy);
2926       }
2927       */
2928
2929     }
2930   }
2931
2932   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
2933 }
2934
2935 inline static int getElementMoveStepsize(int x, int y)
2936 {
2937   int element = Feld[x][y];
2938   int direction = MovDir[x][y];
2939   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2940   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
2941   int horiz_move = (dx != 0);
2942   int sign = (horiz_move ? dx : dy);
2943   int step = sign * element_info[element].move_stepsize;
2944
2945   /* special values for move stepsize for spring and things on conveyor belt */
2946   if (horiz_move)
2947   {
2948     if (CAN_FALL(element) &&
2949         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2950       step = sign * MOVE_STEPSIZE_NORMAL / 2;
2951     else if (element == EL_SPRING)
2952       step = sign * MOVE_STEPSIZE_NORMAL * 2;
2953   }
2954
2955   return step;
2956 }
2957
2958 void Impact(int x, int y)
2959 {
2960   boolean lastline = (y == lev_fieldy-1);
2961   boolean object_hit = FALSE;
2962   boolean impact = (lastline || object_hit);
2963   int element = Feld[x][y];
2964   int smashed = EL_UNDEFINED;
2965
2966   if (!lastline)        /* check if element below was hit */
2967   {
2968     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
2969       return;
2970
2971     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
2972                                          MovDir[x][y + 1] != MV_DOWN ||
2973                                          MovPos[x][y + 1] <= TILEY / 2));
2974
2975     /* do not smash moving elements that left the smashed field in time */
2976     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
2977         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
2978       object_hit = FALSE;
2979
2980     if (object_hit)
2981       smashed = MovingOrBlocked2Element(x, y + 1);
2982
2983     impact = (lastline || object_hit);
2984   }
2985
2986   if (!lastline && smashed == EL_ACID)  /* element falls into acid */
2987   {
2988     SplashAcid(x, y);
2989     return;
2990   }
2991
2992   /* only reset graphic animation if graphic really changes after impact */
2993   if (impact &&
2994       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
2995   {
2996     ResetGfxAnimation(x, y);
2997     DrawLevelField(x, y);
2998   }
2999
3000   if (impact && CAN_EXPLODE_IMPACT(element))
3001   {
3002     Bang(x, y);
3003     return;
3004   }
3005   else if (impact && element == EL_PEARL)
3006   {
3007     Feld[x][y] = EL_PEARL_BREAKING;
3008     PlaySoundLevel(x, y, SND_PEARL_BREAKING);
3009     return;
3010   }
3011   else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3012   {
3013     PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT);
3014
3015     return;
3016   }
3017
3018   if (impact && element == EL_AMOEBA_DROP)
3019   {
3020     if (object_hit && IS_PLAYER(x, y + 1))
3021       KillHeroUnlessProtected(x, y + 1);
3022     else if (object_hit && smashed == EL_PENGUIN)
3023       Bang(x, y + 1);
3024     else
3025     {
3026       Feld[x][y] = EL_AMOEBA_GROWING;
3027       Store[x][y] = EL_AMOEBA_WET;
3028
3029       ResetRandomAnimationValue(x, y);
3030     }
3031     return;
3032   }
3033
3034   if (object_hit)               /* check which object was hit */
3035   {
3036     if (CAN_PASS_MAGIC_WALL(element) && 
3037         (smashed == EL_MAGIC_WALL ||
3038          smashed == EL_BD_MAGIC_WALL))
3039     {
3040       int xx, yy;
3041       int activated_magic_wall =
3042         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3043          EL_BD_MAGIC_WALL_ACTIVE);
3044
3045       /* activate magic wall / mill */
3046       for (yy=0; yy<lev_fieldy; yy++)
3047         for (xx=0; xx<lev_fieldx; xx++)
3048           if (Feld[xx][yy] == smashed)
3049             Feld[xx][yy] = activated_magic_wall;
3050
3051       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3052       game.magic_wall_active = TRUE;
3053
3054       PlaySoundLevel(x, y, (smashed == EL_MAGIC_WALL ?
3055                             SND_MAGIC_WALL_ACTIVATING :
3056                             SND_BD_MAGIC_WALL_ACTIVATING));
3057     }
3058
3059     if (IS_PLAYER(x, y + 1))
3060     {
3061       if (CAN_SMASH_PLAYER(element))
3062       {
3063         KillHeroUnlessProtected(x, y + 1);
3064         return;
3065       }
3066     }
3067     else if (smashed == EL_PENGUIN)
3068     {
3069       if (CAN_SMASH_PLAYER(element))
3070       {
3071         Bang(x, y + 1);
3072         return;
3073       }
3074     }
3075     else if (element == EL_BD_DIAMOND)
3076     {
3077       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3078       {
3079         Bang(x, y + 1);
3080         return;
3081       }
3082     }
3083     else if ((element == EL_SP_INFOTRON ||
3084               element == EL_SP_ZONK) &&
3085              (smashed == EL_SP_SNIKSNAK ||
3086               smashed == EL_SP_ELECTRON ||
3087               smashed == EL_SP_DISK_ORANGE))
3088     {
3089       Bang(x, y + 1);
3090       return;
3091     }
3092 #if 0
3093     else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3094     {
3095       Bang(x, y + 1);
3096       return;
3097     }
3098 #endif
3099     else if (CAN_SMASH_EVERYTHING(element))
3100     {
3101       if (IS_CLASSIC_ENEMY(smashed) ||
3102           CAN_EXPLODE_SMASHED(smashed))
3103       {
3104         Bang(x, y + 1);
3105         return;
3106       }
3107       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3108       {
3109         if (smashed == EL_LAMP ||
3110             smashed == EL_LAMP_ACTIVE)
3111         {
3112           Bang(x, y + 1);
3113           return;
3114         }
3115         else if (smashed == EL_NUT)
3116         {
3117           Feld[x][y + 1] = EL_NUT_BREAKING;
3118           PlaySoundLevel(x, y, SND_NUT_BREAKING);
3119           RaiseScoreElement(EL_NUT);
3120           return;
3121         }
3122         else if (smashed == EL_PEARL)
3123         {
3124           Feld[x][y + 1] = EL_PEARL_BREAKING;
3125           PlaySoundLevel(x, y, SND_PEARL_BREAKING);
3126           return;
3127         }
3128         else if (smashed == EL_DIAMOND)
3129         {
3130           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3131           PlaySoundLevel(x, y, SND_DIAMOND_BREAKING);
3132           return;
3133         }
3134         else if (IS_BELT_SWITCH(smashed))
3135         {
3136           ToggleBeltSwitch(x, y + 1);
3137         }
3138         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3139                  smashed == EL_SWITCHGATE_SWITCH_DOWN)
3140         {
3141           ToggleSwitchgateSwitch(x, y + 1);
3142         }
3143         else if (smashed == EL_LIGHT_SWITCH ||
3144                  smashed == EL_LIGHT_SWITCH_ACTIVE)
3145         {
3146           ToggleLightSwitch(x, y + 1);
3147         }
3148         else
3149         {
3150           CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3151
3152           CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3153                                           CE_OTHER_IS_SWITCHING);
3154           CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3155                                  CE_SWITCHED, -1);
3156         }
3157       }
3158       else
3159       {
3160         CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3161       }
3162     }
3163   }
3164
3165   /* play sound of magic wall / mill */
3166   if (!lastline &&
3167       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3168        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3169   {
3170     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3171       PlaySoundLevel(x, y, SND_MAGIC_WALL_FILLING);
3172     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3173       PlaySoundLevel(x, y, SND_BD_MAGIC_WALL_FILLING);
3174
3175     return;
3176   }
3177
3178   /* play sound of object that hits the ground */
3179   if (lastline || object_hit)
3180     PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT);
3181 }
3182
3183 void TurnRound(int x, int y)
3184 {
3185   static struct
3186   {
3187     int x, y;
3188   } move_xy[] =
3189   {
3190     {  0,  0 },
3191     { -1,  0 },
3192     { +1,  0 },
3193     {  0,  0 },
3194     {  0, -1 },
3195     {  0,  0 }, { 0, 0 }, { 0, 0 },
3196     {  0, +1 }
3197   };
3198   static struct
3199   {
3200     int left, right, back;
3201   } turn[] =
3202   {
3203     { 0,        0,              0        },
3204     { MV_DOWN,  MV_UP,          MV_RIGHT },
3205     { MV_UP,    MV_DOWN,        MV_LEFT  },
3206     { 0,        0,              0        },
3207     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
3208     { 0,        0,              0        },
3209     { 0,        0,              0        },
3210     { 0,        0,              0        },
3211     { MV_RIGHT, MV_LEFT,        MV_UP    }
3212   };
3213
3214   int element = Feld[x][y];
3215   int old_move_dir = MovDir[x][y];
3216   int left_dir  = turn[old_move_dir].left;
3217   int right_dir = turn[old_move_dir].right;
3218   int back_dir  = turn[old_move_dir].back;
3219
3220   int left_dx  = move_xy[left_dir].x,     left_dy  = move_xy[left_dir].y;
3221   int right_dx = move_xy[right_dir].x,    right_dy = move_xy[right_dir].y;
3222   int move_dx  = move_xy[old_move_dir].x, move_dy  = move_xy[old_move_dir].y;
3223   int back_dx  = move_xy[back_dir].x,     back_dy  = move_xy[back_dir].y;
3224
3225   int left_x  = x + left_dx,  left_y  = y + left_dy;
3226   int right_x = x + right_dx, right_y = y + right_dy;
3227   int move_x  = x + move_dx,  move_y  = y + move_dy;
3228
3229   int xx, yy;
3230
3231   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3232   {
3233     TestIfBadThingTouchesOtherBadThing(x, y);
3234
3235     if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3236       MovDir[x][y] = right_dir;
3237     else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3238       MovDir[x][y] = left_dir;
3239
3240     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3241       MovDelay[x][y] = 9;
3242     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
3243       MovDelay[x][y] = 1;
3244   }
3245   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3246            element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3247   {
3248     TestIfBadThingTouchesOtherBadThing(x, y);
3249
3250     if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3251       MovDir[x][y] = left_dir;
3252     else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3253       MovDir[x][y] = right_dir;
3254
3255     if ((element == EL_SPACESHIP ||
3256          element == EL_SP_SNIKSNAK ||
3257          element == EL_SP_ELECTRON)
3258         && MovDir[x][y] != old_move_dir)
3259       MovDelay[x][y] = 9;
3260     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
3261       MovDelay[x][y] = 1;
3262   }
3263   else if (element == EL_YAMYAM)
3264   {
3265     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3266     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3267
3268     if (can_turn_left && can_turn_right)
3269       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3270     else if (can_turn_left)
3271       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3272     else if (can_turn_right)
3273       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3274     else
3275       MovDir[x][y] = back_dir;
3276
3277     MovDelay[x][y] = 16 + 16 * RND(3);
3278   }
3279   else if (element == EL_DARK_YAMYAM)
3280   {
3281     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3282     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3283
3284     if (can_turn_left && can_turn_right)
3285       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3286     else if (can_turn_left)
3287       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3288     else if (can_turn_right)
3289       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3290     else
3291       MovDir[x][y] = back_dir;
3292
3293     MovDelay[x][y] = 16 + 16 * RND(3);
3294   }
3295   else if (element == EL_PACMAN)
3296   {
3297     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3298     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3299
3300     if (can_turn_left && can_turn_right)
3301       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3302     else if (can_turn_left)
3303       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3304     else if (can_turn_right)
3305       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3306     else
3307       MovDir[x][y] = back_dir;
3308
3309     MovDelay[x][y] = 6 + RND(40);
3310   }
3311   else if (element == EL_PIG)
3312   {
3313     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(left_x, left_y);
3314     boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3315     boolean can_move_on    = PIG_CAN_ENTER_FIELD(move_x, move_y);
3316     boolean should_turn_left, should_turn_right, should_move_on;
3317     int rnd_value = 24;
3318     int rnd = RND(rnd_value);
3319
3320     should_turn_left = (can_turn_left &&
3321                         (!can_move_on ||
3322                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3323                                                    y + back_dy + left_dy)));
3324     should_turn_right = (can_turn_right &&
3325                          (!can_move_on ||
3326                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3327                                                     y + back_dy + right_dy)));
3328     should_move_on = (can_move_on &&
3329                       (!can_turn_left ||
3330                        !can_turn_right ||
3331                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3332                                                  y + move_dy + left_dy) ||
3333                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3334                                                  y + move_dy + right_dy)));
3335
3336     if (should_turn_left || should_turn_right || should_move_on)
3337     {
3338       if (should_turn_left && should_turn_right && should_move_on)
3339         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
3340                         rnd < 2 * rnd_value / 3 ? right_dir :
3341                         old_move_dir);
3342       else if (should_turn_left && should_turn_right)
3343         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3344       else if (should_turn_left && should_move_on)
3345         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3346       else if (should_turn_right && should_move_on)
3347         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3348       else if (should_turn_left)
3349         MovDir[x][y] = left_dir;
3350       else if (should_turn_right)
3351         MovDir[x][y] = right_dir;
3352       else if (should_move_on)
3353         MovDir[x][y] = old_move_dir;
3354     }
3355     else if (can_move_on && rnd > rnd_value / 8)
3356       MovDir[x][y] = old_move_dir;
3357     else if (can_turn_left && can_turn_right)
3358       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3359     else if (can_turn_left && rnd > rnd_value / 8)
3360       MovDir[x][y] = left_dir;
3361     else if (can_turn_right && rnd > rnd_value/8)
3362       MovDir[x][y] = right_dir;
3363     else
3364       MovDir[x][y] = back_dir;
3365
3366     xx = x + move_xy[MovDir[x][y]].x;
3367     yy = y + move_xy[MovDir[x][y]].y;
3368
3369     if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3370       MovDir[x][y] = old_move_dir;
3371
3372     MovDelay[x][y] = 0;
3373   }
3374   else if (element == EL_DRAGON)
3375   {
3376     boolean can_turn_left  = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3377     boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3378     boolean can_move_on    = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3379     int rnd_value = 24;
3380     int rnd = RND(rnd_value);
3381
3382     if (can_move_on && rnd > rnd_value / 8)
3383       MovDir[x][y] = old_move_dir;
3384     else if (can_turn_left && can_turn_right)
3385       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3386     else if (can_turn_left && rnd > rnd_value / 8)
3387       MovDir[x][y] = left_dir;
3388     else if (can_turn_right && rnd > rnd_value / 8)
3389       MovDir[x][y] = right_dir;
3390     else
3391       MovDir[x][y] = back_dir;
3392
3393     xx = x + move_xy[MovDir[x][y]].x;
3394     yy = y + move_xy[MovDir[x][y]].y;
3395
3396     if (!IS_FREE(xx, yy))
3397       MovDir[x][y] = old_move_dir;
3398
3399     MovDelay[x][y] = 0;
3400   }
3401   else if (element == EL_MOLE)
3402   {
3403     boolean can_move_on =
3404       (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3405                             IS_AMOEBOID(Feld[move_x][move_y]) ||
3406                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3407     if (!can_move_on)
3408     {
3409       boolean can_turn_left =
3410         (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3411                               IS_AMOEBOID(Feld[left_x][left_y])));
3412
3413       boolean can_turn_right =
3414         (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3415                               IS_AMOEBOID(Feld[right_x][right_y])));
3416
3417       if (can_turn_left && can_turn_right)
3418         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3419       else if (can_turn_left)
3420         MovDir[x][y] = left_dir;
3421       else
3422         MovDir[x][y] = right_dir;
3423     }
3424
3425     if (MovDir[x][y] != old_move_dir)
3426       MovDelay[x][y] = 9;
3427   }
3428   else if (element == EL_BALLOON)
3429   {
3430     MovDir[x][y] = game.balloon_dir;
3431     MovDelay[x][y] = 0;
3432   }
3433   else if (element == EL_SPRING)
3434   {
3435     if (MovDir[x][y] & MV_HORIZONTAL &&
3436         (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3437          IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3438       MovDir[x][y] = MV_NO_MOVING;
3439
3440     MovDelay[x][y] = 0;
3441   }
3442   else if (element == EL_ROBOT ||
3443            element == EL_SATELLITE ||
3444            element == EL_PENGUIN)
3445   {
3446     int attr_x = -1, attr_y = -1;
3447
3448     if (AllPlayersGone)
3449     {
3450       attr_x = ExitX;
3451       attr_y = ExitY;
3452     }
3453     else
3454     {
3455       int i;
3456
3457       for (i=0; i<MAX_PLAYERS; i++)
3458       {
3459         struct PlayerInfo *player = &stored_player[i];
3460         int jx = player->jx, jy = player->jy;
3461
3462         if (!player->active)
3463           continue;
3464
3465         if (attr_x == -1 ||
3466             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3467         {
3468           attr_x = jx;
3469           attr_y = jy;
3470         }
3471       }
3472     }
3473
3474     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3475     {
3476       attr_x = ZX;
3477       attr_y = ZY;
3478     }
3479
3480     if (element == EL_PENGUIN)
3481     {
3482       int i;
3483       static int xy[4][2] =
3484       {
3485         { 0, -1 },
3486         { -1, 0 },
3487         { +1, 0 },
3488         { 0, +1 }
3489       };
3490
3491       for (i=0; i<4; i++)
3492       {
3493         int ex = x + xy[i % 4][0];
3494         int ey = y + xy[i % 4][1];
3495
3496         if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3497         {
3498           attr_x = ex;
3499           attr_y = ey;
3500           break;
3501         }
3502       }
3503     }
3504
3505     MovDir[x][y] = MV_NO_MOVING;
3506     if (attr_x < x)
3507       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3508     else if (attr_x > x)
3509       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3510     if (attr_y < y)
3511       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3512     else if (attr_y > y)
3513       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3514
3515     if (element == EL_ROBOT)
3516     {
3517       int newx, newy;
3518
3519       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3520         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3521       Moving2Blocked(x, y, &newx, &newy);
3522
3523       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3524         MovDelay[x][y] = 8 + 8 * !RND(3);
3525       else
3526         MovDelay[x][y] = 16;
3527     }
3528     else if (element == EL_PENGUIN)
3529     {
3530       int newx, newy;
3531
3532       MovDelay[x][y] = 1;
3533
3534       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3535       {
3536         boolean first_horiz = RND(2);
3537         int new_move_dir = MovDir[x][y];
3538
3539         MovDir[x][y] =
3540           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3541         Moving2Blocked(x, y, &newx, &newy);
3542
3543         if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3544           return;
3545
3546         MovDir[x][y] =
3547           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3548         Moving2Blocked(x, y, &newx, &newy);
3549
3550         if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3551           return;
3552
3553         MovDir[x][y] = old_move_dir;
3554         return;
3555       }
3556     }
3557     else        /* (element == EL_SATELLITE) */
3558     {
3559       int newx, newy;
3560
3561       MovDelay[x][y] = 1;
3562
3563       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3564       {
3565         boolean first_horiz = RND(2);
3566         int new_move_dir = MovDir[x][y];
3567
3568         MovDir[x][y] =
3569           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3570         Moving2Blocked(x, y, &newx, &newy);
3571
3572         if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3573           return;
3574
3575         MovDir[x][y] =
3576           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3577         Moving2Blocked(x, y, &newx, &newy);
3578
3579         if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3580           return;
3581
3582         MovDir[x][y] = old_move_dir;
3583         return;
3584       }
3585     }
3586   }
3587   else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
3588            element_info[element].move_pattern == MV_TURNING_LEFT ||
3589            element_info[element].move_pattern == MV_TURNING_RIGHT)
3590   {
3591     boolean can_turn_left  = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
3592     boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
3593
3594     if (element_info[element].move_pattern == MV_TURNING_LEFT)
3595       MovDir[x][y] = left_dir;
3596     else if (element_info[element].move_pattern == MV_TURNING_RIGHT)
3597       MovDir[x][y] = right_dir;
3598     else if (can_turn_left && can_turn_right)
3599       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3600     else if (can_turn_left)
3601       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3602     else if (can_turn_right)
3603       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3604     else
3605       MovDir[x][y] = back_dir;
3606
3607     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3608   }
3609   else if (element_info[element].move_pattern == MV_HORIZONTAL ||
3610            element_info[element].move_pattern == MV_VERTICAL)
3611   {
3612     if (element_info[element].move_pattern & old_move_dir)
3613       MovDir[x][y] = back_dir;
3614     else if (element_info[element].move_pattern == MV_HORIZONTAL)
3615       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3616     else if (element_info[element].move_pattern == MV_VERTICAL)
3617       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3618
3619     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3620   }
3621   else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
3622   {
3623     MovDir[x][y] = element_info[element].move_pattern;
3624     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3625   }
3626   else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE)
3627   {
3628     if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
3629       MovDir[x][y] = left_dir;
3630     else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3631       MovDir[x][y] = right_dir;
3632
3633     if (MovDir[x][y] != old_move_dir)
3634       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3635   }
3636   else if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
3637   {
3638     if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
3639       MovDir[x][y] = right_dir;
3640     else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
3641       MovDir[x][y] = left_dir;
3642
3643     if (MovDir[x][y] != old_move_dir)
3644       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3645   }
3646   else if (element_info[element].move_pattern == MV_TOWARDS_PLAYER ||
3647            element_info[element].move_pattern == MV_AWAY_FROM_PLAYER)
3648   {
3649     int attr_x = -1, attr_y = -1;
3650     int newx, newy;
3651     boolean move_away =
3652       (element_info[element].move_pattern == MV_AWAY_FROM_PLAYER);
3653
3654     if (AllPlayersGone)
3655     {
3656       attr_x = ExitX;
3657       attr_y = ExitY;
3658     }
3659     else
3660     {
3661       int i;
3662
3663       for (i=0; i<MAX_PLAYERS; i++)
3664       {
3665         struct PlayerInfo *player = &stored_player[i];
3666         int jx = player->jx, jy = player->jy;
3667
3668         if (!player->active)
3669           continue;
3670
3671         if (attr_x == -1 ||
3672             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3673         {
3674           attr_x = jx;
3675           attr_y = jy;
3676         }
3677       }
3678     }
3679
3680     MovDir[x][y] = MV_NO_MOVING;
3681     if (attr_x < x)
3682       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3683     else if (attr_x > x)
3684       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3685     if (attr_y < y)
3686       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3687     else if (attr_y > y)
3688       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3689
3690     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3691
3692     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3693     {
3694       boolean first_horiz = RND(2);
3695       int new_move_dir = MovDir[x][y];
3696
3697       MovDir[x][y] =
3698         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3699       Moving2Blocked(x, y, &newx, &newy);
3700
3701       if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3702         return;
3703
3704       MovDir[x][y] =
3705         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3706       Moving2Blocked(x, y, &newx, &newy);
3707
3708       if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
3709         return;
3710
3711       MovDir[x][y] = old_move_dir;
3712     }
3713   }
3714   else if (element_info[element].move_pattern == MV_WHEN_PUSHED)
3715   {
3716     if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
3717       MovDir[x][y] = MV_NO_MOVING;
3718
3719     MovDelay[x][y] = 0;
3720   }
3721 }
3722
3723 static boolean JustBeingPushed(int x, int y)
3724 {
3725   int i;
3726
3727   for (i=0; i<MAX_PLAYERS; i++)
3728   {
3729     struct PlayerInfo *player = &stored_player[i];
3730
3731     if (player->active && player->is_pushing && player->MovPos)
3732     {
3733       int next_jx = player->jx + (player->jx - player->last_jx);
3734       int next_jy = player->jy + (player->jy - player->last_jy);
3735
3736       if (x == next_jx && y == next_jy)
3737         return TRUE;
3738     }
3739   }
3740
3741   return FALSE;
3742 }
3743
3744 void StartMoving(int x, int y)
3745 {
3746   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
3747   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
3748   int element = Feld[x][y];
3749
3750   if (Stop[x][y])
3751     return;
3752
3753   /* !!! this should be handled more generic (not only for mole) !!! */
3754   if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
3755     GfxAction[x][y] = ACTION_DEFAULT;
3756
3757   if (CAN_FALL(element) && y < lev_fieldy - 1)
3758   {
3759     if ((x > 0 && IS_PLAYER(x - 1, y)) ||
3760         (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
3761       if (JustBeingPushed(x, y))
3762         return;
3763
3764     if (element == EL_QUICKSAND_FULL)
3765     {
3766       if (IS_FREE(x, y + 1))
3767       {
3768         InitMovingField(x, y, MV_DOWN);
3769         started_moving = TRUE;
3770
3771         Feld[x][y] = EL_QUICKSAND_EMPTYING;
3772         Store[x][y] = EL_ROCK;
3773 #if 1
3774         PlaySoundLevelAction(x, y, ACTION_EMPTYING);
3775 #else
3776         PlaySoundLevel(x, y, SND_QUICKSAND_EMPTYING);
3777 #endif
3778       }
3779       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3780       {
3781         if (!MovDelay[x][y])
3782           MovDelay[x][y] = TILEY + 1;
3783
3784         if (MovDelay[x][y])
3785         {
3786           MovDelay[x][y]--;
3787           if (MovDelay[x][y])
3788             return;
3789         }
3790
3791         Feld[x][y] = EL_QUICKSAND_EMPTY;
3792         Feld[x][y + 1] = EL_QUICKSAND_FULL;
3793         Store[x][y + 1] = Store[x][y];
3794         Store[x][y] = 0;
3795 #if 1
3796         PlaySoundLevelAction(x, y, ACTION_FILLING);
3797 #else
3798         PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
3799 #endif
3800       }
3801     }
3802     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
3803              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3804     {
3805       InitMovingField(x, y, MV_DOWN);
3806       started_moving = TRUE;
3807
3808       Feld[x][y] = EL_QUICKSAND_FILLING;
3809       Store[x][y] = element;
3810 #if 1
3811       PlaySoundLevelAction(x, y, ACTION_FILLING);
3812 #else
3813       PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
3814 #endif
3815     }
3816     else if (element == EL_MAGIC_WALL_FULL)
3817     {
3818       if (IS_FREE(x, y + 1))
3819       {
3820         InitMovingField(x, y, MV_DOWN);
3821         started_moving = TRUE;
3822
3823         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
3824         Store[x][y] = EL_CHANGED(Store[x][y]);
3825       }
3826       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3827       {
3828         if (!MovDelay[x][y])
3829           MovDelay[x][y] = TILEY/4 + 1;
3830
3831         if (MovDelay[x][y])
3832         {
3833           MovDelay[x][y]--;
3834           if (MovDelay[x][y])
3835             return;
3836         }
3837
3838         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
3839         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
3840         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
3841         Store[x][y] = 0;
3842       }
3843     }
3844     else if (element == EL_BD_MAGIC_WALL_FULL)
3845     {
3846       if (IS_FREE(x, y + 1))
3847       {
3848         InitMovingField(x, y, MV_DOWN);
3849         started_moving = TRUE;
3850
3851         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
3852         Store[x][y] = EL_CHANGED2(Store[x][y]);
3853       }
3854       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3855       {
3856         if (!MovDelay[x][y])
3857           MovDelay[x][y] = TILEY/4 + 1;
3858
3859         if (MovDelay[x][y])
3860         {
3861           MovDelay[x][y]--;
3862           if (MovDelay[x][y])
3863             return;
3864         }
3865
3866         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
3867         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
3868         Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
3869         Store[x][y] = 0;
3870       }
3871     }
3872     else if (CAN_PASS_MAGIC_WALL(element) &&
3873              (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3874               Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3875     {
3876       InitMovingField(x, y, MV_DOWN);
3877       started_moving = TRUE;
3878
3879       Feld[x][y] =
3880         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
3881          EL_BD_MAGIC_WALL_FILLING);
3882       Store[x][y] = element;
3883     }
3884 #if 0
3885     else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
3886 #else
3887     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
3888 #endif
3889     {
3890       SplashAcid(x, y);
3891
3892       InitMovingField(x, y, MV_DOWN);
3893       started_moving = TRUE;
3894
3895       Store[x][y] = EL_ACID;
3896 #if 0
3897       /* !!! TEST !!! better use "_FALLING" etc. !!! */
3898       GfxAction[x][y + 1] = ACTION_ACTIVE;
3899 #endif
3900     }
3901 #if 1
3902     else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
3903               CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
3904               (Feld[x][y + 1] == EL_BLOCKED)) ||
3905              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
3906               CAN_SMASH(element) && WasJustFalling[x][y] &&
3907               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
3908
3909 #else
3910 #if 1
3911     else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
3912              CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3913              WasJustMoving[x][y] && !Pushed[x][y + 1])
3914 #else
3915     else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3916              WasJustMoving[x][y])
3917 #endif
3918 #endif
3919
3920     {
3921       /* this is needed for a special case not covered by calling "Impact()"
3922          from "ContinueMoving()": if an element moves to a tile directly below
3923          another element which was just falling on that tile (which was empty
3924          in the previous frame), the falling element above would just stop
3925          instead of smashing the element below (in previous version, the above
3926          element was just checked for "moving" instead of "falling", resulting
3927          in incorrect smashes caused by horizontal movement of the above
3928          element; also, the case of the player being the element to smash was
3929          simply not covered here... :-/ ) */
3930
3931       Impact(x, y);
3932     }
3933     else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
3934     {
3935       if (MovDir[x][y] == MV_NO_MOVING)
3936       {
3937         InitMovingField(x, y, MV_DOWN);
3938         started_moving = TRUE;
3939       }
3940     }
3941     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
3942     {
3943       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
3944         MovDir[x][y] = MV_DOWN;
3945
3946       InitMovingField(x, y, MV_DOWN);
3947       started_moving = TRUE;
3948     }
3949     else if (element == EL_AMOEBA_DROP)
3950     {
3951       Feld[x][y] = EL_AMOEBA_GROWING;
3952       Store[x][y] = EL_AMOEBA_WET;
3953     }
3954     /* Store[x][y + 1] must be zero, because:
3955        (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
3956     */
3957 #if 0
3958 #if OLD_GAME_BEHAVIOUR
3959     else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
3960 #else
3961     else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
3962              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
3963              element != EL_DX_SUPABOMB)
3964 #endif
3965 #else
3966     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
3967               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
3968              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
3969              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
3970 #endif
3971     {
3972       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
3973                                 (IS_FREE(x - 1, y + 1) ||
3974                                  Feld[x - 1][y + 1] == EL_ACID));
3975       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
3976                                 (IS_FREE(x + 1, y + 1) ||
3977                                  Feld[x + 1][y + 1] == EL_ACID));
3978       boolean can_fall_any  = (can_fall_left || can_fall_right);
3979       boolean can_fall_both = (can_fall_left && can_fall_right);
3980
3981       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
3982       {
3983         int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
3984
3985         if (slippery_type == SLIPPERY_ONLY_LEFT)
3986           can_fall_right = FALSE;
3987         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
3988           can_fall_left = FALSE;
3989         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
3990           can_fall_right = FALSE;
3991         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
3992           can_fall_left = FALSE;
3993
3994         can_fall_any  = (can_fall_left || can_fall_right);
3995         can_fall_both = (can_fall_left && can_fall_right);
3996       }
3997
3998       if (can_fall_any)
3999       {
4000         if (can_fall_both &&
4001             (game.emulation != EMU_BOULDERDASH &&
4002              element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4003           can_fall_left = !(can_fall_right = RND(2));
4004
4005         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4006         started_moving = TRUE;
4007       }
4008     }
4009     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4010     {
4011       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
4012       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4013       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4014       int belt_dir = game.belt_dir[belt_nr];
4015
4016       if ((belt_dir == MV_LEFT  && left_is_free) ||
4017           (belt_dir == MV_RIGHT && right_is_free))
4018       {
4019         InitMovingField(x, y, belt_dir);
4020         started_moving = TRUE;
4021
4022         GfxAction[x][y] = ACTION_DEFAULT;
4023       }
4024     }
4025   }
4026
4027   /* not "else if" because of elements that can fall and move (EL_SPRING) */
4028   if (CAN_MOVE(element) && !started_moving)
4029   {
4030     int newx, newy;
4031
4032 #if 1
4033     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4034       return;
4035 #else
4036     if ((element == EL_SATELLITE ||
4037          element == EL_BALLOON ||
4038          element == EL_SPRING)
4039         && JustBeingPushed(x, y))
4040       return;
4041 #endif
4042
4043 #if 0
4044 #if 0
4045     if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4046       Feld[x][y + 1] = EL_EMPTY;        /* was set to EL_BLOCKED above */
4047 #else
4048     if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4049     {
4050       Moving2Blocked(x, y, &newx, &newy);
4051       if (Feld[newx][newy] == EL_BLOCKED)
4052         Feld[newx][newy] = EL_EMPTY;    /* was set to EL_BLOCKED above */
4053     }
4054 #endif
4055 #endif
4056
4057     if (!MovDelay[x][y])        /* start new movement phase */
4058     {
4059       /* all objects that can change their move direction after each step
4060          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4061
4062       if (element != EL_YAMYAM &&
4063           element != EL_DARK_YAMYAM &&
4064           element != EL_PACMAN &&
4065           !(element_info[element].move_pattern & MV_ANY_DIRECTION) &&
4066           element_info[element].move_pattern != MV_TURNING_LEFT &&
4067           element_info[element].move_pattern != MV_TURNING_RIGHT)
4068       {
4069         TurnRound(x, y);
4070
4071         if (MovDelay[x][y] && (element == EL_BUG ||
4072                                element == EL_SPACESHIP ||
4073                                element == EL_SP_SNIKSNAK ||
4074                                element == EL_SP_ELECTRON ||
4075                                element == EL_MOLE))
4076           DrawLevelField(x, y);
4077       }
4078     }
4079
4080     if (MovDelay[x][y])         /* wait some time before next movement */
4081     {
4082       MovDelay[x][y]--;
4083
4084 #if 0
4085       if (element == EL_YAMYAM)
4086       {
4087         printf("::: %d\n",
4088                el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4089         DrawLevelElementAnimation(x, y, element);
4090       }
4091 #endif
4092
4093       if (MovDelay[x][y])       /* element still has to wait some time */
4094       {
4095 #if 0
4096         /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4097         ResetGfxAnimation(x, y);
4098 #endif
4099         GfxAction[x][y] = ACTION_WAITING;
4100       }
4101
4102       if (element == EL_ROBOT ||
4103 #if 0
4104           element == EL_PACMAN ||
4105 #endif
4106           element == EL_YAMYAM ||
4107           element == EL_DARK_YAMYAM)
4108       {
4109 #if 0
4110         DrawLevelElementAnimation(x, y, element);
4111 #else
4112         DrawLevelElementAnimationIfNeeded(x, y, element);
4113 #endif
4114         PlaySoundLevelAction(x, y, ACTION_WAITING);
4115       }
4116       else if (element == EL_SP_ELECTRON)
4117         DrawLevelElementAnimationIfNeeded(x, y, element);
4118       else if (element == EL_DRAGON)
4119       {
4120         int i;
4121         int dir = MovDir[x][y];
4122         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4123         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
4124         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
4125                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
4126                        dir == MV_UP     ? IMG_FLAMES_1_UP :
4127                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4128         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4129
4130 #if 0
4131         printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4132 #endif
4133
4134         GfxAction[x][y] = ACTION_ATTACKING;
4135
4136         if (IS_PLAYER(x, y))
4137           DrawPlayerField(x, y);
4138         else
4139           DrawLevelField(x, y);
4140
4141         PlaySoundLevelActionIfLoop(x, y, ACTION_ATTACKING);
4142
4143         for (i=1; i <= 3; i++)
4144         {
4145           int xx = x + i * dx;
4146           int yy = y + i * dy;
4147           int sx = SCREENX(xx);
4148           int sy = SCREENY(yy);
4149           int flame_graphic = graphic + (i - 1);
4150
4151           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4152             break;
4153
4154           if (MovDelay[x][y])
4155           {
4156             int flamed = MovingOrBlocked2Element(xx, yy);
4157
4158             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4159               Bang(xx, yy);
4160             else
4161               RemoveMovingField(xx, yy);
4162
4163             Feld[xx][yy] = EL_FLAMES;
4164             if (IN_SCR_FIELD(sx, sy))
4165             {
4166               DrawLevelFieldCrumbledSand(xx, yy);
4167               DrawGraphic(sx, sy, flame_graphic, frame);
4168             }
4169           }
4170           else
4171           {
4172             if (Feld[xx][yy] == EL_FLAMES)
4173               Feld[xx][yy] = EL_EMPTY;
4174             DrawLevelField(xx, yy);
4175           }
4176         }
4177       }
4178
4179       if (MovDelay[x][y])       /* element still has to wait some time */
4180       {
4181         PlaySoundLevelAction(x, y, ACTION_WAITING);
4182
4183         return;
4184       }
4185
4186       /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4187          for all other elements GfxAction will be set by InitMovingField() */
4188       if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4189         GfxAction[x][y] = ACTION_MOVING;
4190     }
4191
4192     /* now make next step */
4193
4194     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4195
4196     if (DONT_COLLIDE_WITH(element) &&
4197         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4198         !PLAYER_PROTECTED(newx, newy))
4199     {
4200 #if 1
4201       TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4202       return;
4203 #else
4204       /* player killed by element which is deadly when colliding with */
4205       MovDir[x][y] = 0;
4206       KillHero(PLAYERINFO(newx, newy));
4207       return;
4208 #endif
4209
4210     }
4211     else if ((element == EL_PENGUIN ||
4212               element == EL_ROBOT ||
4213               element == EL_SATELLITE ||
4214               element == EL_BALLOON ||
4215               IS_CUSTOM_ELEMENT(element)) &&
4216              IN_LEV_FIELD(newx, newy) &&
4217              MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4218     {
4219       SplashAcid(x, y);
4220       Store[x][y] = EL_ACID;
4221     }
4222     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4223     {
4224       if (Feld[newx][newy] == EL_EXIT_OPEN)
4225       {
4226         Feld[x][y] = EL_EMPTY;
4227         DrawLevelField(x, y);
4228
4229         PlaySoundLevel(newx, newy, SND_PENGUIN_PASSING);
4230         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4231           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4232
4233         local_player->friends_still_needed--;
4234         if (!local_player->friends_still_needed &&
4235             !local_player->GameOver && AllPlayersGone)
4236           local_player->LevelSolved = local_player->GameOver = TRUE;
4237
4238         return;
4239       }
4240       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4241       {
4242         if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4243           DrawLevelField(newx, newy);
4244         else
4245           MovDir[x][y] = MV_NO_MOVING;
4246       }
4247       else if (!IS_FREE(newx, newy))
4248       {
4249         GfxAction[x][y] = ACTION_WAITING;
4250
4251         if (IS_PLAYER(x, y))
4252           DrawPlayerField(x, y);
4253         else
4254           DrawLevelField(x, y);
4255         return;
4256       }
4257     }
4258     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4259     {
4260       if (IS_FOOD_PIG(Feld[newx][newy]))
4261       {
4262         if (IS_MOVING(newx, newy))
4263           RemoveMovingField(newx, newy);
4264         else
4265         {
4266           Feld[newx][newy] = EL_EMPTY;
4267           DrawLevelField(newx, newy);
4268         }
4269
4270         PlaySoundLevel(x, y, SND_PIG_DIGGING);
4271       }
4272       else if (!IS_FREE(newx, newy))
4273       {
4274         if (IS_PLAYER(x, y))
4275           DrawPlayerField(x, y);
4276         else
4277           DrawLevelField(x, y);
4278         return;
4279       }
4280     }
4281     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4282     {
4283       if (!IS_FREE(newx, newy))
4284       {
4285         if (IS_PLAYER(x, y))
4286           DrawPlayerField(x, y);
4287         else
4288           DrawLevelField(x, y);
4289
4290         return;
4291       }
4292       else
4293       {
4294         boolean wanna_flame = !RND(10);
4295         int dx = newx - x, dy = newy - y;
4296         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4297         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4298         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4299                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4300         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4301                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4302
4303         if ((wanna_flame ||
4304              IS_CLASSIC_ENEMY(element1) ||
4305              IS_CLASSIC_ENEMY(element2)) &&
4306             element1 != EL_DRAGON && element2 != EL_DRAGON &&
4307             element1 != EL_FLAMES && element2 != EL_FLAMES)
4308         {
4309 #if 1
4310           ResetGfxAnimation(x, y);
4311           GfxAction[x][y] = ACTION_ATTACKING;
4312 #endif
4313
4314           if (IS_PLAYER(x, y))
4315             DrawPlayerField(x, y);
4316           else
4317             DrawLevelField(x, y);
4318
4319           PlaySoundLevel(x, y, SND_DRAGON_ATTACKING);
4320
4321           MovDelay[x][y] = 50;
4322
4323           Feld[newx][newy] = EL_FLAMES;
4324           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4325             Feld[newx1][newy1] = EL_FLAMES;
4326           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4327             Feld[newx2][newy2] = EL_FLAMES;
4328
4329           return;
4330         }
4331       }
4332     }
4333     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4334              Feld[newx][newy] == EL_DIAMOND)
4335     {
4336       if (IS_MOVING(newx, newy))
4337         RemoveMovingField(newx, newy);
4338       else
4339       {
4340         Feld[newx][newy] = EL_EMPTY;
4341         DrawLevelField(newx, newy);
4342       }
4343
4344       PlaySoundLevel(x, y, SND_YAMYAM_DIGGING);
4345     }
4346     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4347              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4348     {
4349       if (AmoebaNr[newx][newy])
4350       {
4351         AmoebaCnt2[AmoebaNr[newx][newy]]--;
4352         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4353             Feld[newx][newy] == EL_BD_AMOEBA)
4354           AmoebaCnt[AmoebaNr[newx][newy]]--;
4355       }
4356
4357       if (IS_MOVING(newx, newy))
4358         RemoveMovingField(newx, newy);
4359       else
4360       {
4361         Feld[newx][newy] = EL_EMPTY;
4362         DrawLevelField(newx, newy);
4363       }
4364
4365       PlaySoundLevel(x, y, SND_DARK_YAMYAM_DIGGING);
4366     }
4367     else if ((element == EL_PACMAN || element == EL_MOLE)
4368              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4369     {
4370       if (AmoebaNr[newx][newy])
4371       {
4372         AmoebaCnt2[AmoebaNr[newx][newy]]--;
4373         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4374             Feld[newx][newy] == EL_BD_AMOEBA)
4375           AmoebaCnt[AmoebaNr[newx][newy]]--;
4376       }
4377
4378       if (element == EL_MOLE)
4379       {
4380         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4381         PlaySoundLevel(x, y, SND_MOLE_DIGGING);
4382
4383         ResetGfxAnimation(x, y);
4384         GfxAction[x][y] = ACTION_DIGGING;
4385         DrawLevelField(x, y);
4386
4387         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
4388         return;                         /* wait for shrinking amoeba */
4389       }
4390       else      /* element == EL_PACMAN */
4391       {
4392         Feld[newx][newy] = EL_EMPTY;
4393         DrawLevelField(newx, newy);
4394         PlaySoundLevel(x, y, SND_PACMAN_DIGGING);
4395       }
4396     }
4397     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4398              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4399               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4400     {
4401       /* wait for shrinking amoeba to completely disappear */
4402       return;
4403     }
4404     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4405     {
4406       /* object was running against a wall */
4407
4408       TurnRound(x, y);
4409
4410 #if 1
4411       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
4412         DrawLevelElementAnimation(x, y, element);
4413 #else
4414       if (element == EL_BUG ||
4415           element == EL_SPACESHIP ||
4416           element == EL_SP_SNIKSNAK)
4417         DrawLevelField(x, y);
4418       else if (element == EL_MOLE)
4419         DrawLevelField(x, y);
4420       else if (element == EL_BD_BUTTERFLY ||
4421                element == EL_BD_FIREFLY)
4422         DrawLevelElementAnimationIfNeeded(x, y, element);
4423       else if (element == EL_SATELLITE)
4424         DrawLevelElementAnimationIfNeeded(x, y, element);
4425       else if (element == EL_SP_ELECTRON)
4426         DrawLevelElementAnimationIfNeeded(x, y, element);
4427 #endif
4428
4429       if (DONT_TOUCH(element))
4430         TestIfBadThingTouchesHero(x, y);
4431
4432 #if 0
4433       PlaySoundLevelAction(x, y, ACTION_WAITING);
4434 #endif
4435
4436       return;
4437     }
4438
4439     InitMovingField(x, y, MovDir[x][y]);
4440
4441     PlaySoundLevelAction(x, y, ACTION_MOVING);
4442   }
4443
4444   if (MovDir[x][y])
4445     ContinueMoving(x, y);
4446 }
4447
4448 void ContinueMoving(int x, int y)
4449 {
4450   int element = Feld[x][y];
4451   int direction = MovDir[x][y];
4452   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4453   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4454   int newx = x + dx, newy = y + dy;
4455   int nextx = newx + dx, nexty = newy + dy;
4456   boolean pushed = Pushed[x][y];
4457
4458   MovPos[x][y] += getElementMoveStepsize(x, y);
4459
4460   if (pushed)           /* special case: moving object pushed by player */
4461     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4462
4463   if (ABS(MovPos[x][y]) < TILEX)
4464   {
4465     DrawLevelField(x, y);
4466
4467     return;     /* element is still moving */
4468   }
4469
4470   /* element reached destination field */
4471
4472   Feld[x][y] = EL_EMPTY;
4473   Feld[newx][newy] = element;
4474   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
4475
4476   if (element == EL_MOLE)
4477   {
4478     Feld[x][y] = EL_SAND;
4479
4480     DrawLevelFieldCrumbledSandNeighbours(x, y);
4481   }
4482   else if (element == EL_QUICKSAND_FILLING)
4483   {
4484     element = Feld[newx][newy] = get_next_element(element);
4485     Store[newx][newy] = Store[x][y];
4486   }
4487   else if (element == EL_QUICKSAND_EMPTYING)
4488   {
4489     Feld[x][y] = get_next_element(element);
4490     element = Feld[newx][newy] = Store[x][y];
4491   }
4492   else if (element == EL_MAGIC_WALL_FILLING)
4493   {
4494     element = Feld[newx][newy] = get_next_element(element);
4495     if (!game.magic_wall_active)
4496       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4497     Store[newx][newy] = Store[x][y];
4498   }
4499   else if (element == EL_MAGIC_WALL_EMPTYING)
4500   {
4501     Feld[x][y] = get_next_element(element);
4502     if (!game.magic_wall_active)
4503       Feld[x][y] = EL_MAGIC_WALL_DEAD;
4504     element = Feld[newx][newy] = Store[x][y];
4505   }
4506   else if (element == EL_BD_MAGIC_WALL_FILLING)
4507   {
4508     element = Feld[newx][newy] = get_next_element(element);
4509     if (!game.magic_wall_active)
4510       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
4511     Store[newx][newy] = Store[x][y];
4512   }
4513   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
4514   {
4515     Feld[x][y] = get_next_element(element);
4516     if (!game.magic_wall_active)
4517       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4518     element = Feld[newx][newy] = Store[x][y];
4519   }
4520   else if (element == EL_AMOEBA_DROPPING)
4521   {
4522     Feld[x][y] = get_next_element(element);
4523     element = Feld[newx][newy] = Store[x][y];
4524   }
4525   else if (element == EL_SOKOBAN_OBJECT)
4526   {
4527     if (Back[x][y])
4528       Feld[x][y] = Back[x][y];
4529
4530     if (Back[newx][newy])
4531       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
4532
4533     Back[x][y] = Back[newx][newy] = 0;
4534   }
4535   else if (Store[x][y] == EL_ACID)
4536   {
4537     element = Feld[newx][newy] = EL_ACID;
4538   }
4539
4540   Store[x][y] = 0;
4541   MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4542   MovDelay[newx][newy] = 0;
4543
4544   /* copy element change control values to new field */
4545   ChangeDelay[newx][newy] = ChangeDelay[x][y];
4546   ChangePage[newx][newy] = ChangePage[x][y];
4547   Changed[newx][newy] = Changed[x][y];
4548   ChangeEvent[newx][newy] = ChangeEvent[x][y];
4549
4550   ChangeDelay[x][y] = 0;
4551   ChangePage[x][y] = -1;
4552   Changed[x][y] = CE_BITMASK_DEFAULT;
4553   ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
4554
4555   /* copy animation control values to new field */
4556   GfxFrame[newx][newy]  = GfxFrame[x][y];
4557   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame */
4558   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
4559
4560   Pushed[x][y] = Pushed[newx][newy] = FALSE;
4561
4562   ResetGfxAnimation(x, y);      /* reset animation values for old field */
4563
4564 #if 0
4565   /* 2.1.1 (does not work correctly for spring) */
4566   if (!CAN_MOVE(element))
4567     MovDir[newx][newy] = 0;
4568 #else
4569
4570 #if 0
4571   /* (does not work for falling objects that slide horizontally) */
4572   if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
4573     MovDir[newx][newy] = 0;
4574 #else
4575   /*
4576   if (!CAN_MOVE(element) ||
4577       (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
4578     MovDir[newx][newy] = 0;
4579   */
4580
4581   if (!CAN_MOVE(element) ||
4582       (CAN_FALL(element) && direction == MV_DOWN))
4583     MovDir[newx][newy] = 0;
4584
4585 #endif
4586 #endif
4587
4588   DrawLevelField(x, y);
4589   DrawLevelField(newx, newy);
4590
4591   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
4592
4593   /* prevent pushed element from moving on in pushed direction */
4594   if (pushed && CAN_MOVE(element) &&
4595       element_info[element].move_pattern & MV_ANY_DIRECTION &&
4596       !(element_info[element].move_pattern & direction))
4597     TurnRound(newx, newy);
4598
4599   if (!pushed)  /* special case: moving object pushed by player */
4600   {
4601     WasJustMoving[newx][newy] = 3;
4602
4603     if (CAN_FALL(element) && direction == MV_DOWN)
4604       WasJustFalling[newx][newy] = 3;
4605   }
4606
4607   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
4608   {
4609     TestIfBadThingTouchesHero(newx, newy);
4610     TestIfBadThingTouchesFriend(newx, newy);
4611     TestIfBadThingTouchesOtherBadThing(newx, newy);
4612   }
4613   else if (element == EL_PENGUIN)
4614     TestIfFriendTouchesBadThing(newx, newy);
4615
4616   if (CAN_FALL(element) && direction == MV_DOWN &&
4617       (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
4618     Impact(x, newy);
4619
4620 #if 1
4621   TestIfElementTouchesCustomElement(x, y);              /* for empty space */
4622 #endif
4623
4624 #if 0
4625   if (ChangePage[newx][newy] != -1)                     /* delayed change */
4626     ChangeElement(newx, newy, ChangePage[newx][newy]);
4627 #endif
4628
4629   if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
4630     CheckElementSideChange(newx, newy, Feld[newx][newy], direction,
4631                            CE_COLLISION, -1);
4632
4633   TestIfPlayerTouchesCustomElement(newx, newy);
4634   TestIfElementTouchesCustomElement(newx, newy);
4635 }
4636
4637 int AmoebeNachbarNr(int ax, int ay)
4638 {
4639   int i;
4640   int element = Feld[ax][ay];
4641   int group_nr = 0;
4642   static int xy[4][2] =
4643   {
4644     { 0, -1 },
4645     { -1, 0 },
4646     { +1, 0 },
4647     { 0, +1 }
4648   };
4649
4650   for (i=0; i<4; i++)
4651   {
4652     int x = ax + xy[i][0];
4653     int y = ay + xy[i][1];
4654
4655     if (!IN_LEV_FIELD(x, y))
4656       continue;
4657
4658     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
4659       group_nr = AmoebaNr[x][y];
4660   }
4661
4662   return group_nr;
4663 }
4664
4665 void AmoebenVereinigen(int ax, int ay)
4666 {
4667   int i, x, y, xx, yy;
4668   int new_group_nr = AmoebaNr[ax][ay];
4669   static int xy[4][2] =
4670   {
4671     { 0, -1 },
4672     { -1, 0 },
4673     { +1, 0 },
4674     { 0, +1 }
4675   };
4676
4677   if (new_group_nr == 0)
4678     return;
4679
4680   for (i=0; i<4; i++)
4681   {
4682     x = ax + xy[i][0];
4683     y = ay + xy[i][1];
4684
4685     if (!IN_LEV_FIELD(x, y))
4686       continue;
4687
4688     if ((Feld[x][y] == EL_AMOEBA_FULL ||
4689          Feld[x][y] == EL_BD_AMOEBA ||
4690          Feld[x][y] == EL_AMOEBA_DEAD) &&
4691         AmoebaNr[x][y] != new_group_nr)
4692     {
4693       int old_group_nr = AmoebaNr[x][y];
4694
4695       if (old_group_nr == 0)
4696         return;
4697
4698       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
4699       AmoebaCnt[old_group_nr] = 0;
4700       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
4701       AmoebaCnt2[old_group_nr] = 0;
4702
4703       for (yy=0; yy<lev_fieldy; yy++)
4704       {
4705         for (xx=0; xx<lev_fieldx; xx++)
4706         {
4707           if (AmoebaNr[xx][yy] == old_group_nr)
4708             AmoebaNr[xx][yy] = new_group_nr;
4709         }
4710       }
4711     }
4712   }
4713 }
4714
4715 void AmoebeUmwandeln(int ax, int ay)
4716 {
4717   int i, x, y;
4718
4719   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
4720   {
4721     int group_nr = AmoebaNr[ax][ay];
4722
4723 #ifdef DEBUG
4724     if (group_nr == 0)
4725     {
4726       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
4727       printf("AmoebeUmwandeln(): This should never happen!\n");
4728       return;
4729     }
4730 #endif
4731
4732     for (y=0; y<lev_fieldy; y++)
4733     {
4734       for (x=0; x<lev_fieldx; x++)
4735       {
4736         if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
4737         {
4738           AmoebaNr[x][y] = 0;
4739           Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
4740         }
4741       }
4742     }
4743     PlaySoundLevel(ax, ay, (IS_GEM(level.amoeba_content) ?
4744                             SND_AMOEBA_TURNING_TO_GEM :
4745                             SND_AMOEBA_TURNING_TO_ROCK));
4746     Bang(ax, ay);
4747   }
4748   else
4749   {
4750     static int xy[4][2] =
4751     {
4752       { 0, -1 },
4753       { -1, 0 },
4754       { +1, 0 },
4755       { 0, +1 }
4756     };
4757
4758     for (i=0; i<4; i++)
4759     {
4760       x = ax + xy[i][0];
4761       y = ay + xy[i][1];
4762
4763       if (!IN_LEV_FIELD(x, y))
4764         continue;
4765
4766       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
4767       {
4768         PlaySoundLevel(x, y, (IS_GEM(level.amoeba_content) ?
4769                               SND_AMOEBA_TURNING_TO_GEM :
4770                               SND_AMOEBA_TURNING_TO_ROCK));
4771         Bang(x, y);
4772       }
4773     }
4774   }
4775 }
4776
4777 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
4778 {
4779   int x, y;
4780   int group_nr = AmoebaNr[ax][ay];
4781   boolean done = FALSE;
4782
4783 #ifdef DEBUG
4784   if (group_nr == 0)
4785   {
4786     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
4787     printf("AmoebeUmwandelnBD(): This should never happen!\n");
4788     return;
4789   }
4790 #endif
4791
4792   for (y=0; y<lev_fieldy; y++)
4793   {
4794     for (x=0; x<lev_fieldx; x++)
4795     {
4796       if (AmoebaNr[x][y] == group_nr &&
4797           (Feld[x][y] == EL_AMOEBA_DEAD ||
4798            Feld[x][y] == EL_BD_AMOEBA ||
4799            Feld[x][y] == EL_AMOEBA_GROWING))
4800       {
4801         AmoebaNr[x][y] = 0;
4802         Feld[x][y] = new_element;
4803         InitField(x, y, FALSE);
4804         DrawLevelField(x, y);
4805         done = TRUE;
4806       }
4807     }
4808   }
4809
4810   if (done)
4811     PlaySoundLevel(ax, ay, (new_element == EL_BD_ROCK ?
4812                             SND_BD_AMOEBA_TURNING_TO_ROCK :
4813                             SND_BD_AMOEBA_TURNING_TO_GEM));
4814 }
4815
4816 void AmoebeWaechst(int x, int y)
4817 {
4818   static unsigned long sound_delay = 0;
4819   static unsigned long sound_delay_value = 0;
4820
4821   if (!MovDelay[x][y])          /* start new growing cycle */
4822   {
4823     MovDelay[x][y] = 7;
4824
4825     if (DelayReached(&sound_delay, sound_delay_value))
4826     {
4827 #if 1
4828       PlaySoundLevelElementAction(x, y, Store[x][y], ACTION_GROWING);
4829 #else
4830       if (Store[x][y] == EL_BD_AMOEBA)
4831         PlaySoundLevel(x, y, SND_BD_AMOEBA_GROWING);
4832       else
4833         PlaySoundLevel(x, y, SND_AMOEBA_GROWING);
4834 #endif
4835       sound_delay_value = 30;
4836     }
4837   }
4838
4839   if (MovDelay[x][y])           /* wait some time before growing bigger */
4840   {
4841     MovDelay[x][y]--;
4842     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4843     {
4844       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
4845                                            6 - MovDelay[x][y]);
4846
4847       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
4848     }
4849
4850     if (!MovDelay[x][y])
4851     {
4852       Feld[x][y] = Store[x][y];
4853       Store[x][y] = 0;
4854       DrawLevelField(x, y);
4855     }
4856   }
4857 }
4858
4859 void AmoebaDisappearing(int x, int y)
4860 {
4861   static unsigned long sound_delay = 0;
4862   static unsigned long sound_delay_value = 0;
4863
4864   if (!MovDelay[x][y])          /* start new shrinking cycle */
4865   {
4866     MovDelay[x][y] = 7;
4867
4868     if (DelayReached(&sound_delay, sound_delay_value))
4869       sound_delay_value = 30;
4870   }
4871
4872   if (MovDelay[x][y])           /* wait some time before shrinking */
4873   {
4874     MovDelay[x][y]--;
4875     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4876     {
4877       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
4878                                            6 - MovDelay[x][y]);
4879
4880       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
4881     }
4882
4883     if (!MovDelay[x][y])
4884     {
4885       Feld[x][y] = EL_EMPTY;
4886       DrawLevelField(x, y);
4887
4888       /* don't let mole enter this field in this cycle;
4889          (give priority to objects falling to this field from above) */
4890       Stop[x][y] = TRUE;
4891     }
4892   }
4893 }
4894
4895 void AmoebeAbleger(int ax, int ay)
4896 {
4897   int i;
4898   int element = Feld[ax][ay];
4899   int graphic = el2img(element);
4900   int newax = ax, neway = ay;
4901   static int xy[4][2] =
4902   {
4903     { 0, -1 },
4904     { -1, 0 },
4905     { +1, 0 },
4906     { 0, +1 }
4907   };
4908
4909   if (!level.amoeba_speed)
4910   {
4911     Feld[ax][ay] = EL_AMOEBA_DEAD;
4912     DrawLevelField(ax, ay);
4913     return;
4914   }
4915
4916   if (IS_ANIMATED(graphic))
4917     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
4918
4919   if (!MovDelay[ax][ay])        /* start making new amoeba field */
4920     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
4921
4922   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
4923   {
4924     MovDelay[ax][ay]--;
4925     if (MovDelay[ax][ay])
4926       return;
4927   }
4928
4929   if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
4930   {
4931     int start = RND(4);
4932     int x = ax + xy[start][0];
4933     int y = ay + xy[start][1];
4934
4935     if (!IN_LEV_FIELD(x, y))
4936       return;
4937
4938     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
4939     if (IS_FREE(x, y) ||
4940         Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
4941     {
4942       newax = x;
4943       neway = y;
4944     }
4945
4946     if (newax == ax && neway == ay)
4947       return;
4948   }
4949   else                          /* normal or "filled" (BD style) amoeba */
4950   {
4951     int start = RND(4);
4952     boolean waiting_for_player = FALSE;
4953
4954     for (i=0; i<4; i++)
4955     {
4956       int j = (start + i) % 4;
4957       int x = ax + xy[j][0];
4958       int y = ay + xy[j][1];
4959
4960       if (!IN_LEV_FIELD(x, y))
4961         continue;
4962
4963       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
4964       if (IS_FREE(x, y) ||
4965           Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
4966       {
4967         newax = x;
4968         neway = y;
4969         break;
4970       }
4971       else if (IS_PLAYER(x, y))
4972         waiting_for_player = TRUE;
4973     }
4974
4975     if (newax == ax && neway == ay)             /* amoeba cannot grow */
4976     {
4977       if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
4978       {
4979         Feld[ax][ay] = EL_AMOEBA_DEAD;
4980         DrawLevelField(ax, ay);
4981         AmoebaCnt[AmoebaNr[ax][ay]]--;
4982
4983         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
4984         {
4985           if (element == EL_AMOEBA_FULL)
4986             AmoebeUmwandeln(ax, ay);
4987           else if (element == EL_BD_AMOEBA)
4988             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
4989         }
4990       }
4991       return;
4992     }
4993     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
4994     {
4995       /* amoeba gets larger by growing in some direction */
4996
4997       int new_group_nr = AmoebaNr[ax][ay];
4998
4999 #ifdef DEBUG
5000   if (new_group_nr == 0)
5001   {
5002     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5003     printf("AmoebeAbleger(): This should never happen!\n");
5004     return;
5005   }
5006 #endif
5007
5008       AmoebaNr[newax][neway] = new_group_nr;
5009       AmoebaCnt[new_group_nr]++;
5010       AmoebaCnt2[new_group_nr]++;
5011
5012       /* if amoeba touches other amoeba(s) after growing, unify them */
5013       AmoebenVereinigen(newax, neway);
5014
5015       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5016       {
5017         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5018         return;
5019       }
5020     }
5021   }
5022
5023   if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5024       (neway == lev_fieldy - 1 && newax != ax))
5025   {
5026     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
5027     Store[newax][neway] = element;
5028   }
5029   else if (neway == ay)
5030   {
5031     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
5032 #if 1
5033     PlaySoundLevelAction(newax, neway, ACTION_GROWING);
5034 #else
5035     PlaySoundLevel(newax, neway, SND_AMOEBA_GROWING);
5036 #endif
5037   }
5038   else
5039   {
5040     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
5041     Feld[ax][ay] = EL_AMOEBA_DROPPING;
5042     Store[ax][ay] = EL_AMOEBA_DROP;
5043     ContinueMoving(ax, ay);
5044     return;
5045   }
5046
5047   DrawLevelField(newax, neway);
5048 }
5049
5050 void Life(int ax, int ay)
5051 {
5052   int x1, y1, x2, y2;
5053   static int life[4] = { 2, 3, 3, 3 };  /* parameters for "game of life" */
5054   int life_time = 40;
5055   int element = Feld[ax][ay];
5056   int graphic = el2img(element);
5057   boolean changed = FALSE;
5058
5059   if (IS_ANIMATED(graphic))
5060     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5061
5062   if (Stop[ax][ay])
5063     return;
5064
5065   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
5066     MovDelay[ax][ay] = life_time;
5067
5068   if (MovDelay[ax][ay])         /* wait some time before next cycle */
5069   {
5070     MovDelay[ax][ay]--;
5071     if (MovDelay[ax][ay])
5072       return;
5073   }
5074
5075   for (y1=-1; y1<2; y1++) for(x1=-1; x1<2; x1++)
5076   {
5077     int xx = ax+x1, yy = ay+y1;
5078     int nachbarn = 0;
5079
5080     if (!IN_LEV_FIELD(xx, yy))
5081       continue;
5082
5083     for (y2=-1; y2<2; y2++) for (x2=-1; x2<2; x2++)
5084     {
5085       int x = xx+x2, y = yy+y2;
5086
5087       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5088         continue;
5089
5090       if (((Feld[x][y] == element ||
5091             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5092            !Stop[x][y]) ||
5093           (IS_FREE(x, y) && Stop[x][y]))
5094         nachbarn++;
5095     }
5096
5097     if (xx == ax && yy == ay)           /* field in the middle */
5098     {
5099       if (nachbarn < life[0] || nachbarn > life[1])
5100       {
5101         Feld[xx][yy] = EL_EMPTY;
5102         if (!Stop[xx][yy])
5103           DrawLevelField(xx, yy);
5104         Stop[xx][yy] = TRUE;
5105         changed = TRUE;
5106       }
5107     }
5108     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5109     else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5110     {                                   /* free border field */
5111       if (nachbarn >= life[2] && nachbarn <= life[3])
5112       {
5113         Feld[xx][yy] = element;
5114         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5115         if (!Stop[xx][yy])
5116           DrawLevelField(xx, yy);
5117         Stop[xx][yy] = TRUE;
5118         changed = TRUE;
5119       }
5120     }
5121   }
5122
5123   if (changed)
5124     PlaySoundLevel(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5125                    SND_GAME_OF_LIFE_GROWING);
5126 }
5127
5128 static void InitRobotWheel(int x, int y)
5129 {
5130   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5131 }
5132
5133 static void RunRobotWheel(int x, int y)
5134 {
5135   PlaySoundLevel(x, y, SND_ROBOT_WHEEL_ACTIVE);
5136 }
5137
5138 static void StopRobotWheel(int x, int y)
5139 {
5140   if (ZX == x && ZY == y)
5141     ZX = ZY = -1;
5142 }
5143
5144 static void InitTimegateWheel(int x, int y)
5145 {
5146   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5147 }
5148
5149 static void RunTimegateWheel(int x, int y)
5150 {
5151   PlaySoundLevel(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5152 }
5153
5154 void CheckExit(int x, int y)
5155 {
5156   if (local_player->gems_still_needed > 0 ||
5157       local_player->sokobanfields_still_needed > 0 ||
5158       local_player->lights_still_needed > 0)
5159   {
5160     int element = Feld[x][y];
5161     int graphic = el2img(element);
5162
5163     if (IS_ANIMATED(graphic))
5164       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5165
5166     return;
5167   }
5168
5169   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
5170     return;
5171
5172   Feld[x][y] = EL_EXIT_OPENING;
5173
5174   PlaySoundLevelNearest(x, y, SND_CLASS_EXIT_OPENING);
5175 }
5176
5177 void CheckExitSP(int x, int y)
5178 {
5179   if (local_player->gems_still_needed > 0)
5180   {
5181     int element = Feld[x][y];
5182     int graphic = el2img(element);
5183
5184     if (IS_ANIMATED(graphic))
5185       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5186
5187     return;
5188   }
5189
5190   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
5191     return;
5192
5193   Feld[x][y] = EL_SP_EXIT_OPENING;
5194
5195   PlaySoundLevelNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5196 }
5197
5198 static void CloseAllOpenTimegates()
5199 {
5200   int x, y;
5201
5202   for (y=0; y<lev_fieldy; y++)
5203   {
5204     for (x=0; x<lev_fieldx; x++)
5205     {
5206       int element = Feld[x][y];
5207
5208       if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5209       {
5210         Feld[x][y] = EL_TIMEGATE_CLOSING;
5211 #if 1
5212         PlaySoundLevelAction(x, y, ACTION_CLOSING);
5213 #else
5214         PlaySoundLevel(x, y, SND_TIMEGATE_CLOSING);
5215 #endif
5216       }
5217     }
5218   }
5219 }
5220
5221 void EdelsteinFunkeln(int x, int y)
5222 {
5223   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5224     return;
5225
5226   if (Feld[x][y] == EL_BD_DIAMOND)
5227     return;
5228
5229   if (MovDelay[x][y] == 0)      /* next animation frame */
5230     MovDelay[x][y] = 11 * !SimpleRND(500);
5231
5232   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
5233   {
5234     MovDelay[x][y]--;
5235
5236     if (setup.direct_draw && MovDelay[x][y])
5237       SetDrawtoField(DRAW_BUFFERED);
5238
5239     DrawLevelElementAnimation(x, y, Feld[x][y]);
5240
5241     if (MovDelay[x][y] != 0)
5242     {
5243       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5244                                            10 - MovDelay[x][y]);
5245
5246       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5247
5248       if (setup.direct_draw)
5249       {
5250         int dest_x, dest_y;
5251
5252         dest_x = FX + SCREENX(x) * TILEX;
5253         dest_y = FY + SCREENY(y) * TILEY;
5254
5255         BlitBitmap(drawto_field, window,
5256                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5257         SetDrawtoField(DRAW_DIRECT);
5258       }
5259     }
5260   }
5261 }
5262
5263 void MauerWaechst(int x, int y)
5264 {
5265   int delay = 6;
5266
5267   if (!MovDelay[x][y])          /* next animation frame */
5268     MovDelay[x][y] = 3 * delay;
5269
5270   if (MovDelay[x][y])           /* wait some time before next frame */
5271   {
5272     MovDelay[x][y]--;
5273
5274     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5275     {
5276       int graphic = el_dir2img(Feld[x][y], MovDir[x][y]);
5277       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5278
5279       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5280     }
5281
5282     if (!MovDelay[x][y])
5283     {
5284       if (MovDir[x][y] == MV_LEFT)
5285       {
5286         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5287           DrawLevelField(x - 1, y);
5288       }
5289       else if (MovDir[x][y] == MV_RIGHT)
5290       {
5291         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5292           DrawLevelField(x + 1, y);
5293       }
5294       else if (MovDir[x][y] == MV_UP)
5295       {
5296         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5297           DrawLevelField(x, y - 1);
5298       }
5299       else
5300       {
5301         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5302           DrawLevelField(x, y + 1);
5303       }
5304
5305       Feld[x][y] = Store[x][y];
5306       Store[x][y] = 0;
5307       MovDir[x][y] = MV_NO_MOVING;
5308       DrawLevelField(x, y);
5309     }
5310   }
5311 }
5312
5313 void MauerAbleger(int ax, int ay)
5314 {
5315   int element = Feld[ax][ay];
5316   int graphic = el2img(element);
5317   boolean oben_frei = FALSE, unten_frei = FALSE;
5318   boolean links_frei = FALSE, rechts_frei = FALSE;
5319   boolean oben_massiv = FALSE, unten_massiv = FALSE;
5320   boolean links_massiv = FALSE, rechts_massiv = FALSE;
5321   boolean new_wall = FALSE;
5322
5323   if (IS_ANIMATED(graphic))
5324     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5325
5326   if (!MovDelay[ax][ay])        /* start building new wall */
5327     MovDelay[ax][ay] = 6;
5328
5329   if (MovDelay[ax][ay])         /* wait some time before building new wall */
5330   {
5331     MovDelay[ax][ay]--;
5332     if (MovDelay[ax][ay])
5333       return;
5334   }
5335
5336   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5337     oben_frei = TRUE;
5338   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5339     unten_frei = TRUE;
5340   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5341     links_frei = TRUE;
5342   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5343     rechts_frei = TRUE;
5344
5345   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5346       element == EL_EXPANDABLE_WALL_ANY)
5347   {
5348     if (oben_frei)
5349     {
5350       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5351       Store[ax][ay-1] = element;
5352       MovDir[ax][ay-1] = MV_UP;
5353       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5354         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5355                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5356       new_wall = TRUE;
5357     }
5358     if (unten_frei)
5359     {
5360       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5361       Store[ax][ay+1] = element;
5362       MovDir[ax][ay+1] = MV_DOWN;
5363       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5364         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5365                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5366       new_wall = TRUE;
5367     }
5368   }
5369
5370   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5371       element == EL_EXPANDABLE_WALL_ANY ||
5372       element == EL_EXPANDABLE_WALL)
5373   {
5374     if (links_frei)
5375     {
5376       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5377       Store[ax-1][ay] = element;
5378       MovDir[ax-1][ay] = MV_LEFT;
5379       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5380         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5381                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5382       new_wall = TRUE;
5383     }
5384
5385     if (rechts_frei)
5386     {
5387       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5388       Store[ax+1][ay] = element;
5389       MovDir[ax+1][ay] = MV_RIGHT;
5390       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5391         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5392                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5393       new_wall = TRUE;
5394     }
5395   }
5396
5397   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5398     DrawLevelField(ax, ay);
5399
5400   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5401     oben_massiv = TRUE;
5402   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5403     unten_massiv = TRUE;
5404   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5405     links_massiv = TRUE;
5406   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5407     rechts_massiv = TRUE;
5408
5409   if (((oben_massiv && unten_massiv) ||
5410        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5411        element == EL_EXPANDABLE_WALL) &&
5412       ((links_massiv && rechts_massiv) ||
5413        element == EL_EXPANDABLE_WALL_VERTICAL))
5414     Feld[ax][ay] = EL_WALL;
5415
5416   if (new_wall)
5417 #if 1
5418     PlaySoundLevelAction(ax, ay, ACTION_GROWING);
5419 #else
5420     PlaySoundLevel(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5421 #endif
5422 }
5423
5424 void CheckForDragon(int x, int y)
5425 {
5426   int i, j;
5427   boolean dragon_found = FALSE;
5428   static int xy[4][2] =
5429   {
5430     { 0, -1 },
5431     { -1, 0 },
5432     { +1, 0 },
5433     { 0, +1 }
5434   };
5435
5436   for (i=0; i<4; i++)
5437   {
5438     for (j=0; j<4; j++)
5439     {
5440       int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5441
5442       if (IN_LEV_FIELD(xx, yy) &&
5443           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
5444       {
5445         if (Feld[xx][yy] == EL_DRAGON)
5446           dragon_found = TRUE;
5447       }
5448       else
5449         break;
5450     }
5451   }
5452
5453   if (!dragon_found)
5454   {
5455     for (i=0; i<4; i++)
5456     {
5457       for (j=0; j<3; j++)
5458       {
5459         int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5460   
5461         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
5462         {
5463           Feld[xx][yy] = EL_EMPTY;
5464           DrawLevelField(xx, yy);
5465         }
5466         else
5467           break;
5468       }
5469     }
5470   }
5471 }
5472
5473 static void InitBuggyBase(int x, int y)
5474 {
5475   int element = Feld[x][y];
5476   int activating_delay = FRAMES_PER_SECOND / 4;
5477
5478   ChangeDelay[x][y] =
5479     (element == EL_SP_BUGGY_BASE ?
5480      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
5481      element == EL_SP_BUGGY_BASE_ACTIVATING ?
5482      activating_delay :
5483      element == EL_SP_BUGGY_BASE_ACTIVE ?
5484      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
5485 }
5486
5487 static void WarnBuggyBase(int x, int y)
5488 {
5489   int i;
5490   static int xy[4][2] =
5491   {
5492     { 0, -1 },
5493     { -1, 0 },
5494     { +1, 0 },
5495     { 0, +1 }
5496   };
5497
5498   for (i=0; i<4; i++)
5499   {
5500     int xx = x + xy[i][0], yy = y + xy[i][1];
5501
5502     if (IS_PLAYER(xx, yy))
5503     {
5504       PlaySoundLevel(x, y, SND_SP_BUGGY_BASE_ACTIVE);
5505
5506       break;
5507     }
5508   }
5509 }
5510
5511 static void InitTrap(int x, int y)
5512 {
5513   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
5514 }
5515
5516 static void ActivateTrap(int x, int y)
5517 {
5518   PlaySoundLevel(x, y, SND_TRAP_ACTIVATING);
5519 }
5520
5521 static void ChangeActiveTrap(int x, int y)
5522 {
5523   int graphic = IMG_TRAP_ACTIVE;
5524
5525   /* if new animation frame was drawn, correct crumbled sand border */
5526   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
5527     DrawLevelFieldCrumbledSand(x, y);
5528 }
5529
5530 static void ChangeElementNowExt(int x, int y, int target_element)
5531 {
5532   /* check if element under player changes from accessible to unaccessible
5533      (needed for special case of dropping element which then changes) */
5534   if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
5535       IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
5536   {
5537     Bang(x, y);
5538     return;
5539   }
5540
5541   RemoveField(x, y);
5542   Feld[x][y] = target_element;
5543
5544   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
5545
5546   ResetGfxAnimation(x, y);
5547   ResetRandomAnimationValue(x, y);
5548
5549   InitField(x, y, FALSE);
5550   if (CAN_MOVE(Feld[x][y]))
5551     InitMovDir(x, y);
5552
5553   DrawLevelField(x, y);
5554
5555   if (GFX_CRUMBLED(Feld[x][y]))
5556     DrawLevelFieldCrumbledSandNeighbours(x, y);
5557
5558   TestIfBadThingTouchesHero(x, y);
5559   TestIfPlayerTouchesCustomElement(x, y);
5560   TestIfElementTouchesCustomElement(x, y);
5561
5562   if (ELEM_IS_PLAYER(target_element))
5563     RelocatePlayer(x, y, target_element);
5564 }
5565
5566 static boolean ChangeElementNow(int x, int y, int element, int page)
5567 {
5568   struct ElementChangeInfo *change = &element_info[element].change_page[page];
5569
5570   /* always use default change event to prevent running into a loop */
5571   if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
5572     ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
5573
5574   /* do not change already changed elements with same change event */
5575 #if 0
5576   if (Changed[x][y] & ChangeEvent[x][y])
5577     return FALSE;
5578 #else
5579   if (Changed[x][y])
5580     return FALSE;
5581 #endif
5582
5583   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
5584
5585   CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
5586
5587   if (change->explode)
5588   {
5589     Bang(x, y);
5590
5591     return TRUE;
5592   }
5593
5594   if (change->use_content)
5595   {
5596     boolean complete_change = TRUE;
5597     boolean can_change[3][3];
5598     int xx, yy;
5599
5600     for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5601     {
5602       boolean half_destructible;
5603       int ex = x + xx - 1;
5604       int ey = y + yy - 1;
5605       int e;
5606
5607       can_change[xx][yy] = TRUE;
5608
5609       if (ex == x && ey == y)   /* do not check changing element itself */
5610         continue;
5611
5612       if (change->content[xx][yy] == EL_EMPTY_SPACE)
5613       {
5614         can_change[xx][yy] = FALSE;     /* do not change empty borders */
5615
5616         continue;
5617       }
5618
5619       if (!IN_LEV_FIELD(ex, ey))
5620       {
5621         can_change[xx][yy] = FALSE;
5622         complete_change = FALSE;
5623
5624         continue;
5625       }
5626
5627       e = Feld[ex][ey];
5628
5629       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5630         e = MovingOrBlocked2Element(ex, ey);
5631
5632       half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
5633
5634       if ((change->power <= CP_NON_DESTRUCTIVE  && !IS_FREE(ex, ey)) ||
5635           (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
5636           (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
5637       {
5638         can_change[xx][yy] = FALSE;
5639         complete_change = FALSE;
5640       }
5641     }
5642
5643     if (!change->only_complete || complete_change)
5644     {
5645       boolean something_has_changed = FALSE;
5646
5647       if (change->only_complete && change->use_random_change &&
5648           RND(100) < change->random)
5649         return FALSE;
5650
5651       for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5652       {
5653         int ex = x + xx - 1;
5654         int ey = y + yy - 1;
5655
5656         if (can_change[xx][yy] && (!change->use_random_change ||
5657                                    RND(100) < change->random))
5658         {
5659           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5660             RemoveMovingField(ex, ey);
5661
5662           ChangeEvent[ex][ey] = ChangeEvent[x][y];
5663
5664           ChangeElementNowExt(ex, ey, change->content[xx][yy]);
5665
5666           something_has_changed = TRUE;
5667
5668           /* for symmetry reasons, freeze newly created border elements */
5669           if (ex != x || ey != y)
5670             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
5671         }
5672       }
5673
5674       if (something_has_changed)
5675         PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5676     }
5677   }
5678   else
5679   {
5680     ChangeElementNowExt(x, y, change->target_element);
5681
5682     PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5683   }
5684
5685   return TRUE;
5686 }
5687
5688 static void ChangeElement(int x, int y, int page)
5689 {
5690   int element = MovingOrBlocked2Element(x, y);
5691   struct ElementInfo *ei = &element_info[element];
5692   struct ElementChangeInfo *change = &ei->change_page[page];
5693
5694 #if 0
5695 #ifdef DEBUG
5696   if (!CAN_CHANGE(element))
5697   {
5698     printf("\n\n");
5699     printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
5700            x, y, element, element_info[element].token_name);
5701     printf("ChangeElement(): This should never happen!\n");
5702     printf("\n\n");
5703   }
5704 #endif
5705 #endif
5706
5707   if (ChangeDelay[x][y] == 0)           /* initialize element change */
5708   {
5709     ChangeDelay[x][y] = (    change->delay_fixed  * change->delay_frames +
5710                          RND(change->delay_random * change->delay_frames)) + 1;
5711
5712     ResetGfxAnimation(x, y);
5713     ResetRandomAnimationValue(x, y);
5714
5715     if (change->pre_change_function)
5716       change->pre_change_function(x, y);
5717   }
5718
5719   ChangeDelay[x][y]--;
5720
5721   if (ChangeDelay[x][y] != 0)           /* continue element change */
5722   {
5723     int graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
5724
5725     if (IS_ANIMATED(graphic))
5726       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5727
5728     if (change->change_function)
5729       change->change_function(x, y);
5730   }
5731   else                                  /* finish element change */
5732   {
5733     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
5734     {
5735       page = ChangePage[x][y];
5736       ChangePage[x][y] = -1;
5737     }
5738
5739     if (IS_MOVING(x, y))                /* never change a running system ;-) */
5740     {
5741       ChangeDelay[x][y] = 1;            /* try change after next move step */
5742       ChangePage[x][y] = page;          /* remember page to use for change */
5743
5744       return;
5745     }
5746
5747     if (ChangeElementNow(x, y, element, page))
5748     {
5749       if (change->post_change_function)
5750         change->post_change_function(x, y);
5751     }
5752   }
5753 }
5754
5755 static boolean CheckTriggeredElementSideChange(int lx, int ly,
5756                                                int trigger_element,
5757                                                int trigger_side,
5758                                                int trigger_event)
5759 {
5760   int i, j, x, y;
5761
5762   if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
5763     return FALSE;
5764
5765   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
5766   {
5767     int element = EL_CUSTOM_START + i;
5768
5769     boolean change_element = FALSE;
5770     int page = 0;
5771
5772     if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5773       continue;
5774
5775     for (j=0; j < element_info[element].num_change_pages; j++)
5776     {
5777       struct ElementChangeInfo *change = &element_info[element].change_page[j];
5778
5779       if (change->can_change &&
5780 #if 1
5781           change->events & CH_EVENT_BIT(trigger_event) &&
5782 #endif
5783           change->sides & trigger_side &&
5784           change->trigger_element == trigger_element)
5785       {
5786 #if 0
5787         if (!(change->events & CH_EVENT_BIT(trigger_event)))
5788           printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
5789                  trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
5790 #endif
5791
5792         change_element = TRUE;
5793         page = j;
5794
5795         break;
5796       }
5797     }
5798
5799     if (!change_element)
5800       continue;
5801
5802     for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
5803     {
5804 #if 0
5805       if (x == lx && y == ly)   /* do not change trigger element itself */
5806         continue;
5807 #endif
5808
5809       if (Feld[x][y] == element)
5810       {
5811         ChangeDelay[x][y] = 1;
5812         ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5813         ChangeElement(x, y, page);
5814       }
5815     }
5816   }
5817
5818   return TRUE;
5819 }
5820
5821 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
5822                                            int trigger_event)
5823 {
5824   return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
5825                                          trigger_event);
5826 }
5827
5828 static boolean CheckElementSideChange(int x, int y, int element, int side,
5829                                       int trigger_event, int page)
5830 {
5831   if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5832     return FALSE;
5833
5834   if (Feld[x][y] == EL_BLOCKED)
5835   {
5836     Blocked2Moving(x, y, &x, &y);
5837     element = Feld[x][y];
5838   }
5839
5840   if (page < 0)
5841     page = element_info[element].event_page_nr[trigger_event];
5842
5843   if (!(element_info[element].change_page[page].sides & side))
5844     return FALSE;
5845
5846   ChangeDelay[x][y] = 1;
5847   ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5848   ChangeElement(x, y, page);
5849
5850   return TRUE;
5851 }
5852
5853 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
5854 {
5855   return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
5856 }
5857
5858 static void PlayerActions(struct PlayerInfo *player, byte player_action)
5859 {
5860   static byte stored_player_action[MAX_PLAYERS];
5861   static int num_stored_actions = 0;
5862   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
5863   int left      = player_action & JOY_LEFT;
5864   int right     = player_action & JOY_RIGHT;
5865   int up        = player_action & JOY_UP;
5866   int down      = player_action & JOY_DOWN;
5867   int button1   = player_action & JOY_BUTTON_1;
5868   int button2   = player_action & JOY_BUTTON_2;
5869   int dx        = (left ? -1    : right ? 1     : 0);
5870   int dy        = (up   ? -1    : down  ? 1     : 0);
5871
5872   stored_player_action[player->index_nr] = 0;
5873   num_stored_actions++;
5874
5875   if (!player->active || tape.pausing)
5876     return;
5877
5878   if (player_action)
5879   {
5880     if (button1)
5881       snapped = SnapField(player, dx, dy);
5882     else
5883     {
5884       if (button2)
5885         dropped = DropElement(player);
5886
5887       moved = MovePlayer(player, dx, dy);
5888     }
5889
5890     if (tape.single_step && tape.recording && !tape.pausing)
5891     {
5892       if (button1 || (dropped && !moved))
5893       {
5894         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
5895         SnapField(player, 0, 0);                /* stop snapping */
5896       }
5897     }
5898
5899     stored_player_action[player->index_nr] = player_action;
5900   }
5901   else
5902   {
5903     /* no actions for this player (no input at player's configured device) */
5904
5905     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
5906     SnapField(player, 0, 0);
5907     CheckGravityMovement(player);
5908
5909     if (player->MovPos == 0)
5910       InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
5911
5912     if (player->MovPos == 0)    /* needed for tape.playing */
5913       player->is_moving = FALSE;
5914   }
5915
5916   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
5917   {
5918     TapeRecordAction(stored_player_action);
5919     num_stored_actions = 0;
5920   }
5921 }
5922
5923 void GameActions()
5924 {
5925   static unsigned long action_delay = 0;
5926   unsigned long action_delay_value;
5927   int magic_wall_x = 0, magic_wall_y = 0;
5928   int i, x, y, element, graphic;
5929   byte *recorded_player_action;
5930   byte summarized_player_action = 0;
5931
5932   if (game_status != GAME_MODE_PLAYING)
5933     return;
5934
5935   action_delay_value =
5936     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
5937
5938   if (tape.playing && tape.index_search && !tape.pausing)
5939     action_delay_value = 0;
5940
5941   /* ---------- main game synchronization point ---------- */
5942
5943   WaitUntilDelayReached(&action_delay, action_delay_value);
5944
5945   if (network_playing && !network_player_action_received)
5946   {
5947     /*
5948 #ifdef DEBUG
5949     printf("DEBUG: try to get network player actions in time\n");
5950 #endif
5951     */
5952
5953 #if defined(PLATFORM_UNIX)
5954     /* last chance to get network player actions without main loop delay */
5955     HandleNetworking();
5956 #endif
5957
5958     if (game_status != GAME_MODE_PLAYING)
5959       return;
5960
5961     if (!network_player_action_received)
5962     {
5963       /*
5964 #ifdef DEBUG
5965       printf("DEBUG: failed to get network player actions in time\n");
5966 #endif
5967       */
5968       return;
5969     }
5970   }
5971
5972   if (tape.pausing)
5973     return;
5974
5975   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
5976
5977   for (i=0; i<MAX_PLAYERS; i++)
5978   {
5979     summarized_player_action |= stored_player[i].action;
5980
5981     if (!network_playing)
5982       stored_player[i].effective_action = stored_player[i].action;
5983   }
5984
5985 #if defined(PLATFORM_UNIX)
5986   if (network_playing)
5987     SendToServer_MovePlayer(summarized_player_action);
5988 #endif
5989
5990   if (!options.network && !setup.team_mode)
5991     local_player->effective_action = summarized_player_action;
5992
5993   for (i=0; i<MAX_PLAYERS; i++)
5994   {
5995     int actual_player_action = stored_player[i].effective_action;
5996
5997     if (stored_player[i].programmed_action)
5998       actual_player_action = stored_player[i].programmed_action;
5999
6000     if (recorded_player_action)
6001       actual_player_action = recorded_player_action[i];
6002
6003     PlayerActions(&stored_player[i], actual_player_action);
6004     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6005   }
6006
6007   network_player_action_received = FALSE;
6008
6009   ScrollScreen(NULL, SCROLL_GO_ON);
6010
6011 #if 0
6012   FrameCounter++;
6013   TimeFrames++;
6014
6015   for (i=0; i<MAX_PLAYERS; i++)
6016     stored_player[i].Frame++;
6017 #endif
6018
6019 #if 1
6020   if (game.engine_version < VERSION_IDENT(2,2,0,7))
6021   {
6022     for (i=0; i<MAX_PLAYERS; i++)
6023     {
6024       struct PlayerInfo *player = &stored_player[i];
6025       int x = player->jx;
6026       int y = player->jy;
6027
6028       if (player->active && player->is_pushing && player->is_moving &&
6029           IS_MOVING(x, y))
6030       {
6031         ContinueMoving(x, y);
6032
6033         /* continue moving after pushing (this is actually a bug) */
6034         if (!IS_MOVING(x, y))
6035         {
6036           Stop[x][y] = FALSE;
6037         }
6038       }
6039     }
6040   }
6041 #endif
6042
6043   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6044   {
6045     Changed[x][y] = CE_BITMASK_DEFAULT;
6046     ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6047
6048 #if DEBUG
6049     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6050     {
6051       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6052       printf("GameActions(): This should never happen!\n");
6053
6054       ChangePage[x][y] = -1;
6055     }
6056 #endif
6057
6058     Stop[x][y] = FALSE;
6059     if (WasJustMoving[x][y] > 0)
6060       WasJustMoving[x][y]--;
6061     if (WasJustFalling[x][y] > 0)
6062       WasJustFalling[x][y]--;
6063
6064     GfxFrame[x][y]++;
6065
6066 #if 1
6067     /* reset finished pushing action (not done in ContinueMoving() to allow
6068        continous pushing animation for elements with zero push delay) */
6069     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6070     {
6071       ResetGfxAnimation(x, y);
6072       DrawLevelField(x, y);
6073     }
6074 #endif
6075
6076 #if DEBUG
6077     if (IS_BLOCKED(x, y))
6078     {
6079       int oldx, oldy;
6080
6081       Blocked2Moving(x, y, &oldx, &oldy);
6082       if (!IS_MOVING(oldx, oldy))
6083       {
6084         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6085         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6086         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6087         printf("GameActions(): This should never happen!\n");
6088       }
6089     }
6090 #endif
6091   }
6092
6093   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6094   {
6095     element = Feld[x][y];
6096 #if 1
6097     graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
6098 #else
6099     graphic = el2img(element);
6100 #endif
6101
6102 #if 0
6103     if (element == -1)
6104     {
6105       printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6106
6107       element = graphic = 0;
6108     }
6109 #endif
6110
6111     if (graphic_info[graphic].anim_global_sync)
6112       GfxFrame[x][y] = FrameCounter;
6113
6114     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6115         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6116       ResetRandomAnimationValue(x, y);
6117
6118     SetRandomAnimationValue(x, y);
6119
6120 #if 1
6121     PlaySoundLevelActionIfLoop(x, y, GfxAction[x][y]);
6122 #endif
6123
6124     if (IS_INACTIVE(element))
6125     {
6126       if (IS_ANIMATED(graphic))
6127         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6128
6129       continue;
6130     }
6131
6132 #if 1
6133     /* this may take place after moving, so 'element' may have changed */
6134     if (IS_CHANGING(x, y))
6135     {
6136 #if 0
6137       ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6138                     element_info[element].event_page_nr[CE_DELAY]);
6139 #else
6140       ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6141 #endif
6142
6143       element = Feld[x][y];
6144       graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
6145     }
6146 #endif
6147
6148     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6149     {
6150       StartMoving(x, y);
6151
6152 #if 1
6153       graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
6154 #if 0
6155       if (element == EL_MOLE)
6156         printf("::: %d, %d, %d [%d]\n",
6157                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6158                GfxAction[x][y]);
6159 #endif
6160 #if 0
6161       if (element == EL_YAMYAM)
6162         printf("::: %d, %d, %d\n",
6163                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6164 #endif
6165 #endif
6166
6167       if (IS_ANIMATED(graphic) &&
6168           !IS_MOVING(x, y) &&
6169           !Stop[x][y])
6170       {
6171         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6172
6173 #if 0
6174         if (element == EL_MOLE)
6175           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6176 #endif
6177       }
6178
6179       if (IS_GEM(element) || element == EL_SP_INFOTRON)
6180         EdelsteinFunkeln(x, y);
6181     }
6182     else if ((element == EL_ACID ||
6183               element == EL_EXIT_OPEN ||
6184               element == EL_SP_EXIT_OPEN ||
6185               element == EL_SP_TERMINAL ||
6186               element == EL_SP_TERMINAL_ACTIVE ||
6187               element == EL_EXTRA_TIME ||
6188               element == EL_SHIELD_NORMAL ||
6189               element == EL_SHIELD_DEADLY) &&
6190              IS_ANIMATED(graphic))
6191       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6192     else if (IS_MOVING(x, y))
6193       ContinueMoving(x, y);
6194     else if (IS_ACTIVE_BOMB(element))
6195       CheckDynamite(x, y);
6196 #if 0
6197     else if (element == EL_EXPLOSION && !game.explosions_delayed)
6198       Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6199 #endif
6200     else if (element == EL_AMOEBA_GROWING)
6201       AmoebeWaechst(x, y);
6202     else if (element == EL_AMOEBA_SHRINKING)
6203       AmoebaDisappearing(x, y);
6204
6205 #if !USE_NEW_AMOEBA_CODE
6206     else if (IS_AMOEBALIVE(element))
6207       AmoebeAbleger(x, y);
6208 #endif
6209
6210     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6211       Life(x, y);
6212     else if (element == EL_EXIT_CLOSED)
6213       CheckExit(x, y);
6214     else if (element == EL_SP_EXIT_CLOSED)
6215       CheckExitSP(x, y);
6216     else if (element == EL_EXPANDABLE_WALL_GROWING)
6217       MauerWaechst(x, y);
6218     else if (element == EL_EXPANDABLE_WALL ||
6219              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6220              element == EL_EXPANDABLE_WALL_VERTICAL ||
6221              element == EL_EXPANDABLE_WALL_ANY)
6222       MauerAbleger(x, y);
6223     else if (element == EL_FLAMES)
6224       CheckForDragon(x, y);
6225 #if 0
6226     else if (IS_AUTO_CHANGING(element))
6227       ChangeElement(x, y);
6228 #endif
6229     else if (element == EL_EXPLOSION)
6230       ; /* drawing of correct explosion animation is handled separately */
6231     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6232       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6233
6234 #if 0
6235     /* this may take place after moving, so 'element' may have changed */
6236     if (IS_AUTO_CHANGING(Feld[x][y]))
6237       ChangeElement(x, y);
6238 #endif
6239
6240     if (IS_BELT_ACTIVE(element))
6241       PlaySoundLevelAction(x, y, ACTION_ACTIVE);
6242
6243     if (game.magic_wall_active)
6244     {
6245       int jx = local_player->jx, jy = local_player->jy;
6246
6247       /* play the element sound at the position nearest to the player */
6248       if ((element == EL_MAGIC_WALL_FULL ||
6249            element == EL_MAGIC_WALL_ACTIVE ||
6250            element == EL_MAGIC_WALL_EMPTYING ||
6251            element == EL_BD_MAGIC_WALL_FULL ||
6252            element == EL_BD_MAGIC_WALL_ACTIVE ||
6253            element == EL_BD_MAGIC_WALL_EMPTYING) &&
6254           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6255       {
6256         magic_wall_x = x;
6257         magic_wall_y = y;
6258       }
6259     }
6260   }
6261
6262 #if USE_NEW_AMOEBA_CODE
6263   /* new experimental amoeba growth stuff */
6264 #if 1
6265   if (!(FrameCounter % 8))
6266 #endif
6267   {
6268     static unsigned long random = 1684108901;
6269
6270     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
6271     {
6272 #if 0
6273       x = (random >> 10) % lev_fieldx;
6274       y = (random >> 20) % lev_fieldy;
6275 #else
6276       x = RND(lev_fieldx);
6277       y = RND(lev_fieldy);
6278 #endif
6279       element = Feld[x][y];
6280
6281       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6282       if (!IS_PLAYER(x,y) &&
6283           (element == EL_EMPTY ||
6284            element == EL_SAND ||
6285            element == EL_QUICKSAND_EMPTY ||
6286            element == EL_ACID_SPLASH_LEFT ||
6287            element == EL_ACID_SPLASH_RIGHT))
6288       {
6289         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
6290             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
6291             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
6292             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
6293           Feld[x][y] = EL_AMOEBA_DROP;
6294       }
6295
6296       random = random * 129 + 1;
6297     }
6298   }
6299 #endif
6300
6301 #if 0
6302   if (game.explosions_delayed)
6303 #endif
6304   {
6305     game.explosions_delayed = FALSE;
6306
6307     for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6308     {
6309       element = Feld[x][y];
6310
6311       if (ExplodeField[x][y])
6312         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
6313       else if (element == EL_EXPLOSION)
6314         Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6315
6316       ExplodeField[x][y] = EX_NO_EXPLOSION;
6317     }
6318
6319     game.explosions_delayed = TRUE;
6320   }
6321
6322   if (game.magic_wall_active)
6323   {
6324     if (!(game.magic_wall_time_left % 4))
6325     {
6326       int element = Feld[magic_wall_x][magic_wall_y];
6327
6328       if (element == EL_BD_MAGIC_WALL_FULL ||
6329           element == EL_BD_MAGIC_WALL_ACTIVE ||
6330           element == EL_BD_MAGIC_WALL_EMPTYING)
6331         PlaySoundLevel(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
6332       else
6333         PlaySoundLevel(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
6334     }
6335
6336     if (game.magic_wall_time_left > 0)
6337     {
6338       game.magic_wall_time_left--;
6339       if (!game.magic_wall_time_left)
6340       {
6341         for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6342         {
6343           element = Feld[x][y];
6344
6345           if (element == EL_MAGIC_WALL_ACTIVE ||
6346               element == EL_MAGIC_WALL_FULL)
6347           {
6348             Feld[x][y] = EL_MAGIC_WALL_DEAD;
6349             DrawLevelField(x, y);
6350           }
6351           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
6352                    element == EL_BD_MAGIC_WALL_FULL)
6353           {
6354             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6355             DrawLevelField(x, y);
6356           }
6357         }
6358
6359         game.magic_wall_active = FALSE;
6360       }
6361     }
6362   }
6363
6364   if (game.light_time_left > 0)
6365   {
6366     game.light_time_left--;
6367
6368     if (game.light_time_left == 0)
6369       RedrawAllLightSwitchesAndInvisibleElements();
6370   }
6371
6372   if (game.timegate_time_left > 0)
6373   {
6374     game.timegate_time_left--;
6375
6376     if (game.timegate_time_left == 0)
6377       CloseAllOpenTimegates();
6378   }
6379
6380   for (i=0; i<MAX_PLAYERS; i++)
6381   {
6382     struct PlayerInfo *player = &stored_player[i];
6383
6384     if (SHIELD_ON(player))
6385     {
6386       if (player->shield_deadly_time_left)
6387         PlaySoundLevel(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
6388       else if (player->shield_normal_time_left)
6389         PlaySoundLevel(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
6390     }
6391   }
6392
6393   if (TimeFrames >= FRAMES_PER_SECOND)
6394   {
6395     TimeFrames = 0;
6396     TimePlayed++;
6397
6398     for (i=0; i<MAX_PLAYERS; i++)
6399     {
6400       struct PlayerInfo *player = &stored_player[i];
6401
6402       if (SHIELD_ON(player))
6403       {
6404         player->shield_normal_time_left--;
6405
6406         if (player->shield_deadly_time_left > 0)
6407           player->shield_deadly_time_left--;
6408       }
6409     }
6410
6411     if (tape.recording || tape.playing)
6412       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
6413
6414     if (TimeLeft > 0)
6415     {
6416       TimeLeft--;
6417
6418       if (TimeLeft <= 10 && setup.time_limit)
6419         PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
6420
6421       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
6422
6423       if (!TimeLeft && setup.time_limit)
6424         for (i=0; i<MAX_PLAYERS; i++)
6425           KillHero(&stored_player[i]);
6426     }
6427     else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
6428       DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
6429   }
6430
6431   DrawAllPlayers();
6432
6433   if (options.debug)                    /* calculate frames per second */
6434   {
6435     static unsigned long fps_counter = 0;
6436     static int fps_frames = 0;
6437     unsigned long fps_delay_ms = Counter() - fps_counter;
6438
6439     fps_frames++;
6440
6441     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
6442     {
6443       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
6444
6445       fps_frames = 0;
6446       fps_counter = Counter();
6447     }
6448
6449     redraw_mask |= REDRAW_FPS;
6450   }
6451
6452 #if 0
6453   if (stored_player[0].jx != stored_player[0].last_jx ||
6454       stored_player[0].jy != stored_player[0].last_jy)
6455     printf("::: %d, %d, %d, %d, %d\n",
6456            stored_player[0].MovDir,
6457            stored_player[0].MovPos,
6458            stored_player[0].GfxPos,
6459            stored_player[0].Frame,
6460            stored_player[0].StepFrame);
6461 #endif
6462
6463 #if 1
6464   FrameCounter++;
6465   TimeFrames++;
6466
6467   for (i=0; i<MAX_PLAYERS; i++)
6468   {
6469     int move_frames =
6470       MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
6471
6472     stored_player[i].Frame += move_frames;
6473
6474     if (stored_player[i].MovPos != 0)
6475       stored_player[i].StepFrame += move_frames;
6476   }
6477 #endif
6478
6479 #if 1
6480   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
6481   {
6482     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
6483
6484     local_player->show_envelope = 0;
6485   }
6486 #endif
6487 }
6488
6489 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
6490 {
6491   int min_x = x, min_y = y, max_x = x, max_y = y;
6492   int i;
6493
6494   for (i=0; i<MAX_PLAYERS; i++)
6495   {
6496     int jx = stored_player[i].jx, jy = stored_player[i].jy;
6497
6498     if (!stored_player[i].active || &stored_player[i] == player)
6499       continue;
6500
6501     min_x = MIN(min_x, jx);
6502     min_y = MIN(min_y, jy);
6503     max_x = MAX(max_x, jx);
6504     max_y = MAX(max_y, jy);
6505   }
6506
6507   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
6508 }
6509
6510 static boolean AllPlayersInVisibleScreen()
6511 {
6512   int i;
6513
6514   for (i=0; i<MAX_PLAYERS; i++)
6515   {
6516     int jx = stored_player[i].jx, jy = stored_player[i].jy;
6517
6518     if (!stored_player[i].active)
6519       continue;
6520
6521     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6522       return FALSE;
6523   }
6524
6525   return TRUE;
6526 }
6527
6528 void ScrollLevel(int dx, int dy)
6529 {
6530   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
6531   int x, y;
6532
6533   BlitBitmap(drawto_field, drawto_field,
6534              FX + TILEX * (dx == -1) - softscroll_offset,
6535              FY + TILEY * (dy == -1) - softscroll_offset,
6536              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
6537              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
6538              FX + TILEX * (dx == 1) - softscroll_offset,
6539              FY + TILEY * (dy == 1) - softscroll_offset);
6540
6541   if (dx)
6542   {
6543     x = (dx == 1 ? BX1 : BX2);
6544     for (y=BY1; y <= BY2; y++)
6545       DrawScreenField(x, y);
6546   }
6547
6548   if (dy)
6549   {
6550     y = (dy == 1 ? BY1 : BY2);
6551     for (x=BX1; x <= BX2; x++)
6552       DrawScreenField(x, y);
6553   }
6554
6555   redraw_mask |= REDRAW_FIELD;
6556 }
6557
6558 static void CheckGravityMovement(struct PlayerInfo *player)
6559 {
6560   if (game.gravity && !player->programmed_action)
6561   {
6562     int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
6563     int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
6564     int move_dir =
6565       (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
6566        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
6567        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
6568     int jx = player->jx, jy = player->jy;
6569     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
6570     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
6571     int new_jx = jx + dx, new_jy = jy + dy;
6572     boolean field_under_player_is_free =
6573       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
6574     boolean player_is_moving_to_valid_field =
6575       (IN_LEV_FIELD(new_jx, new_jy) &&
6576        (Feld[new_jx][new_jy] == EL_SP_BASE ||
6577         Feld[new_jx][new_jy] == EL_SAND));
6578     /* !!! extend EL_SAND to anything diggable !!! */
6579
6580     if (field_under_player_is_free &&
6581         !player_is_moving_to_valid_field &&
6582         !IS_WALKABLE_INSIDE(Feld[jx][jy]))
6583       player->programmed_action = MV_DOWN;
6584   }
6585 }
6586
6587 /*
6588   MovePlayerOneStep()
6589   -----------------------------------------------------------------------------
6590   dx, dy:               direction (non-diagonal) to try to move the player to
6591   real_dx, real_dy:     direction as read from input device (can be diagonal)
6592 */
6593
6594 boolean MovePlayerOneStep(struct PlayerInfo *player,
6595                           int dx, int dy, int real_dx, int real_dy)
6596 {
6597 #if 0
6598   static int change_sides[4][2] =
6599   {
6600     /* enter side        leave side */
6601     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* moving left  */
6602     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* moving right */
6603     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     },      /* moving up    */
6604     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  }       /* moving down  */
6605   };
6606   int move_direction = (dx == -1 ? MV_LEFT :
6607                         dx == +1 ? MV_RIGHT :
6608                         dy == -1 ? MV_UP :
6609                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
6610   int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6611   int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6612 #endif
6613   int jx = player->jx, jy = player->jy;
6614   int new_jx = jx + dx, new_jy = jy + dy;
6615   int element;
6616   int can_move;
6617
6618   if (!player->active || (!dx && !dy))
6619     return MF_NO_ACTION;
6620
6621   player->MovDir = (dx < 0 ? MV_LEFT :
6622                     dx > 0 ? MV_RIGHT :
6623                     dy < 0 ? MV_UP :
6624                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
6625
6626   if (!IN_LEV_FIELD(new_jx, new_jy))
6627     return MF_NO_ACTION;
6628
6629   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
6630     return MF_NO_ACTION;
6631
6632 #if 0
6633   element = MovingOrBlocked2Element(new_jx, new_jy);
6634 #else
6635   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
6636 #endif
6637
6638   if (DONT_RUN_INTO(element))
6639   {
6640     if (element == EL_ACID && dx == 0 && dy == 1)
6641     {
6642       SplashAcid(jx, jy);
6643       Feld[jx][jy] = EL_PLAYER_1;
6644       InitMovingField(jx, jy, MV_DOWN);
6645       Store[jx][jy] = EL_ACID;
6646       ContinueMoving(jx, jy);
6647       BuryHero(player);
6648     }
6649     else
6650       TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
6651
6652     return MF_MOVING;
6653   }
6654
6655   can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
6656   if (can_move != MF_MOVING)
6657     return can_move;
6658
6659   /* check if DigField() has caused relocation of the player */
6660   if (player->jx != jx || player->jy != jy)
6661     return MF_NO_ACTION;
6662
6663   StorePlayer[jx][jy] = 0;
6664   player->last_jx = jx;
6665   player->last_jy = jy;
6666   player->jx = new_jx;
6667   player->jy = new_jy;
6668   StorePlayer[new_jx][new_jy] = player->element_nr;
6669
6670   player->MovPos =
6671     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
6672
6673   ScrollPlayer(player, SCROLL_INIT);
6674
6675 #if 0
6676   if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
6677   {
6678     CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6679                                     CE_OTHER_GETS_LEFT);
6680     CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6681                            CE_LEFT_BY_PLAYER, -1);
6682   }
6683
6684   if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
6685   {
6686     CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
6687                                     enter_side, CE_OTHER_GETS_ENTERED);
6688     CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
6689                            CE_ENTERED_BY_PLAYER, -1);
6690   }
6691 #endif
6692
6693   return MF_MOVING;
6694 }
6695
6696 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
6697 {
6698   int jx = player->jx, jy = player->jy;
6699   int old_jx = jx, old_jy = jy;
6700   int moved = MF_NO_ACTION;
6701
6702   if (!player->active || (!dx && !dy))
6703     return FALSE;
6704
6705 #if 0
6706   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6707       !tape.playing)
6708     return FALSE;
6709 #else
6710   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6711       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
6712     return FALSE;
6713 #endif
6714
6715   /* remove the last programmed player action */
6716   player->programmed_action = 0;
6717
6718   if (player->MovPos)
6719   {
6720     /* should only happen if pre-1.2 tape recordings are played */
6721     /* this is only for backward compatibility */
6722
6723     int original_move_delay_value = player->move_delay_value;
6724
6725 #if DEBUG
6726     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
6727            tape.counter);
6728 #endif
6729
6730     /* scroll remaining steps with finest movement resolution */
6731     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
6732
6733     while (player->MovPos)
6734     {
6735       ScrollPlayer(player, SCROLL_GO_ON);
6736       ScrollScreen(NULL, SCROLL_GO_ON);
6737       FrameCounter++;
6738       DrawAllPlayers();
6739       BackToFront();
6740     }
6741
6742     player->move_delay_value = original_move_delay_value;
6743   }
6744
6745   if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
6746   {
6747     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
6748       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
6749   }
6750   else
6751   {
6752     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
6753       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
6754   }
6755
6756   jx = player->jx;
6757   jy = player->jy;
6758
6759   if (moved & MF_MOVING && !ScreenMovPos &&
6760       (player == local_player || !options.network))
6761   {
6762     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
6763     int offset = (setup.scroll_delay ? 3 : 0);
6764
6765     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6766     {
6767       /* actual player has left the screen -- scroll in that direction */
6768       if (jx != old_jx)         /* player has moved horizontally */
6769         scroll_x += (jx - old_jx);
6770       else                      /* player has moved vertically */
6771         scroll_y += (jy - old_jy);
6772     }
6773     else
6774     {
6775       if (jx != old_jx)         /* player has moved horizontally */
6776       {
6777         if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
6778             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
6779           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
6780
6781         /* don't scroll over playfield boundaries */
6782         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
6783           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
6784
6785         /* don't scroll more than one field at a time */
6786         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
6787
6788         /* don't scroll against the player's moving direction */
6789         if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
6790             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
6791           scroll_x = old_scroll_x;
6792       }
6793       else                      /* player has moved vertically */
6794       {
6795         if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
6796             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
6797           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
6798
6799         /* don't scroll over playfield boundaries */
6800         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
6801           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
6802
6803         /* don't scroll more than one field at a time */
6804         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
6805
6806         /* don't scroll against the player's moving direction */
6807         if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
6808             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
6809           scroll_y = old_scroll_y;
6810       }
6811     }
6812
6813     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
6814     {
6815       if (!options.network && !AllPlayersInVisibleScreen())
6816       {
6817         scroll_x = old_scroll_x;
6818         scroll_y = old_scroll_y;
6819       }
6820       else
6821       {
6822         ScrollScreen(player, SCROLL_INIT);
6823         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
6824       }
6825     }
6826   }
6827
6828 #if 0
6829 #if 1
6830   InitPlayerGfxAnimation(player, ACTION_DEFAULT);
6831 #else
6832   if (!(moved & MF_MOVING) && !player->is_pushing)
6833     player->Frame = 0;
6834 #endif
6835 #endif
6836
6837   player->StepFrame = 0;
6838
6839   if (moved & MF_MOVING)
6840   {
6841     if (old_jx != jx && old_jy == jy)
6842       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
6843     else if (old_jx == jx && old_jy != jy)
6844       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
6845
6846     DrawLevelField(jx, jy);     /* for "crumbled sand" */
6847
6848     player->last_move_dir = player->MovDir;
6849     player->is_moving = TRUE;
6850 #if 1
6851     player->is_snapping = FALSE;
6852 #endif
6853
6854 #if 1
6855     player->is_switching = FALSE;
6856 #endif
6857
6858
6859 #if 1
6860     {
6861       static int change_sides[4][2] =
6862       {
6863         /* enter side           leave side */
6864         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
6865         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
6866         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
6867         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
6868       };
6869       int move_direction = player->MovDir;
6870       int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6871       int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6872
6873 #if 1
6874       if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
6875       {
6876         CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
6877                                         leave_side, CE_OTHER_GETS_LEFT);
6878         CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
6879                                leave_side, CE_LEFT_BY_PLAYER, -1);
6880       }
6881
6882       if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
6883       {
6884         CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
6885                                         enter_side, CE_OTHER_GETS_ENTERED);
6886         CheckElementSideChange(jx, jy, Feld[jx][jy],
6887                                enter_side, CE_ENTERED_BY_PLAYER, -1);
6888       }
6889 #endif
6890
6891     }
6892 #endif
6893
6894
6895   }
6896   else
6897   {
6898     CheckGravityMovement(player);
6899
6900     /*
6901     player->last_move_dir = MV_NO_MOVING;
6902     */
6903     player->is_moving = FALSE;
6904   }
6905
6906   if (game.engine_version < VERSION_IDENT(3,0,7,0))
6907   {
6908     TestIfHeroTouchesBadThing(jx, jy);
6909     TestIfPlayerTouchesCustomElement(jx, jy);
6910   }
6911
6912   if (!player->active)
6913     RemoveHero(player);
6914
6915   return moved;
6916 }
6917
6918 void ScrollPlayer(struct PlayerInfo *player, int mode)
6919 {
6920   int jx = player->jx, jy = player->jy;
6921   int last_jx = player->last_jx, last_jy = player->last_jy;
6922   int move_stepsize = TILEX / player->move_delay_value;
6923
6924   if (!player->active || !player->MovPos)
6925     return;
6926
6927   if (mode == SCROLL_INIT)
6928   {
6929     player->actual_frame_counter = FrameCounter;
6930     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
6931
6932     if (Feld[last_jx][last_jy] == EL_EMPTY)
6933       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
6934
6935 #if 0
6936     DrawPlayer(player);
6937 #endif
6938     return;
6939   }
6940   else if (!FrameReached(&player->actual_frame_counter, 1))
6941     return;
6942
6943   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
6944   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
6945
6946   if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
6947     Feld[last_jx][last_jy] = EL_EMPTY;
6948
6949   /* before DrawPlayer() to draw correct player graphic for this case */
6950   if (player->MovPos == 0)
6951     CheckGravityMovement(player);
6952
6953 #if 0
6954   DrawPlayer(player);   /* needed here only to cleanup last field */
6955 #endif
6956
6957   if (player->MovPos == 0)      /* player reached destination field */
6958   {
6959     if (IS_PASSABLE(Feld[last_jx][last_jy]))
6960     {
6961       /* continue with normal speed after quickly moving through gate */
6962       HALVE_PLAYER_SPEED(player);
6963
6964       /* be able to make the next move without delay */
6965       player->move_delay = 0;
6966     }
6967
6968     player->last_jx = jx;
6969     player->last_jy = jy;
6970
6971     if (Feld[jx][jy] == EL_EXIT_OPEN ||
6972         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
6973         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
6974     {
6975       DrawPlayer(player);       /* needed here only to cleanup last field */
6976       RemoveHero(player);
6977
6978       if (local_player->friends_still_needed == 0 ||
6979           IS_SP_ELEMENT(Feld[jx][jy]))
6980         player->LevelSolved = player->GameOver = TRUE;
6981     }
6982
6983     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
6984     {
6985       TestIfHeroTouchesBadThing(jx, jy);
6986       TestIfPlayerTouchesCustomElement(jx, jy);
6987 #if 1
6988       TestIfElementTouchesCustomElement(jx, jy);        /* for empty space */
6989 #endif
6990
6991       if (!player->active)
6992         RemoveHero(player);
6993     }
6994
6995     if (tape.single_step && tape.recording && !tape.pausing &&
6996         !player->programmed_action)
6997       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6998   }
6999 }
7000
7001 void ScrollScreen(struct PlayerInfo *player, int mode)
7002 {
7003   static unsigned long screen_frame_counter = 0;
7004
7005   if (mode == SCROLL_INIT)
7006   {
7007     /* set scrolling step size according to actual player's moving speed */
7008     ScrollStepSize = TILEX / player->move_delay_value;
7009
7010     screen_frame_counter = FrameCounter;
7011     ScreenMovDir = player->MovDir;
7012     ScreenMovPos = player->MovPos;
7013     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7014     return;
7015   }
7016   else if (!FrameReached(&screen_frame_counter, 1))
7017     return;
7018
7019   if (ScreenMovPos)
7020   {
7021     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7022     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7023     redraw_mask |= REDRAW_FIELD;
7024   }
7025   else
7026     ScreenMovDir = MV_NO_MOVING;
7027 }
7028
7029 void TestIfPlayerTouchesCustomElement(int x, int y)
7030 {
7031   static int xy[4][2] =
7032   {
7033     { 0, -1 },
7034     { -1, 0 },
7035     { +1, 0 },
7036     { 0, +1 }
7037   };
7038   static int change_sides[4][2] =
7039   {
7040     /* center side       border side */
7041     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
7042     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
7043     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
7044     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
7045   };
7046   static int touch_dir[4] =
7047   {
7048     MV_LEFT | MV_RIGHT,
7049     MV_UP   | MV_DOWN,
7050     MV_UP   | MV_DOWN,
7051     MV_LEFT | MV_RIGHT
7052   };
7053   int center_element = Feld[x][y];      /* should always be non-moving! */
7054   int i;
7055
7056   for (i=0; i<4; i++)
7057   {
7058     int xx = x + xy[i][0];
7059     int yy = y + xy[i][1];
7060     int center_side = change_sides[i][0];
7061     int border_side = change_sides[i][1];
7062     int border_element;
7063
7064     if (!IN_LEV_FIELD(xx, yy))
7065       continue;
7066
7067     if (IS_PLAYER(x, y))
7068     {
7069       if (game.engine_version < VERSION_IDENT(3,0,7,0))
7070         border_element = Feld[xx][yy];          /* may be moving! */
7071       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7072         border_element = Feld[xx][yy];
7073       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
7074         border_element = MovingOrBlocked2Element(xx, yy);
7075       else
7076         continue;               /* center and border element do not touch */
7077
7078       CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7079                                       CE_OTHER_GETS_TOUCHED);
7080       CheckElementSideChange(xx, yy, border_element, border_side,
7081                              CE_TOUCHED_BY_PLAYER, -1);
7082     }
7083     else if (IS_PLAYER(xx, yy))
7084     {
7085       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7086       {
7087         struct PlayerInfo *player = PLAYERINFO(xx, yy);
7088
7089         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7090           continue;             /* center and border element do not touch */
7091       }
7092
7093       CheckTriggeredElementSideChange(x, y, center_element, center_side,
7094                                       CE_OTHER_GETS_TOUCHED);
7095       CheckElementSideChange(x, y, center_element, center_side,
7096                              CE_TOUCHED_BY_PLAYER, -1);
7097
7098       break;
7099     }
7100   }
7101 }
7102
7103 void TestIfElementTouchesCustomElement(int x, int y)
7104 {
7105   static int xy[4][2] =
7106   {
7107     { 0, -1 },
7108     { -1, 0 },
7109     { +1, 0 },
7110     { 0, +1 }
7111   };
7112   static int change_sides[4][2] =
7113   {
7114     /* center side       border side */
7115     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
7116     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
7117     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
7118     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
7119   };
7120   static int touch_dir[4] =
7121   {
7122     MV_LEFT | MV_RIGHT,
7123     MV_UP   | MV_DOWN,
7124     MV_UP   | MV_DOWN,
7125     MV_LEFT | MV_RIGHT
7126   };
7127   boolean change_center_element = FALSE;
7128   int center_element_change_page = 0;
7129   int center_element = Feld[x][y];      /* should always be non-moving! */
7130   int i, j;
7131
7132   for (i=0; i<4; i++)
7133   {
7134     int xx = x + xy[i][0];
7135     int yy = y + xy[i][1];
7136     int center_side = change_sides[i][0];
7137     int border_side = change_sides[i][1];
7138     int border_element;
7139
7140     if (!IN_LEV_FIELD(xx, yy))
7141       continue;
7142
7143     if (game.engine_version < VERSION_IDENT(3,0,7,0))
7144       border_element = Feld[xx][yy];    /* may be moving! */
7145     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7146       border_element = Feld[xx][yy];
7147     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
7148       border_element = MovingOrBlocked2Element(xx, yy);
7149     else
7150       continue;                 /* center and border element do not touch */
7151
7152     /* check for change of center element (but change it only once) */
7153     if (IS_CUSTOM_ELEMENT(center_element) &&
7154         HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7155         !change_center_element)
7156     {
7157       for (j=0; j < element_info[center_element].num_change_pages; j++)
7158       {
7159         struct ElementChangeInfo *change =
7160           &element_info[center_element].change_page[j];
7161
7162         if (change->can_change &&
7163             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7164             change->sides & border_side &&
7165             change->trigger_element == border_element)
7166         {
7167           change_center_element = TRUE;
7168           center_element_change_page = j;
7169
7170           break;
7171         }
7172       }
7173     }
7174
7175     /* check for change of border element */
7176     if (IS_CUSTOM_ELEMENT(border_element) &&
7177         HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7178     {
7179       for (j=0; j < element_info[border_element].num_change_pages; j++)
7180       {
7181         struct ElementChangeInfo *change =
7182           &element_info[border_element].change_page[j];
7183
7184         if (change->can_change &&
7185             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7186             change->sides & center_side &&
7187             change->trigger_element == center_element)
7188         {
7189           CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7190                                  CE_OTHER_IS_TOUCHING, j);
7191           break;
7192         }
7193       }
7194     }
7195   }
7196
7197   if (change_center_element)
7198     CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7199                            CE_OTHER_IS_TOUCHING, center_element_change_page);
7200 }
7201
7202 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
7203 {
7204   int i, kill_x = -1, kill_y = -1;
7205   static int test_xy[4][2] =
7206   {
7207     { 0, -1 },
7208     { -1, 0 },
7209     { +1, 0 },
7210     { 0, +1 }
7211   };
7212   static int test_dir[4] =
7213   {
7214     MV_UP,
7215     MV_LEFT,
7216     MV_RIGHT,
7217     MV_DOWN
7218   };
7219
7220   for (i=0; i<4; i++)
7221   {
7222     int test_x, test_y, test_move_dir, test_element;
7223
7224     test_x = good_x + test_xy[i][0];
7225     test_y = good_y + test_xy[i][1];
7226     if (!IN_LEV_FIELD(test_x, test_y))
7227       continue;
7228
7229     test_move_dir =
7230       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7231
7232 #if 0
7233     test_element = Feld[test_x][test_y];
7234 #else
7235     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
7236 #endif
7237
7238     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7239        2nd case: DONT_TOUCH style bad thing does not move away from good thing
7240     */
7241     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
7242         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
7243     {
7244       kill_x = test_x;
7245       kill_y = test_y;
7246       break;
7247     }
7248   }
7249
7250   if (kill_x != -1 || kill_y != -1)
7251   {
7252     if (IS_PLAYER(good_x, good_y))
7253     {
7254       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
7255
7256       if (player->shield_deadly_time_left > 0)
7257         Bang(kill_x, kill_y);
7258       else if (!PLAYER_PROTECTED(good_x, good_y))
7259         KillHero(player);
7260     }
7261     else
7262       Bang(good_x, good_y);
7263   }
7264 }
7265
7266 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
7267 {
7268   int i, kill_x = -1, kill_y = -1;
7269   int bad_element = Feld[bad_x][bad_y];
7270   static int test_xy[4][2] =
7271   {
7272     { 0, -1 },
7273     { -1, 0 },
7274     { +1, 0 },
7275     { 0, +1 }
7276   };
7277   static int touch_dir[4] =
7278   {
7279     MV_LEFT | MV_RIGHT,
7280     MV_UP   | MV_DOWN,
7281     MV_UP   | MV_DOWN,
7282     MV_LEFT | MV_RIGHT
7283   };
7284   static int test_dir[4] =
7285   {
7286     MV_UP,
7287     MV_LEFT,
7288     MV_RIGHT,
7289     MV_DOWN
7290   };
7291
7292   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
7293     return;
7294
7295   for (i=0; i<4; i++)
7296   {
7297     int test_x, test_y, test_move_dir, test_element;
7298
7299     test_x = bad_x + test_xy[i][0];
7300     test_y = bad_y + test_xy[i][1];
7301     if (!IN_LEV_FIELD(test_x, test_y))
7302       continue;
7303
7304     test_move_dir =
7305       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7306
7307     test_element = Feld[test_x][test_y];
7308
7309     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7310        2nd case: DONT_TOUCH style bad thing does not move away from good thing
7311     */
7312     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
7313         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
7314     {
7315       /* good thing is player or penguin that does not move away */
7316       if (IS_PLAYER(test_x, test_y))
7317       {
7318         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
7319
7320         if (bad_element == EL_ROBOT && player->is_moving)
7321           continue;     /* robot does not kill player if he is moving */
7322
7323         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7324         {
7325           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7326             continue;           /* center and border element do not touch */
7327         }
7328
7329         kill_x = test_x;
7330         kill_y = test_y;
7331         break;
7332       }
7333       else if (test_element == EL_PENGUIN)
7334       {
7335         kill_x = test_x;
7336         kill_y = test_y;
7337         break;
7338       }
7339     }
7340   }
7341
7342   if (kill_x != -1 || kill_y != -1)
7343   {
7344     if (IS_PLAYER(kill_x, kill_y))
7345     {
7346       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
7347
7348       if (player->shield_deadly_time_left > 0)
7349         Bang(bad_x, bad_y);
7350       else if (!PLAYER_PROTECTED(kill_x, kill_y))
7351         KillHero(player);
7352     }
7353     else
7354       Bang(kill_x, kill_y);
7355   }
7356 }
7357
7358 void TestIfHeroTouchesBadThing(int x, int y)
7359 {
7360   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7361 }
7362
7363 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
7364 {
7365   TestIfGoodThingHitsBadThing(x, y, move_dir);
7366 }
7367
7368 void TestIfBadThingTouchesHero(int x, int y)
7369 {
7370   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7371 }
7372
7373 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
7374 {
7375   TestIfBadThingHitsGoodThing(x, y, move_dir);
7376 }
7377
7378 void TestIfFriendTouchesBadThing(int x, int y)
7379 {
7380   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7381 }
7382
7383 void TestIfBadThingTouchesFriend(int x, int y)
7384 {
7385   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7386 }
7387
7388 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
7389 {
7390   int i, kill_x = bad_x, kill_y = bad_y;
7391   static int xy[4][2] =
7392   {
7393     { 0, -1 },
7394     { -1, 0 },
7395     { +1, 0 },
7396     { 0, +1 }
7397   };
7398
7399   for (i=0; i<4; i++)
7400   {
7401     int x, y, element;
7402
7403     x = bad_x + xy[i][0];
7404     y = bad_y + xy[i][1];
7405     if (!IN_LEV_FIELD(x, y))
7406       continue;
7407
7408     element = Feld[x][y];
7409     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
7410         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
7411     {
7412       kill_x = x;
7413       kill_y = y;
7414       break;
7415     }
7416   }
7417
7418   if (kill_x != bad_x || kill_y != bad_y)
7419     Bang(bad_x, bad_y);
7420 }
7421
7422 void KillHero(struct PlayerInfo *player)
7423 {
7424   int jx = player->jx, jy = player->jy;
7425
7426   if (!player->active)
7427     return;
7428
7429   /* remove accessible field at the player's position */
7430   Feld[jx][jy] = EL_EMPTY;
7431
7432   /* deactivate shield (else Bang()/Explode() would not work right) */
7433   player->shield_normal_time_left = 0;
7434   player->shield_deadly_time_left = 0;
7435
7436   Bang(jx, jy);
7437   BuryHero(player);
7438 }
7439
7440 static void KillHeroUnlessProtected(int x, int y)
7441 {
7442   if (!PLAYER_PROTECTED(x, y))
7443     KillHero(PLAYERINFO(x, y));
7444 }
7445
7446 void BuryHero(struct PlayerInfo *player)
7447 {
7448   int jx = player->jx, jy = player->jy;
7449
7450   if (!player->active)
7451     return;
7452
7453 #if 1
7454   PlaySoundLevelElementAction(jx, jy, player->element_nr, ACTION_DYING);
7455 #else
7456   PlaySoundLevel(jx, jy, SND_CLASS_PLAYER_DYING);
7457 #endif
7458   PlaySoundLevel(jx, jy, SND_GAME_LOSING);
7459
7460   player->GameOver = TRUE;
7461   RemoveHero(player);
7462 }
7463
7464 void RemoveHero(struct PlayerInfo *player)
7465 {
7466   int jx = player->jx, jy = player->jy;
7467   int i, found = FALSE;
7468
7469   player->present = FALSE;
7470   player->active = FALSE;
7471
7472   if (!ExplodeField[jx][jy])
7473     StorePlayer[jx][jy] = 0;
7474
7475   for (i=0; i<MAX_PLAYERS; i++)
7476     if (stored_player[i].active)
7477       found = TRUE;
7478
7479   if (!found)
7480     AllPlayersGone = TRUE;
7481
7482   ExitX = ZX = jx;
7483   ExitY = ZY = jy;
7484 }
7485
7486 /*
7487   =============================================================================
7488   checkDiagonalPushing()
7489   -----------------------------------------------------------------------------
7490   check if diagonal input device direction results in pushing of object
7491   (by checking if the alternative direction is walkable, diggable, ...)
7492   =============================================================================
7493 */
7494
7495 static boolean checkDiagonalPushing(struct PlayerInfo *player,
7496                                     int x, int y, int real_dx, int real_dy)
7497 {
7498   int jx, jy, dx, dy, xx, yy;
7499
7500   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
7501     return TRUE;
7502
7503   /* diagonal direction: check alternative direction */
7504   jx = player->jx;
7505   jy = player->jy;
7506   dx = x - jx;
7507   dy = y - jy;
7508   xx = jx + (dx == 0 ? real_dx : 0);
7509   yy = jy + (dy == 0 ? real_dy : 0);
7510
7511   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
7512 }
7513
7514 /*
7515   =============================================================================
7516   DigField()
7517   -----------------------------------------------------------------------------
7518   x, y:                 field next to player (non-diagonal) to try to dig to
7519   real_dx, real_dy:     direction as read from input device (can be diagonal)
7520   =============================================================================
7521 */
7522
7523 int DigField(struct PlayerInfo *player,
7524              int x, int y, int real_dx, int real_dy, int mode)
7525 {
7526   static int change_sides[4] =
7527   {
7528     CH_SIDE_RIGHT,      /* moving left  */
7529     CH_SIDE_LEFT,       /* moving right */
7530     CH_SIDE_BOTTOM,     /* moving up    */
7531     CH_SIDE_TOP,        /* moving down  */
7532   };
7533   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
7534   int jx = player->jx, jy = player->jy;
7535   int dx = x - jx, dy = y - jy;
7536   int nextx = x + dx, nexty = y + dy;
7537   int move_direction = (dx == -1 ? MV_LEFT :
7538                         dx == +1 ? MV_RIGHT :
7539                         dy == -1 ? MV_UP :
7540                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
7541   int dig_side = change_sides[MV_DIR_BIT(move_direction)];
7542   int element;
7543
7544   if (player->MovPos == 0)
7545   {
7546     player->is_digging = FALSE;
7547     player->is_collecting = FALSE;
7548   }
7549
7550   if (player->MovPos == 0)      /* last pushing move finished */
7551     player->is_pushing = FALSE;
7552
7553   if (mode == DF_NO_PUSH)       /* player just stopped pushing */
7554   {
7555     player->is_switching = FALSE;
7556     player->push_delay = 0;
7557
7558     return MF_NO_ACTION;
7559   }
7560
7561   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
7562     return MF_NO_ACTION;
7563
7564 #if 0
7565   if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
7566 #else
7567   if (IS_TUBE(Feld[jx][jy]) ||
7568       (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
7569 #endif
7570   {
7571     int i = 0;
7572     int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
7573     int tube_leave_directions[][2] =
7574     {
7575       { EL_TUBE_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7576       { EL_TUBE_VERTICAL,                                    MV_UP | MV_DOWN },
7577       { EL_TUBE_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
7578       { EL_TUBE_VERTICAL_LEFT,          MV_LEFT |            MV_UP | MV_DOWN },
7579       { EL_TUBE_VERTICAL_RIGHT,                   MV_RIGHT | MV_UP | MV_DOWN },
7580       { EL_TUBE_HORIZONTAL_UP,          MV_LEFT | MV_RIGHT | MV_UP           },
7581       { EL_TUBE_HORIZONTAL_DOWN,        MV_LEFT | MV_RIGHT |         MV_DOWN },
7582       { EL_TUBE_LEFT_UP,                MV_LEFT |            MV_UP           },
7583       { EL_TUBE_LEFT_DOWN,              MV_LEFT |                    MV_DOWN },
7584       { EL_TUBE_RIGHT_UP,                         MV_RIGHT | MV_UP           },
7585       { EL_TUBE_RIGHT_DOWN,                       MV_RIGHT |         MV_DOWN },
7586       { -1,                             MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
7587     };
7588
7589     while (tube_leave_directions[i][0] != tube_element)
7590     {
7591       i++;
7592       if (tube_leave_directions[i][0] == -1)    /* should not happen */
7593         break;
7594     }
7595
7596     if (!(tube_leave_directions[i][1] & move_direction))
7597       return MF_NO_ACTION;      /* tube has no opening in this direction */
7598   }
7599
7600   element = Feld[x][y];
7601
7602   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
7603       game.engine_version >= VERSION_IDENT(2,2,0,0))
7604     return MF_NO_ACTION;
7605
7606   switch (element)
7607   {
7608     case EL_SP_PORT_LEFT:
7609     case EL_SP_PORT_RIGHT:
7610     case EL_SP_PORT_UP:
7611     case EL_SP_PORT_DOWN:
7612     case EL_SP_PORT_HORIZONTAL:
7613     case EL_SP_PORT_VERTICAL:
7614     case EL_SP_PORT_ANY:
7615     case EL_SP_GRAVITY_PORT_LEFT:
7616     case EL_SP_GRAVITY_PORT_RIGHT:
7617     case EL_SP_GRAVITY_PORT_UP:
7618     case EL_SP_GRAVITY_PORT_DOWN:
7619       if ((dx == -1 &&
7620            element != EL_SP_PORT_LEFT &&
7621            element != EL_SP_GRAVITY_PORT_LEFT &&
7622            element != EL_SP_PORT_HORIZONTAL &&
7623            element != EL_SP_PORT_ANY) ||
7624           (dx == +1 &&
7625            element != EL_SP_PORT_RIGHT &&
7626            element != EL_SP_GRAVITY_PORT_RIGHT &&
7627            element != EL_SP_PORT_HORIZONTAL &&
7628            element != EL_SP_PORT_ANY) ||
7629           (dy == -1 &&
7630            element != EL_SP_PORT_UP &&
7631            element != EL_SP_GRAVITY_PORT_UP &&
7632            element != EL_SP_PORT_VERTICAL &&
7633            element != EL_SP_PORT_ANY) ||
7634           (dy == +1 &&
7635            element != EL_SP_PORT_DOWN &&
7636            element != EL_SP_GRAVITY_PORT_DOWN &&
7637            element != EL_SP_PORT_VERTICAL &&
7638            element != EL_SP_PORT_ANY) ||
7639           !IN_LEV_FIELD(nextx, nexty) ||
7640           !IS_FREE(nextx, nexty))
7641         return MF_NO_ACTION;
7642
7643       if (element == EL_SP_GRAVITY_PORT_LEFT ||
7644           element == EL_SP_GRAVITY_PORT_RIGHT ||
7645           element == EL_SP_GRAVITY_PORT_UP ||
7646           element == EL_SP_GRAVITY_PORT_DOWN)
7647         game.gravity = !game.gravity;
7648
7649       /* automatically move to the next field with double speed */
7650       player->programmed_action = move_direction;
7651       DOUBLE_PLAYER_SPEED(player);
7652
7653       PlaySoundLevel(x, y, SND_CLASS_SP_PORT_PASSING);
7654       break;
7655
7656     case EL_TUBE_ANY:
7657     case EL_TUBE_VERTICAL:
7658     case EL_TUBE_HORIZONTAL:
7659     case EL_TUBE_VERTICAL_LEFT:
7660     case EL_TUBE_VERTICAL_RIGHT:
7661     case EL_TUBE_HORIZONTAL_UP:
7662     case EL_TUBE_HORIZONTAL_DOWN:
7663     case EL_TUBE_LEFT_UP:
7664     case EL_TUBE_LEFT_DOWN:
7665     case EL_TUBE_RIGHT_UP:
7666     case EL_TUBE_RIGHT_DOWN:
7667       {
7668         int i = 0;
7669         int tube_enter_directions[][2] =
7670         {
7671           { EL_TUBE_ANY,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7672           { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
7673           { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
7674           { EL_TUBE_VERTICAL_LEFT,                MV_RIGHT | MV_UP | MV_DOWN },
7675           { EL_TUBE_VERTICAL_RIGHT,     MV_LEFT            | MV_UP | MV_DOWN },
7676           { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT |         MV_DOWN },
7677           { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT | MV_UP           },
7678           { EL_TUBE_LEFT_UP,                      MV_RIGHT |         MV_DOWN },
7679           { EL_TUBE_LEFT_DOWN,                    MV_RIGHT | MV_UP           },
7680           { EL_TUBE_RIGHT_UP,           MV_LEFT |                    MV_DOWN },
7681           { EL_TUBE_RIGHT_DOWN,         MV_LEFT |            MV_UP           },
7682           { -1,                         MV_NO_MOVING                         }
7683         };
7684
7685         while (tube_enter_directions[i][0] != element)
7686         {
7687           i++;
7688           if (tube_enter_directions[i][0] == -1)        /* should not happen */
7689             break;
7690         }
7691
7692         if (!(tube_enter_directions[i][1] & move_direction))
7693           return MF_NO_ACTION;  /* tube has no opening in this direction */
7694
7695         PlaySoundLevel(x, y, SND_CLASS_TUBE_WALKING);
7696       }
7697       break;
7698
7699     default:
7700
7701       if (IS_WALKABLE(element))
7702       {
7703         int sound_action = ACTION_WALKING;
7704
7705         if (element >= EL_GATE_1 && element <= EL_GATE_4)
7706         {
7707           if (!player->key[element - EL_GATE_1])
7708             return MF_NO_ACTION;
7709         }
7710         else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
7711         {
7712           if (!player->key[element - EL_GATE_1_GRAY])
7713             return MF_NO_ACTION;
7714         }
7715         else if (element == EL_EXIT_OPEN ||
7716                  element == EL_SP_EXIT_OPEN ||
7717                  element == EL_SP_EXIT_OPENING)
7718         {
7719           sound_action = ACTION_PASSING;        /* player is passing exit */
7720         }
7721         else if (element == EL_EMPTY)
7722         {
7723           sound_action = ACTION_MOVING;         /* nothing to walk on */
7724         }
7725
7726         /* play sound from background or player, whatever is available */
7727         if (element_info[element].sound[sound_action] != SND_UNDEFINED)
7728           PlaySoundLevelElementAction(x, y, element, sound_action);
7729         else
7730           PlaySoundLevelElementAction(x, y, player->element_nr, sound_action);
7731
7732         break;
7733       }
7734       else if (IS_PASSABLE(element))
7735       {
7736         if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
7737           return MF_NO_ACTION;
7738
7739 #if 1
7740         if (CAN_MOVE(element))  /* only fixed elements can be passed! */
7741           return MF_NO_ACTION;
7742 #endif
7743
7744         if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
7745         {
7746           if (!player->key[element - EL_EM_GATE_1])
7747             return MF_NO_ACTION;
7748         }
7749         else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
7750         {
7751           if (!player->key[element - EL_EM_GATE_1_GRAY])
7752             return MF_NO_ACTION;
7753         }
7754
7755         /* automatically move to the next field with double speed */
7756         player->programmed_action = move_direction;
7757         DOUBLE_PLAYER_SPEED(player);
7758
7759         PlaySoundLevelAction(x, y, ACTION_PASSING);
7760
7761         break;
7762       }
7763       else if (IS_DIGGABLE(element))
7764       {
7765         RemoveField(x, y);
7766
7767         if (mode != DF_SNAP)
7768         {
7769 #if 1
7770           GfxElement[x][y] = GFX_ELEMENT(element);
7771 #else
7772           GfxElement[x][y] =
7773             (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
7774 #endif
7775           player->is_digging = TRUE;
7776         }
7777
7778         PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
7779
7780         CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
7781
7782 #if 1
7783         if (mode == DF_SNAP)
7784           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
7785 #endif
7786
7787         break;
7788       }
7789       else if (IS_COLLECTIBLE(element))
7790       {
7791         RemoveField(x, y);
7792
7793         if (mode != DF_SNAP)
7794         {
7795           GfxElement[x][y] = element;
7796           player->is_collecting = TRUE;
7797         }
7798
7799         if (element == EL_SPEED_PILL)
7800           player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
7801         else if (element == EL_EXTRA_TIME && level.time > 0)
7802         {
7803           TimeLeft += 10;
7804           DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7805         }
7806         else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
7807         {
7808           player->shield_normal_time_left += 10;
7809           if (element == EL_SHIELD_DEADLY)
7810             player->shield_deadly_time_left += 10;
7811         }
7812         else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
7813         {
7814           if (player->inventory_size < MAX_INVENTORY_SIZE)
7815             player->inventory_element[player->inventory_size++] = element;
7816
7817           DrawText(DX_DYNAMITE, DY_DYNAMITE,
7818                    int2str(local_player->inventory_size, 3), FONT_TEXT_2);
7819         }
7820         else if (element == EL_DYNABOMB_INCREASE_NUMBER)
7821         {
7822           player->dynabomb_count++;
7823           player->dynabombs_left++;
7824         }
7825         else if (element == EL_DYNABOMB_INCREASE_SIZE)
7826         {
7827           player->dynabomb_size++;
7828         }
7829         else if (element == EL_DYNABOMB_INCREASE_POWER)
7830         {
7831           player->dynabomb_xl = TRUE;
7832         }
7833         else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
7834                  (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
7835         {
7836           int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
7837                         element - EL_KEY_1 : element - EL_EM_KEY_1);
7838
7839           player->key[key_nr] = TRUE;
7840
7841           DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
7842                              el2edimg(EL_KEY_1 + key_nr));
7843           redraw_mask |= REDRAW_DOOR_1;
7844         }
7845         else if (IS_ENVELOPE(element))
7846         {
7847 #if 1
7848           player->show_envelope = element;
7849 #else
7850           ShowEnvelope(element - EL_ENVELOPE_1);
7851 #endif
7852         }
7853         else if (IS_DROPPABLE(element)) /* can be collected and dropped */
7854         {
7855           int i;
7856
7857           for (i=0; i < element_info[element].collect_count; i++)
7858             if (player->inventory_size < MAX_INVENTORY_SIZE)
7859               player->inventory_element[player->inventory_size++] = element;
7860
7861           DrawText(DX_DYNAMITE, DY_DYNAMITE,
7862                    int2str(local_player->inventory_size, 3), FONT_TEXT_2);
7863         }
7864         else if (element_info[element].collect_count > 0)
7865         {
7866           local_player->gems_still_needed -=
7867             element_info[element].collect_count;
7868           if (local_player->gems_still_needed < 0)
7869             local_player->gems_still_needed = 0;
7870
7871           DrawText(DX_EMERALDS, DY_EMERALDS,
7872                    int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
7873         }
7874
7875         RaiseScoreElement(element);
7876         PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
7877
7878         CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
7879
7880 #if 1
7881         if (mode == DF_SNAP)
7882           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
7883 #endif
7884
7885         break;
7886       }
7887       else if (IS_PUSHABLE(element))
7888       {
7889         if (mode == DF_SNAP && element != EL_BD_ROCK)
7890           return MF_NO_ACTION;
7891
7892         if (CAN_FALL(element) && dy)
7893           return MF_NO_ACTION;
7894
7895         if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
7896             !(element == EL_SPRING && use_spring_bug))
7897           return MF_NO_ACTION;
7898
7899 #if 1
7900         if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
7901             ((move_direction & MV_VERTICAL &&
7902               ((element_info[element].move_pattern & MV_LEFT &&
7903                 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
7904                (element_info[element].move_pattern & MV_RIGHT &&
7905                 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
7906              (move_direction & MV_HORIZONTAL &&
7907               ((element_info[element].move_pattern & MV_UP &&
7908                 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
7909                (element_info[element].move_pattern & MV_DOWN &&
7910                 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
7911           return MF_NO_ACTION;
7912 #endif
7913
7914 #if 1
7915         /* do not push elements already moving away faster than player */
7916         if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
7917             ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
7918           return MF_NO_ACTION;
7919 #else
7920         if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
7921           return MF_NO_ACTION;
7922 #endif
7923
7924 #if 1
7925         if (game.engine_version >= VERSION_IDENT(3,0,7,1))
7926         {
7927           if (player->push_delay_value == -1)
7928             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7929         }
7930         else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
7931         {
7932           if (!player->is_pushing)
7933             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7934         }
7935
7936         /*
7937         if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
7938             (game.engine_version < VERSION_IDENT(3,0,7,1) ||
7939              !player_is_pushing))
7940           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7941         */
7942 #else
7943         if (!player->is_pushing &&
7944             game.engine_version >= VERSION_IDENT(2,2,0,7))
7945           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7946 #endif
7947
7948 #if 0
7949         printf("::: push delay: %ld [%d, %d] [%d]\n",
7950                player->push_delay_value, FrameCounter, game.engine_version,
7951                player->is_pushing);
7952 #endif
7953
7954         player->is_pushing = TRUE;
7955
7956         if (!(IN_LEV_FIELD(nextx, nexty) &&
7957               (IS_FREE(nextx, nexty) ||
7958                (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
7959                 IS_SB_ELEMENT(element)))))
7960           return MF_NO_ACTION;
7961
7962         if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
7963           return MF_NO_ACTION;
7964
7965         if (player->push_delay == 0)    /* new pushing; restart delay */
7966           player->push_delay = FrameCounter;
7967
7968         if (!FrameReached(&player->push_delay, player->push_delay_value) &&
7969             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
7970             element != EL_SPRING && element != EL_BALLOON)
7971         {
7972           /* make sure that there is no move delay before next try to push */
7973           if (game.engine_version >= VERSION_IDENT(3,0,7,1))
7974             player->move_delay = INITIAL_MOVE_DELAY_OFF;
7975
7976           return MF_NO_ACTION;
7977         }
7978
7979 #if 0
7980         printf("::: NOW PUSHING... [%d]\n", FrameCounter);
7981 #endif
7982
7983         if (IS_SB_ELEMENT(element))
7984         {
7985           if (element == EL_SOKOBAN_FIELD_FULL)
7986           {
7987             Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
7988             local_player->sokobanfields_still_needed++;
7989           }
7990
7991           if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
7992           {
7993             Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
7994             local_player->sokobanfields_still_needed--;
7995           }
7996
7997           Feld[x][y] = EL_SOKOBAN_OBJECT;
7998
7999           if (Back[x][y] == Back[nextx][nexty])
8000             PlaySoundLevelAction(x, y, ACTION_PUSHING);
8001           else if (Back[x][y] != 0)
8002             PlaySoundLevelElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
8003                                         ACTION_EMPTYING);
8004           else
8005             PlaySoundLevelElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
8006                                         ACTION_FILLING);
8007
8008           if (local_player->sokobanfields_still_needed == 0 &&
8009               game.emulation == EMU_SOKOBAN)
8010           {
8011             player->LevelSolved = player->GameOver = TRUE;
8012             PlaySoundLevel(x, y, SND_GAME_SOKOBAN_SOLVING);
8013           }
8014         }
8015         else
8016           PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
8017
8018         InitMovingField(x, y, move_direction);
8019         GfxAction[x][y] = ACTION_PUSHING;
8020
8021         if (mode == DF_SNAP)
8022           ContinueMoving(x, y);
8023         else
8024           MovPos[x][y] = (dx != 0 ? dx : dy);
8025
8026         Pushed[x][y] = TRUE;
8027         Pushed[nextx][nexty] = TRUE;
8028
8029         if (game.engine_version < VERSION_IDENT(2,2,0,7))
8030           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8031         else
8032           player->push_delay_value = -1;        /* get new value later */
8033
8034         CheckTriggeredElementSideChange(x, y, element, dig_side,
8035                                         CE_OTHER_GETS_PUSHED);
8036         CheckElementSideChange(x, y, element, dig_side,
8037                                CE_PUSHED_BY_PLAYER, -1);
8038
8039         break;
8040       }
8041       else if (IS_SWITCHABLE(element))
8042       {
8043         if (PLAYER_SWITCHING(player, x, y))
8044           return MF_ACTION;
8045
8046         player->is_switching = TRUE;
8047         player->switch_x = x;
8048         player->switch_y = y;
8049
8050         PlaySoundLevelElementAction(x, y, element, ACTION_ACTIVATING);
8051
8052         if (element == EL_ROBOT_WHEEL)
8053         {
8054           Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
8055           ZX = x;
8056           ZY = y;
8057
8058           DrawLevelField(x, y);
8059         }
8060         else if (element == EL_SP_TERMINAL)
8061         {
8062           int xx, yy;
8063
8064           for (yy=0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
8065           {
8066             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
8067               Bang(xx, yy);
8068             else if (Feld[xx][yy] == EL_SP_TERMINAL)
8069               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
8070           }
8071         }
8072         else if (IS_BELT_SWITCH(element))
8073         {
8074           ToggleBeltSwitch(x, y);
8075         }
8076         else if (element == EL_SWITCHGATE_SWITCH_UP ||
8077                  element == EL_SWITCHGATE_SWITCH_DOWN)
8078         {
8079           ToggleSwitchgateSwitch(x, y);
8080         }
8081         else if (element == EL_LIGHT_SWITCH ||
8082                  element == EL_LIGHT_SWITCH_ACTIVE)
8083         {
8084           ToggleLightSwitch(x, y);
8085
8086 #if 0
8087           PlaySoundLevel(x, y, element == EL_LIGHT_SWITCH ?
8088                          SND_LIGHT_SWITCH_ACTIVATING :
8089                          SND_LIGHT_SWITCH_DEACTIVATING);
8090 #endif
8091         }
8092         else if (element == EL_TIMEGATE_SWITCH)
8093         {
8094           ActivateTimegateSwitch(x, y);
8095         }
8096         else if (element == EL_BALLOON_SWITCH_LEFT ||
8097                  element == EL_BALLOON_SWITCH_RIGHT ||
8098                  element == EL_BALLOON_SWITCH_UP ||
8099                  element == EL_BALLOON_SWITCH_DOWN ||
8100                  element == EL_BALLOON_SWITCH_ANY)
8101         {
8102           if (element == EL_BALLOON_SWITCH_ANY)
8103             game.balloon_dir = move_direction;
8104           else
8105             game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT :
8106                                 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
8107                                 element == EL_BALLOON_SWITCH_UP    ? MV_UP :
8108                                 element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN :
8109                                 MV_NO_MOVING);
8110         }
8111         else if (element == EL_LAMP)
8112         {
8113           Feld[x][y] = EL_LAMP_ACTIVE;
8114           local_player->lights_still_needed--;
8115
8116           DrawLevelField(x, y);
8117         }
8118         else if (element == EL_TIME_ORB_FULL)
8119         {
8120           Feld[x][y] = EL_TIME_ORB_EMPTY;
8121           TimeLeft += 10;
8122           DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8123
8124           DrawLevelField(x, y);
8125
8126 #if 0
8127           PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
8128 #endif
8129         }
8130
8131         return MF_ACTION;
8132       }
8133       else
8134       {
8135         if (!PLAYER_SWITCHING(player, x, y))
8136         {
8137           player->is_switching = TRUE;
8138           player->switch_x = x;
8139           player->switch_y = y;
8140
8141           CheckTriggeredElementSideChange(x, y, element, dig_side,
8142                                           CE_OTHER_IS_SWITCHING);
8143           CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
8144         }
8145
8146         CheckTriggeredElementSideChange(x, y, element, dig_side,
8147                                         CE_OTHER_GETS_PRESSED);
8148         CheckElementSideChange(x, y, element, dig_side,
8149                                CE_PRESSED_BY_PLAYER, -1);
8150       }
8151
8152       return MF_NO_ACTION;
8153   }
8154
8155   player->push_delay = 0;
8156
8157   if (Feld[x][y] != element)            /* really digged/collected something */
8158     player->is_collecting = !player->is_digging;
8159
8160   return MF_MOVING;
8161 }
8162
8163 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
8164 {
8165   int jx = player->jx, jy = player->jy;
8166   int x = jx + dx, y = jy + dy;
8167   int snap_direction = (dx == -1 ? MV_LEFT :
8168                         dx == +1 ? MV_RIGHT :
8169                         dy == -1 ? MV_UP :
8170                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
8171
8172   if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
8173     return FALSE;
8174
8175   if (!player->active || !IN_LEV_FIELD(x, y))
8176     return FALSE;
8177
8178   if (dx && dy)
8179     return FALSE;
8180
8181   if (!dx && !dy)
8182   {
8183     if (player->MovPos == 0)
8184       player->is_pushing = FALSE;
8185
8186     player->is_snapping = FALSE;
8187
8188     if (player->MovPos == 0)
8189     {
8190       player->is_moving = FALSE;
8191       player->is_digging = FALSE;
8192       player->is_collecting = FALSE;
8193     }
8194
8195     return FALSE;
8196   }
8197
8198   if (player->is_snapping)
8199     return FALSE;
8200
8201   player->MovDir = snap_direction;
8202
8203   player->is_moving = FALSE;
8204   player->is_digging = FALSE;
8205   player->is_collecting = FALSE;
8206
8207   if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
8208     return FALSE;
8209
8210   player->is_snapping = TRUE;
8211
8212   player->is_moving = FALSE;
8213   player->is_digging = FALSE;
8214   player->is_collecting = FALSE;
8215
8216   DrawLevelField(x, y);
8217   BackToFront();
8218
8219   return TRUE;
8220 }
8221
8222 boolean DropElement(struct PlayerInfo *player)
8223 {
8224   int jx = player->jx, jy = player->jy;
8225   int old_element;
8226
8227   if (!player->active || player->MovPos)
8228     return FALSE;
8229
8230   old_element = Feld[jx][jy];
8231
8232   /* check if player has anything that can be dropped */
8233   if (player->inventory_size == 0 && player->dynabombs_left == 0)
8234     return FALSE;
8235
8236   /* check if anything can be dropped at the current position */
8237   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
8238     return FALSE;
8239
8240   /* collected custom elements can only be dropped on empty fields */
8241   if (player->inventory_size > 0 &&
8242       IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
8243       && old_element != EL_EMPTY)
8244     return FALSE;
8245
8246   if (old_element != EL_EMPTY)
8247     Back[jx][jy] = old_element;         /* store old element on this field */
8248
8249   MovDelay[jx][jy] = 96;
8250
8251   ResetGfxAnimation(jx, jy);
8252   ResetRandomAnimationValue(jx, jy);
8253
8254   if (player->inventory_size > 0)
8255   {
8256     int new_element = player->inventory_element[--player->inventory_size];
8257
8258     Feld[jx][jy] = (new_element == EL_DYNAMITE ? EL_DYNAMITE_ACTIVE :
8259                     new_element == EL_SP_DISK_RED ? EL_SP_DISK_RED_ACTIVE :
8260                     new_element);
8261
8262     DrawText(DX_DYNAMITE, DY_DYNAMITE,
8263              int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8264
8265     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8266       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8267
8268     PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8269
8270     CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
8271     CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
8272
8273     TestIfElementTouchesCustomElement(jx, jy);
8274   }
8275   else          /* player is dropping a dyna bomb */
8276   {
8277     player->dynabombs_left--;
8278
8279     Feld[jx][jy] =
8280       EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1);
8281
8282     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8283       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8284
8285     PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8286   }
8287
8288   return TRUE;
8289 }
8290
8291 /* ------------------------------------------------------------------------- */
8292 /* game sound playing functions                                              */
8293 /* ------------------------------------------------------------------------- */
8294
8295 static int *loop_sound_frame = NULL;
8296 static int *loop_sound_volume = NULL;
8297
8298 void InitPlaySoundLevel()
8299 {
8300   int num_sounds = getSoundListSize();
8301
8302   if (loop_sound_frame != NULL)
8303     free(loop_sound_frame);
8304
8305   if (loop_sound_volume != NULL)
8306     free(loop_sound_volume);
8307
8308   loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
8309   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
8310 }
8311
8312 static void PlaySoundLevel(int x, int y, int nr)
8313 {
8314   int sx = SCREENX(x), sy = SCREENY(y);
8315   int volume, stereo_position;
8316   int max_distance = 8;
8317   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
8318
8319   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
8320       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
8321     return;
8322
8323   if (!IN_LEV_FIELD(x, y) ||
8324       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
8325       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
8326     return;
8327
8328   volume = SOUND_MAX_VOLUME;
8329
8330   if (!IN_SCR_FIELD(sx, sy))
8331   {
8332     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
8333     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
8334
8335     volume -= volume * (dx > dy ? dx : dy) / max_distance;
8336   }
8337
8338   stereo_position = (SOUND_MAX_LEFT +
8339                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
8340                      (SCR_FIELDX + 2 * max_distance));
8341
8342   if (IS_LOOP_SOUND(nr))
8343   {
8344     /* This assures that quieter loop sounds do not overwrite louder ones,
8345        while restarting sound volume comparison with each new game frame. */
8346
8347     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
8348       return;
8349
8350     loop_sound_volume[nr] = volume;
8351     loop_sound_frame[nr] = FrameCounter;
8352   }
8353
8354   PlaySoundExt(nr, volume, stereo_position, type);
8355 }
8356
8357 static void PlaySoundLevelNearest(int x, int y, int sound_action)
8358 {
8359   PlaySoundLevel(x < LEVELX(BX1) ? LEVELX(BX1) :
8360                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
8361                  y < LEVELY(BY1) ? LEVELY(BY1) :
8362                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
8363                  sound_action);
8364 }
8365
8366 static void PlaySoundLevelAction(int x, int y, int action)
8367 {
8368   PlaySoundLevelElementAction(x, y, Feld[x][y], action);
8369 }
8370
8371 static void PlaySoundLevelElementAction(int x, int y, int element, int action)
8372 {
8373   int sound_effect = element_info[element].sound[action];
8374
8375   if (sound_effect != SND_UNDEFINED)
8376     PlaySoundLevel(x, y, sound_effect);
8377 }
8378
8379 static void PlaySoundLevelActionIfLoop(int x, int y, int action)
8380 {
8381   int sound_effect = element_info[Feld[x][y]].sound[action];
8382
8383   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8384     PlaySoundLevel(x, y, sound_effect);
8385 }
8386
8387 static void StopSoundLevelActionIfLoop(int x, int y, int action)
8388 {
8389   int sound_effect = element_info[Feld[x][y]].sound[action];
8390
8391   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8392     StopSoundExt(sound_effect, SND_CTRL_STOP_SOUND);
8393 }
8394
8395 void RaiseScore(int value)
8396 {
8397   local_player->score += value;
8398   DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
8399 }
8400
8401 void RaiseScoreElement(int element)
8402 {
8403   switch(element)
8404   {
8405     case EL_EMERALD:
8406     case EL_BD_DIAMOND:
8407     case EL_EMERALD_YELLOW:
8408     case EL_EMERALD_RED:
8409     case EL_EMERALD_PURPLE:
8410     case EL_SP_INFOTRON:
8411       RaiseScore(level.score[SC_EMERALD]);
8412       break;
8413     case EL_DIAMOND:
8414       RaiseScore(level.score[SC_DIAMOND]);
8415       break;
8416     case EL_CRYSTAL:
8417       RaiseScore(level.score[SC_CRYSTAL]);
8418       break;
8419     case EL_PEARL:
8420       RaiseScore(level.score[SC_PEARL]);
8421       break;
8422     case EL_BUG:
8423     case EL_BD_BUTTERFLY:
8424     case EL_SP_ELECTRON:
8425       RaiseScore(level.score[SC_BUG]);
8426       break;
8427     case EL_SPACESHIP:
8428     case EL_BD_FIREFLY:
8429     case EL_SP_SNIKSNAK:
8430       RaiseScore(level.score[SC_SPACESHIP]);
8431       break;
8432     case EL_YAMYAM:
8433     case EL_DARK_YAMYAM:
8434       RaiseScore(level.score[SC_YAMYAM]);
8435       break;
8436     case EL_ROBOT:
8437       RaiseScore(level.score[SC_ROBOT]);
8438       break;
8439     case EL_PACMAN:
8440       RaiseScore(level.score[SC_PACMAN]);
8441       break;
8442     case EL_NUT:
8443       RaiseScore(level.score[SC_NUT]);
8444       break;
8445     case EL_DYNAMITE:
8446     case EL_SP_DISK_RED:
8447     case EL_DYNABOMB_INCREASE_NUMBER:
8448     case EL_DYNABOMB_INCREASE_SIZE:
8449     case EL_DYNABOMB_INCREASE_POWER:
8450       RaiseScore(level.score[SC_DYNAMITE]);
8451       break;
8452     case EL_SHIELD_NORMAL:
8453     case EL_SHIELD_DEADLY:
8454       RaiseScore(level.score[SC_SHIELD]);
8455       break;
8456     case EL_EXTRA_TIME:
8457       RaiseScore(level.score[SC_TIME_BONUS]);
8458       break;
8459     case EL_KEY_1:
8460     case EL_KEY_2:
8461     case EL_KEY_3:
8462     case EL_KEY_4:
8463       RaiseScore(level.score[SC_KEY]);
8464       break;
8465     default:
8466       RaiseScore(element_info[element].collect_score);
8467       break;
8468   }
8469 }
8470
8471 void RequestQuitGame(boolean ask_if_really_quit)
8472 {
8473   if (AllPlayersGone ||
8474       !ask_if_really_quit ||
8475       level_editor_test_game ||
8476       Request("Do you really want to quit the game ?",
8477               REQ_ASK | REQ_STAY_CLOSED))
8478   {
8479 #if defined(PLATFORM_UNIX)
8480     if (options.network)
8481       SendToServer_StopPlaying();
8482     else
8483 #endif
8484     {
8485       game_status = GAME_MODE_MAIN;
8486       DrawMainMenu();
8487     }
8488   }
8489   else
8490   {
8491     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
8492   }
8493 }
8494
8495
8496 /* ---------- new game button stuff ---------------------------------------- */
8497
8498 /* graphic position values for game buttons */
8499 #define GAME_BUTTON_XSIZE       30
8500 #define GAME_BUTTON_YSIZE       30
8501 #define GAME_BUTTON_XPOS        5
8502 #define GAME_BUTTON_YPOS        215
8503 #define SOUND_BUTTON_XPOS       5
8504 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
8505
8506 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8507 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8508 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8509 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8510 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8511 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8512
8513 static struct
8514 {
8515   int x, y;
8516   int gadget_id;
8517   char *infotext;
8518 } gamebutton_info[NUM_GAME_BUTTONS] =
8519 {
8520   {
8521     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
8522     GAME_CTRL_ID_STOP,
8523     "stop game"
8524   },
8525   {
8526     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
8527     GAME_CTRL_ID_PAUSE,
8528     "pause game"
8529   },
8530   {
8531     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
8532     GAME_CTRL_ID_PLAY,
8533     "play game"
8534   },
8535   {
8536     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
8537     SOUND_CTRL_ID_MUSIC,
8538     "background music on/off"
8539   },
8540   {
8541     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
8542     SOUND_CTRL_ID_LOOPS,
8543     "sound loops on/off"
8544   },
8545   {
8546     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
8547     SOUND_CTRL_ID_SIMPLE,
8548     "normal sounds on/off"
8549   }
8550 };
8551
8552 void CreateGameButtons()
8553 {
8554   int i;
8555
8556   for (i=0; i<NUM_GAME_BUTTONS; i++)
8557   {
8558     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
8559     struct GadgetInfo *gi;
8560     int button_type;
8561     boolean checked;
8562     unsigned long event_mask;
8563     int gd_xoffset, gd_yoffset;
8564     int gd_x1, gd_x2, gd_y1, gd_y2;
8565     int id = i;
8566
8567     gd_xoffset = gamebutton_info[i].x;
8568     gd_yoffset = gamebutton_info[i].y;
8569     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
8570     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
8571
8572     if (id == GAME_CTRL_ID_STOP ||
8573         id == GAME_CTRL_ID_PAUSE ||
8574         id == GAME_CTRL_ID_PLAY)
8575     {
8576       button_type = GD_TYPE_NORMAL_BUTTON;
8577       checked = FALSE;
8578       event_mask = GD_EVENT_RELEASED;
8579       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8580       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8581     }
8582     else
8583     {
8584       button_type = GD_TYPE_CHECK_BUTTON;
8585       checked =
8586         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
8587          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
8588          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
8589       event_mask = GD_EVENT_PRESSED;
8590       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
8591       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8592     }
8593
8594     gi = CreateGadget(GDI_CUSTOM_ID, id,
8595                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
8596                       GDI_X, DX + gd_xoffset,
8597                       GDI_Y, DY + gd_yoffset,
8598                       GDI_WIDTH, GAME_BUTTON_XSIZE,
8599                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
8600                       GDI_TYPE, button_type,
8601                       GDI_STATE, GD_BUTTON_UNPRESSED,
8602                       GDI_CHECKED, checked,
8603                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
8604                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
8605                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
8606                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
8607                       GDI_EVENT_MASK, event_mask,
8608                       GDI_CALLBACK_ACTION, HandleGameButtons,
8609                       GDI_END);
8610
8611     if (gi == NULL)
8612       Error(ERR_EXIT, "cannot create gadget");
8613
8614     game_gadget[id] = gi;
8615   }
8616 }
8617
8618 void FreeGameButtons()
8619 {
8620   int i;
8621
8622   for (i=0; i<NUM_GAME_BUTTONS; i++)
8623     FreeGadget(game_gadget[i]);
8624 }
8625
8626 static void MapGameButtons()
8627 {
8628   int i;
8629
8630   for (i=0; i<NUM_GAME_BUTTONS; i++)
8631     MapGadget(game_gadget[i]);
8632 }
8633
8634 void UnmapGameButtons()
8635 {
8636   int i;
8637
8638   for (i=0; i<NUM_GAME_BUTTONS; i++)
8639     UnmapGadget(game_gadget[i]);
8640 }
8641
8642 static void HandleGameButtons(struct GadgetInfo *gi)
8643 {
8644   int id = gi->custom_id;
8645
8646   if (game_status != GAME_MODE_PLAYING)
8647     return;
8648
8649   switch (id)
8650   {
8651     case GAME_CTRL_ID_STOP:
8652       RequestQuitGame(TRUE);
8653       break;
8654
8655     case GAME_CTRL_ID_PAUSE:
8656       if (options.network)
8657       {
8658 #if defined(PLATFORM_UNIX)
8659         if (tape.pausing)
8660           SendToServer_ContinuePlaying();
8661         else
8662           SendToServer_PausePlaying();
8663 #endif
8664       }
8665       else
8666         TapeTogglePause(TAPE_TOGGLE_MANUAL);
8667       break;
8668
8669     case GAME_CTRL_ID_PLAY:
8670       if (tape.pausing)
8671       {
8672 #if defined(PLATFORM_UNIX)
8673         if (options.network)
8674           SendToServer_ContinuePlaying();
8675         else
8676 #endif
8677         {
8678           tape.pausing = FALSE;
8679           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
8680         }
8681       }
8682       break;
8683
8684     case SOUND_CTRL_ID_MUSIC:
8685       if (setup.sound_music)
8686       { 
8687         setup.sound_music = FALSE;
8688         FadeMusic();
8689       }
8690       else if (audio.music_available)
8691       { 
8692         setup.sound = setup.sound_music = TRUE;
8693
8694         SetAudioMode(setup.sound);
8695         PlayMusic(level_nr);
8696       }
8697       break;
8698
8699     case SOUND_CTRL_ID_LOOPS:
8700       if (setup.sound_loops)
8701         setup.sound_loops = FALSE;
8702       else if (audio.loops_available)
8703       {
8704         setup.sound = setup.sound_loops = TRUE;
8705         SetAudioMode(setup.sound);
8706       }
8707       break;
8708
8709     case SOUND_CTRL_ID_SIMPLE:
8710       if (setup.sound_simple)
8711         setup.sound_simple = FALSE;
8712       else if (audio.sound_available)
8713       {
8714         setup.sound = setup.sound_simple = TRUE;
8715         SetAudioMode(setup.sound);
8716       }
8717       break;
8718
8719     default:
8720       break;
8721   }
8722 }