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