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