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