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