X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Fgame.c;h=09c7fa096b0d8edfae177c88fac18735f44a343f;hb=451802c5bfcfe06a61b229b1332ae0ae68fd5de8;hp=6e0914df5e867453e176780bf7daf1d940a1ee48;hpb=3c036eb0bd76347ddaaf112018512c9f2a357e1f;p=rocksndiamonds.git diff --git a/src/game.c b/src/game.c index 6e0914df..09c7fa09 100644 --- a/src/game.c +++ b/src/game.c @@ -962,7 +962,7 @@ static struct GamePanelControlInfo game_panel_controls[] = ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND) #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \ - ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y])) + ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_FOOD_DARK_YAMYAM(Tile[x][y])) #define PACMAN_CAN_ENTER_FIELD(e, x, y) \ ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y])) @@ -1069,9 +1069,9 @@ static void HandleElementChange(int, int, int); static void ExecuteCustomElementAction(int, int, int, int); static boolean ChangeElement(int, int, int, int); -static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int); +static boolean CheckTriggeredElementChangeExt(int, int, int, int, int, int, int); #define CheckTriggeredElementChange(x, y, e, ev) \ - CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1) + CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1) #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \ CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1) #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \ @@ -3195,6 +3195,17 @@ static void InitGameEngine(void) SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE); } + // ---------- initialize if element can trigger global animations ----------- + + for (i = 0; i < MAX_NUM_ELEMENTS; i++) + { + struct ElementInfo *ei = &element_info[i]; + + ei->has_anim_event = FALSE; + } + + InitGlobalAnimEventsForCustomElements(); + // ---------- initialize internal run-time variables ------------------------ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) @@ -3260,12 +3271,14 @@ static void InitGameEngine(void) for (j = 0; j < ei->num_change_pages; j++) { - ei->change_page[j].actual_trigger_element = EL_EMPTY; - ei->change_page[j].actual_trigger_player = EL_EMPTY; - ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE; - ei->change_page[j].actual_trigger_side = CH_SIDE_NONE; - ei->change_page[j].actual_trigger_ce_value = 0; - ei->change_page[j].actual_trigger_ce_score = 0; + struct ElementChangeInfo *change = &ei->change_page[j]; + + change->actual_trigger_element = EL_EMPTY; + change->actual_trigger_player = EL_EMPTY; + change->actual_trigger_player_bits = CH_PLAYER_NONE; + change->actual_trigger_side = CH_SIDE_NONE; + change->actual_trigger_ce_value = 0; + change->actual_trigger_ce_score = 0; } } @@ -3283,16 +3296,18 @@ static void InitGameEngine(void) for (j = 0; j < ei->num_change_pages; j++) { - if (!ei->change_page[j].can_change_or_has_action) + struct ElementChangeInfo *change = &ei->change_page[j]; + + if (!change->can_change_or_has_action) continue; - if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION]) + if (change->has_event[CE_BY_OTHER_ACTION]) { - int trigger_element = ei->change_page[j].trigger_element; + int trigger_element = change->trigger_element; for (k = 0; k < NUM_CHANGE_EVENTS; k++) { - if (ei->change_page[j].has_event[k]) + if (change->has_event[k]) { if (IS_GROUP_ELEMENT(trigger_element)) { @@ -3501,10 +3516,10 @@ static void InitGameEngine(void) { int element = EL_CUSTOM_START + i; - if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) || - HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) || - HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) || - HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X)) + if (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) || + HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) || + HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) || + HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X)) game.use_mouse_actions = TRUE; } } @@ -3614,6 +3629,10 @@ void InitGame(void) // force restarting global animations displayed during game play RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING); + // this is required for "transforming" fade modes like cross-fading + // (else global animations will be stopped, but not restarted here) + SetAnimStatusBeforeFading(GAME_MODE_PSEUDO_RESTARTING); + SetGameStatus(GAME_MODE_PLAYING); } @@ -5405,17 +5424,9 @@ void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y) void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y) { - int oldx = x, oldy = y; int direction = MovDir[x][y]; - - if (direction == MV_LEFT) - oldx++; - else if (direction == MV_RIGHT) - oldx--; - else if (direction == MV_UP) - oldy++; - else if (direction == MV_DOWN) - oldy--; + int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0); + int oldy = y + (direction & MV_UP ? +1 : direction & MV_DOWN ? -1 : 0); *comes_from_x = oldx; *comes_from_y = oldy; @@ -5430,16 +5441,17 @@ static int MovingOrBlocked2Element(int x, int y) int oldx, oldy; Blocked2Moving(x, y, &oldx, &oldy); + return Tile[oldx][oldy]; } - else - return element; + + return element; } static int MovingOrBlocked2ElementIfNotLeaving(int x, int y) { // like MovingOrBlocked2Element(), but if element is moving - // and (x,y) is the field the moving element is just leaving, + // and (x, y) is the field the moving element is just leaving, // return EL_BLOCKED instead of the element value int element = Tile[x][y]; @@ -5662,14 +5674,13 @@ static void DrawRelocateScreen(int old_x, int old_y, int x, int y, { // relocation _without_ centering of screen - int center_scroll_x = SCROLL_POSITION_X(old_x); - int center_scroll_y = SCROLL_POSITION_Y(old_y); - int offset_x = x + (scroll_x - center_scroll_x); - int offset_y = y + (scroll_y - center_scroll_y); + // apply distance between old and new player position to scroll position + int shifted_scroll_x = scroll_x + (x - old_x); + int shifted_scroll_y = scroll_y + (y - old_y); - // for new screen position, apply previous offset to center position - new_scroll_x = SCROLL_POSITION_X(offset_x); - new_scroll_y = SCROLL_POSITION_Y(offset_y); + // make sure that shifted scroll position does not scroll beyond screen + new_scroll_x = SCROLL_POSITION_X(shifted_scroll_x + MIDPOSX); + new_scroll_y = SCROLL_POSITION_Y(shifted_scroll_y + MIDPOSY); } if (quick_relocation) @@ -5836,9 +5847,6 @@ static void Explode(int ex, int ey, int phase, int mode) int last_phase; int border_element; - // !!! eliminate this variable !!! - int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2); - if (game.explosions_delayed) { ExplodeField[ex][ey] = mode; @@ -5848,6 +5856,8 @@ static void Explode(int ex, int ey, int phase, int mode) if (phase == EX_PHASE_START) // initialize 'Store[][]' field { int center_element = Tile[ex][ey]; + int ce_value = CustomValue[ex][ey]; + int ce_score = element_info[center_element].collect_score; int artwork_element, explosion_element; // set these values later // remove things displayed in background while burning dynamite @@ -5986,6 +5996,14 @@ static void Explode(int ex, int ey, int phase, int mode) else Store[x][y] = EL_EMPTY; + if (IS_CUSTOM_ELEMENT(center_element)) + Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value : + Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score : + Store[x][y] >= EL_PREV_CE_8 && + Store[x][y] <= EL_NEXT_CE_8 ? + RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) : + Store[x][y]); + if (x != ex || y != ey || mode == EX_TYPE_BORDER || center_element == EL_AMOEBA_TO_DIAMOND) Store2[x][y] = element; @@ -6119,7 +6137,7 @@ static void Explode(int ex, int ey, int phase, int mode) int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING); int frame = getGraphicAnimationFrameXY(graphic, x, y); - if (phase == delay) + if (phase == 1) TEST_DrawLevelFieldCrumbled(x, y); if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY) @@ -7545,7 +7563,7 @@ static void TurnRoundExt(int x, int y) boolean can_turn_left = CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y); boolean can_turn_right = - CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y); + CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y); if (element_info[element].move_stepsize == 0) // "not moving" return; @@ -8375,7 +8393,7 @@ static void StartMoving(int x, int y) PlayLevelSound(newx, newy, SND_PENGUIN_PASSING); if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy))) - DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0); + DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0); game.friends_still_needed--; if (!game.friends_still_needed && @@ -8387,7 +8405,7 @@ static void StartMoving(int x, int y) } else if (IS_FOOD_PENGUIN(Tile[newx][newy])) { - if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING) + if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING) TEST_DrawLevelField(newx, newy); else GfxDir[x][y] = MovDir[x][y] = MV_NONE; @@ -8692,7 +8710,7 @@ void ContinueMoving(int x, int y) if (pushed_by_player) // special case: moving object pushed by player { - MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos)); + MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos)); } else if (use_step_delay) // special case: moving object has step delay { @@ -8954,7 +8972,7 @@ void ContinueMoving(int x, int y) CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER, player->index_bit, push_side); - CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X, + CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X, player->index_bit, push_side); } @@ -10622,17 +10640,26 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change) if (GFX_CRUMBLED(new_element)) TEST_DrawLevelFieldCrumbledNeighbours(x, y); - } - // check if element under the player changes from accessible to unaccessible - // (needed for special case of dropping element which then changes) - // (must be checked after creating new element for walkable group elements) - if (IS_PLAYER(x, y) && !player_explosion_protected && - IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element)) - { - Bang(x, y); + if (old_element == EL_EXPLOSION) + { + Store[x][y] = Store2[x][y] = 0; - return; + // check if new element replaces an exploding player, requiring cleanup + if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present) + StorePlayer[x][y] = 0; + } + + // check if element under the player changes from accessible to unaccessible + // (needed for special case of dropping element which then changes) + // (must be checked after creating new element for walkable group elements) + if (IS_PLAYER(x, y) && !player_explosion_protected && + IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element)) + { + KillPlayer(PLAYERINFO(x, y)); + + return; + } } // "ChangeCount" not set yet to allow "entered by player" change one time @@ -10700,6 +10727,9 @@ static boolean ChangeElement(int x, int y, int element, int page) ChangeCount[x][y]++; // count number of changes in the same frame + if (ei->has_anim_event) + HandleGlobalAnimEventByElementChange(element, page, x, y); + if (change->explode) { Bang(x, y); @@ -10934,13 +10964,14 @@ static void HandleElementChange(int x, int y, int page) if (ChangeDelay[x][y] != 0) // continue element change { - if (change->can_change) - { - int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); + int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); - if (IS_ANIMATED(graphic)) - DrawLevelGraphicAnimationIfNeeded(x, y, graphic); + // also needed if CE can not change, but has CE delay with CE action + if (IS_ANIMATED(graphic)) + DrawLevelGraphicAnimationIfNeeded(x, y, graphic); + if (change->can_change) + { if (change->change_function) change->change_function(x, y); } @@ -11708,6 +11739,49 @@ void AdvanceGfxFrame(void) } } +static void HandleMouseAction(struct MouseActionInfo *mouse_action, + struct MouseActionInfo *mouse_action_last) +{ + if (mouse_action->button) + { + int new_button = (mouse_action->button && mouse_action_last->button == 0); + int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button); + int x = mouse_action->lx; + int y = mouse_action->ly; + int element = Tile[x][y]; + + if (new_button) + { + CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button); + CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X, + ch_button); + } + + CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button); + CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X, + ch_button); + + if (level.use_step_counter) + { + boolean counted_click = FALSE; + + // element clicked that can change when clicked/pressed + if (CAN_CHANGE_OR_HAS_ACTION(element) && + (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) || + HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE))) + counted_click = TRUE; + + // element clicked that can trigger change when clicked/pressed + if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] || + trigger_events[element][CE_MOUSE_PRESSED_ON_X]) + counted_click = TRUE; + + if (new_button && counted_click) + CheckLevelTime_StepCounter(); + } + } +} + void StartGameActions(boolean init_network_game, boolean record_tape, int random_seed) { @@ -12274,45 +12348,7 @@ void GameActions_RND(void) #endif } - if (mouse_action.button) - { - int new_button = (mouse_action.button && mouse_action_last.button == 0); - int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button); - - x = mouse_action.lx; - y = mouse_action.ly; - element = Tile[x][y]; - - if (new_button) - { - CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button); - CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X, - ch_button); - } - - CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button); - CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X, - ch_button); - - if (level.use_step_counter) - { - boolean counted_click = FALSE; - - // element clicked that can change when clicked/pressed - if (CAN_CHANGE_OR_HAS_ACTION(element) && - (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) || - HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE))) - counted_click = TRUE; - - // element clicked that can trigger change when clicked/pressed - if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] || - trigger_events[element][CE_MOUSE_PRESSED_ON_X]) - counted_click = TRUE; - - if (new_button && counted_click) - CheckLevelTime_StepCounter(); - } - } + HandleMouseAction(&mouse_action, &mouse_action_last); SCAN_PLAYFIELD(x, y) { @@ -12436,7 +12472,7 @@ void GameActions_RND(void) element == EL_DIAGONAL_SHRINKING || element == EL_DIAGONAL_GROWING) { - graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]); + graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]); DrawLevelGraphicAnimationIfNeeded(x, y, graphic); } @@ -12481,7 +12517,7 @@ void GameActions_RND(void) y = RND(lev_fieldy); element = Tile[x][y]; - if (!IS_PLAYER(x,y) && + if (!IS_PLAYER(x, y) && (element == EL_EMPTY || CAN_GROW_INTO(element) || element == EL_QUICKSAND_EMPTY || @@ -12857,7 +12893,7 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, !AllPlayersInSight(player, new_jx, new_jy)) return MP_NO_ACTION; - can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG); + can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG); if (can_move != MP_MOVING) return can_move; @@ -13227,13 +13263,18 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) CE_PLAYER_LEAVES_X, player->index_bit, leave_side); - if (IS_CUSTOM_ELEMENT(new_element)) - CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER, - player->index_bit, enter_side); + // needed because pushed element has not yet reached its destination, + // so it would trigger a change event at its previous field location + if (!player->is_pushing) + { + if (IS_CUSTOM_ELEMENT(new_element)) + CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER, + player->index_bit, enter_side); - CheckTriggeredElementChangeByPlayer(jx, jy, new_element, - CE_PLAYER_ENTERS_X, - player->index_bit, enter_side); + CheckTriggeredElementChangeByPlayer(jx, jy, new_element, + CE_PLAYER_ENTERS_X, + player->index_bit, enter_side); + } CheckTriggeredElementChangeBySide(jx, jy, player->initial_element, CE_MOVE_OF_X, move_direction); @@ -13244,8 +13285,8 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) TestIfPlayerTouchesBadThing(jx, jy); TestIfPlayerTouchesCustomElement(jx, jy); - /* needed because pushed element has not yet reached its destination, - so it would trigger a change event at its previous field location */ + // needed because pushed element has not yet reached its destination, + // so it would trigger a change event at its previous field location if (!player->is_pushing) TestIfElementTouchesCustomElement(jx, jy); // for empty space @@ -13432,8 +13473,9 @@ void TestIfPlayerTouchesCustomElement(int x, int y) incorrectly give EL_PLAYER_1 for "player->element_nr") */ int player_element = PLAYERINFO(x, y)->initial_element; + // as element "X" is the player here, check opposite (center) side CheckElementChangeBySide(xx, yy, border_element, player_element, - CE_TOUCHING_X, border_side); + CE_TOUCHING_X, center_side); } } else if (IS_PLAYER(xx, yy)) // player found at border element @@ -13459,8 +13501,9 @@ void TestIfPlayerTouchesCustomElement(int x, int y) incorrectly give EL_PLAYER_1 for "player->element_nr") */ int player_element = PLAYERINFO(xx, yy)->initial_element; + // as element "X" is the player here, check opposite (border) side CheckElementChangeBySide(x, y, center_element, player_element, - CE_TOUCHING_X, center_side); + CE_TOUCHING_X, border_side); } break; @@ -13567,7 +13610,7 @@ void TestIfElementTouchesCustomElement(int x, int y) CheckElementChangeBySide(xx, yy, border_element, center_element, CE_TOUCHING_X, center_side); - // (center element cannot be player, so we dont have to check this here) + // (center element cannot be player, so we don't have to check this here) } for (i = 0; i < NUM_DIRECTIONS; i++) @@ -13594,6 +13637,7 @@ void TestIfElementTouchesCustomElement(int x, int y) incorrectly give EL_PLAYER_1 for "player->element_nr") */ int player_element = PLAYERINFO(xx, yy)->initial_element; + // as element "X" is the player here, check opposite (border) side CheckElementChangeBySide(x, y, center_element, player_element, CE_TOUCHING_X, border_side); } @@ -13951,7 +13995,7 @@ void KillPlayer(struct PlayerInfo *player) player->killed = TRUE; // remove accessible field at the player's position - Tile[jx][jy] = EL_EMPTY; + RemoveField(jx, jy); // deactivate shield (else Bang()/Explode() would not work right) player->shield_normal_time_left = 0; @@ -14175,7 +14219,7 @@ static int DigField(struct PlayerInfo *player, if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction)) return MP_NO_ACTION; // field has no opening in this direction - if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction)) + if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction)) return MP_NO_ACTION; // field has no opening in this direction if (player_can_move && element == EL_ACID && move_direction == MV_DOWN) @@ -15241,10 +15285,12 @@ static void StopLevelSoundActionIfLoop(int x, int y, int action) static int getLevelMusicNr(void) { + int level_pos = level_nr - leveldir_current->first_level; + if (levelset.music[level_nr] != MUS_UNDEFINED) return levelset.music[level_nr]; // from config file else - return MAP_NOCONF_MUSIC(level_nr); // from music dir + return MAP_NOCONF_MUSIC(level_pos); // from music dir } static void FadeLevelSounds(void) @@ -15649,8 +15695,9 @@ static char *getRestartGameMessage(void) return message; } -static void RequestRestartGame(char *message) +static void RequestRestartGame(void) { + char *message = getRestartGameMessage(); boolean has_started_game = hasStartedNetworkGame(); int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM); int door_state = DOOR_CLOSE_1; @@ -15710,7 +15757,7 @@ boolean CheckRestartGame(void) if (!setup.ask_on_game_over) return FALSE; - RequestRestartGame(getRestartGameMessage()); + RequestRestartGame(); return TRUE; }