rnd-20021117-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 graphic = IMG_EXPLOSION;
1712     int frame = (phase / delay - 1);
1713
1714     if (game.emulation == EMU_SUPAPLEX)
1715       graphic = (Store[x][y] == EL_SP_INFOTRON ?
1716                  IMG_SP_EXPLOSION_INFOTRON :
1717                  IMG_SP_EXPLOSION);
1718
1719     if (phase == delay)
1720       DrawCrumbledSand(SCREENX(x), SCREENY(y));
1721
1722     if (IS_PFORTE(Store[x][y]))
1723     {
1724       DrawLevelElement(x, y, Store[x][y]);
1725       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
1726     }
1727     else
1728       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
1729   }
1730 }
1731
1732 void DynaExplode(int ex, int ey)
1733 {
1734   int i, j;
1735   int dynabomb_size = 1;
1736   boolean dynabomb_xl = FALSE;
1737   struct PlayerInfo *player;
1738   static int xy[4][2] =
1739   {
1740     { 0, -1 },
1741     { -1, 0 },
1742     { +1, 0 },
1743     { 0, +1 }
1744   };
1745
1746   if (IS_ACTIVE_BOMB(Feld[ex][ey]))
1747   {
1748     player = &stored_player[Feld[ex][ey] - EL_DYNABOMB_PLAYER1_ACTIVE];
1749     dynabomb_size = player->dynabomb_size;
1750     dynabomb_xl = player->dynabomb_xl;
1751     player->dynabombs_left++;
1752   }
1753
1754   Explode(ex, ey, EX_PHASE_START, EX_CENTER);
1755
1756   for (i=0; i<4; i++)
1757   {
1758     for (j=1; j<=dynabomb_size; j++)
1759     {
1760       int x = ex + j * xy[i % 4][0];
1761       int y = ey + j * xy[i % 4][1];
1762       int element;
1763
1764       if (!IN_LEV_FIELD(x, y) || IS_MASSIVE(Feld[x][y]))
1765         break;
1766
1767       element = Feld[x][y];
1768
1769       /* do not restart explosions of fields with active bombs */
1770       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
1771         continue;
1772
1773       Explode(x, y, EX_PHASE_START, EX_BORDER);
1774
1775       if (element != EL_EMPTY &&
1776           element != EL_SAND &&
1777           element != EL_EXPLOSION &&
1778           !dynabomb_xl)
1779         break;
1780     }
1781   }
1782 }
1783
1784 void Bang(int x, int y)
1785 {
1786   int element = Feld[x][y];
1787
1788   if (game.emulation == EMU_SUPAPLEX)
1789     PlaySoundLevel(x, y, SND_SP_ELEMENT_EXPLODING);
1790   else
1791     PlaySoundLevel(x, y, SND_ELEMENT_EXPLODING);
1792
1793 #if 0
1794   if (IS_PLAYER(x, y))  /* remove objects that might cause smaller explosion */
1795     element = EL_EMPTY;
1796 #endif
1797
1798   switch(element)
1799   {
1800     case EL_BUG:
1801     case EL_SPACESHIP:
1802     case EL_BD_BUTTERFLY:
1803     case EL_BD_FIREFLY:
1804     case EL_YAMYAM:
1805     case EL_DARK_YAMYAM:
1806     case EL_ROBOT:
1807     case EL_PACMAN:
1808     case EL_MOLE:
1809       RaiseScoreElement(element);
1810       Explode(x, y, EX_PHASE_START, EX_NORMAL);
1811       break;
1812     case EL_DYNABOMB_PLAYER1_ACTIVE:
1813     case EL_DYNABOMB_PLAYER2_ACTIVE:
1814     case EL_DYNABOMB_PLAYER3_ACTIVE:
1815     case EL_DYNABOMB_PLAYER4_ACTIVE:
1816     case EL_DYNABOMB_NR:
1817     case EL_DYNABOMB_SZ:
1818     case EL_DYNABOMB_XL:
1819       DynaExplode(x, y);
1820       break;
1821     case EL_PENGUIN:
1822     case EL_LAMP:
1823     case EL_LAMP_ACTIVE:
1824       if (IS_PLAYER(x, y))
1825         Explode(x, y, EX_PHASE_START, EX_NORMAL);
1826       else
1827         Explode(x, y, EX_PHASE_START, EX_CENTER);
1828       break;
1829     default:
1830       Explode(x, y, EX_PHASE_START, EX_NORMAL);
1831       break;
1832   }
1833 }
1834
1835 void Blurb(int x, int y)
1836 {
1837   int element = Feld[x][y];
1838
1839   if (element != EL_ACID_SPLASH_LEFT &&
1840       element != EL_ACID_SPLASH_RIGHT)  /* start */
1841   {
1842     PlaySoundLevel(x, y, SND_ACID_SPLASHING);
1843     if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
1844         (!IN_LEV_FIELD(x-1, y-1) ||
1845          !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
1846     {
1847       Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
1848     }
1849     if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
1850         (!IN_LEV_FIELD(x+1, y-1) ||
1851          !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
1852     {
1853       Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
1854     }
1855   }
1856   else                                                          /* go on */
1857   {
1858     int graphic = (element == EL_ACID_SPLASH_LEFT ?
1859                    IMG_ACID_SPLASH_LEFT :
1860                    IMG_ACID_SPLASH_RIGHT);
1861
1862     if (!MovDelay[x][y])        /* initialize animation counter */
1863       MovDelay[x][y] = 9;
1864
1865     if (MovDelay[x][y])         /* continue animation */
1866     {
1867       MovDelay[x][y]--;
1868       if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1869       {
1870         int frame = getGraphicAnimationFrame(graphic, 8 - MovDelay[x][y]);
1871
1872         DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
1873       }
1874
1875       if (!MovDelay[x][y])
1876       {
1877         Feld[x][y] = EL_EMPTY;
1878         DrawLevelField(x, y);
1879       }
1880     }
1881   }
1882 }
1883
1884 static void ToggleBeltSwitch(int x, int y)
1885 {
1886   static int belt_base_element[4] =
1887   {
1888     EL_CONVEYOR_BELT1_LEFT,
1889     EL_CONVEYOR_BELT2_LEFT,
1890     EL_CONVEYOR_BELT3_LEFT,
1891     EL_CONVEYOR_BELT4_LEFT
1892   };
1893   static int belt_base_active_element[4] =
1894   {
1895     EL_CONVEYOR_BELT1_LEFT_ACTIVE,
1896     EL_CONVEYOR_BELT2_LEFT_ACTIVE,
1897     EL_CONVEYOR_BELT3_LEFT_ACTIVE,
1898     EL_CONVEYOR_BELT4_LEFT_ACTIVE
1899   };
1900   static int belt_base_switch_element[4] =
1901   {
1902     EL_CONVEYOR_BELT1_SWITCH_LEFT,
1903     EL_CONVEYOR_BELT2_SWITCH_LEFT,
1904     EL_CONVEYOR_BELT3_SWITCH_LEFT,
1905     EL_CONVEYOR_BELT4_SWITCH_LEFT
1906   };
1907   static int belt_move_dir[4] =
1908   {
1909     MV_LEFT,
1910     MV_NO_MOVING,
1911     MV_RIGHT,
1912     MV_NO_MOVING,
1913   };
1914
1915   int element = Feld[x][y];
1916   int belt_nr = getBeltNrFromBeltSwitchElement(element);
1917   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
1918   int belt_dir = belt_move_dir[belt_dir_nr];
1919   int xx, yy, i;
1920
1921   if (!IS_BELT_SWITCH(element))
1922     return;
1923
1924   game.belt_dir_nr[belt_nr] = belt_dir_nr;
1925   game.belt_dir[belt_nr] = belt_dir;
1926
1927   if (belt_dir_nr == 3)
1928     belt_dir_nr = 1;
1929
1930   /* set frame order for belt animation graphic according to belt direction */
1931   for (i=0; i<3; i++)
1932   {
1933     int element = belt_base_active_element[belt_nr] + i;
1934     int graphic = el2img(element);
1935
1936     if (belt_dir == MV_LEFT)
1937       new_graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
1938     else
1939       new_graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
1940   }
1941
1942   for (yy=0; yy<lev_fieldy; yy++)
1943   {
1944     for (xx=0; xx<lev_fieldx; xx++)
1945     {
1946       int element = Feld[xx][yy];
1947
1948       if (IS_BELT_SWITCH(element))
1949       {
1950         int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
1951
1952         if (e_belt_nr == belt_nr)
1953         {
1954           Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
1955           DrawLevelField(xx, yy);
1956         }
1957       }
1958       else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
1959       {
1960         int e_belt_nr = getBeltNrFromBeltElement(element);
1961
1962         if (e_belt_nr == belt_nr)
1963         {
1964           int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
1965
1966           Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
1967           DrawLevelField(xx, yy);
1968         }
1969       }
1970       else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
1971       {
1972         int e_belt_nr = getBeltNrFromBeltActiveElement(element);
1973
1974         if (e_belt_nr == belt_nr)
1975         {
1976           int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
1977
1978           Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
1979           DrawLevelField(xx, yy);
1980         }
1981       }
1982     }
1983   }
1984 }
1985
1986 static void ToggleSwitchgateSwitch(int x, int y)
1987 {
1988   int xx, yy;
1989
1990   game.switchgate_pos = !game.switchgate_pos;
1991
1992   for (yy=0; yy<lev_fieldy; yy++)
1993   {
1994     for (xx=0; xx<lev_fieldx; xx++)
1995     {
1996       int element = Feld[xx][yy];
1997
1998       if (element == EL_SWITCHGATE_SWITCH_UP ||
1999           element == EL_SWITCHGATE_SWITCH_DOWN)
2000       {
2001         Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2002         DrawLevelField(xx, yy);
2003       }
2004       else if (element == EL_SWITCHGATE_OPEN ||
2005                element == EL_SWITCHGATE_OPENING)
2006       {
2007         Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
2008         PlaySoundLevel(xx, yy, SND_SWITCHGATE_CLOSING);
2009       }
2010       else if (element == EL_SWITCHGATE_CLOSED ||
2011                element == EL_SWITCHGATE_CLOSING)
2012       {
2013         Feld[xx][yy] = EL_SWITCHGATE_OPENING;
2014         PlaySoundLevel(xx, yy, SND_SWITCHGATE_OPENING);
2015       }
2016     }
2017   }
2018 }
2019
2020 static int getInvisibleActiveFromInvisibleElement(int element)
2021 {
2022   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
2023           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
2024           EL_INVISIBLE_SAND_ACTIVE);
2025 }
2026
2027 static int getInvisibleFromInvisibleActiveElement(int element)
2028 {
2029   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
2030           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
2031           EL_INVISIBLE_SAND);
2032 }
2033
2034 static void RedrawAllLightSwitchesAndInvisibleElements()
2035 {
2036   int x, y;
2037
2038   for (y=0; y<lev_fieldy; y++)
2039   {
2040     for (x=0; x<lev_fieldx; x++)
2041     {
2042       int element = Feld[x][y];
2043
2044       if (element == EL_LIGHT_SWITCH &&
2045           game.light_time_left > 0)
2046       {
2047         Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
2048         DrawLevelField(x, y);
2049       }
2050       else if (element == EL_LIGHT_SWITCH_ACTIVE &&
2051                game.light_time_left == 0)
2052       {
2053         Feld[x][y] = EL_LIGHT_SWITCH;
2054         DrawLevelField(x, y);
2055       }
2056       else if (element == EL_INVISIBLE_STEELWALL ||
2057                element == EL_INVISIBLE_WALL ||
2058                element == EL_INVISIBLE_SAND)
2059       {
2060         if (game.light_time_left > 0)
2061           Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
2062
2063         DrawLevelField(x, y);
2064       }
2065       else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
2066                element == EL_INVISIBLE_WALL_ACTIVE ||
2067                element == EL_INVISIBLE_SAND_ACTIVE)
2068       {
2069         if (game.light_time_left == 0)
2070           Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
2071
2072         DrawLevelField(x, y);
2073       }
2074     }
2075   }
2076 }
2077
2078 static void ToggleLightSwitch(int x, int y)
2079 {
2080   int element = Feld[x][y];
2081
2082   game.light_time_left =
2083     (element == EL_LIGHT_SWITCH ?
2084      level.time_light * FRAMES_PER_SECOND : 0);
2085
2086   RedrawAllLightSwitchesAndInvisibleElements();
2087 }
2088
2089 static void ActivateTimegateSwitch(int x, int y)
2090 {
2091   int xx, yy;
2092
2093   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
2094
2095   for (yy=0; yy<lev_fieldy; yy++)
2096   {
2097     for (xx=0; xx<lev_fieldx; xx++)
2098     {
2099       int element = Feld[xx][yy];
2100
2101       if (element == EL_TIMEGATE_CLOSED ||
2102           element == EL_TIMEGATE_CLOSING)
2103       {
2104         Feld[xx][yy] = EL_TIMEGATE_OPENING;
2105         PlaySoundLevel(xx, yy, SND_TIMEGATE_OPENING);
2106       }
2107
2108       /*
2109       else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
2110       {
2111         Feld[xx][yy] = EL_TIMEGATE_SWITCH;
2112         DrawLevelField(xx, yy);
2113       }
2114       */
2115
2116     }
2117   }
2118
2119   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
2120 }
2121
2122 void Impact(int x, int y)
2123 {
2124   boolean lastline = (y == lev_fieldy-1);
2125   boolean object_hit = FALSE;
2126   int element = Feld[x][y];
2127   int smashed = 0;
2128
2129   if (!lastline)        /* check if element below was hit */
2130   {
2131     if (Feld[x][y+1] == EL_PLAYER_IS_LEAVING)
2132       return;
2133
2134     object_hit = (!IS_FREE(x, y+1) && (!IS_MOVING(x, y+1) ||
2135                                       MovDir[x][y+1]!=MV_DOWN ||
2136                                       MovPos[x][y+1]<=TILEY/2));
2137     if (object_hit)
2138       smashed = MovingOrBlocked2Element(x, y+1);
2139   }
2140
2141   if (!lastline && smashed == EL_ACID)  /* element falls into acid */
2142   {
2143     Blurb(x, y);
2144     return;
2145   }
2146
2147   if ((element == EL_BOMB ||
2148        element == EL_SP_DISK_ORANGE ||
2149        element == EL_DX_SUPABOMB) &&
2150       (lastline || object_hit)) /* element is bomb */
2151   {
2152     Bang(x, y);
2153     return;
2154   }
2155   else if (element == EL_PEARL)
2156   {
2157     Feld[x][y] = EL_PEARL_BREAKING;
2158     PlaySoundLevel(x, y, SND_PEARL_BREAKING);
2159     return;
2160   }
2161
2162   if (element == EL_AMOEBA_DROP && (lastline || object_hit))
2163   {
2164     if (object_hit && IS_PLAYER(x, y+1))
2165       KillHeroUnlessProtected(x, y+1);
2166     else if (object_hit && smashed == EL_PENGUIN)
2167       Bang(x, y+1);
2168     else
2169     {
2170       Feld[x][y] = EL_AMOEBA_CREATING;
2171       Store[x][y] = EL_AMOEBA_WET;
2172     }
2173     return;
2174   }
2175
2176   if (!lastline && object_hit)          /* check which object was hit */
2177   {
2178     if (CAN_CHANGE(element) && 
2179         (smashed == EL_MAGIC_WALL || smashed == EL_BD_MAGIC_WALL))
2180     {
2181       int xx, yy;
2182       int activated_magic_wall =
2183         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
2184          EL_BD_MAGIC_WALL_ACTIVE);
2185
2186       /* activate magic wall / mill */
2187       for (yy=0; yy<lev_fieldy; yy++)
2188         for (xx=0; xx<lev_fieldx; xx++)
2189           if (Feld[xx][yy] == smashed)
2190             Feld[xx][yy] = activated_magic_wall;
2191
2192       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
2193       game.magic_wall_active = TRUE;
2194
2195       PlaySoundLevel(x, y, (smashed == EL_MAGIC_WALL ?
2196                             SND_MAGIC_WALL_ACTIVATING :
2197                             SND_BD_MAGIC_WALL_ACTIVATING));
2198     }
2199
2200     if (IS_PLAYER(x, y+1))
2201     {
2202       KillHeroUnlessProtected(x, y+1);
2203       return;
2204     }
2205     else if (smashed == EL_PENGUIN)
2206     {
2207       Bang(x, y+1);
2208       return;
2209     }
2210     else if (element == EL_BD_DIAMOND)
2211     {
2212       if (IS_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
2213       {
2214         Bang(x, y+1);
2215         return;
2216       }
2217     }
2218     else if ((element == EL_SP_INFOTRON || element == EL_SP_ZONK) &&
2219              (smashed == EL_SP_SNIKSNAK || smashed == EL_SP_ELECTRON ||
2220               smashed == EL_SP_DISK_ORANGE))
2221     {
2222       Bang(x, y+1);
2223       return;
2224     }
2225     else if (element == EL_ROCK ||
2226              element == EL_SP_ZONK ||
2227              element == EL_BD_ROCK)
2228     {
2229       if (IS_ENEMY(smashed) ||
2230           smashed == EL_BOMB || smashed == EL_SP_DISK_ORANGE ||
2231           smashed == EL_DX_SUPABOMB ||
2232           smashed == EL_SATELLITE || smashed == EL_PIG ||
2233           smashed == EL_DRAGON || smashed == EL_MOLE)
2234       {
2235         Bang(x, y+1);
2236         return;
2237       }
2238       else if (!IS_MOVING(x, y+1))
2239       {
2240         if (smashed == EL_LAMP || smashed == EL_LAMP_ACTIVE)
2241         {
2242           Bang(x, y+1);
2243           return;
2244         }
2245         else if (smashed == EL_NUT)
2246         {
2247           Feld[x][y+1] = EL_NUT_CRACKING;
2248           PlaySoundLevel(x, y, SND_NUT_CRACKING);
2249           RaiseScoreElement(EL_NUT);
2250           return;
2251         }
2252         else if (smashed == EL_PEARL)
2253         {
2254           Feld[x][y+1] = EL_PEARL_BREAKING;
2255           PlaySoundLevel(x, y, SND_PEARL_BREAKING);
2256           return;
2257         }
2258         else if (smashed == EL_DIAMOND)
2259         {
2260           Feld[x][y+1] = EL_EMPTY;
2261           PlaySoundLevel(x, y, SND_DIAMOND_BREAKING);
2262           return;
2263         }
2264         else if (IS_BELT_SWITCH(smashed))
2265         {
2266           ToggleBeltSwitch(x, y+1);
2267         }
2268         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
2269                  smashed == EL_SWITCHGATE_SWITCH_DOWN)
2270         {
2271           ToggleSwitchgateSwitch(x, y+1);
2272         }
2273         else if (smashed == EL_LIGHT_SWITCH ||
2274                  smashed == EL_LIGHT_SWITCH_ACTIVE)
2275         {
2276           ToggleLightSwitch(x, y+1);
2277         }
2278       }
2279     }
2280   }
2281
2282   /* play sound of magic wall / mill */
2283   if (!lastline &&
2284       (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE ||
2285        Feld[x][y+1] == EL_BD_MAGIC_WALL_ACTIVE))
2286   {
2287     if (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE)
2288       PlaySoundLevel(x, y, SND_MAGIC_WALL_CHANGING);
2289     else if (Feld[x][y+1] == EL_BD_MAGIC_WALL_ACTIVE)
2290       PlaySoundLevel(x, y, SND_BD_MAGIC_WALL_CHANGING);
2291
2292     return;
2293   }
2294
2295   /* play sound of object that hits the ground */
2296   if (lastline || object_hit)
2297     PlaySoundLevelElementAction(x, y, element, SND_ACTION_IMPACT);
2298 }
2299
2300 void TurnRound(int x, int y)
2301 {
2302   static struct
2303   {
2304     int x, y;
2305   } move_xy[] =
2306   {
2307     { 0, 0 },
2308     {-1, 0 },
2309     {+1, 0 },
2310     { 0, 0 },
2311     { 0, -1 },
2312     { 0, 0 }, { 0, 0 }, { 0, 0 },
2313     { 0, +1 }
2314   };
2315   static struct
2316   {
2317     int left, right, back;
2318   } turn[] =
2319   {
2320     { 0,        0,              0 },
2321     { MV_DOWN,  MV_UP,          MV_RIGHT },
2322     { MV_UP,    MV_DOWN,        MV_LEFT },
2323     { 0,        0,              0 },
2324     { MV_LEFT,  MV_RIGHT,       MV_DOWN },
2325     { 0,0,0 },  { 0,0,0 },      { 0,0,0 },
2326     { MV_RIGHT, MV_LEFT,        MV_UP }
2327   };
2328
2329   int element = Feld[x][y];
2330   int old_move_dir = MovDir[x][y];
2331   int left_dir  = turn[old_move_dir].left;
2332   int right_dir = turn[old_move_dir].right;
2333   int back_dir  = turn[old_move_dir].back;
2334
2335   int left_dx  = move_xy[left_dir].x,     left_dy  = move_xy[left_dir].y;
2336   int right_dx = move_xy[right_dir].x,    right_dy = move_xy[right_dir].y;
2337   int move_dx  = move_xy[old_move_dir].x, move_dy  = move_xy[old_move_dir].y;
2338   int back_dx  = move_xy[back_dir].x,     back_dy  = move_xy[back_dir].y;
2339
2340   int left_x  = x + left_dx,  left_y  = y + left_dy;
2341   int right_x = x + right_dx, right_y = y + right_dy;
2342   int move_x  = x + move_dx,  move_y  = y + move_dy;
2343
2344   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2345   {
2346     TestIfBadThingTouchesOtherBadThing(x, y);
2347
2348     if (IN_LEV_FIELD(right_x, right_y) &&
2349         IS_FREE(right_x, right_y))
2350       MovDir[x][y] = right_dir;
2351     else if (!IN_LEV_FIELD(move_x, move_y) ||
2352              !IS_FREE(move_x, move_y))
2353       MovDir[x][y] = left_dir;
2354
2355     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
2356       MovDelay[x][y] = 9;
2357     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
2358       MovDelay[x][y] = 1;
2359   }
2360   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2361            element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2362   {
2363     TestIfBadThingTouchesOtherBadThing(x, y);
2364
2365     if (IN_LEV_FIELD(left_x, left_y) &&
2366         IS_FREE(left_x, left_y))
2367       MovDir[x][y] = left_dir;
2368     else if (!IN_LEV_FIELD(move_x, move_y) ||
2369              !IS_FREE(move_x, move_y))
2370       MovDir[x][y] = right_dir;
2371
2372     if ((element == EL_SPACESHIP ||
2373          element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2374         && MovDir[x][y] != old_move_dir)
2375       MovDelay[x][y] = 9;
2376     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
2377       MovDelay[x][y] = 1;
2378   }
2379   else if (element == EL_YAMYAM)
2380   {
2381     boolean can_turn_left = FALSE, can_turn_right = FALSE;
2382
2383     if (IN_LEV_FIELD(left_x, left_y) &&
2384         (IS_FREE_OR_PLAYER(left_x, left_y) ||
2385          Feld[left_x][left_y] == EL_DIAMOND))
2386       can_turn_left = TRUE;
2387     if (IN_LEV_FIELD(right_x, right_y) &&
2388         (IS_FREE_OR_PLAYER(right_x, right_y) ||
2389          Feld[right_x][right_y] == EL_DIAMOND))
2390       can_turn_right = TRUE;
2391
2392     if (can_turn_left && can_turn_right)
2393       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
2394     else if (can_turn_left)
2395       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
2396     else if (can_turn_right)
2397       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
2398     else
2399       MovDir[x][y] = back_dir;
2400
2401     MovDelay[x][y] = 16+16*RND(3);
2402   }
2403   else if (element == EL_DARK_YAMYAM)
2404   {
2405     boolean can_turn_left = FALSE, can_turn_right = FALSE;
2406
2407     if (IN_LEV_FIELD(left_x, left_y) &&
2408         (IS_FREE_OR_PLAYER(left_x, left_y) ||
2409          IS_MAMPF2(Feld[left_x][left_y])))
2410       can_turn_left = TRUE;
2411     if (IN_LEV_FIELD(right_x, right_y) &&
2412         (IS_FREE_OR_PLAYER(right_x, right_y) ||
2413          IS_MAMPF2(Feld[right_x][right_y])))
2414       can_turn_right = TRUE;
2415
2416     if (can_turn_left && can_turn_right)
2417       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
2418     else if (can_turn_left)
2419       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
2420     else if (can_turn_right)
2421       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
2422     else
2423       MovDir[x][y] = back_dir;
2424
2425     MovDelay[x][y] = 16+16*RND(3);
2426   }
2427   else if (element == EL_PACMAN)
2428   {
2429     boolean can_turn_left = FALSE, can_turn_right = FALSE;
2430
2431     if (IN_LEV_FIELD(left_x, left_y) &&
2432         (IS_FREE_OR_PLAYER(left_x, left_y) ||
2433          IS_AMOEBOID(Feld[left_x][left_y])))
2434       can_turn_left = TRUE;
2435     if (IN_LEV_FIELD(right_x, right_y) &&
2436         (IS_FREE_OR_PLAYER(right_x, right_y) ||
2437          IS_AMOEBOID(Feld[right_x][right_y])))
2438       can_turn_right = TRUE;
2439
2440     if (can_turn_left && can_turn_right)
2441       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
2442     else if (can_turn_left)
2443       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
2444     else if (can_turn_right)
2445       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
2446     else
2447       MovDir[x][y] = back_dir;
2448
2449     MovDelay[x][y] = 6+RND(40);
2450   }
2451   else if (element == EL_PIG)
2452   {
2453     boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE;
2454     boolean should_turn_left = FALSE, should_turn_right = FALSE;
2455     boolean should_move_on = FALSE;
2456     int rnd_value = 24;
2457     int rnd = RND(rnd_value);
2458
2459     if (IN_LEV_FIELD(left_x, left_y) &&
2460         (IS_FREE(left_x, left_y) || IS_GEM(Feld[left_x][left_y])))
2461       can_turn_left = TRUE;
2462     if (IN_LEV_FIELD(right_x, right_y) &&
2463         (IS_FREE(right_x, right_y) || IS_GEM(Feld[right_x][right_y])))
2464       can_turn_right = TRUE;
2465     if (IN_LEV_FIELD(move_x, move_y) &&
2466         (IS_FREE(move_x, move_y) || IS_GEM(Feld[move_x][move_y])))
2467       can_move_on = TRUE;
2468
2469     if (can_turn_left &&
2470         (!can_move_on ||
2471          (IN_LEV_FIELD(x+back_dx+left_dx, y+back_dy+left_dy) &&
2472           !IS_FREE(x+back_dx+left_dx, y+back_dy+left_dy))))
2473       should_turn_left = TRUE;
2474     if (can_turn_right &&
2475         (!can_move_on ||
2476          (IN_LEV_FIELD(x+back_dx+right_dx, y+back_dy+right_dy) &&
2477           !IS_FREE(x+back_dx+right_dx, y+back_dy+right_dy))))
2478       should_turn_right = TRUE;
2479     if (can_move_on &&
2480         (!can_turn_left || !can_turn_right ||
2481          (IN_LEV_FIELD(x+move_dx+left_dx, y+move_dy+left_dy) &&
2482           !IS_FREE(x+move_dx+left_dx, y+move_dy+left_dy)) ||
2483          (IN_LEV_FIELD(x+move_dx+right_dx, y+move_dy+right_dy) &&
2484           !IS_FREE(x+move_dx+right_dx, y+move_dy+right_dy))))
2485       should_move_on = TRUE;
2486
2487     if (should_turn_left || should_turn_right || should_move_on)
2488     {
2489       if (should_turn_left && should_turn_right && should_move_on)
2490         MovDir[x][y] = (rnd < rnd_value/3 ? left_dir :
2491                         rnd < 2*rnd_value/3 ? right_dir :
2492                         old_move_dir);
2493       else if (should_turn_left && should_turn_right)
2494         MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : right_dir);
2495       else if (should_turn_left && should_move_on)
2496         MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : old_move_dir);
2497       else if (should_turn_right && should_move_on)
2498         MovDir[x][y] = (rnd < rnd_value/2 ? right_dir : old_move_dir);
2499       else if (should_turn_left)
2500         MovDir[x][y] = left_dir;
2501       else if (should_turn_right)
2502         MovDir[x][y] = right_dir;
2503       else if (should_move_on)
2504         MovDir[x][y] = old_move_dir;
2505     }
2506     else if (can_move_on && rnd > rnd_value/8)
2507       MovDir[x][y] = old_move_dir;
2508     else if (can_turn_left && can_turn_right)
2509       MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : right_dir);
2510     else if (can_turn_left && rnd > rnd_value/8)
2511       MovDir[x][y] = left_dir;
2512     else if (can_turn_right && rnd > rnd_value/8)
2513       MovDir[x][y] = right_dir;
2514     else
2515       MovDir[x][y] = back_dir;
2516
2517     if (!IS_FREE(x+move_xy[MovDir[x][y]].x, y+move_xy[MovDir[x][y]].y) &&
2518         !IS_GEM(Feld[x+move_xy[MovDir[x][y]].x][y+move_xy[MovDir[x][y]].y]))
2519       MovDir[x][y] = old_move_dir;
2520
2521     MovDelay[x][y] = 0;
2522   }
2523   else if (element == EL_DRAGON)
2524   {
2525     boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE;
2526     int rnd_value = 24;
2527     int rnd = RND(rnd_value);
2528
2529     if (IN_LEV_FIELD(left_x, left_y) && IS_FREE(left_x, left_y))
2530       can_turn_left = TRUE;
2531     if (IN_LEV_FIELD(right_x, right_y) && IS_FREE(right_x, right_y))
2532       can_turn_right = TRUE;
2533     if (IN_LEV_FIELD(move_x, move_y) && IS_FREE(move_x, move_y))
2534       can_move_on = TRUE;
2535
2536     if (can_move_on && rnd > rnd_value/8)
2537       MovDir[x][y] = old_move_dir;
2538     else if (can_turn_left && can_turn_right)
2539       MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : right_dir);
2540     else if (can_turn_left && rnd > rnd_value/8)
2541       MovDir[x][y] = left_dir;
2542     else if (can_turn_right && rnd > rnd_value/8)
2543       MovDir[x][y] = right_dir;
2544     else
2545       MovDir[x][y] = back_dir;
2546
2547     if (!IS_FREE(x+move_xy[MovDir[x][y]].x, y+move_xy[MovDir[x][y]].y))
2548       MovDir[x][y] = old_move_dir;
2549
2550     MovDelay[x][y] = 0;
2551   }
2552   else if (element == EL_MOLE)
2553   {
2554     boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE;
2555
2556     if (IN_LEV_FIELD(move_x, move_y) &&
2557         (IS_FREE(move_x, move_y) || IS_AMOEBOID(Feld[move_x][move_y]) ||
2558          Feld[move_x][move_y] == EL_AMOEBA_SHRINKING))
2559       can_move_on = TRUE;
2560
2561     if (!can_move_on)
2562     {
2563       if (IN_LEV_FIELD(left_x, left_y) &&
2564           (IS_FREE(left_x, left_y) || IS_AMOEBOID(Feld[left_x][left_y])))
2565         can_turn_left = TRUE;
2566       if (IN_LEV_FIELD(right_x, right_y) &&
2567           (IS_FREE(right_x, right_y) || IS_AMOEBOID(Feld[right_x][right_y])))
2568         can_turn_right = TRUE;
2569
2570       if (can_turn_left && can_turn_right)
2571         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
2572       else if (can_turn_left)
2573         MovDir[x][y] = left_dir;
2574       else
2575         MovDir[x][y] = right_dir;
2576     }
2577
2578     if (MovDir[x][y] != old_move_dir)
2579       MovDelay[x][y] = 9;
2580   }
2581   else if (element == EL_BALLOON)
2582   {
2583     MovDir[x][y] = game.balloon_dir;
2584     MovDelay[x][y] = 0;
2585   }
2586   else if (element == EL_SPRING)
2587   {
2588     if ((MovDir[x][y] == MV_LEFT || MovDir[x][y] == MV_RIGHT) &&
2589         (!IN_LEV_FIELD(move_x, move_y) || !IS_FREE(move_x, move_y) ||
2590          (IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))
2591       MovDir[x][y] = MV_NO_MOVING;
2592
2593     MovDelay[x][y] = 0;
2594   }
2595   else if (element == EL_ROBOT ||
2596            element == EL_SATELLITE ||
2597            element == EL_PENGUIN)
2598   {
2599     int attr_x = -1, attr_y = -1;
2600
2601     if (AllPlayersGone)
2602     {
2603       attr_x = ExitX;
2604       attr_y = ExitY;
2605     }
2606     else
2607     {
2608       int i;
2609
2610       for (i=0; i<MAX_PLAYERS; i++)
2611       {
2612         struct PlayerInfo *player = &stored_player[i];
2613         int jx = player->jx, jy = player->jy;
2614
2615         if (!player->active)
2616           continue;
2617
2618         if (attr_x == -1 || ABS(jx-x)+ABS(jy-y) < ABS(attr_x-x)+ABS(attr_y-y))
2619         {
2620           attr_x = jx;
2621           attr_y = jy;
2622         }
2623       }
2624     }
2625
2626     if (element == EL_ROBOT && ZX>=0 && ZY>=0)
2627     {
2628       attr_x = ZX;
2629       attr_y = ZY;
2630     }
2631
2632     if (element == EL_PENGUIN)
2633     {
2634       int i;
2635       static int xy[4][2] =
2636       {
2637         { 0, -1 },
2638         { -1, 0 },
2639         { +1, 0 },
2640         { 0, +1 }
2641       };
2642
2643       for (i=0; i<4; i++)
2644       {
2645         int ex = x + xy[i%4][0];
2646         int ey = y + xy[i%4][1];
2647
2648         if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
2649         {
2650           attr_x = ex;
2651           attr_y = ey;
2652           break;
2653         }
2654       }
2655     }
2656
2657     MovDir[x][y] = MV_NO_MOVING;
2658     if (attr_x<x)
2659       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
2660     else if (attr_x>x)
2661       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
2662     if (attr_y<y)
2663       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
2664     else if (attr_y>y)
2665       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
2666
2667     if (element == EL_ROBOT)
2668     {
2669       int newx, newy;
2670
2671       if ((MovDir[x][y]&(MV_LEFT|MV_RIGHT)) && (MovDir[x][y]&(MV_UP|MV_DOWN)))
2672         MovDir[x][y] &= (RND(2) ? (MV_LEFT|MV_RIGHT) : (MV_UP|MV_DOWN));
2673       Moving2Blocked(x, y, &newx, &newy);
2674
2675       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
2676         MovDelay[x][y] = 8+8*!RND(3);
2677       else
2678         MovDelay[x][y] = 16;
2679     }
2680     else
2681     {
2682       int newx, newy;
2683
2684       MovDelay[x][y] = 1;
2685
2686       if ((MovDir[x][y]&(MV_LEFT|MV_RIGHT)) && (MovDir[x][y]&(MV_UP|MV_DOWN)))
2687       {
2688         boolean first_horiz = RND(2);
2689         int new_move_dir = MovDir[x][y];
2690
2691         MovDir[x][y] =
2692           new_move_dir & (first_horiz ? (MV_LEFT|MV_RIGHT) : (MV_UP|MV_DOWN));
2693         Moving2Blocked(x, y, &newx, &newy);
2694
2695         if (IN_LEV_FIELD(newx, newy) &&
2696             (IS_FREE(newx, newy) ||
2697              Feld[newx][newy] == EL_ACID ||
2698              (element == EL_PENGUIN &&
2699               (Feld[newx][newy] == EL_EXIT_OPEN ||
2700                IS_MAMPF3(Feld[newx][newy])))))
2701           return;
2702
2703         MovDir[x][y] =
2704           new_move_dir & (!first_horiz ? (MV_LEFT|MV_RIGHT) : (MV_UP|MV_DOWN));
2705         Moving2Blocked(x, y, &newx, &newy);
2706
2707         if (IN_LEV_FIELD(newx, newy) &&
2708             (IS_FREE(newx, newy) ||
2709              Feld[newx][newy] == EL_ACID ||
2710              (element == EL_PENGUIN &&
2711               (Feld[newx][newy] == EL_EXIT_OPEN ||
2712                IS_MAMPF3(Feld[newx][newy])))))
2713           return;
2714
2715         MovDir[x][y] = old_move_dir;
2716         return;
2717       }
2718     }
2719   }
2720 }
2721
2722 static boolean JustBeingPushed(int x, int y)
2723 {
2724   int i;
2725
2726   for (i=0; i<MAX_PLAYERS; i++)
2727   {
2728     struct PlayerInfo *player = &stored_player[i];
2729
2730     if (player->active && player->Pushing && player->MovPos)
2731     {
2732       int next_jx = player->jx + (player->jx - player->last_jx);
2733       int next_jy = player->jy + (player->jy - player->last_jy);
2734
2735       if (x == next_jx && y == next_jy)
2736         return TRUE;
2737     }
2738   }
2739
2740   return FALSE;
2741 }
2742
2743 void StartMoving(int x, int y)
2744 {
2745   int element = Feld[x][y];
2746
2747   if (Stop[x][y])
2748     return;
2749
2750   GfxAction[x][y] = GFX_ACTION_DEFAULT;
2751
2752   if (CAN_FALL(element) && y<lev_fieldy-1)
2753   {
2754     if ((x>0 && IS_PLAYER(x-1, y)) || (x<lev_fieldx-1 && IS_PLAYER(x+1, y)))
2755       if (JustBeingPushed(x, y))
2756         return;
2757
2758     if (element == EL_QUICKSAND_FULL)
2759     {
2760       if (IS_FREE(x, y+1))
2761       {
2762         InitMovingField(x, y, MV_DOWN);
2763         Feld[x][y] = EL_QUICKSAND_EMPTYING;
2764         Store[x][y] = EL_ROCK;
2765         PlaySoundLevel(x, y, SND_QUICKSAND_EMPTYING);
2766       }
2767       else if (Feld[x][y+1] == EL_QUICKSAND_EMPTY)
2768       {
2769         if (!MovDelay[x][y])
2770           MovDelay[x][y] = TILEY + 1;
2771
2772         if (MovDelay[x][y])
2773         {
2774           MovDelay[x][y]--;
2775           if (MovDelay[x][y])
2776             return;
2777         }
2778
2779         Feld[x][y] = EL_QUICKSAND_EMPTY;
2780         Feld[x][y+1] = EL_QUICKSAND_FULL;
2781         Store[x][y+1] = Store[x][y];
2782         Store[x][y] = 0;
2783         PlaySoundLevel(x, y, SND_QUICKSAND_SLIPPING);
2784       }
2785     }
2786     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
2787              Feld[x][y+1] == EL_QUICKSAND_EMPTY)
2788     {
2789       InitMovingField(x, y, MV_DOWN);
2790       Feld[x][y] = EL_QUICKSAND_FILLING;
2791       Store[x][y] = element;
2792       PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
2793     }
2794     else if (element == EL_MAGIC_WALL_FULL)
2795     {
2796       if (IS_FREE(x, y+1))
2797       {
2798         InitMovingField(x, y, MV_DOWN);
2799         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
2800         Store[x][y] = EL_CHANGED(Store[x][y]);
2801       }
2802       else if (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE)
2803       {
2804         if (!MovDelay[x][y])
2805           MovDelay[x][y] = TILEY/4 + 1;
2806
2807         if (MovDelay[x][y])
2808         {
2809           MovDelay[x][y]--;
2810           if (MovDelay[x][y])
2811             return;
2812         }
2813
2814         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
2815         Feld[x][y+1] = EL_MAGIC_WALL_FULL;
2816         Store[x][y+1] = EL_CHANGED(Store[x][y]);
2817         Store[x][y] = 0;
2818       }
2819     }
2820     else if (element == EL_BD_MAGIC_WALL_FULL)
2821     {
2822       if (IS_FREE(x, y+1))
2823       {
2824         InitMovingField(x, y, MV_DOWN);
2825         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
2826         Store[x][y] = EL_CHANGED2(Store[x][y]);
2827       }
2828       else if (Feld[x][y+1] == EL_BD_MAGIC_WALL_ACTIVE)
2829       {
2830         if (!MovDelay[x][y])
2831           MovDelay[x][y] = TILEY/4 + 1;
2832
2833         if (MovDelay[x][y])
2834         {
2835           MovDelay[x][y]--;
2836           if (MovDelay[x][y])
2837             return;
2838         }
2839
2840         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
2841         Feld[x][y+1] = EL_BD_MAGIC_WALL_FULL;
2842         Store[x][y+1] = EL_CHANGED2(Store[x][y]);
2843         Store[x][y] = 0;
2844       }
2845     }
2846     else if (CAN_CHANGE(element) &&
2847              (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE ||
2848               Feld[x][y+1] == EL_BD_MAGIC_WALL_ACTIVE))
2849     {
2850       InitMovingField(x, y, MV_DOWN);
2851       Feld[x][y] =
2852         (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
2853          EL_BD_MAGIC_WALL_FILLING);
2854       Store[x][y] = element;
2855     }
2856     else if (CAN_SMASH(element) && Feld[x][y+1] == EL_ACID)
2857     {
2858       Blurb(x, y);
2859       InitMovingField(x, y, MV_DOWN);
2860       Store[x][y] = EL_ACID;
2861     }
2862     else if (CAN_SMASH(element) && Feld[x][y+1] == EL_BLOCKED &&
2863              JustStopped[x][y])
2864     {
2865       Impact(x, y);
2866     }
2867     else if (IS_FREE(x, y+1))
2868     {
2869       InitMovingField(x, y, MV_DOWN);
2870     }
2871     else if (element == EL_AMOEBA_DROP)
2872     {
2873       Feld[x][y] = EL_AMOEBA_CREATING;
2874       Store[x][y] = EL_AMOEBA_WET;
2875     }
2876     /* Store[x][y+1] must be zero, because:
2877        (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y+1] == EL_QUICKSAND_EMPTY
2878     */
2879 #if 0
2880 #if OLD_GAME_BEHAVIOUR
2881     else if (IS_SLIPPERY(Feld[x][y+1]) && !Store[x][y+1])
2882 #else
2883     else if (IS_SLIPPERY(Feld[x][y+1]) && !Store[x][y+1] &&
2884              !IS_FALLING(x, y+1) && !JustStopped[x][y+1] &&
2885              element != EL_DX_SUPABOMB)
2886 #endif
2887 #else
2888     else if ((IS_SLIPPERY(Feld[x][y+1]) ||
2889               (IS_EM_SLIPPERY_WALL(Feld[x][y+1]) && IS_GEM(element))) &&
2890              !IS_FALLING(x, y+1) && !JustStopped[x][y+1] &&
2891              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
2892 #endif
2893     {
2894       boolean left  = (x>0 && IS_FREE(x-1, y) &&
2895                        (IS_FREE(x-1, y+1) || Feld[x-1][y+1] == EL_ACID));
2896       boolean right = (x<lev_fieldx-1 && IS_FREE(x+1, y) &&
2897                        (IS_FREE(x+1, y+1) || Feld[x+1][y+1] == EL_ACID));
2898
2899       if (left || right)
2900       {
2901         if (left && right &&
2902             (game.emulation != EMU_BOULDERDASH &&
2903              element != EL_BD_ROCK && element != EL_BD_DIAMOND))
2904           left = !(right = RND(2));
2905
2906         InitMovingField(x, y, left ? MV_LEFT : MV_RIGHT);
2907       }
2908     }
2909     else if (IS_BELT_ACTIVE(Feld[x][y+1]))
2910     {
2911       boolean left_is_free  = (x>0 && IS_FREE(x-1, y));
2912       boolean right_is_free = (x<lev_fieldx-1 && IS_FREE(x+1, y));
2913       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y+1]);
2914       int belt_dir = game.belt_dir[belt_nr];
2915
2916       if ((belt_dir == MV_LEFT  && left_is_free) ||
2917           (belt_dir == MV_RIGHT && right_is_free))
2918       {
2919         InitMovingField(x, y, belt_dir);
2920         GfxAction[x][y] = GFX_ACTION_DEFAULT;
2921       }
2922     }
2923   }
2924
2925   if (CAN_MOVE(element))        /* not "else if" because of EL_SPRING */
2926   {
2927     int newx, newy;
2928
2929     if ((element == EL_SATELLITE ||
2930          element == EL_BALLOON ||
2931          element == EL_SPRING)
2932         && JustBeingPushed(x, y))
2933       return;
2934
2935     if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
2936       Feld[x][y + 1] = EL_EMPTY;        /* was set to EL_BLOCKED above */
2937
2938     if (!MovDelay[x][y])        /* start new movement phase */
2939     {
2940       /* all objects that can change their move direction after each step */
2941       /* (MAMPFER, MAMPFER2 and PACMAN go straight until they hit a wall  */
2942
2943       if (element != EL_YAMYAM &&
2944           element != EL_DARK_YAMYAM &&
2945           element != EL_PACMAN)
2946       {
2947 #if 0
2948   if (element == EL_SPRING)
2949     printf("1--> %d\n", MovDir[x][y]);
2950 #endif
2951         TurnRound(x, y);
2952 #if 0
2953   if (element == EL_SPRING)
2954     printf("2--> %d\n", MovDir[x][y]);
2955 #endif
2956         if (MovDelay[x][y] && (element == EL_BUG ||
2957                                element == EL_SPACESHIP ||
2958                                element == EL_SP_SNIKSNAK ||
2959                                element == EL_SP_ELECTRON ||
2960                                element == EL_MOLE))
2961           DrawLevelField(x, y);
2962       }
2963     }
2964
2965     if (MovDelay[x][y])         /* wait some time before next movement */
2966     {
2967       MovDelay[x][y]--;
2968
2969       if (element == EL_ROBOT ||
2970           element == EL_YAMYAM || element == EL_DARK_YAMYAM)
2971       {
2972         if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2973         {
2974           int graphic = el2img(element);
2975           int frame = getGraphicAnimationFrame(graphic, MovDelay[x][y] % 8);
2976
2977           DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2978         }
2979
2980         if (MovDelay[x][y] % 4 == 3)
2981         {
2982           if (element == EL_YAMYAM)
2983             PlaySoundLevel(x, y, SND_YAMYAM_WAITING);
2984           else if (element == EL_DARK_YAMYAM)
2985             PlaySoundLevel(x, y, SND_DARK_YAMYAM_WAITING);
2986         }
2987       }
2988       else if (element == EL_SP_ELECTRON)
2989         DrawGraphicAnimation(x, y, IMG_SP_ELECTRON);
2990       else if (element == EL_DRAGON)
2991       {
2992         int i;
2993         int dir = MovDir[x][y];
2994         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2995         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
2996         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_LEFT1 :
2997                        dir == MV_RIGHT  ? IMG_FLAMES_RIGHT1 :
2998                        dir == MV_UP     ? IMG_FLAMES_UP1 :
2999                        dir == MV_DOWN   ? IMG_FLAMES_DOWN1 : IMG_EMPTY);
3000         int frame = getGraphicAnimationFrame(graphic, -1);
3001
3002         for (i=1; i<=3; i++)
3003         {
3004           int xx = x + i*dx, yy = y + i*dy;
3005           int sx = SCREENX(xx), sy = SCREENY(yy);
3006           int flame_graphic = graphic + (i - 1);
3007
3008           if (!IN_LEV_FIELD(xx, yy) ||
3009               IS_SOLID(Feld[xx][yy]) || Feld[xx][yy] == EL_EXPLOSION)
3010             break;
3011
3012           if (MovDelay[x][y])
3013           {
3014             int flamed = MovingOrBlocked2Element(xx, yy);
3015
3016             if (IS_ENEMY(flamed) || IS_EXPLOSIVE(flamed))
3017               Bang(xx, yy);
3018             else
3019               RemoveMovingField(xx, yy);
3020
3021             Feld[xx][yy] = EL_FLAMES;
3022             if (IN_SCR_FIELD(sx, sy))
3023               DrawGraphic(sx, sy, flame_graphic, frame);
3024           }
3025           else
3026           {
3027             if (Feld[xx][yy] == EL_FLAMES)
3028               Feld[xx][yy] = EL_EMPTY;
3029             DrawLevelField(xx, yy);
3030           }
3031         }
3032       }
3033
3034       if (MovDelay[x][y])       /* element still has to wait some time */
3035       {
3036         PlaySoundLevelAction(x, y, SND_ACTION_WAITING);
3037
3038         return;
3039       }
3040     }
3041
3042     /* now make next step */
3043
3044     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
3045
3046     if (IS_ENEMY(element) && IS_PLAYER(newx, newy) &&
3047         !PLAYER_PROTECTED(newx, newy))
3048     {
3049
3050 #if 1
3051       TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
3052       return;
3053 #else
3054       /* enemy got the player */
3055       MovDir[x][y] = 0;
3056       KillHero(PLAYERINFO(newx, newy));
3057       return;
3058 #endif
3059
3060     }
3061     else if ((element == EL_PENGUIN || element == EL_ROBOT ||
3062               element == EL_SATELLITE || element == EL_BALLOON) &&
3063              IN_LEV_FIELD(newx, newy) &&
3064              MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
3065     {
3066       Blurb(x, y);
3067       Store[x][y] = EL_ACID;
3068     }
3069     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
3070     {
3071       if (Feld[newx][newy] == EL_EXIT_OPEN)
3072       {
3073         Feld[x][y] = EL_EMPTY;
3074         DrawLevelField(x, y);
3075
3076         PlaySoundLevel(newx, newy, SND_PENGUIN_PASSING_EXIT);
3077         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
3078           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
3079
3080         local_player->friends_still_needed--;
3081         if (!local_player->friends_still_needed &&
3082             !local_player->GameOver && AllPlayersGone)
3083           local_player->LevelSolved = local_player->GameOver = TRUE;
3084
3085         return;
3086       }
3087       else if (IS_MAMPF3(Feld[newx][newy]))
3088       {
3089         if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
3090           DrawLevelField(newx, newy);
3091         else
3092           MovDir[x][y] = MV_NO_MOVING;
3093       }
3094       else if (!IS_FREE(newx, newy))
3095       {
3096         if (IS_PLAYER(x, y))
3097           DrawPlayerField(x, y);
3098         else
3099           DrawLevelField(x, y);
3100         return;
3101       }
3102     }
3103     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
3104     {
3105       if (IS_GEM(Feld[newx][newy]))
3106       {
3107         if (IS_MOVING(newx, newy))
3108           RemoveMovingField(newx, newy);
3109         else
3110         {
3111           Feld[newx][newy] = EL_EMPTY;
3112           DrawLevelField(newx, newy);
3113         }
3114
3115         PlaySoundLevel(x, y, SND_PIG_EATING);
3116       }
3117       else if (!IS_FREE(newx, newy))
3118       {
3119         if (IS_PLAYER(x, y))
3120           DrawPlayerField(x, y);
3121         else
3122           DrawLevelField(x, y);
3123         return;
3124       }
3125     }
3126     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
3127     {
3128       if (!IS_FREE(newx, newy))
3129       {
3130         if (IS_PLAYER(x, y))
3131           DrawPlayerField(x, y);
3132         else
3133           DrawLevelField(x, y);
3134         return;
3135       }
3136       else
3137       {
3138         boolean wanna_flame = !RND(10);
3139         int dx = newx - x, dy = newy - y;
3140         int newx1 = newx+1*dx, newy1 = newy+1*dy;
3141         int newx2 = newx+2*dx, newy2 = newy+2*dy;
3142         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
3143                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
3144         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
3145                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
3146
3147         if ((wanna_flame || IS_ENEMY(element1) || IS_ENEMY(element2)) &&
3148             element1 != EL_DRAGON && element2 != EL_DRAGON &&
3149             element1 != EL_FLAMES && element2 != EL_FLAMES)
3150         {
3151           if (IS_PLAYER(x, y))
3152             DrawPlayerField(x, y);
3153           else
3154             DrawLevelField(x, y);
3155
3156           PlaySoundLevel(x, y, SND_DRAGON_ATTACKING);
3157
3158           MovDelay[x][y] = 50;
3159           Feld[newx][newy] = EL_FLAMES;
3160           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
3161             Feld[newx1][newy1] = EL_FLAMES;
3162           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
3163             Feld[newx2][newy2] = EL_FLAMES;
3164           return;
3165         }
3166       }
3167     }
3168     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
3169              Feld[newx][newy] == EL_DIAMOND)
3170     {
3171       if (IS_MOVING(newx, newy))
3172         RemoveMovingField(newx, newy);
3173       else
3174       {
3175         Feld[newx][newy] = EL_EMPTY;
3176         DrawLevelField(newx, newy);
3177       }
3178
3179       PlaySoundLevel(x, y, SND_YAMYAM_EATING);
3180     }
3181     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
3182              IS_MAMPF2(Feld[newx][newy]))
3183     {
3184       if (AmoebaNr[newx][newy])
3185       {
3186         AmoebaCnt2[AmoebaNr[newx][newy]]--;
3187         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
3188             Feld[newx][newy] == EL_BD_AMOEBA)
3189           AmoebaCnt[AmoebaNr[newx][newy]]--;
3190       }
3191
3192       if (IS_MOVING(newx, newy))
3193         RemoveMovingField(newx, newy);
3194       else
3195       {
3196         Feld[newx][newy] = EL_EMPTY;
3197         DrawLevelField(newx, newy);
3198       }
3199
3200       PlaySoundLevel(x, y, SND_DARK_YAMYAM_EATING);
3201     }
3202     else if ((element == EL_PACMAN || element == EL_MOLE)
3203              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
3204     {
3205       if (AmoebaNr[newx][newy])
3206       {
3207         AmoebaCnt2[AmoebaNr[newx][newy]]--;
3208         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
3209             Feld[newx][newy] == EL_BD_AMOEBA)
3210           AmoebaCnt[AmoebaNr[newx][newy]]--;
3211       }
3212
3213       if (element == EL_MOLE)
3214       {
3215         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
3216         PlaySoundLevel(x, y, SND_MOLE_EATING);
3217         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
3218         return;                         /* wait for shrinking amoeba */
3219       }
3220       else      /* element == EL_PACMAN */
3221       {
3222         Feld[newx][newy] = EL_EMPTY;
3223         DrawLevelField(newx, newy);
3224         PlaySoundLevel(x, y, SND_PACMAN_EATING);
3225       }
3226     }
3227     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
3228              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
3229               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
3230     {
3231       /* wait for shrinking amoeba to completely disappear */
3232       return;
3233     }
3234     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
3235     {
3236       /* object was running against a wall */
3237
3238       TurnRound(x, y);
3239
3240       if (element == EL_BUG || element == EL_SPACESHIP ||
3241           element == EL_SP_SNIKSNAK)
3242         DrawLevelField(x, y);
3243       else if (element == EL_BUG || element == EL_SPACESHIP ||
3244                element == EL_SP_SNIKSNAK || element == EL_MOLE)
3245         DrawLevelField(x, y);
3246       else if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
3247         DrawGraphicAnimation(x, y, el2img(element));
3248       else if (element == EL_SATELLITE)
3249         DrawGraphicAnimation(x, y, IMG_SATELLITE);
3250       else if (element == EL_SP_ELECTRON)
3251         DrawGraphicAnimation(x, y, IMG_SP_ELECTRON);
3252
3253       if (DONT_TOUCH(element))
3254         TestIfBadThingTouchesHero(x, y);
3255
3256       PlaySoundLevelAction(x, y, SND_ACTION_WAITING);
3257
3258       return;
3259     }
3260
3261     InitMovingField(x, y, MovDir[x][y]);
3262
3263     PlaySoundLevelAction(x, y, SND_ACTION_MOVING);
3264   }
3265
3266   if (MovDir[x][y])
3267     ContinueMoving(x, y);
3268 }
3269
3270 void ContinueMoving(int x, int y)
3271 {
3272   int element = Feld[x][y];
3273   int direction = MovDir[x][y];
3274   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3275   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3276   int horiz_move = (dx != 0);
3277   int newx = x + dx, newy = y + dy;
3278   int step = (horiz_move ? dx : dy) * TILEX / 8;
3279
3280   if (element == EL_AMOEBA_DROP || element == EL_AMOEBA_DRIPPING)
3281     step /= 2;
3282   else if (element == EL_QUICKSAND_FILLING ||
3283            element == EL_QUICKSAND_EMPTYING)
3284     step /= 4;
3285   else if (element == EL_MAGIC_WALL_FILLING ||
3286            element == EL_BD_MAGIC_WALL_FILLING ||
3287            element == EL_MAGIC_WALL_EMPTYING ||
3288            element == EL_BD_MAGIC_WALL_EMPTYING)
3289     step /= 2;
3290   else if (CAN_FALL(element) && horiz_move &&
3291            y < lev_fieldy-1 && IS_BELT_ACTIVE(Feld[x][y+1]))
3292     step /= 2;
3293   else if (element == EL_SPRING && horiz_move)
3294     step *= 2;
3295
3296 #if OLD_GAME_BEHAVIOUR
3297   else if (CAN_FALL(element) && horiz_move && !IS_SP_ELEMENT(element))
3298     step*=2;
3299 #endif
3300
3301   MovPos[x][y] += step;
3302
3303   if (ABS(MovPos[x][y]) >= TILEX)       /* object reached its destination */
3304   {
3305     Feld[x][y] = EL_EMPTY;
3306     Feld[newx][newy] = element;
3307
3308     if (element == EL_MOLE)
3309     {
3310       int i;
3311       static int xy[4][2] =
3312       {
3313         { 0, -1 },
3314         { -1, 0 },
3315         { +1, 0 },
3316         { 0, +1 }
3317       };
3318
3319       Feld[x][y] = EL_SAND;
3320       DrawLevelField(x, y);
3321
3322       for(i=0; i<4; i++)
3323       {
3324         int xx, yy;
3325
3326         xx = x + xy[i][0];
3327         yy = y + xy[i][1];
3328
3329         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_SAND)
3330           DrawLevelField(xx, yy);       /* for "DrawCrumbledSand()" */
3331       }
3332     }
3333
3334     if (element == EL_QUICKSAND_FILLING)
3335     {
3336       element = Feld[newx][newy] = get_next_element(element);
3337       Store[newx][newy] = Store[x][y];
3338     }
3339     else if (element == EL_QUICKSAND_EMPTYING)
3340     {
3341       Feld[x][y] = get_next_element(element);
3342       element = Feld[newx][newy] = Store[x][y];
3343     }
3344     else if (element == EL_MAGIC_WALL_FILLING)
3345     {
3346       element = Feld[newx][newy] = get_next_element(element);
3347       if (!game.magic_wall_active)
3348         element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
3349       Store[newx][newy] = Store[x][y];
3350     }
3351     else if (element == EL_MAGIC_WALL_EMPTYING)
3352     {
3353       Feld[x][y] = get_next_element(element);
3354       if (!game.magic_wall_active)
3355         Feld[x][y] = EL_MAGIC_WALL_DEAD;
3356       element = Feld[newx][newy] = Store[x][y];
3357     }
3358     else if (element == EL_BD_MAGIC_WALL_FILLING)
3359     {
3360       element = Feld[newx][newy] = get_next_element(element);
3361       if (!game.magic_wall_active)
3362         element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
3363       Store[newx][newy] = Store[x][y];
3364     }
3365     else if (element == EL_BD_MAGIC_WALL_EMPTYING)
3366     {
3367       Feld[x][y] = get_next_element(element);
3368       if (!game.magic_wall_active)
3369         Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
3370       element = Feld[newx][newy] = Store[x][y];
3371     }
3372     else if (element == EL_AMOEBA_DRIPPING)
3373     {
3374       Feld[x][y] = get_next_element(element);
3375       element = Feld[newx][newy] = Store[x][y];
3376     }
3377     else if (Store[x][y] == EL_ACID)
3378     {
3379       element = Feld[newx][newy] = EL_ACID;
3380     }
3381
3382     Store[x][y] = 0;
3383     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3384     MovDelay[newx][newy] = 0;
3385
3386     GfxAction[newx][newy] = GfxAction[x][y];    /* keep action one frame */
3387     GfxAction[x][y] = GFX_ACTION_DEFAULT;
3388
3389 #if 0
3390     if (!CAN_MOVE(element))
3391       MovDir[newx][newy] = 0;
3392 #else
3393     if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
3394       MovDir[newx][newy] = 0;
3395 #endif
3396
3397     DrawLevelField(x, y);
3398     DrawLevelField(newx, newy);
3399
3400     Stop[newx][newy] = TRUE;
3401     JustStopped[newx][newy] = 3;
3402
3403     if (DONT_TOUCH(element))    /* object may be nasty to player or others */
3404     {
3405       TestIfBadThingTouchesHero(newx, newy);
3406       TestIfBadThingTouchesFriend(newx, newy);
3407       TestIfBadThingTouchesOtherBadThing(newx, newy);
3408     }
3409     else if (element == EL_PENGUIN)
3410       TestIfFriendTouchesBadThing(newx, newy);
3411
3412     if (CAN_SMASH(element) && direction == MV_DOWN &&
3413         (newy == lev_fieldy-1 || !IS_FREE(x, newy+1)))
3414       Impact(x, newy);
3415   }
3416   else                          /* still moving on */
3417   {
3418 #if 0
3419     if (GfxAction[x][y] == GFX_ACTION_DEFAULT)
3420     {
3421       printf("reset GfxAction...\n");
3422
3423       GfxAction[x][y] = GFX_ACTION_MOVING;
3424     }
3425 #endif
3426
3427     DrawLevelField(x, y);
3428   }
3429 }
3430
3431 int AmoebeNachbarNr(int ax, int ay)
3432 {
3433   int i;
3434   int element = Feld[ax][ay];
3435   int group_nr = 0;
3436   static int xy[4][2] =
3437   {
3438     { 0, -1 },
3439     { -1, 0 },
3440     { +1, 0 },
3441     { 0, +1 }
3442   };
3443
3444   for (i=0; i<4; i++)
3445   {
3446     int x = ax + xy[i][0];
3447     int y = ay + xy[i][1];
3448
3449     if (!IN_LEV_FIELD(x, y))
3450       continue;
3451
3452     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
3453       group_nr = AmoebaNr[x][y];
3454   }
3455
3456   return group_nr;
3457 }
3458
3459 void AmoebenVereinigen(int ax, int ay)
3460 {
3461   int i, x, y, xx, yy;
3462   int new_group_nr = AmoebaNr[ax][ay];
3463   static int xy[4][2] =
3464   {
3465     { 0, -1 },
3466     { -1, 0 },
3467     { +1, 0 },
3468     { 0, +1 }
3469   };
3470
3471   if (new_group_nr == 0)
3472     return;
3473
3474   for (i=0; i<4; i++)
3475   {
3476     x = ax + xy[i][0];
3477     y = ay + xy[i][1];
3478
3479     if (!IN_LEV_FIELD(x, y))
3480       continue;
3481
3482     if ((Feld[x][y] == EL_AMOEBA_FULL ||
3483          Feld[x][y] == EL_BD_AMOEBA ||
3484          Feld[x][y] == EL_AMOEBA_DEAD) &&
3485         AmoebaNr[x][y] != new_group_nr)
3486     {
3487       int old_group_nr = AmoebaNr[x][y];
3488
3489       if (old_group_nr == 0)
3490         return;
3491
3492       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
3493       AmoebaCnt[old_group_nr] = 0;
3494       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
3495       AmoebaCnt2[old_group_nr] = 0;
3496
3497       for (yy=0; yy<lev_fieldy; yy++)
3498       {
3499         for (xx=0; xx<lev_fieldx; xx++)
3500         {
3501           if (AmoebaNr[xx][yy] == old_group_nr)
3502             AmoebaNr[xx][yy] = new_group_nr;
3503         }
3504       }
3505     }
3506   }
3507 }
3508
3509 void AmoebeUmwandeln(int ax, int ay)
3510 {
3511   int i, x, y;
3512
3513   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
3514   {
3515     int group_nr = AmoebaNr[ax][ay];
3516
3517 #ifdef DEBUG
3518     if (group_nr == 0)
3519     {
3520       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
3521       printf("AmoebeUmwandeln(): This should never happen!\n");
3522       return;
3523     }
3524 #endif
3525
3526     for (y=0; y<lev_fieldy; y++)
3527     {
3528       for (x=0; x<lev_fieldx; x++)
3529       {
3530         if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
3531         {
3532           AmoebaNr[x][y] = 0;
3533           Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
3534         }
3535       }
3536     }
3537     PlaySoundLevel(ax, ay, (IS_GEM(level.amoeba_content) ?
3538                             SND_AMOEBA_TURNING_TO_GEM :
3539                             SND_AMOEBA_TURNING_TO_ROCK));
3540     Bang(ax, ay);
3541   }
3542   else
3543   {
3544     static int xy[4][2] =
3545     {
3546       { 0, -1 },
3547       { -1, 0 },
3548       { +1, 0 },
3549       { 0, +1 }
3550     };
3551
3552     for (i=0; i<4; i++)
3553     {
3554       x = ax + xy[i][0];
3555       y = ay + xy[i][1];
3556
3557       if (!IN_LEV_FIELD(x, y))
3558         continue;
3559
3560       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
3561       {
3562         PlaySoundLevel(x, y, (IS_GEM(level.amoeba_content) ?
3563                               SND_AMOEBA_TURNING_TO_GEM :
3564                               SND_AMOEBA_TURNING_TO_ROCK));
3565         Bang(x, y);
3566       }
3567     }
3568   }
3569 }
3570
3571 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
3572 {
3573   int x, y;
3574   int group_nr = AmoebaNr[ax][ay];
3575   boolean done = FALSE;
3576
3577 #ifdef DEBUG
3578   if (group_nr == 0)
3579   {
3580     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
3581     printf("AmoebeUmwandelnBD(): This should never happen!\n");
3582     return;
3583   }
3584 #endif
3585
3586   for (y=0; y<lev_fieldy; y++)
3587   {
3588     for (x=0; x<lev_fieldx; x++)
3589     {
3590       if (AmoebaNr[x][y] == group_nr &&
3591           (Feld[x][y] == EL_AMOEBA_DEAD ||
3592            Feld[x][y] == EL_BD_AMOEBA ||
3593            Feld[x][y] == EL_AMOEBA_CREATING))
3594       {
3595         AmoebaNr[x][y] = 0;
3596         Feld[x][y] = new_element;
3597         InitField(x, y, FALSE);
3598         DrawLevelField(x, y);
3599         done = TRUE;
3600       }
3601     }
3602   }
3603
3604   if (done)
3605     PlaySoundLevel(ax, ay, (new_element == EL_BD_ROCK ?
3606                             SND_BD_AMOEBA_TURNING_TO_ROCK :
3607                             SND_BD_AMOEBA_TURNING_TO_GEM));
3608 }
3609
3610 void AmoebeWaechst(int x, int y)
3611 {
3612   static unsigned long sound_delay = 0;
3613   static unsigned long sound_delay_value = 0;
3614
3615   if (!MovDelay[x][y])          /* start new growing cycle */
3616   {
3617     MovDelay[x][y] = 7;
3618
3619     if (DelayReached(&sound_delay, sound_delay_value))
3620     {
3621       if (Store[x][y] == EL_BD_AMOEBA)
3622         PlaySoundLevel(x, y, SND_BD_AMOEBA_CREATING);
3623       else
3624         PlaySoundLevel(x, y, SND_AMOEBA_CREATING);
3625       sound_delay_value = 30;
3626     }
3627   }
3628
3629   if (MovDelay[x][y])           /* wait some time before growing bigger */
3630   {
3631     MovDelay[x][y]--;
3632     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3633     {
3634       int frame = getGraphicAnimationFrame(IMG_AMOEBA_CREATING,
3635                                            6 - MovDelay[x][y]);
3636
3637       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_CREATING, frame);
3638     }
3639
3640     if (!MovDelay[x][y])
3641     {
3642       Feld[x][y] = Store[x][y];
3643       Store[x][y] = 0;
3644       DrawLevelField(x, y);
3645     }
3646   }
3647 }
3648
3649 void AmoebaDisappearing(int x, int y)
3650 {
3651   static unsigned long sound_delay = 0;
3652   static unsigned long sound_delay_value = 0;
3653
3654   if (!MovDelay[x][y])          /* start new shrinking cycle */
3655   {
3656     MovDelay[x][y] = 7;
3657
3658     if (DelayReached(&sound_delay, sound_delay_value))
3659       sound_delay_value = 30;
3660   }
3661
3662   if (MovDelay[x][y])           /* wait some time before shrinking */
3663   {
3664     MovDelay[x][y]--;
3665     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3666     {
3667       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
3668                                            6 - MovDelay[x][y]);
3669
3670       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
3671     }
3672
3673     if (!MovDelay[x][y])
3674     {
3675       Feld[x][y] = EL_EMPTY;
3676       DrawLevelField(x, y);
3677
3678       /* don't let mole enter this field in this cycle;
3679          (give priority to objects falling to this field from above) */
3680       Stop[x][y] = TRUE;
3681     }
3682   }
3683 }
3684
3685 void AmoebeAbleger(int ax, int ay)
3686 {
3687   int i;
3688   int element = Feld[ax][ay];
3689   int newax = ax, neway = ay;
3690   static int xy[4][2] =
3691   {
3692     { 0, -1 },
3693     { -1, 0 },
3694     { +1, 0 },
3695     { 0, +1 }
3696   };
3697
3698   if (!level.amoeba_speed)
3699   {
3700     Feld[ax][ay] = EL_AMOEBA_DEAD;
3701     DrawLevelField(ax, ay);
3702     return;
3703   }
3704
3705   if (!MovDelay[ax][ay])        /* start making new amoeba field */
3706     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
3707
3708   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
3709   {
3710     MovDelay[ax][ay]--;
3711     if (MovDelay[ax][ay])
3712       return;
3713   }
3714
3715   if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
3716   {
3717     int start = RND(4);
3718     int x = ax + xy[start][0];
3719     int y = ay + xy[start][1];
3720
3721     if (!IN_LEV_FIELD(x, y))
3722       return;
3723
3724     if (IS_FREE(x, y) ||
3725         Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
3726     {
3727       newax = x;
3728       neway = y;
3729     }
3730
3731     if (newax == ax && neway == ay)
3732       return;
3733   }
3734   else                          /* normal or "filled" (BD style) amoeba */
3735   {
3736     int start = RND(4);
3737     boolean waiting_for_player = FALSE;
3738
3739     for (i=0; i<4; i++)
3740     {
3741       int j = (start + i) % 4;
3742       int x = ax + xy[j][0];
3743       int y = ay + xy[j][1];
3744
3745       if (!IN_LEV_FIELD(x, y))
3746         continue;
3747
3748       if (IS_FREE(x, y) ||
3749           Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
3750       {
3751         newax = x;
3752         neway = y;
3753         break;
3754       }
3755       else if (IS_PLAYER(x, y))
3756         waiting_for_player = TRUE;
3757     }
3758
3759     if (newax == ax && neway == ay)             /* amoeba cannot grow */
3760     {
3761       if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
3762       {
3763         Feld[ax][ay] = EL_AMOEBA_DEAD;
3764         DrawLevelField(ax, ay);
3765         AmoebaCnt[AmoebaNr[ax][ay]]--;
3766
3767         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
3768         {
3769           if (element == EL_AMOEBA_FULL)
3770             AmoebeUmwandeln(ax, ay);
3771           else if (element == EL_BD_AMOEBA)
3772             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
3773         }
3774       }
3775       return;
3776     }
3777     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
3778     {
3779       /* amoeba gets larger by growing in some direction */
3780
3781       int new_group_nr = AmoebaNr[ax][ay];
3782
3783 #ifdef DEBUG
3784   if (new_group_nr == 0)
3785   {
3786     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
3787     printf("AmoebeAbleger(): This should never happen!\n");
3788     return;
3789   }
3790 #endif
3791
3792       AmoebaNr[newax][neway] = new_group_nr;
3793       AmoebaCnt[new_group_nr]++;
3794       AmoebaCnt2[new_group_nr]++;
3795
3796       /* if amoeba touches other amoeba(s) after growing, unify them */
3797       AmoebenVereinigen(newax, neway);
3798
3799       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
3800       {
3801         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
3802         return;
3803       }
3804     }
3805   }
3806
3807   if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
3808       (neway == lev_fieldy - 1 && newax != ax))
3809   {
3810     Feld[newax][neway] = EL_AMOEBA_CREATING;    /* creation of new amoeba */
3811     Store[newax][neway] = element;
3812   }
3813   else if (neway == ay)
3814   {
3815     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
3816     PlaySoundLevel(newax, neway, SND_AMOEBA_DROP_CREATING);
3817   }
3818   else
3819   {
3820     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
3821     Feld[ax][ay] = EL_AMOEBA_DRIPPING;
3822     Store[ax][ay] = EL_AMOEBA_DROP;
3823     ContinueMoving(ax, ay);
3824     return;
3825   }
3826
3827   DrawLevelField(newax, neway);
3828 }
3829
3830 void Life(int ax, int ay)
3831 {
3832   int x1, y1, x2, y2;
3833   static int life[4] = { 2, 3, 3, 3 };  /* parameters for "game of life" */
3834   int life_time = 40;
3835   int element = Feld[ax][ay];
3836   boolean changed = FALSE;
3837
3838   if (Stop[ax][ay])
3839     return;
3840
3841   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
3842     MovDelay[ax][ay] = life_time;
3843
3844   if (MovDelay[ax][ay])         /* wait some time before next cycle */
3845   {
3846     MovDelay[ax][ay]--;
3847     if (MovDelay[ax][ay])
3848       return;
3849   }
3850
3851   for (y1=-1; y1<2; y1++) for(x1=-1; x1<2; x1++)
3852   {
3853     int xx = ax+x1, yy = ay+y1;
3854     int nachbarn = 0;
3855
3856     if (!IN_LEV_FIELD(xx, yy))
3857       continue;
3858
3859     for (y2=-1; y2<2; y2++) for (x2=-1; x2<2; x2++)
3860     {
3861       int x = xx+x2, y = yy+y2;
3862
3863       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
3864         continue;
3865
3866       if (((Feld[x][y] == element ||
3867             (element == EL_GAMEOFLIFE && IS_PLAYER(x, y))) &&
3868            !Stop[x][y]) ||
3869           (IS_FREE(x, y) && Stop[x][y]))
3870         nachbarn++;
3871     }
3872
3873     if (xx == ax && yy == ay)           /* field in the middle */
3874     {
3875       if (nachbarn < life[0] || nachbarn > life[1])
3876       {
3877         Feld[xx][yy] = EL_EMPTY;
3878         if (!Stop[xx][yy])
3879           DrawLevelField(xx, yy);
3880         Stop[xx][yy] = TRUE;
3881         changed = TRUE;
3882       }
3883     }
3884     else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
3885     {                                   /* free border field */
3886       if (nachbarn >= life[2] && nachbarn <= life[3])
3887       {
3888         Feld[xx][yy] = element;
3889         MovDelay[xx][yy] = (element == EL_GAMEOFLIFE ? 0 : life_time-1);
3890         if (!Stop[xx][yy])
3891           DrawLevelField(xx, yy);
3892         Stop[xx][yy] = TRUE;
3893         changed = TRUE;
3894       }
3895     }
3896   }
3897
3898   if (changed)
3899     PlaySoundLevel(ax, ay, element == EL_GAMEOFLIFE ? SND_GAMEOFLIFE_CREATING :
3900                    SND_BIOMAZE_CREATING);
3901 }
3902
3903 void RobotWheel(int x, int y)
3904 {
3905   if (!MovDelay[x][y])          /* next animation frame */
3906     MovDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
3907
3908   if (MovDelay[x][y])           /* wait some time before next frame */
3909   {
3910     MovDelay[x][y]--;
3911     if (MovDelay[x][y])
3912     {
3913       if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3914       {
3915         int frame = getGraphicAnimationFrame(IMG_ROBOT_WHEEL_ACTIVE, -1);
3916
3917         DrawGraphic(SCREENX(x), SCREENY(y), IMG_ROBOT_WHEEL_ACTIVE, frame);
3918       }
3919
3920       if (!(MovDelay[x][y]%4))
3921         PlaySoundLevel(x, y, SND_ROBOT_WHEEL_ACTIVE);
3922       return;
3923     }
3924   }
3925
3926   Feld[x][y] = EL_ROBOT_WHEEL;
3927   DrawLevelField(x, y);
3928   if (ZX == x && ZY == y)
3929     ZX = ZY = -1;
3930 }
3931
3932 void TimegateWheel(int x, int y)
3933 {
3934   if (!MovDelay[x][y])          /* next animation frame */
3935     MovDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
3936
3937   if (MovDelay[x][y])           /* wait some time before next frame */
3938   {
3939     MovDelay[x][y]--;
3940     if (MovDelay[x][y])
3941     {
3942       if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3943       {
3944         int frame = getGraphicAnimationFrame(IMG_TIMEGATE_SWITCH_ACTIVE, -1);
3945
3946         DrawGraphic(SCREENX(x), SCREENY(y), IMG_TIMEGATE_SWITCH_ACTIVE, frame);
3947       }
3948
3949       if (!(MovDelay[x][y]%4))
3950         PlaySoundLevel(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
3951       return;
3952     }
3953   }
3954
3955   Feld[x][y] = EL_TIMEGATE_SWITCH;
3956   DrawLevelField(x, y);
3957   if (ZX == x && ZY == y)
3958     ZX = ZY = -1;
3959 }
3960
3961 void Blubber(int x, int y)
3962 {
3963   DrawGraphicAnimation(x, y, IMG_ACID);
3964 }
3965
3966 void NussKnacken(int x, int y)
3967 {
3968   if (!MovDelay[x][y])          /* next animation frame */
3969     MovDelay[x][y] = 7;
3970
3971   if (MovDelay[x][y])           /* wait some time before next frame */
3972   {
3973     MovDelay[x][y]--;
3974     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3975     {
3976       int frame = getGraphicAnimationFrame(IMG_NUT_CRACKING,
3977                                            6 - MovDelay[x][y]);
3978
3979       DrawGraphic(SCREENX(x), SCREENY(y), IMG_NUT_CRACKING, frame);
3980     }
3981
3982     if (!MovDelay[x][y])
3983     {
3984       Feld[x][y] = EL_EMERALD;
3985       DrawLevelField(x, y);
3986     }
3987   }
3988 }
3989
3990 void BreakingPearl(int x, int y)
3991 {
3992   if (!MovDelay[x][y])          /* next animation frame */
3993     MovDelay[x][y] = 9;
3994
3995   if (MovDelay[x][y])           /* wait some time before next frame */
3996   {
3997     MovDelay[x][y]--;
3998     if (MovDelay[x][y]/2 && 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     if (!MovDelay[x][y])
4007     {
4008       Feld[x][y] = EL_EMPTY;
4009       DrawLevelField(x, y);
4010     }
4011   }
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     if (!(MovDelay[x][y]%delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4065     {
4066       int frame = getGraphicAnimationFrame(IMG_EXIT_OPENING,
4067                                            29 - MovDelay[x][y]);
4068
4069       DrawGraphic(SCREENX(x), SCREENY(y), IMG_EXIT_OPENING, frame);
4070     }
4071
4072     if (!MovDelay[x][y])
4073     {
4074       Feld[x][y] = EL_EXIT_OPEN;
4075       DrawLevelField(x, y);
4076     }
4077   }
4078 }
4079
4080 void AusgangstuerBlinken(int x, int y)
4081 {
4082   DrawGraphicAnimation(x, y, IMG_EXIT_OPEN);
4083 }
4084
4085 void OpenSwitchgate(int x, int y)
4086 {
4087   int delay = 6;
4088
4089   if (!MovDelay[x][y])          /* next animation frame */
4090     MovDelay[x][y] = 5 * delay;
4091
4092   if (MovDelay[x][y])           /* wait some time before next frame */
4093   {
4094     MovDelay[x][y]--;
4095
4096     if (!(MovDelay[x][y] % delay) && 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     if (!MovDelay[x][y])
4105     {
4106       Feld[x][y] = EL_SWITCHGATE_OPEN;
4107       DrawLevelField(x, y);
4108     }
4109   }
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) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4124     {
4125       int frame = getGraphicAnimationFrame(IMG_SWITCHGATE_CLOSING,
4126                                            29 - MovDelay[x][y]);
4127
4128       DrawGraphic(SCREENX(x), SCREENY(y), IMG_SWITCHGATE_CLOSING, frame);
4129     }
4130
4131     if (!MovDelay[x][y])
4132     {
4133       Feld[x][y] = EL_SWITCHGATE_CLOSED;
4134       DrawLevelField(x, y);
4135     }
4136   }
4137 }
4138
4139 void OpenTimegate(int x, int y)
4140 {
4141   int delay = 6;
4142
4143   if (!MovDelay[x][y])          /* next animation frame */
4144     MovDelay[x][y] = 5 * delay;
4145
4146   if (MovDelay[x][y])           /* wait some time before next frame */
4147   {
4148     MovDelay[x][y]--;
4149
4150     if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4151     {
4152       int frame = getGraphicAnimationFrame(IMG_TIMEGATE_OPENING,
4153                                            29 - MovDelay[x][y]);
4154
4155       DrawGraphic(SCREENX(x), SCREENY(y), IMG_TIMEGATE_OPENING, frame);
4156     }
4157
4158     if (!MovDelay[x][y])
4159     {
4160       Feld[x][y] = EL_TIMEGATE_OPEN;
4161       DrawLevelField(x, y);
4162     }
4163   }
4164 }
4165
4166 void CloseTimegate(int x, int y)
4167 {
4168   int delay = 6;
4169
4170   if (!MovDelay[x][y])          /* next animation frame */
4171     MovDelay[x][y] = 5 * delay;
4172
4173   if (MovDelay[x][y])           /* wait some time before next frame */
4174   {
4175     MovDelay[x][y]--;
4176
4177     if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4178     {
4179       int frame = getGraphicAnimationFrame(IMG_TIMEGATE_CLOSING,
4180                                            29 - MovDelay[x][y]);
4181
4182       DrawGraphic(SCREENX(x), SCREENY(y), IMG_TIMEGATE_CLOSING, frame);
4183     }
4184
4185     if (!MovDelay[x][y])
4186     {
4187       Feld[x][y] = EL_TIMEGATE_CLOSED;
4188       DrawLevelField(x, y);
4189     }
4190   }
4191 }
4192
4193 static void CloseAllOpenTimegates()
4194 {
4195   int x, y;
4196
4197   for (y=0; y<lev_fieldy; y++)
4198   {
4199     for (x=0; x<lev_fieldx; x++)
4200     {
4201       int element = Feld[x][y];
4202
4203       if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
4204       {
4205         Feld[x][y] = EL_TIMEGATE_CLOSING;
4206         PlaySoundLevel(x, y, SND_TIMEGATE_CLOSING);
4207       }
4208     }
4209   }
4210 }
4211
4212 void EdelsteinFunkeln(int x, int y)
4213 {
4214   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
4215     return;
4216
4217   if (Feld[x][y] == EL_BD_DIAMOND)
4218     DrawGraphicAnimation(x, y, IMG_BD_DIAMOND);
4219   else
4220   {
4221     if (!MovDelay[x][y])        /* next animation frame */
4222       MovDelay[x][y] = 11 * !SimpleRND(500);
4223
4224     if (MovDelay[x][y])         /* wait some time before next frame */
4225     {
4226       MovDelay[x][y]--;
4227
4228       if (setup.direct_draw && MovDelay[x][y])
4229         SetDrawtoField(DRAW_BUFFERED);
4230
4231       DrawGraphic(SCREENX(x), SCREENY(y), el2img(Feld[x][y]), 0);
4232
4233       if (MovDelay[x][y])
4234       {
4235         int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
4236                                              10 - MovDelay[x][y]);
4237
4238         DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
4239
4240         if (setup.direct_draw)
4241         {
4242           int dest_x, dest_y;
4243
4244           dest_x = FX + SCREENX(x) * TILEX;
4245           dest_y = FY + SCREENY(y) * TILEY;
4246
4247           BlitBitmap(drawto_field, window,
4248                      dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
4249           SetDrawtoField(DRAW_DIRECT);
4250         }
4251       }
4252     }
4253   }
4254 }
4255
4256 void MauerWaechst(int x, int y)
4257 {
4258   int delay = 6;
4259
4260   if (!MovDelay[x][y])          /* next animation frame */
4261     MovDelay[x][y] = 3 * delay;
4262
4263   if (MovDelay[x][y])           /* wait some time before next frame */
4264   {
4265     MovDelay[x][y]--;
4266
4267     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4268     {
4269       int graphic = el_dir2img(Feld[x][y], MovDir[x][y]);
4270       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
4271
4272       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4273     }
4274
4275     if (!MovDelay[x][y])
4276     {
4277       if (MovDir[x][y] == MV_LEFT)
4278       {
4279         if (IN_LEV_FIELD(x - 1, y) && IS_MAUER(Feld[x - 1][y]))
4280           DrawLevelField(x - 1, y);
4281       }
4282       else if (MovDir[x][y] == MV_RIGHT)
4283       {
4284         if (IN_LEV_FIELD(x + 1, y) && IS_MAUER(Feld[x + 1][y]))
4285           DrawLevelField(x + 1, y);
4286       }
4287       else if (MovDir[x][y] == MV_UP)
4288       {
4289         if (IN_LEV_FIELD(x, y - 1) && IS_MAUER(Feld[x][y - 1]))
4290           DrawLevelField(x, y - 1);
4291       }
4292       else
4293       {
4294         if (IN_LEV_FIELD(x, y + 1) && IS_MAUER(Feld[x][y + 1]))
4295           DrawLevelField(x, y + 1);
4296       }
4297
4298       Feld[x][y] = Store[x][y];
4299       Store[x][y] = 0;
4300       MovDir[x][y] = MV_NO_MOVING;
4301       DrawLevelField(x, y);
4302     }
4303   }
4304 }
4305
4306 void MauerAbleger(int ax, int ay)
4307 {
4308   int element = Feld[ax][ay];
4309   boolean oben_frei = FALSE, unten_frei = FALSE;
4310   boolean links_frei = FALSE, rechts_frei = FALSE;
4311   boolean oben_massiv = FALSE, unten_massiv = FALSE;
4312   boolean links_massiv = FALSE, rechts_massiv = FALSE;
4313   boolean new_wall = FALSE;
4314
4315   if (!MovDelay[ax][ay])        /* start building new wall */
4316     MovDelay[ax][ay] = 6;
4317
4318   if (MovDelay[ax][ay])         /* wait some time before building new wall */
4319   {
4320     MovDelay[ax][ay]--;
4321     if (MovDelay[ax][ay])
4322       return;
4323   }
4324
4325   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
4326     oben_frei = TRUE;
4327   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
4328     unten_frei = TRUE;
4329   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
4330     links_frei = TRUE;
4331   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
4332     rechts_frei = TRUE;
4333
4334   if (element == EL_WALL_GROWING_Y || element == EL_WALL_GROWING_XY)
4335   {
4336     if (oben_frei)
4337     {
4338       Feld[ax][ay-1] = EL_WALL_GROWING_ACTIVE;
4339       Store[ax][ay-1] = element;
4340       MovDir[ax][ay-1] = MV_UP;
4341       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
4342         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
4343                     IMG_WALL_GROWING_ACTIVE_UP, 0);
4344       new_wall = TRUE;
4345     }
4346     if (unten_frei)
4347     {
4348       Feld[ax][ay+1] = EL_WALL_GROWING_ACTIVE;
4349       Store[ax][ay+1] = element;
4350       MovDir[ax][ay+1] = MV_DOWN;
4351       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
4352         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
4353                     IMG_WALL_GROWING_ACTIVE_DOWN, 0);
4354       new_wall = TRUE;
4355     }
4356   }
4357
4358   if (element == EL_WALL_GROWING_X || element == EL_WALL_GROWING_XY ||
4359       element == EL_WALL_GROWING)
4360   {
4361     if (links_frei)
4362     {
4363       Feld[ax-1][ay] = EL_WALL_GROWING_ACTIVE;
4364       Store[ax-1][ay] = element;
4365       MovDir[ax-1][ay] = MV_LEFT;
4366       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
4367         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
4368                     IMG_WALL_GROWING_ACTIVE_LEFT, 0);
4369       new_wall = TRUE;
4370     }
4371
4372     if (rechts_frei)
4373     {
4374       Feld[ax+1][ay] = EL_WALL_GROWING_ACTIVE;
4375       Store[ax+1][ay] = element;
4376       MovDir[ax+1][ay] = MV_RIGHT;
4377       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
4378         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
4379                     IMG_WALL_GROWING_ACTIVE_RIGHT, 0);
4380       new_wall = TRUE;
4381     }
4382   }
4383
4384   if (element == EL_WALL_GROWING && (links_frei || rechts_frei))
4385     DrawLevelField(ax, ay);
4386
4387   if (!IN_LEV_FIELD(ax, ay-1) || IS_MAUER(Feld[ax][ay-1]))
4388     oben_massiv = TRUE;
4389   if (!IN_LEV_FIELD(ax, ay+1) || IS_MAUER(Feld[ax][ay+1]))
4390     unten_massiv = TRUE;
4391   if (!IN_LEV_FIELD(ax-1, ay) || IS_MAUER(Feld[ax-1][ay]))
4392     links_massiv = TRUE;
4393   if (!IN_LEV_FIELD(ax+1, ay) || IS_MAUER(Feld[ax+1][ay]))
4394     rechts_massiv = TRUE;
4395
4396   if (((oben_massiv && unten_massiv) ||
4397        element == EL_WALL_GROWING_X || element == EL_WALL_GROWING) &&
4398       ((links_massiv && rechts_massiv) ||
4399        element == EL_WALL_GROWING_Y))
4400     Feld[ax][ay] = EL_WALL;
4401
4402   if (new_wall)
4403     PlaySoundLevel(ax, ay, SND_WALL_GROWING);
4404 }
4405
4406 void CheckForDragon(int x, int y)
4407 {
4408   int i, j;
4409   boolean dragon_found = FALSE;
4410   static int xy[4][2] =
4411   {
4412     { 0, -1 },
4413     { -1, 0 },
4414     { +1, 0 },
4415     { 0, +1 }
4416   };
4417
4418   for (i=0; i<4; i++)
4419   {
4420     for (j=0; j<4; j++)
4421     {
4422       int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
4423
4424       if (IN_LEV_FIELD(xx, yy) &&
4425           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
4426       {
4427         if (Feld[xx][yy] == EL_DRAGON)
4428           dragon_found = TRUE;
4429       }
4430       else
4431         break;
4432     }
4433   }
4434
4435   if (!dragon_found)
4436   {
4437     for (i=0; i<4; i++)
4438     {
4439       for (j=0; j<3; j++)
4440       {
4441         int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
4442   
4443         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
4444         {
4445           Feld[xx][yy] = EL_EMPTY;
4446           DrawLevelField(xx, yy);
4447         }
4448         else
4449           break;
4450       }
4451     }
4452   }
4453 }
4454
4455 static void CheckBuggyBase(int x, int y)
4456 {
4457   int element = Feld[x][y];
4458
4459   if (element == EL_SP_BUGGY_BASE)
4460   {
4461     if (!MovDelay[x][y])        /* wait some time before activating base */
4462       MovDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
4463
4464     if (MovDelay[x][y])
4465     {
4466       MovDelay[x][y]--;
4467       if (MovDelay[x][y] < 5 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4468         DrawGraphic(SCREENX(x), SCREENY(y), IMG_SP_BUGGY_BASE, 0);
4469       if (MovDelay[x][y])
4470         return;
4471
4472       Feld[x][y] = EL_SP_BUGGY_BASE_ACTIVE;
4473     }
4474   }
4475   else if (element == EL_SP_BUGGY_BASE_ACTIVE)
4476   {
4477     if (!MovDelay[x][y])        /* start activating buggy base */
4478       MovDelay[x][y] = 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND);
4479
4480     if (MovDelay[x][y])
4481     {
4482       MovDelay[x][y]--;
4483       if (MovDelay[x][y])
4484       {
4485         int i;
4486         static int xy[4][2] =
4487         {
4488           { 0, -1 },
4489           { -1, 0 },
4490           { +1, 0 },
4491           { 0, +1 }
4492         };
4493
4494         if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4495         {
4496           int graphic = IMG_SP_BUGGY_BASE_ACTIVE;
4497           int frame = getGraphicAnimationFrame(graphic, SimpleRND(100));
4498
4499           DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4500         }
4501
4502         for (i=0; i<4; i++)
4503         {
4504           int xx = x + xy[i][0], yy = y + xy[i][1];
4505
4506           if (IS_PLAYER(xx, yy))
4507           {
4508             PlaySoundLevel(x, y, SND_SP_BUGGY_BASE_ACTIVE);
4509             break;
4510           }
4511         }
4512
4513         return;
4514       }
4515
4516       Feld[x][y] = EL_SP_BUGGY_BASE;
4517       DrawLevelField(x, y);
4518     }
4519   }
4520 }
4521
4522 static void CheckTrap(int x, int y)
4523 {
4524   int element = Feld[x][y];
4525
4526   if (element == EL_TRAP)
4527   {
4528     if (!MovDelay[x][y])        /* wait some time before activating trap */
4529       MovDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
4530
4531     if (MovDelay[x][y])
4532     {
4533       MovDelay[x][y]--;
4534       if (MovDelay[x][y])
4535         return;
4536
4537       Feld[x][y] = EL_TRAP_ACTIVE;
4538       PlaySoundLevel(x, y, SND_TRAP_ACTIVATING);
4539     }
4540   }
4541   else if (element == EL_TRAP_ACTIVE)
4542   {
4543     int delay = 4;
4544     int num_frames = 8;
4545
4546     if (!MovDelay[x][y])        /* start activating trap */
4547       MovDelay[x][y] = num_frames * delay;
4548
4549     if (MovDelay[x][y])
4550     {
4551       MovDelay[x][y]--;
4552
4553       if (MovDelay[x][y])
4554       {
4555         if (!(MovDelay[x][y] % delay))
4556         {
4557           if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4558           {
4559             int graphic = IMG_TRAP_ACTIVE;
4560             int frame = getGraphicAnimationFrame(graphic, 31 - MovDelay[x][y]);
4561
4562             DrawGraphic(SCREENX(x),SCREENY(y), graphic, frame);
4563             DrawCrumbledSand(SCREENX(x), SCREENY(y));
4564           }
4565         }
4566
4567         return;
4568       }
4569
4570       Feld[x][y] = EL_TRAP;
4571       DrawLevelField(x, y);
4572     }
4573   }
4574 }
4575
4576 static void DrawBeltAnimation(int x, int y, int element)
4577 {
4578   int belt_nr = getBeltNrFromBeltActiveElement(element);
4579   int belt_dir = game.belt_dir[belt_nr];
4580
4581   if (belt_dir != MV_NO_MOVING)
4582   {
4583     int graphic = el2img(element);
4584
4585     DrawGraphicAnimation(x, y, graphic);
4586
4587     if (!(FrameCounter % 2))
4588       PlaySoundLevel(x, y, SND_CONVEYOR_BELT_ACTIVE);
4589   }
4590 }
4591
4592 static void PlayerActions(struct PlayerInfo *player, byte player_action)
4593 {
4594   static byte stored_player_action[MAX_PLAYERS];
4595   static int num_stored_actions = 0;
4596 #if 0
4597   static boolean save_tape_entry = FALSE;
4598 #endif
4599   boolean moved = FALSE, snapped = FALSE, bombed = FALSE;
4600   int left      = player_action & JOY_LEFT;
4601   int right     = player_action & JOY_RIGHT;
4602   int up        = player_action & JOY_UP;
4603   int down      = player_action & JOY_DOWN;
4604   int button1   = player_action & JOY_BUTTON_1;
4605   int button2   = player_action & JOY_BUTTON_2;
4606   int dx        = (left ? -1    : right ? 1     : 0);
4607   int dy        = (up   ? -1    : down  ? 1     : 0);
4608
4609   stored_player_action[player->index_nr] = 0;
4610   num_stored_actions++;
4611
4612   if (!player->active || tape.pausing)
4613     return;
4614
4615   if (player_action)
4616   {
4617 #if 0
4618     save_tape_entry = TRUE;
4619 #endif
4620     player->frame_reset_delay = 0;
4621
4622     if (button1)
4623       snapped = SnapField(player, dx, dy);
4624     else
4625     {
4626       if (button2)
4627         bombed = PlaceBomb(player);
4628       moved = MoveFigure(player, dx, dy);
4629     }
4630
4631     if (tape.single_step && tape.recording && !tape.pausing)
4632     {
4633       if (button1 || (bombed && !moved))
4634       {
4635         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
4636         SnapField(player, 0, 0);                /* stop snapping */
4637       }
4638     }
4639
4640 #if 0
4641     if (tape.recording && (moved || snapped || bombed))
4642     {
4643       if (bombed && !moved)
4644         player_action &= JOY_BUTTON;
4645
4646       stored_player_action[player->index_nr] = player_action;
4647       save_tape_entry = TRUE;
4648     }
4649     else if (tape.playing && snapped)
4650       SnapField(player, 0, 0);                  /* stop snapping */
4651 #else
4652     stored_player_action[player->index_nr] = player_action;
4653 #endif
4654   }
4655   else
4656   {
4657     /* no actions for this player (no input at player's configured device) */
4658
4659     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
4660     SnapField(player, 0, 0);
4661     CheckGravityMovement(player);
4662
4663 #if 1
4664     if (player->MovPos == 0)    /* needed for tape.playing */
4665       player->is_moving = FALSE;
4666 #endif
4667 #if 0
4668     if (player->MovPos == 0)    /* needed for tape.playing */
4669       player->last_move_dir = MV_NO_MOVING;
4670
4671     /* !!! CHECK THIS AGAIN !!!
4672        (Seems to be needed for some EL_ROBOT stuff, but breaks
4673        tapes when walking through pipes!)
4674     */
4675
4676     /* it seems that "player->last_move_dir" is misused as some sort of
4677        "player->is_just_moving_in_this_moment", which is needed for the
4678        robot stuff (robots don't kill players when they are moving)
4679     */
4680 #endif 
4681
4682     /* if the player does not move for some time, reset animation to start */
4683     if (++player->frame_reset_delay > player->move_delay_value)
4684       player->Frame = 0;
4685   }
4686
4687 #if 0
4688   if (tape.recording && num_stored_actions >= MAX_PLAYERS && save_tape_entry)
4689   {
4690     TapeRecordAction(stored_player_action);
4691     num_stored_actions = 0;
4692     save_tape_entry = FALSE;
4693   }
4694 #else
4695   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
4696   {
4697     TapeRecordAction(stored_player_action);
4698     num_stored_actions = 0;
4699   }
4700 #endif
4701
4702 #if 0
4703   if (tape.playing && !tape.pausing && !player_action &&
4704       tape.counter < tape.length)
4705   {
4706     int jx = player->jx, jy = player->jy;
4707     int next_joy =
4708       tape.pos[tape.counter].action[player->index_nr] & (JOY_LEFT|JOY_RIGHT);
4709
4710     if ((next_joy == JOY_LEFT || next_joy == JOY_RIGHT) &&
4711         (player->MovDir != JOY_UP && player->MovDir != JOY_DOWN))
4712     {
4713       int dx = (next_joy == JOY_LEFT ? -1 : +1);
4714
4715       if (IN_LEV_FIELD(jx+dx, jy) && IS_PUSHABLE(Feld[jx+dx][jy]))
4716       {
4717         int el = Feld[jx+dx][jy];
4718         int push_delay = (IS_SB_ELEMENT(el) || el == EL_SATELLITE ? 2 :
4719                           (el == EL_BALLOON || el == EL_SPRING) ? 0 : 10);
4720
4721         if (tape.delay_played + push_delay >= tape.pos[tape.counter].delay)
4722         {
4723           player->MovDir = next_joy;
4724           player->Frame = FrameCounter % 4;
4725           player->Pushing = TRUE;
4726         }
4727       }
4728     }
4729   }
4730 #endif
4731 }
4732
4733 void GameActions()
4734 {
4735   static unsigned long action_delay = 0;
4736   unsigned long action_delay_value;
4737   int sieb_x = 0, sieb_y = 0;
4738   int i, x, y, element;
4739   byte *recorded_player_action;
4740   byte summarized_player_action = 0;
4741
4742   if (game_status != PLAYING)
4743     return;
4744
4745   action_delay_value =
4746     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
4747
4748   if (tape.playing && tape.index_search && !tape.pausing)
4749     action_delay_value = 0;
4750
4751   /* ---------- main game synchronization point ---------- */
4752
4753   WaitUntilDelayReached(&action_delay, action_delay_value);
4754
4755   if (network_playing && !network_player_action_received)
4756   {
4757     /*
4758 #ifdef DEBUG
4759     printf("DEBUG: try to get network player actions in time\n");
4760 #endif
4761     */
4762
4763 #if defined(PLATFORM_UNIX)
4764     /* last chance to get network player actions without main loop delay */
4765     HandleNetworking();
4766 #endif
4767
4768     if (game_status != PLAYING)
4769       return;
4770
4771     if (!network_player_action_received)
4772     {
4773       /*
4774 #ifdef DEBUG
4775       printf("DEBUG: failed to get network player actions in time\n");
4776 #endif
4777       */
4778       return;
4779     }
4780   }
4781
4782   if (tape.pausing)
4783     return;
4784
4785   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
4786
4787   for (i=0; i<MAX_PLAYERS; i++)
4788   {
4789     summarized_player_action |= stored_player[i].action;
4790
4791     if (!network_playing)
4792       stored_player[i].effective_action = stored_player[i].action;
4793   }
4794
4795 #if defined(PLATFORM_UNIX)
4796   if (network_playing)
4797     SendToServer_MovePlayer(summarized_player_action);
4798 #endif
4799
4800   if (!options.network && !setup.team_mode)
4801     local_player->effective_action = summarized_player_action;
4802
4803   for (i=0; i<MAX_PLAYERS; i++)
4804   {
4805     int actual_player_action = stored_player[i].effective_action;
4806
4807     if (stored_player[i].programmed_action)
4808       actual_player_action = stored_player[i].programmed_action;
4809
4810     if (recorded_player_action)
4811       actual_player_action = recorded_player_action[i];
4812
4813     PlayerActions(&stored_player[i], actual_player_action);
4814     ScrollFigure(&stored_player[i], SCROLL_GO_ON);
4815   }
4816
4817   network_player_action_received = FALSE;
4818
4819   ScrollScreen(NULL, SCROLL_GO_ON);
4820
4821
4822
4823 #ifdef DEBUG
4824 #if 0
4825   if (TimeFrames == 0 && local_player->active)
4826   {
4827     extern unsigned int last_RND();
4828
4829     printf("DEBUG: %03d last RND was %d \t [state checksum is %d]\n",
4830            TimePlayed, last_RND(), getStateCheckSum(TimePlayed));
4831   }
4832 #endif
4833 #endif
4834
4835 #ifdef DEBUG
4836 #if 0
4837   if (GameFrameDelay >= 500)
4838     printf("FrameCounter == %d\n", FrameCounter);
4839 #endif
4840 #endif
4841
4842
4843
4844   FrameCounter++;
4845   TimeFrames++;
4846
4847   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
4848   {
4849     Stop[x][y] = FALSE;
4850     if (JustStopped[x][y] > 0)
4851       JustStopped[x][y]--;
4852
4853 #if DEBUG
4854     if (IS_BLOCKED(x, y))
4855     {
4856       int oldx, oldy;
4857
4858       Blocked2Moving(x, y, &oldx, &oldy);
4859       if (!IS_MOVING(oldx, oldy))
4860       {
4861         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
4862         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
4863         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
4864         printf("GameActions(): This should never happen!\n");
4865       }
4866     }
4867 #endif
4868   }
4869
4870   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
4871   {
4872     element = Feld[x][y];
4873
4874     if (IS_INACTIVE(element))
4875       continue;
4876
4877     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
4878     {
4879       StartMoving(x, y);
4880
4881       if (IS_GEM(element) || element == EL_SP_INFOTRON)
4882         EdelsteinFunkeln(x, y);
4883     }
4884     else if (IS_MOVING(x, y))
4885       ContinueMoving(x, y);
4886     else if (IS_ACTIVE_BOMB(element))
4887       CheckDynamite(x, y);
4888 #if 0
4889     else if (element == EL_EXPLOSION && !game.explosions_delayed)
4890       Explode(x, y, Frame[x][y], EX_NORMAL);
4891 #endif
4892     else if (element == EL_AMOEBA_CREATING)
4893       AmoebeWaechst(x, y);
4894     else if (element == EL_AMOEBA_SHRINKING)
4895       AmoebaDisappearing(x, y);
4896
4897 #if !USE_NEW_AMOEBA_CODE
4898     else if (IS_AMOEBALIVE(element))
4899       AmoebeAbleger(x, y);
4900 #endif
4901
4902     else if (element == EL_GAMEOFLIFE || element == EL_BIOMAZE)
4903       Life(x, y);
4904     else if (element == EL_ROBOT_WHEEL_ACTIVE)
4905       RobotWheel(x, y);
4906     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4907       TimegateWheel(x, y);
4908     else if (element == EL_ACID)
4909       Blubber(x, y);
4910     else if (element == EL_ACID_SPLASH_LEFT ||
4911              element == EL_ACID_SPLASH_RIGHT)
4912       Blurb(x, y);
4913     else if (element == EL_NUT_CRACKING)
4914       NussKnacken(x, y);
4915     else if (element == EL_PEARL_BREAKING)
4916       BreakingPearl(x, y);
4917     else if (element == EL_EXIT_CLOSED)
4918       AusgangstuerPruefen(x, y);
4919     else if (element == EL_SP_EXIT_CLOSED)
4920       AusgangstuerPruefen_SP(x, y);
4921     else if (element == EL_EXIT_OPENING)
4922       AusgangstuerOeffnen(x, y);
4923     else if (element == EL_EXIT_OPEN)
4924       AusgangstuerBlinken(x, y);
4925     else if (element == EL_SP_EXIT_OPEN)
4926       ;         /* !!! ADD SOME (OPTIONAL) ANIMATIONS HERE !!! */
4927     else if (element == EL_WALL_GROWING_ACTIVE)
4928       MauerWaechst(x, y);
4929     else if (element == EL_WALL_GROWING ||
4930              element == EL_WALL_GROWING_X ||
4931              element == EL_WALL_GROWING_Y ||
4932              element == EL_WALL_GROWING_XY)
4933       MauerAbleger(x, y);
4934     else if (element == EL_FLAMES)
4935       CheckForDragon(x, y);
4936     else if (element == EL_SP_BUGGY_BASE || element == EL_SP_BUGGY_BASE_ACTIVE)
4937       CheckBuggyBase(x, y);
4938     else if (element == EL_TRAP || element == EL_TRAP_ACTIVE)
4939       CheckTrap(x, y);
4940     else if (element == EL_SP_TERMINAL)
4941       DrawGraphicAnimation(x, y, IMG_SP_TERMINAL);
4942     else if (element == EL_SP_TERMINAL_ACTIVE)
4943     {
4944       DrawGraphicAnimation(x, y, IMG_SP_TERMINAL_ACTIVE);
4945
4946 #if 0
4947       if (!(FrameCounter % 4))
4948         PlaySoundLevel(x, y, SND_SP_TERMINAL_ACTIVE);
4949 #endif
4950     }
4951     else if (IS_BELT_ACTIVE(element))
4952       DrawBeltAnimation(x, y, element);
4953     else if (element == EL_SWITCHGATE_OPENING)
4954       OpenSwitchgate(x, y);
4955     else if (element == EL_SWITCHGATE_CLOSING)
4956       CloseSwitchgate(x, y);
4957     else if (element == EL_TIMEGATE_OPENING)
4958       OpenTimegate(x, y);
4959     else if (element == EL_TIMEGATE_CLOSING)
4960       CloseTimegate(x, y);
4961     else if (element == EL_EXTRA_TIME)
4962       DrawGraphicAnimation(x, y, IMG_EXTRA_TIME);
4963     else if (element == EL_SHIELD_NORMAL)
4964     {
4965       DrawGraphicAnimation(x, y, IMG_SHIELD_NORMAL);
4966
4967 #if 0
4968       if (!(FrameCounter % 4))
4969         PlaySoundLevel(x, y, SND_SHIELD_PASSIVE_ACTIVATED);
4970 #endif
4971     }
4972     else if (element == EL_SHIELD_DEADLY)
4973     {
4974       DrawGraphicAnimation(x, y, IMG_SHIELD_DEADLY);
4975
4976 #if 0
4977       if (!(FrameCounter % 4))
4978         PlaySoundLevel(x, y, SND_SHIELD_DEADLY_ACTIVE);
4979 #endif
4980     }
4981
4982     if (game.magic_wall_active)
4983     {
4984       boolean sieb = FALSE;
4985       int jx = local_player->jx, jy = local_player->jy;
4986
4987       if (element == EL_MAGIC_WALL_FULL ||
4988           element == EL_MAGIC_WALL_ACTIVE ||
4989           element == EL_MAGIC_WALL_EMPTYING)
4990       {
4991         SiebAktivieren(x, y, 1);
4992         sieb = TRUE;
4993       }
4994       else if (element == EL_BD_MAGIC_WALL_FULL ||
4995                element == EL_BD_MAGIC_WALL_ACTIVE ||
4996                element == EL_BD_MAGIC_WALL_EMPTYING)
4997       {
4998         SiebAktivieren(x, y, 2);
4999         sieb = TRUE;
5000       }
5001
5002       /* play the element sound at the position nearest to the player */
5003       if (sieb && ABS(x-jx)+ABS(y-jy) < ABS(sieb_x-jx)+ABS(sieb_y-jy))
5004       {
5005         sieb_x = x;
5006         sieb_y = y;
5007       }
5008     }
5009   }
5010
5011 #if USE_NEW_AMOEBA_CODE
5012   /* new experimental amoeba growth stuff */
5013 #if 1
5014   if (!(FrameCounter % 8))
5015 #endif
5016   {
5017     static unsigned long random = 1684108901;
5018
5019     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
5020     {
5021 #if 0
5022       x = (random >> 10) % lev_fieldx;
5023       y = (random >> 20) % lev_fieldy;
5024 #else
5025       x = RND(lev_fieldx);
5026       y = RND(lev_fieldy);
5027 #endif
5028       element = Feld[x][y];
5029
5030       if (!IS_PLAYER(x,y) &&
5031           (element == EL_EMPTY ||
5032            element == EL_SAND ||
5033            element == EL_QUICKSAND_EMPTY ||
5034            element == EL_ACID_SPLASH_LEFT ||
5035            element == EL_ACID_SPLASH_RIGHT))
5036       {
5037         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
5038             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
5039             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
5040             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
5041           Feld[x][y] = EL_AMOEBA_DROP;
5042       }
5043
5044       random = random * 129 + 1;
5045     }
5046   }
5047 #endif
5048
5049 #if 0
5050   if (game.explosions_delayed)
5051 #endif
5052   {
5053     game.explosions_delayed = FALSE;
5054
5055     for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
5056     {
5057       element = Feld[x][y];
5058
5059       if (ExplodeField[x][y])
5060         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
5061       else if (element == EL_EXPLOSION)
5062         Explode(x, y, Frame[x][y], EX_NORMAL);
5063
5064       ExplodeField[x][y] = EX_NO_EXPLOSION;
5065     }
5066
5067     game.explosions_delayed = TRUE;
5068   }
5069
5070   if (game.magic_wall_active)
5071   {
5072     if (!(game.magic_wall_time_left % 4))
5073     {
5074       int element = Feld[sieb_x][sieb_y];
5075
5076       if (element == EL_BD_MAGIC_WALL_FULL ||
5077           element == EL_BD_MAGIC_WALL_ACTIVE ||
5078           element == EL_BD_MAGIC_WALL_EMPTYING)
5079         PlaySoundLevel(sieb_x, sieb_y, SND_BD_MAGIC_WALL_ACTIVE);
5080       else
5081         PlaySoundLevel(sieb_x, sieb_y, SND_MAGIC_WALL_ACTIVE);
5082     }
5083
5084     if (game.magic_wall_time_left > 0)
5085     {
5086       game.magic_wall_time_left--;
5087       if (!game.magic_wall_time_left)
5088       {
5089         for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
5090         {
5091           element = Feld[x][y];
5092
5093           if (element == EL_MAGIC_WALL_ACTIVE ||
5094               element == EL_MAGIC_WALL_FULL)
5095           {
5096             Feld[x][y] = EL_MAGIC_WALL_DEAD;
5097             DrawLevelField(x, y);
5098           }
5099           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
5100                    element == EL_BD_MAGIC_WALL_FULL)
5101           {
5102             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5103             DrawLevelField(x, y);
5104           }
5105         }
5106
5107         game.magic_wall_active = FALSE;
5108       }
5109     }
5110   }
5111
5112   if (game.light_time_left > 0)
5113   {
5114     game.light_time_left--;
5115
5116     if (game.light_time_left == 0)
5117       RedrawAllLightSwitchesAndInvisibleElements();
5118   }
5119
5120   if (game.timegate_time_left > 0)
5121   {
5122     game.timegate_time_left--;
5123
5124     if (game.timegate_time_left == 0)
5125       CloseAllOpenTimegates();
5126   }
5127
5128   for (i=0; i<MAX_PLAYERS; i++)
5129   {
5130     struct PlayerInfo *player = &stored_player[i];
5131
5132     if (SHIELD_ON(player))
5133     {
5134       if (player->shield_deadly_time_left)
5135         PlaySoundLevel(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
5136       else if (player->shield_normal_time_left)
5137         PlaySoundLevel(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
5138     }
5139   }
5140
5141   if (TimeFrames >= (1000 / GameFrameDelay))
5142   {
5143     TimeFrames = 0;
5144     TimePlayed++;
5145
5146     for (i=0; i<MAX_PLAYERS; i++)
5147     {
5148       struct PlayerInfo *player = &stored_player[i];
5149
5150       if (SHIELD_ON(player))
5151       {
5152         player->shield_normal_time_left--;
5153
5154         if (player->shield_deadly_time_left > 0)
5155           player->shield_deadly_time_left--;
5156       }
5157     }
5158
5159     if (tape.recording || tape.playing)
5160       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
5161
5162     if (TimeLeft > 0)
5163     {
5164       TimeLeft--;
5165
5166       if (TimeLeft <= 10 && setup.time_limit)
5167         PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MAX_RIGHT);
5168
5169       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
5170
5171       if (!TimeLeft && setup.time_limit)
5172         for (i=0; i<MAX_PLAYERS; i++)
5173           KillHero(&stored_player[i]);
5174     }
5175     else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
5176       DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FS_SMALL, FC_YELLOW);
5177   }
5178
5179   DrawAllPlayers();
5180
5181   if (options.debug)                    /* calculate frames per second */
5182   {
5183     static unsigned long fps_counter = 0;
5184     static int fps_frames = 0;
5185     unsigned long fps_delay_ms = Counter() - fps_counter;
5186
5187     fps_frames++;
5188
5189     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
5190     {
5191       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
5192
5193       fps_frames = 0;
5194       fps_counter = Counter();
5195     }
5196
5197     redraw_mask |= REDRAW_FPS;
5198   }
5199 }
5200
5201 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
5202 {
5203   int min_x = x, min_y = y, max_x = x, max_y = y;
5204   int i;
5205
5206   for (i=0; i<MAX_PLAYERS; i++)
5207   {
5208     int jx = stored_player[i].jx, jy = stored_player[i].jy;
5209
5210     if (!stored_player[i].active || &stored_player[i] == player)
5211       continue;
5212
5213     min_x = MIN(min_x, jx);
5214     min_y = MIN(min_y, jy);
5215     max_x = MAX(max_x, jx);
5216     max_y = MAX(max_y, jy);
5217   }
5218
5219   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
5220 }
5221
5222 static boolean AllPlayersInVisibleScreen()
5223 {
5224   int i;
5225
5226   for (i=0; i<MAX_PLAYERS; i++)
5227   {
5228     int jx = stored_player[i].jx, jy = stored_player[i].jy;
5229
5230     if (!stored_player[i].active)
5231       continue;
5232
5233     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
5234       return FALSE;
5235   }
5236
5237   return TRUE;
5238 }
5239
5240 void ScrollLevel(int dx, int dy)
5241 {
5242   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
5243   int x, y;
5244
5245   BlitBitmap(drawto_field, drawto_field,
5246              FX + TILEX*(dx == -1) - softscroll_offset,
5247              FY + TILEY*(dy == -1) - softscroll_offset,
5248              SXSIZE - TILEX*(dx!=0) + 2*softscroll_offset,
5249              SYSIZE - TILEY*(dy!=0) + 2*softscroll_offset,
5250              FX + TILEX*(dx == 1) - softscroll_offset,
5251              FY + TILEY*(dy == 1) - softscroll_offset);
5252
5253   if (dx)
5254   {
5255     x = (dx == 1 ? BX1 : BX2);
5256     for (y=BY1; y<=BY2; y++)
5257       DrawScreenField(x, y);
5258   }
5259
5260   if (dy)
5261   {
5262     y = (dy == 1 ? BY1 : BY2);
5263     for (x=BX1; x<=BX2; x++)
5264       DrawScreenField(x, y);
5265   }
5266
5267   redraw_mask |= REDRAW_FIELD;
5268 }
5269
5270 static void CheckGravityMovement(struct PlayerInfo *player)
5271 {
5272   if (level.gravity && !player->programmed_action)
5273   {
5274     int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
5275     int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
5276     int move_dir =
5277       (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
5278        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
5279        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
5280     int jx = player->jx, jy = player->jy;
5281     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
5282     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
5283     int new_jx = jx + dx, new_jy = jy + dy;
5284     boolean field_under_player_is_free =
5285       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
5286     boolean player_is_moving_to_valid_field =
5287       (IN_LEV_FIELD(new_jx, new_jy) &&
5288        (Feld[new_jx][new_jy] == EL_SP_BASE ||
5289         Feld[new_jx][new_jy] == EL_SAND));
5290
5291     if (field_under_player_is_free &&
5292         !player_is_moving_to_valid_field &&
5293         !IS_TUBE(Feld[jx][jy]))
5294       player->programmed_action = MV_DOWN;
5295   }
5296 }
5297
5298 boolean MoveFigureOneStep(struct PlayerInfo *player,
5299                           int dx, int dy, int real_dx, int real_dy)
5300 {
5301   int jx = player->jx, jy = player->jy;
5302   int new_jx = jx+dx, new_jy = jy+dy;
5303   int element;
5304   int can_move;
5305
5306   if (!player->active || (!dx && !dy))
5307     return MF_NO_ACTION;
5308
5309   player->MovDir = (dx < 0 ? MV_LEFT :
5310                     dx > 0 ? MV_RIGHT :
5311                     dy < 0 ? MV_UP :
5312                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
5313
5314   if (!IN_LEV_FIELD(new_jx, new_jy))
5315     return MF_NO_ACTION;
5316
5317   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
5318     return MF_NO_ACTION;
5319
5320 #if 0
5321   element = MovingOrBlocked2Element(new_jx, new_jy);
5322 #else
5323   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
5324 #endif
5325
5326   if (DONT_GO_TO(element))
5327   {
5328     if (element == EL_ACID && dx == 0 && dy == 1)
5329     {
5330       Blurb(jx, jy);
5331       Feld[jx][jy] = EL_PLAYER1;
5332       InitMovingField(jx, jy, MV_DOWN);
5333       Store[jx][jy] = EL_ACID;
5334       ContinueMoving(jx, jy);
5335       BuryHero(player);
5336     }
5337     else
5338       TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
5339
5340     return MF_MOVING;
5341   }
5342
5343   can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
5344   if (can_move != MF_MOVING)
5345     return can_move;
5346
5347   StorePlayer[jx][jy] = 0;
5348   player->last_jx = jx;
5349   player->last_jy = jy;
5350   jx = player->jx = new_jx;
5351   jy = player->jy = new_jy;
5352   StorePlayer[jx][jy] = player->element_nr;
5353
5354   player->MovPos =
5355     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
5356
5357   ScrollFigure(player, SCROLL_INIT);
5358
5359   return MF_MOVING;
5360 }
5361
5362 boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
5363 {
5364   int jx = player->jx, jy = player->jy;
5365   int old_jx = jx, old_jy = jy;
5366   int moved = MF_NO_ACTION;
5367
5368   if (!player->active || (!dx && !dy))
5369     return FALSE;
5370
5371 #if 0
5372   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
5373       !tape.playing)
5374     return FALSE;
5375 #else
5376   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
5377       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
5378     return FALSE;
5379 #endif
5380
5381   /* remove the last programmed player action */
5382   player->programmed_action = 0;
5383
5384   if (player->MovPos)
5385   {
5386     /* should only happen if pre-1.2 tape recordings are played */
5387     /* this is only for backward compatibility */
5388
5389     int original_move_delay_value = player->move_delay_value;
5390
5391 #if DEBUG
5392     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES.\n");
5393 #endif
5394
5395     /* scroll remaining steps with finest movement resolution */
5396     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
5397
5398     while (player->MovPos)
5399     {
5400       ScrollFigure(player, SCROLL_GO_ON);
5401       ScrollScreen(NULL, SCROLL_GO_ON);
5402       FrameCounter++;
5403       DrawAllPlayers();
5404       BackToFront();
5405     }
5406
5407     player->move_delay_value = original_move_delay_value;
5408   }
5409
5410   if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
5411   {
5412     if (!(moved |= MoveFigureOneStep(player, 0, dy, dx, dy)))
5413       moved |= MoveFigureOneStep(player, dx, 0, dx, dy);
5414   }
5415   else
5416   {
5417     if (!(moved |= MoveFigureOneStep(player, dx, 0, dx, dy)))
5418       moved |= MoveFigureOneStep(player, 0, dy, dx, dy);
5419   }
5420
5421   jx = player->jx;
5422   jy = player->jy;
5423
5424   if (moved & MF_MOVING && !ScreenMovPos &&
5425       (player == local_player || !options.network))
5426   {
5427     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
5428     int offset = (setup.scroll_delay ? 3 : 0);
5429
5430     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
5431     {
5432       /* actual player has left the screen -- scroll in that direction */
5433       if (jx != old_jx)         /* player has moved horizontally */
5434         scroll_x += (jx - old_jx);
5435       else                      /* player has moved vertically */
5436         scroll_y += (jy - old_jy);
5437     }
5438     else
5439     {
5440       if (jx != old_jx)         /* player has moved horizontally */
5441       {
5442         if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
5443             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
5444           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
5445
5446         /* don't scroll over playfield boundaries */
5447         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5448           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5449
5450         /* don't scroll more than one field at a time */
5451         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
5452
5453         /* don't scroll against the player's moving direction */
5454         if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
5455             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
5456           scroll_x = old_scroll_x;
5457       }
5458       else                      /* player has moved vertically */
5459       {
5460         if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
5461             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
5462           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
5463
5464         /* don't scroll over playfield boundaries */
5465         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5466           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5467
5468         /* don't scroll more than one field at a time */
5469         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
5470
5471         /* don't scroll against the player's moving direction */
5472         if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
5473             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
5474           scroll_y = old_scroll_y;
5475       }
5476     }
5477
5478     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
5479     {
5480       if (!options.network && !AllPlayersInVisibleScreen())
5481       {
5482         scroll_x = old_scroll_x;
5483         scroll_y = old_scroll_y;
5484       }
5485       else
5486       {
5487         ScrollScreen(player, SCROLL_INIT);
5488         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
5489       }
5490     }
5491   }
5492
5493   if (!(moved & MF_MOVING) && !player->Pushing)
5494     player->Frame = 0;
5495   else
5496 #if 0
5497     player->Frame = (player->Frame + 1) % 4;
5498 #else
5499     player->Frame += 1 * 0;
5500 #endif
5501
5502   if (moved & MF_MOVING)
5503   {
5504     if (old_jx != jx && old_jy == jy)
5505       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
5506     else if (old_jx == jx && old_jy != jy)
5507       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
5508
5509     DrawLevelField(jx, jy);     /* for "DrawCrumbledSand()" */
5510
5511     player->last_move_dir = player->MovDir;
5512     player->is_moving = TRUE;
5513   }
5514   else
5515   {
5516     CheckGravityMovement(player);
5517
5518     /*
5519     player->last_move_dir = MV_NO_MOVING;
5520     */
5521     player->is_moving = FALSE;
5522   }
5523
5524   TestIfHeroTouchesBadThing(jx, jy);
5525
5526   if (!player->active)
5527     RemoveHero(player);
5528
5529   return moved;
5530 }
5531
5532 void ScrollFigure(struct PlayerInfo *player, int mode)
5533 {
5534   int jx = player->jx, jy = player->jy;
5535   int last_jx = player->last_jx, last_jy = player->last_jy;
5536   int move_stepsize = TILEX / player->move_delay_value;
5537
5538   if (!player->active || !player->MovPos)
5539     return;
5540
5541   if (mode == SCROLL_INIT)
5542   {
5543     player->actual_frame_counter = FrameCounter;
5544     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
5545     if (player->Frame)
5546       player->Frame += 1;
5547
5548     if (Feld[last_jx][last_jy] == EL_EMPTY)
5549       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
5550
5551     DrawPlayer(player);
5552     return;
5553   }
5554   else if (!FrameReached(&player->actual_frame_counter, 1))
5555     return;
5556
5557   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
5558   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
5559   player->Frame += 1;
5560
5561   if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
5562     Feld[last_jx][last_jy] = EL_EMPTY;
5563
5564   /* before DrawPlayer() to draw correct player graphic for this case */
5565   if (player->MovPos == 0)
5566     CheckGravityMovement(player);
5567
5568   DrawPlayer(player);
5569
5570   if (player->MovPos == 0)
5571   {
5572     if (IS_QUICK_GATE(Feld[last_jx][last_jy]))
5573     {
5574       /* continue with normal speed after quickly moving through gate */
5575       HALVE_PLAYER_SPEED(player);
5576
5577       /* be able to make the next move without delay */
5578       player->move_delay = 0;
5579     }
5580
5581     player->last_jx = jx;
5582     player->last_jy = jy;
5583
5584     if (Feld[jx][jy] == EL_EXIT_OPEN ||
5585         Feld[jx][jy] == EL_SP_EXIT_OPEN)
5586     {
5587       RemoveHero(player);
5588
5589       if (local_player->friends_still_needed == 0 ||
5590           Feld[jx][jy] == EL_SP_EXIT_OPEN)
5591         player->LevelSolved = player->GameOver = TRUE;
5592     }
5593
5594     if (tape.single_step && tape.recording && !tape.pausing &&
5595         !player->programmed_action)
5596       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
5597   }
5598 }
5599
5600 void ScrollScreen(struct PlayerInfo *player, int mode)
5601 {
5602   static unsigned long screen_frame_counter = 0;
5603
5604   if (mode == SCROLL_INIT)
5605   {
5606     /* set scrolling step size according to actual player's moving speed */
5607     ScrollStepSize = TILEX / player->move_delay_value;
5608
5609     screen_frame_counter = FrameCounter;
5610     ScreenMovDir = player->MovDir;
5611     ScreenMovPos = player->MovPos;
5612     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
5613     return;
5614   }
5615   else if (!FrameReached(&screen_frame_counter, 1))
5616     return;
5617
5618   if (ScreenMovPos)
5619   {
5620     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
5621     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
5622     redraw_mask |= REDRAW_FIELD;
5623   }
5624   else
5625     ScreenMovDir = MV_NO_MOVING;
5626 }
5627
5628 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
5629 {
5630   int i, kill_x = -1, kill_y = -1;
5631   static int test_xy[4][2] =
5632   {
5633     { 0, -1 },
5634     { -1, 0 },
5635     { +1, 0 },
5636     { 0, +1 }
5637   };
5638   static int test_dir[4] =
5639   {
5640     MV_UP,
5641     MV_LEFT,
5642     MV_RIGHT,
5643     MV_DOWN
5644   };
5645
5646   for (i=0; i<4; i++)
5647   {
5648     int test_x, test_y, test_move_dir, test_element;
5649
5650     test_x = good_x + test_xy[i][0];
5651     test_y = good_y + test_xy[i][1];
5652     if (!IN_LEV_FIELD(test_x, test_y))
5653       continue;
5654
5655     test_move_dir =
5656       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
5657
5658 #if 0
5659     test_element = Feld[test_x][test_y];
5660 #else
5661     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
5662 #endif
5663
5664     /* 1st case: good thing is moving towards DONT_GO_TO style bad thing;
5665        2nd case: DONT_TOUCH style bad thing does not move away from good thing
5666     */
5667     if ((DONT_GO_TO(test_element) && good_move_dir == test_dir[i]) ||
5668         (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
5669     {
5670       kill_x = test_x;
5671       kill_y = test_y;
5672       break;
5673     }
5674   }
5675
5676   if (kill_x != -1 || kill_y != -1)
5677   {
5678     if (IS_PLAYER(good_x, good_y))
5679     {
5680       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
5681
5682       if (player->shield_deadly_time_left > 0)
5683         Bang(kill_x, kill_y);
5684       else if (!PLAYER_PROTECTED(good_x, good_y))
5685         KillHero(player);
5686     }
5687     else
5688       Bang(good_x, good_y);
5689   }
5690 }
5691
5692 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
5693 {
5694   int i, kill_x = -1, kill_y = -1;
5695   int bad_element = Feld[bad_x][bad_y];
5696   static int test_xy[4][2] =
5697   {
5698     { 0, -1 },
5699     { -1, 0 },
5700     { +1, 0 },
5701     { 0, +1 }
5702   };
5703   static int test_dir[4] =
5704   {
5705     MV_UP,
5706     MV_LEFT,
5707     MV_RIGHT,
5708     MV_DOWN
5709   };
5710
5711   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
5712     return;
5713
5714   for (i=0; i<4; i++)
5715   {
5716     int test_x, test_y, test_move_dir, test_element;
5717
5718     test_x = bad_x + test_xy[i][0];
5719     test_y = bad_y + test_xy[i][1];
5720     if (!IN_LEV_FIELD(test_x, test_y))
5721       continue;
5722
5723     test_move_dir =
5724       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
5725
5726     test_element = Feld[test_x][test_y];
5727
5728     /* 1st case: good thing is moving towards DONT_GO_TO style bad thing;
5729        2nd case: DONT_TOUCH style bad thing does not move away from good thing
5730     */
5731     if ((DONT_GO_TO(bad_element) &&  bad_move_dir == test_dir[i]) ||
5732         (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
5733     {
5734       /* good thing is player or penguin that does not move away */
5735       if (IS_PLAYER(test_x, test_y))
5736       {
5737         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
5738
5739         if (bad_element == EL_ROBOT && player->is_moving)
5740           continue;     /* robot does not kill player if he is moving */
5741
5742         kill_x = test_x;
5743         kill_y = test_y;
5744         break;
5745       }
5746       else if (test_element == EL_PENGUIN)
5747       {
5748         kill_x = test_x;
5749         kill_y = test_y;
5750         break;
5751       }
5752     }
5753   }
5754
5755   if (kill_x != -1 || kill_y != -1)
5756   {
5757     if (IS_PLAYER(kill_x, kill_y))
5758     {
5759       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
5760
5761 #if 0
5762       int dir = player->MovDir;
5763       int newx = player->jx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5764       int newy = player->jy + (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
5765
5766       if (Feld[bad_x][bad_y] == EL_ROBOT && player->is_moving &&
5767           newx != bad_x && newy != bad_y)
5768         ;       /* robot does not kill player if he is moving */
5769       else
5770         printf("-> %d\n", player->MovDir);
5771
5772       if (Feld[bad_x][bad_y] == EL_ROBOT && player->is_moving &&
5773           newx != bad_x && newy != bad_y)
5774         ;       /* robot does not kill player if he is moving */
5775       else
5776         ;
5777 #endif
5778
5779       if (player->shield_deadly_time_left > 0)
5780         Bang(bad_x, bad_y);
5781       else if (!PLAYER_PROTECTED(kill_x, kill_y))
5782         KillHero(player);
5783     }
5784     else
5785       Bang(kill_x, kill_y);
5786   }
5787 }
5788
5789 void TestIfHeroTouchesBadThing(int x, int y)
5790 {
5791   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
5792 }
5793
5794 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
5795 {
5796   TestIfGoodThingHitsBadThing(x, y, move_dir);
5797 }
5798
5799 void TestIfBadThingTouchesHero(int x, int y)
5800 {
5801   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
5802 }
5803
5804 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
5805 {
5806   TestIfBadThingHitsGoodThing(x, y, move_dir);
5807 }
5808
5809 void TestIfFriendTouchesBadThing(int x, int y)
5810 {
5811   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
5812 }
5813
5814 void TestIfBadThingTouchesFriend(int x, int y)
5815 {
5816   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
5817 }
5818
5819 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
5820 {
5821   int i, kill_x = bad_x, kill_y = bad_y;
5822   static int xy[4][2] =
5823   {
5824     { 0, -1 },
5825     { -1, 0 },
5826     { +1, 0 },
5827     { 0, +1 }
5828   };
5829
5830   for (i=0; i<4; i++)
5831   {
5832     int x, y, element;
5833
5834     x = bad_x + xy[i][0];
5835     y = bad_y + xy[i][1];
5836     if (!IN_LEV_FIELD(x, y))
5837       continue;
5838
5839     element = Feld[x][y];
5840     if (IS_AMOEBOID(element) || element == EL_GAMEOFLIFE ||
5841         element == EL_AMOEBA_CREATING || element == EL_AMOEBA_DROP)
5842     {
5843       kill_x = x;
5844       kill_y = y;
5845       break;
5846     }
5847   }
5848
5849   if (kill_x != bad_x || kill_y != bad_y)
5850     Bang(bad_x, bad_y);
5851 }
5852
5853 void KillHero(struct PlayerInfo *player)
5854 {
5855   int jx = player->jx, jy = player->jy;
5856
5857   if (!player->active)
5858     return;
5859
5860   if (IS_PFORTE(Feld[jx][jy]))
5861     Feld[jx][jy] = EL_EMPTY;
5862
5863   /* deactivate shield (else Bang()/Explode() would not work right) */
5864   player->shield_normal_time_left = 0;
5865   player->shield_deadly_time_left = 0;
5866
5867   Bang(jx, jy);
5868   BuryHero(player);
5869 }
5870
5871 static void KillHeroUnlessProtected(int x, int y)
5872 {
5873   if (!PLAYER_PROTECTED(x, y))
5874     KillHero(PLAYERINFO(x, y));
5875 }
5876
5877 void BuryHero(struct PlayerInfo *player)
5878 {
5879   int jx = player->jx, jy = player->jy;
5880
5881   if (!player->active)
5882     return;
5883
5884   PlaySoundLevel(jx, jy, SND_PLAYER_DYING);
5885   PlaySoundLevel(jx, jy, SND_GAME_LOSING);
5886
5887   player->GameOver = TRUE;
5888   RemoveHero(player);
5889 }
5890
5891 void RemoveHero(struct PlayerInfo *player)
5892 {
5893   int jx = player->jx, jy = player->jy;
5894   int i, found = FALSE;
5895
5896   player->present = FALSE;
5897   player->active = FALSE;
5898
5899   if (!ExplodeField[jx][jy])
5900     StorePlayer[jx][jy] = 0;
5901
5902   for (i=0; i<MAX_PLAYERS; i++)
5903     if (stored_player[i].active)
5904       found = TRUE;
5905
5906   if (!found)
5907     AllPlayersGone = TRUE;
5908
5909   ExitX = ZX = jx;
5910   ExitY = ZY = jy;
5911 }
5912
5913 int DigField(struct PlayerInfo *player,
5914              int x, int y, int real_dx, int real_dy, int mode)
5915 {
5916   int jx = player->jx, jy = player->jy;
5917   int dx = x - jx, dy = y - jy;
5918   int move_direction = (dx == -1 ? MV_LEFT :
5919                         dx == +1 ? MV_RIGHT :
5920                         dy == -1 ? MV_UP :
5921                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
5922   int element;
5923
5924   if (player->MovPos == 0)
5925     player->Pushing = FALSE;
5926
5927   if (mode == DF_NO_PUSH)
5928   {
5929     player->Switching = FALSE;
5930     player->push_delay = 0;
5931     return MF_NO_ACTION;
5932   }
5933
5934   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
5935     return MF_NO_ACTION;
5936
5937   if (IS_TUBE(Feld[jx][jy]))
5938   {
5939     int i = 0;
5940     int tube_leave_directions[][2] =
5941     {
5942       { EL_TUBE_ALL,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
5943       { EL_TUBE_VERTICAL,                                    MV_UP | MV_DOWN },
5944       { EL_TUBE_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
5945       { EL_TUBE_VERTICAL_LEFT,          MV_LEFT |            MV_UP | MV_DOWN },
5946       { EL_TUBE_VERTICAL_RIGHT,                   MV_RIGHT | MV_UP | MV_DOWN },
5947       { EL_TUBE_HORIZONTAL_UP,          MV_LEFT | MV_RIGHT | MV_UP           },
5948       { EL_TUBE_HORIZONTAL_DOWN,        MV_LEFT | MV_RIGHT |         MV_DOWN },
5949       { EL_TUBE_LEFT_UP,                MV_LEFT |            MV_UP           },
5950       { EL_TUBE_LEFT_DOWN,              MV_LEFT |                    MV_DOWN },
5951       { EL_TUBE_RIGHT_UP,                         MV_RIGHT | MV_UP           },
5952       { EL_TUBE_RIGHT_DOWN,                       MV_RIGHT |         MV_DOWN },
5953       { -1,                             MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
5954     };
5955
5956     while (tube_leave_directions[i][0] != Feld[jx][jy])
5957     {
5958       i++;
5959       if (tube_leave_directions[i][0] == -1)    /* should not happen */
5960         break;
5961     }
5962
5963     if (!(tube_leave_directions[i][1] & move_direction))
5964       return MF_NO_ACTION;      /* tube has no opening in this direction */
5965   }
5966
5967   element = Feld[x][y];
5968
5969   switch (element)
5970   {
5971     case EL_EMPTY:
5972     case EL_SAND:
5973     case EL_INVISIBLE_SAND:
5974     case EL_INVISIBLE_SAND_ACTIVE:
5975     case EL_TRAP:
5976     case EL_SP_BASE:
5977     case EL_SP_BUGGY_BASE:
5978       RemoveField(x, y);
5979       PlaySoundLevelElementAction(x, y, element, SND_ACTION_DIGGING);
5980       break;
5981
5982     case EL_EMERALD:
5983     case EL_BD_DIAMOND:
5984     case EL_EMERALD_YELLOW:
5985     case EL_EMERALD_RED:
5986     case EL_EMERALD_PURPLE:
5987     case EL_DIAMOND:
5988     case EL_SP_INFOTRON:
5989     case EL_PEARL:
5990     case EL_CRYSTAL:
5991       RemoveField(x, y);
5992       local_player->gems_still_needed -= (element == EL_DIAMOND ? 3 :
5993                                           element == EL_PEARL ? 5 :
5994                                           element == EL_CRYSTAL ? 8 : 1);
5995       if (local_player->gems_still_needed < 0)
5996         local_player->gems_still_needed = 0;
5997       RaiseScoreElement(element);
5998       DrawText(DX_EMERALDS, DY_EMERALDS,
5999                int2str(local_player->gems_still_needed, 3),
6000                FS_SMALL, FC_YELLOW);
6001       PlaySoundLevelElementAction(x, y, element, SND_ACTION_COLLECTING);
6002       break;
6003
6004     case EL_SPEED_PILL:
6005       RemoveField(x, y);
6006       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
6007       PlaySoundLevel(x, y, SND_SPEED_PILL_COLLECTING);
6008       break;
6009
6010     case EL_ENVELOPE:
6011       Feld[x][y] = EL_EMPTY;
6012       PlaySoundLevel(x, y, SND_ENVELOPE_COLLECTING);
6013       break;
6014
6015     case EL_EXTRA_TIME:
6016       RemoveField(x, y);
6017       if (level.time > 0)
6018       {
6019         TimeLeft += 10;
6020         DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
6021       }
6022       PlaySoundStereo(SND_EXTRA_TIME_COLLECTING, SOUND_MAX_RIGHT);
6023       break;
6024
6025     case EL_SHIELD_NORMAL:
6026       RemoveField(x, y);
6027       player->shield_normal_time_left += 10;
6028       PlaySoundLevel(x, y, SND_SHIELD_NORMAL_COLLECTING);
6029       break;
6030
6031     case EL_SHIELD_DEADLY:
6032       RemoveField(x, y);
6033       player->shield_normal_time_left += 10;
6034       player->shield_deadly_time_left += 10;
6035       PlaySoundLevel(x, y, SND_SHIELD_DEADLY_COLLECTING);
6036       break;
6037
6038     case EL_DYNAMITE:
6039     case EL_SP_DISK_RED:
6040       RemoveField(x, y);
6041       player->dynamite++;
6042       RaiseScoreElement(EL_DYNAMITE);
6043       DrawText(DX_DYNAMITE, DY_DYNAMITE,
6044                int2str(local_player->dynamite, 3),
6045                FS_SMALL, FC_YELLOW);
6046       PlaySoundLevelElementAction(x, y, element, SND_ACTION_COLLECTING);
6047       break;
6048
6049     case EL_DYNABOMB_NR:
6050       RemoveField(x, y);
6051       player->dynabomb_count++;
6052       player->dynabombs_left++;
6053       RaiseScoreElement(EL_DYNAMITE);
6054       PlaySoundLevel(x, y, SND_DYNABOMB_NR_COLLECTING);
6055       break;
6056
6057     case EL_DYNABOMB_SZ:
6058       RemoveField(x, y);
6059       player->dynabomb_size++;
6060       RaiseScoreElement(EL_DYNAMITE);
6061       PlaySoundLevel(x, y, SND_DYNABOMB_SZ_COLLECTING);
6062       break;
6063
6064     case EL_DYNABOMB_XL:
6065       RemoveField(x, y);
6066       player->dynabomb_xl = TRUE;
6067       RaiseScoreElement(EL_DYNAMITE);
6068       PlaySoundLevel(x, y, SND_DYNABOMB_XL_COLLECTING);
6069       break;
6070
6071     case EL_KEY1:
6072     case EL_KEY2:
6073     case EL_KEY3:
6074     case EL_KEY4:
6075     {
6076       int key_nr = element - EL_KEY1;
6077
6078       RemoveField(x, y);
6079       player->key[key_nr] = TRUE;
6080       RaiseScoreElement(element);
6081       DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
6082                          GFX_SCHLUESSEL1 + key_nr);
6083       DrawMiniGraphicExt(window, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
6084                          GFX_SCHLUESSEL1 + key_nr);
6085       PlaySoundLevel(x, y, SND_KEY_COLLECTING);
6086       break;
6087     }
6088
6089     case EL_EM_KEY1:
6090     case EL_EM_KEY2:
6091     case EL_EM_KEY3:
6092     case EL_EM_KEY4:
6093     {
6094       int key_nr = element - EL_EM_KEY1;
6095
6096       RemoveField(x, y);
6097       player->key[key_nr] = TRUE;
6098       RaiseScoreElement(element);
6099       DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
6100                          GFX_SCHLUESSEL1 + key_nr);
6101       DrawMiniGraphicExt(window, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
6102                          GFX_SCHLUESSEL1 + key_nr);
6103       PlaySoundLevel(x, y, SND_KEY_COLLECTING);
6104       break;
6105     }
6106
6107     case EL_ROBOT_WHEEL:
6108       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
6109       ZX = x;
6110       ZY = y;
6111       DrawLevelField(x, y);
6112       PlaySoundLevel(x, y, SND_ROBOT_WHEEL_ACTIVATING);
6113       return MF_ACTION;
6114       break;
6115
6116     case EL_SP_TERMINAL:
6117       {
6118         int xx, yy;
6119
6120         PlaySoundLevel(x, y, SND_SP_TERMINAL_ACTIVATING);
6121
6122         for (yy=0; yy<lev_fieldy; yy++)
6123         {
6124           for (xx=0; xx<lev_fieldx; xx++)
6125           {
6126             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
6127               Bang(xx, yy);
6128             else if (Feld[xx][yy] == EL_SP_TERMINAL)
6129               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
6130           }
6131         }
6132
6133         return MF_ACTION;
6134       }
6135       break;
6136
6137     case EL_CONVEYOR_BELT1_SWITCH_LEFT:
6138     case EL_CONVEYOR_BELT1_SWITCH_MIDDLE:
6139     case EL_CONVEYOR_BELT1_SWITCH_RIGHT:
6140     case EL_CONVEYOR_BELT2_SWITCH_LEFT:
6141     case EL_CONVEYOR_BELT2_SWITCH_MIDDLE:
6142     case EL_CONVEYOR_BELT2_SWITCH_RIGHT:
6143     case EL_CONVEYOR_BELT3_SWITCH_LEFT:
6144     case EL_CONVEYOR_BELT3_SWITCH_MIDDLE:
6145     case EL_CONVEYOR_BELT3_SWITCH_RIGHT:
6146     case EL_CONVEYOR_BELT4_SWITCH_LEFT:
6147     case EL_CONVEYOR_BELT4_SWITCH_MIDDLE:
6148     case EL_CONVEYOR_BELT4_SWITCH_RIGHT:
6149       if (!player->Switching)
6150       {
6151         player->Switching = TRUE;
6152         ToggleBeltSwitch(x, y);
6153         PlaySoundLevel(x, y, SND_CONVEYOR_BELT_SWITCH_ACTIVATING);
6154       }
6155       return MF_ACTION;
6156       break;
6157
6158     case EL_SWITCHGATE_SWITCH_UP:
6159     case EL_SWITCHGATE_SWITCH_DOWN:
6160       if (!player->Switching)
6161       {
6162         player->Switching = TRUE;
6163         ToggleSwitchgateSwitch(x, y);
6164         PlaySoundLevel(x, y, SND_SWITCHGATE_SWITCH_ACTIVATING);
6165       }
6166       return MF_ACTION;
6167       break;
6168
6169     case EL_LIGHT_SWITCH:
6170     case EL_LIGHT_SWITCH_ACTIVE:
6171       if (!player->Switching)
6172       {
6173         player->Switching = TRUE;
6174         ToggleLightSwitch(x, y);
6175         PlaySoundLevel(x, y, element == EL_LIGHT_SWITCH ?
6176                        SND_LIGHT_SWITCH_ACTIVATING :
6177                        SND_LIGHT_SWITCH_DEACTIVATING);
6178       }
6179       return MF_ACTION;
6180       break;
6181
6182     case EL_TIMEGATE_SWITCH:
6183       ActivateTimegateSwitch(x, y);
6184       PlaySoundLevel(x, y, SND_TIMEGATE_SWITCH_ACTIVATING);
6185
6186       return MF_ACTION;
6187       break;
6188
6189     case EL_BALLOON_SEND_LEFT:
6190     case EL_BALLOON_SEND_RIGHT:
6191     case EL_BALLOON_SEND_UP:
6192     case EL_BALLOON_SEND_DOWN:
6193     case EL_BALLOON_SEND_ANY_DIRECTION:
6194       if (element == EL_BALLOON_SEND_ANY_DIRECTION)
6195         game.balloon_dir = move_direction;
6196       else
6197         game.balloon_dir = (element == EL_BALLOON_SEND_LEFT  ? MV_LEFT :
6198                             element == EL_BALLOON_SEND_RIGHT ? MV_RIGHT :
6199                             element == EL_BALLOON_SEND_UP    ? MV_UP :
6200                             element == EL_BALLOON_SEND_DOWN  ? MV_DOWN :
6201                             MV_NO_MOVING);
6202       PlaySoundLevel(x, y, SND_BALLOON_SWITCH_ACTIVATING);
6203
6204       return MF_ACTION;
6205       break;
6206
6207       /* the following elements cannot be pushed by "snapping" */
6208     case EL_ROCK:
6209     case EL_BOMB:
6210     case EL_DX_SUPABOMB:
6211     case EL_NUT:
6212     case EL_TIME_ORB_EMPTY:
6213     case EL_SP_ZONK:
6214     case EL_SP_DISK_ORANGE:
6215     case EL_SPRING:
6216       if (mode == DF_SNAP)
6217         return MF_NO_ACTION;
6218       /* no "break" -- fall through to next case */
6219       /* the following elements can be pushed by "snapping" */
6220     case EL_BD_ROCK:
6221       if (dy)
6222         return MF_NO_ACTION;
6223
6224       player->Pushing = TRUE;
6225
6226       if (!IN_LEV_FIELD(x+dx, y+dy) || !IS_FREE(x+dx, y+dy))
6227         return MF_NO_ACTION;
6228
6229       if (real_dy)
6230       {
6231         if (IN_LEV_FIELD(jx, jy+real_dy) && !IS_SOLID(Feld[jx][jy+real_dy]))
6232           return MF_NO_ACTION;
6233       }
6234
6235       if (player->push_delay == 0)
6236         player->push_delay = FrameCounter;
6237 #if 0
6238       if (!FrameReached(&player->push_delay, player->push_delay_value) &&
6239           !tape.playing && element != EL_SPRING)
6240         return MF_NO_ACTION;
6241 #else
6242       if (!FrameReached(&player->push_delay, player->push_delay_value) &&
6243           !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
6244           element != EL_SPRING)
6245         return MF_NO_ACTION;
6246 #endif
6247
6248       if (mode == DF_SNAP)
6249       {
6250         InitMovingField(x, y, move_direction);
6251         ContinueMoving(x, y);
6252       }
6253       else
6254       {
6255         RemoveField(x, y);
6256         Feld[x + dx][y + dy] = element;
6257       }
6258
6259       if (element == EL_SPRING)
6260       {
6261         Feld[x + dx][y + dy] = EL_SPRING;
6262         MovDir[x + dx][y + dy] = move_direction;
6263       }
6264
6265       player->push_delay_value = (element == EL_SPRING ? 0 : 2 + RND(8));
6266
6267       DrawLevelField(x + dx, y + dy);
6268       PlaySoundLevelElementAction(x, y, element, SND_ACTION_PUSHING);
6269       break;
6270
6271     case EL_GATE1:
6272     case EL_GATE2:
6273     case EL_GATE3:
6274     case EL_GATE4:
6275       if (!player->key[element - EL_GATE1])
6276         return MF_NO_ACTION;
6277       break;
6278
6279     case EL_GATE1_GRAY:
6280     case EL_GATE2_GRAY:
6281     case EL_GATE3_GRAY:
6282     case EL_GATE4_GRAY:
6283       if (!player->key[element - EL_GATE1_GRAY])
6284         return MF_NO_ACTION;
6285       break;
6286
6287     case EL_EM_GATE1:
6288     case EL_EM_GATE2:
6289     case EL_EM_GATE3:
6290     case EL_EM_GATE4:
6291       if (!player->key[element - EL_EM_GATE1])
6292         return MF_NO_ACTION;
6293       if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
6294         return MF_NO_ACTION;
6295
6296       /* automatically move to the next field with double speed */
6297       player->programmed_action = move_direction;
6298       DOUBLE_PLAYER_SPEED(player);
6299
6300       PlaySoundLevel(x, y, SND_GATE_PASSING);
6301       break;
6302
6303     case EL_EM_GATE1_GRAY:
6304     case EL_EM_GATE2_GRAY:
6305     case EL_EM_GATE3_GRAY:
6306     case EL_EM_GATE4_GRAY:
6307       if (!player->key[element - EL_EM_GATE1_GRAY])
6308         return MF_NO_ACTION;
6309       if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
6310         return MF_NO_ACTION;
6311
6312       /* automatically move to the next field with double speed */
6313       player->programmed_action = move_direction;
6314       DOUBLE_PLAYER_SPEED(player);
6315
6316       PlaySoundLevel(x, y, SND_GATE_PASSING);
6317       break;
6318
6319     case EL_SWITCHGATE_OPEN:
6320     case EL_TIMEGATE_OPEN:
6321       if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
6322         return MF_NO_ACTION;
6323
6324       /* automatically move to the next field with double speed */
6325       player->programmed_action = move_direction;
6326       DOUBLE_PLAYER_SPEED(player);
6327
6328       PlaySoundLevelElementAction(x, y, element, SND_ACTION_PASSING);
6329       break;
6330
6331     case EL_SP_PORT1_LEFT:
6332     case EL_SP_PORT2_LEFT:
6333     case EL_SP_PORT1_RIGHT:
6334     case EL_SP_PORT2_RIGHT:
6335     case EL_SP_PORT1_UP:
6336     case EL_SP_PORT2_UP:
6337     case EL_SP_PORT1_DOWN:
6338     case EL_SP_PORT2_DOWN:
6339     case EL_SP_PORT_X:
6340     case EL_SP_PORT_Y:
6341     case EL_SP_PORT_XY:
6342       if ((dx == -1 &&
6343            element != EL_SP_PORT1_LEFT &&
6344            element != EL_SP_PORT2_LEFT &&
6345            element != EL_SP_PORT_X &&
6346            element != EL_SP_PORT_XY) ||
6347           (dx == +1 &&
6348            element != EL_SP_PORT1_RIGHT &&
6349            element != EL_SP_PORT2_RIGHT &&
6350            element != EL_SP_PORT_X &&
6351            element != EL_SP_PORT_XY) ||
6352           (dy == -1 &&
6353            element != EL_SP_PORT1_UP &&
6354            element != EL_SP_PORT2_UP &&
6355            element != EL_SP_PORT_Y &&
6356            element != EL_SP_PORT_XY) ||
6357           (dy == +1 &&
6358            element != EL_SP_PORT1_DOWN &&
6359            element != EL_SP_PORT2_DOWN &&
6360            element != EL_SP_PORT_Y &&
6361            element != EL_SP_PORT_XY) ||
6362           !IN_LEV_FIELD(x + dx, y + dy) ||
6363           !IS_FREE(x + dx, y + dy))
6364         return MF_NO_ACTION;
6365
6366       /* automatically move to the next field with double speed */
6367       player->programmed_action = move_direction;
6368       DOUBLE_PLAYER_SPEED(player);
6369
6370       PlaySoundLevel(x, y, SND_SP_PORT_PASSING);
6371       break;
6372
6373     case EL_TUBE_ALL:
6374     case EL_TUBE_VERTICAL:
6375     case EL_TUBE_HORIZONTAL:
6376     case EL_TUBE_VERTICAL_LEFT:
6377     case EL_TUBE_VERTICAL_RIGHT:
6378     case EL_TUBE_HORIZONTAL_UP:
6379     case EL_TUBE_HORIZONTAL_DOWN:
6380     case EL_TUBE_LEFT_UP:
6381     case EL_TUBE_LEFT_DOWN:
6382     case EL_TUBE_RIGHT_UP:
6383     case EL_TUBE_RIGHT_DOWN:
6384       {
6385         int i = 0;
6386         int tube_enter_directions[][2] =
6387         {
6388           { EL_TUBE_ALL,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
6389           { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
6390           { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
6391           { EL_TUBE_VERTICAL_LEFT,                MV_RIGHT | MV_UP | MV_DOWN },
6392           { EL_TUBE_VERTICAL_RIGHT,     MV_LEFT            | MV_UP | MV_DOWN },
6393           { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT |         MV_DOWN },
6394           { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT | MV_UP           },
6395           { EL_TUBE_LEFT_UP,                      MV_RIGHT |         MV_DOWN },
6396           { EL_TUBE_LEFT_DOWN,                    MV_RIGHT | MV_UP           },
6397           { EL_TUBE_RIGHT_UP,           MV_LEFT |                    MV_DOWN },
6398           { EL_TUBE_RIGHT_DOWN,         MV_LEFT |            MV_UP           },
6399           { -1,                         MV_NO_MOVING                         }
6400         };
6401
6402         while (tube_enter_directions[i][0] != element)
6403         {
6404           i++;
6405           if (tube_enter_directions[i][0] == -1)        /* should not happen */
6406             break;
6407         }
6408
6409         if (!(tube_enter_directions[i][1] & move_direction))
6410           return MF_NO_ACTION;  /* tube has no opening in this direction */
6411
6412         PlaySoundLevel(x, y, SND_TUBE_PASSING);
6413       }
6414       break;
6415
6416     case EL_EXIT_CLOSED:
6417     case EL_SP_EXIT_CLOSED:
6418     case EL_EXIT_OPENING:
6419       return MF_NO_ACTION;
6420       break;
6421
6422     case EL_EXIT_OPEN:
6423     case EL_SP_EXIT_OPEN:
6424       if (mode == DF_SNAP)
6425         return MF_NO_ACTION;
6426
6427       if (element == EL_EXIT_OPEN)
6428         PlaySoundLevel(x, y, SND_EXIT_PASSING);
6429       else
6430         PlaySoundLevel(x, y, SND_SP_EXIT_PASSING);
6431
6432       break;
6433
6434     case EL_LAMP:
6435       Feld[x][y] = EL_LAMP_ACTIVE;
6436       local_player->lights_still_needed--;
6437       DrawLevelField(x, y);
6438       PlaySoundLevel(x, y, SND_LAMP_ACTIVATING);
6439       return MF_ACTION;
6440       break;
6441
6442     case EL_TIME_ORB_FULL:
6443       Feld[x][y] = EL_TIME_ORB_EMPTY;
6444       TimeLeft += 10;
6445       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
6446       DrawLevelField(x, y);
6447       PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MAX_RIGHT);
6448       return MF_ACTION;
6449       break;
6450
6451     case EL_SOKOBAN_FIELD_EMPTY:
6452       break;
6453
6454     case EL_SOKOBAN_OBJECT:
6455     case EL_SOKOBAN_FIELD_FULL:
6456     case EL_SATELLITE:
6457     case EL_SP_DISK_YELLOW:
6458     case EL_BALLOON:
6459       if (mode == DF_SNAP)
6460         return MF_NO_ACTION;
6461
6462       player->Pushing = TRUE;
6463
6464       if (!IN_LEV_FIELD(x+dx, y+dy)
6465           || (!IS_FREE(x+dx, y+dy)
6466               && (Feld[x+dx][y+dy] != EL_SOKOBAN_FIELD_EMPTY
6467                   || !IS_SB_ELEMENT(element))))
6468         return MF_NO_ACTION;
6469
6470       if (dx && real_dy)
6471       {
6472         if (IN_LEV_FIELD(jx, jy+real_dy) && !IS_SOLID(Feld[jx][jy+real_dy]))
6473           return MF_NO_ACTION;
6474       }
6475       else if (dy && real_dx)
6476       {
6477         if (IN_LEV_FIELD(jx+real_dx, jy) && !IS_SOLID(Feld[jx+real_dx][jy]))
6478           return MF_NO_ACTION;
6479       }
6480
6481       if (player->push_delay == 0)
6482         player->push_delay = FrameCounter;
6483 #if 0
6484       if (!FrameReached(&player->push_delay, player->push_delay_value) &&
6485           !tape.playing && element != EL_BALLOON)
6486         return MF_NO_ACTION;
6487 #else
6488       if (!FrameReached(&player->push_delay, player->push_delay_value) &&
6489           !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
6490           element != EL_BALLOON)
6491         return MF_NO_ACTION;
6492 #endif
6493
6494       if (IS_SB_ELEMENT(element))
6495       {
6496         if (element == EL_SOKOBAN_FIELD_FULL)
6497         {
6498           Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
6499           local_player->sokobanfields_still_needed++;
6500         }
6501         else
6502           RemoveField(x, y);
6503
6504         if (Feld[x+dx][y+dy] == EL_SOKOBAN_FIELD_EMPTY)
6505         {
6506           Feld[x+dx][y+dy] = EL_SOKOBAN_FIELD_FULL;
6507           local_player->sokobanfields_still_needed--;
6508           if (element == EL_SOKOBAN_OBJECT)
6509             PlaySoundLevel(x, y, SND_SOKOBAN_FIELD_FILLING);
6510           else
6511             PlaySoundLevel(x, y, SND_SOKOBAN_OBJECT_PUSHING);
6512         }
6513         else
6514         {
6515           Feld[x+dx][y+dy] = EL_SOKOBAN_OBJECT;
6516           if (element == EL_SOKOBAN_FIELD_FULL)
6517             PlaySoundLevel(x, y, SND_SOKOBAN_FIELD_EMPTYING);
6518           else
6519             PlaySoundLevel(x, y, SND_SOKOBAN_OBJECT_PUSHING);
6520         }
6521       }
6522       else
6523       {
6524         RemoveField(x, y);
6525         Feld[x+dx][y+dy] = element;
6526         PlaySoundLevelElementAction(x, y, element, SND_ACTION_PUSHING);
6527       }
6528
6529       player->push_delay_value = (element == EL_BALLOON ? 0 : 2);
6530
6531       DrawLevelField(x, y);
6532       DrawLevelField(x + dx, y + dy);
6533
6534       if (IS_SB_ELEMENT(element) &&
6535           local_player->sokobanfields_still_needed == 0 &&
6536           game.emulation == EMU_SOKOBAN)
6537       {
6538         player->LevelSolved = player->GameOver = TRUE;
6539         PlaySoundLevel(x, y, SND_SOKOBAN_GAME_SOLVING);
6540       }
6541
6542       break;
6543
6544     case EL_PENGUIN:
6545     case EL_PIG:
6546     case EL_DRAGON:
6547       break;
6548
6549     default:
6550       return MF_NO_ACTION;
6551   }
6552
6553   player->push_delay = 0;
6554
6555   return MF_MOVING;
6556 }
6557
6558 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
6559 {
6560   int jx = player->jx, jy = player->jy;
6561   int x = jx + dx, y = jy + dy;
6562
6563   if (!player->active || !IN_LEV_FIELD(x, y))
6564     return FALSE;
6565
6566   if (dx && dy)
6567     return FALSE;
6568
6569   if (!dx && !dy)
6570   {
6571     if (player->MovPos == 0)
6572       player->Pushing = FALSE;
6573
6574     player->snapped = FALSE;
6575     return FALSE;
6576   }
6577
6578   if (player->snapped)
6579     return FALSE;
6580
6581   player->MovDir = (dx < 0 ? MV_LEFT :
6582                     dx > 0 ? MV_RIGHT :
6583                     dy < 0 ? MV_UP :
6584                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
6585
6586   if (!DigField(player, x, y, 0, 0, DF_SNAP))
6587     return FALSE;
6588
6589   player->snapped = TRUE;
6590   DrawLevelField(x, y);
6591   BackToFront();
6592
6593   return TRUE;
6594 }
6595
6596 boolean PlaceBomb(struct PlayerInfo *player)
6597 {
6598   int jx = player->jx, jy = player->jy;
6599   int element;
6600
6601   if (!player->active || player->MovPos)
6602     return FALSE;
6603
6604   element = Feld[jx][jy];
6605
6606   if ((player->dynamite == 0 && player->dynabombs_left == 0) ||
6607       IS_ACTIVE_BOMB(element) || element == EL_EXPLOSION)
6608     return FALSE;
6609
6610   if (element != EL_EMPTY)
6611     Store[jx][jy] = element;
6612
6613   if (player->dynamite)
6614   {
6615     Feld[jx][jy] = EL_DYNAMITE_ACTIVE;
6616     MovDelay[jx][jy] = 96;
6617     player->dynamite--;
6618     DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(local_player->dynamite, 3),
6619              FS_SMALL, FC_YELLOW);
6620     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
6621     {
6622       if (game.emulation == EMU_SUPAPLEX)
6623         DrawGraphic(SCREENX(jx), SCREENY(jy), IMG_SP_DISK_RED, 0);
6624       else
6625         DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), IMG_DYNAMITE_ACTIVE, 0);
6626     }
6627
6628     PlaySoundLevel(jx, jy, SND_DYNAMITE_DROPPING);
6629   }
6630   else
6631   {
6632     Feld[jx][jy] =
6633       EL_DYNABOMB_PLAYER1_ACTIVE + (player->element_nr - EL_PLAYER1);
6634     MovDelay[jx][jy] = 96;
6635     player->dynabombs_left--;
6636     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
6637       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
6638
6639     PlaySoundLevel(jx, jy, SND_DYNABOMB_DROPPING);
6640   }
6641
6642   return TRUE;
6643 }
6644
6645 void PlaySoundLevel(int x, int y, int nr)
6646 {
6647   static int loop_sound_frame[NUM_SOUND_FILES];
6648   static int loop_sound_volume[NUM_SOUND_FILES];
6649   int sx = SCREENX(x), sy = SCREENY(y);
6650   int volume, stereo_position;
6651   int max_distance = 8;
6652   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
6653
6654   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
6655       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
6656     return;
6657
6658   if (!IN_LEV_FIELD(x, y) ||
6659       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
6660       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
6661     return;
6662
6663   volume = SOUND_MAX_VOLUME;
6664
6665   if (!IN_SCR_FIELD(sx, sy))
6666   {
6667     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
6668     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
6669
6670     volume -= volume * (dx > dy ? dx : dy) / max_distance;
6671   }
6672
6673   stereo_position = (SOUND_MAX_LEFT +
6674                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
6675                      (SCR_FIELDX + 2 * max_distance));
6676
6677   if (IS_LOOP_SOUND(nr))
6678   {
6679     /* This assures that quieter loop sounds do not overwrite louder ones,
6680        while restarting sound volume comparison with each new game frame. */
6681
6682     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
6683       return;
6684
6685     loop_sound_volume[nr] = volume;
6686     loop_sound_frame[nr] = FrameCounter;
6687   }
6688
6689   PlaySoundExt(nr, volume, stereo_position, type);
6690 }
6691
6692 void PlaySoundLevelAction(int x, int y, int sound_action)
6693 {
6694   PlaySoundLevelElementAction(x, y, Feld[x][y], sound_action);
6695 }
6696
6697 void PlaySoundLevelElementAction(int x, int y, int element, int sound_action)
6698 {
6699   int sound_effect = element_action_sound[element][sound_action];
6700
6701   if (sound_effect != -1)
6702     PlaySoundLevel(x, y, sound_effect);
6703 }
6704
6705 void RaiseScore(int value)
6706 {
6707   local_player->score += value;
6708   DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5),
6709            FS_SMALL, FC_YELLOW);
6710 }
6711
6712 void RaiseScoreElement(int element)
6713 {
6714   switch(element)
6715   {
6716     case EL_EMERALD:
6717     case EL_BD_DIAMOND:
6718     case EL_EMERALD_YELLOW:
6719     case EL_EMERALD_RED:
6720     case EL_EMERALD_PURPLE:
6721       RaiseScore(level.score[SC_EDELSTEIN]);
6722       break;
6723     case EL_DIAMOND:
6724       RaiseScore(level.score[SC_DIAMANT]);
6725       break;
6726     case EL_BUG:
6727     case EL_BD_BUTTERFLY:
6728       RaiseScore(level.score[SC_KAEFER]);
6729       break;
6730     case EL_SPACESHIP:
6731     case EL_BD_FIREFLY:
6732       RaiseScore(level.score[SC_FLIEGER]);
6733       break;
6734     case EL_YAMYAM:
6735     case EL_DARK_YAMYAM:
6736       RaiseScore(level.score[SC_MAMPFER]);
6737       break;
6738     case EL_ROBOT:
6739       RaiseScore(level.score[SC_ROBOT]);
6740       break;
6741     case EL_PACMAN:
6742       RaiseScore(level.score[SC_PACMAN]);
6743       break;
6744     case EL_NUT:
6745       RaiseScore(level.score[SC_KOKOSNUSS]);
6746       break;
6747     case EL_DYNAMITE:
6748       RaiseScore(level.score[SC_DYNAMIT]);
6749       break;
6750     case EL_KEY1:
6751     case EL_KEY2:
6752     case EL_KEY3:
6753     case EL_KEY4:
6754       RaiseScore(level.score[SC_SCHLUESSEL]);
6755       break;
6756     default:
6757       break;
6758   }
6759 }
6760
6761 void RequestQuitGame(boolean ask_if_really_quit)
6762 {
6763   if (AllPlayersGone ||
6764       !ask_if_really_quit ||
6765       level_editor_test_game ||
6766       Request("Do you really want to quit the game ?",
6767               REQ_ASK | REQ_STAY_CLOSED))
6768   {
6769 #if defined(PLATFORM_UNIX)
6770     if (options.network)
6771       SendToServer_StopPlaying();
6772     else
6773 #endif
6774     {
6775       game_status = MAINMENU;
6776       DrawMainMenu();
6777     }
6778   }
6779   else
6780   {
6781     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
6782   }
6783 }
6784
6785
6786 /* ---------- new game button stuff ---------------------------------------- */
6787
6788 /* graphic position values for game buttons */
6789 #define GAME_BUTTON_XSIZE       30
6790 #define GAME_BUTTON_YSIZE       30
6791 #define GAME_BUTTON_XPOS        5
6792 #define GAME_BUTTON_YPOS        215
6793 #define SOUND_BUTTON_XPOS       5
6794 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
6795
6796 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
6797 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
6798 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
6799 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
6800 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
6801 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
6802
6803 static struct
6804 {
6805   int x, y;
6806   int gadget_id;
6807   char *infotext;
6808 } gamebutton_info[NUM_GAME_BUTTONS] =
6809 {
6810   {
6811     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
6812     GAME_CTRL_ID_STOP,
6813     "stop game"
6814   },
6815   {
6816     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
6817     GAME_CTRL_ID_PAUSE,
6818     "pause game"
6819   },
6820   {
6821     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
6822     GAME_CTRL_ID_PLAY,
6823     "play game"
6824   },
6825   {
6826     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
6827     SOUND_CTRL_ID_MUSIC,
6828     "background music on/off"
6829   },
6830   {
6831     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
6832     SOUND_CTRL_ID_LOOPS,
6833     "sound loops on/off"
6834   },
6835   {
6836     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
6837     SOUND_CTRL_ID_SIMPLE,
6838     "normal sounds on/off"
6839   }
6840 };
6841
6842 void CreateGameButtons()
6843 {
6844   int i;
6845
6846   for (i=0; i<NUM_GAME_BUTTONS; i++)
6847   {
6848     Bitmap *gd_bitmap = pix[PIX_DOOR];
6849     struct GadgetInfo *gi;
6850     int button_type;
6851     boolean checked;
6852     unsigned long event_mask;
6853     int gd_xoffset, gd_yoffset;
6854     int gd_x1, gd_x2, gd_y1, gd_y2;
6855     int id = i;
6856
6857     gd_xoffset = gamebutton_info[i].x;
6858     gd_yoffset = gamebutton_info[i].y;
6859     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
6860     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
6861
6862     if (id == GAME_CTRL_ID_STOP ||
6863         id == GAME_CTRL_ID_PAUSE ||
6864         id == GAME_CTRL_ID_PLAY)
6865     {
6866       button_type = GD_TYPE_NORMAL_BUTTON;
6867       checked = FALSE;
6868       event_mask = GD_EVENT_RELEASED;
6869       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
6870       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
6871     }
6872     else
6873     {
6874       button_type = GD_TYPE_CHECK_BUTTON;
6875       checked =
6876         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
6877          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
6878          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
6879       event_mask = GD_EVENT_PRESSED;
6880       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
6881       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
6882     }
6883
6884     gi = CreateGadget(GDI_CUSTOM_ID, id,
6885                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
6886                       GDI_X, DX + gd_xoffset,
6887                       GDI_Y, DY + gd_yoffset,
6888                       GDI_WIDTH, GAME_BUTTON_XSIZE,
6889                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
6890                       GDI_TYPE, button_type,
6891                       GDI_STATE, GD_BUTTON_UNPRESSED,
6892                       GDI_CHECKED, checked,
6893                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
6894                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
6895                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
6896                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
6897                       GDI_EVENT_MASK, event_mask,
6898                       GDI_CALLBACK_ACTION, HandleGameButtons,
6899                       GDI_END);
6900
6901     if (gi == NULL)
6902       Error(ERR_EXIT, "cannot create gadget");
6903
6904     game_gadget[id] = gi;
6905   }
6906 }
6907
6908 static void MapGameButtons()
6909 {
6910   int i;
6911
6912   for (i=0; i<NUM_GAME_BUTTONS; i++)
6913     MapGadget(game_gadget[i]);
6914 }
6915
6916 void UnmapGameButtons()
6917 {
6918   int i;
6919
6920   for (i=0; i<NUM_GAME_BUTTONS; i++)
6921     UnmapGadget(game_gadget[i]);
6922 }
6923
6924 static void HandleGameButtons(struct GadgetInfo *gi)
6925 {
6926   int id = gi->custom_id;
6927
6928   if (game_status != PLAYING)
6929     return;
6930
6931   switch (id)
6932   {
6933     case GAME_CTRL_ID_STOP:
6934       RequestQuitGame(TRUE);
6935       break;
6936
6937     case GAME_CTRL_ID_PAUSE:
6938       if (options.network)
6939       {
6940 #if defined(PLATFORM_UNIX)
6941         if (tape.pausing)
6942           SendToServer_ContinuePlaying();
6943         else
6944           SendToServer_PausePlaying();
6945 #endif
6946       }
6947       else
6948         TapeTogglePause(TAPE_TOGGLE_MANUAL);
6949       break;
6950
6951     case GAME_CTRL_ID_PLAY:
6952       if (tape.pausing)
6953       {
6954 #if defined(PLATFORM_UNIX)
6955         if (options.network)
6956           SendToServer_ContinuePlaying();
6957         else
6958 #endif
6959         {
6960           tape.pausing = FALSE;
6961           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
6962         }
6963       }
6964       break;
6965
6966     case SOUND_CTRL_ID_MUSIC:
6967       if (setup.sound_music)
6968       { 
6969         setup.sound_music = FALSE;
6970         FadeMusic();
6971       }
6972       else if (audio.music_available)
6973       { 
6974         setup.sound = setup.sound_music = TRUE;
6975         PlayMusic(level_nr);
6976       }
6977       break;
6978
6979     case SOUND_CTRL_ID_LOOPS:
6980       if (setup.sound_loops)
6981         setup.sound_loops = FALSE;
6982       else if (audio.loops_available)
6983         setup.sound = setup.sound_loops = TRUE;
6984       break;
6985
6986     case SOUND_CTRL_ID_SIMPLE:
6987       if (setup.sound_simple)
6988         setup.sound_simple = FALSE;
6989       else if (audio.sound_available)
6990         setup.sound = setup.sound_simple = TRUE;
6991       break;
6992
6993     default:
6994       break;
6995   }
6996 }