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