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