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