X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Fgame.c;h=f8289cc9138937767facf986edaa3b21fa908628;hb=564a563aee2c9fdb37116d41342b36eda63c1f6a;hp=f5558fdb57e8ea0a87f7975c8650490102cce5bb;hpb=7373e992fb880976e1dc7c559bcd7a19b8bcfe80;p=rocksndiamonds.git diff --git a/src/game.c b/src/game.c index f5558fdb..f8289cc9 100644 --- a/src/game.c +++ b/src/game.c @@ -101,11 +101,29 @@ #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) || \ @@ -120,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) || \ @@ -144,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))) @@ -168,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); @@ -450,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 & \ @@ -560,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; @@ -599,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; @@ -664,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; @@ -671,10 +762,6 @@ 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; @@ -748,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 +} + /* ============================================================================= @@ -800,6 +994,15 @@ 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 */ @@ -820,7 +1023,7 @@ static void InitGameEngine() /* ---------- 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]; @@ -836,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]; @@ -844,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]; @@ -861,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) && @@ -887,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; @@ -900,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; @@ -918,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; @@ -933,7 +1145,7 @@ 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; @@ -1048,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; @@ -1059,6 +1308,7 @@ 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; @@ -1069,6 +1319,8 @@ void InitGame() 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; @@ -1076,11 +1328,11 @@ void InitGame() player->num_special_action_sleeping = 0; /* determine number of special actions for bored and sleeping animation */ - for (j=ACTION_BORING_1; j <= ACTION_BORING_8; j++) + for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++) { boolean found = FALSE; - for (k=0; k < NUM_DIRECTIONS; k++) + 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; @@ -1090,11 +1342,11 @@ void InitGame() else break; } - for (j=ACTION_SLEEPING_1; j <= ACTION_SLEEPING_3; j++) + for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++) { boolean found = FALSE; - for (k=0; k < NUM_DIRECTIONS; k++) + 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; @@ -1113,9 +1365,13 @@ 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 = game.initial_push_delay_value; + player->drop_delay = 0; + player->last_jx = player->last_jy = 0; player->jx = player->jy = 0; @@ -1124,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; @@ -1166,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; @@ -1247,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; @@ -1261,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; } @@ -1303,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 (element_info[element].move_pattern == MV_HORIZONTAL) + 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 (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]; @@ -1634,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]; @@ -1668,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) { @@ -1840,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; @@ -2030,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) { @@ -2121,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 */ @@ -2141,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 */ @@ -2185,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); } } } @@ -2197,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) { @@ -2236,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; @@ -2315,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; } @@ -2406,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; } @@ -2422,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 @@ -2442,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]; @@ -2458,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; @@ -2468,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) { @@ -2477,6 +2892,17 @@ 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; @@ -2486,9 +2912,22 @@ void Explode(int ex, int ey, int phase, int mode) 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); @@ -2496,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); @@ -2514,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); @@ -2535,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; @@ -2546,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++; @@ -2556,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]; @@ -2594,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 @@ -2653,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); @@ -2665,23 +3117,42 @@ void Bang(int x, int y) void SplashAcid(int x, int 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 (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; + + 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 (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_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; + 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() @@ -2704,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); @@ -2720,13 +3191,13 @@ static void InitBeltMovement() } } - for(y=0; y= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) && ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX) @@ -3046,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; } @@ -3079,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 @@ -3104,8 +3587,8 @@ void Impact(int x, int y) EL_BD_MAGIC_WALL_ACTIVE); /* activate magic wall / mill */ - for (yy=0; yyjx, jy = player->jy; @@ -3571,7 +4058,7 @@ inline static void TurnRoundExt(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]; @@ -3667,17 +4154,30 @@ inline static void TurnRoundExt(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) @@ -3689,50 +4189,49 @@ inline static void TurnRoundExt(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) { @@ -3743,7 +4242,7 @@ inline static void TurnRoundExt(int x, int y) { int i; - for (i=0; ijx, jy = player->jy; @@ -3781,37 +4280,108 @@ inline static void TurnRoundExt(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; -static void TurnRound(int x, int y) -{ - int direction = MovDir[x][y]; + for (i = 0; i < 4; i++) + { + int move_dir = test_dir[start_test + i]; + int move_dir_preference; -#if 0 - GfxDir[x][y] = MovDir[x][y]; -#endif + xx = x + test_xy[start_test + i][0]; + yy = y + test_xy[start_test + i][1]; - TurnRoundExt(x, y); + 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]; @@ -3833,7 +4403,7 @@ 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; @@ -4001,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; @@ -4042,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) { @@ -4120,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)); @@ -4130,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; @@ -4154,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) @@ -4181,9 +4815,12 @@ 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); @@ -4270,7 +4907,7 @@ void StartMoving(int x, int y) 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; @@ -4285,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); @@ -4327,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 */ @@ -4340,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 || @@ -4347,16 +4997,22 @@ 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 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING); if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy))) @@ -4371,7 +5027,7 @@ 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 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING; @@ -4384,6 +5040,7 @@ void StartMoving(int x, int y) DrawPlayerField(x, y); else DrawLevelField(x, y); + return; } } @@ -4407,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)) @@ -4486,8 +5214,15 @@ 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; @@ -4517,6 +5252,7 @@ void StartMoving(int x, int y) DrawLevelField(x, y); MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */ + return; /* wait for shrinking amoeba */ } else /* element == EL_PACMAN */ @@ -4580,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) { @@ -4694,6 +5446,24 @@ void ContinueMoving(int x, int y) 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)) @@ -4724,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; @@ -4753,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 @@ -4761,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); @@ -4782,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]; @@ -4812,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]; @@ -4835,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; @@ -5871,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 */ @@ -5897,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; @@ -5907,7 +6770,7 @@ 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]; @@ -5916,7 +6779,12 @@ static boolean CheckTriggeredElementSideChange(int lx, int ly, 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))) @@ -5934,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); @@ -5990,17 +6888,49 @@ 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 was_waiting = player->is_waiting; + boolean last_waiting = player->is_waiting; + int move_dir = player->MovDir; + + player->last_action_waiting = player->action_waiting; if (is_waiting) { - int jx = player->jx, jy = player->jy; - int element = player->element_nr; - int action; - - if (!was_waiting) /* not waiting -> waiting */ + if (!last_waiting) /* not waiting -> waiting */ { player->is_waiting = TRUE; @@ -6016,26 +6946,92 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting) InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir); } - if (game.player_sleeping_delay_fixed != -1 && - game.player_sleeping_delay_random != -1 && + 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 != -1 && - game.player_boring_delay_random != -1 && + else if (game.player_boring_delay_fixed + + game.player_boring_delay_random > 0 && FrameCounter >= player->frame_counter_bored) player->is_bored = TRUE; - action = (player->is_sleeping ? ACTION_SLEEPING : - player->is_bored ? ACTION_BORING : ACTION_WAITING); + player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING : + player->is_bored ? ACTION_BORING : + ACTION_WAITING); - if (!was_waiting) - PlayLevelSoundElementAction(jx, jy, element, action); - else - PlayLevelSoundElementActionIfLoop(jx, jy, element, action); + 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 (was_waiting) /* waiting -> not waiting */ + else if (last_waiting) /* waiting -> not waiting */ { player->is_waiting = FALSE; player->is_bored = FALSE; @@ -6046,6 +7042,11 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting) 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; } } @@ -6078,6 +7079,11 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action) 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 @@ -6119,7 +7125,7 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action) /* 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); @@ -6129,6 +7135,8 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action) if (player->MovPos == 0) /* needed for tape.playing */ player->is_moving = FALSE; + player->is_dropping = FALSE; + return 0; } @@ -6198,7 +7206,7 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action) /* 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); @@ -6280,7 +7288,7 @@ void GameActions() recorded_player_action = (tape.playing ? TapePlayAction() : NULL); - for (i=0; ieffective_action = summarized_player_action; - for (i=0; i < MAX_PLAYERS; i++) + for (i = 0; i < MAX_PLAYERS; i++) { int actual_player_action = stored_player[i].effective_action; @@ -6327,21 +7335,40 @@ void GameActions() FrameCounter++; TimeFrames++; - 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); @@ -6355,7 +7382,7 @@ void GameActions() } #endif - for (y=0; y 0) + stored_player[i].drop_delay--; } #endif @@ -6816,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) @@ -6899,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; } } @@ -6964,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; @@ -6977,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; @@ -6995,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 @@ -7024,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) && @@ -7180,6 +8276,8 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) player->is_switching = FALSE; #endif + player->is_dropping = FALSE; + #if 1 { @@ -7260,6 +8358,7 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) #if 0 DrawPlayer(player); #endif + return; } else if (!FrameReached(&player->actual_frame_counter, 1)) @@ -7268,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 */ @@ -7281,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 */ @@ -7289,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; @@ -7378,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]; @@ -7436,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 */ @@ -7454,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]; @@ -7479,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]; @@ -7487,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; @@ -7501,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]; @@ -7509,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); @@ -7524,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; @@ -7542,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; @@ -7580,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 @@ -7617,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; @@ -7672,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 @@ -7721,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; @@ -7762,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_PROTECTED(x, y)) + if (!PLAYER_ENEMY_PROTECTED(x, y)) + KillHero(PLAYERINFO(x, y)); +} + +static void KillHeroUnlessExplosionProtected(int x, int y) +{ + if (!PLAYER_EXPLOSION_PROTECTED(x, y)) KillHero(PLAYERINFO(x, y)); } @@ -7797,7 +9033,7 @@ void RemoveHero(struct PlayerInfo *player) if (!ExplodeField[jx][jy]) StorePlayer[jx][jy] = 0; - for (i=0; ijx, jy = player->jy; +#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) @@ -7886,6 +9127,8 @@ 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 @@ -7922,6 +9165,17 @@ 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) && @@ -7941,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 && @@ -7964,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 || @@ -7973,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 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING); break; +#if 0 case EL_TUBE_ANY: case EL_TUBE_VERTICAL: case EL_TUBE_HORIZONTAL: @@ -8020,6 +9291,7 @@ int DigField(struct PlayerInfo *player, PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING); } break; +#endif default: @@ -8027,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]) @@ -8061,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; @@ -8079,7 +9358,18 @@ 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 PlayLevelSoundAction(x, y, ACTION_PASSING); @@ -8179,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; @@ -8218,7 +9508,7 @@ 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 @@ -8386,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); @@ -8525,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(); @@ -8547,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; @@ -8571,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); @@ -8592,6 +9896,11 @@ boolean DropElement(struct PlayerInfo *player) 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); @@ -8600,9 +9909,9 @@ 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); @@ -8610,6 +9919,71 @@ boolean DropElement(struct PlayerInfo *player) 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; } @@ -8624,13 +9998,10 @@ void InitPlayLevelSound() { int num_sounds = getSoundListSize(); - if (loop_sound_frame != NULL) - free(loop_sound_frame); - - if (loop_sound_volume != NULL) - free(loop_sound_volume); + checked_free(loop_sound_frame); + checked_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)); } @@ -8723,19 +10094,15 @@ 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 1 if (levelset.music[level_nr] != MUS_UNDEFINED) PlayMusic(levelset.music[level_nr]); /* from config file */ else - PlayMusic(-(level_nr + 1)); /* from music dir */ -#else - PlayMusic(level_nr); -#endif + PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */ } void RaiseScore(int value) @@ -8899,7 +10266,7 @@ void CreateGameButtons() { int i; - for (i=0; i