rnd-20030210-2-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 "tools.h"
18 #include "screens.h"
19 #include "init.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 MoveFigure() */
36 #define MF_NO_ACTION            0
37 #define MF_MOVING               1
38 #define MF_ACTION               2
39
40 /* for ScrollFigure() */
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 #define INIT_GFX_RANDOM()       (SimpleRND(1000000))
93
94 /* game button identifiers */
95 #define GAME_CTRL_ID_STOP               0
96 #define GAME_CTRL_ID_PAUSE              1
97 #define GAME_CTRL_ID_PLAY               2
98 #define SOUND_CTRL_ID_MUSIC             3
99 #define SOUND_CTRL_ID_LOOPS             4
100 #define SOUND_CTRL_ID_SIMPLE            5
101
102 #define NUM_GAME_BUTTONS                6
103
104 /* forward declaration for internal use */
105 #if 0
106 static void ResetGfxAnimation(int, int);
107 #endif
108
109 static void InitBeltMovement(void);
110 static void CloseAllOpenTimegates(void);
111 static void CheckGravityMovement(struct PlayerInfo *);
112 static void KillHeroUnlessProtected(int, int);
113
114 static void PlaySoundLevel(int, int, int);
115 static void PlaySoundLevelNearest(int, int, int);
116 static void PlaySoundLevelAction(int, int, int);
117 static void PlaySoundLevelElementAction(int, int, int, int);
118
119 static void MapGameButtons();
120 static void HandleGameButtons(struct GadgetInfo *);
121
122 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
123
124
125 /* ------------------------------------------------------------------------- */
126 /* definition of elements that automatically change to other elements after  */
127 /* a specified time, eventually calling a function when changing             */
128 /* ------------------------------------------------------------------------- */
129
130 /* forward declaration for changer functions */
131 static void InitBuggyBase(int x, int y);
132 static void WarnBuggyBase(int x, int y);
133
134 static void InitTrap(int x, int y);
135 static void ActivateTrap(int x, int y);
136 static void ChangeActiveTrap(int x, int y);
137
138 static void InitRobotWheel(int x, int y);
139 static void RunRobotWheel(int x, int y);
140 static void StopRobotWheel(int x, int y);
141
142 static void InitTimegateWheel(int x, int y);
143 static void RunTimegateWheel(int x, int y);
144
145 struct ChangingElementInfo
146 {
147   int base_element;
148   int next_element;
149   int change_delay;
150   void (*pre_change_function)(int x, int y);
151   void (*change_function)(int x, int y);
152   void (*post_change_function)(int x, int y);
153 };
154
155 static struct ChangingElementInfo changing_element_list[] =
156 {
157   { EL_NUT_CRACKING,            EL_EMERALD,              6, NULL, NULL, NULL },
158   { EL_PEARL_BREAKING,          EL_EMPTY,                8, NULL, NULL, NULL },
159   { EL_EXIT_OPENING,            EL_EXIT_OPEN,           29, NULL, NULL, NULL },
160
161   { EL_SWITCHGATE_OPENING,      EL_SWITCHGATE_OPEN,     29, NULL, NULL, NULL },
162   { EL_SWITCHGATE_CLOSING,      EL_SWITCHGATE_CLOSED,   29, NULL, NULL, NULL },
163
164   { EL_TIMEGATE_OPENING,        EL_TIMEGATE_OPEN,       29, NULL, NULL, NULL },
165   { EL_TIMEGATE_CLOSING,        EL_TIMEGATE_CLOSED,     29, NULL, NULL, NULL },
166
167   { EL_ACID_SPLASH_LEFT,        EL_EMPTY,                8, NULL, NULL, NULL },
168   { EL_ACID_SPLASH_RIGHT,       EL_EMPTY,                8, NULL, NULL, NULL },
169
170   { EL_SP_BUGGY_BASE,           EL_SP_BUGGY_BASE_ACTIVATING, 0,
171     InitBuggyBase, NULL, NULL },
172   { EL_SP_BUGGY_BASE_ACTIVATING,EL_SP_BUGGY_BASE_ACTIVE, 0,
173     InitBuggyBase, NULL, NULL },
174   { EL_SP_BUGGY_BASE_ACTIVE,    EL_SP_BUGGY_BASE,        0,
175     InitBuggyBase, WarnBuggyBase, NULL },
176
177   { EL_TRAP,                    EL_TRAP_ACTIVE,          0,
178     InitTrap, NULL, ActivateTrap },
179   { EL_TRAP_ACTIVE,             EL_TRAP,                31,
180     NULL, ChangeActiveTrap, NULL },
181
182   { EL_ROBOT_WHEEL_ACTIVE,      EL_ROBOT_WHEEL,          0,
183     InitRobotWheel, RunRobotWheel, StopRobotWheel },
184
185   { EL_TIMEGATE_SWITCH_ACTIVE,  EL_TIMEGATE_SWITCH,      0,
186     InitTimegateWheel, RunTimegateWheel, NULL },
187
188   { EL_UNDEFINED,               EL_UNDEFINED,           -1, NULL        }
189 };
190
191 static struct ChangingElementInfo changing_element[MAX_NUM_ELEMENTS];
192
193 #define IS_AUTO_CHANGING(e)  (changing_element[e].base_element != EL_UNDEFINED)
194
195
196 void GetPlayerConfig()
197 {
198   if (!audio.sound_available)
199     setup.sound = FALSE;
200
201   if (!audio.loops_available)
202     setup.sound_loops = FALSE;
203
204   if (!audio.music_available)
205     setup.sound_music = FALSE;
206
207   if (!video.fullscreen_available)
208     setup.fullscreen = FALSE;
209
210   setup.sound_simple = setup.sound;
211
212   SetAudioMode(setup.sound);
213   InitJoysticks();
214 }
215
216 static int getBeltNrFromBeltElement(int element)
217 {
218   return (element < EL_CONVEYOR_BELT2_LEFT ? 0 :
219           element < EL_CONVEYOR_BELT3_LEFT ? 1 :
220           element < EL_CONVEYOR_BELT4_LEFT ? 2 : 3);
221 }
222
223 static int getBeltNrFromBeltActiveElement(int element)
224 {
225   return (element < EL_CONVEYOR_BELT2_LEFT_ACTIVE ? 0 :
226           element < EL_CONVEYOR_BELT3_LEFT_ACTIVE ? 1 :
227           element < EL_CONVEYOR_BELT4_LEFT_ACTIVE ? 2 : 3);
228 }
229
230 static int getBeltNrFromBeltSwitchElement(int element)
231 {
232   return (element < EL_CONVEYOR_BELT2_SWITCH_LEFT ? 0 :
233           element < EL_CONVEYOR_BELT3_SWITCH_LEFT ? 1 :
234           element < EL_CONVEYOR_BELT4_SWITCH_LEFT ? 2 : 3);
235 }
236
237 static int getBeltDirNrFromBeltSwitchElement(int element)
238 {
239   static int belt_base_element[4] =
240   {
241     EL_CONVEYOR_BELT1_SWITCH_LEFT,
242     EL_CONVEYOR_BELT2_SWITCH_LEFT,
243     EL_CONVEYOR_BELT3_SWITCH_LEFT,
244     EL_CONVEYOR_BELT4_SWITCH_LEFT
245   };
246
247   int belt_nr = getBeltNrFromBeltSwitchElement(element);
248   int belt_dir_nr = element - belt_base_element[belt_nr];
249
250   return (belt_dir_nr % 3);
251 }
252
253 static int getBeltDirFromBeltSwitchElement(int element)
254 {
255   static int belt_move_dir[3] =
256   {
257     MV_LEFT,
258     MV_NO_MOVING,
259     MV_RIGHT
260   };
261
262   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
263
264   return belt_move_dir[belt_dir_nr];
265 }
266
267 static void InitField(int x, int y, boolean init_game)
268 {
269   switch (Feld[x][y])
270   {
271     case EL_SP_MURPHY:
272       if (init_game)
273       {
274         if (stored_player[0].present)
275         {
276           Feld[x][y] = EL_SP_MURPHY_CLONE;
277           break;
278         }
279
280         Feld[x][y] = EL_PLAYER1;
281       }
282       /* no break! */
283     case EL_PLAYER1:
284     case EL_PLAYER2:
285     case EL_PLAYER3:
286     case EL_PLAYER4:
287       if (init_game)
288       {
289         struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER1];
290         int jx = player->jx, jy = player->jy;
291
292         player->present = TRUE;
293
294         if (!options.network || player->connected)
295         {
296           player->active = TRUE;
297
298           /* remove potentially duplicate players */
299           if (StorePlayer[jx][jy] == Feld[x][y])
300             StorePlayer[jx][jy] = 0;
301
302           StorePlayer[x][y] = Feld[x][y];
303
304           if (options.debug)
305           {
306             printf("Player %d activated.\n", player->element_nr);
307             printf("[Local player is %d and currently %s.]\n",
308                    local_player->element_nr,
309                    local_player->active ? "active" : "not active");
310           }
311         }
312
313         Feld[x][y] = EL_EMPTY;
314         player->jx = player->last_jx = x;
315         player->jy = player->last_jy = y;
316       }
317       break;
318
319     case EL_STONEBLOCK:
320       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
321         Feld[x][y] = EL_ACIDPOOL_TOPLEFT;
322       else if (x > 0 && Feld[x-1][y] == EL_ACID)
323         Feld[x][y] = EL_ACIDPOOL_TOPRIGHT;
324       else if (y > 0 && Feld[x][y-1] == EL_ACIDPOOL_TOPLEFT)
325         Feld[x][y] = EL_ACIDPOOL_BOTTOMLEFT;
326       else if (y > 0 && Feld[x][y-1] == EL_ACID)
327         Feld[x][y] = EL_ACIDPOOL_BOTTOM;
328       else if (y > 0 && Feld[x][y-1] == EL_ACIDPOOL_TOPRIGHT)
329         Feld[x][y] = EL_ACIDPOOL_BOTTOMRIGHT;
330       break;
331
332     case EL_BUG_RIGHT:
333     case EL_BUG_UP:
334     case EL_BUG_LEFT:
335     case EL_BUG_DOWN:
336     case EL_BUG:
337     case EL_SPACESHIP_RIGHT:
338     case EL_SPACESHIP_UP:
339     case EL_SPACESHIP_LEFT:
340     case EL_SPACESHIP_DOWN:
341     case EL_SPACESHIP:
342     case EL_BD_BUTTERFLY_RIGHT:
343     case EL_BD_BUTTERFLY_UP:
344     case EL_BD_BUTTERFLY_LEFT:
345     case EL_BD_BUTTERFLY_DOWN:
346     case EL_BD_BUTTERFLY:
347     case EL_BD_FIREFLY_RIGHT:
348     case EL_BD_FIREFLY_UP:
349     case EL_BD_FIREFLY_LEFT:
350     case EL_BD_FIREFLY_DOWN:
351     case EL_BD_FIREFLY:
352     case EL_PACMAN_RIGHT:
353     case EL_PACMAN_UP:
354     case EL_PACMAN_LEFT:
355     case EL_PACMAN_DOWN:
356     case EL_YAMYAM:
357     case EL_DARK_YAMYAM:
358     case EL_ROBOT:
359     case EL_PACMAN:
360     case EL_SP_SNIKSNAK:
361     case EL_SP_ELECTRON:
362     case EL_MOLE_LEFT:
363     case EL_MOLE_RIGHT:
364     case EL_MOLE_UP:
365     case EL_MOLE_DOWN:
366     case EL_MOLE:
367       InitMovDir(x, y);
368       break;
369
370     case EL_AMOEBA_FULL:
371     case EL_BD_AMOEBA:
372       InitAmoebaNr(x, y);
373       break;
374
375     case EL_AMOEBA_DROP:
376       if (y == lev_fieldy - 1)
377       {
378         Feld[x][y] = EL_AMOEBA_CREATING;
379         Store[x][y] = EL_AMOEBA_WET;
380       }
381       break;
382
383     case EL_DYNAMITE_ACTIVE:
384       MovDelay[x][y] = 96;
385       break;
386
387     case EL_LAMP:
388       local_player->lights_still_needed++;
389       break;
390
391     case EL_SOKOBAN_FIELD_EMPTY:
392       local_player->sokobanfields_still_needed++;
393       break;
394
395     case EL_PENGUIN:
396       local_player->friends_still_needed++;
397       break;
398
399     case EL_PIG:
400     case EL_DRAGON:
401       MovDir[x][y] = 1 << RND(4);
402       break;
403
404     case EL_SP_EMPTY:
405       Feld[x][y] = EL_EMPTY;
406       break;
407
408     case EL_EM_KEY1_FILE:
409       Feld[x][y] = EL_EM_KEY1;
410       break;
411     case EL_EM_KEY2_FILE:
412       Feld[x][y] = EL_EM_KEY2;
413       break;
414     case EL_EM_KEY3_FILE:
415       Feld[x][y] = EL_EM_KEY3;
416       break;
417     case EL_EM_KEY4_FILE:
418       Feld[x][y] = EL_EM_KEY4;
419       break;
420
421     case EL_CONVEYOR_BELT1_SWITCH_LEFT:
422     case EL_CONVEYOR_BELT1_SWITCH_MIDDLE:
423     case EL_CONVEYOR_BELT1_SWITCH_RIGHT:
424     case EL_CONVEYOR_BELT2_SWITCH_LEFT:
425     case EL_CONVEYOR_BELT2_SWITCH_MIDDLE:
426     case EL_CONVEYOR_BELT2_SWITCH_RIGHT:
427     case EL_CONVEYOR_BELT3_SWITCH_LEFT:
428     case EL_CONVEYOR_BELT3_SWITCH_MIDDLE:
429     case EL_CONVEYOR_BELT3_SWITCH_RIGHT:
430     case EL_CONVEYOR_BELT4_SWITCH_LEFT:
431     case EL_CONVEYOR_BELT4_SWITCH_MIDDLE:
432     case EL_CONVEYOR_BELT4_SWITCH_RIGHT:
433       if (init_game)
434       {
435         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
436         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
437         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
438
439         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
440         {
441           game.belt_dir[belt_nr] = belt_dir;
442           game.belt_dir_nr[belt_nr] = belt_dir_nr;
443         }
444         else    /* more than one switch -- set it like the first switch */
445         {
446           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
447         }
448       }
449       break;
450
451     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
452       if (init_game)
453         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
454       break;
455
456     case EL_LIGHT_SWITCH_ACTIVE:
457       if (init_game)
458         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
459       break;
460
461     default:
462       break;
463   }
464 }
465
466 void DrawGameDoorValues()
467 {
468   int i, j;
469
470   for (i=0; i<MAX_PLAYERS; i++)
471     for (j=0; j<4; j++)
472       if (stored_player[i].key[j])
473         DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
474                            IMG_KEY1 + j);
475
476   DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
477            int2str(local_player->gems_still_needed, 3), FS_SMALL, FC_YELLOW);
478   DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
479            int2str(local_player->dynamite, 3), FS_SMALL, FC_YELLOW);
480   DrawText(DX + XX_SCORE, DY + YY_SCORE,
481            int2str(local_player->score, 5), FS_SMALL, FC_YELLOW);
482   DrawText(DX + XX_TIME, DY + YY_TIME,
483            int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
484 }
485
486
487 /*
488   =============================================================================
489   InitGameEngine()
490   -----------------------------------------------------------------------------
491   initialize game engine due to level / tape version number
492   =============================================================================
493 */
494
495 static void InitGameEngine()
496 {
497   int i;
498
499   game.engine_version = (tape.playing ? tape.engine_version :
500                          level.game_version);
501
502 #if 0
503     printf("level %d: level version == %06d\n", level_nr, level.game_version);
504     printf("          tape version == %06d [%s] [file: %06d]\n",
505            tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
506            tape.file_version);
507     printf("       => game.engine_version == %06d\n", game.engine_version);
508 #endif
509
510   /* dynamically adjust player properties according to game engine version */
511   game.initial_move_delay =
512     (game.engine_version <= VERSION_IDENT(2,0,1) ? INITIAL_MOVE_DELAY_ON :
513      INITIAL_MOVE_DELAY_OFF);
514
515   /* dynamically adjust player properties according to level information */
516   game.initial_move_delay_value =
517     (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
518
519   /* dynamically adjust element properties according to game engine version */
520   {
521     static int ep_em_slippery_wall[] =
522     {
523       EL_STEELWALL,
524       EL_WALL,
525       EL_WALL_GROWING,
526       EL_WALL_GROWING_X,
527       EL_WALL_GROWING_Y,
528       EL_WALL_GROWING_XY
529     };
530     static int ep_em_slippery_wall_num = SIZEOF_ARRAY_INT(ep_em_slippery_wall);
531
532     for (i=0; i<ep_em_slippery_wall_num; i++)
533     {
534       if (level.em_slippery_gems)       /* special EM style gems behaviour */
535         Properties2[ep_em_slippery_wall[i]] |=
536           EP_BIT_EM_SLIPPERY_WALL;
537       else
538         Properties2[ep_em_slippery_wall[i]] &=
539           ~EP_BIT_EM_SLIPPERY_WALL;
540     }
541
542     /* "EL_WALL_GROWING_ACTIVE" wasn't slippery for EM gems in version 2.0.1 */
543     if (level.em_slippery_gems && game.engine_version > VERSION_IDENT(2,0,1))
544       Properties2[EL_WALL_GROWING_ACTIVE] |= EP_BIT_EM_SLIPPERY_WALL;
545     else
546       Properties2[EL_WALL_GROWING_ACTIVE] &=~EP_BIT_EM_SLIPPERY_WALL;
547   }
548
549   /* initialize changing elements information */
550   for (i=0; i<MAX_NUM_ELEMENTS; i++)
551   {
552     changing_element[i].base_element = EL_UNDEFINED;
553     changing_element[i].next_element = EL_UNDEFINED;
554     changing_element[i].change_delay = -1;
555     changing_element[i].pre_change_function = NULL;
556     changing_element[i].change_function = NULL;
557     changing_element[i].post_change_function = NULL;
558   }
559
560   i = 0;
561   while (changing_element_list[i].base_element != EL_UNDEFINED)
562   {
563     struct ChangingElementInfo *ce = &changing_element_list[i];
564     int element = ce->base_element;
565
566     changing_element[element].base_element         = ce->base_element;
567     changing_element[element].next_element         = ce->next_element;
568     changing_element[element].change_delay         = ce->change_delay;
569     changing_element[element].pre_change_function  = ce->pre_change_function;
570     changing_element[element].change_function      = ce->change_function;
571     changing_element[element].post_change_function = ce->post_change_function;
572
573     i++;
574   }
575 }
576
577
578 /*
579   =============================================================================
580   InitGame()
581   -----------------------------------------------------------------------------
582   initialize and start new game
583   =============================================================================
584 */
585
586 void InitGame()
587 {
588   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
589   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
590   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
591   int i, j, x, y;
592
593   InitGameEngine();
594
595 #if 0
596 #if DEBUG
597 #if USE_NEW_AMOEBA_CODE
598   printf("Using new amoeba code.\n");
599 #else
600   printf("Using old amoeba code.\n");
601 #endif
602 #endif
603 #endif
604
605   /* don't play tapes over network */
606   network_playing = (options.network && !tape.playing);
607
608   for (i=0; i<MAX_PLAYERS; i++)
609   {
610     struct PlayerInfo *player = &stored_player[i];
611
612     player->index_nr = i;
613     player->element_nr = EL_PLAYER1 + i;
614
615     player->present = FALSE;
616     player->active = FALSE;
617
618     player->action = 0;
619     player->effective_action = 0;
620     player->programmed_action = 0;
621
622     player->score = 0;
623     player->gems_still_needed = level.gems_needed;
624     player->sokobanfields_still_needed = 0;
625     player->lights_still_needed = 0;
626     player->friends_still_needed = 0;
627
628     for (j=0; j<4; j++)
629       player->key[j] = FALSE;
630
631     player->dynamite = 0;
632     player->dynabomb_count = 0;
633     player->dynabomb_size = 1;
634     player->dynabombs_left = 0;
635     player->dynabomb_xl = FALSE;
636
637     player->MovDir = MV_NO_MOVING;
638     player->MovPos = 0;
639     player->Pushing = FALSE;
640     player->Switching = FALSE;
641     player->GfxPos = 0;
642     player->Frame = 0;
643
644     player->actual_frame_counter = 0;
645
646     player->frame_reset_delay = 0;
647
648     player->last_move_dir = MV_NO_MOVING;
649     player->is_moving = FALSE;
650
651     player->move_delay       = game.initial_move_delay;
652     player->move_delay_value = game.initial_move_delay_value;
653
654     player->push_delay = 0;
655     player->push_delay_value = 5;
656
657     player->snapped = FALSE;
658
659     player->last_jx = player->last_jy = 0;
660     player->jx = player->jy = 0;
661
662     player->shield_normal_time_left = 0;
663     player->shield_deadly_time_left = 0;
664
665     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
666     SnapField(player, 0, 0);
667
668     player->LevelSolved = FALSE;
669     player->GameOver = FALSE;
670   }
671
672   network_player_action_received = FALSE;
673
674 #if defined(PLATFORM_UNIX)
675   /* initial null action */
676   if (network_playing)
677     SendToServer_MovePlayer(MV_NO_MOVING);
678 #endif
679
680   ZX = ZY = -1;
681
682   FrameCounter = 0;
683   TimeFrames = 0;
684   TimePlayed = 0;
685   TimeLeft = level.time;
686
687   ScreenMovDir = MV_NO_MOVING;
688   ScreenMovPos = 0;
689   ScreenGfxPos = 0;
690
691   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
692
693   AllPlayersGone = FALSE;
694
695   game.yam_content_nr = 0;
696   game.magic_wall_active = FALSE;
697   game.magic_wall_time_left = 0;
698   game.light_time_left = 0;
699   game.timegate_time_left = 0;
700   game.switchgate_pos = 0;
701   game.balloon_dir = MV_NO_MOVING;
702   game.explosions_delayed = TRUE;
703
704   for (i=0; i<4; i++)
705   {
706     game.belt_dir[i] = MV_NO_MOVING;
707     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
708   }
709
710   for (i=0; i<MAX_NUM_AMOEBA; i++)
711     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
712
713   for (x=0; x<lev_fieldx; x++)
714   {
715     for (y=0; y<lev_fieldy; y++)
716     {
717       Feld[x][y] = Ur[x][y];
718       MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
719       Store[x][y] = Store2[x][y] = StorePlayer[x][y] = 0;
720       AmoebaNr[x][y] = 0;
721       JustStopped[x][y] = 0;
722       Stop[x][y] = FALSE;
723       ExplodePhase[x][y] = 0;
724       ExplodeField[x][y] = EX_NO_EXPLOSION;
725
726       GfxFrame[x][y] = 0;
727       GfxAction[x][y] = ACTION_DEFAULT;
728       GfxRandom[x][y] = INIT_GFX_RANDOM();
729     }
730   }
731
732   for(y=0; y<lev_fieldy; y++)
733   {
734     for(x=0; x<lev_fieldx; x++)
735     {
736       if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
737         emulate_bd = FALSE;
738       if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
739         emulate_sb = FALSE;
740       if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
741         emulate_sp = FALSE;
742
743       InitField(x, y, TRUE);
744     }
745   }
746
747   InitBeltMovement();
748
749   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
750                     emulate_sb ? EMU_SOKOBAN :
751                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
752
753   /* correct non-moving belts to start moving left */
754   for (i=0; i<4; i++)
755     if (game.belt_dir[i] == MV_NO_MOVING)
756       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
757
758   /* check if any connected player was not found in playfield */
759   for (i=0; i<MAX_PLAYERS; i++)
760   {
761     struct PlayerInfo *player = &stored_player[i];
762
763     if (player->connected && !player->present)
764     {
765       for (j=0; j<MAX_PLAYERS; j++)
766       {
767         struct PlayerInfo *some_player = &stored_player[j];
768         int jx = some_player->jx, jy = some_player->jy;
769
770         /* assign first free player found that is present in the playfield */
771         if (some_player->present && !some_player->connected)
772         {
773           player->present = TRUE;
774           player->active = TRUE;
775           some_player->present = FALSE;
776
777           StorePlayer[jx][jy] = player->element_nr;
778           player->jx = player->last_jx = jx;
779           player->jy = player->last_jy = jy;
780
781           break;
782         }
783       }
784     }
785   }
786
787   if (tape.playing)
788   {
789     /* when playing a tape, eliminate all players who do not participate */
790
791     for (i=0; i<MAX_PLAYERS; i++)
792     {
793       if (stored_player[i].active && !tape.player_participates[i])
794       {
795         struct PlayerInfo *player = &stored_player[i];
796         int jx = player->jx, jy = player->jy;
797
798         player->active = FALSE;
799         StorePlayer[jx][jy] = 0;
800         Feld[jx][jy] = EL_EMPTY;
801       }
802     }
803   }
804   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
805   {
806     /* when in single player mode, eliminate all but the first active player */
807
808     for (i=0; i<MAX_PLAYERS; i++)
809     {
810       if (stored_player[i].active)
811       {
812         for (j=i+1; j<MAX_PLAYERS; j++)
813         {
814           if (stored_player[j].active)
815           {
816             struct PlayerInfo *player = &stored_player[j];
817             int jx = player->jx, jy = player->jy;
818
819             player->active = FALSE;
820             StorePlayer[jx][jy] = 0;
821             Feld[jx][jy] = EL_EMPTY;
822           }
823         }
824       }
825     }
826   }
827
828   /* when recording the game, store which players take part in the game */
829   if (tape.recording)
830   {
831     for (i=0; i<MAX_PLAYERS; i++)
832       if (stored_player[i].active)
833         tape.player_participates[i] = TRUE;
834   }
835
836   if (options.debug)
837   {
838     for (i=0; i<MAX_PLAYERS; i++)
839     {
840       struct PlayerInfo *player = &stored_player[i];
841
842       printf("Player %d: present == %d, connected == %d, active == %d.\n",
843              i+1,
844              player->present,
845              player->connected,
846              player->active);
847       if (local_player == player)
848         printf("Player  %d is local player.\n", i+1);
849     }
850   }
851
852   if (BorderElement == EL_EMPTY)
853   {
854     SBX_Left = 0;
855     SBX_Right = lev_fieldx - SCR_FIELDX;
856     SBY_Upper = 0;
857     SBY_Lower = lev_fieldy - SCR_FIELDY;
858   }
859   else
860   {
861     SBX_Left = -1;
862     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
863     SBY_Upper = -1;
864     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
865   }
866
867   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
868     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
869
870   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
871     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
872
873   scroll_x = SBX_Left;
874   scroll_y = SBY_Upper;
875   if (local_player->jx >= SBX_Left + MIDPOSX)
876     scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
877                 local_player->jx - MIDPOSX :
878                 SBX_Right);
879   if (local_player->jy >= SBY_Upper + MIDPOSY)
880     scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
881                 local_player->jy - MIDPOSY :
882                 SBY_Lower);
883
884   CloseDoor(DOOR_CLOSE_1);
885
886   DrawLevel();
887   DrawAllPlayers();
888
889   /* after drawing the level, correct some elements */
890   if (game.timegate_time_left == 0)
891     CloseAllOpenTimegates();
892
893   if (setup.soft_scrolling)
894     BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
895
896   redraw_mask |= REDRAW_FROM_BACKBUFFER;
897   FadeToFront();
898
899   /* copy default game door content to main double buffer */
900   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
901              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
902
903   if (level_nr < 100)
904     DrawText(DX + XX_LEVEL, DY + YY_LEVEL,
905              int2str(level_nr, 2), FS_SMALL, FC_YELLOW);
906   else
907   {
908     DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
909                 int2str(level_nr, 3), FS_SMALL, FC_SPECIAL3, FONT_OPAQUE);
910     BlitBitmap(drawto, drawto,
911                DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
912                FONT5_XSIZE * 3, FONT5_YSIZE - 1,
913                DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
914   }
915
916   DrawGameDoorValues();
917
918   UnmapGameButtons();
919   UnmapTapeButtons();
920   game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
921   game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
922   game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
923   MapGameButtons();
924   MapTapeButtons();
925
926   /* copy actual game door content to door double buffer for OpenDoor() */
927   BlitBitmap(drawto, bitmap_db_door,
928              DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
929
930   OpenDoor(DOOR_OPEN_ALL);
931
932   PlaySoundStereo(SND_GAME_STARTING, SOUND_MAX_RIGHT);
933   if (setup.sound_music)
934     PlayMusic(level_nr);
935
936   KeyboardAutoRepeatOff();
937
938   if (options.debug)
939   {
940     for (i=0; i<4; i++)
941       printf("Player %d %sactive.\n",
942              i + 1, (stored_player[i].active ? "" : "not "));
943   }
944 }
945
946 void InitMovDir(int x, int y)
947 {
948   int i, element = Feld[x][y];
949   static int xy[4][2] =
950   {
951     {  0, +1 },
952     { +1,  0 },
953     {  0, -1 },
954     { -1,  0 }
955   };
956   static int direction[3][4] =
957   {
958     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
959     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
960     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
961   };
962
963   switch(element)
964   {
965     case EL_BUG_RIGHT:
966     case EL_BUG_UP:
967     case EL_BUG_LEFT:
968     case EL_BUG_DOWN:
969       Feld[x][y] = EL_BUG;
970       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
971       break;
972
973     case EL_SPACESHIP_RIGHT:
974     case EL_SPACESHIP_UP:
975     case EL_SPACESHIP_LEFT:
976     case EL_SPACESHIP_DOWN:
977       Feld[x][y] = EL_SPACESHIP;
978       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
979       break;
980
981     case EL_BD_BUTTERFLY_RIGHT:
982     case EL_BD_BUTTERFLY_UP:
983     case EL_BD_BUTTERFLY_LEFT:
984     case EL_BD_BUTTERFLY_DOWN:
985       Feld[x][y] = EL_BD_BUTTERFLY;
986       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
987       break;
988
989     case EL_BD_FIREFLY_RIGHT:
990     case EL_BD_FIREFLY_UP:
991     case EL_BD_FIREFLY_LEFT:
992     case EL_BD_FIREFLY_DOWN:
993       Feld[x][y] = EL_BD_FIREFLY;
994       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
995       break;
996
997     case EL_PACMAN_RIGHT:
998     case EL_PACMAN_UP:
999     case EL_PACMAN_LEFT:
1000     case EL_PACMAN_DOWN:
1001       Feld[x][y] = EL_PACMAN;
1002       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1003       break;
1004
1005     case EL_SP_SNIKSNAK:
1006       MovDir[x][y] = MV_UP;
1007       break;
1008
1009     case EL_SP_ELECTRON:
1010       MovDir[x][y] = MV_LEFT;
1011       break;
1012
1013     case EL_MOLE_LEFT:
1014     case EL_MOLE_RIGHT:
1015     case EL_MOLE_UP:
1016     case EL_MOLE_DOWN:
1017       Feld[x][y] = EL_MOLE;
1018       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1019       break;
1020
1021     default:
1022       MovDir[x][y] = 1 << RND(4);
1023       if (element != EL_BUG &&
1024           element != EL_SPACESHIP &&
1025           element != EL_BD_BUTTERFLY &&
1026           element != EL_BD_FIREFLY)
1027         break;
1028
1029       for (i=0; i<4; i++)
1030       {
1031         int x1 = x + xy[i][0];
1032         int y1 = y + xy[i][1];
1033
1034         if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1035         {
1036           if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1037           {
1038             MovDir[x][y] = direction[0][i];
1039             break;
1040           }
1041           else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1042                    element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1043           {
1044             MovDir[x][y] = direction[1][i];
1045             break;
1046           }
1047         }
1048       }
1049       break;
1050   }
1051 }
1052
1053 void InitAmoebaNr(int x, int y)
1054 {
1055   int i;
1056   int group_nr = AmoebeNachbarNr(x, y);
1057
1058   if (group_nr == 0)
1059   {
1060     for (i=1; i<MAX_NUM_AMOEBA; i++)
1061     {
1062       if (AmoebaCnt[i] == 0)
1063       {
1064         group_nr = i;
1065         break;
1066       }
1067     }
1068   }
1069
1070   AmoebaNr[x][y] = group_nr;
1071   AmoebaCnt[group_nr]++;
1072   AmoebaCnt2[group_nr]++;
1073 }
1074
1075 void GameWon()
1076 {
1077   int hi_pos;
1078   boolean raise_level = FALSE;
1079
1080   if (local_player->MovPos)
1081     return;
1082
1083   if (tape.playing && tape.auto_play)
1084     tape.auto_play_level_solved = TRUE;
1085
1086   local_player->LevelSolved = FALSE;
1087
1088   PlaySoundStereo(SND_GAME_WINNING, SOUND_MAX_RIGHT);
1089
1090   if (TimeLeft)
1091   {
1092     if (!tape.playing && setup.sound_loops)
1093       PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
1094                    SND_CTRL_PLAY_LOOP);
1095
1096     while (TimeLeft > 0)
1097     {
1098       if (!tape.playing && !setup.sound_loops)
1099         PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_RIGHT);
1100       if (TimeLeft > 0 && !(TimeLeft % 10))
1101         RaiseScore(level.score[SC_ZEITBONUS]);
1102       if (TimeLeft > 100 && !(TimeLeft % 10))
1103         TimeLeft -= 10;
1104       else
1105         TimeLeft--;
1106       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
1107       BackToFront();
1108
1109       if (!tape.playing)
1110         Delay(10);
1111     }
1112
1113     if (!tape.playing && setup.sound_loops)
1114       StopSound(SND_GAME_LEVELTIME_BONUS);
1115   }
1116   else if (level.time == 0)             /* level without time limit */
1117   {
1118     if (!tape.playing && setup.sound_loops)
1119       PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
1120                    SND_CTRL_PLAY_LOOP);
1121
1122     while (TimePlayed < 999)
1123     {
1124       if (!tape.playing && !setup.sound_loops)
1125         PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_RIGHT);
1126       if (TimePlayed < 999 && !(TimePlayed % 10))
1127         RaiseScore(level.score[SC_ZEITBONUS]);
1128       if (TimePlayed < 900 && !(TimePlayed % 10))
1129         TimePlayed += 10;
1130       else
1131         TimePlayed++;
1132       DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FS_SMALL, FC_YELLOW);
1133       BackToFront();
1134
1135       if (!tape.playing)
1136         Delay(10);
1137     }
1138
1139     if (!tape.playing && setup.sound_loops)
1140       StopSound(SND_GAME_LEVELTIME_BONUS);
1141   }
1142
1143 #if 0
1144   FadeSounds();
1145 #endif
1146
1147   /* Hero disappears */
1148   DrawLevelField(ExitX, ExitY);
1149   BackToFront();
1150
1151   if (tape.playing)
1152     return;
1153
1154   CloseDoor(DOOR_CLOSE_1);
1155
1156   if (tape.recording)
1157   {
1158     TapeStop();
1159     SaveTape(tape.level_nr);            /* Ask to save tape */
1160   }
1161
1162   if (level_nr == leveldir_current->handicap_level)
1163   {
1164     leveldir_current->handicap_level++;
1165     SaveLevelSetup_SeriesInfo();
1166   }
1167
1168   if (level_editor_test_game)
1169     local_player->score = -1;   /* no highscore when playing from editor */
1170   else if (level_nr < leveldir_current->last_level)
1171     raise_level = TRUE;         /* advance to next level */
1172
1173   if ((hi_pos = NewHiScore()) >= 0) 
1174   {
1175     game_status = HALLOFFAME;
1176     DrawHallOfFame(hi_pos);
1177     if (raise_level)
1178     {
1179       level_nr++;
1180       TapeErase();
1181     }
1182   }
1183   else
1184   {
1185     game_status = MAINMENU;
1186     if (raise_level)
1187     {
1188       level_nr++;
1189       TapeErase();
1190     }
1191     DrawMainMenu();
1192   }
1193
1194   BackToFront();
1195 }
1196
1197 int NewHiScore()
1198 {
1199   int k, l;
1200   int position = -1;
1201
1202   LoadScore(level_nr);
1203
1204   if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
1205       local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score) 
1206     return -1;
1207
1208   for (k=0; k<MAX_SCORE_ENTRIES; k++) 
1209   {
1210     if (local_player->score > highscore[k].Score)
1211     {
1212       /* player has made it to the hall of fame */
1213
1214       if (k < MAX_SCORE_ENTRIES - 1)
1215       {
1216         int m = MAX_SCORE_ENTRIES - 1;
1217
1218 #ifdef ONE_PER_NAME
1219         for (l=k; l<MAX_SCORE_ENTRIES; l++)
1220           if (!strcmp(setup.player_name, highscore[l].Name))
1221             m = l;
1222         if (m == k)     /* player's new highscore overwrites his old one */
1223           goto put_into_list;
1224 #endif
1225
1226         for (l=m; l>k; l--)
1227         {
1228           strcpy(highscore[l].Name, highscore[l - 1].Name);
1229           highscore[l].Score = highscore[l - 1].Score;
1230         }
1231       }
1232
1233 #ifdef ONE_PER_NAME
1234       put_into_list:
1235 #endif
1236       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
1237       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
1238       highscore[k].Score = local_player->score; 
1239       position = k;
1240       break;
1241     }
1242
1243 #ifdef ONE_PER_NAME
1244     else if (!strncmp(setup.player_name, highscore[k].Name,
1245                       MAX_PLAYER_NAME_LEN))
1246       break;    /* player already there with a higher score */
1247 #endif
1248
1249   }
1250
1251   if (position >= 0) 
1252     SaveScore(level_nr);
1253
1254   return position;
1255 }
1256
1257 static void ResetRandomAnimationValue(int x, int y)
1258 {
1259   GfxRandom[x][y] = INIT_GFX_RANDOM();
1260 }
1261
1262 static void ResetGfxAnimation(int x, int y)
1263 {
1264 #if 0
1265 #if 1
1266   int element = Feld[x][y];
1267   int graphic = el2img(element);
1268
1269   /* reset random value not until one full delay cycle was reached */
1270   if (ANIM_MODE(graphic) == ANIM_RANDOM &&
1271       GfxFrame[x][y] > ANIM_DELAY(graphic))
1272 #endif
1273     ResetRandomAnimationValue(x, y);
1274 #endif
1275
1276   GfxFrame[x][y] = 0;
1277   GfxAction[x][y] = ACTION_DEFAULT;
1278
1279 #if 0
1280   if (Feld[x][y] == EL_ROCK)
1281     printf("ResetGfxAnimation: EL_ROCK [%d, %d]\n",
1282            JustStopped[x][y], MovDir[x][y]);
1283 #endif
1284 }
1285
1286 void InitMovingField(int x, int y, int direction)
1287 {
1288   int element = Feld[x][y];
1289   int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1290   int newy = y + (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
1291
1292   if (!JustStopped[x][y] || direction != MovDir[x][y])
1293     ResetGfxAnimation(x, y);
1294
1295   MovDir[newx][newy] = MovDir[x][y] = direction;
1296
1297   if (Feld[newx][newy] == EL_EMPTY)
1298     Feld[newx][newy] = EL_BLOCKED;
1299
1300   if (direction == MV_DOWN && CAN_FALL(element))
1301     GfxAction[x][y] = ACTION_FALLING;
1302   else
1303     GfxAction[x][y] = ACTION_MOVING;
1304
1305   GfxFrame[newx][newy] = GfxFrame[x][y];
1306   GfxAction[newx][newy] = GfxAction[x][y];
1307   GfxRandom[newx][newy] = GfxRandom[x][y];
1308 }
1309
1310 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
1311 {
1312   int direction = MovDir[x][y];
1313   int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
1314   int newy = y + (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
1315
1316   *goes_to_x = newx;
1317   *goes_to_y = newy;
1318 }
1319
1320 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
1321 {
1322   int oldx = x, oldy = y;
1323   int direction = MovDir[x][y];
1324
1325   if (direction == MV_LEFT)
1326     oldx++;
1327   else if (direction == MV_RIGHT)
1328     oldx--;
1329   else if (direction == MV_UP)
1330     oldy++;
1331   else if (direction == MV_DOWN)
1332     oldy--;
1333
1334   *comes_from_x = oldx;
1335   *comes_from_y = oldy;
1336 }
1337
1338 int MovingOrBlocked2Element(int x, int y)
1339 {
1340   int element = Feld[x][y];
1341
1342   if (element == EL_BLOCKED)
1343   {
1344     int oldx, oldy;
1345
1346     Blocked2Moving(x, y, &oldx, &oldy);
1347     return Feld[oldx][oldy];
1348   }
1349   else
1350     return element;
1351 }
1352
1353 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
1354 {
1355   /* like MovingOrBlocked2Element(), but if element is moving
1356      and (x,y) is the field the moving element is just leaving,
1357      return EL_BLOCKED instead of the element value */
1358   int element = Feld[x][y];
1359
1360   if (IS_MOVING(x, y))
1361   {
1362     if (element == EL_BLOCKED)
1363     {
1364       int oldx, oldy;
1365
1366       Blocked2Moving(x, y, &oldx, &oldy);
1367       return Feld[oldx][oldy];
1368     }
1369     else
1370       return EL_BLOCKED;
1371   }
1372   else
1373     return element;
1374 }
1375
1376 static void RemoveField(int x, int y)
1377 {
1378   Feld[x][y] = EL_EMPTY;
1379   MovPos[x][y] = 0;
1380   MovDir[x][y] = 0;
1381   MovDelay[x][y] = 0;
1382 }
1383
1384 void RemoveMovingField(int x, int y)
1385 {
1386   int oldx = x, oldy = y, newx = x, newy = y;
1387
1388   if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
1389     return;
1390
1391   if (IS_MOVING(x, y))
1392   {
1393     Moving2Blocked(x, y, &newx, &newy);
1394     if (Feld[newx][newy] != EL_BLOCKED)
1395       return;
1396   }
1397   else if (Feld[x][y] == EL_BLOCKED)
1398   {
1399     Blocked2Moving(x, y, &oldx, &oldy);
1400     if (!IS_MOVING(oldx, oldy))
1401       return;
1402   }
1403
1404   if (Feld[x][y] == EL_BLOCKED &&
1405       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
1406        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
1407        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
1408        Feld[oldx][oldy] == EL_AMOEBA_DRIPPING))
1409     Feld[oldx][oldy] = get_next_element(Feld[oldx][oldy]);
1410   else
1411     Feld[oldx][oldy] = EL_EMPTY;
1412
1413   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
1414
1415   Feld[newx][newy] = EL_EMPTY;
1416   MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
1417   MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
1418   GfxAction[oldx][oldy] = GfxAction[newx][newy] = ACTION_DEFAULT;
1419
1420   DrawLevelField(oldx, oldy);
1421   DrawLevelField(newx, newy);
1422 }
1423
1424 void DrawDynamite(int x, int y)
1425 {
1426   int sx = SCREENX(x), sy = SCREENY(y);
1427   int graphic = el2img(Feld[x][y]);
1428   int frame;
1429
1430   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
1431     return;
1432
1433   if (Store[x][y])
1434     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
1435
1436   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
1437
1438   if (game.emulation == EMU_SUPAPLEX)
1439     DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
1440   else if (Store[x][y])
1441     DrawGraphicThruMask(sx, sy, graphic, frame);
1442   else
1443     DrawGraphic(sx, sy, graphic, frame);
1444 }
1445
1446 void CheckDynamite(int x, int y)
1447 {
1448   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
1449   {
1450     MovDelay[x][y]--;
1451
1452     if (MovDelay[x][y] != 0)
1453     {
1454       DrawDynamite(x, y);
1455
1456       /* !!! correct: "PlaySoundLevelActionIfLoop" etc. !!! */
1457       PlaySoundLevelAction(x, y, ACTION_ACTIVE);
1458
1459       return;
1460     }
1461   }
1462
1463   if (Feld[x][y] == EL_DYNAMITE_ACTIVE)
1464     StopSound(SND_DYNAMITE_ACTIVE);
1465   else
1466     StopSound(SND_DYNABOMB_ACTIVE);
1467
1468   Bang(x, y);
1469 }
1470
1471 void Explode(int ex, int ey, int phase, int mode)
1472 {
1473   int x, y;
1474   int num_phase = 9;
1475   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
1476   int last_phase = num_phase * delay;
1477   int half_phase = (num_phase / 2) * delay;
1478   int first_phase_after_start = EX_PHASE_START + 1;
1479
1480   if (game.explosions_delayed)
1481   {
1482     ExplodeField[ex][ey] = mode;
1483     return;
1484   }
1485
1486   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
1487   {
1488     int center_element = Feld[ex][ey];
1489
1490     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
1491     {
1492       /* put moving element to center field (and let it explode there) */
1493       center_element = MovingOrBlocked2Element(ex, ey);
1494       RemoveMovingField(ex, ey);
1495       Feld[ex][ey] = center_element;
1496     }
1497
1498     for (y=ey-1; y<=ey+1; y++) for(x=ex-1; x<=ex+1; x++)
1499     {
1500       int element;
1501
1502       if (!IN_LEV_FIELD(x, y) ||
1503           ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
1504            (x != ex || y != ey)))
1505         continue;
1506
1507       element = Feld[x][y];
1508
1509       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
1510       {
1511         element = MovingOrBlocked2Element(x, y);
1512         RemoveMovingField(x, y);
1513       }
1514
1515       if (IS_MASSIVE(element) || element == EL_FLAMES)
1516         continue;
1517
1518       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
1519       {
1520         if (IS_ACTIVE_BOMB(element))
1521         {
1522           /* re-activate things under the bomb like gate or penguin */
1523           Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
1524           Store[x][y] = 0;
1525         }
1526
1527         continue;
1528       }
1529
1530       if (element == EL_EXPLOSION)
1531         element = Store2[x][y];
1532
1533       if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
1534       {
1535         switch(StorePlayer[ex][ey])
1536         {
1537           case EL_PLAYER2:
1538             Store[x][y] = EL_EMERALD_RED;
1539             break;
1540           case EL_PLAYER3:
1541             Store[x][y] = EL_EMERALD;
1542             break;
1543           case EL_PLAYER4:
1544             Store[x][y] = EL_EMERALD_PURPLE;
1545             break;
1546           case EL_PLAYER1:
1547           default:
1548             Store[x][y] = EL_EMERALD_YELLOW;
1549             break;
1550         }
1551
1552         if (game.emulation == EMU_SUPAPLEX)
1553           Store[x][y] = EL_EMPTY;
1554       }
1555       else if (center_element == EL_MOLE)
1556         Store[x][y] = EL_EMERALD_RED;
1557       else if (center_element == EL_PENGUIN)
1558         Store[x][y] = EL_EMERALD_PURPLE;
1559       else if (center_element == EL_BUG)
1560         Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
1561       else if (center_element == EL_BD_BUTTERFLY)
1562         Store[x][y] = EL_BD_DIAMOND;
1563       else if (center_element == EL_SP_ELECTRON)
1564         Store[x][y] = EL_SP_INFOTRON;
1565       else if (center_element == EL_YAMYAM)
1566         Store[x][y] = level.yam_content[game.yam_content_nr][x-ex+1][y-ey+1];
1567       else if (center_element == EL_AMOEBA_TO_DIAMOND)
1568         Store[x][y] = level.amoeba_content;
1569       else if (element == EL_WALL_EMERALD)
1570         Store[x][y] = EL_EMERALD;
1571       else if (element == EL_WALL_DIAMOND)
1572         Store[x][y] = EL_DIAMOND;
1573       else if (element == EL_WALL_BD_DIAMOND)
1574         Store[x][y] = EL_BD_DIAMOND;
1575       else if (element == EL_WALL_EMERALD_YELLOW)
1576         Store[x][y] = EL_EMERALD_YELLOW;
1577       else if (element == EL_WALL_EMERALD_RED)
1578         Store[x][y] = EL_EMERALD_RED;
1579       else if (element == EL_WALL_EMERALD_PURPLE)
1580         Store[x][y] = EL_EMERALD_PURPLE;
1581       else if (element == EL_WALL_PEARL)
1582         Store[x][y] = EL_PEARL;
1583       else if (element == EL_WALL_CRYSTAL)
1584         Store[x][y] = EL_CRYSTAL;
1585       else if (!IS_PFORTE(Store[x][y]))
1586         Store[x][y] = EL_EMPTY;
1587
1588       if (x != ex || y != ey ||
1589           center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
1590         Store2[x][y] = element;
1591
1592       if (AmoebaNr[x][y] &&
1593           (element == EL_AMOEBA_FULL ||
1594            element == EL_BD_AMOEBA ||
1595            element == EL_AMOEBA_CREATING))
1596       {
1597         AmoebaCnt[AmoebaNr[x][y]]--;
1598         AmoebaCnt2[AmoebaNr[x][y]]--;
1599       }
1600
1601       Feld[x][y] = EL_EXPLOSION;
1602       MovDir[x][y] = MovPos[x][y] = 0;
1603       AmoebaNr[x][y] = 0;
1604       ExplodePhase[x][y] = 1;
1605       Stop[x][y] = TRUE;
1606     }
1607
1608     if (center_element == EL_YAMYAM)
1609       game.yam_content_nr = (game.yam_content_nr + 1) % level.num_yam_contents;
1610
1611     return;
1612   }
1613
1614   if (Stop[ex][ey])
1615     return;
1616
1617   x = ex;
1618   y = ey;
1619
1620   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
1621
1622   if (phase == first_phase_after_start)
1623   {
1624     int element = Store2[x][y];
1625
1626     if (element == EL_BLACK_ORB)
1627     {
1628       Feld[x][y] = Store2[x][y];
1629       Store2[x][y] = 0;
1630       Bang(x, y);
1631     }
1632   }
1633   else if (phase == half_phase)
1634   {
1635     int element = Store2[x][y];
1636
1637     if (IS_PLAYER(x, y))
1638       KillHeroUnlessProtected(x, y);
1639     else if (IS_EXPLOSIVE(element))
1640     {
1641       Feld[x][y] = Store2[x][y];
1642       Store2[x][y] = 0;
1643       Bang(x, y);
1644     }
1645     else if (element == EL_AMOEBA_TO_DIAMOND)
1646       AmoebeUmwandeln(x, y);
1647   }
1648
1649   if (phase == last_phase)
1650   {
1651     int element;
1652
1653     element = Feld[x][y] = Store[x][y];
1654     Store[x][y] = Store2[x][y] = 0;
1655     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
1656     InitField(x, y, FALSE);
1657     if (CAN_MOVE(element) || COULD_MOVE(element))
1658       InitMovDir(x, y);
1659     DrawLevelField(x, y);
1660
1661     if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
1662       StorePlayer[x][y] = 0;
1663   }
1664   else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1665   {
1666     int stored = Store[x][y];
1667     int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
1668                    stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
1669                    IMG_SP_EXPLOSION);
1670     int frame = getGraphicAnimationFrame(graphic, phase - delay);
1671
1672     if (phase == delay)
1673       DrawCrumbledSand(SCREENX(x), SCREENY(y));
1674
1675     if (IS_PFORTE(Store[x][y]))
1676     {
1677       DrawLevelElement(x, y, Store[x][y]);
1678       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
1679     }
1680     else
1681       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
1682   }
1683 }
1684
1685 void DynaExplode(int ex, int ey)
1686 {
1687   int i, j;
1688   int dynabomb_size = 1;
1689   boolean dynabomb_xl = FALSE;
1690   struct PlayerInfo *player;
1691   static int xy[4][2] =
1692   {
1693     { 0, -1 },
1694     { -1, 0 },
1695     { +1, 0 },
1696     { 0, +1 }
1697   };
1698
1699   if (IS_ACTIVE_BOMB(Feld[ex][ey]))
1700   {
1701     player = &stored_player[Feld[ex][ey] - EL_DYNABOMB_PLAYER1_ACTIVE];
1702     dynabomb_size = player->dynabomb_size;
1703     dynabomb_xl = player->dynabomb_xl;
1704     player->dynabombs_left++;
1705   }
1706
1707   Explode(ex, ey, EX_PHASE_START, EX_CENTER);
1708
1709   for (i=0; i<4; i++)
1710   {
1711     for (j=1; j<=dynabomb_size; j++)
1712     {
1713       int x = ex + j * xy[i % 4][0];
1714       int y = ey + j * xy[i % 4][1];
1715       int element;
1716
1717       if (!IN_LEV_FIELD(x, y) || IS_MASSIVE(Feld[x][y]))
1718         break;
1719
1720       element = Feld[x][y];
1721
1722       /* do not restart explosions of fields with active bombs */
1723       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
1724         continue;
1725
1726       Explode(x, y, EX_PHASE_START, EX_BORDER);
1727
1728       if (element != EL_EMPTY &&
1729           element != EL_SAND &&
1730           element != EL_EXPLOSION &&
1731           !dynabomb_xl)
1732         break;
1733     }
1734   }
1735 }
1736
1737 void Bang(int x, int y)
1738 {
1739   int element = Feld[x][y];
1740
1741   if (game.emulation == EMU_SUPAPLEX)
1742     PlaySoundLevel(x, y, SND_SP_ELEMENT_EXPLODING);
1743   else
1744     PlaySoundLevel(x, y, SND_ELEMENT_EXPLODING);
1745
1746 #if 0
1747   if (IS_PLAYER(x, y))  /* remove objects that might cause smaller explosion */
1748     element = EL_EMPTY;
1749 #endif
1750
1751   switch(element)
1752   {
1753     case EL_BUG:
1754     case EL_SPACESHIP:
1755     case EL_BD_BUTTERFLY:
1756     case EL_BD_FIREFLY:
1757     case EL_YAMYAM:
1758     case EL_DARK_YAMYAM:
1759     case EL_ROBOT:
1760     case EL_PACMAN:
1761     case EL_MOLE:
1762       RaiseScoreElement(element);
1763       Explode(x, y, EX_PHASE_START, EX_NORMAL);
1764       break;
1765     case EL_DYNABOMB_PLAYER1_ACTIVE:
1766     case EL_DYNABOMB_PLAYER2_ACTIVE:
1767     case EL_DYNABOMB_PLAYER3_ACTIVE:
1768     case EL_DYNABOMB_PLAYER4_ACTIVE:
1769     case EL_DYNABOMB_NR:
1770     case EL_DYNABOMB_SZ:
1771     case EL_DYNABOMB_XL:
1772       DynaExplode(x, y);
1773       break;
1774     case EL_PENGUIN:
1775     case EL_LAMP:
1776     case EL_LAMP_ACTIVE:
1777       if (IS_PLAYER(x, y))
1778         Explode(x, y, EX_PHASE_START, EX_NORMAL);
1779       else
1780         Explode(x, y, EX_PHASE_START, EX_CENTER);
1781       break;
1782     default:
1783       Explode(x, y, EX_PHASE_START, EX_NORMAL);
1784       break;
1785   }
1786 }
1787
1788 void SplashAcid(int x, int y)
1789 {
1790   int element = Feld[x][y];
1791
1792   if (element != EL_ACID_SPLASH_LEFT &&
1793       element != EL_ACID_SPLASH_RIGHT)
1794   {
1795     PlaySoundLevel(x, y, SND_ACID_SPLASHING);
1796
1797     if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
1798         (!IN_LEV_FIELD(x-1, y-1) ||
1799          !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
1800       Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
1801
1802     if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
1803         (!IN_LEV_FIELD(x+1, y-1) ||
1804          !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
1805       Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
1806   }
1807
1808 #if 0
1809   else                                                          /* go on */
1810   {
1811     int graphic = (element == EL_ACID_SPLASH_LEFT ?
1812                    IMG_ACID_SPLASH_LEFT :
1813                    IMG_ACID_SPLASH_RIGHT);
1814
1815     if (!MovDelay[x][y])        /* initialize animation counter */
1816       MovDelay[x][y] = 9;
1817
1818     if (MovDelay[x][y])         /* continue animation */
1819     {
1820       MovDelay[x][y]--;
1821       if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1822       {
1823         int frame = getGraphicAnimationFrame(graphic, 8 - MovDelay[x][y]);
1824
1825         DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
1826       }
1827
1828       if (!MovDelay[x][y])
1829       {
1830         Feld[x][y] = EL_EMPTY;
1831         DrawLevelField(x, y);
1832       }
1833     }
1834   }
1835 #endif
1836 }
1837
1838 static void InitBeltMovement()
1839 {
1840   static int belt_base_element[4] =
1841   {
1842     EL_CONVEYOR_BELT1_LEFT,
1843     EL_CONVEYOR_BELT2_LEFT,
1844     EL_CONVEYOR_BELT3_LEFT,
1845     EL_CONVEYOR_BELT4_LEFT
1846   };
1847   static int belt_base_active_element[4] =
1848   {
1849     EL_CONVEYOR_BELT1_LEFT_ACTIVE,
1850     EL_CONVEYOR_BELT2_LEFT_ACTIVE,
1851     EL_CONVEYOR_BELT3_LEFT_ACTIVE,
1852     EL_CONVEYOR_BELT4_LEFT_ACTIVE
1853   };
1854
1855   int x, y, i, j;
1856
1857   /* set frame order for belt animation graphic according to belt direction */
1858   for (i=0; i<4; i++)
1859   {
1860     int belt_nr = i;
1861
1862     for (j=0; j<3; j++)
1863     {
1864       int element = belt_base_active_element[belt_nr] + j;
1865       int graphic = el2img(element);
1866
1867       if (game.belt_dir[i] == MV_LEFT)
1868         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
1869       else
1870         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
1871     }
1872   }
1873
1874   for(y=0; y<lev_fieldy; y++)
1875   {
1876     for(x=0; x<lev_fieldx; x++)
1877     {
1878       int element = Feld[x][y];
1879
1880       for (i=0; i<4; i++)
1881       {
1882         if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
1883         {
1884           int e_belt_nr = getBeltNrFromBeltElement(element);
1885           int belt_nr = i;
1886
1887           if (e_belt_nr == belt_nr)
1888           {
1889             int belt_part = Feld[x][y] - belt_base_element[belt_nr];
1890
1891             Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
1892           }
1893         }
1894       }
1895     }
1896   }
1897 }
1898
1899 static void ToggleBeltSwitch(int x, int y)
1900 {
1901   static int belt_base_element[4] =
1902   {
1903     EL_CONVEYOR_BELT1_LEFT,
1904     EL_CONVEYOR_BELT2_LEFT,
1905     EL_CONVEYOR_BELT3_LEFT,
1906     EL_CONVEYOR_BELT4_LEFT
1907   };
1908   static int belt_base_active_element[4] =
1909   {
1910     EL_CONVEYOR_BELT1_LEFT_ACTIVE,
1911     EL_CONVEYOR_BELT2_LEFT_ACTIVE,
1912     EL_CONVEYOR_BELT3_LEFT_ACTIVE,
1913     EL_CONVEYOR_BELT4_LEFT_ACTIVE
1914   };
1915   static int belt_base_switch_element[4] =
1916   {
1917     EL_CONVEYOR_BELT1_SWITCH_LEFT,
1918     EL_CONVEYOR_BELT2_SWITCH_LEFT,
1919     EL_CONVEYOR_BELT3_SWITCH_LEFT,
1920     EL_CONVEYOR_BELT4_SWITCH_LEFT
1921   };
1922   static int belt_move_dir[4] =
1923   {
1924     MV_LEFT,
1925     MV_NO_MOVING,
1926     MV_RIGHT,
1927     MV_NO_MOVING,
1928   };
1929
1930   int element = Feld[x][y];
1931   int belt_nr = getBeltNrFromBeltSwitchElement(element);
1932   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
1933   int belt_dir = belt_move_dir[belt_dir_nr];
1934   int xx, yy, i;
1935
1936   if (!IS_BELT_SWITCH(element))
1937     return;
1938
1939   game.belt_dir_nr[belt_nr] = belt_dir_nr;
1940   game.belt_dir[belt_nr] = belt_dir;
1941
1942   if (belt_dir_nr == 3)
1943     belt_dir_nr = 1;
1944
1945   /* set frame order for belt animation graphic according to belt direction */
1946   for (i=0; i<3; i++)
1947   {
1948     int element = belt_base_active_element[belt_nr] + i;
1949     int graphic = el2img(element);
1950
1951     if (belt_dir == MV_LEFT)
1952       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
1953     else
1954       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
1955   }
1956
1957   for (yy=0; yy<lev_fieldy; yy++)
1958   {
1959     for (xx=0; xx<lev_fieldx; xx++)
1960     {
1961       int element = Feld[xx][yy];
1962
1963       if (IS_BELT_SWITCH(element))
1964       {
1965         int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
1966
1967         if (e_belt_nr == belt_nr)
1968         {
1969           Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
1970           DrawLevelField(xx, yy);
1971         }
1972       }
1973       else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
1974       {
1975         int e_belt_nr = getBeltNrFromBeltElement(element);
1976
1977         if (e_belt_nr == belt_nr)
1978         {
1979           int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
1980
1981           Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
1982           DrawLevelField(xx, yy);
1983         }
1984       }
1985       else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
1986       {
1987         int e_belt_nr = getBeltNrFromBeltActiveElement(element);
1988
1989         if (e_belt_nr == belt_nr)
1990         {
1991           int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
1992
1993           Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
1994           DrawLevelField(xx, yy);
1995         }
1996       }
1997     }
1998   }
1999 }
2000
2001 static void ToggleSwitchgateSwitch(int x, int y)
2002 {
2003   int xx, yy;
2004
2005   game.switchgate_pos = !game.switchgate_pos;
2006
2007   for (yy=0; yy<lev_fieldy; yy++)
2008   {
2009     for (xx=0; xx<lev_fieldx; xx++)
2010     {
2011       int element = Feld[xx][yy];
2012
2013       if (element == EL_SWITCHGATE_SWITCH_UP ||
2014           element == EL_SWITCHGATE_SWITCH_DOWN)
2015       {
2016         Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2017         DrawLevelField(xx, yy);
2018       }
2019       else if (element == EL_SWITCHGATE_OPEN ||
2020                element == EL_SWITCHGATE_OPENING)
2021       {
2022         Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
2023         PlaySoundLevel(xx, yy, SND_SWITCHGATE_CLOSING);
2024       }
2025       else if (element == EL_SWITCHGATE_CLOSED ||
2026                element == EL_SWITCHGATE_CLOSING)
2027       {
2028         Feld[xx][yy] = EL_SWITCHGATE_OPENING;
2029         PlaySoundLevel(xx, yy, SND_SWITCHGATE_OPENING);
2030       }
2031     }
2032   }
2033 }
2034
2035 static int getInvisibleActiveFromInvisibleElement(int element)
2036 {
2037   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
2038           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
2039           EL_INVISIBLE_SAND_ACTIVE);
2040 }
2041
2042 static int getInvisibleFromInvisibleActiveElement(int element)
2043 {
2044   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
2045           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
2046           EL_INVISIBLE_SAND);
2047 }
2048
2049 static void RedrawAllLightSwitchesAndInvisibleElements()
2050 {
2051   int x, y;
2052
2053   for (y=0; y<lev_fieldy; y++)
2054   {
2055     for (x=0; x<lev_fieldx; x++)
2056     {
2057       int element = Feld[x][y];
2058
2059       if (element == EL_LIGHT_SWITCH &&
2060           game.light_time_left > 0)
2061       {
2062         Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
2063         DrawLevelField(x, y);
2064       }
2065       else if (element == EL_LIGHT_SWITCH_ACTIVE &&
2066                game.light_time_left == 0)
2067       {
2068         Feld[x][y] = EL_LIGHT_SWITCH;
2069         DrawLevelField(x, y);
2070       }
2071       else if (element == EL_INVISIBLE_STEELWALL ||
2072                element == EL_INVISIBLE_WALL ||
2073                element == EL_INVISIBLE_SAND)
2074       {
2075         if (game.light_time_left > 0)
2076           Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
2077
2078         DrawLevelField(x, y);
2079       }
2080       else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
2081                element == EL_INVISIBLE_WALL_ACTIVE ||
2082                element == EL_INVISIBLE_SAND_ACTIVE)
2083       {
2084         if (game.light_time_left == 0)
2085           Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
2086
2087         DrawLevelField(x, y);
2088       }
2089     }
2090   }
2091 }
2092
2093 static void ToggleLightSwitch(int x, int y)
2094 {
2095   int element = Feld[x][y];
2096
2097   game.light_time_left =
2098     (element == EL_LIGHT_SWITCH ?
2099      level.time_light * FRAMES_PER_SECOND : 0);
2100
2101   RedrawAllLightSwitchesAndInvisibleElements();
2102 }
2103
2104 static void ActivateTimegateSwitch(int x, int y)
2105 {
2106   int xx, yy;
2107
2108   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
2109
2110   for (yy=0; yy<lev_fieldy; yy++)
2111   {
2112     for (xx=0; xx<lev_fieldx; xx++)
2113     {
2114       int element = Feld[xx][yy];
2115
2116       if (element == EL_TIMEGATE_CLOSED ||
2117           element == EL_TIMEGATE_CLOSING)
2118       {
2119         Feld[xx][yy] = EL_TIMEGATE_OPENING;
2120         PlaySoundLevel(xx, yy, SND_TIMEGATE_OPENING);
2121       }
2122
2123       /*
2124       else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
2125       {
2126         Feld[xx][yy] = EL_TIMEGATE_SWITCH;
2127         DrawLevelField(xx, yy);
2128       }
2129       */
2130
2131     }
2132   }
2133
2134   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
2135 }
2136
2137 void Impact(int x, int y)
2138 {
2139   boolean lastline = (y == lev_fieldy-1);
2140   boolean object_hit = FALSE;
2141   int element = Feld[x][y];
2142   int smashed = 0;
2143
2144   if (!lastline)        /* check if element below was hit */
2145   {
2146     if (Feld[x][y+1] == EL_PLAYER_IS_LEAVING)
2147       return;
2148
2149     object_hit = (!IS_FREE(x, y+1) && (!IS_MOVING(x, y+1) ||
2150                                       MovDir[x][y+1] != MV_DOWN ||
2151                                       MovPos[x][y+1] <= TILEY / 2));
2152     if (object_hit)
2153       smashed = MovingOrBlocked2Element(x, y+1);
2154   }
2155
2156   if (!lastline && smashed == EL_ACID)  /* element falls into acid */
2157   {
2158     SplashAcid(x, y);
2159     return;
2160   }
2161
2162 #if 1
2163   if (lastline || object_hit)
2164   {
2165 #if 0
2166     MovDir[x][y] = 0;
2167 #endif
2168     ResetGfxAnimation(x, y);
2169     DrawLevelField(x, y);
2170   }
2171 #endif
2172
2173   if ((element == EL_BOMB ||
2174        element == EL_SP_DISK_ORANGE ||
2175        element == EL_DX_SUPABOMB) &&
2176       (lastline || object_hit))         /* element is bomb */
2177   {
2178     Bang(x, y);
2179     return;
2180   }
2181   else if (element == EL_PEARL)
2182   {
2183     Feld[x][y] = EL_PEARL_BREAKING;
2184     PlaySoundLevel(x, y, SND_PEARL_BREAKING);
2185     return;
2186   }
2187
2188   if (element == EL_AMOEBA_DROP && (lastline || object_hit))
2189   {
2190     if (object_hit && IS_PLAYER(x, y+1))
2191       KillHeroUnlessProtected(x, y+1);
2192     else if (object_hit && smashed == EL_PENGUIN)
2193       Bang(x, y+1);
2194     else
2195     {
2196       Feld[x][y] = EL_AMOEBA_CREATING;
2197       Store[x][y] = EL_AMOEBA_WET;
2198
2199       ResetRandomAnimationValue(x, y);
2200     }
2201     return;
2202   }
2203
2204   if (!lastline && object_hit)          /* check which object was hit */
2205   {
2206     if (CAN_CHANGE(element) && 
2207         (smashed == EL_MAGIC_WALL ||
2208          smashed == EL_BD_MAGIC_WALL))
2209     {
2210       int xx, yy;
2211       int activated_magic_wall =
2212         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
2213          EL_BD_MAGIC_WALL_ACTIVE);
2214
2215       /* activate magic wall / mill */
2216       for (yy=0; yy<lev_fieldy; yy++)
2217         for (xx=0; xx<lev_fieldx; xx++)
2218           if (Feld[xx][yy] == smashed)
2219             Feld[xx][yy] = activated_magic_wall;
2220
2221       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
2222       game.magic_wall_active = TRUE;
2223
2224       PlaySoundLevel(x, y, (smashed == EL_MAGIC_WALL ?
2225                             SND_MAGIC_WALL_ACTIVATING :
2226                             SND_BD_MAGIC_WALL_ACTIVATING));
2227     }
2228
2229     if (IS_PLAYER(x, y + 1))
2230     {
2231       KillHeroUnlessProtected(x, y+1);
2232       return;
2233     }
2234     else if (smashed == EL_PENGUIN)
2235     {
2236       Bang(x, y + 1);
2237       return;
2238     }
2239     else if (element == EL_BD_DIAMOND)
2240     {
2241       if (IS_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
2242       {
2243         Bang(x, y + 1);
2244         return;
2245       }
2246     }
2247     else if ((element == EL_SP_INFOTRON ||
2248               element == EL_SP_ZONK) &&
2249              (smashed == EL_SP_SNIKSNAK ||
2250               smashed == EL_SP_ELECTRON ||
2251               smashed == EL_SP_DISK_ORANGE))
2252     {
2253       Bang(x, y + 1);
2254       return;
2255     }
2256     else if (element == EL_ROCK ||
2257              element == EL_SP_ZONK ||
2258              element == EL_BD_ROCK)
2259     {
2260       if (IS_ENEMY(smashed) ||
2261           smashed == EL_BOMB ||
2262           smashed == EL_SP_DISK_ORANGE ||
2263           smashed == EL_DX_SUPABOMB ||
2264           smashed == EL_SATELLITE ||
2265           smashed == EL_PIG ||
2266           smashed == EL_DRAGON ||
2267           smashed == EL_MOLE)
2268       {
2269         Bang(x, y + 1);
2270         return;
2271       }
2272       else if (!IS_MOVING(x, y + 1))
2273       {
2274         if (smashed == EL_LAMP ||
2275             smashed == EL_LAMP_ACTIVE)
2276         {
2277           Bang(x, y + 1);
2278           return;
2279         }
2280         else if (smashed == EL_NUT)
2281         {
2282           Feld[x][y+1] = EL_NUT_CRACKING;
2283           PlaySoundLevel(x, y, SND_NUT_CRACKING);
2284           RaiseScoreElement(EL_NUT);
2285           return;
2286         }
2287         else if (smashed == EL_PEARL)
2288         {
2289           Feld[x][y+1] = EL_PEARL_BREAKING;
2290           PlaySoundLevel(x, y, SND_PEARL_BREAKING);
2291           return;
2292         }
2293         else if (smashed == EL_DIAMOND)
2294         {
2295           Feld[x][y+1] = EL_EMPTY;
2296           PlaySoundLevel(x, y, SND_DIAMOND_BREAKING);
2297           return;
2298         }
2299         else if (IS_BELT_SWITCH(smashed))
2300         {
2301           ToggleBeltSwitch(x, y+1);
2302         }
2303         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
2304                  smashed == EL_SWITCHGATE_SWITCH_DOWN)
2305         {
2306           ToggleSwitchgateSwitch(x, y+1);
2307         }
2308         else if (smashed == EL_LIGHT_SWITCH ||
2309                  smashed == EL_LIGHT_SWITCH_ACTIVE)
2310         {
2311           ToggleLightSwitch(x, y+1);
2312         }
2313       }
2314     }
2315   }
2316
2317   /* play sound of magic wall / mill */
2318   if (!lastline &&
2319       (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE ||
2320        Feld[x][y+1] == EL_BD_MAGIC_WALL_ACTIVE))
2321   {
2322     if (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE)
2323       PlaySoundLevel(x, y, SND_MAGIC_WALL_CHANGING);
2324     else if (Feld[x][y+1] == EL_BD_MAGIC_WALL_ACTIVE)
2325       PlaySoundLevel(x, y, SND_BD_MAGIC_WALL_CHANGING);
2326
2327     return;
2328   }
2329
2330 #if 0
2331   printf("::: -> %d,%d [%d]\n", element, ACTION_IMPACT,
2332          element_info[element].sound[ACTION_IMPACT]);
2333
2334   PlaySound(177);
2335 #endif
2336
2337   /* play sound of object that hits the ground */
2338   if (lastline || object_hit)
2339     PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT);
2340 }
2341
2342 void TurnRound(int x, int y)
2343 {
2344   static struct
2345   {
2346     int x, y;
2347   } move_xy[] =
2348   {
2349     { 0, 0 },
2350     {-1, 0 },
2351     {+1, 0 },
2352     { 0, 0 },
2353     { 0, -1 },
2354     { 0, 0 }, { 0, 0 }, { 0, 0 },
2355     { 0, +1 }
2356   };
2357   static struct
2358   {
2359     int left, right, back;
2360   } turn[] =
2361   {
2362     { 0,        0,              0 },
2363     { MV_DOWN,  MV_UP,          MV_RIGHT },
2364     { MV_UP,    MV_DOWN,        MV_LEFT },
2365     { 0,        0,              0 },
2366     { MV_LEFT,  MV_RIGHT,       MV_DOWN },
2367     { 0,0,0 },  { 0,0,0 },      { 0,0,0 },
2368     { MV_RIGHT, MV_LEFT,        MV_UP }
2369   };
2370
2371   int element = Feld[x][y];
2372   int old_move_dir = MovDir[x][y];
2373   int left_dir  = turn[old_move_dir].left;
2374   int right_dir = turn[old_move_dir].right;
2375   int back_dir  = turn[old_move_dir].back;
2376
2377   int left_dx  = move_xy[left_dir].x,     left_dy  = move_xy[left_dir].y;
2378   int right_dx = move_xy[right_dir].x,    right_dy = move_xy[right_dir].y;
2379   int move_dx  = move_xy[old_move_dir].x, move_dy  = move_xy[old_move_dir].y;
2380   int back_dx  = move_xy[back_dir].x,     back_dy  = move_xy[back_dir].y;
2381
2382   int left_x  = x + left_dx,  left_y  = y + left_dy;
2383   int right_x = x + right_dx, right_y = y + right_dy;
2384   int move_x  = x + move_dx,  move_y  = y + move_dy;
2385
2386   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2387   {
2388     TestIfBadThingTouchesOtherBadThing(x, y);
2389
2390     if (IN_LEV_FIELD(right_x, right_y) &&
2391         IS_FREE(right_x, right_y))
2392       MovDir[x][y] = right_dir;
2393     else if (!IN_LEV_FIELD(move_x, move_y) ||
2394              !IS_FREE(move_x, move_y))
2395       MovDir[x][y] = left_dir;
2396
2397     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
2398       MovDelay[x][y] = 9;
2399     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
2400       MovDelay[x][y] = 1;
2401   }
2402   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2403            element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2404   {
2405     TestIfBadThingTouchesOtherBadThing(x, y);
2406
2407     if (IN_LEV_FIELD(left_x, left_y) &&
2408         IS_FREE(left_x, left_y))
2409       MovDir[x][y] = left_dir;
2410     else if (!IN_LEV_FIELD(move_x, move_y) ||
2411              !IS_FREE(move_x, move_y))
2412       MovDir[x][y] = right_dir;
2413
2414     if ((element == EL_SPACESHIP ||
2415          element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2416         && MovDir[x][y] != old_move_dir)
2417       MovDelay[x][y] = 9;
2418     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
2419       MovDelay[x][y] = 1;
2420   }
2421   else if (element == EL_YAMYAM)
2422   {
2423     boolean can_turn_left = FALSE, can_turn_right = FALSE;
2424
2425     if (IN_LEV_FIELD(left_x, left_y) &&
2426         (IS_FREE_OR_PLAYER(left_x, left_y) ||
2427          Feld[left_x][left_y] == EL_DIAMOND))
2428       can_turn_left = TRUE;
2429     if (IN_LEV_FIELD(right_x, right_y) &&
2430         (IS_FREE_OR_PLAYER(right_x, right_y) ||
2431          Feld[right_x][right_y] == EL_DIAMOND))
2432       can_turn_right = TRUE;
2433
2434     if (can_turn_left && can_turn_right)
2435       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
2436     else if (can_turn_left)
2437       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
2438     else if (can_turn_right)
2439       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
2440     else
2441       MovDir[x][y] = back_dir;
2442
2443     MovDelay[x][y] = 16+16*RND(3);
2444   }
2445   else if (element == EL_DARK_YAMYAM)
2446   {
2447     boolean can_turn_left = FALSE, can_turn_right = FALSE;
2448
2449     if (IN_LEV_FIELD(left_x, left_y) &&
2450         (IS_FREE_OR_PLAYER(left_x, left_y) ||
2451          IS_MAMPF2(Feld[left_x][left_y])))
2452       can_turn_left = TRUE;
2453     if (IN_LEV_FIELD(right_x, right_y) &&
2454         (IS_FREE_OR_PLAYER(right_x, right_y) ||
2455          IS_MAMPF2(Feld[right_x][right_y])))
2456       can_turn_right = TRUE;
2457
2458     if (can_turn_left && can_turn_right)
2459       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
2460     else if (can_turn_left)
2461       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
2462     else if (can_turn_right)
2463       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
2464     else
2465       MovDir[x][y] = back_dir;
2466
2467     MovDelay[x][y] = 16+16*RND(3);
2468   }
2469   else if (element == EL_PACMAN)
2470   {
2471     boolean can_turn_left = FALSE, can_turn_right = FALSE;
2472
2473     if (IN_LEV_FIELD(left_x, left_y) &&
2474         (IS_FREE_OR_PLAYER(left_x, left_y) ||
2475          IS_AMOEBOID(Feld[left_x][left_y])))
2476       can_turn_left = TRUE;
2477     if (IN_LEV_FIELD(right_x, right_y) &&
2478         (IS_FREE_OR_PLAYER(right_x, right_y) ||
2479          IS_AMOEBOID(Feld[right_x][right_y])))
2480       can_turn_right = TRUE;
2481
2482     if (can_turn_left && can_turn_right)
2483       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
2484     else if (can_turn_left)
2485       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
2486     else if (can_turn_right)
2487       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
2488     else
2489       MovDir[x][y] = back_dir;
2490
2491     MovDelay[x][y] = 6+RND(40);
2492   }
2493   else if (element == EL_PIG)
2494   {
2495     boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE;
2496     boolean should_turn_left = FALSE, should_turn_right = FALSE;
2497     boolean should_move_on = FALSE;
2498     int rnd_value = 24;
2499     int rnd = RND(rnd_value);
2500
2501     if (IN_LEV_FIELD(left_x, left_y) &&
2502         (IS_FREE(left_x, left_y) || IS_GEM(Feld[left_x][left_y])))
2503       can_turn_left = TRUE;
2504     if (IN_LEV_FIELD(right_x, right_y) &&
2505         (IS_FREE(right_x, right_y) || IS_GEM(Feld[right_x][right_y])))
2506       can_turn_right = TRUE;
2507     if (IN_LEV_FIELD(move_x, move_y) &&
2508         (IS_FREE(move_x, move_y) || IS_GEM(Feld[move_x][move_y])))
2509       can_move_on = TRUE;
2510
2511     if (can_turn_left &&
2512         (!can_move_on ||
2513          (IN_LEV_FIELD(x+back_dx+left_dx, y+back_dy+left_dy) &&
2514           !IS_FREE(x+back_dx+left_dx, y+back_dy+left_dy))))
2515       should_turn_left = TRUE;
2516     if (can_turn_right &&
2517         (!can_move_on ||
2518          (IN_LEV_FIELD(x+back_dx+right_dx, y+back_dy+right_dy) &&
2519           !IS_FREE(x+back_dx+right_dx, y+back_dy+right_dy))))
2520       should_turn_right = TRUE;
2521     if (can_move_on &&
2522         (!can_turn_left || !can_turn_right ||
2523          (IN_LEV_FIELD(x+move_dx+left_dx, y+move_dy+left_dy) &&
2524           !IS_FREE(x+move_dx+left_dx, y+move_dy+left_dy)) ||
2525          (IN_LEV_FIELD(x+move_dx+right_dx, y+move_dy+right_dy) &&
2526           !IS_FREE(x+move_dx+right_dx, y+move_dy+right_dy))))
2527       should_move_on = TRUE;
2528
2529     if (should_turn_left || should_turn_right || should_move_on)
2530     {
2531       if (should_turn_left && should_turn_right && should_move_on)
2532         MovDir[x][y] = (rnd < rnd_value/3 ? left_dir :
2533                         rnd < 2*rnd_value/3 ? right_dir :
2534                         old_move_dir);
2535       else if (should_turn_left && should_turn_right)
2536         MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : right_dir);
2537       else if (should_turn_left && should_move_on)
2538         MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : old_move_dir);
2539       else if (should_turn_right && should_move_on)
2540         MovDir[x][y] = (rnd < rnd_value/2 ? right_dir : old_move_dir);
2541       else if (should_turn_left)
2542         MovDir[x][y] = left_dir;
2543       else if (should_turn_right)
2544         MovDir[x][y] = right_dir;
2545       else if (should_move_on)
2546         MovDir[x][y] = old_move_dir;
2547     }
2548     else if (can_move_on && rnd > rnd_value/8)
2549       MovDir[x][y] = old_move_dir;
2550     else if (can_turn_left && can_turn_right)
2551       MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : right_dir);
2552     else if (can_turn_left && rnd > rnd_value/8)
2553       MovDir[x][y] = left_dir;
2554     else if (can_turn_right && rnd > rnd_value/8)
2555       MovDir[x][y] = right_dir;
2556     else
2557       MovDir[x][y] = back_dir;
2558
2559     if (!IS_FREE(x+move_xy[MovDir[x][y]].x, y+move_xy[MovDir[x][y]].y) &&
2560         !IS_GEM(Feld[x+move_xy[MovDir[x][y]].x][y+move_xy[MovDir[x][y]].y]))
2561       MovDir[x][y] = old_move_dir;
2562
2563     MovDelay[x][y] = 0;
2564   }
2565   else if (element == EL_DRAGON)
2566   {
2567     boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE;
2568     int rnd_value = 24;
2569     int rnd = RND(rnd_value);
2570
2571     if (IN_LEV_FIELD(left_x, left_y) && IS_FREE(left_x, left_y))
2572       can_turn_left = TRUE;
2573     if (IN_LEV_FIELD(right_x, right_y) && IS_FREE(right_x, right_y))
2574       can_turn_right = TRUE;
2575     if (IN_LEV_FIELD(move_x, move_y) && IS_FREE(move_x, move_y))
2576       can_move_on = TRUE;
2577
2578     if (can_move_on && rnd > rnd_value/8)
2579       MovDir[x][y] = old_move_dir;
2580     else if (can_turn_left && can_turn_right)
2581       MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : right_dir);
2582     else if (can_turn_left && rnd > rnd_value/8)
2583       MovDir[x][y] = left_dir;
2584     else if (can_turn_right && rnd > rnd_value/8)
2585       MovDir[x][y] = right_dir;
2586     else
2587       MovDir[x][y] = back_dir;
2588
2589     if (!IS_FREE(x+move_xy[MovDir[x][y]].x, y+move_xy[MovDir[x][y]].y))
2590       MovDir[x][y] = old_move_dir;
2591
2592     MovDelay[x][y] = 0;
2593   }
2594   else if (element == EL_MOLE)
2595   {
2596     boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE;
2597
2598     if (IN_LEV_FIELD(move_x, move_y) &&
2599         (IS_FREE(move_x, move_y) || IS_AMOEBOID(Feld[move_x][move_y]) ||
2600          Feld[move_x][move_y] == EL_AMOEBA_SHRINKING))
2601       can_move_on = TRUE;
2602
2603     if (!can_move_on)
2604     {
2605       if (IN_LEV_FIELD(left_x, left_y) &&
2606           (IS_FREE(left_x, left_y) || IS_AMOEBOID(Feld[left_x][left_y])))
2607         can_turn_left = TRUE;
2608       if (IN_LEV_FIELD(right_x, right_y) &&
2609           (IS_FREE(right_x, right_y) || IS_AMOEBOID(Feld[right_x][right_y])))
2610         can_turn_right = TRUE;
2611
2612       if (can_turn_left && can_turn_right)
2613         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
2614       else if (can_turn_left)
2615         MovDir[x][y] = left_dir;
2616       else
2617         MovDir[x][y] = right_dir;
2618     }
2619
2620     if (MovDir[x][y] != old_move_dir)
2621       MovDelay[x][y] = 9;
2622   }
2623   else if (element == EL_BALLOON)
2624   {
2625     MovDir[x][y] = game.balloon_dir;
2626     MovDelay[x][y] = 0;
2627   }
2628   else if (element == EL_SPRING)
2629   {
2630     if ((MovDir[x][y] == MV_LEFT || MovDir[x][y] == MV_RIGHT) &&
2631         (!IN_LEV_FIELD(move_x, move_y) || !IS_FREE(move_x, move_y) ||
2632          (IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))
2633       MovDir[x][y] = MV_NO_MOVING;
2634
2635     MovDelay[x][y] = 0;
2636   }
2637   else if (element == EL_ROBOT ||
2638            element == EL_SATELLITE ||
2639            element == EL_PENGUIN)
2640   {
2641     int attr_x = -1, attr_y = -1;
2642
2643     if (AllPlayersGone)
2644     {
2645       attr_x = ExitX;
2646       attr_y = ExitY;
2647     }
2648     else
2649     {
2650       int i;
2651
2652       for (i=0; i<MAX_PLAYERS; i++)
2653       {
2654         struct PlayerInfo *player = &stored_player[i];
2655         int jx = player->jx, jy = player->jy;
2656
2657         if (!player->active)
2658           continue;
2659
2660         if (attr_x == -1 || ABS(jx-x)+ABS(jy-y) < ABS(attr_x-x)+ABS(attr_y-y))
2661         {
2662           attr_x = jx;
2663           attr_y = jy;
2664         }
2665       }
2666     }
2667
2668     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
2669     {
2670       attr_x = ZX;
2671       attr_y = ZY;
2672     }
2673
2674     if (element == EL_PENGUIN)
2675     {
2676       int i;
2677       static int xy[4][2] =
2678       {
2679         { 0, -1 },
2680         { -1, 0 },
2681         { +1, 0 },
2682         { 0, +1 }
2683       };
2684
2685       for (i=0; i<4; i++)
2686       {
2687         int ex = x + xy[i%4][0];
2688         int ey = y + xy[i%4][1];
2689
2690         if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
2691         {
2692           attr_x = ex;
2693           attr_y = ey;
2694           break;
2695         }
2696       }
2697     }
2698
2699     MovDir[x][y] = MV_NO_MOVING;
2700     if (attr_x<x)
2701       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
2702     else if (attr_x>x)
2703       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
2704     if (attr_y<y)
2705       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
2706     else if (attr_y>y)
2707       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
2708
2709     if (element == EL_ROBOT)
2710     {
2711       int newx, newy;
2712
2713       if ((MovDir[x][y]&(MV_LEFT|MV_RIGHT)) && (MovDir[x][y]&(MV_UP|MV_DOWN)))
2714         MovDir[x][y] &= (RND(2) ? (MV_LEFT|MV_RIGHT) : (MV_UP|MV_DOWN));
2715       Moving2Blocked(x, y, &newx, &newy);
2716
2717       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
2718         MovDelay[x][y] = 8+8*!RND(3);
2719       else
2720         MovDelay[x][y] = 16;
2721     }
2722     else
2723     {
2724       int newx, newy;
2725
2726       MovDelay[x][y] = 1;
2727
2728       if ((MovDir[x][y]&(MV_LEFT|MV_RIGHT)) && (MovDir[x][y]&(MV_UP|MV_DOWN)))
2729       {
2730         boolean first_horiz = RND(2);
2731         int new_move_dir = MovDir[x][y];
2732
2733         MovDir[x][y] =
2734           new_move_dir & (first_horiz ? (MV_LEFT|MV_RIGHT) : (MV_UP|MV_DOWN));
2735         Moving2Blocked(x, y, &newx, &newy);
2736
2737         if (IN_LEV_FIELD(newx, newy) &&
2738             (IS_FREE(newx, newy) ||
2739              Feld[newx][newy] == EL_ACID ||
2740              (element == EL_PENGUIN &&
2741               (Feld[newx][newy] == EL_EXIT_OPEN ||
2742                IS_MAMPF3(Feld[newx][newy])))))
2743           return;
2744
2745         MovDir[x][y] =
2746           new_move_dir & (!first_horiz ? (MV_LEFT|MV_RIGHT) : (MV_UP|MV_DOWN));
2747         Moving2Blocked(x, y, &newx, &newy);
2748
2749         if (IN_LEV_FIELD(newx, newy) &&
2750             (IS_FREE(newx, newy) ||
2751              Feld[newx][newy] == EL_ACID ||
2752              (element == EL_PENGUIN &&
2753               (Feld[newx][newy] == EL_EXIT_OPEN ||
2754                IS_MAMPF3(Feld[newx][newy])))))
2755           return;
2756
2757         MovDir[x][y] = old_move_dir;
2758         return;
2759       }
2760     }
2761   }
2762 }
2763
2764 static boolean JustBeingPushed(int x, int y)
2765 {
2766   int i;
2767
2768   for (i=0; i<MAX_PLAYERS; i++)
2769   {
2770     struct PlayerInfo *player = &stored_player[i];
2771
2772     if (player->active && player->Pushing && player->MovPos)
2773     {
2774       int next_jx = player->jx + (player->jx - player->last_jx);
2775       int next_jy = player->jy + (player->jy - player->last_jy);
2776
2777       if (x == next_jx && y == next_jy)
2778         return TRUE;
2779     }
2780   }
2781
2782   return FALSE;
2783 }
2784
2785 void StartMoving(int x, int y)
2786 {
2787   static boolean use_spring_bug = TRUE;
2788   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
2789   int element = Feld[x][y];
2790
2791   if (Stop[x][y])
2792     return;
2793
2794   GfxAction[x][y] = ACTION_DEFAULT;
2795
2796   if (CAN_FALL(element) && y < lev_fieldy - 1)
2797   {
2798     if ((x>0 && IS_PLAYER(x-1, y)) || (x<lev_fieldx-1 && IS_PLAYER(x+1, y)))
2799       if (JustBeingPushed(x, y))
2800         return;
2801
2802     if (element == EL_QUICKSAND_FULL)
2803     {
2804       if (IS_FREE(x, y+1))
2805       {
2806         InitMovingField(x, y, MV_DOWN);
2807         started_moving = TRUE;
2808
2809         Feld[x][y] = EL_QUICKSAND_EMPTYING;
2810         Store[x][y] = EL_ROCK;
2811         PlaySoundLevel(x, y, SND_QUICKSAND_EMPTYING);
2812       }
2813       else if (Feld[x][y+1] == EL_QUICKSAND_EMPTY)
2814       {
2815         if (!MovDelay[x][y])
2816           MovDelay[x][y] = TILEY + 1;
2817
2818         if (MovDelay[x][y])
2819         {
2820           MovDelay[x][y]--;
2821           if (MovDelay[x][y])
2822             return;
2823         }
2824
2825         Feld[x][y] = EL_QUICKSAND_EMPTY;
2826         Feld[x][y+1] = EL_QUICKSAND_FULL;
2827         Store[x][y+1] = Store[x][y];
2828         Store[x][y] = 0;
2829         PlaySoundLevel(x, y, SND_QUICKSAND_SLIPPING);
2830       }
2831     }
2832     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
2833              Feld[x][y+1] == EL_QUICKSAND_EMPTY)
2834     {
2835       InitMovingField(x, y, MV_DOWN);
2836       started_moving = TRUE;
2837
2838       Feld[x][y] = EL_QUICKSAND_FILLING;
2839       Store[x][y] = element;
2840       PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
2841     }
2842     else if (element == EL_MAGIC_WALL_FULL)
2843     {
2844       if (IS_FREE(x, y+1))
2845       {
2846         InitMovingField(x, y, MV_DOWN);
2847         started_moving = TRUE;
2848
2849         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
2850         Store[x][y] = EL_CHANGED(Store[x][y]);
2851       }
2852       else if (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE)
2853       {
2854         if (!MovDelay[x][y])
2855           MovDelay[x][y] = TILEY/4 + 1;
2856
2857         if (MovDelay[x][y])
2858         {
2859           MovDelay[x][y]--;
2860           if (MovDelay[x][y])
2861             return;
2862         }
2863
2864         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
2865         Feld[x][y+1] = EL_MAGIC_WALL_FULL;
2866         Store[x][y+1] = EL_CHANGED(Store[x][y]);
2867         Store[x][y] = 0;
2868       }
2869     }
2870     else if (element == EL_BD_MAGIC_WALL_FULL)
2871     {
2872       if (IS_FREE(x, y+1))
2873       {
2874         InitMovingField(x, y, MV_DOWN);
2875         started_moving = TRUE;
2876
2877         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
2878         Store[x][y] = EL_CHANGED2(Store[x][y]);
2879       }
2880       else if (Feld[x][y+1] == EL_BD_MAGIC_WALL_ACTIVE)
2881       {
2882         if (!MovDelay[x][y])
2883           MovDelay[x][y] = TILEY/4 + 1;
2884
2885         if (MovDelay[x][y])
2886         {
2887           MovDelay[x][y]--;
2888           if (MovDelay[x][y])
2889             return;
2890         }
2891
2892         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
2893         Feld[x][y+1] = EL_BD_MAGIC_WALL_FULL;
2894         Store[x][y+1] = EL_CHANGED2(Store[x][y]);
2895         Store[x][y] = 0;
2896       }
2897     }
2898     else if (CAN_CHANGE(element) &&
2899              (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE ||
2900               Feld[x][y+1] == EL_BD_MAGIC_WALL_ACTIVE))
2901     {
2902       InitMovingField(x, y, MV_DOWN);
2903       started_moving = TRUE;
2904
2905       Feld[x][y] =
2906         (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
2907          EL_BD_MAGIC_WALL_FILLING);
2908       Store[x][y] = element;
2909     }
2910 #if 0
2911     else if (CAN_SMASH(element) && Feld[x][y+1] == EL_ACID)
2912 #else
2913     else if (CAN_FALL(element) && Feld[x][y+1] == EL_ACID)
2914 #endif
2915     {
2916       SplashAcid(x, y);
2917
2918       InitMovingField(x, y, MV_DOWN);
2919       started_moving = TRUE;
2920
2921       Store[x][y] = EL_ACID;
2922     }
2923     else if (CAN_SMASH(element) && Feld[x][y+1] == EL_BLOCKED &&
2924              JustStopped[x][y])
2925     {
2926       Impact(x, y);
2927     }
2928     else if (IS_FREE(x, y+1) && element == EL_SPRING && use_spring_bug)
2929     {
2930       if (MovDir[x][y] == MV_NO_MOVING)
2931       {
2932         InitMovingField(x, y, MV_DOWN);
2933         started_moving = TRUE;
2934       }
2935     }
2936     else if (IS_FREE(x, y+1))
2937     {
2938       if (JustStopped[x][y])    /* prevent animation from being restarted */
2939         MovDir[x][y] = MV_DOWN;
2940
2941       InitMovingField(x, y, MV_DOWN);
2942       started_moving = TRUE;
2943     }
2944     else if (element == EL_AMOEBA_DROP)
2945     {
2946       Feld[x][y] = EL_AMOEBA_CREATING;
2947       Store[x][y] = EL_AMOEBA_WET;
2948     }
2949     /* Store[x][y+1] must be zero, because:
2950        (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y+1] == EL_QUICKSAND_EMPTY
2951     */
2952 #if 0
2953 #if OLD_GAME_BEHAVIOUR
2954     else if (IS_SLIPPERY(Feld[x][y+1]) && !Store[x][y+1])
2955 #else
2956     else if (IS_SLIPPERY(Feld[x][y+1]) && !Store[x][y+1] &&
2957              !IS_FALLING(x, y+1) && !JustStopped[x][y+1] &&
2958              element != EL_DX_SUPABOMB)
2959 #endif
2960 #else
2961     else if ((IS_SLIPPERY(Feld[x][y+1]) ||
2962               (IS_EM_SLIPPERY_WALL(Feld[x][y+1]) && IS_GEM(element))) &&
2963              !IS_FALLING(x, y+1) && !JustStopped[x][y+1] &&
2964              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
2965 #endif
2966     {
2967       boolean left  = (x>0 && IS_FREE(x-1, y) &&
2968                        (IS_FREE(x-1, y+1) || Feld[x-1][y+1] == EL_ACID));
2969       boolean right = (x<lev_fieldx-1 && IS_FREE(x+1, y) &&
2970                        (IS_FREE(x+1, y+1) || Feld[x+1][y+1] == EL_ACID));
2971
2972       if (left || right)
2973       {
2974         if (left && right &&
2975             (game.emulation != EMU_BOULDERDASH &&
2976              element != EL_BD_ROCK && element != EL_BD_DIAMOND))
2977           left = !(right = RND(2));
2978
2979         InitMovingField(x, y, left ? MV_LEFT : MV_RIGHT);
2980         started_moving = TRUE;
2981       }
2982     }
2983     else if (IS_BELT_ACTIVE(Feld[x][y+1]))
2984     {
2985       boolean left_is_free  = (x>0 && IS_FREE(x-1, y));
2986       boolean right_is_free = (x<lev_fieldx-1 && IS_FREE(x+1, y));
2987       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y+1]);
2988       int belt_dir = game.belt_dir[belt_nr];
2989
2990       if ((belt_dir == MV_LEFT  && left_is_free) ||
2991           (belt_dir == MV_RIGHT && right_is_free))
2992       {
2993         InitMovingField(x, y, belt_dir);
2994         started_moving = TRUE;
2995
2996         GfxAction[x][y] = ACTION_DEFAULT;
2997       }
2998     }
2999   }
3000
3001   /* not "else if" because of EL_SPRING */
3002   if (CAN_MOVE(element) && !started_moving)
3003   {
3004     int newx, newy;
3005
3006     if ((element == EL_SATELLITE ||
3007          element == EL_BALLOON ||
3008          element == EL_SPRING)
3009         && JustBeingPushed(x, y))
3010       return;
3011
3012 #if 0
3013 #if 0
3014     if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
3015       Feld[x][y + 1] = EL_EMPTY;        /* was set to EL_BLOCKED above */
3016 #else
3017     if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
3018     {
3019       Moving2Blocked(x, y, &newx, &newy);
3020       if (Feld[newx][newy] == EL_BLOCKED)
3021         Feld[newx][newy] = EL_EMPTY;    /* was set to EL_BLOCKED above */
3022     }
3023 #endif
3024 #endif
3025
3026     if (!MovDelay[x][y])        /* start new movement phase */
3027     {
3028       /* all objects that can change their move direction after each step */
3029       /* (MAMPFER, MAMPFER2 and PACMAN go straight until they hit a wall  */
3030
3031       if (element != EL_YAMYAM &&
3032           element != EL_DARK_YAMYAM &&
3033           element != EL_PACMAN)
3034       {
3035 #if 0
3036   if (element == EL_SPRING)
3037     printf("1--> %d\n", MovDir[x][y]);
3038 #endif
3039         TurnRound(x, y);
3040 #if 0
3041   if (element == EL_SPRING)
3042     printf("2--> %d\n", MovDir[x][y]);
3043 #endif
3044         if (MovDelay[x][y] && (element == EL_BUG ||
3045                                element == EL_SPACESHIP ||
3046                                element == EL_SP_SNIKSNAK ||
3047                                element == EL_SP_ELECTRON ||
3048                                element == EL_MOLE))
3049           DrawLevelField(x, y);
3050       }
3051     }
3052
3053     if (MovDelay[x][y])         /* wait some time before next movement */
3054     {
3055       MovDelay[x][y]--;
3056
3057       if (element == EL_ROBOT ||
3058           element == EL_YAMYAM ||
3059           element == EL_DARK_YAMYAM)
3060       {
3061 #if 1
3062         DrawLevelElementAnimationIfNeeded(x, y, element);
3063 #else
3064         if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3065         {
3066           int graphic = el2img(element);
3067           int frame = getGraphicAnimationFrame(graphic, MovDelay[x][y] % 8);
3068
3069           DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3070         }
3071 #endif
3072
3073 #if 1
3074         PlaySoundLevelAction(x, y, ACTION_WAITING);
3075 #else
3076         if (MovDelay[x][y] % 4 == 3)
3077         {
3078           if (element == EL_YAMYAM)
3079             PlaySoundLevel(x, y, SND_YAMYAM_WAITING);
3080           else if (element == EL_DARK_YAMYAM)
3081             PlaySoundLevel(x, y, SND_DARK_YAMYAM_WAITING);
3082         }
3083 #endif
3084       }
3085       else if (element == EL_SP_ELECTRON)
3086         DrawLevelElementAnimationIfNeeded(x, y, element);
3087       else if (element == EL_DRAGON)
3088       {
3089         int i;
3090         int dir = MovDir[x][y];
3091         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
3092         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
3093         int graphic = (dir == MV_LEFT   ? IMG_FLAMES1_LEFT :
3094                        dir == MV_RIGHT  ? IMG_FLAMES1_RIGHT :
3095                        dir == MV_UP     ? IMG_FLAMES1_UP :
3096                        dir == MV_DOWN   ? IMG_FLAMES1_DOWN : IMG_EMPTY);
3097         int frame = getGraphicAnimationFrame(graphic, -1);
3098
3099         for (i=1; i<=3; i++)
3100         {
3101           int xx = x + i*dx, yy = y + i*dy;
3102           int sx = SCREENX(xx), sy = SCREENY(yy);
3103           int flame_graphic = graphic + (i - 1);
3104
3105           if (!IN_LEV_FIELD(xx, yy) ||
3106               IS_SOLID(Feld[xx][yy]) || Feld[xx][yy] == EL_EXPLOSION)
3107             break;
3108
3109           if (MovDelay[x][y])
3110           {
3111             int flamed = MovingOrBlocked2Element(xx, yy);
3112
3113             if (IS_ENEMY(flamed) || IS_EXPLOSIVE(flamed))
3114               Bang(xx, yy);
3115             else
3116               RemoveMovingField(xx, yy);
3117
3118             Feld[xx][yy] = EL_FLAMES;
3119             if (IN_SCR_FIELD(sx, sy))
3120               DrawGraphic(sx, sy, flame_graphic, frame);
3121           }
3122           else
3123           {
3124             if (Feld[xx][yy] == EL_FLAMES)
3125               Feld[xx][yy] = EL_EMPTY;
3126             DrawLevelField(xx, yy);
3127           }
3128         }
3129       }
3130
3131       if (MovDelay[x][y])       /* element still has to wait some time */
3132       {
3133         PlaySoundLevelAction(x, y, ACTION_WAITING);
3134
3135         return;
3136       }
3137     }
3138
3139     /* now make next step */
3140
3141     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
3142
3143     if (IS_ENEMY(element) && IS_PLAYER(newx, newy) &&
3144         !PLAYER_PROTECTED(newx, newy))
3145     {
3146
3147 #if 1
3148       TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
3149       return;
3150 #else
3151       /* enemy got the player */
3152       MovDir[x][y] = 0;
3153       KillHero(PLAYERINFO(newx, newy));
3154       return;
3155 #endif
3156
3157     }
3158     else if ((element == EL_PENGUIN || element == EL_ROBOT ||
3159               element == EL_SATELLITE || element == EL_BALLOON) &&
3160              IN_LEV_FIELD(newx, newy) &&
3161              MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
3162     {
3163       SplashAcid(x, y);
3164       Store[x][y] = EL_ACID;
3165     }
3166     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
3167     {
3168       if (Feld[newx][newy] == EL_EXIT_OPEN)
3169       {
3170         Feld[x][y] = EL_EMPTY;
3171         DrawLevelField(x, y);
3172
3173         PlaySoundLevel(newx, newy, SND_PENGUIN_PASSING_EXIT);
3174         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
3175           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
3176
3177         local_player->friends_still_needed--;
3178         if (!local_player->friends_still_needed &&
3179             !local_player->GameOver && AllPlayersGone)
3180           local_player->LevelSolved = local_player->GameOver = TRUE;
3181
3182         return;
3183       }
3184       else if (IS_MAMPF3(Feld[newx][newy]))
3185       {
3186         if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
3187           DrawLevelField(newx, newy);
3188         else
3189           MovDir[x][y] = MV_NO_MOVING;
3190       }
3191       else if (!IS_FREE(newx, newy))
3192       {
3193         if (IS_PLAYER(x, y))
3194           DrawPlayerField(x, y);
3195         else
3196           DrawLevelField(x, y);
3197         return;
3198       }
3199     }
3200     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
3201     {
3202       if (IS_GEM(Feld[newx][newy]))
3203       {
3204         if (IS_MOVING(newx, newy))
3205           RemoveMovingField(newx, newy);
3206         else
3207         {
3208           Feld[newx][newy] = EL_EMPTY;
3209           DrawLevelField(newx, newy);
3210         }
3211
3212         PlaySoundLevel(x, y, SND_PIG_EATING);
3213       }
3214       else if (!IS_FREE(newx, newy))
3215       {
3216         if (IS_PLAYER(x, y))
3217           DrawPlayerField(x, y);
3218         else
3219           DrawLevelField(x, y);
3220         return;
3221       }
3222     }
3223     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
3224     {
3225       if (!IS_FREE(newx, newy))
3226       {
3227         if (IS_PLAYER(x, y))
3228           DrawPlayerField(x, y);
3229         else
3230           DrawLevelField(x, y);
3231         return;
3232       }
3233       else
3234       {
3235         boolean wanna_flame = !RND(10);
3236         int dx = newx - x, dy = newy - y;
3237         int newx1 = newx+1*dx, newy1 = newy+1*dy;
3238         int newx2 = newx+2*dx, newy2 = newy+2*dy;
3239         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
3240                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
3241         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
3242                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
3243
3244         if ((wanna_flame || IS_ENEMY(element1) || IS_ENEMY(element2)) &&
3245             element1 != EL_DRAGON && element2 != EL_DRAGON &&
3246             element1 != EL_FLAMES && element2 != EL_FLAMES)
3247         {
3248           if (IS_PLAYER(x, y))
3249             DrawPlayerField(x, y);
3250           else
3251             DrawLevelField(x, y);
3252
3253           PlaySoundLevel(x, y, SND_DRAGON_ATTACKING);
3254
3255           MovDelay[x][y] = 50;
3256           Feld[newx][newy] = EL_FLAMES;
3257           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
3258             Feld[newx1][newy1] = EL_FLAMES;
3259           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
3260             Feld[newx2][newy2] = EL_FLAMES;
3261           return;
3262         }
3263       }
3264     }
3265     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
3266              Feld[newx][newy] == EL_DIAMOND)
3267     {
3268       if (IS_MOVING(newx, newy))
3269         RemoveMovingField(newx, newy);
3270       else
3271       {
3272         Feld[newx][newy] = EL_EMPTY;
3273         DrawLevelField(newx, newy);
3274       }
3275
3276       PlaySoundLevel(x, y, SND_YAMYAM_EATING);
3277     }
3278     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
3279              IS_MAMPF2(Feld[newx][newy]))
3280     {
3281       if (AmoebaNr[newx][newy])
3282       {
3283         AmoebaCnt2[AmoebaNr[newx][newy]]--;
3284         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
3285             Feld[newx][newy] == EL_BD_AMOEBA)
3286           AmoebaCnt[AmoebaNr[newx][newy]]--;
3287       }
3288
3289       if (IS_MOVING(newx, newy))
3290         RemoveMovingField(newx, newy);
3291       else
3292       {
3293         Feld[newx][newy] = EL_EMPTY;
3294         DrawLevelField(newx, newy);
3295       }
3296
3297       PlaySoundLevel(x, y, SND_DARK_YAMYAM_EATING);
3298     }
3299     else if ((element == EL_PACMAN || element == EL_MOLE)
3300              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
3301     {
3302       if (AmoebaNr[newx][newy])
3303       {
3304         AmoebaCnt2[AmoebaNr[newx][newy]]--;
3305         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
3306             Feld[newx][newy] == EL_BD_AMOEBA)
3307           AmoebaCnt[AmoebaNr[newx][newy]]--;
3308       }
3309
3310       if (element == EL_MOLE)
3311       {
3312         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
3313         PlaySoundLevel(x, y, SND_MOLE_EATING);
3314         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
3315         return;                         /* wait for shrinking amoeba */
3316       }
3317       else      /* element == EL_PACMAN */
3318       {
3319         Feld[newx][newy] = EL_EMPTY;
3320         DrawLevelField(newx, newy);
3321         PlaySoundLevel(x, y, SND_PACMAN_EATING);
3322       }
3323     }
3324     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
3325              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
3326               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
3327     {
3328       /* wait for shrinking amoeba to completely disappear */
3329       return;
3330     }
3331     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
3332     {
3333       /* object was running against a wall */
3334
3335       TurnRound(x, y);
3336
3337       if (element == EL_BUG || element == EL_SPACESHIP ||
3338           element == EL_SP_SNIKSNAK)
3339         DrawLevelField(x, y);
3340       else if (element == EL_BUG || element == EL_SPACESHIP ||
3341                element == EL_SP_SNIKSNAK || element == EL_MOLE)
3342         DrawLevelField(x, y);
3343       else if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
3344         DrawLevelElementAnimationIfNeeded(x, y, element);
3345       else if (element == EL_SATELLITE)
3346         DrawLevelElementAnimationIfNeeded(x, y, element);
3347       else if (element == EL_SP_ELECTRON)
3348         DrawLevelElementAnimationIfNeeded(x, y, element);
3349
3350       if (DONT_TOUCH(element))
3351         TestIfBadThingTouchesHero(x, y);
3352
3353       PlaySoundLevelAction(x, y, ACTION_WAITING);
3354
3355       return;
3356     }
3357
3358     InitMovingField(x, y, MovDir[x][y]);
3359
3360     PlaySoundLevelAction(x, y, ACTION_MOVING);
3361   }
3362
3363   if (MovDir[x][y])
3364     ContinueMoving(x, y);
3365 }
3366
3367 void ContinueMoving(int x, int y)
3368 {
3369   int element = Feld[x][y];
3370   int direction = MovDir[x][y];
3371   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3372   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3373   int horiz_move = (dx != 0);
3374   int newx = x + dx, newy = y + dy;
3375   int step = (horiz_move ? dx : dy) * TILEX / 8;
3376
3377   if (element == EL_AMOEBA_DROP || element == EL_AMOEBA_DRIPPING)
3378     step /= 2;
3379   else if (element == EL_QUICKSAND_FILLING ||
3380            element == EL_QUICKSAND_EMPTYING)
3381     step /= 4;
3382   else if (element == EL_MAGIC_WALL_FILLING ||
3383            element == EL_BD_MAGIC_WALL_FILLING ||
3384            element == EL_MAGIC_WALL_EMPTYING ||
3385            element == EL_BD_MAGIC_WALL_EMPTYING)
3386     step /= 2;
3387   else if (CAN_FALL(element) && horiz_move &&
3388            y < lev_fieldy-1 && IS_BELT_ACTIVE(Feld[x][y+1]))
3389     step /= 2;
3390   else if (element == EL_SPRING && horiz_move)
3391     step *= 2;
3392
3393 #if OLD_GAME_BEHAVIOUR
3394   else if (CAN_FALL(element) && horiz_move && !IS_SP_ELEMENT(element))
3395     step*=2;
3396 #endif
3397
3398   MovPos[x][y] += step;
3399
3400   if (ABS(MovPos[x][y]) >= TILEX)       /* object reached its destination */
3401   {
3402     Feld[x][y] = EL_EMPTY;
3403     Feld[newx][newy] = element;
3404
3405     if (element == EL_MOLE)
3406     {
3407       int i;
3408       static int xy[4][2] =
3409       {
3410         { 0, -1 },
3411         { -1, 0 },
3412         { +1, 0 },
3413         { 0, +1 }
3414       };
3415
3416       Feld[x][y] = EL_SAND;
3417       DrawLevelField(x, y);
3418
3419       for(i=0; i<4; i++)
3420       {
3421         int xx, yy;
3422
3423         xx = x + xy[i][0];
3424         yy = y + xy[i][1];
3425
3426         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_SAND)
3427           DrawLevelField(xx, yy);       /* for "DrawCrumbledSand()" */
3428       }
3429     }
3430
3431     if (element == EL_QUICKSAND_FILLING)
3432     {
3433       element = Feld[newx][newy] = get_next_element(element);
3434       Store[newx][newy] = Store[x][y];
3435     }
3436     else if (element == EL_QUICKSAND_EMPTYING)
3437     {
3438       Feld[x][y] = get_next_element(element);
3439       element = Feld[newx][newy] = Store[x][y];
3440     }
3441     else if (element == EL_MAGIC_WALL_FILLING)
3442     {
3443       element = Feld[newx][newy] = get_next_element(element);
3444       if (!game.magic_wall_active)
3445         element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
3446       Store[newx][newy] = Store[x][y];
3447     }
3448     else if (element == EL_MAGIC_WALL_EMPTYING)
3449     {
3450       Feld[x][y] = get_next_element(element);
3451       if (!game.magic_wall_active)
3452         Feld[x][y] = EL_MAGIC_WALL_DEAD;
3453       element = Feld[newx][newy] = Store[x][y];
3454     }
3455     else if (element == EL_BD_MAGIC_WALL_FILLING)
3456     {
3457       element = Feld[newx][newy] = get_next_element(element);
3458       if (!game.magic_wall_active)
3459         element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
3460       Store[newx][newy] = Store[x][y];
3461     }
3462     else if (element == EL_BD_MAGIC_WALL_EMPTYING)
3463     {
3464       Feld[x][y] = get_next_element(element);
3465       if (!game.magic_wall_active)
3466         Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
3467       element = Feld[newx][newy] = Store[x][y];
3468     }
3469     else if (element == EL_AMOEBA_DRIPPING)
3470     {
3471       Feld[x][y] = get_next_element(element);
3472       element = Feld[newx][newy] = Store[x][y];
3473     }
3474     else if (Store[x][y] == EL_ACID)
3475     {
3476       element = Feld[newx][newy] = EL_ACID;
3477     }
3478
3479     Store[x][y] = 0;
3480     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3481     MovDelay[newx][newy] = 0;
3482
3483 #if 0
3484     /* all done in "InitMovingField()" */
3485     GfxAction[newx][newy] = GfxAction[x][y];    /* keep action one frame */
3486     GfxRandom[newx][newy] = GfxRandom[x][y];    /* keep same random value */
3487 #endif
3488
3489     /* copy animation control values to new field */
3490     GfxFrame[newx][newy]  = GfxFrame[x][y];
3491     GfxAction[newx][newy] = GfxAction[x][y];
3492     GfxRandom[newx][newy] = GfxRandom[x][y];
3493
3494     ResetGfxAnimation(x, y);    /* reset animation values for old field */
3495
3496 #if 1
3497 #if 0
3498     if (!CAN_MOVE(element))
3499       MovDir[newx][newy] = 0;
3500 #else
3501     /*
3502     if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
3503       MovDir[newx][newy] = 0;
3504     */
3505
3506     if (!CAN_MOVE(element) ||
3507         (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
3508       MovDir[newx][newy] = 0;
3509 #endif
3510 #endif
3511
3512     DrawLevelField(x, y);
3513     DrawLevelField(newx, newy);
3514
3515     Stop[newx][newy] = TRUE;
3516     JustStopped[newx][newy] = 3;
3517
3518     if (DONT_TOUCH(element))    /* object may be nasty to player or others */
3519     {
3520       TestIfBadThingTouchesHero(newx, newy);
3521       TestIfBadThingTouchesFriend(newx, newy);
3522       TestIfBadThingTouchesOtherBadThing(newx, newy);
3523     }
3524     else if (element == EL_PENGUIN)
3525       TestIfFriendTouchesBadThing(newx, newy);
3526
3527     if (CAN_SMASH(element) && direction == MV_DOWN &&
3528         (newy == lev_fieldy-1 || !IS_FREE(x, newy+1)))
3529       Impact(x, newy);
3530   }
3531   else                          /* still moving on */
3532   {
3533 #if 0
3534     if (GfxAction[x][y] == ACTION_DEFAULT)
3535     {
3536       printf("reset GfxAction...\n");
3537
3538       GfxAction[x][y] = ACTION_MOVING;
3539     }
3540 #endif
3541
3542     DrawLevelField(x, y);
3543   }
3544 }
3545
3546 int AmoebeNachbarNr(int ax, int ay)
3547 {
3548   int i;
3549   int element = Feld[ax][ay];
3550   int group_nr = 0;
3551   static int xy[4][2] =
3552   {
3553     { 0, -1 },
3554     { -1, 0 },
3555     { +1, 0 },
3556     { 0, +1 }
3557   };
3558
3559   for (i=0; i<4; i++)
3560   {
3561     int x = ax + xy[i][0];
3562     int y = ay + xy[i][1];
3563
3564     if (!IN_LEV_FIELD(x, y))
3565       continue;
3566
3567     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
3568       group_nr = AmoebaNr[x][y];
3569   }
3570
3571   return group_nr;
3572 }
3573
3574 void AmoebenVereinigen(int ax, int ay)
3575 {
3576   int i, x, y, xx, yy;
3577   int new_group_nr = AmoebaNr[ax][ay];
3578   static int xy[4][2] =
3579   {
3580     { 0, -1 },
3581     { -1, 0 },
3582     { +1, 0 },
3583     { 0, +1 }
3584   };
3585
3586   if (new_group_nr == 0)
3587     return;
3588
3589   for (i=0; i<4; i++)
3590   {
3591     x = ax + xy[i][0];
3592     y = ay + xy[i][1];
3593
3594     if (!IN_LEV_FIELD(x, y))
3595       continue;
3596
3597     if ((Feld[x][y] == EL_AMOEBA_FULL ||
3598          Feld[x][y] == EL_BD_AMOEBA ||
3599          Feld[x][y] == EL_AMOEBA_DEAD) &&
3600         AmoebaNr[x][y] != new_group_nr)
3601     {
3602       int old_group_nr = AmoebaNr[x][y];
3603
3604       if (old_group_nr == 0)
3605         return;
3606
3607       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
3608       AmoebaCnt[old_group_nr] = 0;
3609       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
3610       AmoebaCnt2[old_group_nr] = 0;
3611
3612       for (yy=0; yy<lev_fieldy; yy++)
3613       {
3614         for (xx=0; xx<lev_fieldx; xx++)
3615         {
3616           if (AmoebaNr[xx][yy] == old_group_nr)
3617             AmoebaNr[xx][yy] = new_group_nr;
3618         }
3619       }
3620     }
3621   }
3622 }
3623
3624 void AmoebeUmwandeln(int ax, int ay)
3625 {
3626   int i, x, y;
3627
3628   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
3629   {
3630     int group_nr = AmoebaNr[ax][ay];
3631
3632 #ifdef DEBUG
3633     if (group_nr == 0)
3634     {
3635       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
3636       printf("AmoebeUmwandeln(): This should never happen!\n");
3637       return;
3638     }
3639 #endif
3640
3641     for (y=0; y<lev_fieldy; y++)
3642     {
3643       for (x=0; x<lev_fieldx; x++)
3644       {
3645         if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
3646         {
3647           AmoebaNr[x][y] = 0;
3648           Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
3649         }
3650       }
3651     }
3652     PlaySoundLevel(ax, ay, (IS_GEM(level.amoeba_content) ?
3653                             SND_AMOEBA_TURNING_TO_GEM :
3654                             SND_AMOEBA_TURNING_TO_ROCK));
3655     Bang(ax, ay);
3656   }
3657   else
3658   {
3659     static int xy[4][2] =
3660     {
3661       { 0, -1 },
3662       { -1, 0 },
3663       { +1, 0 },
3664       { 0, +1 }
3665     };
3666
3667     for (i=0; i<4; i++)
3668     {
3669       x = ax + xy[i][0];
3670       y = ay + xy[i][1];
3671
3672       if (!IN_LEV_FIELD(x, y))
3673         continue;
3674
3675       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
3676       {
3677         PlaySoundLevel(x, y, (IS_GEM(level.amoeba_content) ?
3678                               SND_AMOEBA_TURNING_TO_GEM :
3679                               SND_AMOEBA_TURNING_TO_ROCK));
3680         Bang(x, y);
3681       }
3682     }
3683   }
3684 }
3685
3686 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
3687 {
3688   int x, y;
3689   int group_nr = AmoebaNr[ax][ay];
3690   boolean done = FALSE;
3691
3692 #ifdef DEBUG
3693   if (group_nr == 0)
3694   {
3695     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
3696     printf("AmoebeUmwandelnBD(): This should never happen!\n");
3697     return;
3698   }
3699 #endif
3700
3701   for (y=0; y<lev_fieldy; y++)
3702   {
3703     for (x=0; x<lev_fieldx; x++)
3704     {
3705       if (AmoebaNr[x][y] == group_nr &&
3706           (Feld[x][y] == EL_AMOEBA_DEAD ||
3707            Feld[x][y] == EL_BD_AMOEBA ||
3708            Feld[x][y] == EL_AMOEBA_CREATING))
3709       {
3710         AmoebaNr[x][y] = 0;
3711         Feld[x][y] = new_element;
3712         InitField(x, y, FALSE);
3713         DrawLevelField(x, y);
3714         done = TRUE;
3715       }
3716     }
3717   }
3718
3719   if (done)
3720     PlaySoundLevel(ax, ay, (new_element == EL_BD_ROCK ?
3721                             SND_BD_AMOEBA_TURNING_TO_ROCK :
3722                             SND_BD_AMOEBA_TURNING_TO_GEM));
3723 }
3724
3725 void AmoebeWaechst(int x, int y)
3726 {
3727   static unsigned long sound_delay = 0;
3728   static unsigned long sound_delay_value = 0;
3729
3730   if (!MovDelay[x][y])          /* start new growing cycle */
3731   {
3732     MovDelay[x][y] = 7;
3733
3734     if (DelayReached(&sound_delay, sound_delay_value))
3735     {
3736       if (Store[x][y] == EL_BD_AMOEBA)
3737         PlaySoundLevel(x, y, SND_BD_AMOEBA_CREATING);
3738       else
3739         PlaySoundLevel(x, y, SND_AMOEBA_CREATING);
3740       sound_delay_value = 30;
3741     }
3742   }
3743
3744   if (MovDelay[x][y])           /* wait some time before growing bigger */
3745   {
3746     MovDelay[x][y]--;
3747     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3748     {
3749       int frame = getGraphicAnimationFrame(IMG_AMOEBA_CREATING,
3750                                            6 - MovDelay[x][y]);
3751
3752       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_CREATING, frame);
3753     }
3754
3755     if (!MovDelay[x][y])
3756     {
3757       Feld[x][y] = Store[x][y];
3758       Store[x][y] = 0;
3759       DrawLevelField(x, y);
3760     }
3761   }
3762 }
3763
3764 void AmoebaDisappearing(int x, int y)
3765 {
3766   static unsigned long sound_delay = 0;
3767   static unsigned long sound_delay_value = 0;
3768
3769   if (!MovDelay[x][y])          /* start new shrinking cycle */
3770   {
3771     MovDelay[x][y] = 7;
3772
3773     if (DelayReached(&sound_delay, sound_delay_value))
3774       sound_delay_value = 30;
3775   }
3776
3777   if (MovDelay[x][y])           /* wait some time before shrinking */
3778   {
3779     MovDelay[x][y]--;
3780     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3781     {
3782       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
3783                                            6 - MovDelay[x][y]);
3784
3785       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
3786     }
3787
3788     if (!MovDelay[x][y])
3789     {
3790       Feld[x][y] = EL_EMPTY;
3791       DrawLevelField(x, y);
3792
3793       /* don't let mole enter this field in this cycle;
3794          (give priority to objects falling to this field from above) */
3795       Stop[x][y] = TRUE;
3796     }
3797   }
3798 }
3799
3800 void AmoebeAbleger(int ax, int ay)
3801 {
3802   int i;
3803   int element = Feld[ax][ay];
3804   int newax = ax, neway = ay;
3805   static int xy[4][2] =
3806   {
3807     { 0, -1 },
3808     { -1, 0 },
3809     { +1, 0 },
3810     { 0, +1 }
3811   };
3812
3813   if (!level.amoeba_speed)
3814   {
3815     Feld[ax][ay] = EL_AMOEBA_DEAD;
3816     DrawLevelField(ax, ay);
3817     return;
3818   }
3819
3820   if (!MovDelay[ax][ay])        /* start making new amoeba field */
3821     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
3822
3823   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
3824   {
3825     MovDelay[ax][ay]--;
3826     if (MovDelay[ax][ay])
3827       return;
3828   }
3829
3830   if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
3831   {
3832     int start = RND(4);
3833     int x = ax + xy[start][0];
3834     int y = ay + xy[start][1];
3835
3836     if (!IN_LEV_FIELD(x, y))
3837       return;
3838
3839     if (IS_FREE(x, y) ||
3840         Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
3841     {
3842       newax = x;
3843       neway = y;
3844     }
3845
3846     if (newax == ax && neway == ay)
3847       return;
3848   }
3849   else                          /* normal or "filled" (BD style) amoeba */
3850   {
3851     int start = RND(4);
3852     boolean waiting_for_player = FALSE;
3853
3854     for (i=0; i<4; i++)
3855     {
3856       int j = (start + i) % 4;
3857       int x = ax + xy[j][0];
3858       int y = ay + xy[j][1];
3859
3860       if (!IN_LEV_FIELD(x, y))
3861         continue;
3862
3863       if (IS_FREE(x, y) ||
3864           Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
3865       {
3866         newax = x;
3867         neway = y;
3868         break;
3869       }
3870       else if (IS_PLAYER(x, y))
3871         waiting_for_player = TRUE;
3872     }
3873
3874     if (newax == ax && neway == ay)             /* amoeba cannot grow */
3875     {
3876       if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
3877       {
3878         Feld[ax][ay] = EL_AMOEBA_DEAD;
3879         DrawLevelField(ax, ay);
3880         AmoebaCnt[AmoebaNr[ax][ay]]--;
3881
3882         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
3883         {
3884           if (element == EL_AMOEBA_FULL)
3885             AmoebeUmwandeln(ax, ay);
3886           else if (element == EL_BD_AMOEBA)
3887             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
3888         }
3889       }
3890       return;
3891     }
3892     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
3893     {
3894       /* amoeba gets larger by growing in some direction */
3895
3896       int new_group_nr = AmoebaNr[ax][ay];
3897
3898 #ifdef DEBUG
3899   if (new_group_nr == 0)
3900   {
3901     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
3902     printf("AmoebeAbleger(): This should never happen!\n");
3903     return;
3904   }
3905 #endif
3906
3907       AmoebaNr[newax][neway] = new_group_nr;
3908       AmoebaCnt[new_group_nr]++;
3909       AmoebaCnt2[new_group_nr]++;
3910
3911       /* if amoeba touches other amoeba(s) after growing, unify them */
3912       AmoebenVereinigen(newax, neway);
3913
3914       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
3915       {
3916         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
3917         return;
3918       }
3919     }
3920   }
3921
3922   if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
3923       (neway == lev_fieldy - 1 && newax != ax))
3924   {
3925     Feld[newax][neway] = EL_AMOEBA_CREATING;    /* creation of new amoeba */
3926     Store[newax][neway] = element;
3927   }
3928   else if (neway == ay)
3929   {
3930     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
3931     PlaySoundLevel(newax, neway, SND_AMOEBA_DROP_CREATING);
3932   }
3933   else
3934   {
3935     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
3936     Feld[ax][ay] = EL_AMOEBA_DRIPPING;
3937     Store[ax][ay] = EL_AMOEBA_DROP;
3938     ContinueMoving(ax, ay);
3939     return;
3940   }
3941
3942   DrawLevelField(newax, neway);
3943 }
3944
3945 void Life(int ax, int ay)
3946 {
3947   int x1, y1, x2, y2;
3948   static int life[4] = { 2, 3, 3, 3 };  /* parameters for "game of life" */
3949   int life_time = 40;
3950   int element = Feld[ax][ay];
3951   boolean changed = FALSE;
3952
3953   if (Stop[ax][ay])
3954     return;
3955
3956   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
3957     MovDelay[ax][ay] = life_time;
3958
3959   if (MovDelay[ax][ay])         /* wait some time before next cycle */
3960   {
3961     MovDelay[ax][ay]--;
3962     if (MovDelay[ax][ay])
3963       return;
3964   }
3965
3966   for (y1=-1; y1<2; y1++) for(x1=-1; x1<2; x1++)
3967   {
3968     int xx = ax+x1, yy = ay+y1;
3969     int nachbarn = 0;
3970
3971     if (!IN_LEV_FIELD(xx, yy))
3972       continue;
3973
3974     for (y2=-1; y2<2; y2++) for (x2=-1; x2<2; x2++)
3975     {
3976       int x = xx+x2, y = yy+y2;
3977
3978       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
3979         continue;
3980
3981       if (((Feld[x][y] == element ||
3982             (element == EL_GAMEOFLIFE && IS_PLAYER(x, y))) &&
3983            !Stop[x][y]) ||
3984           (IS_FREE(x, y) && Stop[x][y]))
3985         nachbarn++;
3986     }
3987
3988     if (xx == ax && yy == ay)           /* field in the middle */
3989     {
3990       if (nachbarn < life[0] || nachbarn > life[1])
3991       {
3992         Feld[xx][yy] = EL_EMPTY;
3993         if (!Stop[xx][yy])
3994           DrawLevelField(xx, yy);
3995         Stop[xx][yy] = TRUE;
3996         changed = TRUE;
3997       }
3998     }
3999     else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
4000     {                                   /* free border field */
4001       if (nachbarn >= life[2] && nachbarn <= life[3])
4002       {
4003         Feld[xx][yy] = element;
4004         MovDelay[xx][yy] = (element == EL_GAMEOFLIFE ? 0 : life_time-1);
4005         if (!Stop[xx][yy])
4006           DrawLevelField(xx, yy);
4007         Stop[xx][yy] = TRUE;
4008         changed = TRUE;
4009       }
4010     }
4011   }
4012
4013   if (changed)
4014     PlaySoundLevel(ax, ay, element == EL_GAMEOFLIFE ? SND_GAMEOFLIFE_CREATING :
4015                    SND_BIOMAZE_CREATING);
4016 }
4017
4018 static void InitRobotWheel(int x, int y)
4019 {
4020   MovDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
4021 }
4022
4023 static void RunRobotWheel(int x, int y)
4024 {
4025   PlaySoundLevel(x, y, SND_ROBOT_WHEEL_ACTIVE);
4026 }
4027
4028 static void StopRobotWheel(int x, int y)
4029 {
4030   if (ZX == x && ZY == y)
4031     ZX = ZY = -1;
4032 }
4033
4034 static void InitTimegateWheel(int x, int y)
4035 {
4036   MovDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
4037 }
4038
4039 static void RunTimegateWheel(int x, int y)
4040 {
4041   PlaySoundLevel(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
4042 }
4043
4044 void CheckExit(int x, int y)
4045 {
4046   if (local_player->gems_still_needed > 0 ||
4047       local_player->sokobanfields_still_needed > 0 ||
4048       local_player->lights_still_needed > 0)
4049     return;
4050
4051   Feld[x][y] = EL_EXIT_OPENING;
4052
4053   PlaySoundLevelNearest(x, y, SND_EXIT_OPENING);
4054 }
4055
4056 void CheckExitSP(int x, int y)
4057 {
4058   if (local_player->gems_still_needed > 0)
4059     return;
4060
4061   Feld[x][y] = EL_SP_EXIT_OPEN;
4062
4063   PlaySoundLevelNearest(x, y, SND_SP_EXIT_OPENING);
4064 }
4065
4066 static void CloseAllOpenTimegates()
4067 {
4068   int x, y;
4069
4070   for (y=0; y<lev_fieldy; y++)
4071   {
4072     for (x=0; x<lev_fieldx; x++)
4073     {
4074       int element = Feld[x][y];
4075
4076       if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
4077       {
4078         Feld[x][y] = EL_TIMEGATE_CLOSING;
4079         PlaySoundLevel(x, y, SND_TIMEGATE_CLOSING);
4080       }
4081     }
4082   }
4083 }
4084
4085 void EdelsteinFunkeln(int x, int y)
4086 {
4087   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
4088     return;
4089
4090   if (Feld[x][y] == EL_BD_DIAMOND)
4091 #if 0
4092     DrawLevelElementAnimation(x, y, el2img(Feld[x][y]));
4093 #else
4094     return;
4095 #endif
4096   else
4097   {
4098     if (MovDelay[x][y] == 0)    /* next animation frame */
4099       MovDelay[x][y] = 11 * !SimpleRND(500);
4100
4101     if (MovDelay[x][y] != 0)    /* wait some time before next frame */
4102     {
4103       MovDelay[x][y]--;
4104
4105       if (setup.direct_draw && MovDelay[x][y])
4106         SetDrawtoField(DRAW_BUFFERED);
4107
4108 #if 0
4109       DrawGraphic(SCREENX(x), SCREENY(y), el2img(Feld[x][y]), 0);
4110 #else
4111       DrawLevelElementAnimation(x, y, Feld[x][y]);
4112 #endif
4113
4114       if (MovDelay[x][y] != 0)
4115       {
4116         int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
4117                                              10 - MovDelay[x][y]);
4118
4119         DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
4120
4121         if (setup.direct_draw)
4122         {
4123           int dest_x, dest_y;
4124
4125           dest_x = FX + SCREENX(x) * TILEX;
4126           dest_y = FY + SCREENY(y) * TILEY;
4127
4128           BlitBitmap(drawto_field, window,
4129                      dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
4130           SetDrawtoField(DRAW_DIRECT);
4131         }
4132       }
4133     }
4134   }
4135 }
4136
4137 void MauerWaechst(int x, int y)
4138 {
4139   int delay = 6;
4140
4141   if (!MovDelay[x][y])          /* next animation frame */
4142     MovDelay[x][y] = 3 * delay;
4143
4144   if (MovDelay[x][y])           /* wait some time before next frame */
4145   {
4146     MovDelay[x][y]--;
4147
4148     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4149     {
4150       int graphic = el_dir2img(Feld[x][y], MovDir[x][y]);
4151       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
4152
4153       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4154     }
4155
4156     if (!MovDelay[x][y])
4157     {
4158       if (MovDir[x][y] == MV_LEFT)
4159       {
4160         if (IN_LEV_FIELD(x - 1, y) && IS_MAUER(Feld[x - 1][y]))
4161           DrawLevelField(x - 1, y);
4162       }
4163       else if (MovDir[x][y] == MV_RIGHT)
4164       {
4165         if (IN_LEV_FIELD(x + 1, y) && IS_MAUER(Feld[x + 1][y]))
4166           DrawLevelField(x + 1, y);
4167       }
4168       else if (MovDir[x][y] == MV_UP)
4169       {
4170         if (IN_LEV_FIELD(x, y - 1) && IS_MAUER(Feld[x][y - 1]))
4171           DrawLevelField(x, y - 1);
4172       }
4173       else
4174       {
4175         if (IN_LEV_FIELD(x, y + 1) && IS_MAUER(Feld[x][y + 1]))
4176           DrawLevelField(x, y + 1);
4177       }
4178
4179       Feld[x][y] = Store[x][y];
4180       Store[x][y] = 0;
4181       MovDir[x][y] = MV_NO_MOVING;
4182       DrawLevelField(x, y);
4183     }
4184   }
4185 }
4186
4187 void MauerAbleger(int ax, int ay)
4188 {
4189   int element = Feld[ax][ay];
4190   boolean oben_frei = FALSE, unten_frei = FALSE;
4191   boolean links_frei = FALSE, rechts_frei = FALSE;
4192   boolean oben_massiv = FALSE, unten_massiv = FALSE;
4193   boolean links_massiv = FALSE, rechts_massiv = FALSE;
4194   boolean new_wall = FALSE;
4195
4196   if (!MovDelay[ax][ay])        /* start building new wall */
4197     MovDelay[ax][ay] = 6;
4198
4199   if (MovDelay[ax][ay])         /* wait some time before building new wall */
4200   {
4201     MovDelay[ax][ay]--;
4202     if (MovDelay[ax][ay])
4203       return;
4204   }
4205
4206   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
4207     oben_frei = TRUE;
4208   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
4209     unten_frei = TRUE;
4210   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
4211     links_frei = TRUE;
4212   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
4213     rechts_frei = TRUE;
4214
4215   if (element == EL_WALL_GROWING_Y || element == EL_WALL_GROWING_XY)
4216   {
4217     if (oben_frei)
4218     {
4219       Feld[ax][ay-1] = EL_WALL_GROWING_ACTIVE;
4220       Store[ax][ay-1] = element;
4221       MovDir[ax][ay-1] = MV_UP;
4222       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
4223         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
4224                     IMG_WALL_GROWING_ACTIVE_UP, 0);
4225       new_wall = TRUE;
4226     }
4227     if (unten_frei)
4228     {
4229       Feld[ax][ay+1] = EL_WALL_GROWING_ACTIVE;
4230       Store[ax][ay+1] = element;
4231       MovDir[ax][ay+1] = MV_DOWN;
4232       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
4233         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
4234                     IMG_WALL_GROWING_ACTIVE_DOWN, 0);
4235       new_wall = TRUE;
4236     }
4237   }
4238
4239   if (element == EL_WALL_GROWING_X || element == EL_WALL_GROWING_XY ||
4240       element == EL_WALL_GROWING)
4241   {
4242     if (links_frei)
4243     {
4244       Feld[ax-1][ay] = EL_WALL_GROWING_ACTIVE;
4245       Store[ax-1][ay] = element;
4246       MovDir[ax-1][ay] = MV_LEFT;
4247       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
4248         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
4249                     IMG_WALL_GROWING_ACTIVE_LEFT, 0);
4250       new_wall = TRUE;
4251     }
4252
4253     if (rechts_frei)
4254     {
4255       Feld[ax+1][ay] = EL_WALL_GROWING_ACTIVE;
4256       Store[ax+1][ay] = element;
4257       MovDir[ax+1][ay] = MV_RIGHT;
4258       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
4259         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
4260                     IMG_WALL_GROWING_ACTIVE_RIGHT, 0);
4261       new_wall = TRUE;
4262     }
4263   }
4264
4265   if (element == EL_WALL_GROWING && (links_frei || rechts_frei))
4266     DrawLevelField(ax, ay);
4267
4268   if (!IN_LEV_FIELD(ax, ay-1) || IS_MAUER(Feld[ax][ay-1]))
4269     oben_massiv = TRUE;
4270   if (!IN_LEV_FIELD(ax, ay+1) || IS_MAUER(Feld[ax][ay+1]))
4271     unten_massiv = TRUE;
4272   if (!IN_LEV_FIELD(ax-1, ay) || IS_MAUER(Feld[ax-1][ay]))
4273     links_massiv = TRUE;
4274   if (!IN_LEV_FIELD(ax+1, ay) || IS_MAUER(Feld[ax+1][ay]))
4275     rechts_massiv = TRUE;
4276
4277   if (((oben_massiv && unten_massiv) ||
4278        element == EL_WALL_GROWING_X || element == EL_WALL_GROWING) &&
4279       ((links_massiv && rechts_massiv) ||
4280        element == EL_WALL_GROWING_Y))
4281     Feld[ax][ay] = EL_WALL;
4282
4283   if (new_wall)
4284     PlaySoundLevel(ax, ay, SND_WALL_GROWING);
4285 }
4286
4287 void CheckForDragon(int x, int y)
4288 {
4289   int i, j;
4290   boolean dragon_found = FALSE;
4291   static int xy[4][2] =
4292   {
4293     { 0, -1 },
4294     { -1, 0 },
4295     { +1, 0 },
4296     { 0, +1 }
4297   };
4298
4299   for (i=0; i<4; i++)
4300   {
4301     for (j=0; j<4; j++)
4302     {
4303       int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
4304
4305       if (IN_LEV_FIELD(xx, yy) &&
4306           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
4307       {
4308         if (Feld[xx][yy] == EL_DRAGON)
4309           dragon_found = TRUE;
4310       }
4311       else
4312         break;
4313     }
4314   }
4315
4316   if (!dragon_found)
4317   {
4318     for (i=0; i<4; i++)
4319     {
4320       for (j=0; j<3; j++)
4321       {
4322         int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
4323   
4324         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
4325         {
4326           Feld[xx][yy] = EL_EMPTY;
4327           DrawLevelField(xx, yy);
4328         }
4329         else
4330           break;
4331       }
4332     }
4333   }
4334 }
4335
4336 static void InitBuggyBase(int x, int y)
4337 {
4338   int element = Feld[x][y];
4339   int activating_delay = FRAMES_PER_SECOND / 4;
4340
4341   MovDelay[x][y] =
4342     (element == EL_SP_BUGGY_BASE ?
4343      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
4344      element == EL_SP_BUGGY_BASE_ACTIVATING ?
4345      activating_delay :
4346      element == EL_SP_BUGGY_BASE_ACTIVE ?
4347      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
4348 }
4349
4350 static void WarnBuggyBase(int x, int y)
4351 {
4352   int i;
4353   static int xy[4][2] =
4354   {
4355     { 0, -1 },
4356     { -1, 0 },
4357     { +1, 0 },
4358     { 0, +1 }
4359   };
4360
4361   for (i=0; i<4; i++)
4362   {
4363     int xx = x + xy[i][0], yy = y + xy[i][1];
4364
4365     if (IS_PLAYER(xx, yy))
4366     {
4367       PlaySoundLevel(x, y, SND_SP_BUGGY_BASE_ACTIVE);
4368
4369       break;
4370     }
4371   }
4372 }
4373
4374 static void InitTrap(int x, int y)
4375 {
4376   MovDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
4377 }
4378
4379 static void ActivateTrap(int x, int y)
4380 {
4381   PlaySoundLevel(x, y, SND_TRAP_ACTIVATING);
4382 }
4383
4384 static void ChangeActiveTrap(int x, int y)
4385 {
4386   int graphic = IMG_TRAP_ACTIVE;
4387
4388   /* if new animation frame was drawn, correct crumbled sand border */
4389   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
4390     DrawCrumbledSand(SCREENX(x), SCREENY(y));
4391 }
4392
4393 static void ChangeElement(int x, int y)
4394 {
4395   int element = Feld[x][y];
4396
4397   if (MovDelay[x][y] == 0)              /* initialize element change */
4398   {
4399     MovDelay[x][y] = changing_element[element].change_delay + 1;
4400
4401     ResetGfxAnimation(x, y);
4402     ResetRandomAnimationValue(x, y);
4403
4404     if (changing_element[element].pre_change_function)
4405       changing_element[element].pre_change_function(x, y);
4406   }
4407
4408   MovDelay[x][y]--;
4409
4410   if (MovDelay[x][y] != 0)              /* continue element change */
4411   {
4412     if (IS_ANIMATED(el2img(element)))
4413       DrawLevelElementAnimationIfNeeded(x, y, element);
4414
4415     if (changing_element[element].change_function)
4416       changing_element[element].change_function(x, y);
4417   }
4418   else                                  /* finish element change */
4419   {
4420     Feld[x][y] = changing_element[element].next_element;
4421
4422     ResetGfxAnimation(x, y);
4423     ResetRandomAnimationValue(x, y);
4424
4425     DrawLevelField(x, y);
4426
4427     if (changing_element[element].post_change_function)
4428       changing_element[element].post_change_function(x, y);
4429   }
4430 }
4431
4432 static void PlayerActions(struct PlayerInfo *player, byte player_action)
4433 {
4434   static byte stored_player_action[MAX_PLAYERS];
4435   static int num_stored_actions = 0;
4436 #if 0
4437   static boolean save_tape_entry = FALSE;
4438 #endif
4439   boolean moved = FALSE, snapped = FALSE, bombed = FALSE;
4440   int left      = player_action & JOY_LEFT;
4441   int right     = player_action & JOY_RIGHT;
4442   int up        = player_action & JOY_UP;
4443   int down      = player_action & JOY_DOWN;
4444   int button1   = player_action & JOY_BUTTON_1;
4445   int button2   = player_action & JOY_BUTTON_2;
4446   int dx        = (left ? -1    : right ? 1     : 0);
4447   int dy        = (up   ? -1    : down  ? 1     : 0);
4448
4449   stored_player_action[player->index_nr] = 0;
4450   num_stored_actions++;
4451
4452   if (!player->active || tape.pausing)
4453     return;
4454
4455   if (player_action)
4456   {
4457 #if 0
4458     save_tape_entry = TRUE;
4459 #endif
4460     player->frame_reset_delay = 0;
4461
4462     if (button1)
4463       snapped = SnapField(player, dx, dy);
4464     else
4465     {
4466       if (button2)
4467         bombed = PlaceBomb(player);
4468       moved = MoveFigure(player, dx, dy);
4469     }
4470
4471     if (tape.single_step && tape.recording && !tape.pausing)
4472     {
4473       if (button1 || (bombed && !moved))
4474       {
4475         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
4476         SnapField(player, 0, 0);                /* stop snapping */
4477       }
4478     }
4479
4480 #if 0
4481     if (tape.recording && (moved || snapped || bombed))
4482     {
4483       if (bombed && !moved)
4484         player_action &= JOY_BUTTON;
4485
4486       stored_player_action[player->index_nr] = player_action;
4487       save_tape_entry = TRUE;
4488     }
4489     else if (tape.playing && snapped)
4490       SnapField(player, 0, 0);                  /* stop snapping */
4491 #else
4492     stored_player_action[player->index_nr] = player_action;
4493 #endif
4494   }
4495   else
4496   {
4497     /* no actions for this player (no input at player's configured device) */
4498
4499     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
4500     SnapField(player, 0, 0);
4501     CheckGravityMovement(player);
4502
4503 #if 1
4504     if (player->MovPos == 0)    /* needed for tape.playing */
4505       player->is_moving = FALSE;
4506 #endif
4507 #if 0
4508     if (player->MovPos == 0)    /* needed for tape.playing */
4509       player->last_move_dir = MV_NO_MOVING;
4510
4511     /* !!! CHECK THIS AGAIN !!!
4512        (Seems to be needed for some EL_ROBOT stuff, but breaks
4513        tapes when walking through pipes!)
4514     */
4515
4516     /* it seems that "player->last_move_dir" is misused as some sort of
4517        "player->is_just_moving_in_this_moment", which is needed for the
4518        robot stuff (robots don't kill players when they are moving)
4519     */
4520 #endif 
4521
4522     /* if the player does not move for some time, reset animation to start */
4523     if (++player->frame_reset_delay > player->move_delay_value)
4524       player->Frame = 0;
4525   }
4526
4527 #if 0
4528   if (tape.recording && num_stored_actions >= MAX_PLAYERS && save_tape_entry)
4529   {
4530     TapeRecordAction(stored_player_action);
4531     num_stored_actions = 0;
4532     save_tape_entry = FALSE;
4533   }
4534 #else
4535   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
4536   {
4537     TapeRecordAction(stored_player_action);
4538     num_stored_actions = 0;
4539   }
4540 #endif
4541
4542 #if 0
4543   if (tape.playing && !tape.pausing && !player_action &&
4544       tape.counter < tape.length)
4545   {
4546     int jx = player->jx, jy = player->jy;
4547     int next_joy =
4548       tape.pos[tape.counter].action[player->index_nr] & (JOY_LEFT|JOY_RIGHT);
4549
4550     if ((next_joy == JOY_LEFT || next_joy == JOY_RIGHT) &&
4551         (player->MovDir != JOY_UP && player->MovDir != JOY_DOWN))
4552     {
4553       int dx = (next_joy == JOY_LEFT ? -1 : +1);
4554
4555       if (IN_LEV_FIELD(jx+dx, jy) && IS_PUSHABLE(Feld[jx+dx][jy]))
4556       {
4557         int el = Feld[jx+dx][jy];
4558         int push_delay = (IS_SB_ELEMENT(el) || el == EL_SATELLITE ? 2 :
4559                           (el == EL_BALLOON || el == EL_SPRING) ? 0 : 10);
4560
4561         if (tape.delay_played + push_delay >= tape.pos[tape.counter].delay)
4562         {
4563           player->MovDir = next_joy;
4564           player->Frame = FrameCounter % 4;
4565           player->Pushing = TRUE;
4566         }
4567       }
4568     }
4569   }
4570 #endif
4571 }
4572
4573 void GameActions()
4574 {
4575   static unsigned long action_delay = 0;
4576   unsigned long action_delay_value;
4577   int magic_wall_x = 0, magic_wall_y = 0;
4578   int i, x, y, element, graphic;
4579   byte *recorded_player_action;
4580   byte summarized_player_action = 0;
4581
4582   if (game_status != PLAYING)
4583     return;
4584
4585   action_delay_value =
4586     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
4587
4588   if (tape.playing && tape.index_search && !tape.pausing)
4589     action_delay_value = 0;
4590
4591   /* ---------- main game synchronization point ---------- */
4592
4593   WaitUntilDelayReached(&action_delay, action_delay_value);
4594
4595   if (network_playing && !network_player_action_received)
4596   {
4597     /*
4598 #ifdef DEBUG
4599     printf("DEBUG: try to get network player actions in time\n");
4600 #endif
4601     */
4602
4603 #if defined(PLATFORM_UNIX)
4604     /* last chance to get network player actions without main loop delay */
4605     HandleNetworking();
4606 #endif
4607
4608     if (game_status != PLAYING)
4609       return;
4610
4611     if (!network_player_action_received)
4612     {
4613       /*
4614 #ifdef DEBUG
4615       printf("DEBUG: failed to get network player actions in time\n");
4616 #endif
4617       */
4618       return;
4619     }
4620   }
4621
4622   if (tape.pausing)
4623     return;
4624
4625   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
4626
4627   for (i=0; i<MAX_PLAYERS; i++)
4628   {
4629     summarized_player_action |= stored_player[i].action;
4630
4631     if (!network_playing)
4632       stored_player[i].effective_action = stored_player[i].action;
4633   }
4634
4635 #if defined(PLATFORM_UNIX)
4636   if (network_playing)
4637     SendToServer_MovePlayer(summarized_player_action);
4638 #endif
4639
4640   if (!options.network && !setup.team_mode)
4641     local_player->effective_action = summarized_player_action;
4642
4643   for (i=0; i<MAX_PLAYERS; i++)
4644   {
4645     int actual_player_action = stored_player[i].effective_action;
4646
4647     if (stored_player[i].programmed_action)
4648       actual_player_action = stored_player[i].programmed_action;
4649
4650     if (recorded_player_action)
4651       actual_player_action = recorded_player_action[i];
4652
4653     PlayerActions(&stored_player[i], actual_player_action);
4654     ScrollFigure(&stored_player[i], SCROLL_GO_ON);
4655   }
4656
4657   network_player_action_received = FALSE;
4658
4659   ScrollScreen(NULL, SCROLL_GO_ON);
4660
4661
4662
4663 #ifdef DEBUG
4664 #if 0
4665   if (TimeFrames == 0 && local_player->active)
4666   {
4667     extern unsigned int last_RND();
4668
4669     printf("DEBUG: %03d last RND was %d \t [state checksum is %d]\n",
4670            TimePlayed, last_RND(), getStateCheckSum(TimePlayed));
4671   }
4672 #endif
4673 #endif
4674
4675 #ifdef DEBUG
4676 #if 0
4677   if (GameFrameDelay >= 500)
4678     printf("FrameCounter == %d\n", FrameCounter);
4679 #endif
4680 #endif
4681
4682   FrameCounter++;
4683   TimeFrames++;
4684
4685   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
4686   {
4687     Stop[x][y] = FALSE;
4688     if (JustStopped[x][y] > 0)
4689       JustStopped[x][y]--;
4690
4691     GfxFrame[x][y]++;
4692
4693 #if DEBUG
4694     if (IS_BLOCKED(x, y))
4695     {
4696       int oldx, oldy;
4697
4698       Blocked2Moving(x, y, &oldx, &oldy);
4699       if (!IS_MOVING(oldx, oldy))
4700       {
4701         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
4702         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
4703         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
4704         printf("GameActions(): This should never happen!\n");
4705       }
4706     }
4707 #endif
4708   }
4709
4710   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
4711   {
4712     element = Feld[x][y];
4713     graphic = el2img(element);
4714
4715 #if 1
4716     if (graphic_info[graphic].anim_global_sync)
4717       GfxFrame[x][y] = FrameCounter;
4718 #endif
4719
4720     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
4721         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
4722       ResetRandomAnimationValue(x, y);
4723
4724     SetRandomAnimationValue(x, y);
4725
4726     if (IS_INACTIVE(element))
4727     {
4728
4729 #if 1
4730       if (IS_ANIMATED(graphic))
4731         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
4732 #endif
4733
4734       continue;
4735     }
4736
4737     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
4738     {
4739       StartMoving(x, y);
4740
4741       if (IS_ANIMATED(graphic) &&
4742           !IS_MOVING(x, y) &&
4743           !Stop[x][y])
4744         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
4745
4746       if (IS_GEM(element) || element == EL_SP_INFOTRON)
4747         EdelsteinFunkeln(x, y);
4748     }
4749
4750 #if 1
4751     else if ((element == EL_ACID ||
4752               element == EL_EXIT_OPEN ||
4753               element == EL_SP_EXIT_OPEN ||
4754               element == EL_SP_TERMINAL ||
4755               element == EL_SP_TERMINAL_ACTIVE ||
4756               element == EL_EXTRA_TIME ||
4757               element == EL_SHIELD_NORMAL ||
4758               element == EL_SHIELD_DEADLY) &&
4759              IS_ANIMATED(graphic))
4760       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
4761 #endif
4762
4763     else if (IS_MOVING(x, y))
4764       ContinueMoving(x, y);
4765     else if (IS_ACTIVE_BOMB(element))
4766       CheckDynamite(x, y);
4767 #if 0
4768     else if (element == EL_EXPLOSION && !game.explosions_delayed)
4769       Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
4770 #endif
4771     else if (element == EL_AMOEBA_CREATING)
4772       AmoebeWaechst(x, y);
4773     else if (element == EL_AMOEBA_SHRINKING)
4774       AmoebaDisappearing(x, y);
4775
4776 #if !USE_NEW_AMOEBA_CODE
4777     else if (IS_AMOEBALIVE(element))
4778       AmoebeAbleger(x, y);
4779 #endif
4780
4781     else if (element == EL_GAMEOFLIFE || element == EL_BIOMAZE)
4782       Life(x, y);
4783 #if 0
4784     else if (element == EL_ROBOT_WHEEL_ACTIVE)
4785       RobotWheel(x, y);
4786     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4787       TimegateWheel(x, y);
4788 #endif
4789 #if 0
4790     else if (element == EL_ACID_SPLASH_LEFT ||
4791              element == EL_ACID_SPLASH_RIGHT)
4792       SplashAcid(x, y);
4793 #endif
4794 #if 0
4795     else if (element == EL_NUT_CRACKING)
4796       NussKnacken(x, y);
4797     else if (element == EL_PEARL_BREAKING)
4798       BreakingPearl(x, y);
4799 #endif
4800     else if (element == EL_EXIT_CLOSED)
4801       CheckExit(x, y);
4802     else if (element == EL_SP_EXIT_CLOSED)
4803       CheckExitSP(x, y);
4804 #if 0
4805     else if (element == EL_EXIT_OPENING)
4806       AusgangstuerOeffnen(x, y);
4807 #endif
4808     else if (element == EL_WALL_GROWING_ACTIVE)
4809       MauerWaechst(x, y);
4810     else if (element == EL_WALL_GROWING ||
4811              element == EL_WALL_GROWING_X ||
4812              element == EL_WALL_GROWING_Y ||
4813              element == EL_WALL_GROWING_XY)
4814       MauerAbleger(x, y);
4815     else if (element == EL_FLAMES)
4816       CheckForDragon(x, y);
4817 #if 0
4818     else if (element == EL_SP_BUGGY_BASE ||
4819              element == EL_SP_BUGGY_BASE_ACTIVATING ||
4820              element == EL_SP_BUGGY_BASE_ACTIVE)
4821       CheckBuggyBase(x, y);
4822     else if (element == EL_TRAP ||
4823              element == EL_TRAP_ACTIVE)
4824       CheckTrap(x, y);
4825     else if (IS_BELT_ACTIVE(element))
4826       DrawBeltAnimation(x, y, element);
4827     else if (element == EL_SWITCHGATE_OPENING)
4828       OpenSwitchgate(x, y);
4829     else if (element == EL_SWITCHGATE_CLOSING)
4830       CloseSwitchgate(x, y);
4831     else if (element == EL_TIMEGATE_OPENING)
4832       OpenTimegate(x, y);
4833     else if (element == EL_TIMEGATE_CLOSING)
4834       CloseTimegate(x, y);
4835 #endif
4836
4837     else if (IS_AUTO_CHANGING(element))
4838       ChangeElement(x, y);
4839
4840 #if 1
4841     else if (element == EL_EXPLOSION)
4842       ; /* drawing of correct explosion animation is handled separately */
4843     else if (IS_ANIMATED(graphic))
4844       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
4845 #endif
4846
4847     if (IS_BELT_ACTIVE(element))
4848       PlaySoundLevelAction(x, y, ACTION_ACTIVE);
4849
4850     if (game.magic_wall_active)
4851     {
4852       int jx = local_player->jx, jy = local_player->jy;
4853
4854       /* play the element sound at the position nearest to the player */
4855       if ((element == EL_MAGIC_WALL_FULL ||
4856            element == EL_MAGIC_WALL_ACTIVE ||
4857            element == EL_MAGIC_WALL_EMPTYING ||
4858            element == EL_BD_MAGIC_WALL_FULL ||
4859            element == EL_BD_MAGIC_WALL_ACTIVE ||
4860            element == EL_BD_MAGIC_WALL_EMPTYING) &&
4861           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
4862       {
4863         magic_wall_x = x;
4864         magic_wall_y = y;
4865       }
4866     }
4867   }
4868
4869 #if USE_NEW_AMOEBA_CODE
4870   /* new experimental amoeba growth stuff */
4871 #if 1
4872   if (!(FrameCounter % 8))
4873 #endif
4874   {
4875     static unsigned long random = 1684108901;
4876
4877     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
4878     {
4879 #if 0
4880       x = (random >> 10) % lev_fieldx;
4881       y = (random >> 20) % lev_fieldy;
4882 #else
4883       x = RND(lev_fieldx);
4884       y = RND(lev_fieldy);
4885 #endif
4886       element = Feld[x][y];
4887
4888       if (!IS_PLAYER(x,y) &&
4889           (element == EL_EMPTY ||
4890            element == EL_SAND ||
4891            element == EL_QUICKSAND_EMPTY ||
4892            element == EL_ACID_SPLASH_LEFT ||
4893            element == EL_ACID_SPLASH_RIGHT))
4894       {
4895         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
4896             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
4897             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
4898             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
4899           Feld[x][y] = EL_AMOEBA_DROP;
4900       }
4901
4902       random = random * 129 + 1;
4903     }
4904   }
4905 #endif
4906
4907 #if 0
4908   if (game.explosions_delayed)
4909 #endif
4910   {
4911     game.explosions_delayed = FALSE;
4912
4913     for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
4914     {
4915       element = Feld[x][y];
4916
4917       if (ExplodeField[x][y])
4918         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
4919       else if (element == EL_EXPLOSION)
4920         Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
4921
4922       ExplodeField[x][y] = EX_NO_EXPLOSION;
4923     }
4924
4925     game.explosions_delayed = TRUE;
4926   }
4927
4928   if (game.magic_wall_active)
4929   {
4930     if (!(game.magic_wall_time_left % 4))
4931     {
4932       int element = Feld[magic_wall_x][magic_wall_y];
4933
4934       if (element == EL_BD_MAGIC_WALL_FULL ||
4935           element == EL_BD_MAGIC_WALL_ACTIVE ||
4936           element == EL_BD_MAGIC_WALL_EMPTYING)
4937         PlaySoundLevel(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
4938       else
4939         PlaySoundLevel(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
4940     }
4941
4942     if (game.magic_wall_time_left > 0)
4943     {
4944       game.magic_wall_time_left--;
4945       if (!game.magic_wall_time_left)
4946       {
4947         for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
4948         {
4949           element = Feld[x][y];
4950
4951           if (element == EL_MAGIC_WALL_ACTIVE ||
4952               element == EL_MAGIC_WALL_FULL)
4953           {
4954             Feld[x][y] = EL_MAGIC_WALL_DEAD;
4955             DrawLevelField(x, y);
4956           }
4957           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
4958                    element == EL_BD_MAGIC_WALL_FULL)
4959           {
4960             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4961             DrawLevelField(x, y);
4962           }
4963         }
4964
4965         game.magic_wall_active = FALSE;
4966       }
4967     }
4968   }
4969
4970   if (game.light_time_left > 0)
4971   {
4972     game.light_time_left--;
4973
4974     if (game.light_time_left == 0)
4975       RedrawAllLightSwitchesAndInvisibleElements();
4976   }
4977
4978   if (game.timegate_time_left > 0)
4979   {
4980     game.timegate_time_left--;
4981
4982     if (game.timegate_time_left == 0)
4983       CloseAllOpenTimegates();
4984   }
4985
4986   for (i=0; i<MAX_PLAYERS; i++)
4987   {
4988     struct PlayerInfo *player = &stored_player[i];
4989
4990     if (SHIELD_ON(player))
4991     {
4992       if (player->shield_deadly_time_left)
4993         PlaySoundLevel(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
4994       else if (player->shield_normal_time_left)
4995         PlaySoundLevel(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
4996     }
4997   }
4998
4999   if (TimeFrames >= (1000 / GameFrameDelay))
5000   {
5001     TimeFrames = 0;
5002     TimePlayed++;
5003
5004     for (i=0; i<MAX_PLAYERS; i++)
5005     {
5006       struct PlayerInfo *player = &stored_player[i];
5007
5008       if (SHIELD_ON(player))
5009       {
5010         player->shield_normal_time_left--;
5011
5012         if (player->shield_deadly_time_left > 0)
5013           player->shield_deadly_time_left--;
5014       }
5015     }
5016
5017     if (tape.recording || tape.playing)
5018       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
5019
5020     if (TimeLeft > 0)
5021     {
5022       TimeLeft--;
5023
5024       if (TimeLeft <= 10 && setup.time_limit)
5025         PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MAX_RIGHT);
5026
5027       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
5028
5029       if (!TimeLeft && setup.time_limit)
5030         for (i=0; i<MAX_PLAYERS; i++)
5031           KillHero(&stored_player[i]);
5032     }
5033     else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
5034       DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FS_SMALL, FC_YELLOW);
5035   }
5036
5037   DrawAllPlayers();
5038
5039   if (options.debug)                    /* calculate frames per second */
5040   {
5041     static unsigned long fps_counter = 0;
5042     static int fps_frames = 0;
5043     unsigned long fps_delay_ms = Counter() - fps_counter;
5044
5045     fps_frames++;
5046
5047     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
5048     {
5049       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
5050
5051       fps_frames = 0;
5052       fps_counter = Counter();
5053     }
5054
5055     redraw_mask |= REDRAW_FPS;
5056   }
5057 }
5058
5059 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
5060 {
5061   int min_x = x, min_y = y, max_x = x, max_y = y;
5062   int i;
5063
5064   for (i=0; i<MAX_PLAYERS; i++)
5065   {
5066     int jx = stored_player[i].jx, jy = stored_player[i].jy;
5067
5068     if (!stored_player[i].active || &stored_player[i] == player)
5069       continue;
5070
5071     min_x = MIN(min_x, jx);
5072     min_y = MIN(min_y, jy);
5073     max_x = MAX(max_x, jx);
5074     max_y = MAX(max_y, jy);
5075   }
5076
5077   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
5078 }
5079
5080 static boolean AllPlayersInVisibleScreen()
5081 {
5082   int i;
5083
5084   for (i=0; i<MAX_PLAYERS; i++)
5085   {
5086     int jx = stored_player[i].jx, jy = stored_player[i].jy;
5087
5088     if (!stored_player[i].active)
5089       continue;
5090
5091     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
5092       return FALSE;
5093   }
5094
5095   return TRUE;
5096 }
5097
5098 void ScrollLevel(int dx, int dy)
5099 {
5100   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
5101   int x, y;
5102
5103   BlitBitmap(drawto_field, drawto_field,
5104              FX + TILEX*(dx == -1) - softscroll_offset,
5105              FY + TILEY*(dy == -1) - softscroll_offset,
5106              SXSIZE - TILEX*(dx!=0) + 2*softscroll_offset,
5107              SYSIZE - TILEY*(dy!=0) + 2*softscroll_offset,
5108              FX + TILEX*(dx == 1) - softscroll_offset,
5109              FY + TILEY*(dy == 1) - softscroll_offset);
5110
5111   if (dx)
5112   {
5113     x = (dx == 1 ? BX1 : BX2);
5114     for (y=BY1; y<=BY2; y++)
5115       DrawScreenField(x, y);
5116   }
5117
5118   if (dy)
5119   {
5120     y = (dy == 1 ? BY1 : BY2);
5121     for (x=BX1; x<=BX2; x++)
5122       DrawScreenField(x, y);
5123   }
5124
5125   redraw_mask |= REDRAW_FIELD;
5126 }
5127
5128 static void CheckGravityMovement(struct PlayerInfo *player)
5129 {
5130   if (level.gravity && !player->programmed_action)
5131   {
5132     int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
5133     int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
5134     int move_dir =
5135       (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
5136        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
5137        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
5138     int jx = player->jx, jy = player->jy;
5139     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
5140     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
5141     int new_jx = jx + dx, new_jy = jy + dy;
5142     boolean field_under_player_is_free =
5143       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
5144     boolean player_is_moving_to_valid_field =
5145       (IN_LEV_FIELD(new_jx, new_jy) &&
5146        (Feld[new_jx][new_jy] == EL_SP_BASE ||
5147         Feld[new_jx][new_jy] == EL_SAND));
5148
5149     if (field_under_player_is_free &&
5150         !player_is_moving_to_valid_field &&
5151         !IS_TUBE(Feld[jx][jy]))
5152       player->programmed_action = MV_DOWN;
5153   }
5154 }
5155
5156 boolean MoveFigureOneStep(struct PlayerInfo *player,
5157                           int dx, int dy, int real_dx, int real_dy)
5158 {
5159   int jx = player->jx, jy = player->jy;
5160   int new_jx = jx+dx, new_jy = jy+dy;
5161   int element;
5162   int can_move;
5163
5164   if (!player->active || (!dx && !dy))
5165     return MF_NO_ACTION;
5166
5167   player->MovDir = (dx < 0 ? MV_LEFT :
5168                     dx > 0 ? MV_RIGHT :
5169                     dy < 0 ? MV_UP :
5170                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
5171
5172   if (!IN_LEV_FIELD(new_jx, new_jy))
5173     return MF_NO_ACTION;
5174
5175   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
5176     return MF_NO_ACTION;
5177
5178 #if 0
5179   element = MovingOrBlocked2Element(new_jx, new_jy);
5180 #else
5181   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
5182 #endif
5183
5184   if (DONT_GO_TO(element))
5185   {
5186     if (element == EL_ACID && dx == 0 && dy == 1)
5187     {
5188       SplashAcid(jx, jy);
5189       Feld[jx][jy] = EL_PLAYER1;
5190       InitMovingField(jx, jy, MV_DOWN);
5191       Store[jx][jy] = EL_ACID;
5192       ContinueMoving(jx, jy);
5193       BuryHero(player);
5194     }
5195     else
5196       TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
5197
5198     return MF_MOVING;
5199   }
5200
5201   can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
5202   if (can_move != MF_MOVING)
5203     return can_move;
5204
5205   StorePlayer[jx][jy] = 0;
5206   player->last_jx = jx;
5207   player->last_jy = jy;
5208   jx = player->jx = new_jx;
5209   jy = player->jy = new_jy;
5210   StorePlayer[jx][jy] = player->element_nr;
5211
5212   player->MovPos =
5213     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
5214
5215   ScrollFigure(player, SCROLL_INIT);
5216
5217   return MF_MOVING;
5218 }
5219
5220 boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
5221 {
5222   int jx = player->jx, jy = player->jy;
5223   int old_jx = jx, old_jy = jy;
5224   int moved = MF_NO_ACTION;
5225
5226   if (!player->active || (!dx && !dy))
5227     return FALSE;
5228
5229 #if 0
5230   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
5231       !tape.playing)
5232     return FALSE;
5233 #else
5234   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
5235       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
5236     return FALSE;
5237 #endif
5238
5239   /* remove the last programmed player action */
5240   player->programmed_action = 0;
5241
5242   if (player->MovPos)
5243   {
5244     /* should only happen if pre-1.2 tape recordings are played */
5245     /* this is only for backward compatibility */
5246
5247     int original_move_delay_value = player->move_delay_value;
5248
5249 #if DEBUG
5250     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
5251            tape.counter);
5252 #endif
5253
5254     /* scroll remaining steps with finest movement resolution */
5255     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
5256
5257     while (player->MovPos)
5258     {
5259       ScrollFigure(player, SCROLL_GO_ON);
5260       ScrollScreen(NULL, SCROLL_GO_ON);
5261       FrameCounter++;
5262       DrawAllPlayers();
5263       BackToFront();
5264     }
5265
5266     player->move_delay_value = original_move_delay_value;
5267   }
5268
5269   if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
5270   {
5271     if (!(moved |= MoveFigureOneStep(player, 0, dy, dx, dy)))
5272       moved |= MoveFigureOneStep(player, dx, 0, dx, dy);
5273   }
5274   else
5275   {
5276     if (!(moved |= MoveFigureOneStep(player, dx, 0, dx, dy)))
5277       moved |= MoveFigureOneStep(player, 0, dy, dx, dy);
5278   }
5279
5280   jx = player->jx;
5281   jy = player->jy;
5282
5283   if (moved & MF_MOVING && !ScreenMovPos &&
5284       (player == local_player || !options.network))
5285   {
5286     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
5287     int offset = (setup.scroll_delay ? 3 : 0);
5288
5289     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
5290     {
5291       /* actual player has left the screen -- scroll in that direction */
5292       if (jx != old_jx)         /* player has moved horizontally */
5293         scroll_x += (jx - old_jx);
5294       else                      /* player has moved vertically */
5295         scroll_y += (jy - old_jy);
5296     }
5297     else
5298     {
5299       if (jx != old_jx)         /* player has moved horizontally */
5300       {
5301         if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
5302             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
5303           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
5304
5305         /* don't scroll over playfield boundaries */
5306         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5307           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5308
5309         /* don't scroll more than one field at a time */
5310         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
5311
5312         /* don't scroll against the player's moving direction */
5313         if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
5314             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
5315           scroll_x = old_scroll_x;
5316       }
5317       else                      /* player has moved vertically */
5318       {
5319         if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
5320             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
5321           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
5322
5323         /* don't scroll over playfield boundaries */
5324         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5325           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5326
5327         /* don't scroll more than one field at a time */
5328         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
5329
5330         /* don't scroll against the player's moving direction */
5331         if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
5332             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
5333           scroll_y = old_scroll_y;
5334       }
5335     }
5336
5337     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
5338     {
5339       if (!options.network && !AllPlayersInVisibleScreen())
5340       {
5341         scroll_x = old_scroll_x;
5342         scroll_y = old_scroll_y;
5343       }
5344       else
5345       {
5346         ScrollScreen(player, SCROLL_INIT);
5347         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
5348       }
5349     }
5350   }
5351
5352   if (!(moved & MF_MOVING) && !player->Pushing)
5353     player->Frame = 0;
5354   else
5355 #if 0
5356     player->Frame = (player->Frame + 1) % 4;
5357 #else
5358     player->Frame += 1 * 0;
5359 #endif
5360
5361   if (moved & MF_MOVING)
5362   {
5363     if (old_jx != jx && old_jy == jy)
5364       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
5365     else if (old_jx == jx && old_jy != jy)
5366       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
5367
5368     DrawLevelField(jx, jy);     /* for "DrawCrumbledSand()" */
5369
5370     player->last_move_dir = player->MovDir;
5371     player->is_moving = TRUE;
5372   }
5373   else
5374   {
5375     CheckGravityMovement(player);
5376
5377     /*
5378     player->last_move_dir = MV_NO_MOVING;
5379     */
5380     player->is_moving = FALSE;
5381   }
5382
5383   TestIfHeroTouchesBadThing(jx, jy);
5384
5385   if (!player->active)
5386     RemoveHero(player);
5387
5388   return moved;
5389 }
5390
5391 void ScrollFigure(struct PlayerInfo *player, int mode)
5392 {
5393   int jx = player->jx, jy = player->jy;
5394   int last_jx = player->last_jx, last_jy = player->last_jy;
5395   int move_stepsize = TILEX / player->move_delay_value;
5396
5397   if (!player->active || !player->MovPos)
5398     return;
5399
5400   if (mode == SCROLL_INIT)
5401   {
5402     player->actual_frame_counter = FrameCounter;
5403     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
5404     if (player->Frame)
5405       player->Frame += 1;
5406
5407     if (Feld[last_jx][last_jy] == EL_EMPTY)
5408       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
5409
5410     DrawPlayer(player);
5411     return;
5412   }
5413   else if (!FrameReached(&player->actual_frame_counter, 1))
5414     return;
5415
5416   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
5417   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
5418   player->Frame += 1;
5419
5420   if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
5421     Feld[last_jx][last_jy] = EL_EMPTY;
5422
5423   /* before DrawPlayer() to draw correct player graphic for this case */
5424   if (player->MovPos == 0)
5425     CheckGravityMovement(player);
5426
5427   DrawPlayer(player);
5428
5429   if (player->MovPos == 0)
5430   {
5431     if (IS_QUICK_GATE(Feld[last_jx][last_jy]))
5432     {
5433       /* continue with normal speed after quickly moving through gate */
5434       HALVE_PLAYER_SPEED(player);
5435
5436       /* be able to make the next move without delay */
5437       player->move_delay = 0;
5438     }
5439
5440     player->last_jx = jx;
5441     player->last_jy = jy;
5442
5443     if (Feld[jx][jy] == EL_EXIT_OPEN ||
5444         Feld[jx][jy] == EL_SP_EXIT_OPEN)
5445     {
5446       RemoveHero(player);
5447
5448       if (local_player->friends_still_needed == 0 ||
5449           Feld[jx][jy] == EL_SP_EXIT_OPEN)
5450         player->LevelSolved = player->GameOver = TRUE;
5451     }
5452
5453     if (tape.single_step && tape.recording && !tape.pausing &&
5454         !player->programmed_action)
5455       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
5456   }
5457 }
5458
5459 void ScrollScreen(struct PlayerInfo *player, int mode)
5460 {
5461   static unsigned long screen_frame_counter = 0;
5462
5463   if (mode == SCROLL_INIT)
5464   {
5465     /* set scrolling step size according to actual player's moving speed */
5466     ScrollStepSize = TILEX / player->move_delay_value;
5467
5468     screen_frame_counter = FrameCounter;
5469     ScreenMovDir = player->MovDir;
5470     ScreenMovPos = player->MovPos;
5471     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
5472     return;
5473   }
5474   else if (!FrameReached(&screen_frame_counter, 1))
5475     return;
5476
5477   if (ScreenMovPos)
5478   {
5479     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
5480     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
5481     redraw_mask |= REDRAW_FIELD;
5482   }
5483   else
5484     ScreenMovDir = MV_NO_MOVING;
5485 }
5486
5487 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
5488 {
5489   int i, kill_x = -1, kill_y = -1;
5490   static int test_xy[4][2] =
5491   {
5492     { 0, -1 },
5493     { -1, 0 },
5494     { +1, 0 },
5495     { 0, +1 }
5496   };
5497   static int test_dir[4] =
5498   {
5499     MV_UP,
5500     MV_LEFT,
5501     MV_RIGHT,
5502     MV_DOWN
5503   };
5504
5505   for (i=0; i<4; i++)
5506   {
5507     int test_x, test_y, test_move_dir, test_element;
5508
5509     test_x = good_x + test_xy[i][0];
5510     test_y = good_y + test_xy[i][1];
5511     if (!IN_LEV_FIELD(test_x, test_y))
5512       continue;
5513
5514     test_move_dir =
5515       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
5516
5517 #if 0
5518     test_element = Feld[test_x][test_y];
5519 #else
5520     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
5521 #endif
5522
5523     /* 1st case: good thing is moving towards DONT_GO_TO style bad thing;
5524        2nd case: DONT_TOUCH style bad thing does not move away from good thing
5525     */
5526     if ((DONT_GO_TO(test_element) && good_move_dir == test_dir[i]) ||
5527         (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
5528     {
5529       kill_x = test_x;
5530       kill_y = test_y;
5531       break;
5532     }
5533   }
5534
5535   if (kill_x != -1 || kill_y != -1)
5536   {
5537     if (IS_PLAYER(good_x, good_y))
5538     {
5539       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
5540
5541       if (player->shield_deadly_time_left > 0)
5542         Bang(kill_x, kill_y);
5543       else if (!PLAYER_PROTECTED(good_x, good_y))
5544         KillHero(player);
5545     }
5546     else
5547       Bang(good_x, good_y);
5548   }
5549 }
5550
5551 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
5552 {
5553   int i, kill_x = -1, kill_y = -1;
5554   int bad_element = Feld[bad_x][bad_y];
5555   static int test_xy[4][2] =
5556   {
5557     { 0, -1 },
5558     { -1, 0 },
5559     { +1, 0 },
5560     { 0, +1 }
5561   };
5562   static int test_dir[4] =
5563   {
5564     MV_UP,
5565     MV_LEFT,
5566     MV_RIGHT,
5567     MV_DOWN
5568   };
5569
5570   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
5571     return;
5572
5573   for (i=0; i<4; i++)
5574   {
5575     int test_x, test_y, test_move_dir, test_element;
5576
5577     test_x = bad_x + test_xy[i][0];
5578     test_y = bad_y + test_xy[i][1];
5579     if (!IN_LEV_FIELD(test_x, test_y))
5580       continue;
5581
5582     test_move_dir =
5583       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
5584
5585     test_element = Feld[test_x][test_y];
5586
5587     /* 1st case: good thing is moving towards DONT_GO_TO style bad thing;
5588        2nd case: DONT_TOUCH style bad thing does not move away from good thing
5589     */
5590     if ((DONT_GO_TO(bad_element) &&  bad_move_dir == test_dir[i]) ||
5591         (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
5592     {
5593       /* good thing is player or penguin that does not move away */
5594       if (IS_PLAYER(test_x, test_y))
5595       {
5596         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
5597
5598         if (bad_element == EL_ROBOT && player->is_moving)
5599           continue;     /* robot does not kill player if he is moving */
5600
5601         kill_x = test_x;
5602         kill_y = test_y;
5603         break;
5604       }
5605       else if (test_element == EL_PENGUIN)
5606       {
5607         kill_x = test_x;
5608         kill_y = test_y;
5609         break;
5610       }
5611     }
5612   }
5613
5614   if (kill_x != -1 || kill_y != -1)
5615   {
5616     if (IS_PLAYER(kill_x, kill_y))
5617     {
5618       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
5619
5620 #if 0
5621       int dir = player->MovDir;
5622       int newx = player->jx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5623       int newy = player->jy + (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
5624
5625       if (Feld[bad_x][bad_y] == EL_ROBOT && player->is_moving &&
5626           newx != bad_x && newy != bad_y)
5627         ;       /* robot does not kill player if he is moving */
5628       else
5629         printf("-> %d\n", player->MovDir);
5630
5631       if (Feld[bad_x][bad_y] == EL_ROBOT && player->is_moving &&
5632           newx != bad_x && newy != bad_y)
5633         ;       /* robot does not kill player if he is moving */
5634       else
5635         ;
5636 #endif
5637
5638       if (player->shield_deadly_time_left > 0)
5639         Bang(bad_x, bad_y);
5640       else if (!PLAYER_PROTECTED(kill_x, kill_y))
5641         KillHero(player);
5642     }
5643     else
5644       Bang(kill_x, kill_y);
5645   }
5646 }
5647
5648 void TestIfHeroTouchesBadThing(int x, int y)
5649 {
5650   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
5651 }
5652
5653 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
5654 {
5655   TestIfGoodThingHitsBadThing(x, y, move_dir);
5656 }
5657
5658 void TestIfBadThingTouchesHero(int x, int y)
5659 {
5660   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
5661 }
5662
5663 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
5664 {
5665   TestIfBadThingHitsGoodThing(x, y, move_dir);
5666 }
5667
5668 void TestIfFriendTouchesBadThing(int x, int y)
5669 {
5670   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
5671 }
5672
5673 void TestIfBadThingTouchesFriend(int x, int y)
5674 {
5675   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
5676 }
5677
5678 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
5679 {
5680   int i, kill_x = bad_x, kill_y = bad_y;
5681   static int xy[4][2] =
5682   {
5683     { 0, -1 },
5684     { -1, 0 },
5685     { +1, 0 },
5686     { 0, +1 }
5687   };
5688
5689   for (i=0; i<4; i++)
5690   {
5691     int x, y, element;
5692
5693     x = bad_x + xy[i][0];
5694     y = bad_y + xy[i][1];
5695     if (!IN_LEV_FIELD(x, y))
5696       continue;
5697
5698     element = Feld[x][y];
5699     if (IS_AMOEBOID(element) || element == EL_GAMEOFLIFE ||
5700         element == EL_AMOEBA_CREATING || element == EL_AMOEBA_DROP)
5701     {
5702       kill_x = x;
5703       kill_y = y;
5704       break;
5705     }
5706   }
5707
5708   if (kill_x != bad_x || kill_y != bad_y)
5709     Bang(bad_x, bad_y);
5710 }
5711
5712 void KillHero(struct PlayerInfo *player)
5713 {
5714   int jx = player->jx, jy = player->jy;
5715
5716   if (!player->active)
5717     return;
5718
5719   if (IS_PFORTE(Feld[jx][jy]))
5720     Feld[jx][jy] = EL_EMPTY;
5721
5722   /* deactivate shield (else Bang()/Explode() would not work right) */
5723   player->shield_normal_time_left = 0;
5724   player->shield_deadly_time_left = 0;
5725
5726   Bang(jx, jy);
5727   BuryHero(player);
5728 }
5729
5730 static void KillHeroUnlessProtected(int x, int y)
5731 {
5732   if (!PLAYER_PROTECTED(x, y))
5733     KillHero(PLAYERINFO(x, y));
5734 }
5735
5736 void BuryHero(struct PlayerInfo *player)
5737 {
5738   int jx = player->jx, jy = player->jy;
5739
5740   if (!player->active)
5741     return;
5742
5743   PlaySoundLevel(jx, jy, SND_PLAYER_DYING);
5744   PlaySoundLevel(jx, jy, SND_GAME_LOSING);
5745
5746   player->GameOver = TRUE;
5747   RemoveHero(player);
5748 }
5749
5750 void RemoveHero(struct PlayerInfo *player)
5751 {
5752   int jx = player->jx, jy = player->jy;
5753   int i, found = FALSE;
5754
5755   player->present = FALSE;
5756   player->active = FALSE;
5757
5758   if (!ExplodeField[jx][jy])
5759     StorePlayer[jx][jy] = 0;
5760
5761   for (i=0; i<MAX_PLAYERS; i++)
5762     if (stored_player[i].active)
5763       found = TRUE;
5764
5765   if (!found)
5766     AllPlayersGone = TRUE;
5767
5768   ExitX = ZX = jx;
5769   ExitY = ZY = jy;
5770 }
5771
5772 int DigField(struct PlayerInfo *player,
5773              int x, int y, int real_dx, int real_dy, int mode)
5774 {
5775   int jx = player->jx, jy = player->jy;
5776   int dx = x - jx, dy = y - jy;
5777   int move_direction = (dx == -1 ? MV_LEFT :
5778                         dx == +1 ? MV_RIGHT :
5779                         dy == -1 ? MV_UP :
5780                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
5781   int element;
5782
5783   if (player->MovPos == 0)
5784     player->Pushing = FALSE;
5785
5786   if (mode == DF_NO_PUSH)
5787   {
5788     player->Switching = FALSE;
5789     player->push_delay = 0;
5790     return MF_NO_ACTION;
5791   }
5792
5793   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
5794     return MF_NO_ACTION;
5795
5796   if (IS_TUBE(Feld[jx][jy]))
5797   {
5798     int i = 0;
5799     int tube_leave_directions[][2] =
5800     {
5801       { EL_TUBE_ALL,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
5802       { EL_TUBE_VERTICAL,                                    MV_UP | MV_DOWN },
5803       { EL_TUBE_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
5804       { EL_TUBE_VERTICAL_LEFT,          MV_LEFT |            MV_UP | MV_DOWN },
5805       { EL_TUBE_VERTICAL_RIGHT,                   MV_RIGHT | MV_UP | MV_DOWN },
5806       { EL_TUBE_HORIZONTAL_UP,          MV_LEFT | MV_RIGHT | MV_UP           },
5807       { EL_TUBE_HORIZONTAL_DOWN,        MV_LEFT | MV_RIGHT |         MV_DOWN },
5808       { EL_TUBE_LEFT_UP,                MV_LEFT |            MV_UP           },
5809       { EL_TUBE_LEFT_DOWN,              MV_LEFT |                    MV_DOWN },
5810       { EL_TUBE_RIGHT_UP,                         MV_RIGHT | MV_UP           },
5811       { EL_TUBE_RIGHT_DOWN,                       MV_RIGHT |         MV_DOWN },
5812       { -1,                             MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
5813     };
5814
5815     while (tube_leave_directions[i][0] != Feld[jx][jy])
5816     {
5817       i++;
5818       if (tube_leave_directions[i][0] == -1)    /* should not happen */
5819         break;
5820     }
5821
5822     if (!(tube_leave_directions[i][1] & move_direction))
5823       return MF_NO_ACTION;      /* tube has no opening in this direction */
5824   }
5825
5826   element = Feld[x][y];
5827
5828   switch (element)
5829   {
5830     case EL_EMPTY:
5831     case EL_SAND:
5832     case EL_INVISIBLE_SAND:
5833     case EL_INVISIBLE_SAND_ACTIVE:
5834     case EL_TRAP:
5835     case EL_SP_BASE:
5836     case EL_SP_BUGGY_BASE:
5837     case EL_SP_BUGGY_BASE_ACTIVATING:
5838       RemoveField(x, y);
5839       PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
5840       break;
5841
5842     case EL_EMERALD:
5843     case EL_BD_DIAMOND:
5844     case EL_EMERALD_YELLOW:
5845     case EL_EMERALD_RED:
5846     case EL_EMERALD_PURPLE:
5847     case EL_DIAMOND:
5848     case EL_SP_INFOTRON:
5849     case EL_PEARL:
5850     case EL_CRYSTAL:
5851       RemoveField(x, y);
5852       local_player->gems_still_needed -= (element == EL_DIAMOND ? 3 :
5853                                           element == EL_PEARL ? 5 :
5854                                           element == EL_CRYSTAL ? 8 : 1);
5855       if (local_player->gems_still_needed < 0)
5856         local_player->gems_still_needed = 0;
5857       RaiseScoreElement(element);
5858       DrawText(DX_EMERALDS, DY_EMERALDS,
5859                int2str(local_player->gems_still_needed, 3),
5860                FS_SMALL, FC_YELLOW);
5861       PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
5862       break;
5863
5864     case EL_SPEED_PILL:
5865       RemoveField(x, y);
5866       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
5867       PlaySoundLevel(x, y, SND_SPEED_PILL_COLLECTING);
5868       break;
5869
5870     case EL_ENVELOPE:
5871       Feld[x][y] = EL_EMPTY;
5872       PlaySoundLevel(x, y, SND_ENVELOPE_COLLECTING);
5873       break;
5874
5875     case EL_EXTRA_TIME:
5876       RemoveField(x, y);
5877       if (level.time > 0)
5878       {
5879         TimeLeft += 10;
5880         DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
5881       }
5882       PlaySoundStereo(SND_EXTRA_TIME_COLLECTING, SOUND_MAX_RIGHT);
5883       break;
5884
5885     case EL_SHIELD_NORMAL:
5886       RemoveField(x, y);
5887       player->shield_normal_time_left += 10;
5888       PlaySoundLevel(x, y, SND_SHIELD_NORMAL_COLLECTING);
5889       break;
5890
5891     case EL_SHIELD_DEADLY:
5892       RemoveField(x, y);
5893       player->shield_normal_time_left += 10;
5894       player->shield_deadly_time_left += 10;
5895       PlaySoundLevel(x, y, SND_SHIELD_DEADLY_COLLECTING);
5896       break;
5897
5898     case EL_DYNAMITE:
5899     case EL_SP_DISK_RED:
5900       RemoveField(x, y);
5901       player->dynamite++;
5902       RaiseScoreElement(EL_DYNAMITE);
5903       DrawText(DX_DYNAMITE, DY_DYNAMITE,
5904                int2str(local_player->dynamite, 3),
5905                FS_SMALL, FC_YELLOW);
5906       PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
5907       break;
5908
5909     case EL_DYNABOMB_NR:
5910       RemoveField(x, y);
5911       player->dynabomb_count++;
5912       player->dynabombs_left++;
5913       RaiseScoreElement(EL_DYNAMITE);
5914       PlaySoundLevel(x, y, SND_DYNABOMB_NR_COLLECTING);
5915       break;
5916
5917     case EL_DYNABOMB_SZ:
5918       RemoveField(x, y);
5919       player->dynabomb_size++;
5920       RaiseScoreElement(EL_DYNAMITE);
5921       PlaySoundLevel(x, y, SND_DYNABOMB_SZ_COLLECTING);
5922       break;
5923
5924     case EL_DYNABOMB_XL:
5925       RemoveField(x, y);
5926       player->dynabomb_xl = TRUE;
5927       RaiseScoreElement(EL_DYNAMITE);
5928       PlaySoundLevel(x, y, SND_DYNABOMB_XL_COLLECTING);
5929       break;
5930
5931     case EL_KEY1:
5932     case EL_KEY2:
5933     case EL_KEY3:
5934     case EL_KEY4:
5935     {
5936       int key_nr = element - EL_KEY1;
5937
5938       RemoveField(x, y);
5939       player->key[key_nr] = TRUE;
5940       RaiseScoreElement(element);
5941       DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
5942                          IMG_KEY1 + key_nr);
5943       DrawMiniGraphicExt(window, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
5944                          IMG_KEY1 + key_nr);
5945       PlaySoundLevel(x, y, SND_KEY_COLLECTING);
5946       break;
5947     }
5948
5949     case EL_EM_KEY1:
5950     case EL_EM_KEY2:
5951     case EL_EM_KEY3:
5952     case EL_EM_KEY4:
5953     {
5954       int key_nr = element - EL_EM_KEY1;
5955
5956       RemoveField(x, y);
5957       player->key[key_nr] = TRUE;
5958       RaiseScoreElement(element);
5959       DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
5960                          IMG_KEY1 + key_nr);
5961       DrawMiniGraphicExt(window, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
5962                          IMG_KEY1 + key_nr);
5963       PlaySoundLevel(x, y, SND_KEY_COLLECTING);
5964       break;
5965     }
5966
5967     case EL_ROBOT_WHEEL:
5968       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
5969       ZX = x;
5970       ZY = y;
5971       DrawLevelField(x, y);
5972       PlaySoundLevel(x, y, SND_ROBOT_WHEEL_ACTIVATING);
5973       return MF_ACTION;
5974       break;
5975
5976     case EL_SP_TERMINAL:
5977       {
5978         int xx, yy;
5979
5980         PlaySoundLevel(x, y, SND_SP_TERMINAL_ACTIVATING);
5981
5982         for (yy=0; yy<lev_fieldy; yy++)
5983         {
5984           for (xx=0; xx<lev_fieldx; xx++)
5985           {
5986             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
5987               Bang(xx, yy);
5988             else if (Feld[xx][yy] == EL_SP_TERMINAL)
5989               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
5990           }
5991         }
5992
5993         return MF_ACTION;
5994       }
5995       break;
5996
5997     case EL_CONVEYOR_BELT1_SWITCH_LEFT:
5998     case EL_CONVEYOR_BELT1_SWITCH_MIDDLE:
5999     case EL_CONVEYOR_BELT1_SWITCH_RIGHT:
6000     case EL_CONVEYOR_BELT2_SWITCH_LEFT:
6001     case EL_CONVEYOR_BELT2_SWITCH_MIDDLE:
6002     case EL_CONVEYOR_BELT2_SWITCH_RIGHT:
6003     case EL_CONVEYOR_BELT3_SWITCH_LEFT:
6004     case EL_CONVEYOR_BELT3_SWITCH_MIDDLE:
6005     case EL_CONVEYOR_BELT3_SWITCH_RIGHT:
6006     case EL_CONVEYOR_BELT4_SWITCH_LEFT:
6007     case EL_CONVEYOR_BELT4_SWITCH_MIDDLE:
6008     case EL_CONVEYOR_BELT4_SWITCH_RIGHT:
6009       if (!player->Switching)
6010       {
6011         player->Switching = TRUE;
6012         ToggleBeltSwitch(x, y);
6013         PlaySoundLevel(x, y, SND_CONVEYOR_BELT_SWITCH_ACTIVATING);
6014       }
6015       return MF_ACTION;
6016       break;
6017
6018     case EL_SWITCHGATE_SWITCH_UP:
6019     case EL_SWITCHGATE_SWITCH_DOWN:
6020       if (!player->Switching)
6021       {
6022         player->Switching = TRUE;
6023         ToggleSwitchgateSwitch(x, y);
6024         PlaySoundLevel(x, y, SND_SWITCHGATE_SWITCH_ACTIVATING);
6025       }
6026       return MF_ACTION;
6027       break;
6028
6029     case EL_LIGHT_SWITCH:
6030     case EL_LIGHT_SWITCH_ACTIVE:
6031       if (!player->Switching)
6032       {
6033         player->Switching = TRUE;
6034         ToggleLightSwitch(x, y);
6035         PlaySoundLevel(x, y, element == EL_LIGHT_SWITCH ?
6036                        SND_LIGHT_SWITCH_ACTIVATING :
6037                        SND_LIGHT_SWITCH_DEACTIVATING);
6038       }
6039       return MF_ACTION;
6040       break;
6041
6042     case EL_TIMEGATE_SWITCH:
6043       ActivateTimegateSwitch(x, y);
6044       PlaySoundLevel(x, y, SND_TIMEGATE_SWITCH_ACTIVATING);
6045
6046       return MF_ACTION;
6047       break;
6048
6049     case EL_BALLOON_SEND_LEFT:
6050     case EL_BALLOON_SEND_RIGHT:
6051     case EL_BALLOON_SEND_UP:
6052     case EL_BALLOON_SEND_DOWN:
6053     case EL_BALLOON_SEND_ANY_DIRECTION:
6054       if (element == EL_BALLOON_SEND_ANY_DIRECTION)
6055         game.balloon_dir = move_direction;
6056       else
6057         game.balloon_dir = (element == EL_BALLOON_SEND_LEFT  ? MV_LEFT :
6058                             element == EL_BALLOON_SEND_RIGHT ? MV_RIGHT :
6059                             element == EL_BALLOON_SEND_UP    ? MV_UP :
6060                             element == EL_BALLOON_SEND_DOWN  ? MV_DOWN :
6061                             MV_NO_MOVING);
6062       PlaySoundLevel(x, y, SND_BALLOON_SWITCH_ACTIVATING);
6063
6064       return MF_ACTION;
6065       break;
6066
6067       /* the following elements cannot be pushed by "snapping" */
6068     case EL_ROCK:
6069     case EL_BOMB:
6070     case EL_DX_SUPABOMB:
6071     case EL_NUT:
6072     case EL_TIME_ORB_EMPTY:
6073     case EL_SP_ZONK:
6074     case EL_SP_DISK_ORANGE:
6075     case EL_SPRING:
6076       if (mode == DF_SNAP)
6077         return MF_NO_ACTION;
6078
6079       /* no "break" -- fall through to next case */
6080
6081       /* the following elements can be pushed by "snapping" */
6082     case EL_BD_ROCK:
6083       if (dy)
6084         return MF_NO_ACTION;
6085
6086       player->Pushing = TRUE;
6087
6088       if (!IN_LEV_FIELD(x+dx, y+dy) || !IS_FREE(x+dx, y+dy))
6089         return MF_NO_ACTION;
6090
6091       if (real_dy)
6092       {
6093         if (IN_LEV_FIELD(jx, jy+real_dy) && !IS_SOLID(Feld[jx][jy+real_dy]))
6094           return MF_NO_ACTION;
6095       }
6096
6097       if (player->push_delay == 0)
6098         player->push_delay = FrameCounter;
6099 #if 0
6100       if (!FrameReached(&player->push_delay, player->push_delay_value) &&
6101           !tape.playing &&
6102           element != EL_SPRING)
6103         return MF_NO_ACTION;
6104 #else
6105       if (!FrameReached(&player->push_delay, player->push_delay_value) &&
6106           !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
6107           element != EL_SPRING)
6108         return MF_NO_ACTION;
6109 #endif
6110
6111       if (mode == DF_SNAP)
6112       {
6113         InitMovingField(x, y, move_direction);
6114         ContinueMoving(x, y);
6115       }
6116       else
6117       {
6118         RemoveField(x, y);
6119         Feld[x + dx][y + dy] = element;
6120       }
6121
6122       if (element == EL_SPRING)
6123       {
6124         Feld[x + dx][y + dy] = EL_SPRING;
6125         MovDir[x + dx][y + dy] = move_direction;
6126       }
6127
6128       player->push_delay_value = (element == EL_SPRING ? 0 : 2 + RND(8));
6129
6130       DrawLevelField(x + dx, y + dy);
6131       PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
6132       break;
6133
6134     case EL_GATE1:
6135     case EL_GATE2:
6136     case EL_GATE3:
6137     case EL_GATE4:
6138       if (!player->key[element - EL_GATE1])
6139         return MF_NO_ACTION;
6140       break;
6141
6142     case EL_GATE1_GRAY:
6143     case EL_GATE2_GRAY:
6144     case EL_GATE3_GRAY:
6145     case EL_GATE4_GRAY:
6146       if (!player->key[element - EL_GATE1_GRAY])
6147         return MF_NO_ACTION;
6148       break;
6149
6150     case EL_EM_GATE1:
6151     case EL_EM_GATE2:
6152     case EL_EM_GATE3:
6153     case EL_EM_GATE4:
6154       if (!player->key[element - EL_EM_GATE1])
6155         return MF_NO_ACTION;
6156       if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
6157         return MF_NO_ACTION;
6158
6159       /* automatically move to the next field with double speed */
6160       player->programmed_action = move_direction;
6161       DOUBLE_PLAYER_SPEED(player);
6162
6163       PlaySoundLevel(x, y, SND_GATE_PASSING);
6164       break;
6165
6166     case EL_EM_GATE1_GRAY:
6167     case EL_EM_GATE2_GRAY:
6168     case EL_EM_GATE3_GRAY:
6169     case EL_EM_GATE4_GRAY:
6170       if (!player->key[element - EL_EM_GATE1_GRAY])
6171         return MF_NO_ACTION;
6172       if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
6173         return MF_NO_ACTION;
6174
6175       /* automatically move to the next field with double speed */
6176       player->programmed_action = move_direction;
6177       DOUBLE_PLAYER_SPEED(player);
6178
6179       PlaySoundLevel(x, y, SND_GATE_PASSING);
6180       break;
6181
6182     case EL_SWITCHGATE_OPEN:
6183     case EL_TIMEGATE_OPEN:
6184       if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
6185         return MF_NO_ACTION;
6186
6187       /* automatically move to the next field with double speed */
6188       player->programmed_action = move_direction;
6189       DOUBLE_PLAYER_SPEED(player);
6190
6191       PlaySoundLevelElementAction(x, y, element, ACTION_PASSING);
6192       break;
6193
6194     case EL_SP_PORT1_LEFT:
6195     case EL_SP_PORT2_LEFT:
6196     case EL_SP_PORT1_RIGHT:
6197     case EL_SP_PORT2_RIGHT:
6198     case EL_SP_PORT1_UP:
6199     case EL_SP_PORT2_UP:
6200     case EL_SP_PORT1_DOWN:
6201     case EL_SP_PORT2_DOWN:
6202     case EL_SP_PORT_X:
6203     case EL_SP_PORT_Y:
6204     case EL_SP_PORT_XY:
6205       if ((dx == -1 &&
6206            element != EL_SP_PORT1_LEFT &&
6207            element != EL_SP_PORT2_LEFT &&
6208            element != EL_SP_PORT_X &&
6209            element != EL_SP_PORT_XY) ||
6210           (dx == +1 &&
6211            element != EL_SP_PORT1_RIGHT &&
6212            element != EL_SP_PORT2_RIGHT &&
6213            element != EL_SP_PORT_X &&
6214            element != EL_SP_PORT_XY) ||
6215           (dy == -1 &&
6216            element != EL_SP_PORT1_UP &&
6217            element != EL_SP_PORT2_UP &&
6218            element != EL_SP_PORT_Y &&
6219            element != EL_SP_PORT_XY) ||
6220           (dy == +1 &&
6221            element != EL_SP_PORT1_DOWN &&
6222            element != EL_SP_PORT2_DOWN &&
6223            element != EL_SP_PORT_Y &&
6224            element != EL_SP_PORT_XY) ||
6225           !IN_LEV_FIELD(x + dx, y + dy) ||
6226           !IS_FREE(x + dx, y + dy))
6227         return MF_NO_ACTION;
6228
6229       /* automatically move to the next field with double speed */
6230       player->programmed_action = move_direction;
6231       DOUBLE_PLAYER_SPEED(player);
6232
6233       PlaySoundLevel(x, y, SND_SP_PORT_PASSING);
6234       break;
6235
6236     case EL_TUBE_ALL:
6237     case EL_TUBE_VERTICAL:
6238     case EL_TUBE_HORIZONTAL:
6239     case EL_TUBE_VERTICAL_LEFT:
6240     case EL_TUBE_VERTICAL_RIGHT:
6241     case EL_TUBE_HORIZONTAL_UP:
6242     case EL_TUBE_HORIZONTAL_DOWN:
6243     case EL_TUBE_LEFT_UP:
6244     case EL_TUBE_LEFT_DOWN:
6245     case EL_TUBE_RIGHT_UP:
6246     case EL_TUBE_RIGHT_DOWN:
6247       {
6248         int i = 0;
6249         int tube_enter_directions[][2] =
6250         {
6251           { EL_TUBE_ALL,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
6252           { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
6253           { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
6254           { EL_TUBE_VERTICAL_LEFT,                MV_RIGHT | MV_UP | MV_DOWN },
6255           { EL_TUBE_VERTICAL_RIGHT,     MV_LEFT            | MV_UP | MV_DOWN },
6256           { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT |         MV_DOWN },
6257           { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT | MV_UP           },
6258           { EL_TUBE_LEFT_UP,                      MV_RIGHT |         MV_DOWN },
6259           { EL_TUBE_LEFT_DOWN,                    MV_RIGHT | MV_UP           },
6260           { EL_TUBE_RIGHT_UP,           MV_LEFT |                    MV_DOWN },
6261           { EL_TUBE_RIGHT_DOWN,         MV_LEFT |            MV_UP           },
6262           { -1,                         MV_NO_MOVING                         }
6263         };
6264
6265         while (tube_enter_directions[i][0] != element)
6266         {
6267           i++;
6268           if (tube_enter_directions[i][0] == -1)        /* should not happen */
6269             break;
6270         }
6271
6272         if (!(tube_enter_directions[i][1] & move_direction))
6273           return MF_NO_ACTION;  /* tube has no opening in this direction */
6274
6275         PlaySoundLevel(x, y, SND_TUBE_PASSING);
6276       }
6277       break;
6278
6279     case EL_EXIT_CLOSED:
6280     case EL_SP_EXIT_CLOSED:
6281     case EL_EXIT_OPENING:
6282       return MF_NO_ACTION;
6283       break;
6284
6285     case EL_EXIT_OPEN:
6286     case EL_SP_EXIT_OPEN:
6287       if (mode == DF_SNAP)
6288         return MF_NO_ACTION;
6289
6290       if (element == EL_EXIT_OPEN)
6291         PlaySoundLevel(x, y, SND_EXIT_PASSING);
6292       else
6293         PlaySoundLevel(x, y, SND_SP_EXIT_PASSING);
6294
6295       break;
6296
6297     case EL_LAMP:
6298       Feld[x][y] = EL_LAMP_ACTIVE;
6299       local_player->lights_still_needed--;
6300       DrawLevelField(x, y);
6301       PlaySoundLevel(x, y, SND_LAMP_ACTIVATING);
6302       return MF_ACTION;
6303       break;
6304
6305     case EL_TIME_ORB_FULL:
6306       Feld[x][y] = EL_TIME_ORB_EMPTY;
6307       TimeLeft += 10;
6308       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
6309       DrawLevelField(x, y);
6310       PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MAX_RIGHT);
6311       return MF_ACTION;
6312       break;
6313
6314     case EL_SOKOBAN_FIELD_EMPTY:
6315       break;
6316
6317     case EL_SOKOBAN_OBJECT:
6318     case EL_SOKOBAN_FIELD_FULL:
6319     case EL_SATELLITE:
6320     case EL_SP_DISK_YELLOW:
6321     case EL_BALLOON:
6322       if (mode == DF_SNAP)
6323         return MF_NO_ACTION;
6324
6325       player->Pushing = TRUE;
6326
6327       if (!IN_LEV_FIELD(x+dx, y+dy)
6328           || (!IS_FREE(x+dx, y+dy)
6329               && (Feld[x+dx][y+dy] != EL_SOKOBAN_FIELD_EMPTY
6330                   || !IS_SB_ELEMENT(element))))
6331         return MF_NO_ACTION;
6332
6333       if (dx && real_dy)
6334       {
6335         if (IN_LEV_FIELD(jx, jy+real_dy) && !IS_SOLID(Feld[jx][jy+real_dy]))
6336           return MF_NO_ACTION;
6337       }
6338       else if (dy && real_dx)
6339       {
6340         if (IN_LEV_FIELD(jx+real_dx, jy) && !IS_SOLID(Feld[jx+real_dx][jy]))
6341           return MF_NO_ACTION;
6342       }
6343
6344       if (player->push_delay == 0)
6345         player->push_delay = FrameCounter;
6346 #if 0
6347       if (!FrameReached(&player->push_delay, player->push_delay_value) &&
6348           !tape.playing && element != EL_BALLOON)
6349         return MF_NO_ACTION;
6350 #else
6351       if (!FrameReached(&player->push_delay, player->push_delay_value) &&
6352           !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
6353           element != EL_BALLOON)
6354         return MF_NO_ACTION;
6355 #endif
6356
6357       if (IS_SB_ELEMENT(element))
6358       {
6359         if (element == EL_SOKOBAN_FIELD_FULL)
6360         {
6361           Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
6362           local_player->sokobanfields_still_needed++;
6363         }
6364         else
6365           RemoveField(x, y);
6366
6367         if (Feld[x+dx][y+dy] == EL_SOKOBAN_FIELD_EMPTY)
6368         {
6369           Feld[x+dx][y+dy] = EL_SOKOBAN_FIELD_FULL;
6370           local_player->sokobanfields_still_needed--;
6371           if (element == EL_SOKOBAN_OBJECT)
6372             PlaySoundLevel(x, y, SND_SOKOBAN_FIELD_FILLING);
6373           else
6374             PlaySoundLevel(x, y, SND_SOKOBAN_OBJECT_PUSHING);
6375         }
6376         else
6377         {
6378           Feld[x+dx][y+dy] = EL_SOKOBAN_OBJECT;
6379           if (element == EL_SOKOBAN_FIELD_FULL)
6380             PlaySoundLevel(x, y, SND_SOKOBAN_FIELD_EMPTYING);
6381           else
6382             PlaySoundLevel(x, y, SND_SOKOBAN_OBJECT_PUSHING);
6383         }
6384       }
6385       else
6386       {
6387         RemoveField(x, y);
6388         Feld[x+dx][y+dy] = element;
6389         PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
6390       }
6391
6392       player->push_delay_value = (element == EL_BALLOON ? 0 : 2);
6393
6394       DrawLevelField(x, y);
6395       DrawLevelField(x + dx, y + dy);
6396
6397       if (IS_SB_ELEMENT(element) &&
6398           local_player->sokobanfields_still_needed == 0 &&
6399           game.emulation == EMU_SOKOBAN)
6400       {
6401         player->LevelSolved = player->GameOver = TRUE;
6402         PlaySoundLevel(x, y, SND_SOKOBAN_GAME_SOLVING);
6403       }
6404
6405       break;
6406
6407     case EL_PENGUIN:
6408     case EL_PIG:
6409     case EL_DRAGON:
6410       break;
6411
6412     default:
6413       if (IS_PUSHABLE(element))
6414       {
6415         if (mode == DF_SNAP)
6416           return MF_NO_ACTION;
6417
6418         if (CAN_FALL(element) && dy)
6419           return MF_NO_ACTION;
6420
6421         player->Pushing = TRUE;
6422
6423         if (!IN_LEV_FIELD(x+dx, y+dy) || !IS_FREE(x+dx, y+dy))
6424           return MF_NO_ACTION;
6425
6426         if (dx && real_dy)
6427         {
6428           if (IN_LEV_FIELD(jx, jy+real_dy) && !IS_SOLID(Feld[jx][jy+real_dy]))
6429             return MF_NO_ACTION;
6430         }
6431         else if (dy && real_dx)
6432         {
6433           if (IN_LEV_FIELD(jx+real_dx, jy) && !IS_SOLID(Feld[jx+real_dx][jy]))
6434             return MF_NO_ACTION;
6435         }
6436
6437         if (player->push_delay == 0)
6438           player->push_delay = FrameCounter;
6439
6440         if (!FrameReached(&player->push_delay, player->push_delay_value) &&
6441             !(tape.playing && tape.file_version < FILE_VERSION_2_0))
6442           return MF_NO_ACTION;
6443
6444         RemoveField(x, y);
6445         Feld[x + dx][y + dy] = element;
6446
6447         player->push_delay_value = 2 + RND(8);
6448
6449         DrawLevelField(x + dx, y + dy);
6450         PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
6451
6452         break;
6453       }
6454
6455       return MF_NO_ACTION;
6456   }
6457
6458   player->push_delay = 0;
6459
6460   return MF_MOVING;
6461 }
6462
6463 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
6464 {
6465   int jx = player->jx, jy = player->jy;
6466   int x = jx + dx, y = jy + dy;
6467
6468   if (!player->active || !IN_LEV_FIELD(x, y))
6469     return FALSE;
6470
6471   if (dx && dy)
6472     return FALSE;
6473
6474   if (!dx && !dy)
6475   {
6476     if (player->MovPos == 0)
6477       player->Pushing = FALSE;
6478
6479     player->snapped = FALSE;
6480     return FALSE;
6481   }
6482
6483   if (player->snapped)
6484     return FALSE;
6485
6486   player->MovDir = (dx < 0 ? MV_LEFT :
6487                     dx > 0 ? MV_RIGHT :
6488                     dy < 0 ? MV_UP :
6489                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
6490
6491   if (!DigField(player, x, y, 0, 0, DF_SNAP))
6492     return FALSE;
6493
6494   player->snapped = TRUE;
6495   DrawLevelField(x, y);
6496   BackToFront();
6497
6498   return TRUE;
6499 }
6500
6501 boolean PlaceBomb(struct PlayerInfo *player)
6502 {
6503   int jx = player->jx, jy = player->jy;
6504   int element;
6505
6506   if (!player->active || player->MovPos)
6507     return FALSE;
6508
6509   element = Feld[jx][jy];
6510
6511   if ((player->dynamite == 0 && player->dynabombs_left == 0) ||
6512       IS_ACTIVE_BOMB(element) || element == EL_EXPLOSION)
6513     return FALSE;
6514
6515   if (element != EL_EMPTY)
6516     Store[jx][jy] = element;
6517
6518   MovDelay[jx][jy] = 96;
6519
6520   ResetGfxAnimation(jx, jy);
6521   ResetRandomAnimationValue(jx, jy);
6522
6523   if (player->dynamite)
6524   {
6525     Feld[jx][jy] = EL_DYNAMITE_ACTIVE;
6526     player->dynamite--;
6527
6528     DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(local_player->dynamite, 3),
6529              FS_SMALL, FC_YELLOW);
6530     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
6531     {
6532       if (game.emulation == EMU_SUPAPLEX)
6533         DrawGraphic(SCREENX(jx), SCREENY(jy), IMG_SP_DISK_RED, 0);
6534       else
6535         DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), IMG_DYNAMITE_ACTIVE, 0);
6536     }
6537
6538     PlaySoundLevel(jx, jy, SND_DYNAMITE_DROPPING);
6539   }
6540   else
6541   {
6542     Feld[jx][jy] =
6543       EL_DYNABOMB_PLAYER1_ACTIVE + (player->element_nr - EL_PLAYER1);
6544     player->dynabombs_left--;
6545
6546     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
6547       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
6548
6549     PlaySoundLevel(jx, jy, SND_DYNABOMB_DROPPING);
6550   }
6551
6552   return TRUE;
6553 }
6554
6555 /* ------------------------------------------------------------------------- */
6556 /* game sound playing functions                                              */
6557 /* ------------------------------------------------------------------------- */
6558
6559 static int *loop_sound_frame = NULL;
6560 static int *loop_sound_volume = NULL;
6561
6562 void InitPlaySoundLevel()
6563 {
6564   int num_sounds = getSoundListSize();
6565
6566   if (loop_sound_frame != NULL)
6567     free(loop_sound_frame);
6568
6569   if (loop_sound_volume != NULL)
6570     free(loop_sound_volume);
6571
6572   loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
6573   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
6574 }
6575
6576 static void PlaySoundLevel(int x, int y, int nr)
6577 {
6578   int sx = SCREENX(x), sy = SCREENY(y);
6579   int volume, stereo_position;
6580   int max_distance = 8;
6581   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
6582
6583   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
6584       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
6585     return;
6586
6587   if (!IN_LEV_FIELD(x, y) ||
6588       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
6589       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
6590     return;
6591
6592   volume = SOUND_MAX_VOLUME;
6593
6594   if (!IN_SCR_FIELD(sx, sy))
6595   {
6596     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
6597     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
6598
6599     volume -= volume * (dx > dy ? dx : dy) / max_distance;
6600   }
6601
6602   stereo_position = (SOUND_MAX_LEFT +
6603                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
6604                      (SCR_FIELDX + 2 * max_distance));
6605
6606   if (IS_LOOP_SOUND(nr))
6607   {
6608     /* This assures that quieter loop sounds do not overwrite louder ones,
6609        while restarting sound volume comparison with each new game frame. */
6610
6611     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
6612       return;
6613
6614     loop_sound_volume[nr] = volume;
6615     loop_sound_frame[nr] = FrameCounter;
6616   }
6617
6618   PlaySoundExt(nr, volume, stereo_position, type);
6619 }
6620
6621 static void PlaySoundLevelNearest(int x, int y, int sound_action)
6622 {
6623   PlaySoundLevel(x < LEVELX(BX1) ? LEVELX(BX1) :
6624                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
6625                  y < LEVELY(BY1) ? LEVELY(BY1) :
6626                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
6627                  sound_action);
6628 }
6629
6630 static void PlaySoundLevelAction(int x, int y, int sound_action)
6631 {
6632   PlaySoundLevelElementAction(x, y, Feld[x][y], sound_action);
6633 }
6634
6635 static void PlaySoundLevelElementAction(int x, int y, int element,
6636                                         int sound_action)
6637 {
6638   int sound_effect = element_info[element].sound[sound_action];
6639
6640   if (sound_effect != SND_UNDEFINED)
6641     PlaySoundLevel(x, y, sound_effect);
6642 }
6643
6644 void RaiseScore(int value)
6645 {
6646   local_player->score += value;
6647   DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5),
6648            FS_SMALL, FC_YELLOW);
6649 }
6650
6651 void RaiseScoreElement(int element)
6652 {
6653   switch(element)
6654   {
6655     case EL_EMERALD:
6656     case EL_BD_DIAMOND:
6657     case EL_EMERALD_YELLOW:
6658     case EL_EMERALD_RED:
6659     case EL_EMERALD_PURPLE:
6660       RaiseScore(level.score[SC_EDELSTEIN]);
6661       break;
6662     case EL_DIAMOND:
6663       RaiseScore(level.score[SC_DIAMANT]);
6664       break;
6665     case EL_BUG:
6666     case EL_BD_BUTTERFLY:
6667       RaiseScore(level.score[SC_KAEFER]);
6668       break;
6669     case EL_SPACESHIP:
6670     case EL_BD_FIREFLY:
6671       RaiseScore(level.score[SC_FLIEGER]);
6672       break;
6673     case EL_YAMYAM:
6674     case EL_DARK_YAMYAM:
6675       RaiseScore(level.score[SC_MAMPFER]);
6676       break;
6677     case EL_ROBOT:
6678       RaiseScore(level.score[SC_ROBOT]);
6679       break;
6680     case EL_PACMAN:
6681       RaiseScore(level.score[SC_PACMAN]);
6682       break;
6683     case EL_NUT:
6684       RaiseScore(level.score[SC_KOKOSNUSS]);
6685       break;
6686     case EL_DYNAMITE:
6687       RaiseScore(level.score[SC_DYNAMIT]);
6688       break;
6689     case EL_KEY1:
6690     case EL_KEY2:
6691     case EL_KEY3:
6692     case EL_KEY4:
6693       RaiseScore(level.score[SC_SCHLUESSEL]);
6694       break;
6695     default:
6696       break;
6697   }
6698 }
6699
6700 void RequestQuitGame(boolean ask_if_really_quit)
6701 {
6702   if (AllPlayersGone ||
6703       !ask_if_really_quit ||
6704       level_editor_test_game ||
6705       Request("Do you really want to quit the game ?",
6706               REQ_ASK | REQ_STAY_CLOSED))
6707   {
6708 #if defined(PLATFORM_UNIX)
6709     if (options.network)
6710       SendToServer_StopPlaying();
6711     else
6712 #endif
6713     {
6714       game_status = MAINMENU;
6715       DrawMainMenu();
6716     }
6717   }
6718   else
6719   {
6720     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
6721   }
6722 }
6723
6724
6725 /* ---------- new game button stuff ---------------------------------------- */
6726
6727 /* graphic position values for game buttons */
6728 #define GAME_BUTTON_XSIZE       30
6729 #define GAME_BUTTON_YSIZE       30
6730 #define GAME_BUTTON_XPOS        5
6731 #define GAME_BUTTON_YPOS        215
6732 #define SOUND_BUTTON_XPOS       5
6733 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
6734
6735 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
6736 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
6737 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
6738 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
6739 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
6740 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
6741
6742 static struct
6743 {
6744   int x, y;
6745   int gadget_id;
6746   char *infotext;
6747 } gamebutton_info[NUM_GAME_BUTTONS] =
6748 {
6749   {
6750     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
6751     GAME_CTRL_ID_STOP,
6752     "stop game"
6753   },
6754   {
6755     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
6756     GAME_CTRL_ID_PAUSE,
6757     "pause game"
6758   },
6759   {
6760     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
6761     GAME_CTRL_ID_PLAY,
6762     "play game"
6763   },
6764   {
6765     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
6766     SOUND_CTRL_ID_MUSIC,
6767     "background music on/off"
6768   },
6769   {
6770     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
6771     SOUND_CTRL_ID_LOOPS,
6772     "sound loops on/off"
6773   },
6774   {
6775     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
6776     SOUND_CTRL_ID_SIMPLE,
6777     "normal sounds on/off"
6778   }
6779 };
6780
6781 void CreateGameButtons()
6782 {
6783   int i;
6784
6785   for (i=0; i<NUM_GAME_BUTTONS; i++)
6786   {
6787     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
6788     struct GadgetInfo *gi;
6789     int button_type;
6790     boolean checked;
6791     unsigned long event_mask;
6792     int gd_xoffset, gd_yoffset;
6793     int gd_x1, gd_x2, gd_y1, gd_y2;
6794     int id = i;
6795
6796     gd_xoffset = gamebutton_info[i].x;
6797     gd_yoffset = gamebutton_info[i].y;
6798     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
6799     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
6800
6801     if (id == GAME_CTRL_ID_STOP ||
6802         id == GAME_CTRL_ID_PAUSE ||
6803         id == GAME_CTRL_ID_PLAY)
6804     {
6805       button_type = GD_TYPE_NORMAL_BUTTON;
6806       checked = FALSE;
6807       event_mask = GD_EVENT_RELEASED;
6808       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
6809       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
6810     }
6811     else
6812     {
6813       button_type = GD_TYPE_CHECK_BUTTON;
6814       checked =
6815         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
6816          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
6817          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
6818       event_mask = GD_EVENT_PRESSED;
6819       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
6820       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
6821     }
6822
6823     gi = CreateGadget(GDI_CUSTOM_ID, id,
6824                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
6825                       GDI_X, DX + gd_xoffset,
6826                       GDI_Y, DY + gd_yoffset,
6827                       GDI_WIDTH, GAME_BUTTON_XSIZE,
6828                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
6829                       GDI_TYPE, button_type,
6830                       GDI_STATE, GD_BUTTON_UNPRESSED,
6831                       GDI_CHECKED, checked,
6832                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
6833                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
6834                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
6835                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
6836                       GDI_EVENT_MASK, event_mask,
6837                       GDI_CALLBACK_ACTION, HandleGameButtons,
6838                       GDI_END);
6839
6840     if (gi == NULL)
6841       Error(ERR_EXIT, "cannot create gadget");
6842
6843     game_gadget[id] = gi;
6844   }
6845 }
6846
6847 void FreeGameButtons()
6848 {
6849   int i;
6850
6851   for (i=0; i<NUM_GAME_BUTTONS; i++)
6852     FreeGadget(game_gadget[i]);
6853 }
6854
6855 static void MapGameButtons()
6856 {
6857   int i;
6858
6859   for (i=0; i<NUM_GAME_BUTTONS; i++)
6860     MapGadget(game_gadget[i]);
6861 }
6862
6863 void UnmapGameButtons()
6864 {
6865   int i;
6866
6867   for (i=0; i<NUM_GAME_BUTTONS; i++)
6868     UnmapGadget(game_gadget[i]);
6869 }
6870
6871 static void HandleGameButtons(struct GadgetInfo *gi)
6872 {
6873   int id = gi->custom_id;
6874
6875   if (game_status != PLAYING)
6876     return;
6877
6878   switch (id)
6879   {
6880     case GAME_CTRL_ID_STOP:
6881       RequestQuitGame(TRUE);
6882       break;
6883
6884     case GAME_CTRL_ID_PAUSE:
6885       if (options.network)
6886       {
6887 #if defined(PLATFORM_UNIX)
6888         if (tape.pausing)
6889           SendToServer_ContinuePlaying();
6890         else
6891           SendToServer_PausePlaying();
6892 #endif
6893       }
6894       else
6895         TapeTogglePause(TAPE_TOGGLE_MANUAL);
6896       break;
6897
6898     case GAME_CTRL_ID_PLAY:
6899       if (tape.pausing)
6900       {
6901 #if defined(PLATFORM_UNIX)
6902         if (options.network)
6903           SendToServer_ContinuePlaying();
6904         else
6905 #endif
6906         {
6907           tape.pausing = FALSE;
6908           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
6909         }
6910       }
6911       break;
6912
6913     case SOUND_CTRL_ID_MUSIC:
6914       if (setup.sound_music)
6915       { 
6916         setup.sound_music = FALSE;
6917         FadeMusic();
6918       }
6919       else if (audio.music_available)
6920       { 
6921         setup.sound = setup.sound_music = TRUE;
6922         PlayMusic(level_nr);
6923       }
6924       break;
6925
6926     case SOUND_CTRL_ID_LOOPS:
6927       if (setup.sound_loops)
6928         setup.sound_loops = FALSE;
6929       else if (audio.loops_available)
6930         setup.sound = setup.sound_loops = TRUE;
6931       break;
6932
6933     case SOUND_CTRL_ID_SIMPLE:
6934       if (setup.sound_simple)
6935         setup.sound_simple = FALSE;
6936       else if (audio.sound_available)
6937         setup.sound = setup.sound_simple = TRUE;
6938       break;
6939
6940     default:
6941       break;
6942   }
6943 }