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