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