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