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