From 3a45bf52549b02bc40279d33861e15e79f827ed2 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Fri, 14 May 2021 23:09:41 -0400 Subject: [PATCH] Solitaire: Add stack for the playable cards on top of the waste stack While the waste stack and the playable card on top of the waste stack are collectively referred to as the "waste", it's programatically nice to separate them to enable 3-card-draw mode. In that mode, the playable stack will contain 3 cards with a slight x-axis shift, while the waste stack underneath will remain unshifted. So rather than introducing some ugly logic to CardStack to handle this, it's more convenient to have a separate stack on top of the waste stack. --- Userland/Games/Solitaire/CardStack.cpp | 15 +++++++++- Userland/Games/Solitaire/CardStack.h | 7 ++++- Userland/Games/Solitaire/Game.cpp | 41 ++++++++++++++++++++++---- Userland/Games/Solitaire/Game.h | 1 + 4 files changed, 56 insertions(+), 8 deletions(-) diff --git a/Userland/Games/Solitaire/CardStack.cpp b/Userland/Games/Solitaire/CardStack.cpp index a9ecfa1435..d7694b90bc 100644 --- a/Userland/Games/Solitaire/CardStack.cpp +++ b/Userland/Games/Solitaire/CardStack.cpp @@ -52,6 +52,8 @@ void CardStack::draw(GUI::Painter& painter, const Gfx::Color& background_color) } break; case Waste: + break; + case Play: if (is_empty() || (m_stack.size() == 1 && peek().is_moving())) painter.draw_rect(m_base, background_color.darkened(0.5)); break; @@ -131,7 +133,7 @@ void CardStack::add_all_grabbed_cards(const Gfx::IntPoint& click_location, Nonnu bool CardStack::is_allowed_to_push(const Card& card) const { - if (m_type == Stock || m_type == Waste) + if (m_type == Stock || m_type == Waste || m_type == Play) return false; if (m_type == Normal && is_empty()) @@ -191,6 +193,17 @@ NonnullRefPtr CardStack::pop() return card; } +void CardStack::move_to_stack(CardStack& stack) +{ + while (!m_stack.is_empty()) { + auto card = m_stack.take_first(); + m_stack_positions.take_first(); + stack.push(move(card)); + } + + calculate_bounding_box(); +} + void CardStack::calculate_bounding_box() { m_bounding_box = Gfx::IntRect(m_position, { Card::width, Card::height }); diff --git a/Userland/Games/Solitaire/CardStack.h b/Userland/Games/Solitaire/CardStack.h index 5521b54194..5acd22d473 100644 --- a/Userland/Games/Solitaire/CardStack.h +++ b/Userland/Games/Solitaire/CardStack.h @@ -18,6 +18,7 @@ public: Stock, Normal, Waste, + Play, Foundation }; @@ -36,6 +37,7 @@ public: void push(NonnullRefPtr card); NonnullRefPtr pop(); + void move_to_stack(CardStack&); void rebound_cards(); bool is_allowed_to_push(const Card&) const; @@ -59,8 +61,11 @@ private: case Normal: return { 0, 20, 1, 3 }; case Stock: - case Waste: return { 2, 1, 8, 1 }; + case Waste: + return { 0, 0, 1, 0 }; + case Play: + return { 20, 0, 1, 0 }; default: return {}; } diff --git a/Userland/Games/Solitaire/Game.cpp b/Userland/Games/Solitaire/Game.cpp index 6e1f29d5a6..1b041ef16a 100644 --- a/Userland/Games/Solitaire/Game.cpp +++ b/Userland/Games/Solitaire/Game.cpp @@ -22,6 +22,7 @@ Game::Game() m_stacks[Stock] = CardStack({ 10, 10 }, CardStack::Type::Stock); m_stacks[Waste] = CardStack({ 10 + Card::width + 10, 10 }, CardStack::Type::Waste); + m_stacks[Play] = CardStack({ 10 + Card::width + 10, 10 }, CardStack::Type::Play); m_stacks[Foundation4] = CardStack({ Game::width - Card::width - 10, 10 }, CardStack::Type::Foundation); m_stacks[Foundation3] = CardStack({ Game::width - 2 * Card::width - 20, 10 }, CardStack::Type::Foundation); m_stacks[Foundation2] = CardStack({ Game::width - 3 * Card::width - 30, 10 }, CardStack::Type::Foundation); @@ -148,16 +149,26 @@ void Game::mousedown_event(GUI::MouseEvent& event) auto click_location = event.position(); for (auto& to_check : m_stacks) { + if (to_check.type() == CardStack::Type::Waste) + continue; + if (to_check.bounding_box().contains(click_location)) { 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()) + 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(); @@ -167,9 +178,9 @@ void Game::mousedown_event(GUI::MouseEvent& event) update_score(-100); update(stock.bounding_box()); } else { - move_card(stock, waste); + play.move_to_stack(waste); + move_card(stock, play); } - } else if (!to_check.is_empty()) { auto& top_card = to_check.peek(); @@ -213,12 +224,21 @@ void Game::mouseup_event(GUI::MouseEvent& event) m_focused_stack->pop(); } + 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_stack->push(move(card)); + } + } + update(m_focused_stack->bounding_box()); update(stack.bounding_box()); - if (m_focused_stack->type() == CardStack::Type::Waste && stack.type() == CardStack::Type::Normal) { + if (m_focused_stack->type() == CardStack::Type::Play && stack.type() == CardStack::Type::Normal) { update_score(5); - } else if (m_focused_stack->type() == CardStack::Type::Waste && stack.type() == CardStack::Type::Foundation) { + } 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); @@ -279,7 +299,7 @@ void Game::doubleclick_event(GUI::MouseEvent& event) auto click_location = event.position(); for (auto& to_check : m_stacks) { - if (to_check.type() == CardStack::Type::Foundation || to_check.type() == CardStack::Type::Stock) + if (to_check.type() == CardStack::Type::Foundation || to_check.type() == CardStack::Type::Stock || to_check.type() == CardStack::Type::Waste) continue; if (to_check.bounding_box().contains(click_location) && !to_check.is_empty()) { @@ -296,6 +316,15 @@ void Game::doubleclick_event(GUI::MouseEvent& event) else break; + if (to_check.type() == CardStack::Type::Play) { + auto& waste = this->stack(Waste); + if (to_check.is_empty() && !waste.is_empty()) { + auto card = waste.pop(); + m_focused_cards.append(card); + to_check.push(move(card)); + } + } + update_score(10); } break; diff --git a/Userland/Games/Solitaire/Game.h b/Userland/Games/Solitaire/Game.h index 0a4902154f..9fa44b1a6e 100644 --- a/Userland/Games/Solitaire/Game.h +++ b/Userland/Games/Solitaire/Game.h @@ -84,6 +84,7 @@ private: enum StackLocation { Stock, Waste, + Play, Foundation1, Foundation2, Foundation3,