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