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