X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Fgame.c;h=91c8841ab9657c815e9cdb87359febc4adf9b0d7;hp=4ea608c3ac6f45538768a1b56453e62c0a73db78;hb=86b0ea5594dc5a9db7ac5d71fa2b7487a4fc1f9d;hpb=5ec67e956f52fc441582d30b4748d44febdbeed6 diff --git a/src/game.c b/src/game.c index 4ea608c3..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) + \ @@ -107,7 +110,7 @@ (condition))) #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \ - ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 1) + 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)) @@ -163,9 +166,11 @@ static void CloseAllOpenTimegates(void); static void CheckGravityMovement(struct PlayerInfo *); static void KillHeroUnlessProtected(int, int); -static void CheckTriggeredElementChange(int, int); -static void CheckPlayerElementChange(int, int, 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); @@ -202,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, @@ -352,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; @@ -376,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(); @@ -433,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; + } + + Feld[x][y] = EL_PLAYER_1; + } + } - player->present = TRUE; + if (init_game) + { + struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1]; + int jx = player->jx, jy = player->jy; - if (!options.network || player->connected) - { - player->active = TRUE; + player->present = TRUE; - /* remove potentially duplicate players */ - if (StorePlayer[jx][jy] == Feld[x][y]) - StorePlayer[jx][jy] = 0; + if (!options.network || player->connected) + { + player->active = TRUE; - StorePlayer[x][y] = Feld[x][y]; + /* remove potentially duplicate players */ + if (StorePlayer[jx][jy] == Feld[x][y]) + StorePlayer[jx][jy] = 0; - 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"); - } - } + StorePlayer[x][y] = Feld[x][y]; - Feld[x][y] = EL_EMPTY; - player->jx = player->last_jx = x; - player->jy = player->last_jy = 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: @@ -653,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, @@ -671,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 : @@ -688,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 : @@ -697,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; @@ -848,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); @@ -900,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); @@ -1211,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); @@ -1596,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)) @@ -1616,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); @@ -1706,63 +1989,218 @@ 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; + + SetDrawtoField(DRAW_BUFFERED); + + BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY); + + SetDrawtoField(DRAW_BACKBUFFER); + + for (y=0; y < 2; y++) for (x=0; x < i; x++) + { + int ex = (x == 0 ? -1 : x == i - 1 ? +1 : 0); + int ey = (y == 0 ? -1 : y == 1 ? +1 : 0); + + DrawEnvelopeBorder(startx + x, starty + y, ex, ey); + } + + redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER; + BackToFront(); + + Delay(GAME_FRAME_DELAY); } - if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */ + /* open envelope window vertically */ + for (i=2; i <= level.envelope_ysize + 2; i += 2) { - int center_element = Feld[ex][ey]; + int xsize = level.envelope_xsize + 2; + int startx = (SXSIZE / MINI_TILEX - (xsize - 1)) / 2; + int starty = (SYSIZE / MINI_TILEY - i) / 2; - /* remove things displayed in background while burning dynamite */ - if (!IS_INDESTRUCTIBLE(Back[ex][ey])) - Back[ex][ey] = 0; + SetDrawtoField(DRAW_BUFFERED); - if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey)) + BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY); + + SetDrawtoField(DRAW_BACKBUFFER); + + for (y=0; y < i; y++) for (x=0; x < xsize; x++) { - /* put moving element to center field (and let it explode there) */ - center_element = MovingOrBlocked2Element(ex, ey); - RemoveMovingField(ex, ey); - Feld[ex][ey] = center_element; + 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); } - for (y = ey - 1; y <= ey + 1; y++) for(x = ex - 1; x <= ex + 1; x++) - { - int element; + DrawTextToTextArea(SX + (startx + 1) * MINI_TILEX, + SY + (starty + 1) * MINI_TILEY, level.envelope, + FONT_TEXT_1, level.envelope_xsize, i - 2); - if (!IN_LEV_FIELD(x, y) || - ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) && - (x != ex || y != ey))) - continue; + redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER; + BackToFront(); - element = Feld[x][y]; + Delay(GAME_FRAME_DELAY); + } - if (IS_MOVING(x, y) || IS_BLOCKED(x, y)) - { - element = MovingOrBlocked2Element(x, y); - RemoveMovingField(x, y); - } + Delay(3000); -#if 1 - if (IS_EXPLOSION_PROOF(element)) - continue; -#else - if ((IS_INDESTRUCTIBLE(element) && - (game.engine_version < VERSION_IDENT(2,2,0) || - (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) || - element == EL_FLAMES) - continue; + 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) || + (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) || + element == EL_FLAMES) + continue; #endif if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y))) @@ -1778,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]) @@ -1820,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) @@ -1841,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; @@ -1848,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 || @@ -1857,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; } @@ -1929,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))) { @@ -2005,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 && @@ -2016,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)) { @@ -2026,6 +2502,7 @@ void Bang(int x, int y) player->element_nr); } +#if 0 #if 1 PlaySoundLevelAction(x, y, ACTION_EXPLODING); #else @@ -2034,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 */ @@ -2072,11 +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(element, CE_OTHER_EXPLODING); + CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING); } void SplashAcid(int x, int y) @@ -2309,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() @@ -2407,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); @@ -2423,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); @@ -2435,7 +2947,9 @@ 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); @@ -2452,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; } @@ -2536,6 +3047,13 @@ void Impact(int x, int y) Bang(x, y + 1); return; } +#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) || @@ -2585,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); + } } } @@ -3016,12 +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 = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y); boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y); - 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); @@ -3169,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; @@ -3315,12 +3845,21 @@ void StartMoving(int x, int y) #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 { - /* - printf("::: %d\n", MovDir[x][y]); - */ + /* 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); } @@ -3364,31 +3903,47 @@ void StartMoving(int x, int y) 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 (left || right) + if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1])) { - if (left && right && + 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 (can_fall_any) + { + 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; - -#if 0 - if (element == EL_BOMB) - printf("::: SLIP DOWN [%d]\n", FrameCounter); -#endif } } 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]; @@ -3403,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; @@ -3436,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); @@ -3537,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 @@ -3721,6 +4282,11 @@ void StartMoving(int x, int y) { Feld[newx][newy] = EL_AMOEBA_SHRINKING; PlaySoundLevel(x, y, SND_MOLE_DIGGING); + + ResetGfxAnimation(x, y); + GfxAction[x][y] = ACTION_DIGGING; + DrawLevelField(x, y); + MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */ return; /* wait for shrinking amoeba */ } @@ -3745,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 || @@ -3787,65 +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; - struct PlayerInfo *player = (IS_PLAYER(x, y) ? PLAYERINFO(x, y) : NULL); -#if 0 - boolean pushing = (player != NULL && player->Pushing && player->MovPos != 0); -#else - boolean pushing = (player != NULL && player->Pushing && player->is_moving); -#endif - -#if 0 - if (player && player->is_moving && player->MovPos == 0) - printf("::: !!!\n"); -#endif - - 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 1 - if (pushing) /* special case: moving object pushed by player */ -#if 1 + if (pushed) /* special case: moving object pushed by player */ MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos)); -#else - MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->GfxPos)); -#endif -#endif - -#if 0 - if (element == EL_SPRING) - printf("::: spring moves %d [%d: %d, %d, %d/%d]\n", - MovPos[x][y], - pushing, - (player?player->Pushing:-42), - (player?player->is_moving:-42), - (player?player->MovPos:-42), - (player?player->GfxPos:-42)); -#endif if (ABS(MovPos[x][y]) >= TILEX) /* object reached its destination */ { @@ -3855,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]; @@ -3922,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; @@ -3939,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 @@ -3960,13 +4475,9 @@ void ContinueMoving(int x, int y) DrawLevelField(x, y); DrawLevelField(newx, newy); -#if 0 - if (game.engine_version >= RELEASE_IDENT(2,2,0,7) || !pushing) -#endif - Stop[newx][newy] = TRUE; /* ignore this element until the next frame */ -#if 1 - if (!pushing) -#endif + 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 */ @@ -3978,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 */ { @@ -4295,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) { @@ -4319,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) { @@ -4463,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]) @@ -4878,12 +5392,19 @@ static void ChangeActiveTrap(int x, int y) DrawLevelFieldCrumbledSand(x, y); } -static void ChangeElementDoIt(int x, int y, int element_new) +static void ChangeElementNowExt(int x, int y, int target_element) { - CheckTriggeredElementChange(Feld[x][y], CE_OTHER_CHANGING); - - RemoveField(x, y); - Feld[x][y] = element_new; + /* 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] = target_element; ResetGfxAnimation(x, y); ResetRandomAnimationValue(x, y); @@ -4895,56 +5416,133 @@ static void ChangeElementDoIt(int x, int y, int element_new) 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 */ + + continue; + } + + if (!IN_LEV_FIELD(ex, ey)) + { + can_change[xx][yy] = FALSE; + complete_change = FALSE; - if (!IN_LEV_FIELD(xx, yy) || - !IN_SCR_FIELD(sxx, syy) || - !CAN_BE_CRUMBLED(Feld[xx][yy]) || - IS_MOVING(xx, yy)) 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]--; @@ -4956,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 */ @@ -4970,54 +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; @@ -5211,6 +5859,16 @@ void GameActions() GfxFrame[x][y]++; +#if 1 + /* reset finished pushing action (not done in ContinueMoving() to allow + continous pushing animation for elements without push delay) */ + if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y)) + { + ResetGfxAnimation(x, y); + DrawLevelField(x, y); + } +#endif + #if DEBUG if (IS_BLOCKED(x, y)) { @@ -5271,7 +5929,7 @@ void GameActions() /* this may take place after moving, so 'element' may have changed */ if (IS_CHANGING(x, y)) { - ChangeElement(x, y); + ChangeElement(x, y, element_info[element].event_page_num[CE_DELAY]); element = Feld[x][y]; graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]); } @@ -5284,9 +5942,10 @@ void GameActions() #if 1 graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]); #if 0 - if (element == EL_PACMAN) - printf("::: %d, %d, %d\n", - IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]); + if (element == EL_MOLE) + printf("::: %d, %d, %d [%d]\n", + IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y], + GfxAction[x][y]); #endif #if 0 if (element == EL_YAMYAM) @@ -5302,7 +5961,7 @@ void GameActions() DrawLevelGraphicAnimationIfNeeded(x, y, graphic); #if 0 - if (element == EL_YAMYAM) + if (element == EL_MOLE) printf("::: %d, %d\n", graphic, GfxFrame[x][y]); #endif } @@ -5409,6 +6068,7 @@ void GameActions() #endif element = Feld[x][y]; + /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */ if (!IS_PLAYER(x,y) && (element == EL_EMPTY || element == EL_SAND || @@ -5652,24 +6312,24 @@ void ScrollLevel(int dx, int dy) int x, y; BlitBitmap(drawto_field, drawto_field, - FX + TILEX*(dx == -1) - softscroll_offset, - FY + TILEY*(dy == -1) - softscroll_offset, - SXSIZE - TILEX*(dx!=0) + 2*softscroll_offset, - SYSIZE - TILEY*(dy!=0) + 2*softscroll_offset, - FX + TILEX*(dx == 1) - softscroll_offset, - FY + TILEY*(dy == 1) - softscroll_offset); + FX + TILEX * (dx == -1) - softscroll_offset, + FY + TILEY * (dy == -1) - softscroll_offset, + SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset, + SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset, + FX + TILEX * (dx == 1) - softscroll_offset, + FY + TILEY * (dy == 1) - softscroll_offset); if (dx) { x = (dx == 1 ? BX1 : BX2); - for (y=BY1; y<=BY2; y++) + for (y=BY1; y <= BY2; y++) DrawScreenField(x, y); } if (dy) { y = (dy == 1 ? BY1 : BY2); - for (x=BX1; x<=BX2; x++) + for (x=BX1; x <= BX2; x++) DrawScreenField(x, y); } @@ -5696,6 +6356,7 @@ static void CheckGravityMovement(struct PlayerInfo *player) (IN_LEV_FIELD(new_jx, new_jy) && (Feld[new_jx][new_jy] == EL_SP_BASE || Feld[new_jx][new_jy] == EL_SAND)); + /* !!! extend EL_SAND to anything diggable !!! */ if (field_under_player_is_free && !player_is_moving_to_valid_field && @@ -5760,6 +6421,10 @@ boolean MoveFigureOneStep(struct PlayerInfo *player, if (can_move != MF_MOVING) return can_move; + /* check if DigField() has caused relocation of the player */ + if (player->jx != jx || player->jy != jy) + return MF_NO_ACTION; + StorePlayer[jx][jy] = 0; player->last_jx = jx; player->last_jy = jy; @@ -5929,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 { @@ -5941,6 +6609,7 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy) } TestIfHeroTouchesBadThing(jx, jy); + TestIfPlayerTouchesCustomElement(jx, jy); if (!player->active) RemoveHero(player); @@ -5965,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)) @@ -5981,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) { @@ -6000,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 || @@ -6041,6 +6715,126 @@ void ScrollScreen(struct PlayerInfo *player, int mode) ScreenMovDir = MV_NO_MOVING; } +void TestIfPlayerTouchesCustomElement(int x, int y) +{ + static boolean check_changing = FALSE; + static int xy[4][2] = + { + { 0, -1 }, + { -1, 0 }, + { +1, 0 }, + { 0, +1 } + }; + int i; + + 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]; + + if (!IN_LEV_FIELD(xx, yy)) + continue; + + if (IS_PLAYER(x, y)) + { + CheckTriggeredElementChange(xx, yy, Feld[xx][yy], CE_OTHER_GETS_TOUCHED); + CheckElementChange(xx, yy, Feld[xx][yy], CE_TOUCHED_BY_PLAYER); + } + else if (IS_PLAYER(xx, yy)) + { + CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_GETS_TOUCHED); + CheckElementChange(x, y, Feld[x][y], CE_TOUCHED_BY_PLAYER); + + break; + } + } + + check_changing = FALSE; +} + +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; @@ -6331,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, @@ -6357,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 : @@ -6392,14 +7192,7 @@ int DigField(struct PlayerInfo *player, } if (IS_MOVING(x, y) || IS_PLAYER(x, y)) - { -#if 0 - if (FrameCounter == 437) - printf("::: ---> IS_MOVING %d\n", MovDir[x][y]); -#endif - return MF_NO_ACTION; - } #if 0 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy])) @@ -6439,255 +7232,40 @@ 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) { -#if 0 - case EL_EMPTY: - PlaySoundLevelElementAction(x, y, player->element_nr, ACTION_MOVING); + case EL_ROBOT_WHEEL: + Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE; + ZX = x; + ZY = y; + DrawLevelField(x, y); + PlaySoundLevel(x, y, SND_ROBOT_WHEEL_ACTIVATING); + return MF_ACTION; break; -#endif -#if 0 - 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 (mode != DF_SNAP && element != EL_EMPTY) + case EL_SP_TERMINAL: { - GfxElement[x][y] = (CAN_BE_CRUMBLED(element) ? EL_SAND : element); - player->is_digging = TRUE; - } + int xx, yy; + + PlaySoundLevel(x, y, SND_SP_TERMINAL_ACTIVATING); + + for (yy=0; yyis_collecting = TRUE; - } - - 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; - -#if 0 - 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; -#endif - - 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; - ZY = y; - DrawLevelField(x, y); - PlaySoundLevel(x, y, SND_ROBOT_WHEEL_ACTIVATING); - return MF_ACTION; - break; - - case EL_SP_TERMINAL: - { - int xx, yy; - - PlaySoundLevel(x, y, SND_SP_TERMINAL_ACTIVATING); - - for (yy=0; yyPushing = 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 - printf("want push... %d [%d]\n", FrameCounter, player->push_delay_value); -#endif - -#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 - { -#if 1 - InitMovingField(x, y, (dx < 0 ? MV_LEFT : - dx > 0 ? MV_RIGHT : - dy < 0 ? MV_UP : MV_DOWN)); - MovPos[x][y] = (dx != 0 ? dx : dy); -#else - RemoveField(x, y); - Feld[x + dx][y + dy] = element; -#endif - } - -#if 0 - printf("pushing %d/%d ... %d [%d]\n", dx, dy, - FrameCounter, player->push_delay_value); -#endif - -#if 0 - if (element == EL_SPRING) - { - Feld[x + dx][y + dy] = EL_SPRING; - MovDir[x + dx][y + dy] = move_direction; - } -#endif - - player->push_delay_value = (element == EL_SPRING ? 0 : 2 + RND(8)); - - DrawLevelField(x + dx, y + dy); - PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING); - - CheckTriggeredElementChange(element, CE_OTHER_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: @@ -6948,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); @@ -6998,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--; @@ -7037,121 +7446,61 @@ int DigField(struct PlayerInfo *player, return MF_ACTION; break; -#if 0 - case EL_SOKOBAN_FIELD_EMPTY: - break; -#endif - - 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; - CheckTriggeredElementChange(element, CE_OTHER_PUSHING); + 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; + } - break; + /* automatically move to the next field with double speed */ + player->programmed_action = move_direction; + DOUBLE_PLAYER_SPEED(player); - case EL_PENGUIN: - case EL_PIG: - case EL_DRAGON: - break; - - default: + PlaySoundLevelAction(x, y, ACTION_PASSING); - if (IS_WALKABLE(element)) - { - PlaySoundLevelElementAction(x, y, player->element_nr, ACTION_MOVING); break; } else if (IS_DIGGABLE(element)) @@ -7160,8 +7509,12 @@ int DigField(struct PlayerInfo *player, 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; } @@ -7179,29 +7532,112 @@ int DigField(struct PlayerInfo *player, player->is_collecting = TRUE; } - RaiseScoreElement(element); + 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)) @@ -7211,37 +7647,68 @@ 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; -#if 1 - InitMovingField(x, y, (dx < 0 ? MV_LEFT : - dx > 0 ? MV_RIGHT : - dy < 0 ? MV_UP : MV_DOWN)); - MovPos[x][y] = (dx != 0 ? dx : dy); -#else - RemoveField(x, y); - Feld[x + dx][y + dy] = element; -#endif + 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(element, CE_OTHER_PUSHING); - CheckPlayerElementChange(x + dx, y + dy, element, CE_PUSHED_BY_PLAYER); + CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_PUSHED); + CheckElementChange(x, y, element, CE_PUSHED_BY_PLAYER); break; } else { - CheckPlayerElementChange(x, y, element, CE_PRESSED_BY_PLAYER); + CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_PRESSED); + CheckElementChange(x, y, element, CE_PRESSED_BY_PLAYER); } return MF_NO_ACTION; @@ -7259,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; @@ -7280,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(); @@ -7306,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); @@ -7531,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: @@ -7550,7 +8041,7 @@ void RaiseScoreElement(int element) RaiseScore(level.score[SC_KEY]); break; default: - RaiseScore(element_info[element].score); + RaiseScore(element_info[element].collect_score); break; } } @@ -7777,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; @@ -7785,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: