rnd-20031013-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR      FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE     FALSE
29
30 /* for DigField() */
31 #define DF_NO_PUSH              0
32 #define DF_DIG                  1
33 #define DF_SNAP                 2
34
35 /* for 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 = 2;
931       element_info[i].push_delay_random = 8;
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, -1);
4121
4122         for (i=1; i<=3; i++)
4123         {
4124           int xx = x + i*dx, yy = y + i*dy;
4125           int sx = SCREENX(xx), sy = SCREENY(yy);
4126           int flame_graphic = graphic + (i - 1);
4127
4128           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4129             break;
4130
4131           if (MovDelay[x][y])
4132           {
4133             int flamed = MovingOrBlocked2Element(xx, yy);
4134
4135             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4136               Bang(xx, yy);
4137             else
4138               RemoveMovingField(xx, yy);
4139
4140             Feld[xx][yy] = EL_FLAMES;
4141             if (IN_SCR_FIELD(sx, sy))
4142               DrawGraphic(sx, sy, flame_graphic, frame);
4143           }
4144           else
4145           {
4146             if (Feld[xx][yy] == EL_FLAMES)
4147               Feld[xx][yy] = EL_EMPTY;
4148             DrawLevelField(xx, yy);
4149           }
4150         }
4151       }
4152
4153       if (MovDelay[x][y])       /* element still has to wait some time */
4154       {
4155         PlaySoundLevelAction(x, y, ACTION_WAITING);
4156
4157         return;
4158       }
4159
4160       /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4161          for all other elements GfxAction will be set by InitMovingField() */
4162       if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4163         GfxAction[x][y] = ACTION_MOVING;
4164     }
4165
4166     /* now make next step */
4167
4168     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4169
4170     if (DONT_COLLIDE_WITH(element) &&
4171         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4172         !PLAYER_PROTECTED(newx, newy))
4173     {
4174 #if 1
4175       TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4176       return;
4177 #else
4178       /* player killed by element which is deadly when colliding with */
4179       MovDir[x][y] = 0;
4180       KillHero(PLAYERINFO(newx, newy));
4181       return;
4182 #endif
4183
4184     }
4185     else if ((element == EL_PENGUIN ||
4186               element == EL_ROBOT ||
4187               element == EL_SATELLITE ||
4188               element == EL_BALLOON ||
4189               IS_CUSTOM_ELEMENT(element)) &&
4190              IN_LEV_FIELD(newx, newy) &&
4191              MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4192     {
4193       SplashAcid(x, y);
4194       Store[x][y] = EL_ACID;
4195     }
4196     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4197     {
4198       if (Feld[newx][newy] == EL_EXIT_OPEN)
4199       {
4200         Feld[x][y] = EL_EMPTY;
4201         DrawLevelField(x, y);
4202
4203         PlaySoundLevel(newx, newy, SND_PENGUIN_PASSING);
4204         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4205           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4206
4207         local_player->friends_still_needed--;
4208         if (!local_player->friends_still_needed &&
4209             !local_player->GameOver && AllPlayersGone)
4210           local_player->LevelSolved = local_player->GameOver = TRUE;
4211
4212         return;
4213       }
4214       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4215       {
4216         if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4217           DrawLevelField(newx, newy);
4218         else
4219           MovDir[x][y] = MV_NO_MOVING;
4220       }
4221       else if (!IS_FREE(newx, newy))
4222       {
4223         GfxAction[x][y] = ACTION_WAITING;
4224
4225         if (IS_PLAYER(x, y))
4226           DrawPlayerField(x, y);
4227         else
4228           DrawLevelField(x, y);
4229         return;
4230       }
4231     }
4232     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4233     {
4234       if (IS_FOOD_PIG(Feld[newx][newy]))
4235       {
4236         if (IS_MOVING(newx, newy))
4237           RemoveMovingField(newx, newy);
4238         else
4239         {
4240           Feld[newx][newy] = EL_EMPTY;
4241           DrawLevelField(newx, newy);
4242         }
4243
4244         PlaySoundLevel(x, y, SND_PIG_DIGGING);
4245       }
4246       else if (!IS_FREE(newx, newy))
4247       {
4248         if (IS_PLAYER(x, y))
4249           DrawPlayerField(x, y);
4250         else
4251           DrawLevelField(x, y);
4252         return;
4253       }
4254     }
4255     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4256     {
4257       if (!IS_FREE(newx, newy))
4258       {
4259         if (IS_PLAYER(x, y))
4260           DrawPlayerField(x, y);
4261         else
4262           DrawLevelField(x, y);
4263         return;
4264       }
4265       else
4266       {
4267         boolean wanna_flame = !RND(10);
4268         int dx = newx - x, dy = newy - y;
4269         int newx1 = newx+1*dx, newy1 = newy+1*dy;
4270         int newx2 = newx+2*dx, newy2 = newy+2*dy;
4271         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4272                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4273         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4274                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4275
4276         if ((wanna_flame ||
4277              IS_CLASSIC_ENEMY(element1) ||
4278              IS_CLASSIC_ENEMY(element2)) &&
4279             element1 != EL_DRAGON && element2 != EL_DRAGON &&
4280             element1 != EL_FLAMES && element2 != EL_FLAMES)
4281         {
4282 #if 1
4283           ResetGfxAnimation(x, y);
4284           GfxAction[x][y] = ACTION_ATTACKING;
4285 #endif
4286
4287           if (IS_PLAYER(x, y))
4288             DrawPlayerField(x, y);
4289           else
4290             DrawLevelField(x, y);
4291
4292           PlaySoundLevel(x, y, SND_DRAGON_ATTACKING);
4293
4294           MovDelay[x][y] = 50;
4295           Feld[newx][newy] = EL_FLAMES;
4296           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4297             Feld[newx1][newy1] = EL_FLAMES;
4298           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4299             Feld[newx2][newy2] = EL_FLAMES;
4300
4301           return;
4302         }
4303       }
4304     }
4305     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4306              Feld[newx][newy] == EL_DIAMOND)
4307     {
4308       if (IS_MOVING(newx, newy))
4309         RemoveMovingField(newx, newy);
4310       else
4311       {
4312         Feld[newx][newy] = EL_EMPTY;
4313         DrawLevelField(newx, newy);
4314       }
4315
4316       PlaySoundLevel(x, y, SND_YAMYAM_DIGGING);
4317     }
4318     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4319              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4320     {
4321       if (AmoebaNr[newx][newy])
4322       {
4323         AmoebaCnt2[AmoebaNr[newx][newy]]--;
4324         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4325             Feld[newx][newy] == EL_BD_AMOEBA)
4326           AmoebaCnt[AmoebaNr[newx][newy]]--;
4327       }
4328
4329       if (IS_MOVING(newx, newy))
4330         RemoveMovingField(newx, newy);
4331       else
4332       {
4333         Feld[newx][newy] = EL_EMPTY;
4334         DrawLevelField(newx, newy);
4335       }
4336
4337       PlaySoundLevel(x, y, SND_DARK_YAMYAM_DIGGING);
4338     }
4339     else if ((element == EL_PACMAN || element == EL_MOLE)
4340              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4341     {
4342       if (AmoebaNr[newx][newy])
4343       {
4344         AmoebaCnt2[AmoebaNr[newx][newy]]--;
4345         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4346             Feld[newx][newy] == EL_BD_AMOEBA)
4347           AmoebaCnt[AmoebaNr[newx][newy]]--;
4348       }
4349
4350       if (element == EL_MOLE)
4351       {
4352         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4353         PlaySoundLevel(x, y, SND_MOLE_DIGGING);
4354
4355         ResetGfxAnimation(x, y);
4356         GfxAction[x][y] = ACTION_DIGGING;
4357         DrawLevelField(x, y);
4358
4359         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
4360         return;                         /* wait for shrinking amoeba */
4361       }
4362       else      /* element == EL_PACMAN */
4363       {
4364         Feld[newx][newy] = EL_EMPTY;
4365         DrawLevelField(newx, newy);
4366         PlaySoundLevel(x, y, SND_PACMAN_DIGGING);
4367       }
4368     }
4369     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4370              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4371               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4372     {
4373       /* wait for shrinking amoeba to completely disappear */
4374       return;
4375     }
4376     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4377     {
4378       /* object was running against a wall */
4379
4380       TurnRound(x, y);
4381
4382 #if 1
4383       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
4384         DrawLevelElementAnimation(x, y, element);
4385 #else
4386       if (element == EL_BUG ||
4387           element == EL_SPACESHIP ||
4388           element == EL_SP_SNIKSNAK)
4389         DrawLevelField(x, y);
4390       else if (element == EL_MOLE)
4391         DrawLevelField(x, y);
4392       else if (element == EL_BD_BUTTERFLY ||
4393                element == EL_BD_FIREFLY)
4394         DrawLevelElementAnimationIfNeeded(x, y, element);
4395       else if (element == EL_SATELLITE)
4396         DrawLevelElementAnimationIfNeeded(x, y, element);
4397       else if (element == EL_SP_ELECTRON)
4398         DrawLevelElementAnimationIfNeeded(x, y, element);
4399 #endif
4400
4401       if (DONT_TOUCH(element))
4402         TestIfBadThingTouchesHero(x, y);
4403
4404 #if 0
4405       PlaySoundLevelAction(x, y, ACTION_WAITING);
4406 #endif
4407
4408       return;
4409     }
4410
4411     InitMovingField(x, y, MovDir[x][y]);
4412
4413     PlaySoundLevelAction(x, y, ACTION_MOVING);
4414   }
4415
4416   if (MovDir[x][y])
4417     ContinueMoving(x, y);
4418 }
4419
4420 void ContinueMoving(int x, int y)
4421 {
4422   int element = Feld[x][y];
4423   int direction = MovDir[x][y];
4424   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4425   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4426   int newx = x + dx, newy = y + dy;
4427   int nextx = newx + dx, nexty = newy + dy;
4428   boolean pushed = Pushed[x][y];
4429
4430   MovPos[x][y] += getElementMoveStepsize(x, y);
4431
4432   if (pushed)           /* special case: moving object pushed by player */
4433     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4434
4435   if (ABS(MovPos[x][y]) < TILEX)
4436   {
4437     DrawLevelField(x, y);
4438
4439     return;     /* element is still moving */
4440   }
4441
4442   /* element reached destination field */
4443
4444   Feld[x][y] = EL_EMPTY;
4445   Feld[newx][newy] = element;
4446   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
4447
4448   if (element == EL_MOLE)
4449   {
4450     Feld[x][y] = EL_SAND;
4451
4452     DrawLevelFieldCrumbledSandNeighbours(x, y);
4453   }
4454   else if (element == EL_QUICKSAND_FILLING)
4455   {
4456     element = Feld[newx][newy] = get_next_element(element);
4457     Store[newx][newy] = Store[x][y];
4458   }
4459   else if (element == EL_QUICKSAND_EMPTYING)
4460   {
4461     Feld[x][y] = get_next_element(element);
4462     element = Feld[newx][newy] = Store[x][y];
4463   }
4464   else if (element == EL_MAGIC_WALL_FILLING)
4465   {
4466     element = Feld[newx][newy] = get_next_element(element);
4467     if (!game.magic_wall_active)
4468       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4469     Store[newx][newy] = Store[x][y];
4470   }
4471   else if (element == EL_MAGIC_WALL_EMPTYING)
4472   {
4473     Feld[x][y] = get_next_element(element);
4474     if (!game.magic_wall_active)
4475       Feld[x][y] = EL_MAGIC_WALL_DEAD;
4476     element = Feld[newx][newy] = Store[x][y];
4477   }
4478   else if (element == EL_BD_MAGIC_WALL_FILLING)
4479   {
4480     element = Feld[newx][newy] = get_next_element(element);
4481     if (!game.magic_wall_active)
4482       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
4483     Store[newx][newy] = Store[x][y];
4484   }
4485   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
4486   {
4487     Feld[x][y] = get_next_element(element);
4488     if (!game.magic_wall_active)
4489       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4490     element = Feld[newx][newy] = Store[x][y];
4491   }
4492   else if (element == EL_AMOEBA_DROPPING)
4493   {
4494     Feld[x][y] = get_next_element(element);
4495     element = Feld[newx][newy] = Store[x][y];
4496   }
4497   else if (element == EL_SOKOBAN_OBJECT)
4498   {
4499     if (Back[x][y])
4500       Feld[x][y] = Back[x][y];
4501
4502     if (Back[newx][newy])
4503       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
4504
4505     Back[x][y] = Back[newx][newy] = 0;
4506   }
4507   else if (Store[x][y] == EL_ACID)
4508   {
4509     element = Feld[newx][newy] = EL_ACID;
4510   }
4511
4512   Store[x][y] = 0;
4513   MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4514   MovDelay[newx][newy] = 0;
4515
4516   /* copy element change control values to new field */
4517   ChangeDelay[newx][newy] = ChangeDelay[x][y];
4518   ChangePage[newx][newy] = ChangePage[x][y];
4519   Changed[newx][newy] = Changed[x][y];
4520   ChangeEvent[newx][newy] = ChangeEvent[x][y];
4521
4522   ChangeDelay[x][y] = 0;
4523   ChangePage[x][y] = -1;
4524   Changed[x][y] = CE_BITMASK_DEFAULT;
4525   ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
4526
4527   /* copy animation control values to new field */
4528   GfxFrame[newx][newy]  = GfxFrame[x][y];
4529   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame */
4530   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
4531
4532   Pushed[x][y] = Pushed[newx][newy] = FALSE;
4533
4534   ResetGfxAnimation(x, y);      /* reset animation values for old field */
4535
4536 #if 0
4537   /* 2.1.1 (does not work correctly for spring) */
4538   if (!CAN_MOVE(element))
4539     MovDir[newx][newy] = 0;
4540 #else
4541
4542 #if 0
4543   /* (does not work for falling objects that slide horizontally) */
4544   if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
4545     MovDir[newx][newy] = 0;
4546 #else
4547   /*
4548   if (!CAN_MOVE(element) ||
4549       (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
4550     MovDir[newx][newy] = 0;
4551   */
4552
4553   if (!CAN_MOVE(element) ||
4554       (CAN_FALL(element) && direction == MV_DOWN))
4555     MovDir[newx][newy] = 0;
4556
4557 #endif
4558 #endif
4559
4560   DrawLevelField(x, y);
4561   DrawLevelField(newx, newy);
4562
4563   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
4564
4565   /* prevent pushed element from moving on in pushed direction */
4566   if (pushed && CAN_MOVE(element) &&
4567       element_info[element].move_pattern & MV_ANY_DIRECTION &&
4568       !(element_info[element].move_pattern & direction))
4569     TurnRound(newx, newy);
4570
4571   if (!pushed)  /* special case: moving object pushed by player */
4572   {
4573     WasJustMoving[newx][newy] = 3;
4574
4575     if (CAN_FALL(element) && direction == MV_DOWN)
4576       WasJustFalling[newx][newy] = 3;
4577   }
4578
4579   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
4580   {
4581     TestIfBadThingTouchesHero(newx, newy);
4582     TestIfBadThingTouchesFriend(newx, newy);
4583     TestIfBadThingTouchesOtherBadThing(newx, newy);
4584   }
4585   else if (element == EL_PENGUIN)
4586     TestIfFriendTouchesBadThing(newx, newy);
4587
4588   if (CAN_FALL(element) && direction == MV_DOWN &&
4589       (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
4590     Impact(x, newy);
4591
4592 #if 1
4593   TestIfElementTouchesCustomElement(x, y);              /* for empty space */
4594 #endif
4595
4596 #if 0
4597   if (ChangePage[newx][newy] != -1)                     /* delayed change */
4598     ChangeElement(newx, newy, ChangePage[newx][newy]);
4599 #endif
4600
4601   if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
4602     CheckElementSideChange(newx, newy, Feld[newx][newy], direction,
4603                            CE_COLLISION, -1);
4604
4605   TestIfPlayerTouchesCustomElement(newx, newy);
4606   TestIfElementTouchesCustomElement(newx, newy);
4607 }
4608
4609 int AmoebeNachbarNr(int ax, int ay)
4610 {
4611   int i;
4612   int element = Feld[ax][ay];
4613   int group_nr = 0;
4614   static int xy[4][2] =
4615   {
4616     { 0, -1 },
4617     { -1, 0 },
4618     { +1, 0 },
4619     { 0, +1 }
4620   };
4621
4622   for (i=0; i<4; i++)
4623   {
4624     int x = ax + xy[i][0];
4625     int y = ay + xy[i][1];
4626
4627     if (!IN_LEV_FIELD(x, y))
4628       continue;
4629
4630     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
4631       group_nr = AmoebaNr[x][y];
4632   }
4633
4634   return group_nr;
4635 }
4636
4637 void AmoebenVereinigen(int ax, int ay)
4638 {
4639   int i, x, y, xx, yy;
4640   int new_group_nr = AmoebaNr[ax][ay];
4641   static int xy[4][2] =
4642   {
4643     { 0, -1 },
4644     { -1, 0 },
4645     { +1, 0 },
4646     { 0, +1 }
4647   };
4648
4649   if (new_group_nr == 0)
4650     return;
4651
4652   for (i=0; i<4; i++)
4653   {
4654     x = ax + xy[i][0];
4655     y = ay + xy[i][1];
4656
4657     if (!IN_LEV_FIELD(x, y))
4658       continue;
4659
4660     if ((Feld[x][y] == EL_AMOEBA_FULL ||
4661          Feld[x][y] == EL_BD_AMOEBA ||
4662          Feld[x][y] == EL_AMOEBA_DEAD) &&
4663         AmoebaNr[x][y] != new_group_nr)
4664     {
4665       int old_group_nr = AmoebaNr[x][y];
4666
4667       if (old_group_nr == 0)
4668         return;
4669
4670       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
4671       AmoebaCnt[old_group_nr] = 0;
4672       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
4673       AmoebaCnt2[old_group_nr] = 0;
4674
4675       for (yy=0; yy<lev_fieldy; yy++)
4676       {
4677         for (xx=0; xx<lev_fieldx; xx++)
4678         {
4679           if (AmoebaNr[xx][yy] == old_group_nr)
4680             AmoebaNr[xx][yy] = new_group_nr;
4681         }
4682       }
4683     }
4684   }
4685 }
4686
4687 void AmoebeUmwandeln(int ax, int ay)
4688 {
4689   int i, x, y;
4690
4691   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
4692   {
4693     int group_nr = AmoebaNr[ax][ay];
4694
4695 #ifdef DEBUG
4696     if (group_nr == 0)
4697     {
4698       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
4699       printf("AmoebeUmwandeln(): This should never happen!\n");
4700       return;
4701     }
4702 #endif
4703
4704     for (y=0; y<lev_fieldy; y++)
4705     {
4706       for (x=0; x<lev_fieldx; x++)
4707       {
4708         if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
4709         {
4710           AmoebaNr[x][y] = 0;
4711           Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
4712         }
4713       }
4714     }
4715     PlaySoundLevel(ax, ay, (IS_GEM(level.amoeba_content) ?
4716                             SND_AMOEBA_TURNING_TO_GEM :
4717                             SND_AMOEBA_TURNING_TO_ROCK));
4718     Bang(ax, ay);
4719   }
4720   else
4721   {
4722     static int xy[4][2] =
4723     {
4724       { 0, -1 },
4725       { -1, 0 },
4726       { +1, 0 },
4727       { 0, +1 }
4728     };
4729
4730     for (i=0; i<4; i++)
4731     {
4732       x = ax + xy[i][0];
4733       y = ay + xy[i][1];
4734
4735       if (!IN_LEV_FIELD(x, y))
4736         continue;
4737
4738       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
4739       {
4740         PlaySoundLevel(x, y, (IS_GEM(level.amoeba_content) ?
4741                               SND_AMOEBA_TURNING_TO_GEM :
4742                               SND_AMOEBA_TURNING_TO_ROCK));
4743         Bang(x, y);
4744       }
4745     }
4746   }
4747 }
4748
4749 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
4750 {
4751   int x, y;
4752   int group_nr = AmoebaNr[ax][ay];
4753   boolean done = FALSE;
4754
4755 #ifdef DEBUG
4756   if (group_nr == 0)
4757   {
4758     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
4759     printf("AmoebeUmwandelnBD(): This should never happen!\n");
4760     return;
4761   }
4762 #endif
4763
4764   for (y=0; y<lev_fieldy; y++)
4765   {
4766     for (x=0; x<lev_fieldx; x++)
4767     {
4768       if (AmoebaNr[x][y] == group_nr &&
4769           (Feld[x][y] == EL_AMOEBA_DEAD ||
4770            Feld[x][y] == EL_BD_AMOEBA ||
4771            Feld[x][y] == EL_AMOEBA_GROWING))
4772       {
4773         AmoebaNr[x][y] = 0;
4774         Feld[x][y] = new_element;
4775         InitField(x, y, FALSE);
4776         DrawLevelField(x, y);
4777         done = TRUE;
4778       }
4779     }
4780   }
4781
4782   if (done)
4783     PlaySoundLevel(ax, ay, (new_element == EL_BD_ROCK ?
4784                             SND_BD_AMOEBA_TURNING_TO_ROCK :
4785                             SND_BD_AMOEBA_TURNING_TO_GEM));
4786 }
4787
4788 void AmoebeWaechst(int x, int y)
4789 {
4790   static unsigned long sound_delay = 0;
4791   static unsigned long sound_delay_value = 0;
4792
4793   if (!MovDelay[x][y])          /* start new growing cycle */
4794   {
4795     MovDelay[x][y] = 7;
4796
4797     if (DelayReached(&sound_delay, sound_delay_value))
4798     {
4799 #if 1
4800       PlaySoundLevelElementAction(x, y, Store[x][y], ACTION_GROWING);
4801 #else
4802       if (Store[x][y] == EL_BD_AMOEBA)
4803         PlaySoundLevel(x, y, SND_BD_AMOEBA_GROWING);
4804       else
4805         PlaySoundLevel(x, y, SND_AMOEBA_GROWING);
4806 #endif
4807       sound_delay_value = 30;
4808     }
4809   }
4810
4811   if (MovDelay[x][y])           /* wait some time before growing bigger */
4812   {
4813     MovDelay[x][y]--;
4814     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4815     {
4816       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
4817                                            6 - MovDelay[x][y]);
4818
4819       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
4820     }
4821
4822     if (!MovDelay[x][y])
4823     {
4824       Feld[x][y] = Store[x][y];
4825       Store[x][y] = 0;
4826       DrawLevelField(x, y);
4827     }
4828   }
4829 }
4830
4831 void AmoebaDisappearing(int x, int y)
4832 {
4833   static unsigned long sound_delay = 0;
4834   static unsigned long sound_delay_value = 0;
4835
4836   if (!MovDelay[x][y])          /* start new shrinking cycle */
4837   {
4838     MovDelay[x][y] = 7;
4839
4840     if (DelayReached(&sound_delay, sound_delay_value))
4841       sound_delay_value = 30;
4842   }
4843
4844   if (MovDelay[x][y])           /* wait some time before shrinking */
4845   {
4846     MovDelay[x][y]--;
4847     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4848     {
4849       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
4850                                            6 - MovDelay[x][y]);
4851
4852       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
4853     }
4854
4855     if (!MovDelay[x][y])
4856     {
4857       Feld[x][y] = EL_EMPTY;
4858       DrawLevelField(x, y);
4859
4860       /* don't let mole enter this field in this cycle;
4861          (give priority to objects falling to this field from above) */
4862       Stop[x][y] = TRUE;
4863     }
4864   }
4865 }
4866
4867 void AmoebeAbleger(int ax, int ay)
4868 {
4869   int i;
4870   int element = Feld[ax][ay];
4871   int graphic = el2img(element);
4872   int newax = ax, neway = ay;
4873   static int xy[4][2] =
4874   {
4875     { 0, -1 },
4876     { -1, 0 },
4877     { +1, 0 },
4878     { 0, +1 }
4879   };
4880
4881   if (!level.amoeba_speed)
4882   {
4883     Feld[ax][ay] = EL_AMOEBA_DEAD;
4884     DrawLevelField(ax, ay);
4885     return;
4886   }
4887
4888   if (IS_ANIMATED(graphic))
4889     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
4890
4891   if (!MovDelay[ax][ay])        /* start making new amoeba field */
4892     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
4893
4894   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
4895   {
4896     MovDelay[ax][ay]--;
4897     if (MovDelay[ax][ay])
4898       return;
4899   }
4900
4901   if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
4902   {
4903     int start = RND(4);
4904     int x = ax + xy[start][0];
4905     int y = ay + xy[start][1];
4906
4907     if (!IN_LEV_FIELD(x, y))
4908       return;
4909
4910     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
4911     if (IS_FREE(x, y) ||
4912         Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
4913     {
4914       newax = x;
4915       neway = y;
4916     }
4917
4918     if (newax == ax && neway == ay)
4919       return;
4920   }
4921   else                          /* normal or "filled" (BD style) amoeba */
4922   {
4923     int start = RND(4);
4924     boolean waiting_for_player = FALSE;
4925
4926     for (i=0; i<4; i++)
4927     {
4928       int j = (start + i) % 4;
4929       int x = ax + xy[j][0];
4930       int y = ay + xy[j][1];
4931
4932       if (!IN_LEV_FIELD(x, y))
4933         continue;
4934
4935       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
4936       if (IS_FREE(x, y) ||
4937           Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
4938       {
4939         newax = x;
4940         neway = y;
4941         break;
4942       }
4943       else if (IS_PLAYER(x, y))
4944         waiting_for_player = TRUE;
4945     }
4946
4947     if (newax == ax && neway == ay)             /* amoeba cannot grow */
4948     {
4949       if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
4950       {
4951         Feld[ax][ay] = EL_AMOEBA_DEAD;
4952         DrawLevelField(ax, ay);
4953         AmoebaCnt[AmoebaNr[ax][ay]]--;
4954
4955         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
4956         {
4957           if (element == EL_AMOEBA_FULL)
4958             AmoebeUmwandeln(ax, ay);
4959           else if (element == EL_BD_AMOEBA)
4960             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
4961         }
4962       }
4963       return;
4964     }
4965     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
4966     {
4967       /* amoeba gets larger by growing in some direction */
4968
4969       int new_group_nr = AmoebaNr[ax][ay];
4970
4971 #ifdef DEBUG
4972   if (new_group_nr == 0)
4973   {
4974     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
4975     printf("AmoebeAbleger(): This should never happen!\n");
4976     return;
4977   }
4978 #endif
4979
4980       AmoebaNr[newax][neway] = new_group_nr;
4981       AmoebaCnt[new_group_nr]++;
4982       AmoebaCnt2[new_group_nr]++;
4983
4984       /* if amoeba touches other amoeba(s) after growing, unify them */
4985       AmoebenVereinigen(newax, neway);
4986
4987       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
4988       {
4989         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
4990         return;
4991       }
4992     }
4993   }
4994
4995   if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
4996       (neway == lev_fieldy - 1 && newax != ax))
4997   {
4998     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
4999     Store[newax][neway] = element;
5000   }
5001   else if (neway == ay)
5002   {
5003     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
5004 #if 1
5005     PlaySoundLevelAction(newax, neway, ACTION_GROWING);
5006 #else
5007     PlaySoundLevel(newax, neway, SND_AMOEBA_GROWING);
5008 #endif
5009   }
5010   else
5011   {
5012     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
5013     Feld[ax][ay] = EL_AMOEBA_DROPPING;
5014     Store[ax][ay] = EL_AMOEBA_DROP;
5015     ContinueMoving(ax, ay);
5016     return;
5017   }
5018
5019   DrawLevelField(newax, neway);
5020 }
5021
5022 void Life(int ax, int ay)
5023 {
5024   int x1, y1, x2, y2;
5025   static int life[4] = { 2, 3, 3, 3 };  /* parameters for "game of life" */
5026   int life_time = 40;
5027   int element = Feld[ax][ay];
5028   int graphic = el2img(element);
5029   boolean changed = FALSE;
5030
5031   if (IS_ANIMATED(graphic))
5032     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5033
5034   if (Stop[ax][ay])
5035     return;
5036
5037   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
5038     MovDelay[ax][ay] = life_time;
5039
5040   if (MovDelay[ax][ay])         /* wait some time before next cycle */
5041   {
5042     MovDelay[ax][ay]--;
5043     if (MovDelay[ax][ay])
5044       return;
5045   }
5046
5047   for (y1=-1; y1<2; y1++) for(x1=-1; x1<2; x1++)
5048   {
5049     int xx = ax+x1, yy = ay+y1;
5050     int nachbarn = 0;
5051
5052     if (!IN_LEV_FIELD(xx, yy))
5053       continue;
5054
5055     for (y2=-1; y2<2; y2++) for (x2=-1; x2<2; x2++)
5056     {
5057       int x = xx+x2, y = yy+y2;
5058
5059       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5060         continue;
5061
5062       if (((Feld[x][y] == element ||
5063             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5064            !Stop[x][y]) ||
5065           (IS_FREE(x, y) && Stop[x][y]))
5066         nachbarn++;
5067     }
5068
5069     if (xx == ax && yy == ay)           /* field in the middle */
5070     {
5071       if (nachbarn < life[0] || nachbarn > life[1])
5072       {
5073         Feld[xx][yy] = EL_EMPTY;
5074         if (!Stop[xx][yy])
5075           DrawLevelField(xx, yy);
5076         Stop[xx][yy] = TRUE;
5077         changed = TRUE;
5078       }
5079     }
5080     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5081     else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5082     {                                   /* free border field */
5083       if (nachbarn >= life[2] && nachbarn <= life[3])
5084       {
5085         Feld[xx][yy] = element;
5086         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5087         if (!Stop[xx][yy])
5088           DrawLevelField(xx, yy);
5089         Stop[xx][yy] = TRUE;
5090         changed = TRUE;
5091       }
5092     }
5093   }
5094
5095   if (changed)
5096     PlaySoundLevel(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5097                    SND_GAME_OF_LIFE_GROWING);
5098 }
5099
5100 static void InitRobotWheel(int x, int y)
5101 {
5102   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5103 }
5104
5105 static void RunRobotWheel(int x, int y)
5106 {
5107   PlaySoundLevel(x, y, SND_ROBOT_WHEEL_ACTIVE);
5108 }
5109
5110 static void StopRobotWheel(int x, int y)
5111 {
5112   if (ZX == x && ZY == y)
5113     ZX = ZY = -1;
5114 }
5115
5116 static void InitTimegateWheel(int x, int y)
5117 {
5118   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5119 }
5120
5121 static void RunTimegateWheel(int x, int y)
5122 {
5123   PlaySoundLevel(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5124 }
5125
5126 void CheckExit(int x, int y)
5127 {
5128   if (local_player->gems_still_needed > 0 ||
5129       local_player->sokobanfields_still_needed > 0 ||
5130       local_player->lights_still_needed > 0)
5131   {
5132     int element = Feld[x][y];
5133     int graphic = el2img(element);
5134
5135     if (IS_ANIMATED(graphic))
5136       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5137
5138     return;
5139   }
5140
5141   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
5142     return;
5143
5144   Feld[x][y] = EL_EXIT_OPENING;
5145
5146   PlaySoundLevelNearest(x, y, SND_CLASS_EXIT_OPENING);
5147 }
5148
5149 void CheckExitSP(int x, int y)
5150 {
5151   if (local_player->gems_still_needed > 0)
5152   {
5153     int element = Feld[x][y];
5154     int graphic = el2img(element);
5155
5156     if (IS_ANIMATED(graphic))
5157       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5158
5159     return;
5160   }
5161
5162   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
5163     return;
5164
5165   Feld[x][y] = EL_SP_EXIT_OPENING;
5166
5167   PlaySoundLevelNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5168 }
5169
5170 static void CloseAllOpenTimegates()
5171 {
5172   int x, y;
5173
5174   for (y=0; y<lev_fieldy; y++)
5175   {
5176     for (x=0; x<lev_fieldx; x++)
5177     {
5178       int element = Feld[x][y];
5179
5180       if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5181       {
5182         Feld[x][y] = EL_TIMEGATE_CLOSING;
5183 #if 1
5184         PlaySoundLevelAction(x, y, ACTION_CLOSING);
5185 #else
5186         PlaySoundLevel(x, y, SND_TIMEGATE_CLOSING);
5187 #endif
5188       }
5189     }
5190   }
5191 }
5192
5193 void EdelsteinFunkeln(int x, int y)
5194 {
5195   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5196     return;
5197
5198   if (Feld[x][y] == EL_BD_DIAMOND)
5199     return;
5200
5201   if (MovDelay[x][y] == 0)      /* next animation frame */
5202     MovDelay[x][y] = 11 * !SimpleRND(500);
5203
5204   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
5205   {
5206     MovDelay[x][y]--;
5207
5208     if (setup.direct_draw && MovDelay[x][y])
5209       SetDrawtoField(DRAW_BUFFERED);
5210
5211     DrawLevelElementAnimation(x, y, Feld[x][y]);
5212
5213     if (MovDelay[x][y] != 0)
5214     {
5215       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5216                                            10 - MovDelay[x][y]);
5217
5218       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5219
5220       if (setup.direct_draw)
5221       {
5222         int dest_x, dest_y;
5223
5224         dest_x = FX + SCREENX(x) * TILEX;
5225         dest_y = FY + SCREENY(y) * TILEY;
5226
5227         BlitBitmap(drawto_field, window,
5228                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5229         SetDrawtoField(DRAW_DIRECT);
5230       }
5231     }
5232   }
5233 }
5234
5235 void MauerWaechst(int x, int y)
5236 {
5237   int delay = 6;
5238
5239   if (!MovDelay[x][y])          /* next animation frame */
5240     MovDelay[x][y] = 3 * delay;
5241
5242   if (MovDelay[x][y])           /* wait some time before next frame */
5243   {
5244     MovDelay[x][y]--;
5245
5246     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5247     {
5248       int graphic = el_dir2img(Feld[x][y], MovDir[x][y]);
5249       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5250
5251       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5252     }
5253
5254     if (!MovDelay[x][y])
5255     {
5256       if (MovDir[x][y] == MV_LEFT)
5257       {
5258         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5259           DrawLevelField(x - 1, y);
5260       }
5261       else if (MovDir[x][y] == MV_RIGHT)
5262       {
5263         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5264           DrawLevelField(x + 1, y);
5265       }
5266       else if (MovDir[x][y] == MV_UP)
5267       {
5268         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5269           DrawLevelField(x, y - 1);
5270       }
5271       else
5272       {
5273         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5274           DrawLevelField(x, y + 1);
5275       }
5276
5277       Feld[x][y] = Store[x][y];
5278       Store[x][y] = 0;
5279       MovDir[x][y] = MV_NO_MOVING;
5280       DrawLevelField(x, y);
5281     }
5282   }
5283 }
5284
5285 void MauerAbleger(int ax, int ay)
5286 {
5287   int element = Feld[ax][ay];
5288   int graphic = el2img(element);
5289   boolean oben_frei = FALSE, unten_frei = FALSE;
5290   boolean links_frei = FALSE, rechts_frei = FALSE;
5291   boolean oben_massiv = FALSE, unten_massiv = FALSE;
5292   boolean links_massiv = FALSE, rechts_massiv = FALSE;
5293   boolean new_wall = FALSE;
5294
5295   if (IS_ANIMATED(graphic))
5296     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5297
5298   if (!MovDelay[ax][ay])        /* start building new wall */
5299     MovDelay[ax][ay] = 6;
5300
5301   if (MovDelay[ax][ay])         /* wait some time before building new wall */
5302   {
5303     MovDelay[ax][ay]--;
5304     if (MovDelay[ax][ay])
5305       return;
5306   }
5307
5308   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5309     oben_frei = TRUE;
5310   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5311     unten_frei = TRUE;
5312   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5313     links_frei = TRUE;
5314   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5315     rechts_frei = TRUE;
5316
5317   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5318       element == EL_EXPANDABLE_WALL_ANY)
5319   {
5320     if (oben_frei)
5321     {
5322       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5323       Store[ax][ay-1] = element;
5324       MovDir[ax][ay-1] = MV_UP;
5325       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5326         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5327                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5328       new_wall = TRUE;
5329     }
5330     if (unten_frei)
5331     {
5332       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5333       Store[ax][ay+1] = element;
5334       MovDir[ax][ay+1] = MV_DOWN;
5335       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5336         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5337                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5338       new_wall = TRUE;
5339     }
5340   }
5341
5342   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5343       element == EL_EXPANDABLE_WALL_ANY ||
5344       element == EL_EXPANDABLE_WALL)
5345   {
5346     if (links_frei)
5347     {
5348       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5349       Store[ax-1][ay] = element;
5350       MovDir[ax-1][ay] = MV_LEFT;
5351       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5352         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5353                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5354       new_wall = TRUE;
5355     }
5356
5357     if (rechts_frei)
5358     {
5359       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5360       Store[ax+1][ay] = element;
5361       MovDir[ax+1][ay] = MV_RIGHT;
5362       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5363         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5364                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5365       new_wall = TRUE;
5366     }
5367   }
5368
5369   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5370     DrawLevelField(ax, ay);
5371
5372   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5373     oben_massiv = TRUE;
5374   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5375     unten_massiv = TRUE;
5376   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5377     links_massiv = TRUE;
5378   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5379     rechts_massiv = TRUE;
5380
5381   if (((oben_massiv && unten_massiv) ||
5382        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5383        element == EL_EXPANDABLE_WALL) &&
5384       ((links_massiv && rechts_massiv) ||
5385        element == EL_EXPANDABLE_WALL_VERTICAL))
5386     Feld[ax][ay] = EL_WALL;
5387
5388   if (new_wall)
5389 #if 1
5390     PlaySoundLevelAction(ax, ay, ACTION_GROWING);
5391 #else
5392     PlaySoundLevel(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5393 #endif
5394 }
5395
5396 void CheckForDragon(int x, int y)
5397 {
5398   int i, j;
5399   boolean dragon_found = FALSE;
5400   static int xy[4][2] =
5401   {
5402     { 0, -1 },
5403     { -1, 0 },
5404     { +1, 0 },
5405     { 0, +1 }
5406   };
5407
5408   for (i=0; i<4; i++)
5409   {
5410     for (j=0; j<4; j++)
5411     {
5412       int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5413
5414       if (IN_LEV_FIELD(xx, yy) &&
5415           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
5416       {
5417         if (Feld[xx][yy] == EL_DRAGON)
5418           dragon_found = TRUE;
5419       }
5420       else
5421         break;
5422     }
5423   }
5424
5425   if (!dragon_found)
5426   {
5427     for (i=0; i<4; i++)
5428     {
5429       for (j=0; j<3; j++)
5430       {
5431         int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5432   
5433         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
5434         {
5435           Feld[xx][yy] = EL_EMPTY;
5436           DrawLevelField(xx, yy);
5437         }
5438         else
5439           break;
5440       }
5441     }
5442   }
5443 }
5444
5445 static void InitBuggyBase(int x, int y)
5446 {
5447   int element = Feld[x][y];
5448   int activating_delay = FRAMES_PER_SECOND / 4;
5449
5450   ChangeDelay[x][y] =
5451     (element == EL_SP_BUGGY_BASE ?
5452      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
5453      element == EL_SP_BUGGY_BASE_ACTIVATING ?
5454      activating_delay :
5455      element == EL_SP_BUGGY_BASE_ACTIVE ?
5456      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
5457 }
5458
5459 static void WarnBuggyBase(int x, int y)
5460 {
5461   int i;
5462   static int xy[4][2] =
5463   {
5464     { 0, -1 },
5465     { -1, 0 },
5466     { +1, 0 },
5467     { 0, +1 }
5468   };
5469
5470   for (i=0; i<4; i++)
5471   {
5472     int xx = x + xy[i][0], yy = y + xy[i][1];
5473
5474     if (IS_PLAYER(xx, yy))
5475     {
5476       PlaySoundLevel(x, y, SND_SP_BUGGY_BASE_ACTIVE);
5477
5478       break;
5479     }
5480   }
5481 }
5482
5483 static void InitTrap(int x, int y)
5484 {
5485   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
5486 }
5487
5488 static void ActivateTrap(int x, int y)
5489 {
5490   PlaySoundLevel(x, y, SND_TRAP_ACTIVATING);
5491 }
5492
5493 static void ChangeActiveTrap(int x, int y)
5494 {
5495   int graphic = IMG_TRAP_ACTIVE;
5496
5497   /* if new animation frame was drawn, correct crumbled sand border */
5498   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
5499     DrawLevelFieldCrumbledSand(x, y);
5500 }
5501
5502 static void ChangeElementNowExt(int x, int y, int target_element)
5503 {
5504   /* check if element under player changes from accessible to unaccessible
5505      (needed for special case of dropping element which then changes) */
5506   if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
5507       IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
5508   {
5509     Bang(x, y);
5510     return;
5511   }
5512
5513   RemoveField(x, y);
5514   Feld[x][y] = target_element;
5515
5516   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
5517
5518   ResetGfxAnimation(x, y);
5519   ResetRandomAnimationValue(x, y);
5520
5521   InitField(x, y, FALSE);
5522   if (CAN_MOVE(Feld[x][y]))
5523     InitMovDir(x, y);
5524
5525   DrawLevelField(x, y);
5526
5527   if (GFX_CRUMBLED(Feld[x][y]))
5528     DrawLevelFieldCrumbledSandNeighbours(x, y);
5529
5530   TestIfBadThingTouchesHero(x, y);
5531   TestIfPlayerTouchesCustomElement(x, y);
5532   TestIfElementTouchesCustomElement(x, y);
5533
5534   if (ELEM_IS_PLAYER(target_element))
5535     RelocatePlayer(x, y, target_element);
5536 }
5537
5538 static boolean ChangeElementNow(int x, int y, int element, int page)
5539 {
5540   struct ElementChangeInfo *change = &element_info[element].change_page[page];
5541
5542   /* always use default change event to prevent running into a loop */
5543   if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
5544     ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
5545
5546   /* do not change already changed elements with same change event */
5547 #if 0
5548   if (Changed[x][y] & ChangeEvent[x][y])
5549     return FALSE;
5550 #else
5551   if (Changed[x][y])
5552     return FALSE;
5553 #endif
5554
5555   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
5556
5557   CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
5558
5559   if (change->explode)
5560   {
5561     Bang(x, y);
5562
5563     return TRUE;
5564   }
5565
5566   if (change->use_content)
5567   {
5568     boolean complete_change = TRUE;
5569     boolean can_change[3][3];
5570     int xx, yy;
5571
5572     for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5573     {
5574       boolean half_destructible;
5575       int ex = x + xx - 1;
5576       int ey = y + yy - 1;
5577       int e;
5578
5579       can_change[xx][yy] = TRUE;
5580
5581       if (ex == x && ey == y)   /* do not check changing element itself */
5582         continue;
5583
5584       if (change->content[xx][yy] == EL_EMPTY_SPACE)
5585       {
5586         can_change[xx][yy] = FALSE;     /* do not change empty borders */
5587
5588         continue;
5589       }
5590
5591       if (!IN_LEV_FIELD(ex, ey))
5592       {
5593         can_change[xx][yy] = FALSE;
5594         complete_change = FALSE;
5595
5596         continue;
5597       }
5598
5599       e = Feld[ex][ey];
5600
5601       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5602         e = MovingOrBlocked2Element(ex, ey);
5603
5604       half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
5605
5606       if ((change->power <= CP_NON_DESTRUCTIVE  && !IS_FREE(ex, ey)) ||
5607           (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
5608           (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
5609       {
5610         can_change[xx][yy] = FALSE;
5611         complete_change = FALSE;
5612       }
5613     }
5614
5615     if (!change->only_complete || complete_change)
5616     {
5617       boolean something_has_changed = FALSE;
5618
5619       if (change->only_complete && change->use_random_change &&
5620           RND(100) < change->random)
5621         return FALSE;
5622
5623       for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5624       {
5625         int ex = x + xx - 1;
5626         int ey = y + yy - 1;
5627
5628         if (can_change[xx][yy] && (!change->use_random_change ||
5629                                    RND(100) < change->random))
5630         {
5631           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5632             RemoveMovingField(ex, ey);
5633
5634           ChangeEvent[ex][ey] = ChangeEvent[x][y];
5635
5636           ChangeElementNowExt(ex, ey, change->content[xx][yy]);
5637
5638           something_has_changed = TRUE;
5639
5640           /* for symmetry reasons, freeze newly created border elements */
5641           if (ex != x || ey != y)
5642             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
5643         }
5644       }
5645
5646       if (something_has_changed)
5647         PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5648     }
5649   }
5650   else
5651   {
5652     ChangeElementNowExt(x, y, change->target_element);
5653
5654     PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5655   }
5656
5657   return TRUE;
5658 }
5659
5660 static void ChangeElement(int x, int y, int page)
5661 {
5662   int element = MovingOrBlocked2Element(x, y);
5663   struct ElementInfo *ei = &element_info[element];
5664   struct ElementChangeInfo *change = &ei->change_page[page];
5665
5666 #if 0
5667 #ifdef DEBUG
5668   if (!CAN_CHANGE(element))
5669   {
5670     printf("\n\n");
5671     printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
5672            x, y, element, element_info[element].token_name);
5673     printf("ChangeElement(): This should never happen!\n");
5674     printf("\n\n");
5675   }
5676 #endif
5677 #endif
5678
5679   if (ChangeDelay[x][y] == 0)           /* initialize element change */
5680   {
5681     ChangeDelay[x][y] = (    change->delay_fixed  * change->delay_frames +
5682                          RND(change->delay_random * change->delay_frames)) + 1;
5683
5684     ResetGfxAnimation(x, y);
5685     ResetRandomAnimationValue(x, y);
5686
5687     if (change->pre_change_function)
5688       change->pre_change_function(x, y);
5689   }
5690
5691   ChangeDelay[x][y]--;
5692
5693   if (ChangeDelay[x][y] != 0)           /* continue element change */
5694   {
5695     int graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
5696
5697     if (IS_ANIMATED(graphic))
5698       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5699
5700     if (change->change_function)
5701       change->change_function(x, y);
5702   }
5703   else                                  /* finish element change */
5704   {
5705     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
5706     {
5707       page = ChangePage[x][y];
5708       ChangePage[x][y] = -1;
5709     }
5710
5711     if (IS_MOVING(x, y))                /* never change a running system ;-) */
5712     {
5713       ChangeDelay[x][y] = 1;            /* try change after next move step */
5714       ChangePage[x][y] = page;          /* remember page to use for change */
5715
5716       return;
5717     }
5718
5719     if (ChangeElementNow(x, y, element, page))
5720     {
5721       if (change->post_change_function)
5722         change->post_change_function(x, y);
5723     }
5724   }
5725 }
5726
5727 static boolean CheckTriggeredElementSideChange(int lx, int ly,
5728                                                int trigger_element,
5729                                                int trigger_side,
5730                                                int trigger_event)
5731 {
5732   int i, j, x, y;
5733
5734   if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
5735     return FALSE;
5736
5737   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
5738   {
5739     int element = EL_CUSTOM_START + i;
5740
5741     boolean change_element = FALSE;
5742     int page = 0;
5743
5744     if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5745       continue;
5746
5747     for (j=0; j < element_info[element].num_change_pages; j++)
5748     {
5749       struct ElementChangeInfo *change = &element_info[element].change_page[j];
5750
5751       if (change->can_change &&
5752           change->sides & trigger_side &&
5753           change->trigger_element == trigger_element)
5754       {
5755         change_element = TRUE;
5756         page = j;
5757
5758         break;
5759       }
5760     }
5761
5762     if (!change_element)
5763       continue;
5764
5765     for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
5766     {
5767 #if 0
5768       if (x == lx && y == ly)   /* do not change trigger element itself */
5769         continue;
5770 #endif
5771
5772       if (Feld[x][y] == element)
5773       {
5774         ChangeDelay[x][y] = 1;
5775         ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5776         ChangeElement(x, y, page);
5777       }
5778     }
5779   }
5780
5781   return TRUE;
5782 }
5783
5784 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
5785                                            int trigger_event)
5786 {
5787   return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
5788                                          trigger_event);
5789 }
5790
5791 static boolean CheckElementSideChange(int x, int y, int element, int side,
5792                                       int trigger_event, int page)
5793 {
5794   if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5795     return FALSE;
5796
5797   if (Feld[x][y] == EL_BLOCKED)
5798   {
5799     Blocked2Moving(x, y, &x, &y);
5800     element = Feld[x][y];
5801   }
5802
5803   if (page < 0)
5804     page = element_info[element].event_page_nr[trigger_event];
5805
5806   if (!(element_info[element].change_page[page].sides & side))
5807     return FALSE;
5808
5809   ChangeDelay[x][y] = 1;
5810   ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5811   ChangeElement(x, y, page);
5812
5813   return TRUE;
5814 }
5815
5816 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
5817 {
5818   return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
5819 }
5820
5821 static void PlayerActions(struct PlayerInfo *player, byte player_action)
5822 {
5823   static byte stored_player_action[MAX_PLAYERS];
5824   static int num_stored_actions = 0;
5825   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
5826   int left      = player_action & JOY_LEFT;
5827   int right     = player_action & JOY_RIGHT;
5828   int up        = player_action & JOY_UP;
5829   int down      = player_action & JOY_DOWN;
5830   int button1   = player_action & JOY_BUTTON_1;
5831   int button2   = player_action & JOY_BUTTON_2;
5832   int dx        = (left ? -1    : right ? 1     : 0);
5833   int dy        = (up   ? -1    : down  ? 1     : 0);
5834
5835   stored_player_action[player->index_nr] = 0;
5836   num_stored_actions++;
5837
5838   if (!player->active || tape.pausing)
5839     return;
5840
5841   if (player_action)
5842   {
5843     if (button1)
5844       snapped = SnapField(player, dx, dy);
5845     else
5846     {
5847       if (button2)
5848         dropped = DropElement(player);
5849
5850       moved = MovePlayer(player, dx, dy);
5851     }
5852
5853     if (tape.single_step && tape.recording && !tape.pausing)
5854     {
5855       if (button1 || (dropped && !moved))
5856       {
5857         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
5858         SnapField(player, 0, 0);                /* stop snapping */
5859       }
5860     }
5861
5862     stored_player_action[player->index_nr] = player_action;
5863   }
5864   else
5865   {
5866     /* no actions for this player (no input at player's configured device) */
5867
5868     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
5869     SnapField(player, 0, 0);
5870     CheckGravityMovement(player);
5871
5872     if (player->MovPos == 0)
5873       InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
5874
5875     if (player->MovPos == 0)    /* needed for tape.playing */
5876       player->is_moving = FALSE;
5877   }
5878
5879   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
5880   {
5881     TapeRecordAction(stored_player_action);
5882     num_stored_actions = 0;
5883   }
5884 }
5885
5886 void GameActions()
5887 {
5888   static unsigned long action_delay = 0;
5889   unsigned long action_delay_value;
5890   int magic_wall_x = 0, magic_wall_y = 0;
5891   int i, x, y, element, graphic;
5892   byte *recorded_player_action;
5893   byte summarized_player_action = 0;
5894
5895   if (game_status != GAME_MODE_PLAYING)
5896     return;
5897
5898   action_delay_value =
5899     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
5900
5901   if (tape.playing && tape.index_search && !tape.pausing)
5902     action_delay_value = 0;
5903
5904   /* ---------- main game synchronization point ---------- */
5905
5906   WaitUntilDelayReached(&action_delay, action_delay_value);
5907
5908   if (network_playing && !network_player_action_received)
5909   {
5910     /*
5911 #ifdef DEBUG
5912     printf("DEBUG: try to get network player actions in time\n");
5913 #endif
5914     */
5915
5916 #if defined(PLATFORM_UNIX)
5917     /* last chance to get network player actions without main loop delay */
5918     HandleNetworking();
5919 #endif
5920
5921     if (game_status != GAME_MODE_PLAYING)
5922       return;
5923
5924     if (!network_player_action_received)
5925     {
5926       /*
5927 #ifdef DEBUG
5928       printf("DEBUG: failed to get network player actions in time\n");
5929 #endif
5930       */
5931       return;
5932     }
5933   }
5934
5935   if (tape.pausing)
5936     return;
5937
5938   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
5939
5940   for (i=0; i<MAX_PLAYERS; i++)
5941   {
5942     summarized_player_action |= stored_player[i].action;
5943
5944     if (!network_playing)
5945       stored_player[i].effective_action = stored_player[i].action;
5946   }
5947
5948 #if defined(PLATFORM_UNIX)
5949   if (network_playing)
5950     SendToServer_MovePlayer(summarized_player_action);
5951 #endif
5952
5953   if (!options.network && !setup.team_mode)
5954     local_player->effective_action = summarized_player_action;
5955
5956   for (i=0; i<MAX_PLAYERS; i++)
5957   {
5958     int actual_player_action = stored_player[i].effective_action;
5959
5960     if (stored_player[i].programmed_action)
5961       actual_player_action = stored_player[i].programmed_action;
5962
5963     if (recorded_player_action)
5964       actual_player_action = recorded_player_action[i];
5965
5966     PlayerActions(&stored_player[i], actual_player_action);
5967     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
5968   }
5969
5970   network_player_action_received = FALSE;
5971
5972   ScrollScreen(NULL, SCROLL_GO_ON);
5973
5974 #if 0
5975   FrameCounter++;
5976   TimeFrames++;
5977
5978   for (i=0; i<MAX_PLAYERS; i++)
5979     stored_player[i].Frame++;
5980 #endif
5981
5982 #if 1
5983   if (game.engine_version < RELEASE_IDENT(2,2,0,7))
5984   {
5985     for (i=0; i<MAX_PLAYERS; i++)
5986     {
5987       struct PlayerInfo *player = &stored_player[i];
5988       int x = player->jx;
5989       int y = player->jy;
5990
5991       if (player->active && player->is_pushing && player->is_moving &&
5992           IS_MOVING(x, y))
5993       {
5994         ContinueMoving(x, y);
5995
5996         /* continue moving after pushing (this is actually a bug) */
5997         if (!IS_MOVING(x, y))
5998         {
5999           Stop[x][y] = FALSE;
6000         }
6001       }
6002     }
6003   }
6004 #endif
6005
6006   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6007   {
6008     Changed[x][y] = CE_BITMASK_DEFAULT;
6009     ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6010
6011 #if DEBUG
6012     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6013     {
6014       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6015       printf("GameActions(): This should never happen!\n");
6016
6017       ChangePage[x][y] = -1;
6018     }
6019 #endif
6020
6021     Stop[x][y] = FALSE;
6022     if (WasJustMoving[x][y] > 0)
6023       WasJustMoving[x][y]--;
6024     if (WasJustFalling[x][y] > 0)
6025       WasJustFalling[x][y]--;
6026
6027     GfxFrame[x][y]++;
6028
6029 #if 1
6030     /* reset finished pushing action (not done in ContinueMoving() to allow
6031        continous pushing animation for elements with zero push delay) */
6032     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6033     {
6034       ResetGfxAnimation(x, y);
6035       DrawLevelField(x, y);
6036     }
6037 #endif
6038
6039 #if DEBUG
6040     if (IS_BLOCKED(x, y))
6041     {
6042       int oldx, oldy;
6043
6044       Blocked2Moving(x, y, &oldx, &oldy);
6045       if (!IS_MOVING(oldx, oldy))
6046       {
6047         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6048         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6049         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6050         printf("GameActions(): This should never happen!\n");
6051       }
6052     }
6053 #endif
6054   }
6055
6056   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6057   {
6058     element = Feld[x][y];
6059 #if 1
6060     graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
6061 #else
6062     graphic = el2img(element);
6063 #endif
6064
6065 #if 0
6066     if (element == -1)
6067     {
6068       printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6069
6070       element = graphic = 0;
6071     }
6072 #endif
6073
6074     if (graphic_info[graphic].anim_global_sync)
6075       GfxFrame[x][y] = FrameCounter;
6076
6077     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6078         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6079       ResetRandomAnimationValue(x, y);
6080
6081     SetRandomAnimationValue(x, y);
6082
6083 #if 1
6084     PlaySoundLevelActionIfLoop(x, y, GfxAction[x][y]);
6085 #endif
6086
6087     if (IS_INACTIVE(element))
6088     {
6089       if (IS_ANIMATED(graphic))
6090         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6091
6092       continue;
6093     }
6094
6095 #if 1
6096     /* this may take place after moving, so 'element' may have changed */
6097     if (IS_CHANGING(x, y))
6098     {
6099 #if 0
6100       ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6101                     element_info[element].event_page_nr[CE_DELAY]);
6102 #else
6103       ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6104 #endif
6105
6106       element = Feld[x][y];
6107       graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
6108     }
6109 #endif
6110
6111     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6112     {
6113       StartMoving(x, y);
6114
6115 #if 1
6116       graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
6117 #if 0
6118       if (element == EL_MOLE)
6119         printf("::: %d, %d, %d [%d]\n",
6120                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6121                GfxAction[x][y]);
6122 #endif
6123 #if 0
6124       if (element == EL_YAMYAM)
6125         printf("::: %d, %d, %d\n",
6126                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6127 #endif
6128 #endif
6129
6130       if (IS_ANIMATED(graphic) &&
6131           !IS_MOVING(x, y) &&
6132           !Stop[x][y])
6133       {
6134         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6135
6136 #if 0
6137         if (element == EL_MOLE)
6138           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6139 #endif
6140       }
6141
6142       if (IS_GEM(element) || element == EL_SP_INFOTRON)
6143         EdelsteinFunkeln(x, y);
6144     }
6145     else if ((element == EL_ACID ||
6146               element == EL_EXIT_OPEN ||
6147               element == EL_SP_EXIT_OPEN ||
6148               element == EL_SP_TERMINAL ||
6149               element == EL_SP_TERMINAL_ACTIVE ||
6150               element == EL_EXTRA_TIME ||
6151               element == EL_SHIELD_NORMAL ||
6152               element == EL_SHIELD_DEADLY) &&
6153              IS_ANIMATED(graphic))
6154       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6155     else if (IS_MOVING(x, y))
6156       ContinueMoving(x, y);
6157     else if (IS_ACTIVE_BOMB(element))
6158       CheckDynamite(x, y);
6159 #if 0
6160     else if (element == EL_EXPLOSION && !game.explosions_delayed)
6161       Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6162 #endif
6163     else if (element == EL_AMOEBA_GROWING)
6164       AmoebeWaechst(x, y);
6165     else if (element == EL_AMOEBA_SHRINKING)
6166       AmoebaDisappearing(x, y);
6167
6168 #if !USE_NEW_AMOEBA_CODE
6169     else if (IS_AMOEBALIVE(element))
6170       AmoebeAbleger(x, y);
6171 #endif
6172
6173     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6174       Life(x, y);
6175     else if (element == EL_EXIT_CLOSED)
6176       CheckExit(x, y);
6177     else if (element == EL_SP_EXIT_CLOSED)
6178       CheckExitSP(x, y);
6179     else if (element == EL_EXPANDABLE_WALL_GROWING)
6180       MauerWaechst(x, y);
6181     else if (element == EL_EXPANDABLE_WALL ||
6182              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6183              element == EL_EXPANDABLE_WALL_VERTICAL ||
6184              element == EL_EXPANDABLE_WALL_ANY)
6185       MauerAbleger(x, y);
6186     else if (element == EL_FLAMES)
6187       CheckForDragon(x, y);
6188 #if 0
6189     else if (IS_AUTO_CHANGING(element))
6190       ChangeElement(x, y);
6191 #endif
6192     else if (element == EL_EXPLOSION)
6193       ; /* drawing of correct explosion animation is handled separately */
6194     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6195       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6196
6197 #if 0
6198     /* this may take place after moving, so 'element' may have changed */
6199     if (IS_AUTO_CHANGING(Feld[x][y]))
6200       ChangeElement(x, y);
6201 #endif
6202
6203     if (IS_BELT_ACTIVE(element))
6204       PlaySoundLevelAction(x, y, ACTION_ACTIVE);
6205
6206     if (game.magic_wall_active)
6207     {
6208       int jx = local_player->jx, jy = local_player->jy;
6209
6210       /* play the element sound at the position nearest to the player */
6211       if ((element == EL_MAGIC_WALL_FULL ||
6212            element == EL_MAGIC_WALL_ACTIVE ||
6213            element == EL_MAGIC_WALL_EMPTYING ||
6214            element == EL_BD_MAGIC_WALL_FULL ||
6215            element == EL_BD_MAGIC_WALL_ACTIVE ||
6216            element == EL_BD_MAGIC_WALL_EMPTYING) &&
6217           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6218       {
6219         magic_wall_x = x;
6220         magic_wall_y = y;
6221       }
6222     }
6223   }
6224
6225 #if USE_NEW_AMOEBA_CODE
6226   /* new experimental amoeba growth stuff */
6227 #if 1
6228   if (!(FrameCounter % 8))
6229 #endif
6230   {
6231     static unsigned long random = 1684108901;
6232
6233     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
6234     {
6235 #if 0
6236       x = (random >> 10) % lev_fieldx;
6237       y = (random >> 20) % lev_fieldy;
6238 #else
6239       x = RND(lev_fieldx);
6240       y = RND(lev_fieldy);
6241 #endif
6242       element = Feld[x][y];
6243
6244       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6245       if (!IS_PLAYER(x,y) &&
6246           (element == EL_EMPTY ||
6247            element == EL_SAND ||
6248            element == EL_QUICKSAND_EMPTY ||
6249            element == EL_ACID_SPLASH_LEFT ||
6250            element == EL_ACID_SPLASH_RIGHT))
6251       {
6252         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
6253             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
6254             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
6255             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
6256           Feld[x][y] = EL_AMOEBA_DROP;
6257       }
6258
6259       random = random * 129 + 1;
6260     }
6261   }
6262 #endif
6263
6264 #if 0
6265   if (game.explosions_delayed)
6266 #endif
6267   {
6268     game.explosions_delayed = FALSE;
6269
6270     for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6271     {
6272       element = Feld[x][y];
6273
6274       if (ExplodeField[x][y])
6275         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
6276       else if (element == EL_EXPLOSION)
6277         Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6278
6279       ExplodeField[x][y] = EX_NO_EXPLOSION;
6280     }
6281
6282     game.explosions_delayed = TRUE;
6283   }
6284
6285   if (game.magic_wall_active)
6286   {
6287     if (!(game.magic_wall_time_left % 4))
6288     {
6289       int element = Feld[magic_wall_x][magic_wall_y];
6290
6291       if (element == EL_BD_MAGIC_WALL_FULL ||
6292           element == EL_BD_MAGIC_WALL_ACTIVE ||
6293           element == EL_BD_MAGIC_WALL_EMPTYING)
6294         PlaySoundLevel(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
6295       else
6296         PlaySoundLevel(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
6297     }
6298
6299     if (game.magic_wall_time_left > 0)
6300     {
6301       game.magic_wall_time_left--;
6302       if (!game.magic_wall_time_left)
6303       {
6304         for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6305         {
6306           element = Feld[x][y];
6307
6308           if (element == EL_MAGIC_WALL_ACTIVE ||
6309               element == EL_MAGIC_WALL_FULL)
6310           {
6311             Feld[x][y] = EL_MAGIC_WALL_DEAD;
6312             DrawLevelField(x, y);
6313           }
6314           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
6315                    element == EL_BD_MAGIC_WALL_FULL)
6316           {
6317             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6318             DrawLevelField(x, y);
6319           }
6320         }
6321
6322         game.magic_wall_active = FALSE;
6323       }
6324     }
6325   }
6326
6327   if (game.light_time_left > 0)
6328   {
6329     game.light_time_left--;
6330
6331     if (game.light_time_left == 0)
6332       RedrawAllLightSwitchesAndInvisibleElements();
6333   }
6334
6335   if (game.timegate_time_left > 0)
6336   {
6337     game.timegate_time_left--;
6338
6339     if (game.timegate_time_left == 0)
6340       CloseAllOpenTimegates();
6341   }
6342
6343   for (i=0; i<MAX_PLAYERS; i++)
6344   {
6345     struct PlayerInfo *player = &stored_player[i];
6346
6347     if (SHIELD_ON(player))
6348     {
6349       if (player->shield_deadly_time_left)
6350         PlaySoundLevel(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
6351       else if (player->shield_normal_time_left)
6352         PlaySoundLevel(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
6353     }
6354   }
6355
6356   if (TimeFrames >= FRAMES_PER_SECOND)
6357   {
6358     TimeFrames = 0;
6359     TimePlayed++;
6360
6361     for (i=0; i<MAX_PLAYERS; i++)
6362     {
6363       struct PlayerInfo *player = &stored_player[i];
6364
6365       if (SHIELD_ON(player))
6366       {
6367         player->shield_normal_time_left--;
6368
6369         if (player->shield_deadly_time_left > 0)
6370           player->shield_deadly_time_left--;
6371       }
6372     }
6373
6374     if (tape.recording || tape.playing)
6375       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
6376
6377     if (TimeLeft > 0)
6378     {
6379       TimeLeft--;
6380
6381       if (TimeLeft <= 10 && setup.time_limit)
6382         PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
6383
6384       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
6385
6386       if (!TimeLeft && setup.time_limit)
6387         for (i=0; i<MAX_PLAYERS; i++)
6388           KillHero(&stored_player[i]);
6389     }
6390     else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
6391       DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
6392   }
6393
6394   DrawAllPlayers();
6395
6396   if (options.debug)                    /* calculate frames per second */
6397   {
6398     static unsigned long fps_counter = 0;
6399     static int fps_frames = 0;
6400     unsigned long fps_delay_ms = Counter() - fps_counter;
6401
6402     fps_frames++;
6403
6404     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
6405     {
6406       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
6407
6408       fps_frames = 0;
6409       fps_counter = Counter();
6410     }
6411
6412     redraw_mask |= REDRAW_FPS;
6413   }
6414
6415 #if 0
6416   if (stored_player[0].jx != stored_player[0].last_jx ||
6417       stored_player[0].jy != stored_player[0].last_jy)
6418     printf("::: %d, %d, %d, %d, %d\n",
6419            stored_player[0].MovDir,
6420            stored_player[0].MovPos,
6421            stored_player[0].GfxPos,
6422            stored_player[0].Frame,
6423            stored_player[0].StepFrame);
6424 #endif
6425
6426 #if 1
6427   FrameCounter++;
6428   TimeFrames++;
6429
6430   for (i=0; i<MAX_PLAYERS; i++)
6431   {
6432     int move_frames =
6433       MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
6434
6435     stored_player[i].Frame += move_frames;
6436
6437     if (stored_player[i].MovPos != 0)
6438       stored_player[i].StepFrame += move_frames;
6439   }
6440 #endif
6441
6442 #if 1
6443   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
6444   {
6445     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
6446
6447     local_player->show_envelope = 0;
6448   }
6449 #endif
6450 }
6451
6452 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
6453 {
6454   int min_x = x, min_y = y, max_x = x, max_y = y;
6455   int i;
6456
6457   for (i=0; i<MAX_PLAYERS; i++)
6458   {
6459     int jx = stored_player[i].jx, jy = stored_player[i].jy;
6460
6461     if (!stored_player[i].active || &stored_player[i] == player)
6462       continue;
6463
6464     min_x = MIN(min_x, jx);
6465     min_y = MIN(min_y, jy);
6466     max_x = MAX(max_x, jx);
6467     max_y = MAX(max_y, jy);
6468   }
6469
6470   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
6471 }
6472
6473 static boolean AllPlayersInVisibleScreen()
6474 {
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)
6482       continue;
6483
6484     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6485       return FALSE;
6486   }
6487
6488   return TRUE;
6489 }
6490
6491 void ScrollLevel(int dx, int dy)
6492 {
6493   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
6494   int x, y;
6495
6496   BlitBitmap(drawto_field, drawto_field,
6497              FX + TILEX * (dx == -1) - softscroll_offset,
6498              FY + TILEY * (dy == -1) - softscroll_offset,
6499              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
6500              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
6501              FX + TILEX * (dx == 1) - softscroll_offset,
6502              FY + TILEY * (dy == 1) - softscroll_offset);
6503
6504   if (dx)
6505   {
6506     x = (dx == 1 ? BX1 : BX2);
6507     for (y=BY1; y <= BY2; y++)
6508       DrawScreenField(x, y);
6509   }
6510
6511   if (dy)
6512   {
6513     y = (dy == 1 ? BY1 : BY2);
6514     for (x=BX1; x <= BX2; x++)
6515       DrawScreenField(x, y);
6516   }
6517
6518   redraw_mask |= REDRAW_FIELD;
6519 }
6520
6521 static void CheckGravityMovement(struct PlayerInfo *player)
6522 {
6523   if (game.gravity && !player->programmed_action)
6524   {
6525     int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
6526     int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
6527     int move_dir =
6528       (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
6529        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
6530        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
6531     int jx = player->jx, jy = player->jy;
6532     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
6533     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
6534     int new_jx = jx + dx, new_jy = jy + dy;
6535     boolean field_under_player_is_free =
6536       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
6537     boolean player_is_moving_to_valid_field =
6538       (IN_LEV_FIELD(new_jx, new_jy) &&
6539        (Feld[new_jx][new_jy] == EL_SP_BASE ||
6540         Feld[new_jx][new_jy] == EL_SAND));
6541     /* !!! extend EL_SAND to anything diggable !!! */
6542
6543     if (field_under_player_is_free &&
6544         !player_is_moving_to_valid_field &&
6545         !IS_WALKABLE_INSIDE(Feld[jx][jy]))
6546       player->programmed_action = MV_DOWN;
6547   }
6548 }
6549
6550 /*
6551   MovePlayerOneStep()
6552   -----------------------------------------------------------------------------
6553   dx, dy:               direction (non-diagonal) to try to move the player to
6554   real_dx, real_dy:     direction as read from input device (can be diagonal)
6555 */
6556
6557 boolean MovePlayerOneStep(struct PlayerInfo *player,
6558                           int dx, int dy, int real_dx, int real_dy)
6559 {
6560 #if 0
6561   static int change_sides[4][2] =
6562   {
6563     /* enter side        leave side */
6564     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* moving left  */
6565     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* moving right */
6566     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     },      /* moving up    */
6567     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  }       /* moving down  */
6568   };
6569   int move_direction = (dx == -1 ? MV_LEFT :
6570                         dx == +1 ? MV_RIGHT :
6571                         dy == -1 ? MV_UP :
6572                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
6573   int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6574   int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6575 #endif
6576   int jx = player->jx, jy = player->jy;
6577   int new_jx = jx + dx, new_jy = jy + dy;
6578   int element;
6579   int can_move;
6580
6581   if (!player->active || (!dx && !dy))
6582     return MF_NO_ACTION;
6583
6584   player->MovDir = (dx < 0 ? MV_LEFT :
6585                     dx > 0 ? MV_RIGHT :
6586                     dy < 0 ? MV_UP :
6587                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
6588
6589   if (!IN_LEV_FIELD(new_jx, new_jy))
6590     return MF_NO_ACTION;
6591
6592   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
6593     return MF_NO_ACTION;
6594
6595 #if 0
6596   element = MovingOrBlocked2Element(new_jx, new_jy);
6597 #else
6598   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
6599 #endif
6600
6601   if (DONT_RUN_INTO(element))
6602   {
6603     if (element == EL_ACID && dx == 0 && dy == 1)
6604     {
6605       SplashAcid(jx, jy);
6606       Feld[jx][jy] = EL_PLAYER_1;
6607       InitMovingField(jx, jy, MV_DOWN);
6608       Store[jx][jy] = EL_ACID;
6609       ContinueMoving(jx, jy);
6610       BuryHero(player);
6611     }
6612     else
6613       TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
6614
6615     return MF_MOVING;
6616   }
6617
6618   can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
6619   if (can_move != MF_MOVING)
6620     return can_move;
6621
6622   /* check if DigField() has caused relocation of the player */
6623   if (player->jx != jx || player->jy != jy)
6624     return MF_NO_ACTION;
6625
6626   StorePlayer[jx][jy] = 0;
6627   player->last_jx = jx;
6628   player->last_jy = jy;
6629   player->jx = new_jx;
6630   player->jy = new_jy;
6631   StorePlayer[new_jx][new_jy] = player->element_nr;
6632
6633   player->MovPos =
6634     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
6635
6636   ScrollPlayer(player, SCROLL_INIT);
6637
6638 #if 0
6639   if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
6640   {
6641     CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6642                                     CE_OTHER_GETS_LEFT);
6643     CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6644                            CE_LEFT_BY_PLAYER, -1);
6645   }
6646
6647   if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
6648   {
6649     CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
6650                                     enter_side, CE_OTHER_GETS_ENTERED);
6651     CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
6652                            CE_ENTERED_BY_PLAYER, -1);
6653   }
6654 #endif
6655
6656   return MF_MOVING;
6657 }
6658
6659 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
6660 {
6661   int jx = player->jx, jy = player->jy;
6662   int old_jx = jx, old_jy = jy;
6663   int moved = MF_NO_ACTION;
6664
6665   if (!player->active || (!dx && !dy))
6666     return FALSE;
6667
6668 #if 0
6669   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6670       !tape.playing)
6671     return FALSE;
6672 #else
6673   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6674       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
6675     return FALSE;
6676 #endif
6677
6678   /* remove the last programmed player action */
6679   player->programmed_action = 0;
6680
6681   if (player->MovPos)
6682   {
6683     /* should only happen if pre-1.2 tape recordings are played */
6684     /* this is only for backward compatibility */
6685
6686     int original_move_delay_value = player->move_delay_value;
6687
6688 #if DEBUG
6689     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
6690            tape.counter);
6691 #endif
6692
6693     /* scroll remaining steps with finest movement resolution */
6694     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
6695
6696     while (player->MovPos)
6697     {
6698       ScrollPlayer(player, SCROLL_GO_ON);
6699       ScrollScreen(NULL, SCROLL_GO_ON);
6700       FrameCounter++;
6701       DrawAllPlayers();
6702       BackToFront();
6703     }
6704
6705     player->move_delay_value = original_move_delay_value;
6706   }
6707
6708   if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
6709   {
6710     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
6711       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
6712   }
6713   else
6714   {
6715     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
6716       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
6717   }
6718
6719   jx = player->jx;
6720   jy = player->jy;
6721
6722   if (moved & MF_MOVING && !ScreenMovPos &&
6723       (player == local_player || !options.network))
6724   {
6725     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
6726     int offset = (setup.scroll_delay ? 3 : 0);
6727
6728     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6729     {
6730       /* actual player has left the screen -- scroll in that direction */
6731       if (jx != old_jx)         /* player has moved horizontally */
6732         scroll_x += (jx - old_jx);
6733       else                      /* player has moved vertically */
6734         scroll_y += (jy - old_jy);
6735     }
6736     else
6737     {
6738       if (jx != old_jx)         /* player has moved horizontally */
6739       {
6740         if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
6741             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
6742           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
6743
6744         /* don't scroll over playfield boundaries */
6745         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
6746           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
6747
6748         /* don't scroll more than one field at a time */
6749         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
6750
6751         /* don't scroll against the player's moving direction */
6752         if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
6753             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
6754           scroll_x = old_scroll_x;
6755       }
6756       else                      /* player has moved vertically */
6757       {
6758         if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
6759             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
6760           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
6761
6762         /* don't scroll over playfield boundaries */
6763         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
6764           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
6765
6766         /* don't scroll more than one field at a time */
6767         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
6768
6769         /* don't scroll against the player's moving direction */
6770         if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
6771             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
6772           scroll_y = old_scroll_y;
6773       }
6774     }
6775
6776     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
6777     {
6778       if (!options.network && !AllPlayersInVisibleScreen())
6779       {
6780         scroll_x = old_scroll_x;
6781         scroll_y = old_scroll_y;
6782       }
6783       else
6784       {
6785         ScrollScreen(player, SCROLL_INIT);
6786         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
6787       }
6788     }
6789   }
6790
6791 #if 0
6792 #if 1
6793   InitPlayerGfxAnimation(player, ACTION_DEFAULT);
6794 #else
6795   if (!(moved & MF_MOVING) && !player->is_pushing)
6796     player->Frame = 0;
6797 #endif
6798 #endif
6799
6800   player->StepFrame = 0;
6801
6802   if (moved & MF_MOVING)
6803   {
6804     if (old_jx != jx && old_jy == jy)
6805       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
6806     else if (old_jx == jx && old_jy != jy)
6807       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
6808
6809     DrawLevelField(jx, jy);     /* for "crumbled sand" */
6810
6811     player->last_move_dir = player->MovDir;
6812     player->is_moving = TRUE;
6813 #if 1
6814     player->is_snapping = FALSE;
6815 #endif
6816
6817 #if 1
6818     player->is_switching = FALSE;
6819 #endif
6820
6821
6822 #if 1
6823     {
6824       static int change_sides[4][2] =
6825       {
6826         /* enter side           leave side */
6827         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
6828         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
6829         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
6830         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
6831       };
6832       int move_direction = player->MovDir;
6833       int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6834       int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6835
6836 #if 1
6837       if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
6838       {
6839         CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
6840                                         leave_side, CE_OTHER_GETS_LEFT);
6841         CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
6842                                leave_side, CE_LEFT_BY_PLAYER, -1);
6843       }
6844
6845       if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
6846       {
6847         CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
6848                                         enter_side, CE_OTHER_GETS_ENTERED);
6849         CheckElementSideChange(jx, jy, Feld[jx][jy],
6850                                enter_side, CE_ENTERED_BY_PLAYER, -1);
6851       }
6852 #endif
6853
6854     }
6855 #endif
6856
6857
6858   }
6859   else
6860   {
6861     CheckGravityMovement(player);
6862
6863     /*
6864     player->last_move_dir = MV_NO_MOVING;
6865     */
6866     player->is_moving = FALSE;
6867   }
6868
6869   if (game.engine_version < VERSION_IDENT(3,0,7))
6870   {
6871     TestIfHeroTouchesBadThing(jx, jy);
6872     TestIfPlayerTouchesCustomElement(jx, jy);
6873   }
6874
6875   if (!player->active)
6876     RemoveHero(player);
6877
6878   return moved;
6879 }
6880
6881 void ScrollPlayer(struct PlayerInfo *player, int mode)
6882 {
6883   int jx = player->jx, jy = player->jy;
6884   int last_jx = player->last_jx, last_jy = player->last_jy;
6885   int move_stepsize = TILEX / player->move_delay_value;
6886
6887   if (!player->active || !player->MovPos)
6888     return;
6889
6890   if (mode == SCROLL_INIT)
6891   {
6892     player->actual_frame_counter = FrameCounter;
6893     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
6894
6895     if (Feld[last_jx][last_jy] == EL_EMPTY)
6896       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
6897
6898 #if 0
6899     DrawPlayer(player);
6900 #endif
6901     return;
6902   }
6903   else if (!FrameReached(&player->actual_frame_counter, 1))
6904     return;
6905
6906   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
6907   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
6908
6909   if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
6910     Feld[last_jx][last_jy] = EL_EMPTY;
6911
6912   /* before DrawPlayer() to draw correct player graphic for this case */
6913   if (player->MovPos == 0)
6914     CheckGravityMovement(player);
6915
6916 #if 0
6917   DrawPlayer(player);   /* needed here only to cleanup last field */
6918 #endif
6919
6920   if (player->MovPos == 0)      /* player reached destination field */
6921   {
6922     if (IS_PASSABLE(Feld[last_jx][last_jy]))
6923     {
6924       /* continue with normal speed after quickly moving through gate */
6925       HALVE_PLAYER_SPEED(player);
6926
6927       /* be able to make the next move without delay */
6928       player->move_delay = 0;
6929     }
6930
6931     player->last_jx = jx;
6932     player->last_jy = jy;
6933
6934     if (Feld[jx][jy] == EL_EXIT_OPEN ||
6935         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
6936         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
6937     {
6938       DrawPlayer(player);       /* needed here only to cleanup last field */
6939       RemoveHero(player);
6940
6941       if (local_player->friends_still_needed == 0 ||
6942           IS_SP_ELEMENT(Feld[jx][jy]))
6943         player->LevelSolved = player->GameOver = TRUE;
6944     }
6945
6946     if (game.engine_version >= VERSION_IDENT(3,0,7))
6947     {
6948       TestIfHeroTouchesBadThing(jx, jy);
6949       TestIfPlayerTouchesCustomElement(jx, jy);
6950 #if 1
6951       TestIfElementTouchesCustomElement(jx, jy);        /* for empty space */
6952 #endif
6953
6954       if (!player->active)
6955         RemoveHero(player);
6956     }
6957
6958     if (tape.single_step && tape.recording && !tape.pausing &&
6959         !player->programmed_action)
6960       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6961   }
6962 }
6963
6964 void ScrollScreen(struct PlayerInfo *player, int mode)
6965 {
6966   static unsigned long screen_frame_counter = 0;
6967
6968   if (mode == SCROLL_INIT)
6969   {
6970     /* set scrolling step size according to actual player's moving speed */
6971     ScrollStepSize = TILEX / player->move_delay_value;
6972
6973     screen_frame_counter = FrameCounter;
6974     ScreenMovDir = player->MovDir;
6975     ScreenMovPos = player->MovPos;
6976     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
6977     return;
6978   }
6979   else if (!FrameReached(&screen_frame_counter, 1))
6980     return;
6981
6982   if (ScreenMovPos)
6983   {
6984     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
6985     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
6986     redraw_mask |= REDRAW_FIELD;
6987   }
6988   else
6989     ScreenMovDir = MV_NO_MOVING;
6990 }
6991
6992 void TestIfPlayerTouchesCustomElement(int x, int y)
6993 {
6994   static int xy[4][2] =
6995   {
6996     { 0, -1 },
6997     { -1, 0 },
6998     { +1, 0 },
6999     { 0, +1 }
7000   };
7001   static int change_sides[4][2] =
7002   {
7003     /* center side       border side */
7004     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
7005     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
7006     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
7007     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
7008   };
7009   static int touch_dir[4] =
7010   {
7011     MV_LEFT | MV_RIGHT,
7012     MV_UP   | MV_DOWN,
7013     MV_UP   | MV_DOWN,
7014     MV_LEFT | MV_RIGHT
7015   };
7016   int center_element = Feld[x][y];      /* should always be non-moving! */
7017   int i;
7018
7019   for (i=0; i<4; i++)
7020   {
7021     int xx = x + xy[i][0];
7022     int yy = y + xy[i][1];
7023     int center_side = change_sides[i][0];
7024     int border_side = change_sides[i][1];
7025     int border_element;
7026
7027     if (!IN_LEV_FIELD(xx, yy))
7028       continue;
7029
7030     if (IS_PLAYER(x, y))
7031     {
7032       if (game.engine_version < VERSION_IDENT(3,0,7))
7033         border_element = Feld[xx][yy];          /* may be moving! */
7034       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7035         border_element = Feld[xx][yy];
7036       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
7037         border_element = MovingOrBlocked2Element(xx, yy);
7038       else
7039         continue;               /* center and border element do not touch */
7040
7041       CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7042                                       CE_OTHER_GETS_TOUCHED);
7043       CheckElementSideChange(xx, yy, border_element, border_side,
7044                              CE_TOUCHED_BY_PLAYER, -1);
7045     }
7046     else if (IS_PLAYER(xx, yy))
7047     {
7048       if (game.engine_version >= VERSION_IDENT(3,0,7))
7049       {
7050         struct PlayerInfo *player = PLAYERINFO(xx, yy);
7051
7052         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7053           continue;             /* center and border element do not touch */
7054       }
7055
7056       CheckTriggeredElementSideChange(x, y, center_element, center_side,
7057                                       CE_OTHER_GETS_TOUCHED);
7058       CheckElementSideChange(x, y, center_element, center_side,
7059                              CE_TOUCHED_BY_PLAYER, -1);
7060
7061       break;
7062     }
7063   }
7064 }
7065
7066 void TestIfElementTouchesCustomElement(int x, int y)
7067 {
7068   static int xy[4][2] =
7069   {
7070     { 0, -1 },
7071     { -1, 0 },
7072     { +1, 0 },
7073     { 0, +1 }
7074   };
7075   static int change_sides[4][2] =
7076   {
7077     /* center side       border side */
7078     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
7079     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
7080     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
7081     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
7082   };
7083   static int touch_dir[4] =
7084   {
7085     MV_LEFT | MV_RIGHT,
7086     MV_UP   | MV_DOWN,
7087     MV_UP   | MV_DOWN,
7088     MV_LEFT | MV_RIGHT
7089   };
7090   boolean change_center_element = FALSE;
7091   int center_element_change_page = 0;
7092   int center_element = Feld[x][y];      /* should always be non-moving! */
7093   int i, j;
7094
7095   for (i=0; i<4; i++)
7096   {
7097     int xx = x + xy[i][0];
7098     int yy = y + xy[i][1];
7099     int center_side = change_sides[i][0];
7100     int border_side = change_sides[i][1];
7101     int border_element;
7102
7103     if (!IN_LEV_FIELD(xx, yy))
7104       continue;
7105
7106     if (game.engine_version < VERSION_IDENT(3,0,7))
7107       border_element = Feld[xx][yy];    /* may be moving! */
7108     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7109       border_element = Feld[xx][yy];
7110     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
7111       border_element = MovingOrBlocked2Element(xx, yy);
7112     else
7113       continue;                 /* center and border element do not touch */
7114
7115     /* check for change of center element (but change it only once) */
7116     if (IS_CUSTOM_ELEMENT(center_element) &&
7117         HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7118         !change_center_element)
7119     {
7120       for (j=0; j < element_info[center_element].num_change_pages; j++)
7121       {
7122         struct ElementChangeInfo *change =
7123           &element_info[center_element].change_page[j];
7124
7125         if (change->can_change &&
7126             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7127             change->sides & border_side &&
7128             change->trigger_element == border_element)
7129         {
7130           change_center_element = TRUE;
7131           center_element_change_page = j;
7132
7133           break;
7134         }
7135       }
7136     }
7137
7138     /* check for change of border element */
7139     if (IS_CUSTOM_ELEMENT(border_element) &&
7140         HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7141     {
7142       for (j=0; j < element_info[border_element].num_change_pages; j++)
7143       {
7144         struct ElementChangeInfo *change =
7145           &element_info[border_element].change_page[j];
7146
7147         if (change->can_change &&
7148             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7149             change->sides & center_side &&
7150             change->trigger_element == center_element)
7151         {
7152           CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7153                                  CE_OTHER_IS_TOUCHING, j);
7154           break;
7155         }
7156       }
7157     }
7158   }
7159
7160   if (change_center_element)
7161     CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7162                            CE_OTHER_IS_TOUCHING, center_element_change_page);
7163 }
7164
7165 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
7166 {
7167   int i, kill_x = -1, kill_y = -1;
7168   static int test_xy[4][2] =
7169   {
7170     { 0, -1 },
7171     { -1, 0 },
7172     { +1, 0 },
7173     { 0, +1 }
7174   };
7175   static int test_dir[4] =
7176   {
7177     MV_UP,
7178     MV_LEFT,
7179     MV_RIGHT,
7180     MV_DOWN
7181   };
7182
7183   for (i=0; i<4; i++)
7184   {
7185     int test_x, test_y, test_move_dir, test_element;
7186
7187     test_x = good_x + test_xy[i][0];
7188     test_y = good_y + test_xy[i][1];
7189     if (!IN_LEV_FIELD(test_x, test_y))
7190       continue;
7191
7192     test_move_dir =
7193       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7194
7195 #if 0
7196     test_element = Feld[test_x][test_y];
7197 #else
7198     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
7199 #endif
7200
7201     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7202        2nd case: DONT_TOUCH style bad thing does not move away from good thing
7203     */
7204     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
7205         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
7206     {
7207       kill_x = test_x;
7208       kill_y = test_y;
7209       break;
7210     }
7211   }
7212
7213   if (kill_x != -1 || kill_y != -1)
7214   {
7215     if (IS_PLAYER(good_x, good_y))
7216     {
7217       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
7218
7219       if (player->shield_deadly_time_left > 0)
7220         Bang(kill_x, kill_y);
7221       else if (!PLAYER_PROTECTED(good_x, good_y))
7222         KillHero(player);
7223     }
7224     else
7225       Bang(good_x, good_y);
7226   }
7227 }
7228
7229 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
7230 {
7231   int i, kill_x = -1, kill_y = -1;
7232   int bad_element = Feld[bad_x][bad_y];
7233   static int test_xy[4][2] =
7234   {
7235     { 0, -1 },
7236     { -1, 0 },
7237     { +1, 0 },
7238     { 0, +1 }
7239   };
7240   static int touch_dir[4] =
7241   {
7242     MV_LEFT | MV_RIGHT,
7243     MV_UP   | MV_DOWN,
7244     MV_UP   | MV_DOWN,
7245     MV_LEFT | MV_RIGHT
7246   };
7247   static int test_dir[4] =
7248   {
7249     MV_UP,
7250     MV_LEFT,
7251     MV_RIGHT,
7252     MV_DOWN
7253   };
7254
7255   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
7256     return;
7257
7258   for (i=0; i<4; i++)
7259   {
7260     int test_x, test_y, test_move_dir, test_element;
7261
7262     test_x = bad_x + test_xy[i][0];
7263     test_y = bad_y + test_xy[i][1];
7264     if (!IN_LEV_FIELD(test_x, test_y))
7265       continue;
7266
7267     test_move_dir =
7268       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7269
7270     test_element = Feld[test_x][test_y];
7271
7272     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7273        2nd case: DONT_TOUCH style bad thing does not move away from good thing
7274     */
7275     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
7276         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
7277     {
7278       /* good thing is player or penguin that does not move away */
7279       if (IS_PLAYER(test_x, test_y))
7280       {
7281         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
7282
7283         if (bad_element == EL_ROBOT && player->is_moving)
7284           continue;     /* robot does not kill player if he is moving */
7285
7286         if (game.engine_version >= VERSION_IDENT(3,0,7))
7287         {
7288           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7289             continue;           /* center and border element do not touch */
7290         }
7291
7292         kill_x = test_x;
7293         kill_y = test_y;
7294         break;
7295       }
7296       else if (test_element == EL_PENGUIN)
7297       {
7298         kill_x = test_x;
7299         kill_y = test_y;
7300         break;
7301       }
7302     }
7303   }
7304
7305   if (kill_x != -1 || kill_y != -1)
7306   {
7307     if (IS_PLAYER(kill_x, kill_y))
7308     {
7309       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
7310
7311       if (player->shield_deadly_time_left > 0)
7312         Bang(bad_x, bad_y);
7313       else if (!PLAYER_PROTECTED(kill_x, kill_y))
7314         KillHero(player);
7315     }
7316     else
7317       Bang(kill_x, kill_y);
7318   }
7319 }
7320
7321 void TestIfHeroTouchesBadThing(int x, int y)
7322 {
7323   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7324 }
7325
7326 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
7327 {
7328   TestIfGoodThingHitsBadThing(x, y, move_dir);
7329 }
7330
7331 void TestIfBadThingTouchesHero(int x, int y)
7332 {
7333   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7334 }
7335
7336 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
7337 {
7338   TestIfBadThingHitsGoodThing(x, y, move_dir);
7339 }
7340
7341 void TestIfFriendTouchesBadThing(int x, int y)
7342 {
7343   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7344 }
7345
7346 void TestIfBadThingTouchesFriend(int x, int y)
7347 {
7348   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7349 }
7350
7351 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
7352 {
7353   int i, kill_x = bad_x, kill_y = bad_y;
7354   static int xy[4][2] =
7355   {
7356     { 0, -1 },
7357     { -1, 0 },
7358     { +1, 0 },
7359     { 0, +1 }
7360   };
7361
7362   for (i=0; i<4; i++)
7363   {
7364     int x, y, element;
7365
7366     x = bad_x + xy[i][0];
7367     y = bad_y + xy[i][1];
7368     if (!IN_LEV_FIELD(x, y))
7369       continue;
7370
7371     element = Feld[x][y];
7372     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
7373         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
7374     {
7375       kill_x = x;
7376       kill_y = y;
7377       break;
7378     }
7379   }
7380
7381   if (kill_x != bad_x || kill_y != bad_y)
7382     Bang(bad_x, bad_y);
7383 }
7384
7385 void KillHero(struct PlayerInfo *player)
7386 {
7387   int jx = player->jx, jy = player->jy;
7388
7389   if (!player->active)
7390     return;
7391
7392   /* remove accessible field at the player's position */
7393   Feld[jx][jy] = EL_EMPTY;
7394
7395   /* deactivate shield (else Bang()/Explode() would not work right) */
7396   player->shield_normal_time_left = 0;
7397   player->shield_deadly_time_left = 0;
7398
7399   Bang(jx, jy);
7400   BuryHero(player);
7401 }
7402
7403 static void KillHeroUnlessProtected(int x, int y)
7404 {
7405   if (!PLAYER_PROTECTED(x, y))
7406     KillHero(PLAYERINFO(x, y));
7407 }
7408
7409 void BuryHero(struct PlayerInfo *player)
7410 {
7411   int jx = player->jx, jy = player->jy;
7412
7413   if (!player->active)
7414     return;
7415
7416 #if 1
7417   PlaySoundLevelElementAction(jx, jy, player->element_nr, ACTION_DYING);
7418 #else
7419   PlaySoundLevel(jx, jy, SND_CLASS_PLAYER_DYING);
7420 #endif
7421   PlaySoundLevel(jx, jy, SND_GAME_LOSING);
7422
7423   player->GameOver = TRUE;
7424   RemoveHero(player);
7425 }
7426
7427 void RemoveHero(struct PlayerInfo *player)
7428 {
7429   int jx = player->jx, jy = player->jy;
7430   int i, found = FALSE;
7431
7432   player->present = FALSE;
7433   player->active = FALSE;
7434
7435   if (!ExplodeField[jx][jy])
7436     StorePlayer[jx][jy] = 0;
7437
7438   for (i=0; i<MAX_PLAYERS; i++)
7439     if (stored_player[i].active)
7440       found = TRUE;
7441
7442   if (!found)
7443     AllPlayersGone = TRUE;
7444
7445   ExitX = ZX = jx;
7446   ExitY = ZY = jy;
7447 }
7448
7449 /*
7450   =============================================================================
7451   checkDiagonalPushing()
7452   -----------------------------------------------------------------------------
7453   check if diagonal input device direction results in pushing of object
7454   (by checking if the alternative direction is walkable, diggable, ...)
7455   =============================================================================
7456 */
7457
7458 static boolean checkDiagonalPushing(struct PlayerInfo *player,
7459                                     int x, int y, int real_dx, int real_dy)
7460 {
7461   int jx, jy, dx, dy, xx, yy;
7462
7463   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
7464     return TRUE;
7465
7466   /* diagonal direction: check alternative direction */
7467   jx = player->jx;
7468   jy = player->jy;
7469   dx = x - jx;
7470   dy = y - jy;
7471   xx = jx + (dx == 0 ? real_dx : 0);
7472   yy = jy + (dy == 0 ? real_dy : 0);
7473
7474   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
7475 }
7476
7477 /*
7478   =============================================================================
7479   DigField()
7480   -----------------------------------------------------------------------------
7481   x, y:                 field next to player (non-diagonal) to try to dig to
7482   real_dx, real_dy:     direction as read from input device (can be diagonal)
7483   =============================================================================
7484 */
7485
7486 int DigField(struct PlayerInfo *player,
7487              int x, int y, int real_dx, int real_dy, int mode)
7488 {
7489   static int change_sides[4] =
7490   {
7491     CH_SIDE_RIGHT,      /* moving left  */
7492     CH_SIDE_LEFT,       /* moving right */
7493     CH_SIDE_BOTTOM,     /* moving up    */
7494     CH_SIDE_TOP,        /* moving down  */
7495   };
7496   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0));
7497   int jx = player->jx, jy = player->jy;
7498   int dx = x - jx, dy = y - jy;
7499   int nextx = x + dx, nexty = y + dy;
7500   int move_direction = (dx == -1 ? MV_LEFT :
7501                         dx == +1 ? MV_RIGHT :
7502                         dy == -1 ? MV_UP :
7503                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
7504   int dig_side = change_sides[MV_DIR_BIT(move_direction)];
7505   int element;
7506
7507   if (player->MovPos == 0)
7508   {
7509     player->is_digging = FALSE;
7510     player->is_collecting = FALSE;
7511   }
7512
7513   if (player->MovPos == 0)      /* last pushing move finished */
7514     player->is_pushing = FALSE;
7515
7516   if (mode == DF_NO_PUSH)       /* player just stopped pushing */
7517   {
7518     player->is_switching = FALSE;
7519     player->push_delay = 0;
7520
7521     return MF_NO_ACTION;
7522   }
7523
7524   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
7525     return MF_NO_ACTION;
7526
7527 #if 0
7528   if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
7529 #else
7530   if (IS_TUBE(Feld[jx][jy]) ||
7531       (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0)))
7532 #endif
7533   {
7534     int i = 0;
7535     int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
7536     int tube_leave_directions[][2] =
7537     {
7538       { EL_TUBE_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7539       { EL_TUBE_VERTICAL,                                    MV_UP | MV_DOWN },
7540       { EL_TUBE_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
7541       { EL_TUBE_VERTICAL_LEFT,          MV_LEFT |            MV_UP | MV_DOWN },
7542       { EL_TUBE_VERTICAL_RIGHT,                   MV_RIGHT | MV_UP | MV_DOWN },
7543       { EL_TUBE_HORIZONTAL_UP,          MV_LEFT | MV_RIGHT | MV_UP           },
7544       { EL_TUBE_HORIZONTAL_DOWN,        MV_LEFT | MV_RIGHT |         MV_DOWN },
7545       { EL_TUBE_LEFT_UP,                MV_LEFT |            MV_UP           },
7546       { EL_TUBE_LEFT_DOWN,              MV_LEFT |                    MV_DOWN },
7547       { EL_TUBE_RIGHT_UP,                         MV_RIGHT | MV_UP           },
7548       { EL_TUBE_RIGHT_DOWN,                       MV_RIGHT |         MV_DOWN },
7549       { -1,                             MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
7550     };
7551
7552     while (tube_leave_directions[i][0] != tube_element)
7553     {
7554       i++;
7555       if (tube_leave_directions[i][0] == -1)    /* should not happen */
7556         break;
7557     }
7558
7559     if (!(tube_leave_directions[i][1] & move_direction))
7560       return MF_NO_ACTION;      /* tube has no opening in this direction */
7561   }
7562
7563   element = Feld[x][y];
7564
7565   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
7566       game.engine_version >= VERSION_IDENT(2,2,0))
7567     return MF_NO_ACTION;
7568
7569   switch (element)
7570   {
7571     case EL_SP_PORT_LEFT:
7572     case EL_SP_PORT_RIGHT:
7573     case EL_SP_PORT_UP:
7574     case EL_SP_PORT_DOWN:
7575     case EL_SP_PORT_HORIZONTAL:
7576     case EL_SP_PORT_VERTICAL:
7577     case EL_SP_PORT_ANY:
7578     case EL_SP_GRAVITY_PORT_LEFT:
7579     case EL_SP_GRAVITY_PORT_RIGHT:
7580     case EL_SP_GRAVITY_PORT_UP:
7581     case EL_SP_GRAVITY_PORT_DOWN:
7582       if ((dx == -1 &&
7583            element != EL_SP_PORT_LEFT &&
7584            element != EL_SP_GRAVITY_PORT_LEFT &&
7585            element != EL_SP_PORT_HORIZONTAL &&
7586            element != EL_SP_PORT_ANY) ||
7587           (dx == +1 &&
7588            element != EL_SP_PORT_RIGHT &&
7589            element != EL_SP_GRAVITY_PORT_RIGHT &&
7590            element != EL_SP_PORT_HORIZONTAL &&
7591            element != EL_SP_PORT_ANY) ||
7592           (dy == -1 &&
7593            element != EL_SP_PORT_UP &&
7594            element != EL_SP_GRAVITY_PORT_UP &&
7595            element != EL_SP_PORT_VERTICAL &&
7596            element != EL_SP_PORT_ANY) ||
7597           (dy == +1 &&
7598            element != EL_SP_PORT_DOWN &&
7599            element != EL_SP_GRAVITY_PORT_DOWN &&
7600            element != EL_SP_PORT_VERTICAL &&
7601            element != EL_SP_PORT_ANY) ||
7602           !IN_LEV_FIELD(nextx, nexty) ||
7603           !IS_FREE(nextx, nexty))
7604         return MF_NO_ACTION;
7605
7606       if (element == EL_SP_GRAVITY_PORT_LEFT ||
7607           element == EL_SP_GRAVITY_PORT_RIGHT ||
7608           element == EL_SP_GRAVITY_PORT_UP ||
7609           element == EL_SP_GRAVITY_PORT_DOWN)
7610         game.gravity = !game.gravity;
7611
7612       /* automatically move to the next field with double speed */
7613       player->programmed_action = move_direction;
7614       DOUBLE_PLAYER_SPEED(player);
7615
7616       PlaySoundLevel(x, y, SND_CLASS_SP_PORT_PASSING);
7617       break;
7618
7619     case EL_TUBE_ANY:
7620     case EL_TUBE_VERTICAL:
7621     case EL_TUBE_HORIZONTAL:
7622     case EL_TUBE_VERTICAL_LEFT:
7623     case EL_TUBE_VERTICAL_RIGHT:
7624     case EL_TUBE_HORIZONTAL_UP:
7625     case EL_TUBE_HORIZONTAL_DOWN:
7626     case EL_TUBE_LEFT_UP:
7627     case EL_TUBE_LEFT_DOWN:
7628     case EL_TUBE_RIGHT_UP:
7629     case EL_TUBE_RIGHT_DOWN:
7630       {
7631         int i = 0;
7632         int tube_enter_directions[][2] =
7633         {
7634           { EL_TUBE_ANY,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7635           { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
7636           { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
7637           { EL_TUBE_VERTICAL_LEFT,                MV_RIGHT | MV_UP | MV_DOWN },
7638           { EL_TUBE_VERTICAL_RIGHT,     MV_LEFT            | MV_UP | MV_DOWN },
7639           { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT |         MV_DOWN },
7640           { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT | MV_UP           },
7641           { EL_TUBE_LEFT_UP,                      MV_RIGHT |         MV_DOWN },
7642           { EL_TUBE_LEFT_DOWN,                    MV_RIGHT | MV_UP           },
7643           { EL_TUBE_RIGHT_UP,           MV_LEFT |                    MV_DOWN },
7644           { EL_TUBE_RIGHT_DOWN,         MV_LEFT |            MV_UP           },
7645           { -1,                         MV_NO_MOVING                         }
7646         };
7647
7648         while (tube_enter_directions[i][0] != element)
7649         {
7650           i++;
7651           if (tube_enter_directions[i][0] == -1)        /* should not happen */
7652             break;
7653         }
7654
7655         if (!(tube_enter_directions[i][1] & move_direction))
7656           return MF_NO_ACTION;  /* tube has no opening in this direction */
7657
7658         PlaySoundLevel(x, y, SND_CLASS_TUBE_WALKING);
7659       }
7660       break;
7661
7662     default:
7663
7664       if (IS_WALKABLE(element))
7665       {
7666         int sound_action = ACTION_WALKING;
7667
7668         if (element >= EL_GATE_1 && element <= EL_GATE_4)
7669         {
7670           if (!player->key[element - EL_GATE_1])
7671             return MF_NO_ACTION;
7672         }
7673         else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
7674         {
7675           if (!player->key[element - EL_GATE_1_GRAY])
7676             return MF_NO_ACTION;
7677         }
7678         else if (element == EL_EXIT_OPEN ||
7679                  element == EL_SP_EXIT_OPEN ||
7680                  element == EL_SP_EXIT_OPENING)
7681         {
7682           sound_action = ACTION_PASSING;        /* player is passing exit */
7683         }
7684         else if (element == EL_EMPTY)
7685         {
7686           sound_action = ACTION_MOVING;         /* nothing to walk on */
7687         }
7688
7689         /* play sound from background or player, whatever is available */
7690         if (element_info[element].sound[sound_action] != SND_UNDEFINED)
7691           PlaySoundLevelElementAction(x, y, element, sound_action);
7692         else
7693           PlaySoundLevelElementAction(x, y, player->element_nr, sound_action);
7694
7695         break;
7696       }
7697       else if (IS_PASSABLE(element))
7698       {
7699         if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
7700           return MF_NO_ACTION;
7701
7702 #if 1
7703         if (CAN_MOVE(element))  /* only fixed elements can be passed! */
7704           return MF_NO_ACTION;
7705 #endif
7706
7707         if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
7708         {
7709           if (!player->key[element - EL_EM_GATE_1])
7710             return MF_NO_ACTION;
7711         }
7712         else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
7713         {
7714           if (!player->key[element - EL_EM_GATE_1_GRAY])
7715             return MF_NO_ACTION;
7716         }
7717
7718         /* automatically move to the next field with double speed */
7719         player->programmed_action = move_direction;
7720         DOUBLE_PLAYER_SPEED(player);
7721
7722         PlaySoundLevelAction(x, y, ACTION_PASSING);
7723
7724         break;
7725       }
7726       else if (IS_DIGGABLE(element))
7727       {
7728         RemoveField(x, y);
7729
7730         if (mode != DF_SNAP)
7731         {
7732 #if 1
7733           GfxElement[x][y] = GFX_ELEMENT(element);
7734 #else
7735           GfxElement[x][y] =
7736             (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
7737 #endif
7738           player->is_digging = TRUE;
7739         }
7740
7741         PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
7742
7743         CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
7744
7745 #if 1
7746         if (mode == DF_SNAP)
7747           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
7748 #endif
7749
7750         break;
7751       }
7752       else if (IS_COLLECTIBLE(element))
7753       {
7754         RemoveField(x, y);
7755
7756         if (mode != DF_SNAP)
7757         {
7758           GfxElement[x][y] = element;
7759           player->is_collecting = TRUE;
7760         }
7761
7762         if (element == EL_SPEED_PILL)
7763           player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
7764         else if (element == EL_EXTRA_TIME && level.time > 0)
7765         {
7766           TimeLeft += 10;
7767           DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7768         }
7769         else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
7770         {
7771           player->shield_normal_time_left += 10;
7772           if (element == EL_SHIELD_DEADLY)
7773             player->shield_deadly_time_left += 10;
7774         }
7775         else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
7776         {
7777           if (player->inventory_size < MAX_INVENTORY_SIZE)
7778             player->inventory_element[player->inventory_size++] = element;
7779
7780           DrawText(DX_DYNAMITE, DY_DYNAMITE,
7781                    int2str(local_player->inventory_size, 3), FONT_TEXT_2);
7782         }
7783         else if (element == EL_DYNABOMB_INCREASE_NUMBER)
7784         {
7785           player->dynabomb_count++;
7786           player->dynabombs_left++;
7787         }
7788         else if (element == EL_DYNABOMB_INCREASE_SIZE)
7789         {
7790           player->dynabomb_size++;
7791         }
7792         else if (element == EL_DYNABOMB_INCREASE_POWER)
7793         {
7794           player->dynabomb_xl = TRUE;
7795         }
7796         else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
7797                  (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
7798         {
7799           int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
7800                         element - EL_KEY_1 : element - EL_EM_KEY_1);
7801
7802           player->key[key_nr] = TRUE;
7803
7804           DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
7805                              el2edimg(EL_KEY_1 + key_nr));
7806           redraw_mask |= REDRAW_DOOR_1;
7807         }
7808         else if (IS_ENVELOPE(element))
7809         {
7810 #if 1
7811           player->show_envelope = element;
7812 #else
7813           ShowEnvelope(element - EL_ENVELOPE_1);
7814 #endif
7815         }
7816         else if (IS_DROPPABLE(element)) /* can be collected and dropped */
7817         {
7818           int i;
7819
7820           for (i=0; i < element_info[element].collect_count; i++)
7821             if (player->inventory_size < MAX_INVENTORY_SIZE)
7822               player->inventory_element[player->inventory_size++] = element;
7823
7824           DrawText(DX_DYNAMITE, DY_DYNAMITE,
7825                    int2str(local_player->inventory_size, 3), FONT_TEXT_2);
7826         }
7827         else if (element_info[element].collect_count > 0)
7828         {
7829           local_player->gems_still_needed -=
7830             element_info[element].collect_count;
7831           if (local_player->gems_still_needed < 0)
7832             local_player->gems_still_needed = 0;
7833
7834           DrawText(DX_EMERALDS, DY_EMERALDS,
7835                    int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
7836         }
7837
7838         RaiseScoreElement(element);
7839         PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
7840
7841         CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
7842
7843 #if 1
7844         if (mode == DF_SNAP)
7845           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
7846 #endif
7847
7848         break;
7849       }
7850       else if (IS_PUSHABLE(element))
7851       {
7852         if (mode == DF_SNAP && element != EL_BD_ROCK)
7853           return MF_NO_ACTION;
7854
7855         if (CAN_FALL(element) && dy)
7856           return MF_NO_ACTION;
7857
7858         if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
7859             !(element == EL_SPRING && use_spring_bug))
7860           return MF_NO_ACTION;
7861
7862 #if 1
7863         /* do not push elements already moving away faster than player */
7864         if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
7865             ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
7866           return MF_NO_ACTION;
7867 #else
7868         if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
7869           return MF_NO_ACTION;
7870 #endif
7871         if (!player->is_pushing &&
7872             game.engine_version >= RELEASE_IDENT(2,2,0,7))
7873           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7874
7875         player->is_pushing = TRUE;
7876
7877         if (!(IN_LEV_FIELD(nextx, nexty) &&
7878               (IS_FREE(nextx, nexty) ||
7879                (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
7880                 IS_SB_ELEMENT(element)))))
7881           return MF_NO_ACTION;
7882
7883         if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
7884           return MF_NO_ACTION;
7885
7886         if (player->push_delay == 0)    /* new pushing; restart delay */
7887           player->push_delay = FrameCounter;
7888
7889         if (!FrameReached(&player->push_delay, player->push_delay_value) &&
7890             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
7891             element != EL_SPRING && element != EL_BALLOON)
7892           return MF_NO_ACTION;
7893
7894         if (IS_SB_ELEMENT(element))
7895         {
7896           if (element == EL_SOKOBAN_FIELD_FULL)
7897           {
7898             Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
7899             local_player->sokobanfields_still_needed++;
7900           }
7901
7902           if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
7903           {
7904             Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
7905             local_player->sokobanfields_still_needed--;
7906           }
7907
7908           Feld[x][y] = EL_SOKOBAN_OBJECT;
7909
7910           if (Back[x][y] == Back[nextx][nexty])
7911             PlaySoundLevelAction(x, y, ACTION_PUSHING);
7912           else if (Back[x][y] != 0)
7913             PlaySoundLevelElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
7914                                         ACTION_EMPTYING);
7915           else
7916             PlaySoundLevelElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
7917                                         ACTION_FILLING);
7918
7919           if (local_player->sokobanfields_still_needed == 0 &&
7920               game.emulation == EMU_SOKOBAN)
7921           {
7922             player->LevelSolved = player->GameOver = TRUE;
7923             PlaySoundLevel(x, y, SND_GAME_SOKOBAN_SOLVING);
7924           }
7925         }
7926         else
7927           PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
7928
7929         InitMovingField(x, y, move_direction);
7930         GfxAction[x][y] = ACTION_PUSHING;
7931
7932         if (mode == DF_SNAP)
7933           ContinueMoving(x, y);
7934         else
7935           MovPos[x][y] = (dx != 0 ? dx : dy);
7936
7937         Pushed[x][y] = TRUE;
7938         Pushed[nextx][nexty] = TRUE;
7939
7940         if (game.engine_version < RELEASE_IDENT(2,2,0,7))
7941           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7942
7943         CheckTriggeredElementSideChange(x, y, element, dig_side,
7944                                         CE_OTHER_GETS_PUSHED);
7945         CheckElementSideChange(x, y, element, dig_side,
7946                                CE_PUSHED_BY_PLAYER, -1);
7947
7948         break;
7949       }
7950       else if (IS_SWITCHABLE(element))
7951       {
7952         if (PLAYER_SWITCHING(player, x, y))
7953           return MF_ACTION;
7954
7955         player->is_switching = TRUE;
7956         player->switch_x = x;
7957         player->switch_y = y;
7958
7959         PlaySoundLevelElementAction(x, y, element, ACTION_ACTIVATING);
7960
7961         if (element == EL_ROBOT_WHEEL)
7962         {
7963           Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
7964           ZX = x;
7965           ZY = y;
7966
7967           DrawLevelField(x, y);
7968         }
7969         else if (element == EL_SP_TERMINAL)
7970         {
7971           int xx, yy;
7972
7973           for (yy=0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
7974           {
7975             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
7976               Bang(xx, yy);
7977             else if (Feld[xx][yy] == EL_SP_TERMINAL)
7978               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
7979           }
7980         }
7981         else if (IS_BELT_SWITCH(element))
7982         {
7983           ToggleBeltSwitch(x, y);
7984         }
7985         else if (element == EL_SWITCHGATE_SWITCH_UP ||
7986                  element == EL_SWITCHGATE_SWITCH_DOWN)
7987         {
7988           ToggleSwitchgateSwitch(x, y);
7989         }
7990         else if (element == EL_LIGHT_SWITCH ||
7991                  element == EL_LIGHT_SWITCH_ACTIVE)
7992         {
7993           ToggleLightSwitch(x, y);
7994
7995 #if 0
7996           PlaySoundLevel(x, y, element == EL_LIGHT_SWITCH ?
7997                          SND_LIGHT_SWITCH_ACTIVATING :
7998                          SND_LIGHT_SWITCH_DEACTIVATING);
7999 #endif
8000         }
8001         else if (element == EL_TIMEGATE_SWITCH)
8002         {
8003           ActivateTimegateSwitch(x, y);
8004         }
8005         else if (element == EL_BALLOON_SWITCH_LEFT ||
8006                  element == EL_BALLOON_SWITCH_RIGHT ||
8007                  element == EL_BALLOON_SWITCH_UP ||
8008                  element == EL_BALLOON_SWITCH_DOWN ||
8009                  element == EL_BALLOON_SWITCH_ANY)
8010         {
8011           if (element == EL_BALLOON_SWITCH_ANY)
8012             game.balloon_dir = move_direction;
8013           else
8014             game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT :
8015                                 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
8016                                 element == EL_BALLOON_SWITCH_UP    ? MV_UP :
8017                                 element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN :
8018                                 MV_NO_MOVING);
8019         }
8020         else if (element == EL_LAMP)
8021         {
8022           Feld[x][y] = EL_LAMP_ACTIVE;
8023           local_player->lights_still_needed--;
8024
8025           DrawLevelField(x, y);
8026         }
8027         else if (element == EL_TIME_ORB_FULL)
8028         {
8029           Feld[x][y] = EL_TIME_ORB_EMPTY;
8030           TimeLeft += 10;
8031           DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8032
8033           DrawLevelField(x, y);
8034
8035 #if 0
8036           PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
8037 #endif
8038         }
8039
8040         return MF_ACTION;
8041       }
8042       else
8043       {
8044         if (!PLAYER_SWITCHING(player, x, y))
8045         {
8046           player->is_switching = TRUE;
8047           player->switch_x = x;
8048           player->switch_y = y;
8049
8050           CheckTriggeredElementSideChange(x, y, element, dig_side,
8051                                           CE_OTHER_IS_SWITCHING);
8052           CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
8053         }
8054
8055         CheckTriggeredElementSideChange(x, y, element, dig_side,
8056                                         CE_OTHER_GETS_PRESSED);
8057         CheckElementSideChange(x, y, element, dig_side,
8058                                CE_PRESSED_BY_PLAYER, -1);
8059       }
8060
8061       return MF_NO_ACTION;
8062   }
8063
8064   player->push_delay = 0;
8065
8066   if (Feld[x][y] != element)            /* really digged/collected something */
8067     player->is_collecting = !player->is_digging;
8068
8069   return MF_MOVING;
8070 }
8071
8072 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
8073 {
8074   int jx = player->jx, jy = player->jy;
8075   int x = jx + dx, y = jy + dy;
8076   int snap_direction = (dx == -1 ? MV_LEFT :
8077                         dx == +1 ? MV_RIGHT :
8078                         dy == -1 ? MV_UP :
8079                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
8080
8081   if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0))
8082     return FALSE;
8083
8084   if (!player->active || !IN_LEV_FIELD(x, y))
8085     return FALSE;
8086
8087   if (dx && dy)
8088     return FALSE;
8089
8090   if (!dx && !dy)
8091   {
8092     if (player->MovPos == 0)
8093       player->is_pushing = FALSE;
8094
8095     player->is_snapping = FALSE;
8096
8097     if (player->MovPos == 0)
8098     {
8099       player->is_moving = FALSE;
8100       player->is_digging = FALSE;
8101       player->is_collecting = FALSE;
8102     }
8103
8104     return FALSE;
8105   }
8106
8107   if (player->is_snapping)
8108     return FALSE;
8109
8110   player->MovDir = snap_direction;
8111
8112   player->is_moving = FALSE;
8113   player->is_digging = FALSE;
8114   player->is_collecting = FALSE;
8115
8116   if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
8117     return FALSE;
8118
8119   player->is_snapping = TRUE;
8120
8121   player->is_moving = FALSE;
8122   player->is_digging = FALSE;
8123   player->is_collecting = FALSE;
8124
8125   DrawLevelField(x, y);
8126   BackToFront();
8127
8128   return TRUE;
8129 }
8130
8131 boolean DropElement(struct PlayerInfo *player)
8132 {
8133   int jx = player->jx, jy = player->jy;
8134   int old_element;
8135
8136   if (!player->active || player->MovPos)
8137     return FALSE;
8138
8139   old_element = Feld[jx][jy];
8140
8141   /* check if player has anything that can be dropped */
8142   if (player->inventory_size == 0 && player->dynabombs_left == 0)
8143     return FALSE;
8144
8145   /* check if anything can be dropped at the current position */
8146   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
8147     return FALSE;
8148
8149   /* collected custom elements can only be dropped on empty fields */
8150   if (player->inventory_size > 0 &&
8151       IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
8152       && old_element != EL_EMPTY)
8153     return FALSE;
8154
8155   if (old_element != EL_EMPTY)
8156     Back[jx][jy] = old_element;         /* store old element on this field */
8157
8158   MovDelay[jx][jy] = 96;
8159
8160   ResetGfxAnimation(jx, jy);
8161   ResetRandomAnimationValue(jx, jy);
8162
8163   if (player->inventory_size > 0)
8164   {
8165     int new_element = player->inventory_element[--player->inventory_size];
8166
8167     Feld[jx][jy] = (new_element == EL_DYNAMITE ? EL_DYNAMITE_ACTIVE :
8168                     new_element == EL_SP_DISK_RED ? EL_SP_DISK_RED_ACTIVE :
8169                     new_element);
8170
8171     DrawText(DX_DYNAMITE, DY_DYNAMITE,
8172              int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8173
8174     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8175       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8176
8177     PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8178
8179     CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
8180     CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
8181
8182     TestIfElementTouchesCustomElement(jx, jy);
8183   }
8184   else          /* player is dropping a dyna bomb */
8185   {
8186     player->dynabombs_left--;
8187
8188     Feld[jx][jy] =
8189       EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1);
8190
8191     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8192       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8193
8194     PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8195   }
8196
8197   return TRUE;
8198 }
8199
8200 /* ------------------------------------------------------------------------- */
8201 /* game sound playing functions                                              */
8202 /* ------------------------------------------------------------------------- */
8203
8204 static int *loop_sound_frame = NULL;
8205 static int *loop_sound_volume = NULL;
8206
8207 void InitPlaySoundLevel()
8208 {
8209   int num_sounds = getSoundListSize();
8210
8211   if (loop_sound_frame != NULL)
8212     free(loop_sound_frame);
8213
8214   if (loop_sound_volume != NULL)
8215     free(loop_sound_volume);
8216
8217   loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
8218   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
8219 }
8220
8221 static void PlaySoundLevel(int x, int y, int nr)
8222 {
8223   int sx = SCREENX(x), sy = SCREENY(y);
8224   int volume, stereo_position;
8225   int max_distance = 8;
8226   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
8227
8228   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
8229       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
8230     return;
8231
8232   if (!IN_LEV_FIELD(x, y) ||
8233       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
8234       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
8235     return;
8236
8237   volume = SOUND_MAX_VOLUME;
8238
8239   if (!IN_SCR_FIELD(sx, sy))
8240   {
8241     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
8242     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
8243
8244     volume -= volume * (dx > dy ? dx : dy) / max_distance;
8245   }
8246
8247   stereo_position = (SOUND_MAX_LEFT +
8248                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
8249                      (SCR_FIELDX + 2 * max_distance));
8250
8251   if (IS_LOOP_SOUND(nr))
8252   {
8253     /* This assures that quieter loop sounds do not overwrite louder ones,
8254        while restarting sound volume comparison with each new game frame. */
8255
8256     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
8257       return;
8258
8259     loop_sound_volume[nr] = volume;
8260     loop_sound_frame[nr] = FrameCounter;
8261   }
8262
8263   PlaySoundExt(nr, volume, stereo_position, type);
8264 }
8265
8266 static void PlaySoundLevelNearest(int x, int y, int sound_action)
8267 {
8268   PlaySoundLevel(x < LEVELX(BX1) ? LEVELX(BX1) :
8269                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
8270                  y < LEVELY(BY1) ? LEVELY(BY1) :
8271                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
8272                  sound_action);
8273 }
8274
8275 static void PlaySoundLevelAction(int x, int y, int action)
8276 {
8277   PlaySoundLevelElementAction(x, y, Feld[x][y], action);
8278 }
8279
8280 static void PlaySoundLevelElementAction(int x, int y, int element, int action)
8281 {
8282   int sound_effect = element_info[element].sound[action];
8283
8284   if (sound_effect != SND_UNDEFINED)
8285     PlaySoundLevel(x, y, sound_effect);
8286 }
8287
8288 static void PlaySoundLevelActionIfLoop(int x, int y, int action)
8289 {
8290   int sound_effect = element_info[Feld[x][y]].sound[action];
8291
8292   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8293     PlaySoundLevel(x, y, sound_effect);
8294 }
8295
8296 static void StopSoundLevelActionIfLoop(int x, int y, int action)
8297 {
8298   int sound_effect = element_info[Feld[x][y]].sound[action];
8299
8300   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8301     StopSoundExt(sound_effect, SND_CTRL_STOP_SOUND);
8302 }
8303
8304 void RaiseScore(int value)
8305 {
8306   local_player->score += value;
8307   DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
8308 }
8309
8310 void RaiseScoreElement(int element)
8311 {
8312   switch(element)
8313   {
8314     case EL_EMERALD:
8315     case EL_BD_DIAMOND:
8316     case EL_EMERALD_YELLOW:
8317     case EL_EMERALD_RED:
8318     case EL_EMERALD_PURPLE:
8319     case EL_SP_INFOTRON:
8320       RaiseScore(level.score[SC_EMERALD]);
8321       break;
8322     case EL_DIAMOND:
8323       RaiseScore(level.score[SC_DIAMOND]);
8324       break;
8325     case EL_CRYSTAL:
8326       RaiseScore(level.score[SC_CRYSTAL]);
8327       break;
8328     case EL_PEARL:
8329       RaiseScore(level.score[SC_PEARL]);
8330       break;
8331     case EL_BUG:
8332     case EL_BD_BUTTERFLY:
8333     case EL_SP_ELECTRON:
8334       RaiseScore(level.score[SC_BUG]);
8335       break;
8336     case EL_SPACESHIP:
8337     case EL_BD_FIREFLY:
8338     case EL_SP_SNIKSNAK:
8339       RaiseScore(level.score[SC_SPACESHIP]);
8340       break;
8341     case EL_YAMYAM:
8342     case EL_DARK_YAMYAM:
8343       RaiseScore(level.score[SC_YAMYAM]);
8344       break;
8345     case EL_ROBOT:
8346       RaiseScore(level.score[SC_ROBOT]);
8347       break;
8348     case EL_PACMAN:
8349       RaiseScore(level.score[SC_PACMAN]);
8350       break;
8351     case EL_NUT:
8352       RaiseScore(level.score[SC_NUT]);
8353       break;
8354     case EL_DYNAMITE:
8355     case EL_SP_DISK_RED:
8356     case EL_DYNABOMB_INCREASE_NUMBER:
8357     case EL_DYNABOMB_INCREASE_SIZE:
8358     case EL_DYNABOMB_INCREASE_POWER:
8359       RaiseScore(level.score[SC_DYNAMITE]);
8360       break;
8361     case EL_SHIELD_NORMAL:
8362     case EL_SHIELD_DEADLY:
8363       RaiseScore(level.score[SC_SHIELD]);
8364       break;
8365     case EL_EXTRA_TIME:
8366       RaiseScore(level.score[SC_TIME_BONUS]);
8367       break;
8368     case EL_KEY_1:
8369     case EL_KEY_2:
8370     case EL_KEY_3:
8371     case EL_KEY_4:
8372       RaiseScore(level.score[SC_KEY]);
8373       break;
8374     default:
8375       RaiseScore(element_info[element].collect_score);
8376       break;
8377   }
8378 }
8379
8380 void RequestQuitGame(boolean ask_if_really_quit)
8381 {
8382   if (AllPlayersGone ||
8383       !ask_if_really_quit ||
8384       level_editor_test_game ||
8385       Request("Do you really want to quit the game ?",
8386               REQ_ASK | REQ_STAY_CLOSED))
8387   {
8388 #if defined(PLATFORM_UNIX)
8389     if (options.network)
8390       SendToServer_StopPlaying();
8391     else
8392 #endif
8393     {
8394       game_status = GAME_MODE_MAIN;
8395       DrawMainMenu();
8396     }
8397   }
8398   else
8399   {
8400     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
8401   }
8402 }
8403
8404
8405 /* ---------- new game button stuff ---------------------------------------- */
8406
8407 /* graphic position values for game buttons */
8408 #define GAME_BUTTON_XSIZE       30
8409 #define GAME_BUTTON_YSIZE       30
8410 #define GAME_BUTTON_XPOS        5
8411 #define GAME_BUTTON_YPOS        215
8412 #define SOUND_BUTTON_XPOS       5
8413 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
8414
8415 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8416 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8417 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8418 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8419 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8420 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8421
8422 static struct
8423 {
8424   int x, y;
8425   int gadget_id;
8426   char *infotext;
8427 } gamebutton_info[NUM_GAME_BUTTONS] =
8428 {
8429   {
8430     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
8431     GAME_CTRL_ID_STOP,
8432     "stop game"
8433   },
8434   {
8435     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
8436     GAME_CTRL_ID_PAUSE,
8437     "pause game"
8438   },
8439   {
8440     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
8441     GAME_CTRL_ID_PLAY,
8442     "play game"
8443   },
8444   {
8445     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
8446     SOUND_CTRL_ID_MUSIC,
8447     "background music on/off"
8448   },
8449   {
8450     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
8451     SOUND_CTRL_ID_LOOPS,
8452     "sound loops on/off"
8453   },
8454   {
8455     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
8456     SOUND_CTRL_ID_SIMPLE,
8457     "normal sounds on/off"
8458   }
8459 };
8460
8461 void CreateGameButtons()
8462 {
8463   int i;
8464
8465   for (i=0; i<NUM_GAME_BUTTONS; i++)
8466   {
8467     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
8468     struct GadgetInfo *gi;
8469     int button_type;
8470     boolean checked;
8471     unsigned long event_mask;
8472     int gd_xoffset, gd_yoffset;
8473     int gd_x1, gd_x2, gd_y1, gd_y2;
8474     int id = i;
8475
8476     gd_xoffset = gamebutton_info[i].x;
8477     gd_yoffset = gamebutton_info[i].y;
8478     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
8479     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
8480
8481     if (id == GAME_CTRL_ID_STOP ||
8482         id == GAME_CTRL_ID_PAUSE ||
8483         id == GAME_CTRL_ID_PLAY)
8484     {
8485       button_type = GD_TYPE_NORMAL_BUTTON;
8486       checked = FALSE;
8487       event_mask = GD_EVENT_RELEASED;
8488       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8489       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8490     }
8491     else
8492     {
8493       button_type = GD_TYPE_CHECK_BUTTON;
8494       checked =
8495         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
8496          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
8497          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
8498       event_mask = GD_EVENT_PRESSED;
8499       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
8500       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8501     }
8502
8503     gi = CreateGadget(GDI_CUSTOM_ID, id,
8504                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
8505                       GDI_X, DX + gd_xoffset,
8506                       GDI_Y, DY + gd_yoffset,
8507                       GDI_WIDTH, GAME_BUTTON_XSIZE,
8508                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
8509                       GDI_TYPE, button_type,
8510                       GDI_STATE, GD_BUTTON_UNPRESSED,
8511                       GDI_CHECKED, checked,
8512                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
8513                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
8514                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
8515                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
8516                       GDI_EVENT_MASK, event_mask,
8517                       GDI_CALLBACK_ACTION, HandleGameButtons,
8518                       GDI_END);
8519
8520     if (gi == NULL)
8521       Error(ERR_EXIT, "cannot create gadget");
8522
8523     game_gadget[id] = gi;
8524   }
8525 }
8526
8527 void FreeGameButtons()
8528 {
8529   int i;
8530
8531   for (i=0; i<NUM_GAME_BUTTONS; i++)
8532     FreeGadget(game_gadget[i]);
8533 }
8534
8535 static void MapGameButtons()
8536 {
8537   int i;
8538
8539   for (i=0; i<NUM_GAME_BUTTONS; i++)
8540     MapGadget(game_gadget[i]);
8541 }
8542
8543 void UnmapGameButtons()
8544 {
8545   int i;
8546
8547   for (i=0; i<NUM_GAME_BUTTONS; i++)
8548     UnmapGadget(game_gadget[i]);
8549 }
8550
8551 static void HandleGameButtons(struct GadgetInfo *gi)
8552 {
8553   int id = gi->custom_id;
8554
8555   if (game_status != GAME_MODE_PLAYING)
8556     return;
8557
8558   switch (id)
8559   {
8560     case GAME_CTRL_ID_STOP:
8561       RequestQuitGame(TRUE);
8562       break;
8563
8564     case GAME_CTRL_ID_PAUSE:
8565       if (options.network)
8566       {
8567 #if defined(PLATFORM_UNIX)
8568         if (tape.pausing)
8569           SendToServer_ContinuePlaying();
8570         else
8571           SendToServer_PausePlaying();
8572 #endif
8573       }
8574       else
8575         TapeTogglePause(TAPE_TOGGLE_MANUAL);
8576       break;
8577
8578     case GAME_CTRL_ID_PLAY:
8579       if (tape.pausing)
8580       {
8581 #if defined(PLATFORM_UNIX)
8582         if (options.network)
8583           SendToServer_ContinuePlaying();
8584         else
8585 #endif
8586         {
8587           tape.pausing = FALSE;
8588           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
8589         }
8590       }
8591       break;
8592
8593     case SOUND_CTRL_ID_MUSIC:
8594       if (setup.sound_music)
8595       { 
8596         setup.sound_music = FALSE;
8597         FadeMusic();
8598       }
8599       else if (audio.music_available)
8600       { 
8601         setup.sound = setup.sound_music = TRUE;
8602
8603         SetAudioMode(setup.sound);
8604         PlayMusic(level_nr);
8605       }
8606       break;
8607
8608     case SOUND_CTRL_ID_LOOPS:
8609       if (setup.sound_loops)
8610         setup.sound_loops = FALSE;
8611       else if (audio.loops_available)
8612       {
8613         setup.sound = setup.sound_loops = TRUE;
8614         SetAudioMode(setup.sound);
8615       }
8616       break;
8617
8618     case SOUND_CTRL_ID_SIMPLE:
8619       if (setup.sound_simple)
8620         setup.sound_simple = FALSE;
8621       else if (audio.sound_available)
8622       {
8623         setup.sound = setup.sound_simple = TRUE;
8624         SetAudioMode(setup.sound);
8625       }
8626       break;
8627
8628     default:
8629       break;
8630   }
8631 }