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