Bläddra i källkod

Solitaire: Add keys for drawing and moving cards to foundation stacks

Also shifts logic of starting game length timer into function
`start_timer_if_necessary`, so it can be called from original
mouse event handler and new `auto_move_eligible_cards_to_stacks`
Matthew B. Jones 4 år sedan
förälder
incheckning
ecaae2d10f
2 ändrade filer med 139 tillägg och 72 borttagningar
  1. 135 72
      Userland/Games/Solitaire/Game.cpp
  2. 4 0
      Userland/Games/Solitaire/Game.h

+ 135 - 72
Userland/Games/Solitaire/Game.cpp

@@ -131,6 +131,14 @@ void Game::setup(Mode mode)
     update();
     update();
 }
 }
 
 
+void Game::start_timer_if_necessary()
+{
+    if (on_game_start && m_waiting_for_new_game) {
+        on_game_start();
+        m_waiting_for_new_game = false;
+    }
+}
+
 void Game::score_move(CardStack& from, CardStack& to, bool inverse = false)
 void Game::score_move(CardStack& from, CardStack& to, bool inverse = false)
 {
 {
     if (from.type() == CardStack::Type::Play && to.type() == CardStack::Type::Normal) {
     if (from.type() == CardStack::Type::Play && to.type() == CardStack::Type::Normal) {
@@ -157,10 +165,16 @@ void Game::keydown_event(GUI::KeyEvent& event)
     if (m_new_game_animation || m_game_over_animation)
     if (m_new_game_animation || m_game_over_animation)
         return;
         return;
 
 
-    if (event.shift() && (event.key() == KeyCode::Key_F12))
+    if (event.shift() && event.key() == KeyCode::Key_F12) {
         start_game_over_animation();
         start_game_over_animation();
-    else if (event.shift() && (event.key() == KeyCode::Key_F11))
+    } else if (event.key() == KeyCode::Key_Tab) {
+        auto_move_eligible_cards_to_stacks();
+    } else if (event.key() == KeyCode::Key_Space) {
+        draw_cards();
+        invalidate_layout(); // FIXME: Stock stack won't render properly after draw_cards() without this
+    } else if (event.shift() && event.key() == KeyCode::Key_F11) {
         dump_layout();
         dump_layout();
+    }
 }
 }
 
 
 void Game::mousedown_event(GUI::MouseEvent& event)
 void Game::mousedown_event(GUI::MouseEvent& event)
@@ -170,10 +184,7 @@ void Game::mousedown_event(GUI::MouseEvent& event)
     if (m_new_game_animation || m_game_over_animation)
     if (m_new_game_animation || m_game_over_animation)
         return;
         return;
 
 
-    if (on_game_start && m_waiting_for_new_game) {
-        on_game_start();
-        m_waiting_for_new_game = false;
-    }
+    start_timer_if_necessary();
 
 
     auto click_location = event.position();
     auto click_location = event.position();
     for (auto& to_check : m_stacks) {
     for (auto& to_check : m_stacks) {
@@ -182,66 +193,7 @@ void Game::mousedown_event(GUI::MouseEvent& event)
 
 
         if (to_check.bounding_box().contains(click_location)) {
         if (to_check.bounding_box().contains(click_location)) {
             if (to_check.type() == CardStack::Type::Stock) {
             if (to_check.type() == CardStack::Type::Stock) {
-                auto& waste = stack(Waste);
-                auto& stock = stack(Stock);
-                auto& play = stack(Play);
-
-                if (stock.is_empty()) {
-                    if (waste.is_empty() && play.is_empty())
-                        return;
-
-                    update(waste.bounding_box());
-                    update(play.bounding_box());
-
-                    while (!play.is_empty()) {
-                        auto card = play.pop();
-                        stock.push(card);
-                    }
-
-                    while (!waste.is_empty()) {
-                        auto card = waste.pop();
-                        stock.push(card);
-                    }
-
-                    if (m_passes_left_before_punishment == 0)
-                        update_score(recycle_rules().punishment);
-                    else
-                        --m_passes_left_before_punishment;
-
-                    update(stock.bounding_box());
-                } else {
-                    auto play_bounding_box = play.bounding_box();
-                    play.move_to_stack(waste);
-
-                    size_t cards_to_draw = 0;
-                    switch (m_mode) {
-                    case Mode::SingleCardDraw:
-                        cards_to_draw = 1;
-                        break;
-                    case Mode::ThreeCardDraw:
-                        cards_to_draw = 3;
-                        break;
-                    default:
-                        VERIFY_NOT_REACHED();
-                        break;
-                    }
-
-                    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
-                        update(play_bounding_box);
-                }
+                draw_cards();
             } else if (!to_check.is_empty()) {
             } else if (!to_check.is_empty()) {
                 auto& top_card = to_check.peek();
                 auto& top_card = to_check.peek();
 
 
@@ -289,12 +241,7 @@ void Game::mouseup_event(GUI::MouseEvent& event)
                     remember_move_for_undo(*m_focused_stack, stack, m_focused_cards);
                     remember_move_for_undo(*m_focused_stack, stack, m_focused_cards);
 
 
                     if (m_focused_stack->type() == CardStack::Type::Play) {
                     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.prepend(card);
-                            m_focused_stack->push(move(card));
-                        }
+                        pop_waste_to_play_stack();
                     }
                     }
 
 
                     update(m_focused_stack->bounding_box());
                     update(m_focused_stack->bounding_box());
@@ -416,6 +363,122 @@ void Game::move_card(CardStack& from, CardStack& to)
     update(to.bounding_box());
     update(to.bounding_box());
 }
 }
 
 
+void Game::draw_cards()
+{
+    auto& waste = stack(Waste);
+    auto& stock = stack(Stock);
+    auto& play = stack(Play);
+
+    if (stock.is_empty()) {
+        if (waste.is_empty() && play.is_empty())
+            return;
+
+        update(waste.bounding_box());
+        update(play.bounding_box());
+
+        while (!play.is_empty()) {
+            auto card = play.pop();
+            stock.push(card);
+        }
+
+        while (!waste.is_empty()) {
+            auto card = waste.pop();
+            stock.push(card);
+        }
+
+        if (m_passes_left_before_punishment == 0)
+            update_score(recycle_rules().punishment);
+        else
+            --m_passes_left_before_punishment;
+
+        update(stock.bounding_box());
+    } else {
+        auto play_bounding_box = play.bounding_box();
+        play.move_to_stack(waste);
+
+        size_t cards_to_draw = 0;
+        switch (m_mode) {
+        case Mode::SingleCardDraw:
+            cards_to_draw = 1;
+            break;
+        case Mode::ThreeCardDraw:
+            cards_to_draw = 3;
+            break;
+        default:
+            VERIFY_NOT_REACHED();
+            break;
+        }
+
+        update(stock.bounding_box());
+
+        for (size_t i = 0; (i < cards_to_draw) && !stock.is_empty(); ++i) {
+            auto card = stock.pop();
+            play.push(move(card));
+        }
+
+        if (play.bounding_box().size().width() > play_bounding_box.size().width())
+            update(play.bounding_box());
+        else
+            update(play_bounding_box);
+    }
+}
+
+void Game::pop_waste_to_play_stack()
+{
+    auto& waste = this->stack(Waste);
+    auto& play = this->stack(Play);
+    if (play.is_empty() && !waste.is_empty()) {
+        auto card = waste.pop();
+        m_focused_cards.append(card);
+        play.push(move(card));
+    }
+}
+
+void Game::auto_move_eligible_cards_to_stacks()
+{
+    bool card_was_moved = false;
+
+    for (auto& to_check : m_stacks) {
+        if (to_check.type() != CardStack::Type::Normal && to_check.type() != CardStack::Type::Play)
+            continue;
+
+        if (to_check.is_empty())
+            continue;
+
+        auto& top_card = to_check.peek();
+        if (top_card.is_upside_down())
+            continue;
+
+        if (stack(Foundation1).is_allowed_to_push(top_card)) {
+            move_card(to_check, stack(Foundation1));
+            card_was_moved = true;
+            if (to_check.type() == CardStack::Type::Play)
+                pop_waste_to_play_stack();
+        } else if (stack(Foundation2).is_allowed_to_push(top_card)) {
+            move_card(to_check, stack(Foundation2));
+            card_was_moved = true;
+            if (to_check.type() == CardStack::Type::Play)
+                pop_waste_to_play_stack();
+        } else if (stack(Foundation3).is_allowed_to_push(top_card)) {
+            move_card(to_check, stack(Foundation3));
+            card_was_moved = true;
+            if (to_check.type() == CardStack::Type::Play)
+                pop_waste_to_play_stack();
+        } else if (stack(Foundation4).is_allowed_to_push(top_card)) {
+            move_card(to_check, stack(Foundation4));
+            card_was_moved = true;
+            if (to_check.type() == CardStack::Type::Play)
+                pop_waste_to_play_stack();
+        }
+    }
+
+    // If at least one card was moved, check again to see if now any additional cards can now be moved
+    if (card_was_moved) {
+        start_timer_if_necessary();
+        auto_move_eligible_cards_to_stacks();
+    }
+}
+
 void Game::mark_intersecting_stacks_dirty(Card& intersecting_card)
 void Game::mark_intersecting_stacks_dirty(Card& intersecting_card)
 {
 {
     for (auto& stack : m_stacks) {
     for (auto& stack : m_stacks) {

+ 4 - 0
Userland/Games/Solitaire/Game.h

@@ -160,6 +160,10 @@ private:
     void remember_flip_for_undo(Card& card);
     void remember_flip_for_undo(Card& card);
     void update_score(int to_add);
     void update_score(int to_add);
     void move_card(CardStack& from, CardStack& to);
     void move_card(CardStack& from, CardStack& to);
+    void draw_cards();
+    void pop_waste_to_play_stack();
+    void auto_move_eligible_cards_to_stacks();
+    void start_timer_if_necessary();
     void start_game_over_animation();
     void start_game_over_animation();
     void stop_game_over_animation();
     void stop_game_over_animation();
     void create_new_animation_card();
     void create_new_animation_card();