rnd-20030611-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 (!CAN_CHANGE(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   boolean impact = (lastline || object_hit);
2320   int element = Feld[x][y];
2321   int smashed = EL_UNDEFINED;
2322
2323   if (!lastline)        /* check if element below was hit */
2324   {
2325     if (Feld[x][y+1] == EL_PLAYER_IS_LEAVING)
2326       return;
2327
2328     object_hit = (!IS_FREE(x, y+1) && (!IS_MOVING(x, y+1) ||
2329                                        MovDir[x][y+1] != MV_DOWN ||
2330                                        MovPos[x][y+1] <= TILEY / 2));
2331     if (object_hit)
2332       smashed = MovingOrBlocked2Element(x, y+1);
2333
2334     impact = (lastline || object_hit);
2335   }
2336
2337   if (!lastline && smashed == EL_ACID)  /* element falls into acid */
2338   {
2339     SplashAcid(x, y);
2340     return;
2341   }
2342
2343   if (impact)
2344   {
2345     ResetGfxAnimation(x, y);
2346     DrawLevelField(x, y);
2347   }
2348
2349 #if 1
2350   if (impact && CAN_EXPLODE_IMPACT(element))
2351 #else
2352   if ((element == EL_BOMB ||
2353        element == EL_SP_DISK_ORANGE ||
2354        element == EL_DX_SUPABOMB) &&
2355       (lastline || object_hit))         /* element is bomb */
2356 #endif
2357   {
2358     Bang(x, y);
2359     return;
2360   }
2361   else if (element == EL_PEARL)
2362   {
2363     Feld[x][y] = EL_PEARL_BREAKING;
2364     PlaySoundLevel(x, y, SND_PEARL_BREAKING);
2365     return;
2366   }
2367
2368   if (impact && element == EL_AMOEBA_DROP)
2369   {
2370     if (object_hit && IS_PLAYER(x, y+1))
2371       KillHeroUnlessProtected(x, y+1);
2372     else if (object_hit && smashed == EL_PENGUIN)
2373       Bang(x, y + 1);
2374     else
2375     {
2376       Feld[x][y] = EL_AMOEBA_GROWING;
2377       Store[x][y] = EL_AMOEBA_WET;
2378
2379       ResetRandomAnimationValue(x, y);
2380     }
2381     return;
2382   }
2383
2384 #if 1
2385   if (object_hit)               /* check which object was hit */
2386 #else
2387   if (!lastline && object_hit)          /* check which object was hit */
2388 #endif
2389   {
2390     if (CAN_PASS_MAGIC_WALL(element) && 
2391         (smashed == EL_MAGIC_WALL ||
2392          smashed == EL_BD_MAGIC_WALL))
2393     {
2394       int xx, yy;
2395       int activated_magic_wall =
2396         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
2397          EL_BD_MAGIC_WALL_ACTIVE);
2398
2399       /* activate magic wall / mill */
2400       for (yy=0; yy<lev_fieldy; yy++)
2401         for (xx=0; xx<lev_fieldx; xx++)
2402           if (Feld[xx][yy] == smashed)
2403             Feld[xx][yy] = activated_magic_wall;
2404
2405       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
2406       game.magic_wall_active = TRUE;
2407
2408       PlaySoundLevel(x, y, (smashed == EL_MAGIC_WALL ?
2409                             SND_MAGIC_WALL_ACTIVATING :
2410                             SND_BD_MAGIC_WALL_ACTIVATING));
2411     }
2412
2413     if (IS_PLAYER(x, y + 1))
2414     {
2415       if (CAN_SMASH_PLAYER(element))
2416       {
2417         KillHeroUnlessProtected(x, y+1);
2418         return;
2419       }
2420     }
2421     else if (smashed == EL_PENGUIN)
2422     {
2423       if (CAN_SMASH_PLAYER(element))
2424       {
2425         Bang(x, y + 1);
2426         return;
2427       }
2428     }
2429     else if (element == EL_BD_DIAMOND)
2430     {
2431       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
2432       {
2433         Bang(x, y + 1);
2434         return;
2435       }
2436     }
2437     else if ((element == EL_SP_INFOTRON ||
2438               element == EL_SP_ZONK) &&
2439              (smashed == EL_SP_SNIKSNAK ||
2440               smashed == EL_SP_ELECTRON ||
2441               smashed == EL_SP_DISK_ORANGE))
2442     {
2443       Bang(x, y + 1);
2444       return;
2445     }
2446 #if 1
2447     else if (CAN_SMASH_EVERYTHING(element))
2448 #else
2449     else if (element == EL_ROCK ||
2450              element == EL_SP_ZONK ||
2451              element == EL_BD_ROCK)
2452 #endif
2453     {
2454       if (IS_CLASSIC_ENEMY(smashed) ||
2455 #if 1
2456           CAN_EXPLODE_SMASHED(smashed))
2457 #else
2458           smashed == EL_BOMB ||
2459           smashed == EL_SP_DISK_ORANGE ||
2460           smashed == EL_DX_SUPABOMB ||
2461           smashed == EL_SATELLITE ||
2462           smashed == EL_PIG ||
2463           smashed == EL_DRAGON ||
2464           smashed == EL_MOLE)
2465 #endif
2466       {
2467         Bang(x, y + 1);
2468         return;
2469       }
2470       else if (!IS_MOVING(x, y + 1))
2471       {
2472         if (smashed == EL_LAMP ||
2473             smashed == EL_LAMP_ACTIVE)
2474         {
2475           Bang(x, y + 1);
2476           return;
2477         }
2478         else if (smashed == EL_NUT)
2479         {
2480           Feld[x][y+1] = EL_NUT_BREAKING;
2481           PlaySoundLevel(x, y, SND_NUT_BREAKING);
2482           RaiseScoreElement(EL_NUT);
2483           return;
2484         }
2485         else if (smashed == EL_PEARL)
2486         {
2487           Feld[x][y+1] = EL_PEARL_BREAKING;
2488           PlaySoundLevel(x, y, SND_PEARL_BREAKING);
2489           return;
2490         }
2491         else if (smashed == EL_DIAMOND)
2492         {
2493           Feld[x][y+1] = EL_EMPTY;
2494           PlaySoundLevel(x, y, SND_DIAMOND_BREAKING);
2495           return;
2496         }
2497         else if (IS_BELT_SWITCH(smashed))
2498         {
2499           ToggleBeltSwitch(x, y+1);
2500         }
2501         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
2502                  smashed == EL_SWITCHGATE_SWITCH_DOWN)
2503         {
2504           ToggleSwitchgateSwitch(x, y+1);
2505         }
2506         else if (smashed == EL_LIGHT_SWITCH ||
2507                  smashed == EL_LIGHT_SWITCH_ACTIVE)
2508         {
2509           ToggleLightSwitch(x, y+1);
2510         }
2511       }
2512     }
2513   }
2514
2515   /* play sound of magic wall / mill */
2516   if (!lastline &&
2517       (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE ||
2518        Feld[x][y+1] == EL_BD_MAGIC_WALL_ACTIVE))
2519   {
2520     if (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE)
2521       PlaySoundLevel(x, y, SND_MAGIC_WALL_FILLING);
2522     else if (Feld[x][y+1] == EL_BD_MAGIC_WALL_ACTIVE)
2523       PlaySoundLevel(x, y, SND_BD_MAGIC_WALL_FILLING);
2524
2525     return;
2526   }
2527
2528   /* play sound of object that hits the ground */
2529   if (lastline || object_hit)
2530     PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT);
2531 }
2532
2533 void TurnRound(int x, int y)
2534 {
2535   static struct
2536   {
2537     int x, y;
2538   } move_xy[] =
2539   {
2540     {  0,  0 },
2541     { -1,  0 },
2542     { +1,  0 },
2543     {  0,  0 },
2544     {  0, -1 },
2545     {  0,  0 }, { 0, 0 }, { 0, 0 },
2546     {  0, +1 }
2547   };
2548   static struct
2549   {
2550     int left, right, back;
2551   } turn[] =
2552   {
2553     { 0,        0,              0        },
2554     { MV_DOWN,  MV_UP,          MV_RIGHT },
2555     { MV_UP,    MV_DOWN,        MV_LEFT  },
2556     { 0,        0,              0        },
2557     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
2558     { 0,0,0 },  { 0,0,0 },      { 0,0,0  },
2559     { MV_RIGHT, MV_LEFT,        MV_UP    }
2560   };
2561
2562   int element = Feld[x][y];
2563   int old_move_dir = MovDir[x][y];
2564   int left_dir  = turn[old_move_dir].left;
2565   int right_dir = turn[old_move_dir].right;
2566   int back_dir  = turn[old_move_dir].back;
2567
2568   int left_dx  = move_xy[left_dir].x,     left_dy  = move_xy[left_dir].y;
2569   int right_dx = move_xy[right_dir].x,    right_dy = move_xy[right_dir].y;
2570   int move_dx  = move_xy[old_move_dir].x, move_dy  = move_xy[old_move_dir].y;
2571   int back_dx  = move_xy[back_dir].x,     back_dy  = move_xy[back_dir].y;
2572
2573   int left_x  = x + left_dx,  left_y  = y + left_dy;
2574   int right_x = x + right_dx, right_y = y + right_dy;
2575   int move_x  = x + move_dx,  move_y  = y + move_dy;
2576
2577   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2578   {
2579     TestIfBadThingTouchesOtherBadThing(x, y);
2580
2581     if (IN_LEV_FIELD(right_x, right_y) &&
2582         IS_FREE(right_x, right_y))
2583       MovDir[x][y] = right_dir;
2584     else if (!IN_LEV_FIELD(move_x, move_y) ||
2585              !IS_FREE(move_x, move_y))
2586       MovDir[x][y] = left_dir;
2587
2588     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
2589       MovDelay[x][y] = 9;
2590     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
2591       MovDelay[x][y] = 1;
2592   }
2593   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2594            element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2595   {
2596     TestIfBadThingTouchesOtherBadThing(x, y);
2597
2598     if (IN_LEV_FIELD(left_x, left_y) &&
2599         IS_FREE(left_x, left_y))
2600       MovDir[x][y] = left_dir;
2601     else if (!IN_LEV_FIELD(move_x, move_y) ||
2602              !IS_FREE(move_x, move_y))
2603       MovDir[x][y] = right_dir;
2604
2605     if ((element == EL_SPACESHIP ||
2606          element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2607         && MovDir[x][y] != old_move_dir)
2608       MovDelay[x][y] = 9;
2609     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
2610       MovDelay[x][y] = 1;
2611   }
2612   else if (element == EL_YAMYAM)
2613   {
2614     boolean can_turn_left = FALSE, can_turn_right = FALSE;
2615
2616     if (IN_LEV_FIELD(left_x, left_y) &&
2617         (IS_FREE_OR_PLAYER(left_x, left_y) ||
2618          Feld[left_x][left_y] == EL_DIAMOND))
2619       can_turn_left = TRUE;
2620     if (IN_LEV_FIELD(right_x, right_y) &&
2621         (IS_FREE_OR_PLAYER(right_x, right_y) ||
2622          Feld[right_x][right_y] == EL_DIAMOND))
2623       can_turn_right = TRUE;
2624
2625     if (can_turn_left && can_turn_right)
2626       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
2627     else if (can_turn_left)
2628       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
2629     else if (can_turn_right)
2630       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
2631     else
2632       MovDir[x][y] = back_dir;
2633
2634     MovDelay[x][y] = 16+16*RND(3);
2635   }
2636   else if (element == EL_DARK_YAMYAM)
2637   {
2638     boolean can_turn_left = FALSE, can_turn_right = FALSE;
2639
2640     if (IN_LEV_FIELD(left_x, left_y) &&
2641         (IS_FREE_OR_PLAYER(left_x, left_y) ||
2642          IS_FOOD_DARK_YAMYAM(Feld[left_x][left_y])))
2643       can_turn_left = TRUE;
2644     if (IN_LEV_FIELD(right_x, right_y) &&
2645         (IS_FREE_OR_PLAYER(right_x, right_y) ||
2646          IS_FOOD_DARK_YAMYAM(Feld[right_x][right_y])))
2647       can_turn_right = TRUE;
2648
2649     if (can_turn_left && can_turn_right)
2650       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
2651     else if (can_turn_left)
2652       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
2653     else if (can_turn_right)
2654       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
2655     else
2656       MovDir[x][y] = back_dir;
2657
2658     MovDelay[x][y] = 16+16*RND(3);
2659   }
2660   else if (element == EL_PACMAN)
2661   {
2662     boolean can_turn_left = FALSE, can_turn_right = FALSE;
2663
2664     if (IN_LEV_FIELD(left_x, left_y) &&
2665         (IS_FREE_OR_PLAYER(left_x, left_y) ||
2666          IS_AMOEBOID(Feld[left_x][left_y])))
2667       can_turn_left = TRUE;
2668     if (IN_LEV_FIELD(right_x, right_y) &&
2669         (IS_FREE_OR_PLAYER(right_x, right_y) ||
2670          IS_AMOEBOID(Feld[right_x][right_y])))
2671       can_turn_right = TRUE;
2672
2673     if (can_turn_left && can_turn_right)
2674       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
2675     else if (can_turn_left)
2676       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
2677     else if (can_turn_right)
2678       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
2679     else
2680       MovDir[x][y] = back_dir;
2681
2682     MovDelay[x][y] = 6+RND(40);
2683   }
2684   else if (element == EL_PIG)
2685   {
2686     boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE;
2687     boolean should_turn_left = FALSE, should_turn_right = FALSE;
2688     boolean should_move_on = FALSE;
2689     int rnd_value = 24;
2690     int rnd = RND(rnd_value);
2691
2692     if (IN_LEV_FIELD(left_x, left_y) &&
2693         (IS_FREE(left_x, left_y) || IS_FOOD_PIG(Feld[left_x][left_y])))
2694       can_turn_left = TRUE;
2695     if (IN_LEV_FIELD(right_x, right_y) &&
2696         (IS_FREE(right_x, right_y) || IS_FOOD_PIG(Feld[right_x][right_y])))
2697       can_turn_right = TRUE;
2698     if (IN_LEV_FIELD(move_x, move_y) &&
2699         (IS_FREE(move_x, move_y) || IS_FOOD_PIG(Feld[move_x][move_y])))
2700       can_move_on = TRUE;
2701
2702     if (can_turn_left &&
2703         (!can_move_on ||
2704          (IN_LEV_FIELD(x+back_dx+left_dx, y+back_dy+left_dy) &&
2705           !IS_FREE(x+back_dx+left_dx, y+back_dy+left_dy))))
2706       should_turn_left = TRUE;
2707     if (can_turn_right &&
2708         (!can_move_on ||
2709          (IN_LEV_FIELD(x+back_dx+right_dx, y+back_dy+right_dy) &&
2710           !IS_FREE(x+back_dx+right_dx, y+back_dy+right_dy))))
2711       should_turn_right = TRUE;
2712     if (can_move_on &&
2713         (!can_turn_left || !can_turn_right ||
2714          (IN_LEV_FIELD(x+move_dx+left_dx, y+move_dy+left_dy) &&
2715           !IS_FREE(x+move_dx+left_dx, y+move_dy+left_dy)) ||
2716          (IN_LEV_FIELD(x+move_dx+right_dx, y+move_dy+right_dy) &&
2717           !IS_FREE(x+move_dx+right_dx, y+move_dy+right_dy))))
2718       should_move_on = TRUE;
2719
2720     if (should_turn_left || should_turn_right || should_move_on)
2721     {
2722       if (should_turn_left && should_turn_right && should_move_on)
2723         MovDir[x][y] = (rnd < rnd_value/3 ? left_dir :
2724                         rnd < 2*rnd_value/3 ? right_dir :
2725                         old_move_dir);
2726       else if (should_turn_left && should_turn_right)
2727         MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : right_dir);
2728       else if (should_turn_left && should_move_on)
2729         MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : old_move_dir);
2730       else if (should_turn_right && should_move_on)
2731         MovDir[x][y] = (rnd < rnd_value/2 ? right_dir : old_move_dir);
2732       else if (should_turn_left)
2733         MovDir[x][y] = left_dir;
2734       else if (should_turn_right)
2735         MovDir[x][y] = right_dir;
2736       else if (should_move_on)
2737         MovDir[x][y] = old_move_dir;
2738     }
2739     else if (can_move_on && rnd > rnd_value/8)
2740       MovDir[x][y] = old_move_dir;
2741     else if (can_turn_left && can_turn_right)
2742       MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : right_dir);
2743     else if (can_turn_left && rnd > rnd_value/8)
2744       MovDir[x][y] = left_dir;
2745     else if (can_turn_right && rnd > rnd_value/8)
2746       MovDir[x][y] = right_dir;
2747     else
2748       MovDir[x][y] = back_dir;
2749
2750     if (!IS_FREE(x+move_xy[MovDir[x][y]].x, y+move_xy[MovDir[x][y]].y) &&
2751         !IS_FOOD_PIG(Feld[x+move_xy[MovDir[x][y]].x][y+move_xy[MovDir[x][y]].y]))
2752       MovDir[x][y] = old_move_dir;
2753
2754     MovDelay[x][y] = 0;
2755   }
2756   else if (element == EL_DRAGON)
2757   {
2758     boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE;
2759     int rnd_value = 24;
2760     int rnd = RND(rnd_value);
2761
2762     if (IN_LEV_FIELD(left_x, left_y) && IS_FREE(left_x, left_y))
2763       can_turn_left = TRUE;
2764     if (IN_LEV_FIELD(right_x, right_y) && IS_FREE(right_x, right_y))
2765       can_turn_right = TRUE;
2766     if (IN_LEV_FIELD(move_x, move_y) && IS_FREE(move_x, move_y))
2767       can_move_on = TRUE;
2768
2769     if (can_move_on && rnd > rnd_value/8)
2770       MovDir[x][y] = old_move_dir;
2771     else if (can_turn_left && can_turn_right)
2772       MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : right_dir);
2773     else if (can_turn_left && rnd > rnd_value/8)
2774       MovDir[x][y] = left_dir;
2775     else if (can_turn_right && rnd > rnd_value/8)
2776       MovDir[x][y] = right_dir;
2777     else
2778       MovDir[x][y] = back_dir;
2779
2780     if (!IS_FREE(x+move_xy[MovDir[x][y]].x, y+move_xy[MovDir[x][y]].y))
2781       MovDir[x][y] = old_move_dir;
2782
2783     MovDelay[x][y] = 0;
2784   }
2785   else if (element == EL_MOLE)
2786   {
2787     boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE;
2788
2789     if (IN_LEV_FIELD(move_x, move_y) &&
2790         (IS_FREE(move_x, move_y) || IS_AMOEBOID(Feld[move_x][move_y]) ||
2791          Feld[move_x][move_y] == EL_AMOEBA_SHRINKING))
2792       can_move_on = TRUE;
2793
2794     if (!can_move_on)
2795     {
2796       if (IN_LEV_FIELD(left_x, left_y) &&
2797           (IS_FREE(left_x, left_y) || IS_AMOEBOID(Feld[left_x][left_y])))
2798         can_turn_left = TRUE;
2799       if (IN_LEV_FIELD(right_x, right_y) &&
2800           (IS_FREE(right_x, right_y) || IS_AMOEBOID(Feld[right_x][right_y])))
2801         can_turn_right = TRUE;
2802
2803       if (can_turn_left && can_turn_right)
2804         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
2805       else if (can_turn_left)
2806         MovDir[x][y] = left_dir;
2807       else
2808         MovDir[x][y] = right_dir;
2809     }
2810
2811     if (MovDir[x][y] != old_move_dir)
2812       MovDelay[x][y] = 9;
2813   }
2814   else if (element == EL_BALLOON)
2815   {
2816     MovDir[x][y] = game.balloon_dir;
2817     MovDelay[x][y] = 0;
2818   }
2819   else if (element == EL_SPRING)
2820   {
2821     if ((MovDir[x][y] == MV_LEFT || MovDir[x][y] == MV_RIGHT) &&
2822         (!IN_LEV_FIELD(move_x, move_y) || !IS_FREE(move_x, move_y) ||
2823          (IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))
2824       MovDir[x][y] = MV_NO_MOVING;
2825
2826     MovDelay[x][y] = 0;
2827   }
2828   else if (element == EL_ROBOT ||
2829            element == EL_SATELLITE ||
2830            element == EL_PENGUIN)
2831   {
2832     int attr_x = -1, attr_y = -1;
2833
2834     if (AllPlayersGone)
2835     {
2836       attr_x = ExitX;
2837       attr_y = ExitY;
2838     }
2839     else
2840     {
2841       int i;
2842
2843       for (i=0; i<MAX_PLAYERS; i++)
2844       {
2845         struct PlayerInfo *player = &stored_player[i];
2846         int jx = player->jx, jy = player->jy;
2847
2848         if (!player->active)
2849           continue;
2850
2851         if (attr_x == -1 || ABS(jx-x)+ABS(jy-y) < ABS(attr_x-x)+ABS(attr_y-y))
2852         {
2853           attr_x = jx;
2854           attr_y = jy;
2855         }
2856       }
2857     }
2858
2859     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
2860     {
2861       attr_x = ZX;
2862       attr_y = ZY;
2863     }
2864
2865     if (element == EL_PENGUIN)
2866     {
2867       int i;
2868       static int xy[4][2] =
2869       {
2870         { 0, -1 },
2871         { -1, 0 },
2872         { +1, 0 },
2873         { 0, +1 }
2874       };
2875
2876       for (i=0; i<4; i++)
2877       {
2878         int ex = x + xy[i % 4][0];
2879         int ey = y + xy[i % 4][1];
2880
2881         if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
2882         {
2883           attr_x = ex;
2884           attr_y = ey;
2885           break;
2886         }
2887       }
2888     }
2889
2890     MovDir[x][y] = MV_NO_MOVING;
2891     if (attr_x < x)
2892       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
2893     else if (attr_x > x)
2894       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
2895     if (attr_y < y)
2896       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
2897     else if (attr_y > y)
2898       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
2899
2900     if (element == EL_ROBOT)
2901     {
2902       int newx, newy;
2903
2904       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
2905         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
2906       Moving2Blocked(x, y, &newx, &newy);
2907
2908       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
2909         MovDelay[x][y] = 8+8*!RND(3);
2910       else
2911         MovDelay[x][y] = 16;
2912     }
2913     else
2914     {
2915       int newx, newy;
2916
2917       MovDelay[x][y] = 1;
2918
2919       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
2920       {
2921         boolean first_horiz = RND(2);
2922         int new_move_dir = MovDir[x][y];
2923
2924         MovDir[x][y] =
2925           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
2926         Moving2Blocked(x, y, &newx, &newy);
2927
2928         if (IN_LEV_FIELD(newx, newy) &&
2929             (IS_FREE(newx, newy) ||
2930              Feld[newx][newy] == EL_ACID ||
2931              (element == EL_PENGUIN &&
2932               (Feld[newx][newy] == EL_EXIT_OPEN ||
2933                IS_FOOD_PENGUIN(Feld[newx][newy])))))
2934           return;
2935
2936         MovDir[x][y] =
2937           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
2938         Moving2Blocked(x, y, &newx, &newy);
2939
2940         if (IN_LEV_FIELD(newx, newy) &&
2941             (IS_FREE(newx, newy) ||
2942              Feld[newx][newy] == EL_ACID ||
2943              (element == EL_PENGUIN &&
2944               (Feld[newx][newy] == EL_EXIT_OPEN ||
2945                IS_FOOD_PENGUIN(Feld[newx][newy])))))
2946           return;
2947
2948         MovDir[x][y] = old_move_dir;
2949         return;
2950       }
2951     }
2952   }
2953   else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS)
2954   {
2955     boolean can_turn_left = FALSE, can_turn_right = FALSE;
2956
2957     if (IN_LEV_FIELD(left_x, left_y) &&
2958         (IS_FREE(left_x, left_y) ||
2959          (DONT_COLLIDE_WITH(element) && IS_FREE_OR_PLAYER(left_x, left_y))))
2960       can_turn_left = TRUE;
2961     if (IN_LEV_FIELD(right_x, right_y) &&
2962         (IS_FREE(right_x, right_y) ||
2963          (DONT_COLLIDE_WITH(element) && IS_FREE_OR_PLAYER(right_x, right_y))))
2964       can_turn_right = TRUE;
2965
2966     if (can_turn_left && can_turn_right)
2967       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
2968     else if (can_turn_left)
2969       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
2970     else if (can_turn_right)
2971       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
2972     else
2973       MovDir[x][y] = back_dir;
2974
2975     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
2976   }
2977   else if (element_info[element].move_pattern == MV_HORIZONTAL ||
2978            element_info[element].move_pattern == MV_VERTICAL)
2979   {
2980     if (element_info[element].move_pattern & old_move_dir)
2981       MovDir[x][y] = back_dir;
2982     else if (element_info[element].move_pattern == MV_HORIZONTAL)
2983       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2984     else if (element_info[element].move_pattern == MV_VERTICAL)
2985       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2986
2987     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
2988   }
2989   else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
2990   {
2991     MovDir[x][y] = element_info[element].move_pattern;
2992     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
2993   }
2994   else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE)
2995   {
2996     if (IN_LEV_FIELD(left_x, left_y) &&
2997         (IS_FREE(left_x, left_y) ||
2998          (DONT_COLLIDE_WITH(element) && IS_FREE_OR_PLAYER(left_x, left_y))))
2999       MovDir[x][y] = left_dir;
3000     else if (!IN_LEV_FIELD(move_x, move_y) ||
3001              (!IS_FREE(move_x, move_y) &&
3002               (!DONT_COLLIDE_WITH(element) || !IS_FREE_OR_PLAYER(move_x, move_y))))
3003       MovDir[x][y] = right_dir;
3004
3005     if (MovDir[x][y] != old_move_dir)
3006       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3007   }
3008   else if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
3009   {
3010     if (IN_LEV_FIELD(right_x, right_y) &&
3011         (IS_FREE(right_x, right_y) ||
3012          (DONT_COLLIDE_WITH(element) && IS_FREE_OR_PLAYER(right_x, right_y))))
3013       MovDir[x][y] = right_dir;
3014     else if (!IN_LEV_FIELD(move_x, move_y) ||
3015              (!IS_FREE(move_x, move_y) &&
3016               (!DONT_COLLIDE_WITH(element) || !IS_FREE_OR_PLAYER(move_x, move_y))))
3017       MovDir[x][y] = left_dir;
3018
3019     if (MovDir[x][y] != old_move_dir)
3020       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3021   }
3022   else if (element_info[element].move_pattern == MV_TOWARDS_PLAYER ||
3023            element_info[element].move_pattern == MV_AWAY_FROM_PLAYER)
3024   {
3025     int attr_x = -1, attr_y = -1;
3026     int newx, newy;
3027     boolean move_away =
3028       (element_info[element].move_pattern == MV_AWAY_FROM_PLAYER);
3029
3030     if (AllPlayersGone)
3031     {
3032       attr_x = ExitX;
3033       attr_y = ExitY;
3034     }
3035     else
3036     {
3037       int i;
3038
3039       for (i=0; i<MAX_PLAYERS; i++)
3040       {
3041         struct PlayerInfo *player = &stored_player[i];
3042         int jx = player->jx, jy = player->jy;
3043
3044         if (!player->active)
3045           continue;
3046
3047         if (attr_x == -1 || ABS(jx-x)+ABS(jy-y) < ABS(attr_x-x)+ABS(attr_y-y))
3048         {
3049           attr_x = jx;
3050           attr_y = jy;
3051         }
3052       }
3053     }
3054
3055     MovDir[x][y] = MV_NO_MOVING;
3056     if (attr_x < x)
3057       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
3058     else if (attr_x > x)
3059       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
3060     if (attr_y < y)
3061       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
3062     else if (attr_y > y)
3063       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
3064
3065     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
3066
3067     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3068     {
3069       boolean first_horiz = RND(2);
3070       int new_move_dir = MovDir[x][y];
3071
3072       MovDir[x][y] =
3073         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3074       Moving2Blocked(x, y, &newx, &newy);
3075
3076       if (IN_LEV_FIELD(newx, newy) && (IS_FREE(newx, newy) ||
3077                                        (DONT_COLLIDE_WITH(element) &&
3078                                         IS_FREE_OR_PLAYER(newx, newy)) ||
3079                                        Feld[newx][newy] == EL_ACID))
3080         return;
3081
3082       MovDir[x][y] =
3083         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3084       Moving2Blocked(x, y, &newx, &newy);
3085
3086       if (IN_LEV_FIELD(newx, newy) && (IS_FREE(newx, newy) ||
3087                                        (DONT_COLLIDE_WITH(element) &&
3088                                         IS_FREE_OR_PLAYER(newx, newy)) ||
3089                                        Feld[newx][newy] == EL_ACID))
3090         return;
3091
3092       MovDir[x][y] = old_move_dir;
3093     }
3094   }
3095 }
3096
3097 static boolean JustBeingPushed(int x, int y)
3098 {
3099   int i;
3100
3101   for (i=0; i<MAX_PLAYERS; i++)
3102   {
3103     struct PlayerInfo *player = &stored_player[i];
3104
3105     if (player->active && player->Pushing && player->MovPos)
3106     {
3107       int next_jx = player->jx + (player->jx - player->last_jx);
3108       int next_jy = player->jy + (player->jy - player->last_jy);
3109
3110       if (x == next_jx && y == next_jy)
3111         return TRUE;
3112     }
3113   }
3114
3115   return FALSE;
3116 }
3117
3118 void StartMoving(int x, int y)
3119 {
3120   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0));
3121   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
3122   int element = Feld[x][y];
3123
3124   if (Stop[x][y])
3125     return;
3126
3127   GfxAction[x][y] = ACTION_DEFAULT;
3128
3129   if (CAN_FALL(element) && y < lev_fieldy - 1)
3130   {
3131     if ((x>0 && IS_PLAYER(x-1, y)) || (x<lev_fieldx-1 && IS_PLAYER(x+1, y)))
3132       if (JustBeingPushed(x, y))
3133         return;
3134
3135     if (element == EL_QUICKSAND_FULL)
3136     {
3137       if (IS_FREE(x, y+1))
3138       {
3139         InitMovingField(x, y, MV_DOWN);
3140         started_moving = TRUE;
3141
3142         Feld[x][y] = EL_QUICKSAND_EMPTYING;
3143         Store[x][y] = EL_ROCK;
3144 #if 1
3145         PlaySoundLevelAction(x, y, ACTION_EMPTYING);
3146 #else
3147         PlaySoundLevel(x, y, SND_QUICKSAND_EMPTYING);
3148 #endif
3149       }
3150       else if (Feld[x][y+1] == EL_QUICKSAND_EMPTY)
3151       {
3152         if (!MovDelay[x][y])
3153           MovDelay[x][y] = TILEY + 1;
3154
3155         if (MovDelay[x][y])
3156         {
3157           MovDelay[x][y]--;
3158           if (MovDelay[x][y])
3159             return;
3160         }
3161
3162         Feld[x][y] = EL_QUICKSAND_EMPTY;
3163         Feld[x][y+1] = EL_QUICKSAND_FULL;
3164         Store[x][y+1] = Store[x][y];
3165         Store[x][y] = 0;
3166 #if 1
3167         PlaySoundLevelAction(x, y, ACTION_FILLING);
3168 #else
3169         PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
3170 #endif
3171       }
3172     }
3173     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
3174              Feld[x][y+1] == EL_QUICKSAND_EMPTY)
3175     {
3176       InitMovingField(x, y, MV_DOWN);
3177       started_moving = TRUE;
3178
3179       Feld[x][y] = EL_QUICKSAND_FILLING;
3180       Store[x][y] = element;
3181 #if 1
3182       PlaySoundLevelAction(x, y, ACTION_FILLING);
3183 #else
3184       PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
3185 #endif
3186     }
3187     else if (element == EL_MAGIC_WALL_FULL)
3188     {
3189       if (IS_FREE(x, y+1))
3190       {
3191         InitMovingField(x, y, MV_DOWN);
3192         started_moving = TRUE;
3193
3194         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
3195         Store[x][y] = EL_CHANGED(Store[x][y]);
3196       }
3197       else if (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE)
3198       {
3199         if (!MovDelay[x][y])
3200           MovDelay[x][y] = TILEY/4 + 1;
3201
3202         if (MovDelay[x][y])
3203         {
3204           MovDelay[x][y]--;
3205           if (MovDelay[x][y])
3206             return;
3207         }
3208
3209         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
3210         Feld[x][y+1] = EL_MAGIC_WALL_FULL;
3211         Store[x][y+1] = EL_CHANGED(Store[x][y]);
3212         Store[x][y] = 0;
3213       }
3214     }
3215     else if (element == EL_BD_MAGIC_WALL_FULL)
3216     {
3217       if (IS_FREE(x, y+1))
3218       {
3219         InitMovingField(x, y, MV_DOWN);
3220         started_moving = TRUE;
3221
3222         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
3223         Store[x][y] = EL_CHANGED2(Store[x][y]);
3224       }
3225       else if (Feld[x][y+1] == EL_BD_MAGIC_WALL_ACTIVE)
3226       {
3227         if (!MovDelay[x][y])
3228           MovDelay[x][y] = TILEY/4 + 1;
3229
3230         if (MovDelay[x][y])
3231         {
3232           MovDelay[x][y]--;
3233           if (MovDelay[x][y])
3234             return;
3235         }
3236
3237         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
3238         Feld[x][y+1] = EL_BD_MAGIC_WALL_FULL;
3239         Store[x][y+1] = EL_CHANGED2(Store[x][y]);
3240         Store[x][y] = 0;
3241       }
3242     }
3243     else if (CAN_PASS_MAGIC_WALL(element) &&
3244              (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE ||
3245               Feld[x][y+1] == EL_BD_MAGIC_WALL_ACTIVE))
3246     {
3247       InitMovingField(x, y, MV_DOWN);
3248       started_moving = TRUE;
3249
3250       Feld[x][y] =
3251         (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
3252          EL_BD_MAGIC_WALL_FILLING);
3253       Store[x][y] = element;
3254     }
3255 #if 0
3256     else if (CAN_SMASH(element) && Feld[x][y+1] == EL_ACID)
3257 #else
3258     else if (CAN_FALL(element) && Feld[x][y+1] == EL_ACID)
3259 #endif
3260     {
3261       SplashAcid(x, y);
3262
3263       InitMovingField(x, y, MV_DOWN);
3264       started_moving = TRUE;
3265
3266       Store[x][y] = EL_ACID;
3267 #if 0
3268       /* !!! TEST !!! better use "_FALLING" etc. !!! */
3269       GfxAction[x][y+1] = ACTION_ACTIVE;
3270 #endif
3271     }
3272     else if (CAN_SMASH(element) && Feld[x][y+1] == EL_BLOCKED &&
3273              JustStopped[x][y])
3274     {
3275       Impact(x, y);
3276     }
3277     else if (IS_FREE(x, y+1) && element == EL_SPRING && use_spring_bug)
3278     {
3279       if (MovDir[x][y] == MV_NO_MOVING)
3280       {
3281         InitMovingField(x, y, MV_DOWN);
3282         started_moving = TRUE;
3283       }
3284     }
3285     else if (IS_FREE(x, y+1))
3286     {
3287       if (JustStopped[x][y])    /* prevent animation from being restarted */
3288         MovDir[x][y] = MV_DOWN;
3289
3290       InitMovingField(x, y, MV_DOWN);
3291       started_moving = TRUE;
3292     }
3293     else if (element == EL_AMOEBA_DROP)
3294     {
3295       Feld[x][y] = EL_AMOEBA_GROWING;
3296       Store[x][y] = EL_AMOEBA_WET;
3297     }
3298     /* Store[x][y+1] must be zero, because:
3299        (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y+1] == EL_QUICKSAND_EMPTY
3300     */
3301 #if 0
3302 #if OLD_GAME_BEHAVIOUR
3303     else if (IS_SLIPPERY(Feld[x][y+1]) && !Store[x][y+1])
3304 #else
3305     else if (IS_SLIPPERY(Feld[x][y+1]) && !Store[x][y+1] &&
3306              !IS_FALLING(x, y+1) && !JustStopped[x][y+1] &&
3307              element != EL_DX_SUPABOMB)
3308 #endif
3309 #else
3310     else if ((IS_SLIPPERY(Feld[x][y+1]) ||
3311               (IS_EM_SLIPPERY_WALL(Feld[x][y+1]) && IS_GEM(element))) &&
3312              !IS_FALLING(x, y+1) && !JustStopped[x][y+1] &&
3313              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
3314 #endif
3315     {
3316       boolean left  = (x>0 && IS_FREE(x-1, y) &&
3317                        (IS_FREE(x-1, y+1) || Feld[x-1][y+1] == EL_ACID));
3318       boolean right = (x<lev_fieldx-1 && IS_FREE(x+1, y) &&
3319                        (IS_FREE(x+1, y+1) || Feld[x+1][y+1] == EL_ACID));
3320
3321       if (left || right)
3322       {
3323         if (left && right &&
3324             (game.emulation != EMU_BOULDERDASH &&
3325              element != EL_BD_ROCK && element != EL_BD_DIAMOND))
3326           left = !(right = RND(2));
3327
3328         InitMovingField(x, y, left ? MV_LEFT : MV_RIGHT);
3329         started_moving = TRUE;
3330       }
3331     }
3332     else if (IS_BELT_ACTIVE(Feld[x][y+1]))
3333     {
3334       boolean left_is_free  = (x>0 && IS_FREE(x-1, y));
3335       boolean right_is_free = (x<lev_fieldx-1 && IS_FREE(x+1, y));
3336       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y+1]);
3337       int belt_dir = game.belt_dir[belt_nr];
3338
3339       if ((belt_dir == MV_LEFT  && left_is_free) ||
3340           (belt_dir == MV_RIGHT && right_is_free))
3341       {
3342         InitMovingField(x, y, belt_dir);
3343         started_moving = TRUE;
3344
3345         GfxAction[x][y] = ACTION_DEFAULT;
3346       }
3347     }
3348   }
3349
3350   /* not "else if" because of EL_SPRING */
3351   if (CAN_MOVE(element) && !started_moving)
3352   {
3353     int newx, newy;
3354
3355     if ((element == EL_SATELLITE ||
3356          element == EL_BALLOON ||
3357          element == EL_SPRING)
3358         && JustBeingPushed(x, y))
3359       return;
3360
3361 #if 0
3362 #if 0
3363     if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
3364       Feld[x][y + 1] = EL_EMPTY;        /* was set to EL_BLOCKED above */
3365 #else
3366     if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
3367     {
3368       Moving2Blocked(x, y, &newx, &newy);
3369       if (Feld[newx][newy] == EL_BLOCKED)
3370         Feld[newx][newy] = EL_EMPTY;    /* was set to EL_BLOCKED above */
3371     }
3372 #endif
3373 #endif
3374
3375     if (!MovDelay[x][y])        /* start new movement phase */
3376     {
3377       /* all objects that can change their move direction after each step
3378          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
3379
3380       if (element != EL_YAMYAM &&
3381           element != EL_DARK_YAMYAM &&
3382           element != EL_PACMAN &&
3383           !(element_info[element].move_pattern & MV_ANY_DIRECTION))
3384       {
3385         TurnRound(x, y);
3386
3387         if (MovDelay[x][y] && (element == EL_BUG ||
3388                                element == EL_SPACESHIP ||
3389                                element == EL_SP_SNIKSNAK ||
3390                                element == EL_SP_ELECTRON ||
3391                                element == EL_MOLE))
3392           DrawLevelField(x, y);
3393       }
3394     }
3395
3396     if (MovDelay[x][y])         /* wait some time before next movement */
3397     {
3398       MovDelay[x][y]--;
3399
3400 #if 0
3401       if (element == EL_YAMYAM)
3402       {
3403         printf("::: %d\n",
3404                el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
3405         DrawLevelElementAnimation(x, y, element);
3406       }
3407 #endif
3408
3409       if (MovDelay[x][y])       /* element still has to wait some time */
3410       {
3411 #if 0
3412         /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
3413         ResetGfxAnimation(x, y);
3414 #endif
3415         GfxAction[x][y] = ACTION_WAITING;
3416       }
3417
3418       if (element == EL_ROBOT ||
3419 #if 0
3420           element == EL_PACMAN ||
3421 #endif
3422           element == EL_YAMYAM ||
3423           element == EL_DARK_YAMYAM)
3424       {
3425 #if 0
3426         DrawLevelElementAnimation(x, y, element);
3427 #else
3428         DrawLevelElementAnimationIfNeeded(x, y, element);
3429 #endif
3430         PlaySoundLevelAction(x, y, ACTION_WAITING);
3431       }
3432       else if (element == EL_SP_ELECTRON)
3433         DrawLevelElementAnimationIfNeeded(x, y, element);
3434       else if (element == EL_DRAGON)
3435       {
3436         int i;
3437         int dir = MovDir[x][y];
3438         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
3439         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
3440         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
3441                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
3442                        dir == MV_UP     ? IMG_FLAMES_1_UP :
3443                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
3444         int frame = getGraphicAnimationFrame(graphic, -1);
3445
3446         for (i=1; i<=3; i++)
3447         {
3448           int xx = x + i*dx, yy = y + i*dy;
3449           int sx = SCREENX(xx), sy = SCREENY(yy);
3450           int flame_graphic = graphic + (i - 1);
3451
3452           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
3453             break;
3454
3455           if (MovDelay[x][y])
3456           {
3457             int flamed = MovingOrBlocked2Element(xx, yy);
3458
3459             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
3460               Bang(xx, yy);
3461             else
3462               RemoveMovingField(xx, yy);
3463
3464             Feld[xx][yy] = EL_FLAMES;
3465             if (IN_SCR_FIELD(sx, sy))
3466               DrawGraphic(sx, sy, flame_graphic, frame);
3467           }
3468           else
3469           {
3470             if (Feld[xx][yy] == EL_FLAMES)
3471               Feld[xx][yy] = EL_EMPTY;
3472             DrawLevelField(xx, yy);
3473           }
3474         }
3475       }
3476
3477       if (MovDelay[x][y])       /* element still has to wait some time */
3478       {
3479         PlaySoundLevelAction(x, y, ACTION_WAITING);
3480
3481         return;
3482       }
3483
3484       GfxAction[x][y] = ACTION_MOVING;
3485     }
3486
3487     /* now make next step */
3488
3489     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
3490
3491     if (DONT_COLLIDE_WITH(element) && IS_PLAYER(newx, newy) &&
3492         !PLAYER_PROTECTED(newx, newy))
3493     {
3494 #if 1
3495       TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
3496       return;
3497 #else
3498       /* player killed by element which is deadly when colliding with */
3499       MovDir[x][y] = 0;
3500       KillHero(PLAYERINFO(newx, newy));
3501       return;
3502 #endif
3503
3504     }
3505     else if ((element == EL_PENGUIN ||
3506               element == EL_ROBOT ||
3507               element == EL_SATELLITE ||
3508               element == EL_BALLOON ||
3509               IS_CUSTOM_ELEMENT(element)) &&
3510              IN_LEV_FIELD(newx, newy) &&
3511              MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
3512     {
3513       SplashAcid(x, y);
3514       Store[x][y] = EL_ACID;
3515     }
3516     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
3517     {
3518       if (Feld[newx][newy] == EL_EXIT_OPEN)
3519       {
3520         Feld[x][y] = EL_EMPTY;
3521         DrawLevelField(x, y);
3522
3523         PlaySoundLevel(newx, newy, SND_PENGUIN_PASSING);
3524         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
3525           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
3526
3527         local_player->friends_still_needed--;
3528         if (!local_player->friends_still_needed &&
3529             !local_player->GameOver && AllPlayersGone)
3530           local_player->LevelSolved = local_player->GameOver = TRUE;
3531
3532         return;
3533       }
3534       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
3535       {
3536         if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
3537           DrawLevelField(newx, newy);
3538         else
3539           MovDir[x][y] = MV_NO_MOVING;
3540       }
3541       else if (!IS_FREE(newx, newy))
3542       {
3543         GfxAction[x][y] = ACTION_WAITING;
3544
3545         if (IS_PLAYER(x, y))
3546           DrawPlayerField(x, y);
3547         else
3548           DrawLevelField(x, y);
3549         return;
3550       }
3551     }
3552     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
3553     {
3554       if (IS_FOOD_PIG(Feld[newx][newy]))
3555       {
3556         if (IS_MOVING(newx, newy))
3557           RemoveMovingField(newx, newy);
3558         else
3559         {
3560           Feld[newx][newy] = EL_EMPTY;
3561           DrawLevelField(newx, newy);
3562         }
3563
3564         PlaySoundLevel(x, y, SND_PIG_DIGGING);
3565       }
3566       else if (!IS_FREE(newx, newy))
3567       {
3568         if (IS_PLAYER(x, y))
3569           DrawPlayerField(x, y);
3570         else
3571           DrawLevelField(x, y);
3572         return;
3573       }
3574     }
3575     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
3576     {
3577       if (!IS_FREE(newx, newy))
3578       {
3579         if (IS_PLAYER(x, y))
3580           DrawPlayerField(x, y);
3581         else
3582           DrawLevelField(x, y);
3583         return;
3584       }
3585       else
3586       {
3587         boolean wanna_flame = !RND(10);
3588         int dx = newx - x, dy = newy - y;
3589         int newx1 = newx+1*dx, newy1 = newy+1*dy;
3590         int newx2 = newx+2*dx, newy2 = newy+2*dy;
3591         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
3592                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
3593         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
3594                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
3595
3596         if ((wanna_flame ||
3597              IS_CLASSIC_ENEMY(element1) ||
3598              IS_CLASSIC_ENEMY(element2)) &&
3599             element1 != EL_DRAGON && element2 != EL_DRAGON &&
3600             element1 != EL_FLAMES && element2 != EL_FLAMES)
3601         {
3602           if (IS_PLAYER(x, y))
3603             DrawPlayerField(x, y);
3604           else
3605             DrawLevelField(x, y);
3606
3607           PlaySoundLevel(x, y, SND_DRAGON_ATTACKING);
3608
3609           MovDelay[x][y] = 50;
3610           Feld[newx][newy] = EL_FLAMES;
3611           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
3612             Feld[newx1][newy1] = EL_FLAMES;
3613           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
3614             Feld[newx2][newy2] = EL_FLAMES;
3615           return;
3616         }
3617       }
3618     }
3619     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
3620              Feld[newx][newy] == EL_DIAMOND)
3621     {
3622       if (IS_MOVING(newx, newy))
3623         RemoveMovingField(newx, newy);
3624       else
3625       {
3626         Feld[newx][newy] = EL_EMPTY;
3627         DrawLevelField(newx, newy);
3628       }
3629
3630       PlaySoundLevel(x, y, SND_YAMYAM_DIGGING);
3631     }
3632     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
3633              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
3634     {
3635       if (AmoebaNr[newx][newy])
3636       {
3637         AmoebaCnt2[AmoebaNr[newx][newy]]--;
3638         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
3639             Feld[newx][newy] == EL_BD_AMOEBA)
3640           AmoebaCnt[AmoebaNr[newx][newy]]--;
3641       }
3642
3643       if (IS_MOVING(newx, newy))
3644         RemoveMovingField(newx, newy);
3645       else
3646       {
3647         Feld[newx][newy] = EL_EMPTY;
3648         DrawLevelField(newx, newy);
3649       }
3650
3651       PlaySoundLevel(x, y, SND_DARK_YAMYAM_DIGGING);
3652     }
3653     else if ((element == EL_PACMAN || element == EL_MOLE)
3654              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
3655     {
3656       if (AmoebaNr[newx][newy])
3657       {
3658         AmoebaCnt2[AmoebaNr[newx][newy]]--;
3659         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
3660             Feld[newx][newy] == EL_BD_AMOEBA)
3661           AmoebaCnt[AmoebaNr[newx][newy]]--;
3662       }
3663
3664       if (element == EL_MOLE)
3665       {
3666         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
3667         PlaySoundLevel(x, y, SND_MOLE_DIGGING);
3668         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
3669         return;                         /* wait for shrinking amoeba */
3670       }
3671       else      /* element == EL_PACMAN */
3672       {
3673         Feld[newx][newy] = EL_EMPTY;
3674         DrawLevelField(newx, newy);
3675         PlaySoundLevel(x, y, SND_PACMAN_DIGGING);
3676       }
3677     }
3678     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
3679              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
3680               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
3681     {
3682       /* wait for shrinking amoeba to completely disappear */
3683       return;
3684     }
3685     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
3686     {
3687       /* object was running against a wall */
3688
3689       TurnRound(x, y);
3690
3691 #if 1
3692       DrawLevelElementAnimation(x, y, element);
3693 #else
3694       if (element == EL_BUG ||
3695           element == EL_SPACESHIP ||
3696           element == EL_SP_SNIKSNAK)
3697         DrawLevelField(x, y);
3698       else if (element == EL_MOLE)
3699         DrawLevelField(x, y);
3700       else if (element == EL_BD_BUTTERFLY ||
3701                element == EL_BD_FIREFLY)
3702         DrawLevelElementAnimationIfNeeded(x, y, element);
3703       else if (element == EL_SATELLITE)
3704         DrawLevelElementAnimationIfNeeded(x, y, element);
3705       else if (element == EL_SP_ELECTRON)
3706         DrawLevelElementAnimationIfNeeded(x, y, element);
3707 #endif
3708
3709       if (DONT_TOUCH(element))
3710         TestIfBadThingTouchesHero(x, y);
3711
3712 #if 0
3713       PlaySoundLevelAction(x, y, ACTION_WAITING);
3714 #endif
3715
3716       return;
3717     }
3718
3719     InitMovingField(x, y, MovDir[x][y]);
3720
3721     PlaySoundLevelAction(x, y, ACTION_MOVING);
3722   }
3723
3724   if (MovDir[x][y])
3725     ContinueMoving(x, y);
3726 }
3727
3728 void ContinueMoving(int x, int y)
3729 {
3730   int element = Feld[x][y];
3731   int direction = MovDir[x][y];
3732   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3733   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3734   int horiz_move = (dx != 0);
3735   int newx = x + dx, newy = y + dy;
3736   int step = (horiz_move ? dx : dy) * TILEX / MOVE_DELAY_NORMAL_SPEED;
3737
3738   if (element == EL_AMOEBA_DROP || element == EL_AMOEBA_DROPPING)
3739     step /= 2;
3740   else if (element == EL_QUICKSAND_FILLING ||
3741            element == EL_QUICKSAND_EMPTYING)
3742     step /= 4;
3743   else if (element == EL_MAGIC_WALL_FILLING ||
3744            element == EL_BD_MAGIC_WALL_FILLING ||
3745            element == EL_MAGIC_WALL_EMPTYING ||
3746            element == EL_BD_MAGIC_WALL_EMPTYING)
3747     step /= 2;
3748   else if (CAN_FALL(element) && horiz_move &&
3749            y < lev_fieldy-1 && IS_BELT_ACTIVE(Feld[x][y+1]))
3750     step /= 2;
3751   else if (element == EL_SPRING && horiz_move)
3752     step *= 2;
3753   else if (IS_CUSTOM_ELEMENT(element))
3754     step = SIGN(step) * element_info[element].move_stepsize;
3755
3756 #if OLD_GAME_BEHAVIOUR
3757   else if (CAN_FALL(element) && horiz_move && !IS_SP_ELEMENT(element))
3758     step*=2;
3759 #endif
3760
3761   MovPos[x][y] += step;
3762
3763   if (ABS(MovPos[x][y]) >= TILEX)       /* object reached its destination */
3764   {
3765     Feld[x][y] = EL_EMPTY;
3766     Feld[newx][newy] = element;
3767
3768     if (element == EL_MOLE)
3769     {
3770       int i;
3771       static int xy[4][2] =
3772       {
3773         { 0, -1 },
3774         { -1, 0 },
3775         { +1, 0 },
3776         { 0, +1 }
3777       };
3778
3779       Feld[x][y] = EL_SAND;
3780       DrawLevelField(x, y);
3781
3782       for(i=0; i<4; i++)
3783       {
3784         int xx, yy;
3785
3786         xx = x + xy[i][0];
3787         yy = y + xy[i][1];
3788
3789         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_SAND)
3790           DrawLevelField(xx, yy);       /* for "crumbled sand" */
3791       }
3792     }
3793
3794     if (element == EL_QUICKSAND_FILLING)
3795     {
3796       element = Feld[newx][newy] = get_next_element(element);
3797       Store[newx][newy] = Store[x][y];
3798     }
3799     else if (element == EL_QUICKSAND_EMPTYING)
3800     {
3801       Feld[x][y] = get_next_element(element);
3802       element = Feld[newx][newy] = Store[x][y];
3803     }
3804     else if (element == EL_MAGIC_WALL_FILLING)
3805     {
3806       element = Feld[newx][newy] = get_next_element(element);
3807       if (!game.magic_wall_active)
3808         element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
3809       Store[newx][newy] = Store[x][y];
3810     }
3811     else if (element == EL_MAGIC_WALL_EMPTYING)
3812     {
3813       Feld[x][y] = get_next_element(element);
3814       if (!game.magic_wall_active)
3815         Feld[x][y] = EL_MAGIC_WALL_DEAD;
3816       element = Feld[newx][newy] = Store[x][y];
3817     }
3818     else if (element == EL_BD_MAGIC_WALL_FILLING)
3819     {
3820       element = Feld[newx][newy] = get_next_element(element);
3821       if (!game.magic_wall_active)
3822         element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
3823       Store[newx][newy] = Store[x][y];
3824     }
3825     else if (element == EL_BD_MAGIC_WALL_EMPTYING)
3826     {
3827       Feld[x][y] = get_next_element(element);
3828       if (!game.magic_wall_active)
3829         Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
3830       element = Feld[newx][newy] = Store[x][y];
3831     }
3832     else if (element == EL_AMOEBA_DROPPING)
3833     {
3834       Feld[x][y] = get_next_element(element);
3835       element = Feld[newx][newy] = Store[x][y];
3836     }
3837     else if (Store[x][y] == EL_ACID)
3838     {
3839       element = Feld[newx][newy] = EL_ACID;
3840     }
3841
3842     Store[x][y] = 0;
3843     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3844     MovDelay[newx][newy] = 0;
3845
3846     /* copy animation control values to new field */
3847     GfxFrame[newx][newy]  = GfxFrame[x][y];
3848     GfxAction[newx][newy] = GfxAction[x][y];    /* keep action one frame */
3849     GfxRandom[newx][newy] = GfxRandom[x][y];    /* keep same random value */
3850
3851     ResetGfxAnimation(x, y);    /* reset animation values for old field */
3852
3853 #if 1
3854 #if 0
3855     if (!CAN_MOVE(element))
3856       MovDir[newx][newy] = 0;
3857 #else
3858     /*
3859     if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
3860       MovDir[newx][newy] = 0;
3861     */
3862
3863     if (!CAN_MOVE(element) ||
3864         (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
3865       MovDir[newx][newy] = 0;
3866 #endif
3867 #endif
3868
3869     DrawLevelField(x, y);
3870     DrawLevelField(newx, newy);
3871
3872     Stop[newx][newy] = TRUE;
3873     JustStopped[newx][newy] = 3;
3874
3875     if (DONT_TOUCH(element))    /* object may be nasty to player or others */
3876     {
3877       TestIfBadThingTouchesHero(newx, newy);
3878       TestIfBadThingTouchesFriend(newx, newy);
3879       TestIfBadThingTouchesOtherBadThing(newx, newy);
3880     }
3881     else if (element == EL_PENGUIN)
3882       TestIfFriendTouchesBadThing(newx, newy);
3883
3884     if (CAN_SMASH(element) && direction == MV_DOWN &&
3885         (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
3886       Impact(x, newy);
3887   }
3888   else                          /* still moving on */
3889   {
3890     DrawLevelField(x, y);
3891   }
3892 }
3893
3894 int AmoebeNachbarNr(int ax, int ay)
3895 {
3896   int i;
3897   int element = Feld[ax][ay];
3898   int group_nr = 0;
3899   static int xy[4][2] =
3900   {
3901     { 0, -1 },
3902     { -1, 0 },
3903     { +1, 0 },
3904     { 0, +1 }
3905   };
3906
3907   for (i=0; i<4; i++)
3908   {
3909     int x = ax + xy[i][0];
3910     int y = ay + xy[i][1];
3911
3912     if (!IN_LEV_FIELD(x, y))
3913       continue;
3914
3915     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
3916       group_nr = AmoebaNr[x][y];
3917   }
3918
3919   return group_nr;
3920 }
3921
3922 void AmoebenVereinigen(int ax, int ay)
3923 {
3924   int i, x, y, xx, yy;
3925   int new_group_nr = AmoebaNr[ax][ay];
3926   static int xy[4][2] =
3927   {
3928     { 0, -1 },
3929     { -1, 0 },
3930     { +1, 0 },
3931     { 0, +1 }
3932   };
3933
3934   if (new_group_nr == 0)
3935     return;
3936
3937   for (i=0; i<4; i++)
3938   {
3939     x = ax + xy[i][0];
3940     y = ay + xy[i][1];
3941
3942     if (!IN_LEV_FIELD(x, y))
3943       continue;
3944
3945     if ((Feld[x][y] == EL_AMOEBA_FULL ||
3946          Feld[x][y] == EL_BD_AMOEBA ||
3947          Feld[x][y] == EL_AMOEBA_DEAD) &&
3948         AmoebaNr[x][y] != new_group_nr)
3949     {
3950       int old_group_nr = AmoebaNr[x][y];
3951
3952       if (old_group_nr == 0)
3953         return;
3954
3955       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
3956       AmoebaCnt[old_group_nr] = 0;
3957       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
3958       AmoebaCnt2[old_group_nr] = 0;
3959
3960       for (yy=0; yy<lev_fieldy; yy++)
3961       {
3962         for (xx=0; xx<lev_fieldx; xx++)
3963         {
3964           if (AmoebaNr[xx][yy] == old_group_nr)
3965             AmoebaNr[xx][yy] = new_group_nr;
3966         }
3967       }
3968     }
3969   }
3970 }
3971
3972 void AmoebeUmwandeln(int ax, int ay)
3973 {
3974   int i, x, y;
3975
3976   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
3977   {
3978     int group_nr = AmoebaNr[ax][ay];
3979
3980 #ifdef DEBUG
3981     if (group_nr == 0)
3982     {
3983       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
3984       printf("AmoebeUmwandeln(): This should never happen!\n");
3985       return;
3986     }
3987 #endif
3988
3989     for (y=0; y<lev_fieldy; y++)
3990     {
3991       for (x=0; x<lev_fieldx; x++)
3992       {
3993         if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
3994         {
3995           AmoebaNr[x][y] = 0;
3996           Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
3997         }
3998       }
3999     }
4000     PlaySoundLevel(ax, ay, (IS_GEM(level.amoeba_content) ?
4001                             SND_AMOEBA_TURNING_TO_GEM :
4002                             SND_AMOEBA_TURNING_TO_ROCK));
4003     Bang(ax, ay);
4004   }
4005   else
4006   {
4007     static int xy[4][2] =
4008     {
4009       { 0, -1 },
4010       { -1, 0 },
4011       { +1, 0 },
4012       { 0, +1 }
4013     };
4014
4015     for (i=0; i<4; i++)
4016     {
4017       x = ax + xy[i][0];
4018       y = ay + xy[i][1];
4019
4020       if (!IN_LEV_FIELD(x, y))
4021         continue;
4022
4023       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
4024       {
4025         PlaySoundLevel(x, y, (IS_GEM(level.amoeba_content) ?
4026                               SND_AMOEBA_TURNING_TO_GEM :
4027                               SND_AMOEBA_TURNING_TO_ROCK));
4028         Bang(x, y);
4029       }
4030     }
4031   }
4032 }
4033
4034 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
4035 {
4036   int x, y;
4037   int group_nr = AmoebaNr[ax][ay];
4038   boolean done = FALSE;
4039
4040 #ifdef DEBUG
4041   if (group_nr == 0)
4042   {
4043     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
4044     printf("AmoebeUmwandelnBD(): This should never happen!\n");
4045     return;
4046   }
4047 #endif
4048
4049   for (y=0; y<lev_fieldy; y++)
4050   {
4051     for (x=0; x<lev_fieldx; x++)
4052     {
4053       if (AmoebaNr[x][y] == group_nr &&
4054           (Feld[x][y] == EL_AMOEBA_DEAD ||
4055            Feld[x][y] == EL_BD_AMOEBA ||
4056            Feld[x][y] == EL_AMOEBA_GROWING))
4057       {
4058         AmoebaNr[x][y] = 0;
4059         Feld[x][y] = new_element;
4060         InitField(x, y, FALSE);
4061         DrawLevelField(x, y);
4062         done = TRUE;
4063       }
4064     }
4065   }
4066
4067   if (done)
4068     PlaySoundLevel(ax, ay, (new_element == EL_BD_ROCK ?
4069                             SND_BD_AMOEBA_TURNING_TO_ROCK :
4070                             SND_BD_AMOEBA_TURNING_TO_GEM));
4071 }
4072
4073 void AmoebeWaechst(int x, int y)
4074 {
4075   static unsigned long sound_delay = 0;
4076   static unsigned long sound_delay_value = 0;
4077
4078   if (!MovDelay[x][y])          /* start new growing cycle */
4079   {
4080     MovDelay[x][y] = 7;
4081
4082     if (DelayReached(&sound_delay, sound_delay_value))
4083     {
4084 #if 1
4085       PlaySoundLevelElementAction(x, y, Store[x][y], ACTION_GROWING);
4086 #else
4087       if (Store[x][y] == EL_BD_AMOEBA)
4088         PlaySoundLevel(x, y, SND_BD_AMOEBA_GROWING);
4089       else
4090         PlaySoundLevel(x, y, SND_AMOEBA_GROWING);
4091 #endif
4092       sound_delay_value = 30;
4093     }
4094   }
4095
4096   if (MovDelay[x][y])           /* wait some time before growing bigger */
4097   {
4098     MovDelay[x][y]--;
4099     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4100     {
4101       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
4102                                            6 - MovDelay[x][y]);
4103
4104       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
4105     }
4106
4107     if (!MovDelay[x][y])
4108     {
4109       Feld[x][y] = Store[x][y];
4110       Store[x][y] = 0;
4111       DrawLevelField(x, y);
4112     }
4113   }
4114 }
4115
4116 void AmoebaDisappearing(int x, int y)
4117 {
4118   static unsigned long sound_delay = 0;
4119   static unsigned long sound_delay_value = 0;
4120
4121   if (!MovDelay[x][y])          /* start new shrinking cycle */
4122   {
4123     MovDelay[x][y] = 7;
4124
4125     if (DelayReached(&sound_delay, sound_delay_value))
4126       sound_delay_value = 30;
4127   }
4128
4129   if (MovDelay[x][y])           /* wait some time before shrinking */
4130   {
4131     MovDelay[x][y]--;
4132     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4133     {
4134       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
4135                                            6 - MovDelay[x][y]);
4136
4137       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
4138     }
4139
4140     if (!MovDelay[x][y])
4141     {
4142       Feld[x][y] = EL_EMPTY;
4143       DrawLevelField(x, y);
4144
4145       /* don't let mole enter this field in this cycle;
4146          (give priority to objects falling to this field from above) */
4147       Stop[x][y] = TRUE;
4148     }
4149   }
4150 }
4151
4152 void AmoebeAbleger(int ax, int ay)
4153 {
4154   int i;
4155   int element = Feld[ax][ay];
4156   int graphic = el2img(element);
4157   int newax = ax, neway = ay;
4158   static int xy[4][2] =
4159   {
4160     { 0, -1 },
4161     { -1, 0 },
4162     { +1, 0 },
4163     { 0, +1 }
4164   };
4165
4166   if (!level.amoeba_speed)
4167   {
4168     Feld[ax][ay] = EL_AMOEBA_DEAD;
4169     DrawLevelField(ax, ay);
4170     return;
4171   }
4172
4173   if (IS_ANIMATED(graphic))
4174     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
4175
4176   if (!MovDelay[ax][ay])        /* start making new amoeba field */
4177     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
4178
4179   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
4180   {
4181     MovDelay[ax][ay]--;
4182     if (MovDelay[ax][ay])
4183       return;
4184   }
4185
4186   if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
4187   {
4188     int start = RND(4);
4189     int x = ax + xy[start][0];
4190     int y = ay + xy[start][1];
4191
4192     if (!IN_LEV_FIELD(x, y))
4193       return;
4194
4195     if (IS_FREE(x, y) ||
4196         Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
4197     {
4198       newax = x;
4199       neway = y;
4200     }
4201
4202     if (newax == ax && neway == ay)
4203       return;
4204   }
4205   else                          /* normal or "filled" (BD style) amoeba */
4206   {
4207     int start = RND(4);
4208     boolean waiting_for_player = FALSE;
4209
4210     for (i=0; i<4; i++)
4211     {
4212       int j = (start + i) % 4;
4213       int x = ax + xy[j][0];
4214       int y = ay + xy[j][1];
4215
4216       if (!IN_LEV_FIELD(x, y))
4217         continue;
4218
4219       if (IS_FREE(x, y) ||
4220           Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
4221       {
4222         newax = x;
4223         neway = y;
4224         break;
4225       }
4226       else if (IS_PLAYER(x, y))
4227         waiting_for_player = TRUE;
4228     }
4229
4230     if (newax == ax && neway == ay)             /* amoeba cannot grow */
4231     {
4232       if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
4233       {
4234         Feld[ax][ay] = EL_AMOEBA_DEAD;
4235         DrawLevelField(ax, ay);
4236         AmoebaCnt[AmoebaNr[ax][ay]]--;
4237
4238         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
4239         {
4240           if (element == EL_AMOEBA_FULL)
4241             AmoebeUmwandeln(ax, ay);
4242           else if (element == EL_BD_AMOEBA)
4243             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
4244         }
4245       }
4246       return;
4247     }
4248     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
4249     {
4250       /* amoeba gets larger by growing in some direction */
4251
4252       int new_group_nr = AmoebaNr[ax][ay];
4253
4254 #ifdef DEBUG
4255   if (new_group_nr == 0)
4256   {
4257     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
4258     printf("AmoebeAbleger(): This should never happen!\n");
4259     return;
4260   }
4261 #endif
4262
4263       AmoebaNr[newax][neway] = new_group_nr;
4264       AmoebaCnt[new_group_nr]++;
4265       AmoebaCnt2[new_group_nr]++;
4266
4267       /* if amoeba touches other amoeba(s) after growing, unify them */
4268       AmoebenVereinigen(newax, neway);
4269
4270       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
4271       {
4272         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
4273         return;
4274       }
4275     }
4276   }
4277
4278   if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
4279       (neway == lev_fieldy - 1 && newax != ax))
4280   {
4281     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
4282     Store[newax][neway] = element;
4283   }
4284   else if (neway == ay)
4285   {
4286     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
4287 #if 1
4288     PlaySoundLevelAction(newax, neway, ACTION_GROWING);
4289 #else
4290     PlaySoundLevel(newax, neway, SND_AMOEBA_GROWING);
4291 #endif
4292   }
4293   else
4294   {
4295     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
4296     Feld[ax][ay] = EL_AMOEBA_DROPPING;
4297     Store[ax][ay] = EL_AMOEBA_DROP;
4298     ContinueMoving(ax, ay);
4299     return;
4300   }
4301
4302   DrawLevelField(newax, neway);
4303 }
4304
4305 void Life(int ax, int ay)
4306 {
4307   int x1, y1, x2, y2;
4308   static int life[4] = { 2, 3, 3, 3 };  /* parameters for "game of life" */
4309   int life_time = 40;
4310   int element = Feld[ax][ay];
4311   int graphic = el2img(element);
4312   boolean changed = FALSE;
4313
4314   if (IS_ANIMATED(graphic))
4315     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
4316
4317   if (Stop[ax][ay])
4318     return;
4319
4320   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
4321     MovDelay[ax][ay] = life_time;
4322
4323   if (MovDelay[ax][ay])         /* wait some time before next cycle */
4324   {
4325     MovDelay[ax][ay]--;
4326     if (MovDelay[ax][ay])
4327       return;
4328   }
4329
4330   for (y1=-1; y1<2; y1++) for(x1=-1; x1<2; x1++)
4331   {
4332     int xx = ax+x1, yy = ay+y1;
4333     int nachbarn = 0;
4334
4335     if (!IN_LEV_FIELD(xx, yy))
4336       continue;
4337
4338     for (y2=-1; y2<2; y2++) for (x2=-1; x2<2; x2++)
4339     {
4340       int x = xx+x2, y = yy+y2;
4341
4342       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
4343         continue;
4344
4345       if (((Feld[x][y] == element ||
4346             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
4347            !Stop[x][y]) ||
4348           (IS_FREE(x, y) && Stop[x][y]))
4349         nachbarn++;
4350     }
4351
4352     if (xx == ax && yy == ay)           /* field in the middle */
4353     {
4354       if (nachbarn < life[0] || nachbarn > life[1])
4355       {
4356         Feld[xx][yy] = EL_EMPTY;
4357         if (!Stop[xx][yy])
4358           DrawLevelField(xx, yy);
4359         Stop[xx][yy] = TRUE;
4360         changed = TRUE;
4361       }
4362     }
4363     else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
4364     {                                   /* free border field */
4365       if (nachbarn >= life[2] && nachbarn <= life[3])
4366       {
4367         Feld[xx][yy] = element;
4368         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
4369         if (!Stop[xx][yy])
4370           DrawLevelField(xx, yy);
4371         Stop[xx][yy] = TRUE;
4372         changed = TRUE;
4373       }
4374     }
4375   }
4376
4377   if (changed)
4378     PlaySoundLevel(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
4379                    SND_GAME_OF_LIFE_GROWING);
4380 }
4381
4382 static void InitRobotWheel(int x, int y)
4383 {
4384   MovDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
4385 }
4386
4387 static void RunRobotWheel(int x, int y)
4388 {
4389   PlaySoundLevel(x, y, SND_ROBOT_WHEEL_ACTIVE);
4390 }
4391
4392 static void StopRobotWheel(int x, int y)
4393 {
4394   if (ZX == x && ZY == y)
4395     ZX = ZY = -1;
4396 }
4397
4398 static void InitTimegateWheel(int x, int y)
4399 {
4400   MovDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
4401 }
4402
4403 static void RunTimegateWheel(int x, int y)
4404 {
4405   PlaySoundLevel(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
4406 }
4407
4408 void CheckExit(int x, int y)
4409 {
4410   if (local_player->gems_still_needed > 0 ||
4411       local_player->sokobanfields_still_needed > 0 ||
4412       local_player->lights_still_needed > 0)
4413   {
4414     int element = Feld[x][y];
4415     int graphic = el2img(element);
4416
4417     if (IS_ANIMATED(graphic))
4418       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
4419
4420     return;
4421   }
4422
4423   Feld[x][y] = EL_EXIT_OPENING;
4424
4425   PlaySoundLevelNearest(x, y, SND_CLASS_EXIT_OPENING);
4426 }
4427
4428 void CheckExitSP(int x, int y)
4429 {
4430   if (local_player->gems_still_needed > 0)
4431   {
4432     int element = Feld[x][y];
4433     int graphic = el2img(element);
4434
4435     if (IS_ANIMATED(graphic))
4436       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
4437
4438     return;
4439   }
4440
4441   Feld[x][y] = EL_SP_EXIT_OPEN;
4442
4443   PlaySoundLevelNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
4444 }
4445
4446 static void CloseAllOpenTimegates()
4447 {
4448   int x, y;
4449
4450   for (y=0; y<lev_fieldy; y++)
4451   {
4452     for (x=0; x<lev_fieldx; x++)
4453     {
4454       int element = Feld[x][y];
4455
4456       if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
4457       {
4458         Feld[x][y] = EL_TIMEGATE_CLOSING;
4459 #if 1
4460         PlaySoundLevelAction(x, y, ACTION_CLOSING);
4461 #else
4462         PlaySoundLevel(x, y, SND_TIMEGATE_CLOSING);
4463 #endif
4464       }
4465     }
4466   }
4467 }
4468
4469 void EdelsteinFunkeln(int x, int y)
4470 {
4471   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
4472     return;
4473
4474   if (Feld[x][y] == EL_BD_DIAMOND)
4475     return;
4476
4477   if (MovDelay[x][y] == 0)      /* next animation frame */
4478     MovDelay[x][y] = 11 * !SimpleRND(500);
4479
4480   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
4481   {
4482     MovDelay[x][y]--;
4483
4484     if (setup.direct_draw && MovDelay[x][y])
4485       SetDrawtoField(DRAW_BUFFERED);
4486
4487     DrawLevelElementAnimation(x, y, Feld[x][y]);
4488
4489     if (MovDelay[x][y] != 0)
4490     {
4491       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
4492                                            10 - MovDelay[x][y]);
4493
4494       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
4495
4496       if (setup.direct_draw)
4497       {
4498         int dest_x, dest_y;
4499
4500         dest_x = FX + SCREENX(x) * TILEX;
4501         dest_y = FY + SCREENY(y) * TILEY;
4502
4503         BlitBitmap(drawto_field, window,
4504                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
4505         SetDrawtoField(DRAW_DIRECT);
4506       }
4507     }
4508   }
4509 }
4510
4511 void MauerWaechst(int x, int y)
4512 {
4513   int delay = 6;
4514
4515   if (!MovDelay[x][y])          /* next animation frame */
4516     MovDelay[x][y] = 3 * delay;
4517
4518   if (MovDelay[x][y])           /* wait some time before next frame */
4519   {
4520     MovDelay[x][y]--;
4521
4522     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4523     {
4524       int graphic = el_dir2img(Feld[x][y], MovDir[x][y]);
4525       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
4526
4527       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4528     }
4529
4530     if (!MovDelay[x][y])
4531     {
4532       if (MovDir[x][y] == MV_LEFT)
4533       {
4534         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
4535           DrawLevelField(x - 1, y);
4536       }
4537       else if (MovDir[x][y] == MV_RIGHT)
4538       {
4539         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
4540           DrawLevelField(x + 1, y);
4541       }
4542       else if (MovDir[x][y] == MV_UP)
4543       {
4544         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
4545           DrawLevelField(x, y - 1);
4546       }
4547       else
4548       {
4549         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
4550           DrawLevelField(x, y + 1);
4551       }
4552
4553       Feld[x][y] = Store[x][y];
4554       Store[x][y] = 0;
4555       MovDir[x][y] = MV_NO_MOVING;
4556       DrawLevelField(x, y);
4557     }
4558   }
4559 }
4560
4561 void MauerAbleger(int ax, int ay)
4562 {
4563   int element = Feld[ax][ay];
4564   int graphic = el2img(element);
4565   boolean oben_frei = FALSE, unten_frei = FALSE;
4566   boolean links_frei = FALSE, rechts_frei = FALSE;
4567   boolean oben_massiv = FALSE, unten_massiv = FALSE;
4568   boolean links_massiv = FALSE, rechts_massiv = FALSE;
4569   boolean new_wall = FALSE;
4570
4571   if (IS_ANIMATED(graphic))
4572     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
4573
4574   if (!MovDelay[ax][ay])        /* start building new wall */
4575     MovDelay[ax][ay] = 6;
4576
4577   if (MovDelay[ax][ay])         /* wait some time before building new wall */
4578   {
4579     MovDelay[ax][ay]--;
4580     if (MovDelay[ax][ay])
4581       return;
4582   }
4583
4584   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
4585     oben_frei = TRUE;
4586   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
4587     unten_frei = TRUE;
4588   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
4589     links_frei = TRUE;
4590   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
4591     rechts_frei = TRUE;
4592
4593   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
4594       element == EL_EXPANDABLE_WALL_ANY)
4595   {
4596     if (oben_frei)
4597     {
4598       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
4599       Store[ax][ay-1] = element;
4600       MovDir[ax][ay-1] = MV_UP;
4601       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
4602         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
4603                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
4604       new_wall = TRUE;
4605     }
4606     if (unten_frei)
4607     {
4608       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
4609       Store[ax][ay+1] = element;
4610       MovDir[ax][ay+1] = MV_DOWN;
4611       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
4612         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
4613                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
4614       new_wall = TRUE;
4615     }
4616   }
4617
4618   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
4619       element == EL_EXPANDABLE_WALL_ANY ||
4620       element == EL_EXPANDABLE_WALL)
4621   {
4622     if (links_frei)
4623     {
4624       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
4625       Store[ax-1][ay] = element;
4626       MovDir[ax-1][ay] = MV_LEFT;
4627       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
4628         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
4629                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
4630       new_wall = TRUE;
4631     }
4632
4633     if (rechts_frei)
4634     {
4635       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
4636       Store[ax+1][ay] = element;
4637       MovDir[ax+1][ay] = MV_RIGHT;
4638       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
4639         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
4640                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
4641       new_wall = TRUE;
4642     }
4643   }
4644
4645   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
4646     DrawLevelField(ax, ay);
4647
4648   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
4649     oben_massiv = TRUE;
4650   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
4651     unten_massiv = TRUE;
4652   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
4653     links_massiv = TRUE;
4654   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
4655     rechts_massiv = TRUE;
4656
4657   if (((oben_massiv && unten_massiv) ||
4658        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
4659        element == EL_EXPANDABLE_WALL) &&
4660       ((links_massiv && rechts_massiv) ||
4661        element == EL_EXPANDABLE_WALL_VERTICAL))
4662     Feld[ax][ay] = EL_WALL;
4663
4664   if (new_wall)
4665 #if 1
4666     PlaySoundLevelAction(ax, ay, ACTION_GROWING);
4667 #else
4668     PlaySoundLevel(ax, ay, SND_EXPANDABLE_WALL_GROWING);
4669 #endif
4670 }
4671
4672 void CheckForDragon(int x, int y)
4673 {
4674   int i, j;
4675   boolean dragon_found = FALSE;
4676   static int xy[4][2] =
4677   {
4678     { 0, -1 },
4679     { -1, 0 },
4680     { +1, 0 },
4681     { 0, +1 }
4682   };
4683
4684   for (i=0; i<4; i++)
4685   {
4686     for (j=0; j<4; j++)
4687     {
4688       int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
4689
4690       if (IN_LEV_FIELD(xx, yy) &&
4691           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
4692       {
4693         if (Feld[xx][yy] == EL_DRAGON)
4694           dragon_found = TRUE;
4695       }
4696       else
4697         break;
4698     }
4699   }
4700
4701   if (!dragon_found)
4702   {
4703     for (i=0; i<4; i++)
4704     {
4705       for (j=0; j<3; j++)
4706       {
4707         int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
4708   
4709         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
4710         {
4711           Feld[xx][yy] = EL_EMPTY;
4712           DrawLevelField(xx, yy);
4713         }
4714         else
4715           break;
4716       }
4717     }
4718   }
4719 }
4720
4721 static void InitBuggyBase(int x, int y)
4722 {
4723   int element = Feld[x][y];
4724   int activating_delay = FRAMES_PER_SECOND / 4;
4725
4726   MovDelay[x][y] =
4727     (element == EL_SP_BUGGY_BASE ?
4728      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
4729      element == EL_SP_BUGGY_BASE_ACTIVATING ?
4730      activating_delay :
4731      element == EL_SP_BUGGY_BASE_ACTIVE ?
4732      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
4733 }
4734
4735 static void WarnBuggyBase(int x, int y)
4736 {
4737   int i;
4738   static int xy[4][2] =
4739   {
4740     { 0, -1 },
4741     { -1, 0 },
4742     { +1, 0 },
4743     { 0, +1 }
4744   };
4745
4746   for (i=0; i<4; i++)
4747   {
4748     int xx = x + xy[i][0], yy = y + xy[i][1];
4749
4750     if (IS_PLAYER(xx, yy))
4751     {
4752       PlaySoundLevel(x, y, SND_SP_BUGGY_BASE_ACTIVE);
4753
4754       break;
4755     }
4756   }
4757 }
4758
4759 static void InitTrap(int x, int y)
4760 {
4761   MovDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
4762 }
4763
4764 static void ActivateTrap(int x, int y)
4765 {
4766   PlaySoundLevel(x, y, SND_TRAP_ACTIVATING);
4767 }
4768
4769 static void ChangeActiveTrap(int x, int y)
4770 {
4771   int graphic = IMG_TRAP_ACTIVE;
4772
4773   /* if new animation frame was drawn, correct crumbled sand border */
4774   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
4775     DrawLevelFieldCrumbledSand(x, y);
4776 }
4777
4778 static void ChangeElement(int x, int y)
4779 {
4780   int element = Feld[x][y];
4781
4782   if (IS_MOVING(x, y))                  /* never change a running system :-) */
4783     return;
4784
4785   if (MovDelay[x][y] == 0)              /* initialize element change */
4786   {
4787     MovDelay[x][y] = changing_element[element].change_delay + 1;
4788
4789     if (IS_CUSTOM_ELEMENT(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
4790     {
4791       int max_random_delay = element_info[element].change.delay_random;
4792       int delay_frames = element_info[element].change.delay_frames;
4793
4794       MovDelay[x][y] += RND(max_random_delay * delay_frames);
4795     }
4796
4797     ResetGfxAnimation(x, y);
4798     ResetRandomAnimationValue(x, y);
4799
4800     if (changing_element[element].pre_change_function)
4801       changing_element[element].pre_change_function(x, y);
4802   }
4803
4804   MovDelay[x][y]--;
4805
4806   if (MovDelay[x][y] != 0)              /* continue element change */
4807   {
4808     if (IS_ANIMATED(el2img(element)))
4809       DrawLevelElementAnimationIfNeeded(x, y, element);
4810
4811     if (changing_element[element].change_function)
4812       changing_element[element].change_function(x, y);
4813   }
4814   else                                  /* finish element change */
4815   {
4816     Feld[x][y] = changing_element[element].next_element;
4817
4818     ResetGfxAnimation(x, y);
4819     ResetRandomAnimationValue(x, y);
4820
4821 #if 1
4822     InitField(x, y, FALSE);
4823     if (CAN_MOVE(element))
4824       InitMovDir(x, y);
4825 #endif
4826     DrawLevelField(x, y);
4827
4828     if (changing_element[element].post_change_function)
4829       changing_element[element].post_change_function(x, y);
4830   }
4831 }
4832
4833 static void PlayerActions(struct PlayerInfo *player, byte player_action)
4834 {
4835   static byte stored_player_action[MAX_PLAYERS];
4836   static int num_stored_actions = 0;
4837   boolean moved = FALSE, snapped = FALSE, bombed = FALSE;
4838   int left      = player_action & JOY_LEFT;
4839   int right     = player_action & JOY_RIGHT;
4840   int up        = player_action & JOY_UP;
4841   int down      = player_action & JOY_DOWN;
4842   int button1   = player_action & JOY_BUTTON_1;
4843   int button2   = player_action & JOY_BUTTON_2;
4844   int dx        = (left ? -1    : right ? 1     : 0);
4845   int dy        = (up   ? -1    : down  ? 1     : 0);
4846
4847   stored_player_action[player->index_nr] = 0;
4848   num_stored_actions++;
4849
4850   if (!player->active || tape.pausing)
4851     return;
4852
4853   if (player_action)
4854   {
4855     if (button1)
4856       snapped = SnapField(player, dx, dy);
4857     else
4858     {
4859       if (button2)
4860         bombed = PlaceBomb(player);
4861       moved = MoveFigure(player, dx, dy);
4862     }
4863
4864     if (tape.single_step && tape.recording && !tape.pausing)
4865     {
4866       if (button1 || (bombed && !moved))
4867       {
4868         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
4869         SnapField(player, 0, 0);                /* stop snapping */
4870       }
4871     }
4872
4873     stored_player_action[player->index_nr] = player_action;
4874   }
4875   else
4876   {
4877     /* no actions for this player (no input at player's configured device) */
4878
4879     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
4880     SnapField(player, 0, 0);
4881     CheckGravityMovement(player);
4882
4883     if (player->MovPos == 0)
4884     {
4885 #if 0
4886       printf("Trying... Player frame reset\n");
4887 #endif
4888
4889       InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
4890     }
4891
4892     if (player->MovPos == 0)    /* needed for tape.playing */
4893       player->is_moving = FALSE;
4894   }
4895
4896   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
4897   {
4898     TapeRecordAction(stored_player_action);
4899     num_stored_actions = 0;
4900   }
4901 }
4902
4903 void GameActions()
4904 {
4905   static unsigned long action_delay = 0;
4906   unsigned long action_delay_value;
4907   int magic_wall_x = 0, magic_wall_y = 0;
4908   int i, x, y, element, graphic;
4909   byte *recorded_player_action;
4910   byte summarized_player_action = 0;
4911
4912   if (game_status != GAME_MODE_PLAYING)
4913     return;
4914
4915   action_delay_value =
4916     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
4917
4918   if (tape.playing && tape.index_search && !tape.pausing)
4919     action_delay_value = 0;
4920
4921   /* ---------- main game synchronization point ---------- */
4922
4923   WaitUntilDelayReached(&action_delay, action_delay_value);
4924
4925   if (network_playing && !network_player_action_received)
4926   {
4927     /*
4928 #ifdef DEBUG
4929     printf("DEBUG: try to get network player actions in time\n");
4930 #endif
4931     */
4932
4933 #if defined(PLATFORM_UNIX)
4934     /* last chance to get network player actions without main loop delay */
4935     HandleNetworking();
4936 #endif
4937
4938     if (game_status != GAME_MODE_PLAYING)
4939       return;
4940
4941     if (!network_player_action_received)
4942     {
4943       /*
4944 #ifdef DEBUG
4945       printf("DEBUG: failed to get network player actions in time\n");
4946 #endif
4947       */
4948       return;
4949     }
4950   }
4951
4952   if (tape.pausing)
4953     return;
4954
4955   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
4956
4957   for (i=0; i<MAX_PLAYERS; i++)
4958   {
4959     summarized_player_action |= stored_player[i].action;
4960
4961     if (!network_playing)
4962       stored_player[i].effective_action = stored_player[i].action;
4963   }
4964
4965 #if defined(PLATFORM_UNIX)
4966   if (network_playing)
4967     SendToServer_MovePlayer(summarized_player_action);
4968 #endif
4969
4970   if (!options.network && !setup.team_mode)
4971     local_player->effective_action = summarized_player_action;
4972
4973   for (i=0; i<MAX_PLAYERS; i++)
4974   {
4975     int actual_player_action = stored_player[i].effective_action;
4976
4977     if (stored_player[i].programmed_action)
4978       actual_player_action = stored_player[i].programmed_action;
4979
4980     if (recorded_player_action)
4981       actual_player_action = recorded_player_action[i];
4982
4983     PlayerActions(&stored_player[i], actual_player_action);
4984     ScrollFigure(&stored_player[i], SCROLL_GO_ON);
4985   }
4986
4987   network_player_action_received = FALSE;
4988
4989   ScrollScreen(NULL, SCROLL_GO_ON);
4990
4991 #if 0
4992   FrameCounter++;
4993   TimeFrames++;
4994
4995   for (i=0; i<MAX_PLAYERS; i++)
4996     stored_player[i].Frame++;
4997 #endif
4998
4999   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
5000   {
5001     Stop[x][y] = FALSE;
5002     if (JustStopped[x][y] > 0)
5003       JustStopped[x][y]--;
5004
5005     GfxFrame[x][y]++;
5006
5007 #if DEBUG
5008     if (IS_BLOCKED(x, y))
5009     {
5010       int oldx, oldy;
5011
5012       Blocked2Moving(x, y, &oldx, &oldy);
5013       if (!IS_MOVING(oldx, oldy))
5014       {
5015         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
5016         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
5017         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
5018         printf("GameActions(): This should never happen!\n");
5019       }
5020     }
5021 #endif
5022   }
5023
5024   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
5025   {
5026     element = Feld[x][y];
5027 #if 1
5028     graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
5029 #else
5030     graphic = el2img(element);
5031 #endif
5032
5033 #if 0
5034     if (element == -1)
5035     {
5036       printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
5037
5038       element = graphic = 0;
5039     }
5040 #endif
5041
5042     if (graphic_info[graphic].anim_global_sync)
5043       GfxFrame[x][y] = FrameCounter;
5044
5045     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
5046         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
5047       ResetRandomAnimationValue(x, y);
5048
5049     SetRandomAnimationValue(x, y);
5050
5051 #if 1
5052     PlaySoundLevelActionIfLoop(x, y, GfxAction[x][y]);
5053 #endif
5054
5055     if (IS_INACTIVE(element))
5056     {
5057       if (IS_ANIMATED(graphic))
5058         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5059
5060       continue;
5061     }
5062
5063     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
5064     {
5065       StartMoving(x, y);
5066
5067 #if 1
5068       graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
5069 #if 0
5070       if (element == EL_PACMAN)
5071         printf("::: %d, %d, %d\n",
5072                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
5073 #endif
5074 #if 0
5075       if (element == EL_YAMYAM)
5076         printf("::: %d, %d, %d\n",
5077                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
5078 #endif
5079 #endif
5080
5081       if (IS_ANIMATED(graphic) &&
5082           !IS_MOVING(x, y) &&
5083           !Stop[x][y])
5084       {
5085         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5086
5087 #if 0
5088         if (element == EL_YAMYAM)
5089           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
5090 #endif
5091       }
5092
5093       if (IS_GEM(element) || element == EL_SP_INFOTRON)
5094         EdelsteinFunkeln(x, y);
5095     }
5096     else if ((element == EL_ACID ||
5097               element == EL_EXIT_OPEN ||
5098               element == EL_SP_EXIT_OPEN ||
5099               element == EL_SP_TERMINAL ||
5100               element == EL_SP_TERMINAL_ACTIVE ||
5101               element == EL_EXTRA_TIME ||
5102               element == EL_SHIELD_NORMAL ||
5103               element == EL_SHIELD_DEADLY) &&
5104              IS_ANIMATED(graphic))
5105       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5106     else if (IS_MOVING(x, y))
5107       ContinueMoving(x, y);
5108     else if (IS_ACTIVE_BOMB(element))
5109       CheckDynamite(x, y);
5110 #if 0
5111     else if (element == EL_EXPLOSION && !game.explosions_delayed)
5112       Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
5113 #endif
5114     else if (element == EL_AMOEBA_GROWING)
5115       AmoebeWaechst(x, y);
5116     else if (element == EL_AMOEBA_SHRINKING)
5117       AmoebaDisappearing(x, y);
5118
5119 #if !USE_NEW_AMOEBA_CODE
5120     else if (IS_AMOEBALIVE(element))
5121       AmoebeAbleger(x, y);
5122 #endif
5123
5124     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
5125       Life(x, y);
5126     else if (element == EL_EXIT_CLOSED)
5127       CheckExit(x, y);
5128     else if (element == EL_SP_EXIT_CLOSED)
5129       CheckExitSP(x, y);
5130     else if (element == EL_EXPANDABLE_WALL_GROWING)
5131       MauerWaechst(x, y);
5132     else if (element == EL_EXPANDABLE_WALL ||
5133              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5134              element == EL_EXPANDABLE_WALL_VERTICAL ||
5135              element == EL_EXPANDABLE_WALL_ANY)
5136       MauerAbleger(x, y);
5137     else if (element == EL_FLAMES)
5138       CheckForDragon(x, y);
5139 #if 0
5140     else if (IS_AUTO_CHANGING(element))
5141       ChangeElement(x, y);
5142 #endif
5143     else if (element == EL_EXPLOSION)
5144       ; /* drawing of correct explosion animation is handled separately */
5145     else if (IS_ANIMATED(graphic) && !IS_AUTO_CHANGING(element))
5146       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5147
5148 #if 1
5149     /* this may take place after moving, therefore element may have changed */
5150     if (IS_AUTO_CHANGING(Feld[x][y]))
5151       ChangeElement(x, y);
5152 #endif
5153
5154     if (IS_BELT_ACTIVE(element))
5155       PlaySoundLevelAction(x, y, ACTION_ACTIVE);
5156
5157     if (game.magic_wall_active)
5158     {
5159       int jx = local_player->jx, jy = local_player->jy;
5160
5161       /* play the element sound at the position nearest to the player */
5162       if ((element == EL_MAGIC_WALL_FULL ||
5163            element == EL_MAGIC_WALL_ACTIVE ||
5164            element == EL_MAGIC_WALL_EMPTYING ||
5165            element == EL_BD_MAGIC_WALL_FULL ||
5166            element == EL_BD_MAGIC_WALL_ACTIVE ||
5167            element == EL_BD_MAGIC_WALL_EMPTYING) &&
5168           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
5169       {
5170         magic_wall_x = x;
5171         magic_wall_y = y;
5172       }
5173     }
5174   }
5175
5176 #if USE_NEW_AMOEBA_CODE
5177   /* new experimental amoeba growth stuff */
5178 #if 1
5179   if (!(FrameCounter % 8))
5180 #endif
5181   {
5182     static unsigned long random = 1684108901;
5183
5184     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
5185     {
5186 #if 0
5187       x = (random >> 10) % lev_fieldx;
5188       y = (random >> 20) % lev_fieldy;
5189 #else
5190       x = RND(lev_fieldx);
5191       y = RND(lev_fieldy);
5192 #endif
5193       element = Feld[x][y];
5194
5195       if (!IS_PLAYER(x,y) &&
5196           (element == EL_EMPTY ||
5197            element == EL_SAND ||
5198            element == EL_QUICKSAND_EMPTY ||
5199            element == EL_ACID_SPLASH_LEFT ||
5200            element == EL_ACID_SPLASH_RIGHT))
5201       {
5202         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
5203             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
5204             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
5205             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
5206           Feld[x][y] = EL_AMOEBA_DROP;
5207       }
5208
5209       random = random * 129 + 1;
5210     }
5211   }
5212 #endif
5213
5214 #if 0
5215   if (game.explosions_delayed)
5216 #endif
5217   {
5218     game.explosions_delayed = FALSE;
5219
5220     for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
5221     {
5222       element = Feld[x][y];
5223
5224       if (ExplodeField[x][y])
5225         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
5226       else if (element == EL_EXPLOSION)
5227         Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
5228
5229       ExplodeField[x][y] = EX_NO_EXPLOSION;
5230     }
5231
5232     game.explosions_delayed = TRUE;
5233   }
5234
5235   if (game.magic_wall_active)
5236   {
5237     if (!(game.magic_wall_time_left % 4))
5238     {
5239       int element = Feld[magic_wall_x][magic_wall_y];
5240
5241       if (element == EL_BD_MAGIC_WALL_FULL ||
5242           element == EL_BD_MAGIC_WALL_ACTIVE ||
5243           element == EL_BD_MAGIC_WALL_EMPTYING)
5244         PlaySoundLevel(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
5245       else
5246         PlaySoundLevel(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
5247     }
5248
5249     if (game.magic_wall_time_left > 0)
5250     {
5251       game.magic_wall_time_left--;
5252       if (!game.magic_wall_time_left)
5253       {
5254         for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
5255         {
5256           element = Feld[x][y];
5257
5258           if (element == EL_MAGIC_WALL_ACTIVE ||
5259               element == EL_MAGIC_WALL_FULL)
5260           {
5261             Feld[x][y] = EL_MAGIC_WALL_DEAD;
5262             DrawLevelField(x, y);
5263           }
5264           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
5265                    element == EL_BD_MAGIC_WALL_FULL)
5266           {
5267             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5268             DrawLevelField(x, y);
5269           }
5270         }
5271
5272         game.magic_wall_active = FALSE;
5273       }
5274     }
5275   }
5276
5277   if (game.light_time_left > 0)
5278   {
5279     game.light_time_left--;
5280
5281     if (game.light_time_left == 0)
5282       RedrawAllLightSwitchesAndInvisibleElements();
5283   }
5284
5285   if (game.timegate_time_left > 0)
5286   {
5287     game.timegate_time_left--;
5288
5289     if (game.timegate_time_left == 0)
5290       CloseAllOpenTimegates();
5291   }
5292
5293   for (i=0; i<MAX_PLAYERS; i++)
5294   {
5295     struct PlayerInfo *player = &stored_player[i];
5296
5297     if (SHIELD_ON(player))
5298     {
5299       if (player->shield_deadly_time_left)
5300         PlaySoundLevel(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
5301       else if (player->shield_normal_time_left)
5302         PlaySoundLevel(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
5303     }
5304   }
5305
5306   if (TimeFrames >= (1000 / GameFrameDelay))
5307   {
5308     TimeFrames = 0;
5309     TimePlayed++;
5310
5311     for (i=0; i<MAX_PLAYERS; i++)
5312     {
5313       struct PlayerInfo *player = &stored_player[i];
5314
5315       if (SHIELD_ON(player))
5316       {
5317         player->shield_normal_time_left--;
5318
5319         if (player->shield_deadly_time_left > 0)
5320           player->shield_deadly_time_left--;
5321       }
5322     }
5323
5324     if (tape.recording || tape.playing)
5325       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
5326
5327     if (TimeLeft > 0)
5328     {
5329       TimeLeft--;
5330
5331       if (TimeLeft <= 10 && setup.time_limit)
5332         PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
5333
5334       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
5335
5336       if (!TimeLeft && setup.time_limit)
5337         for (i=0; i<MAX_PLAYERS; i++)
5338           KillHero(&stored_player[i]);
5339     }
5340     else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
5341       DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
5342   }
5343
5344   DrawAllPlayers();
5345
5346   if (options.debug)                    /* calculate frames per second */
5347   {
5348     static unsigned long fps_counter = 0;
5349     static int fps_frames = 0;
5350     unsigned long fps_delay_ms = Counter() - fps_counter;
5351
5352     fps_frames++;
5353
5354     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
5355     {
5356       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
5357
5358       fps_frames = 0;
5359       fps_counter = Counter();
5360     }
5361
5362     redraw_mask |= REDRAW_FPS;
5363   }
5364
5365 #if 0
5366   if (stored_player[0].jx != stored_player[0].last_jx ||
5367       stored_player[0].jy != stored_player[0].last_jy)
5368     printf("::: %d, %d, %d, %d, %d\n",
5369            stored_player[0].MovDir,
5370            stored_player[0].MovPos,
5371            stored_player[0].GfxPos,
5372            stored_player[0].Frame,
5373            stored_player[0].StepFrame);
5374 #endif
5375
5376 #if 1
5377   FrameCounter++;
5378   TimeFrames++;
5379
5380   for (i=0; i<MAX_PLAYERS; i++)
5381   {
5382     int move_frames =
5383       MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
5384
5385     stored_player[i].Frame += move_frames;
5386
5387     if (stored_player[i].MovPos != 0)
5388       stored_player[i].StepFrame += move_frames;
5389   }
5390 #endif
5391 }
5392
5393 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
5394 {
5395   int min_x = x, min_y = y, max_x = x, max_y = y;
5396   int i;
5397
5398   for (i=0; i<MAX_PLAYERS; i++)
5399   {
5400     int jx = stored_player[i].jx, jy = stored_player[i].jy;
5401
5402     if (!stored_player[i].active || &stored_player[i] == player)
5403       continue;
5404
5405     min_x = MIN(min_x, jx);
5406     min_y = MIN(min_y, jy);
5407     max_x = MAX(max_x, jx);
5408     max_y = MAX(max_y, jy);
5409   }
5410
5411   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
5412 }
5413
5414 static boolean AllPlayersInVisibleScreen()
5415 {
5416   int i;
5417
5418   for (i=0; i<MAX_PLAYERS; i++)
5419   {
5420     int jx = stored_player[i].jx, jy = stored_player[i].jy;
5421
5422     if (!stored_player[i].active)
5423       continue;
5424
5425     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
5426       return FALSE;
5427   }
5428
5429   return TRUE;
5430 }
5431
5432 void ScrollLevel(int dx, int dy)
5433 {
5434   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
5435   int x, y;
5436
5437   BlitBitmap(drawto_field, drawto_field,
5438              FX + TILEX*(dx == -1) - softscroll_offset,
5439              FY + TILEY*(dy == -1) - softscroll_offset,
5440              SXSIZE - TILEX*(dx!=0) + 2*softscroll_offset,
5441              SYSIZE - TILEY*(dy!=0) + 2*softscroll_offset,
5442              FX + TILEX*(dx == 1) - softscroll_offset,
5443              FY + TILEY*(dy == 1) - softscroll_offset);
5444
5445   if (dx)
5446   {
5447     x = (dx == 1 ? BX1 : BX2);
5448     for (y=BY1; y<=BY2; y++)
5449       DrawScreenField(x, y);
5450   }
5451
5452   if (dy)
5453   {
5454     y = (dy == 1 ? BY1 : BY2);
5455     for (x=BX1; x<=BX2; x++)
5456       DrawScreenField(x, y);
5457   }
5458
5459   redraw_mask |= REDRAW_FIELD;
5460 }
5461
5462 static void CheckGravityMovement(struct PlayerInfo *player)
5463 {
5464   if (level.gravity && !player->programmed_action)
5465   {
5466     int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
5467     int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
5468     int move_dir =
5469       (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
5470        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
5471        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
5472     int jx = player->jx, jy = player->jy;
5473     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
5474     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
5475     int new_jx = jx + dx, new_jy = jy + dy;
5476     boolean field_under_player_is_free =
5477       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
5478     boolean player_is_moving_to_valid_field =
5479       (IN_LEV_FIELD(new_jx, new_jy) &&
5480        (Feld[new_jx][new_jy] == EL_SP_BASE ||
5481         Feld[new_jx][new_jy] == EL_SAND));
5482
5483     if (field_under_player_is_free &&
5484         !player_is_moving_to_valid_field &&
5485         !IS_WALKABLE_INSIDE(Feld[jx][jy]))
5486       player->programmed_action = MV_DOWN;
5487   }
5488 }
5489
5490 /*
5491   MoveFigureOneStep()
5492   -----------------------------------------------------------------------------
5493   dx, dy:               direction (non-diagonal) to try to move the player to
5494   real_dx, real_dy:     direction as read from input device (can be diagonal)
5495 */
5496
5497 boolean MoveFigureOneStep(struct PlayerInfo *player,
5498                           int dx, int dy, int real_dx, int real_dy)
5499 {
5500   int jx = player->jx, jy = player->jy;
5501   int new_jx = jx+dx, new_jy = jy+dy;
5502   int element;
5503   int can_move;
5504
5505   if (!player->active || (!dx && !dy))
5506     return MF_NO_ACTION;
5507
5508   player->MovDir = (dx < 0 ? MV_LEFT :
5509                     dx > 0 ? MV_RIGHT :
5510                     dy < 0 ? MV_UP :
5511                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
5512
5513   if (!IN_LEV_FIELD(new_jx, new_jy))
5514     return MF_NO_ACTION;
5515
5516   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
5517     return MF_NO_ACTION;
5518
5519 #if 0
5520   element = MovingOrBlocked2Element(new_jx, new_jy);
5521 #else
5522   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
5523 #endif
5524
5525   if (DONT_RUN_INTO(element))
5526   {
5527     if (element == EL_ACID && dx == 0 && dy == 1)
5528     {
5529       SplashAcid(jx, jy);
5530       Feld[jx][jy] = EL_PLAYER_1;
5531       InitMovingField(jx, jy, MV_DOWN);
5532       Store[jx][jy] = EL_ACID;
5533       ContinueMoving(jx, jy);
5534       BuryHero(player);
5535     }
5536     else
5537       TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
5538
5539     return MF_MOVING;
5540   }
5541
5542   can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
5543   if (can_move != MF_MOVING)
5544     return can_move;
5545
5546   StorePlayer[jx][jy] = 0;
5547   player->last_jx = jx;
5548   player->last_jy = jy;
5549   jx = player->jx = new_jx;
5550   jy = player->jy = new_jy;
5551   StorePlayer[jx][jy] = player->element_nr;
5552
5553   player->MovPos =
5554     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
5555
5556   ScrollFigure(player, SCROLL_INIT);
5557
5558   return MF_MOVING;
5559 }
5560
5561 boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
5562 {
5563   int jx = player->jx, jy = player->jy;
5564   int old_jx = jx, old_jy = jy;
5565   int moved = MF_NO_ACTION;
5566
5567   if (!player->active || (!dx && !dy))
5568     return FALSE;
5569
5570 #if 0
5571   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
5572       !tape.playing)
5573     return FALSE;
5574 #else
5575   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
5576       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
5577     return FALSE;
5578 #endif
5579
5580   /* remove the last programmed player action */
5581   player->programmed_action = 0;
5582
5583   if (player->MovPos)
5584   {
5585     /* should only happen if pre-1.2 tape recordings are played */
5586     /* this is only for backward compatibility */
5587
5588     int original_move_delay_value = player->move_delay_value;
5589
5590 #if DEBUG
5591     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
5592            tape.counter);
5593 #endif
5594
5595     /* scroll remaining steps with finest movement resolution */
5596     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
5597
5598     while (player->MovPos)
5599     {
5600       ScrollFigure(player, SCROLL_GO_ON);
5601       ScrollScreen(NULL, SCROLL_GO_ON);
5602       FrameCounter++;
5603       DrawAllPlayers();
5604       BackToFront();
5605     }
5606
5607     player->move_delay_value = original_move_delay_value;
5608   }
5609
5610   if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
5611   {
5612     if (!(moved |= MoveFigureOneStep(player, 0, dy, dx, dy)))
5613       moved |= MoveFigureOneStep(player, dx, 0, dx, dy);
5614   }
5615   else
5616   {
5617     if (!(moved |= MoveFigureOneStep(player, dx, 0, dx, dy)))
5618       moved |= MoveFigureOneStep(player, 0, dy, dx, dy);
5619   }
5620
5621   jx = player->jx;
5622   jy = player->jy;
5623
5624   if (moved & MF_MOVING && !ScreenMovPos &&
5625       (player == local_player || !options.network))
5626   {
5627     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
5628     int offset = (setup.scroll_delay ? 3 : 0);
5629
5630     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
5631     {
5632       /* actual player has left the screen -- scroll in that direction */
5633       if (jx != old_jx)         /* player has moved horizontally */
5634         scroll_x += (jx - old_jx);
5635       else                      /* player has moved vertically */
5636         scroll_y += (jy - old_jy);
5637     }
5638     else
5639     {
5640       if (jx != old_jx)         /* player has moved horizontally */
5641       {
5642         if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
5643             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
5644           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
5645
5646         /* don't scroll over playfield boundaries */
5647         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5648           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5649
5650         /* don't scroll more than one field at a time */
5651         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
5652
5653         /* don't scroll against the player's moving direction */
5654         if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
5655             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
5656           scroll_x = old_scroll_x;
5657       }
5658       else                      /* player has moved vertically */
5659       {
5660         if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
5661             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
5662           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
5663
5664         /* don't scroll over playfield boundaries */
5665         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5666           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5667
5668         /* don't scroll more than one field at a time */
5669         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
5670
5671         /* don't scroll against the player's moving direction */
5672         if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
5673             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
5674           scroll_y = old_scroll_y;
5675       }
5676     }
5677
5678     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
5679     {
5680       if (!options.network && !AllPlayersInVisibleScreen())
5681       {
5682         scroll_x = old_scroll_x;
5683         scroll_y = old_scroll_y;
5684       }
5685       else
5686       {
5687         ScrollScreen(player, SCROLL_INIT);
5688         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
5689       }
5690     }
5691   }
5692
5693 #if 0
5694 #if 1
5695   InitPlayerGfxAnimation(player, ACTION_DEFAULT);
5696 #else
5697   if (!(moved & MF_MOVING) && !player->Pushing)
5698     player->Frame = 0;
5699 #endif
5700 #endif
5701
5702   player->StepFrame = 0;
5703
5704   if (moved & MF_MOVING)
5705   {
5706     if (old_jx != jx && old_jy == jy)
5707       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
5708     else if (old_jx == jx && old_jy != jy)
5709       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
5710
5711     DrawLevelField(jx, jy);     /* for "crumbled sand" */
5712
5713     player->last_move_dir = player->MovDir;
5714     player->is_moving = TRUE;
5715   }
5716   else
5717   {
5718     CheckGravityMovement(player);
5719
5720     /*
5721     player->last_move_dir = MV_NO_MOVING;
5722     */
5723     player->is_moving = FALSE;
5724   }
5725
5726   TestIfHeroTouchesBadThing(jx, jy);
5727
5728   if (!player->active)
5729     RemoveHero(player);
5730
5731   return moved;
5732 }
5733
5734 void ScrollFigure(struct PlayerInfo *player, int mode)
5735 {
5736   int jx = player->jx, jy = player->jy;
5737   int last_jx = player->last_jx, last_jy = player->last_jy;
5738   int move_stepsize = TILEX / player->move_delay_value;
5739
5740   if (!player->active || !player->MovPos)
5741     return;
5742
5743   if (mode == SCROLL_INIT)
5744   {
5745     player->actual_frame_counter = FrameCounter;
5746     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
5747
5748     if (Feld[last_jx][last_jy] == EL_EMPTY)
5749       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
5750
5751     DrawPlayer(player);
5752     return;
5753   }
5754   else if (!FrameReached(&player->actual_frame_counter, 1))
5755     return;
5756
5757   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
5758   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
5759
5760   if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
5761     Feld[last_jx][last_jy] = EL_EMPTY;
5762
5763   /* before DrawPlayer() to draw correct player graphic for this case */
5764   if (player->MovPos == 0)
5765     CheckGravityMovement(player);
5766
5767   DrawPlayer(player);
5768
5769   if (player->MovPos == 0)
5770   {
5771     if (IS_PASSABLE(Feld[last_jx][last_jy]))
5772     {
5773       /* continue with normal speed after quickly moving through gate */
5774       HALVE_PLAYER_SPEED(player);
5775
5776       /* be able to make the next move without delay */
5777       player->move_delay = 0;
5778     }
5779
5780     player->last_jx = jx;
5781     player->last_jy = jy;
5782
5783     if (Feld[jx][jy] == EL_EXIT_OPEN ||
5784         Feld[jx][jy] == EL_SP_EXIT_OPEN)
5785     {
5786       RemoveHero(player);
5787
5788       if (local_player->friends_still_needed == 0 ||
5789           Feld[jx][jy] == EL_SP_EXIT_OPEN)
5790         player->LevelSolved = player->GameOver = TRUE;
5791     }
5792
5793     if (tape.single_step && tape.recording && !tape.pausing &&
5794         !player->programmed_action)
5795       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
5796   }
5797 }
5798
5799 void ScrollScreen(struct PlayerInfo *player, int mode)
5800 {
5801   static unsigned long screen_frame_counter = 0;
5802
5803   if (mode == SCROLL_INIT)
5804   {
5805     /* set scrolling step size according to actual player's moving speed */
5806     ScrollStepSize = TILEX / player->move_delay_value;
5807
5808     screen_frame_counter = FrameCounter;
5809     ScreenMovDir = player->MovDir;
5810     ScreenMovPos = player->MovPos;
5811     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
5812     return;
5813   }
5814   else if (!FrameReached(&screen_frame_counter, 1))
5815     return;
5816
5817   if (ScreenMovPos)
5818   {
5819     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
5820     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
5821     redraw_mask |= REDRAW_FIELD;
5822   }
5823   else
5824     ScreenMovDir = MV_NO_MOVING;
5825 }
5826
5827 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
5828 {
5829   int i, kill_x = -1, kill_y = -1;
5830   static int test_xy[4][2] =
5831   {
5832     { 0, -1 },
5833     { -1, 0 },
5834     { +1, 0 },
5835     { 0, +1 }
5836   };
5837   static int test_dir[4] =
5838   {
5839     MV_UP,
5840     MV_LEFT,
5841     MV_RIGHT,
5842     MV_DOWN
5843   };
5844
5845   for (i=0; i<4; i++)
5846   {
5847     int test_x, test_y, test_move_dir, test_element;
5848
5849     test_x = good_x + test_xy[i][0];
5850     test_y = good_y + test_xy[i][1];
5851     if (!IN_LEV_FIELD(test_x, test_y))
5852       continue;
5853
5854     test_move_dir =
5855       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
5856
5857 #if 0
5858     test_element = Feld[test_x][test_y];
5859 #else
5860     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
5861 #endif
5862
5863     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
5864        2nd case: DONT_TOUCH style bad thing does not move away from good thing
5865     */
5866     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
5867         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
5868     {
5869       kill_x = test_x;
5870       kill_y = test_y;
5871       break;
5872     }
5873   }
5874
5875   if (kill_x != -1 || kill_y != -1)
5876   {
5877     if (IS_PLAYER(good_x, good_y))
5878     {
5879       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
5880
5881       if (player->shield_deadly_time_left > 0)
5882         Bang(kill_x, kill_y);
5883       else if (!PLAYER_PROTECTED(good_x, good_y))
5884         KillHero(player);
5885     }
5886     else
5887       Bang(good_x, good_y);
5888   }
5889 }
5890
5891 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
5892 {
5893   int i, kill_x = -1, kill_y = -1;
5894   int bad_element = Feld[bad_x][bad_y];
5895   static int test_xy[4][2] =
5896   {
5897     { 0, -1 },
5898     { -1, 0 },
5899     { +1, 0 },
5900     { 0, +1 }
5901   };
5902   static int test_dir[4] =
5903   {
5904     MV_UP,
5905     MV_LEFT,
5906     MV_RIGHT,
5907     MV_DOWN
5908   };
5909
5910   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
5911     return;
5912
5913   for (i=0; i<4; i++)
5914   {
5915     int test_x, test_y, test_move_dir, test_element;
5916
5917     test_x = bad_x + test_xy[i][0];
5918     test_y = bad_y + test_xy[i][1];
5919     if (!IN_LEV_FIELD(test_x, test_y))
5920       continue;
5921
5922     test_move_dir =
5923       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
5924
5925     test_element = Feld[test_x][test_y];
5926
5927     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
5928        2nd case: DONT_TOUCH style bad thing does not move away from good thing
5929     */
5930     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
5931         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
5932     {
5933       /* good thing is player or penguin that does not move away */
5934       if (IS_PLAYER(test_x, test_y))
5935       {
5936         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
5937
5938         if (bad_element == EL_ROBOT && player->is_moving)
5939           continue;     /* robot does not kill player if he is moving */
5940
5941         kill_x = test_x;
5942         kill_y = test_y;
5943         break;
5944       }
5945       else if (test_element == EL_PENGUIN)
5946       {
5947         kill_x = test_x;
5948         kill_y = test_y;
5949         break;
5950       }
5951     }
5952   }
5953
5954   if (kill_x != -1 || kill_y != -1)
5955   {
5956     if (IS_PLAYER(kill_x, kill_y))
5957     {
5958       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
5959
5960 #if 0
5961       int dir = player->MovDir;
5962       int newx = player->jx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5963       int newy = player->jy + (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
5964
5965       if (Feld[bad_x][bad_y] == EL_ROBOT && player->is_moving &&
5966           newx != bad_x && newy != bad_y)
5967         ;       /* robot does not kill player if he is moving */
5968       else
5969         printf("-> %d\n", player->MovDir);
5970
5971       if (Feld[bad_x][bad_y] == EL_ROBOT && player->is_moving &&
5972           newx != bad_x && newy != bad_y)
5973         ;       /* robot does not kill player if he is moving */
5974       else
5975         ;
5976 #endif
5977
5978       if (player->shield_deadly_time_left > 0)
5979         Bang(bad_x, bad_y);
5980       else if (!PLAYER_PROTECTED(kill_x, kill_y))
5981         KillHero(player);
5982     }
5983     else
5984       Bang(kill_x, kill_y);
5985   }
5986 }
5987
5988 void TestIfHeroTouchesBadThing(int x, int y)
5989 {
5990   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
5991 }
5992
5993 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
5994 {
5995   TestIfGoodThingHitsBadThing(x, y, move_dir);
5996 }
5997
5998 void TestIfBadThingTouchesHero(int x, int y)
5999 {
6000   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
6001 }
6002
6003 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
6004 {
6005   TestIfBadThingHitsGoodThing(x, y, move_dir);
6006 }
6007
6008 void TestIfFriendTouchesBadThing(int x, int y)
6009 {
6010   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
6011 }
6012
6013 void TestIfBadThingTouchesFriend(int x, int y)
6014 {
6015   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
6016 }
6017
6018 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
6019 {
6020   int i, kill_x = bad_x, kill_y = bad_y;
6021   static int xy[4][2] =
6022   {
6023     { 0, -1 },
6024     { -1, 0 },
6025     { +1, 0 },
6026     { 0, +1 }
6027   };
6028
6029   for (i=0; i<4; i++)
6030   {
6031     int x, y, element;
6032
6033     x = bad_x + xy[i][0];
6034     y = bad_y + xy[i][1];
6035     if (!IN_LEV_FIELD(x, y))
6036       continue;
6037
6038     element = Feld[x][y];
6039     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
6040         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
6041     {
6042       kill_x = x;
6043       kill_y = y;
6044       break;
6045     }
6046   }
6047
6048   if (kill_x != bad_x || kill_y != bad_y)
6049     Bang(bad_x, bad_y);
6050 }
6051
6052 void KillHero(struct PlayerInfo *player)
6053 {
6054   int jx = player->jx, jy = player->jy;
6055
6056   if (!player->active)
6057     return;
6058
6059   /* remove accessible field at the player's position */
6060   Feld[jx][jy] = EL_EMPTY;
6061
6062   /* deactivate shield (else Bang()/Explode() would not work right) */
6063   player->shield_normal_time_left = 0;
6064   player->shield_deadly_time_left = 0;
6065
6066   Bang(jx, jy);
6067   BuryHero(player);
6068 }
6069
6070 static void KillHeroUnlessProtected(int x, int y)
6071 {
6072   if (!PLAYER_PROTECTED(x, y))
6073     KillHero(PLAYERINFO(x, y));
6074 }
6075
6076 void BuryHero(struct PlayerInfo *player)
6077 {
6078   int jx = player->jx, jy = player->jy;
6079
6080   if (!player->active)
6081     return;
6082
6083   PlaySoundLevel(jx, jy, SND_CLASS_PLAYER_DYING);
6084   PlaySoundLevel(jx, jy, SND_GAME_LOSING);
6085
6086   player->GameOver = TRUE;
6087   RemoveHero(player);
6088 }
6089
6090 void RemoveHero(struct PlayerInfo *player)
6091 {
6092   int jx = player->jx, jy = player->jy;
6093   int i, found = FALSE;
6094
6095   player->present = FALSE;
6096   player->active = FALSE;
6097
6098   if (!ExplodeField[jx][jy])
6099     StorePlayer[jx][jy] = 0;
6100
6101   for (i=0; i<MAX_PLAYERS; i++)
6102     if (stored_player[i].active)
6103       found = TRUE;
6104
6105   if (!found)
6106     AllPlayersGone = TRUE;
6107
6108   ExitX = ZX = jx;
6109   ExitY = ZY = jy;
6110 }
6111
6112 /*
6113   checkDiagonalPushing()
6114   -----------------------------------------------------------------------------
6115   check if diagonal input device direction results in pushing of object
6116   (by checking if the alternative direction is walkable, diggable, ...)
6117 */
6118
6119 static boolean checkDiagonalPushing(struct PlayerInfo *player,
6120                                     int x, int y, int real_dx, int real_dy)
6121 {
6122   int jx, jy, dx, dy, xx, yy;
6123
6124   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
6125     return TRUE;
6126
6127   /* diagonal direction: check alternative direction */
6128   jx = player->jx;
6129   jy = player->jy;
6130   dx = x - jx;
6131   dy = y - jy;
6132   xx = jx + (dx == 0 ? real_dx : 0);
6133   yy = jy + (dy == 0 ? real_dy : 0);
6134
6135   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
6136 }
6137
6138 /*
6139   DigField()
6140   -----------------------------------------------------------------------------
6141   x, y:                 field next to player (non-diagonal) to try to dig to
6142   real_dx, real_dy:     direction as read from input device (can be diagonal)
6143 */
6144
6145 int DigField(struct PlayerInfo *player,
6146              int x, int y, int real_dx, int real_dy, int mode)
6147 {
6148   int jx = player->jx, jy = player->jy;
6149   int dx = x - jx, dy = y - jy;
6150   int move_direction = (dx == -1 ? MV_LEFT :
6151                         dx == +1 ? MV_RIGHT :
6152                         dy == -1 ? MV_UP :
6153                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
6154   int element;
6155
6156   if (player->MovPos == 0)
6157   {
6158     player->is_digging = FALSE;
6159     player->is_collecting = FALSE;
6160   }
6161
6162   if (player->MovPos == 0)      /* last pushing move finished */
6163     player->Pushing = FALSE;
6164
6165   if (mode == DF_NO_PUSH)       /* player just stopped pushing */
6166   {
6167     player->Switching = FALSE;
6168     player->push_delay = 0;
6169
6170     return MF_NO_ACTION;
6171   }
6172
6173   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
6174     return MF_NO_ACTION;
6175
6176 #if 0
6177   if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
6178 #else
6179   if (IS_TUBE(Feld[jx][jy]) ||
6180       (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0)))
6181 #endif
6182   {
6183     int i = 0;
6184     int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
6185     int tube_leave_directions[][2] =
6186     {
6187       { EL_TUBE_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
6188       { EL_TUBE_VERTICAL,                                    MV_UP | MV_DOWN },
6189       { EL_TUBE_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
6190       { EL_TUBE_VERTICAL_LEFT,          MV_LEFT |            MV_UP | MV_DOWN },
6191       { EL_TUBE_VERTICAL_RIGHT,                   MV_RIGHT | MV_UP | MV_DOWN },
6192       { EL_TUBE_HORIZONTAL_UP,          MV_LEFT | MV_RIGHT | MV_UP           },
6193       { EL_TUBE_HORIZONTAL_DOWN,        MV_LEFT | MV_RIGHT |         MV_DOWN },
6194       { EL_TUBE_LEFT_UP,                MV_LEFT |            MV_UP           },
6195       { EL_TUBE_LEFT_DOWN,              MV_LEFT |                    MV_DOWN },
6196       { EL_TUBE_RIGHT_UP,                         MV_RIGHT | MV_UP           },
6197       { EL_TUBE_RIGHT_DOWN,                       MV_RIGHT |         MV_DOWN },
6198       { -1,                             MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
6199     };
6200
6201     while (tube_leave_directions[i][0] != tube_element)
6202     {
6203       i++;
6204       if (tube_leave_directions[i][0] == -1)    /* should not happen */
6205         break;
6206     }
6207
6208     if (!(tube_leave_directions[i][1] & move_direction))
6209       return MF_NO_ACTION;      /* tube has no opening in this direction */
6210   }
6211
6212   element = Feld[x][y];
6213
6214 #if 1
6215   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
6216       game.engine_version >= VERSION_IDENT(2,2,0))
6217     return MF_NO_ACTION;
6218 #endif
6219
6220   switch (element)
6221   {
6222     case EL_EMPTY:
6223     case EL_SAND:
6224     case EL_INVISIBLE_SAND:
6225     case EL_INVISIBLE_SAND_ACTIVE:
6226     case EL_TRAP:
6227     case EL_SP_BASE:
6228     case EL_SP_BUGGY_BASE:
6229     case EL_SP_BUGGY_BASE_ACTIVATING:
6230       RemoveField(x, y);
6231 #if 1
6232       if (mode != DF_SNAP && element != EL_EMPTY)
6233       {
6234         GfxElement[x][y] = (CAN_BE_CRUMBLED(element) ? EL_SAND : element);
6235         player->is_digging = TRUE;
6236       }
6237 #endif
6238       PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
6239       break;
6240
6241     case EL_EMERALD:
6242     case EL_BD_DIAMOND:
6243     case EL_EMERALD_YELLOW:
6244     case EL_EMERALD_RED:
6245     case EL_EMERALD_PURPLE:
6246     case EL_DIAMOND:
6247     case EL_SP_INFOTRON:
6248     case EL_PEARL:
6249     case EL_CRYSTAL:
6250       RemoveField(x, y);
6251 #if 1
6252       if (mode != DF_SNAP)
6253       {
6254         GfxElement[x][y] = element;
6255         player->is_collecting = TRUE;
6256       }
6257 #endif
6258       local_player->gems_still_needed -= (element == EL_DIAMOND ? 3 :
6259                                           element == EL_PEARL ? 5 :
6260                                           element == EL_CRYSTAL ? 8 : 1);
6261       if (local_player->gems_still_needed < 0)
6262         local_player->gems_still_needed = 0;
6263       RaiseScoreElement(element);
6264       DrawText(DX_EMERALDS, DY_EMERALDS,
6265                int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
6266       PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
6267       break;
6268
6269     case EL_SPEED_PILL:
6270       RemoveField(x, y);
6271       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
6272       PlaySoundLevel(x, y, SND_SPEED_PILL_COLLECTING);
6273       break;
6274
6275     case EL_ENVELOPE:
6276       Feld[x][y] = EL_EMPTY;
6277       PlaySoundLevel(x, y, SND_ENVELOPE_COLLECTING);
6278       break;
6279
6280     case EL_EXTRA_TIME:
6281       RemoveField(x, y);
6282       if (level.time > 0)
6283       {
6284         TimeLeft += 10;
6285         DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
6286       }
6287       PlaySoundStereo(SND_EXTRA_TIME_COLLECTING, SOUND_MIDDLE);
6288       break;
6289
6290     case EL_SHIELD_NORMAL:
6291       RemoveField(x, y);
6292       player->shield_normal_time_left += 10;
6293       PlaySoundLevel(x, y, SND_SHIELD_NORMAL_COLLECTING);
6294       break;
6295
6296     case EL_SHIELD_DEADLY:
6297       RemoveField(x, y);
6298       player->shield_normal_time_left += 10;
6299       player->shield_deadly_time_left += 10;
6300       PlaySoundLevel(x, y, SND_SHIELD_DEADLY_COLLECTING);
6301       break;
6302
6303     case EL_DYNAMITE:
6304     case EL_SP_DISK_RED:
6305       RemoveField(x, y);
6306       player->dynamite++;
6307       player->use_disk_red_graphic = (element == EL_SP_DISK_RED);
6308       RaiseScoreElement(EL_DYNAMITE);
6309       DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(local_player->dynamite, 3),
6310                FONT_TEXT_2);
6311       PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
6312       break;
6313
6314     case EL_DYNABOMB_INCREASE_NUMBER:
6315       RemoveField(x, y);
6316       player->dynabomb_count++;
6317       player->dynabombs_left++;
6318       RaiseScoreElement(EL_DYNAMITE);
6319       PlaySoundLevel(x, y, SND_DYNABOMB_INCREASE_NUMBER_COLLECTING);
6320       break;
6321
6322     case EL_DYNABOMB_INCREASE_SIZE:
6323       RemoveField(x, y);
6324       player->dynabomb_size++;
6325       RaiseScoreElement(EL_DYNAMITE);
6326       PlaySoundLevel(x, y, SND_DYNABOMB_INCREASE_SIZE_COLLECTING);
6327       break;
6328
6329     case EL_DYNABOMB_INCREASE_POWER:
6330       RemoveField(x, y);
6331       player->dynabomb_xl = TRUE;
6332       RaiseScoreElement(EL_DYNAMITE);
6333       PlaySoundLevel(x, y, SND_DYNABOMB_INCREASE_POWER_COLLECTING);
6334       break;
6335
6336     case EL_KEY_1:
6337     case EL_KEY_2:
6338     case EL_KEY_3:
6339     case EL_KEY_4:
6340     {
6341       int key_nr = element - EL_KEY_1;
6342       int graphic = el2edimg(element);
6343
6344       RemoveField(x, y);
6345       player->key[key_nr] = TRUE;
6346       RaiseScoreElement(element);
6347       DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
6348                          graphic);
6349       DrawMiniGraphicExt(window, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
6350                          graphic);
6351       PlaySoundLevel(x, y, SND_CLASS_KEY_COLLECTING);
6352       break;
6353     }
6354
6355     case EL_EM_KEY_1:
6356     case EL_EM_KEY_2:
6357     case EL_EM_KEY_3:
6358     case EL_EM_KEY_4:
6359     {
6360       int key_nr = element - EL_EM_KEY_1;
6361       int graphic = el2edimg(EL_KEY_1 + key_nr);
6362
6363       RemoveField(x, y);
6364       player->key[key_nr] = TRUE;
6365       RaiseScoreElement(element);
6366       DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
6367                          graphic);
6368       DrawMiniGraphicExt(window, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
6369                          graphic);
6370       PlaySoundLevel(x, y, SND_CLASS_KEY_COLLECTING);
6371       break;
6372     }
6373
6374     case EL_ROBOT_WHEEL:
6375       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
6376       ZX = x;
6377       ZY = y;
6378       DrawLevelField(x, y);
6379       PlaySoundLevel(x, y, SND_ROBOT_WHEEL_ACTIVATING);
6380       return MF_ACTION;
6381       break;
6382
6383     case EL_SP_TERMINAL:
6384       {
6385         int xx, yy;
6386
6387         PlaySoundLevel(x, y, SND_SP_TERMINAL_ACTIVATING);
6388
6389         for (yy=0; yy<lev_fieldy; yy++)
6390         {
6391           for (xx=0; xx<lev_fieldx; xx++)
6392           {
6393             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
6394               Bang(xx, yy);
6395             else if (Feld[xx][yy] == EL_SP_TERMINAL)
6396               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
6397           }
6398         }
6399
6400         return MF_ACTION;
6401       }
6402       break;
6403
6404     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
6405     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
6406     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
6407     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
6408     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
6409     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
6410     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
6411     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
6412     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
6413     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
6414     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
6415     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
6416       if (!player->Switching)
6417       {
6418         player->Switching = TRUE;
6419         ToggleBeltSwitch(x, y);
6420         PlaySoundLevel(x, y, SND_CLASS_CONVEYOR_BELT_SWITCH_ACTIVATING);
6421       }
6422       return MF_ACTION;
6423       break;
6424
6425     case EL_SWITCHGATE_SWITCH_UP:
6426     case EL_SWITCHGATE_SWITCH_DOWN:
6427       if (!player->Switching)
6428       {
6429         player->Switching = TRUE;
6430         ToggleSwitchgateSwitch(x, y);
6431         PlaySoundLevel(x, y, SND_CLASS_SWITCHGATE_SWITCH_ACTIVATING);
6432       }
6433       return MF_ACTION;
6434       break;
6435
6436     case EL_LIGHT_SWITCH:
6437     case EL_LIGHT_SWITCH_ACTIVE:
6438       if (!player->Switching)
6439       {
6440         player->Switching = TRUE;
6441         ToggleLightSwitch(x, y);
6442         PlaySoundLevel(x, y, element == EL_LIGHT_SWITCH ?
6443                        SND_LIGHT_SWITCH_ACTIVATING :
6444                        SND_LIGHT_SWITCH_DEACTIVATING);
6445       }
6446       return MF_ACTION;
6447       break;
6448
6449     case EL_TIMEGATE_SWITCH:
6450       ActivateTimegateSwitch(x, y);
6451       PlaySoundLevel(x, y, SND_TIMEGATE_SWITCH_ACTIVATING);
6452
6453       return MF_ACTION;
6454       break;
6455
6456     case EL_BALLOON_SWITCH_LEFT:
6457     case EL_BALLOON_SWITCH_RIGHT:
6458     case EL_BALLOON_SWITCH_UP:
6459     case EL_BALLOON_SWITCH_DOWN:
6460     case EL_BALLOON_SWITCH_ANY:
6461       if (element == EL_BALLOON_SWITCH_ANY)
6462         game.balloon_dir = move_direction;
6463       else
6464         game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT :
6465                             element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
6466                             element == EL_BALLOON_SWITCH_UP    ? MV_UP :
6467                             element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN :
6468                             MV_NO_MOVING);
6469       PlaySoundLevel(x, y, SND_CLASS_BALLOON_SWITCH_ACTIVATING);
6470
6471       return MF_ACTION;
6472       break;
6473
6474       /* the following elements cannot be pushed by "snapping" */
6475     case EL_ROCK:
6476     case EL_BOMB:
6477     case EL_DX_SUPABOMB:
6478     case EL_NUT:
6479     case EL_TIME_ORB_EMPTY:
6480     case EL_SP_ZONK:
6481     case EL_SP_DISK_ORANGE:
6482     case EL_SPRING:
6483       if (mode == DF_SNAP)
6484         return MF_NO_ACTION;
6485
6486       /* no "break" -- fall through to next case */
6487
6488       /* the following elements can be pushed by "snapping" */
6489     case EL_BD_ROCK:
6490       if (dy)
6491         return MF_NO_ACTION;
6492
6493       player->Pushing = TRUE;
6494
6495 #if 0
6496       if (element == EL_ROCK)
6497         printf("::: wanna push [%d] [%d]\n",
6498                FrameCounter, player->push_delay_value);
6499 #endif
6500
6501       if (!IN_LEV_FIELD(x+dx, y+dy) || !IS_FREE(x+dx, y+dy))
6502         return MF_NO_ACTION;
6503
6504       if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
6505         return MF_NO_ACTION;
6506
6507       if (player->push_delay == 0)
6508         player->push_delay = FrameCounter;
6509 #if 0
6510       if (!FrameReached(&player->push_delay, player->push_delay_value) &&
6511           !tape.playing &&
6512           element != EL_SPRING)
6513         return MF_NO_ACTION;
6514 #else
6515       if (!FrameReached(&player->push_delay, player->push_delay_value) &&
6516           !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
6517           element != EL_SPRING)
6518         return MF_NO_ACTION;
6519 #endif
6520
6521       if (mode == DF_SNAP)
6522       {
6523         InitMovingField(x, y, move_direction);
6524         ContinueMoving(x, y);
6525       }
6526       else
6527       {
6528         RemoveField(x, y);
6529         Feld[x + dx][y + dy] = element;
6530       }
6531
6532       if (element == EL_SPRING)
6533       {
6534         Feld[x + dx][y + dy] = EL_SPRING;
6535         MovDir[x + dx][y + dy] = move_direction;
6536       }
6537
6538       player->push_delay_value = (element == EL_SPRING ? 0 : 2 + RND(8));
6539
6540       DrawLevelField(x + dx, y + dy);
6541       PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
6542       break;
6543
6544     case EL_GATE_1:
6545     case EL_GATE_2:
6546     case EL_GATE_3:
6547     case EL_GATE_4:
6548       if (!player->key[element - EL_GATE_1])
6549         return MF_NO_ACTION;
6550       break;
6551
6552     case EL_GATE_1_GRAY:
6553     case EL_GATE_2_GRAY:
6554     case EL_GATE_3_GRAY:
6555     case EL_GATE_4_GRAY:
6556       if (!player->key[element - EL_GATE_1_GRAY])
6557         return MF_NO_ACTION;
6558       break;
6559
6560     case EL_EM_GATE_1:
6561     case EL_EM_GATE_2:
6562     case EL_EM_GATE_3:
6563     case EL_EM_GATE_4:
6564       if (!player->key[element - EL_EM_GATE_1])
6565         return MF_NO_ACTION;
6566       if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
6567         return MF_NO_ACTION;
6568
6569       /* automatically move to the next field with double speed */
6570       player->programmed_action = move_direction;
6571       DOUBLE_PLAYER_SPEED(player);
6572
6573       PlaySoundLevel(x, y, SND_CLASS_GATE_PASSING);
6574       break;
6575
6576     case EL_EM_GATE_1_GRAY:
6577     case EL_EM_GATE_2_GRAY:
6578     case EL_EM_GATE_3_GRAY:
6579     case EL_EM_GATE_4_GRAY:
6580       if (!player->key[element - EL_EM_GATE_1_GRAY])
6581         return MF_NO_ACTION;
6582       if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
6583         return MF_NO_ACTION;
6584
6585       /* automatically move to the next field with double speed */
6586       player->programmed_action = move_direction;
6587       DOUBLE_PLAYER_SPEED(player);
6588
6589 #if 1
6590       PlaySoundLevelAction(x, y, ACTION_PASSING);
6591 #else
6592       PlaySoundLevel(x, y, SND_GATE_PASSING);
6593 #endif
6594       break;
6595
6596     case EL_SWITCHGATE_OPEN:
6597     case EL_TIMEGATE_OPEN:
6598       if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
6599         return MF_NO_ACTION;
6600
6601       /* automatically move to the next field with double speed */
6602       player->programmed_action = move_direction;
6603       DOUBLE_PLAYER_SPEED(player);
6604
6605       PlaySoundLevelElementAction(x, y, element, ACTION_PASSING);
6606       break;
6607
6608     case EL_SP_PORT_LEFT:
6609     case EL_SP_PORT_RIGHT:
6610     case EL_SP_PORT_UP:
6611     case EL_SP_PORT_DOWN:
6612     case EL_SP_PORT_HORIZONTAL:
6613     case EL_SP_PORT_VERTICAL:
6614     case EL_SP_PORT_ANY:
6615     case EL_SP_GRAVITY_PORT_LEFT:
6616     case EL_SP_GRAVITY_PORT_RIGHT:
6617     case EL_SP_GRAVITY_PORT_UP:
6618     case EL_SP_GRAVITY_PORT_DOWN:
6619       if ((dx == -1 &&
6620            element != EL_SP_PORT_LEFT &&
6621            element != EL_SP_GRAVITY_PORT_LEFT &&
6622            element != EL_SP_PORT_HORIZONTAL &&
6623            element != EL_SP_PORT_ANY) ||
6624           (dx == +1 &&
6625            element != EL_SP_PORT_RIGHT &&
6626            element != EL_SP_GRAVITY_PORT_RIGHT &&
6627            element != EL_SP_PORT_HORIZONTAL &&
6628            element != EL_SP_PORT_ANY) ||
6629           (dy == -1 &&
6630            element != EL_SP_PORT_UP &&
6631            element != EL_SP_GRAVITY_PORT_UP &&
6632            element != EL_SP_PORT_VERTICAL &&
6633            element != EL_SP_PORT_ANY) ||
6634           (dy == +1 &&
6635            element != EL_SP_PORT_DOWN &&
6636            element != EL_SP_GRAVITY_PORT_DOWN &&
6637            element != EL_SP_PORT_VERTICAL &&
6638            element != EL_SP_PORT_ANY) ||
6639           !IN_LEV_FIELD(x + dx, y + dy) ||
6640           !IS_FREE(x + dx, y + dy))
6641         return MF_NO_ACTION;
6642
6643       /* automatically move to the next field with double speed */
6644       player->programmed_action = move_direction;
6645       DOUBLE_PLAYER_SPEED(player);
6646
6647       PlaySoundLevel(x, y, SND_CLASS_SP_PORT_PASSING);
6648       break;
6649
6650     case EL_TUBE_ANY:
6651     case EL_TUBE_VERTICAL:
6652     case EL_TUBE_HORIZONTAL:
6653     case EL_TUBE_VERTICAL_LEFT:
6654     case EL_TUBE_VERTICAL_RIGHT:
6655     case EL_TUBE_HORIZONTAL_UP:
6656     case EL_TUBE_HORIZONTAL_DOWN:
6657     case EL_TUBE_LEFT_UP:
6658     case EL_TUBE_LEFT_DOWN:
6659     case EL_TUBE_RIGHT_UP:
6660     case EL_TUBE_RIGHT_DOWN:
6661       {
6662         int i = 0;
6663         int tube_enter_directions[][2] =
6664         {
6665           { EL_TUBE_ANY,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
6666           { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
6667           { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
6668           { EL_TUBE_VERTICAL_LEFT,                MV_RIGHT | MV_UP | MV_DOWN },
6669           { EL_TUBE_VERTICAL_RIGHT,     MV_LEFT            | MV_UP | MV_DOWN },
6670           { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT |         MV_DOWN },
6671           { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT | MV_UP           },
6672           { EL_TUBE_LEFT_UP,                      MV_RIGHT |         MV_DOWN },
6673           { EL_TUBE_LEFT_DOWN,                    MV_RIGHT | MV_UP           },
6674           { EL_TUBE_RIGHT_UP,           MV_LEFT |                    MV_DOWN },
6675           { EL_TUBE_RIGHT_DOWN,         MV_LEFT |            MV_UP           },
6676           { -1,                         MV_NO_MOVING                         }
6677         };
6678
6679         while (tube_enter_directions[i][0] != element)
6680         {
6681           i++;
6682           if (tube_enter_directions[i][0] == -1)        /* should not happen */
6683             break;
6684         }
6685
6686         if (!(tube_enter_directions[i][1] & move_direction))
6687           return MF_NO_ACTION;  /* tube has no opening in this direction */
6688
6689         PlaySoundLevel(x, y, SND_CLASS_TUBE_PASSING);
6690       }
6691       break;
6692
6693     case EL_EXIT_CLOSED:
6694     case EL_SP_EXIT_CLOSED:
6695     case EL_EXIT_OPENING:
6696       return MF_NO_ACTION;
6697       break;
6698
6699     case EL_EXIT_OPEN:
6700     case EL_SP_EXIT_OPEN:
6701       if (mode == DF_SNAP)
6702         return MF_NO_ACTION;
6703
6704       if (element == EL_EXIT_OPEN)
6705         PlaySoundLevel(x, y, SND_CLASS_EXIT_PASSING);
6706       else
6707         PlaySoundLevel(x, y, SND_CLASS_SP_EXIT_PASSING);
6708
6709       break;
6710
6711     case EL_LAMP:
6712       Feld[x][y] = EL_LAMP_ACTIVE;
6713       local_player->lights_still_needed--;
6714       DrawLevelField(x, y);
6715       PlaySoundLevel(x, y, SND_LAMP_ACTIVATING);
6716       return MF_ACTION;
6717       break;
6718
6719     case EL_TIME_ORB_FULL:
6720       Feld[x][y] = EL_TIME_ORB_EMPTY;
6721       TimeLeft += 10;
6722       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
6723       DrawLevelField(x, y);
6724       PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
6725       return MF_ACTION;
6726       break;
6727
6728     case EL_SOKOBAN_FIELD_EMPTY:
6729       break;
6730
6731     case EL_SOKOBAN_OBJECT:
6732     case EL_SOKOBAN_FIELD_FULL:
6733     case EL_SATELLITE:
6734     case EL_SP_DISK_YELLOW:
6735     case EL_BALLOON:
6736       if (mode == DF_SNAP)
6737         return MF_NO_ACTION;
6738
6739       player->Pushing = TRUE;
6740
6741       if (!IN_LEV_FIELD(x+dx, y+dy)
6742           || (!IS_FREE(x+dx, y+dy)
6743               && (Feld[x+dx][y+dy] != EL_SOKOBAN_FIELD_EMPTY
6744                   || !IS_SB_ELEMENT(element))))
6745         return MF_NO_ACTION;
6746
6747       if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
6748         return MF_NO_ACTION;
6749
6750       if (player->push_delay == 0)
6751         player->push_delay = FrameCounter;
6752 #if 0
6753       if (!FrameReached(&player->push_delay, player->push_delay_value) &&
6754           !tape.playing && element != EL_BALLOON)
6755         return MF_NO_ACTION;
6756 #else
6757       if (!FrameReached(&player->push_delay, player->push_delay_value) &&
6758           !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
6759           element != EL_BALLOON)
6760         return MF_NO_ACTION;
6761 #endif
6762
6763       if (IS_SB_ELEMENT(element))
6764       {
6765         if (element == EL_SOKOBAN_FIELD_FULL)
6766         {
6767           Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
6768           local_player->sokobanfields_still_needed++;
6769         }
6770         else
6771           RemoveField(x, y);
6772
6773         if (Feld[x+dx][y+dy] == EL_SOKOBAN_FIELD_EMPTY)
6774         {
6775           Feld[x+dx][y+dy] = EL_SOKOBAN_FIELD_FULL;
6776           local_player->sokobanfields_still_needed--;
6777           if (element == EL_SOKOBAN_OBJECT)
6778 #if 1
6779             PlaySoundLevelAction(x+dx, y+dy, ACTION_FILLING);
6780 #else
6781             PlaySoundLevel(x, y, SND_CLASS_SOKOBAN_FIELD_FILLING);
6782 #endif
6783           else
6784 #if 1
6785             PlaySoundLevelAction(x+dx, y+dy, ACTION_PUSHING);
6786 #else
6787             PlaySoundLevel(x, y, SND_SOKOBAN_OBJECT_PUSHING);
6788 #endif
6789         }
6790         else
6791         {
6792           Feld[x+dx][y+dy] = EL_SOKOBAN_OBJECT;
6793           if (element == EL_SOKOBAN_FIELD_FULL)
6794 #if 1
6795             PlaySoundLevelAction(x+dx, y+dy, ACTION_EMPTYING);
6796 #else
6797             PlaySoundLevel(x, y, SND_SOKOBAN_FIELD_EMPTYING);
6798 #endif
6799           else
6800 #if 1
6801             PlaySoundLevelAction(x+dx, y+dy, ACTION_PUSHING);
6802 #else
6803             PlaySoundLevel(x, y, SND_SOKOBAN_OBJECT_PUSHING);
6804 #endif
6805         }
6806       }
6807       else
6808       {
6809         RemoveField(x, y);
6810         Feld[x+dx][y+dy] = element;
6811         PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
6812       }
6813
6814       player->push_delay_value = (element == EL_BALLOON ? 0 : 2);
6815
6816       DrawLevelField(x, y);
6817       DrawLevelField(x + dx, y + dy);
6818
6819       if (IS_SB_ELEMENT(element) &&
6820           local_player->sokobanfields_still_needed == 0 &&
6821           game.emulation == EMU_SOKOBAN)
6822       {
6823         player->LevelSolved = player->GameOver = TRUE;
6824         PlaySoundLevel(x, y, SND_GAME_SOKOBAN_SOLVING);
6825       }
6826
6827       break;
6828
6829     case EL_PENGUIN:
6830     case EL_PIG:
6831     case EL_DRAGON:
6832       break;
6833
6834     default:
6835
6836       if (IS_WALKABLE(element))
6837       {
6838         break;
6839       }
6840       else if (IS_DIGGABLE(element))
6841       {
6842         RemoveField(x, y);
6843 #if 1
6844         if (mode != DF_SNAP)
6845         {
6846           GfxElement[x][y] =
6847             (CAN_BE_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
6848           player->is_digging = TRUE;
6849         }
6850 #endif
6851         PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
6852
6853         break;
6854       }
6855       else if (IS_COLLECTIBLE(element))
6856       {
6857         RemoveField(x, y);
6858 #if 1
6859         if (mode != DF_SNAP)
6860         {
6861           GfxElement[x][y] = element;
6862           player->is_collecting = TRUE;
6863         }
6864 #endif
6865         PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
6866
6867         break;
6868       }
6869       else if (IS_PUSHABLE(element))
6870       {
6871         if (mode == DF_SNAP)
6872           return MF_NO_ACTION;
6873
6874         if (CAN_FALL(element) && dy)
6875           return MF_NO_ACTION;
6876
6877         if (!player->Pushing &&
6878             game.engine_version >= RELEASE_IDENT(2,2,0,7))
6879           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
6880
6881         player->Pushing = TRUE;
6882
6883         if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
6884           return MF_NO_ACTION;
6885
6886         if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
6887           return MF_NO_ACTION;
6888
6889         if (player->push_delay == 0)    /* new pushing; restart delay */
6890           player->push_delay = FrameCounter;
6891
6892         if (!FrameReached(&player->push_delay, player->push_delay_value) &&
6893             !(tape.playing && tape.file_version < FILE_VERSION_2_0))
6894           return MF_NO_ACTION;
6895
6896         RemoveField(x, y);
6897         Feld[x + dx][y + dy] = element;
6898
6899 #if 1
6900         if (game.engine_version < RELEASE_IDENT(2,2,0,7))
6901           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
6902 #else
6903         player->push_delay_value = 2 + RND(8);
6904 #endif
6905
6906         DrawLevelField(x + dx, y + dy);
6907         PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
6908
6909         break;
6910       }
6911
6912       return MF_NO_ACTION;
6913   }
6914
6915   player->push_delay = 0;
6916
6917   if (Feld[x][y] != element)            /* really digged/collected something */
6918     player->is_collecting = !player->is_digging;
6919
6920   return MF_MOVING;
6921 }
6922
6923 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
6924 {
6925   int jx = player->jx, jy = player->jy;
6926   int x = jx + dx, y = jy + dy;
6927
6928   if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0))
6929     return FALSE;
6930
6931   if (!player->active || !IN_LEV_FIELD(x, y))
6932     return FALSE;
6933
6934   if (dx && dy)
6935     return FALSE;
6936
6937   if (!dx && !dy)
6938   {
6939     if (player->MovPos == 0)
6940       player->Pushing = FALSE;
6941
6942     player->snapped = FALSE;
6943
6944     if (player->MovPos == 0)
6945     {
6946       player->is_digging = FALSE;
6947       player->is_collecting = FALSE;
6948     }
6949
6950     return FALSE;
6951   }
6952
6953   if (player->snapped)
6954     return FALSE;
6955
6956   player->MovDir = (dx < 0 ? MV_LEFT :
6957                     dx > 0 ? MV_RIGHT :
6958                     dy < 0 ? MV_UP :
6959                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
6960
6961   if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
6962     return FALSE;
6963
6964   player->snapped = TRUE;
6965   player->is_digging = FALSE;
6966   player->is_collecting = FALSE;
6967
6968   DrawLevelField(x, y);
6969   BackToFront();
6970
6971   return TRUE;
6972 }
6973
6974 boolean PlaceBomb(struct PlayerInfo *player)
6975 {
6976   int jx = player->jx, jy = player->jy;
6977   int element;
6978
6979   if (!player->active || player->MovPos)
6980     return FALSE;
6981
6982   element = Feld[jx][jy];
6983
6984   if ((player->dynamite == 0 && player->dynabombs_left == 0) ||
6985       IS_ACTIVE_BOMB(element) || element == EL_EXPLOSION)
6986     return FALSE;
6987
6988 #if 0
6989   if (element != EL_EMPTY)
6990     return FALSE;
6991 #endif
6992
6993   if (element != EL_EMPTY)
6994   {
6995 #if 0
6996     Store[jx][jy] = element;
6997 #else
6998     Back[jx][jy] = element;
6999 #endif
7000   }
7001
7002   MovDelay[jx][jy] = 96;
7003
7004   ResetGfxAnimation(jx, jy);
7005   ResetRandomAnimationValue(jx, jy);
7006
7007   if (player->dynamite)
7008   {
7009     Feld[jx][jy] = (player->use_disk_red_graphic ? EL_SP_DISK_RED_ACTIVE :
7010                     EL_DYNAMITE_ACTIVE);
7011     player->dynamite--;
7012
7013     DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(local_player->dynamite, 3),
7014              FONT_TEXT_2);
7015     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
7016     {
7017 #if 1
7018       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
7019 #else
7020       if (game.emulation == EMU_SUPAPLEX)
7021         DrawGraphic(SCREENX(jx), SCREENY(jy), IMG_SP_DISK_RED, 0);
7022       else
7023         DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), IMG_DYNAMITE_ACTIVE, 0);
7024 #endif
7025     }
7026
7027     PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
7028   }
7029   else
7030   {
7031     Feld[jx][jy] =
7032       EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1);
7033     player->dynabombs_left--;
7034
7035     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
7036       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
7037
7038     PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
7039   }
7040
7041   return TRUE;
7042 }
7043
7044 /* ------------------------------------------------------------------------- */
7045 /* game sound playing functions                                              */
7046 /* ------------------------------------------------------------------------- */
7047
7048 static int *loop_sound_frame = NULL;
7049 static int *loop_sound_volume = NULL;
7050
7051 void InitPlaySoundLevel()
7052 {
7053   int num_sounds = getSoundListSize();
7054
7055   if (loop_sound_frame != NULL)
7056     free(loop_sound_frame);
7057
7058   if (loop_sound_volume != NULL)
7059     free(loop_sound_volume);
7060
7061   loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
7062   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
7063 }
7064
7065 static void PlaySoundLevel(int x, int y, int nr)
7066 {
7067   int sx = SCREENX(x), sy = SCREENY(y);
7068   int volume, stereo_position;
7069   int max_distance = 8;
7070   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
7071
7072   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
7073       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
7074     return;
7075
7076   if (!IN_LEV_FIELD(x, y) ||
7077       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
7078       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
7079     return;
7080
7081   volume = SOUND_MAX_VOLUME;
7082
7083   if (!IN_SCR_FIELD(sx, sy))
7084   {
7085     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
7086     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
7087
7088     volume -= volume * (dx > dy ? dx : dy) / max_distance;
7089   }
7090
7091   stereo_position = (SOUND_MAX_LEFT +
7092                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
7093                      (SCR_FIELDX + 2 * max_distance));
7094
7095   if (IS_LOOP_SOUND(nr))
7096   {
7097     /* This assures that quieter loop sounds do not overwrite louder ones,
7098        while restarting sound volume comparison with each new game frame. */
7099
7100     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
7101       return;
7102
7103     loop_sound_volume[nr] = volume;
7104     loop_sound_frame[nr] = FrameCounter;
7105   }
7106
7107   PlaySoundExt(nr, volume, stereo_position, type);
7108 }
7109
7110 static void PlaySoundLevelNearest(int x, int y, int sound_action)
7111 {
7112   PlaySoundLevel(x < LEVELX(BX1) ? LEVELX(BX1) :
7113                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
7114                  y < LEVELY(BY1) ? LEVELY(BY1) :
7115                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
7116                  sound_action);
7117 }
7118
7119 static void PlaySoundLevelAction(int x, int y, int action)
7120 {
7121   PlaySoundLevelElementAction(x, y, Feld[x][y], action);
7122 }
7123
7124 static void PlaySoundLevelElementAction(int x, int y, int element, int action)
7125 {
7126   int sound_effect = element_info[element].sound[action];
7127
7128   if (sound_effect != SND_UNDEFINED)
7129     PlaySoundLevel(x, y, sound_effect);
7130 }
7131
7132 static void PlaySoundLevelActionIfLoop(int x, int y, int action)
7133 {
7134   int sound_effect = element_info[Feld[x][y]].sound[action];
7135
7136   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
7137     PlaySoundLevel(x, y, sound_effect);
7138 }
7139
7140 static void StopSoundLevelActionIfLoop(int x, int y, int action)
7141 {
7142   int sound_effect = element_info[Feld[x][y]].sound[action];
7143
7144   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
7145     StopSoundExt(sound_effect, SND_CTRL_STOP_SOUND);
7146 }
7147
7148 void RaiseScore(int value)
7149 {
7150   local_player->score += value;
7151   DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
7152 }
7153
7154 void RaiseScoreElement(int element)
7155 {
7156   switch(element)
7157   {
7158     case EL_EMERALD:
7159     case EL_BD_DIAMOND:
7160     case EL_EMERALD_YELLOW:
7161     case EL_EMERALD_RED:
7162     case EL_EMERALD_PURPLE:
7163     case EL_SP_INFOTRON:
7164       RaiseScore(level.score[SC_EMERALD]);
7165       break;
7166     case EL_DIAMOND:
7167       RaiseScore(level.score[SC_DIAMOND]);
7168       break;
7169     case EL_CRYSTAL:
7170       RaiseScore(level.score[SC_CRYSTAL]);
7171       break;
7172     case EL_PEARL:
7173       RaiseScore(level.score[SC_PEARL]);
7174       break;
7175     case EL_BUG:
7176     case EL_BD_BUTTERFLY:
7177     case EL_SP_ELECTRON:
7178       RaiseScore(level.score[SC_BUG]);
7179       break;
7180     case EL_SPACESHIP:
7181     case EL_BD_FIREFLY:
7182     case EL_SP_SNIKSNAK:
7183       RaiseScore(level.score[SC_SPACESHIP]);
7184       break;
7185     case EL_YAMYAM:
7186     case EL_DARK_YAMYAM:
7187       RaiseScore(level.score[SC_YAMYAM]);
7188       break;
7189     case EL_ROBOT:
7190       RaiseScore(level.score[SC_ROBOT]);
7191       break;
7192     case EL_PACMAN:
7193       RaiseScore(level.score[SC_PACMAN]);
7194       break;
7195     case EL_NUT:
7196       RaiseScore(level.score[SC_NUT]);
7197       break;
7198     case EL_DYNAMITE:
7199     case EL_DYNABOMB_INCREASE_NUMBER:
7200     case EL_DYNABOMB_INCREASE_SIZE:
7201     case EL_DYNABOMB_INCREASE_POWER:
7202       RaiseScore(level.score[SC_DYNAMITE]);
7203       break;
7204     case EL_SHIELD_NORMAL:
7205     case EL_SHIELD_DEADLY:
7206       RaiseScore(level.score[SC_SHIELD]);
7207       break;
7208     case EL_EXTRA_TIME:
7209       RaiseScore(level.score[SC_TIME_BONUS]);
7210       break;
7211     case EL_KEY_1:
7212     case EL_KEY_2:
7213     case EL_KEY_3:
7214     case EL_KEY_4:
7215       RaiseScore(level.score[SC_KEY]);
7216       break;
7217     default:
7218       break;
7219   }
7220 }
7221
7222 void RequestQuitGame(boolean ask_if_really_quit)
7223 {
7224   if (AllPlayersGone ||
7225       !ask_if_really_quit ||
7226       level_editor_test_game ||
7227       Request("Do you really want to quit the game ?",
7228               REQ_ASK | REQ_STAY_CLOSED))
7229   {
7230 #if defined(PLATFORM_UNIX)
7231     if (options.network)
7232       SendToServer_StopPlaying();
7233     else
7234 #endif
7235     {
7236       game_status = GAME_MODE_MAIN;
7237       DrawMainMenu();
7238     }
7239   }
7240   else
7241   {
7242     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
7243   }
7244 }
7245
7246
7247 /* ---------- new game button stuff ---------------------------------------- */
7248
7249 /* graphic position values for game buttons */
7250 #define GAME_BUTTON_XSIZE       30
7251 #define GAME_BUTTON_YSIZE       30
7252 #define GAME_BUTTON_XPOS        5
7253 #define GAME_BUTTON_YPOS        215
7254 #define SOUND_BUTTON_XPOS       5
7255 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
7256
7257 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
7258 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
7259 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
7260 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
7261 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
7262 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
7263
7264 static struct
7265 {
7266   int x, y;
7267   int gadget_id;
7268   char *infotext;
7269 } gamebutton_info[NUM_GAME_BUTTONS] =
7270 {
7271   {
7272     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
7273     GAME_CTRL_ID_STOP,
7274     "stop game"
7275   },
7276   {
7277     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
7278     GAME_CTRL_ID_PAUSE,
7279     "pause game"
7280   },
7281   {
7282     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
7283     GAME_CTRL_ID_PLAY,
7284     "play game"
7285   },
7286   {
7287     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
7288     SOUND_CTRL_ID_MUSIC,
7289     "background music on/off"
7290   },
7291   {
7292     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
7293     SOUND_CTRL_ID_LOOPS,
7294     "sound loops on/off"
7295   },
7296   {
7297     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
7298     SOUND_CTRL_ID_SIMPLE,
7299     "normal sounds on/off"
7300   }
7301 };
7302
7303 void CreateGameButtons()
7304 {
7305   int i;
7306
7307   for (i=0; i<NUM_GAME_BUTTONS; i++)
7308   {
7309     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
7310     struct GadgetInfo *gi;
7311     int button_type;
7312     boolean checked;
7313     unsigned long event_mask;
7314     int gd_xoffset, gd_yoffset;
7315     int gd_x1, gd_x2, gd_y1, gd_y2;
7316     int id = i;
7317
7318     gd_xoffset = gamebutton_info[i].x;
7319     gd_yoffset = gamebutton_info[i].y;
7320     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
7321     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
7322
7323     if (id == GAME_CTRL_ID_STOP ||
7324         id == GAME_CTRL_ID_PAUSE ||
7325         id == GAME_CTRL_ID_PLAY)
7326     {
7327       button_type = GD_TYPE_NORMAL_BUTTON;
7328       checked = FALSE;
7329       event_mask = GD_EVENT_RELEASED;
7330       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
7331       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
7332     }
7333     else
7334     {
7335       button_type = GD_TYPE_CHECK_BUTTON;
7336       checked =
7337         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
7338          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
7339          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
7340       event_mask = GD_EVENT_PRESSED;
7341       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
7342       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
7343     }
7344
7345     gi = CreateGadget(GDI_CUSTOM_ID, id,
7346                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
7347                       GDI_X, DX + gd_xoffset,
7348                       GDI_Y, DY + gd_yoffset,
7349                       GDI_WIDTH, GAME_BUTTON_XSIZE,
7350                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
7351                       GDI_TYPE, button_type,
7352                       GDI_STATE, GD_BUTTON_UNPRESSED,
7353                       GDI_CHECKED, checked,
7354                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
7355                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
7356                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
7357                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
7358                       GDI_EVENT_MASK, event_mask,
7359                       GDI_CALLBACK_ACTION, HandleGameButtons,
7360                       GDI_END);
7361
7362     if (gi == NULL)
7363       Error(ERR_EXIT, "cannot create gadget");
7364
7365     game_gadget[id] = gi;
7366   }
7367 }
7368
7369 void FreeGameButtons()
7370 {
7371   int i;
7372
7373   for (i=0; i<NUM_GAME_BUTTONS; i++)
7374     FreeGadget(game_gadget[i]);
7375 }
7376
7377 static void MapGameButtons()
7378 {
7379   int i;
7380
7381   for (i=0; i<NUM_GAME_BUTTONS; i++)
7382     MapGadget(game_gadget[i]);
7383 }
7384
7385 void UnmapGameButtons()
7386 {
7387   int i;
7388
7389   for (i=0; i<NUM_GAME_BUTTONS; i++)
7390     UnmapGadget(game_gadget[i]);
7391 }
7392
7393 static void HandleGameButtons(struct GadgetInfo *gi)
7394 {
7395   int id = gi->custom_id;
7396
7397   if (game_status != GAME_MODE_PLAYING)
7398     return;
7399
7400   switch (id)
7401   {
7402     case GAME_CTRL_ID_STOP:
7403       RequestQuitGame(TRUE);
7404       break;
7405
7406     case GAME_CTRL_ID_PAUSE:
7407       if (options.network)
7408       {
7409 #if defined(PLATFORM_UNIX)
7410         if (tape.pausing)
7411           SendToServer_ContinuePlaying();
7412         else
7413           SendToServer_PausePlaying();
7414 #endif
7415       }
7416       else
7417         TapeTogglePause(TAPE_TOGGLE_MANUAL);
7418       break;
7419
7420     case GAME_CTRL_ID_PLAY:
7421       if (tape.pausing)
7422       {
7423 #if defined(PLATFORM_UNIX)
7424         if (options.network)
7425           SendToServer_ContinuePlaying();
7426         else
7427 #endif
7428         {
7429           tape.pausing = FALSE;
7430           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
7431         }
7432       }
7433       break;
7434
7435     case SOUND_CTRL_ID_MUSIC:
7436       if (setup.sound_music)
7437       { 
7438         setup.sound_music = FALSE;
7439         FadeMusic();
7440       }
7441       else if (audio.music_available)
7442       { 
7443         setup.sound = setup.sound_music = TRUE;
7444         PlayMusic(level_nr);
7445       }
7446       break;
7447
7448     case SOUND_CTRL_ID_LOOPS:
7449       if (setup.sound_loops)
7450         setup.sound_loops = FALSE;
7451       else if (audio.loops_available)
7452         setup.sound = setup.sound_loops = TRUE;
7453       break;
7454
7455     case SOUND_CTRL_ID_SIMPLE:
7456       if (setup.sound_simple)
7457         setup.sound_simple = FALSE;
7458       else if (audio.sound_available)
7459         setup.sound = setup.sound_simple = TRUE;
7460       break;
7461
7462     default:
7463       break;
7464   }
7465 }