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