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