X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Fgame.c;h=91c8841ab9657c815e9cdb87359febc4adf9b0d7;hp=770f34bc1e5eaf84c754d87bc39243c49b3b183a;hb=86b0ea5594dc5a9db7ac5d71fa2b7487a4fc1f9d;hpb=3c83944be69b4f9e1c3dd0a615805e3ee4178324 diff --git a/src/game.c b/src/game.c index 770f34bc..91c8841a 100644 --- a/src/game.c +++ b/src/game.c @@ -89,6 +89,9 @@ #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value)) #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value)) +/* values for other actions */ +#define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED) + #define INIT_GFX_RANDOM() (SimpleRND(1000000)) #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \ @@ -96,6 +99,55 @@ #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \ RND(element_info[e].move_delay_random)) +#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_FREE_OR_PLAYER(x, y)))) + +#define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition) \ + (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \ + (condition))) + +#define ELEMENT_CAN_ENTER_FIELD(e, x, y) \ + ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0) + +#define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y) \ + ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID)) + +#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)) + +#define YAMYAM_CAN_ENTER_FIELD(x, y) \ + (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \ + Feld[x][y] == EL_DIAMOND)) + +#define DARK_YAMYAM_CAN_ENTER_FIELD(x, y) \ + (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \ + IS_FOOD_DARK_YAMYAM(Feld[x][y]))) + +#define PACMAN_CAN_ENTER_FIELD(x, y) \ + (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \ + IS_AMOEBOID(Feld[x][y]))) + +#define PIG_CAN_ENTER_FIELD(x, y) \ + (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \ + IS_FOOD_PIG(Feld[x][y]))) + +#define PENGUIN_CAN_ENTER_FIELD(x, y) \ + (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \ + IS_FOOD_PENGUIN(Feld[x][y]) || \ + Feld[x][y] == EL_EXIT_OPEN || \ + Feld[x][y] == EL_ACID)) + +#define MOLE_CAN_ENTER_FIELD(x, y, condition) \ + (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition))) + +#define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y)) +#define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y)) + /* game button identifiers */ #define GAME_CTRL_ID_STOP 0 #define GAME_CTRL_ID_PAUSE 1 @@ -114,7 +166,11 @@ static void CloseAllOpenTimegates(void); static void CheckGravityMovement(struct PlayerInfo *); static void KillHeroUnlessProtected(int, int); -static void ChangeElementDoIt(int, int, int); +static void TestIfPlayerTouchesCustomElement(int, int); +static void TestIfElementTouchesCustomElement(int, int); + +static boolean CheckTriggeredElementChange(int, int, int, int); +static boolean CheckElementChange(int, int, int, int); static void PlaySoundLevel(int, int, int); static void PlaySoundLevelNearest(int, int, int); @@ -151,15 +207,15 @@ static void RunTimegateWheel(int x, int y); struct ChangingElementInfo { - int base_element; - int next_element; + int element; + int target_element; int change_delay; void (*pre_change_function)(int x, int y); void (*change_function)(int x, int y); void (*post_change_function)(int x, int y); }; -static struct ChangingElementInfo changing_element_list[] = +static struct ChangingElementInfo change_delay_list[] = { { EL_NUT_BREAKING, @@ -301,20 +357,78 @@ static struct ChangingElementInfo changing_element_list[] = } }; -static struct ChangingElementInfo changing_element[MAX_NUM_ELEMENTS]; +struct +{ + int element; + int push_delay_fixed, push_delay_random; +} +push_delay_list[] = +{ + { EL_SPRING, 0, 0 }, + { EL_BALLOON, 0, 0 }, + + { EL_SOKOBAN_OBJECT, 2, 0 }, + { EL_SOKOBAN_FIELD_FULL, 2, 0 }, + { EL_SATELLITE, 2, 0 }, + { EL_SP_DISK_YELLOW, 2, 0 }, + + { EL_UNDEFINED, 0, 0 }, +}; + +struct +{ + int element; + int move_stepsize; +} +move_stepsize_list[] = +{ + { EL_AMOEBA_DROP, 2 }, + { EL_AMOEBA_DROPPING, 2 }, + { EL_QUICKSAND_FILLING, 1 }, + { EL_QUICKSAND_EMPTYING, 1 }, + { EL_MAGIC_WALL_FILLING, 2 }, + { EL_BD_MAGIC_WALL_FILLING, 2 }, + { EL_MAGIC_WALL_EMPTYING, 2 }, + { EL_BD_MAGIC_WALL_EMPTYING, 2 }, + + { EL_UNDEFINED, 0 }, +}; + +struct +{ + int element; + int count; +} +collect_count_list[] = +{ + { EL_EMERALD, 1 }, + { EL_BD_DIAMOND, 1 }, + { EL_EMERALD_YELLOW, 1 }, + { EL_EMERALD_RED, 1 }, + { EL_EMERALD_PURPLE, 1 }, + { EL_DIAMOND, 3 }, + { EL_SP_INFOTRON, 1 }, + { EL_PEARL, 5 }, + { EL_CRYSTAL, 8 }, + + { EL_UNDEFINED, 0 }, +}; + static unsigned long trigger_events[MAX_NUM_ELEMENTS]; -#define IS_AUTO_CHANGING(e) (changing_element[e].base_element != EL_UNDEFINED) +#define IS_AUTO_CHANGING(e) (element_info[e].change_events & \ + CH_EVENT_BIT(CE_DELAY)) #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0) #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \ IS_JUST_CHANGING(x, y)) -#define TRIGGERS_BY_COLLECTING(e) (trigger_events[e] & CE_OTHER_COLLECTING) + +#define CE_PAGE(e, ce) (element_info[e].event_page[ce]) void GetPlayerConfig() { if (!audio.sound_available) - setup.sound = FALSE; + setup.sound_simple = FALSE; if (!audio.loops_available) setup.sound_loops = FALSE; @@ -325,7 +439,7 @@ void GetPlayerConfig() if (!video.fullscreen_available) setup.fullscreen = FALSE; - setup.sound_simple = setup.sound; + setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music); SetAudioMode(setup.sound); InitJoysticks(); @@ -382,62 +496,71 @@ static int getBeltDirFromBeltSwitchElement(int element) return belt_move_dir[belt_dir_nr]; } -static void InitField(int x, int y, boolean init_game) +static void InitPlayerField(int x, int y, int element, boolean init_game) { - int element = Feld[x][y]; - - switch (element) + if (element == EL_SP_MURPHY) { - case EL_SP_MURPHY: - if (init_game) + if (init_game) + { + if (stored_player[0].present) { - if (stored_player[0].present) - { - Feld[x][y] = EL_SP_MURPHY_CLONE; - break; - } - else - { - stored_player[0].use_murphy_graphic = TRUE; - } + Feld[x][y] = EL_SP_MURPHY_CLONE; - Feld[x][y] = EL_PLAYER_1; + return; } - /* no break! */ - case EL_PLAYER_1: - case EL_PLAYER_2: - case EL_PLAYER_3: - case EL_PLAYER_4: - if (init_game) + else { - struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1]; - int jx = player->jx, jy = player->jy; + stored_player[0].use_murphy_graphic = TRUE; + } - player->present = TRUE; + Feld[x][y] = EL_PLAYER_1; + } + } - if (!options.network || player->connected) - { - player->active = TRUE; + if (init_game) + { + struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1]; + int jx = player->jx, jy = player->jy; - /* remove potentially duplicate players */ - if (StorePlayer[jx][jy] == Feld[x][y]) - StorePlayer[jx][jy] = 0; + player->present = TRUE; - StorePlayer[x][y] = Feld[x][y]; + if (!options.network || player->connected) + { + player->active = TRUE; - if (options.debug) - { - printf("Player %d activated.\n", player->element_nr); - printf("[Local player is %d and currently %s.]\n", - local_player->element_nr, - local_player->active ? "active" : "not active"); - } - } + /* remove potentially duplicate players */ + if (StorePlayer[jx][jy] == Feld[x][y]) + StorePlayer[jx][jy] = 0; - Feld[x][y] = EL_EMPTY; - player->jx = player->last_jx = x; - player->jy = player->last_jy = y; + StorePlayer[x][y] = Feld[x][y]; + + if (options.debug) + { + printf("Player %d activated.\n", player->element_nr); + printf("[Local player is %d and currently %s.]\n", + local_player->element_nr, + local_player->active ? "active" : "not active"); } + } + + Feld[x][y] = EL_EMPTY; + player->jx = player->last_jx = x; + player->jy = player->last_jy = y; + } +} + +static void InitField(int x, int y, boolean init_game) +{ + int element = Feld[x][y]; + + switch (element) + { + case EL_SP_MURPHY: + case EL_PLAYER_1: + case EL_PLAYER_2: + case EL_PLAYER_3: + case EL_PLAYER_4: + InitPlayerField(x, y, element, init_game); break; case EL_STONEBLOCK: @@ -602,7 +725,7 @@ void DrawGameDoorValues() DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS, int2str(local_player->gems_still_needed, 3), FONT_TEXT_2); DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE, - int2str(local_player->dynamite, 3), FONT_TEXT_2); + int2str(local_player->inventory_size, 3), FONT_TEXT_2); DrawText(DX + XX_SCORE, DY + YY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2); DrawText(DX + XX_TIME, DY + YY_TIME, @@ -620,7 +743,7 @@ void DrawGameDoorValues() static void InitGameEngine() { - int i; + int i, j, k; /* set game engine from tape file when re-playing, else from level file */ game.engine_version = (tape.playing ? tape.engine_version : @@ -637,6 +760,8 @@ static void InitGameEngine() printf(" => game.engine_version == %06d\n", game.engine_version); #endif + /* ---------- 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 : @@ -646,59 +771,166 @@ static void InitGameEngine() game.initial_move_delay_value = (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED); + /* ---------- initialize changing elements ------------------------------- */ + /* initialize changing elements information */ - for (i=0; ichange = &ei->change_page[0]; + + if (!IS_CUSTOM_ELEMENT(i)) + { + ei->change->target_element = EL_EMPTY_SPACE; + ei->change->delay_fixed = 0; + ei->change->delay_random = 0; + ei->change->delay_frames = 1; + } + + ei->change_events = CE_BITMASK_DEFAULT; + for (j=0; j < NUM_CHANGE_EVENTS; j++) + { + ei->event_page_num[j] = 0; + ei->event_page[j] = &ei->change_page[0]; + } } /* add changing elements from pre-defined list */ - i = 0; - while (changing_element_list[i].base_element != EL_UNDEFINED) + 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]; + + ei->change->target_element = ch_delay->target_element; + ei->change->delay_fixed = ch_delay->change_delay; + + ei->change->pre_change_function = ch_delay->pre_change_function; + ei->change->change_function = ch_delay->change_function; + ei->change->post_change_function = ch_delay->post_change_function; + + ei->change_events |= CH_EVENT_BIT(CE_DELAY); + } + +#if 1 + /* add change events from custom element configuration */ + for (i=0; i < NUM_CUSTOM_ELEMENTS; i++) { - struct ChangingElementInfo *ce = &changing_element_list[i]; - int element = ce->base_element; + struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i]; - changing_element[element].base_element = ce->base_element; - changing_element[element].next_element = ce->next_element; - changing_element[element].change_delay = ce->change_delay; - changing_element[element].pre_change_function = ce->pre_change_function; - changing_element[element].change_function = ce->change_function; - changing_element[element].post_change_function = ce->post_change_function; + for (j=0; j < ei->num_change_pages; j++) + { + if (!ei->change_page[j].can_change) + continue; - i++; + 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) && + !(ei->change_events & CH_EVENT_BIT(k))) + { + ei->change_events |= CH_EVENT_BIT(k); + ei->event_page_num[k] = j; + ei->event_page[k] = &ei->change_page[j]; + } + } + } } - /* add changing elements from custom element configuration */ +#else + + /* add change events from custom element configuration */ for (i=0; i < NUM_CUSTOM_ELEMENTS; i++) { int element = EL_CUSTOM_START + i; - struct ElementChangeInfo *change = &element_info[element].change; /* only add custom elements that change after fixed/random frame delay */ - if (!CAN_CHANGE(element) || !HAS_CHANGE_EVENT(element, CE_DELAY)) - continue; - - changing_element[element].base_element = element; - changing_element[element].next_element = change->successor; - changing_element[element].change_delay = (change->delay_fixed * - change->delay_frames); + if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY)) + element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY); } +#endif + + /* ---------- initialize trigger events ---------------------------------- */ /* initialize trigger events information */ for (i=0; inum_change_pages; j++) + { + if (!ei->change_page->can_change) + continue; + + if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER)) + { + int trigger_element = ei->change_page[j].trigger_element; + + 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; +#endif + + /* ---------- initialize push delay -------------------------------------- */ + + /* initialize push delay values to default */ + for (i=0; ikey[j] = FALSE; - player->dynamite = 0; player->dynabomb_count = 0; player->dynabomb_size = 1; player->dynabombs_left = 0; @@ -777,7 +1008,6 @@ void InitGame() player->actual_frame_counter = 0; player->last_move_dir = MV_NO_MOVING; - player->is_moving = FALSE; player->is_moving = FALSE; player->is_waiting = FALSE; @@ -798,6 +1028,8 @@ void InitGame() player->shield_normal_time_left = 0; player->shield_deadly_time_left = 0; + player->inventory_size = 0; + DigField(player, 0, 0, 0, 0, DF_NO_PUSH); SnapField(player, 0, 0); @@ -850,13 +1082,15 @@ void InitGame() { for (y=0; yjx >= SBX_Left + MIDPOSX) - scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ? - local_player->jx - MIDPOSX : - SBX_Right); - if (local_player->jy >= SBY_Upper + MIDPOSY) - scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ? - local_player->jy - MIDPOSY : - SBY_Lower); + /* if local player not found, look for custom element that might create + the player (make some assumptions about the right custom element) */ + if (!local_player->present) + { + int start_x = 0, start_y = 0; + int found_rating = 0; + int found_element = EL_UNDEFINED; + + for(y=0; y < lev_fieldy; y++) for(x=0; x < lev_fieldx; x++) + { + int element = Feld[x][y]; + int content; + int xx, yy; + boolean is_player; + + if (!IS_CUSTOM_ELEMENT(element)) + continue; + + if (CAN_CHANGE(element)) + { + for (i=0; i < element_info[element].num_change_pages; i++) + { + content = element_info[element].change_page[i].target_element; + is_player = ELEM_IS_PLAYER(content); + + if (is_player && (found_rating < 3 || element < found_element)) + { + start_x = x; + start_y = y; + + found_rating = 3; + found_element = element; + } + } + } + + for(yy=0; yy < 3; yy++) for(xx=0; xx < 3; xx++) + { + content = element_info[element].content[xx][yy]; + is_player = ELEM_IS_PLAYER(content); + + if (is_player && (found_rating < 2 || element < found_element)) + { + start_x = x + xx - 1; + start_y = y + yy - 1; + + found_rating = 2; + found_element = element; + } + + if (!CAN_CHANGE(element)) + continue; + + for (i=0; i < element_info[element].num_change_pages; i++) + { + content = element_info[element].change_page[i].content[xx][yy]; + is_player = ELEM_IS_PLAYER(content); + + if (is_player && (found_rating < 1 || element < found_element)) + { + start_x = x + xx - 1; + start_y = y + yy - 1; + + found_rating = 1; + found_element = element; + } + } + } + } + + scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left : + start_x > SBX_Right + MIDPOSX ? SBX_Right : + start_x - MIDPOSX); + + scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper : + start_y > SBY_Lower + MIDPOSY ? SBY_Lower : + start_y - MIDPOSY); + } + else + { +#if 1 + scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left : + local_player->jx > SBX_Right + MIDPOSX ? SBX_Right : + local_player->jx - MIDPOSX); + + scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper : + local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower : + local_player->jy - MIDPOSY); +#else + scroll_x = SBX_Left; + scroll_y = SBY_Upper; + if (local_player->jx >= SBX_Left + MIDPOSX) + scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ? + local_player->jx - MIDPOSX : + SBX_Right); + if (local_player->jy >= SBY_Upper + MIDPOSY) + scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ? + local_player->jy - MIDPOSY : + SBY_Lower); +#endif + } CloseDoor(DOOR_CLOSE_1); @@ -1161,7 +1486,9 @@ void InitMovDir(int x, int y) { if (element_info[element].move_direction_initial != MV_NO_MOVING) MovDir[x][y] = element_info[element].move_direction_initial; - else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS) + 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) MovDir[x][y] = 1 << RND(4); else if (element_info[element].move_pattern == MV_HORIZONTAL) MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT); @@ -1454,8 +1781,10 @@ static void ResetGfxAnimation(int x, int y) void InitMovingField(int x, int y, int direction) { int element = Feld[x][y]; - int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0); - int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0); + 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; + int newy = y + dy; if (!JustStopped[x][y] || direction != MovDir[x][y]) ResetGfxAnimation(x, y); @@ -1544,18 +1873,26 @@ static int MovingOrBlocked2ElementIfNotLeaving(int x, int y) static void RemoveField(int x, int y) { Feld[x][y] = EL_EMPTY; - GfxElement[x][y] = EL_UNDEFINED; + MovPos[x][y] = 0; MovDir[x][y] = 0; MovDelay[x][y] = 0; + + AmoebaNr[x][y] = 0; ChangeDelay[x][y] = 0; + Pushed[x][y] = FALSE; + + GfxElement[x][y] = EL_UNDEFINED; + GfxAction[x][y] = ACTION_DEFAULT; } void RemoveMovingField(int x, int y) { int oldx = x, oldy = y, newx = x, newy = y; + int element = Feld[x][y]; + int next_element = EL_UNDEFINED; - if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y)) + if (element != EL_BLOCKED && !IS_MOVING(x, y)) return; if (IS_MOVING(x, y)) @@ -1564,29 +1901,27 @@ void RemoveMovingField(int x, int y) if (Feld[newx][newy] != EL_BLOCKED) return; } - else if (Feld[x][y] == EL_BLOCKED) + else if (element == EL_BLOCKED) { Blocked2Moving(x, y, &oldx, &oldy); if (!IS_MOVING(oldx, oldy)) return; } - if (Feld[x][y] == EL_BLOCKED && + if (element == EL_BLOCKED && (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING || Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING || Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING || Feld[oldx][oldy] == EL_AMOEBA_DROPPING)) - Feld[oldx][oldy] = get_next_element(Feld[oldx][oldy]); - else - Feld[oldx][oldy] = EL_EMPTY; + next_element = get_next_element(Feld[oldx][oldy]); + + RemoveField(oldx, oldy); + RemoveField(newx, newy); Store[oldx][oldy] = Store2[oldx][oldy] = 0; - Feld[newx][newy] = EL_EMPTY; - MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0; - MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0; - ChangeDelay[oldx][oldy] = ChangeDelay[newx][newy] = 0; - GfxAction[oldx][oldy] = GfxAction[newx][newy] = ACTION_DEFAULT; + if (next_element != EL_UNDEFINED) + Feld[oldx][oldy] = next_element; DrawLevelField(oldx, oldy); DrawLevelField(newx, newy); @@ -1654,57 +1989,212 @@ void CheckDynamite(int x, int y) Bang(x, y); } -void Explode(int ex, int ey, int phase, int mode) +void ShowEnvelope() { - int x, y; - int num_phase = 9; - int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2); - int last_phase = num_phase * delay; - int half_phase = (num_phase / 2) * delay; - int first_phase_after_start = EX_PHASE_START + 1; + int i, x, y; - if (game.explosions_delayed) + /* open envelope window horizontally */ + for (i=2; i <= level.envelope_xsize + 2; i += 2) { - ExplodeField[ex][ey] = mode; - return; - } + int startx = (SXSIZE / MINI_TILEX - i) / 2; + int starty = (SYSIZE / MINI_TILEY) / 2 - 1; - if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */ - { - int center_element = Feld[ex][ey]; + SetDrawtoField(DRAW_BUFFERED); - /* remove things displayed in background while burning dynamite */ - if (!IS_INDESTRUCTIBLE(Back[ex][ey])) - Back[ex][ey] = 0; + BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY); - if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey)) - { - /* put moving element to center field (and let it explode there) */ - center_element = MovingOrBlocked2Element(ex, ey); - RemoveMovingField(ex, ey); - Feld[ex][ey] = center_element; - } + SetDrawtoField(DRAW_BACKBUFFER); - for (y = ey - 1; y <= ey + 1; y++) for(x = ex - 1; x <= ex + 1; x++) + for (y=0; y < 2; y++) for (x=0; x < i; x++) { - int element; + int ex = (x == 0 ? -1 : x == i - 1 ? +1 : 0); + int ey = (y == 0 ? -1 : y == 1 ? +1 : 0); - if (!IN_LEV_FIELD(x, y) || - ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) && - (x != ex || y != ey))) - continue; + DrawEnvelopeBorder(startx + x, starty + y, ex, ey); + } - element = Feld[x][y]; + redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER; + BackToFront(); - if (IS_MOVING(x, y) || IS_BLOCKED(x, y)) - { - element = MovingOrBlocked2Element(x, y); - RemoveMovingField(x, y); - } + Delay(GAME_FRAME_DELAY); + } + + /* open envelope window vertically */ + for (i=2; i <= level.envelope_ysize + 2; i += 2) + { + int xsize = level.envelope_xsize + 2; + int startx = (SXSIZE / MINI_TILEX - (xsize - 1)) / 2; + int starty = (SYSIZE / MINI_TILEY - i) / 2; + + SetDrawtoField(DRAW_BUFFERED); + + BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY); + + SetDrawtoField(DRAW_BACKBUFFER); + + for (y=0; y < i; y++) for (x=0; x < xsize; x++) + { + int ex = (x == 0 ? -1 : x == xsize - 1 ? +1 : 0); + int ey = (y == 0 ? -1 : y == i - 1 ? +1 : 0); + + DrawEnvelopeBorder(startx + x, starty + y, ex, ey); + } + + DrawTextToTextArea(SX + (startx + 1) * MINI_TILEX, + SY + (starty + 1) * MINI_TILEY, level.envelope, + FONT_TEXT_1, level.envelope_xsize, i - 2); + + redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER; + BackToFront(); + + Delay(GAME_FRAME_DELAY); + } + + Delay(3000); + + SetDrawtoField(DRAW_BUFFERED); + + redraw_mask |= REDRAW_FIELD; + BackToFront(); +} + +void RelocatePlayer(int x, int y, int element) +{ + struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1]; + + if (player->present) + { + while (player->MovPos) + { + ScrollFigure(player, SCROLL_GO_ON); + ScrollScreen(NULL, SCROLL_GO_ON); + FrameCounter++; + DrawAllPlayers(); + BackToFront(); + } + + RemoveField(player->jx, player->jy); + DrawLevelField(player->jx, player->jy); + } + + InitPlayerField(x, y, element, TRUE); + + if (player == local_player) + { + int scroll_xx = -999, scroll_yy = -999; + + while (scroll_xx != scroll_x || scroll_yy != scroll_y) + { + int dx = 0, dy = 0; + int fx = FX, fy = FY; + + scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left : + local_player->jx > SBX_Right + MIDPOSX ? SBX_Right : + local_player->jx - MIDPOSX); + + scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper : + local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower : + local_player->jy - MIDPOSY); + + dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0); + dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0); + + scroll_x -= dx; + scroll_y -= dy; + + fx += dx * TILEX / 2; + fy += dy * TILEY / 2; + + ScrollLevel(dx, dy); + DrawAllPlayers(); + + /* scroll in to steps of half tile size to make things smoother */ + BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY); + FlushDisplay(); + Delay(GAME_FRAME_DELAY); + + /* scroll second step to align at full tile size */ + BackToFront(); + Delay(GAME_FRAME_DELAY); + } + } +} + +void Explode(int ex, int ey, int phase, int mode) +{ + int x, y; + int num_phase = 9; + int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2); + int last_phase = num_phase * delay; + int half_phase = (num_phase / 2) * delay; + int first_phase_after_start = EX_PHASE_START + 1; + + if (game.explosions_delayed) + { + ExplodeField[ex][ey] = mode; + return; + } + + if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */ + { + int center_element = Feld[ex][ey]; + +#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) && + center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER)) + return; +#endif + + if (mode == EX_NORMAL || mode == EX_CENTER) + PlaySoundLevelAction(ex, ey, ACTION_EXPLODING); + + /* remove things displayed in background while burning dynamite */ + if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey])) + Back[ex][ey] = 0; + + if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey)) + { + /* put moving element to center field (and let it explode there) */ + center_element = MovingOrBlocked2Element(ex, ey); + RemoveMovingField(ex, ey); + Feld[ex][ey] = center_element; + } + + 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; + int element; + + if (!IN_LEV_FIELD(x, y) || + ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) && + (x != ex || y != ey))) + continue; + + element = Feld[x][y]; + + if (IS_MOVING(x, y) || IS_BLOCKED(x, y)) + { + element = MovingOrBlocked2Element(x, y); + + if (!IS_EXPLOSION_PROOF(element)) + RemoveMovingField(x, y); + } #if 1 + +#if 0 if (IS_EXPLOSION_PROOF(element)) continue; +#else + /* indestructible elements can only explode in center (but not flames) */ + if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) || + element == EL_FLAMES) + continue; +#endif + #else if ((IS_INDESTRUCTIBLE(element) && (game.engine_version < VERSION_IDENT(2,2,0) || @@ -1726,13 +2216,31 @@ void Explode(int ex, int ey, int phase, int mode) } /* save walkable background elements while explosion on same tile */ +#if 0 if (IS_INDESTRUCTIBLE(element)) Back[x][y] = element; +#else + if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element)) + Back[x][y] = element; +#endif /* ignite explodable elements reached by other explosion */ if (element == EL_EXPLOSION) element = Store2[x][y]; +#if 1 + if (AmoebaNr[x][y] && + (element == EL_AMOEBA_FULL || + element == EL_BD_AMOEBA || + element == EL_AMOEBA_GROWING)) + { + AmoebaCnt[AmoebaNr[x][y]]--; + AmoebaCnt2[AmoebaNr[x][y]]--; + } + + RemoveField(x, y); +#endif + if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey)) { switch(StorePlayer[ex][ey]) @@ -1768,11 +2276,10 @@ void Explode(int ex, int ey, int phase, int mode) else if (center_element == EL_AMOEBA_TO_DIAMOND) Store[x][y] = level.amoeba_content; else if (center_element == EL_YAMYAM) - Store[x][y] = - level.yamyam_content[game.yamyam_content_nr][x - ex + 1][y - ey + 1]; - else if (IS_CUSTOM_ELEMENT(center_element)) - Store[x][y] = - element_info[center_element].content[x - ex + 1][y - ey + 1]; + Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy]; + else if (IS_CUSTOM_ELEMENT(center_element) && + element_info[center_element].content[xx][yy] != EL_EMPTY) + Store[x][y] = element_info[center_element].content[xx][yy]; else if (element == EL_WALL_EMERALD) Store[x][y] = EL_EMERALD; else if (element == EL_WALL_DIAMOND) @@ -1789,6 +2296,8 @@ void Explode(int ex, int ey, int phase, int mode) Store[x][y] = EL_PEARL; else if (element == EL_WALL_CRYSTAL) Store[x][y] = EL_CRYSTAL; + else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element)) + Store[x][y] = element_info[element].content[1][1]; else Store[x][y] = EL_EMPTY; @@ -1796,6 +2305,7 @@ void Explode(int ex, int ey, int phase, int mode) center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER) Store2[x][y] = element; +#if 0 if (AmoebaNr[x][y] && (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA || @@ -1805,14 +2315,21 @@ void Explode(int ex, int ey, int phase, int mode) AmoebaCnt2[AmoebaNr[x][y]]--; } +#if 1 + RemoveField(x, y); +#else + MovDir[x][y] = MovPos[x][y] = 0; + AmoebaNr[x][y] = 0; +#endif +#endif + Feld[x][y] = EL_EXPLOSION; #if 1 GfxElement[x][y] = center_element; #else GfxElement[x][y] = EL_UNDEFINED; #endif - MovDir[x][y] = MovPos[x][y] = 0; - AmoebaNr[x][y] = 0; + ExplodePhase[x][y] = 1; Stop[x][y] = TRUE; } @@ -1877,8 +2394,14 @@ void Explode(int ex, int ey, int phase, int mode) InitMovDir(x, y); DrawLevelField(x, y); + if (CAN_BE_CRUMBLED(element)) + DrawLevelFieldCrumbledSandNeighbours(x, y); + if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present) StorePlayer[x][y] = 0; + + if (ELEM_IS_PLAYER(element)) + RelocatePlayer(x, y, element); } else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y))) { @@ -1953,6 +2476,7 @@ void DynaExplode(int ex, int ey) Explode(x, y, EX_PHASE_START, EX_BORDER); + /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */ if (element != EL_EMPTY && element != EL_SAND && element != EL_EXPLOSION && @@ -1964,7 +2488,11 @@ void DynaExplode(int ex, int ey) void Bang(int x, int y) { +#if 1 + int element = MovingOrBlocked2Element(x, y); +#else int element = Feld[x][y]; +#endif if (IS_PLAYER(x, y)) { @@ -1974,6 +2502,7 @@ void Bang(int x, int y) player->element_nr); } +#if 0 #if 1 PlaySoundLevelAction(x, y, ACTION_EXPLODING); #else @@ -1982,6 +2511,7 @@ void Bang(int x, int y) else PlaySoundLevel(x, y, SND_ELEMENT_EXPLODING); #endif +#endif #if 0 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */ @@ -2020,9 +2550,14 @@ void Bang(int x, int y) Explode(x, y, EX_PHASE_START, EX_CENTER); break; default: - Explode(x, y, EX_PHASE_START, EX_NORMAL); + if (CAN_EXPLODE_1X1(element)) + Explode(x, y, EX_PHASE_START, EX_CENTER); + else + Explode(x, y, EX_PHASE_START, EX_NORMAL); break; } + + CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING); } void SplashAcid(int x, int y) @@ -2255,14 +2790,16 @@ static int getInvisibleActiveFromInvisibleElement(int element) { return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE : element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE : - EL_INVISIBLE_SAND_ACTIVE); + element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE : + element); } static int getInvisibleFromInvisibleActiveElement(int element) { return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL : element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL : - EL_INVISIBLE_SAND); + element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND : + element); } static void RedrawAllLightSwitchesAndInvisibleElements() @@ -2353,6 +2890,29 @@ static void ActivateTimegateSwitch(int x, int y) Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE; } +inline static int getElementMoveStepsize(int x, int y) +{ + int element = Feld[x][y]; + 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 horiz_move = (dx != 0); + int sign = (horiz_move ? dx : dy); + int step = sign * element_info[element].move_stepsize; + + /* special values for move stepsize for spring and things on conveyor belt */ + if (horiz_move) + { + if (CAN_FALL(element) && + y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1])) + step = sign * MOVE_STEPSIZE_NORMAL / 2; + else if (element == EL_SPRING) + step = sign * MOVE_STEPSIZE_NORMAL * 2; + } + + return step; +} + void Impact(int x, int y) { boolean lastline = (y == lev_fieldy-1); @@ -2369,6 +2929,12 @@ void Impact(int x, int y) object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) || MovDir[x][y + 1] != MV_DOWN || MovPos[x][y + 1] <= TILEY / 2)); + + /* do not smash moving elements that left the smashed field in time */ + if (game.engine_version >= RELEASE_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) && + ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX) + object_hit = FALSE; + if (object_hit) smashed = MovingOrBlocked2Element(x, y + 1); @@ -2381,20 +2947,15 @@ void Impact(int x, int y) return; } - if (impact) + /* only reset graphic animation if graphic really changes after impact */ + if (impact && + el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element)) { ResetGfxAnimation(x, y); DrawLevelField(x, y); } -#if 1 if (impact && CAN_EXPLODE_IMPACT(element)) -#else - if ((element == EL_BOMB || - element == EL_SP_DISK_ORANGE || - element == EL_DX_SUPABOMB) && - (lastline || object_hit)) /* element is bomb */ -#endif { Bang(x, y); return; @@ -2405,13 +2966,10 @@ void Impact(int x, int y) PlaySoundLevel(x, y, SND_PEARL_BREAKING); return; } - else if (impact && CAN_CHANGE(element) && - HAS_CHANGE_EVENT(element, CE_IMPACT)) + else if (impact && CheckElementChange(x, y, element, CE_IMPACT)) { PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT); - ChangeElementDoIt(x, y, element_info[element].change.successor); - return; } @@ -2431,11 +2989,7 @@ void Impact(int x, int y) return; } -#if 1 if (object_hit) /* check which object was hit */ -#else - if (!lastline && object_hit) /* check which object was hit */ -#endif { if (CAN_PASS_MAGIC_WALL(element) && (smashed == EL_MAGIC_WALL || @@ -2493,35 +3047,22 @@ void Impact(int x, int y) Bang(x, y + 1); return; } -#if 1 - else if (CAN_SMASH_EVERYTHING(element)) -#else - else if (element == EL_ROCK || - element == EL_SP_ZONK || - element == EL_BD_ROCK) +#if 0 + else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed)) + { + Bang(x, y + 1); + return; + } #endif + else if (CAN_SMASH_EVERYTHING(element)) { if (IS_CLASSIC_ENEMY(smashed) || -#if 1 CAN_EXPLODE_SMASHED(smashed)) -#else - smashed == EL_BOMB || - smashed == EL_SP_DISK_ORANGE || - smashed == EL_DX_SUPABOMB || - smashed == EL_SATELLITE || - smashed == EL_PIG || - smashed == EL_DRAGON || - smashed == EL_MOLE) -#endif { Bang(x, y + 1); return; } -#if 1 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1)) -#else - else if (!IS_MOVING(x, y + 1)) -#endif { if (smashed == EL_LAMP || smashed == EL_LAMP_ACTIVE) @@ -2544,11 +3085,7 @@ void Impact(int x, int y) } else if (smashed == EL_DIAMOND) { -#if 1 Feld[x][y + 1] = EL_DIAMOND_BREAKING; -#else - Feld[x][y + 1] = EL_EMPTY; -#endif PlaySoundLevel(x, y, SND_DIAMOND_BREAKING); return; } @@ -2566,12 +3103,15 @@ void Impact(int x, int y) { ToggleLightSwitch(x, y + 1); } - else if (CAN_CHANGE(smashed) && - HAS_CHANGE_EVENT(smashed, CE_SMASHED)) + else { - ChangeElementDoIt(x, y + 1, element_info[smashed].change.successor); + CheckElementChange(x, y + 1, smashed, CE_SMASHED); } } + else + { + CheckElementChange(x, y + 1, smashed, CE_SMASHED); + } } } @@ -2618,7 +3158,9 @@ void TurnRound(int x, int y) { MV_UP, MV_DOWN, MV_LEFT }, { 0, 0, 0 }, { MV_LEFT, MV_RIGHT, MV_DOWN }, - { 0,0,0 }, { 0,0,0 }, { 0,0,0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, { MV_RIGHT, MV_LEFT, MV_UP } }; @@ -2637,15 +3179,15 @@ void TurnRound(int x, int y) int right_x = x + right_dx, right_y = y + right_dy; int move_x = x + move_dx, move_y = y + move_dy; + int xx, yy; + if (element == EL_BUG || element == EL_BD_BUTTERFLY) { TestIfBadThingTouchesOtherBadThing(x, y); - if (IN_LEV_FIELD(right_x, right_y) && - IS_FREE(right_x, right_y)) + if (ENEMY_CAN_ENTER_FIELD(right_x, right_y)) MovDir[x][y] = right_dir; - else if (!IN_LEV_FIELD(move_x, move_y) || - !IS_FREE(move_x, move_y)) + else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y)) MovDir[x][y] = left_dir; if (element == EL_BUG && MovDir[x][y] != old_move_dir) @@ -2658,15 +3200,14 @@ void TurnRound(int x, int y) { TestIfBadThingTouchesOtherBadThing(x, y); - if (IN_LEV_FIELD(left_x, left_y) && - IS_FREE(left_x, left_y)) + if (ENEMY_CAN_ENTER_FIELD(left_x, left_y)) MovDir[x][y] = left_dir; - else if (!IN_LEV_FIELD(move_x, move_y) || - !IS_FREE(move_x, move_y)) + else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y)) MovDir[x][y] = right_dir; if ((element == EL_SPACESHIP || - element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON) + element == EL_SP_SNIKSNAK || + element == EL_SP_ELECTRON) && MovDir[x][y] != old_move_dir) MovDelay[x][y] = 9; else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */ @@ -2674,16 +3215,8 @@ void TurnRound(int x, int y) } else if (element == EL_YAMYAM) { - boolean can_turn_left = FALSE, can_turn_right = FALSE; - - if (IN_LEV_FIELD(left_x, left_y) && - (IS_FREE_OR_PLAYER(left_x, left_y) || - Feld[left_x][left_y] == EL_DIAMOND)) - can_turn_left = TRUE; - if (IN_LEV_FIELD(right_x, right_y) && - (IS_FREE_OR_PLAYER(right_x, right_y) || - Feld[right_x][right_y] == EL_DIAMOND)) - can_turn_right = TRUE; + boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(left_x, left_y); + boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y); if (can_turn_left && can_turn_right) MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir); @@ -2694,20 +3227,12 @@ void TurnRound(int x, int y) else MovDir[x][y] = back_dir; - MovDelay[x][y] = 16+16*RND(3); + MovDelay[x][y] = 16 + 16 * RND(3); } else if (element == EL_DARK_YAMYAM) { - boolean can_turn_left = FALSE, can_turn_right = FALSE; - - if (IN_LEV_FIELD(left_x, left_y) && - (IS_FREE_OR_PLAYER(left_x, left_y) || - IS_FOOD_DARK_YAMYAM(Feld[left_x][left_y]))) - can_turn_left = TRUE; - if (IN_LEV_FIELD(right_x, right_y) && - (IS_FREE_OR_PLAYER(right_x, right_y) || - IS_FOOD_DARK_YAMYAM(Feld[right_x][right_y]))) - can_turn_right = TRUE; + boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y); + boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y); if (can_turn_left && can_turn_right) MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir); @@ -2718,20 +3243,12 @@ void TurnRound(int x, int y) else MovDir[x][y] = back_dir; - MovDelay[x][y] = 16+16*RND(3); + MovDelay[x][y] = 16 + 16 * RND(3); } else if (element == EL_PACMAN) { - boolean can_turn_left = FALSE, can_turn_right = FALSE; - - if (IN_LEV_FIELD(left_x, left_y) && - (IS_FREE_OR_PLAYER(left_x, left_y) || - IS_AMOEBOID(Feld[left_x][left_y]))) - can_turn_left = TRUE; - if (IN_LEV_FIELD(right_x, right_y) && - (IS_FREE_OR_PLAYER(right_x, right_y) || - IS_AMOEBOID(Feld[right_x][right_y]))) - can_turn_right = TRUE; + boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(left_x, left_y); + boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y); if (can_turn_left && can_turn_right) MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir); @@ -2742,56 +3259,45 @@ void TurnRound(int x, int y) else MovDir[x][y] = back_dir; - MovDelay[x][y] = 6+RND(40); + MovDelay[x][y] = 6 + RND(40); } else if (element == EL_PIG) { - boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE; - boolean should_turn_left = FALSE, should_turn_right = FALSE; - boolean should_move_on = FALSE; + boolean can_turn_left = PIG_CAN_ENTER_FIELD(left_x, left_y); + boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y); + boolean can_move_on = PIG_CAN_ENTER_FIELD(move_x, move_y); + boolean should_turn_left, should_turn_right, should_move_on; int rnd_value = 24; int rnd = RND(rnd_value); - if (IN_LEV_FIELD(left_x, left_y) && - (IS_FREE(left_x, left_y) || IS_FOOD_PIG(Feld[left_x][left_y]))) - can_turn_left = TRUE; - if (IN_LEV_FIELD(right_x, right_y) && - (IS_FREE(right_x, right_y) || IS_FOOD_PIG(Feld[right_x][right_y]))) - can_turn_right = TRUE; - if (IN_LEV_FIELD(move_x, move_y) && - (IS_FREE(move_x, move_y) || IS_FOOD_PIG(Feld[move_x][move_y]))) - can_move_on = TRUE; - - if (can_turn_left && - (!can_move_on || - (IN_LEV_FIELD(x+back_dx+left_dx, y+back_dy+left_dy) && - !IS_FREE(x+back_dx+left_dx, y+back_dy+left_dy)))) - should_turn_left = TRUE; - if (can_turn_right && - (!can_move_on || - (IN_LEV_FIELD(x+back_dx+right_dx, y+back_dy+right_dy) && - !IS_FREE(x+back_dx+right_dx, y+back_dy+right_dy)))) - should_turn_right = TRUE; - if (can_move_on && - (!can_turn_left || !can_turn_right || - (IN_LEV_FIELD(x+move_dx+left_dx, y+move_dy+left_dy) && - !IS_FREE(x+move_dx+left_dx, y+move_dy+left_dy)) || - (IN_LEV_FIELD(x+move_dx+right_dx, y+move_dy+right_dy) && - !IS_FREE(x+move_dx+right_dx, y+move_dy+right_dy)))) - should_move_on = TRUE; + should_turn_left = (can_turn_left && + (!can_move_on || + IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx, + y + back_dy + left_dy))); + should_turn_right = (can_turn_right && + (!can_move_on || + IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx, + y + back_dy + right_dy))); + should_move_on = (can_move_on && + (!can_turn_left || + !can_turn_right || + IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx, + y + move_dy + left_dy) || + IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx, + y + move_dy + right_dy))); if (should_turn_left || should_turn_right || should_move_on) { if (should_turn_left && should_turn_right && should_move_on) - MovDir[x][y] = (rnd < rnd_value/3 ? left_dir : - rnd < 2*rnd_value/3 ? right_dir : + MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir : + rnd < 2 * rnd_value / 3 ? right_dir : old_move_dir); else if (should_turn_left && should_turn_right) - MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : right_dir); + MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir); else if (should_turn_left && should_move_on) - MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : old_move_dir); + MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir); else if (should_turn_right && should_move_on) - MovDir[x][y] = (rnd < rnd_value/2 ? right_dir : old_move_dir); + MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir); else if (should_turn_left) MovDir[x][y] = left_dir; else if (should_turn_right) @@ -2799,69 +3305,67 @@ void TurnRound(int x, int y) else if (should_move_on) MovDir[x][y] = old_move_dir; } - else if (can_move_on && rnd > rnd_value/8) + else if (can_move_on && rnd > rnd_value / 8) MovDir[x][y] = old_move_dir; else if (can_turn_left && can_turn_right) - MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : right_dir); - else if (can_turn_left && rnd > rnd_value/8) + MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir); + else if (can_turn_left && rnd > rnd_value / 8) MovDir[x][y] = left_dir; else if (can_turn_right && rnd > rnd_value/8) MovDir[x][y] = right_dir; else MovDir[x][y] = back_dir; - if (!IS_FREE(x+move_xy[MovDir[x][y]].x, y+move_xy[MovDir[x][y]].y) && - !IS_FOOD_PIG(Feld[x+move_xy[MovDir[x][y]].x][y+move_xy[MovDir[x][y]].y])) + xx = x + move_xy[MovDir[x][y]].x; + yy = y + move_xy[MovDir[x][y]].y; + + if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])) MovDir[x][y] = old_move_dir; MovDelay[x][y] = 0; } else if (element == EL_DRAGON) { - boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE; + boolean can_turn_left = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y); + boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y); + boolean can_move_on = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y); int rnd_value = 24; int rnd = RND(rnd_value); - if (IN_LEV_FIELD(left_x, left_y) && IS_FREE(left_x, left_y)) - can_turn_left = TRUE; - if (IN_LEV_FIELD(right_x, right_y) && IS_FREE(right_x, right_y)) - can_turn_right = TRUE; - if (IN_LEV_FIELD(move_x, move_y) && IS_FREE(move_x, move_y)) - can_move_on = TRUE; - - if (can_move_on && rnd > rnd_value/8) + if (can_move_on && rnd > rnd_value / 8) MovDir[x][y] = old_move_dir; else if (can_turn_left && can_turn_right) - MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : right_dir); - else if (can_turn_left && rnd > rnd_value/8) + MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir); + else if (can_turn_left && rnd > rnd_value / 8) MovDir[x][y] = left_dir; - else if (can_turn_right && rnd > rnd_value/8) + else if (can_turn_right && rnd > rnd_value / 8) MovDir[x][y] = right_dir; else MovDir[x][y] = back_dir; - if (!IS_FREE(x+move_xy[MovDir[x][y]].x, y+move_xy[MovDir[x][y]].y)) + xx = x + move_xy[MovDir[x][y]].x; + yy = y + move_xy[MovDir[x][y]].y; + + if (!IS_FREE(xx, yy)) MovDir[x][y] = old_move_dir; MovDelay[x][y] = 0; } else if (element == EL_MOLE) { - boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE; - - if (IN_LEV_FIELD(move_x, move_y) && - (IS_FREE(move_x, move_y) || IS_AMOEBOID(Feld[move_x][move_y]) || - Feld[move_x][move_y] == EL_AMOEBA_SHRINKING)) - can_move_on = TRUE; - + boolean can_move_on = + (MOLE_CAN_ENTER_FIELD(move_x, move_y, + IS_AMOEBOID(Feld[move_x][move_y]) || + Feld[move_x][move_y] == EL_AMOEBA_SHRINKING)); if (!can_move_on) { - if (IN_LEV_FIELD(left_x, left_y) && - (IS_FREE(left_x, left_y) || IS_AMOEBOID(Feld[left_x][left_y]))) - can_turn_left = TRUE; - if (IN_LEV_FIELD(right_x, right_y) && - (IS_FREE(right_x, right_y) || IS_AMOEBOID(Feld[right_x][right_y]))) - can_turn_right = TRUE; + boolean can_turn_left = + (MOLE_CAN_ENTER_FIELD(left_x, left_y, + IS_AMOEBOID(Feld[left_x][left_y]))); + + boolean can_turn_right = + (MOLE_CAN_ENTER_FIELD(right_x, right_y, + IS_AMOEBOID(Feld[right_x][right_y]))); if (can_turn_left && can_turn_right) MovDir[x][y] = (RND(2) ? left_dir : right_dir); @@ -2881,9 +3385,9 @@ void TurnRound(int x, int y) } else if (element == EL_SPRING) { - if ((MovDir[x][y] == MV_LEFT || MovDir[x][y] == MV_RIGHT) && - (!IN_LEV_FIELD(move_x, move_y) || !IS_FREE(move_x, move_y) || - (IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1)))) + if (MovDir[x][y] & MV_HORIZONTAL && + (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) || + IN_LEV_FIELD_AND_IS_FREE(x, y + 1))) MovDir[x][y] = MV_NO_MOVING; MovDelay[x][y] = 0; @@ -2911,7 +3415,8 @@ void TurnRound(int x, int y) if (!player->active) continue; - if (attr_x == -1 || ABS(jx-x)+ABS(jy-y) < ABS(attr_x-x)+ABS(attr_y-y)) + if (attr_x == -1 || + ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y)) { attr_x = jx; attr_y = jy; @@ -2969,11 +3474,40 @@ void TurnRound(int x, int y) Moving2Blocked(x, y, &newx, &newy); if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy)) - MovDelay[x][y] = 8+8*!RND(3); + MovDelay[x][y] = 8 + 8 * !RND(3); else MovDelay[x][y] = 16; } - else + else if (element == EL_PENGUIN) + { + int newx, newy; + + MovDelay[x][y] = 1; + + if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL) + { + boolean first_horiz = RND(2); + int new_move_dir = MovDir[x][y]; + + MovDir[x][y] = + new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL); + Moving2Blocked(x, y, &newx, &newy); + + if (PENGUIN_CAN_ENTER_FIELD(newx, newy)) + return; + + MovDir[x][y] = + new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL); + Moving2Blocked(x, y, &newx, &newy); + + if (PENGUIN_CAN_ENTER_FIELD(newx, newy)) + return; + + MovDir[x][y] = old_move_dir; + return; + } + } + else /* (element == EL_SATELLITE) */ { int newx, newy; @@ -2988,24 +3522,14 @@ void TurnRound(int x, int y) new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL); Moving2Blocked(x, y, &newx, &newy); - if (IN_LEV_FIELD(newx, newy) && - (IS_FREE(newx, newy) || - Feld[newx][newy] == EL_ACID || - (element == EL_PENGUIN && - (Feld[newx][newy] == EL_EXIT_OPEN || - IS_FOOD_PENGUIN(Feld[newx][newy]))))) + if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy)) return; MovDir[x][y] = new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL); Moving2Blocked(x, y, &newx, &newy); - if (IN_LEV_FIELD(newx, newy) && - (IS_FREE(newx, newy) || - Feld[newx][newy] == EL_ACID || - (element == EL_PENGUIN && - (Feld[newx][newy] == EL_EXIT_OPEN || - IS_FOOD_PENGUIN(Feld[newx][newy]))))) + if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy)) return; MovDir[x][y] = old_move_dir; @@ -3013,20 +3537,18 @@ void TurnRound(int x, int y) } } } - else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS) + 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) { - boolean can_turn_left = FALSE, can_turn_right = FALSE; + 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); - if (IN_LEV_FIELD(left_x, left_y) && - (IS_FREE(left_x, left_y) || - (DONT_COLLIDE_WITH(element) && IS_FREE_OR_PLAYER(left_x, left_y)))) - can_turn_left = TRUE; - if (IN_LEV_FIELD(right_x, right_y) && - (IS_FREE(right_x, right_y) || - (DONT_COLLIDE_WITH(element) && IS_FREE_OR_PLAYER(right_x, right_y)))) - can_turn_right = TRUE; - - if (can_turn_left && can_turn_right) + if (element_info[element].move_pattern == MV_TURNING_LEFT) + MovDir[x][y] = left_dir; + else if (element_info[element].move_pattern == MV_TURNING_RIGHT) + MovDir[x][y] = 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) MovDir[x][y] = (RND(2) ? left_dir : back_dir); @@ -3056,13 +3578,9 @@ void TurnRound(int x, int y) } else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE) { - if (IN_LEV_FIELD(left_x, left_y) && - (IS_FREE(left_x, left_y) || - (DONT_COLLIDE_WITH(element) && IS_FREE_OR_PLAYER(left_x, left_y)))) + if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y)) MovDir[x][y] = left_dir; - else if (!IN_LEV_FIELD(move_x, move_y) || - (!IS_FREE(move_x, move_y) && - (!DONT_COLLIDE_WITH(element) || !IS_FREE_OR_PLAYER(move_x, move_y)))) + else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y)) MovDir[x][y] = right_dir; if (MovDir[x][y] != old_move_dir) @@ -3070,13 +3588,9 @@ void TurnRound(int x, int y) } else if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE) { - if (IN_LEV_FIELD(right_x, right_y) && - (IS_FREE(right_x, right_y) || - (DONT_COLLIDE_WITH(element) && IS_FREE_OR_PLAYER(right_x, right_y)))) + if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y)) MovDir[x][y] = right_dir; - else if (!IN_LEV_FIELD(move_x, move_y) || - (!IS_FREE(move_x, move_y) && - (!DONT_COLLIDE_WITH(element) || !IS_FREE_OR_PLAYER(move_x, move_y)))) + else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y)) MovDir[x][y] = left_dir; if (MovDir[x][y] != old_move_dir) @@ -3107,7 +3621,8 @@ void TurnRound(int x, int y) if (!player->active) continue; - if (attr_x == -1 || ABS(jx-x)+ABS(jy-y) < ABS(attr_x-x)+ABS(attr_y-y)) + if (attr_x == -1 || + ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y)) { attr_x = jx; attr_y = jy; @@ -3136,20 +3651,14 @@ void TurnRound(int x, int y) new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL); Moving2Blocked(x, y, &newx, &newy); - if (IN_LEV_FIELD(newx, newy) && (IS_FREE(newx, newy) || - (DONT_COLLIDE_WITH(element) && - IS_FREE_OR_PLAYER(newx, newy)) || - Feld[newx][newy] == EL_ACID)) + if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy)) return; MovDir[x][y] = new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL); Moving2Blocked(x, y, &newx, &newy); - if (IN_LEV_FIELD(newx, newy) && (IS_FREE(newx, newy) || - (DONT_COLLIDE_WITH(element) && - IS_FREE_OR_PLAYER(newx, newy)) || - Feld[newx][newy] == EL_ACID)) + if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy)) return; MovDir[x][y] = old_move_dir; @@ -3187,11 +3696,14 @@ void StartMoving(int x, int y) if (Stop[x][y]) return; - GfxAction[x][y] = ACTION_DEFAULT; + /* !!! this should be handled more generic (not only for mole) !!! */ + if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING) + GfxAction[x][y] = ACTION_DEFAULT; if (CAN_FALL(element) && y < lev_fieldy - 1) { - if ((x>0 && IS_PLAYER(x-1, y)) || (x 0 && IS_PLAYER(x - 1, y)) || + (x < lev_fieldx-1 && IS_PLAYER(x + 1, y))) if (JustBeingPushed(x, y)) return; @@ -3332,11 +3844,26 @@ void StartMoving(int x, int y) GfxAction[x][y + 1] = ACTION_ACTIVE; #endif } +#if 1 +#if 1 + else if (game.engine_version < RELEASE_IDENT(2,2,0,7) && + CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED && + JustStopped[x][y] && !Pushed[x][y + 1]) +#else else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED && JustStopped[x][y]) +#endif { + /* calling "Impact()" here is not only completely unneccessary + (because it already gets called from "ContinueMoving()" in + all relevant situations), but also completely bullshit, because + "JustStopped" also indicates a finished *horizontal* movement; + we must keep this trash for backwards compatibility with older + tapes */ + Impact(x, y); } +#endif else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug) { if (MovDir[x][y] == MV_NO_MOVING) @@ -3370,32 +3897,53 @@ void StartMoving(int x, int y) element != EL_DX_SUPABOMB) #endif #else - else if ((IS_SLIPPERY(Feld[x][y + 1]) || + else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) || (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) && !IS_FALLING(x, y + 1) && !JustStopped[x][y + 1] && element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE) #endif { - boolean left = (x>0 && IS_FREE(x-1, y) && - (IS_FREE(x-1, y + 1) || Feld[x-1][y + 1] == EL_ACID)); - boolean right = (x 0 && IS_FREE(x - 1, y) && + (IS_FREE(x - 1, y + 1) || + Feld[x - 1][y + 1] == EL_ACID)); + boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) && + (IS_FREE(x + 1, y + 1) || + Feld[x + 1][y + 1] == EL_ACID)); + boolean can_fall_any = (can_fall_left || can_fall_right); + boolean can_fall_both = (can_fall_left && can_fall_right); + + if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1])) + { + int slippery_type = element_info[Feld[x][y + 1]].slippery_type; + + if (slippery_type == SLIPPERY_ONLY_LEFT) + can_fall_right = FALSE; + else if (slippery_type == SLIPPERY_ONLY_RIGHT) + can_fall_left = FALSE; + else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both) + can_fall_right = FALSE; + else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both) + can_fall_left = FALSE; + + can_fall_any = (can_fall_left || can_fall_right); + can_fall_both = (can_fall_left && can_fall_right); + } - if (left || right) + if (can_fall_any) { - if (left && right && + if (can_fall_both && (game.emulation != EMU_BOULDERDASH && element != EL_BD_ROCK && element != EL_BD_DIAMOND)) - left = !(right = RND(2)); + can_fall_left = !(can_fall_right = RND(2)); - InitMovingField(x, y, left ? MV_LEFT : MV_RIGHT); + InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT); started_moving = TRUE; } } else if (IS_BELT_ACTIVE(Feld[x][y + 1])) { - boolean left_is_free = (x>0 && IS_FREE(x-1, y)); - boolean right_is_free = (x 0 && IS_FREE(x - 1, y)); + boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y)); int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]); int belt_dir = game.belt_dir[belt_nr]; @@ -3410,7 +3958,7 @@ void StartMoving(int x, int y) } } - /* not "else if" because of EL_SPRING */ + /* not "else if" because of elements that can fall and move (EL_SPRING) */ if (CAN_MOVE(element) && !started_moving) { int newx, newy; @@ -3443,7 +3991,9 @@ 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_ANY_DIRECTION) && + element_info[element].move_pattern != MV_TURNING_LEFT && + element_info[element].move_pattern != MV_TURNING_RIGHT) { TurnRound(x, y); @@ -3544,14 +4094,18 @@ void StartMoving(int x, int y) return; } - GfxAction[x][y] = ACTION_MOVING; + /* 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; } /* now make next step */ Moving2Blocked(x, y, &newx, &newy); /* get next screen position */ - if (DONT_COLLIDE_WITH(element) && IS_PLAYER(newx, newy) && + if (DONT_COLLIDE_WITH(element) && + IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) && !PLAYER_PROTECTED(newx, newy)) { #if 1 @@ -3728,9 +4282,14 @@ void StartMoving(int x, int y) { Feld[newx][newy] = EL_AMOEBA_SHRINKING; PlaySoundLevel(x, y, SND_MOLE_DIGGING); - MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */ - return; /* wait for shrinking amoeba */ - } + + 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; @@ -3752,7 +4311,8 @@ void StartMoving(int x, int y) TurnRound(x, y); #if 1 - DrawLevelElementAnimation(x, y, element); + if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */ + DrawLevelElementAnimation(x, y, element); #else if (element == EL_BUG || element == EL_SPACESHIP || @@ -3794,34 +4354,14 @@ void ContinueMoving(int x, int y) 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 horiz_move = (dx != 0); int newx = x + dx, newy = y + dy; - int step = (horiz_move ? dx : dy) * TILEX / MOVE_DELAY_NORMAL_SPEED; - - if (element == EL_AMOEBA_DROP || element == EL_AMOEBA_DROPPING) - step /= 2; - else if (element == EL_QUICKSAND_FILLING || - element == EL_QUICKSAND_EMPTYING) - step /= 4; - else if (element == EL_MAGIC_WALL_FILLING || - element == EL_BD_MAGIC_WALL_FILLING || - element == EL_MAGIC_WALL_EMPTYING || - element == EL_BD_MAGIC_WALL_EMPTYING) - step /= 2; - else if (CAN_FALL(element) && horiz_move && - y < lev_fieldy-1 && IS_BELT_ACTIVE(Feld[x][y+1])) - step /= 2; - else if (element == EL_SPRING && horiz_move) - step *= 2; - else if (IS_CUSTOM_ELEMENT(element)) - step = SIGN(step) * element_info[element].move_stepsize; + int nextx = newx + dx, nexty = newy + dy; + boolean pushed = Pushed[x][y]; -#if OLD_GAME_BEHAVIOUR - else if (CAN_FALL(element) && horiz_move && !IS_SP_ELEMENT(element)) - step*=2; -#endif + MovPos[x][y] += getElementMoveStepsize(x, y); - MovPos[x][y] += step; + if (pushed) /* special case: moving object pushed by player */ + MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos)); if (ABS(MovPos[x][y]) >= TILEX) /* object reached its destination */ { @@ -3831,31 +4371,11 @@ void ContinueMoving(int x, int y) if (element == EL_MOLE) { - int i; - static int xy[4][2] = - { - { 0, -1 }, - { -1, 0 }, - { +1, 0 }, - { 0, +1 } - }; - Feld[x][y] = EL_SAND; - DrawLevelField(x, y); - - for(i=0; i<4; i++) - { - int xx, yy; - - xx = x + xy[i][0]; - yy = y + xy[i][1]; - if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_SAND) - DrawLevelField(xx, yy); /* for "crumbled sand" */ - } + DrawLevelFieldCrumbledSandNeighbours(x, y); } - - if (element == EL_QUICKSAND_FILLING) + else if (element == EL_QUICKSAND_FILLING) { element = Feld[newx][newy] = get_next_element(element); Store[newx][newy] = Store[x][y]; @@ -3898,6 +4418,16 @@ void ContinueMoving(int x, int y) Feld[x][y] = get_next_element(element); element = Feld[newx][newy] = Store[x][y]; } + else if (element == EL_SOKOBAN_OBJECT) + { + if (Back[x][y]) + Feld[x][y] = Back[x][y]; + + if (Back[newx][newy]) + Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL; + + Back[x][y] = Back[newx][newy] = 0; + } else if (Store[x][y] == EL_ACID) { element = Feld[newx][newy] = EL_ACID; @@ -3915,20 +4445,29 @@ void ContinueMoving(int x, int y) GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */ GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */ + Pushed[x][y] = Pushed[newx][newy] = FALSE; + ResetGfxAnimation(x, y); /* reset animation values for old field */ -#if 1 #if 0 + /* 2.1.1 (does not work correctly for spring) */ if (!CAN_MOVE(element)) MovDir[newx][newy] = 0; #else - /* + +#if 0 + /* (does not work for falling objects that slide horizontally) */ if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN) MovDir[newx][newy] = 0; +#else + /* + if (!CAN_MOVE(element) || + (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN)) + MovDir[newx][newy] = 0; */ if (!CAN_MOVE(element) || - (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN)) + (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)) MovDir[newx][newy] = 0; #endif #endif @@ -3936,8 +4475,10 @@ void ContinueMoving(int x, int y) DrawLevelField(x, y); DrawLevelField(newx, newy); - Stop[newx][newy] = TRUE; - JustStopped[newx][newy] = 3; + Stop[newx][newy] = TRUE; /* ignore this element until the next frame */ + + if (!pushed) /* special case: moving object pushed by player */ + JustStopped[newx][newy] = 3; if (DONT_TOUCH(element)) /* object may be nasty to player or others */ { @@ -3948,15 +4489,15 @@ void ContinueMoving(int x, int y) else if (element == EL_PENGUIN) TestIfFriendTouchesBadThing(newx, newy); -#if 1 if (CAN_FALL(element) && direction == MV_DOWN && (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1))) Impact(x, newy); -#else - if (CAN_SMASH(element) && direction == MV_DOWN && - (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1))) - Impact(x, newy); -#endif + + if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty)) + CheckElementChange(newx, newy, element, CE_COLLISION); + + TestIfPlayerTouchesCustomElement(newx, newy); + TestIfElementTouchesCustomElement(newx, newy); } else /* still moving on */ { @@ -4265,6 +4806,7 @@ void AmoebeAbleger(int ax, int ay) if (!IN_LEV_FIELD(x, y)) return; + /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */ if (IS_FREE(x, y) || Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY) { @@ -4289,6 +4831,7 @@ void AmoebeAbleger(int ax, int ay) if (!IN_LEV_FIELD(x, y)) continue; + /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */ if (IS_FREE(x, y) || Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY) { @@ -4433,6 +4976,7 @@ void Life(int ax, int ay) changed = TRUE; } } + /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */ else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND) { /* free border field */ if (nachbarn >= life[2] && nachbarn <= life[3]) @@ -4848,10 +5392,19 @@ static void ChangeActiveTrap(int x, int y) DrawLevelFieldCrumbledSand(x, y); } -static void ChangeElementDoIt(int x, int y, int element) +static void ChangeElementNowExt(int x, int y, int target_element) { + /* check if element under player changes from accessible to unaccessible + (needed for special case of dropping element which then changes) */ + if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) && + IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element)) + { + Bang(x, y); + return; + } + RemoveField(x, y); - Feld[x][y] = element; + Feld[x][y] = target_element; ResetGfxAnimation(x, y); ResetRandomAnimationValue(x, y); @@ -4863,56 +5416,133 @@ static void ChangeElementDoIt(int x, int y, int element) DrawLevelField(x, y); if (CAN_BE_CRUMBLED(Feld[x][y])) + DrawLevelFieldCrumbledSandNeighbours(x, y); + + TestIfBadThingTouchesHero(x, y); + TestIfPlayerTouchesCustomElement(x, y); + TestIfElementTouchesCustomElement(x, y); + + if (ELEM_IS_PLAYER(target_element)) + RelocatePlayer(x, y, target_element); +} + +static void ChangeElementNow(int x, int y, int element, int page) +{ + struct ElementChangeInfo *change = &element_info[element].change_page[page]; + + CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING); + + if (change->explode) { - int sx = SCREENX(x), sy = SCREENY(y); - static int xy[4][2] = - { - { 0, -1 }, - { -1, 0 }, - { +1, 0 }, - { 0, +1 } - }; - int i; + Bang(x, y); + return; + } + + if (change->use_content) + { + boolean complete_change = TRUE; + boolean can_change[3][3]; + int xx, yy; - for(i=0; i<4; i++) + for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++) { - int xx = x + xy[i][0]; - int yy = y + xy[i][1]; - int sxx = sx + xy[i][0]; - int syy = sy + xy[i][1]; + boolean half_destructible; + int ex = x + xx - 1; + int ey = y + yy - 1; + int e; + + can_change[xx][yy] = TRUE; + + if (ex == x && ey == y) /* do not check changing element itself */ + continue; + + if (change->content[xx][yy] == EL_EMPTY_SPACE) + { + can_change[xx][yy] = FALSE; /* do not change empty borders */ - if (!IN_LEV_FIELD(xx, yy) || - !IN_SCR_FIELD(sxx, syy) || - !CAN_BE_CRUMBLED(Feld[xx][yy]) || - IS_MOVING(xx, yy)) continue; + } + + if (!IN_LEV_FIELD(ex, ey)) + { + can_change[xx][yy] = FALSE; + complete_change = FALSE; + + continue; + } + + e = Feld[ex][ey]; + + if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey)) + e = MovingOrBlocked2Element(ex, ey); + + half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e)); + + if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) || + (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) || + (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e))) + { + can_change[xx][yy] = FALSE; + complete_change = FALSE; + } + } + + if (!change->only_complete || complete_change) + { + boolean something_has_changed = FALSE; + + if (change->only_complete && change->use_random_change && + RND(100) < change->random) + return; + + for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++) + { + int ex = x + xx - 1; + int ey = y + yy - 1; + + if (can_change[xx][yy] && (!change->use_random_change || + RND(100) < change->random)) + { + if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey)) + RemoveMovingField(ex, ey); + + ChangeElementNowExt(ex, ey, change->content[xx][yy]); + + something_has_changed = TRUE; + + /* for symmetry reasons, stop newly created border elements */ + if (ex != x || ey != y) + Stop[ex][ey] = TRUE; + } + } - DrawLevelField(xx, yy); + if (something_has_changed) + PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING); } } + else + { + ChangeElementNowExt(x, y, change->target_element); + + PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING); + } } -static void ChangeElement(int x, int y) +static void ChangeElement(int x, int y, int page) { - int element = Feld[x][y]; + int element = MovingOrBlocked2Element(x, y); + struct ElementChangeInfo *change = &element_info[element].change_page[page]; if (ChangeDelay[x][y] == 0) /* initialize element change */ { - ChangeDelay[x][y] = changing_element[element].change_delay + 1; - - if (IS_CUSTOM_ELEMENT(element) && HAS_CHANGE_EVENT(element, CE_DELAY)) - { - int max_random_delay = element_info[element].change.delay_random; - int delay_frames = element_info[element].change.delay_frames; - - ChangeDelay[x][y] += RND(max_random_delay * delay_frames); - } + ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames + + RND(change->delay_random * change->delay_frames)) + 1; ResetGfxAnimation(x, y); ResetRandomAnimationValue(x, y); - if (changing_element[element].pre_change_function) - changing_element[element].pre_change_function(x, y); + if (change->pre_change_function) + change->pre_change_function(x, y); } ChangeDelay[x][y]--; @@ -4924,13 +5554,11 @@ static void ChangeElement(int x, int y) if (IS_ANIMATED(graphic)) DrawLevelGraphicAnimationIfNeeded(x, y, graphic); - if (changing_element[element].change_function) - changing_element[element].change_function(x, y); + if (change->change_function) + change->change_function(x, y); } else /* finish element change */ { - int next_element = changing_element[element].next_element; - if (IS_MOVING(x, y)) /* never change a running system ;-) */ { ChangeDelay[x][y] = 1; /* try change after next move step */ @@ -4938,45 +5566,111 @@ static void ChangeElement(int x, int y) return; } - if (next_element != EL_UNDEFINED) - ChangeElementDoIt(x, y, next_element); - else - ChangeElementDoIt(x, y, element_info[element].change.successor); + ChangeElementNow(x, y, element, page); - if (changing_element[element].post_change_function) - changing_element[element].post_change_function(x, y); + if (change->post_change_function) + change->post_change_function(x, y); } } -static void CheckTriggeredElementChange(int trigger_element, int trigger_event) +static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element, + int trigger_event) { - int i, x, y; + int i, j, x, y; if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event))) - return; + return FALSE; - for (i=0; itrigger_element != trigger_element) + continue; +#endif + for (y=0; yMovPos == 0) - { -#if 0 - printf("Trying... Player frame reset\n"); -#endif - InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir); - } if (player->MovPos == 0) /* needed for tape.playing */ player->is_moving = FALSE; @@ -5138,6 +5827,30 @@ void GameActions() stored_player[i].Frame++; #endif +#if 1 + if (game.engine_version < RELEASE_IDENT(2,2,0,7)) + { + for (i=0; ijx; + int y = player->jy; + + if (player->active && player->Pushing && player->is_moving && + IS_MOVING(x, y)) + { + ContinueMoving(x, y); + + /* continue moving after pushing (this is actually a bug) */ + if (!IS_MOVING(x, y)) + { + Stop[x][y] = FALSE; + } + } + } + } +#endif + for (y=0; yjx != jx || player->jy != jy) + return MF_NO_ACTION; + StorePlayer[jx][jy] = 0; player->last_jx = jx; player->last_jy = jy; @@ -5864,6 +6594,9 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy) player->last_move_dir = player->MovDir; player->is_moving = TRUE; +#if 1 + player->snapped = FALSE; +#endif } else { @@ -5876,6 +6609,7 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy) } TestIfHeroTouchesBadThing(jx, jy); + TestIfPlayerTouchesCustomElement(jx, jy); if (!player->active) RemoveHero(player); @@ -5900,7 +6634,9 @@ void ScrollFigure(struct PlayerInfo *player, int mode) if (Feld[last_jx][last_jy] == EL_EMPTY) Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING; +#if 0 DrawPlayer(player); +#endif return; } else if (!FrameReached(&player->actual_frame_counter, 1)) @@ -5916,7 +6652,9 @@ void ScrollFigure(struct PlayerInfo *player, int mode) if (player->MovPos == 0) CheckGravityMovement(player); - DrawPlayer(player); +#if 0 + DrawPlayer(player); /* needed here only to cleanup last field */ +#endif if (player->MovPos == 0) { @@ -5935,6 +6673,7 @@ void ScrollFigure(struct PlayerInfo *player, int mode) if (Feld[jx][jy] == EL_EXIT_OPEN || Feld[jx][jy] == EL_SP_EXIT_OPEN) { + DrawPlayer(player); /* needed here only to cleanup last field */ RemoveHero(player); if (local_player->friends_still_needed == 0 || @@ -5976,71 +6715,191 @@ void ScrollScreen(struct PlayerInfo *player, int mode) ScreenMovDir = MV_NO_MOVING; } -void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir) +void TestIfPlayerTouchesCustomElement(int x, int y) { - int i, kill_x = -1, kill_y = -1; - static int test_xy[4][2] = + static boolean check_changing = FALSE; + static int xy[4][2] = { { 0, -1 }, { -1, 0 }, { +1, 0 }, { 0, +1 } }; - static int test_dir[4] = - { - MV_UP, - MV_LEFT, - MV_RIGHT, - MV_DOWN - }; + int i; + + if (check_changing) /* prevent this function from running into a loop */ + return; + + check_changing = TRUE; for (i=0; i<4; i++) { - int test_x, test_y, test_move_dir, test_element; + int xx = x + xy[i][0]; + int yy = y + xy[i][1]; - test_x = good_x + test_xy[i][0]; - test_y = good_y + test_xy[i][1]; - if (!IN_LEV_FIELD(test_x, test_y)) + if (!IN_LEV_FIELD(xx, yy)) continue; - test_move_dir = - (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING); - -#if 0 - test_element = Feld[test_x][test_y]; -#else - test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y); -#endif - - /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing; - 2nd case: DONT_TOUCH style bad thing does not move away from good thing - */ - if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) || - (DONT_TOUCH(test_element) && test_move_dir != test_dir[i])) + if (IS_PLAYER(x, y)) { - kill_x = test_x; - kill_y = test_y; - break; + CheckTriggeredElementChange(xx, yy, Feld[xx][yy], CE_OTHER_GETS_TOUCHED); + CheckElementChange(xx, yy, Feld[xx][yy], CE_TOUCHED_BY_PLAYER); } - } - - if (kill_x != -1 || kill_y != -1) - { - if (IS_PLAYER(good_x, good_y)) + else if (IS_PLAYER(xx, yy)) { - struct PlayerInfo *player = PLAYERINFO(good_x, good_y); + CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_GETS_TOUCHED); + CheckElementChange(x, y, Feld[x][y], CE_TOUCHED_BY_PLAYER); - if (player->shield_deadly_time_left > 0) - Bang(kill_x, kill_y); - else if (!PLAYER_PROTECTED(good_x, good_y)) - KillHero(player); + break; } - else - Bang(good_x, good_y); } + + check_changing = FALSE; } -void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir) +void TestIfElementTouchesCustomElement(int x, int y) +{ + static boolean check_changing = FALSE; + static int xy[4][2] = + { + { 0, -1 }, + { -1, 0 }, + { +1, 0 }, + { 0, +1 } + }; + boolean change_center_element = FALSE; + int center_element_change_page = 0; + int center_element = Feld[x][y]; + int i, j; + + if (check_changing) /* prevent this function from running into a loop */ + return; + + check_changing = TRUE; + + for (i=0; i<4; i++) + { + int xx = x + xy[i][0]; + int yy = y + xy[i][1]; + int border_element; + + if (!IN_LEV_FIELD(xx, yy)) + continue; + + border_element = Feld[xx][yy]; + + /* check for change of center element (but change it only once) */ + if (IS_CUSTOM_ELEMENT(center_element) && + 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++) + { + struct ElementChangeInfo *change = + &element_info[center_element].change_page[j]; + + if (change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) && + change->trigger_element == border_element) + { + change_center_element = TRUE; + center_element_change_page = j; + + break; + } + } + } + + /* check for change of border element */ + 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++) + { + struct ElementChangeInfo *change = + &element_info[border_element].change_page[j]; + + if (change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) && + change->trigger_element == center_element) + { + CheckElementChangeExt(xx,yy, border_element,CE_OTHER_IS_TOUCHING, j); + break; + } + } + } + } + + if (change_center_element) + CheckElementChangeExt(x, y, center_element, CE_OTHER_IS_TOUCHING, + center_element_change_page); + + check_changing = FALSE; +} + +void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir) +{ + int i, kill_x = -1, kill_y = -1; + static int test_xy[4][2] = + { + { 0, -1 }, + { -1, 0 }, + { +1, 0 }, + { 0, +1 } + }; + static int test_dir[4] = + { + MV_UP, + MV_LEFT, + MV_RIGHT, + MV_DOWN + }; + + for (i=0; i<4; i++) + { + int test_x, test_y, test_move_dir, test_element; + + test_x = good_x + test_xy[i][0]; + test_y = good_y + test_xy[i][1]; + if (!IN_LEV_FIELD(test_x, test_y)) + continue; + + test_move_dir = + (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING); + +#if 0 + test_element = Feld[test_x][test_y]; +#else + test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y); +#endif + + /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing; + 2nd case: DONT_TOUCH style bad thing does not move away from good thing + */ + if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) || + (DONT_TOUCH(test_element) && test_move_dir != test_dir[i])) + { + kill_x = test_x; + kill_y = test_y; + break; + } + } + + if (kill_x != -1 || kill_y != -1) + { + if (IS_PLAYER(good_x, good_y)) + { + struct PlayerInfo *player = PLAYERINFO(good_x, good_y); + + if (player->shield_deadly_time_left > 0) + Bang(kill_x, kill_y); + else if (!PLAYER_PROTECTED(good_x, good_y)) + KillHero(player); + } + else + Bang(good_x, good_y); + } +} + +void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir) { int i, kill_x = -1, kill_y = -1; int bad_element = Feld[bad_x][bad_y]; @@ -6266,10 +7125,12 @@ void RemoveHero(struct PlayerInfo *player) } /* + ============================================================================= checkDiagonalPushing() ----------------------------------------------------------------------------- check if diagonal input device direction results in pushing of object (by checking if the alternative direction is walkable, diggable, ...) + ============================================================================= */ static boolean checkDiagonalPushing(struct PlayerInfo *player, @@ -6292,17 +7153,21 @@ static boolean checkDiagonalPushing(struct PlayerInfo *player, } /* + ============================================================================= DigField() ----------------------------------------------------------------------------- x, y: field next to player (non-diagonal) to try to dig to real_dx, real_dy: direction as read from input device (can be diagonal) + ============================================================================= */ int DigField(struct PlayerInfo *player, int x, int y, int real_dx, int real_dy, int mode) { + boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0)); int jx = player->jx, jy = player->jy; 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 : @@ -6367,218 +7232,12 @@ int DigField(struct PlayerInfo *player, element = Feld[x][y]; -#if 1 if (mode == DF_SNAP && !IS_SNAPPABLE(element) && game.engine_version >= VERSION_IDENT(2,2,0)) return MF_NO_ACTION; -#endif switch (element) { - case EL_EMPTY: - case EL_SAND: - case EL_INVISIBLE_SAND: - case EL_INVISIBLE_SAND_ACTIVE: - case EL_TRAP: - case EL_SP_BASE: - case EL_SP_BUGGY_BASE: - case EL_SP_BUGGY_BASE_ACTIVATING: - RemoveField(x, y); -#if 1 - if (mode != DF_SNAP && element != EL_EMPTY) - { - GfxElement[x][y] = (CAN_BE_CRUMBLED(element) ? EL_SAND : element); - player->is_digging = TRUE; - } -#endif - PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING); - break; - - case EL_EMERALD: - case EL_BD_DIAMOND: - case EL_EMERALD_YELLOW: - case EL_EMERALD_RED: - case EL_EMERALD_PURPLE: - case EL_DIAMOND: - case EL_SP_INFOTRON: - case EL_PEARL: - case EL_CRYSTAL: - RemoveField(x, y); -#if 1 - if (mode != DF_SNAP) - { - GfxElement[x][y] = element; - player->is_collecting = TRUE; - } -#endif - local_player->gems_still_needed -= (element == EL_DIAMOND ? 3 : - element == EL_PEARL ? 5 : - element == EL_CRYSTAL ? 8 : 1); - if (local_player->gems_still_needed < 0) - local_player->gems_still_needed = 0; - RaiseScoreElement(element); - DrawText(DX_EMERALDS, DY_EMERALDS, - int2str(local_player->gems_still_needed, 3), FONT_TEXT_2); - PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING); - CheckTriggeredElementChange(element, CE_OTHER_COLLECTING); - break; - - case EL_SPEED_PILL: - RemoveField(x, y); - player->move_delay_value = MOVE_DELAY_HIGH_SPEED; -#if 1 - PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING); -#else - PlaySoundLevel(x, y, SND_SPEED_PILL_COLLECTING); -#endif - CheckTriggeredElementChange(element, CE_OTHER_COLLECTING); - break; - - case EL_ENVELOPE: - Feld[x][y] = EL_EMPTY; -#if 1 - PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING); -#else - PlaySoundLevel(x, y, SND_ENVELOPE_COLLECTING); -#endif - CheckTriggeredElementChange(element, CE_OTHER_COLLECTING); - break; - - case EL_EXTRA_TIME: - RemoveField(x, y); - if (level.time > 0) - { - TimeLeft += 10; - DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2); - } -#if 1 - PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING); -#else - PlaySoundStereo(SND_EXTRA_TIME_COLLECTING, SOUND_MIDDLE); -#endif - CheckTriggeredElementChange(element, CE_OTHER_COLLECTING); - break; - - case EL_SHIELD_NORMAL: - RemoveField(x, y); - player->shield_normal_time_left += 10; -#if 1 - PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING); -#else - PlaySoundLevel(x, y, SND_SHIELD_NORMAL_COLLECTING); -#endif - CheckTriggeredElementChange(element, CE_OTHER_COLLECTING); - break; - - case EL_SHIELD_DEADLY: - RemoveField(x, y); - player->shield_normal_time_left += 10; - player->shield_deadly_time_left += 10; -#if 1 - PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING); -#else - PlaySoundLevel(x, y, SND_SHIELD_DEADLY_COLLECTING); -#endif - CheckTriggeredElementChange(element, CE_OTHER_COLLECTING); - break; - - case EL_DYNAMITE: - case EL_SP_DISK_RED: - RemoveField(x, y); - player->dynamite++; - player->use_disk_red_graphic = (element == EL_SP_DISK_RED); - RaiseScoreElement(EL_DYNAMITE); - DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(local_player->dynamite, 3), - FONT_TEXT_2); - PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING); - CheckTriggeredElementChange(element, CE_OTHER_COLLECTING); - break; - - case EL_DYNABOMB_INCREASE_NUMBER: - RemoveField(x, y); - player->dynabomb_count++; - player->dynabombs_left++; - RaiseScoreElement(EL_DYNAMITE); -#if 1 - PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING); -#else - PlaySoundLevel(x, y, SND_DYNABOMB_INCREASE_NUMBER_COLLECTING); -#endif - CheckTriggeredElementChange(element, CE_OTHER_COLLECTING); - break; - - case EL_DYNABOMB_INCREASE_SIZE: - RemoveField(x, y); - player->dynabomb_size++; - RaiseScoreElement(EL_DYNAMITE); -#if 1 - PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING); -#else - PlaySoundLevel(x, y, SND_DYNABOMB_INCREASE_SIZE_COLLECTING); -#endif - CheckTriggeredElementChange(element, CE_OTHER_COLLECTING); - break; - - case EL_DYNABOMB_INCREASE_POWER: - RemoveField(x, y); - player->dynabomb_xl = TRUE; - RaiseScoreElement(EL_DYNAMITE); -#if 1 - PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING); -#else - PlaySoundLevel(x, y, SND_DYNABOMB_INCREASE_POWER_COLLECTING); -#endif - CheckTriggeredElementChange(element, CE_OTHER_COLLECTING); - break; - - case EL_KEY_1: - case EL_KEY_2: - case EL_KEY_3: - case EL_KEY_4: - { - int key_nr = element - EL_KEY_1; - int graphic = el2edimg(element); - - RemoveField(x, y); - player->key[key_nr] = TRUE; - RaiseScoreElement(element); - DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS, - graphic); - DrawMiniGraphicExt(window, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS, - graphic); -#if 1 - PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING); -#else - PlaySoundLevel(x, y, SND_CLASS_KEY_COLLECTING); -#endif - CheckTriggeredElementChange(element, CE_OTHER_COLLECTING); - break; - } - - case EL_EM_KEY_1: - case EL_EM_KEY_2: - case EL_EM_KEY_3: - case EL_EM_KEY_4: - { - int key_nr = element - EL_EM_KEY_1; - int graphic = el2edimg(EL_KEY_1 + key_nr); - - RemoveField(x, y); - player->key[key_nr] = TRUE; - RaiseScoreElement(element); - DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS, - graphic); - DrawMiniGraphicExt(window, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS, - graphic); -#if 1 - PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING); -#else - PlaySoundLevel(x, y, SND_CLASS_KEY_COLLECTING); -#endif - CheckTriggeredElementChange(element, CE_OTHER_COLLECTING); - break; - } - case EL_ROBOT_WHEEL: Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE; ZX = x; @@ -6679,140 +7338,6 @@ int DigField(struct PlayerInfo *player, return MF_ACTION; break; - /* the following elements cannot be pushed by "snapping" */ - case EL_ROCK: - case EL_BOMB: - case EL_DX_SUPABOMB: - case EL_NUT: - case EL_TIME_ORB_EMPTY: - case EL_SP_ZONK: - case EL_SP_DISK_ORANGE: - case EL_SPRING: - if (mode == DF_SNAP) - return MF_NO_ACTION; - - /* no "break" -- fall through to next case */ - - /* the following elements can be pushed by "snapping" */ - case EL_BD_ROCK: - if (dy) - return MF_NO_ACTION; - - player->Pushing = TRUE; - -#if 0 - if (element == EL_ROCK) - printf("::: wanna push [%d] [%d]\n", - FrameCounter, player->push_delay_value); -#endif - - if (!IN_LEV_FIELD(x+dx, y+dy) || !IS_FREE(x+dx, y+dy)) - return MF_NO_ACTION; - - if (!checkDiagonalPushing(player, x, y, real_dx, real_dy)) - return MF_NO_ACTION; - - if (player->push_delay == 0) - player->push_delay = FrameCounter; -#if 0 - if (!FrameReached(&player->push_delay, player->push_delay_value) && - !tape.playing && - element != EL_SPRING) - return MF_NO_ACTION; -#else - if (!FrameReached(&player->push_delay, player->push_delay_value) && - !(tape.playing && tape.file_version < FILE_VERSION_2_0) && - element != EL_SPRING) - return MF_NO_ACTION; -#endif - - if (mode == DF_SNAP) - { - InitMovingField(x, y, move_direction); - ContinueMoving(x, y); - } - else - { - RemoveField(x, y); - Feld[x + dx][y + dy] = element; - } - - if (element == EL_SPRING) - { - Feld[x + dx][y + dy] = EL_SPRING; - MovDir[x + dx][y + dy] = move_direction; - } - - player->push_delay_value = (element == EL_SPRING ? 0 : 2 + RND(8)); - - DrawLevelField(x + dx, y + dy); - PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING); - break; - - case EL_GATE_1: - case EL_GATE_2: - case EL_GATE_3: - case EL_GATE_4: - if (!player->key[element - EL_GATE_1]) - return MF_NO_ACTION; - break; - - case EL_GATE_1_GRAY: - case EL_GATE_2_GRAY: - case EL_GATE_3_GRAY: - case EL_GATE_4_GRAY: - if (!player->key[element - EL_GATE_1_GRAY]) - return MF_NO_ACTION; - break; - - case EL_EM_GATE_1: - case EL_EM_GATE_2: - case EL_EM_GATE_3: - case EL_EM_GATE_4: - if (!player->key[element - EL_EM_GATE_1]) - return MF_NO_ACTION; - if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy)) - return MF_NO_ACTION; - - /* automatically move to the next field with double speed */ - player->programmed_action = move_direction; - DOUBLE_PLAYER_SPEED(player); - - PlaySoundLevel(x, y, SND_CLASS_GATE_PASSING); - break; - - case EL_EM_GATE_1_GRAY: - case EL_EM_GATE_2_GRAY: - case EL_EM_GATE_3_GRAY: - case EL_EM_GATE_4_GRAY: - if (!player->key[element - EL_EM_GATE_1_GRAY]) - return MF_NO_ACTION; - if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy)) - return MF_NO_ACTION; - - /* automatically move to the next field with double speed */ - player->programmed_action = move_direction; - DOUBLE_PLAYER_SPEED(player); - -#if 1 - PlaySoundLevelAction(x, y, ACTION_PASSING); -#else - PlaySoundLevel(x, y, SND_GATE_PASSING); -#endif - break; - - case EL_SWITCHGATE_OPEN: - case EL_TIMEGATE_OPEN: - if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy)) - return MF_NO_ACTION; - - /* automatically move to the next field with double speed */ - player->programmed_action = move_direction; - DOUBLE_PLAYER_SPEED(player); - - PlaySoundLevelElementAction(x, y, element, ACTION_PASSING); - break; - case EL_SP_PORT_LEFT: case EL_SP_PORT_RIGHT: case EL_SP_PORT_UP: @@ -6844,10 +7369,16 @@ int DigField(struct PlayerInfo *player, element != EL_SP_GRAVITY_PORT_DOWN && element != EL_SP_PORT_VERTICAL && element != EL_SP_PORT_ANY) || - !IN_LEV_FIELD(x + dx, y + dy) || - !IS_FREE(x + dx, y + dy)) + !IN_LEV_FIELD(nextx, nexty) || + !IS_FREE(nextx, nexty)) return MF_NO_ACTION; + if (element == EL_SP_GRAVITY_PORT_LEFT || + element == EL_SP_GRAVITY_PORT_RIGHT || + element == EL_SP_GRAVITY_PORT_UP || + element == EL_SP_GRAVITY_PORT_DOWN) + level.gravity = !level.gravity; + /* automatically move to the next field with double speed */ player->programmed_action = move_direction; DOUBLE_PLAYER_SPEED(player); @@ -6894,28 +7425,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_PASSING); + PlaySoundLevel(x, y, SND_CLASS_TUBE_WALKING); } break; - case EL_EXIT_CLOSED: - case EL_SP_EXIT_CLOSED: - case EL_EXIT_OPENING: - return MF_NO_ACTION; - break; - - case EL_EXIT_OPEN: - case EL_SP_EXIT_OPEN: - if (mode == DF_SNAP) - return MF_NO_ACTION; - - if (element == EL_EXIT_OPEN) - PlaySoundLevel(x, y, SND_CLASS_EXIT_PASSING); - else - PlaySoundLevel(x, y, SND_CLASS_SP_EXIT_PASSING); - - break; - case EL_LAMP: Feld[x][y] = EL_LAMP_ACTIVE; local_player->lights_still_needed--; @@ -6933,129 +7446,78 @@ int DigField(struct PlayerInfo *player, return MF_ACTION; break; - case EL_SOKOBAN_FIELD_EMPTY: - break; - - case EL_SOKOBAN_OBJECT: - case EL_SOKOBAN_FIELD_FULL: - case EL_SATELLITE: - case EL_SP_DISK_YELLOW: - case EL_BALLOON: - if (mode == DF_SNAP) - return MF_NO_ACTION; - - player->Pushing = TRUE; - - if (!IN_LEV_FIELD(x+dx, y+dy) - || (!IS_FREE(x+dx, y+dy) - && (Feld[x+dx][y+dy] != EL_SOKOBAN_FIELD_EMPTY - || !IS_SB_ELEMENT(element)))) - return MF_NO_ACTION; - - if (!checkDiagonalPushing(player, x, y, real_dx, real_dy)) - return MF_NO_ACTION; - - if (player->push_delay == 0) - player->push_delay = FrameCounter; -#if 0 - if (!FrameReached(&player->push_delay, player->push_delay_value) && - !tape.playing && element != EL_BALLOON) - return MF_NO_ACTION; -#else - if (!FrameReached(&player->push_delay, player->push_delay_value) && - !(tape.playing && tape.file_version < FILE_VERSION_2_0) && - element != EL_BALLOON) - return MF_NO_ACTION; -#endif + default: - if (IS_SB_ELEMENT(element)) + if (IS_WALKABLE(element)) { - if (element == EL_SOKOBAN_FIELD_FULL) + int sound_action = ACTION_WALKING; + + if (element >= EL_GATE_1 && element <= EL_GATE_4) { - Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY; - local_player->sokobanfields_still_needed++; + if (!player->key[element - EL_GATE_1]) + return MF_NO_ACTION; } - else - RemoveField(x, y); - - if (Feld[x+dx][y+dy] == EL_SOKOBAN_FIELD_EMPTY) + else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY) { - Feld[x+dx][y+dy] = EL_SOKOBAN_FIELD_FULL; - local_player->sokobanfields_still_needed--; - if (element == EL_SOKOBAN_OBJECT) -#if 1 - PlaySoundLevelAction(x+dx, y+dy, ACTION_FILLING); -#else - PlaySoundLevel(x, y, SND_CLASS_SOKOBAN_FIELD_FILLING); -#endif - else -#if 1 - PlaySoundLevelAction(x+dx, y+dy, ACTION_PUSHING); -#else - PlaySoundLevel(x, y, SND_SOKOBAN_OBJECT_PUSHING); -#endif + if (!player->key[element - EL_GATE_1_GRAY]) + return MF_NO_ACTION; } - else + else if (element == EL_EXIT_OPEN || element == EL_SP_EXIT_OPEN) { - Feld[x+dx][y+dy] = EL_SOKOBAN_OBJECT; - if (element == EL_SOKOBAN_FIELD_FULL) -#if 1 - PlaySoundLevelAction(x+dx, y+dy, ACTION_EMPTYING); -#else - PlaySoundLevel(x, y, SND_SOKOBAN_FIELD_EMPTYING); -#endif - else -#if 1 - PlaySoundLevelAction(x+dx, y+dy, ACTION_PUSHING); -#else - PlaySoundLevel(x, y, SND_SOKOBAN_OBJECT_PUSHING); -#endif + sound_action = ACTION_PASSING; /* player is passing exit */ + } + else if (element == EL_EMPTY) + { + sound_action = ACTION_MOVING; /* nothing to walk on */ } - } - else - { - RemoveField(x, y); - Feld[x+dx][y+dy] = element; - PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING); - } - - player->push_delay_value = (element == EL_BALLOON ? 0 : 2); - DrawLevelField(x, y); - DrawLevelField(x + dx, y + dy); + /* play sound from background or player, whatever is available */ + if (element_info[element].sound[sound_action] != SND_UNDEFINED) + PlaySoundLevelElementAction(x, y, element, sound_action); + else + PlaySoundLevelElementAction(x, y, player->element_nr, sound_action); - if (IS_SB_ELEMENT(element) && - local_player->sokobanfields_still_needed == 0 && - game.emulation == EMU_SOKOBAN) - { - player->LevelSolved = player->GameOver = TRUE; - PlaySoundLevel(x, y, SND_GAME_SOKOBAN_SOLVING); + break; } + else if (IS_PASSABLE(element)) + { + if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty)) + return MF_NO_ACTION; - break; + if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4) + { + if (!player->key[element - EL_EM_GATE_1]) + return MF_NO_ACTION; + } + else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY) + { + if (!player->key[element - EL_EM_GATE_1_GRAY]) + return MF_NO_ACTION; + } - case EL_PENGUIN: - case EL_PIG: - case EL_DRAGON: - break; + /* automatically move to the next field with double speed */ + player->programmed_action = move_direction; + DOUBLE_PLAYER_SPEED(player); - default: + PlaySoundLevelAction(x, y, ACTION_PASSING); - if (IS_WALKABLE(element)) - { break; } else if (IS_DIGGABLE(element)) { RemoveField(x, y); -#if 1 + if (mode != DF_SNAP) { +#if 1 + GfxElement[x][y] = GFX_ELEMENT(element); +#else GfxElement[x][y] = (CAN_BE_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element)); +#endif player->is_digging = TRUE; } -#endif + PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING); break; @@ -7063,34 +7525,119 @@ int DigField(struct PlayerInfo *player, else if (IS_COLLECTIBLE(element)) { RemoveField(x, y); -#if 1 + if (mode != DF_SNAP) { GfxElement[x][y] = element; player->is_collecting = TRUE; } -#endif + + if (element == EL_SPEED_PILL) + player->move_delay_value = MOVE_DELAY_HIGH_SPEED; + else if (element == EL_EXTRA_TIME && level.time > 0) + { + TimeLeft += 10; + DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2); + } + else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY) + { + player->shield_normal_time_left += 10; + if (element == EL_SHIELD_DEADLY) + player->shield_deadly_time_left += 10; + } + else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED) + { + if (player->inventory_size < MAX_INVENTORY_SIZE) + player->inventory_element[player->inventory_size++] = element; + + player->use_disk_red_graphic = (element == EL_SP_DISK_RED); + + DrawText(DX_DYNAMITE, DY_DYNAMITE, + int2str(local_player->inventory_size, 3), FONT_TEXT_2); + } + else if (element == EL_DYNABOMB_INCREASE_NUMBER) + { + player->dynabomb_count++; + player->dynabombs_left++; + } + else if (element == EL_DYNABOMB_INCREASE_SIZE) + { + player->dynabomb_size++; + } + else if (element == EL_DYNABOMB_INCREASE_POWER) + { + player->dynabomb_xl = TRUE; + } + else if ((element >= EL_KEY_1 && element <= EL_KEY_4) || + (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4)) + { + int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ? + element - EL_KEY_1 : element - EL_EM_KEY_1); + + player->key[key_nr] = TRUE; + + DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS, + el2edimg(EL_KEY_1 + key_nr)); + redraw_mask |= REDRAW_DOOR_1; + } + else if (element == EL_ENVELOPE) + { + ShowEnvelope(); + } + else if (IS_DROPPABLE(element)) /* can be collected and dropped */ + { + int 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; + + DrawText(DX_DYNAMITE, DY_DYNAMITE, + int2str(local_player->inventory_size, 3), FONT_TEXT_2); + } + else if (element_info[element].collect_count > 0) + { + local_player->gems_still_needed -= + element_info[element].collect_count; + if (local_player->gems_still_needed < 0) + local_player->gems_still_needed = 0; + + DrawText(DX_EMERALDS, DY_EMERALDS, + int2str(local_player->gems_still_needed, 3), FONT_TEXT_2); + } + + RaiseScoreElement(element); PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING); - CheckTriggeredElementChange(element, CE_OTHER_COLLECTING); + CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED); break; } else if (IS_PUSHABLE(element)) { - if (mode == DF_SNAP) + if (mode == DF_SNAP && element != EL_BD_ROCK) return MF_NO_ACTION; if (CAN_FALL(element) && dy) 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)) + return MF_NO_ACTION; + + if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING) + return MF_NO_ACTION; + if (!player->Pushing && game.engine_version >= RELEASE_IDENT(2,2,0,7)) player->push_delay_value = GET_NEW_PUSH_DELAY(element); player->Pushing = TRUE; - if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy)) + if (!(IN_LEV_FIELD(nextx, nexty) && + (IS_FREE(nextx, nexty) || + (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY && + IS_SB_ELEMENT(element))))) return MF_NO_ACTION; if (!checkDiagonalPushing(player, x, y, real_dx, real_dy)) @@ -7100,24 +7647,69 @@ int DigField(struct PlayerInfo *player, player->push_delay = FrameCounter; if (!FrameReached(&player->push_delay, player->push_delay_value) && - !(tape.playing && tape.file_version < FILE_VERSION_2_0)) + !(tape.playing && tape.file_version < FILE_VERSION_2_0) && + element != EL_SPRING && element != EL_BALLOON) return MF_NO_ACTION; - RemoveField(x, y); - Feld[x + dx][y + dy] = element; + if (IS_SB_ELEMENT(element)) + { + if (element == EL_SOKOBAN_FIELD_FULL) + { + Back[x][y] = EL_SOKOBAN_FIELD_EMPTY; + local_player->sokobanfields_still_needed++; + } + + if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) + { + Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY; + local_player->sokobanfields_still_needed--; + } + + Feld[x][y] = EL_SOKOBAN_OBJECT; + + if (Back[x][y] == Back[nextx][nexty]) + PlaySoundLevelAction(x, y, ACTION_PUSHING); + else if (Back[x][y] != 0) + PlaySoundLevelElementAction(x, y, EL_SOKOBAN_FIELD_FULL, + ACTION_EMPTYING); + else + PlaySoundLevelElementAction(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); + } + } + else + PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING); + + InitMovingField(x, y, move_direction); + GfxAction[x][y] = ACTION_PUSHING; + + if (mode == DF_SNAP) + ContinueMoving(x, y); + else + MovPos[x][y] = (dx != 0 ? dx : dy); + + Pushed[x][y] = TRUE; + Pushed[nextx][nexty] = TRUE; -#if 1 if (game.engine_version < RELEASE_IDENT(2,2,0,7)) player->push_delay_value = GET_NEW_PUSH_DELAY(element); -#else - player->push_delay_value = 2 + RND(8); -#endif - DrawLevelField(x + dx, y + dy); - PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING); + CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_PUSHED); + CheckElementChange(x, y, element, CE_PUSHED_BY_PLAYER); break; } + else + { + CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_PRESSED); + CheckElementChange(x, y, element, CE_PRESSED_BY_PLAYER); + } return MF_NO_ACTION; } @@ -7134,6 +7726,10 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy) { int jx = player->jx, jy = player->jy; int x = jx + dx, y = jy + dy; + int snap_direction = (dx == -1 ? MV_LEFT : + dx == +1 ? MV_RIGHT : + dy == -1 ? MV_UP : + dy == +1 ? MV_DOWN : MV_NO_MOVING); if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0)) return FALSE; @@ -7155,25 +7751,42 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy) { player->is_digging = FALSE; player->is_collecting = FALSE; +#if 1 + player->is_moving = FALSE; +#endif } +#if 0 + printf("::: trying to snap...\n"); +#endif + return FALSE; } if (player->snapped) return FALSE; - player->MovDir = (dx < 0 ? MV_LEFT : - dx > 0 ? MV_RIGHT : - dy < 0 ? MV_UP : - dy > 0 ? MV_DOWN : MV_NO_MOVING); + player->MovDir = snap_direction; + +#if 1 + player->is_digging = FALSE; + player->is_collecting = FALSE; +#if 1 + player->is_moving = FALSE; +#endif +#endif if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION) return FALSE; player->snapped = TRUE; +#if 1 player->is_digging = FALSE; player->is_collecting = FALSE; +#if 1 + player->is_moving = FALSE; +#endif +#endif DrawLevelField(x, y); BackToFront(); @@ -7181,66 +7794,68 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy) return TRUE; } -boolean PlaceBomb(struct PlayerInfo *player) +boolean DropElement(struct PlayerInfo *player) { int jx = player->jx, jy = player->jy; - int element; + int old_element; if (!player->active || player->MovPos) return FALSE; - element = Feld[jx][jy]; + old_element = Feld[jx][jy]; - if ((player->dynamite == 0 && player->dynabombs_left == 0) || - IS_ACTIVE_BOMB(element) || element == EL_EXPLOSION) + /* check if player has anything that can be dropped */ + if (player->inventory_size == 0 && player->dynabombs_left == 0) return FALSE; -#if 0 - if (element != EL_EMPTY) + /* check if anything can be dropped at the current position */ + if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION) return FALSE; -#endif - if (element != EL_EMPTY) - { -#if 0 - Store[jx][jy] = element; -#else - Back[jx][jy] = element; -#endif - } + /* collected custom elements can only be dropped on empty fields */ + if (player->inventory_size > 0 && + IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1]) + && old_element != EL_EMPTY) + return FALSE; + + 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->dynamite) + if (player->inventory_size > 0) { + int new_element = player->inventory_element[--player->inventory_size]; + +#if 1 + Feld[jx][jy] = (new_element == EL_DYNAMITE ? EL_DYNAMITE_ACTIVE : + new_element == EL_SP_DISK_RED ? EL_SP_DISK_RED_ACTIVE : + new_element); +#else Feld[jx][jy] = (player->use_disk_red_graphic ? EL_SP_DISK_RED_ACTIVE : EL_DYNAMITE_ACTIVE); - player->dynamite--; +#endif + + DrawText(DX_DYNAMITE, DY_DYNAMITE, + int2str(local_player->inventory_size, 3), FONT_TEXT_2); - DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(local_player->dynamite, 3), - FONT_TEXT_2); if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy))) - { -#if 1 DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0); -#else - if (game.emulation == EMU_SUPAPLEX) - DrawGraphic(SCREENX(jx), SCREENY(jy), IMG_SP_DISK_RED, 0); - else - DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), IMG_DYNAMITE_ACTIVE, 0); -#endif - } PlaySoundLevelAction(jx, jy, ACTION_DROPPING); + + CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED); + CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER); } - else + else /* player is dropping a dyna bomb */ { + player->dynabombs_left--; + Feld[jx][jy] = EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1); - player->dynabombs_left--; if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy))) DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0); @@ -7406,6 +8021,7 @@ void RaiseScoreElement(int element) RaiseScore(level.score[SC_NUT]); break; case EL_DYNAMITE: + case EL_SP_DISK_RED: case EL_DYNABOMB_INCREASE_NUMBER: case EL_DYNABOMB_INCREASE_SIZE: case EL_DYNABOMB_INCREASE_POWER: @@ -7425,6 +8041,7 @@ void RaiseScoreElement(int element) RaiseScore(level.score[SC_KEY]); break; default: + RaiseScore(element_info[element].collect_score); break; } } @@ -7651,6 +8268,8 @@ static void HandleGameButtons(struct GadgetInfo *gi) else if (audio.music_available) { setup.sound = setup.sound_music = TRUE; + + SetAudioMode(setup.sound); PlayMusic(level_nr); } break; @@ -7659,14 +8278,20 @@ static void HandleGameButtons(struct GadgetInfo *gi) if (setup.sound_loops) setup.sound_loops = FALSE; else if (audio.loops_available) + { setup.sound = setup.sound_loops = TRUE; + SetAudioMode(setup.sound); + } break; case SOUND_CTRL_ID_SIMPLE: if (setup.sound_simple) setup.sound_simple = FALSE; else if (audio.sound_available) + { setup.sound = setup.sound_simple = TRUE; + SetAudioMode(setup.sound); + } break; default: