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