X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Fgame.c;h=f8289cc9138937767facf986edaa3b21fa908628;hb=564a563aee2c9fdb37116d41342b36eda63c1f6a;hp=8ff077ef02242dad5f2e90b7c9a4a05417a3662f;hpb=8cc8c1704027665811c09ef8fb4bae5439143fc7;p=rocksndiamonds.git diff --git a/src/game.c b/src/game.c index 8ff077ef..f8289cc9 100644 --- a/src/game.c +++ b/src/game.c @@ -98,12 +98,32 @@ RND(element_info[e].push_delay_random)) #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \ RND(element_info[e].move_delay_random)) +#define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \ + (element_info[e].move_delay_random)) +#define ELEMENT_CAN_ENTER_FIELD_BASE(e, x, y, condition) \ + (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \ + (CAN_MOVE_INTO_ACID(e) && \ + Feld[x][y] == EL_ACID) || \ + (condition))) + +#if 0 +#define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \ + (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \ + (condition) || \ + (DONT_COLLIDE_WITH(e) && \ + IS_PLAYER(x, y) && \ + !PLAYER_ENEMY_PROTECTED(x, y)))) +#else #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \ (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \ (condition) || \ + (CAN_MOVE_INTO_ACID(e) && \ + Feld[x][y] == EL_ACID) || \ (DONT_COLLIDE_WITH(e) && \ - IS_FREE_OR_PLAYER(x, y)))) + IS_PLAYER(x, y) && \ + !PLAYER_ENEMY_PROTECTED(x, y)))) +#endif #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition) \ (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \ @@ -118,7 +138,11 @@ #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y) \ ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID)) -#define ENEMY_CAN_ENTER_FIELD(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y)) +#if 0 +#define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y)) +#else +#define ENEMY_CAN_ENTER_FIELD(e, x, y) ELEMENT_CAN_ENTER_FIELD_BASE(e, x, y, 0) +#endif #define YAMYAM_CAN_ENTER_FIELD(x, y) \ (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \ @@ -142,6 +166,28 @@ Feld[x][y] == EL_EXIT_OPEN || \ Feld[x][y] == EL_ACID)) +#define GROUP_NR(e) ((e) - EL_GROUP_START) +#define MOVE_ENTER_EL(e) (element_info[e].move_enter_element) +#define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE) +#define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START)) + +#define IS_EQUAL_OR_IN_GROUP(e, ge) \ + (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge)) + +#if 0 +#define CE_ENTER_FIELD_COND(e, x, y) \ + (!IS_PLAYER(x, y) && \ + (Feld[x][y] == EL_ACID || \ + IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))) +#else +#define CE_ENTER_FIELD_COND(e, x, y) \ + (!IS_PLAYER(x, y) && \ + IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))) +#endif + +#define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \ + ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, CE_ENTER_FIELD_COND(e, x, y)) + #define MOLE_CAN_ENTER_FIELD(x, y, condition) \ (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition))) @@ -166,13 +212,17 @@ static boolean MovePlayer(struct PlayerInfo *, int, int); static void ScrollPlayer(struct PlayerInfo *, int); static void ScrollScreen(struct PlayerInfo *, int); +int DigField(struct PlayerInfo *, int, int, int, int, int, int, int); + static void InitBeltMovement(void); static void CloseAllOpenTimegates(void); static void CheckGravityMovement(struct PlayerInfo *); -static void KillHeroUnlessProtected(int, int); +static void KillHeroUnlessEnemyProtected(int, int); +static void KillHeroUnlessExplosionProtected(int, int); static void TestIfPlayerTouchesCustomElement(int, int); static void TestIfElementTouchesCustomElement(int, int); +static void TestIfElementHitsCustomElement(int, int, int); static void ChangeElement(int, int, int); static boolean CheckTriggeredElementSideChange(int, int, int, int, int); @@ -180,12 +230,14 @@ static boolean CheckTriggeredElementChange(int, int, int, int); static boolean CheckElementSideChange(int, int, int, int, int, int); static boolean CheckElementChange(int, int, int, int); -static void PlaySoundLevel(int, int, int); -static void PlaySoundLevelNearest(int, int, int); -static void PlaySoundLevelAction(int, int, int); -static void PlaySoundLevelElementAction(int, int, int, int); -static void PlaySoundLevelActionIfLoop(int, int, int); -static void StopSoundLevelActionIfLoop(int, int, int); +static void PlayLevelSound(int, int, int); +static void PlayLevelSoundNearest(int, int, int); +static void PlayLevelSoundAction(int, int, int); +static void PlayLevelSoundElementAction(int, int, int, int); +static void PlayLevelSoundElementActionIfLoop(int, int, int, int); +static void PlayLevelSoundActionIfLoop(int, int, int); +static void StopLevelSoundActionIfLoop(int, int, int); +static void PlayLevelMusic(); static void MapGameButtons(); static void HandleGameButtons(struct GadgetInfo *); @@ -446,6 +498,28 @@ collect_count_list[] = { EL_UNDEFINED, 0 }, }; +struct +{ + int element; + int direction; +} +tube_access[] = +{ + { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }, + { EL_TUBE_VERTICAL, MV_UP | MV_DOWN }, + { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT }, + { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN }, + { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN }, + { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP }, + { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN }, + { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP }, + { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN }, + { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP }, + { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN }, + + { EL_UNDEFINED, 0 } +}; + static unsigned long trigger_events[MAX_NUM_ELEMENTS]; #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \ @@ -556,6 +630,10 @@ static void InitPlayerField(int x, int y, int element, boolean init_game) player->present = TRUE; + player->block_last_field = (element == EL_SP_MURPHY ? + level.sp_block_last_field : + level.block_last_field); + if (!options.network || player->connected) { player->active = TRUE; @@ -595,6 +673,18 @@ static void InitField(int x, int y, boolean init_game) InitPlayerField(x, y, element, init_game); break; + case EL_SOKOBAN_FIELD_PLAYER: + element = Feld[x][y] = EL_PLAYER_1; + InitField(x, y, init_game); + + element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY; + InitField(x, y, init_game); + break; + + case EL_SOKOBAN_FIELD_EMPTY: + local_player->sokobanfields_still_needed++; + break; + case EL_STONEBLOCK: if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID) Feld[x][y] = EL_ACID_POOL_TOPLEFT; @@ -660,6 +750,11 @@ static void InitField(int x, int y, boolean init_game) break; case EL_DYNAMITE_ACTIVE: + case EL_SP_DISK_RED_ACTIVE: + case EL_DYNABOMB_PLAYER_1_ACTIVE: + case EL_DYNABOMB_PLAYER_2_ACTIVE: + case EL_DYNABOMB_PLAYER_3_ACTIVE: + case EL_DYNABOMB_PLAYER_4_ACTIVE: MovDelay[x][y] = 96; break; @@ -667,17 +762,13 @@ static void InitField(int x, int y, boolean init_game) local_player->lights_still_needed++; break; - case EL_SOKOBAN_FIELD_EMPTY: - local_player->sokobanfields_still_needed++; - break; - case EL_PENGUIN: local_player->friends_still_needed++; break; case EL_PIG: case EL_DRAGON: - MovDir[x][y] = 1 << RND(4); + GfxDir[x][y] = MovDir[x][y] = 1 << RND(4); break; #if 0 @@ -744,16 +835,68 @@ static void InitField(int x, int y, boolean init_game) default: if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element)) InitMovDir(x, y); + else if (IS_GROUP_ELEMENT(element)) + { + struct ElementGroupInfo *group = element_info[element].group; + int last_anim_random_frame = gfx.anim_random_frame; + int element_pos; + + if (group->choice_mode == ANIM_RANDOM) + gfx.anim_random_frame = RND(group->num_elements_resolved); + + element_pos = getAnimationFrame(group->num_elements_resolved, 1, + group->choice_mode, 0, + group->choice_pos); + + if (group->choice_mode == ANIM_RANDOM) + gfx.anim_random_frame = last_anim_random_frame; + + group->choice_pos++; + + Feld[x][y] = group->element_resolved[element_pos]; + + InitField(x, y, init_game); + } break; } } +static inline void InitField_WithBug1(int x, int y, boolean init_game) +{ + InitField(x, y, init_game); + + /* not needed to call InitMovDir() -- already done by InitField()! */ + if (game.engine_version < VERSION_IDENT(3,0,9,0) && + CAN_MOVE(Feld[x][y])) + InitMovDir(x, y); +} + +static inline void InitField_WithBug2(int x, int y, boolean init_game) +{ + int old_element = Feld[x][y]; + + InitField(x, y, init_game); + + /* not needed to call InitMovDir() -- already done by InitField()! */ + if (game.engine_version < VERSION_IDENT(3,0,9,0) && + CAN_MOVE(old_element) && + (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN)) + InitMovDir(x, y); + + /* this case is in fact a combination of not less than three bugs: + first, it calls InitMovDir() for elements that can move, although this is + already done by InitField(); then, it checks the element that was at this + field _before_ the call to InitField() (which can change it) + + */ +} + void DrawGameDoorValues() { int i, j; - for (i=0; i NUM_GROUP_ELEMENTS) /* recursion too deep */ + { + Error(ERR_WARN, "recursion too deep when resolving group element %d", + group_element - EL_GROUP_START + 1); + + /* replace element which caused too deep recursion by question mark */ + group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN; + + return; + } + + if (recursion_depth == 0) /* initialization */ + { + group = element_info[group_element].group; + group_nr = group_element - EL_GROUP_START; + + group->num_elements_resolved = 0; + group->choice_pos = 0; + } + + for (i = 0; i < actual_group->num_elements; i++) + { + int element = actual_group->element[i]; + + if (group->num_elements_resolved == NUM_FILE_ELEMENTS) + break; + + if (IS_GROUP_ELEMENT(element)) + resolve_group_element(element, recursion_depth + 1); + else + { + group->element_resolved[group->num_elements_resolved++] = element; + element_info[element].in_group[group_nr] = TRUE; + } + } + +#if 0 + if (recursion_depth == 0 && group_element <= EL_GROUP_4) + { + printf("::: group %d: %d resolved elements\n", + group_element - EL_GROUP_START, group->num_elements_resolved); + for (i = 0; i < group->num_elements_resolved; i++) + printf("::: - %d ['%s']\n", group->element_resolved[i], + element_info[group->element_resolved[i]].token_name); + } +#endif +} + /* ============================================================================= @@ -796,21 +994,36 @@ static void InitGameEngine() printf(" => game.engine_version == %06d\n", game.engine_version); #endif + /* ---------- recursively resolve group elements ------------------------- */ + + for (i = 0; i < MAX_NUM_ELEMENTS; i++) + for (j = 0; j < NUM_GROUP_ELEMENTS; j++) + element_info[i].in_group[j] = FALSE; + + for (i = 0; i < NUM_GROUP_ELEMENTS; i++) + resolve_group_element(EL_GROUP_START + i, 0); + /* ---------- initialize player's initial move delay --------------------- */ /* dynamically adjust player properties according to game engine version */ game.initial_move_delay = - (game.engine_version <= VERSION_IDENT(2,0,1) ? INITIAL_MOVE_DELAY_ON : + (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON : INITIAL_MOVE_DELAY_OFF); /* dynamically adjust player properties according to level information */ game.initial_move_delay_value = (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED); + /* ---------- initialize player's initial push delay --------------------- */ + + /* dynamically adjust player properties according to game engine version */ + game.initial_push_delay_value = + (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1); + /* ---------- initialize changing elements ------------------------------- */ /* initialize changing elements information */ - for (i=0; i < MAX_NUM_ELEMENTS; i++) + for (i = 0; i < MAX_NUM_ELEMENTS; i++) { struct ElementInfo *ei = &element_info[i]; @@ -826,7 +1039,7 @@ static void InitGameEngine() } ei->change_events = CE_BITMASK_DEFAULT; - for (j=0; j < NUM_CHANGE_EVENTS; j++) + for (j = 0; j < NUM_CHANGE_EVENTS; j++) { ei->event_page_nr[j] = 0; ei->event_page[j] = &ei->change_page[0]; @@ -834,7 +1047,7 @@ static void InitGameEngine() } /* add changing elements from pre-defined list */ - for (i=0; change_delay_list[i].element != EL_UNDEFINED; i++) + for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++) { struct ChangingElementInfo *ch_delay = &change_delay_list[i]; struct ElementInfo *ei = &element_info[ch_delay->element]; @@ -851,16 +1064,16 @@ static void InitGameEngine() #if 1 /* add change events from custom element configuration */ - for (i=0; i < NUM_CUSTOM_ELEMENTS; i++) + for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) { struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i]; - for (j=0; j < ei->num_change_pages; j++) + for (j = 0; j < ei->num_change_pages; j++) { if (!ei->change_page[j].can_change) continue; - for (k=0; k < NUM_CHANGE_EVENTS; k++) + for (k = 0; k < NUM_CHANGE_EVENTS; k++) { /* only add event page for the first page found with this event */ if (ei->change_page[j].events & CH_EVENT_BIT(k) && @@ -877,7 +1090,7 @@ static void InitGameEngine() #else /* add change events from custom element configuration */ - for (i=0; i < NUM_CUSTOM_ELEMENTS; i++) + for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) { int element = EL_CUSTOM_START + i; @@ -890,16 +1103,16 @@ static void InitGameEngine() /* ---------- initialize trigger events ---------------------------------- */ /* initialize trigger events information */ - for (i=0; inum_change_pages; j++) + for (j = 0; j < ei->num_change_pages; j++) { if (!ei->change_page[j].can_change) continue; @@ -908,13 +1121,22 @@ static void InitGameEngine() { int trigger_element = ei->change_page[j].trigger_element; - trigger_events[trigger_element] |= ei->change_page[j].events; + if (IS_GROUP_ELEMENT(trigger_element)) + { + struct ElementGroupInfo *group = element_info[trigger_element].group; + + for (k = 0; k < group->num_elements_resolved; k++) + trigger_events[group->element_resolved[k]] + |= ei->change_page[j].events; + } + else + trigger_events[trigger_element] |= ei->change_page[j].events; } } } #else /* add trigger events from element change event properties */ - for (i=0; itrigger_element] |= element_info[i].change->events; @@ -923,17 +1145,17 @@ static void InitGameEngine() /* ---------- initialize push delay -------------------------------------- */ /* initialize push delay values to default */ - for (i=0; i= VERSION_IDENT(3,0,9,0)) + { + for (i = 0; i < MAX_NUM_ELEMENTS; i++) + { + if (IS_SP_ELEMENT(i)) + { + element_info[i].push_delay_fixed = 6; + element_info[i].push_delay_random = 0; + } + } + } + /* ---------- initialize move stepsize ----------------------------------- */ /* initialize move stepsize values to default */ - for (i=0; ilights_still_needed = 0; player->friends_still_needed = 0; - for (j=0; j<4; j++) + for (j = 0; j < 4; j++) player->key[j] = FALSE; player->dynabomb_count = 0; @@ -1038,8 +1293,12 @@ void InitGame() player->use_murphy_graphic = FALSE; + player->block_last_field = FALSE; + player->actual_frame_counter = 0; + player->step_counter = 0; + player->last_move_dir = MV_NO_MOVING; player->is_waiting = FALSE; @@ -1049,6 +1308,54 @@ void InitGame() player->is_collecting = FALSE; player->is_pushing = FALSE; player->is_switching = FALSE; + player->is_dropping = FALSE; + + player->is_bored = FALSE; + player->is_sleeping = FALSE; + + player->frame_counter_bored = -1; + player->frame_counter_sleeping = -1; + + player->anim_delay_counter = 0; + player->post_delay_counter = 0; + + player->action_waiting = ACTION_DEFAULT; + player->last_action_waiting = ACTION_DEFAULT; + player->special_action_bored = ACTION_DEFAULT; + player->special_action_sleeping = ACTION_DEFAULT; + + player->num_special_action_bored = 0; + player->num_special_action_sleeping = 0; + + /* determine number of special actions for bored and sleeping animation */ + for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++) + { + boolean found = FALSE; + + for (k = 0; k < NUM_DIRECTIONS; k++) + if (el_act_dir2img(player->element_nr, j, k) != + el_act_dir2img(player->element_nr, ACTION_DEFAULT, k)) + found = TRUE; + + if (found) + player->num_special_action_bored++; + else + break; + } + for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++) + { + boolean found = FALSE; + + for (k = 0; k < NUM_DIRECTIONS; k++) + if (el_act_dir2img(player->element_nr, j, k) != + el_act_dir2img(player->element_nr, ACTION_DEFAULT, k)) + found = TRUE; + + if (found) + player->num_special_action_sleeping++; + else + break; + } player->switch_x = -1; player->switch_y = -1; @@ -1058,8 +1365,12 @@ void InitGame() player->move_delay = game.initial_move_delay; player->move_delay_value = game.initial_move_delay_value; + player->move_delay_reset_counter = 0; + player->push_delay = 0; - player->push_delay_value = 5; + player->push_delay_value = game.initial_push_delay_value; + + player->drop_delay = 0; player->last_jx = player->last_jy = 0; player->jx = player->jy = 0; @@ -1069,7 +1380,7 @@ void InitGame() player->inventory_size = 0; - DigField(player, 0, 0, 0, 0, DF_NO_PUSH); + DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH); SnapField(player, 0, 0); player->LevelSolved = FALSE; @@ -1111,18 +1422,18 @@ void InitGame() game.envelope_active = FALSE; - for (i=0; i<4; i++) + for (i = 0; i < 4; i++) { game.belt_dir[i] = MV_NO_MOVING; game.belt_dir_nr[i] = 3; /* not moving, next moving left */ } - for (i=0; iconnected && !player->present) { - for (j=0; jjx, jy = some_player->jy; @@ -1191,7 +1531,9 @@ void InitGame() { player->present = TRUE; player->active = TRUE; + some_player->present = FALSE; + some_player->active = FALSE; StorePlayer[jx][jy] = player->element_nr; player->jx = player->last_jx = jx; @@ -1205,9 +1547,9 @@ void InitGame() if (tape.playing) { - /* when playing a tape, eliminate all players who do not participate */ + /* when playing a tape, eliminate all players which do not participate */ - for (i=0; ijx, jy = player->jy; player->active = FALSE; + player->present = FALSE; + StorePlayer[jx][jy] = 0; Feld[jx][jy] = EL_EMPTY; } @@ -1247,14 +1591,14 @@ void InitGame() /* when recording the game, store which players take part in the game */ if (tape.recording) { - for (i=0; imove_direction_initial; + int move_pattern = ei->move_pattern; + + if (move_direction_initial == MV_START_PREVIOUS) + { + if (MovDir[x][y] != MV_NO_MOVING) + return; + + move_direction_initial = MV_START_AUTOMATIC; + } + + if (move_direction_initial == MV_START_RANDOM) + MovDir[x][y] = 1 << RND(4); + else if (move_direction_initial & MV_ANY_DIRECTION) + MovDir[x][y] = move_direction_initial; + else if (move_pattern == MV_ALL_DIRECTIONS || + move_pattern == MV_TURNING_LEFT || + move_pattern == MV_TURNING_RIGHT || + move_pattern == MV_TURNING_LEFT_RIGHT || + move_pattern == MV_TURNING_RIGHT_LEFT || + move_pattern == MV_TURNING_RANDOM) MovDir[x][y] = 1 << RND(4); - else if (element_info[element].move_pattern == MV_HORIZONTAL) + else if (move_pattern == MV_HORIZONTAL) MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT); - else if (element_info[element].move_pattern == MV_VERTICAL) + else if (move_pattern == MV_VERTICAL) MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN); - else if (element_info[element].move_pattern & MV_ANY_DIRECTION) + else if (move_pattern & MV_ANY_DIRECTION) MovDir[x][y] = element_info[element].move_pattern; - else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE || - element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE) + else if (move_pattern == MV_ALONG_LEFT_SIDE || + move_pattern == MV_ALONG_RIGHT_SIDE) { - for (i=0; i<4; i++) + for (i = 0; i < 4; i++) { int x1 = x + xy[i][0]; int y1 = y + xy[i][1]; if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1)) { - if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE) + if (move_pattern == MV_ALONG_RIGHT_SIDE) MovDir[x][y] = direction[0][i]; else MovDir[x][y] = direction[1][i]; @@ -1573,7 +1939,7 @@ void InitMovDir(int x, int y) element != EL_BD_FIREFLY) break; - for (i=0; i<4; i++) + for (i = 0; i < 4; i++) { int x1 = x + xy[i][0]; int y1 = y + xy[i][1]; @@ -1596,6 +1962,8 @@ void InitMovDir(int x, int y) } break; } + + GfxDir[x][y] = MovDir[x][y]; } void InitAmoebaNr(int x, int y) @@ -1605,7 +1973,7 @@ void InitAmoebaNr(int x, int y) if (group_nr == 0) { - for (i=1; iscore < highscore[MAX_SCORE_ENTRIES - 1].Score) return -1; - for (k=0; kscore > highscore[k].Score) { @@ -1777,14 +2145,14 @@ int NewHiScore() int m = MAX_SCORE_ENTRIES - 1; #ifdef ONE_PER_NAME - for (l=k; lk; l--) + for (l = m; l > k; l--) { strcpy(highscore[l].Name, highscore[l - 1].Name); highscore[l].Score = highscore[l - 1].Score; @@ -1840,6 +2208,7 @@ static void ResetGfxAnimation(int x, int y) { GfxFrame[x][y] = 0; GfxAction[x][y] = ACTION_DEFAULT; + GfxDir[x][y] = MovDir[x][y]; } void InitMovingField(int x, int y, int direction) @@ -1854,6 +2223,7 @@ void InitMovingField(int x, int y, int direction) ResetGfxAnimation(x, y); MovDir[newx][newy] = MovDir[x][y] = direction; + GfxDir[x][y] = direction; if (Feld[newx][newy] == EL_EMPTY) Feld[newx][newy] = EL_BLOCKED; @@ -1864,8 +2234,9 @@ void InitMovingField(int x, int y, int direction) GfxAction[x][y] = ACTION_MOVING; GfxFrame[newx][newy] = GfxFrame[x][y]; - GfxAction[newx][newy] = GfxAction[x][y]; GfxRandom[newx][newy] = GfxRandom[x][y]; + GfxAction[newx][newy] = GfxAction[x][y]; + GfxDir[newx][newy] = GfxDir[x][y]; } void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y) @@ -1949,6 +2320,7 @@ static void RemoveField(int x, int y) GfxElement[x][y] = EL_UNDEFINED; GfxAction[x][y] = ACTION_DEFAULT; + GfxDir[x][y] = MV_NO_MOVING; } void RemoveMovingField(int x, int y) @@ -1963,8 +2335,25 @@ void RemoveMovingField(int x, int y) if (IS_MOVING(x, y)) { Moving2Blocked(x, y, &newx, &newy); +#if 0 + if (Feld[newx][newy] != EL_BLOCKED) + return; +#else if (Feld[newx][newy] != EL_BLOCKED) + { + /* element is moving, but target field is not free (blocked), but + already occupied by something different (example: acid pool); + in this case, only remove the moving field, but not the target */ + + RemoveField(oldx, oldy); + + Store[oldx][oldy] = Store2[oldx][oldy] = 0; + + DrawLevelField(oldx, oldy); + return; + } +#endif } else if (element == EL_BLOCKED) { @@ -2035,14 +2424,14 @@ void CheckDynamite(int x, int y) if (MovDelay[x][y] != 0) { DrawDynamite(x, y); - PlaySoundLevelActionIfLoop(x, y, ACTION_ACTIVE); + PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE); return; } } #if 1 - StopSoundLevelActionIfLoop(x, y, ACTION_ACTIVE); + StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE); #else if (Feld[x][y] == EL_DYNAMITE_ACTIVE || Feld[x][y] == EL_SP_DISK_RED_ACTIVE) @@ -2054,9 +2443,17 @@ void CheckDynamite(int x, int y) Bang(x, y); } -void RelocatePlayer(int x, int y, int element) +void RelocatePlayer(int x, int y, int element_raw) { + int element = (element_raw == EL_SP_MURPHY ? EL_PLAYER_1 : element_raw); struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1]; + boolean ffwd_delay = (tape.playing && tape.fast_forward); + boolean no_delay = (tape.index_search); + int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay); + int wait_delay_value = (no_delay ? 0 : frame_delay_value); + + if (player->GameOver) /* do not reanimate dead player */ + return; #if 1 RemoveField(x, y); /* temporarily remove newly placed player */ @@ -2074,7 +2471,7 @@ void RelocatePlayer(int x, int y, int element) DrawPlayer(player); BackToFront(); - Delay(GAME_FRAME_DELAY); + Delay(wait_delay_value); } DrawPlayer(player); /* needed here only to cleanup last field */ @@ -2118,11 +2515,11 @@ void RelocatePlayer(int x, int y, int element) /* scroll in two steps of half tile size to make things smoother */ BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY); FlushDisplay(); - Delay(GAME_FRAME_DELAY); + Delay(wait_delay_value); /* scroll second step to align at full tile size */ BackToFront(); - Delay(GAME_FRAME_DELAY); + Delay(wait_delay_value); } } } @@ -2130,11 +2527,21 @@ void RelocatePlayer(int x, int y, int element) void Explode(int ex, int ey, int phase, int mode) { int x, y; +#if 0 int num_phase = 9; +#endif + + /* !!! eliminate this variable !!! */ int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2); + +#if 1 + int last_phase; +#else int last_phase = num_phase * delay; int half_phase = (num_phase / 2) * delay; int first_phase_after_start = EX_PHASE_START + 1; +#endif + int border_element; if (game.explosions_delayed) { @@ -2149,13 +2556,13 @@ void Explode(int ex, int ey, int phase, int mode) #if 0 /* --- This is only really needed (and now handled) in "Impact()". --- */ /* do not explode moving elements that left the explode field in time */ - if (game.engine_version >= RELEASE_IDENT(2,2,0,7) && + if (game.engine_version >= VERSION_IDENT(2,2,0,7) && center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER)) return; #endif if (mode == EX_NORMAL || mode == EX_CENTER) - PlaySoundLevelAction(ex, ey, ACTION_EXPLODING); + PlayLevelSoundAction(ex, ey, ACTION_EXPLODING); /* remove things displayed in background while burning dynamite */ if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey])) @@ -2169,7 +2576,11 @@ void Explode(int ex, int ey, int phase, int mode) Feld[ex][ey] = center_element; } - for (y = ey - 1; y <= ey + 1; y++) for(x = ex - 1; x <= ex + 1; x++) +#if 1 + last_phase = element_info[center_element].explosion_delay; +#endif + + for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++) { int xx = x - ex + 1; int yy = y - ey + 1; @@ -2204,7 +2615,7 @@ void Explode(int ex, int ey, int phase, int mode) #else if ((IS_INDESTRUCTIBLE(element) && - (game.engine_version < VERSION_IDENT(2,2,0) || + (game.engine_version < VERSION_IDENT(2,2,0,0) || (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) || element == EL_FLAMES) continue; @@ -2248,22 +2659,22 @@ void Explode(int ex, int ey, int phase, int mode) RemoveField(x, y); #endif - if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey)) + if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey)) { switch(StorePlayer[ex][ey]) { case EL_PLAYER_2: - Store[x][y] = EL_EMERALD_RED; + Store[x][y] = EL_PLAYER_IS_EXPLODING_2; break; case EL_PLAYER_3: - Store[x][y] = EL_EMERALD; + Store[x][y] = EL_PLAYER_IS_EXPLODING_3; break; case EL_PLAYER_4: - Store[x][y] = EL_EMERALD_PURPLE; + Store[x][y] = EL_PLAYER_IS_EXPLODING_4; break; case EL_PLAYER_1: default: - Store[x][y] = EL_EMERALD_YELLOW; + Store[x][y] = EL_PLAYER_IS_EXPLODING_1; break; } @@ -2326,6 +2737,7 @@ void Explode(int ex, int ey, int phase, int mode) RemoveField(x, y); #else MovDir[x][y] = MovPos[x][y] = 0; + GfxDir[x][y] = MovDir[x][y]; AmoebaNr[x][y] = 0; #endif #endif @@ -2338,6 +2750,9 @@ void Explode(int ex, int ey, int phase, int mode) #endif ExplodePhase[x][y] = 1; +#if 1 + ExplodeDelay[x][y] = last_phase; +#endif Stop[x][y] = TRUE; } @@ -2354,6 +2769,10 @@ void Explode(int ex, int ey, int phase, int mode) x = ex; y = ey; +#if 1 + last_phase = ExplodeDelay[x][y]; +#endif + ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0); #ifdef DEBUG @@ -2374,6 +2793,69 @@ void Explode(int ex, int ey, int phase, int mode) } #endif +#if 1 + + border_element = Store2[x][y]; + if (IS_PLAYER(x, y)) + border_element = StorePlayer[x][y]; + + if (phase == element_info[border_element].ignition_delay || + phase == last_phase) + { + boolean border_explosion = FALSE; + +#if 1 + if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present) +#else + if (IS_PLAYER(x, y)) +#endif + { + KillHeroUnlessExplosionProtected(x, y); + border_explosion = TRUE; + +#if 0 + if (phase == last_phase) + printf("::: IS_PLAYER\n"); +#endif + } + else if (CAN_EXPLODE_BY_EXPLOSION(border_element)) + { + Feld[x][y] = Store2[x][y]; + Store2[x][y] = 0; + Bang(x, y); + border_explosion = TRUE; + +#if 0 + if (phase == last_phase) + printf("::: CAN_EXPLODE_BY_EXPLOSION\n"); +#endif + } + else if (border_element == EL_AMOEBA_TO_DIAMOND) + { + AmoebeUmwandeln(x, y); + Store2[x][y] = 0; + border_explosion = TRUE; + +#if 0 + if (phase == last_phase) + printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n", + element_info[border_element].explosion_delay, + element_info[border_element].ignition_delay, + phase); +#endif + } + +#if 1 + /* if an element just explodes due to another explosion (chain-reaction), + do not immediately end the new explosion when it was the last frame of + the explosion (as it would be done in the following "if"-statement!) */ + if (border_explosion && phase == last_phase) + return; +#endif + } + +#else + if (phase == first_phase_after_start) { int element = Store2[x][y]; @@ -2390,8 +2872,8 @@ void Explode(int ex, int ey, int phase, int mode) int element = Store2[x][y]; if (IS_PLAYER(x, y)) - KillHeroUnlessProtected(x, y); - else if (CAN_EXPLODE_BY_FIRE(element)) + KillHeroUnlessExplosionProtected(x, y); + else if (CAN_EXPLODE_BY_EXPLOSION(element)) { Feld[x][y] = Store2[x][y]; Store2[x][y] = 0; @@ -2400,6 +2882,7 @@ void Explode(int ex, int ey, int phase, int mode) else if (element == EL_AMOEBA_TO_DIAMOND) AmoebeUmwandeln(x, y); } +#endif if (phase == last_phase) { @@ -2409,17 +2892,42 @@ void Explode(int ex, int ey, int phase, int mode) Store[x][y] = Store2[x][y] = 0; GfxElement[x][y] = EL_UNDEFINED; + /* player can escape from explosions and might therefore be still alive */ + if (element >= EL_PLAYER_IS_EXPLODING_1 && + element <= EL_PLAYER_IS_EXPLODING_4) + Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ? + EL_EMPTY : + element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW : + element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED : + element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD : + EL_EMERALD_PURPLE); + + /* restore probably existing indestructible background element */ if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y])) element = Feld[x][y] = Back[x][y]; Back[x][y] = 0; MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0; + GfxDir[x][y] = MV_NO_MOVING; ChangeDelay[x][y] = 0; ChangePage[x][y] = -1; +#if 1 + InitField_WithBug2(x, y, FALSE); +#else InitField(x, y, FALSE); +#if 1 + /* !!! not needed !!! */ +#if 1 + if (game.engine_version < VERSION_IDENT(3,0,9,0) && + CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE) + InitMovDir(x, y); +#else if (CAN_MOVE(element)) InitMovDir(x, y); +#endif +#endif +#endif DrawLevelField(x, y); TestIfElementTouchesCustomElement(x, y); @@ -2427,13 +2935,17 @@ void Explode(int ex, int ey, int phase, int mode) if (GFX_CRUMBLED(element)) DrawLevelFieldCrumbledSandNeighbours(x, y); - if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present) + if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present) StorePlayer[x][y] = 0; if (ELEM_IS_PLAYER(element)) RelocatePlayer(x, y, element); } +#if 1 + else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y))) +#else else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y))) +#endif { #if 1 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING); @@ -2445,6 +2957,12 @@ void Explode(int ex, int ey, int phase, int mode) #endif int frame = getGraphicAnimationFrame(graphic, phase - delay); +#if 0 + printf("::: %d ['%s'] -> %d\n", GfxElement[x][y], + element_info[GfxElement[x][y]].token_name, + graphic); +#endif + if (phase == delay) DrawLevelFieldCrumbledSand(x, y); @@ -2466,6 +2984,7 @@ void Explode(int ex, int ey, int phase, int mode) void DynaExplode(int ex, int ey) { int i, j; + int dynabomb_element = Feld[ex][ey]; int dynabomb_size = 1; boolean dynabomb_xl = FALSE; struct PlayerInfo *player; @@ -2477,9 +2996,9 @@ void DynaExplode(int ex, int ey) { 0, +1 } }; - if (IS_ACTIVE_BOMB(Feld[ex][ey])) + if (IS_ACTIVE_BOMB(dynabomb_element)) { - player = &stored_player[Feld[ex][ey] - EL_DYNABOMB_PLAYER_1_ACTIVE]; + player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE]; dynabomb_size = player->dynabomb_size; dynabomb_xl = player->dynabomb_xl; player->dynabombs_left++; @@ -2487,9 +3006,9 @@ void DynaExplode(int ex, int ey) Explode(ex, ey, EX_PHASE_START, EX_CENTER); - for (i=0; i<4; i++) + for (i = 0; i < 4; i++) { - for (j=1; j<=dynabomb_size; j++) + for (j = 1; j <= dynabomb_size; j++) { int x = ex + j * xy[i % 4][0]; int y = ey + j * xy[i % 4][1]; @@ -2525,7 +3044,7 @@ void Bang(int x, int y) #endif #if 1 - if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y)) + if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y)) #else if (IS_PLAYER(x, y)) #endif @@ -2538,12 +3057,12 @@ void Bang(int x, int y) #if 0 #if 1 - PlaySoundLevelAction(x, y, ACTION_EXPLODING); + PlayLevelSoundAction(x, y, ACTION_EXPLODING); #else if (game.emulation == EMU_SUPAPLEX) - PlaySoundLevel(x, y, SND_SP_ELEMENT_EXPLODING); + PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING); else - PlaySoundLevel(x, y, SND_ELEMENT_EXPLODING); + PlayLevelSound(x, y, SND_ELEMENT_EXPLODING); #endif #endif @@ -2584,7 +3103,9 @@ void Bang(int x, int y) Explode(x, y, EX_PHASE_START, EX_CENTER); break; default: - if (CAN_EXPLODE_1X1(element)) + if (CAN_EXPLODE_DYNA(element)) + DynaExplode(x, y); + else if (CAN_EXPLODE_1X1(element)) Explode(x, y, EX_PHASE_START, EX_CENTER); else Explode(x, y, EX_PHASE_START, EX_NORMAL); @@ -2596,23 +3117,42 @@ void Bang(int x, int y) void SplashAcid(int x, int y) { - int element = Feld[x][y]; +#if 1 + if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) && + (!IN_LEV_FIELD(x - 1, y - 2) || + !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2)))) + Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT; - if (element != EL_ACID_SPLASH_LEFT && - element != EL_ACID_SPLASH_RIGHT) - { - PlaySoundLevel(x, y, SND_ACID_SPLASHING); + if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) && + (!IN_LEV_FIELD(x + 1, y - 2) || + !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2)))) + Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT; - if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) && - (!IN_LEV_FIELD(x-1, y-1) || - !CAN_FALL(MovingOrBlocked2Element(x-1, y-1)))) - Feld[x-1][y] = EL_ACID_SPLASH_LEFT; + PlayLevelSound(x, y, SND_ACID_SPLASHING); +#else + /* input: position of element entering acid (obsolete) */ + + int element = Feld[x][y]; + + if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID) + return; - if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) && - (!IN_LEV_FIELD(x+1, y-1) || - !CAN_FALL(MovingOrBlocked2Element(x+1, y-1)))) - Feld[x+1][y] = EL_ACID_SPLASH_RIGHT; + if (element != EL_ACID_SPLASH_LEFT && + element != EL_ACID_SPLASH_RIGHT) + { + PlayLevelSound(x, y, SND_ACID_SPLASHING); + + if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) && + (!IN_LEV_FIELD(x - 1, y - 1) || + !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1)))) + Feld[x - 1][y] = EL_ACID_SPLASH_LEFT; + + if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) && + (!IN_LEV_FIELD(x + 1, y - 1) || + !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1)))) + Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT; } +#endif } static void InitBeltMovement() @@ -2635,11 +3175,11 @@ static void InitBeltMovement() int x, y, i, j; /* set frame order for belt animation graphic according to belt direction */ - for (i=0; i<4; i++) + for (i = 0; i < 4; i++) { int belt_nr = i; - for (j=0; j<3; j++) + for (j = 0; j < 3; j++) { int element = belt_base_active_element[belt_nr] + j; int graphic = el2img(element); @@ -2651,13 +3191,13 @@ static void InitBeltMovement() } } - for(y=0; y= RELEASE_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) && + if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) && ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX) object_hit = FALSE; @@ -2977,7 +3529,7 @@ void Impact(int x, int y) if (!lastline && smashed == EL_ACID) /* element falls into acid */ { - SplashAcid(x, y); + SplashAcid(x, y + 1); return; } @@ -2997,12 +3549,12 @@ void Impact(int x, int y) else if (impact && element == EL_PEARL) { Feld[x][y] = EL_PEARL_BREAKING; - PlaySoundLevel(x, y, SND_PEARL_BREAKING); + PlayLevelSound(x, y, SND_PEARL_BREAKING); return; } else if (impact && CheckElementChange(x, y, element, CE_IMPACT)) { - PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT); + PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT); return; } @@ -3010,7 +3562,7 @@ void Impact(int x, int y) if (impact && element == EL_AMOEBA_DROP) { if (object_hit && IS_PLAYER(x, y + 1)) - KillHeroUnlessProtected(x, y + 1); + KillHeroUnlessEnemyProtected(x, y + 1); else if (object_hit && smashed == EL_PENGUIN) Bang(x, y + 1); else @@ -3035,15 +3587,15 @@ void Impact(int x, int y) EL_BD_MAGIC_WALL_ACTIVE); /* activate magic wall / mill */ - for (yy=0; yy rnd_value / 8) MovDir[x][y] = old_move_dir; else if (can_turn_left && can_turn_right) @@ -3385,8 +3946,25 @@ void TurnRound(int x, int y) xx = x + move_xy[MovDir[x][y]].x; yy = y + move_xy[MovDir[x][y]].y; +#if 0 + if (FrameCounter < 1 && x == 0 && y == 29) + printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y], + xx, yy, Feld[xx][yy], + FrameCounter); +#endif + +#if 1 + if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy)) + MovDir[x][y] = old_move_dir; +#else if (!IS_FREE(xx, yy)) MovDir[x][y] = old_move_dir; +#endif + +#if 0 + if (FrameCounter < 1 && x == 0 && y == 29) + printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter); +#endif MovDelay[x][y] = 0; } @@ -3446,7 +4024,7 @@ void TurnRound(int x, int y) { int i; - for (i=0; ijx, jy = player->jy; @@ -3480,7 +4058,7 @@ void TurnRound(int x, int y) { 0, +1 } }; - for (i=0; i<4; i++) + for (i = 0; i < 4; i++) { int ex = x + xy[i % 4][0]; int ey = y + xy[i % 4][1]; @@ -3576,17 +4154,30 @@ void TurnRound(int x, int y) } } } - else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS || - element_info[element].move_pattern == MV_TURNING_LEFT || - element_info[element].move_pattern == MV_TURNING_RIGHT) + else if (move_pattern == MV_TURNING_LEFT || + move_pattern == MV_TURNING_RIGHT || + move_pattern == MV_TURNING_LEFT_RIGHT || + move_pattern == MV_TURNING_RIGHT_LEFT || + move_pattern == MV_TURNING_RANDOM || + move_pattern == MV_ALL_DIRECTIONS) { - boolean can_turn_left = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y); - boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y); + boolean can_turn_left = + CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y); + boolean can_turn_right = + CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y); - if (element_info[element].move_pattern == MV_TURNING_LEFT) + if (move_pattern == MV_TURNING_LEFT) MovDir[x][y] = left_dir; - else if (element_info[element].move_pattern == MV_TURNING_RIGHT) + else if (move_pattern == MV_TURNING_RIGHT) MovDir[x][y] = right_dir; + else if (move_pattern == MV_TURNING_LEFT_RIGHT) + MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir); + else if (move_pattern == MV_TURNING_RIGHT_LEFT) + MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir); + else if (move_pattern == MV_TURNING_RANDOM) + MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir : + can_turn_right && !can_turn_left ? right_dir : + RND(2) ? left_dir : right_dir); else if (can_turn_left && can_turn_right) MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir); else if (can_turn_left) @@ -3598,50 +4189,49 @@ void TurnRound(int x, int y) MovDelay[x][y] = GET_NEW_MOVE_DELAY(element); } - else if (element_info[element].move_pattern == MV_HORIZONTAL || - element_info[element].move_pattern == MV_VERTICAL) + else if (move_pattern == MV_HORIZONTAL || + move_pattern == MV_VERTICAL) { - if (element_info[element].move_pattern & old_move_dir) + if (move_pattern & old_move_dir) MovDir[x][y] = back_dir; - else if (element_info[element].move_pattern == MV_HORIZONTAL) + else if (move_pattern == MV_HORIZONTAL) MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT); - else if (element_info[element].move_pattern == MV_VERTICAL) + else if (move_pattern == MV_VERTICAL) MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN); MovDelay[x][y] = GET_NEW_MOVE_DELAY(element); } - else if (element_info[element].move_pattern & MV_ANY_DIRECTION) + else if (move_pattern & MV_ANY_DIRECTION) { - MovDir[x][y] = element_info[element].move_pattern; + MovDir[x][y] = move_pattern; MovDelay[x][y] = GET_NEW_MOVE_DELAY(element); } - else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE) + else if (move_pattern == MV_ALONG_LEFT_SIDE) { - if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y)) + if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y)) MovDir[x][y] = left_dir; - else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y)) + else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y)) MovDir[x][y] = right_dir; if (MovDir[x][y] != old_move_dir) MovDelay[x][y] = GET_NEW_MOVE_DELAY(element); } - else if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE) + else if (move_pattern == MV_ALONG_RIGHT_SIDE) { - if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y)) + if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y)) MovDir[x][y] = right_dir; - else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y)) + else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y)) MovDir[x][y] = left_dir; if (MovDir[x][y] != old_move_dir) MovDelay[x][y] = GET_NEW_MOVE_DELAY(element); } - else if (element_info[element].move_pattern == MV_TOWARDS_PLAYER || - element_info[element].move_pattern == MV_AWAY_FROM_PLAYER) + else if (move_pattern == MV_TOWARDS_PLAYER || + move_pattern == MV_AWAY_FROM_PLAYER) { int attr_x = -1, attr_y = -1; int newx, newy; - boolean move_away = - (element_info[element].move_pattern == MV_AWAY_FROM_PLAYER); + boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER); if (AllPlayersGone) { @@ -3652,7 +4242,7 @@ void TurnRound(int x, int y) { int i; - for (i=0; ijx, jy = player->jy; @@ -3690,33 +4280,130 @@ void TurnRound(int x, int y) new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL); Moving2Blocked(x, y, &newx, &newy); - if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy)) + if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)) return; MovDir[x][y] = new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL); Moving2Blocked(x, y, &newx, &newy); - if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy)) + if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)) return; MovDir[x][y] = old_move_dir; } } - else if (element_info[element].move_pattern == MV_WHEN_PUSHED) + else if (move_pattern == MV_WHEN_PUSHED || + move_pattern == MV_WHEN_DROPPED) { if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y)) MovDir[x][y] = MV_NO_MOVING; MovDelay[x][y] = 0; } + else if (move_pattern & MV_MAZE_RUNNER_STYLE) + { + static int test_xy[7][2] = + { + { 0, -1 }, + { -1, 0 }, + { +1, 0 }, + { 0, +1 }, + { 0, -1 }, + { -1, 0 }, + { +1, 0 }, + }; + static int test_dir[7] = + { + MV_UP, + MV_LEFT, + MV_RIGHT, + MV_DOWN, + MV_UP, + MV_LEFT, + MV_RIGHT, + }; + boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER); + int move_preference = -1000000; /* start with very low preference */ + int new_move_dir = MV_NO_MOVING; + int start_test = RND(4); + int i; + + for (i = 0; i < 4; i++) + { + int move_dir = test_dir[start_test + i]; + int move_dir_preference; + + xx = x + test_xy[start_test + i][0]; + yy = y + test_xy[start_test + i][1]; + + if (hunter_mode && IN_LEV_FIELD(xx, yy) && + (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING)) + { + new_move_dir = move_dir; + + break; + } + + if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy)) + continue; + + move_dir_preference = -1 * RunnerVisit[xx][yy]; + if (hunter_mode && PlayerVisit[xx][yy] > 0) + move_dir_preference = PlayerVisit[xx][yy]; + + if (move_dir_preference > move_preference) + { + /* prefer field that has not been visited for the longest time */ + move_preference = move_dir_preference; + new_move_dir = move_dir; + } + else if (move_dir_preference == move_preference && + move_dir == old_move_dir) + { + /* prefer last direction when all directions are preferred equally */ + move_preference = move_dir_preference; + new_move_dir = move_dir; + } + } + + MovDir[x][y] = new_move_dir; + if (old_move_dir != new_move_dir) + MovDelay[x][y] = 9; + } +} + +static void TurnRound(int x, int y) +{ + int direction = MovDir[x][y]; + +#if 0 + GfxDir[x][y] = MovDir[x][y]; +#endif + + TurnRoundExt(x, y); + +#if 1 + GfxDir[x][y] = MovDir[x][y]; +#endif + + if (direction != MovDir[x][y]) + GfxFrame[x][y] = 0; + +#if 1 + if (MovDelay[x][y]) + GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction); +#else + if (MovDelay[x][y]) + GfxAction[x][y] = ACTION_WAITING; +#endif } static boolean JustBeingPushed(int x, int y) { int i; - for (i=0; i 0 && IS_PLAYER(x - 1, y)) || - (x < lev_fieldx-1 && IS_PLAYER(x + 1, y))) + if ((x > 0 && IS_PLAYER(x - 1, y)) || + (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y))) if (JustBeingPushed(x, y)) return; @@ -3763,9 +4457,9 @@ void StartMoving(int x, int y) Feld[x][y] = EL_QUICKSAND_EMPTYING; Store[x][y] = EL_ROCK; #if 1 - PlaySoundLevelAction(x, y, ACTION_EMPTYING); + PlayLevelSoundAction(x, y, ACTION_EMPTYING); #else - PlaySoundLevel(x, y, SND_QUICKSAND_EMPTYING); + PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING); #endif } else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY) @@ -3785,9 +4479,9 @@ void StartMoving(int x, int y) Store[x][y + 1] = Store[x][y]; Store[x][y] = 0; #if 1 - PlaySoundLevelAction(x, y, ACTION_FILLING); + PlayLevelSoundAction(x, y, ACTION_FILLING); #else - PlaySoundLevel(x, y, SND_QUICKSAND_FILLING); + PlayLevelSound(x, y, SND_QUICKSAND_FILLING); #endif } } @@ -3800,9 +4494,9 @@ void StartMoving(int x, int y) Feld[x][y] = EL_QUICKSAND_FILLING; Store[x][y] = element; #if 1 - PlaySoundLevelAction(x, y, ACTION_FILLING); + PlayLevelSoundAction(x, y, ACTION_FILLING); #else - PlaySoundLevel(x, y, SND_QUICKSAND_FILLING); + PlayLevelSound(x, y, SND_QUICKSAND_FILLING); #endif } else if (element == EL_MAGIC_WALL_FULL) @@ -3879,7 +4573,7 @@ void StartMoving(int x, int y) else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID) #endif { - SplashAcid(x, y); + SplashAcid(x, y + 1); InitMovingField(x, y, MV_DOWN); started_moving = TRUE; @@ -3891,16 +4585,16 @@ void StartMoving(int x, int y) #endif } #if 1 - else if ((game.engine_version < RELEASE_IDENT(2,2,0,7) && + else if ((game.engine_version < VERSION_IDENT(2,2,0,7) && CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] && (Feld[x][y + 1] == EL_BLOCKED)) || - (game.engine_version >= VERSION_IDENT(3,0,7) && + (game.engine_version >= VERSION_IDENT(3,0,7,0) && CAN_SMASH(element) && WasJustFalling[x][y] && (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1)))) #else #if 1 - else if (game.engine_version < RELEASE_IDENT(2,2,0,7) && + else if (game.engine_version < VERSION_IDENT(2,2,0,7) && CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED && WasJustMoving[x][y] && !Pushed[x][y + 1]) #else @@ -3920,9 +4614,14 @@ void StartMoving(int x, int y) element; also, the case of the player being the element to smash was simply not covered here... :-/ ) */ +#if 0 + WasJustMoving[x][y] = 0; + WasJustFalling[x][y] = 0; +#endif + Impact(x, y); } - else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug) + else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug) { if (MovDir[x][y] == MV_NO_MOVING) { @@ -3998,7 +4697,11 @@ void StartMoving(int x, int y) started_moving = TRUE; } } +#if 0 + else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element)) +#else else if (IS_BELT_ACTIVE(Feld[x][y + 1])) +#endif { boolean left_is_free = (x > 0 && IS_FREE(x - 1, y)); boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y)); @@ -4008,19 +4711,35 @@ void StartMoving(int x, int y) if ((belt_dir == MV_LEFT && left_is_free) || (belt_dir == MV_RIGHT && right_is_free)) { +#if 1 + int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1); +#endif + InitMovingField(x, y, belt_dir); started_moving = TRUE; +#if 1 + Pushed[x][y] = TRUE; + Pushed[nextx][y] = TRUE; +#endif + GfxAction[x][y] = ACTION_DEFAULT; } + else + { + MovDir[x][y] = 0; /* if element was moving, stop it */ + } } } /* not "else if" because of elements that can fall and move (EL_SPRING) */ if (CAN_MOVE(element) && !started_moving) { + int move_pattern = element_info[element].move_pattern; int newx, newy; + Moving2Blocked(x, y, &newx, &newy); + #if 1 if (IS_PUSHABLE(element) && JustBeingPushed(x, y)) return; @@ -4032,6 +4751,43 @@ void StartMoving(int x, int y) return; #endif +#if 1 + if (game.engine_version >= VERSION_IDENT(3,0,9,0) && + WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) && + (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy))) + { +#if 0 + printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n", + element, element_info[element].token_name, + WasJustMoving[x][y], + HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING), + HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING), + HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING), + HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT)); +#endif + +#if 1 + WasJustMoving[x][y] = 0; +#endif + + TestIfElementHitsCustomElement(x, y, MovDir[x][y]); + +#if 0 + if (Feld[x][y] != element) /* element has changed */ + { + element = Feld[x][y]; + move_pattern = element_info[element].move_pattern; + + if (!CAN_MOVE(element)) + return; + } +#else + if (Feld[x][y] != element) /* element has changed */ + return; +#endif + } +#endif + #if 0 #if 0 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN) @@ -4046,6 +4802,11 @@ void StartMoving(int x, int y) #endif #endif +#if 0 + if (FrameCounter < 1 && x == 0 && y == 29) + printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter); +#endif + if (!MovDelay[x][y]) /* start new movement phase */ { /* all objects that can change their move direction after each step @@ -4054,12 +4815,20 @@ void StartMoving(int x, int y) if (element != EL_YAMYAM && element != EL_DARK_YAMYAM && element != EL_PACMAN && - !(element_info[element].move_pattern & MV_ANY_DIRECTION) && - element_info[element].move_pattern != MV_TURNING_LEFT && - element_info[element].move_pattern != MV_TURNING_RIGHT) + !(move_pattern & MV_ANY_DIRECTION) && + move_pattern != MV_TURNING_LEFT && + move_pattern != MV_TURNING_RIGHT && + move_pattern != MV_TURNING_LEFT_RIGHT && + move_pattern != MV_TURNING_RIGHT_LEFT && + move_pattern != MV_TURNING_RANDOM) { TurnRound(x, y); +#if 0 + if (FrameCounter < 1 && x == 0 && y == 29) + printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter); +#endif + if (MovDelay[x][y] && (element == EL_BUG || element == EL_SPACESHIP || element == EL_SP_SNIKSNAK || @@ -4088,7 +4857,13 @@ void StartMoving(int x, int y) /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */ ResetGfxAnimation(x, y); #endif + +#if 0 + if (GfxAction[x][y] != ACTION_WAITING) + printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]); + GfxAction[x][y] = ACTION_WAITING; +#endif } if (element == EL_ROBOT || @@ -4103,7 +4878,7 @@ void StartMoving(int x, int y) #else DrawLevelElementAnimationIfNeeded(x, y, element); #endif - PlaySoundLevelAction(x, y, ACTION_WAITING); + PlayLevelSoundAction(x, y, ACTION_WAITING); } else if (element == EL_SP_ELECTRON) DrawLevelElementAnimationIfNeeded(x, y, element); @@ -4130,9 +4905,9 @@ void StartMoving(int x, int y) else DrawLevelField(x, y); - PlaySoundLevelActionIfLoop(x, y, ACTION_ATTACKING); + PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING); - for (i=1; i <= 3; i++) + for (i = 1; i <= 3; i++) { int xx = x + i * dx; int yy = y + i * dy; @@ -4147,7 +4922,7 @@ void StartMoving(int x, int y) { int flamed = MovingOrBlocked2Element(xx, yy); - if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed)) + if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed)) Bang(xx, yy); else RemoveMovingField(xx, yy); @@ -4170,15 +4945,17 @@ void StartMoving(int x, int y) if (MovDelay[x][y]) /* element still has to wait some time */ { - PlaySoundLevelAction(x, y, ACTION_WAITING); + PlayLevelSoundAction(x, y, ACTION_WAITING); return; } +#if 0 /* special case of "moving" animation of waiting elements (FIX THIS !!!); for all other elements GfxAction will be set by InitMovingField() */ if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY) GfxAction[x][y] = ACTION_MOVING; +#endif } /* now make next step */ @@ -4187,10 +4964,11 @@ void StartMoving(int x, int y) if (DONT_COLLIDE_WITH(element) && IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) && - !PLAYER_PROTECTED(newx, newy)) + !PLAYER_ENEMY_PROTECTED(newx, newy)) { #if 1 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]); + return; #else /* player killed by element which is deadly when colliding with */ @@ -4200,6 +4978,18 @@ void StartMoving(int x, int y) #endif } +#if 1 +#if 1 + else if (CAN_MOVE_INTO_ACID(element) && + IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID && + (MovDir[x][y] == MV_DOWN || + game.engine_version > VERSION_IDENT(3,0,8,0))) +#else + else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN && + IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID) +#endif +#else + else if ((element == EL_PENGUIN || element == EL_ROBOT || element == EL_SATELLITE || @@ -4207,18 +4997,24 @@ void StartMoving(int x, int y) IS_CUSTOM_ELEMENT(element)) && IN_LEV_FIELD(newx, newy) && MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID) +#endif { - SplashAcid(x, y); + SplashAcid(newx, newy); Store[x][y] = EL_ACID; } else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy)) { if (Feld[newx][newy] == EL_EXIT_OPEN) { +#if 1 + RemoveField(x, y); + DrawLevelField(x, y); +#else Feld[x][y] = EL_EMPTY; DrawLevelField(x, y); +#endif - PlaySoundLevel(newx, newy, SND_PENGUIN_PASSING); + PlayLevelSound(newx, newy, SND_PENGUIN_PASSING); if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy))) DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0); @@ -4231,10 +5027,10 @@ void StartMoving(int x, int y) } else if (IS_FOOD_PENGUIN(Feld[newx][newy])) { - if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING) + if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING) DrawLevelField(newx, newy); else - MovDir[x][y] = MV_NO_MOVING; + GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING; } else if (!IS_FREE(newx, newy)) { @@ -4244,6 +5040,7 @@ void StartMoving(int x, int y) DrawPlayerField(x, y); else DrawLevelField(x, y); + return; } } @@ -4259,7 +5056,7 @@ void StartMoving(int x, int y) DrawLevelField(newx, newy); } - PlaySoundLevel(x, y, SND_PIG_DIGGING); + PlayLevelSound(x, y, SND_PIG_DIGGING); } else if (!IS_FREE(newx, newy)) { @@ -4267,9 +5064,80 @@ void StartMoving(int x, int y) DrawPlayerField(x, y); else DrawLevelField(x, y); + return; } } + +#if 1 + + /* + else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy)) + */ + + else if (IS_CUSTOM_ELEMENT(element) && + CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy) + +#if 0 + && + !IS_FREE(newx, newy) +#endif + +) + { + int new_element = Feld[newx][newy]; + +#if 0 + printf("::: '%s' digs '%s' [%d]\n", + element_info[element].token_name, + element_info[Feld[newx][newy]].token_name, + StorePlayer[newx][newy]); +#endif + + if (!IS_FREE(newx, newy)) + { + int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING : + IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING : + ACTION_BREAKING); + + /* no element can dig solid indestructible elements */ + if (IS_INDESTRUCTIBLE(new_element) && + !IS_DIGGABLE(new_element) && + !IS_COLLECTIBLE(new_element)) + return; + + if (AmoebaNr[newx][newy] && + (new_element == EL_AMOEBA_FULL || + new_element == EL_BD_AMOEBA || + new_element == EL_AMOEBA_GROWING)) + { + AmoebaCnt[AmoebaNr[newx][newy]]--; + AmoebaCnt2[AmoebaNr[newx][newy]]--; + } + + if (IS_MOVING(newx, newy)) + RemoveMovingField(newx, newy); + else + { + RemoveField(newx, newy); + DrawLevelField(newx, newy); + } + + PlayLevelSoundAction(x, y, action); + } + + if (new_element == element_info[element].move_enter_element) + element_info[element].can_leave_element = TRUE; + + if (move_pattern & MV_MAZE_RUNNER_STYLE) + { + RunnerVisit[x][y] = FrameCounter; + PlayerVisit[x][y] /= 8; /* expire player visit path */ + } + } + +#endif + else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy)) { if (!IS_FREE(newx, newy)) @@ -4308,7 +5176,7 @@ void StartMoving(int x, int y) else DrawLevelField(x, y); - PlaySoundLevel(x, y, SND_DRAGON_ATTACKING); + PlayLevelSound(x, y, SND_DRAGON_ATTACKING); MovDelay[x][y] = 50; @@ -4333,7 +5201,7 @@ void StartMoving(int x, int y) DrawLevelField(newx, newy); } - PlaySoundLevel(x, y, SND_YAMYAM_DIGGING); + PlayLevelSound(x, y, SND_YAMYAM_DIGGING); } else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) && IS_FOOD_DARK_YAMYAM(Feld[newx][newy])) @@ -4346,15 +5214,22 @@ void StartMoving(int x, int y) AmoebaCnt[AmoebaNr[newx][newy]]--; } +#if 0 + /* !!! test !!! */ + if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy)) +#else if (IS_MOVING(newx, newy)) +#endif + { RemoveMovingField(newx, newy); + } else { Feld[newx][newy] = EL_EMPTY; DrawLevelField(newx, newy); } - PlaySoundLevel(x, y, SND_DARK_YAMYAM_DIGGING); + PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING); } else if ((element == EL_PACMAN || element == EL_MOLE) && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy])) @@ -4370,20 +5245,21 @@ void StartMoving(int x, int y) if (element == EL_MOLE) { Feld[newx][newy] = EL_AMOEBA_SHRINKING; - PlaySoundLevel(x, y, SND_MOLE_DIGGING); + PlayLevelSound(x, y, SND_MOLE_DIGGING); ResetGfxAnimation(x, y); GfxAction[x][y] = ACTION_DIGGING; DrawLevelField(x, y); MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */ + return; /* wait for shrinking amoeba */ } else /* element == EL_PACMAN */ { Feld[newx][newy] = EL_EMPTY; DrawLevelField(newx, newy); - PlaySoundLevel(x, y, SND_PACMAN_DIGGING); + PlayLevelSound(x, y, SND_PACMAN_DIGGING); } } else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) && @@ -4422,7 +5298,7 @@ void StartMoving(int x, int y) TestIfBadThingTouchesHero(x, y); #if 0 - PlaySoundLevelAction(x, y, ACTION_WAITING); + PlayLevelSoundAction(x, y, ACTION_WAITING); #endif return; @@ -4430,7 +5306,7 @@ void StartMoving(int x, int y) InitMovingField(x, y, MovDir[x][y]); - PlaySoundLevelAction(x, y, ACTION_MOVING); + PlayLevelSoundAction(x, y, ACTION_MOVING); } if (MovDir[x][y]) @@ -4440,17 +5316,33 @@ void StartMoving(int x, int y) void ContinueMoving(int x, int y) { int element = Feld[x][y]; + struct ElementInfo *ei = &element_info[element]; int direction = MovDir[x][y]; int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0); int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0); int newx = x + dx, newy = y + dy; +#if 0 int nextx = newx + dx, nexty = newy + dy; - boolean pushed = Pushed[x][y]; +#endif +#if 1 + boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y)); + boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y)); +#else + boolean pushed_by_player = Pushed[x][y]; +#endif MovPos[x][y] += getElementMoveStepsize(x, y); - if (pushed) /* special case: moving object pushed by player */ +#if 0 + if (pushed_by_player && IS_PLAYER(x, y)) + { + /* special case: moving object pushed by player */ + MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos)); + } +#else + if (pushed_by_player) /* special case: moving object pushed by player */ MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos)); +#endif if (ABS(MovPos[x][y]) < TILEX) { @@ -4546,13 +5438,32 @@ void ContinueMoving(int x, int y) /* copy animation control values to new field */ GfxFrame[newx][newy] = GfxFrame[x][y]; - GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */ GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */ + GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */ + GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */ Pushed[x][y] = Pushed[newx][newy] = FALSE; ResetGfxAnimation(x, y); /* reset animation values for old field */ +#if 1 + /* some elements can leave other elements behind after moving */ + if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) && + ei->move_leave_element != EL_EMPTY && + (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || + ei->can_leave_element_last)) + { + Feld[x][y] = ei->move_leave_element; + InitField(x, y, FALSE); + + if (GFX_CRUMBLED(Feld[x][y])) + DrawLevelFieldCrumbledSandNeighbours(x, y); + } + + ei->can_leave_element_last = ei->can_leave_element; + ei->can_leave_element = FALSE; +#endif + #if 0 /* 2.1.1 (does not work correctly for spring) */ if (!CAN_MOVE(element)) @@ -4572,7 +5483,7 @@ void ContinueMoving(int x, int y) if (!CAN_MOVE(element) || (CAN_FALL(element) && direction == MV_DOWN)) - MovDir[newx][newy] = 0; + GfxDir[x][y] = MovDir[newx][newy] = 0; #endif #endif @@ -4583,12 +5494,19 @@ void ContinueMoving(int x, int y) Stop[newx][newy] = TRUE; /* ignore this element until the next frame */ /* prevent pushed element from moving on in pushed direction */ - if (pushed && CAN_MOVE(element) && + if (pushed_by_player && CAN_MOVE(element) && element_info[element].move_pattern & MV_ANY_DIRECTION && !(element_info[element].move_pattern & direction)) TurnRound(newx, newy); - if (!pushed) /* special case: moving object pushed by player */ +#if 1 + /* prevent elements on conveyor belt from moving on in last direction */ + if (pushed_by_conveyor && CAN_FALL(element) && + direction & MV_HORIZONTAL) + MovDir[newx][newy] = 0; +#endif + + if (!pushed_by_player) { WasJustMoving[newx][newy] = 3; @@ -4600,7 +5518,9 @@ void ContinueMoving(int x, int y) { TestIfBadThingTouchesHero(newx, newy); TestIfBadThingTouchesFriend(newx, newy); - TestIfBadThingTouchesOtherBadThing(newx, newy); + + if (!IS_CUSTOM_ELEMENT(element)) + TestIfBadThingTouchesOtherBadThing(newx, newy); } else if (element == EL_PENGUIN) TestIfFriendTouchesBadThing(newx, newy); @@ -4610,7 +5530,7 @@ void ContinueMoving(int x, int y) Impact(x, newy); #if 1 - TestIfElementTouchesCustomElement(x, y); /* for empty space */ + TestIfElementTouchesCustomElement(x, y); /* empty or new element */ #endif #if 0 @@ -4618,9 +5538,82 @@ void ContinueMoving(int x, int y) ChangeElement(newx, newy, ChangePage[newx][newy]); #endif +#if 1 + + TestIfElementHitsCustomElement(newx, newy, direction); + +#else + if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty)) - CheckElementSideChange(newx, newy, Feld[newx][newy], direction, - CE_COLLISION, -1); + { + int hitting_element = Feld[newx][newy]; + + /* !!! fix side (direction) orientation here and elsewhere !!! */ + CheckElementSideChange(newx, newy, hitting_element, + direction, CE_HITTING_SOMETHING, -1); + +#if 0 + if (IN_LEV_FIELD(nextx, nexty)) + { + int opposite_direction = MV_DIR_OPPOSITE(direction); + int hitting_side = direction; + int touched_side = opposite_direction; + int touched_element = MovingOrBlocked2Element(nextx, nexty); + boolean object_hit = (!IS_MOVING(nextx, nexty) || + MovDir[nextx][nexty] != direction || + ABS(MovPos[nextx][nexty]) <= TILEY / 2); + + if (object_hit) + { + int i; + + CheckElementSideChange(nextx, nexty, touched_element, + opposite_direction, CE_HIT_BY_SOMETHING, -1); + + if (IS_CUSTOM_ELEMENT(hitting_element) && + HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING)) + { + for (i = 0; i < element_info[hitting_element].num_change_pages; i++) + { + struct ElementChangeInfo *change = + &element_info[hitting_element].change_page[i]; + + if (change->can_change && + change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) && + change->sides & touched_side && + change->trigger_element == touched_element) + { + CheckElementSideChange(newx, newy, hitting_element, + CH_SIDE_ANY, CE_OTHER_IS_HITTING, i); + break; + } + } + } + + if (IS_CUSTOM_ELEMENT(touched_element) && + HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT)) + { + for (i = 0; i < element_info[touched_element].num_change_pages; i++) + { + struct ElementChangeInfo *change = + &element_info[touched_element].change_page[i]; + + if (change->can_change && + change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) && + change->sides & hitting_side && + change->trigger_element == hitting_element) + { + CheckElementSideChange(nextx, nexty, touched_element, + CH_SIDE_ANY, CE_OTHER_GETS_HIT, i); + break; + } + } + } + } + } +#endif + } +#endif TestIfPlayerTouchesCustomElement(newx, newy); TestIfElementTouchesCustomElement(newx, newy); @@ -4639,7 +5632,7 @@ int AmoebeNachbarNr(int ax, int ay) { 0, +1 } }; - for (i=0; i<4; i++) + for (i = 0; i < 4; i++) { int x = ax + xy[i][0]; int y = ay + xy[i][1]; @@ -4669,7 +5662,7 @@ void AmoebenVereinigen(int ax, int ay) if (new_group_nr == 0) return; - for (i=0; i<4; i++) + for (i = 0; i < 4; i++) { x = ax + xy[i][0]; y = ay + xy[i][1]; @@ -4692,9 +5685,9 @@ void AmoebenVereinigen(int ax, int ay) AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr]; AmoebaCnt2[old_group_nr] = 0; - for (yy=0; yyrandom) return FALSE; - for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++) + for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++) { int ex = x + xx - 1; int ey = y + yy - 1; @@ -5664,14 +6666,14 @@ static boolean ChangeElementNow(int x, int y, int element, int page) } if (something_has_changed) - PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING); + PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING); } } else { ChangeElementNowExt(x, y, change->target_element); - PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING); + PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING); } return TRUE; @@ -5712,7 +6714,7 @@ static void ChangeElement(int x, int y, int page) if (ChangeDelay[x][y] != 0) /* continue element change */ { - int graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]); + int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); if (IS_ANIMATED(graphic)) DrawLevelGraphicAnimationIfNeeded(x, y, graphic); @@ -5728,7 +6730,11 @@ static void ChangeElement(int x, int y, int page) ChangePage[x][y] = -1; } +#if 0 + if (IS_MOVING(x, y) && !change->explode) +#else if (IS_MOVING(x, y)) /* never change a running system ;-) */ +#endif { ChangeDelay[x][y] = 1; /* try change after next move step */ ChangePage[x][y] = page; /* remember page to use for change */ @@ -5754,7 +6760,7 @@ static boolean CheckTriggeredElementSideChange(int lx, int ly, if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event))) return FALSE; - for (i=0; i < NUM_CUSTOM_ELEMENTS; i++) + for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) { int element = EL_CUSTOM_START + i; @@ -5764,14 +6770,28 @@ static boolean CheckTriggeredElementSideChange(int lx, int ly, if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event)) continue; - for (j=0; j < element_info[element].num_change_pages; j++) + for (j = 0; j < element_info[element].num_change_pages; j++) { struct ElementChangeInfo *change = &element_info[element].change_page[j]; if (change->can_change && +#if 1 + change->events & CH_EVENT_BIT(trigger_event) && +#endif change->sides & trigger_side && - change->trigger_element == trigger_element) +#if 1 + IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element) +#else + change->trigger_element == trigger_element +#endif + ) { +#if 0 + if (!(change->events & CH_EVENT_BIT(trigger_event))) + printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n", + trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event); +#endif + change_element = TRUE; page = j; @@ -5782,7 +6802,7 @@ static boolean CheckTriggeredElementSideChange(int lx, int ly, if (!change_element) continue; - for (y=0; ycan_change && + change->events & CH_EVENT_BIT(trigger_event) && + change->sides & side) + { + change_element = TRUE; + page = i; + + break; + } + } + + if (!change_element) + return FALSE; + } + +#else + + /* !!! this check misses pages with same event, but different side !!! */ + if (page < 0) page = element_info[element].event_page_nr[trigger_event]; if (!(element_info[element].change_page[page].sides & side)) return FALSE; +#endif ChangeDelay[x][y] = 1; ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event); @@ -5838,6 +6888,271 @@ static boolean CheckElementChange(int x, int y, int element, int trigger_event) return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1); } +static void PlayPlayerSound(struct PlayerInfo *player) +{ + int jx = player->jx, jy = player->jy; + int element = player->element_nr; + int last_action = player->last_action_waiting; + int action = player->action_waiting; + + if (player->is_waiting) + { + if (action != last_action) + PlayLevelSoundElementAction(jx, jy, element, action); + else + PlayLevelSoundElementActionIfLoop(jx, jy, element, action); + } + else + { + if (action != last_action) + StopSound(element_info[element].sound[last_action]); + + if (last_action == ACTION_SLEEPING) + PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING); + } +} + +static void PlayAllPlayersSound() +{ + int i; + + for (i = 0; i < MAX_PLAYERS; i++) + if (stored_player[i].active) + PlayPlayerSound(&stored_player[i]); +} + +static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting) +{ + boolean last_waiting = player->is_waiting; + int move_dir = player->MovDir; + + player->last_action_waiting = player->action_waiting; + + if (is_waiting) + { + if (!last_waiting) /* not waiting -> waiting */ + { + player->is_waiting = TRUE; + + player->frame_counter_bored = + FrameCounter + + game.player_boring_delay_fixed + + SimpleRND(game.player_boring_delay_random); + player->frame_counter_sleeping = + FrameCounter + + game.player_sleeping_delay_fixed + + SimpleRND(game.player_sleeping_delay_random); + + InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir); + } + + if (game.player_sleeping_delay_fixed + + game.player_sleeping_delay_random > 0 && + player->anim_delay_counter == 0 && + player->post_delay_counter == 0 && + FrameCounter >= player->frame_counter_sleeping) + player->is_sleeping = TRUE; + else if (game.player_boring_delay_fixed + + game.player_boring_delay_random > 0 && + FrameCounter >= player->frame_counter_bored) + player->is_bored = TRUE; + + player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING : + player->is_bored ? ACTION_BORING : + ACTION_WAITING); + + if (player->is_sleeping) + { + if (player->num_special_action_sleeping > 0) + { + if (player->anim_delay_counter == 0 && player->post_delay_counter == 0) + { + int last_special_action = player->special_action_sleeping; + int num_special_action = player->num_special_action_sleeping; + int special_action = + (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 : + last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING : + last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ? + last_special_action + 1 : ACTION_SLEEPING); + int special_graphic = + el_act_dir2img(player->element_nr, special_action, move_dir); + + player->anim_delay_counter = + graphic_info[special_graphic].anim_delay_fixed + + SimpleRND(graphic_info[special_graphic].anim_delay_random); + player->post_delay_counter = + graphic_info[special_graphic].post_delay_fixed + + SimpleRND(graphic_info[special_graphic].post_delay_random); + + player->special_action_sleeping = special_action; + } + + if (player->anim_delay_counter > 0) + { + player->action_waiting = player->special_action_sleeping; + player->anim_delay_counter--; + } + else if (player->post_delay_counter > 0) + { + player->post_delay_counter--; + } + } + } + else if (player->is_bored) + { + if (player->num_special_action_bored > 0) + { + if (player->anim_delay_counter == 0 && player->post_delay_counter == 0) + { + int special_action = + ACTION_BORING_1 + SimpleRND(player->num_special_action_bored); + int special_graphic = + el_act_dir2img(player->element_nr, special_action, move_dir); + + player->anim_delay_counter = + graphic_info[special_graphic].anim_delay_fixed + + SimpleRND(graphic_info[special_graphic].anim_delay_random); + player->post_delay_counter = + graphic_info[special_graphic].post_delay_fixed + + SimpleRND(graphic_info[special_graphic].post_delay_random); + + player->special_action_bored = special_action; + } + + if (player->anim_delay_counter > 0) + { + player->action_waiting = player->special_action_bored; + player->anim_delay_counter--; + } + else if (player->post_delay_counter > 0) + { + player->post_delay_counter--; + } + } + } + } + else if (last_waiting) /* waiting -> not waiting */ + { + player->is_waiting = FALSE; + player->is_bored = FALSE; + player->is_sleeping = FALSE; + + player->frame_counter_bored = -1; + player->frame_counter_sleeping = -1; + + player->anim_delay_counter = 0; + player->post_delay_counter = 0; + + player->action_waiting = ACTION_DEFAULT; + + player->special_action_bored = ACTION_DEFAULT; + player->special_action_sleeping = ACTION_DEFAULT; + } +} + +#if 1 +static byte PlayerActions(struct PlayerInfo *player, byte player_action) +{ +#if 0 + static byte stored_player_action[MAX_PLAYERS]; + static int num_stored_actions = 0; +#endif + boolean moved = FALSE, snapped = FALSE, dropped = FALSE; + int left = player_action & JOY_LEFT; + int right = player_action & JOY_RIGHT; + int up = player_action & JOY_UP; + int down = player_action & JOY_DOWN; + int button1 = player_action & JOY_BUTTON_1; + int button2 = player_action & JOY_BUTTON_2; + int dx = (left ? -1 : right ? 1 : 0); + int dy = (up ? -1 : down ? 1 : 0); + +#if 0 + stored_player_action[player->index_nr] = 0; + num_stored_actions++; +#endif + +#if 0 + printf("::: player %d [%d]\n", player->index_nr, FrameCounter); +#endif + + if (!player->active || tape.pausing) + return 0; + +#if 0 + printf("::: [%d %d %d %d] [%d %d]\n", + left, right, up, down, button1, button2); +#endif + + if (player_action) + { +#if 0 + printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter); +#endif + + if (button1) + snapped = SnapField(player, dx, dy); + else + { + if (button2) + dropped = DropElement(player); + + moved = MovePlayer(player, dx, dy); + } + + if (tape.single_step && tape.recording && !tape.pausing) + { + if (button1 || (dropped && !moved)) + { + TapeTogglePause(TAPE_TOGGLE_AUTOMATIC); + SnapField(player, 0, 0); /* stop snapping */ + } + } + + SetPlayerWaiting(player, FALSE); + +#if 1 + return player_action; +#else + stored_player_action[player->index_nr] = player_action; +#endif + } + else + { +#if 0 + printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter); +#endif + + /* no actions for this player (no input at player's configured device) */ + + DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH); + SnapField(player, 0, 0); + CheckGravityMovement(player); + + if (player->MovPos == 0) + SetPlayerWaiting(player, TRUE); + + if (player->MovPos == 0) /* needed for tape.playing */ + player->is_moving = FALSE; + + player->is_dropping = FALSE; + + return 0; + } + +#if 0 + if (tape.recording && num_stored_actions >= MAX_PLAYERS) + { + printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter); + + TapeRecordAction(stored_player_action); + num_stored_actions = 0; + } +#endif +} + +#else + static void PlayerActions(struct PlayerInfo *player, byte player_action) { static byte stored_player_action[MAX_PLAYERS]; @@ -5855,11 +7170,15 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action) stored_player_action[player->index_nr] = 0; num_stored_actions++; + printf("::: player %d [%d]\n", player->index_nr, FrameCounter); + if (!player->active || tape.pausing) return; if (player_action) { + printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter); + if (button1) snapped = SnapField(player, dx, dy); else @@ -5883,9 +7202,11 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action) } else { + printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter); + /* no actions for this player (no input at player's configured device) */ - DigField(player, 0, 0, 0, 0, DF_NO_PUSH); + DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH); SnapField(player, 0, 0); CheckGravityMovement(player); @@ -5898,10 +7219,13 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action) if (tape.recording && num_stored_actions >= MAX_PLAYERS) { + printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter); + TapeRecordAction(stored_player_action); num_stored_actions = 0; } } +#endif void GameActions() { @@ -5911,6 +7235,9 @@ void GameActions() int i, x, y, element, graphic; byte *recorded_player_action; byte summarized_player_action = 0; +#if 1 + byte tape_action[MAX_PLAYERS]; +#endif if (game_status != GAME_MODE_PLAYING) return; @@ -5955,9 +7282,13 @@ void GameActions() if (tape.pausing) return; +#if 0 + printf("::: getting new tape action [%d]\n", FrameCounter); +#endif + recorded_player_action = (tape.playing ? TapePlayAction() : NULL); - for (i=0; ieffective_action = summarized_player_action; - for (i=0; ijx; int y = player->jy; +#if 1 + if (player->active && player->is_pushing && player->is_moving && + IS_MOVING(x, y) && + (game.engine_version < VERSION_IDENT(2,2,0,7) || + Feld[x][y] == EL_SPRING)) +#else if (player->active && player->is_pushing && player->is_moving && IS_MOVING(x, y)) +#endif { ContinueMoving(x, y); @@ -6023,7 +7382,7 @@ void GameActions() } #endif - for (y=0; y 0) @@ -6321,7 +7691,7 @@ void GameActions() game.magic_wall_time_left--; if (!game.magic_wall_time_left) { - for (y=0; yshield_deadly_time_left) - PlaySoundLevel(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE); + PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE); else if (player->shield_normal_time_left) - PlaySoundLevel(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE); + PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE); } } @@ -6378,7 +7748,7 @@ void GameActions() TimeFrames = 0; TimePlayed++; - for (i=0; i 0) + stored_player[i].drop_delay--; } #endif @@ -6474,7 +7848,7 @@ static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y) int min_x = x, min_y = y, max_x = x, max_y = y; int i; - for (i=0; iprogrammed_action) @@ -6557,12 +7963,19 @@ static void CheckGravityMovement(struct PlayerInfo *player) boolean player_is_moving_to_valid_field = (IN_LEV_FIELD(new_jx, new_jy) && (Feld[new_jx][new_jy] == EL_SP_BASE || - Feld[new_jx][new_jy] == EL_SAND)); + Feld[new_jx][new_jy] == EL_SAND || + (IS_SP_PORT(Feld[new_jx][new_jy]) && + canEnterSupaplexPort(new_jx, new_jy, dx, dy)))); /* !!! extend EL_SAND to anything diggable !!! */ + boolean player_is_standing_on_valid_field = + (IS_WALKABLE_INSIDE(Feld[jx][jy]) || + (IS_WALKABLE(Feld[jx][jy]) && + !(element_info[Feld[jx][jy]].access_direction & MV_DOWN))); + if (field_under_player_is_free && - !player_is_moving_to_valid_field && - !IS_WALKABLE_INSIDE(Feld[jx][jy])) + !player_is_standing_on_valid_field && + !player_is_moving_to_valid_field) player->programmed_action = MV_DOWN; } } @@ -6622,7 +8035,7 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, { if (element == EL_ACID && dx == 0 && dy == 1) { - SplashAcid(jx, jy); + SplashAcid(new_jx, new_jy); Feld[jx][jy] = EL_PLAYER_1; InitMovingField(jx, jy, MV_DOWN); Store[jx][jy] = EL_ACID; @@ -6635,7 +8048,7 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, return MF_MOVING; } - can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG); + can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG); if (can_move != MF_MOVING) return can_move; @@ -6653,6 +8066,12 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, player->MovPos = (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value); + player->step_counter++; + + player->drop_delay = 0; + + PlayerVisit[jx][jy] = FrameCounter; + ScrollPlayer(player, SCROLL_INIT); #if 0 @@ -6682,8 +8101,27 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) int old_jx = jx, old_jy = jy; int moved = MF_NO_ACTION; +#if 1 + if (!player->active) + return FALSE; + + if (!dx && !dy) + { + if (player->MovPos == 0) + { + player->is_moving = FALSE; + player->is_digging = FALSE; + player->is_collecting = FALSE; + player->is_snapping = FALSE; + player->is_pushing = FALSE; + } + + return FALSE; + } +#else if (!player->active || (!dx && !dy)) return FALSE; +#endif #if 0 if (!FrameReached(&player->move_delay, player->move_delay_value) && @@ -6838,6 +8276,8 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) player->is_switching = FALSE; #endif + player->is_dropping = FALSE; + #if 1 { @@ -6886,7 +8326,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) player->is_moving = FALSE; } - if (game.engine_version < VERSION_IDENT(3,0,7)) + if (game.engine_version < VERSION_IDENT(3,0,7,0)) { TestIfHeroTouchesBadThing(jx, jy); TestIfPlayerTouchesCustomElement(jx, jy); @@ -6918,6 +8358,7 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) #if 0 DrawPlayer(player); #endif + return; } else if (!FrameReached(&player->actual_frame_counter, 1)) @@ -6926,7 +8367,8 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize; player->GfxPos = move_stepsize * (player->MovPos / move_stepsize); - if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING) + if (!player->block_last_field && + Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING) Feld[last_jx][last_jy] = EL_EMPTY; /* before DrawPlayer() to draw correct player graphic for this case */ @@ -6939,6 +8381,21 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) if (player->MovPos == 0) /* player reached destination field */ { +#if 1 + if (player->move_delay_reset_counter > 0) + { + player->move_delay_reset_counter--; + + if (player->move_delay_reset_counter == 0) + { + /* continue with normal speed after quickly moving through gate */ + HALVE_PLAYER_SPEED(player); + + /* be able to make the next move without delay */ + player->move_delay = 0; + } + } +#else if (IS_PASSABLE(Feld[last_jx][last_jy])) { /* continue with normal speed after quickly moving through gate */ @@ -6947,6 +8404,11 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) /* be able to make the next move without delay */ player->move_delay = 0; } +#endif + + if (player->block_last_field && + Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING) + Feld[last_jx][last_jy] = EL_EMPTY; player->last_jx = jx; player->last_jy = jy; @@ -6963,7 +8425,7 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) player->LevelSolved = player->GameOver = TRUE; } - if (game.engine_version >= VERSION_IDENT(3,0,7)) + if (game.engine_version >= VERSION_IDENT(3,0,7,0)) { TestIfHeroTouchesBadThing(jx, jy); TestIfPlayerTouchesCustomElement(jx, jy); @@ -7036,7 +8498,7 @@ void TestIfPlayerTouchesCustomElement(int x, int y) int center_element = Feld[x][y]; /* should always be non-moving! */ int i; - for (i=0; i<4; i++) + for (i = 0; i < 4; i++) { int xx = x + xy[i][0]; int yy = y + xy[i][1]; @@ -7049,7 +8511,7 @@ void TestIfPlayerTouchesCustomElement(int x, int y) if (IS_PLAYER(x, y)) { - if (game.engine_version < VERSION_IDENT(3,0,7)) + if (game.engine_version < VERSION_IDENT(3,0,7,0)) border_element = Feld[xx][yy]; /* may be moving! */ else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy)) border_element = Feld[xx][yy]; @@ -7065,7 +8527,7 @@ void TestIfPlayerTouchesCustomElement(int x, int y) } else if (IS_PLAYER(xx, yy)) { - if (game.engine_version >= VERSION_IDENT(3,0,7)) + if (game.engine_version >= VERSION_IDENT(3,0,7,0)) { struct PlayerInfo *player = PLAYERINFO(xx, yy); @@ -7094,7 +8556,7 @@ void TestIfElementTouchesCustomElement(int x, int y) }; static int change_sides[4][2] = { - /* center side border side */ + /* center side border side */ { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */ { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */ { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */ @@ -7112,7 +8574,7 @@ void TestIfElementTouchesCustomElement(int x, int y) int center_element = Feld[x][y]; /* should always be non-moving! */ int i, j; - for (i=0; i<4; i++) + for (i = 0; i < 4; i++) { int xx = x + xy[i][0]; int yy = y + xy[i][1]; @@ -7123,7 +8585,7 @@ void TestIfElementTouchesCustomElement(int x, int y) if (!IN_LEV_FIELD(xx, yy)) continue; - if (game.engine_version < VERSION_IDENT(3,0,7)) + if (game.engine_version < VERSION_IDENT(3,0,7,0)) border_element = Feld[xx][yy]; /* may be moving! */ else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy)) border_element = Feld[xx][yy]; @@ -7137,7 +8599,7 @@ void TestIfElementTouchesCustomElement(int x, int y) HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) && !change_center_element) { - for (j=0; j < element_info[center_element].num_change_pages; j++) + for (j = 0; j < element_info[center_element].num_change_pages; j++) { struct ElementChangeInfo *change = &element_info[center_element].change_page[j]; @@ -7145,7 +8607,12 @@ void TestIfElementTouchesCustomElement(int x, int y) if (change->can_change && change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) && change->sides & border_side && - change->trigger_element == border_element) +#if 1 + IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element) +#else + change->trigger_element == border_element +#endif + ) { change_center_element = TRUE; center_element_change_page = j; @@ -7159,7 +8626,7 @@ void TestIfElementTouchesCustomElement(int x, int y) if (IS_CUSTOM_ELEMENT(border_element) && HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING)) { - for (j=0; j < element_info[border_element].num_change_pages; j++) + for (j = 0; j < element_info[border_element].num_change_pages; j++) { struct ElementChangeInfo *change = &element_info[border_element].change_page[j]; @@ -7167,7 +8634,12 @@ void TestIfElementTouchesCustomElement(int x, int y) if (change->can_change && change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) && change->sides & center_side && - change->trigger_element == center_element) +#if 1 + IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element) +#else + change->trigger_element == center_element +#endif + ) { CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY, CE_OTHER_IS_TOUCHING, j); @@ -7182,6 +8654,106 @@ void TestIfElementTouchesCustomElement(int x, int y) CE_OTHER_IS_TOUCHING, center_element_change_page); } +void TestIfElementHitsCustomElement(int x, int y, int direction) +{ + int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0); + int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0); + int hitx = x + dx, hity = y + dy; + int hitting_element = Feld[x][y]; +#if 0 + boolean object_hit = (IN_LEV_FIELD(hitx, hity) && + !IS_FREE(hitx, hity) && + (!IS_MOVING(hitx, hity) || + MovDir[hitx][hity] != direction || + ABS(MovPos[hitx][hity]) <= TILEY / 2)); +#endif + + if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity)) + return; + +#if 0 + if (IN_LEV_FIELD(hitx, hity) && !object_hit) + return; +#endif + + CheckElementSideChange(x, y, hitting_element, + direction, CE_HITTING_SOMETHING, -1); + + if (IN_LEV_FIELD(hitx, hity)) + { + int opposite_direction = MV_DIR_OPPOSITE(direction); + int hitting_side = direction; + int touched_side = opposite_direction; + int touched_element = MovingOrBlocked2Element(hitx, hity); +#if 1 + boolean object_hit = (!IS_MOVING(hitx, hity) || + MovDir[hitx][hity] != direction || + ABS(MovPos[hitx][hity]) <= TILEY / 2); + + object_hit = TRUE; +#endif + + if (object_hit) + { + int i; + + CheckElementSideChange(hitx, hity, touched_element, + opposite_direction, CE_HIT_BY_SOMETHING, -1); + + if (IS_CUSTOM_ELEMENT(hitting_element) && + HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING)) + { + for (i = 0; i < element_info[hitting_element].num_change_pages; i++) + { + struct ElementChangeInfo *change = + &element_info[hitting_element].change_page[i]; + + if (change->can_change && + change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) && + change->sides & touched_side && + +#if 1 + IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element) +#else + change->trigger_element == touched_element +#endif + ) + { + CheckElementSideChange(x, y, hitting_element, + CH_SIDE_ANY, CE_OTHER_IS_HITTING, i); + break; + } + } + } + + if (IS_CUSTOM_ELEMENT(touched_element) && + HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT)) + { + for (i = 0; i < element_info[touched_element].num_change_pages; i++) + { + struct ElementChangeInfo *change = + &element_info[touched_element].change_page[i]; + + if (change->can_change && + change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) && + change->sides & hitting_side && +#if 1 + IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element) +#else + change->trigger_element == hitting_element +#endif + ) + { + CheckElementSideChange(hitx, hity, touched_element, + CH_SIDE_ANY, CE_OTHER_GETS_HIT, i); + break; + } + } + } + } + } +} + void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir) { int i, kill_x = -1, kill_y = -1; @@ -7200,7 +8772,7 @@ void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir) MV_DOWN }; - for (i=0; i<4; i++) + for (i = 0; i < 4; i++) { int test_x, test_y, test_move_dir, test_element; @@ -7238,7 +8810,7 @@ void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir) if (player->shield_deadly_time_left > 0) Bang(kill_x, kill_y); - else if (!PLAYER_PROTECTED(good_x, good_y)) + else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y)) KillHero(player); } else @@ -7275,7 +8847,7 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir) if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */ return; - for (i=0; i<4; i++) + for (i = 0; i < 4; i++) { int test_x, test_y, test_move_dir, test_element; @@ -7303,7 +8875,7 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir) if (bad_element == EL_ROBOT && player->is_moving) continue; /* robot does not kill player if he is moving */ - if (game.engine_version >= VERSION_IDENT(3,0,7)) + if (game.engine_version >= VERSION_IDENT(3,0,7,0)) { if (player->MovPos != 0 && !(player->MovDir & touch_dir[i])) continue; /* center and border element do not touch */ @@ -7330,7 +8902,7 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir) if (player->shield_deadly_time_left > 0) Bang(bad_x, bad_y); - else if (!PLAYER_PROTECTED(kill_x, kill_y)) + else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y)) KillHero(player); } else @@ -7379,7 +8951,7 @@ void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y) { 0, +1 } }; - for (i=0; i<4; i++) + for (i = 0; i < 4; i++) { int x, y, element; @@ -7420,9 +8992,15 @@ void KillHero(struct PlayerInfo *player) BuryHero(player); } -static void KillHeroUnlessProtected(int x, int y) +static void KillHeroUnlessEnemyProtected(int x, int y) +{ + if (!PLAYER_ENEMY_PROTECTED(x, y)) + KillHero(PLAYERINFO(x, y)); +} + +static void KillHeroUnlessExplosionProtected(int x, int y) { - if (!PLAYER_PROTECTED(x, y)) + if (!PLAYER_EXPLOSION_PROTECTED(x, y)) KillHero(PLAYERINFO(x, y)); } @@ -7434,11 +9012,11 @@ void BuryHero(struct PlayerInfo *player) return; #if 1 - PlaySoundLevelElementAction(jx, jy, player->element_nr, ACTION_DYING); + PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING); #else - PlaySoundLevel(jx, jy, SND_CLASS_PLAYER_DYING); + PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING); #endif - PlaySoundLevel(jx, jy, SND_GAME_LOSING); + PlayLevelSound(jx, jy, SND_GAME_LOSING); player->GameOver = TRUE; RemoveHero(player); @@ -7455,7 +9033,7 @@ void RemoveHero(struct PlayerInfo *player) if (!ExplodeField[jx][jy]) StorePlayer[jx][jy] = 0; - for (i=0; ijx, jy = player->jy; +#if 0 + boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0)); +#endif + int jx = oldx, jy = oldy; int dx = x - jx, dy = y - jy; int nextx = x + dx, nexty = y + dy; int move_direction = (dx == -1 ? MV_LEFT : dx == +1 ? MV_RIGHT : dy == -1 ? MV_UP : dy == +1 ? MV_DOWN : MV_NO_MOVING); + int opposite_direction = MV_DIR_OPPOSITE(move_direction); int dig_side = change_sides[MV_DIR_BIT(move_direction)]; + int old_element = Feld[jx][jy]; int element; if (player->MovPos == 0) @@ -7544,11 +9127,13 @@ int DigField(struct PlayerInfo *player, if (IS_MOVING(x, y) || IS_PLAYER(x, y)) return MF_NO_ACTION; +#if 0 + #if 0 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy])) #else if (IS_TUBE(Feld[jx][jy]) || - (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0))) + (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))) #endif { int i = 0; @@ -7580,10 +9165,21 @@ int DigField(struct PlayerInfo *player, return MF_NO_ACTION; /* tube has no opening in this direction */ } +#else + + if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)) + old_element = Back[jx][jy]; + +#endif + + if (IS_WALKABLE(old_element) && + !(element_info[old_element].access_direction & move_direction)) + return MF_NO_ACTION; /* field has no opening in this direction */ + element = Feld[x][y]; if (mode == DF_SNAP && !IS_SNAPPABLE(element) && - game.engine_version >= VERSION_IDENT(2,2,0)) + game.engine_version >= VERSION_IDENT(2,2,0,0)) return MF_NO_ACTION; switch (element) @@ -7599,6 +9195,10 @@ int DigField(struct PlayerInfo *player, case EL_SP_GRAVITY_PORT_RIGHT: case EL_SP_GRAVITY_PORT_UP: case EL_SP_GRAVITY_PORT_DOWN: +#if 1 + if (!canEnterSupaplexPort(x, y, dx, dy)) + return MF_NO_ACTION; +#else if ((dx == -1 && element != EL_SP_PORT_LEFT && element != EL_SP_GRAVITY_PORT_LEFT && @@ -7622,6 +9222,7 @@ int DigField(struct PlayerInfo *player, !IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty)) return MF_NO_ACTION; +#endif if (element == EL_SP_GRAVITY_PORT_LEFT || element == EL_SP_GRAVITY_PORT_RIGHT || @@ -7631,11 +9232,23 @@ int DigField(struct PlayerInfo *player, /* automatically move to the next field with double speed */ player->programmed_action = move_direction; +#if 1 + if (player->move_delay_reset_counter == 0) + { + player->move_delay_reset_counter = 2; /* two double speed steps */ + + DOUBLE_PLAYER_SPEED(player); + } +#else + player->move_delay_reset_counter = 2; + DOUBLE_PLAYER_SPEED(player); +#endif - PlaySoundLevel(x, y, SND_CLASS_SP_PORT_PASSING); + PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING); break; +#if 0 case EL_TUBE_ANY: case EL_TUBE_VERTICAL: case EL_TUBE_HORIZONTAL: @@ -7675,9 +9288,10 @@ int DigField(struct PlayerInfo *player, if (!(tube_enter_directions[i][1] & move_direction)) return MF_NO_ACTION; /* tube has no opening in this direction */ - PlaySoundLevel(x, y, SND_CLASS_TUBE_WALKING); + PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING); } break; +#endif default: @@ -7685,6 +9299,9 @@ int DigField(struct PlayerInfo *player, { int sound_action = ACTION_WALKING; + if (!(element_info[element].access_direction & opposite_direction)) + return MF_NO_ACTION; /* field not accessible from this direction */ + if (element >= EL_GATE_1 && element <= EL_GATE_4) { if (!player->key[element - EL_GATE_1]) @@ -7708,9 +9325,9 @@ int DigField(struct PlayerInfo *player, /* play sound from background or player, whatever is available */ if (element_info[element].sound[sound_action] != SND_UNDEFINED) - PlaySoundLevelElementAction(x, y, element, sound_action); + PlayLevelSoundElementAction(x, y, element, sound_action); else - PlaySoundLevelElementAction(x, y, player->element_nr, sound_action); + PlayLevelSoundElementAction(x, y, player->element_nr, sound_action); break; } @@ -7719,6 +9336,10 @@ int DigField(struct PlayerInfo *player, if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty)) return MF_NO_ACTION; + if (IS_CUSTOM_ELEMENT(element) && + !(element_info[element].access_direction & opposite_direction)) + return MF_NO_ACTION; /* field not accessible from this direction */ + #if 1 if (CAN_MOVE(element)) /* only fixed elements can be passed! */ return MF_NO_ACTION; @@ -7737,9 +9358,20 @@ int DigField(struct PlayerInfo *player, /* automatically move to the next field with double speed */ player->programmed_action = move_direction; +#if 1 + if (player->move_delay_reset_counter == 0) + { + player->move_delay_reset_counter = 2; /* two double speed steps */ + + DOUBLE_PLAYER_SPEED(player); + } +#else + player->move_delay_reset_counter = 2; + DOUBLE_PLAYER_SPEED(player); +#endif - PlaySoundLevelAction(x, y, ACTION_PASSING); + PlayLevelSoundAction(x, y, ACTION_PASSING); break; } @@ -7758,7 +9390,7 @@ int DigField(struct PlayerInfo *player, player->is_digging = TRUE; } - PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING); + PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING); CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED); @@ -7837,7 +9469,7 @@ int DigField(struct PlayerInfo *player, { int i; - for (i=0; i < element_info[element].collect_count; i++) + for (i = 0; i < element_info[element].collect_count; i++) if (player->inventory_size < MAX_INVENTORY_SIZE) player->inventory_element[player->inventory_size++] = element; @@ -7856,7 +9488,7 @@ int DigField(struct PlayerInfo *player, } RaiseScoreElement(element); - PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING); + PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING); CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED); @@ -7876,8 +9508,23 @@ int DigField(struct PlayerInfo *player, return MF_NO_ACTION; if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) && - !(element == EL_SPRING && use_spring_bug)) + !(element == EL_SPRING && level.use_spring_bug)) + return MF_NO_ACTION; + +#if 1 + if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 && + ((move_direction & MV_VERTICAL && + ((element_info[element].move_pattern & MV_LEFT && + IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) || + (element_info[element].move_pattern & MV_RIGHT && + IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) || + (move_direction & MV_HORIZONTAL && + ((element_info[element].move_pattern & MV_UP && + IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) || + (element_info[element].move_pattern & MV_DOWN && + IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1)))))) return MF_NO_ACTION; +#endif #if 1 /* do not push elements already moving away faster than player */ @@ -7888,9 +9535,36 @@ int DigField(struct PlayerInfo *player, if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING) return MF_NO_ACTION; #endif + +#if 1 + if (game.engine_version >= VERSION_IDENT(3,0,7,1)) + { + if (player->push_delay_value == -1) + player->push_delay_value = GET_NEW_PUSH_DELAY(element); + } + else if (game.engine_version >= VERSION_IDENT(2,2,0,7)) + { + if (!player->is_pushing) + player->push_delay_value = GET_NEW_PUSH_DELAY(element); + } + + /* + if (game.engine_version >= VERSION_IDENT(2,2,0,7) && + (game.engine_version < VERSION_IDENT(3,0,7,1) || + !player_is_pushing)) + player->push_delay_value = GET_NEW_PUSH_DELAY(element); + */ +#else if (!player->is_pushing && - game.engine_version >= RELEASE_IDENT(2,2,0,7)) + game.engine_version >= VERSION_IDENT(2,2,0,7)) player->push_delay_value = GET_NEW_PUSH_DELAY(element); +#endif + +#if 0 + printf("::: push delay: %ld [%d, %d] [%d]\n", + player->push_delay_value, FrameCounter, game.engine_version, + player->is_pushing); +#endif player->is_pushing = TRUE; @@ -7909,7 +9583,17 @@ int DigField(struct PlayerInfo *player, if (!FrameReached(&player->push_delay, player->push_delay_value) && !(tape.playing && tape.file_version < FILE_VERSION_2_0) && element != EL_SPRING && element != EL_BALLOON) + { + /* make sure that there is no move delay before next try to push */ + if (game.engine_version >= VERSION_IDENT(3,0,7,1)) + player->move_delay = INITIAL_MOVE_DELAY_OFF; + return MF_NO_ACTION; + } + +#if 0 + printf("::: NOW PUSHING... [%d]\n", FrameCounter); +#endif if (IS_SB_ELEMENT(element)) { @@ -7928,23 +9612,23 @@ int DigField(struct PlayerInfo *player, Feld[x][y] = EL_SOKOBAN_OBJECT; if (Back[x][y] == Back[nextx][nexty]) - PlaySoundLevelAction(x, y, ACTION_PUSHING); + PlayLevelSoundAction(x, y, ACTION_PUSHING); else if (Back[x][y] != 0) - PlaySoundLevelElementAction(x, y, EL_SOKOBAN_FIELD_FULL, + PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL, ACTION_EMPTYING); else - PlaySoundLevelElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY, + PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY, ACTION_FILLING); if (local_player->sokobanfields_still_needed == 0 && game.emulation == EMU_SOKOBAN) { player->LevelSolved = player->GameOver = TRUE; - PlaySoundLevel(x, y, SND_GAME_SOKOBAN_SOLVING); + PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING); } } else - PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING); + PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING); InitMovingField(x, y, move_direction); GfxAction[x][y] = ACTION_PUSHING; @@ -7957,8 +9641,10 @@ int DigField(struct PlayerInfo *player, Pushed[x][y] = TRUE; Pushed[nextx][nexty] = TRUE; - if (game.engine_version < RELEASE_IDENT(2,2,0,7)) + if (game.engine_version < VERSION_IDENT(2,2,0,7)) player->push_delay_value = GET_NEW_PUSH_DELAY(element); + else + player->push_delay_value = -1; /* get new value later */ CheckTriggeredElementSideChange(x, y, element, dig_side, CE_OTHER_GETS_PUSHED); @@ -7976,7 +9662,7 @@ int DigField(struct PlayerInfo *player, player->switch_x = x; player->switch_y = y; - PlaySoundLevelElementAction(x, y, element, ACTION_ACTIVATING); + PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING); if (element == EL_ROBOT_WHEEL) { @@ -7990,7 +9676,7 @@ int DigField(struct PlayerInfo *player, { int xx, yy; - for (yy=0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++) + for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++) { if (Feld[xx][yy] == EL_SP_DISK_YELLOW) Bang(xx, yy); @@ -8013,7 +9699,7 @@ int DigField(struct PlayerInfo *player, ToggleLightSwitch(x, y); #if 0 - PlaySoundLevel(x, y, element == EL_LIGHT_SWITCH ? + PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ? SND_LIGHT_SWITCH_ACTIVATING : SND_LIGHT_SWITCH_DEACTIVATING); #endif @@ -8098,7 +9784,7 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy) dy == -1 ? MV_UP : dy == +1 ? MV_DOWN : MV_NO_MOVING); - if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0)) + if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0)) return FALSE; if (!player->active || !IN_LEV_FIELD(x, y)) @@ -8129,18 +9815,30 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy) player->MovDir = snap_direction; - player->is_moving = FALSE; - player->is_digging = FALSE; - player->is_collecting = FALSE; +#if 1 + if (player->MovPos == 0) +#endif + { + player->is_moving = FALSE; + player->is_digging = FALSE; + player->is_collecting = FALSE; + } + + player->is_dropping = FALSE; - if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION) + if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION) return FALSE; player->is_snapping = TRUE; - player->is_moving = FALSE; - player->is_digging = FALSE; - player->is_collecting = FALSE; +#if 1 + if (player->MovPos == 0) +#endif + { + player->is_moving = FALSE; + player->is_digging = FALSE; + player->is_collecting = FALSE; + } DrawLevelField(x, y); BackToFront(); @@ -8151,13 +9849,13 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy) boolean DropElement(struct PlayerInfo *player) { int jx = player->jx, jy = player->jy; - int old_element; + int old_element = Feld[jx][jy]; + int new_element; - if (!player->active || player->MovPos) + /* check if player is active, not moving and ready to drop */ + if (!player->active || player->MovPos || player->drop_delay > 0) return FALSE; - old_element = Feld[jx][jy]; - /* check if player has anything that can be dropped */ if (player->inventory_size == 0 && player->dynabombs_left == 0) return FALSE; @@ -8175,18 +9873,20 @@ boolean DropElement(struct PlayerInfo *player) if (old_element != EL_EMPTY) Back[jx][jy] = old_element; /* store old element on this field */ - MovDelay[jx][jy] = 96; - ResetGfxAnimation(jx, jy); ResetRandomAnimationValue(jx, jy); if (player->inventory_size > 0) { - int new_element = player->inventory_element[--player->inventory_size]; + player->inventory_size--; + new_element = player->inventory_element[player->inventory_size]; - Feld[jx][jy] = (new_element == EL_DYNAMITE ? EL_DYNAMITE_ACTIVE : - new_element == EL_SP_DISK_RED ? EL_SP_DISK_RED_ACTIVE : - new_element); + if (new_element == EL_DYNAMITE) + new_element = EL_DYNAMITE_ACTIVE; + else if (new_element == EL_SP_DISK_RED) + new_element = EL_SP_DISK_RED_ACTIVE; + + Feld[jx][jy] = new_element; DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(local_player->inventory_size, 3), FONT_TEXT_2); @@ -8194,7 +9894,12 @@ boolean DropElement(struct PlayerInfo *player) if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy))) DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0); - PlaySoundLevelAction(jx, jy, ACTION_DROPPING); + PlayLevelSoundAction(jx, jy, ACTION_DROPPING); + +#if 1 + /* needed if previous element just changed to "empty" in the last frame */ + Changed[jx][jy] = 0; /* allow another change */ +#endif CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED); CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER); @@ -8204,16 +9909,81 @@ boolean DropElement(struct PlayerInfo *player) else /* player is dropping a dyna bomb */ { player->dynabombs_left--; + new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr; - Feld[jx][jy] = - EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1); + Feld[jx][jy] = new_element; if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy))) DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0); - PlaySoundLevelAction(jx, jy, ACTION_DROPPING); + PlayLevelSoundAction(jx, jy, ACTION_DROPPING); + } + + + +#if 1 + + if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */ + { +#if 1 + InitField_WithBug1(jx, jy, FALSE); +#else + InitField(jx, jy, FALSE); + if (CAN_MOVE(Feld[jx][jy])) + InitMovDir(jx, jy); +#endif + } + + new_element = Feld[jx][jy]; + + if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) && + element_info[new_element].move_pattern == MV_WHEN_DROPPED) + { + int move_stepsize = element_info[new_element].move_stepsize; + int direction, dx, dy, nextx, nexty; + + if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC) + MovDir[jx][jy] = player->MovDir; + + direction = MovDir[jx][jy]; + dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0); + dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0); + nextx = jx + dx; + nexty = jy + dy; + + if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty)) + { +#if 0 + WasJustMoving[jx][jy] = 3; +#else + InitMovingField(jx, jy, direction); + ContinueMoving(jx, jy); +#endif + } + else + { + Changed[jx][jy] = 0; /* allow another change */ + +#if 1 + TestIfElementHitsCustomElement(jx, jy, direction); +#else + CheckElementSideChange(jx, jy, new_element, + direction, CE_HITTING_SOMETHING, -1); +#endif + } + + player->drop_delay = 2 * TILEX / move_stepsize + 1; } +#if 0 + player->drop_delay = 8 + 8 + 8; +#endif + +#endif + + player->is_dropping = TRUE; + + return TRUE; } @@ -8224,21 +9994,18 @@ boolean DropElement(struct PlayerInfo *player) static int *loop_sound_frame = NULL; static int *loop_sound_volume = NULL; -void InitPlaySoundLevel() +void InitPlayLevelSound() { int num_sounds = getSoundListSize(); - if (loop_sound_frame != NULL) - free(loop_sound_frame); + checked_free(loop_sound_frame); + checked_free(loop_sound_volume); - if (loop_sound_volume != NULL) - free(loop_sound_volume); - - loop_sound_frame = checked_calloc(num_sounds * sizeof(int)); + loop_sound_frame = checked_calloc(num_sounds * sizeof(int)); loop_sound_volume = checked_calloc(num_sounds * sizeof(int)); } -static void PlaySoundLevel(int x, int y, int nr) +static void PlayLevelSound(int x, int y, int nr) { int sx = SCREENX(x), sy = SCREENY(y); int volume, stereo_position; @@ -8283,42 +10050,59 @@ static void PlaySoundLevel(int x, int y, int nr) PlaySoundExt(nr, volume, stereo_position, type); } -static void PlaySoundLevelNearest(int x, int y, int sound_action) +static void PlayLevelSoundNearest(int x, int y, int sound_action) { - PlaySoundLevel(x < LEVELX(BX1) ? LEVELX(BX1) : + PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) : x > LEVELX(BX2) ? LEVELX(BX2) : x, y < LEVELY(BY1) ? LEVELY(BY1) : y > LEVELY(BY2) ? LEVELY(BY2) : y, sound_action); } -static void PlaySoundLevelAction(int x, int y, int action) +static void PlayLevelSoundAction(int x, int y, int action) { - PlaySoundLevelElementAction(x, y, Feld[x][y], action); + PlayLevelSoundElementAction(x, y, Feld[x][y], action); } -static void PlaySoundLevelElementAction(int x, int y, int element, int action) +static void PlayLevelSoundElementAction(int x, int y, int element, int action) { int sound_effect = element_info[element].sound[action]; if (sound_effect != SND_UNDEFINED) - PlaySoundLevel(x, y, sound_effect); + PlayLevelSound(x, y, sound_effect); +} + +static void PlayLevelSoundElementActionIfLoop(int x, int y, int element, + int action) +{ + int sound_effect = element_info[element].sound[action]; + + if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect)) + PlayLevelSound(x, y, sound_effect); } -static void PlaySoundLevelActionIfLoop(int x, int y, int action) +static void PlayLevelSoundActionIfLoop(int x, int y, int action) { int sound_effect = element_info[Feld[x][y]].sound[action]; if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect)) - PlaySoundLevel(x, y, sound_effect); + PlayLevelSound(x, y, sound_effect); } -static void StopSoundLevelActionIfLoop(int x, int y, int action) +static void StopLevelSoundActionIfLoop(int x, int y, int action) { int sound_effect = element_info[Feld[x][y]].sound[action]; if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect)) - StopSoundExt(sound_effect, SND_CTRL_STOP_SOUND); + StopSound(sound_effect); +} + +static void PlayLevelMusic() +{ + if (levelset.music[level_nr] != MUS_UNDEFINED) + PlayMusic(levelset.music[level_nr]); /* from config file */ + else + PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */ } void RaiseScore(int value) @@ -8482,7 +10266,7 @@ void CreateGameButtons() { int i; - for (i=0; i