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