1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 05:07:35 +00:00

Solitaire: Add undo functionality

This commit is contained in:
Matthew B. Jones 2021-06-02 18:16:49 -06:00 committed by GitHub
parent 9444272ba0
commit ab4f4ddc3c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 125 additions and 10 deletions

View file

@ -113,6 +113,8 @@ void Game::setup(Mode mode)
m_passes_left_before_punishment = recycle_rules().passes_allowed_before_punishment;
m_score = 0;
update_score(0);
if (on_undo_availability_change)
on_undo_availability_change(false);
for (int i = 0; i < Card::card_count; ++i) {
m_new_deck.append(Card::construct(Card::Type::Clubs, i));
@ -129,6 +131,19 @@ void Game::setup(Mode mode)
update();
}
void Game::score_move(CardStack& from, CardStack& to, bool inverse = false)
{
if (from.type() == CardStack::Type::Play && to.type() == CardStack::Type::Normal) {
update_score(5 * (inverse ? -1 : 1));
} else if (from.type() == CardStack::Type::Play && to.type() == CardStack::Type::Foundation) {
update_score(10 * (inverse ? -1 : 1));
} else if (from.type() == CardStack::Type::Normal && to.type() == CardStack::Type::Foundation) {
update_score(10 * (inverse ? -1 : 1));
} else if (from.type() == CardStack::Type::Foundation && to.type() == CardStack::Type::Normal) {
update_score(-15 * (inverse ? -1 : 1));
}
}
void Game::update_score(int to_add)
{
m_score = max(static_cast<int>(m_score) + to_add, 0);
@ -213,11 +228,15 @@ void Game::mousedown_event(GUI::MouseEvent& event)
update(stock.bounding_box());
NonnullRefPtrVector<Card> cards_drawn;
for (size_t i = 0; (i < cards_to_draw) && !stock.is_empty(); ++i) {
auto card = stock.pop();
cards_drawn.prepend(card);
play.push(move(card));
}
remember_move_for_undo(stock, play, cards_drawn);
if (play.bounding_box().size().width() > play_bounding_box.size().width())
update(play.bounding_box());
else
@ -231,6 +250,7 @@ void Game::mousedown_event(GUI::MouseEvent& event)
top_card.set_upside_down(false);
update_score(5);
update(top_card.rect());
remember_flip_for_undo(top_card);
}
} else if (m_focused_cards.is_empty()) {
to_check.add_all_grabbed_cards(click_location, m_focused_cards);
@ -266,11 +286,13 @@ void Game::mouseup_event(GUI::MouseEvent& event)
m_focused_stack->pop();
}
remember_move_for_undo(*m_focused_stack, stack, m_focused_cards);
if (m_focused_stack->type() == CardStack::Type::Play) {
auto& waste = this->stack(Waste);
if (m_focused_stack->is_empty() && !waste.is_empty()) {
auto card = waste.pop();
m_focused_cards.append(card);
m_focused_cards.prepend(card);
m_focused_stack->push(move(card));
}
}
@ -278,15 +300,7 @@ void Game::mouseup_event(GUI::MouseEvent& event)
update(m_focused_stack->bounding_box());
update(stack.bounding_box());
if (m_focused_stack->type() == CardStack::Type::Play && stack.type() == CardStack::Type::Normal) {
update_score(5);
} else if (m_focused_stack->type() == CardStack::Type::Play && stack.type() == CardStack::Type::Foundation) {
update_score(10);
} else if (m_focused_stack->type() == CardStack::Type::Normal && stack.type() == CardStack::Type::Foundation) {
update_score(10);
} else if (m_focused_stack->type() == CardStack::Type::Foundation && stack.type() == CardStack::Type::Normal) {
update_score(-15);
}
score_move(*m_focused_stack, stack);
rebound = false;
break;
@ -397,6 +411,8 @@ void Game::move_card(CardStack& from, CardStack& to)
mark_intersecting_stacks_dirty(card);
to.push(card);
remember_move_for_undo(from, to, m_focused_cards);
update(to.bounding_box());
}
@ -482,6 +498,75 @@ void Game::paint_event(GUI::PaintEvent& event)
}
}
void Game::remember_move_for_undo(CardStack& from, CardStack& to, NonnullRefPtrVector<Card> moved_cards)
{
m_last_move.type = LastMove::Type::MoveCards;
m_last_move.from = &from;
m_last_move.cards = moved_cards;
m_last_move.to = &to;
if (on_undo_availability_change)
on_undo_availability_change(true);
}
void Game::remember_flip_for_undo(Card& card)
{
NonnullRefPtrVector<Card> cards;
cards.append(card);
m_last_move.type = LastMove::Type::FlipCard;
m_last_move.cards = cards;
if (on_undo_availability_change)
on_undo_availability_change(true);
}
void Game::perform_undo()
{
if (m_last_move.type == LastMove::Type::Invalid)
return;
if (m_last_move.type == LastMove::Type::FlipCard) {
m_last_move.cards.at(0).set_upside_down(true);
if (on_undo_availability_change)
on_undo_availability_change(false);
invalidate_layout();
return;
}
if (m_last_move.from->type() == CardStack::Type::Play && m_mode == Mode::SingleCardDraw) {
auto& waste = stack(Waste);
if (!m_last_move.from->is_empty())
waste.push(m_last_move.from->pop());
}
for (auto& to_intersect : m_last_move.cards) {
mark_intersecting_stacks_dirty(to_intersect);
m_last_move.from->push(to_intersect);
m_last_move.to->pop();
}
if (m_last_move.from->type() == CardStack::Type::Stock) {
auto& waste = this->stack(Waste);
auto& play = this->stack(Play);
NonnullRefPtrVector<Card> cards_popped;
for (size_t i = 0; i < m_last_move.cards.size(); i++) {
if (!waste.is_empty()) {
auto card = waste.pop();
cards_popped.prepend(card);
}
}
for (auto& card : cards_popped) {
m_focused_cards.append(card);
play.push(move(card));
}
}
score_move(*m_last_move.from, *m_last_move.to, true);
m_last_move = {};
if (on_undo_availability_change)
on_undo_availability_change(false);
invalidate_layout();
}
void Game::dump_layout() const
{
if constexpr (SOLITAIRE_DEBUG) {