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