mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-30 22:54:35 +03:00
Spider: Make the last move undoable
The lets the user undo the last card move. Card moves which cause cards to be moved to the waste stack cannot be undone.
This commit is contained in:
parent
6d18164ab0
commit
c97421eabe
Notes:
sideshowbarker
2024-07-17 05:46:00 +09:00
Author: https://github.com/gunnarbeutner Commit: https://github.com/SerenityOS/serenity/commit/c97421eabe Pull-request: https://github.com/SerenityOS/serenity/pull/15611 Reviewed-by: https://github.com/linusg ✅
@ -37,6 +37,9 @@ void Game::setup(Mode mode)
|
||||
|
||||
m_mode = mode;
|
||||
|
||||
if (on_undo_availability_change)
|
||||
on_undo_availability_change(false);
|
||||
|
||||
if (on_game_end)
|
||||
on_game_end(GameOverReason::NewGame, m_score);
|
||||
|
||||
@ -74,6 +77,28 @@ void Game::setup(Mode mode)
|
||||
update();
|
||||
}
|
||||
|
||||
void Game::perform_undo()
|
||||
{
|
||||
if (m_last_move.type == LastMove::Type::Invalid)
|
||||
return;
|
||||
|
||||
if (!m_last_move.was_visible)
|
||||
m_last_move.from->peek().set_upside_down(true);
|
||||
|
||||
NonnullRefPtrVector<Card> cards;
|
||||
for (size_t i = 0; i < m_last_move.card_count; i++)
|
||||
cards.append(m_last_move.to->pop());
|
||||
for (ssize_t i = m_last_move.card_count - 1; i >= 0; i--)
|
||||
m_last_move.from->push(cards[i]);
|
||||
|
||||
update_score(-1);
|
||||
|
||||
m_last_move = {};
|
||||
if (on_undo_availability_change)
|
||||
on_undo_availability_change(false);
|
||||
invalidate_layout();
|
||||
}
|
||||
|
||||
void Game::start_timer_if_necessary()
|
||||
{
|
||||
if (on_game_start && m_waiting_for_new_game) {
|
||||
@ -141,6 +166,9 @@ void Game::detect_full_stacks()
|
||||
update(current_pile.peek().rect());
|
||||
|
||||
update_score(101);
|
||||
|
||||
if (on_undo_availability_change)
|
||||
on_undo_availability_change(false);
|
||||
}
|
||||
|
||||
last_value = to_underlying(card.rank());
|
||||
@ -159,6 +187,9 @@ void Game::detect_victory()
|
||||
return;
|
||||
}
|
||||
|
||||
if (on_undo_availability_change)
|
||||
on_undo_availability_change(false);
|
||||
|
||||
if (on_game_end)
|
||||
on_game_end(GameOverReason::Victory, m_score);
|
||||
}
|
||||
@ -198,6 +229,17 @@ void Game::paint_event(GUI::PaintEvent& event)
|
||||
}
|
||||
}
|
||||
|
||||
void Game::remember_move_for_undo(CardStack& from, CardStack& to, size_t card_count, bool was_visible)
|
||||
{
|
||||
m_last_move.type = LastMove::Type::MoveCards;
|
||||
m_last_move.from = &from;
|
||||
m_last_move.card_count = card_count;
|
||||
m_last_move.to = &to;
|
||||
m_last_move.was_visible = was_visible;
|
||||
if (on_undo_availability_change)
|
||||
on_undo_availability_change(true);
|
||||
}
|
||||
|
||||
void Game::mousedown_event(GUI::MouseEvent& event)
|
||||
{
|
||||
GUI::Frame::mousedown_event(event);
|
||||
@ -239,7 +281,10 @@ void Game::mousedown_event(GUI::MouseEvent& event)
|
||||
|
||||
void Game::move_focused_cards(CardStack& stack)
|
||||
{
|
||||
auto card_count = moving_cards().size();
|
||||
drop_cards_on_stack(stack, Cards::CardStack::MovementRule::Any);
|
||||
bool was_visible = moving_cards_source_stack()->is_empty() || !moving_cards_source_stack()->peek().is_upside_down();
|
||||
remember_move_for_undo(*moving_cards_source_stack(), stack, card_count, was_visible);
|
||||
update_score(-1);
|
||||
moving_cards_source_stack()->make_top_card_visible();
|
||||
detect_full_stacks();
|
||||
|
@ -40,13 +40,29 @@ public:
|
||||
Mode mode() const { return m_mode; }
|
||||
void setup(Mode);
|
||||
|
||||
void perform_undo();
|
||||
|
||||
Function<void(uint32_t)> on_score_update;
|
||||
Function<void()> on_game_start;
|
||||
Function<void(GameOverReason, uint32_t)> on_game_end;
|
||||
Function<void(bool)> on_undo_availability_change;
|
||||
|
||||
private:
|
||||
Game();
|
||||
|
||||
struct LastMove {
|
||||
enum class Type {
|
||||
Invalid,
|
||||
MoveCards
|
||||
};
|
||||
|
||||
Type type { Type::Invalid };
|
||||
CardStack* from { nullptr };
|
||||
size_t card_count;
|
||||
bool was_visible;
|
||||
CardStack* to { nullptr };
|
||||
};
|
||||
|
||||
enum StackLocation {
|
||||
Completed,
|
||||
Stock,
|
||||
@ -68,6 +84,7 @@ private:
|
||||
};
|
||||
|
||||
void start_timer_if_necessary();
|
||||
void remember_move_for_undo(CardStack&, CardStack&, size_t, bool);
|
||||
void update_score(int delta);
|
||||
void draw_cards();
|
||||
void detect_full_stacks();
|
||||
@ -82,6 +99,7 @@ private:
|
||||
|
||||
Mode m_mode { Mode::SingleSuit };
|
||||
|
||||
LastMove m_last_move;
|
||||
NonnullRefPtrVector<Card> m_new_deck;
|
||||
Gfx::IntPoint m_mouse_down_location;
|
||||
|
||||
|
@ -239,6 +239,12 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||
game.setup(mode);
|
||||
})));
|
||||
TRY(game_menu->try_add_separator());
|
||||
auto undo_action = GUI::CommonActions::make_undo_action([&](auto&) {
|
||||
game.perform_undo();
|
||||
});
|
||||
undo_action->set_enabled(false);
|
||||
TRY(game_menu->try_add_action(undo_action));
|
||||
TRY(game_menu->try_add_separator());
|
||||
TRY(game_menu->try_add_action(single_suit_action));
|
||||
TRY(game_menu->try_add_action(two_suit_action));
|
||||
TRY(game_menu->try_add_separator());
|
||||
@ -274,6 +280,10 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||
window->set_icon(app_icon.bitmap_for_size(16));
|
||||
window->show();
|
||||
|
||||
game.on_undo_availability_change = [&](bool undo_available) {
|
||||
undo_action->set_enabled(undo_available);
|
||||
};
|
||||
|
||||
game.setup(mode);
|
||||
|
||||
return app->exec();
|
||||
|
Loading…
Reference in New Issue
Block a user