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