1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE FALSE
35 /* for MovePlayer() */
36 #define MF_NO_ACTION 0
40 /* for ScrollPlayer() */
42 #define SCROLL_GO_ON 1
45 #define EX_PHASE_START 0
46 #define EX_NO_EXPLOSION 0
51 /* special positions in the game control window (relative to control window) */
54 #define XX_EMERALDS 29
55 #define YY_EMERALDS 54
56 #define XX_DYNAMITE 29
57 #define YY_DYNAMITE 89
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)
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
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
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))
92 /* values for other actions */
93 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
95 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
97 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
98 RND(element_info[e].push_delay_random))
99 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
100 RND(element_info[e].move_delay_random))
101 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
102 (element_info[e].move_delay_random))
105 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
106 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
108 (DONT_COLLIDE_WITH(e) && \
110 !PLAYER_ENEMY_PROTECTED(x, y))))
112 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
113 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
115 (DONT_COLLIDE_WITH(e) && \
116 IS_FREE_OR_PLAYER(x, y))))
119 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition) \
120 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
123 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
124 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
126 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y) \
127 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
129 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y) \
130 ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID))
132 #define ENEMY_CAN_ENTER_FIELD(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
134 #define YAMYAM_CAN_ENTER_FIELD(x, y) \
135 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
136 Feld[x][y] == EL_DIAMOND))
138 #define DARK_YAMYAM_CAN_ENTER_FIELD(x, y) \
139 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
140 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
142 #define PACMAN_CAN_ENTER_FIELD(x, y) \
143 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
144 IS_AMOEBOID(Feld[x][y])))
146 #define PIG_CAN_ENTER_FIELD(x, y) \
147 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
148 IS_FOOD_PIG(Feld[x][y])))
150 #define PENGUIN_CAN_ENTER_FIELD(x, y) \
151 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
152 IS_FOOD_PENGUIN(Feld[x][y]) || \
153 Feld[x][y] == EL_EXIT_OPEN || \
154 Feld[x][y] == EL_ACID))
158 #define MAZE_RUNNER_CAN_ENTER_FIELD(x, y) \
159 (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
161 #define MAZE_RUNNER_CAN_ENTER_FIELD(x, y) \
162 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
163 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
167 #define GROUP_NR(e) ((e) - EL_GROUP_START)
168 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
169 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
170 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
172 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
173 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
176 #define CE_ENTER_FIELD_COND(e, x, y) \
177 (!IS_PLAYER(x, y) && \
178 (Feld[x][y] == EL_ACID || \
179 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
181 #define CE_ENTER_FIELD_COND(e, x, y) \
182 (!IS_PLAYER(x, y) && \
183 (Feld[x][y] == EL_ACID || \
184 Feld[x][y] == MOVE_ENTER_EL(e) || \
185 (IS_GROUP_ELEMENT(MOVE_ENTER_EL(e)) && \
186 IS_IN_GROUP_EL(Feld[x][y], MOVE_ENTER_EL(e)))))
189 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
190 ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
192 #define MOLE_CAN_ENTER_FIELD(x, y, condition) \
193 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
195 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
196 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
198 /* game button identifiers */
199 #define GAME_CTRL_ID_STOP 0
200 #define GAME_CTRL_ID_PAUSE 1
201 #define GAME_CTRL_ID_PLAY 2
202 #define SOUND_CTRL_ID_MUSIC 3
203 #define SOUND_CTRL_ID_LOOPS 4
204 #define SOUND_CTRL_ID_SIMPLE 5
206 #define NUM_GAME_BUTTONS 6
209 /* forward declaration for internal use */
211 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
212 static boolean MovePlayer(struct PlayerInfo *, int, int);
213 static void ScrollPlayer(struct PlayerInfo *, int);
214 static void ScrollScreen(struct PlayerInfo *, int);
216 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
218 static void InitBeltMovement(void);
219 static void CloseAllOpenTimegates(void);
220 static void CheckGravityMovement(struct PlayerInfo *);
221 static void KillHeroUnlessEnemyProtected(int, int);
222 static void KillHeroUnlessExplosionProtected(int, int);
224 static void TestIfPlayerTouchesCustomElement(int, int);
225 static void TestIfElementTouchesCustomElement(int, int);
226 static void TestIfElementHitsCustomElement(int, int, int);
228 static void ChangeElement(int, int, int);
229 static boolean CheckTriggeredElementSideChange(int, int, int, int, int);
230 static boolean CheckTriggeredElementChange(int, int, int, int);
231 static boolean CheckElementSideChange(int, int, int, int, int, int);
232 static boolean CheckElementChange(int, int, int, int);
234 static void PlayLevelSound(int, int, int);
235 static void PlayLevelSoundNearest(int, int, int);
236 static void PlayLevelSoundAction(int, int, int);
237 static void PlayLevelSoundElementAction(int, int, int, int);
238 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
239 static void PlayLevelSoundActionIfLoop(int, int, int);
240 static void StopLevelSoundActionIfLoop(int, int, int);
241 static void PlayLevelMusic();
243 static void MapGameButtons();
244 static void HandleGameButtons(struct GadgetInfo *);
246 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
249 /* ------------------------------------------------------------------------- */
250 /* definition of elements that automatically change to other elements after */
251 /* a specified time, eventually calling a function when changing */
252 /* ------------------------------------------------------------------------- */
254 /* forward declaration for changer functions */
255 static void InitBuggyBase(int x, int y);
256 static void WarnBuggyBase(int x, int y);
258 static void InitTrap(int x, int y);
259 static void ActivateTrap(int x, int y);
260 static void ChangeActiveTrap(int x, int y);
262 static void InitRobotWheel(int x, int y);
263 static void RunRobotWheel(int x, int y);
264 static void StopRobotWheel(int x, int y);
266 static void InitTimegateWheel(int x, int y);
267 static void RunTimegateWheel(int x, int y);
269 struct ChangingElementInfo
274 void (*pre_change_function)(int x, int y);
275 void (*change_function)(int x, int y);
276 void (*post_change_function)(int x, int y);
279 static struct ChangingElementInfo change_delay_list[] =
330 EL_SWITCHGATE_OPENING,
338 EL_SWITCHGATE_CLOSING,
339 EL_SWITCHGATE_CLOSED,
371 EL_ACID_SPLASH_RIGHT,
380 EL_SP_BUGGY_BASE_ACTIVATING,
387 EL_SP_BUGGY_BASE_ACTIVATING,
388 EL_SP_BUGGY_BASE_ACTIVE,
395 EL_SP_BUGGY_BASE_ACTIVE,
419 EL_ROBOT_WHEEL_ACTIVE,
427 EL_TIMEGATE_SWITCH_ACTIVE,
448 int push_delay_fixed, push_delay_random;
453 { EL_BALLOON, 0, 0 },
455 { EL_SOKOBAN_OBJECT, 2, 0 },
456 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
457 { EL_SATELLITE, 2, 0 },
458 { EL_SP_DISK_YELLOW, 2, 0 },
460 { EL_UNDEFINED, 0, 0 },
468 move_stepsize_list[] =
470 { EL_AMOEBA_DROP, 2 },
471 { EL_AMOEBA_DROPPING, 2 },
472 { EL_QUICKSAND_FILLING, 1 },
473 { EL_QUICKSAND_EMPTYING, 1 },
474 { EL_MAGIC_WALL_FILLING, 2 },
475 { EL_BD_MAGIC_WALL_FILLING, 2 },
476 { EL_MAGIC_WALL_EMPTYING, 2 },
477 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
487 collect_count_list[] =
490 { EL_BD_DIAMOND, 1 },
491 { EL_EMERALD_YELLOW, 1 },
492 { EL_EMERALD_RED, 1 },
493 { EL_EMERALD_PURPLE, 1 },
495 { EL_SP_INFOTRON, 1 },
509 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
510 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
511 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
512 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
513 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
514 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
515 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
516 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
517 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
518 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
519 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
524 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
526 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
527 CH_EVENT_BIT(CE_DELAY))
528 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
529 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
530 IS_JUST_CHANGING(x, y))
532 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
535 void GetPlayerConfig()
537 if (!audio.sound_available)
538 setup.sound_simple = FALSE;
540 if (!audio.loops_available)
541 setup.sound_loops = FALSE;
543 if (!audio.music_available)
544 setup.sound_music = FALSE;
546 if (!video.fullscreen_available)
547 setup.fullscreen = FALSE;
549 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
551 SetAudioMode(setup.sound);
555 static int getBeltNrFromBeltElement(int element)
557 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
558 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
559 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
562 static int getBeltNrFromBeltActiveElement(int element)
564 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
565 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
566 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
569 static int getBeltNrFromBeltSwitchElement(int element)
571 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
572 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
573 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
576 static int getBeltDirNrFromBeltSwitchElement(int element)
578 static int belt_base_element[4] =
580 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
581 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
582 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
583 EL_CONVEYOR_BELT_4_SWITCH_LEFT
586 int belt_nr = getBeltNrFromBeltSwitchElement(element);
587 int belt_dir_nr = element - belt_base_element[belt_nr];
589 return (belt_dir_nr % 3);
592 static int getBeltDirFromBeltSwitchElement(int element)
594 static int belt_move_dir[3] =
601 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
603 return belt_move_dir[belt_dir_nr];
606 static void InitPlayerField(int x, int y, int element, boolean init_game)
608 if (element == EL_SP_MURPHY)
612 if (stored_player[0].present)
614 Feld[x][y] = EL_SP_MURPHY_CLONE;
620 stored_player[0].use_murphy_graphic = TRUE;
623 Feld[x][y] = EL_PLAYER_1;
629 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
630 int jx = player->jx, jy = player->jy;
632 player->present = TRUE;
634 player->block_last_field = (element == EL_SP_MURPHY ?
635 level.sp_block_last_field :
636 level.block_last_field);
638 if (!options.network || player->connected)
640 player->active = TRUE;
642 /* remove potentially duplicate players */
643 if (StorePlayer[jx][jy] == Feld[x][y])
644 StorePlayer[jx][jy] = 0;
646 StorePlayer[x][y] = Feld[x][y];
650 printf("Player %d activated.\n", player->element_nr);
651 printf("[Local player is %d and currently %s.]\n",
652 local_player->element_nr,
653 local_player->active ? "active" : "not active");
657 Feld[x][y] = EL_EMPTY;
658 player->jx = player->last_jx = x;
659 player->jy = player->last_jy = y;
663 static void InitField(int x, int y, boolean init_game)
665 int element = Feld[x][y];
674 InitPlayerField(x, y, element, init_game);
677 case EL_SOKOBAN_FIELD_PLAYER:
678 element = Feld[x][y] = EL_PLAYER_1;
679 InitField(x, y, init_game);
681 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
682 InitField(x, y, init_game);
685 case EL_SOKOBAN_FIELD_EMPTY:
686 local_player->sokobanfields_still_needed++;
690 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
691 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
692 else if (x > 0 && Feld[x-1][y] == EL_ACID)
693 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
694 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
695 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
696 else if (y > 0 && Feld[x][y-1] == EL_ACID)
697 Feld[x][y] = EL_ACID_POOL_BOTTOM;
698 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
699 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
707 case EL_SPACESHIP_RIGHT:
708 case EL_SPACESHIP_UP:
709 case EL_SPACESHIP_LEFT:
710 case EL_SPACESHIP_DOWN:
712 case EL_BD_BUTTERFLY_RIGHT:
713 case EL_BD_BUTTERFLY_UP:
714 case EL_BD_BUTTERFLY_LEFT:
715 case EL_BD_BUTTERFLY_DOWN:
716 case EL_BD_BUTTERFLY:
717 case EL_BD_FIREFLY_RIGHT:
718 case EL_BD_FIREFLY_UP:
719 case EL_BD_FIREFLY_LEFT:
720 case EL_BD_FIREFLY_DOWN:
722 case EL_PACMAN_RIGHT:
746 if (y == lev_fieldy - 1)
748 Feld[x][y] = EL_AMOEBA_GROWING;
749 Store[x][y] = EL_AMOEBA_WET;
753 case EL_DYNAMITE_ACTIVE:
754 case EL_SP_DISK_RED_ACTIVE:
755 case EL_DYNABOMB_PLAYER_1_ACTIVE:
756 case EL_DYNABOMB_PLAYER_2_ACTIVE:
757 case EL_DYNABOMB_PLAYER_3_ACTIVE:
758 case EL_DYNABOMB_PLAYER_4_ACTIVE:
763 local_player->lights_still_needed++;
767 local_player->friends_still_needed++;
772 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
777 Feld[x][y] = EL_EMPTY;
782 case EL_EM_KEY_1_FILE:
783 Feld[x][y] = EL_EM_KEY_1;
785 case EL_EM_KEY_2_FILE:
786 Feld[x][y] = EL_EM_KEY_2;
788 case EL_EM_KEY_3_FILE:
789 Feld[x][y] = EL_EM_KEY_3;
791 case EL_EM_KEY_4_FILE:
792 Feld[x][y] = EL_EM_KEY_4;
796 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
797 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
798 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
799 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
800 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
801 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
802 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
803 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
804 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
805 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
806 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
807 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
810 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
811 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
812 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
814 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
816 game.belt_dir[belt_nr] = belt_dir;
817 game.belt_dir_nr[belt_nr] = belt_dir_nr;
819 else /* more than one switch -- set it like the first switch */
821 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
826 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
828 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
831 case EL_LIGHT_SWITCH_ACTIVE:
833 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
837 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
839 else if (IS_GROUP_ELEMENT(element))
841 struct ElementGroupInfo *group = element_info[element].group;
842 int last_anim_random_frame = gfx.anim_random_frame;
845 if (group->choice_mode == ANIM_RANDOM)
846 gfx.anim_random_frame = RND(group->num_elements_resolved);
848 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
849 group->choice_mode, 0,
852 if (group->choice_mode == ANIM_RANDOM)
853 gfx.anim_random_frame = last_anim_random_frame;
857 Feld[x][y] = group->element_resolved[element_pos];
859 InitField(x, y, init_game);
865 static inline void InitField_WithBug1(int x, int y, boolean init_game)
867 InitField(x, y, init_game);
869 /* not needed to call InitMovDir() -- already done by InitField()! */
870 if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
871 CAN_MOVE(Feld[x][y]))
875 static inline void InitField_WithBug2(int x, int y, boolean init_game)
877 int old_element = Feld[x][y];
879 InitField(x, y, init_game);
881 /* not needed to call InitMovDir() -- already done by InitField()! */
882 if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
883 CAN_MOVE(old_element) &&
884 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
887 /* this case is in fact a combination of not less than three bugs:
888 first, it calls InitMovDir() for elements that can move, although this is
889 already done by InitField(); then, it checks the element that was at this
890 field _before_ the call to InitField() (which can change it)
895 void DrawGameDoorValues()
899 for (i = 0; i < MAX_PLAYERS; i++)
900 for (j = 0; j < 4; j++)
901 if (stored_player[i].key[j])
902 DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
903 el2edimg(EL_KEY_1 + j));
905 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
906 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
907 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
908 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
909 DrawText(DX + XX_SCORE, DY + YY_SCORE,
910 int2str(local_player->score, 5), FONT_TEXT_2);
911 DrawText(DX + XX_TIME, DY + YY_TIME,
912 int2str(TimeLeft, 3), FONT_TEXT_2);
915 static void resolve_group_element(int group_element, int recursion_depth)
918 static struct ElementGroupInfo *group;
919 struct ElementGroupInfo *actual_group = element_info[group_element].group;
922 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
924 Error(ERR_WARN, "recursion too deep when resolving group element %d",
925 group_element - EL_GROUP_START + 1);
927 /* replace element which caused too deep recursion by question mark */
928 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
933 if (recursion_depth == 0) /* initialization */
935 group = element_info[group_element].group;
936 group_nr = group_element - EL_GROUP_START;
938 group->num_elements_resolved = 0;
939 group->choice_pos = 0;
942 for (i = 0; i < actual_group->num_elements; i++)
944 int element = actual_group->element[i];
946 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
949 if (IS_GROUP_ELEMENT(element))
950 resolve_group_element(element, recursion_depth + 1);
953 group->element_resolved[group->num_elements_resolved++] = element;
954 element_info[element].in_group[group_nr] = TRUE;
959 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
961 printf("::: group %d: %d resolved elements\n",
962 group_element - EL_GROUP_START, group->num_elements_resolved);
963 for (i = 0; i < group->num_elements_resolved; i++)
964 printf("::: - %d ['%s']\n", group->element_resolved[i],
965 element_info[group->element_resolved[i]].token_name);
972 =============================================================================
974 -----------------------------------------------------------------------------
975 initialize game engine due to level / tape version number
976 =============================================================================
979 static void InitGameEngine()
983 /* set game engine from tape file when re-playing, else from level file */
984 game.engine_version = (tape.playing ? tape.engine_version :
987 /* dynamically adjust element properties according to game engine version */
988 InitElementPropertiesEngine(game.engine_version);
991 printf("level %d: level version == %06d\n", level_nr, level.game_version);
992 printf(" tape version == %06d [%s] [file: %06d]\n",
993 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
995 printf(" => game.engine_version == %06d\n", game.engine_version);
998 /* ---------- recursively resolve group elements ------------------------- */
1000 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1001 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1002 element_info[i].in_group[j] = FALSE;
1004 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1005 resolve_group_element(EL_GROUP_START + i, 0);
1007 /* ---------- initialize player's initial move delay --------------------- */
1009 /* dynamically adjust player properties according to game engine version */
1010 game.initial_move_delay =
1011 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1012 INITIAL_MOVE_DELAY_OFF);
1014 /* dynamically adjust player properties according to level information */
1015 game.initial_move_delay_value =
1016 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1018 /* ---------- initialize player's initial push delay --------------------- */
1020 /* dynamically adjust player properties according to game engine version */
1021 game.initial_push_delay_value =
1022 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1024 /* ---------- initialize changing elements ------------------------------- */
1026 /* initialize changing elements information */
1027 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1029 struct ElementInfo *ei = &element_info[i];
1031 /* this pointer might have been changed in the level editor */
1032 ei->change = &ei->change_page[0];
1034 if (!IS_CUSTOM_ELEMENT(i))
1036 ei->change->target_element = EL_EMPTY_SPACE;
1037 ei->change->delay_fixed = 0;
1038 ei->change->delay_random = 0;
1039 ei->change->delay_frames = 1;
1042 ei->change_events = CE_BITMASK_DEFAULT;
1043 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1045 ei->event_page_nr[j] = 0;
1046 ei->event_page[j] = &ei->change_page[0];
1050 /* add changing elements from pre-defined list */
1051 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1053 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1054 struct ElementInfo *ei = &element_info[ch_delay->element];
1056 ei->change->target_element = ch_delay->target_element;
1057 ei->change->delay_fixed = ch_delay->change_delay;
1059 ei->change->pre_change_function = ch_delay->pre_change_function;
1060 ei->change->change_function = ch_delay->change_function;
1061 ei->change->post_change_function = ch_delay->post_change_function;
1063 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1067 /* add change events from custom element configuration */
1068 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1070 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1072 for (j = 0; j < ei->num_change_pages; j++)
1074 if (!ei->change_page[j].can_change)
1077 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1079 /* only add event page for the first page found with this event */
1080 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1081 !(ei->change_events & CH_EVENT_BIT(k)))
1083 ei->change_events |= CH_EVENT_BIT(k);
1084 ei->event_page_nr[k] = j;
1085 ei->event_page[k] = &ei->change_page[j];
1093 /* add change events from custom element configuration */
1094 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1096 int element = EL_CUSTOM_START + i;
1098 /* only add custom elements that change after fixed/random frame delay */
1099 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1100 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1104 /* ---------- initialize trigger events ---------------------------------- */
1106 /* initialize trigger events information */
1107 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1108 trigger_events[i] = EP_BITMASK_DEFAULT;
1111 /* add trigger events from element change event properties */
1112 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1114 struct ElementInfo *ei = &element_info[i];
1116 for (j = 0; j < ei->num_change_pages; j++)
1118 if (!ei->change_page[j].can_change)
1121 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1123 int trigger_element = ei->change_page[j].trigger_element;
1125 if (IS_GROUP_ELEMENT(trigger_element))
1127 struct ElementGroupInfo *group = element_info[trigger_element].group;
1129 for (k = 0; k < group->num_elements_resolved; k++)
1130 trigger_events[group->element_resolved[k]]
1131 |= ei->change_page[j].events;
1134 trigger_events[trigger_element] |= ei->change_page[j].events;
1139 /* add trigger events from element change event properties */
1140 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1141 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1142 trigger_events[element_info[i].change->trigger_element] |=
1143 element_info[i].change->events;
1146 /* ---------- initialize push delay -------------------------------------- */
1148 /* initialize push delay values to default */
1149 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1151 if (!IS_CUSTOM_ELEMENT(i))
1153 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1154 element_info[i].push_delay_random = game.default_push_delay_random;
1158 /* set push delay value for certain elements from pre-defined list */
1159 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1161 int e = push_delay_list[i].element;
1163 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1164 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1167 /* set push delay value for Supaplex elements for newer engine versions */
1168 if (game.engine_version >= VERSION_IDENT(3,0,9,0))
1170 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1172 if (IS_SP_ELEMENT(i))
1174 element_info[i].push_delay_fixed = 6;
1175 element_info[i].push_delay_random = 0;
1180 /* ---------- initialize move stepsize ----------------------------------- */
1182 /* initialize move stepsize values to default */
1183 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1184 if (!IS_CUSTOM_ELEMENT(i))
1185 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1187 /* set move stepsize value for certain elements from pre-defined list */
1188 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1190 int e = move_stepsize_list[i].element;
1192 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1195 /* ---------- initialize move dig/leave ---------------------------------- */
1197 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1199 element_info[i].can_leave_element = FALSE;
1200 element_info[i].can_leave_element_last = FALSE;
1203 /* ---------- initialize gem count --------------------------------------- */
1205 /* initialize gem count values for each element */
1206 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1207 if (!IS_CUSTOM_ELEMENT(i))
1208 element_info[i].collect_count = 0;
1210 /* add gem count values for all elements from pre-defined list */
1211 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1212 element_info[collect_count_list[i].element].collect_count =
1213 collect_count_list[i].count;
1215 /* ---------- initialize access direction -------------------------------- */
1217 /* initialize access direction values to default */
1218 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1219 if (!IS_CUSTOM_ELEMENT(i))
1220 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1222 /* set access direction value for certain elements from pre-defined list */
1223 for (i = 0; tube_access[i].element != EL_UNDEFINED; i++)
1224 element_info[tube_access[i].element].access_direction =
1225 tube_access[i].direction;
1230 =============================================================================
1232 -----------------------------------------------------------------------------
1233 initialize and start new game
1234 =============================================================================
1239 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1240 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1241 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1248 #if USE_NEW_AMOEBA_CODE
1249 printf("Using new amoeba code.\n");
1251 printf("Using old amoeba code.\n");
1256 /* don't play tapes over network */
1257 network_playing = (options.network && !tape.playing);
1259 for (i = 0; i < MAX_PLAYERS; i++)
1261 struct PlayerInfo *player = &stored_player[i];
1263 player->index_nr = i;
1264 player->element_nr = EL_PLAYER_1 + i;
1266 player->present = FALSE;
1267 player->active = FALSE;
1270 player->effective_action = 0;
1271 player->programmed_action = 0;
1274 player->gems_still_needed = level.gems_needed;
1275 player->sokobanfields_still_needed = 0;
1276 player->lights_still_needed = 0;
1277 player->friends_still_needed = 0;
1279 for (j = 0; j < 4; j++)
1280 player->key[j] = FALSE;
1282 player->dynabomb_count = 0;
1283 player->dynabomb_size = 1;
1284 player->dynabombs_left = 0;
1285 player->dynabomb_xl = FALSE;
1287 player->MovDir = MV_NO_MOVING;
1290 player->GfxDir = MV_NO_MOVING;
1291 player->GfxAction = ACTION_DEFAULT;
1293 player->StepFrame = 0;
1295 player->use_murphy_graphic = FALSE;
1297 player->block_last_field = FALSE;
1299 player->actual_frame_counter = 0;
1301 player->step_counter = 0;
1303 player->last_move_dir = MV_NO_MOVING;
1305 player->is_waiting = FALSE;
1306 player->is_moving = FALSE;
1307 player->is_digging = FALSE;
1308 player->is_snapping = FALSE;
1309 player->is_collecting = FALSE;
1310 player->is_pushing = FALSE;
1311 player->is_switching = FALSE;
1312 player->is_dropping = FALSE;
1314 player->is_bored = FALSE;
1315 player->is_sleeping = FALSE;
1317 player->frame_counter_bored = -1;
1318 player->frame_counter_sleeping = -1;
1320 player->anim_delay_counter = 0;
1321 player->post_delay_counter = 0;
1323 player->action_waiting = ACTION_DEFAULT;
1324 player->last_action_waiting = ACTION_DEFAULT;
1325 player->special_action_bored = ACTION_DEFAULT;
1326 player->special_action_sleeping = ACTION_DEFAULT;
1328 player->num_special_action_bored = 0;
1329 player->num_special_action_sleeping = 0;
1331 /* determine number of special actions for bored and sleeping animation */
1332 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1334 boolean found = FALSE;
1336 for (k = 0; k < NUM_DIRECTIONS; k++)
1337 if (el_act_dir2img(player->element_nr, j, k) !=
1338 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1342 player->num_special_action_bored++;
1346 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1348 boolean found = FALSE;
1350 for (k = 0; k < NUM_DIRECTIONS; k++)
1351 if (el_act_dir2img(player->element_nr, j, k) !=
1352 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1356 player->num_special_action_sleeping++;
1361 player->switch_x = -1;
1362 player->switch_y = -1;
1364 player->show_envelope = 0;
1366 player->move_delay = game.initial_move_delay;
1367 player->move_delay_value = game.initial_move_delay_value;
1369 player->move_delay_reset_counter = 0;
1371 player->push_delay = 0;
1372 player->push_delay_value = game.initial_push_delay_value;
1374 player->drop_delay = 0;
1376 player->last_jx = player->last_jy = 0;
1377 player->jx = player->jy = 0;
1379 player->shield_normal_time_left = 0;
1380 player->shield_deadly_time_left = 0;
1382 player->inventory_size = 0;
1384 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1385 SnapField(player, 0, 0);
1387 player->LevelSolved = FALSE;
1388 player->GameOver = FALSE;
1391 network_player_action_received = FALSE;
1393 #if defined(PLATFORM_UNIX)
1394 /* initial null action */
1395 if (network_playing)
1396 SendToServer_MovePlayer(MV_NO_MOVING);
1404 TimeLeft = level.time;
1406 ScreenMovDir = MV_NO_MOVING;
1410 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1412 AllPlayersGone = FALSE;
1414 game.yamyam_content_nr = 0;
1415 game.magic_wall_active = FALSE;
1416 game.magic_wall_time_left = 0;
1417 game.light_time_left = 0;
1418 game.timegate_time_left = 0;
1419 game.switchgate_pos = 0;
1420 game.balloon_dir = MV_NO_MOVING;
1421 game.gravity = level.initial_gravity;
1422 game.explosions_delayed = TRUE;
1424 game.envelope_active = FALSE;
1426 for (i = 0; i < 4; i++)
1428 game.belt_dir[i] = MV_NO_MOVING;
1429 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1432 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1433 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1435 for (x = 0; x < lev_fieldx; x++)
1437 for (y = 0; y < lev_fieldy; y++)
1439 Feld[x][y] = level.field[x][y];
1440 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1441 ChangeDelay[x][y] = 0;
1442 ChangePage[x][y] = -1;
1443 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1445 WasJustMoving[x][y] = 0;
1446 WasJustFalling[x][y] = 0;
1448 Pushed[x][y] = FALSE;
1450 Changed[x][y] = CE_BITMASK_DEFAULT;
1451 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1453 ExplodePhase[x][y] = 0;
1454 ExplodeDelay[x][y] = 0;
1455 ExplodeField[x][y] = EX_NO_EXPLOSION;
1457 RunnerVisit[x][y] = 0;
1458 PlayerVisit[x][y] = 0;
1461 GfxRandom[x][y] = INIT_GFX_RANDOM();
1462 GfxElement[x][y] = EL_UNDEFINED;
1463 GfxAction[x][y] = ACTION_DEFAULT;
1464 GfxDir[x][y] = MV_NO_MOVING;
1468 for (y = 0; y < lev_fieldy; y++)
1470 for (x = 0; x < lev_fieldx; x++)
1472 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1474 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1476 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1479 InitField(x, y, TRUE);
1485 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1486 emulate_sb ? EMU_SOKOBAN :
1487 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1489 /* initialize explosion and ignition delay */
1490 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1492 if (!IS_CUSTOM_ELEMENT(i))
1495 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
1496 int last_phase = num_phase * delay;
1497 int half_phase = (num_phase / 2) * delay;
1499 element_info[i].explosion_delay = last_phase;
1500 element_info[i].ignition_delay = half_phase;
1502 if (i == EL_BLACK_ORB)
1503 element_info[i].ignition_delay = 1;
1506 if (element_info[i].explosion_delay < 2) /* !!! check again !!! */
1507 element_info[i].explosion_delay = 2;
1509 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1510 element_info[i].ignition_delay = 1;
1513 /* correct non-moving belts to start moving left */
1514 for (i = 0; i < 4; i++)
1515 if (game.belt_dir[i] == MV_NO_MOVING)
1516 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1518 /* check if any connected player was not found in playfield */
1519 for (i = 0; i < MAX_PLAYERS; i++)
1521 struct PlayerInfo *player = &stored_player[i];
1523 if (player->connected && !player->present)
1525 for (j = 0; j < MAX_PLAYERS; j++)
1527 struct PlayerInfo *some_player = &stored_player[j];
1528 int jx = some_player->jx, jy = some_player->jy;
1530 /* assign first free player found that is present in the playfield */
1531 if (some_player->present && !some_player->connected)
1533 player->present = TRUE;
1534 player->active = TRUE;
1536 some_player->present = FALSE;
1537 some_player->active = FALSE;
1539 StorePlayer[jx][jy] = player->element_nr;
1540 player->jx = player->last_jx = jx;
1541 player->jy = player->last_jy = jy;
1551 /* when playing a tape, eliminate all players which do not participate */
1553 for (i = 0; i < MAX_PLAYERS; i++)
1555 if (stored_player[i].active && !tape.player_participates[i])
1557 struct PlayerInfo *player = &stored_player[i];
1558 int jx = player->jx, jy = player->jy;
1560 player->active = FALSE;
1561 StorePlayer[jx][jy] = 0;
1562 Feld[jx][jy] = EL_EMPTY;
1566 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1568 /* when in single player mode, eliminate all but the first active player */
1570 for (i = 0; i < MAX_PLAYERS; i++)
1572 if (stored_player[i].active)
1574 for (j = i + 1; j < MAX_PLAYERS; j++)
1576 if (stored_player[j].active)
1578 struct PlayerInfo *player = &stored_player[j];
1579 int jx = player->jx, jy = player->jy;
1581 player->active = FALSE;
1582 player->present = FALSE;
1584 StorePlayer[jx][jy] = 0;
1585 Feld[jx][jy] = EL_EMPTY;
1592 /* when recording the game, store which players take part in the game */
1595 for (i = 0; i < MAX_PLAYERS; i++)
1596 if (stored_player[i].active)
1597 tape.player_participates[i] = TRUE;
1602 for (i = 0; i < MAX_PLAYERS; i++)
1604 struct PlayerInfo *player = &stored_player[i];
1606 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1611 if (local_player == player)
1612 printf("Player %d is local player.\n", i+1);
1616 if (BorderElement == EL_EMPTY)
1619 SBX_Right = lev_fieldx - SCR_FIELDX;
1621 SBY_Lower = lev_fieldy - SCR_FIELDY;
1626 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1628 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1631 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1632 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1634 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1635 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1637 /* if local player not found, look for custom element that might create
1638 the player (make some assumptions about the right custom element) */
1639 if (!local_player->present)
1641 int start_x = 0, start_y = 0;
1642 int found_rating = 0;
1643 int found_element = EL_UNDEFINED;
1645 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1647 int element = Feld[x][y];
1652 if (!IS_CUSTOM_ELEMENT(element))
1655 if (CAN_CHANGE(element))
1657 for (i = 0; i < element_info[element].num_change_pages; i++)
1659 content = element_info[element].change_page[i].target_element;
1660 is_player = ELEM_IS_PLAYER(content);
1662 if (is_player && (found_rating < 3 || element < found_element))
1668 found_element = element;
1673 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1675 content = element_info[element].content[xx][yy];
1676 is_player = ELEM_IS_PLAYER(content);
1678 if (is_player && (found_rating < 2 || element < found_element))
1680 start_x = x + xx - 1;
1681 start_y = y + yy - 1;
1684 found_element = element;
1687 if (!CAN_CHANGE(element))
1690 for (i = 0; i < element_info[element].num_change_pages; i++)
1692 content = element_info[element].change_page[i].content[xx][yy];
1693 is_player = ELEM_IS_PLAYER(content);
1695 if (is_player && (found_rating < 1 || element < found_element))
1697 start_x = x + xx - 1;
1698 start_y = y + yy - 1;
1701 found_element = element;
1707 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1708 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1711 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1712 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1718 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1719 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1720 local_player->jx - MIDPOSX);
1722 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1723 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1724 local_player->jy - MIDPOSY);
1726 scroll_x = SBX_Left;
1727 scroll_y = SBY_Upper;
1728 if (local_player->jx >= SBX_Left + MIDPOSX)
1729 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1730 local_player->jx - MIDPOSX :
1732 if (local_player->jy >= SBY_Upper + MIDPOSY)
1733 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1734 local_player->jy - MIDPOSY :
1739 CloseDoor(DOOR_CLOSE_1);
1744 /* after drawing the level, correct some elements */
1745 if (game.timegate_time_left == 0)
1746 CloseAllOpenTimegates();
1748 if (setup.soft_scrolling)
1749 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1751 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1754 /* copy default game door content to main double buffer */
1755 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1756 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1759 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1762 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1763 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1764 BlitBitmap(drawto, drawto,
1765 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1766 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1767 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1768 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1771 DrawGameDoorValues();
1775 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1776 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1777 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1781 /* copy actual game door content to door double buffer for OpenDoor() */
1782 BlitBitmap(drawto, bitmap_db_door,
1783 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1785 OpenDoor(DOOR_OPEN_ALL);
1787 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1789 if (setup.sound_music)
1792 KeyboardAutoRepeatOffUnlessAutoplay();
1796 for (i = 0; i < 4; i++)
1797 printf("Player %d %sactive.\n",
1798 i + 1, (stored_player[i].active ? "" : "not "));
1802 printf("::: starting game [%d]\n", FrameCounter);
1806 void InitMovDir(int x, int y)
1808 int i, element = Feld[x][y];
1809 static int xy[4][2] =
1816 static int direction[3][4] =
1818 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1819 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1820 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1829 Feld[x][y] = EL_BUG;
1830 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1833 case EL_SPACESHIP_RIGHT:
1834 case EL_SPACESHIP_UP:
1835 case EL_SPACESHIP_LEFT:
1836 case EL_SPACESHIP_DOWN:
1837 Feld[x][y] = EL_SPACESHIP;
1838 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1841 case EL_BD_BUTTERFLY_RIGHT:
1842 case EL_BD_BUTTERFLY_UP:
1843 case EL_BD_BUTTERFLY_LEFT:
1844 case EL_BD_BUTTERFLY_DOWN:
1845 Feld[x][y] = EL_BD_BUTTERFLY;
1846 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1849 case EL_BD_FIREFLY_RIGHT:
1850 case EL_BD_FIREFLY_UP:
1851 case EL_BD_FIREFLY_LEFT:
1852 case EL_BD_FIREFLY_DOWN:
1853 Feld[x][y] = EL_BD_FIREFLY;
1854 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1857 case EL_PACMAN_RIGHT:
1859 case EL_PACMAN_LEFT:
1860 case EL_PACMAN_DOWN:
1861 Feld[x][y] = EL_PACMAN;
1862 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1865 case EL_SP_SNIKSNAK:
1866 MovDir[x][y] = MV_UP;
1869 case EL_SP_ELECTRON:
1870 MovDir[x][y] = MV_LEFT;
1877 Feld[x][y] = EL_MOLE;
1878 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1882 if (IS_CUSTOM_ELEMENT(element))
1884 struct ElementInfo *ei = &element_info[element];
1885 int move_direction_initial = ei->move_direction_initial;
1886 int move_pattern = ei->move_pattern;
1888 if (move_direction_initial == MV_START_PREVIOUS)
1890 if (MovDir[x][y] != MV_NO_MOVING)
1893 move_direction_initial = MV_START_AUTOMATIC;
1896 if (move_direction_initial == MV_START_RANDOM)
1897 MovDir[x][y] = 1 << RND(4);
1898 else if (move_direction_initial & MV_ANY_DIRECTION)
1899 MovDir[x][y] = move_direction_initial;
1900 else if (move_pattern == MV_ALL_DIRECTIONS ||
1901 move_pattern == MV_TURNING_LEFT ||
1902 move_pattern == MV_TURNING_RIGHT ||
1903 move_pattern == MV_TURNING_LEFT_RIGHT ||
1904 move_pattern == MV_TURNING_RIGHT_LEFT ||
1905 move_pattern == MV_TURNING_RANDOM)
1906 MovDir[x][y] = 1 << RND(4);
1907 else if (move_pattern == MV_HORIZONTAL)
1908 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1909 else if (move_pattern == MV_VERTICAL)
1910 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1911 else if (move_pattern & MV_ANY_DIRECTION)
1912 MovDir[x][y] = element_info[element].move_pattern;
1913 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
1914 move_pattern == MV_ALONG_RIGHT_SIDE)
1916 for (i = 0; i < 4; i++)
1918 int x1 = x + xy[i][0];
1919 int y1 = y + xy[i][1];
1921 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1923 if (move_pattern == MV_ALONG_RIGHT_SIDE)
1924 MovDir[x][y] = direction[0][i];
1926 MovDir[x][y] = direction[1][i];
1935 MovDir[x][y] = 1 << RND(4);
1937 if (element != EL_BUG &&
1938 element != EL_SPACESHIP &&
1939 element != EL_BD_BUTTERFLY &&
1940 element != EL_BD_FIREFLY)
1943 for (i = 0; i < 4; i++)
1945 int x1 = x + xy[i][0];
1946 int y1 = y + xy[i][1];
1948 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1950 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1952 MovDir[x][y] = direction[0][i];
1955 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1956 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1958 MovDir[x][y] = direction[1][i];
1967 GfxDir[x][y] = MovDir[x][y];
1970 void InitAmoebaNr(int x, int y)
1973 int group_nr = AmoebeNachbarNr(x, y);
1977 for (i = 1; i < MAX_NUM_AMOEBA; i++)
1979 if (AmoebaCnt[i] == 0)
1987 AmoebaNr[x][y] = group_nr;
1988 AmoebaCnt[group_nr]++;
1989 AmoebaCnt2[group_nr]++;
1995 boolean raise_level = FALSE;
1997 if (local_player->MovPos)
2001 if (tape.auto_play) /* tape might already be stopped here */
2002 tape.auto_play_level_solved = TRUE;
2004 if (tape.playing && tape.auto_play)
2005 tape.auto_play_level_solved = TRUE;
2008 local_player->LevelSolved = FALSE;
2010 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2014 if (!tape.playing && setup.sound_loops)
2015 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2016 SND_CTRL_PLAY_LOOP);
2018 while (TimeLeft > 0)
2020 if (!tape.playing && !setup.sound_loops)
2021 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2022 if (TimeLeft > 0 && !(TimeLeft % 10))
2023 RaiseScore(level.score[SC_TIME_BONUS]);
2024 if (TimeLeft > 100 && !(TimeLeft % 10))
2028 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
2035 if (!tape.playing && setup.sound_loops)
2036 StopSound(SND_GAME_LEVELTIME_BONUS);
2038 else if (level.time == 0) /* level without time limit */
2040 if (!tape.playing && setup.sound_loops)
2041 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2042 SND_CTRL_PLAY_LOOP);
2044 while (TimePlayed < 999)
2046 if (!tape.playing && !setup.sound_loops)
2047 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2048 if (TimePlayed < 999 && !(TimePlayed % 10))
2049 RaiseScore(level.score[SC_TIME_BONUS]);
2050 if (TimePlayed < 900 && !(TimePlayed % 10))
2054 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
2061 if (!tape.playing && setup.sound_loops)
2062 StopSound(SND_GAME_LEVELTIME_BONUS);
2065 /* close exit door after last player */
2066 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2067 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2069 int element = Feld[ExitX][ExitY];
2071 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2072 EL_SP_EXIT_CLOSING);
2074 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2077 /* Hero disappears */
2078 DrawLevelField(ExitX, ExitY);
2084 CloseDoor(DOOR_CLOSE_1);
2089 SaveTape(tape.level_nr); /* Ask to save tape */
2092 if (level_nr == leveldir_current->handicap_level)
2094 leveldir_current->handicap_level++;
2095 SaveLevelSetup_SeriesInfo();
2098 if (level_editor_test_game)
2099 local_player->score = -1; /* no highscore when playing from editor */
2100 else if (level_nr < leveldir_current->last_level)
2101 raise_level = TRUE; /* advance to next level */
2103 if ((hi_pos = NewHiScore()) >= 0)
2105 game_status = GAME_MODE_SCORES;
2106 DrawHallOfFame(hi_pos);
2115 game_status = GAME_MODE_MAIN;
2132 LoadScore(level_nr);
2134 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2135 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2138 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2140 if (local_player->score > highscore[k].Score)
2142 /* player has made it to the hall of fame */
2144 if (k < MAX_SCORE_ENTRIES - 1)
2146 int m = MAX_SCORE_ENTRIES - 1;
2149 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2150 if (!strcmp(setup.player_name, highscore[l].Name))
2152 if (m == k) /* player's new highscore overwrites his old one */
2156 for (l = m; l > k; l--)
2158 strcpy(highscore[l].Name, highscore[l - 1].Name);
2159 highscore[l].Score = highscore[l - 1].Score;
2166 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2167 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2168 highscore[k].Score = local_player->score;
2174 else if (!strncmp(setup.player_name, highscore[k].Name,
2175 MAX_PLAYER_NAME_LEN))
2176 break; /* player already there with a higher score */
2182 SaveScore(level_nr);
2187 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2189 if (player->GfxAction != action || player->GfxDir != dir)
2192 printf("Player frame reset! (%d => %d, %d => %d)\n",
2193 player->GfxAction, action, player->GfxDir, dir);
2196 player->GfxAction = action;
2197 player->GfxDir = dir;
2199 player->StepFrame = 0;
2203 static void ResetRandomAnimationValue(int x, int y)
2205 GfxRandom[x][y] = INIT_GFX_RANDOM();
2208 static void ResetGfxAnimation(int x, int y)
2211 GfxAction[x][y] = ACTION_DEFAULT;
2212 GfxDir[x][y] = MovDir[x][y];
2215 void InitMovingField(int x, int y, int direction)
2217 int element = Feld[x][y];
2218 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2219 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2223 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2224 ResetGfxAnimation(x, y);
2226 MovDir[newx][newy] = MovDir[x][y] = direction;
2227 GfxDir[x][y] = direction;
2229 if (Feld[newx][newy] == EL_EMPTY)
2230 Feld[newx][newy] = EL_BLOCKED;
2232 if (direction == MV_DOWN && CAN_FALL(element))
2233 GfxAction[x][y] = ACTION_FALLING;
2235 GfxAction[x][y] = ACTION_MOVING;
2237 GfxFrame[newx][newy] = GfxFrame[x][y];
2238 GfxRandom[newx][newy] = GfxRandom[x][y];
2239 GfxAction[newx][newy] = GfxAction[x][y];
2240 GfxDir[newx][newy] = GfxDir[x][y];
2243 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2245 int direction = MovDir[x][y];
2246 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2247 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2253 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2255 int oldx = x, oldy = y;
2256 int direction = MovDir[x][y];
2258 if (direction == MV_LEFT)
2260 else if (direction == MV_RIGHT)
2262 else if (direction == MV_UP)
2264 else if (direction == MV_DOWN)
2267 *comes_from_x = oldx;
2268 *comes_from_y = oldy;
2271 int MovingOrBlocked2Element(int x, int y)
2273 int element = Feld[x][y];
2275 if (element == EL_BLOCKED)
2279 Blocked2Moving(x, y, &oldx, &oldy);
2280 return Feld[oldx][oldy];
2286 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2288 /* like MovingOrBlocked2Element(), but if element is moving
2289 and (x,y) is the field the moving element is just leaving,
2290 return EL_BLOCKED instead of the element value */
2291 int element = Feld[x][y];
2293 if (IS_MOVING(x, y))
2295 if (element == EL_BLOCKED)
2299 Blocked2Moving(x, y, &oldx, &oldy);
2300 return Feld[oldx][oldy];
2309 static void RemoveField(int x, int y)
2311 Feld[x][y] = EL_EMPTY;
2318 ChangeDelay[x][y] = 0;
2319 ChangePage[x][y] = -1;
2320 Pushed[x][y] = FALSE;
2322 GfxElement[x][y] = EL_UNDEFINED;
2323 GfxAction[x][y] = ACTION_DEFAULT;
2324 GfxDir[x][y] = MV_NO_MOVING;
2327 void RemoveMovingField(int x, int y)
2329 int oldx = x, oldy = y, newx = x, newy = y;
2330 int element = Feld[x][y];
2331 int next_element = EL_UNDEFINED;
2333 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2336 if (IS_MOVING(x, y))
2338 Moving2Blocked(x, y, &newx, &newy);
2339 if (Feld[newx][newy] != EL_BLOCKED)
2342 else if (element == EL_BLOCKED)
2344 Blocked2Moving(x, y, &oldx, &oldy);
2345 if (!IS_MOVING(oldx, oldy))
2349 if (element == EL_BLOCKED &&
2350 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2351 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2352 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2353 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2354 next_element = get_next_element(Feld[oldx][oldy]);
2356 RemoveField(oldx, oldy);
2357 RemoveField(newx, newy);
2359 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2361 if (next_element != EL_UNDEFINED)
2362 Feld[oldx][oldy] = next_element;
2364 DrawLevelField(oldx, oldy);
2365 DrawLevelField(newx, newy);
2368 void DrawDynamite(int x, int y)
2370 int sx = SCREENX(x), sy = SCREENY(y);
2371 int graphic = el2img(Feld[x][y]);
2374 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2377 if (IS_WALKABLE_INSIDE(Back[x][y]))
2381 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2382 else if (Store[x][y])
2383 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2385 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2388 if (Back[x][y] || Store[x][y])
2389 DrawGraphicThruMask(sx, sy, graphic, frame);
2391 DrawGraphic(sx, sy, graphic, frame);
2393 if (game.emulation == EMU_SUPAPLEX)
2394 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2395 else if (Store[x][y])
2396 DrawGraphicThruMask(sx, sy, graphic, frame);
2398 DrawGraphic(sx, sy, graphic, frame);
2402 void CheckDynamite(int x, int y)
2404 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2408 if (MovDelay[x][y] != 0)
2411 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2418 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2420 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2421 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2422 StopSound(SND_DYNAMITE_ACTIVE);
2424 StopSound(SND_DYNABOMB_ACTIVE);
2430 void RelocatePlayer(int x, int y, int element_raw)
2432 int element = (element_raw == EL_SP_MURPHY ? EL_PLAYER_1 : element_raw);
2433 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2434 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2435 boolean no_delay = (tape.index_search);
2436 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2437 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2439 if (player->GameOver) /* do not reanimate dead player */
2443 RemoveField(x, y); /* temporarily remove newly placed player */
2444 DrawLevelField(x, y);
2447 if (player->present)
2449 while (player->MovPos)
2451 ScrollPlayer(player, SCROLL_GO_ON);
2452 ScrollScreen(NULL, SCROLL_GO_ON);
2458 Delay(wait_delay_value);
2461 DrawPlayer(player); /* needed here only to cleanup last field */
2462 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2464 player->is_moving = FALSE;
2467 Feld[x][y] = element;
2468 InitPlayerField(x, y, element, TRUE);
2470 if (player == local_player)
2472 int scroll_xx = -999, scroll_yy = -999;
2474 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2477 int fx = FX, fy = FY;
2479 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2480 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2481 local_player->jx - MIDPOSX);
2483 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2484 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2485 local_player->jy - MIDPOSY);
2487 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2488 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2493 fx += dx * TILEX / 2;
2494 fy += dy * TILEY / 2;
2496 ScrollLevel(dx, dy);
2499 /* scroll in two steps of half tile size to make things smoother */
2500 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2502 Delay(wait_delay_value);
2504 /* scroll second step to align at full tile size */
2506 Delay(wait_delay_value);
2511 void Explode(int ex, int ey, int phase, int mode)
2518 /* !!! eliminate this variable !!! */
2519 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2524 int last_phase = num_phase * delay;
2525 int half_phase = (num_phase / 2) * delay;
2526 int first_phase_after_start = EX_PHASE_START + 1;
2530 if (game.explosions_delayed)
2532 ExplodeField[ex][ey] = mode;
2536 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2538 int center_element = Feld[ex][ey];
2541 /* --- This is only really needed (and now handled) in "Impact()". --- */
2542 /* do not explode moving elements that left the explode field in time */
2543 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2544 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2548 if (mode == EX_NORMAL || mode == EX_CENTER)
2549 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2551 /* remove things displayed in background while burning dynamite */
2552 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2555 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2557 /* put moving element to center field (and let it explode there) */
2558 center_element = MovingOrBlocked2Element(ex, ey);
2559 RemoveMovingField(ex, ey);
2560 Feld[ex][ey] = center_element;
2564 last_phase = element_info[center_element].explosion_delay;
2567 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2569 int xx = x - ex + 1;
2570 int yy = y - ey + 1;
2573 if (!IN_LEV_FIELD(x, y) ||
2574 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2575 (x != ex || y != ey)))
2578 element = Feld[x][y];
2580 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2582 element = MovingOrBlocked2Element(x, y);
2584 if (!IS_EXPLOSION_PROOF(element))
2585 RemoveMovingField(x, y);
2591 if (IS_EXPLOSION_PROOF(element))
2594 /* indestructible elements can only explode in center (but not flames) */
2595 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2596 element == EL_FLAMES)
2601 if ((IS_INDESTRUCTIBLE(element) &&
2602 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2603 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2604 element == EL_FLAMES)
2608 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2610 if (IS_ACTIVE_BOMB(element))
2612 /* re-activate things under the bomb like gate or penguin */
2613 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2620 /* save walkable background elements while explosion on same tile */
2622 if (IS_INDESTRUCTIBLE(element))
2623 Back[x][y] = element;
2625 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2626 Back[x][y] = element;
2629 /* ignite explodable elements reached by other explosion */
2630 if (element == EL_EXPLOSION)
2631 element = Store2[x][y];
2634 if (AmoebaNr[x][y] &&
2635 (element == EL_AMOEBA_FULL ||
2636 element == EL_BD_AMOEBA ||
2637 element == EL_AMOEBA_GROWING))
2639 AmoebaCnt[AmoebaNr[x][y]]--;
2640 AmoebaCnt2[AmoebaNr[x][y]]--;
2646 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
2648 switch(StorePlayer[ex][ey])
2651 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
2654 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
2657 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
2661 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
2665 if (game.emulation == EMU_SUPAPLEX)
2666 Store[x][y] = EL_EMPTY;
2668 else if (center_element == EL_MOLE)
2669 Store[x][y] = EL_EMERALD_RED;
2670 else if (center_element == EL_PENGUIN)
2671 Store[x][y] = EL_EMERALD_PURPLE;
2672 else if (center_element == EL_BUG)
2673 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2674 else if (center_element == EL_BD_BUTTERFLY)
2675 Store[x][y] = EL_BD_DIAMOND;
2676 else if (center_element == EL_SP_ELECTRON)
2677 Store[x][y] = EL_SP_INFOTRON;
2678 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2679 Store[x][y] = level.amoeba_content;
2680 else if (center_element == EL_YAMYAM)
2681 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2682 else if (IS_CUSTOM_ELEMENT(center_element) &&
2683 element_info[center_element].content[xx][yy] != EL_EMPTY)
2684 Store[x][y] = element_info[center_element].content[xx][yy];
2685 else if (element == EL_WALL_EMERALD)
2686 Store[x][y] = EL_EMERALD;
2687 else if (element == EL_WALL_DIAMOND)
2688 Store[x][y] = EL_DIAMOND;
2689 else if (element == EL_WALL_BD_DIAMOND)
2690 Store[x][y] = EL_BD_DIAMOND;
2691 else if (element == EL_WALL_EMERALD_YELLOW)
2692 Store[x][y] = EL_EMERALD_YELLOW;
2693 else if (element == EL_WALL_EMERALD_RED)
2694 Store[x][y] = EL_EMERALD_RED;
2695 else if (element == EL_WALL_EMERALD_PURPLE)
2696 Store[x][y] = EL_EMERALD_PURPLE;
2697 else if (element == EL_WALL_PEARL)
2698 Store[x][y] = EL_PEARL;
2699 else if (element == EL_WALL_CRYSTAL)
2700 Store[x][y] = EL_CRYSTAL;
2701 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2702 Store[x][y] = element_info[element].content[1][1];
2704 Store[x][y] = EL_EMPTY;
2706 if (x != ex || y != ey ||
2707 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2708 Store2[x][y] = element;
2711 if (AmoebaNr[x][y] &&
2712 (element == EL_AMOEBA_FULL ||
2713 element == EL_BD_AMOEBA ||
2714 element == EL_AMOEBA_GROWING))
2716 AmoebaCnt[AmoebaNr[x][y]]--;
2717 AmoebaCnt2[AmoebaNr[x][y]]--;
2723 MovDir[x][y] = MovPos[x][y] = 0;
2724 GfxDir[x][y] = MovDir[x][y];
2729 Feld[x][y] = EL_EXPLOSION;
2731 GfxElement[x][y] = center_element;
2733 GfxElement[x][y] = EL_UNDEFINED;
2736 ExplodePhase[x][y] = 1;
2738 ExplodeDelay[x][y] = last_phase;
2743 if (center_element == EL_YAMYAM)
2744 game.yamyam_content_nr =
2745 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2757 last_phase = ExplodeDelay[x][y];
2760 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2764 /* activate this even in non-DEBUG version until cause for crash in
2765 getGraphicAnimationFrame() (see below) is found and eliminated */
2769 if (GfxElement[x][y] == EL_UNDEFINED)
2772 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2773 printf("Explode(): This should never happen!\n");
2776 GfxElement[x][y] = EL_EMPTY;
2782 border_element = Store2[x][y];
2783 if (IS_PLAYER(x, y))
2784 border_element = StorePlayer[x][y];
2786 if (phase == element_info[border_element].ignition_delay ||
2787 phase == last_phase)
2789 boolean border_explosion = FALSE;
2792 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
2794 if (IS_PLAYER(x, y))
2797 KillHeroUnlessExplosionProtected(x, y);
2798 border_explosion = TRUE;
2801 if (phase == last_phase)
2802 printf("::: IS_PLAYER\n");
2805 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
2807 Feld[x][y] = Store2[x][y];
2810 border_explosion = TRUE;
2813 if (phase == last_phase)
2814 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
2817 else if (border_element == EL_AMOEBA_TO_DIAMOND)
2819 AmoebeUmwandeln(x, y);
2821 border_explosion = TRUE;
2824 if (phase == last_phase)
2825 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
2826 element_info[border_element].explosion_delay,
2827 element_info[border_element].ignition_delay,
2833 /* if an element just explodes due to another explosion (chain-reaction),
2834 do not immediately end the new explosion when it was the last frame of
2835 the explosion (as it would be done in the following "if"-statement!) */
2836 if (border_explosion && phase == last_phase)
2843 if (phase == first_phase_after_start)
2845 int element = Store2[x][y];
2847 if (element == EL_BLACK_ORB)
2849 Feld[x][y] = Store2[x][y];
2854 else if (phase == half_phase)
2856 int element = Store2[x][y];
2858 if (IS_PLAYER(x, y))
2859 KillHeroUnlessExplosionProtected(x, y);
2860 else if (CAN_EXPLODE_BY_EXPLOSION(element))
2862 Feld[x][y] = Store2[x][y];
2866 else if (element == EL_AMOEBA_TO_DIAMOND)
2867 AmoebeUmwandeln(x, y);
2871 if (phase == last_phase)
2875 element = Feld[x][y] = Store[x][y];
2876 Store[x][y] = Store2[x][y] = 0;
2877 GfxElement[x][y] = EL_UNDEFINED;
2879 /* player can escape from explosions and might therefore be still alive */
2880 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
2881 element <= EL_PLAYER_IS_EXPLODING_4)
2882 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
2884 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
2885 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
2886 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
2889 /* restore probably existing indestructible background element */
2890 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2891 element = Feld[x][y] = Back[x][y];
2894 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2895 GfxDir[x][y] = MV_NO_MOVING;
2896 ChangeDelay[x][y] = 0;
2897 ChangePage[x][y] = -1;
2900 InitField_WithBug2(x, y, FALSE);
2902 InitField(x, y, FALSE);
2904 /* !!! not needed !!! */
2906 if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
2907 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
2910 if (CAN_MOVE(element))
2915 DrawLevelField(x, y);
2917 TestIfElementTouchesCustomElement(x, y);
2919 if (GFX_CRUMBLED(element))
2920 DrawLevelFieldCrumbledSandNeighbours(x, y);
2922 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
2923 StorePlayer[x][y] = 0;
2925 if (ELEM_IS_PLAYER(element))
2926 RelocatePlayer(x, y, element);
2929 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2931 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2935 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2937 int stored = Store[x][y];
2938 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2939 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2942 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2945 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2946 element_info[GfxElement[x][y]].token_name,
2951 DrawLevelFieldCrumbledSand(x, y);
2953 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2955 DrawLevelElement(x, y, Back[x][y]);
2956 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2958 else if (IS_WALKABLE_UNDER(Back[x][y]))
2960 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2961 DrawLevelElementThruMask(x, y, Back[x][y]);
2963 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2964 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2968 void DynaExplode(int ex, int ey)
2971 int dynabomb_element = Feld[ex][ey];
2972 int dynabomb_size = 1;
2973 boolean dynabomb_xl = FALSE;
2974 struct PlayerInfo *player;
2975 static int xy[4][2] =
2983 if (IS_ACTIVE_BOMB(dynabomb_element))
2985 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
2986 dynabomb_size = player->dynabomb_size;
2987 dynabomb_xl = player->dynabomb_xl;
2988 player->dynabombs_left++;
2991 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2993 for (i = 0; i < 4; i++)
2995 for (j = 1; j <= dynabomb_size; j++)
2997 int x = ex + j * xy[i % 4][0];
2998 int y = ey + j * xy[i % 4][1];
3001 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3004 element = Feld[x][y];
3006 /* do not restart explosions of fields with active bombs */
3007 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3010 Explode(x, y, EX_PHASE_START, EX_BORDER);
3012 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3013 if (element != EL_EMPTY &&
3014 element != EL_SAND &&
3015 element != EL_EXPLOSION &&
3022 void Bang(int x, int y)
3025 int element = MovingOrBlocked2Element(x, y);
3027 int element = Feld[x][y];
3031 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3033 if (IS_PLAYER(x, y))
3036 struct PlayerInfo *player = PLAYERINFO(x, y);
3038 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3039 player->element_nr);
3044 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3046 if (game.emulation == EMU_SUPAPLEX)
3047 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3049 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3054 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3062 case EL_BD_BUTTERFLY:
3065 case EL_DARK_YAMYAM:
3069 RaiseScoreElement(element);
3070 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3072 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3073 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3074 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3075 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3076 case EL_DYNABOMB_INCREASE_NUMBER:
3077 case EL_DYNABOMB_INCREASE_SIZE:
3078 case EL_DYNABOMB_INCREASE_POWER:
3083 case EL_LAMP_ACTIVE:
3084 if (IS_PLAYER(x, y))
3085 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3087 Explode(x, y, EX_PHASE_START, EX_CENTER);
3090 if (CAN_EXPLODE_DYNA(element))
3092 else if (CAN_EXPLODE_1X1(element))
3093 Explode(x, y, EX_PHASE_START, EX_CENTER);
3095 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3099 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3102 void SplashAcid(int x, int y)
3104 int element = Feld[x][y];
3106 if (element != EL_ACID_SPLASH_LEFT &&
3107 element != EL_ACID_SPLASH_RIGHT)
3109 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3111 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
3112 (!IN_LEV_FIELD(x-1, y-1) ||
3113 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
3114 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
3116 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
3117 (!IN_LEV_FIELD(x+1, y-1) ||
3118 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
3119 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
3123 static void InitBeltMovement()
3125 static int belt_base_element[4] =
3127 EL_CONVEYOR_BELT_1_LEFT,
3128 EL_CONVEYOR_BELT_2_LEFT,
3129 EL_CONVEYOR_BELT_3_LEFT,
3130 EL_CONVEYOR_BELT_4_LEFT
3132 static int belt_base_active_element[4] =
3134 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3135 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3136 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3137 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3142 /* set frame order for belt animation graphic according to belt direction */
3143 for (i = 0; i < 4; i++)
3147 for (j = 0; j < 3; j++)
3149 int element = belt_base_active_element[belt_nr] + j;
3150 int graphic = el2img(element);
3152 if (game.belt_dir[i] == MV_LEFT)
3153 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3155 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3159 for (y = 0; y < lev_fieldy; y++)
3161 for (x = 0; x < lev_fieldx; x++)
3163 int element = Feld[x][y];
3165 for (i = 0; i < 4; i++)
3167 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3169 int e_belt_nr = getBeltNrFromBeltElement(element);
3172 if (e_belt_nr == belt_nr)
3174 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3176 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3184 static void ToggleBeltSwitch(int x, int y)
3186 static int belt_base_element[4] =
3188 EL_CONVEYOR_BELT_1_LEFT,
3189 EL_CONVEYOR_BELT_2_LEFT,
3190 EL_CONVEYOR_BELT_3_LEFT,
3191 EL_CONVEYOR_BELT_4_LEFT
3193 static int belt_base_active_element[4] =
3195 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3196 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3197 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3198 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3200 static int belt_base_switch_element[4] =
3202 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3203 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3204 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3205 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3207 static int belt_move_dir[4] =
3215 int element = Feld[x][y];
3216 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3217 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3218 int belt_dir = belt_move_dir[belt_dir_nr];
3221 if (!IS_BELT_SWITCH(element))
3224 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3225 game.belt_dir[belt_nr] = belt_dir;
3227 if (belt_dir_nr == 3)
3230 /* set frame order for belt animation graphic according to belt direction */
3231 for (i = 0; i < 3; i++)
3233 int element = belt_base_active_element[belt_nr] + i;
3234 int graphic = el2img(element);
3236 if (belt_dir == MV_LEFT)
3237 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3239 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3242 for (yy = 0; yy < lev_fieldy; yy++)
3244 for (xx = 0; xx < lev_fieldx; xx++)
3246 int element = Feld[xx][yy];
3248 if (IS_BELT_SWITCH(element))
3250 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3252 if (e_belt_nr == belt_nr)
3254 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3255 DrawLevelField(xx, yy);
3258 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3260 int e_belt_nr = getBeltNrFromBeltElement(element);
3262 if (e_belt_nr == belt_nr)
3264 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3266 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3267 DrawLevelField(xx, yy);
3270 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3272 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3274 if (e_belt_nr == belt_nr)
3276 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3278 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3279 DrawLevelField(xx, yy);
3286 static void ToggleSwitchgateSwitch(int x, int y)
3290 game.switchgate_pos = !game.switchgate_pos;
3292 for (yy = 0; yy < lev_fieldy; yy++)
3294 for (xx = 0; xx < lev_fieldx; xx++)
3296 int element = Feld[xx][yy];
3298 if (element == EL_SWITCHGATE_SWITCH_UP ||
3299 element == EL_SWITCHGATE_SWITCH_DOWN)
3301 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3302 DrawLevelField(xx, yy);
3304 else if (element == EL_SWITCHGATE_OPEN ||
3305 element == EL_SWITCHGATE_OPENING)
3307 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3309 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3311 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3314 else if (element == EL_SWITCHGATE_CLOSED ||
3315 element == EL_SWITCHGATE_CLOSING)
3317 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3319 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3321 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3328 static int getInvisibleActiveFromInvisibleElement(int element)
3330 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3331 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3332 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3336 static int getInvisibleFromInvisibleActiveElement(int element)
3338 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3339 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3340 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3344 static void RedrawAllLightSwitchesAndInvisibleElements()
3348 for (y = 0; y < lev_fieldy; y++)
3350 for (x = 0; x < lev_fieldx; x++)
3352 int element = Feld[x][y];
3354 if (element == EL_LIGHT_SWITCH &&
3355 game.light_time_left > 0)
3357 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3358 DrawLevelField(x, y);
3360 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3361 game.light_time_left == 0)
3363 Feld[x][y] = EL_LIGHT_SWITCH;
3364 DrawLevelField(x, y);
3366 else if (element == EL_INVISIBLE_STEELWALL ||
3367 element == EL_INVISIBLE_WALL ||
3368 element == EL_INVISIBLE_SAND)
3370 if (game.light_time_left > 0)
3371 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3373 DrawLevelField(x, y);
3375 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3376 element == EL_INVISIBLE_WALL_ACTIVE ||
3377 element == EL_INVISIBLE_SAND_ACTIVE)
3379 if (game.light_time_left == 0)
3380 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3382 DrawLevelField(x, y);
3388 static void ToggleLightSwitch(int x, int y)
3390 int element = Feld[x][y];
3392 game.light_time_left =
3393 (element == EL_LIGHT_SWITCH ?
3394 level.time_light * FRAMES_PER_SECOND : 0);
3396 RedrawAllLightSwitchesAndInvisibleElements();
3399 static void ActivateTimegateSwitch(int x, int y)
3403 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3405 for (yy = 0; yy < lev_fieldy; yy++)
3407 for (xx = 0; xx < lev_fieldx; xx++)
3409 int element = Feld[xx][yy];
3411 if (element == EL_TIMEGATE_CLOSED ||
3412 element == EL_TIMEGATE_CLOSING)
3414 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3415 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3419 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3421 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3422 DrawLevelField(xx, yy);
3429 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3432 inline static int getElementMoveStepsize(int x, int y)
3434 int element = Feld[x][y];
3435 int direction = MovDir[x][y];
3436 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3437 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3438 int horiz_move = (dx != 0);
3439 int sign = (horiz_move ? dx : dy);
3440 int step = sign * element_info[element].move_stepsize;
3442 /* special values for move stepsize for spring and things on conveyor belt */
3446 if (element == EL_SPRING)
3447 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3448 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3449 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3450 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3452 if (CAN_FALL(element) &&
3453 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3454 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3455 else if (element == EL_SPRING)
3456 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3463 void Impact(int x, int y)
3465 boolean lastline = (y == lev_fieldy-1);
3466 boolean object_hit = FALSE;
3467 boolean impact = (lastline || object_hit);
3468 int element = Feld[x][y];
3469 int smashed = EL_UNDEFINED;
3471 if (!lastline) /* check if element below was hit */
3473 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3476 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3477 MovDir[x][y + 1] != MV_DOWN ||
3478 MovPos[x][y + 1] <= TILEY / 2));
3481 object_hit = !IS_FREE(x, y + 1);
3484 /* do not smash moving elements that left the smashed field in time */
3485 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3486 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3490 smashed = MovingOrBlocked2Element(x, y + 1);
3492 impact = (lastline || object_hit);
3495 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3501 /* only reset graphic animation if graphic really changes after impact */
3503 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3505 ResetGfxAnimation(x, y);
3506 DrawLevelField(x, y);
3509 if (impact && CAN_EXPLODE_IMPACT(element))
3514 else if (impact && element == EL_PEARL)
3516 Feld[x][y] = EL_PEARL_BREAKING;
3517 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3520 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3522 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3527 if (impact && element == EL_AMOEBA_DROP)
3529 if (object_hit && IS_PLAYER(x, y + 1))
3530 KillHeroUnlessEnemyProtected(x, y + 1);
3531 else if (object_hit && smashed == EL_PENGUIN)
3535 Feld[x][y] = EL_AMOEBA_GROWING;
3536 Store[x][y] = EL_AMOEBA_WET;
3538 ResetRandomAnimationValue(x, y);
3543 if (object_hit) /* check which object was hit */
3545 if (CAN_PASS_MAGIC_WALL(element) &&
3546 (smashed == EL_MAGIC_WALL ||
3547 smashed == EL_BD_MAGIC_WALL))
3550 int activated_magic_wall =
3551 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3552 EL_BD_MAGIC_WALL_ACTIVE);
3554 /* activate magic wall / mill */
3555 for (yy = 0; yy < lev_fieldy; yy++)
3556 for (xx = 0; xx < lev_fieldx; xx++)
3557 if (Feld[xx][yy] == smashed)
3558 Feld[xx][yy] = activated_magic_wall;
3560 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3561 game.magic_wall_active = TRUE;
3563 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3564 SND_MAGIC_WALL_ACTIVATING :
3565 SND_BD_MAGIC_WALL_ACTIVATING));
3568 if (IS_PLAYER(x, y + 1))
3570 if (CAN_SMASH_PLAYER(element))
3572 KillHeroUnlessEnemyProtected(x, y + 1);
3576 else if (smashed == EL_PENGUIN)
3578 if (CAN_SMASH_PLAYER(element))
3584 else if (element == EL_BD_DIAMOND)
3586 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3592 else if (((element == EL_SP_INFOTRON ||
3593 element == EL_SP_ZONK) &&
3594 (smashed == EL_SP_SNIKSNAK ||
3595 smashed == EL_SP_ELECTRON ||
3596 smashed == EL_SP_DISK_ORANGE)) ||
3597 (element == EL_SP_INFOTRON &&
3598 smashed == EL_SP_DISK_YELLOW))
3604 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3610 else if (CAN_SMASH_EVERYTHING(element))
3612 if (IS_CLASSIC_ENEMY(smashed) ||
3613 CAN_EXPLODE_SMASHED(smashed))
3618 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3620 if (smashed == EL_LAMP ||
3621 smashed == EL_LAMP_ACTIVE)
3626 else if (smashed == EL_NUT)
3628 Feld[x][y + 1] = EL_NUT_BREAKING;
3629 PlayLevelSound(x, y, SND_NUT_BREAKING);
3630 RaiseScoreElement(EL_NUT);
3633 else if (smashed == EL_PEARL)
3635 Feld[x][y + 1] = EL_PEARL_BREAKING;
3636 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3639 else if (smashed == EL_DIAMOND)
3641 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3642 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3645 else if (IS_BELT_SWITCH(smashed))
3647 ToggleBeltSwitch(x, y + 1);
3649 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3650 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3652 ToggleSwitchgateSwitch(x, y + 1);
3654 else if (smashed == EL_LIGHT_SWITCH ||
3655 smashed == EL_LIGHT_SWITCH_ACTIVE)
3657 ToggleLightSwitch(x, y + 1);
3661 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3663 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3664 CE_OTHER_IS_SWITCHING);
3665 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3671 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3676 /* play sound of magic wall / mill */
3678 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3679 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3681 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3682 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3683 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3684 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3689 /* play sound of object that hits the ground */
3690 if (lastline || object_hit)
3691 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3694 inline static void TurnRoundExt(int x, int y)
3706 { 0, 0 }, { 0, 0 }, { 0, 0 },
3711 int left, right, back;
3715 { MV_DOWN, MV_UP, MV_RIGHT },
3716 { MV_UP, MV_DOWN, MV_LEFT },
3718 { MV_LEFT, MV_RIGHT, MV_DOWN },
3722 { MV_RIGHT, MV_LEFT, MV_UP }
3725 int element = Feld[x][y];
3726 int move_pattern = element_info[element].move_pattern;
3728 int old_move_dir = MovDir[x][y];
3729 int left_dir = turn[old_move_dir].left;
3730 int right_dir = turn[old_move_dir].right;
3731 int back_dir = turn[old_move_dir].back;
3733 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3734 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3735 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3736 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3738 int left_x = x + left_dx, left_y = y + left_dy;
3739 int right_x = x + right_dx, right_y = y + right_dy;
3740 int move_x = x + move_dx, move_y = y + move_dy;
3744 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3746 TestIfBadThingTouchesOtherBadThing(x, y);
3748 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3749 MovDir[x][y] = right_dir;
3750 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3751 MovDir[x][y] = left_dir;
3753 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3755 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3758 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3759 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3761 TestIfBadThingTouchesOtherBadThing(x, y);
3763 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3764 MovDir[x][y] = left_dir;
3765 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3766 MovDir[x][y] = right_dir;
3768 if ((element == EL_SPACESHIP ||
3769 element == EL_SP_SNIKSNAK ||
3770 element == EL_SP_ELECTRON)
3771 && MovDir[x][y] != old_move_dir)
3773 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3776 else if (element == EL_YAMYAM)
3778 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3779 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3781 if (can_turn_left && can_turn_right)
3782 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3783 else if (can_turn_left)
3784 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3785 else if (can_turn_right)
3786 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3788 MovDir[x][y] = back_dir;
3790 MovDelay[x][y] = 16 + 16 * RND(3);
3792 else if (element == EL_DARK_YAMYAM)
3794 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3795 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3797 if (can_turn_left && can_turn_right)
3798 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3799 else if (can_turn_left)
3800 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3801 else if (can_turn_right)
3802 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3804 MovDir[x][y] = back_dir;
3806 MovDelay[x][y] = 16 + 16 * RND(3);
3808 else if (element == EL_PACMAN)
3810 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3811 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3813 if (can_turn_left && can_turn_right)
3814 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3815 else if (can_turn_left)
3816 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3817 else if (can_turn_right)
3818 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3820 MovDir[x][y] = back_dir;
3822 MovDelay[x][y] = 6 + RND(40);
3824 else if (element == EL_PIG)
3826 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3827 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3828 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3829 boolean should_turn_left, should_turn_right, should_move_on;
3831 int rnd = RND(rnd_value);
3833 should_turn_left = (can_turn_left &&
3835 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3836 y + back_dy + left_dy)));
3837 should_turn_right = (can_turn_right &&
3839 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3840 y + back_dy + right_dy)));
3841 should_move_on = (can_move_on &&
3844 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3845 y + move_dy + left_dy) ||
3846 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3847 y + move_dy + right_dy)));
3849 if (should_turn_left || should_turn_right || should_move_on)
3851 if (should_turn_left && should_turn_right && should_move_on)
3852 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3853 rnd < 2 * rnd_value / 3 ? right_dir :
3855 else if (should_turn_left && should_turn_right)
3856 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3857 else if (should_turn_left && should_move_on)
3858 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3859 else if (should_turn_right && should_move_on)
3860 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3861 else if (should_turn_left)
3862 MovDir[x][y] = left_dir;
3863 else if (should_turn_right)
3864 MovDir[x][y] = right_dir;
3865 else if (should_move_on)
3866 MovDir[x][y] = old_move_dir;
3868 else if (can_move_on && rnd > rnd_value / 8)
3869 MovDir[x][y] = old_move_dir;
3870 else if (can_turn_left && can_turn_right)
3871 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3872 else if (can_turn_left && rnd > rnd_value / 8)
3873 MovDir[x][y] = left_dir;
3874 else if (can_turn_right && rnd > rnd_value/8)
3875 MovDir[x][y] = right_dir;
3877 MovDir[x][y] = back_dir;
3879 xx = x + move_xy[MovDir[x][y]].x;
3880 yy = y + move_xy[MovDir[x][y]].y;
3882 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3883 MovDir[x][y] = old_move_dir;
3887 else if (element == EL_DRAGON)
3889 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3890 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3891 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3893 int rnd = RND(rnd_value);
3896 if (FrameCounter < 1 && x == 0 && y == 29)
3897 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3900 if (can_move_on && rnd > rnd_value / 8)
3901 MovDir[x][y] = old_move_dir;
3902 else if (can_turn_left && can_turn_right)
3903 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3904 else if (can_turn_left && rnd > rnd_value / 8)
3905 MovDir[x][y] = left_dir;
3906 else if (can_turn_right && rnd > rnd_value / 8)
3907 MovDir[x][y] = right_dir;
3909 MovDir[x][y] = back_dir;
3911 xx = x + move_xy[MovDir[x][y]].x;
3912 yy = y + move_xy[MovDir[x][y]].y;
3915 if (FrameCounter < 1 && x == 0 && y == 29)
3916 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3917 xx, yy, Feld[xx][yy],
3922 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3923 MovDir[x][y] = old_move_dir;
3925 if (!IS_FREE(xx, yy))
3926 MovDir[x][y] = old_move_dir;
3930 if (FrameCounter < 1 && x == 0 && y == 29)
3931 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3936 else if (element == EL_MOLE)
3938 boolean can_move_on =
3939 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3940 IS_AMOEBOID(Feld[move_x][move_y]) ||
3941 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3944 boolean can_turn_left =
3945 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3946 IS_AMOEBOID(Feld[left_x][left_y])));
3948 boolean can_turn_right =
3949 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3950 IS_AMOEBOID(Feld[right_x][right_y])));
3952 if (can_turn_left && can_turn_right)
3953 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3954 else if (can_turn_left)
3955 MovDir[x][y] = left_dir;
3957 MovDir[x][y] = right_dir;
3960 if (MovDir[x][y] != old_move_dir)
3963 else if (element == EL_BALLOON)
3965 MovDir[x][y] = game.balloon_dir;
3968 else if (element == EL_SPRING)
3970 if (MovDir[x][y] & MV_HORIZONTAL &&
3971 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3972 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3973 MovDir[x][y] = MV_NO_MOVING;
3977 else if (element == EL_ROBOT ||
3978 element == EL_SATELLITE ||
3979 element == EL_PENGUIN)
3981 int attr_x = -1, attr_y = -1;
3992 for (i = 0; i < MAX_PLAYERS; i++)
3994 struct PlayerInfo *player = &stored_player[i];
3995 int jx = player->jx, jy = player->jy;
3997 if (!player->active)
4001 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4009 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4015 if (element == EL_PENGUIN)
4018 static int xy[4][2] =
4026 for (i = 0; i < 4; i++)
4028 int ex = x + xy[i % 4][0];
4029 int ey = y + xy[i % 4][1];
4031 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4040 MovDir[x][y] = MV_NO_MOVING;
4042 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4043 else if (attr_x > x)
4044 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4046 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4047 else if (attr_y > y)
4048 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4050 if (element == EL_ROBOT)
4054 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4055 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4056 Moving2Blocked(x, y, &newx, &newy);
4058 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4059 MovDelay[x][y] = 8 + 8 * !RND(3);
4061 MovDelay[x][y] = 16;
4063 else if (element == EL_PENGUIN)
4069 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4071 boolean first_horiz = RND(2);
4072 int new_move_dir = MovDir[x][y];
4075 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4076 Moving2Blocked(x, y, &newx, &newy);
4078 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4082 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4083 Moving2Blocked(x, y, &newx, &newy);
4085 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4088 MovDir[x][y] = old_move_dir;
4092 else /* (element == EL_SATELLITE) */
4098 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4100 boolean first_horiz = RND(2);
4101 int new_move_dir = MovDir[x][y];
4104 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4105 Moving2Blocked(x, y, &newx, &newy);
4107 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4111 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4112 Moving2Blocked(x, y, &newx, &newy);
4114 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4117 MovDir[x][y] = old_move_dir;
4122 else if (move_pattern == MV_TURNING_LEFT ||
4123 move_pattern == MV_TURNING_RIGHT ||
4124 move_pattern == MV_TURNING_LEFT_RIGHT ||
4125 move_pattern == MV_TURNING_RIGHT_LEFT ||
4126 move_pattern == MV_TURNING_RANDOM ||
4127 move_pattern == MV_ALL_DIRECTIONS)
4129 boolean can_turn_left =
4130 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4131 boolean can_turn_right =
4132 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4134 if (move_pattern == MV_TURNING_LEFT)
4135 MovDir[x][y] = left_dir;
4136 else if (move_pattern == MV_TURNING_RIGHT)
4137 MovDir[x][y] = right_dir;
4138 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4139 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4140 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4141 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4142 else if (move_pattern == MV_TURNING_RANDOM)
4143 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4144 can_turn_right && !can_turn_left ? right_dir :
4145 RND(2) ? left_dir : right_dir);
4146 else if (can_turn_left && can_turn_right)
4147 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4148 else if (can_turn_left)
4149 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4150 else if (can_turn_right)
4151 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4153 MovDir[x][y] = back_dir;
4155 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4157 else if (move_pattern == MV_HORIZONTAL ||
4158 move_pattern == MV_VERTICAL)
4160 if (move_pattern & old_move_dir)
4161 MovDir[x][y] = back_dir;
4162 else if (move_pattern == MV_HORIZONTAL)
4163 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4164 else if (move_pattern == MV_VERTICAL)
4165 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4167 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4169 else if (move_pattern & MV_ANY_DIRECTION)
4171 MovDir[x][y] = move_pattern;
4172 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4174 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4176 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4177 MovDir[x][y] = left_dir;
4178 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4179 MovDir[x][y] = right_dir;
4181 if (MovDir[x][y] != old_move_dir)
4182 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4184 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4186 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4187 MovDir[x][y] = right_dir;
4188 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4189 MovDir[x][y] = left_dir;
4191 if (MovDir[x][y] != old_move_dir)
4192 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4194 else if (move_pattern == MV_TOWARDS_PLAYER ||
4195 move_pattern == MV_AWAY_FROM_PLAYER)
4197 int attr_x = -1, attr_y = -1;
4199 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4210 for (i = 0; i < MAX_PLAYERS; i++)
4212 struct PlayerInfo *player = &stored_player[i];
4213 int jx = player->jx, jy = player->jy;
4215 if (!player->active)
4219 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4227 MovDir[x][y] = MV_NO_MOVING;
4229 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4230 else if (attr_x > x)
4231 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4233 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4234 else if (attr_y > y)
4235 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4237 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4239 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4241 boolean first_horiz = RND(2);
4242 int new_move_dir = MovDir[x][y];
4245 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4246 Moving2Blocked(x, y, &newx, &newy);
4248 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4252 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4253 Moving2Blocked(x, y, &newx, &newy);
4255 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4258 MovDir[x][y] = old_move_dir;
4261 else if (move_pattern == MV_WHEN_PUSHED ||
4262 move_pattern == MV_WHEN_DROPPED)
4264 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
4265 MovDir[x][y] = MV_NO_MOVING;
4269 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4271 static int test_xy[7][2] =
4281 static int test_dir[7] =
4291 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4292 int move_preference = -1000000; /* start with very low preference */
4293 int new_move_dir = MV_NO_MOVING;
4294 int start_test = RND(4);
4297 for (i = 0; i < 4; i++)
4299 int move_dir = test_dir[start_test + i];
4300 int move_dir_preference;
4302 xx = x + test_xy[start_test + i][0];
4303 yy = y + test_xy[start_test + i][1];
4305 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4306 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4308 new_move_dir = move_dir;
4313 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4316 move_dir_preference = -1 * RunnerVisit[xx][yy];
4317 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4318 move_dir_preference = PlayerVisit[xx][yy];
4320 if (move_dir_preference > move_preference)
4322 /* prefer field that has not been visited for the longest time */
4323 move_preference = move_dir_preference;
4324 new_move_dir = move_dir;
4326 else if (move_dir_preference == move_preference &&
4327 move_dir == old_move_dir)
4329 /* prefer last direction when all directions are preferred equally */
4330 move_preference = move_dir_preference;
4331 new_move_dir = move_dir;
4335 MovDir[x][y] = new_move_dir;
4336 if (old_move_dir != new_move_dir)
4341 static void TurnRound(int x, int y)
4343 int direction = MovDir[x][y];
4346 GfxDir[x][y] = MovDir[x][y];
4352 GfxDir[x][y] = MovDir[x][y];
4355 if (direction != MovDir[x][y])
4360 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4363 GfxAction[x][y] = ACTION_WAITING;
4367 static boolean JustBeingPushed(int x, int y)
4371 for (i = 0; i < MAX_PLAYERS; i++)
4373 struct PlayerInfo *player = &stored_player[i];
4375 if (player->active && player->is_pushing && player->MovPos)
4377 int next_jx = player->jx + (player->jx - player->last_jx);
4378 int next_jy = player->jy + (player->jy - player->last_jy);
4380 if (x == next_jx && y == next_jy)
4388 void StartMoving(int x, int y)
4391 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4393 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4394 int element = Feld[x][y];
4400 if (MovDelay[x][y] == 0)
4401 GfxAction[x][y] = ACTION_DEFAULT;
4403 /* !!! this should be handled more generic (not only for mole) !!! */
4404 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4405 GfxAction[x][y] = ACTION_DEFAULT;
4408 if (CAN_FALL(element) && y < lev_fieldy - 1)
4410 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4411 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4412 if (JustBeingPushed(x, y))
4415 if (element == EL_QUICKSAND_FULL)
4417 if (IS_FREE(x, y + 1))
4419 InitMovingField(x, y, MV_DOWN);
4420 started_moving = TRUE;
4422 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4423 Store[x][y] = EL_ROCK;
4425 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4427 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4430 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4432 if (!MovDelay[x][y])
4433 MovDelay[x][y] = TILEY + 1;
4442 Feld[x][y] = EL_QUICKSAND_EMPTY;
4443 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4444 Store[x][y + 1] = Store[x][y];
4447 PlayLevelSoundAction(x, y, ACTION_FILLING);
4449 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4453 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4454 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4456 InitMovingField(x, y, MV_DOWN);
4457 started_moving = TRUE;
4459 Feld[x][y] = EL_QUICKSAND_FILLING;
4460 Store[x][y] = element;
4462 PlayLevelSoundAction(x, y, ACTION_FILLING);
4464 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4467 else if (element == EL_MAGIC_WALL_FULL)
4469 if (IS_FREE(x, y + 1))
4471 InitMovingField(x, y, MV_DOWN);
4472 started_moving = TRUE;
4474 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4475 Store[x][y] = EL_CHANGED(Store[x][y]);
4477 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4479 if (!MovDelay[x][y])
4480 MovDelay[x][y] = TILEY/4 + 1;
4489 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4490 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4491 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4495 else if (element == EL_BD_MAGIC_WALL_FULL)
4497 if (IS_FREE(x, y + 1))
4499 InitMovingField(x, y, MV_DOWN);
4500 started_moving = TRUE;
4502 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4503 Store[x][y] = EL_CHANGED2(Store[x][y]);
4505 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4507 if (!MovDelay[x][y])
4508 MovDelay[x][y] = TILEY/4 + 1;
4517 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4518 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4519 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4523 else if (CAN_PASS_MAGIC_WALL(element) &&
4524 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4525 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4527 InitMovingField(x, y, MV_DOWN);
4528 started_moving = TRUE;
4531 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4532 EL_BD_MAGIC_WALL_FILLING);
4533 Store[x][y] = element;
4536 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4538 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4543 InitMovingField(x, y, MV_DOWN);
4544 started_moving = TRUE;
4546 Store[x][y] = EL_ACID;
4548 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4549 GfxAction[x][y + 1] = ACTION_ACTIVE;
4553 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4554 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4555 (Feld[x][y + 1] == EL_BLOCKED)) ||
4556 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4557 CAN_SMASH(element) && WasJustFalling[x][y] &&
4558 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4562 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4563 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4564 WasJustMoving[x][y] && !Pushed[x][y + 1])
4566 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4567 WasJustMoving[x][y])
4572 /* this is needed for a special case not covered by calling "Impact()"
4573 from "ContinueMoving()": if an element moves to a tile directly below
4574 another element which was just falling on that tile (which was empty
4575 in the previous frame), the falling element above would just stop
4576 instead of smashing the element below (in previous version, the above
4577 element was just checked for "moving" instead of "falling", resulting
4578 in incorrect smashes caused by horizontal movement of the above
4579 element; also, the case of the player being the element to smash was
4580 simply not covered here... :-/ ) */
4583 WasJustMoving[x][y] = 0;
4584 WasJustFalling[x][y] = 0;
4589 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
4591 if (MovDir[x][y] == MV_NO_MOVING)
4593 InitMovingField(x, y, MV_DOWN);
4594 started_moving = TRUE;
4597 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4599 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4600 MovDir[x][y] = MV_DOWN;
4602 InitMovingField(x, y, MV_DOWN);
4603 started_moving = TRUE;
4605 else if (element == EL_AMOEBA_DROP)
4607 Feld[x][y] = EL_AMOEBA_GROWING;
4608 Store[x][y] = EL_AMOEBA_WET;
4610 /* Store[x][y + 1] must be zero, because:
4611 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4614 #if OLD_GAME_BEHAVIOUR
4615 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4617 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4618 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4619 element != EL_DX_SUPABOMB)
4622 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4623 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4624 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4625 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4628 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4629 (IS_FREE(x - 1, y + 1) ||
4630 Feld[x - 1][y + 1] == EL_ACID));
4631 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4632 (IS_FREE(x + 1, y + 1) ||
4633 Feld[x + 1][y + 1] == EL_ACID));
4634 boolean can_fall_any = (can_fall_left || can_fall_right);
4635 boolean can_fall_both = (can_fall_left && can_fall_right);
4637 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4639 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4641 if (slippery_type == SLIPPERY_ONLY_LEFT)
4642 can_fall_right = FALSE;
4643 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4644 can_fall_left = FALSE;
4645 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4646 can_fall_right = FALSE;
4647 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4648 can_fall_left = FALSE;
4650 can_fall_any = (can_fall_left || can_fall_right);
4651 can_fall_both = (can_fall_left && can_fall_right);
4656 if (can_fall_both &&
4657 (game.emulation != EMU_BOULDERDASH &&
4658 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4659 can_fall_left = !(can_fall_right = RND(2));
4661 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4662 started_moving = TRUE;
4666 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
4668 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4671 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4672 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4673 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4674 int belt_dir = game.belt_dir[belt_nr];
4676 if ((belt_dir == MV_LEFT && left_is_free) ||
4677 (belt_dir == MV_RIGHT && right_is_free))
4680 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
4683 InitMovingField(x, y, belt_dir);
4684 started_moving = TRUE;
4687 Pushed[x][y] = TRUE;
4688 Pushed[nextx][y] = TRUE;
4691 GfxAction[x][y] = ACTION_DEFAULT;
4695 MovDir[x][y] = 0; /* if element was moving, stop it */
4700 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4701 if (CAN_MOVE(element) && !started_moving)
4703 int move_pattern = element_info[element].move_pattern;
4706 Moving2Blocked(x, y, &newx, &newy);
4709 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4712 if ((element == EL_SATELLITE ||
4713 element == EL_BALLOON ||
4714 element == EL_SPRING)
4715 && JustBeingPushed(x, y))
4720 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4721 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4722 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4725 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4726 element, element_info[element].token_name,
4727 WasJustMoving[x][y],
4728 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4729 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4730 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4731 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4735 WasJustMoving[x][y] = 0;
4738 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4741 if (Feld[x][y] != element) /* element has changed */
4743 element = Feld[x][y];
4744 move_pattern = element_info[element].move_pattern;
4746 if (!CAN_MOVE(element))
4750 if (Feld[x][y] != element) /* element has changed */
4758 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4759 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4761 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4763 Moving2Blocked(x, y, &newx, &newy);
4764 if (Feld[newx][newy] == EL_BLOCKED)
4765 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4771 if (FrameCounter < 1 && x == 0 && y == 29)
4772 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4775 if (!MovDelay[x][y]) /* start new movement phase */
4777 /* all objects that can change their move direction after each step
4778 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4780 if (element != EL_YAMYAM &&
4781 element != EL_DARK_YAMYAM &&
4782 element != EL_PACMAN &&
4783 !(move_pattern & MV_ANY_DIRECTION) &&
4784 move_pattern != MV_TURNING_LEFT &&
4785 move_pattern != MV_TURNING_RIGHT &&
4786 move_pattern != MV_TURNING_LEFT_RIGHT &&
4787 move_pattern != MV_TURNING_RIGHT_LEFT &&
4788 move_pattern != MV_TURNING_RANDOM)
4793 if (FrameCounter < 1 && x == 0 && y == 29)
4794 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4797 if (MovDelay[x][y] && (element == EL_BUG ||
4798 element == EL_SPACESHIP ||
4799 element == EL_SP_SNIKSNAK ||
4800 element == EL_SP_ELECTRON ||
4801 element == EL_MOLE))
4802 DrawLevelField(x, y);
4806 if (MovDelay[x][y]) /* wait some time before next movement */
4811 if (element == EL_YAMYAM)
4814 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4815 DrawLevelElementAnimation(x, y, element);
4819 if (MovDelay[x][y]) /* element still has to wait some time */
4822 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4823 ResetGfxAnimation(x, y);
4827 if (GfxAction[x][y] != ACTION_WAITING)
4828 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4830 GfxAction[x][y] = ACTION_WAITING;
4834 if (element == EL_ROBOT ||
4836 element == EL_PACMAN ||
4838 element == EL_YAMYAM ||
4839 element == EL_DARK_YAMYAM)
4842 DrawLevelElementAnimation(x, y, element);
4844 DrawLevelElementAnimationIfNeeded(x, y, element);
4846 PlayLevelSoundAction(x, y, ACTION_WAITING);
4848 else if (element == EL_SP_ELECTRON)
4849 DrawLevelElementAnimationIfNeeded(x, y, element);
4850 else if (element == EL_DRAGON)
4853 int dir = MovDir[x][y];
4854 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4855 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4856 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4857 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4858 dir == MV_UP ? IMG_FLAMES_1_UP :
4859 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4860 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4863 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4866 GfxAction[x][y] = ACTION_ATTACKING;
4868 if (IS_PLAYER(x, y))
4869 DrawPlayerField(x, y);
4871 DrawLevelField(x, y);
4873 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4875 for (i = 1; i <= 3; i++)
4877 int xx = x + i * dx;
4878 int yy = y + i * dy;
4879 int sx = SCREENX(xx);
4880 int sy = SCREENY(yy);
4881 int flame_graphic = graphic + (i - 1);
4883 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4888 int flamed = MovingOrBlocked2Element(xx, yy);
4890 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
4893 RemoveMovingField(xx, yy);
4895 Feld[xx][yy] = EL_FLAMES;
4896 if (IN_SCR_FIELD(sx, sy))
4898 DrawLevelFieldCrumbledSand(xx, yy);
4899 DrawGraphic(sx, sy, flame_graphic, frame);
4904 if (Feld[xx][yy] == EL_FLAMES)
4905 Feld[xx][yy] = EL_EMPTY;
4906 DrawLevelField(xx, yy);
4911 if (MovDelay[x][y]) /* element still has to wait some time */
4913 PlayLevelSoundAction(x, y, ACTION_WAITING);
4919 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4920 for all other elements GfxAction will be set by InitMovingField() */
4921 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4922 GfxAction[x][y] = ACTION_MOVING;
4926 /* now make next step */
4928 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4930 if (DONT_COLLIDE_WITH(element) &&
4931 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4932 !PLAYER_ENEMY_PROTECTED(newx, newy))
4935 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4939 /* player killed by element which is deadly when colliding with */
4941 KillHero(PLAYERINFO(newx, newy));
4946 else if ((element == EL_PENGUIN ||
4947 element == EL_ROBOT ||
4948 element == EL_SATELLITE ||
4949 element == EL_BALLOON ||
4950 IS_CUSTOM_ELEMENT(element)) &&
4951 IN_LEV_FIELD(newx, newy) &&
4952 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4955 Store[x][y] = EL_ACID;
4957 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4959 if (Feld[newx][newy] == EL_EXIT_OPEN)
4963 DrawLevelField(x, y);
4965 Feld[x][y] = EL_EMPTY;
4966 DrawLevelField(x, y);
4969 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4970 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4971 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4973 local_player->friends_still_needed--;
4974 if (!local_player->friends_still_needed &&
4975 !local_player->GameOver && AllPlayersGone)
4976 local_player->LevelSolved = local_player->GameOver = TRUE;
4980 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4982 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
4983 DrawLevelField(newx, newy);
4985 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4987 else if (!IS_FREE(newx, newy))
4989 GfxAction[x][y] = ACTION_WAITING;
4991 if (IS_PLAYER(x, y))
4992 DrawPlayerField(x, y);
4994 DrawLevelField(x, y);
4999 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5001 if (IS_FOOD_PIG(Feld[newx][newy]))
5003 if (IS_MOVING(newx, newy))
5004 RemoveMovingField(newx, newy);
5007 Feld[newx][newy] = EL_EMPTY;
5008 DrawLevelField(newx, newy);
5011 PlayLevelSound(x, y, SND_PIG_DIGGING);
5013 else if (!IS_FREE(newx, newy))
5015 if (IS_PLAYER(x, y))
5016 DrawPlayerField(x, y);
5018 DrawLevelField(x, y);
5027 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5030 else if (IS_CUSTOM_ELEMENT(element) &&
5031 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5035 !IS_FREE(newx, newy)
5040 int new_element = Feld[newx][newy];
5043 printf("::: '%s' digs '%s' [%d]\n",
5044 element_info[element].token_name,
5045 element_info[Feld[newx][newy]].token_name,
5046 StorePlayer[newx][newy]);
5049 if (!IS_FREE(newx, newy))
5051 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5052 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5055 /* no element can dig solid indestructible elements */
5056 if (IS_INDESTRUCTIBLE(new_element) &&
5057 !IS_DIGGABLE(new_element) &&
5058 !IS_COLLECTIBLE(new_element))
5061 if (AmoebaNr[newx][newy] &&
5062 (new_element == EL_AMOEBA_FULL ||
5063 new_element == EL_BD_AMOEBA ||
5064 new_element == EL_AMOEBA_GROWING))
5066 AmoebaCnt[AmoebaNr[newx][newy]]--;
5067 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5070 if (IS_MOVING(newx, newy))
5071 RemoveMovingField(newx, newy);
5074 RemoveField(newx, newy);
5075 DrawLevelField(newx, newy);
5078 PlayLevelSoundAction(x, y, action);
5081 if (new_element == element_info[element].move_enter_element)
5082 element_info[element].can_leave_element = TRUE;
5084 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5086 RunnerVisit[x][y] = FrameCounter;
5087 PlayerVisit[x][y] /= 8; /* expire player visit path */
5093 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5095 if (!IS_FREE(newx, newy))
5097 if (IS_PLAYER(x, y))
5098 DrawPlayerField(x, y);
5100 DrawLevelField(x, y);
5106 boolean wanna_flame = !RND(10);
5107 int dx = newx - x, dy = newy - y;
5108 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5109 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5110 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5111 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5112 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5113 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5116 IS_CLASSIC_ENEMY(element1) ||
5117 IS_CLASSIC_ENEMY(element2)) &&
5118 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5119 element1 != EL_FLAMES && element2 != EL_FLAMES)
5122 ResetGfxAnimation(x, y);
5123 GfxAction[x][y] = ACTION_ATTACKING;
5126 if (IS_PLAYER(x, y))
5127 DrawPlayerField(x, y);
5129 DrawLevelField(x, y);
5131 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5133 MovDelay[x][y] = 50;
5135 Feld[newx][newy] = EL_FLAMES;
5136 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5137 Feld[newx1][newy1] = EL_FLAMES;
5138 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5139 Feld[newx2][newy2] = EL_FLAMES;
5145 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5146 Feld[newx][newy] == EL_DIAMOND)
5148 if (IS_MOVING(newx, newy))
5149 RemoveMovingField(newx, newy);
5152 Feld[newx][newy] = EL_EMPTY;
5153 DrawLevelField(newx, newy);
5156 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5158 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5159 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5161 if (AmoebaNr[newx][newy])
5163 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5164 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5165 Feld[newx][newy] == EL_BD_AMOEBA)
5166 AmoebaCnt[AmoebaNr[newx][newy]]--;
5169 if (IS_MOVING(newx, newy))
5170 RemoveMovingField(newx, newy);
5173 Feld[newx][newy] = EL_EMPTY;
5174 DrawLevelField(newx, newy);
5177 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5179 else if ((element == EL_PACMAN || element == EL_MOLE)
5180 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5182 if (AmoebaNr[newx][newy])
5184 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5185 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5186 Feld[newx][newy] == EL_BD_AMOEBA)
5187 AmoebaCnt[AmoebaNr[newx][newy]]--;
5190 if (element == EL_MOLE)
5192 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5193 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5195 ResetGfxAnimation(x, y);
5196 GfxAction[x][y] = ACTION_DIGGING;
5197 DrawLevelField(x, y);
5199 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5201 return; /* wait for shrinking amoeba */
5203 else /* element == EL_PACMAN */
5205 Feld[newx][newy] = EL_EMPTY;
5206 DrawLevelField(newx, newy);
5207 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5210 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5211 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5212 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5214 /* wait for shrinking amoeba to completely disappear */
5217 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5219 /* object was running against a wall */
5224 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5225 DrawLevelElementAnimation(x, y, element);
5227 if (element == EL_BUG ||
5228 element == EL_SPACESHIP ||
5229 element == EL_SP_SNIKSNAK)
5230 DrawLevelField(x, y);
5231 else if (element == EL_MOLE)
5232 DrawLevelField(x, y);
5233 else if (element == EL_BD_BUTTERFLY ||
5234 element == EL_BD_FIREFLY)
5235 DrawLevelElementAnimationIfNeeded(x, y, element);
5236 else if (element == EL_SATELLITE)
5237 DrawLevelElementAnimationIfNeeded(x, y, element);
5238 else if (element == EL_SP_ELECTRON)
5239 DrawLevelElementAnimationIfNeeded(x, y, element);
5242 if (DONT_TOUCH(element))
5243 TestIfBadThingTouchesHero(x, y);
5246 PlayLevelSoundAction(x, y, ACTION_WAITING);
5252 InitMovingField(x, y, MovDir[x][y]);
5254 PlayLevelSoundAction(x, y, ACTION_MOVING);
5258 ContinueMoving(x, y);
5261 void ContinueMoving(int x, int y)
5263 int element = Feld[x][y];
5264 struct ElementInfo *ei = &element_info[element];
5265 int direction = MovDir[x][y];
5266 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5267 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5268 int newx = x + dx, newy = y + dy;
5270 int nextx = newx + dx, nexty = newy + dy;
5273 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
5274 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5276 boolean pushed_by_player = Pushed[x][y];
5279 MovPos[x][y] += getElementMoveStepsize(x, y);
5282 if (pushed_by_player && IS_PLAYER(x, y))
5284 /* special case: moving object pushed by player */
5285 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5288 if (pushed_by_player) /* special case: moving object pushed by player */
5289 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5292 if (ABS(MovPos[x][y]) < TILEX)
5294 DrawLevelField(x, y);
5296 return; /* element is still moving */
5299 /* element reached destination field */
5301 Feld[x][y] = EL_EMPTY;
5302 Feld[newx][newy] = element;
5303 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5305 if (element == EL_MOLE)
5307 Feld[x][y] = EL_SAND;
5309 DrawLevelFieldCrumbledSandNeighbours(x, y);
5311 else if (element == EL_QUICKSAND_FILLING)
5313 element = Feld[newx][newy] = get_next_element(element);
5314 Store[newx][newy] = Store[x][y];
5316 else if (element == EL_QUICKSAND_EMPTYING)
5318 Feld[x][y] = get_next_element(element);
5319 element = Feld[newx][newy] = Store[x][y];
5321 else if (element == EL_MAGIC_WALL_FILLING)
5323 element = Feld[newx][newy] = get_next_element(element);
5324 if (!game.magic_wall_active)
5325 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5326 Store[newx][newy] = Store[x][y];
5328 else if (element == EL_MAGIC_WALL_EMPTYING)
5330 Feld[x][y] = get_next_element(element);
5331 if (!game.magic_wall_active)
5332 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5333 element = Feld[newx][newy] = Store[x][y];
5335 else if (element == EL_BD_MAGIC_WALL_FILLING)
5337 element = Feld[newx][newy] = get_next_element(element);
5338 if (!game.magic_wall_active)
5339 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5340 Store[newx][newy] = Store[x][y];
5342 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5344 Feld[x][y] = get_next_element(element);
5345 if (!game.magic_wall_active)
5346 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5347 element = Feld[newx][newy] = Store[x][y];
5349 else if (element == EL_AMOEBA_DROPPING)
5351 Feld[x][y] = get_next_element(element);
5352 element = Feld[newx][newy] = Store[x][y];
5354 else if (element == EL_SOKOBAN_OBJECT)
5357 Feld[x][y] = Back[x][y];
5359 if (Back[newx][newy])
5360 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5362 Back[x][y] = Back[newx][newy] = 0;
5364 else if (Store[x][y] == EL_ACID)
5366 element = Feld[newx][newy] = EL_ACID;
5370 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5371 MovDelay[newx][newy] = 0;
5373 /* copy element change control values to new field */
5374 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5375 ChangePage[newx][newy] = ChangePage[x][y];
5376 Changed[newx][newy] = Changed[x][y];
5377 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5379 ChangeDelay[x][y] = 0;
5380 ChangePage[x][y] = -1;
5381 Changed[x][y] = CE_BITMASK_DEFAULT;
5382 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5384 /* copy animation control values to new field */
5385 GfxFrame[newx][newy] = GfxFrame[x][y];
5386 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5387 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5388 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5390 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5392 ResetGfxAnimation(x, y); /* reset animation values for old field */
5395 /* some elements can leave other elements behind after moving */
5396 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5397 ei->move_leave_element != EL_EMPTY &&
5398 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5399 ei->can_leave_element_last))
5401 Feld[x][y] = ei->move_leave_element;
5402 InitField(x, y, FALSE);
5404 if (GFX_CRUMBLED(Feld[x][y]))
5405 DrawLevelFieldCrumbledSandNeighbours(x, y);
5408 ei->can_leave_element_last = ei->can_leave_element;
5409 ei->can_leave_element = FALSE;
5413 /* 2.1.1 (does not work correctly for spring) */
5414 if (!CAN_MOVE(element))
5415 MovDir[newx][newy] = 0;
5419 /* (does not work for falling objects that slide horizontally) */
5420 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5421 MovDir[newx][newy] = 0;
5424 if (!CAN_MOVE(element) ||
5425 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5426 MovDir[newx][newy] = 0;
5429 if (!CAN_MOVE(element) ||
5430 (CAN_FALL(element) && direction == MV_DOWN))
5431 GfxDir[x][y] = MovDir[newx][newy] = 0;
5436 DrawLevelField(x, y);
5437 DrawLevelField(newx, newy);
5439 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5441 /* prevent pushed element from moving on in pushed direction */
5442 if (pushed_by_player && CAN_MOVE(element) &&
5443 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5444 !(element_info[element].move_pattern & direction))
5445 TurnRound(newx, newy);
5448 /* prevent elements on conveyor belt from moving on in last direction */
5449 if (pushed_by_conveyor && CAN_FALL(element) &&
5450 direction & MV_HORIZONTAL)
5451 MovDir[newx][newy] = 0;
5454 if (!pushed_by_player)
5456 WasJustMoving[newx][newy] = 3;
5458 if (CAN_FALL(element) && direction == MV_DOWN)
5459 WasJustFalling[newx][newy] = 3;
5462 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5464 TestIfBadThingTouchesHero(newx, newy);
5465 TestIfBadThingTouchesFriend(newx, newy);
5467 if (!IS_CUSTOM_ELEMENT(element))
5468 TestIfBadThingTouchesOtherBadThing(newx, newy);
5470 else if (element == EL_PENGUIN)
5471 TestIfFriendTouchesBadThing(newx, newy);
5473 if (CAN_FALL(element) && direction == MV_DOWN &&
5474 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5478 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5482 if (ChangePage[newx][newy] != -1) /* delayed change */
5483 ChangeElement(newx, newy, ChangePage[newx][newy]);
5488 TestIfElementHitsCustomElement(newx, newy, direction);
5492 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5494 int hitting_element = Feld[newx][newy];
5496 /* !!! fix side (direction) orientation here and elsewhere !!! */
5497 CheckElementSideChange(newx, newy, hitting_element,
5498 direction, CE_HITTING_SOMETHING, -1);
5501 if (IN_LEV_FIELD(nextx, nexty))
5503 int opposite_direction = MV_DIR_OPPOSITE(direction);
5504 int hitting_side = direction;
5505 int touched_side = opposite_direction;
5506 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5507 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5508 MovDir[nextx][nexty] != direction ||
5509 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5515 CheckElementSideChange(nextx, nexty, touched_element,
5516 opposite_direction, CE_HIT_BY_SOMETHING, -1);
5518 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5519 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5521 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5523 struct ElementChangeInfo *change =
5524 &element_info[hitting_element].change_page[i];
5526 if (change->can_change &&
5527 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5528 change->sides & touched_side &&
5529 change->trigger_element == touched_element)
5531 CheckElementSideChange(newx, newy, hitting_element,
5532 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
5538 if (IS_CUSTOM_ELEMENT(touched_element) &&
5539 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5541 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5543 struct ElementChangeInfo *change =
5544 &element_info[touched_element].change_page[i];
5546 if (change->can_change &&
5547 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5548 change->sides & hitting_side &&
5549 change->trigger_element == hitting_element)
5551 CheckElementSideChange(nextx, nexty, touched_element,
5552 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
5563 TestIfPlayerTouchesCustomElement(newx, newy);
5564 TestIfElementTouchesCustomElement(newx, newy);
5567 int AmoebeNachbarNr(int ax, int ay)
5570 int element = Feld[ax][ay];
5572 static int xy[4][2] =
5580 for (i = 0; i < 4; i++)
5582 int x = ax + xy[i][0];
5583 int y = ay + xy[i][1];
5585 if (!IN_LEV_FIELD(x, y))
5588 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5589 group_nr = AmoebaNr[x][y];
5595 void AmoebenVereinigen(int ax, int ay)
5597 int i, x, y, xx, yy;
5598 int new_group_nr = AmoebaNr[ax][ay];
5599 static int xy[4][2] =
5607 if (new_group_nr == 0)
5610 for (i = 0; i < 4; i++)
5615 if (!IN_LEV_FIELD(x, y))
5618 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5619 Feld[x][y] == EL_BD_AMOEBA ||
5620 Feld[x][y] == EL_AMOEBA_DEAD) &&
5621 AmoebaNr[x][y] != new_group_nr)
5623 int old_group_nr = AmoebaNr[x][y];
5625 if (old_group_nr == 0)
5628 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5629 AmoebaCnt[old_group_nr] = 0;
5630 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5631 AmoebaCnt2[old_group_nr] = 0;
5633 for (yy = 0; yy < lev_fieldy; yy++)
5635 for (xx = 0; xx < lev_fieldx; xx++)
5637 if (AmoebaNr[xx][yy] == old_group_nr)
5638 AmoebaNr[xx][yy] = new_group_nr;
5645 void AmoebeUmwandeln(int ax, int ay)
5649 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5651 int group_nr = AmoebaNr[ax][ay];
5656 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5657 printf("AmoebeUmwandeln(): This should never happen!\n");
5662 for (y = 0; y < lev_fieldy; y++)
5664 for (x = 0; x < lev_fieldx; x++)
5666 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5669 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5673 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5674 SND_AMOEBA_TURNING_TO_GEM :
5675 SND_AMOEBA_TURNING_TO_ROCK));
5680 static int xy[4][2] =
5688 for (i = 0; i < 4; i++)
5693 if (!IN_LEV_FIELD(x, y))
5696 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5698 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5699 SND_AMOEBA_TURNING_TO_GEM :
5700 SND_AMOEBA_TURNING_TO_ROCK));
5707 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5710 int group_nr = AmoebaNr[ax][ay];
5711 boolean done = FALSE;
5716 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5717 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5722 for (y = 0; y < lev_fieldy; y++)
5724 for (x = 0; x < lev_fieldx; x++)
5726 if (AmoebaNr[x][y] == group_nr &&
5727 (Feld[x][y] == EL_AMOEBA_DEAD ||
5728 Feld[x][y] == EL_BD_AMOEBA ||
5729 Feld[x][y] == EL_AMOEBA_GROWING))
5732 Feld[x][y] = new_element;
5733 InitField(x, y, FALSE);
5734 DrawLevelField(x, y);
5741 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5742 SND_BD_AMOEBA_TURNING_TO_ROCK :
5743 SND_BD_AMOEBA_TURNING_TO_GEM));
5746 void AmoebeWaechst(int x, int y)
5748 static unsigned long sound_delay = 0;
5749 static unsigned long sound_delay_value = 0;
5751 if (!MovDelay[x][y]) /* start new growing cycle */
5755 if (DelayReached(&sound_delay, sound_delay_value))
5758 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5760 if (Store[x][y] == EL_BD_AMOEBA)
5761 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5763 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5765 sound_delay_value = 30;
5769 if (MovDelay[x][y]) /* wait some time before growing bigger */
5772 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5774 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5775 6 - MovDelay[x][y]);
5777 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5780 if (!MovDelay[x][y])
5782 Feld[x][y] = Store[x][y];
5784 DrawLevelField(x, y);
5789 void AmoebaDisappearing(int x, int y)
5791 static unsigned long sound_delay = 0;
5792 static unsigned long sound_delay_value = 0;
5794 if (!MovDelay[x][y]) /* start new shrinking cycle */
5798 if (DelayReached(&sound_delay, sound_delay_value))
5799 sound_delay_value = 30;
5802 if (MovDelay[x][y]) /* wait some time before shrinking */
5805 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5807 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5808 6 - MovDelay[x][y]);
5810 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5813 if (!MovDelay[x][y])
5815 Feld[x][y] = EL_EMPTY;
5816 DrawLevelField(x, y);
5818 /* don't let mole enter this field in this cycle;
5819 (give priority to objects falling to this field from above) */
5825 void AmoebeAbleger(int ax, int ay)
5828 int element = Feld[ax][ay];
5829 int graphic = el2img(element);
5830 int newax = ax, neway = ay;
5831 static int xy[4][2] =
5839 if (!level.amoeba_speed)
5841 Feld[ax][ay] = EL_AMOEBA_DEAD;
5842 DrawLevelField(ax, ay);
5846 if (IS_ANIMATED(graphic))
5847 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5849 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5850 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5852 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5855 if (MovDelay[ax][ay])
5859 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5862 int x = ax + xy[start][0];
5863 int y = ay + xy[start][1];
5865 if (!IN_LEV_FIELD(x, y))
5868 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5869 if (IS_FREE(x, y) ||
5870 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5876 if (newax == ax && neway == ay)
5879 else /* normal or "filled" (BD style) amoeba */
5882 boolean waiting_for_player = FALSE;
5884 for (i = 0; i < 4; i++)
5886 int j = (start + i) % 4;
5887 int x = ax + xy[j][0];
5888 int y = ay + xy[j][1];
5890 if (!IN_LEV_FIELD(x, y))
5893 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5894 if (IS_FREE(x, y) ||
5895 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5901 else if (IS_PLAYER(x, y))
5902 waiting_for_player = TRUE;
5905 if (newax == ax && neway == ay) /* amoeba cannot grow */
5907 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5909 Feld[ax][ay] = EL_AMOEBA_DEAD;
5910 DrawLevelField(ax, ay);
5911 AmoebaCnt[AmoebaNr[ax][ay]]--;
5913 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5915 if (element == EL_AMOEBA_FULL)
5916 AmoebeUmwandeln(ax, ay);
5917 else if (element == EL_BD_AMOEBA)
5918 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5923 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5925 /* amoeba gets larger by growing in some direction */
5927 int new_group_nr = AmoebaNr[ax][ay];
5930 if (new_group_nr == 0)
5932 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5933 printf("AmoebeAbleger(): This should never happen!\n");
5938 AmoebaNr[newax][neway] = new_group_nr;
5939 AmoebaCnt[new_group_nr]++;
5940 AmoebaCnt2[new_group_nr]++;
5942 /* if amoeba touches other amoeba(s) after growing, unify them */
5943 AmoebenVereinigen(newax, neway);
5945 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5947 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5953 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5954 (neway == lev_fieldy - 1 && newax != ax))
5956 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5957 Store[newax][neway] = element;
5959 else if (neway == ay)
5961 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5963 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5965 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5970 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5971 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5972 Store[ax][ay] = EL_AMOEBA_DROP;
5973 ContinueMoving(ax, ay);
5977 DrawLevelField(newax, neway);
5980 void Life(int ax, int ay)
5983 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5985 int element = Feld[ax][ay];
5986 int graphic = el2img(element);
5987 boolean changed = FALSE;
5989 if (IS_ANIMATED(graphic))
5990 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5995 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5996 MovDelay[ax][ay] = life_time;
5998 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6001 if (MovDelay[ax][ay])
6005 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6007 int xx = ax+x1, yy = ay+y1;
6010 if (!IN_LEV_FIELD(xx, yy))
6013 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6015 int x = xx+x2, y = yy+y2;
6017 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6020 if (((Feld[x][y] == element ||
6021 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6023 (IS_FREE(x, y) && Stop[x][y]))
6027 if (xx == ax && yy == ay) /* field in the middle */
6029 if (nachbarn < life[0] || nachbarn > life[1])
6031 Feld[xx][yy] = EL_EMPTY;
6033 DrawLevelField(xx, yy);
6034 Stop[xx][yy] = TRUE;
6038 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6039 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6040 { /* free border field */
6041 if (nachbarn >= life[2] && nachbarn <= life[3])
6043 Feld[xx][yy] = element;
6044 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6046 DrawLevelField(xx, yy);
6047 Stop[xx][yy] = TRUE;
6054 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6055 SND_GAME_OF_LIFE_GROWING);
6058 static void InitRobotWheel(int x, int y)
6060 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6063 static void RunRobotWheel(int x, int y)
6065 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6068 static void StopRobotWheel(int x, int y)
6070 if (ZX == x && ZY == y)
6074 static void InitTimegateWheel(int x, int y)
6076 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6079 static void RunTimegateWheel(int x, int y)
6081 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6084 void CheckExit(int x, int y)
6086 if (local_player->gems_still_needed > 0 ||
6087 local_player->sokobanfields_still_needed > 0 ||
6088 local_player->lights_still_needed > 0)
6090 int element = Feld[x][y];
6091 int graphic = el2img(element);
6093 if (IS_ANIMATED(graphic))
6094 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6099 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6102 Feld[x][y] = EL_EXIT_OPENING;
6104 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6107 void CheckExitSP(int x, int y)
6109 if (local_player->gems_still_needed > 0)
6111 int element = Feld[x][y];
6112 int graphic = el2img(element);
6114 if (IS_ANIMATED(graphic))
6115 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6120 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6123 Feld[x][y] = EL_SP_EXIT_OPENING;
6125 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6128 static void CloseAllOpenTimegates()
6132 for (y = 0; y < lev_fieldy; y++)
6134 for (x = 0; x < lev_fieldx; x++)
6136 int element = Feld[x][y];
6138 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6140 Feld[x][y] = EL_TIMEGATE_CLOSING;
6142 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6144 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6151 void EdelsteinFunkeln(int x, int y)
6153 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6156 if (Feld[x][y] == EL_BD_DIAMOND)
6159 if (MovDelay[x][y] == 0) /* next animation frame */
6160 MovDelay[x][y] = 11 * !SimpleRND(500);
6162 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6166 if (setup.direct_draw && MovDelay[x][y])
6167 SetDrawtoField(DRAW_BUFFERED);
6169 DrawLevelElementAnimation(x, y, Feld[x][y]);
6171 if (MovDelay[x][y] != 0)
6173 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6174 10 - MovDelay[x][y]);
6176 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6178 if (setup.direct_draw)
6182 dest_x = FX + SCREENX(x) * TILEX;
6183 dest_y = FY + SCREENY(y) * TILEY;
6185 BlitBitmap(drawto_field, window,
6186 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6187 SetDrawtoField(DRAW_DIRECT);
6193 void MauerWaechst(int x, int y)
6197 if (!MovDelay[x][y]) /* next animation frame */
6198 MovDelay[x][y] = 3 * delay;
6200 if (MovDelay[x][y]) /* wait some time before next frame */
6204 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6206 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6207 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6209 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6212 if (!MovDelay[x][y])
6214 if (MovDir[x][y] == MV_LEFT)
6216 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6217 DrawLevelField(x - 1, y);
6219 else if (MovDir[x][y] == MV_RIGHT)
6221 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6222 DrawLevelField(x + 1, y);
6224 else if (MovDir[x][y] == MV_UP)
6226 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6227 DrawLevelField(x, y - 1);
6231 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6232 DrawLevelField(x, y + 1);
6235 Feld[x][y] = Store[x][y];
6237 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6238 DrawLevelField(x, y);
6243 void MauerAbleger(int ax, int ay)
6245 int element = Feld[ax][ay];
6246 int graphic = el2img(element);
6247 boolean oben_frei = FALSE, unten_frei = FALSE;
6248 boolean links_frei = FALSE, rechts_frei = FALSE;
6249 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6250 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6251 boolean new_wall = FALSE;
6253 if (IS_ANIMATED(graphic))
6254 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6256 if (!MovDelay[ax][ay]) /* start building new wall */
6257 MovDelay[ax][ay] = 6;
6259 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6262 if (MovDelay[ax][ay])
6266 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6268 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6270 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6272 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6275 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6276 element == EL_EXPANDABLE_WALL_ANY)
6280 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6281 Store[ax][ay-1] = element;
6282 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6283 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6284 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6285 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6290 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6291 Store[ax][ay+1] = element;
6292 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6293 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6294 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6295 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6300 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6301 element == EL_EXPANDABLE_WALL_ANY ||
6302 element == EL_EXPANDABLE_WALL)
6306 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6307 Store[ax-1][ay] = element;
6308 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6309 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6310 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6311 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6317 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6318 Store[ax+1][ay] = element;
6319 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6320 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6321 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6322 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6327 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6328 DrawLevelField(ax, ay);
6330 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6332 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6333 unten_massiv = TRUE;
6334 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6335 links_massiv = TRUE;
6336 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6337 rechts_massiv = TRUE;
6339 if (((oben_massiv && unten_massiv) ||
6340 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6341 element == EL_EXPANDABLE_WALL) &&
6342 ((links_massiv && rechts_massiv) ||
6343 element == EL_EXPANDABLE_WALL_VERTICAL))
6344 Feld[ax][ay] = EL_WALL;
6348 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6350 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6354 void CheckForDragon(int x, int y)
6357 boolean dragon_found = FALSE;
6358 static int xy[4][2] =
6366 for (i = 0; i < 4; i++)
6368 for (j = 0; j < 4; j++)
6370 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6372 if (IN_LEV_FIELD(xx, yy) &&
6373 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6375 if (Feld[xx][yy] == EL_DRAGON)
6376 dragon_found = TRUE;
6385 for (i = 0; i < 4; i++)
6387 for (j = 0; j < 3; j++)
6389 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6391 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6393 Feld[xx][yy] = EL_EMPTY;
6394 DrawLevelField(xx, yy);
6403 static void InitBuggyBase(int x, int y)
6405 int element = Feld[x][y];
6406 int activating_delay = FRAMES_PER_SECOND / 4;
6409 (element == EL_SP_BUGGY_BASE ?
6410 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6411 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6413 element == EL_SP_BUGGY_BASE_ACTIVE ?
6414 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6417 static void WarnBuggyBase(int x, int y)
6420 static int xy[4][2] =
6428 for (i = 0; i < 4; i++)
6430 int xx = x + xy[i][0], yy = y + xy[i][1];
6432 if (IS_PLAYER(xx, yy))
6434 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6441 static void InitTrap(int x, int y)
6443 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6446 static void ActivateTrap(int x, int y)
6448 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6451 static void ChangeActiveTrap(int x, int y)
6453 int graphic = IMG_TRAP_ACTIVE;
6455 /* if new animation frame was drawn, correct crumbled sand border */
6456 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6457 DrawLevelFieldCrumbledSand(x, y);
6460 static void ChangeElementNowExt(int x, int y, int target_element)
6462 int previous_move_direction = MovDir[x][y];
6464 /* check if element under player changes from accessible to unaccessible
6465 (needed for special case of dropping element which then changes) */
6466 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6467 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6474 Feld[x][y] = target_element;
6476 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6478 ResetGfxAnimation(x, y);
6479 ResetRandomAnimationValue(x, y);
6481 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6482 MovDir[x][y] = previous_move_direction;
6485 InitField_WithBug1(x, y, FALSE);
6487 InitField(x, y, FALSE);
6488 if (CAN_MOVE(Feld[x][y]))
6492 DrawLevelField(x, y);
6494 if (GFX_CRUMBLED(Feld[x][y]))
6495 DrawLevelFieldCrumbledSandNeighbours(x, y);
6497 TestIfBadThingTouchesHero(x, y);
6498 TestIfPlayerTouchesCustomElement(x, y);
6499 TestIfElementTouchesCustomElement(x, y);
6501 if (ELEM_IS_PLAYER(target_element))
6502 RelocatePlayer(x, y, target_element);
6505 static boolean ChangeElementNow(int x, int y, int element, int page)
6507 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6509 /* always use default change event to prevent running into a loop */
6510 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6511 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6513 /* do not change already changed elements with same change event */
6515 if (Changed[x][y] & ChangeEvent[x][y])
6522 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6524 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
6526 if (change->explode)
6533 if (change->use_content)
6535 boolean complete_change = TRUE;
6536 boolean can_change[3][3];
6539 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6541 boolean half_destructible;
6542 int ex = x + xx - 1;
6543 int ey = y + yy - 1;
6546 can_change[xx][yy] = TRUE;
6548 if (ex == x && ey == y) /* do not check changing element itself */
6551 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6553 can_change[xx][yy] = FALSE; /* do not change empty borders */
6558 if (!IN_LEV_FIELD(ex, ey))
6560 can_change[xx][yy] = FALSE;
6561 complete_change = FALSE;
6568 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6569 e = MovingOrBlocked2Element(ex, ey);
6571 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6573 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6574 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6575 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6577 can_change[xx][yy] = FALSE;
6578 complete_change = FALSE;
6582 if (!change->only_complete || complete_change)
6584 boolean something_has_changed = FALSE;
6586 if (change->only_complete && change->use_random_change &&
6587 RND(100) < change->random)
6590 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6592 int ex = x + xx - 1;
6593 int ey = y + yy - 1;
6595 if (can_change[xx][yy] && (!change->use_random_change ||
6596 RND(100) < change->random))
6598 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6599 RemoveMovingField(ex, ey);
6601 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6603 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6605 something_has_changed = TRUE;
6607 /* for symmetry reasons, freeze newly created border elements */
6608 if (ex != x || ey != y)
6609 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6613 if (something_has_changed)
6614 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6619 ChangeElementNowExt(x, y, change->target_element);
6621 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6627 static void ChangeElement(int x, int y, int page)
6629 int element = MovingOrBlocked2Element(x, y);
6630 struct ElementInfo *ei = &element_info[element];
6631 struct ElementChangeInfo *change = &ei->change_page[page];
6635 if (!CAN_CHANGE(element))
6638 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6639 x, y, element, element_info[element].token_name);
6640 printf("ChangeElement(): This should never happen!\n");
6646 if (ChangeDelay[x][y] == 0) /* initialize element change */
6648 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6649 RND(change->delay_random * change->delay_frames)) + 1;
6651 ResetGfxAnimation(x, y);
6652 ResetRandomAnimationValue(x, y);
6654 if (change->pre_change_function)
6655 change->pre_change_function(x, y);
6658 ChangeDelay[x][y]--;
6660 if (ChangeDelay[x][y] != 0) /* continue element change */
6662 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6664 if (IS_ANIMATED(graphic))
6665 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6667 if (change->change_function)
6668 change->change_function(x, y);
6670 else /* finish element change */
6672 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6674 page = ChangePage[x][y];
6675 ChangePage[x][y] = -1;
6679 if (IS_MOVING(x, y) && !change->explode)
6681 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6684 ChangeDelay[x][y] = 1; /* try change after next move step */
6685 ChangePage[x][y] = page; /* remember page to use for change */
6690 if (ChangeElementNow(x, y, element, page))
6692 if (change->post_change_function)
6693 change->post_change_function(x, y);
6698 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6699 int trigger_element,
6705 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6708 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6710 int element = EL_CUSTOM_START + i;
6712 boolean change_element = FALSE;
6715 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6718 for (j = 0; j < element_info[element].num_change_pages; j++)
6720 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6722 if (change->can_change &&
6724 change->events & CH_EVENT_BIT(trigger_event) &&
6726 change->sides & trigger_side &&
6728 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)
6730 change->trigger_element == trigger_element
6735 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6736 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6737 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6740 change_element = TRUE;
6747 if (!change_element)
6750 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6753 if (x == lx && y == ly) /* do not change trigger element itself */
6757 if (Feld[x][y] == element)
6759 ChangeDelay[x][y] = 1;
6760 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6761 ChangeElement(x, y, page);
6769 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6772 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6776 static boolean CheckElementSideChange(int x, int y, int element, int side,
6777 int trigger_event, int page)
6779 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6782 if (Feld[x][y] == EL_BLOCKED)
6784 Blocked2Moving(x, y, &x, &y);
6785 element = Feld[x][y];
6791 boolean change_element = FALSE;
6794 for (i = 0; i < element_info[element].num_change_pages; i++)
6796 struct ElementChangeInfo *change = &element_info[element].change_page[i];
6798 if (change->can_change &&
6799 change->events & CH_EVENT_BIT(trigger_event) &&
6800 change->sides & side)
6802 change_element = TRUE;
6809 if (!change_element)
6815 /* !!! this check misses pages with same event, but different side !!! */
6818 page = element_info[element].event_page_nr[trigger_event];
6820 if (!(element_info[element].change_page[page].sides & side))
6824 ChangeDelay[x][y] = 1;
6825 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6826 ChangeElement(x, y, page);
6831 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6833 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6836 static void PlayPlayerSound(struct PlayerInfo *player)
6838 int jx = player->jx, jy = player->jy;
6839 int element = player->element_nr;
6840 int last_action = player->last_action_waiting;
6841 int action = player->action_waiting;
6843 if (player->is_waiting)
6845 if (action != last_action)
6846 PlayLevelSoundElementAction(jx, jy, element, action);
6848 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6852 if (action != last_action)
6853 StopSound(element_info[element].sound[last_action]);
6855 if (last_action == ACTION_SLEEPING)
6856 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6860 static void PlayAllPlayersSound()
6864 for (i = 0; i < MAX_PLAYERS; i++)
6865 if (stored_player[i].active)
6866 PlayPlayerSound(&stored_player[i]);
6869 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6871 boolean last_waiting = player->is_waiting;
6872 int move_dir = player->MovDir;
6874 player->last_action_waiting = player->action_waiting;
6878 if (!last_waiting) /* not waiting -> waiting */
6880 player->is_waiting = TRUE;
6882 player->frame_counter_bored =
6884 game.player_boring_delay_fixed +
6885 SimpleRND(game.player_boring_delay_random);
6886 player->frame_counter_sleeping =
6888 game.player_sleeping_delay_fixed +
6889 SimpleRND(game.player_sleeping_delay_random);
6891 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6894 if (game.player_sleeping_delay_fixed +
6895 game.player_sleeping_delay_random > 0 &&
6896 player->anim_delay_counter == 0 &&
6897 player->post_delay_counter == 0 &&
6898 FrameCounter >= player->frame_counter_sleeping)
6899 player->is_sleeping = TRUE;
6900 else if (game.player_boring_delay_fixed +
6901 game.player_boring_delay_random > 0 &&
6902 FrameCounter >= player->frame_counter_bored)
6903 player->is_bored = TRUE;
6905 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6906 player->is_bored ? ACTION_BORING :
6909 if (player->is_sleeping)
6911 if (player->num_special_action_sleeping > 0)
6913 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6915 int last_special_action = player->special_action_sleeping;
6916 int num_special_action = player->num_special_action_sleeping;
6917 int special_action =
6918 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6919 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6920 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6921 last_special_action + 1 : ACTION_SLEEPING);
6922 int special_graphic =
6923 el_act_dir2img(player->element_nr, special_action, move_dir);
6925 player->anim_delay_counter =
6926 graphic_info[special_graphic].anim_delay_fixed +
6927 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6928 player->post_delay_counter =
6929 graphic_info[special_graphic].post_delay_fixed +
6930 SimpleRND(graphic_info[special_graphic].post_delay_random);
6932 player->special_action_sleeping = special_action;
6935 if (player->anim_delay_counter > 0)
6937 player->action_waiting = player->special_action_sleeping;
6938 player->anim_delay_counter--;
6940 else if (player->post_delay_counter > 0)
6942 player->post_delay_counter--;
6946 else if (player->is_bored)
6948 if (player->num_special_action_bored > 0)
6950 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6952 int special_action =
6953 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6954 int special_graphic =
6955 el_act_dir2img(player->element_nr, special_action, move_dir);
6957 player->anim_delay_counter =
6958 graphic_info[special_graphic].anim_delay_fixed +
6959 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6960 player->post_delay_counter =
6961 graphic_info[special_graphic].post_delay_fixed +
6962 SimpleRND(graphic_info[special_graphic].post_delay_random);
6964 player->special_action_bored = special_action;
6967 if (player->anim_delay_counter > 0)
6969 player->action_waiting = player->special_action_bored;
6970 player->anim_delay_counter--;
6972 else if (player->post_delay_counter > 0)
6974 player->post_delay_counter--;
6979 else if (last_waiting) /* waiting -> not waiting */
6981 player->is_waiting = FALSE;
6982 player->is_bored = FALSE;
6983 player->is_sleeping = FALSE;
6985 player->frame_counter_bored = -1;
6986 player->frame_counter_sleeping = -1;
6988 player->anim_delay_counter = 0;
6989 player->post_delay_counter = 0;
6991 player->action_waiting = ACTION_DEFAULT;
6993 player->special_action_bored = ACTION_DEFAULT;
6994 player->special_action_sleeping = ACTION_DEFAULT;
6999 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7002 static byte stored_player_action[MAX_PLAYERS];
7003 static int num_stored_actions = 0;
7005 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7006 int left = player_action & JOY_LEFT;
7007 int right = player_action & JOY_RIGHT;
7008 int up = player_action & JOY_UP;
7009 int down = player_action & JOY_DOWN;
7010 int button1 = player_action & JOY_BUTTON_1;
7011 int button2 = player_action & JOY_BUTTON_2;
7012 int dx = (left ? -1 : right ? 1 : 0);
7013 int dy = (up ? -1 : down ? 1 : 0);
7016 stored_player_action[player->index_nr] = 0;
7017 num_stored_actions++;
7021 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7024 if (!player->active || tape.pausing)
7028 printf("::: [%d %d %d %d] [%d %d]\n",
7029 left, right, up, down, button1, button2);
7035 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7039 snapped = SnapField(player, dx, dy);
7043 dropped = DropElement(player);
7045 moved = MovePlayer(player, dx, dy);
7048 if (tape.single_step && tape.recording && !tape.pausing)
7050 if (button1 || (dropped && !moved))
7052 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7053 SnapField(player, 0, 0); /* stop snapping */
7057 SetPlayerWaiting(player, FALSE);
7060 return player_action;
7062 stored_player_action[player->index_nr] = player_action;
7068 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7071 /* no actions for this player (no input at player's configured device) */
7073 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7074 SnapField(player, 0, 0);
7075 CheckGravityMovement(player);
7077 if (player->MovPos == 0)
7078 SetPlayerWaiting(player, TRUE);
7080 if (player->MovPos == 0) /* needed for tape.playing */
7081 player->is_moving = FALSE;
7083 player->is_dropping = FALSE;
7089 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7091 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7093 TapeRecordAction(stored_player_action);
7094 num_stored_actions = 0;
7101 static void PlayerActions(struct PlayerInfo *player, byte player_action)
7103 static byte stored_player_action[MAX_PLAYERS];
7104 static int num_stored_actions = 0;
7105 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7106 int left = player_action & JOY_LEFT;
7107 int right = player_action & JOY_RIGHT;
7108 int up = player_action & JOY_UP;
7109 int down = player_action & JOY_DOWN;
7110 int button1 = player_action & JOY_BUTTON_1;
7111 int button2 = player_action & JOY_BUTTON_2;
7112 int dx = (left ? -1 : right ? 1 : 0);
7113 int dy = (up ? -1 : down ? 1 : 0);
7115 stored_player_action[player->index_nr] = 0;
7116 num_stored_actions++;
7118 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7120 if (!player->active || tape.pausing)
7125 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7128 snapped = SnapField(player, dx, dy);
7132 dropped = DropElement(player);
7134 moved = MovePlayer(player, dx, dy);
7137 if (tape.single_step && tape.recording && !tape.pausing)
7139 if (button1 || (dropped && !moved))
7141 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7142 SnapField(player, 0, 0); /* stop snapping */
7146 stored_player_action[player->index_nr] = player_action;
7150 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7152 /* no actions for this player (no input at player's configured device) */
7154 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7155 SnapField(player, 0, 0);
7156 CheckGravityMovement(player);
7158 if (player->MovPos == 0)
7159 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
7161 if (player->MovPos == 0) /* needed for tape.playing */
7162 player->is_moving = FALSE;
7165 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7167 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7169 TapeRecordAction(stored_player_action);
7170 num_stored_actions = 0;
7177 static unsigned long action_delay = 0;
7178 unsigned long action_delay_value;
7179 int magic_wall_x = 0, magic_wall_y = 0;
7180 int i, x, y, element, graphic;
7181 byte *recorded_player_action;
7182 byte summarized_player_action = 0;
7184 byte tape_action[MAX_PLAYERS];
7187 if (game_status != GAME_MODE_PLAYING)
7190 action_delay_value =
7191 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7193 if (tape.playing && tape.index_search && !tape.pausing)
7194 action_delay_value = 0;
7196 /* ---------- main game synchronization point ---------- */
7198 WaitUntilDelayReached(&action_delay, action_delay_value);
7200 if (network_playing && !network_player_action_received)
7204 printf("DEBUG: try to get network player actions in time\n");
7208 #if defined(PLATFORM_UNIX)
7209 /* last chance to get network player actions without main loop delay */
7213 if (game_status != GAME_MODE_PLAYING)
7216 if (!network_player_action_received)
7220 printf("DEBUG: failed to get network player actions in time\n");
7231 printf("::: getting new tape action [%d]\n", FrameCounter);
7234 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7236 for (i = 0; i < MAX_PLAYERS; i++)
7238 summarized_player_action |= stored_player[i].action;
7240 if (!network_playing)
7241 stored_player[i].effective_action = stored_player[i].action;
7244 #if defined(PLATFORM_UNIX)
7245 if (network_playing)
7246 SendToServer_MovePlayer(summarized_player_action);
7249 if (!options.network && !setup.team_mode)
7250 local_player->effective_action = summarized_player_action;
7252 for (i = 0; i < MAX_PLAYERS; i++)
7254 int actual_player_action = stored_player[i].effective_action;
7256 if (stored_player[i].programmed_action)
7257 actual_player_action = stored_player[i].programmed_action;
7259 if (recorded_player_action)
7260 actual_player_action = recorded_player_action[i];
7262 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7264 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7265 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7267 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7272 TapeRecordAction(tape_action);
7275 network_player_action_received = FALSE;
7277 ScrollScreen(NULL, SCROLL_GO_ON);
7283 for (i = 0; i < MAX_PLAYERS; i++)
7284 stored_player[i].Frame++;
7288 /* for downwards compatibility, the following code emulates a fixed bug that
7289 occured when pushing elements (causing elements that just made their last
7290 pushing step to already (if possible) make their first falling step in the
7291 same game frame, which is bad); this code is also needed to use the famous
7292 "spring push bug" which is used in older levels and might be wanted to be
7293 used also in newer levels, but in this case the buggy pushing code is only
7294 affecting the "spring" element and no other elements */
7297 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
7299 if (game.engine_version < VERSION_IDENT(2,2,0,7))
7302 for (i = 0; i < MAX_PLAYERS; i++)
7304 struct PlayerInfo *player = &stored_player[i];
7309 if (player->active && player->is_pushing && player->is_moving &&
7311 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
7312 Feld[x][y] == EL_SPRING))
7314 if (player->active && player->is_pushing && player->is_moving &&
7318 ContinueMoving(x, y);
7320 /* continue moving after pushing (this is actually a bug) */
7321 if (!IS_MOVING(x, y))
7330 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7332 Changed[x][y] = CE_BITMASK_DEFAULT;
7333 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
7336 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7338 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7339 printf("GameActions(): This should never happen!\n");
7341 ChangePage[x][y] = -1;
7346 if (WasJustMoving[x][y] > 0)
7347 WasJustMoving[x][y]--;
7348 if (WasJustFalling[x][y] > 0)
7349 WasJustFalling[x][y]--;
7354 /* reset finished pushing action (not done in ContinueMoving() to allow
7355 continous pushing animation for elements with zero push delay) */
7356 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7358 ResetGfxAnimation(x, y);
7359 DrawLevelField(x, y);
7364 if (IS_BLOCKED(x, y))
7368 Blocked2Moving(x, y, &oldx, &oldy);
7369 if (!IS_MOVING(oldx, oldy))
7371 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7372 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7373 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7374 printf("GameActions(): This should never happen!\n");
7380 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7382 element = Feld[x][y];
7384 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7386 graphic = el2img(element);
7392 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7394 element = graphic = 0;
7398 if (graphic_info[graphic].anim_global_sync)
7399 GfxFrame[x][y] = FrameCounter;
7401 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7402 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7403 ResetRandomAnimationValue(x, y);
7405 SetRandomAnimationValue(x, y);
7408 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7411 if (IS_INACTIVE(element))
7413 if (IS_ANIMATED(graphic))
7414 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7420 /* this may take place after moving, so 'element' may have changed */
7422 if (IS_CHANGING(x, y))
7424 if (IS_CHANGING(x, y) &&
7425 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7429 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7430 element_info[element].event_page_nr[CE_DELAY]);
7432 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7435 element = Feld[x][y];
7436 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7440 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7445 element = Feld[x][y];
7446 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7448 if (element == EL_MOLE)
7449 printf("::: %d, %d, %d [%d]\n",
7450 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7454 if (element == EL_YAMYAM)
7455 printf("::: %d, %d, %d\n",
7456 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7460 if (IS_ANIMATED(graphic) &&
7464 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7467 if (element == EL_BUG)
7468 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7472 if (element == EL_MOLE)
7473 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7477 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7478 EdelsteinFunkeln(x, y);
7480 else if ((element == EL_ACID ||
7481 element == EL_EXIT_OPEN ||
7482 element == EL_SP_EXIT_OPEN ||
7483 element == EL_SP_TERMINAL ||
7484 element == EL_SP_TERMINAL_ACTIVE ||
7485 element == EL_EXTRA_TIME ||
7486 element == EL_SHIELD_NORMAL ||
7487 element == EL_SHIELD_DEADLY) &&
7488 IS_ANIMATED(graphic))
7489 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7490 else if (IS_MOVING(x, y))
7491 ContinueMoving(x, y);
7492 else if (IS_ACTIVE_BOMB(element))
7493 CheckDynamite(x, y);
7495 else if (element == EL_EXPLOSION && !game.explosions_delayed)
7496 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7498 else if (element == EL_AMOEBA_GROWING)
7499 AmoebeWaechst(x, y);
7500 else if (element == EL_AMOEBA_SHRINKING)
7501 AmoebaDisappearing(x, y);
7503 #if !USE_NEW_AMOEBA_CODE
7504 else if (IS_AMOEBALIVE(element))
7505 AmoebeAbleger(x, y);
7508 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7510 else if (element == EL_EXIT_CLOSED)
7512 else if (element == EL_SP_EXIT_CLOSED)
7514 else if (element == EL_EXPANDABLE_WALL_GROWING)
7516 else if (element == EL_EXPANDABLE_WALL ||
7517 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7518 element == EL_EXPANDABLE_WALL_VERTICAL ||
7519 element == EL_EXPANDABLE_WALL_ANY)
7521 else if (element == EL_FLAMES)
7522 CheckForDragon(x, y);
7524 else if (IS_AUTO_CHANGING(element))
7525 ChangeElement(x, y);
7527 else if (element == EL_EXPLOSION)
7528 ; /* drawing of correct explosion animation is handled separately */
7529 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7530 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7533 /* this may take place after moving, so 'element' may have changed */
7534 if (IS_AUTO_CHANGING(Feld[x][y]))
7535 ChangeElement(x, y);
7538 if (IS_BELT_ACTIVE(element))
7539 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7541 if (game.magic_wall_active)
7543 int jx = local_player->jx, jy = local_player->jy;
7545 /* play the element sound at the position nearest to the player */
7546 if ((element == EL_MAGIC_WALL_FULL ||
7547 element == EL_MAGIC_WALL_ACTIVE ||
7548 element == EL_MAGIC_WALL_EMPTYING ||
7549 element == EL_BD_MAGIC_WALL_FULL ||
7550 element == EL_BD_MAGIC_WALL_ACTIVE ||
7551 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7552 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7560 #if USE_NEW_AMOEBA_CODE
7561 /* new experimental amoeba growth stuff */
7563 if (!(FrameCounter % 8))
7566 static unsigned long random = 1684108901;
7568 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7571 x = (random >> 10) % lev_fieldx;
7572 y = (random >> 20) % lev_fieldy;
7574 x = RND(lev_fieldx);
7575 y = RND(lev_fieldy);
7577 element = Feld[x][y];
7579 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7580 if (!IS_PLAYER(x,y) &&
7581 (element == EL_EMPTY ||
7582 element == EL_SAND ||
7583 element == EL_QUICKSAND_EMPTY ||
7584 element == EL_ACID_SPLASH_LEFT ||
7585 element == EL_ACID_SPLASH_RIGHT))
7587 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7588 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7589 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7590 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7591 Feld[x][y] = EL_AMOEBA_DROP;
7594 random = random * 129 + 1;
7600 if (game.explosions_delayed)
7603 game.explosions_delayed = FALSE;
7605 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7607 element = Feld[x][y];
7609 if (ExplodeField[x][y])
7610 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7611 else if (element == EL_EXPLOSION)
7612 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7614 ExplodeField[x][y] = EX_NO_EXPLOSION;
7617 game.explosions_delayed = TRUE;
7620 if (game.magic_wall_active)
7622 if (!(game.magic_wall_time_left % 4))
7624 int element = Feld[magic_wall_x][magic_wall_y];
7626 if (element == EL_BD_MAGIC_WALL_FULL ||
7627 element == EL_BD_MAGIC_WALL_ACTIVE ||
7628 element == EL_BD_MAGIC_WALL_EMPTYING)
7629 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7631 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7634 if (game.magic_wall_time_left > 0)
7636 game.magic_wall_time_left--;
7637 if (!game.magic_wall_time_left)
7639 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7641 element = Feld[x][y];
7643 if (element == EL_MAGIC_WALL_ACTIVE ||
7644 element == EL_MAGIC_WALL_FULL)
7646 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7647 DrawLevelField(x, y);
7649 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7650 element == EL_BD_MAGIC_WALL_FULL)
7652 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7653 DrawLevelField(x, y);
7657 game.magic_wall_active = FALSE;
7662 if (game.light_time_left > 0)
7664 game.light_time_left--;
7666 if (game.light_time_left == 0)
7667 RedrawAllLightSwitchesAndInvisibleElements();
7670 if (game.timegate_time_left > 0)
7672 game.timegate_time_left--;
7674 if (game.timegate_time_left == 0)
7675 CloseAllOpenTimegates();
7678 for (i = 0; i < MAX_PLAYERS; i++)
7680 struct PlayerInfo *player = &stored_player[i];
7682 if (SHIELD_ON(player))
7684 if (player->shield_deadly_time_left)
7685 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7686 else if (player->shield_normal_time_left)
7687 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7691 if (TimeFrames >= FRAMES_PER_SECOND)
7696 for (i = 0; i < MAX_PLAYERS; i++)
7698 struct PlayerInfo *player = &stored_player[i];
7700 if (SHIELD_ON(player))
7702 player->shield_normal_time_left--;
7704 if (player->shield_deadly_time_left > 0)
7705 player->shield_deadly_time_left--;
7709 if (tape.recording || tape.playing)
7710 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7716 if (TimeLeft <= 10 && setup.time_limit)
7717 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7719 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7721 if (!TimeLeft && setup.time_limit)
7722 for (i = 0; i < MAX_PLAYERS; i++)
7723 KillHero(&stored_player[i]);
7725 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7726 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7730 PlayAllPlayersSound();
7732 if (options.debug) /* calculate frames per second */
7734 static unsigned long fps_counter = 0;
7735 static int fps_frames = 0;
7736 unsigned long fps_delay_ms = Counter() - fps_counter;
7740 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7742 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7745 fps_counter = Counter();
7748 redraw_mask |= REDRAW_FPS;
7752 if (stored_player[0].jx != stored_player[0].last_jx ||
7753 stored_player[0].jy != stored_player[0].last_jy)
7754 printf("::: %d, %d, %d, %d, %d\n",
7755 stored_player[0].MovDir,
7756 stored_player[0].MovPos,
7757 stored_player[0].GfxPos,
7758 stored_player[0].Frame,
7759 stored_player[0].StepFrame);
7766 for (i = 0; i < MAX_PLAYERS; i++)
7769 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7771 stored_player[i].Frame += move_frames;
7773 if (stored_player[i].MovPos != 0)
7774 stored_player[i].StepFrame += move_frames;
7776 if (stored_player[i].drop_delay > 0)
7777 stored_player[i].drop_delay--;
7782 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7784 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7786 local_player->show_envelope = 0;
7791 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7793 int min_x = x, min_y = y, max_x = x, max_y = y;
7796 for (i = 0; i < MAX_PLAYERS; i++)
7798 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7800 if (!stored_player[i].active || &stored_player[i] == player)
7803 min_x = MIN(min_x, jx);
7804 min_y = MIN(min_y, jy);
7805 max_x = MAX(max_x, jx);
7806 max_y = MAX(max_y, jy);
7809 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7812 static boolean AllPlayersInVisibleScreen()
7816 for (i = 0; i < MAX_PLAYERS; i++)
7818 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7820 if (!stored_player[i].active)
7823 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7830 void ScrollLevel(int dx, int dy)
7832 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7835 BlitBitmap(drawto_field, drawto_field,
7836 FX + TILEX * (dx == -1) - softscroll_offset,
7837 FY + TILEY * (dy == -1) - softscroll_offset,
7838 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7839 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7840 FX + TILEX * (dx == 1) - softscroll_offset,
7841 FY + TILEY * (dy == 1) - softscroll_offset);
7845 x = (dx == 1 ? BX1 : BX2);
7846 for (y = BY1; y <= BY2; y++)
7847 DrawScreenField(x, y);
7852 y = (dy == 1 ? BY1 : BY2);
7853 for (x = BX1; x <= BX2; x++)
7854 DrawScreenField(x, y);
7857 redraw_mask |= REDRAW_FIELD;
7860 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
7862 int nextx = x + dx, nexty = y + dy;
7863 int element = Feld[x][y];
7866 element != EL_SP_PORT_LEFT &&
7867 element != EL_SP_GRAVITY_PORT_LEFT &&
7868 element != EL_SP_PORT_HORIZONTAL &&
7869 element != EL_SP_PORT_ANY) ||
7871 element != EL_SP_PORT_RIGHT &&
7872 element != EL_SP_GRAVITY_PORT_RIGHT &&
7873 element != EL_SP_PORT_HORIZONTAL &&
7874 element != EL_SP_PORT_ANY) ||
7876 element != EL_SP_PORT_UP &&
7877 element != EL_SP_GRAVITY_PORT_UP &&
7878 element != EL_SP_PORT_VERTICAL &&
7879 element != EL_SP_PORT_ANY) ||
7881 element != EL_SP_PORT_DOWN &&
7882 element != EL_SP_GRAVITY_PORT_DOWN &&
7883 element != EL_SP_PORT_VERTICAL &&
7884 element != EL_SP_PORT_ANY) ||
7885 !IN_LEV_FIELD(nextx, nexty) ||
7886 !IS_FREE(nextx, nexty))
7892 static void CheckGravityMovement(struct PlayerInfo *player)
7894 if (game.gravity && !player->programmed_action)
7896 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7897 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7899 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7900 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7901 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7902 int jx = player->jx, jy = player->jy;
7903 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7904 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7905 int new_jx = jx + dx, new_jy = jy + dy;
7906 boolean field_under_player_is_free =
7907 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7908 boolean player_is_moving_to_valid_field =
7909 (IN_LEV_FIELD(new_jx, new_jy) &&
7910 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7911 Feld[new_jx][new_jy] == EL_SAND ||
7912 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
7913 canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
7914 /* !!! extend EL_SAND to anything diggable !!! */
7916 boolean player_is_standing_on_valid_field =
7917 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
7918 (IS_WALKABLE(Feld[jx][jy]) &&
7919 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
7921 if (field_under_player_is_free &&
7922 !player_is_standing_on_valid_field &&
7923 !player_is_moving_to_valid_field)
7924 player->programmed_action = MV_DOWN;
7930 -----------------------------------------------------------------------------
7931 dx, dy: direction (non-diagonal) to try to move the player to
7932 real_dx, real_dy: direction as read from input device (can be diagonal)
7935 boolean MovePlayerOneStep(struct PlayerInfo *player,
7936 int dx, int dy, int real_dx, int real_dy)
7939 static int change_sides[4][2] =
7941 /* enter side leave side */
7942 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7943 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7944 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7945 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7947 int move_direction = (dx == -1 ? MV_LEFT :
7948 dx == +1 ? MV_RIGHT :
7950 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7951 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7952 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7954 int jx = player->jx, jy = player->jy;
7955 int new_jx = jx + dx, new_jy = jy + dy;
7959 if (!player->active || (!dx && !dy))
7960 return MF_NO_ACTION;
7962 player->MovDir = (dx < 0 ? MV_LEFT :
7965 dy > 0 ? MV_DOWN : MV_NO_MOVING);
7967 if (!IN_LEV_FIELD(new_jx, new_jy))
7968 return MF_NO_ACTION;
7970 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7971 return MF_NO_ACTION;
7974 element = MovingOrBlocked2Element(new_jx, new_jy);
7976 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7979 if (DONT_RUN_INTO(element))
7981 if (element == EL_ACID && dx == 0 && dy == 1)
7984 Feld[jx][jy] = EL_PLAYER_1;
7985 InitMovingField(jx, jy, MV_DOWN);
7986 Store[jx][jy] = EL_ACID;
7987 ContinueMoving(jx, jy);
7991 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7996 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
7997 if (can_move != MF_MOVING)
8000 /* check if DigField() has caused relocation of the player */
8001 if (player->jx != jx || player->jy != jy)
8002 return MF_NO_ACTION;
8004 StorePlayer[jx][jy] = 0;
8005 player->last_jx = jx;
8006 player->last_jy = jy;
8007 player->jx = new_jx;
8008 player->jy = new_jy;
8009 StorePlayer[new_jx][new_jy] = player->element_nr;
8012 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8014 player->step_counter++;
8016 player->drop_delay = 0;
8018 PlayerVisit[jx][jy] = FrameCounter;
8020 ScrollPlayer(player, SCROLL_INIT);
8023 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8025 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
8026 CE_OTHER_GETS_LEFT);
8027 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
8028 CE_LEFT_BY_PLAYER, -1);
8031 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
8033 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
8034 enter_side, CE_OTHER_GETS_ENTERED);
8035 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
8036 CE_ENTERED_BY_PLAYER, -1);
8043 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8045 int jx = player->jx, jy = player->jy;
8046 int old_jx = jx, old_jy = jy;
8047 int moved = MF_NO_ACTION;
8050 if (!player->active)
8055 if (player->MovPos == 0)
8057 player->is_moving = FALSE;
8058 player->is_digging = FALSE;
8059 player->is_collecting = FALSE;
8060 player->is_snapping = FALSE;
8061 player->is_pushing = FALSE;
8067 if (!player->active || (!dx && !dy))
8072 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8076 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8077 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
8081 /* remove the last programmed player action */
8082 player->programmed_action = 0;
8086 /* should only happen if pre-1.2 tape recordings are played */
8087 /* this is only for backward compatibility */
8089 int original_move_delay_value = player->move_delay_value;
8092 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8096 /* scroll remaining steps with finest movement resolution */
8097 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8099 while (player->MovPos)
8101 ScrollPlayer(player, SCROLL_GO_ON);
8102 ScrollScreen(NULL, SCROLL_GO_ON);
8108 player->move_delay_value = original_move_delay_value;
8111 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
8113 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8114 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8118 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8119 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8125 if (moved & MF_MOVING && !ScreenMovPos &&
8126 (player == local_player || !options.network))
8128 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8129 int offset = (setup.scroll_delay ? 3 : 0);
8131 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8133 /* actual player has left the screen -- scroll in that direction */
8134 if (jx != old_jx) /* player has moved horizontally */
8135 scroll_x += (jx - old_jx);
8136 else /* player has moved vertically */
8137 scroll_y += (jy - old_jy);
8141 if (jx != old_jx) /* player has moved horizontally */
8143 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8144 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8145 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8147 /* don't scroll over playfield boundaries */
8148 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8149 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8151 /* don't scroll more than one field at a time */
8152 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8154 /* don't scroll against the player's moving direction */
8155 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8156 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8157 scroll_x = old_scroll_x;
8159 else /* player has moved vertically */
8161 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8162 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8163 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8165 /* don't scroll over playfield boundaries */
8166 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8167 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8169 /* don't scroll more than one field at a time */
8170 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8172 /* don't scroll against the player's moving direction */
8173 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8174 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8175 scroll_y = old_scroll_y;
8179 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8181 if (!options.network && !AllPlayersInVisibleScreen())
8183 scroll_x = old_scroll_x;
8184 scroll_y = old_scroll_y;
8188 ScrollScreen(player, SCROLL_INIT);
8189 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8196 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
8198 if (!(moved & MF_MOVING) && !player->is_pushing)
8203 player->StepFrame = 0;
8205 if (moved & MF_MOVING)
8207 if (old_jx != jx && old_jy == jy)
8208 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8209 else if (old_jx == jx && old_jy != jy)
8210 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8212 DrawLevelField(jx, jy); /* for "crumbled sand" */
8214 player->last_move_dir = player->MovDir;
8215 player->is_moving = TRUE;
8217 player->is_snapping = FALSE;
8221 player->is_switching = FALSE;
8224 player->is_dropping = FALSE;
8229 static int change_sides[4][2] =
8231 /* enter side leave side */
8232 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8233 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8234 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8235 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8237 int move_direction = player->MovDir;
8238 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
8239 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
8242 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
8244 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
8245 leave_side, CE_OTHER_GETS_LEFT);
8246 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
8247 leave_side, CE_LEFT_BY_PLAYER, -1);
8250 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8252 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
8253 enter_side, CE_OTHER_GETS_ENTERED);
8254 CheckElementSideChange(jx, jy, Feld[jx][jy],
8255 enter_side, CE_ENTERED_BY_PLAYER, -1);
8266 CheckGravityMovement(player);
8269 player->last_move_dir = MV_NO_MOVING;
8271 player->is_moving = FALSE;
8274 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8276 TestIfHeroTouchesBadThing(jx, jy);
8277 TestIfPlayerTouchesCustomElement(jx, jy);
8280 if (!player->active)
8286 void ScrollPlayer(struct PlayerInfo *player, int mode)
8288 int jx = player->jx, jy = player->jy;
8289 int last_jx = player->last_jx, last_jy = player->last_jy;
8290 int move_stepsize = TILEX / player->move_delay_value;
8292 if (!player->active || !player->MovPos)
8295 if (mode == SCROLL_INIT)
8297 player->actual_frame_counter = FrameCounter;
8298 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8300 if (Feld[last_jx][last_jy] == EL_EMPTY)
8301 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8309 else if (!FrameReached(&player->actual_frame_counter, 1))
8312 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8313 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8315 if (!player->block_last_field &&
8316 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8317 Feld[last_jx][last_jy] = EL_EMPTY;
8319 /* before DrawPlayer() to draw correct player graphic for this case */
8320 if (player->MovPos == 0)
8321 CheckGravityMovement(player);
8324 DrawPlayer(player); /* needed here only to cleanup last field */
8327 if (player->MovPos == 0) /* player reached destination field */
8330 if (player->move_delay_reset_counter > 0)
8332 player->move_delay_reset_counter--;
8334 if (player->move_delay_reset_counter == 0)
8336 /* continue with normal speed after quickly moving through gate */
8337 HALVE_PLAYER_SPEED(player);
8339 /* be able to make the next move without delay */
8340 player->move_delay = 0;
8344 if (IS_PASSABLE(Feld[last_jx][last_jy]))
8346 /* continue with normal speed after quickly moving through gate */
8347 HALVE_PLAYER_SPEED(player);
8349 /* be able to make the next move without delay */
8350 player->move_delay = 0;
8354 if (player->block_last_field &&
8355 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8356 Feld[last_jx][last_jy] = EL_EMPTY;
8358 player->last_jx = jx;
8359 player->last_jy = jy;
8361 if (Feld[jx][jy] == EL_EXIT_OPEN ||
8362 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8363 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
8365 DrawPlayer(player); /* needed here only to cleanup last field */
8368 if (local_player->friends_still_needed == 0 ||
8369 IS_SP_ELEMENT(Feld[jx][jy]))
8370 player->LevelSolved = player->GameOver = TRUE;
8373 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8375 TestIfHeroTouchesBadThing(jx, jy);
8376 TestIfPlayerTouchesCustomElement(jx, jy);
8378 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8381 if (!player->active)
8385 if (tape.single_step && tape.recording && !tape.pausing &&
8386 !player->programmed_action)
8387 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8391 void ScrollScreen(struct PlayerInfo *player, int mode)
8393 static unsigned long screen_frame_counter = 0;
8395 if (mode == SCROLL_INIT)
8397 /* set scrolling step size according to actual player's moving speed */
8398 ScrollStepSize = TILEX / player->move_delay_value;
8400 screen_frame_counter = FrameCounter;
8401 ScreenMovDir = player->MovDir;
8402 ScreenMovPos = player->MovPos;
8403 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8406 else if (!FrameReached(&screen_frame_counter, 1))
8411 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8412 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8413 redraw_mask |= REDRAW_FIELD;
8416 ScreenMovDir = MV_NO_MOVING;
8419 void TestIfPlayerTouchesCustomElement(int x, int y)
8421 static int xy[4][2] =
8428 static int change_sides[4][2] =
8430 /* center side border side */
8431 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8432 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8433 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8434 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8436 static int touch_dir[4] =
8443 int center_element = Feld[x][y]; /* should always be non-moving! */
8446 for (i = 0; i < 4; i++)
8448 int xx = x + xy[i][0];
8449 int yy = y + xy[i][1];
8450 int center_side = change_sides[i][0];
8451 int border_side = change_sides[i][1];
8454 if (!IN_LEV_FIELD(xx, yy))
8457 if (IS_PLAYER(x, y))
8459 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8460 border_element = Feld[xx][yy]; /* may be moving! */
8461 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8462 border_element = Feld[xx][yy];
8463 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8464 border_element = MovingOrBlocked2Element(xx, yy);
8466 continue; /* center and border element do not touch */
8468 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
8469 CE_OTHER_GETS_TOUCHED);
8470 CheckElementSideChange(xx, yy, border_element, border_side,
8471 CE_TOUCHED_BY_PLAYER, -1);
8473 else if (IS_PLAYER(xx, yy))
8475 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8477 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8479 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8480 continue; /* center and border element do not touch */
8483 CheckTriggeredElementSideChange(x, y, center_element, center_side,
8484 CE_OTHER_GETS_TOUCHED);
8485 CheckElementSideChange(x, y, center_element, center_side,
8486 CE_TOUCHED_BY_PLAYER, -1);
8493 void TestIfElementTouchesCustomElement(int x, int y)
8495 static int xy[4][2] =
8502 static int change_sides[4][2] =
8504 /* center side border side */
8505 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8506 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8507 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8508 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8510 static int touch_dir[4] =
8517 boolean change_center_element = FALSE;
8518 int center_element_change_page = 0;
8519 int center_element = Feld[x][y]; /* should always be non-moving! */
8522 for (i = 0; i < 4; i++)
8524 int xx = x + xy[i][0];
8525 int yy = y + xy[i][1];
8526 int center_side = change_sides[i][0];
8527 int border_side = change_sides[i][1];
8530 if (!IN_LEV_FIELD(xx, yy))
8533 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8534 border_element = Feld[xx][yy]; /* may be moving! */
8535 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8536 border_element = Feld[xx][yy];
8537 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8538 border_element = MovingOrBlocked2Element(xx, yy);
8540 continue; /* center and border element do not touch */
8542 /* check for change of center element (but change it only once) */
8543 if (IS_CUSTOM_ELEMENT(center_element) &&
8544 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8545 !change_center_element)
8547 for (j = 0; j < element_info[center_element].num_change_pages; j++)
8549 struct ElementChangeInfo *change =
8550 &element_info[center_element].change_page[j];
8552 if (change->can_change &&
8553 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8554 change->sides & border_side &&
8556 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
8558 change->trigger_element == border_element
8562 change_center_element = TRUE;
8563 center_element_change_page = j;
8570 /* check for change of border element */
8571 if (IS_CUSTOM_ELEMENT(border_element) &&
8572 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8574 for (j = 0; j < element_info[border_element].num_change_pages; j++)
8576 struct ElementChangeInfo *change =
8577 &element_info[border_element].change_page[j];
8579 if (change->can_change &&
8580 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8581 change->sides & center_side &&
8583 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
8585 change->trigger_element == center_element
8589 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
8590 CE_OTHER_IS_TOUCHING, j);
8597 if (change_center_element)
8598 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
8599 CE_OTHER_IS_TOUCHING, center_element_change_page);
8602 void TestIfElementHitsCustomElement(int x, int y, int direction)
8604 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8605 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8606 int hitx = x + dx, hity = y + dy;
8607 int hitting_element = Feld[x][y];
8609 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8610 !IS_FREE(hitx, hity) &&
8611 (!IS_MOVING(hitx, hity) ||
8612 MovDir[hitx][hity] != direction ||
8613 ABS(MovPos[hitx][hity]) <= TILEY / 2));
8616 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8620 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8624 CheckElementSideChange(x, y, hitting_element,
8625 direction, CE_HITTING_SOMETHING, -1);
8627 if (IN_LEV_FIELD(hitx, hity))
8629 int opposite_direction = MV_DIR_OPPOSITE(direction);
8630 int hitting_side = direction;
8631 int touched_side = opposite_direction;
8632 int touched_element = MovingOrBlocked2Element(hitx, hity);
8634 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8635 MovDir[hitx][hity] != direction ||
8636 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8645 CheckElementSideChange(hitx, hity, touched_element,
8646 opposite_direction, CE_HIT_BY_SOMETHING, -1);
8648 if (IS_CUSTOM_ELEMENT(hitting_element) &&
8649 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8651 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8653 struct ElementChangeInfo *change =
8654 &element_info[hitting_element].change_page[i];
8656 if (change->can_change &&
8657 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8658 change->sides & touched_side &&
8661 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
8663 change->trigger_element == touched_element
8667 CheckElementSideChange(x, y, hitting_element,
8668 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8674 if (IS_CUSTOM_ELEMENT(touched_element) &&
8675 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8677 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8679 struct ElementChangeInfo *change =
8680 &element_info[touched_element].change_page[i];
8682 if (change->can_change &&
8683 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8684 change->sides & hitting_side &&
8686 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
8688 change->trigger_element == hitting_element
8692 CheckElementSideChange(hitx, hity, touched_element,
8693 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8702 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8704 int i, kill_x = -1, kill_y = -1;
8705 static int test_xy[4][2] =
8712 static int test_dir[4] =
8720 for (i = 0; i < 4; i++)
8722 int test_x, test_y, test_move_dir, test_element;
8724 test_x = good_x + test_xy[i][0];
8725 test_y = good_y + test_xy[i][1];
8726 if (!IN_LEV_FIELD(test_x, test_y))
8730 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8733 test_element = Feld[test_x][test_y];
8735 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8738 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8739 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8741 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8742 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8750 if (kill_x != -1 || kill_y != -1)
8752 if (IS_PLAYER(good_x, good_y))
8754 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8756 if (player->shield_deadly_time_left > 0)
8757 Bang(kill_x, kill_y);
8758 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
8762 Bang(good_x, good_y);
8766 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8768 int i, kill_x = -1, kill_y = -1;
8769 int bad_element = Feld[bad_x][bad_y];
8770 static int test_xy[4][2] =
8777 static int touch_dir[4] =
8784 static int test_dir[4] =
8792 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
8795 for (i = 0; i < 4; i++)
8797 int test_x, test_y, test_move_dir, test_element;
8799 test_x = bad_x + test_xy[i][0];
8800 test_y = bad_y + test_xy[i][1];
8801 if (!IN_LEV_FIELD(test_x, test_y))
8805 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8807 test_element = Feld[test_x][test_y];
8809 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8810 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8812 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
8813 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
8815 /* good thing is player or penguin that does not move away */
8816 if (IS_PLAYER(test_x, test_y))
8818 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8820 if (bad_element == EL_ROBOT && player->is_moving)
8821 continue; /* robot does not kill player if he is moving */
8823 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8825 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8826 continue; /* center and border element do not touch */
8833 else if (test_element == EL_PENGUIN)
8842 if (kill_x != -1 || kill_y != -1)
8844 if (IS_PLAYER(kill_x, kill_y))
8846 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8848 if (player->shield_deadly_time_left > 0)
8850 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
8854 Bang(kill_x, kill_y);
8858 void TestIfHeroTouchesBadThing(int x, int y)
8860 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8863 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8865 TestIfGoodThingHitsBadThing(x, y, move_dir);
8868 void TestIfBadThingTouchesHero(int x, int y)
8870 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8873 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8875 TestIfBadThingHitsGoodThing(x, y, move_dir);
8878 void TestIfFriendTouchesBadThing(int x, int y)
8880 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8883 void TestIfBadThingTouchesFriend(int x, int y)
8885 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8888 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8890 int i, kill_x = bad_x, kill_y = bad_y;
8891 static int xy[4][2] =
8899 for (i = 0; i < 4; i++)
8903 x = bad_x + xy[i][0];
8904 y = bad_y + xy[i][1];
8905 if (!IN_LEV_FIELD(x, y))
8908 element = Feld[x][y];
8909 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8910 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8918 if (kill_x != bad_x || kill_y != bad_y)
8922 void KillHero(struct PlayerInfo *player)
8924 int jx = player->jx, jy = player->jy;
8926 if (!player->active)
8929 /* remove accessible field at the player's position */
8930 Feld[jx][jy] = EL_EMPTY;
8932 /* deactivate shield (else Bang()/Explode() would not work right) */
8933 player->shield_normal_time_left = 0;
8934 player->shield_deadly_time_left = 0;
8940 static void KillHeroUnlessEnemyProtected(int x, int y)
8942 if (!PLAYER_ENEMY_PROTECTED(x, y))
8943 KillHero(PLAYERINFO(x, y));
8946 static void KillHeroUnlessExplosionProtected(int x, int y)
8948 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
8949 KillHero(PLAYERINFO(x, y));
8952 void BuryHero(struct PlayerInfo *player)
8954 int jx = player->jx, jy = player->jy;
8956 if (!player->active)
8960 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8962 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8964 PlayLevelSound(jx, jy, SND_GAME_LOSING);
8966 player->GameOver = TRUE;
8970 void RemoveHero(struct PlayerInfo *player)
8972 int jx = player->jx, jy = player->jy;
8973 int i, found = FALSE;
8975 player->present = FALSE;
8976 player->active = FALSE;
8978 if (!ExplodeField[jx][jy])
8979 StorePlayer[jx][jy] = 0;
8981 for (i = 0; i < MAX_PLAYERS; i++)
8982 if (stored_player[i].active)
8986 AllPlayersGone = TRUE;
8993 =============================================================================
8994 checkDiagonalPushing()
8995 -----------------------------------------------------------------------------
8996 check if diagonal input device direction results in pushing of object
8997 (by checking if the alternative direction is walkable, diggable, ...)
8998 =============================================================================
9001 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9002 int x, int y, int real_dx, int real_dy)
9004 int jx, jy, dx, dy, xx, yy;
9006 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
9009 /* diagonal direction: check alternative direction */
9014 xx = jx + (dx == 0 ? real_dx : 0);
9015 yy = jy + (dy == 0 ? real_dy : 0);
9017 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9021 =============================================================================
9023 -----------------------------------------------------------------------------
9024 x, y: field next to player (non-diagonal) to try to dig to
9025 real_dx, real_dy: direction as read from input device (can be diagonal)
9026 =============================================================================
9029 int DigField(struct PlayerInfo *player,
9030 int oldx, int oldy, int x, int y,
9031 int real_dx, int real_dy, int mode)
9033 static int change_sides[4] =
9035 CH_SIDE_RIGHT, /* moving left */
9036 CH_SIDE_LEFT, /* moving right */
9037 CH_SIDE_BOTTOM, /* moving up */
9038 CH_SIDE_TOP, /* moving down */
9041 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
9043 int jx = oldx, jy = oldy;
9044 int dx = x - jx, dy = y - jy;
9045 int nextx = x + dx, nexty = y + dy;
9046 int move_direction = (dx == -1 ? MV_LEFT :
9047 dx == +1 ? MV_RIGHT :
9049 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9050 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9051 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
9052 int old_element = Feld[jx][jy];
9055 if (player->MovPos == 0)
9057 player->is_digging = FALSE;
9058 player->is_collecting = FALSE;
9061 if (player->MovPos == 0) /* last pushing move finished */
9062 player->is_pushing = FALSE;
9064 if (mode == DF_NO_PUSH) /* player just stopped pushing */
9066 player->is_switching = FALSE;
9067 player->push_delay = 0;
9069 return MF_NO_ACTION;
9072 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9073 return MF_NO_ACTION;
9078 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
9080 if (IS_TUBE(Feld[jx][jy]) ||
9081 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
9085 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
9086 int tube_leave_directions[][2] =
9088 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9089 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
9090 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
9091 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
9092 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
9093 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
9094 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
9095 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
9096 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
9097 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
9098 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
9099 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
9102 while (tube_leave_directions[i][0] != tube_element)
9105 if (tube_leave_directions[i][0] == -1) /* should not happen */
9109 if (!(tube_leave_directions[i][1] & move_direction))
9110 return MF_NO_ACTION; /* tube has no opening in this direction */
9115 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9116 old_element = Back[jx][jy];
9120 if (IS_WALKABLE(old_element) &&
9121 !(element_info[old_element].access_direction & move_direction))
9122 return MF_NO_ACTION; /* field has no opening in this direction */
9124 element = Feld[x][y];
9126 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9127 game.engine_version >= VERSION_IDENT(2,2,0,0))
9128 return MF_NO_ACTION;
9132 case EL_SP_PORT_LEFT:
9133 case EL_SP_PORT_RIGHT:
9135 case EL_SP_PORT_DOWN:
9136 case EL_SP_PORT_HORIZONTAL:
9137 case EL_SP_PORT_VERTICAL:
9138 case EL_SP_PORT_ANY:
9139 case EL_SP_GRAVITY_PORT_LEFT:
9140 case EL_SP_GRAVITY_PORT_RIGHT:
9141 case EL_SP_GRAVITY_PORT_UP:
9142 case EL_SP_GRAVITY_PORT_DOWN:
9144 if (!canEnterSupaplexPort(x, y, dx, dy))
9145 return MF_NO_ACTION;
9148 element != EL_SP_PORT_LEFT &&
9149 element != EL_SP_GRAVITY_PORT_LEFT &&
9150 element != EL_SP_PORT_HORIZONTAL &&
9151 element != EL_SP_PORT_ANY) ||
9153 element != EL_SP_PORT_RIGHT &&
9154 element != EL_SP_GRAVITY_PORT_RIGHT &&
9155 element != EL_SP_PORT_HORIZONTAL &&
9156 element != EL_SP_PORT_ANY) ||
9158 element != EL_SP_PORT_UP &&
9159 element != EL_SP_GRAVITY_PORT_UP &&
9160 element != EL_SP_PORT_VERTICAL &&
9161 element != EL_SP_PORT_ANY) ||
9163 element != EL_SP_PORT_DOWN &&
9164 element != EL_SP_GRAVITY_PORT_DOWN &&
9165 element != EL_SP_PORT_VERTICAL &&
9166 element != EL_SP_PORT_ANY) ||
9167 !IN_LEV_FIELD(nextx, nexty) ||
9168 !IS_FREE(nextx, nexty))
9169 return MF_NO_ACTION;
9172 if (element == EL_SP_GRAVITY_PORT_LEFT ||
9173 element == EL_SP_GRAVITY_PORT_RIGHT ||
9174 element == EL_SP_GRAVITY_PORT_UP ||
9175 element == EL_SP_GRAVITY_PORT_DOWN)
9176 game.gravity = !game.gravity;
9178 /* automatically move to the next field with double speed */
9179 player->programmed_action = move_direction;
9181 if (player->move_delay_reset_counter == 0)
9183 player->move_delay_reset_counter = 2; /* two double speed steps */
9185 DOUBLE_PLAYER_SPEED(player);
9188 player->move_delay_reset_counter = 2;
9190 DOUBLE_PLAYER_SPEED(player);
9193 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
9198 case EL_TUBE_VERTICAL:
9199 case EL_TUBE_HORIZONTAL:
9200 case EL_TUBE_VERTICAL_LEFT:
9201 case EL_TUBE_VERTICAL_RIGHT:
9202 case EL_TUBE_HORIZONTAL_UP:
9203 case EL_TUBE_HORIZONTAL_DOWN:
9204 case EL_TUBE_LEFT_UP:
9205 case EL_TUBE_LEFT_DOWN:
9206 case EL_TUBE_RIGHT_UP:
9207 case EL_TUBE_RIGHT_DOWN:
9210 int tube_enter_directions[][2] =
9212 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9213 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
9214 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
9215 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
9216 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
9217 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
9218 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
9219 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
9220 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
9221 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
9222 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
9223 { -1, MV_NO_MOVING }
9226 while (tube_enter_directions[i][0] != element)
9229 if (tube_enter_directions[i][0] == -1) /* should not happen */
9233 if (!(tube_enter_directions[i][1] & move_direction))
9234 return MF_NO_ACTION; /* tube has no opening in this direction */
9236 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
9243 if (IS_WALKABLE(element))
9245 int sound_action = ACTION_WALKING;
9247 if (!(element_info[element].access_direction & opposite_direction))
9248 return MF_NO_ACTION; /* field not accessible from this direction */
9250 if (element >= EL_GATE_1 && element <= EL_GATE_4)
9252 if (!player->key[element - EL_GATE_1])
9253 return MF_NO_ACTION;
9255 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
9257 if (!player->key[element - EL_GATE_1_GRAY])
9258 return MF_NO_ACTION;
9260 else if (element == EL_EXIT_OPEN ||
9261 element == EL_SP_EXIT_OPEN ||
9262 element == EL_SP_EXIT_OPENING)
9264 sound_action = ACTION_PASSING; /* player is passing exit */
9266 else if (element == EL_EMPTY)
9268 sound_action = ACTION_MOVING; /* nothing to walk on */
9271 /* play sound from background or player, whatever is available */
9272 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
9273 PlayLevelSoundElementAction(x, y, element, sound_action);
9275 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9279 else if (IS_PASSABLE(element))
9281 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
9282 return MF_NO_ACTION;
9284 if (IS_CUSTOM_ELEMENT(element) &&
9285 !(element_info[element].access_direction & opposite_direction))
9286 return MF_NO_ACTION; /* field not accessible from this direction */
9289 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
9290 return MF_NO_ACTION;
9293 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
9295 if (!player->key[element - EL_EM_GATE_1])
9296 return MF_NO_ACTION;
9298 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
9300 if (!player->key[element - EL_EM_GATE_1_GRAY])
9301 return MF_NO_ACTION;
9304 /* automatically move to the next field with double speed */
9305 player->programmed_action = move_direction;
9307 if (player->move_delay_reset_counter == 0)
9309 player->move_delay_reset_counter = 2; /* two double speed steps */
9311 DOUBLE_PLAYER_SPEED(player);
9314 player->move_delay_reset_counter = 2;
9316 DOUBLE_PLAYER_SPEED(player);
9319 PlayLevelSoundAction(x, y, ACTION_PASSING);
9323 else if (IS_DIGGABLE(element))
9327 if (mode != DF_SNAP)
9330 GfxElement[x][y] = GFX_ELEMENT(element);
9333 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
9335 player->is_digging = TRUE;
9338 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9340 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
9343 if (mode == DF_SNAP)
9344 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9349 else if (IS_COLLECTIBLE(element))
9353 if (mode != DF_SNAP)
9355 GfxElement[x][y] = element;
9356 player->is_collecting = TRUE;
9359 if (element == EL_SPEED_PILL)
9360 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9361 else if (element == EL_EXTRA_TIME && level.time > 0)
9364 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9366 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9368 player->shield_normal_time_left += 10;
9369 if (element == EL_SHIELD_DEADLY)
9370 player->shield_deadly_time_left += 10;
9372 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9374 if (player->inventory_size < MAX_INVENTORY_SIZE)
9375 player->inventory_element[player->inventory_size++] = element;
9377 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9378 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9380 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9382 player->dynabomb_count++;
9383 player->dynabombs_left++;
9385 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9387 player->dynabomb_size++;
9389 else if (element == EL_DYNABOMB_INCREASE_POWER)
9391 player->dynabomb_xl = TRUE;
9393 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
9394 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
9396 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
9397 element - EL_KEY_1 : element - EL_EM_KEY_1);
9399 player->key[key_nr] = TRUE;
9401 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
9402 el2edimg(EL_KEY_1 + key_nr));
9403 redraw_mask |= REDRAW_DOOR_1;
9405 else if (IS_ENVELOPE(element))
9408 player->show_envelope = element;
9410 ShowEnvelope(element - EL_ENVELOPE_1);
9413 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
9417 for (i = 0; i < element_info[element].collect_count; i++)
9418 if (player->inventory_size < MAX_INVENTORY_SIZE)
9419 player->inventory_element[player->inventory_size++] = element;
9421 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9422 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9424 else if (element_info[element].collect_count > 0)
9426 local_player->gems_still_needed -=
9427 element_info[element].collect_count;
9428 if (local_player->gems_still_needed < 0)
9429 local_player->gems_still_needed = 0;
9431 DrawText(DX_EMERALDS, DY_EMERALDS,
9432 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
9435 RaiseScoreElement(element);
9436 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9438 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
9441 if (mode == DF_SNAP)
9442 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9447 else if (IS_PUSHABLE(element))
9449 if (mode == DF_SNAP && element != EL_BD_ROCK)
9450 return MF_NO_ACTION;
9452 if (CAN_FALL(element) && dy)
9453 return MF_NO_ACTION;
9455 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9456 !(element == EL_SPRING && level.use_spring_bug))
9457 return MF_NO_ACTION;
9460 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9461 ((move_direction & MV_VERTICAL &&
9462 ((element_info[element].move_pattern & MV_LEFT &&
9463 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9464 (element_info[element].move_pattern & MV_RIGHT &&
9465 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9466 (move_direction & MV_HORIZONTAL &&
9467 ((element_info[element].move_pattern & MV_UP &&
9468 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9469 (element_info[element].move_pattern & MV_DOWN &&
9470 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9471 return MF_NO_ACTION;
9475 /* do not push elements already moving away faster than player */
9476 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9477 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9478 return MF_NO_ACTION;
9480 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
9481 return MF_NO_ACTION;
9485 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9487 if (player->push_delay_value == -1)
9488 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9490 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9492 if (!player->is_pushing)
9493 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9497 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
9498 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
9499 !player_is_pushing))
9500 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9503 if (!player->is_pushing &&
9504 game.engine_version >= VERSION_IDENT(2,2,0,7))
9505 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9509 printf("::: push delay: %ld [%d, %d] [%d]\n",
9510 player->push_delay_value, FrameCounter, game.engine_version,
9511 player->is_pushing);
9514 player->is_pushing = TRUE;
9516 if (!(IN_LEV_FIELD(nextx, nexty) &&
9517 (IS_FREE(nextx, nexty) ||
9518 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9519 IS_SB_ELEMENT(element)))))
9520 return MF_NO_ACTION;
9522 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9523 return MF_NO_ACTION;
9525 if (player->push_delay == 0) /* new pushing; restart delay */
9526 player->push_delay = FrameCounter;
9528 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
9529 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9530 element != EL_SPRING && element != EL_BALLOON)
9532 /* make sure that there is no move delay before next try to push */
9533 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9534 player->move_delay = INITIAL_MOVE_DELAY_OFF;
9536 return MF_NO_ACTION;
9540 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
9543 if (IS_SB_ELEMENT(element))
9545 if (element == EL_SOKOBAN_FIELD_FULL)
9547 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9548 local_player->sokobanfields_still_needed++;
9551 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9553 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9554 local_player->sokobanfields_still_needed--;
9557 Feld[x][y] = EL_SOKOBAN_OBJECT;
9559 if (Back[x][y] == Back[nextx][nexty])
9560 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9561 else if (Back[x][y] != 0)
9562 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9565 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9568 if (local_player->sokobanfields_still_needed == 0 &&
9569 game.emulation == EMU_SOKOBAN)
9571 player->LevelSolved = player->GameOver = TRUE;
9572 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9576 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9578 InitMovingField(x, y, move_direction);
9579 GfxAction[x][y] = ACTION_PUSHING;
9581 if (mode == DF_SNAP)
9582 ContinueMoving(x, y);
9584 MovPos[x][y] = (dx != 0 ? dx : dy);
9586 Pushed[x][y] = TRUE;
9587 Pushed[nextx][nexty] = TRUE;
9589 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9590 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9592 player->push_delay_value = -1; /* get new value later */
9594 CheckTriggeredElementSideChange(x, y, element, dig_side,
9595 CE_OTHER_GETS_PUSHED);
9596 CheckElementSideChange(x, y, element, dig_side,
9597 CE_PUSHED_BY_PLAYER, -1);
9601 else if (IS_SWITCHABLE(element))
9603 if (PLAYER_SWITCHING(player, x, y))
9606 player->is_switching = TRUE;
9607 player->switch_x = x;
9608 player->switch_y = y;
9610 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9612 if (element == EL_ROBOT_WHEEL)
9614 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9618 DrawLevelField(x, y);
9620 else if (element == EL_SP_TERMINAL)
9624 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9626 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9628 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9629 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9632 else if (IS_BELT_SWITCH(element))
9634 ToggleBeltSwitch(x, y);
9636 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9637 element == EL_SWITCHGATE_SWITCH_DOWN)
9639 ToggleSwitchgateSwitch(x, y);
9641 else if (element == EL_LIGHT_SWITCH ||
9642 element == EL_LIGHT_SWITCH_ACTIVE)
9644 ToggleLightSwitch(x, y);
9647 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9648 SND_LIGHT_SWITCH_ACTIVATING :
9649 SND_LIGHT_SWITCH_DEACTIVATING);
9652 else if (element == EL_TIMEGATE_SWITCH)
9654 ActivateTimegateSwitch(x, y);
9656 else if (element == EL_BALLOON_SWITCH_LEFT ||
9657 element == EL_BALLOON_SWITCH_RIGHT ||
9658 element == EL_BALLOON_SWITCH_UP ||
9659 element == EL_BALLOON_SWITCH_DOWN ||
9660 element == EL_BALLOON_SWITCH_ANY)
9662 if (element == EL_BALLOON_SWITCH_ANY)
9663 game.balloon_dir = move_direction;
9665 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9666 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9667 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9668 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9671 else if (element == EL_LAMP)
9673 Feld[x][y] = EL_LAMP_ACTIVE;
9674 local_player->lights_still_needed--;
9676 DrawLevelField(x, y);
9678 else if (element == EL_TIME_ORB_FULL)
9680 Feld[x][y] = EL_TIME_ORB_EMPTY;
9682 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9684 DrawLevelField(x, y);
9687 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9695 if (!PLAYER_SWITCHING(player, x, y))
9697 player->is_switching = TRUE;
9698 player->switch_x = x;
9699 player->switch_y = y;
9701 CheckTriggeredElementSideChange(x, y, element, dig_side,
9702 CE_OTHER_IS_SWITCHING);
9703 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
9706 CheckTriggeredElementSideChange(x, y, element, dig_side,
9707 CE_OTHER_GETS_PRESSED);
9708 CheckElementSideChange(x, y, element, dig_side,
9709 CE_PRESSED_BY_PLAYER, -1);
9712 return MF_NO_ACTION;
9715 player->push_delay = 0;
9717 if (Feld[x][y] != element) /* really digged/collected something */
9718 player->is_collecting = !player->is_digging;
9723 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9725 int jx = player->jx, jy = player->jy;
9726 int x = jx + dx, y = jy + dy;
9727 int snap_direction = (dx == -1 ? MV_LEFT :
9728 dx == +1 ? MV_RIGHT :
9730 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9732 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9735 if (!player->active || !IN_LEV_FIELD(x, y))
9743 if (player->MovPos == 0)
9744 player->is_pushing = FALSE;
9746 player->is_snapping = FALSE;
9748 if (player->MovPos == 0)
9750 player->is_moving = FALSE;
9751 player->is_digging = FALSE;
9752 player->is_collecting = FALSE;
9758 if (player->is_snapping)
9761 player->MovDir = snap_direction;
9764 if (player->MovPos == 0)
9767 player->is_moving = FALSE;
9768 player->is_digging = FALSE;
9769 player->is_collecting = FALSE;
9772 player->is_dropping = FALSE;
9774 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9777 player->is_snapping = TRUE;
9780 if (player->MovPos == 0)
9783 player->is_moving = FALSE;
9784 player->is_digging = FALSE;
9785 player->is_collecting = FALSE;
9788 DrawLevelField(x, y);
9794 boolean DropElement(struct PlayerInfo *player)
9796 int jx = player->jx, jy = player->jy;
9797 int old_element = Feld[jx][jy];
9800 /* check if player is active, not moving and ready to drop */
9801 if (!player->active || player->MovPos || player->drop_delay > 0)
9804 /* check if player has anything that can be dropped */
9805 if (player->inventory_size == 0 && player->dynabombs_left == 0)
9808 /* check if anything can be dropped at the current position */
9809 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9812 /* collected custom elements can only be dropped on empty fields */
9813 if (player->inventory_size > 0 &&
9814 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9815 && old_element != EL_EMPTY)
9818 if (old_element != EL_EMPTY)
9819 Back[jx][jy] = old_element; /* store old element on this field */
9821 ResetGfxAnimation(jx, jy);
9822 ResetRandomAnimationValue(jx, jy);
9824 if (player->inventory_size > 0)
9826 player->inventory_size--;
9827 new_element = player->inventory_element[player->inventory_size];
9829 if (new_element == EL_DYNAMITE)
9830 new_element = EL_DYNAMITE_ACTIVE;
9831 else if (new_element == EL_SP_DISK_RED)
9832 new_element = EL_SP_DISK_RED_ACTIVE;
9834 Feld[jx][jy] = new_element;
9836 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9837 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9839 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9840 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9842 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9845 /* needed if previous element just changed to "empty" in the last frame */
9846 Changed[jx][jy] = 0; /* allow another change */
9849 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9850 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9852 TestIfElementTouchesCustomElement(jx, jy);
9854 else /* player is dropping a dyna bomb */
9856 player->dynabombs_left--;
9857 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9859 Feld[jx][jy] = new_element;
9861 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9862 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9864 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9871 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
9874 InitField_WithBug1(jx, jy, FALSE);
9876 InitField(jx, jy, FALSE);
9877 if (CAN_MOVE(Feld[jx][jy]))
9882 new_element = Feld[jx][jy];
9884 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9885 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9887 int move_stepsize = element_info[new_element].move_stepsize;
9888 int direction, dx, dy, nextx, nexty;
9890 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
9891 MovDir[jx][jy] = player->MovDir;
9893 direction = MovDir[jx][jy];
9894 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9895 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9899 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
9902 WasJustMoving[jx][jy] = 3;
9904 InitMovingField(jx, jy, direction);
9905 ContinueMoving(jx, jy);
9910 Changed[jx][jy] = 0; /* allow another change */
9913 TestIfElementHitsCustomElement(jx, jy, direction);
9915 CheckElementSideChange(jx, jy, new_element,
9916 direction, CE_HITTING_SOMETHING, -1);
9920 player->drop_delay = 2 * TILEX / move_stepsize + 1;
9924 player->drop_delay = 8 + 8 + 8;
9929 player->is_dropping = TRUE;
9935 /* ------------------------------------------------------------------------- */
9936 /* game sound playing functions */
9937 /* ------------------------------------------------------------------------- */
9939 static int *loop_sound_frame = NULL;
9940 static int *loop_sound_volume = NULL;
9942 void InitPlayLevelSound()
9944 int num_sounds = getSoundListSize();
9946 checked_free(loop_sound_frame);
9947 checked_free(loop_sound_volume);
9949 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
9950 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9953 static void PlayLevelSound(int x, int y, int nr)
9955 int sx = SCREENX(x), sy = SCREENY(y);
9956 int volume, stereo_position;
9957 int max_distance = 8;
9958 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9960 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9961 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9964 if (!IN_LEV_FIELD(x, y) ||
9965 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9966 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9969 volume = SOUND_MAX_VOLUME;
9971 if (!IN_SCR_FIELD(sx, sy))
9973 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9974 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9976 volume -= volume * (dx > dy ? dx : dy) / max_distance;
9979 stereo_position = (SOUND_MAX_LEFT +
9980 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9981 (SCR_FIELDX + 2 * max_distance));
9983 if (IS_LOOP_SOUND(nr))
9985 /* This assures that quieter loop sounds do not overwrite louder ones,
9986 while restarting sound volume comparison with each new game frame. */
9988 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9991 loop_sound_volume[nr] = volume;
9992 loop_sound_frame[nr] = FrameCounter;
9995 PlaySoundExt(nr, volume, stereo_position, type);
9998 static void PlayLevelSoundNearest(int x, int y, int sound_action)
10000 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
10001 x > LEVELX(BX2) ? LEVELX(BX2) : x,
10002 y < LEVELY(BY1) ? LEVELY(BY1) :
10003 y > LEVELY(BY2) ? LEVELY(BY2) : y,
10007 static void PlayLevelSoundAction(int x, int y, int action)
10009 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
10012 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
10014 int sound_effect = element_info[element].sound[action];
10016 if (sound_effect != SND_UNDEFINED)
10017 PlayLevelSound(x, y, sound_effect);
10020 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
10023 int sound_effect = element_info[element].sound[action];
10025 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10026 PlayLevelSound(x, y, sound_effect);
10029 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
10031 int sound_effect = element_info[Feld[x][y]].sound[action];
10033 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10034 PlayLevelSound(x, y, sound_effect);
10037 static void StopLevelSoundActionIfLoop(int x, int y, int action)
10039 int sound_effect = element_info[Feld[x][y]].sound[action];
10041 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10042 StopSound(sound_effect);
10045 static void PlayLevelMusic()
10047 if (levelset.music[level_nr] != MUS_UNDEFINED)
10048 PlayMusic(levelset.music[level_nr]); /* from config file */
10050 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
10053 void RaiseScore(int value)
10055 local_player->score += value;
10056 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
10059 void RaiseScoreElement(int element)
10064 case EL_BD_DIAMOND:
10065 case EL_EMERALD_YELLOW:
10066 case EL_EMERALD_RED:
10067 case EL_EMERALD_PURPLE:
10068 case EL_SP_INFOTRON:
10069 RaiseScore(level.score[SC_EMERALD]);
10072 RaiseScore(level.score[SC_DIAMOND]);
10075 RaiseScore(level.score[SC_CRYSTAL]);
10078 RaiseScore(level.score[SC_PEARL]);
10081 case EL_BD_BUTTERFLY:
10082 case EL_SP_ELECTRON:
10083 RaiseScore(level.score[SC_BUG]);
10086 case EL_BD_FIREFLY:
10087 case EL_SP_SNIKSNAK:
10088 RaiseScore(level.score[SC_SPACESHIP]);
10091 case EL_DARK_YAMYAM:
10092 RaiseScore(level.score[SC_YAMYAM]);
10095 RaiseScore(level.score[SC_ROBOT]);
10098 RaiseScore(level.score[SC_PACMAN]);
10101 RaiseScore(level.score[SC_NUT]);
10104 case EL_SP_DISK_RED:
10105 case EL_DYNABOMB_INCREASE_NUMBER:
10106 case EL_DYNABOMB_INCREASE_SIZE:
10107 case EL_DYNABOMB_INCREASE_POWER:
10108 RaiseScore(level.score[SC_DYNAMITE]);
10110 case EL_SHIELD_NORMAL:
10111 case EL_SHIELD_DEADLY:
10112 RaiseScore(level.score[SC_SHIELD]);
10114 case EL_EXTRA_TIME:
10115 RaiseScore(level.score[SC_TIME_BONUS]);
10121 RaiseScore(level.score[SC_KEY]);
10124 RaiseScore(element_info[element].collect_score);
10129 void RequestQuitGame(boolean ask_if_really_quit)
10131 if (AllPlayersGone ||
10132 !ask_if_really_quit ||
10133 level_editor_test_game ||
10134 Request("Do you really want to quit the game ?",
10135 REQ_ASK | REQ_STAY_CLOSED))
10137 #if defined(PLATFORM_UNIX)
10138 if (options.network)
10139 SendToServer_StopPlaying();
10143 game_status = GAME_MODE_MAIN;
10149 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10154 /* ---------- new game button stuff ---------------------------------------- */
10156 /* graphic position values for game buttons */
10157 #define GAME_BUTTON_XSIZE 30
10158 #define GAME_BUTTON_YSIZE 30
10159 #define GAME_BUTTON_XPOS 5
10160 #define GAME_BUTTON_YPOS 215
10161 #define SOUND_BUTTON_XPOS 5
10162 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10164 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10165 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10166 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10167 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10168 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10169 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10176 } gamebutton_info[NUM_GAME_BUTTONS] =
10179 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
10184 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
10185 GAME_CTRL_ID_PAUSE,
10189 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
10194 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
10195 SOUND_CTRL_ID_MUSIC,
10196 "background music on/off"
10199 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
10200 SOUND_CTRL_ID_LOOPS,
10201 "sound loops on/off"
10204 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
10205 SOUND_CTRL_ID_SIMPLE,
10206 "normal sounds on/off"
10210 void CreateGameButtons()
10214 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10216 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10217 struct GadgetInfo *gi;
10220 unsigned long event_mask;
10221 int gd_xoffset, gd_yoffset;
10222 int gd_x1, gd_x2, gd_y1, gd_y2;
10225 gd_xoffset = gamebutton_info[i].x;
10226 gd_yoffset = gamebutton_info[i].y;
10227 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10228 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10230 if (id == GAME_CTRL_ID_STOP ||
10231 id == GAME_CTRL_ID_PAUSE ||
10232 id == GAME_CTRL_ID_PLAY)
10234 button_type = GD_TYPE_NORMAL_BUTTON;
10236 event_mask = GD_EVENT_RELEASED;
10237 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10238 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10242 button_type = GD_TYPE_CHECK_BUTTON;
10244 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10245 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10246 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10247 event_mask = GD_EVENT_PRESSED;
10248 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
10249 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10252 gi = CreateGadget(GDI_CUSTOM_ID, id,
10253 GDI_INFO_TEXT, gamebutton_info[i].infotext,
10254 GDI_X, DX + gd_xoffset,
10255 GDI_Y, DY + gd_yoffset,
10256 GDI_WIDTH, GAME_BUTTON_XSIZE,
10257 GDI_HEIGHT, GAME_BUTTON_YSIZE,
10258 GDI_TYPE, button_type,
10259 GDI_STATE, GD_BUTTON_UNPRESSED,
10260 GDI_CHECKED, checked,
10261 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10262 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10263 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10264 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10265 GDI_EVENT_MASK, event_mask,
10266 GDI_CALLBACK_ACTION, HandleGameButtons,
10270 Error(ERR_EXIT, "cannot create gadget");
10272 game_gadget[id] = gi;
10276 void FreeGameButtons()
10280 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10281 FreeGadget(game_gadget[i]);
10284 static void MapGameButtons()
10288 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10289 MapGadget(game_gadget[i]);
10292 void UnmapGameButtons()
10296 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10297 UnmapGadget(game_gadget[i]);
10300 static void HandleGameButtons(struct GadgetInfo *gi)
10302 int id = gi->custom_id;
10304 if (game_status != GAME_MODE_PLAYING)
10309 case GAME_CTRL_ID_STOP:
10310 RequestQuitGame(TRUE);
10313 case GAME_CTRL_ID_PAUSE:
10314 if (options.network)
10316 #if defined(PLATFORM_UNIX)
10318 SendToServer_ContinuePlaying();
10320 SendToServer_PausePlaying();
10324 TapeTogglePause(TAPE_TOGGLE_MANUAL);
10327 case GAME_CTRL_ID_PLAY:
10330 #if defined(PLATFORM_UNIX)
10331 if (options.network)
10332 SendToServer_ContinuePlaying();
10336 tape.pausing = FALSE;
10337 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10342 case SOUND_CTRL_ID_MUSIC:
10343 if (setup.sound_music)
10345 setup.sound_music = FALSE;
10348 else if (audio.music_available)
10350 setup.sound = setup.sound_music = TRUE;
10352 SetAudioMode(setup.sound);
10358 case SOUND_CTRL_ID_LOOPS:
10359 if (setup.sound_loops)
10360 setup.sound_loops = FALSE;
10361 else if (audio.loops_available)
10363 setup.sound = setup.sound_loops = TRUE;
10364 SetAudioMode(setup.sound);
10368 case SOUND_CTRL_ID_SIMPLE:
10369 if (setup.sound_simple)
10370 setup.sound_simple = FALSE;
10371 else if (audio.sound_available)
10373 setup.sound = setup.sound_simple = TRUE;
10374 SetAudioMode(setup.sound);