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