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