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