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 void DrawGameDoorValues()
869 for (i = 0; i < MAX_PLAYERS; i++)
870 for (j = 0; j < 4; j++)
871 if (stored_player[i].key[j])
872 DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
873 el2edimg(EL_KEY_1 + j));
875 DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
876 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
877 DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
878 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
879 DrawText(DX + XX_SCORE, DY + YY_SCORE,
880 int2str(local_player->score, 5), FONT_TEXT_2);
881 DrawText(DX + XX_TIME, DY + YY_TIME,
882 int2str(TimeLeft, 3), FONT_TEXT_2);
885 static void resolve_group_element(int group_element, int recursion_depth)
888 static struct ElementGroupInfo *group;
889 struct ElementGroupInfo *actual_group = element_info[group_element].group;
892 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
894 Error(ERR_WARN, "recursion too deep when resolving group element %d",
895 group_element - EL_GROUP_START + 1);
897 /* replace element which caused too deep recursion by question mark */
898 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
903 if (recursion_depth == 0) /* initialization */
905 group = element_info[group_element].group;
906 group_nr = group_element - EL_GROUP_START;
908 group->num_elements_resolved = 0;
909 group->choice_pos = 0;
912 for (i = 0; i < actual_group->num_elements; i++)
914 int element = actual_group->element[i];
916 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
919 if (IS_GROUP_ELEMENT(element))
920 resolve_group_element(element, recursion_depth + 1);
923 group->element_resolved[group->num_elements_resolved++] = element;
924 element_info[element].in_group[group_nr] = TRUE;
929 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
931 printf("::: group %d: %d resolved elements\n",
932 group_element - EL_GROUP_START, group->num_elements_resolved);
933 for (i = 0; i < group->num_elements_resolved; i++)
934 printf("::: - %d ['%s']\n", group->element_resolved[i],
935 element_info[group->element_resolved[i]].token_name);
942 =============================================================================
944 -----------------------------------------------------------------------------
945 initialize game engine due to level / tape version number
946 =============================================================================
949 static void InitGameEngine()
953 /* set game engine from tape file when re-playing, else from level file */
954 game.engine_version = (tape.playing ? tape.engine_version :
957 /* dynamically adjust element properties according to game engine version */
958 InitElementPropertiesEngine(game.engine_version);
961 printf("level %d: level version == %06d\n", level_nr, level.game_version);
962 printf(" tape version == %06d [%s] [file: %06d]\n",
963 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
965 printf(" => game.engine_version == %06d\n", game.engine_version);
968 /* ---------- recursively resolve group elements ------------------------- */
970 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
971 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
972 element_info[i].in_group[j] = FALSE;
974 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
975 resolve_group_element(EL_GROUP_START + i, 0);
977 /* ---------- initialize player's initial move delay --------------------- */
979 /* dynamically adjust player properties according to game engine version */
980 game.initial_move_delay =
981 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
982 INITIAL_MOVE_DELAY_OFF);
984 /* dynamically adjust player properties according to level information */
985 game.initial_move_delay_value =
986 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
988 /* ---------- initialize player's initial push delay --------------------- */
990 /* dynamically adjust player properties according to game engine version */
991 game.initial_push_delay_value =
992 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
994 /* ---------- initialize changing elements ------------------------------- */
996 /* initialize changing elements information */
997 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
999 struct ElementInfo *ei = &element_info[i];
1001 /* this pointer might have been changed in the level editor */
1002 ei->change = &ei->change_page[0];
1004 if (!IS_CUSTOM_ELEMENT(i))
1006 ei->change->target_element = EL_EMPTY_SPACE;
1007 ei->change->delay_fixed = 0;
1008 ei->change->delay_random = 0;
1009 ei->change->delay_frames = 1;
1012 ei->change_events = CE_BITMASK_DEFAULT;
1013 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1015 ei->event_page_nr[j] = 0;
1016 ei->event_page[j] = &ei->change_page[0];
1020 /* add changing elements from pre-defined list */
1021 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1023 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1024 struct ElementInfo *ei = &element_info[ch_delay->element];
1026 ei->change->target_element = ch_delay->target_element;
1027 ei->change->delay_fixed = ch_delay->change_delay;
1029 ei->change->pre_change_function = ch_delay->pre_change_function;
1030 ei->change->change_function = ch_delay->change_function;
1031 ei->change->post_change_function = ch_delay->post_change_function;
1033 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1037 /* add change events from custom element configuration */
1038 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1040 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1042 for (j = 0; j < ei->num_change_pages; j++)
1044 if (!ei->change_page[j].can_change)
1047 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1049 /* only add event page for the first page found with this event */
1050 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1051 !(ei->change_events & CH_EVENT_BIT(k)))
1053 ei->change_events |= CH_EVENT_BIT(k);
1054 ei->event_page_nr[k] = j;
1055 ei->event_page[k] = &ei->change_page[j];
1063 /* add change events from custom element configuration */
1064 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1066 int element = EL_CUSTOM_START + i;
1068 /* only add custom elements that change after fixed/random frame delay */
1069 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1070 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1074 /* ---------- initialize trigger events ---------------------------------- */
1076 /* initialize trigger events information */
1077 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1078 trigger_events[i] = EP_BITMASK_DEFAULT;
1081 /* add trigger events from element change event properties */
1082 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1084 struct ElementInfo *ei = &element_info[i];
1086 for (j = 0; j < ei->num_change_pages; j++)
1088 if (!ei->change_page[j].can_change)
1091 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1093 int trigger_element = ei->change_page[j].trigger_element;
1095 if (IS_GROUP_ELEMENT(trigger_element))
1097 struct ElementGroupInfo *group = element_info[trigger_element].group;
1099 for (k = 0; k < group->num_elements_resolved; k++)
1100 trigger_events[group->element_resolved[k]]
1101 |= ei->change_page[j].events;
1104 trigger_events[trigger_element] |= ei->change_page[j].events;
1109 /* add trigger events from element change event properties */
1110 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1111 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1112 trigger_events[element_info[i].change->trigger_element] |=
1113 element_info[i].change->events;
1116 /* ---------- initialize push delay -------------------------------------- */
1118 /* initialize push delay values to default */
1119 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1121 if (!IS_CUSTOM_ELEMENT(i))
1123 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1124 element_info[i].push_delay_random = game.default_push_delay_random;
1128 /* set push delay value for certain elements from pre-defined list */
1129 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1131 int e = push_delay_list[i].element;
1133 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1134 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1137 /* set push delay value for Supaplex elements for newer engine versions */
1138 if (game.engine_version >= VERSION_IDENT(3,0,9,0))
1140 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1142 if (IS_SP_ELEMENT(i))
1144 element_info[i].push_delay_fixed = 6;
1145 element_info[i].push_delay_random = 0;
1150 /* ---------- initialize move stepsize ----------------------------------- */
1152 /* initialize move stepsize values to default */
1153 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1154 if (!IS_CUSTOM_ELEMENT(i))
1155 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1157 /* set move stepsize value for certain elements from pre-defined list */
1158 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1160 int e = move_stepsize_list[i].element;
1162 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1165 /* ---------- initialize move dig/leave ---------------------------------- */
1167 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1169 element_info[i].can_leave_element = FALSE;
1170 element_info[i].can_leave_element_last = FALSE;
1173 /* ---------- initialize gem count --------------------------------------- */
1175 /* initialize gem count values for each element */
1176 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1177 if (!IS_CUSTOM_ELEMENT(i))
1178 element_info[i].collect_count = 0;
1180 /* add gem count values for all elements from pre-defined list */
1181 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1182 element_info[collect_count_list[i].element].collect_count =
1183 collect_count_list[i].count;
1185 /* ---------- initialize access direction -------------------------------- */
1187 /* initialize access direction values to default */
1188 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1189 if (!IS_CUSTOM_ELEMENT(i))
1190 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1192 /* set access direction value for certain elements from pre-defined list */
1193 for (i = 0; tube_access[i].element != EL_UNDEFINED; i++)
1194 element_info[tube_access[i].element].access_direction =
1195 tube_access[i].direction;
1200 =============================================================================
1202 -----------------------------------------------------------------------------
1203 initialize and start new game
1204 =============================================================================
1209 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1210 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1211 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1218 #if USE_NEW_AMOEBA_CODE
1219 printf("Using new amoeba code.\n");
1221 printf("Using old amoeba code.\n");
1226 /* don't play tapes over network */
1227 network_playing = (options.network && !tape.playing);
1229 for (i = 0; i < MAX_PLAYERS; i++)
1231 struct PlayerInfo *player = &stored_player[i];
1233 player->index_nr = i;
1234 player->element_nr = EL_PLAYER_1 + i;
1236 player->present = FALSE;
1237 player->active = FALSE;
1240 player->effective_action = 0;
1241 player->programmed_action = 0;
1244 player->gems_still_needed = level.gems_needed;
1245 player->sokobanfields_still_needed = 0;
1246 player->lights_still_needed = 0;
1247 player->friends_still_needed = 0;
1249 for (j = 0; j < 4; j++)
1250 player->key[j] = FALSE;
1252 player->dynabomb_count = 0;
1253 player->dynabomb_size = 1;
1254 player->dynabombs_left = 0;
1255 player->dynabomb_xl = FALSE;
1257 player->MovDir = MV_NO_MOVING;
1260 player->GfxDir = MV_NO_MOVING;
1261 player->GfxAction = ACTION_DEFAULT;
1263 player->StepFrame = 0;
1265 player->use_murphy_graphic = FALSE;
1267 player->block_last_field = FALSE;
1269 player->actual_frame_counter = 0;
1271 player->step_counter = 0;
1273 player->last_move_dir = MV_NO_MOVING;
1275 player->is_waiting = FALSE;
1276 player->is_moving = FALSE;
1277 player->is_digging = FALSE;
1278 player->is_snapping = FALSE;
1279 player->is_collecting = FALSE;
1280 player->is_pushing = FALSE;
1281 player->is_switching = FALSE;
1282 player->is_dropping = FALSE;
1284 player->is_bored = FALSE;
1285 player->is_sleeping = FALSE;
1287 player->frame_counter_bored = -1;
1288 player->frame_counter_sleeping = -1;
1290 player->anim_delay_counter = 0;
1291 player->post_delay_counter = 0;
1293 player->action_waiting = ACTION_DEFAULT;
1294 player->last_action_waiting = ACTION_DEFAULT;
1295 player->special_action_bored = ACTION_DEFAULT;
1296 player->special_action_sleeping = ACTION_DEFAULT;
1298 player->num_special_action_bored = 0;
1299 player->num_special_action_sleeping = 0;
1301 /* determine number of special actions for bored and sleeping animation */
1302 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1304 boolean found = FALSE;
1306 for (k = 0; k < NUM_DIRECTIONS; k++)
1307 if (el_act_dir2img(player->element_nr, j, k) !=
1308 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1312 player->num_special_action_bored++;
1316 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1318 boolean found = FALSE;
1320 for (k = 0; k < NUM_DIRECTIONS; k++)
1321 if (el_act_dir2img(player->element_nr, j, k) !=
1322 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1326 player->num_special_action_sleeping++;
1331 player->switch_x = -1;
1332 player->switch_y = -1;
1334 player->show_envelope = 0;
1336 player->move_delay = game.initial_move_delay;
1337 player->move_delay_value = game.initial_move_delay_value;
1339 player->move_delay_reset_counter = 0;
1341 player->push_delay = 0;
1342 player->push_delay_value = game.initial_push_delay_value;
1344 player->drop_delay = 0;
1346 player->last_jx = player->last_jy = 0;
1347 player->jx = player->jy = 0;
1349 player->shield_normal_time_left = 0;
1350 player->shield_deadly_time_left = 0;
1352 player->inventory_size = 0;
1354 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1355 SnapField(player, 0, 0);
1357 player->LevelSolved = FALSE;
1358 player->GameOver = FALSE;
1361 network_player_action_received = FALSE;
1363 #if defined(PLATFORM_UNIX)
1364 /* initial null action */
1365 if (network_playing)
1366 SendToServer_MovePlayer(MV_NO_MOVING);
1374 TimeLeft = level.time;
1376 ScreenMovDir = MV_NO_MOVING;
1380 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1382 AllPlayersGone = FALSE;
1384 game.yamyam_content_nr = 0;
1385 game.magic_wall_active = FALSE;
1386 game.magic_wall_time_left = 0;
1387 game.light_time_left = 0;
1388 game.timegate_time_left = 0;
1389 game.switchgate_pos = 0;
1390 game.balloon_dir = MV_NO_MOVING;
1391 game.gravity = level.initial_gravity;
1392 game.explosions_delayed = TRUE;
1394 game.envelope_active = FALSE;
1396 for (i = 0; i < 4; i++)
1398 game.belt_dir[i] = MV_NO_MOVING;
1399 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1402 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1403 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1405 for (x = 0; x < lev_fieldx; x++)
1407 for (y = 0; y < lev_fieldy; y++)
1409 Feld[x][y] = level.field[x][y];
1410 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1411 ChangeDelay[x][y] = 0;
1412 ChangePage[x][y] = -1;
1413 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1415 WasJustMoving[x][y] = 0;
1416 WasJustFalling[x][y] = 0;
1418 Pushed[x][y] = FALSE;
1420 Changed[x][y] = CE_BITMASK_DEFAULT;
1421 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1423 ExplodePhase[x][y] = 0;
1424 ExplodeDelay[x][y] = 0;
1425 ExplodeField[x][y] = EX_NO_EXPLOSION;
1427 RunnerVisit[x][y] = 0;
1428 PlayerVisit[x][y] = 0;
1431 GfxRandom[x][y] = INIT_GFX_RANDOM();
1432 GfxElement[x][y] = EL_UNDEFINED;
1433 GfxAction[x][y] = ACTION_DEFAULT;
1434 GfxDir[x][y] = MV_NO_MOVING;
1438 for (y = 0; y < lev_fieldy; y++)
1440 for (x = 0; x < lev_fieldx; x++)
1442 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1444 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1446 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1449 InitField(x, y, TRUE);
1455 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1456 emulate_sb ? EMU_SOKOBAN :
1457 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1459 /* initialize explosion and ignition delay */
1460 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1462 if (!IS_CUSTOM_ELEMENT(i))
1465 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
1466 int last_phase = num_phase * delay;
1467 int half_phase = (num_phase / 2) * delay;
1469 element_info[i].explosion_delay = last_phase;
1470 element_info[i].ignition_delay = half_phase;
1472 if (i == EL_BLACK_ORB)
1473 element_info[i].ignition_delay = 1;
1476 if (element_info[i].explosion_delay < 2) /* !!! check again !!! */
1477 element_info[i].explosion_delay = 2;
1479 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1480 element_info[i].ignition_delay = 1;
1483 /* correct non-moving belts to start moving left */
1484 for (i = 0; i < 4; i++)
1485 if (game.belt_dir[i] == MV_NO_MOVING)
1486 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1488 /* check if any connected player was not found in playfield */
1489 for (i = 0; i < MAX_PLAYERS; i++)
1491 struct PlayerInfo *player = &stored_player[i];
1493 if (player->connected && !player->present)
1495 for (j = 0; j < MAX_PLAYERS; j++)
1497 struct PlayerInfo *some_player = &stored_player[j];
1498 int jx = some_player->jx, jy = some_player->jy;
1500 /* assign first free player found that is present in the playfield */
1501 if (some_player->present && !some_player->connected)
1503 player->present = TRUE;
1504 player->active = TRUE;
1506 some_player->present = FALSE;
1507 some_player->active = FALSE;
1509 StorePlayer[jx][jy] = player->element_nr;
1510 player->jx = player->last_jx = jx;
1511 player->jy = player->last_jy = jy;
1521 /* when playing a tape, eliminate all players which do not participate */
1523 for (i = 0; i < MAX_PLAYERS; i++)
1525 if (stored_player[i].active && !tape.player_participates[i])
1527 struct PlayerInfo *player = &stored_player[i];
1528 int jx = player->jx, jy = player->jy;
1530 player->active = FALSE;
1531 StorePlayer[jx][jy] = 0;
1532 Feld[jx][jy] = EL_EMPTY;
1536 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1538 /* when in single player mode, eliminate all but the first active player */
1540 for (i = 0; i < MAX_PLAYERS; i++)
1542 if (stored_player[i].active)
1544 for (j = i + 1; j < MAX_PLAYERS; j++)
1546 if (stored_player[j].active)
1548 struct PlayerInfo *player = &stored_player[j];
1549 int jx = player->jx, jy = player->jy;
1551 player->active = FALSE;
1552 player->present = FALSE;
1554 StorePlayer[jx][jy] = 0;
1555 Feld[jx][jy] = EL_EMPTY;
1562 /* when recording the game, store which players take part in the game */
1565 for (i = 0; i < MAX_PLAYERS; i++)
1566 if (stored_player[i].active)
1567 tape.player_participates[i] = TRUE;
1572 for (i = 0; i < MAX_PLAYERS; i++)
1574 struct PlayerInfo *player = &stored_player[i];
1576 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1581 if (local_player == player)
1582 printf("Player %d is local player.\n", i+1);
1586 if (BorderElement == EL_EMPTY)
1589 SBX_Right = lev_fieldx - SCR_FIELDX;
1591 SBY_Lower = lev_fieldy - SCR_FIELDY;
1596 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1598 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1601 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1602 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1604 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1605 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1607 /* if local player not found, look for custom element that might create
1608 the player (make some assumptions about the right custom element) */
1609 if (!local_player->present)
1611 int start_x = 0, start_y = 0;
1612 int found_rating = 0;
1613 int found_element = EL_UNDEFINED;
1615 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1617 int element = Feld[x][y];
1622 if (!IS_CUSTOM_ELEMENT(element))
1625 if (CAN_CHANGE(element))
1627 for (i = 0; i < element_info[element].num_change_pages; i++)
1629 content = element_info[element].change_page[i].target_element;
1630 is_player = ELEM_IS_PLAYER(content);
1632 if (is_player && (found_rating < 3 || element < found_element))
1638 found_element = element;
1643 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1645 content = element_info[element].content[xx][yy];
1646 is_player = ELEM_IS_PLAYER(content);
1648 if (is_player && (found_rating < 2 || element < found_element))
1650 start_x = x + xx - 1;
1651 start_y = y + yy - 1;
1654 found_element = element;
1657 if (!CAN_CHANGE(element))
1660 for (i = 0; i < element_info[element].num_change_pages; i++)
1662 content = element_info[element].change_page[i].content[xx][yy];
1663 is_player = ELEM_IS_PLAYER(content);
1665 if (is_player && (found_rating < 1 || element < found_element))
1667 start_x = x + xx - 1;
1668 start_y = y + yy - 1;
1671 found_element = element;
1677 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
1678 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1681 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1682 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1688 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
1689 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1690 local_player->jx - MIDPOSX);
1692 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1693 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1694 local_player->jy - MIDPOSY);
1696 scroll_x = SBX_Left;
1697 scroll_y = SBY_Upper;
1698 if (local_player->jx >= SBX_Left + MIDPOSX)
1699 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1700 local_player->jx - MIDPOSX :
1702 if (local_player->jy >= SBY_Upper + MIDPOSY)
1703 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1704 local_player->jy - MIDPOSY :
1709 CloseDoor(DOOR_CLOSE_1);
1714 /* after drawing the level, correct some elements */
1715 if (game.timegate_time_left == 0)
1716 CloseAllOpenTimegates();
1718 if (setup.soft_scrolling)
1719 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1721 redraw_mask |= REDRAW_FROM_BACKBUFFER;
1724 /* copy default game door content to main double buffer */
1725 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1726 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1729 DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1732 DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1733 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1734 BlitBitmap(drawto, drawto,
1735 DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1736 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1737 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1738 DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1741 DrawGameDoorValues();
1745 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1746 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1747 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1751 /* copy actual game door content to door double buffer for OpenDoor() */
1752 BlitBitmap(drawto, bitmap_db_door,
1753 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1755 OpenDoor(DOOR_OPEN_ALL);
1757 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1759 if (setup.sound_music)
1762 KeyboardAutoRepeatOffUnlessAutoplay();
1766 for (i = 0; i < 4; i++)
1767 printf("Player %d %sactive.\n",
1768 i + 1, (stored_player[i].active ? "" : "not "));
1772 printf("::: starting game [%d]\n", FrameCounter);
1776 void InitMovDir(int x, int y)
1778 int i, element = Feld[x][y];
1779 static int xy[4][2] =
1786 static int direction[3][4] =
1788 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
1789 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
1790 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
1799 Feld[x][y] = EL_BUG;
1800 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1803 case EL_SPACESHIP_RIGHT:
1804 case EL_SPACESHIP_UP:
1805 case EL_SPACESHIP_LEFT:
1806 case EL_SPACESHIP_DOWN:
1807 Feld[x][y] = EL_SPACESHIP;
1808 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1811 case EL_BD_BUTTERFLY_RIGHT:
1812 case EL_BD_BUTTERFLY_UP:
1813 case EL_BD_BUTTERFLY_LEFT:
1814 case EL_BD_BUTTERFLY_DOWN:
1815 Feld[x][y] = EL_BD_BUTTERFLY;
1816 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1819 case EL_BD_FIREFLY_RIGHT:
1820 case EL_BD_FIREFLY_UP:
1821 case EL_BD_FIREFLY_LEFT:
1822 case EL_BD_FIREFLY_DOWN:
1823 Feld[x][y] = EL_BD_FIREFLY;
1824 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1827 case EL_PACMAN_RIGHT:
1829 case EL_PACMAN_LEFT:
1830 case EL_PACMAN_DOWN:
1831 Feld[x][y] = EL_PACMAN;
1832 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1835 case EL_SP_SNIKSNAK:
1836 MovDir[x][y] = MV_UP;
1839 case EL_SP_ELECTRON:
1840 MovDir[x][y] = MV_LEFT;
1847 Feld[x][y] = EL_MOLE;
1848 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1852 if (IS_CUSTOM_ELEMENT(element))
1854 struct ElementInfo *ei = &element_info[element];
1855 int move_direction_initial = ei->move_direction_initial;
1856 int move_pattern = ei->move_pattern;
1858 if (move_direction_initial == MV_START_PREVIOUS)
1860 if (MovDir[x][y] != MV_NO_MOVING)
1863 move_direction_initial = MV_START_AUTOMATIC;
1866 if (move_direction_initial == MV_START_RANDOM)
1867 MovDir[x][y] = 1 << RND(4);
1868 else if (move_direction_initial & MV_ANY_DIRECTION)
1869 MovDir[x][y] = move_direction_initial;
1870 else if (move_pattern == MV_ALL_DIRECTIONS ||
1871 move_pattern == MV_TURNING_LEFT ||
1872 move_pattern == MV_TURNING_RIGHT ||
1873 move_pattern == MV_TURNING_LEFT_RIGHT ||
1874 move_pattern == MV_TURNING_RIGHT_LEFT ||
1875 move_pattern == MV_TURNING_RANDOM)
1876 MovDir[x][y] = 1 << RND(4);
1877 else if (move_pattern == MV_HORIZONTAL)
1878 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1879 else if (move_pattern == MV_VERTICAL)
1880 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1881 else if (move_pattern & MV_ANY_DIRECTION)
1882 MovDir[x][y] = element_info[element].move_pattern;
1883 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
1884 move_pattern == MV_ALONG_RIGHT_SIDE)
1886 for (i = 0; i < 4; i++)
1888 int x1 = x + xy[i][0];
1889 int y1 = y + xy[i][1];
1891 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1893 if (move_pattern == MV_ALONG_RIGHT_SIDE)
1894 MovDir[x][y] = direction[0][i];
1896 MovDir[x][y] = direction[1][i];
1905 MovDir[x][y] = 1 << RND(4);
1907 if (element != EL_BUG &&
1908 element != EL_SPACESHIP &&
1909 element != EL_BD_BUTTERFLY &&
1910 element != EL_BD_FIREFLY)
1913 for (i = 0; i < 4; i++)
1915 int x1 = x + xy[i][0];
1916 int y1 = y + xy[i][1];
1918 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1920 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1922 MovDir[x][y] = direction[0][i];
1925 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1926 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1928 MovDir[x][y] = direction[1][i];
1937 GfxDir[x][y] = MovDir[x][y];
1940 void InitAmoebaNr(int x, int y)
1943 int group_nr = AmoebeNachbarNr(x, y);
1947 for (i = 1; i < MAX_NUM_AMOEBA; i++)
1949 if (AmoebaCnt[i] == 0)
1957 AmoebaNr[x][y] = group_nr;
1958 AmoebaCnt[group_nr]++;
1959 AmoebaCnt2[group_nr]++;
1965 boolean raise_level = FALSE;
1967 if (local_player->MovPos)
1971 if (tape.auto_play) /* tape might already be stopped here */
1972 tape.auto_play_level_solved = TRUE;
1974 if (tape.playing && tape.auto_play)
1975 tape.auto_play_level_solved = TRUE;
1978 local_player->LevelSolved = FALSE;
1980 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1984 if (!tape.playing && setup.sound_loops)
1985 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1986 SND_CTRL_PLAY_LOOP);
1988 while (TimeLeft > 0)
1990 if (!tape.playing && !setup.sound_loops)
1991 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1992 if (TimeLeft > 0 && !(TimeLeft % 10))
1993 RaiseScore(level.score[SC_TIME_BONUS]);
1994 if (TimeLeft > 100 && !(TimeLeft % 10))
1998 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
2005 if (!tape.playing && setup.sound_loops)
2006 StopSound(SND_GAME_LEVELTIME_BONUS);
2008 else if (level.time == 0) /* level without time limit */
2010 if (!tape.playing && setup.sound_loops)
2011 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2012 SND_CTRL_PLAY_LOOP);
2014 while (TimePlayed < 999)
2016 if (!tape.playing && !setup.sound_loops)
2017 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2018 if (TimePlayed < 999 && !(TimePlayed % 10))
2019 RaiseScore(level.score[SC_TIME_BONUS]);
2020 if (TimePlayed < 900 && !(TimePlayed % 10))
2024 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
2031 if (!tape.playing && setup.sound_loops)
2032 StopSound(SND_GAME_LEVELTIME_BONUS);
2035 /* close exit door after last player */
2036 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2037 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2039 int element = Feld[ExitX][ExitY];
2041 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2042 EL_SP_EXIT_CLOSING);
2044 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2047 /* Hero disappears */
2048 DrawLevelField(ExitX, ExitY);
2054 CloseDoor(DOOR_CLOSE_1);
2059 SaveTape(tape.level_nr); /* Ask to save tape */
2062 if (level_nr == leveldir_current->handicap_level)
2064 leveldir_current->handicap_level++;
2065 SaveLevelSetup_SeriesInfo();
2068 if (level_editor_test_game)
2069 local_player->score = -1; /* no highscore when playing from editor */
2070 else if (level_nr < leveldir_current->last_level)
2071 raise_level = TRUE; /* advance to next level */
2073 if ((hi_pos = NewHiScore()) >= 0)
2075 game_status = GAME_MODE_SCORES;
2076 DrawHallOfFame(hi_pos);
2085 game_status = GAME_MODE_MAIN;
2102 LoadScore(level_nr);
2104 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2105 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2108 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2110 if (local_player->score > highscore[k].Score)
2112 /* player has made it to the hall of fame */
2114 if (k < MAX_SCORE_ENTRIES - 1)
2116 int m = MAX_SCORE_ENTRIES - 1;
2119 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2120 if (!strcmp(setup.player_name, highscore[l].Name))
2122 if (m == k) /* player's new highscore overwrites his old one */
2126 for (l = m; l > k; l--)
2128 strcpy(highscore[l].Name, highscore[l - 1].Name);
2129 highscore[l].Score = highscore[l - 1].Score;
2136 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2137 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2138 highscore[k].Score = local_player->score;
2144 else if (!strncmp(setup.player_name, highscore[k].Name,
2145 MAX_PLAYER_NAME_LEN))
2146 break; /* player already there with a higher score */
2152 SaveScore(level_nr);
2157 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2159 if (player->GfxAction != action || player->GfxDir != dir)
2162 printf("Player frame reset! (%d => %d, %d => %d)\n",
2163 player->GfxAction, action, player->GfxDir, dir);
2166 player->GfxAction = action;
2167 player->GfxDir = dir;
2169 player->StepFrame = 0;
2173 static void ResetRandomAnimationValue(int x, int y)
2175 GfxRandom[x][y] = INIT_GFX_RANDOM();
2178 static void ResetGfxAnimation(int x, int y)
2181 GfxAction[x][y] = ACTION_DEFAULT;
2182 GfxDir[x][y] = MovDir[x][y];
2185 void InitMovingField(int x, int y, int direction)
2187 int element = Feld[x][y];
2188 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2189 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2193 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2194 ResetGfxAnimation(x, y);
2196 MovDir[newx][newy] = MovDir[x][y] = direction;
2197 GfxDir[x][y] = direction;
2199 if (Feld[newx][newy] == EL_EMPTY)
2200 Feld[newx][newy] = EL_BLOCKED;
2202 if (direction == MV_DOWN && CAN_FALL(element))
2203 GfxAction[x][y] = ACTION_FALLING;
2205 GfxAction[x][y] = ACTION_MOVING;
2207 GfxFrame[newx][newy] = GfxFrame[x][y];
2208 GfxRandom[newx][newy] = GfxRandom[x][y];
2209 GfxAction[newx][newy] = GfxAction[x][y];
2210 GfxDir[newx][newy] = GfxDir[x][y];
2213 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2215 int direction = MovDir[x][y];
2216 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2217 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2223 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2225 int oldx = x, oldy = y;
2226 int direction = MovDir[x][y];
2228 if (direction == MV_LEFT)
2230 else if (direction == MV_RIGHT)
2232 else if (direction == MV_UP)
2234 else if (direction == MV_DOWN)
2237 *comes_from_x = oldx;
2238 *comes_from_y = oldy;
2241 int MovingOrBlocked2Element(int x, int y)
2243 int element = Feld[x][y];
2245 if (element == EL_BLOCKED)
2249 Blocked2Moving(x, y, &oldx, &oldy);
2250 return Feld[oldx][oldy];
2256 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2258 /* like MovingOrBlocked2Element(), but if element is moving
2259 and (x,y) is the field the moving element is just leaving,
2260 return EL_BLOCKED instead of the element value */
2261 int element = Feld[x][y];
2263 if (IS_MOVING(x, y))
2265 if (element == EL_BLOCKED)
2269 Blocked2Moving(x, y, &oldx, &oldy);
2270 return Feld[oldx][oldy];
2279 static void RemoveField(int x, int y)
2281 Feld[x][y] = EL_EMPTY;
2288 ChangeDelay[x][y] = 0;
2289 ChangePage[x][y] = -1;
2290 Pushed[x][y] = FALSE;
2292 GfxElement[x][y] = EL_UNDEFINED;
2293 GfxAction[x][y] = ACTION_DEFAULT;
2294 GfxDir[x][y] = MV_NO_MOVING;
2297 void RemoveMovingField(int x, int y)
2299 int oldx = x, oldy = y, newx = x, newy = y;
2300 int element = Feld[x][y];
2301 int next_element = EL_UNDEFINED;
2303 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2306 if (IS_MOVING(x, y))
2308 Moving2Blocked(x, y, &newx, &newy);
2309 if (Feld[newx][newy] != EL_BLOCKED)
2312 else if (element == EL_BLOCKED)
2314 Blocked2Moving(x, y, &oldx, &oldy);
2315 if (!IS_MOVING(oldx, oldy))
2319 if (element == EL_BLOCKED &&
2320 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2321 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2322 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2323 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2324 next_element = get_next_element(Feld[oldx][oldy]);
2326 RemoveField(oldx, oldy);
2327 RemoveField(newx, newy);
2329 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2331 if (next_element != EL_UNDEFINED)
2332 Feld[oldx][oldy] = next_element;
2334 DrawLevelField(oldx, oldy);
2335 DrawLevelField(newx, newy);
2338 void DrawDynamite(int x, int y)
2340 int sx = SCREENX(x), sy = SCREENY(y);
2341 int graphic = el2img(Feld[x][y]);
2344 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2347 if (IS_WALKABLE_INSIDE(Back[x][y]))
2351 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2352 else if (Store[x][y])
2353 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2355 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2358 if (Back[x][y] || Store[x][y])
2359 DrawGraphicThruMask(sx, sy, graphic, frame);
2361 DrawGraphic(sx, sy, graphic, frame);
2363 if (game.emulation == EMU_SUPAPLEX)
2364 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2365 else if (Store[x][y])
2366 DrawGraphicThruMask(sx, sy, graphic, frame);
2368 DrawGraphic(sx, sy, graphic, frame);
2372 void CheckDynamite(int x, int y)
2374 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2378 if (MovDelay[x][y] != 0)
2381 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2388 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2390 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2391 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2392 StopSound(SND_DYNAMITE_ACTIVE);
2394 StopSound(SND_DYNABOMB_ACTIVE);
2400 void RelocatePlayer(int x, int y, int element_raw)
2402 int element = (element_raw == EL_SP_MURPHY ? EL_PLAYER_1 : element_raw);
2403 struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2404 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2405 boolean no_delay = (tape.index_search);
2406 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2407 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2409 if (player->GameOver) /* do not reanimate dead player */
2413 RemoveField(x, y); /* temporarily remove newly placed player */
2414 DrawLevelField(x, y);
2417 if (player->present)
2419 while (player->MovPos)
2421 ScrollPlayer(player, SCROLL_GO_ON);
2422 ScrollScreen(NULL, SCROLL_GO_ON);
2428 Delay(wait_delay_value);
2431 DrawPlayer(player); /* needed here only to cleanup last field */
2432 DrawLevelField(player->jx, player->jy); /* remove player graphic */
2434 player->is_moving = FALSE;
2437 Feld[x][y] = element;
2438 InitPlayerField(x, y, element, TRUE);
2440 if (player == local_player)
2442 int scroll_xx = -999, scroll_yy = -999;
2444 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2447 int fx = FX, fy = FY;
2449 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2450 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2451 local_player->jx - MIDPOSX);
2453 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2454 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2455 local_player->jy - MIDPOSY);
2457 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2458 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2463 fx += dx * TILEX / 2;
2464 fy += dy * TILEY / 2;
2466 ScrollLevel(dx, dy);
2469 /* scroll in two steps of half tile size to make things smoother */
2470 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2472 Delay(wait_delay_value);
2474 /* scroll second step to align at full tile size */
2476 Delay(wait_delay_value);
2481 void Explode(int ex, int ey, int phase, int mode)
2488 /* !!! eliminate this variable !!! */
2489 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2494 int last_phase = num_phase * delay;
2495 int half_phase = (num_phase / 2) * delay;
2496 int first_phase_after_start = EX_PHASE_START + 1;
2500 if (game.explosions_delayed)
2502 ExplodeField[ex][ey] = mode;
2506 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2508 int center_element = Feld[ex][ey];
2511 /* --- This is only really needed (and now handled) in "Impact()". --- */
2512 /* do not explode moving elements that left the explode field in time */
2513 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2514 center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2518 if (mode == EX_NORMAL || mode == EX_CENTER)
2519 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2521 /* remove things displayed in background while burning dynamite */
2522 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2525 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2527 /* put moving element to center field (and let it explode there) */
2528 center_element = MovingOrBlocked2Element(ex, ey);
2529 RemoveMovingField(ex, ey);
2530 Feld[ex][ey] = center_element;
2534 last_phase = element_info[center_element].explosion_delay;
2537 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2539 int xx = x - ex + 1;
2540 int yy = y - ey + 1;
2543 if (!IN_LEV_FIELD(x, y) ||
2544 ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2545 (x != ex || y != ey)))
2548 element = Feld[x][y];
2550 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2552 element = MovingOrBlocked2Element(x, y);
2554 if (!IS_EXPLOSION_PROOF(element))
2555 RemoveMovingField(x, y);
2561 if (IS_EXPLOSION_PROOF(element))
2564 /* indestructible elements can only explode in center (but not flames) */
2565 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2566 element == EL_FLAMES)
2571 if ((IS_INDESTRUCTIBLE(element) &&
2572 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2573 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2574 element == EL_FLAMES)
2578 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2580 if (IS_ACTIVE_BOMB(element))
2582 /* re-activate things under the bomb like gate or penguin */
2583 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2590 /* save walkable background elements while explosion on same tile */
2592 if (IS_INDESTRUCTIBLE(element))
2593 Back[x][y] = element;
2595 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2596 Back[x][y] = element;
2599 /* ignite explodable elements reached by other explosion */
2600 if (element == EL_EXPLOSION)
2601 element = Store2[x][y];
2604 if (AmoebaNr[x][y] &&
2605 (element == EL_AMOEBA_FULL ||
2606 element == EL_BD_AMOEBA ||
2607 element == EL_AMOEBA_GROWING))
2609 AmoebaCnt[AmoebaNr[x][y]]--;
2610 AmoebaCnt2[AmoebaNr[x][y]]--;
2616 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
2618 switch(StorePlayer[ex][ey])
2621 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
2624 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
2627 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
2631 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
2635 if (game.emulation == EMU_SUPAPLEX)
2636 Store[x][y] = EL_EMPTY;
2638 else if (center_element == EL_MOLE)
2639 Store[x][y] = EL_EMERALD_RED;
2640 else if (center_element == EL_PENGUIN)
2641 Store[x][y] = EL_EMERALD_PURPLE;
2642 else if (center_element == EL_BUG)
2643 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2644 else if (center_element == EL_BD_BUTTERFLY)
2645 Store[x][y] = EL_BD_DIAMOND;
2646 else if (center_element == EL_SP_ELECTRON)
2647 Store[x][y] = EL_SP_INFOTRON;
2648 else if (center_element == EL_AMOEBA_TO_DIAMOND)
2649 Store[x][y] = level.amoeba_content;
2650 else if (center_element == EL_YAMYAM)
2651 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2652 else if (IS_CUSTOM_ELEMENT(center_element) &&
2653 element_info[center_element].content[xx][yy] != EL_EMPTY)
2654 Store[x][y] = element_info[center_element].content[xx][yy];
2655 else if (element == EL_WALL_EMERALD)
2656 Store[x][y] = EL_EMERALD;
2657 else if (element == EL_WALL_DIAMOND)
2658 Store[x][y] = EL_DIAMOND;
2659 else if (element == EL_WALL_BD_DIAMOND)
2660 Store[x][y] = EL_BD_DIAMOND;
2661 else if (element == EL_WALL_EMERALD_YELLOW)
2662 Store[x][y] = EL_EMERALD_YELLOW;
2663 else if (element == EL_WALL_EMERALD_RED)
2664 Store[x][y] = EL_EMERALD_RED;
2665 else if (element == EL_WALL_EMERALD_PURPLE)
2666 Store[x][y] = EL_EMERALD_PURPLE;
2667 else if (element == EL_WALL_PEARL)
2668 Store[x][y] = EL_PEARL;
2669 else if (element == EL_WALL_CRYSTAL)
2670 Store[x][y] = EL_CRYSTAL;
2671 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2672 Store[x][y] = element_info[element].content[1][1];
2674 Store[x][y] = EL_EMPTY;
2676 if (x != ex || y != ey ||
2677 center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2678 Store2[x][y] = element;
2681 if (AmoebaNr[x][y] &&
2682 (element == EL_AMOEBA_FULL ||
2683 element == EL_BD_AMOEBA ||
2684 element == EL_AMOEBA_GROWING))
2686 AmoebaCnt[AmoebaNr[x][y]]--;
2687 AmoebaCnt2[AmoebaNr[x][y]]--;
2693 MovDir[x][y] = MovPos[x][y] = 0;
2694 GfxDir[x][y] = MovDir[x][y];
2699 Feld[x][y] = EL_EXPLOSION;
2701 GfxElement[x][y] = center_element;
2703 GfxElement[x][y] = EL_UNDEFINED;
2706 ExplodePhase[x][y] = 1;
2708 ExplodeDelay[x][y] = last_phase;
2713 if (center_element == EL_YAMYAM)
2714 game.yamyam_content_nr =
2715 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2727 last_phase = ExplodeDelay[x][y];
2730 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2734 /* activate this even in non-DEBUG version until cause for crash in
2735 getGraphicAnimationFrame() (see below) is found and eliminated */
2739 if (GfxElement[x][y] == EL_UNDEFINED)
2742 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2743 printf("Explode(): This should never happen!\n");
2746 GfxElement[x][y] = EL_EMPTY;
2752 border_element = Store2[x][y];
2753 if (IS_PLAYER(x, y))
2754 border_element = StorePlayer[x][y];
2756 if (phase == element_info[border_element].ignition_delay ||
2757 phase == last_phase)
2759 boolean border_explosion = FALSE;
2762 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
2764 if (IS_PLAYER(x, y))
2767 KillHeroUnlessExplosionProtected(x, y);
2768 border_explosion = TRUE;
2771 if (phase == last_phase)
2772 printf("::: IS_PLAYER\n");
2775 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
2777 Feld[x][y] = Store2[x][y];
2780 border_explosion = TRUE;
2783 if (phase == last_phase)
2784 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
2787 else if (border_element == EL_AMOEBA_TO_DIAMOND)
2789 AmoebeUmwandeln(x, y);
2791 border_explosion = TRUE;
2794 if (phase == last_phase)
2795 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
2796 element_info[border_element].explosion_delay,
2797 element_info[border_element].ignition_delay,
2803 /* if an element just explodes due to another explosion (chain-reaction),
2804 do not immediately end the new explosion when it was the last frame of
2805 the explosion (as it would be done in the following "if"-statement!) */
2806 if (border_explosion && phase == last_phase)
2813 if (phase == first_phase_after_start)
2815 int element = Store2[x][y];
2817 if (element == EL_BLACK_ORB)
2819 Feld[x][y] = Store2[x][y];
2824 else if (phase == half_phase)
2826 int element = Store2[x][y];
2828 if (IS_PLAYER(x, y))
2829 KillHeroUnlessExplosionProtected(x, y);
2830 else if (CAN_EXPLODE_BY_EXPLOSION(element))
2832 Feld[x][y] = Store2[x][y];
2836 else if (element == EL_AMOEBA_TO_DIAMOND)
2837 AmoebeUmwandeln(x, y);
2841 if (phase == last_phase)
2845 element = Feld[x][y] = Store[x][y];
2846 Store[x][y] = Store2[x][y] = 0;
2847 GfxElement[x][y] = EL_UNDEFINED;
2849 /* player can escape from explosions and might therefore be still alive */
2850 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
2851 element <= EL_PLAYER_IS_EXPLODING_4)
2852 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
2854 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
2855 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
2856 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
2859 /* restore probably existing indestructible background element */
2860 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2861 element = Feld[x][y] = Back[x][y];
2864 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2865 GfxDir[x][y] = MV_NO_MOVING;
2866 ChangeDelay[x][y] = 0;
2867 ChangePage[x][y] = -1;
2869 InitField(x, y, FALSE);
2871 /* !!! not needed !!! */
2872 if (CAN_MOVE(element))
2875 DrawLevelField(x, y);
2877 TestIfElementTouchesCustomElement(x, y);
2879 if (GFX_CRUMBLED(element))
2880 DrawLevelFieldCrumbledSandNeighbours(x, y);
2882 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
2883 StorePlayer[x][y] = 0;
2885 if (ELEM_IS_PLAYER(element))
2886 RelocatePlayer(x, y, element);
2889 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2891 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2895 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2897 int stored = Store[x][y];
2898 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2899 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2902 int frame = getGraphicAnimationFrame(graphic, phase - delay);
2905 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2906 element_info[GfxElement[x][y]].token_name,
2911 DrawLevelFieldCrumbledSand(x, y);
2913 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2915 DrawLevelElement(x, y, Back[x][y]);
2916 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2918 else if (IS_WALKABLE_UNDER(Back[x][y]))
2920 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2921 DrawLevelElementThruMask(x, y, Back[x][y]);
2923 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2924 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2928 void DynaExplode(int ex, int ey)
2931 int dynabomb_element = Feld[ex][ey];
2932 int dynabomb_size = 1;
2933 boolean dynabomb_xl = FALSE;
2934 struct PlayerInfo *player;
2935 static int xy[4][2] =
2943 if (IS_ACTIVE_BOMB(dynabomb_element))
2945 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
2946 dynabomb_size = player->dynabomb_size;
2947 dynabomb_xl = player->dynabomb_xl;
2948 player->dynabombs_left++;
2951 Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2953 for (i = 0; i < 4; i++)
2955 for (j = 1; j <= dynabomb_size; j++)
2957 int x = ex + j * xy[i % 4][0];
2958 int y = ey + j * xy[i % 4][1];
2961 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2964 element = Feld[x][y];
2966 /* do not restart explosions of fields with active bombs */
2967 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2970 Explode(x, y, EX_PHASE_START, EX_BORDER);
2972 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2973 if (element != EL_EMPTY &&
2974 element != EL_SAND &&
2975 element != EL_EXPLOSION &&
2982 void Bang(int x, int y)
2985 int element = MovingOrBlocked2Element(x, y);
2987 int element = Feld[x][y];
2991 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
2993 if (IS_PLAYER(x, y))
2996 struct PlayerInfo *player = PLAYERINFO(x, y);
2998 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2999 player->element_nr);
3004 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3006 if (game.emulation == EMU_SUPAPLEX)
3007 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3009 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3014 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3022 case EL_BD_BUTTERFLY:
3025 case EL_DARK_YAMYAM:
3029 RaiseScoreElement(element);
3030 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3032 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3033 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3034 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3035 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3036 case EL_DYNABOMB_INCREASE_NUMBER:
3037 case EL_DYNABOMB_INCREASE_SIZE:
3038 case EL_DYNABOMB_INCREASE_POWER:
3043 case EL_LAMP_ACTIVE:
3044 if (IS_PLAYER(x, y))
3045 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3047 Explode(x, y, EX_PHASE_START, EX_CENTER);
3050 if (CAN_EXPLODE_DYNA(element))
3052 else if (CAN_EXPLODE_1X1(element))
3053 Explode(x, y, EX_PHASE_START, EX_CENTER);
3055 Explode(x, y, EX_PHASE_START, EX_NORMAL);
3059 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3062 void SplashAcid(int x, int y)
3064 int element = Feld[x][y];
3066 if (element != EL_ACID_SPLASH_LEFT &&
3067 element != EL_ACID_SPLASH_RIGHT)
3069 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3071 if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
3072 (!IN_LEV_FIELD(x-1, y-1) ||
3073 !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
3074 Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
3076 if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
3077 (!IN_LEV_FIELD(x+1, y-1) ||
3078 !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
3079 Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
3083 static void InitBeltMovement()
3085 static int belt_base_element[4] =
3087 EL_CONVEYOR_BELT_1_LEFT,
3088 EL_CONVEYOR_BELT_2_LEFT,
3089 EL_CONVEYOR_BELT_3_LEFT,
3090 EL_CONVEYOR_BELT_4_LEFT
3092 static int belt_base_active_element[4] =
3094 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3095 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3096 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3097 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3102 /* set frame order for belt animation graphic according to belt direction */
3103 for (i = 0; i < 4; i++)
3107 for (j = 0; j < 3; j++)
3109 int element = belt_base_active_element[belt_nr] + j;
3110 int graphic = el2img(element);
3112 if (game.belt_dir[i] == MV_LEFT)
3113 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3115 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3119 for (y = 0; y < lev_fieldy; y++)
3121 for (x = 0; x < lev_fieldx; x++)
3123 int element = Feld[x][y];
3125 for (i = 0; i < 4; i++)
3127 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3129 int e_belt_nr = getBeltNrFromBeltElement(element);
3132 if (e_belt_nr == belt_nr)
3134 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3136 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3144 static void ToggleBeltSwitch(int x, int y)
3146 static int belt_base_element[4] =
3148 EL_CONVEYOR_BELT_1_LEFT,
3149 EL_CONVEYOR_BELT_2_LEFT,
3150 EL_CONVEYOR_BELT_3_LEFT,
3151 EL_CONVEYOR_BELT_4_LEFT
3153 static int belt_base_active_element[4] =
3155 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3156 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3157 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3158 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3160 static int belt_base_switch_element[4] =
3162 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3163 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3164 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3165 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3167 static int belt_move_dir[4] =
3175 int element = Feld[x][y];
3176 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3177 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3178 int belt_dir = belt_move_dir[belt_dir_nr];
3181 if (!IS_BELT_SWITCH(element))
3184 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3185 game.belt_dir[belt_nr] = belt_dir;
3187 if (belt_dir_nr == 3)
3190 /* set frame order for belt animation graphic according to belt direction */
3191 for (i = 0; i < 3; i++)
3193 int element = belt_base_active_element[belt_nr] + i;
3194 int graphic = el2img(element);
3196 if (belt_dir == MV_LEFT)
3197 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3199 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3202 for (yy = 0; yy < lev_fieldy; yy++)
3204 for (xx = 0; xx < lev_fieldx; xx++)
3206 int element = Feld[xx][yy];
3208 if (IS_BELT_SWITCH(element))
3210 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3212 if (e_belt_nr == belt_nr)
3214 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3215 DrawLevelField(xx, yy);
3218 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3220 int e_belt_nr = getBeltNrFromBeltElement(element);
3222 if (e_belt_nr == belt_nr)
3224 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3226 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3227 DrawLevelField(xx, yy);
3230 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3232 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3234 if (e_belt_nr == belt_nr)
3236 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3238 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3239 DrawLevelField(xx, yy);
3246 static void ToggleSwitchgateSwitch(int x, int y)
3250 game.switchgate_pos = !game.switchgate_pos;
3252 for (yy = 0; yy < lev_fieldy; yy++)
3254 for (xx = 0; xx < lev_fieldx; xx++)
3256 int element = Feld[xx][yy];
3258 if (element == EL_SWITCHGATE_SWITCH_UP ||
3259 element == EL_SWITCHGATE_SWITCH_DOWN)
3261 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3262 DrawLevelField(xx, yy);
3264 else if (element == EL_SWITCHGATE_OPEN ||
3265 element == EL_SWITCHGATE_OPENING)
3267 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3269 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3271 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3274 else if (element == EL_SWITCHGATE_CLOSED ||
3275 element == EL_SWITCHGATE_CLOSING)
3277 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3279 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3281 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3288 static int getInvisibleActiveFromInvisibleElement(int element)
3290 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3291 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3292 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3296 static int getInvisibleFromInvisibleActiveElement(int element)
3298 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3299 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3300 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3304 static void RedrawAllLightSwitchesAndInvisibleElements()
3308 for (y = 0; y < lev_fieldy; y++)
3310 for (x = 0; x < lev_fieldx; x++)
3312 int element = Feld[x][y];
3314 if (element == EL_LIGHT_SWITCH &&
3315 game.light_time_left > 0)
3317 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3318 DrawLevelField(x, y);
3320 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3321 game.light_time_left == 0)
3323 Feld[x][y] = EL_LIGHT_SWITCH;
3324 DrawLevelField(x, y);
3326 else if (element == EL_INVISIBLE_STEELWALL ||
3327 element == EL_INVISIBLE_WALL ||
3328 element == EL_INVISIBLE_SAND)
3330 if (game.light_time_left > 0)
3331 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3333 DrawLevelField(x, y);
3335 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3336 element == EL_INVISIBLE_WALL_ACTIVE ||
3337 element == EL_INVISIBLE_SAND_ACTIVE)
3339 if (game.light_time_left == 0)
3340 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3342 DrawLevelField(x, y);
3348 static void ToggleLightSwitch(int x, int y)
3350 int element = Feld[x][y];
3352 game.light_time_left =
3353 (element == EL_LIGHT_SWITCH ?
3354 level.time_light * FRAMES_PER_SECOND : 0);
3356 RedrawAllLightSwitchesAndInvisibleElements();
3359 static void ActivateTimegateSwitch(int x, int y)
3363 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3365 for (yy = 0; yy < lev_fieldy; yy++)
3367 for (xx = 0; xx < lev_fieldx; xx++)
3369 int element = Feld[xx][yy];
3371 if (element == EL_TIMEGATE_CLOSED ||
3372 element == EL_TIMEGATE_CLOSING)
3374 Feld[xx][yy] = EL_TIMEGATE_OPENING;
3375 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3379 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3381 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3382 DrawLevelField(xx, yy);
3389 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3392 inline static int getElementMoveStepsize(int x, int y)
3394 int element = Feld[x][y];
3395 int direction = MovDir[x][y];
3396 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3397 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3398 int horiz_move = (dx != 0);
3399 int sign = (horiz_move ? dx : dy);
3400 int step = sign * element_info[element].move_stepsize;
3402 /* special values for move stepsize for spring and things on conveyor belt */
3406 if (element == EL_SPRING)
3407 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3408 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3409 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3410 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3412 if (CAN_FALL(element) &&
3413 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3414 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3415 else if (element == EL_SPRING)
3416 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3423 void Impact(int x, int y)
3425 boolean lastline = (y == lev_fieldy-1);
3426 boolean object_hit = FALSE;
3427 boolean impact = (lastline || object_hit);
3428 int element = Feld[x][y];
3429 int smashed = EL_UNDEFINED;
3431 if (!lastline) /* check if element below was hit */
3433 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3436 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3437 MovDir[x][y + 1] != MV_DOWN ||
3438 MovPos[x][y + 1] <= TILEY / 2));
3441 object_hit = !IS_FREE(x, y + 1);
3444 /* do not smash moving elements that left the smashed field in time */
3445 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3446 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3450 smashed = MovingOrBlocked2Element(x, y + 1);
3452 impact = (lastline || object_hit);
3455 if (!lastline && smashed == EL_ACID) /* element falls into acid */
3461 /* only reset graphic animation if graphic really changes after impact */
3463 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3465 ResetGfxAnimation(x, y);
3466 DrawLevelField(x, y);
3469 if (impact && CAN_EXPLODE_IMPACT(element))
3474 else if (impact && element == EL_PEARL)
3476 Feld[x][y] = EL_PEARL_BREAKING;
3477 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3480 else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3482 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3487 if (impact && element == EL_AMOEBA_DROP)
3489 if (object_hit && IS_PLAYER(x, y + 1))
3490 KillHeroUnlessEnemyProtected(x, y + 1);
3491 else if (object_hit && smashed == EL_PENGUIN)
3495 Feld[x][y] = EL_AMOEBA_GROWING;
3496 Store[x][y] = EL_AMOEBA_WET;
3498 ResetRandomAnimationValue(x, y);
3503 if (object_hit) /* check which object was hit */
3505 if (CAN_PASS_MAGIC_WALL(element) &&
3506 (smashed == EL_MAGIC_WALL ||
3507 smashed == EL_BD_MAGIC_WALL))
3510 int activated_magic_wall =
3511 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3512 EL_BD_MAGIC_WALL_ACTIVE);
3514 /* activate magic wall / mill */
3515 for (yy = 0; yy < lev_fieldy; yy++)
3516 for (xx = 0; xx < lev_fieldx; xx++)
3517 if (Feld[xx][yy] == smashed)
3518 Feld[xx][yy] = activated_magic_wall;
3520 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3521 game.magic_wall_active = TRUE;
3523 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3524 SND_MAGIC_WALL_ACTIVATING :
3525 SND_BD_MAGIC_WALL_ACTIVATING));
3528 if (IS_PLAYER(x, y + 1))
3530 if (CAN_SMASH_PLAYER(element))
3532 KillHeroUnlessEnemyProtected(x, y + 1);
3536 else if (smashed == EL_PENGUIN)
3538 if (CAN_SMASH_PLAYER(element))
3544 else if (element == EL_BD_DIAMOND)
3546 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3552 else if (((element == EL_SP_INFOTRON ||
3553 element == EL_SP_ZONK) &&
3554 (smashed == EL_SP_SNIKSNAK ||
3555 smashed == EL_SP_ELECTRON ||
3556 smashed == EL_SP_DISK_ORANGE)) ||
3557 (element == EL_SP_INFOTRON &&
3558 smashed == EL_SP_DISK_YELLOW))
3564 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3570 else if (CAN_SMASH_EVERYTHING(element))
3572 if (IS_CLASSIC_ENEMY(smashed) ||
3573 CAN_EXPLODE_SMASHED(smashed))
3578 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3580 if (smashed == EL_LAMP ||
3581 smashed == EL_LAMP_ACTIVE)
3586 else if (smashed == EL_NUT)
3588 Feld[x][y + 1] = EL_NUT_BREAKING;
3589 PlayLevelSound(x, y, SND_NUT_BREAKING);
3590 RaiseScoreElement(EL_NUT);
3593 else if (smashed == EL_PEARL)
3595 Feld[x][y + 1] = EL_PEARL_BREAKING;
3596 PlayLevelSound(x, y, SND_PEARL_BREAKING);
3599 else if (smashed == EL_DIAMOND)
3601 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3602 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3605 else if (IS_BELT_SWITCH(smashed))
3607 ToggleBeltSwitch(x, y + 1);
3609 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3610 smashed == EL_SWITCHGATE_SWITCH_DOWN)
3612 ToggleSwitchgateSwitch(x, y + 1);
3614 else if (smashed == EL_LIGHT_SWITCH ||
3615 smashed == EL_LIGHT_SWITCH_ACTIVE)
3617 ToggleLightSwitch(x, y + 1);
3621 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3623 CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3624 CE_OTHER_IS_SWITCHING);
3625 CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3631 CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3636 /* play sound of magic wall / mill */
3638 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3639 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3641 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3642 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3643 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3644 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3649 /* play sound of object that hits the ground */
3650 if (lastline || object_hit)
3651 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3654 inline static void TurnRoundExt(int x, int y)
3666 { 0, 0 }, { 0, 0 }, { 0, 0 },
3671 int left, right, back;
3675 { MV_DOWN, MV_UP, MV_RIGHT },
3676 { MV_UP, MV_DOWN, MV_LEFT },
3678 { MV_LEFT, MV_RIGHT, MV_DOWN },
3682 { MV_RIGHT, MV_LEFT, MV_UP }
3685 int element = Feld[x][y];
3686 int move_pattern = element_info[element].move_pattern;
3688 int old_move_dir = MovDir[x][y];
3689 int left_dir = turn[old_move_dir].left;
3690 int right_dir = turn[old_move_dir].right;
3691 int back_dir = turn[old_move_dir].back;
3693 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
3694 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
3695 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
3696 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
3698 int left_x = x + left_dx, left_y = y + left_dy;
3699 int right_x = x + right_dx, right_y = y + right_dy;
3700 int move_x = x + move_dx, move_y = y + move_dy;
3704 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3706 TestIfBadThingTouchesOtherBadThing(x, y);
3708 if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3709 MovDir[x][y] = right_dir;
3710 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3711 MovDir[x][y] = left_dir;
3713 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3715 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
3718 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3719 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3721 TestIfBadThingTouchesOtherBadThing(x, y);
3723 if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3724 MovDir[x][y] = left_dir;
3725 else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3726 MovDir[x][y] = right_dir;
3728 if ((element == EL_SPACESHIP ||
3729 element == EL_SP_SNIKSNAK ||
3730 element == EL_SP_ELECTRON)
3731 && MovDir[x][y] != old_move_dir)
3733 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
3736 else if (element == EL_YAMYAM)
3738 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3739 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3741 if (can_turn_left && can_turn_right)
3742 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3743 else if (can_turn_left)
3744 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3745 else if (can_turn_right)
3746 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3748 MovDir[x][y] = back_dir;
3750 MovDelay[x][y] = 16 + 16 * RND(3);
3752 else if (element == EL_DARK_YAMYAM)
3754 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3755 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3757 if (can_turn_left && can_turn_right)
3758 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3759 else if (can_turn_left)
3760 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3761 else if (can_turn_right)
3762 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3764 MovDir[x][y] = back_dir;
3766 MovDelay[x][y] = 16 + 16 * RND(3);
3768 else if (element == EL_PACMAN)
3770 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3771 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3773 if (can_turn_left && can_turn_right)
3774 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3775 else if (can_turn_left)
3776 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3777 else if (can_turn_right)
3778 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3780 MovDir[x][y] = back_dir;
3782 MovDelay[x][y] = 6 + RND(40);
3784 else if (element == EL_PIG)
3786 boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y);
3787 boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3788 boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y);
3789 boolean should_turn_left, should_turn_right, should_move_on;
3791 int rnd = RND(rnd_value);
3793 should_turn_left = (can_turn_left &&
3795 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3796 y + back_dy + left_dy)));
3797 should_turn_right = (can_turn_right &&
3799 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3800 y + back_dy + right_dy)));
3801 should_move_on = (can_move_on &&
3804 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3805 y + move_dy + left_dy) ||
3806 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3807 y + move_dy + right_dy)));
3809 if (should_turn_left || should_turn_right || should_move_on)
3811 if (should_turn_left && should_turn_right && should_move_on)
3812 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
3813 rnd < 2 * rnd_value / 3 ? right_dir :
3815 else if (should_turn_left && should_turn_right)
3816 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3817 else if (should_turn_left && should_move_on)
3818 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3819 else if (should_turn_right && should_move_on)
3820 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3821 else if (should_turn_left)
3822 MovDir[x][y] = left_dir;
3823 else if (should_turn_right)
3824 MovDir[x][y] = right_dir;
3825 else if (should_move_on)
3826 MovDir[x][y] = old_move_dir;
3828 else if (can_move_on && rnd > rnd_value / 8)
3829 MovDir[x][y] = old_move_dir;
3830 else if (can_turn_left && can_turn_right)
3831 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3832 else if (can_turn_left && rnd > rnd_value / 8)
3833 MovDir[x][y] = left_dir;
3834 else if (can_turn_right && rnd > rnd_value/8)
3835 MovDir[x][y] = right_dir;
3837 MovDir[x][y] = back_dir;
3839 xx = x + move_xy[MovDir[x][y]].x;
3840 yy = y + move_xy[MovDir[x][y]].y;
3842 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3843 MovDir[x][y] = old_move_dir;
3847 else if (element == EL_DRAGON)
3849 boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3850 boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3851 boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3853 int rnd = RND(rnd_value);
3856 if (FrameCounter < 1 && x == 0 && y == 29)
3857 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3860 if (can_move_on && rnd > rnd_value / 8)
3861 MovDir[x][y] = old_move_dir;
3862 else if (can_turn_left && can_turn_right)
3863 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3864 else if (can_turn_left && rnd > rnd_value / 8)
3865 MovDir[x][y] = left_dir;
3866 else if (can_turn_right && rnd > rnd_value / 8)
3867 MovDir[x][y] = right_dir;
3869 MovDir[x][y] = back_dir;
3871 xx = x + move_xy[MovDir[x][y]].x;
3872 yy = y + move_xy[MovDir[x][y]].y;
3875 if (FrameCounter < 1 && x == 0 && y == 29)
3876 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3877 xx, yy, Feld[xx][yy],
3882 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3883 MovDir[x][y] = old_move_dir;
3885 if (!IS_FREE(xx, yy))
3886 MovDir[x][y] = old_move_dir;
3890 if (FrameCounter < 1 && x == 0 && y == 29)
3891 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3896 else if (element == EL_MOLE)
3898 boolean can_move_on =
3899 (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3900 IS_AMOEBOID(Feld[move_x][move_y]) ||
3901 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3904 boolean can_turn_left =
3905 (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3906 IS_AMOEBOID(Feld[left_x][left_y])));
3908 boolean can_turn_right =
3909 (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3910 IS_AMOEBOID(Feld[right_x][right_y])));
3912 if (can_turn_left && can_turn_right)
3913 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3914 else if (can_turn_left)
3915 MovDir[x][y] = left_dir;
3917 MovDir[x][y] = right_dir;
3920 if (MovDir[x][y] != old_move_dir)
3923 else if (element == EL_BALLOON)
3925 MovDir[x][y] = game.balloon_dir;
3928 else if (element == EL_SPRING)
3930 if (MovDir[x][y] & MV_HORIZONTAL &&
3931 (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3932 IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3933 MovDir[x][y] = MV_NO_MOVING;
3937 else if (element == EL_ROBOT ||
3938 element == EL_SATELLITE ||
3939 element == EL_PENGUIN)
3941 int attr_x = -1, attr_y = -1;
3952 for (i = 0; i < MAX_PLAYERS; i++)
3954 struct PlayerInfo *player = &stored_player[i];
3955 int jx = player->jx, jy = player->jy;
3957 if (!player->active)
3961 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3969 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3975 if (element == EL_PENGUIN)
3978 static int xy[4][2] =
3986 for (i = 0; i < 4; i++)
3988 int ex = x + xy[i % 4][0];
3989 int ey = y + xy[i % 4][1];
3991 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4000 MovDir[x][y] = MV_NO_MOVING;
4002 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4003 else if (attr_x > x)
4004 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4006 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4007 else if (attr_y > y)
4008 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4010 if (element == EL_ROBOT)
4014 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4015 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4016 Moving2Blocked(x, y, &newx, &newy);
4018 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4019 MovDelay[x][y] = 8 + 8 * !RND(3);
4021 MovDelay[x][y] = 16;
4023 else if (element == EL_PENGUIN)
4029 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4031 boolean first_horiz = RND(2);
4032 int new_move_dir = MovDir[x][y];
4035 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4036 Moving2Blocked(x, y, &newx, &newy);
4038 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4042 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4043 Moving2Blocked(x, y, &newx, &newy);
4045 if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4048 MovDir[x][y] = old_move_dir;
4052 else /* (element == EL_SATELLITE) */
4058 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4060 boolean first_horiz = RND(2);
4061 int new_move_dir = MovDir[x][y];
4064 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4065 Moving2Blocked(x, y, &newx, &newy);
4067 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4071 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4072 Moving2Blocked(x, y, &newx, &newy);
4074 if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4077 MovDir[x][y] = old_move_dir;
4082 else if (move_pattern == MV_TURNING_LEFT ||
4083 move_pattern == MV_TURNING_RIGHT ||
4084 move_pattern == MV_TURNING_LEFT_RIGHT ||
4085 move_pattern == MV_TURNING_RIGHT_LEFT ||
4086 move_pattern == MV_TURNING_RANDOM ||
4087 move_pattern == MV_ALL_DIRECTIONS)
4089 boolean can_turn_left =
4090 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4091 boolean can_turn_right =
4092 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4094 if (move_pattern == MV_TURNING_LEFT)
4095 MovDir[x][y] = left_dir;
4096 else if (move_pattern == MV_TURNING_RIGHT)
4097 MovDir[x][y] = right_dir;
4098 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4099 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4100 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4101 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4102 else if (move_pattern == MV_TURNING_RANDOM)
4103 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4104 can_turn_right && !can_turn_left ? right_dir :
4105 RND(2) ? left_dir : right_dir);
4106 else if (can_turn_left && can_turn_right)
4107 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4108 else if (can_turn_left)
4109 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4110 else if (can_turn_right)
4111 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4113 MovDir[x][y] = back_dir;
4115 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4117 else if (move_pattern == MV_HORIZONTAL ||
4118 move_pattern == MV_VERTICAL)
4120 if (move_pattern & old_move_dir)
4121 MovDir[x][y] = back_dir;
4122 else if (move_pattern == MV_HORIZONTAL)
4123 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4124 else if (move_pattern == MV_VERTICAL)
4125 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4127 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4129 else if (move_pattern & MV_ANY_DIRECTION)
4131 MovDir[x][y] = move_pattern;
4132 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4134 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4136 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4137 MovDir[x][y] = left_dir;
4138 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4139 MovDir[x][y] = right_dir;
4141 if (MovDir[x][y] != old_move_dir)
4142 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4144 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4146 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4147 MovDir[x][y] = right_dir;
4148 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4149 MovDir[x][y] = left_dir;
4151 if (MovDir[x][y] != old_move_dir)
4152 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4154 else if (move_pattern == MV_TOWARDS_PLAYER ||
4155 move_pattern == MV_AWAY_FROM_PLAYER)
4157 int attr_x = -1, attr_y = -1;
4159 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4170 for (i = 0; i < MAX_PLAYERS; i++)
4172 struct PlayerInfo *player = &stored_player[i];
4173 int jx = player->jx, jy = player->jy;
4175 if (!player->active)
4179 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4187 MovDir[x][y] = MV_NO_MOVING;
4189 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4190 else if (attr_x > x)
4191 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4193 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4194 else if (attr_y > y)
4195 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4197 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4199 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4201 boolean first_horiz = RND(2);
4202 int new_move_dir = MovDir[x][y];
4205 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4206 Moving2Blocked(x, y, &newx, &newy);
4208 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4212 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4213 Moving2Blocked(x, y, &newx, &newy);
4215 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4218 MovDir[x][y] = old_move_dir;
4221 else if (move_pattern == MV_WHEN_PUSHED ||
4222 move_pattern == MV_WHEN_DROPPED)
4224 if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
4225 MovDir[x][y] = MV_NO_MOVING;
4229 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4231 static int test_xy[7][2] =
4241 static int test_dir[7] =
4251 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4252 int move_preference = -1000000; /* start with very low preference */
4253 int new_move_dir = MV_NO_MOVING;
4254 int start_test = RND(4);
4257 for (i = 0; i < 4; i++)
4259 int move_dir = test_dir[start_test + i];
4260 int move_dir_preference;
4262 xx = x + test_xy[start_test + i][0];
4263 yy = y + test_xy[start_test + i][1];
4265 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4266 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4268 new_move_dir = move_dir;
4273 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4276 move_dir_preference = -1 * RunnerVisit[xx][yy];
4277 if (hunter_mode && PlayerVisit[xx][yy] > 0)
4278 move_dir_preference = PlayerVisit[xx][yy];
4280 if (move_dir_preference > move_preference)
4282 /* prefer field that has not been visited for the longest time */
4283 move_preference = move_dir_preference;
4284 new_move_dir = move_dir;
4286 else if (move_dir_preference == move_preference &&
4287 move_dir == old_move_dir)
4289 /* prefer last direction when all directions are preferred equally */
4290 move_preference = move_dir_preference;
4291 new_move_dir = move_dir;
4295 MovDir[x][y] = new_move_dir;
4296 if (old_move_dir != new_move_dir)
4301 static void TurnRound(int x, int y)
4303 int direction = MovDir[x][y];
4306 GfxDir[x][y] = MovDir[x][y];
4312 GfxDir[x][y] = MovDir[x][y];
4315 if (direction != MovDir[x][y])
4320 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4323 GfxAction[x][y] = ACTION_WAITING;
4327 static boolean JustBeingPushed(int x, int y)
4331 for (i = 0; i < MAX_PLAYERS; i++)
4333 struct PlayerInfo *player = &stored_player[i];
4335 if (player->active && player->is_pushing && player->MovPos)
4337 int next_jx = player->jx + (player->jx - player->last_jx);
4338 int next_jy = player->jy + (player->jy - player->last_jy);
4340 if (x == next_jx && y == next_jy)
4348 void StartMoving(int x, int y)
4350 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4351 boolean started_moving = FALSE; /* some elements can fall _and_ move */
4352 int element = Feld[x][y];
4358 if (MovDelay[x][y] == 0)
4359 GfxAction[x][y] = ACTION_DEFAULT;
4361 /* !!! this should be handled more generic (not only for mole) !!! */
4362 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4363 GfxAction[x][y] = ACTION_DEFAULT;
4366 if (CAN_FALL(element) && y < lev_fieldy - 1)
4368 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4369 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4370 if (JustBeingPushed(x, y))
4373 if (element == EL_QUICKSAND_FULL)
4375 if (IS_FREE(x, y + 1))
4377 InitMovingField(x, y, MV_DOWN);
4378 started_moving = TRUE;
4380 Feld[x][y] = EL_QUICKSAND_EMPTYING;
4381 Store[x][y] = EL_ROCK;
4383 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4385 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4388 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4390 if (!MovDelay[x][y])
4391 MovDelay[x][y] = TILEY + 1;
4400 Feld[x][y] = EL_QUICKSAND_EMPTY;
4401 Feld[x][y + 1] = EL_QUICKSAND_FULL;
4402 Store[x][y + 1] = Store[x][y];
4405 PlayLevelSoundAction(x, y, ACTION_FILLING);
4407 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4411 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4412 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4414 InitMovingField(x, y, MV_DOWN);
4415 started_moving = TRUE;
4417 Feld[x][y] = EL_QUICKSAND_FILLING;
4418 Store[x][y] = element;
4420 PlayLevelSoundAction(x, y, ACTION_FILLING);
4422 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4425 else if (element == EL_MAGIC_WALL_FULL)
4427 if (IS_FREE(x, y + 1))
4429 InitMovingField(x, y, MV_DOWN);
4430 started_moving = TRUE;
4432 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4433 Store[x][y] = EL_CHANGED(Store[x][y]);
4435 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4437 if (!MovDelay[x][y])
4438 MovDelay[x][y] = TILEY/4 + 1;
4447 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4448 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4449 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4453 else if (element == EL_BD_MAGIC_WALL_FULL)
4455 if (IS_FREE(x, y + 1))
4457 InitMovingField(x, y, MV_DOWN);
4458 started_moving = TRUE;
4460 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4461 Store[x][y] = EL_CHANGED2(Store[x][y]);
4463 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4465 if (!MovDelay[x][y])
4466 MovDelay[x][y] = TILEY/4 + 1;
4475 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4476 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4477 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4481 else if (CAN_PASS_MAGIC_WALL(element) &&
4482 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4483 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4485 InitMovingField(x, y, MV_DOWN);
4486 started_moving = TRUE;
4489 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4490 EL_BD_MAGIC_WALL_FILLING);
4491 Store[x][y] = element;
4494 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4496 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4501 InitMovingField(x, y, MV_DOWN);
4502 started_moving = TRUE;
4504 Store[x][y] = EL_ACID;
4506 /* !!! TEST !!! better use "_FALLING" etc. !!! */
4507 GfxAction[x][y + 1] = ACTION_ACTIVE;
4511 else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4512 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4513 (Feld[x][y + 1] == EL_BLOCKED)) ||
4514 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4515 CAN_SMASH(element) && WasJustFalling[x][y] &&
4516 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4520 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4521 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4522 WasJustMoving[x][y] && !Pushed[x][y + 1])
4524 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4525 WasJustMoving[x][y])
4530 /* this is needed for a special case not covered by calling "Impact()"
4531 from "ContinueMoving()": if an element moves to a tile directly below
4532 another element which was just falling on that tile (which was empty
4533 in the previous frame), the falling element above would just stop
4534 instead of smashing the element below (in previous version, the above
4535 element was just checked for "moving" instead of "falling", resulting
4536 in incorrect smashes caused by horizontal movement of the above
4537 element; also, the case of the player being the element to smash was
4538 simply not covered here... :-/ ) */
4541 WasJustMoving[x][y] = 0;
4542 WasJustFalling[x][y] = 0;
4547 else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4549 if (MovDir[x][y] == MV_NO_MOVING)
4551 InitMovingField(x, y, MV_DOWN);
4552 started_moving = TRUE;
4555 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4557 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4558 MovDir[x][y] = MV_DOWN;
4560 InitMovingField(x, y, MV_DOWN);
4561 started_moving = TRUE;
4563 else if (element == EL_AMOEBA_DROP)
4565 Feld[x][y] = EL_AMOEBA_GROWING;
4566 Store[x][y] = EL_AMOEBA_WET;
4568 /* Store[x][y + 1] must be zero, because:
4569 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4572 #if OLD_GAME_BEHAVIOUR
4573 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4575 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4576 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4577 element != EL_DX_SUPABOMB)
4580 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4581 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4582 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4583 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4586 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
4587 (IS_FREE(x - 1, y + 1) ||
4588 Feld[x - 1][y + 1] == EL_ACID));
4589 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4590 (IS_FREE(x + 1, y + 1) ||
4591 Feld[x + 1][y + 1] == EL_ACID));
4592 boolean can_fall_any = (can_fall_left || can_fall_right);
4593 boolean can_fall_both = (can_fall_left && can_fall_right);
4595 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4597 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4599 if (slippery_type == SLIPPERY_ONLY_LEFT)
4600 can_fall_right = FALSE;
4601 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4602 can_fall_left = FALSE;
4603 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4604 can_fall_right = FALSE;
4605 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4606 can_fall_left = FALSE;
4608 can_fall_any = (can_fall_left || can_fall_right);
4609 can_fall_both = (can_fall_left && can_fall_right);
4614 if (can_fall_both &&
4615 (game.emulation != EMU_BOULDERDASH &&
4616 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4617 can_fall_left = !(can_fall_right = RND(2));
4619 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4620 started_moving = TRUE;
4624 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
4626 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4629 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
4630 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4631 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4632 int belt_dir = game.belt_dir[belt_nr];
4634 if ((belt_dir == MV_LEFT && left_is_free) ||
4635 (belt_dir == MV_RIGHT && right_is_free))
4638 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
4641 InitMovingField(x, y, belt_dir);
4642 started_moving = TRUE;
4645 Pushed[x][y] = TRUE;
4646 Pushed[nextx][y] = TRUE;
4649 GfxAction[x][y] = ACTION_DEFAULT;
4653 MovDir[x][y] = 0; /* if element was moving, stop it */
4658 /* not "else if" because of elements that can fall and move (EL_SPRING) */
4659 if (CAN_MOVE(element) && !started_moving)
4661 int move_pattern = element_info[element].move_pattern;
4664 Moving2Blocked(x, y, &newx, &newy);
4667 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4670 if ((element == EL_SATELLITE ||
4671 element == EL_BALLOON ||
4672 element == EL_SPRING)
4673 && JustBeingPushed(x, y))
4678 if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4679 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4680 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4683 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4684 element, element_info[element].token_name,
4685 WasJustMoving[x][y],
4686 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4687 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4688 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4689 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4693 WasJustMoving[x][y] = 0;
4696 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4699 if (Feld[x][y] != element) /* element has changed */
4701 element = Feld[x][y];
4702 move_pattern = element_info[element].move_pattern;
4704 if (!CAN_MOVE(element))
4708 if (Feld[x][y] != element) /* element has changed */
4716 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4717 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
4719 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4721 Moving2Blocked(x, y, &newx, &newy);
4722 if (Feld[newx][newy] == EL_BLOCKED)
4723 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
4729 if (FrameCounter < 1 && x == 0 && y == 29)
4730 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4733 if (!MovDelay[x][y]) /* start new movement phase */
4735 /* all objects that can change their move direction after each step
4736 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4738 if (element != EL_YAMYAM &&
4739 element != EL_DARK_YAMYAM &&
4740 element != EL_PACMAN &&
4741 !(move_pattern & MV_ANY_DIRECTION) &&
4742 move_pattern != MV_TURNING_LEFT &&
4743 move_pattern != MV_TURNING_RIGHT &&
4744 move_pattern != MV_TURNING_LEFT_RIGHT &&
4745 move_pattern != MV_TURNING_RIGHT_LEFT &&
4746 move_pattern != MV_TURNING_RANDOM)
4751 if (FrameCounter < 1 && x == 0 && y == 29)
4752 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4755 if (MovDelay[x][y] && (element == EL_BUG ||
4756 element == EL_SPACESHIP ||
4757 element == EL_SP_SNIKSNAK ||
4758 element == EL_SP_ELECTRON ||
4759 element == EL_MOLE))
4760 DrawLevelField(x, y);
4764 if (MovDelay[x][y]) /* wait some time before next movement */
4769 if (element == EL_YAMYAM)
4772 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4773 DrawLevelElementAnimation(x, y, element);
4777 if (MovDelay[x][y]) /* element still has to wait some time */
4780 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4781 ResetGfxAnimation(x, y);
4785 if (GfxAction[x][y] != ACTION_WAITING)
4786 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4788 GfxAction[x][y] = ACTION_WAITING;
4792 if (element == EL_ROBOT ||
4794 element == EL_PACMAN ||
4796 element == EL_YAMYAM ||
4797 element == EL_DARK_YAMYAM)
4800 DrawLevelElementAnimation(x, y, element);
4802 DrawLevelElementAnimationIfNeeded(x, y, element);
4804 PlayLevelSoundAction(x, y, ACTION_WAITING);
4806 else if (element == EL_SP_ELECTRON)
4807 DrawLevelElementAnimationIfNeeded(x, y, element);
4808 else if (element == EL_DRAGON)
4811 int dir = MovDir[x][y];
4812 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4813 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
4814 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
4815 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
4816 dir == MV_UP ? IMG_FLAMES_1_UP :
4817 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4818 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4821 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4824 GfxAction[x][y] = ACTION_ATTACKING;
4826 if (IS_PLAYER(x, y))
4827 DrawPlayerField(x, y);
4829 DrawLevelField(x, y);
4831 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4833 for (i = 1; i <= 3; i++)
4835 int xx = x + i * dx;
4836 int yy = y + i * dy;
4837 int sx = SCREENX(xx);
4838 int sy = SCREENY(yy);
4839 int flame_graphic = graphic + (i - 1);
4841 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4846 int flamed = MovingOrBlocked2Element(xx, yy);
4848 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
4851 RemoveMovingField(xx, yy);
4853 Feld[xx][yy] = EL_FLAMES;
4854 if (IN_SCR_FIELD(sx, sy))
4856 DrawLevelFieldCrumbledSand(xx, yy);
4857 DrawGraphic(sx, sy, flame_graphic, frame);
4862 if (Feld[xx][yy] == EL_FLAMES)
4863 Feld[xx][yy] = EL_EMPTY;
4864 DrawLevelField(xx, yy);
4869 if (MovDelay[x][y]) /* element still has to wait some time */
4871 PlayLevelSoundAction(x, y, ACTION_WAITING);
4877 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4878 for all other elements GfxAction will be set by InitMovingField() */
4879 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4880 GfxAction[x][y] = ACTION_MOVING;
4884 /* now make next step */
4886 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4888 if (DONT_COLLIDE_WITH(element) &&
4889 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4890 !PLAYER_ENEMY_PROTECTED(newx, newy))
4893 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4897 /* player killed by element which is deadly when colliding with */
4899 KillHero(PLAYERINFO(newx, newy));
4904 else if ((element == EL_PENGUIN ||
4905 element == EL_ROBOT ||
4906 element == EL_SATELLITE ||
4907 element == EL_BALLOON ||
4908 IS_CUSTOM_ELEMENT(element)) &&
4909 IN_LEV_FIELD(newx, newy) &&
4910 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4913 Store[x][y] = EL_ACID;
4915 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4917 if (Feld[newx][newy] == EL_EXIT_OPEN)
4921 DrawLevelField(x, y);
4923 Feld[x][y] = EL_EMPTY;
4924 DrawLevelField(x, y);
4927 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4928 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4929 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4931 local_player->friends_still_needed--;
4932 if (!local_player->friends_still_needed &&
4933 !local_player->GameOver && AllPlayersGone)
4934 local_player->LevelSolved = local_player->GameOver = TRUE;
4938 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4940 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
4941 DrawLevelField(newx, newy);
4943 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4945 else if (!IS_FREE(newx, newy))
4947 GfxAction[x][y] = ACTION_WAITING;
4949 if (IS_PLAYER(x, y))
4950 DrawPlayerField(x, y);
4952 DrawLevelField(x, y);
4957 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4959 if (IS_FOOD_PIG(Feld[newx][newy]))
4961 if (IS_MOVING(newx, newy))
4962 RemoveMovingField(newx, newy);
4965 Feld[newx][newy] = EL_EMPTY;
4966 DrawLevelField(newx, newy);
4969 PlayLevelSound(x, y, SND_PIG_DIGGING);
4971 else if (!IS_FREE(newx, newy))
4973 if (IS_PLAYER(x, y))
4974 DrawPlayerField(x, y);
4976 DrawLevelField(x, y);
4985 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
4988 else if (IS_CUSTOM_ELEMENT(element) &&
4989 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
4993 !IS_FREE(newx, newy)
4998 int new_element = Feld[newx][newy];
5001 printf("::: '%s' digs '%s' [%d]\n",
5002 element_info[element].token_name,
5003 element_info[Feld[newx][newy]].token_name,
5004 StorePlayer[newx][newy]);
5007 if (!IS_FREE(newx, newy))
5009 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5010 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5013 /* no element can dig solid indestructible elements */
5014 if (IS_INDESTRUCTIBLE(new_element) &&
5015 !IS_DIGGABLE(new_element) &&
5016 !IS_COLLECTIBLE(new_element))
5019 if (AmoebaNr[newx][newy] &&
5020 (new_element == EL_AMOEBA_FULL ||
5021 new_element == EL_BD_AMOEBA ||
5022 new_element == EL_AMOEBA_GROWING))
5024 AmoebaCnt[AmoebaNr[newx][newy]]--;
5025 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5028 if (IS_MOVING(newx, newy))
5029 RemoveMovingField(newx, newy);
5032 RemoveField(newx, newy);
5033 DrawLevelField(newx, newy);
5036 PlayLevelSoundAction(x, y, action);
5039 if (new_element == element_info[element].move_enter_element)
5040 element_info[element].can_leave_element = TRUE;
5042 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5044 RunnerVisit[x][y] = FrameCounter;
5045 PlayerVisit[x][y] /= 8; /* expire player visit path */
5051 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5053 if (!IS_FREE(newx, newy))
5055 if (IS_PLAYER(x, y))
5056 DrawPlayerField(x, y);
5058 DrawLevelField(x, y);
5064 boolean wanna_flame = !RND(10);
5065 int dx = newx - x, dy = newy - y;
5066 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5067 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5068 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5069 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5070 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5071 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5074 IS_CLASSIC_ENEMY(element1) ||
5075 IS_CLASSIC_ENEMY(element2)) &&
5076 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5077 element1 != EL_FLAMES && element2 != EL_FLAMES)
5080 ResetGfxAnimation(x, y);
5081 GfxAction[x][y] = ACTION_ATTACKING;
5084 if (IS_PLAYER(x, y))
5085 DrawPlayerField(x, y);
5087 DrawLevelField(x, y);
5089 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5091 MovDelay[x][y] = 50;
5093 Feld[newx][newy] = EL_FLAMES;
5094 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5095 Feld[newx1][newy1] = EL_FLAMES;
5096 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5097 Feld[newx2][newy2] = EL_FLAMES;
5103 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5104 Feld[newx][newy] == EL_DIAMOND)
5106 if (IS_MOVING(newx, newy))
5107 RemoveMovingField(newx, newy);
5110 Feld[newx][newy] = EL_EMPTY;
5111 DrawLevelField(newx, newy);
5114 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5116 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5117 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5119 if (AmoebaNr[newx][newy])
5121 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5122 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5123 Feld[newx][newy] == EL_BD_AMOEBA)
5124 AmoebaCnt[AmoebaNr[newx][newy]]--;
5127 if (IS_MOVING(newx, newy))
5128 RemoveMovingField(newx, newy);
5131 Feld[newx][newy] = EL_EMPTY;
5132 DrawLevelField(newx, newy);
5135 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5137 else if ((element == EL_PACMAN || element == EL_MOLE)
5138 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5140 if (AmoebaNr[newx][newy])
5142 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5143 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5144 Feld[newx][newy] == EL_BD_AMOEBA)
5145 AmoebaCnt[AmoebaNr[newx][newy]]--;
5148 if (element == EL_MOLE)
5150 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5151 PlayLevelSound(x, y, SND_MOLE_DIGGING);
5153 ResetGfxAnimation(x, y);
5154 GfxAction[x][y] = ACTION_DIGGING;
5155 DrawLevelField(x, y);
5157 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
5159 return; /* wait for shrinking amoeba */
5161 else /* element == EL_PACMAN */
5163 Feld[newx][newy] = EL_EMPTY;
5164 DrawLevelField(newx, newy);
5165 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5168 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5169 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5170 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5172 /* wait for shrinking amoeba to completely disappear */
5175 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5177 /* object was running against a wall */
5182 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
5183 DrawLevelElementAnimation(x, y, element);
5185 if (element == EL_BUG ||
5186 element == EL_SPACESHIP ||
5187 element == EL_SP_SNIKSNAK)
5188 DrawLevelField(x, y);
5189 else if (element == EL_MOLE)
5190 DrawLevelField(x, y);
5191 else if (element == EL_BD_BUTTERFLY ||
5192 element == EL_BD_FIREFLY)
5193 DrawLevelElementAnimationIfNeeded(x, y, element);
5194 else if (element == EL_SATELLITE)
5195 DrawLevelElementAnimationIfNeeded(x, y, element);
5196 else if (element == EL_SP_ELECTRON)
5197 DrawLevelElementAnimationIfNeeded(x, y, element);
5200 if (DONT_TOUCH(element))
5201 TestIfBadThingTouchesHero(x, y);
5204 PlayLevelSoundAction(x, y, ACTION_WAITING);
5210 InitMovingField(x, y, MovDir[x][y]);
5212 PlayLevelSoundAction(x, y, ACTION_MOVING);
5216 ContinueMoving(x, y);
5219 void ContinueMoving(int x, int y)
5221 int element = Feld[x][y];
5222 struct ElementInfo *ei = &element_info[element];
5223 int direction = MovDir[x][y];
5224 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5225 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5226 int newx = x + dx, newy = y + dy;
5228 int nextx = newx + dx, nexty = newy + dy;
5231 boolean pushed = (Pushed[x][y] && IS_PLAYER(x, y));
5232 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5234 boolean pushed = Pushed[x][y];
5237 MovPos[x][y] += getElementMoveStepsize(x, y);
5240 if (pushed && IS_PLAYER(x, y))
5242 /* special case: moving object pushed by player */
5243 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5246 if (pushed) /* special case: moving object pushed by player */
5247 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5250 if (ABS(MovPos[x][y]) < TILEX)
5252 DrawLevelField(x, y);
5254 return; /* element is still moving */
5257 /* element reached destination field */
5259 Feld[x][y] = EL_EMPTY;
5260 Feld[newx][newy] = element;
5261 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
5263 if (element == EL_MOLE)
5265 Feld[x][y] = EL_SAND;
5267 DrawLevelFieldCrumbledSandNeighbours(x, y);
5269 else if (element == EL_QUICKSAND_FILLING)
5271 element = Feld[newx][newy] = get_next_element(element);
5272 Store[newx][newy] = Store[x][y];
5274 else if (element == EL_QUICKSAND_EMPTYING)
5276 Feld[x][y] = get_next_element(element);
5277 element = Feld[newx][newy] = Store[x][y];
5279 else if (element == EL_MAGIC_WALL_FILLING)
5281 element = Feld[newx][newy] = get_next_element(element);
5282 if (!game.magic_wall_active)
5283 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5284 Store[newx][newy] = Store[x][y];
5286 else if (element == EL_MAGIC_WALL_EMPTYING)
5288 Feld[x][y] = get_next_element(element);
5289 if (!game.magic_wall_active)
5290 Feld[x][y] = EL_MAGIC_WALL_DEAD;
5291 element = Feld[newx][newy] = Store[x][y];
5293 else if (element == EL_BD_MAGIC_WALL_FILLING)
5295 element = Feld[newx][newy] = get_next_element(element);
5296 if (!game.magic_wall_active)
5297 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5298 Store[newx][newy] = Store[x][y];
5300 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5302 Feld[x][y] = get_next_element(element);
5303 if (!game.magic_wall_active)
5304 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5305 element = Feld[newx][newy] = Store[x][y];
5307 else if (element == EL_AMOEBA_DROPPING)
5309 Feld[x][y] = get_next_element(element);
5310 element = Feld[newx][newy] = Store[x][y];
5312 else if (element == EL_SOKOBAN_OBJECT)
5315 Feld[x][y] = Back[x][y];
5317 if (Back[newx][newy])
5318 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5320 Back[x][y] = Back[newx][newy] = 0;
5322 else if (Store[x][y] == EL_ACID)
5324 element = Feld[newx][newy] = EL_ACID;
5328 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5329 MovDelay[newx][newy] = 0;
5331 /* copy element change control values to new field */
5332 ChangeDelay[newx][newy] = ChangeDelay[x][y];
5333 ChangePage[newx][newy] = ChangePage[x][y];
5334 Changed[newx][newy] = Changed[x][y];
5335 ChangeEvent[newx][newy] = ChangeEvent[x][y];
5337 ChangeDelay[x][y] = 0;
5338 ChangePage[x][y] = -1;
5339 Changed[x][y] = CE_BITMASK_DEFAULT;
5340 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5342 /* copy animation control values to new field */
5343 GfxFrame[newx][newy] = GfxFrame[x][y];
5344 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
5345 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
5346 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
5348 Pushed[x][y] = Pushed[newx][newy] = FALSE;
5350 ResetGfxAnimation(x, y); /* reset animation values for old field */
5353 /* some elements can leave other elements behind after moving */
5354 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5355 ei->move_leave_element != EL_EMPTY &&
5356 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5357 ei->can_leave_element_last))
5359 Feld[x][y] = ei->move_leave_element;
5360 InitField(x, y, FALSE);
5362 if (GFX_CRUMBLED(Feld[x][y]))
5363 DrawLevelFieldCrumbledSandNeighbours(x, y);
5366 ei->can_leave_element_last = ei->can_leave_element;
5367 ei->can_leave_element = FALSE;
5371 /* 2.1.1 (does not work correctly for spring) */
5372 if (!CAN_MOVE(element))
5373 MovDir[newx][newy] = 0;
5377 /* (does not work for falling objects that slide horizontally) */
5378 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5379 MovDir[newx][newy] = 0;
5382 if (!CAN_MOVE(element) ||
5383 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5384 MovDir[newx][newy] = 0;
5387 if (!CAN_MOVE(element) ||
5388 (CAN_FALL(element) && direction == MV_DOWN))
5389 GfxDir[x][y] = MovDir[newx][newy] = 0;
5394 DrawLevelField(x, y);
5395 DrawLevelField(newx, newy);
5397 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
5399 /* prevent pushed element from moving on in pushed direction */
5400 if (pushed && CAN_MOVE(element) &&
5401 element_info[element].move_pattern & MV_ANY_DIRECTION &&
5402 !(element_info[element].move_pattern & direction))
5403 TurnRound(newx, newy);
5406 /* prevent elements on conveyor belt from moving on in last direction */
5407 if (pushed_by_conveyor && CAN_FALL(element) &&
5408 direction & MV_HORIZONTAL)
5409 MovDir[newx][newy] = 0;
5412 if (!pushed) /* special case: moving object pushed by player */
5414 WasJustMoving[newx][newy] = 3;
5416 if (CAN_FALL(element) && direction == MV_DOWN)
5417 WasJustFalling[newx][newy] = 3;
5420 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
5422 TestIfBadThingTouchesHero(newx, newy);
5423 TestIfBadThingTouchesFriend(newx, newy);
5425 if (!IS_CUSTOM_ELEMENT(element))
5426 TestIfBadThingTouchesOtherBadThing(newx, newy);
5428 else if (element == EL_PENGUIN)
5429 TestIfFriendTouchesBadThing(newx, newy);
5431 if (CAN_FALL(element) && direction == MV_DOWN &&
5432 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5436 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5440 if (ChangePage[newx][newy] != -1) /* delayed change */
5441 ChangeElement(newx, newy, ChangePage[newx][newy]);
5446 TestIfElementHitsCustomElement(newx, newy, direction);
5450 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5452 int hitting_element = Feld[newx][newy];
5454 /* !!! fix side (direction) orientation here and elsewhere !!! */
5455 CheckElementSideChange(newx, newy, hitting_element,
5456 direction, CE_HITTING_SOMETHING, -1);
5459 if (IN_LEV_FIELD(nextx, nexty))
5461 int opposite_direction = MV_DIR_OPPOSITE(direction);
5462 int hitting_side = direction;
5463 int touched_side = opposite_direction;
5464 int touched_element = MovingOrBlocked2Element(nextx, nexty);
5465 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5466 MovDir[nextx][nexty] != direction ||
5467 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5473 CheckElementSideChange(nextx, nexty, touched_element,
5474 opposite_direction, CE_HIT_BY_SOMETHING, -1);
5476 if (IS_CUSTOM_ELEMENT(hitting_element) &&
5477 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5479 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5481 struct ElementChangeInfo *change =
5482 &element_info[hitting_element].change_page[i];
5484 if (change->can_change &&
5485 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5486 change->sides & touched_side &&
5487 change->trigger_element == touched_element)
5489 CheckElementSideChange(newx, newy, hitting_element,
5490 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
5496 if (IS_CUSTOM_ELEMENT(touched_element) &&
5497 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5499 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5501 struct ElementChangeInfo *change =
5502 &element_info[touched_element].change_page[i];
5504 if (change->can_change &&
5505 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5506 change->sides & hitting_side &&
5507 change->trigger_element == hitting_element)
5509 CheckElementSideChange(nextx, nexty, touched_element,
5510 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
5521 TestIfPlayerTouchesCustomElement(newx, newy);
5522 TestIfElementTouchesCustomElement(newx, newy);
5525 int AmoebeNachbarNr(int ax, int ay)
5528 int element = Feld[ax][ay];
5530 static int xy[4][2] =
5538 for (i = 0; i < 4; i++)
5540 int x = ax + xy[i][0];
5541 int y = ay + xy[i][1];
5543 if (!IN_LEV_FIELD(x, y))
5546 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5547 group_nr = AmoebaNr[x][y];
5553 void AmoebenVereinigen(int ax, int ay)
5555 int i, x, y, xx, yy;
5556 int new_group_nr = AmoebaNr[ax][ay];
5557 static int xy[4][2] =
5565 if (new_group_nr == 0)
5568 for (i = 0; i < 4; i++)
5573 if (!IN_LEV_FIELD(x, y))
5576 if ((Feld[x][y] == EL_AMOEBA_FULL ||
5577 Feld[x][y] == EL_BD_AMOEBA ||
5578 Feld[x][y] == EL_AMOEBA_DEAD) &&
5579 AmoebaNr[x][y] != new_group_nr)
5581 int old_group_nr = AmoebaNr[x][y];
5583 if (old_group_nr == 0)
5586 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5587 AmoebaCnt[old_group_nr] = 0;
5588 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5589 AmoebaCnt2[old_group_nr] = 0;
5591 for (yy = 0; yy < lev_fieldy; yy++)
5593 for (xx = 0; xx < lev_fieldx; xx++)
5595 if (AmoebaNr[xx][yy] == old_group_nr)
5596 AmoebaNr[xx][yy] = new_group_nr;
5603 void AmoebeUmwandeln(int ax, int ay)
5607 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5609 int group_nr = AmoebaNr[ax][ay];
5614 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5615 printf("AmoebeUmwandeln(): This should never happen!\n");
5620 for (y = 0; y < lev_fieldy; y++)
5622 for (x = 0; x < lev_fieldx; x++)
5624 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5627 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5631 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5632 SND_AMOEBA_TURNING_TO_GEM :
5633 SND_AMOEBA_TURNING_TO_ROCK));
5638 static int xy[4][2] =
5646 for (i = 0; i < 4; i++)
5651 if (!IN_LEV_FIELD(x, y))
5654 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5656 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5657 SND_AMOEBA_TURNING_TO_GEM :
5658 SND_AMOEBA_TURNING_TO_ROCK));
5665 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5668 int group_nr = AmoebaNr[ax][ay];
5669 boolean done = FALSE;
5674 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5675 printf("AmoebeUmwandelnBD(): This should never happen!\n");
5680 for (y = 0; y < lev_fieldy; y++)
5682 for (x = 0; x < lev_fieldx; x++)
5684 if (AmoebaNr[x][y] == group_nr &&
5685 (Feld[x][y] == EL_AMOEBA_DEAD ||
5686 Feld[x][y] == EL_BD_AMOEBA ||
5687 Feld[x][y] == EL_AMOEBA_GROWING))
5690 Feld[x][y] = new_element;
5691 InitField(x, y, FALSE);
5692 DrawLevelField(x, y);
5699 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5700 SND_BD_AMOEBA_TURNING_TO_ROCK :
5701 SND_BD_AMOEBA_TURNING_TO_GEM));
5704 void AmoebeWaechst(int x, int y)
5706 static unsigned long sound_delay = 0;
5707 static unsigned long sound_delay_value = 0;
5709 if (!MovDelay[x][y]) /* start new growing cycle */
5713 if (DelayReached(&sound_delay, sound_delay_value))
5716 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5718 if (Store[x][y] == EL_BD_AMOEBA)
5719 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5721 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5723 sound_delay_value = 30;
5727 if (MovDelay[x][y]) /* wait some time before growing bigger */
5730 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5732 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5733 6 - MovDelay[x][y]);
5735 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5738 if (!MovDelay[x][y])
5740 Feld[x][y] = Store[x][y];
5742 DrawLevelField(x, y);
5747 void AmoebaDisappearing(int x, int y)
5749 static unsigned long sound_delay = 0;
5750 static unsigned long sound_delay_value = 0;
5752 if (!MovDelay[x][y]) /* start new shrinking cycle */
5756 if (DelayReached(&sound_delay, sound_delay_value))
5757 sound_delay_value = 30;
5760 if (MovDelay[x][y]) /* wait some time before shrinking */
5763 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5765 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5766 6 - MovDelay[x][y]);
5768 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5771 if (!MovDelay[x][y])
5773 Feld[x][y] = EL_EMPTY;
5774 DrawLevelField(x, y);
5776 /* don't let mole enter this field in this cycle;
5777 (give priority to objects falling to this field from above) */
5783 void AmoebeAbleger(int ax, int ay)
5786 int element = Feld[ax][ay];
5787 int graphic = el2img(element);
5788 int newax = ax, neway = ay;
5789 static int xy[4][2] =
5797 if (!level.amoeba_speed)
5799 Feld[ax][ay] = EL_AMOEBA_DEAD;
5800 DrawLevelField(ax, ay);
5804 if (IS_ANIMATED(graphic))
5805 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5807 if (!MovDelay[ax][ay]) /* start making new amoeba field */
5808 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5810 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
5813 if (MovDelay[ax][ay])
5817 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5820 int x = ax + xy[start][0];
5821 int y = ay + xy[start][1];
5823 if (!IN_LEV_FIELD(x, y))
5826 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5827 if (IS_FREE(x, y) ||
5828 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5834 if (newax == ax && neway == ay)
5837 else /* normal or "filled" (BD style) amoeba */
5840 boolean waiting_for_player = FALSE;
5842 for (i = 0; i < 4; i++)
5844 int j = (start + i) % 4;
5845 int x = ax + xy[j][0];
5846 int y = ay + xy[j][1];
5848 if (!IN_LEV_FIELD(x, y))
5851 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5852 if (IS_FREE(x, y) ||
5853 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5859 else if (IS_PLAYER(x, y))
5860 waiting_for_player = TRUE;
5863 if (newax == ax && neway == ay) /* amoeba cannot grow */
5865 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5867 Feld[ax][ay] = EL_AMOEBA_DEAD;
5868 DrawLevelField(ax, ay);
5869 AmoebaCnt[AmoebaNr[ax][ay]]--;
5871 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
5873 if (element == EL_AMOEBA_FULL)
5874 AmoebeUmwandeln(ax, ay);
5875 else if (element == EL_BD_AMOEBA)
5876 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5881 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5883 /* amoeba gets larger by growing in some direction */
5885 int new_group_nr = AmoebaNr[ax][ay];
5888 if (new_group_nr == 0)
5890 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5891 printf("AmoebeAbleger(): This should never happen!\n");
5896 AmoebaNr[newax][neway] = new_group_nr;
5897 AmoebaCnt[new_group_nr]++;
5898 AmoebaCnt2[new_group_nr]++;
5900 /* if amoeba touches other amoeba(s) after growing, unify them */
5901 AmoebenVereinigen(newax, neway);
5903 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5905 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5911 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5912 (neway == lev_fieldy - 1 && newax != ax))
5914 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
5915 Store[newax][neway] = element;
5917 else if (neway == ay)
5919 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
5921 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5923 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5928 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
5929 Feld[ax][ay] = EL_AMOEBA_DROPPING;
5930 Store[ax][ay] = EL_AMOEBA_DROP;
5931 ContinueMoving(ax, ay);
5935 DrawLevelField(newax, neway);
5938 void Life(int ax, int ay)
5941 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
5943 int element = Feld[ax][ay];
5944 int graphic = el2img(element);
5945 boolean changed = FALSE;
5947 if (IS_ANIMATED(graphic))
5948 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5953 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
5954 MovDelay[ax][ay] = life_time;
5956 if (MovDelay[ax][ay]) /* wait some time before next cycle */
5959 if (MovDelay[ax][ay])
5963 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5965 int xx = ax+x1, yy = ay+y1;
5968 if (!IN_LEV_FIELD(xx, yy))
5971 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5973 int x = xx+x2, y = yy+y2;
5975 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5978 if (((Feld[x][y] == element ||
5979 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5981 (IS_FREE(x, y) && Stop[x][y]))
5985 if (xx == ax && yy == ay) /* field in the middle */
5987 if (nachbarn < life[0] || nachbarn > life[1])
5989 Feld[xx][yy] = EL_EMPTY;
5991 DrawLevelField(xx, yy);
5992 Stop[xx][yy] = TRUE;
5996 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5997 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5998 { /* free border field */
5999 if (nachbarn >= life[2] && nachbarn <= life[3])
6001 Feld[xx][yy] = element;
6002 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6004 DrawLevelField(xx, yy);
6005 Stop[xx][yy] = TRUE;
6012 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6013 SND_GAME_OF_LIFE_GROWING);
6016 static void InitRobotWheel(int x, int y)
6018 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6021 static void RunRobotWheel(int x, int y)
6023 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6026 static void StopRobotWheel(int x, int y)
6028 if (ZX == x && ZY == y)
6032 static void InitTimegateWheel(int x, int y)
6034 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6037 static void RunTimegateWheel(int x, int y)
6039 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6042 void CheckExit(int x, int y)
6044 if (local_player->gems_still_needed > 0 ||
6045 local_player->sokobanfields_still_needed > 0 ||
6046 local_player->lights_still_needed > 0)
6048 int element = Feld[x][y];
6049 int graphic = el2img(element);
6051 if (IS_ANIMATED(graphic))
6052 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6057 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6060 Feld[x][y] = EL_EXIT_OPENING;
6062 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6065 void CheckExitSP(int x, int y)
6067 if (local_player->gems_still_needed > 0)
6069 int element = Feld[x][y];
6070 int graphic = el2img(element);
6072 if (IS_ANIMATED(graphic))
6073 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6078 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6081 Feld[x][y] = EL_SP_EXIT_OPENING;
6083 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6086 static void CloseAllOpenTimegates()
6090 for (y = 0; y < lev_fieldy; y++)
6092 for (x = 0; x < lev_fieldx; x++)
6094 int element = Feld[x][y];
6096 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6098 Feld[x][y] = EL_TIMEGATE_CLOSING;
6100 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6102 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6109 void EdelsteinFunkeln(int x, int y)
6111 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6114 if (Feld[x][y] == EL_BD_DIAMOND)
6117 if (MovDelay[x][y] == 0) /* next animation frame */
6118 MovDelay[x][y] = 11 * !SimpleRND(500);
6120 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6124 if (setup.direct_draw && MovDelay[x][y])
6125 SetDrawtoField(DRAW_BUFFERED);
6127 DrawLevelElementAnimation(x, y, Feld[x][y]);
6129 if (MovDelay[x][y] != 0)
6131 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6132 10 - MovDelay[x][y]);
6134 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6136 if (setup.direct_draw)
6140 dest_x = FX + SCREENX(x) * TILEX;
6141 dest_y = FY + SCREENY(y) * TILEY;
6143 BlitBitmap(drawto_field, window,
6144 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6145 SetDrawtoField(DRAW_DIRECT);
6151 void MauerWaechst(int x, int y)
6155 if (!MovDelay[x][y]) /* next animation frame */
6156 MovDelay[x][y] = 3 * delay;
6158 if (MovDelay[x][y]) /* wait some time before next frame */
6162 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6164 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6165 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6167 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6170 if (!MovDelay[x][y])
6172 if (MovDir[x][y] == MV_LEFT)
6174 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6175 DrawLevelField(x - 1, y);
6177 else if (MovDir[x][y] == MV_RIGHT)
6179 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6180 DrawLevelField(x + 1, y);
6182 else if (MovDir[x][y] == MV_UP)
6184 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6185 DrawLevelField(x, y - 1);
6189 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6190 DrawLevelField(x, y + 1);
6193 Feld[x][y] = Store[x][y];
6195 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6196 DrawLevelField(x, y);
6201 void MauerAbleger(int ax, int ay)
6203 int element = Feld[ax][ay];
6204 int graphic = el2img(element);
6205 boolean oben_frei = FALSE, unten_frei = FALSE;
6206 boolean links_frei = FALSE, rechts_frei = FALSE;
6207 boolean oben_massiv = FALSE, unten_massiv = FALSE;
6208 boolean links_massiv = FALSE, rechts_massiv = FALSE;
6209 boolean new_wall = FALSE;
6211 if (IS_ANIMATED(graphic))
6212 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6214 if (!MovDelay[ax][ay]) /* start building new wall */
6215 MovDelay[ax][ay] = 6;
6217 if (MovDelay[ax][ay]) /* wait some time before building new wall */
6220 if (MovDelay[ax][ay])
6224 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6226 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6228 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6230 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6233 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6234 element == EL_EXPANDABLE_WALL_ANY)
6238 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6239 Store[ax][ay-1] = element;
6240 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6241 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6242 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6243 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6248 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6249 Store[ax][ay+1] = element;
6250 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6251 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6252 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6253 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6258 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6259 element == EL_EXPANDABLE_WALL_ANY ||
6260 element == EL_EXPANDABLE_WALL)
6264 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6265 Store[ax-1][ay] = element;
6266 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6267 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6268 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6269 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6275 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6276 Store[ax+1][ay] = element;
6277 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6278 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6279 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6280 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6285 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6286 DrawLevelField(ax, ay);
6288 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6290 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6291 unten_massiv = TRUE;
6292 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6293 links_massiv = TRUE;
6294 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6295 rechts_massiv = TRUE;
6297 if (((oben_massiv && unten_massiv) ||
6298 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6299 element == EL_EXPANDABLE_WALL) &&
6300 ((links_massiv && rechts_massiv) ||
6301 element == EL_EXPANDABLE_WALL_VERTICAL))
6302 Feld[ax][ay] = EL_WALL;
6306 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6308 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6312 void CheckForDragon(int x, int y)
6315 boolean dragon_found = FALSE;
6316 static int xy[4][2] =
6324 for (i = 0; i < 4; i++)
6326 for (j = 0; j < 4; j++)
6328 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6330 if (IN_LEV_FIELD(xx, yy) &&
6331 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6333 if (Feld[xx][yy] == EL_DRAGON)
6334 dragon_found = TRUE;
6343 for (i = 0; i < 4; i++)
6345 for (j = 0; j < 3; j++)
6347 int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6349 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6351 Feld[xx][yy] = EL_EMPTY;
6352 DrawLevelField(xx, yy);
6361 static void InitBuggyBase(int x, int y)
6363 int element = Feld[x][y];
6364 int activating_delay = FRAMES_PER_SECOND / 4;
6367 (element == EL_SP_BUGGY_BASE ?
6368 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6369 element == EL_SP_BUGGY_BASE_ACTIVATING ?
6371 element == EL_SP_BUGGY_BASE_ACTIVE ?
6372 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6375 static void WarnBuggyBase(int x, int y)
6378 static int xy[4][2] =
6386 for (i = 0; i < 4; i++)
6388 int xx = x + xy[i][0], yy = y + xy[i][1];
6390 if (IS_PLAYER(xx, yy))
6392 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6399 static void InitTrap(int x, int y)
6401 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6404 static void ActivateTrap(int x, int y)
6406 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6409 static void ChangeActiveTrap(int x, int y)
6411 int graphic = IMG_TRAP_ACTIVE;
6413 /* if new animation frame was drawn, correct crumbled sand border */
6414 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6415 DrawLevelFieldCrumbledSand(x, y);
6418 static void ChangeElementNowExt(int x, int y, int target_element)
6420 int previous_move_direction = MovDir[x][y];
6422 /* check if element under player changes from accessible to unaccessible
6423 (needed for special case of dropping element which then changes) */
6424 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6425 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6432 Feld[x][y] = target_element;
6434 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6436 ResetGfxAnimation(x, y);
6437 ResetRandomAnimationValue(x, y);
6439 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6440 MovDir[x][y] = previous_move_direction;
6442 InitField(x, y, FALSE);
6443 if (CAN_MOVE(Feld[x][y]))
6446 DrawLevelField(x, y);
6448 if (GFX_CRUMBLED(Feld[x][y]))
6449 DrawLevelFieldCrumbledSandNeighbours(x, y);
6451 TestIfBadThingTouchesHero(x, y);
6452 TestIfPlayerTouchesCustomElement(x, y);
6453 TestIfElementTouchesCustomElement(x, y);
6455 if (ELEM_IS_PLAYER(target_element))
6456 RelocatePlayer(x, y, target_element);
6459 static boolean ChangeElementNow(int x, int y, int element, int page)
6461 struct ElementChangeInfo *change = &element_info[element].change_page[page];
6463 /* always use default change event to prevent running into a loop */
6464 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6465 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6467 /* do not change already changed elements with same change event */
6469 if (Changed[x][y] & ChangeEvent[x][y])
6476 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
6478 CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
6480 if (change->explode)
6487 if (change->use_content)
6489 boolean complete_change = TRUE;
6490 boolean can_change[3][3];
6493 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6495 boolean half_destructible;
6496 int ex = x + xx - 1;
6497 int ey = y + yy - 1;
6500 can_change[xx][yy] = TRUE;
6502 if (ex == x && ey == y) /* do not check changing element itself */
6505 if (change->content[xx][yy] == EL_EMPTY_SPACE)
6507 can_change[xx][yy] = FALSE; /* do not change empty borders */
6512 if (!IN_LEV_FIELD(ex, ey))
6514 can_change[xx][yy] = FALSE;
6515 complete_change = FALSE;
6522 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6523 e = MovingOrBlocked2Element(ex, ey);
6525 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6527 if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
6528 (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6529 (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6531 can_change[xx][yy] = FALSE;
6532 complete_change = FALSE;
6536 if (!change->only_complete || complete_change)
6538 boolean something_has_changed = FALSE;
6540 if (change->only_complete && change->use_random_change &&
6541 RND(100) < change->random)
6544 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6546 int ex = x + xx - 1;
6547 int ey = y + yy - 1;
6549 if (can_change[xx][yy] && (!change->use_random_change ||
6550 RND(100) < change->random))
6552 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6553 RemoveMovingField(ex, ey);
6555 ChangeEvent[ex][ey] = ChangeEvent[x][y];
6557 ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6559 something_has_changed = TRUE;
6561 /* for symmetry reasons, freeze newly created border elements */
6562 if (ex != x || ey != y)
6563 Stop[ex][ey] = TRUE; /* no more moving in this frame */
6567 if (something_has_changed)
6568 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6573 ChangeElementNowExt(x, y, change->target_element);
6575 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6581 static void ChangeElement(int x, int y, int page)
6583 int element = MovingOrBlocked2Element(x, y);
6584 struct ElementInfo *ei = &element_info[element];
6585 struct ElementChangeInfo *change = &ei->change_page[page];
6589 if (!CAN_CHANGE(element))
6592 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6593 x, y, element, element_info[element].token_name);
6594 printf("ChangeElement(): This should never happen!\n");
6600 if (ChangeDelay[x][y] == 0) /* initialize element change */
6602 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
6603 RND(change->delay_random * change->delay_frames)) + 1;
6605 ResetGfxAnimation(x, y);
6606 ResetRandomAnimationValue(x, y);
6608 if (change->pre_change_function)
6609 change->pre_change_function(x, y);
6612 ChangeDelay[x][y]--;
6614 if (ChangeDelay[x][y] != 0) /* continue element change */
6616 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6618 if (IS_ANIMATED(graphic))
6619 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6621 if (change->change_function)
6622 change->change_function(x, y);
6624 else /* finish element change */
6626 if (ChangePage[x][y] != -1) /* remember page from delayed change */
6628 page = ChangePage[x][y];
6629 ChangePage[x][y] = -1;
6633 if (IS_MOVING(x, y) && !change->explode)
6635 if (IS_MOVING(x, y)) /* never change a running system ;-) */
6638 ChangeDelay[x][y] = 1; /* try change after next move step */
6639 ChangePage[x][y] = page; /* remember page to use for change */
6644 if (ChangeElementNow(x, y, element, page))
6646 if (change->post_change_function)
6647 change->post_change_function(x, y);
6652 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6653 int trigger_element,
6659 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6662 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6664 int element = EL_CUSTOM_START + i;
6666 boolean change_element = FALSE;
6669 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6672 for (j = 0; j < element_info[element].num_change_pages; j++)
6674 struct ElementChangeInfo *change = &element_info[element].change_page[j];
6676 if (change->can_change &&
6678 change->events & CH_EVENT_BIT(trigger_event) &&
6680 change->sides & trigger_side &&
6682 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)
6684 change->trigger_element == trigger_element
6689 if (!(change->events & CH_EVENT_BIT(trigger_event)))
6690 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6691 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6694 change_element = TRUE;
6701 if (!change_element)
6704 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6707 if (x == lx && y == ly) /* do not change trigger element itself */
6711 if (Feld[x][y] == element)
6713 ChangeDelay[x][y] = 1;
6714 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6715 ChangeElement(x, y, page);
6723 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6726 return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6730 static boolean CheckElementSideChange(int x, int y, int element, int side,
6731 int trigger_event, int page)
6733 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6736 if (Feld[x][y] == EL_BLOCKED)
6738 Blocked2Moving(x, y, &x, &y);
6739 element = Feld[x][y];
6745 boolean change_element = FALSE;
6748 for (i = 0; i < element_info[element].num_change_pages; i++)
6750 struct ElementChangeInfo *change = &element_info[element].change_page[i];
6752 if (change->can_change &&
6753 change->events & CH_EVENT_BIT(trigger_event) &&
6754 change->sides & side)
6756 change_element = TRUE;
6763 if (!change_element)
6769 /* !!! this check misses pages with same event, but different side !!! */
6772 page = element_info[element].event_page_nr[trigger_event];
6774 if (!(element_info[element].change_page[page].sides & side))
6778 ChangeDelay[x][y] = 1;
6779 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6780 ChangeElement(x, y, page);
6785 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6787 return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6790 static void PlayPlayerSound(struct PlayerInfo *player)
6792 int jx = player->jx, jy = player->jy;
6793 int element = player->element_nr;
6794 int last_action = player->last_action_waiting;
6795 int action = player->action_waiting;
6797 if (player->is_waiting)
6799 if (action != last_action)
6800 PlayLevelSoundElementAction(jx, jy, element, action);
6802 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6806 if (action != last_action)
6807 StopSound(element_info[element].sound[last_action]);
6809 if (last_action == ACTION_SLEEPING)
6810 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6814 static void PlayAllPlayersSound()
6818 for (i = 0; i < MAX_PLAYERS; i++)
6819 if (stored_player[i].active)
6820 PlayPlayerSound(&stored_player[i]);
6823 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6825 boolean last_waiting = player->is_waiting;
6826 int move_dir = player->MovDir;
6828 player->last_action_waiting = player->action_waiting;
6832 if (!last_waiting) /* not waiting -> waiting */
6834 player->is_waiting = TRUE;
6836 player->frame_counter_bored =
6838 game.player_boring_delay_fixed +
6839 SimpleRND(game.player_boring_delay_random);
6840 player->frame_counter_sleeping =
6842 game.player_sleeping_delay_fixed +
6843 SimpleRND(game.player_sleeping_delay_random);
6845 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6848 if (game.player_sleeping_delay_fixed +
6849 game.player_sleeping_delay_random > 0 &&
6850 player->anim_delay_counter == 0 &&
6851 player->post_delay_counter == 0 &&
6852 FrameCounter >= player->frame_counter_sleeping)
6853 player->is_sleeping = TRUE;
6854 else if (game.player_boring_delay_fixed +
6855 game.player_boring_delay_random > 0 &&
6856 FrameCounter >= player->frame_counter_bored)
6857 player->is_bored = TRUE;
6859 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6860 player->is_bored ? ACTION_BORING :
6863 if (player->is_sleeping)
6865 if (player->num_special_action_sleeping > 0)
6867 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6869 int last_special_action = player->special_action_sleeping;
6870 int num_special_action = player->num_special_action_sleeping;
6871 int special_action =
6872 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6873 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6874 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6875 last_special_action + 1 : ACTION_SLEEPING);
6876 int special_graphic =
6877 el_act_dir2img(player->element_nr, special_action, move_dir);
6879 player->anim_delay_counter =
6880 graphic_info[special_graphic].anim_delay_fixed +
6881 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6882 player->post_delay_counter =
6883 graphic_info[special_graphic].post_delay_fixed +
6884 SimpleRND(graphic_info[special_graphic].post_delay_random);
6886 player->special_action_sleeping = special_action;
6889 if (player->anim_delay_counter > 0)
6891 player->action_waiting = player->special_action_sleeping;
6892 player->anim_delay_counter--;
6894 else if (player->post_delay_counter > 0)
6896 player->post_delay_counter--;
6900 else if (player->is_bored)
6902 if (player->num_special_action_bored > 0)
6904 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6906 int special_action =
6907 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6908 int special_graphic =
6909 el_act_dir2img(player->element_nr, special_action, move_dir);
6911 player->anim_delay_counter =
6912 graphic_info[special_graphic].anim_delay_fixed +
6913 SimpleRND(graphic_info[special_graphic].anim_delay_random);
6914 player->post_delay_counter =
6915 graphic_info[special_graphic].post_delay_fixed +
6916 SimpleRND(graphic_info[special_graphic].post_delay_random);
6918 player->special_action_bored = special_action;
6921 if (player->anim_delay_counter > 0)
6923 player->action_waiting = player->special_action_bored;
6924 player->anim_delay_counter--;
6926 else if (player->post_delay_counter > 0)
6928 player->post_delay_counter--;
6933 else if (last_waiting) /* waiting -> not waiting */
6935 player->is_waiting = FALSE;
6936 player->is_bored = FALSE;
6937 player->is_sleeping = FALSE;
6939 player->frame_counter_bored = -1;
6940 player->frame_counter_sleeping = -1;
6942 player->anim_delay_counter = 0;
6943 player->post_delay_counter = 0;
6945 player->action_waiting = ACTION_DEFAULT;
6947 player->special_action_bored = ACTION_DEFAULT;
6948 player->special_action_sleeping = ACTION_DEFAULT;
6953 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6956 static byte stored_player_action[MAX_PLAYERS];
6957 static int num_stored_actions = 0;
6959 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6960 int left = player_action & JOY_LEFT;
6961 int right = player_action & JOY_RIGHT;
6962 int up = player_action & JOY_UP;
6963 int down = player_action & JOY_DOWN;
6964 int button1 = player_action & JOY_BUTTON_1;
6965 int button2 = player_action & JOY_BUTTON_2;
6966 int dx = (left ? -1 : right ? 1 : 0);
6967 int dy = (up ? -1 : down ? 1 : 0);
6970 stored_player_action[player->index_nr] = 0;
6971 num_stored_actions++;
6975 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6978 if (!player->active || tape.pausing)
6982 printf("::: [%d %d %d %d] [%d %d]\n",
6983 left, right, up, down, button1, button2);
6989 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6993 snapped = SnapField(player, dx, dy);
6997 dropped = DropElement(player);
6999 moved = MovePlayer(player, dx, dy);
7002 if (tape.single_step && tape.recording && !tape.pausing)
7004 if (button1 || (dropped && !moved))
7006 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7007 SnapField(player, 0, 0); /* stop snapping */
7011 SetPlayerWaiting(player, FALSE);
7014 return player_action;
7016 stored_player_action[player->index_nr] = player_action;
7022 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7025 /* no actions for this player (no input at player's configured device) */
7027 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7028 SnapField(player, 0, 0);
7029 CheckGravityMovement(player);
7031 if (player->MovPos == 0)
7032 SetPlayerWaiting(player, TRUE);
7034 if (player->MovPos == 0) /* needed for tape.playing */
7035 player->is_moving = FALSE;
7037 player->is_dropping = FALSE;
7043 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7045 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7047 TapeRecordAction(stored_player_action);
7048 num_stored_actions = 0;
7055 static void PlayerActions(struct PlayerInfo *player, byte player_action)
7057 static byte stored_player_action[MAX_PLAYERS];
7058 static int num_stored_actions = 0;
7059 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7060 int left = player_action & JOY_LEFT;
7061 int right = player_action & JOY_RIGHT;
7062 int up = player_action & JOY_UP;
7063 int down = player_action & JOY_DOWN;
7064 int button1 = player_action & JOY_BUTTON_1;
7065 int button2 = player_action & JOY_BUTTON_2;
7066 int dx = (left ? -1 : right ? 1 : 0);
7067 int dy = (up ? -1 : down ? 1 : 0);
7069 stored_player_action[player->index_nr] = 0;
7070 num_stored_actions++;
7072 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7074 if (!player->active || tape.pausing)
7079 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7082 snapped = SnapField(player, dx, dy);
7086 dropped = DropElement(player);
7088 moved = MovePlayer(player, dx, dy);
7091 if (tape.single_step && tape.recording && !tape.pausing)
7093 if (button1 || (dropped && !moved))
7095 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7096 SnapField(player, 0, 0); /* stop snapping */
7100 stored_player_action[player->index_nr] = player_action;
7104 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7106 /* no actions for this player (no input at player's configured device) */
7108 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7109 SnapField(player, 0, 0);
7110 CheckGravityMovement(player);
7112 if (player->MovPos == 0)
7113 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
7115 if (player->MovPos == 0) /* needed for tape.playing */
7116 player->is_moving = FALSE;
7119 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7121 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7123 TapeRecordAction(stored_player_action);
7124 num_stored_actions = 0;
7131 static unsigned long action_delay = 0;
7132 unsigned long action_delay_value;
7133 int magic_wall_x = 0, magic_wall_y = 0;
7134 int i, x, y, element, graphic;
7135 byte *recorded_player_action;
7136 byte summarized_player_action = 0;
7138 byte tape_action[MAX_PLAYERS];
7141 if (game_status != GAME_MODE_PLAYING)
7144 action_delay_value =
7145 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7147 if (tape.playing && tape.index_search && !tape.pausing)
7148 action_delay_value = 0;
7150 /* ---------- main game synchronization point ---------- */
7152 WaitUntilDelayReached(&action_delay, action_delay_value);
7154 if (network_playing && !network_player_action_received)
7158 printf("DEBUG: try to get network player actions in time\n");
7162 #if defined(PLATFORM_UNIX)
7163 /* last chance to get network player actions without main loop delay */
7167 if (game_status != GAME_MODE_PLAYING)
7170 if (!network_player_action_received)
7174 printf("DEBUG: failed to get network player actions in time\n");
7185 printf("::: getting new tape action [%d]\n", FrameCounter);
7188 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7190 for (i = 0; i < MAX_PLAYERS; i++)
7192 summarized_player_action |= stored_player[i].action;
7194 if (!network_playing)
7195 stored_player[i].effective_action = stored_player[i].action;
7198 #if defined(PLATFORM_UNIX)
7199 if (network_playing)
7200 SendToServer_MovePlayer(summarized_player_action);
7203 if (!options.network && !setup.team_mode)
7204 local_player->effective_action = summarized_player_action;
7206 for (i = 0; i < MAX_PLAYERS; i++)
7208 int actual_player_action = stored_player[i].effective_action;
7210 if (stored_player[i].programmed_action)
7211 actual_player_action = stored_player[i].programmed_action;
7213 if (recorded_player_action)
7214 actual_player_action = recorded_player_action[i];
7216 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7218 if (tape.recording && tape_action[i] && !tape.player_participates[i])
7219 tape.player_participates[i] = TRUE; /* player just appeared from CE */
7221 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7226 TapeRecordAction(tape_action);
7229 network_player_action_received = FALSE;
7231 ScrollScreen(NULL, SCROLL_GO_ON);
7237 for (i = 0; i < MAX_PLAYERS; i++)
7238 stored_player[i].Frame++;
7242 if (game.engine_version < VERSION_IDENT(2,2,0,7))
7244 for (i = 0; i < MAX_PLAYERS; i++)
7246 struct PlayerInfo *player = &stored_player[i];
7250 if (player->active && player->is_pushing && player->is_moving &&
7253 ContinueMoving(x, y);
7255 /* continue moving after pushing (this is actually a bug) */
7256 if (!IS_MOVING(x, y))
7265 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7267 Changed[x][y] = CE_BITMASK_DEFAULT;
7268 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
7271 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7273 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7274 printf("GameActions(): This should never happen!\n");
7276 ChangePage[x][y] = -1;
7281 if (WasJustMoving[x][y] > 0)
7282 WasJustMoving[x][y]--;
7283 if (WasJustFalling[x][y] > 0)
7284 WasJustFalling[x][y]--;
7289 /* reset finished pushing action (not done in ContinueMoving() to allow
7290 continous pushing animation for elements with zero push delay) */
7291 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7293 ResetGfxAnimation(x, y);
7294 DrawLevelField(x, y);
7299 if (IS_BLOCKED(x, y))
7303 Blocked2Moving(x, y, &oldx, &oldy);
7304 if (!IS_MOVING(oldx, oldy))
7306 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7307 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7308 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7309 printf("GameActions(): This should never happen!\n");
7315 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7317 element = Feld[x][y];
7319 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7321 graphic = el2img(element);
7327 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7329 element = graphic = 0;
7333 if (graphic_info[graphic].anim_global_sync)
7334 GfxFrame[x][y] = FrameCounter;
7336 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7337 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7338 ResetRandomAnimationValue(x, y);
7340 SetRandomAnimationValue(x, y);
7343 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7346 if (IS_INACTIVE(element))
7348 if (IS_ANIMATED(graphic))
7349 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7355 /* this may take place after moving, so 'element' may have changed */
7357 if (IS_CHANGING(x, y))
7359 if (IS_CHANGING(x, y) &&
7360 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7364 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7365 element_info[element].event_page_nr[CE_DELAY]);
7367 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7370 element = Feld[x][y];
7371 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7375 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7380 element = Feld[x][y];
7381 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7383 if (element == EL_MOLE)
7384 printf("::: %d, %d, %d [%d]\n",
7385 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7389 if (element == EL_YAMYAM)
7390 printf("::: %d, %d, %d\n",
7391 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7395 if (IS_ANIMATED(graphic) &&
7399 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7402 if (element == EL_BUG)
7403 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7407 if (element == EL_MOLE)
7408 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7412 if (IS_GEM(element) || element == EL_SP_INFOTRON)
7413 EdelsteinFunkeln(x, y);
7415 else if ((element == EL_ACID ||
7416 element == EL_EXIT_OPEN ||
7417 element == EL_SP_EXIT_OPEN ||
7418 element == EL_SP_TERMINAL ||
7419 element == EL_SP_TERMINAL_ACTIVE ||
7420 element == EL_EXTRA_TIME ||
7421 element == EL_SHIELD_NORMAL ||
7422 element == EL_SHIELD_DEADLY) &&
7423 IS_ANIMATED(graphic))
7424 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7425 else if (IS_MOVING(x, y))
7426 ContinueMoving(x, y);
7427 else if (IS_ACTIVE_BOMB(element))
7428 CheckDynamite(x, y);
7430 else if (element == EL_EXPLOSION && !game.explosions_delayed)
7431 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7433 else if (element == EL_AMOEBA_GROWING)
7434 AmoebeWaechst(x, y);
7435 else if (element == EL_AMOEBA_SHRINKING)
7436 AmoebaDisappearing(x, y);
7438 #if !USE_NEW_AMOEBA_CODE
7439 else if (IS_AMOEBALIVE(element))
7440 AmoebeAbleger(x, y);
7443 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7445 else if (element == EL_EXIT_CLOSED)
7447 else if (element == EL_SP_EXIT_CLOSED)
7449 else if (element == EL_EXPANDABLE_WALL_GROWING)
7451 else if (element == EL_EXPANDABLE_WALL ||
7452 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7453 element == EL_EXPANDABLE_WALL_VERTICAL ||
7454 element == EL_EXPANDABLE_WALL_ANY)
7456 else if (element == EL_FLAMES)
7457 CheckForDragon(x, y);
7459 else if (IS_AUTO_CHANGING(element))
7460 ChangeElement(x, y);
7462 else if (element == EL_EXPLOSION)
7463 ; /* drawing of correct explosion animation is handled separately */
7464 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7465 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7468 /* this may take place after moving, so 'element' may have changed */
7469 if (IS_AUTO_CHANGING(Feld[x][y]))
7470 ChangeElement(x, y);
7473 if (IS_BELT_ACTIVE(element))
7474 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7476 if (game.magic_wall_active)
7478 int jx = local_player->jx, jy = local_player->jy;
7480 /* play the element sound at the position nearest to the player */
7481 if ((element == EL_MAGIC_WALL_FULL ||
7482 element == EL_MAGIC_WALL_ACTIVE ||
7483 element == EL_MAGIC_WALL_EMPTYING ||
7484 element == EL_BD_MAGIC_WALL_FULL ||
7485 element == EL_BD_MAGIC_WALL_ACTIVE ||
7486 element == EL_BD_MAGIC_WALL_EMPTYING) &&
7487 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7495 #if USE_NEW_AMOEBA_CODE
7496 /* new experimental amoeba growth stuff */
7498 if (!(FrameCounter % 8))
7501 static unsigned long random = 1684108901;
7503 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7506 x = (random >> 10) % lev_fieldx;
7507 y = (random >> 20) % lev_fieldy;
7509 x = RND(lev_fieldx);
7510 y = RND(lev_fieldy);
7512 element = Feld[x][y];
7514 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7515 if (!IS_PLAYER(x,y) &&
7516 (element == EL_EMPTY ||
7517 element == EL_SAND ||
7518 element == EL_QUICKSAND_EMPTY ||
7519 element == EL_ACID_SPLASH_LEFT ||
7520 element == EL_ACID_SPLASH_RIGHT))
7522 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7523 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7524 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7525 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7526 Feld[x][y] = EL_AMOEBA_DROP;
7529 random = random * 129 + 1;
7535 if (game.explosions_delayed)
7538 game.explosions_delayed = FALSE;
7540 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7542 element = Feld[x][y];
7544 if (ExplodeField[x][y])
7545 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7546 else if (element == EL_EXPLOSION)
7547 Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7549 ExplodeField[x][y] = EX_NO_EXPLOSION;
7552 game.explosions_delayed = TRUE;
7555 if (game.magic_wall_active)
7557 if (!(game.magic_wall_time_left % 4))
7559 int element = Feld[magic_wall_x][magic_wall_y];
7561 if (element == EL_BD_MAGIC_WALL_FULL ||
7562 element == EL_BD_MAGIC_WALL_ACTIVE ||
7563 element == EL_BD_MAGIC_WALL_EMPTYING)
7564 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7566 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7569 if (game.magic_wall_time_left > 0)
7571 game.magic_wall_time_left--;
7572 if (!game.magic_wall_time_left)
7574 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7576 element = Feld[x][y];
7578 if (element == EL_MAGIC_WALL_ACTIVE ||
7579 element == EL_MAGIC_WALL_FULL)
7581 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7582 DrawLevelField(x, y);
7584 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7585 element == EL_BD_MAGIC_WALL_FULL)
7587 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7588 DrawLevelField(x, y);
7592 game.magic_wall_active = FALSE;
7597 if (game.light_time_left > 0)
7599 game.light_time_left--;
7601 if (game.light_time_left == 0)
7602 RedrawAllLightSwitchesAndInvisibleElements();
7605 if (game.timegate_time_left > 0)
7607 game.timegate_time_left--;
7609 if (game.timegate_time_left == 0)
7610 CloseAllOpenTimegates();
7613 for (i = 0; i < MAX_PLAYERS; i++)
7615 struct PlayerInfo *player = &stored_player[i];
7617 if (SHIELD_ON(player))
7619 if (player->shield_deadly_time_left)
7620 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7621 else if (player->shield_normal_time_left)
7622 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7626 if (TimeFrames >= FRAMES_PER_SECOND)
7631 for (i = 0; i < MAX_PLAYERS; i++)
7633 struct PlayerInfo *player = &stored_player[i];
7635 if (SHIELD_ON(player))
7637 player->shield_normal_time_left--;
7639 if (player->shield_deadly_time_left > 0)
7640 player->shield_deadly_time_left--;
7644 if (tape.recording || tape.playing)
7645 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7651 if (TimeLeft <= 10 && setup.time_limit)
7652 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7654 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7656 if (!TimeLeft && setup.time_limit)
7657 for (i = 0; i < MAX_PLAYERS; i++)
7658 KillHero(&stored_player[i]);
7660 else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7661 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7665 PlayAllPlayersSound();
7667 if (options.debug) /* calculate frames per second */
7669 static unsigned long fps_counter = 0;
7670 static int fps_frames = 0;
7671 unsigned long fps_delay_ms = Counter() - fps_counter;
7675 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
7677 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7680 fps_counter = Counter();
7683 redraw_mask |= REDRAW_FPS;
7687 if (stored_player[0].jx != stored_player[0].last_jx ||
7688 stored_player[0].jy != stored_player[0].last_jy)
7689 printf("::: %d, %d, %d, %d, %d\n",
7690 stored_player[0].MovDir,
7691 stored_player[0].MovPos,
7692 stored_player[0].GfxPos,
7693 stored_player[0].Frame,
7694 stored_player[0].StepFrame);
7701 for (i = 0; i < MAX_PLAYERS; i++)
7704 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
7706 stored_player[i].Frame += move_frames;
7708 if (stored_player[i].MovPos != 0)
7709 stored_player[i].StepFrame += move_frames;
7711 if (stored_player[i].drop_delay > 0)
7712 stored_player[i].drop_delay--;
7717 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7719 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7721 local_player->show_envelope = 0;
7726 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7728 int min_x = x, min_y = y, max_x = x, max_y = y;
7731 for (i = 0; i < MAX_PLAYERS; i++)
7733 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7735 if (!stored_player[i].active || &stored_player[i] == player)
7738 min_x = MIN(min_x, jx);
7739 min_y = MIN(min_y, jy);
7740 max_x = MAX(max_x, jx);
7741 max_y = MAX(max_y, jy);
7744 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7747 static boolean AllPlayersInVisibleScreen()
7751 for (i = 0; i < MAX_PLAYERS; i++)
7753 int jx = stored_player[i].jx, jy = stored_player[i].jy;
7755 if (!stored_player[i].active)
7758 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7765 void ScrollLevel(int dx, int dy)
7767 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7770 BlitBitmap(drawto_field, drawto_field,
7771 FX + TILEX * (dx == -1) - softscroll_offset,
7772 FY + TILEY * (dy == -1) - softscroll_offset,
7773 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7774 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7775 FX + TILEX * (dx == 1) - softscroll_offset,
7776 FY + TILEY * (dy == 1) - softscroll_offset);
7780 x = (dx == 1 ? BX1 : BX2);
7781 for (y = BY1; y <= BY2; y++)
7782 DrawScreenField(x, y);
7787 y = (dy == 1 ? BY1 : BY2);
7788 for (x = BX1; x <= BX2; x++)
7789 DrawScreenField(x, y);
7792 redraw_mask |= REDRAW_FIELD;
7795 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
7797 int nextx = x + dx, nexty = y + dy;
7798 int element = Feld[x][y];
7801 element != EL_SP_PORT_LEFT &&
7802 element != EL_SP_GRAVITY_PORT_LEFT &&
7803 element != EL_SP_PORT_HORIZONTAL &&
7804 element != EL_SP_PORT_ANY) ||
7806 element != EL_SP_PORT_RIGHT &&
7807 element != EL_SP_GRAVITY_PORT_RIGHT &&
7808 element != EL_SP_PORT_HORIZONTAL &&
7809 element != EL_SP_PORT_ANY) ||
7811 element != EL_SP_PORT_UP &&
7812 element != EL_SP_GRAVITY_PORT_UP &&
7813 element != EL_SP_PORT_VERTICAL &&
7814 element != EL_SP_PORT_ANY) ||
7816 element != EL_SP_PORT_DOWN &&
7817 element != EL_SP_GRAVITY_PORT_DOWN &&
7818 element != EL_SP_PORT_VERTICAL &&
7819 element != EL_SP_PORT_ANY) ||
7820 !IN_LEV_FIELD(nextx, nexty) ||
7821 !IS_FREE(nextx, nexty))
7827 static void CheckGravityMovement(struct PlayerInfo *player)
7829 if (game.gravity && !player->programmed_action)
7831 int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7832 int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7834 (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7835 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7836 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7837 int jx = player->jx, jy = player->jy;
7838 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7839 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7840 int new_jx = jx + dx, new_jy = jy + dy;
7841 boolean field_under_player_is_free =
7842 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7843 boolean player_is_moving_to_valid_field =
7844 (IN_LEV_FIELD(new_jx, new_jy) &&
7845 (Feld[new_jx][new_jy] == EL_SP_BASE ||
7846 Feld[new_jx][new_jy] == EL_SAND ||
7847 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
7848 canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
7849 /* !!! extend EL_SAND to anything diggable !!! */
7851 boolean player_is_standing_on_valid_field =
7852 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
7853 (IS_WALKABLE(Feld[jx][jy]) &&
7854 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
7856 if (field_under_player_is_free &&
7857 !player_is_standing_on_valid_field &&
7858 !player_is_moving_to_valid_field)
7859 player->programmed_action = MV_DOWN;
7865 -----------------------------------------------------------------------------
7866 dx, dy: direction (non-diagonal) to try to move the player to
7867 real_dx, real_dy: direction as read from input device (can be diagonal)
7870 boolean MovePlayerOneStep(struct PlayerInfo *player,
7871 int dx, int dy, int real_dx, int real_dy)
7874 static int change_sides[4][2] =
7876 /* enter side leave side */
7877 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
7878 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
7879 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
7880 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
7882 int move_direction = (dx == -1 ? MV_LEFT :
7883 dx == +1 ? MV_RIGHT :
7885 dy == +1 ? MV_DOWN : MV_NO_MOVING);
7886 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7887 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7889 int jx = player->jx, jy = player->jy;
7890 int new_jx = jx + dx, new_jy = jy + dy;
7894 if (!player->active || (!dx && !dy))
7895 return MF_NO_ACTION;
7897 player->MovDir = (dx < 0 ? MV_LEFT :
7900 dy > 0 ? MV_DOWN : MV_NO_MOVING);
7902 if (!IN_LEV_FIELD(new_jx, new_jy))
7903 return MF_NO_ACTION;
7905 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7906 return MF_NO_ACTION;
7909 element = MovingOrBlocked2Element(new_jx, new_jy);
7911 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7914 if (DONT_RUN_INTO(element))
7916 if (element == EL_ACID && dx == 0 && dy == 1)
7919 Feld[jx][jy] = EL_PLAYER_1;
7920 InitMovingField(jx, jy, MV_DOWN);
7921 Store[jx][jy] = EL_ACID;
7922 ContinueMoving(jx, jy);
7926 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7931 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
7932 if (can_move != MF_MOVING)
7935 /* check if DigField() has caused relocation of the player */
7936 if (player->jx != jx || player->jy != jy)
7937 return MF_NO_ACTION;
7939 StorePlayer[jx][jy] = 0;
7940 player->last_jx = jx;
7941 player->last_jy = jy;
7942 player->jx = new_jx;
7943 player->jy = new_jy;
7944 StorePlayer[new_jx][new_jy] = player->element_nr;
7947 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7949 player->step_counter++;
7951 player->drop_delay = 0;
7953 PlayerVisit[jx][jy] = FrameCounter;
7955 ScrollPlayer(player, SCROLL_INIT);
7958 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7960 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7961 CE_OTHER_GETS_LEFT);
7962 CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7963 CE_LEFT_BY_PLAYER, -1);
7966 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7968 CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7969 enter_side, CE_OTHER_GETS_ENTERED);
7970 CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7971 CE_ENTERED_BY_PLAYER, -1);
7978 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7980 int jx = player->jx, jy = player->jy;
7981 int old_jx = jx, old_jy = jy;
7982 int moved = MF_NO_ACTION;
7985 if (!player->active)
7990 if (player->MovPos == 0)
7992 player->is_moving = FALSE;
7993 player->is_digging = FALSE;
7994 player->is_collecting = FALSE;
7995 player->is_snapping = FALSE;
7996 player->is_pushing = FALSE;
8002 if (!player->active || (!dx && !dy))
8007 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8011 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8012 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
8016 /* remove the last programmed player action */
8017 player->programmed_action = 0;
8021 /* should only happen if pre-1.2 tape recordings are played */
8022 /* this is only for backward compatibility */
8024 int original_move_delay_value = player->move_delay_value;
8027 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8031 /* scroll remaining steps with finest movement resolution */
8032 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8034 while (player->MovPos)
8036 ScrollPlayer(player, SCROLL_GO_ON);
8037 ScrollScreen(NULL, SCROLL_GO_ON);
8043 player->move_delay_value = original_move_delay_value;
8046 if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
8048 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8049 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8053 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8054 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8060 if (moved & MF_MOVING && !ScreenMovPos &&
8061 (player == local_player || !options.network))
8063 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8064 int offset = (setup.scroll_delay ? 3 : 0);
8066 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8068 /* actual player has left the screen -- scroll in that direction */
8069 if (jx != old_jx) /* player has moved horizontally */
8070 scroll_x += (jx - old_jx);
8071 else /* player has moved vertically */
8072 scroll_y += (jy - old_jy);
8076 if (jx != old_jx) /* player has moved horizontally */
8078 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8079 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8080 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8082 /* don't scroll over playfield boundaries */
8083 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8084 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8086 /* don't scroll more than one field at a time */
8087 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8089 /* don't scroll against the player's moving direction */
8090 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8091 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8092 scroll_x = old_scroll_x;
8094 else /* player has moved vertically */
8096 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8097 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8098 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8100 /* don't scroll over playfield boundaries */
8101 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8102 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8104 /* don't scroll more than one field at a time */
8105 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8107 /* don't scroll against the player's moving direction */
8108 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8109 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8110 scroll_y = old_scroll_y;
8114 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8116 if (!options.network && !AllPlayersInVisibleScreen())
8118 scroll_x = old_scroll_x;
8119 scroll_y = old_scroll_y;
8123 ScrollScreen(player, SCROLL_INIT);
8124 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8131 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
8133 if (!(moved & MF_MOVING) && !player->is_pushing)
8138 player->StepFrame = 0;
8140 if (moved & MF_MOVING)
8142 if (old_jx != jx && old_jy == jy)
8143 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8144 else if (old_jx == jx && old_jy != jy)
8145 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8147 DrawLevelField(jx, jy); /* for "crumbled sand" */
8149 player->last_move_dir = player->MovDir;
8150 player->is_moving = TRUE;
8152 player->is_snapping = FALSE;
8156 player->is_switching = FALSE;
8159 player->is_dropping = FALSE;
8164 static int change_sides[4][2] =
8166 /* enter side leave side */
8167 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
8168 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
8169 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
8170 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
8172 int move_direction = player->MovDir;
8173 int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
8174 int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
8177 if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
8179 CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
8180 leave_side, CE_OTHER_GETS_LEFT);
8181 CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
8182 leave_side, CE_LEFT_BY_PLAYER, -1);
8185 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8187 CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
8188 enter_side, CE_OTHER_GETS_ENTERED);
8189 CheckElementSideChange(jx, jy, Feld[jx][jy],
8190 enter_side, CE_ENTERED_BY_PLAYER, -1);
8201 CheckGravityMovement(player);
8204 player->last_move_dir = MV_NO_MOVING;
8206 player->is_moving = FALSE;
8209 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8211 TestIfHeroTouchesBadThing(jx, jy);
8212 TestIfPlayerTouchesCustomElement(jx, jy);
8215 if (!player->active)
8221 void ScrollPlayer(struct PlayerInfo *player, int mode)
8223 int jx = player->jx, jy = player->jy;
8224 int last_jx = player->last_jx, last_jy = player->last_jy;
8225 int move_stepsize = TILEX / player->move_delay_value;
8227 if (!player->active || !player->MovPos)
8230 if (mode == SCROLL_INIT)
8232 player->actual_frame_counter = FrameCounter;
8233 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8235 if (Feld[last_jx][last_jy] == EL_EMPTY)
8236 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8244 else if (!FrameReached(&player->actual_frame_counter, 1))
8247 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8248 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8250 if (!player->block_last_field &&
8251 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8252 Feld[last_jx][last_jy] = EL_EMPTY;
8254 /* before DrawPlayer() to draw correct player graphic for this case */
8255 if (player->MovPos == 0)
8256 CheckGravityMovement(player);
8259 DrawPlayer(player); /* needed here only to cleanup last field */
8262 if (player->MovPos == 0) /* player reached destination field */
8265 if (player->move_delay_reset_counter > 0)
8267 player->move_delay_reset_counter--;
8269 if (player->move_delay_reset_counter == 0)
8271 /* continue with normal speed after quickly moving through gate */
8272 HALVE_PLAYER_SPEED(player);
8274 /* be able to make the next move without delay */
8275 player->move_delay = 0;
8279 if (IS_PASSABLE(Feld[last_jx][last_jy]))
8281 /* continue with normal speed after quickly moving through gate */
8282 HALVE_PLAYER_SPEED(player);
8284 /* be able to make the next move without delay */
8285 player->move_delay = 0;
8289 if (player->block_last_field &&
8290 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8291 Feld[last_jx][last_jy] = EL_EMPTY;
8293 player->last_jx = jx;
8294 player->last_jy = jy;
8296 if (Feld[jx][jy] == EL_EXIT_OPEN ||
8297 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8298 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
8300 DrawPlayer(player); /* needed here only to cleanup last field */
8303 if (local_player->friends_still_needed == 0 ||
8304 IS_SP_ELEMENT(Feld[jx][jy]))
8305 player->LevelSolved = player->GameOver = TRUE;
8308 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8310 TestIfHeroTouchesBadThing(jx, jy);
8311 TestIfPlayerTouchesCustomElement(jx, jy);
8313 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
8316 if (!player->active)
8320 if (tape.single_step && tape.recording && !tape.pausing &&
8321 !player->programmed_action)
8322 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8326 void ScrollScreen(struct PlayerInfo *player, int mode)
8328 static unsigned long screen_frame_counter = 0;
8330 if (mode == SCROLL_INIT)
8332 /* set scrolling step size according to actual player's moving speed */
8333 ScrollStepSize = TILEX / player->move_delay_value;
8335 screen_frame_counter = FrameCounter;
8336 ScreenMovDir = player->MovDir;
8337 ScreenMovPos = player->MovPos;
8338 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8341 else if (!FrameReached(&screen_frame_counter, 1))
8346 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8347 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8348 redraw_mask |= REDRAW_FIELD;
8351 ScreenMovDir = MV_NO_MOVING;
8354 void TestIfPlayerTouchesCustomElement(int x, int y)
8356 static int xy[4][2] =
8363 static int change_sides[4][2] =
8365 /* center side border side */
8366 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8367 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8368 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8369 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8371 static int touch_dir[4] =
8378 int center_element = Feld[x][y]; /* should always be non-moving! */
8381 for (i = 0; i < 4; i++)
8383 int xx = x + xy[i][0];
8384 int yy = y + xy[i][1];
8385 int center_side = change_sides[i][0];
8386 int border_side = change_sides[i][1];
8389 if (!IN_LEV_FIELD(xx, yy))
8392 if (IS_PLAYER(x, y))
8394 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8395 border_element = Feld[xx][yy]; /* may be moving! */
8396 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8397 border_element = Feld[xx][yy];
8398 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8399 border_element = MovingOrBlocked2Element(xx, yy);
8401 continue; /* center and border element do not touch */
8403 CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
8404 CE_OTHER_GETS_TOUCHED);
8405 CheckElementSideChange(xx, yy, border_element, border_side,
8406 CE_TOUCHED_BY_PLAYER, -1);
8408 else if (IS_PLAYER(xx, yy))
8410 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8412 struct PlayerInfo *player = PLAYERINFO(xx, yy);
8414 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8415 continue; /* center and border element do not touch */
8418 CheckTriggeredElementSideChange(x, y, center_element, center_side,
8419 CE_OTHER_GETS_TOUCHED);
8420 CheckElementSideChange(x, y, center_element, center_side,
8421 CE_TOUCHED_BY_PLAYER, -1);
8428 void TestIfElementTouchesCustomElement(int x, int y)
8430 static int xy[4][2] =
8437 static int change_sides[4][2] =
8439 /* center side border side */
8440 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
8441 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
8442 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
8443 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
8445 static int touch_dir[4] =
8452 boolean change_center_element = FALSE;
8453 int center_element_change_page = 0;
8454 int center_element = Feld[x][y]; /* should always be non-moving! */
8457 for (i = 0; i < 4; i++)
8459 int xx = x + xy[i][0];
8460 int yy = y + xy[i][1];
8461 int center_side = change_sides[i][0];
8462 int border_side = change_sides[i][1];
8465 if (!IN_LEV_FIELD(xx, yy))
8468 if (game.engine_version < VERSION_IDENT(3,0,7,0))
8469 border_element = Feld[xx][yy]; /* may be moving! */
8470 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8471 border_element = Feld[xx][yy];
8472 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
8473 border_element = MovingOrBlocked2Element(xx, yy);
8475 continue; /* center and border element do not touch */
8477 /* check for change of center element (but change it only once) */
8478 if (IS_CUSTOM_ELEMENT(center_element) &&
8479 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8480 !change_center_element)
8482 for (j = 0; j < element_info[center_element].num_change_pages; j++)
8484 struct ElementChangeInfo *change =
8485 &element_info[center_element].change_page[j];
8487 if (change->can_change &&
8488 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8489 change->sides & border_side &&
8491 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
8493 change->trigger_element == border_element
8497 change_center_element = TRUE;
8498 center_element_change_page = j;
8505 /* check for change of border element */
8506 if (IS_CUSTOM_ELEMENT(border_element) &&
8507 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8509 for (j = 0; j < element_info[border_element].num_change_pages; j++)
8511 struct ElementChangeInfo *change =
8512 &element_info[border_element].change_page[j];
8514 if (change->can_change &&
8515 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8516 change->sides & center_side &&
8518 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
8520 change->trigger_element == center_element
8524 CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
8525 CE_OTHER_IS_TOUCHING, j);
8532 if (change_center_element)
8533 CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
8534 CE_OTHER_IS_TOUCHING, center_element_change_page);
8537 void TestIfElementHitsCustomElement(int x, int y, int direction)
8539 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8540 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8541 int hitx = x + dx, hity = y + dy;
8542 int hitting_element = Feld[x][y];
8544 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8545 !IS_FREE(hitx, hity) &&
8546 (!IS_MOVING(hitx, hity) ||
8547 MovDir[hitx][hity] != direction ||
8548 ABS(MovPos[hitx][hity]) <= TILEY / 2));
8551 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8555 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8559 CheckElementSideChange(x, y, hitting_element,
8560 direction, CE_HITTING_SOMETHING, -1);
8562 if (IN_LEV_FIELD(hitx, hity))
8564 int opposite_direction = MV_DIR_OPPOSITE(direction);
8565 int hitting_side = direction;
8566 int touched_side = opposite_direction;
8567 int touched_element = MovingOrBlocked2Element(hitx, hity);
8569 boolean object_hit = (!IS_MOVING(hitx, hity) ||
8570 MovDir[hitx][hity] != direction ||
8571 ABS(MovPos[hitx][hity]) <= TILEY / 2);
8580 CheckElementSideChange(hitx, hity, touched_element,
8581 opposite_direction, CE_HIT_BY_SOMETHING, -1);
8583 if (IS_CUSTOM_ELEMENT(hitting_element) &&
8584 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8586 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8588 struct ElementChangeInfo *change =
8589 &element_info[hitting_element].change_page[i];
8591 if (change->can_change &&
8592 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8593 change->sides & touched_side &&
8596 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
8598 change->trigger_element == touched_element
8602 CheckElementSideChange(x, y, hitting_element,
8603 CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8609 if (IS_CUSTOM_ELEMENT(touched_element) &&
8610 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8612 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8614 struct ElementChangeInfo *change =
8615 &element_info[touched_element].change_page[i];
8617 if (change->can_change &&
8618 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8619 change->sides & hitting_side &&
8621 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
8623 change->trigger_element == hitting_element
8627 CheckElementSideChange(hitx, hity, touched_element,
8628 CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8637 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8639 int i, kill_x = -1, kill_y = -1;
8640 static int test_xy[4][2] =
8647 static int test_dir[4] =
8655 for (i = 0; i < 4; i++)
8657 int test_x, test_y, test_move_dir, test_element;
8659 test_x = good_x + test_xy[i][0];
8660 test_y = good_y + test_xy[i][1];
8661 if (!IN_LEV_FIELD(test_x, test_y))
8665 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8668 test_element = Feld[test_x][test_y];
8670 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8673 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8674 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8676 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8677 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
8685 if (kill_x != -1 || kill_y != -1)
8687 if (IS_PLAYER(good_x, good_y))
8689 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8691 if (player->shield_deadly_time_left > 0)
8692 Bang(kill_x, kill_y);
8693 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
8697 Bang(good_x, good_y);
8701 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8703 int i, kill_x = -1, kill_y = -1;
8704 int bad_element = Feld[bad_x][bad_y];
8705 static int test_xy[4][2] =
8712 static int touch_dir[4] =
8719 static int test_dir[4] =
8727 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
8730 for (i = 0; i < 4; i++)
8732 int test_x, test_y, test_move_dir, test_element;
8734 test_x = bad_x + test_xy[i][0];
8735 test_y = bad_y + test_xy[i][1];
8736 if (!IN_LEV_FIELD(test_x, test_y))
8740 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8742 test_element = Feld[test_x][test_y];
8744 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8745 2nd case: DONT_TOUCH style bad thing does not move away from good thing
8747 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
8748 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
8750 /* good thing is player or penguin that does not move away */
8751 if (IS_PLAYER(test_x, test_y))
8753 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8755 if (bad_element == EL_ROBOT && player->is_moving)
8756 continue; /* robot does not kill player if he is moving */
8758 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8760 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8761 continue; /* center and border element do not touch */
8768 else if (test_element == EL_PENGUIN)
8777 if (kill_x != -1 || kill_y != -1)
8779 if (IS_PLAYER(kill_x, kill_y))
8781 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8783 if (player->shield_deadly_time_left > 0)
8785 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
8789 Bang(kill_x, kill_y);
8793 void TestIfHeroTouchesBadThing(int x, int y)
8795 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8798 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8800 TestIfGoodThingHitsBadThing(x, y, move_dir);
8803 void TestIfBadThingTouchesHero(int x, int y)
8805 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8808 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8810 TestIfBadThingHitsGoodThing(x, y, move_dir);
8813 void TestIfFriendTouchesBadThing(int x, int y)
8815 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8818 void TestIfBadThingTouchesFriend(int x, int y)
8820 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8823 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8825 int i, kill_x = bad_x, kill_y = bad_y;
8826 static int xy[4][2] =
8834 for (i = 0; i < 4; i++)
8838 x = bad_x + xy[i][0];
8839 y = bad_y + xy[i][1];
8840 if (!IN_LEV_FIELD(x, y))
8843 element = Feld[x][y];
8844 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8845 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8853 if (kill_x != bad_x || kill_y != bad_y)
8857 void KillHero(struct PlayerInfo *player)
8859 int jx = player->jx, jy = player->jy;
8861 if (!player->active)
8864 /* remove accessible field at the player's position */
8865 Feld[jx][jy] = EL_EMPTY;
8867 /* deactivate shield (else Bang()/Explode() would not work right) */
8868 player->shield_normal_time_left = 0;
8869 player->shield_deadly_time_left = 0;
8875 static void KillHeroUnlessEnemyProtected(int x, int y)
8877 if (!PLAYER_ENEMY_PROTECTED(x, y))
8878 KillHero(PLAYERINFO(x, y));
8881 static void KillHeroUnlessExplosionProtected(int x, int y)
8883 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
8884 KillHero(PLAYERINFO(x, y));
8887 void BuryHero(struct PlayerInfo *player)
8889 int jx = player->jx, jy = player->jy;
8891 if (!player->active)
8895 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8897 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8899 PlayLevelSound(jx, jy, SND_GAME_LOSING);
8901 player->GameOver = TRUE;
8905 void RemoveHero(struct PlayerInfo *player)
8907 int jx = player->jx, jy = player->jy;
8908 int i, found = FALSE;
8910 player->present = FALSE;
8911 player->active = FALSE;
8913 if (!ExplodeField[jx][jy])
8914 StorePlayer[jx][jy] = 0;
8916 for (i = 0; i < MAX_PLAYERS; i++)
8917 if (stored_player[i].active)
8921 AllPlayersGone = TRUE;
8928 =============================================================================
8929 checkDiagonalPushing()
8930 -----------------------------------------------------------------------------
8931 check if diagonal input device direction results in pushing of object
8932 (by checking if the alternative direction is walkable, diggable, ...)
8933 =============================================================================
8936 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8937 int x, int y, int real_dx, int real_dy)
8939 int jx, jy, dx, dy, xx, yy;
8941 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
8944 /* diagonal direction: check alternative direction */
8949 xx = jx + (dx == 0 ? real_dx : 0);
8950 yy = jy + (dy == 0 ? real_dy : 0);
8952 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8956 =============================================================================
8958 -----------------------------------------------------------------------------
8959 x, y: field next to player (non-diagonal) to try to dig to
8960 real_dx, real_dy: direction as read from input device (can be diagonal)
8961 =============================================================================
8964 int DigField(struct PlayerInfo *player,
8965 int oldx, int oldy, int x, int y,
8966 int real_dx, int real_dy, int mode)
8968 static int change_sides[4] =
8970 CH_SIDE_RIGHT, /* moving left */
8971 CH_SIDE_LEFT, /* moving right */
8972 CH_SIDE_BOTTOM, /* moving up */
8973 CH_SIDE_TOP, /* moving down */
8975 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8976 int jx = oldx, jy = oldy;
8977 int dx = x - jx, dy = y - jy;
8978 int nextx = x + dx, nexty = y + dy;
8979 int move_direction = (dx == -1 ? MV_LEFT :
8980 dx == +1 ? MV_RIGHT :
8982 dy == +1 ? MV_DOWN : MV_NO_MOVING);
8983 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
8984 int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8985 int old_element = Feld[jx][jy];
8988 if (player->MovPos == 0)
8990 player->is_digging = FALSE;
8991 player->is_collecting = FALSE;
8994 if (player->MovPos == 0) /* last pushing move finished */
8995 player->is_pushing = FALSE;
8997 if (mode == DF_NO_PUSH) /* player just stopped pushing */
8999 player->is_switching = FALSE;
9000 player->push_delay = 0;
9002 return MF_NO_ACTION;
9005 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9006 return MF_NO_ACTION;
9011 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
9013 if (IS_TUBE(Feld[jx][jy]) ||
9014 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
9018 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
9019 int tube_leave_directions[][2] =
9021 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9022 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
9023 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
9024 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
9025 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
9026 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
9027 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
9028 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
9029 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
9030 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
9031 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
9032 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
9035 while (tube_leave_directions[i][0] != tube_element)
9038 if (tube_leave_directions[i][0] == -1) /* should not happen */
9042 if (!(tube_leave_directions[i][1] & move_direction))
9043 return MF_NO_ACTION; /* tube has no opening in this direction */
9048 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9049 old_element = Back[jx][jy];
9053 if (IS_WALKABLE(old_element) &&
9054 !(element_info[old_element].access_direction & move_direction))
9055 return MF_NO_ACTION; /* field has no opening in this direction */
9057 element = Feld[x][y];
9059 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9060 game.engine_version >= VERSION_IDENT(2,2,0,0))
9061 return MF_NO_ACTION;
9065 case EL_SP_PORT_LEFT:
9066 case EL_SP_PORT_RIGHT:
9068 case EL_SP_PORT_DOWN:
9069 case EL_SP_PORT_HORIZONTAL:
9070 case EL_SP_PORT_VERTICAL:
9071 case EL_SP_PORT_ANY:
9072 case EL_SP_GRAVITY_PORT_LEFT:
9073 case EL_SP_GRAVITY_PORT_RIGHT:
9074 case EL_SP_GRAVITY_PORT_UP:
9075 case EL_SP_GRAVITY_PORT_DOWN:
9077 if (!canEnterSupaplexPort(x, y, dx, dy))
9078 return MF_NO_ACTION;
9081 element != EL_SP_PORT_LEFT &&
9082 element != EL_SP_GRAVITY_PORT_LEFT &&
9083 element != EL_SP_PORT_HORIZONTAL &&
9084 element != EL_SP_PORT_ANY) ||
9086 element != EL_SP_PORT_RIGHT &&
9087 element != EL_SP_GRAVITY_PORT_RIGHT &&
9088 element != EL_SP_PORT_HORIZONTAL &&
9089 element != EL_SP_PORT_ANY) ||
9091 element != EL_SP_PORT_UP &&
9092 element != EL_SP_GRAVITY_PORT_UP &&
9093 element != EL_SP_PORT_VERTICAL &&
9094 element != EL_SP_PORT_ANY) ||
9096 element != EL_SP_PORT_DOWN &&
9097 element != EL_SP_GRAVITY_PORT_DOWN &&
9098 element != EL_SP_PORT_VERTICAL &&
9099 element != EL_SP_PORT_ANY) ||
9100 !IN_LEV_FIELD(nextx, nexty) ||
9101 !IS_FREE(nextx, nexty))
9102 return MF_NO_ACTION;
9105 if (element == EL_SP_GRAVITY_PORT_LEFT ||
9106 element == EL_SP_GRAVITY_PORT_RIGHT ||
9107 element == EL_SP_GRAVITY_PORT_UP ||
9108 element == EL_SP_GRAVITY_PORT_DOWN)
9109 game.gravity = !game.gravity;
9111 /* automatically move to the next field with double speed */
9112 player->programmed_action = move_direction;
9114 if (player->move_delay_reset_counter == 0)
9116 player->move_delay_reset_counter = 2; /* two double speed steps */
9118 DOUBLE_PLAYER_SPEED(player);
9121 player->move_delay_reset_counter = 2;
9123 DOUBLE_PLAYER_SPEED(player);
9126 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
9131 case EL_TUBE_VERTICAL:
9132 case EL_TUBE_HORIZONTAL:
9133 case EL_TUBE_VERTICAL_LEFT:
9134 case EL_TUBE_VERTICAL_RIGHT:
9135 case EL_TUBE_HORIZONTAL_UP:
9136 case EL_TUBE_HORIZONTAL_DOWN:
9137 case EL_TUBE_LEFT_UP:
9138 case EL_TUBE_LEFT_DOWN:
9139 case EL_TUBE_RIGHT_UP:
9140 case EL_TUBE_RIGHT_DOWN:
9143 int tube_enter_directions[][2] =
9145 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9146 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
9147 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
9148 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
9149 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
9150 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
9151 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
9152 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
9153 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
9154 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
9155 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
9156 { -1, MV_NO_MOVING }
9159 while (tube_enter_directions[i][0] != element)
9162 if (tube_enter_directions[i][0] == -1) /* should not happen */
9166 if (!(tube_enter_directions[i][1] & move_direction))
9167 return MF_NO_ACTION; /* tube has no opening in this direction */
9169 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
9176 if (IS_WALKABLE(element))
9178 int sound_action = ACTION_WALKING;
9180 if (!(element_info[element].access_direction & opposite_direction))
9181 return MF_NO_ACTION; /* field not accessible from this direction */
9183 if (element >= EL_GATE_1 && element <= EL_GATE_4)
9185 if (!player->key[element - EL_GATE_1])
9186 return MF_NO_ACTION;
9188 else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
9190 if (!player->key[element - EL_GATE_1_GRAY])
9191 return MF_NO_ACTION;
9193 else if (element == EL_EXIT_OPEN ||
9194 element == EL_SP_EXIT_OPEN ||
9195 element == EL_SP_EXIT_OPENING)
9197 sound_action = ACTION_PASSING; /* player is passing exit */
9199 else if (element == EL_EMPTY)
9201 sound_action = ACTION_MOVING; /* nothing to walk on */
9204 /* play sound from background or player, whatever is available */
9205 if (element_info[element].sound[sound_action] != SND_UNDEFINED)
9206 PlayLevelSoundElementAction(x, y, element, sound_action);
9208 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9212 else if (IS_PASSABLE(element))
9214 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
9215 return MF_NO_ACTION;
9217 if (IS_CUSTOM_ELEMENT(element) &&
9218 !(element_info[element].access_direction & opposite_direction))
9219 return MF_NO_ACTION; /* field not accessible from this direction */
9222 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
9223 return MF_NO_ACTION;
9226 if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
9228 if (!player->key[element - EL_EM_GATE_1])
9229 return MF_NO_ACTION;
9231 else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
9233 if (!player->key[element - EL_EM_GATE_1_GRAY])
9234 return MF_NO_ACTION;
9237 /* automatically move to the next field with double speed */
9238 player->programmed_action = move_direction;
9240 if (player->move_delay_reset_counter == 0)
9242 player->move_delay_reset_counter = 2; /* two double speed steps */
9244 DOUBLE_PLAYER_SPEED(player);
9247 player->move_delay_reset_counter = 2;
9249 DOUBLE_PLAYER_SPEED(player);
9252 PlayLevelSoundAction(x, y, ACTION_PASSING);
9256 else if (IS_DIGGABLE(element))
9260 if (mode != DF_SNAP)
9263 GfxElement[x][y] = GFX_ELEMENT(element);
9266 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
9268 player->is_digging = TRUE;
9271 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9273 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
9276 if (mode == DF_SNAP)
9277 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9282 else if (IS_COLLECTIBLE(element))
9286 if (mode != DF_SNAP)
9288 GfxElement[x][y] = element;
9289 player->is_collecting = TRUE;
9292 if (element == EL_SPEED_PILL)
9293 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9294 else if (element == EL_EXTRA_TIME && level.time > 0)
9297 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9299 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9301 player->shield_normal_time_left += 10;
9302 if (element == EL_SHIELD_DEADLY)
9303 player->shield_deadly_time_left += 10;
9305 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9307 if (player->inventory_size < MAX_INVENTORY_SIZE)
9308 player->inventory_element[player->inventory_size++] = element;
9310 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9311 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9313 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9315 player->dynabomb_count++;
9316 player->dynabombs_left++;
9318 else if (element == EL_DYNABOMB_INCREASE_SIZE)
9320 player->dynabomb_size++;
9322 else if (element == EL_DYNABOMB_INCREASE_POWER)
9324 player->dynabomb_xl = TRUE;
9326 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
9327 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
9329 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
9330 element - EL_KEY_1 : element - EL_EM_KEY_1);
9332 player->key[key_nr] = TRUE;
9334 DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
9335 el2edimg(EL_KEY_1 + key_nr));
9336 redraw_mask |= REDRAW_DOOR_1;
9338 else if (IS_ENVELOPE(element))
9341 player->show_envelope = element;
9343 ShowEnvelope(element - EL_ENVELOPE_1);
9346 else if (IS_DROPPABLE(element)) /* can be collected and dropped */
9350 for (i = 0; i < element_info[element].collect_count; i++)
9351 if (player->inventory_size < MAX_INVENTORY_SIZE)
9352 player->inventory_element[player->inventory_size++] = element;
9354 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9355 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9357 else if (element_info[element].collect_count > 0)
9359 local_player->gems_still_needed -=
9360 element_info[element].collect_count;
9361 if (local_player->gems_still_needed < 0)
9362 local_player->gems_still_needed = 0;
9364 DrawText(DX_EMERALDS, DY_EMERALDS,
9365 int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
9368 RaiseScoreElement(element);
9369 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9371 CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
9374 if (mode == DF_SNAP)
9375 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9380 else if (IS_PUSHABLE(element))
9382 if (mode == DF_SNAP && element != EL_BD_ROCK)
9383 return MF_NO_ACTION;
9385 if (CAN_FALL(element) && dy)
9386 return MF_NO_ACTION;
9388 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9389 !(element == EL_SPRING && use_spring_bug))
9390 return MF_NO_ACTION;
9393 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9394 ((move_direction & MV_VERTICAL &&
9395 ((element_info[element].move_pattern & MV_LEFT &&
9396 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9397 (element_info[element].move_pattern & MV_RIGHT &&
9398 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9399 (move_direction & MV_HORIZONTAL &&
9400 ((element_info[element].move_pattern & MV_UP &&
9401 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9402 (element_info[element].move_pattern & MV_DOWN &&
9403 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9404 return MF_NO_ACTION;
9408 /* do not push elements already moving away faster than player */
9409 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9410 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9411 return MF_NO_ACTION;
9413 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
9414 return MF_NO_ACTION;
9418 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9420 if (player->push_delay_value == -1)
9421 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9423 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9425 if (!player->is_pushing)
9426 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9430 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
9431 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
9432 !player_is_pushing))
9433 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9436 if (!player->is_pushing &&
9437 game.engine_version >= VERSION_IDENT(2,2,0,7))
9438 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9442 printf("::: push delay: %ld [%d, %d] [%d]\n",
9443 player->push_delay_value, FrameCounter, game.engine_version,
9444 player->is_pushing);
9447 player->is_pushing = TRUE;
9449 if (!(IN_LEV_FIELD(nextx, nexty) &&
9450 (IS_FREE(nextx, nexty) ||
9451 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9452 IS_SB_ELEMENT(element)))))
9453 return MF_NO_ACTION;
9455 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9456 return MF_NO_ACTION;
9458 if (player->push_delay == 0) /* new pushing; restart delay */
9459 player->push_delay = FrameCounter;
9461 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
9462 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9463 element != EL_SPRING && element != EL_BALLOON)
9465 /* make sure that there is no move delay before next try to push */
9466 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9467 player->move_delay = INITIAL_MOVE_DELAY_OFF;
9469 return MF_NO_ACTION;
9473 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
9476 if (IS_SB_ELEMENT(element))
9478 if (element == EL_SOKOBAN_FIELD_FULL)
9480 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9481 local_player->sokobanfields_still_needed++;
9484 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9486 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9487 local_player->sokobanfields_still_needed--;
9490 Feld[x][y] = EL_SOKOBAN_OBJECT;
9492 if (Back[x][y] == Back[nextx][nexty])
9493 PlayLevelSoundAction(x, y, ACTION_PUSHING);
9494 else if (Back[x][y] != 0)
9495 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9498 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9501 if (local_player->sokobanfields_still_needed == 0 &&
9502 game.emulation == EMU_SOKOBAN)
9504 player->LevelSolved = player->GameOver = TRUE;
9505 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9509 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9511 InitMovingField(x, y, move_direction);
9512 GfxAction[x][y] = ACTION_PUSHING;
9514 if (mode == DF_SNAP)
9515 ContinueMoving(x, y);
9517 MovPos[x][y] = (dx != 0 ? dx : dy);
9519 Pushed[x][y] = TRUE;
9520 Pushed[nextx][nexty] = TRUE;
9522 if (game.engine_version < VERSION_IDENT(2,2,0,7))
9523 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9525 player->push_delay_value = -1; /* get new value later */
9527 CheckTriggeredElementSideChange(x, y, element, dig_side,
9528 CE_OTHER_GETS_PUSHED);
9529 CheckElementSideChange(x, y, element, dig_side,
9530 CE_PUSHED_BY_PLAYER, -1);
9534 else if (IS_SWITCHABLE(element))
9536 if (PLAYER_SWITCHING(player, x, y))
9539 player->is_switching = TRUE;
9540 player->switch_x = x;
9541 player->switch_y = y;
9543 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9545 if (element == EL_ROBOT_WHEEL)
9547 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9551 DrawLevelField(x, y);
9553 else if (element == EL_SP_TERMINAL)
9557 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9559 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9561 else if (Feld[xx][yy] == EL_SP_TERMINAL)
9562 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9565 else if (IS_BELT_SWITCH(element))
9567 ToggleBeltSwitch(x, y);
9569 else if (element == EL_SWITCHGATE_SWITCH_UP ||
9570 element == EL_SWITCHGATE_SWITCH_DOWN)
9572 ToggleSwitchgateSwitch(x, y);
9574 else if (element == EL_LIGHT_SWITCH ||
9575 element == EL_LIGHT_SWITCH_ACTIVE)
9577 ToggleLightSwitch(x, y);
9580 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9581 SND_LIGHT_SWITCH_ACTIVATING :
9582 SND_LIGHT_SWITCH_DEACTIVATING);
9585 else if (element == EL_TIMEGATE_SWITCH)
9587 ActivateTimegateSwitch(x, y);
9589 else if (element == EL_BALLOON_SWITCH_LEFT ||
9590 element == EL_BALLOON_SWITCH_RIGHT ||
9591 element == EL_BALLOON_SWITCH_UP ||
9592 element == EL_BALLOON_SWITCH_DOWN ||
9593 element == EL_BALLOON_SWITCH_ANY)
9595 if (element == EL_BALLOON_SWITCH_ANY)
9596 game.balloon_dir = move_direction;
9598 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
9599 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9600 element == EL_BALLOON_SWITCH_UP ? MV_UP :
9601 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
9604 else if (element == EL_LAMP)
9606 Feld[x][y] = EL_LAMP_ACTIVE;
9607 local_player->lights_still_needed--;
9609 DrawLevelField(x, y);
9611 else if (element == EL_TIME_ORB_FULL)
9613 Feld[x][y] = EL_TIME_ORB_EMPTY;
9615 DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9617 DrawLevelField(x, y);
9620 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9628 if (!PLAYER_SWITCHING(player, x, y))
9630 player->is_switching = TRUE;
9631 player->switch_x = x;
9632 player->switch_y = y;
9634 CheckTriggeredElementSideChange(x, y, element, dig_side,
9635 CE_OTHER_IS_SWITCHING);
9636 CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
9639 CheckTriggeredElementSideChange(x, y, element, dig_side,
9640 CE_OTHER_GETS_PRESSED);
9641 CheckElementSideChange(x, y, element, dig_side,
9642 CE_PRESSED_BY_PLAYER, -1);
9645 return MF_NO_ACTION;
9648 player->push_delay = 0;
9650 if (Feld[x][y] != element) /* really digged/collected something */
9651 player->is_collecting = !player->is_digging;
9656 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9658 int jx = player->jx, jy = player->jy;
9659 int x = jx + dx, y = jy + dy;
9660 int snap_direction = (dx == -1 ? MV_LEFT :
9661 dx == +1 ? MV_RIGHT :
9663 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9665 if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9668 if (!player->active || !IN_LEV_FIELD(x, y))
9676 if (player->MovPos == 0)
9677 player->is_pushing = FALSE;
9679 player->is_snapping = FALSE;
9681 if (player->MovPos == 0)
9683 player->is_moving = FALSE;
9684 player->is_digging = FALSE;
9685 player->is_collecting = FALSE;
9691 if (player->is_snapping)
9694 player->MovDir = snap_direction;
9697 if (player->MovPos == 0)
9700 player->is_moving = FALSE;
9701 player->is_digging = FALSE;
9702 player->is_collecting = FALSE;
9705 player->is_dropping = FALSE;
9707 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9710 player->is_snapping = TRUE;
9713 if (player->MovPos == 0)
9716 player->is_moving = FALSE;
9717 player->is_digging = FALSE;
9718 player->is_collecting = FALSE;
9721 DrawLevelField(x, y);
9727 boolean DropElement(struct PlayerInfo *player)
9729 int jx = player->jx, jy = player->jy;
9730 int old_element = Feld[jx][jy];
9733 /* check if player is active, not moving and ready to drop */
9734 if (!player->active || player->MovPos || player->drop_delay > 0)
9737 /* check if player has anything that can be dropped */
9738 if (player->inventory_size == 0 && player->dynabombs_left == 0)
9741 /* check if anything can be dropped at the current position */
9742 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9745 /* collected custom elements can only be dropped on empty fields */
9746 if (player->inventory_size > 0 &&
9747 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9748 && old_element != EL_EMPTY)
9751 if (old_element != EL_EMPTY)
9752 Back[jx][jy] = old_element; /* store old element on this field */
9754 ResetGfxAnimation(jx, jy);
9755 ResetRandomAnimationValue(jx, jy);
9757 if (player->inventory_size > 0)
9759 player->inventory_size--;
9760 new_element = player->inventory_element[player->inventory_size];
9762 if (new_element == EL_DYNAMITE)
9763 new_element = EL_DYNAMITE_ACTIVE;
9764 else if (new_element == EL_SP_DISK_RED)
9765 new_element = EL_SP_DISK_RED_ACTIVE;
9767 Feld[jx][jy] = new_element;
9769 DrawText(DX_DYNAMITE, DY_DYNAMITE,
9770 int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9772 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9773 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9775 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9778 /* needed if previous element just changed to "empty" in the last frame */
9779 Changed[jx][jy] = 0; /* allow another change */
9782 CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9783 CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9785 TestIfElementTouchesCustomElement(jx, jy);
9787 else /* player is dropping a dyna bomb */
9789 player->dynabombs_left--;
9790 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9792 Feld[jx][jy] = new_element;
9794 if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9795 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9797 PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9804 if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
9806 InitField(jx, jy, FALSE);
9807 if (CAN_MOVE(Feld[jx][jy]))
9811 new_element = Feld[jx][jy];
9813 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9814 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9816 int move_stepsize = element_info[new_element].move_stepsize;
9817 int direction, dx, dy, nextx, nexty;
9819 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
9820 MovDir[jx][jy] = player->MovDir;
9822 direction = MovDir[jx][jy];
9823 dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9824 dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9828 if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
9831 WasJustMoving[jx][jy] = 3;
9833 InitMovingField(jx, jy, direction);
9834 ContinueMoving(jx, jy);
9839 Changed[jx][jy] = 0; /* allow another change */
9842 TestIfElementHitsCustomElement(jx, jy, direction);
9844 CheckElementSideChange(jx, jy, new_element,
9845 direction, CE_HITTING_SOMETHING, -1);
9849 player->drop_delay = 2 * TILEX / move_stepsize + 1;
9853 player->drop_delay = 8 + 8 + 8;
9858 player->is_dropping = TRUE;
9864 /* ------------------------------------------------------------------------- */
9865 /* game sound playing functions */
9866 /* ------------------------------------------------------------------------- */
9868 static int *loop_sound_frame = NULL;
9869 static int *loop_sound_volume = NULL;
9871 void InitPlayLevelSound()
9873 int num_sounds = getSoundListSize();
9875 checked_free(loop_sound_frame);
9876 checked_free(loop_sound_volume);
9878 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
9879 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9882 static void PlayLevelSound(int x, int y, int nr)
9884 int sx = SCREENX(x), sy = SCREENY(y);
9885 int volume, stereo_position;
9886 int max_distance = 8;
9887 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9889 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9890 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9893 if (!IN_LEV_FIELD(x, y) ||
9894 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9895 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9898 volume = SOUND_MAX_VOLUME;
9900 if (!IN_SCR_FIELD(sx, sy))
9902 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9903 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9905 volume -= volume * (dx > dy ? dx : dy) / max_distance;
9908 stereo_position = (SOUND_MAX_LEFT +
9909 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9910 (SCR_FIELDX + 2 * max_distance));
9912 if (IS_LOOP_SOUND(nr))
9914 /* This assures that quieter loop sounds do not overwrite louder ones,
9915 while restarting sound volume comparison with each new game frame. */
9917 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9920 loop_sound_volume[nr] = volume;
9921 loop_sound_frame[nr] = FrameCounter;
9924 PlaySoundExt(nr, volume, stereo_position, type);
9927 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9929 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9930 x > LEVELX(BX2) ? LEVELX(BX2) : x,
9931 y < LEVELY(BY1) ? LEVELY(BY1) :
9932 y > LEVELY(BY2) ? LEVELY(BY2) : y,
9936 static void PlayLevelSoundAction(int x, int y, int action)
9938 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9941 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9943 int sound_effect = element_info[element].sound[action];
9945 if (sound_effect != SND_UNDEFINED)
9946 PlayLevelSound(x, y, sound_effect);
9949 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9952 int sound_effect = element_info[element].sound[action];
9954 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9955 PlayLevelSound(x, y, sound_effect);
9958 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9960 int sound_effect = element_info[Feld[x][y]].sound[action];
9962 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9963 PlayLevelSound(x, y, sound_effect);
9966 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9968 int sound_effect = element_info[Feld[x][y]].sound[action];
9970 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9971 StopSound(sound_effect);
9974 static void PlayLevelMusic()
9976 if (levelset.music[level_nr] != MUS_UNDEFINED)
9977 PlayMusic(levelset.music[level_nr]); /* from config file */
9979 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
9982 void RaiseScore(int value)
9984 local_player->score += value;
9985 DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9988 void RaiseScoreElement(int element)
9994 case EL_EMERALD_YELLOW:
9995 case EL_EMERALD_RED:
9996 case EL_EMERALD_PURPLE:
9997 case EL_SP_INFOTRON:
9998 RaiseScore(level.score[SC_EMERALD]);
10001 RaiseScore(level.score[SC_DIAMOND]);
10004 RaiseScore(level.score[SC_CRYSTAL]);
10007 RaiseScore(level.score[SC_PEARL]);
10010 case EL_BD_BUTTERFLY:
10011 case EL_SP_ELECTRON:
10012 RaiseScore(level.score[SC_BUG]);
10015 case EL_BD_FIREFLY:
10016 case EL_SP_SNIKSNAK:
10017 RaiseScore(level.score[SC_SPACESHIP]);
10020 case EL_DARK_YAMYAM:
10021 RaiseScore(level.score[SC_YAMYAM]);
10024 RaiseScore(level.score[SC_ROBOT]);
10027 RaiseScore(level.score[SC_PACMAN]);
10030 RaiseScore(level.score[SC_NUT]);
10033 case EL_SP_DISK_RED:
10034 case EL_DYNABOMB_INCREASE_NUMBER:
10035 case EL_DYNABOMB_INCREASE_SIZE:
10036 case EL_DYNABOMB_INCREASE_POWER:
10037 RaiseScore(level.score[SC_DYNAMITE]);
10039 case EL_SHIELD_NORMAL:
10040 case EL_SHIELD_DEADLY:
10041 RaiseScore(level.score[SC_SHIELD]);
10043 case EL_EXTRA_TIME:
10044 RaiseScore(level.score[SC_TIME_BONUS]);
10050 RaiseScore(level.score[SC_KEY]);
10053 RaiseScore(element_info[element].collect_score);
10058 void RequestQuitGame(boolean ask_if_really_quit)
10060 if (AllPlayersGone ||
10061 !ask_if_really_quit ||
10062 level_editor_test_game ||
10063 Request("Do you really want to quit the game ?",
10064 REQ_ASK | REQ_STAY_CLOSED))
10066 #if defined(PLATFORM_UNIX)
10067 if (options.network)
10068 SendToServer_StopPlaying();
10072 game_status = GAME_MODE_MAIN;
10078 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10083 /* ---------- new game button stuff ---------------------------------------- */
10085 /* graphic position values for game buttons */
10086 #define GAME_BUTTON_XSIZE 30
10087 #define GAME_BUTTON_YSIZE 30
10088 #define GAME_BUTTON_XPOS 5
10089 #define GAME_BUTTON_YPOS 215
10090 #define SOUND_BUTTON_XPOS 5
10091 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10093 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10094 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10095 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10096 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10097 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10098 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10105 } gamebutton_info[NUM_GAME_BUTTONS] =
10108 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
10113 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
10114 GAME_CTRL_ID_PAUSE,
10118 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
10123 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
10124 SOUND_CTRL_ID_MUSIC,
10125 "background music on/off"
10128 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
10129 SOUND_CTRL_ID_LOOPS,
10130 "sound loops on/off"
10133 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
10134 SOUND_CTRL_ID_SIMPLE,
10135 "normal sounds on/off"
10139 void CreateGameButtons()
10143 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10145 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10146 struct GadgetInfo *gi;
10149 unsigned long event_mask;
10150 int gd_xoffset, gd_yoffset;
10151 int gd_x1, gd_x2, gd_y1, gd_y2;
10154 gd_xoffset = gamebutton_info[i].x;
10155 gd_yoffset = gamebutton_info[i].y;
10156 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10157 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10159 if (id == GAME_CTRL_ID_STOP ||
10160 id == GAME_CTRL_ID_PAUSE ||
10161 id == GAME_CTRL_ID_PLAY)
10163 button_type = GD_TYPE_NORMAL_BUTTON;
10165 event_mask = GD_EVENT_RELEASED;
10166 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10167 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10171 button_type = GD_TYPE_CHECK_BUTTON;
10173 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10174 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10175 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10176 event_mask = GD_EVENT_PRESSED;
10177 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
10178 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10181 gi = CreateGadget(GDI_CUSTOM_ID, id,
10182 GDI_INFO_TEXT, gamebutton_info[i].infotext,
10183 GDI_X, DX + gd_xoffset,
10184 GDI_Y, DY + gd_yoffset,
10185 GDI_WIDTH, GAME_BUTTON_XSIZE,
10186 GDI_HEIGHT, GAME_BUTTON_YSIZE,
10187 GDI_TYPE, button_type,
10188 GDI_STATE, GD_BUTTON_UNPRESSED,
10189 GDI_CHECKED, checked,
10190 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10191 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10192 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10193 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10194 GDI_EVENT_MASK, event_mask,
10195 GDI_CALLBACK_ACTION, HandleGameButtons,
10199 Error(ERR_EXIT, "cannot create gadget");
10201 game_gadget[id] = gi;
10205 void FreeGameButtons()
10209 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10210 FreeGadget(game_gadget[i]);
10213 static void MapGameButtons()
10217 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10218 MapGadget(game_gadget[i]);
10221 void UnmapGameButtons()
10225 for (i = 0; i < NUM_GAME_BUTTONS; i++)
10226 UnmapGadget(game_gadget[i]);
10229 static void HandleGameButtons(struct GadgetInfo *gi)
10231 int id = gi->custom_id;
10233 if (game_status != GAME_MODE_PLAYING)
10238 case GAME_CTRL_ID_STOP:
10239 RequestQuitGame(TRUE);
10242 case GAME_CTRL_ID_PAUSE:
10243 if (options.network)
10245 #if defined(PLATFORM_UNIX)
10247 SendToServer_ContinuePlaying();
10249 SendToServer_PausePlaying();
10253 TapeTogglePause(TAPE_TOGGLE_MANUAL);
10256 case GAME_CTRL_ID_PLAY:
10259 #if defined(PLATFORM_UNIX)
10260 if (options.network)
10261 SendToServer_ContinuePlaying();
10265 tape.pausing = FALSE;
10266 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10271 case SOUND_CTRL_ID_MUSIC:
10272 if (setup.sound_music)
10274 setup.sound_music = FALSE;
10277 else if (audio.music_available)
10279 setup.sound = setup.sound_music = TRUE;
10281 SetAudioMode(setup.sound);
10287 case SOUND_CTRL_ID_LOOPS:
10288 if (setup.sound_loops)
10289 setup.sound_loops = FALSE;
10290 else if (audio.loops_available)
10292 setup.sound = setup.sound_loops = TRUE;
10293 SetAudioMode(setup.sound);
10297 case SOUND_CTRL_ID_SIMPLE:
10298 if (setup.sound_simple)
10299 setup.sound_simple = FALSE;
10300 else if (audio.sound_available)
10302 setup.sound = setup.sound_simple = TRUE;
10303 SetAudioMode(setup.sound);