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