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