diff --git a/Userland/Games/Solitaire/CardStack.cpp b/Userland/Games/Solitaire/CardStack.cpp index 8d77476db0..a9ecfa1435 100644 --- a/Userland/Games/Solitaire/CardStack.cpp +++ b/Userland/Games/Solitaire/CardStack.cpp @@ -75,8 +75,6 @@ void CardStack::draw(GUI::Painter& painter, const Gfx::Color& background_color) if (!card.is_moving()) card.clear_and_draw(painter, background_color); } - - m_dirty = false; } void CardStack::rebound_cards() diff --git a/Userland/Games/Solitaire/CardStack.h b/Userland/Games/Solitaire/CardStack.h index d42fc5af18..5521b54194 100644 --- a/Userland/Games/Solitaire/CardStack.h +++ b/Userland/Games/Solitaire/CardStack.h @@ -24,7 +24,6 @@ public: CardStack(); CardStack(const Gfx::IntPoint& position, Type type); - bool is_dirty() const { return m_dirty; } bool is_empty() const { return m_stack.is_empty(); } bool is_focused() const { return m_focused; } Type type() const { return m_type; } @@ -34,7 +33,6 @@ public: const Gfx::IntRect& bounding_box() const { return m_bounding_box; } void set_focused(bool focused) { m_focused = focused; } - void set_dirty() { m_dirty = true; }; void push(NonnullRefPtr card); NonnullRefPtr pop(); @@ -77,7 +75,6 @@ private: Type m_type { Invalid }; StackRules m_rules; bool m_focused { false }; - bool m_dirty { false }; Gfx::IntRect m_base; }; diff --git a/Userland/Games/Solitaire/Game.cpp b/Userland/Games/Solitaire/Game.cpp index 9efebb5d8c..1aac81557c 100644 --- a/Userland/Games/Solitaire/Game.cpp +++ b/Userland/Games/Solitaire/Game.cpp @@ -6,13 +6,13 @@ #include "Game.h" #include +#include #include REGISTER_WIDGET(Solitaire, Game); namespace Solitaire { -static const Color s_background_color { Color::from_rgb(0x008000) }; static constexpr uint8_t new_game_animation_delay = 5; static constexpr int s_timer_interval_ms = 1000 / 60; @@ -48,21 +48,18 @@ void Game::timer_event(Core::TimerEvent&) { if (m_game_over_animation) { VERIFY(!m_animation.card().is_null()); - if (m_animation.card()->position().x() > Game::width || m_animation.card()->rect().right() < 0) + if (m_animation.card()->position().x() >= Game::width || m_animation.card()->rect().right() <= 0) create_new_animation_card(); - m_animation.tick(); - } - - if (m_has_to_repaint || m_game_over_animation || m_new_game_animation) { - m_repaint_all = false; + if (m_animation.tick()) + update(m_animation.card()->rect()); + } else if (m_new_game_animation) { update(); } } void Game::create_new_animation_card() { - auto card = Card::construct(static_cast(rand() % Card::Type::__Count), rand() % Card::card_count); card->set_position({ rand() % (Game::width - Card::width), rand() % (Game::height / 8) }); @@ -151,27 +148,27 @@ void Game::mousedown_event(GUI::MouseEvent& event) if (waste.is_empty()) return; + update(waste.bounding_box()); + while (!waste.is_empty()) { auto card = waste.pop(); stock.push(card); } - stock.set_dirty(); - waste.set_dirty(); - m_has_to_repaint = true; update_score(-100); + update(stock.bounding_box()); } else { move_card(stock, waste); } + } else if (!to_check.is_empty()) { auto& top_card = to_check.peek(); if (top_card.is_upside_down()) { if (top_card.rect().contains(click_location)) { top_card.set_upside_down(false); - to_check.set_dirty(); update_score(5); - m_has_to_repaint = true; + update(top_card.rect()); } } else if (m_focused_cards.is_empty()) { to_check.add_all_grabbed_cards(click_location, m_focused_cards); @@ -207,8 +204,8 @@ void Game::mouseup_event(GUI::MouseEvent& event) m_focused_stack->pop(); } - m_focused_stack->set_dirty(); - stack.set_dirty(); + update(m_focused_stack->bounding_box()); + update(stack.bounding_box()); if (m_focused_stack->type() == CardStack::Type::Waste && stack.type() == CardStack::Type::Normal) { update_score(5); @@ -232,11 +229,10 @@ void Game::mouseup_event(GUI::MouseEvent& event) mark_intersecting_stacks_dirty(to_intersect); m_focused_stack->rebound_cards(); - m_focused_stack->set_dirty(); + update(m_focused_stack->bounding_box()); } m_mouse_down = false; - m_has_to_repaint = true; } void Game::mousemove_event(GUI::MouseEvent& event) @@ -253,10 +249,10 @@ void Game::mousemove_event(GUI::MouseEvent& event) for (auto& to_intersect : m_focused_cards) { mark_intersecting_stacks_dirty(to_intersect); to_intersect.rect().translate_by(dx, dy); + update(to_intersect.rect()); } m_mouse_down_location = click_location; - m_has_to_repaint = true; } void Game::doubleclick_event(GUI::MouseEvent& event) @@ -296,8 +292,6 @@ void Game::doubleclick_event(GUI::MouseEvent& event) break; } } - - m_has_to_repaint = true; } void Game::check_for_game_over() @@ -314,6 +308,8 @@ void Game::check_for_game_over() void Game::move_card(CardStack& from, CardStack& to) { + update(from.bounding_box()); + auto card = from.pop(); card->set_moving(true); @@ -322,42 +318,35 @@ void Game::move_card(CardStack& from, CardStack& to) mark_intersecting_stacks_dirty(card); to.push(card); - from.set_dirty(); - to.set_dirty(); - - m_has_to_repaint = true; + update(to.bounding_box()); } void Game::mark_intersecting_stacks_dirty(Card& intersecting_card) { for (auto& stack : m_stacks) { - if (intersecting_card.rect().intersects(stack.bounding_box())) { - stack.set_dirty(); - m_has_to_repaint = true; - } + if (intersecting_card.rect().intersects(stack.bounding_box())) + update(stack.bounding_box()); } + + update(intersecting_card.rect()); } void Game::paint_event(GUI::PaintEvent& event) { - GUI::Frame::paint_event(event); + static Gfx::Color s_background_color = palette().color(background_role()); - m_has_to_repaint = false; - if (m_game_over_animation && m_repaint_all) - return; + GUI::Frame::paint_event(event); GUI::Painter painter(*this); painter.add_clip_rect(frame_inner_rect()); painter.add_clip_rect(event.rect()); - if (m_repaint_all) { - painter.fill_rect(event.rect(), s_background_color); + if (m_game_over_animation) { + m_animation.draw(painter); + return; + } - for (auto& stack : m_stacks) - stack.draw(painter, s_background_color); - } else if (m_game_over_animation && !m_animation.card().is_null()) { - m_animation.card()->draw(painter); - } else if (m_new_game_animation) { + if (m_new_game_animation) { if (m_new_game_animation_delay < new_game_animation_delay) { ++m_new_game_animation_delay; } else { @@ -372,37 +361,31 @@ void Game::paint_event(GUI::PaintEvent& event) current_pile.push(m_new_deck.take_last()); ++m_new_game_animation_pile; } - current_pile.set_dirty(); if (m_new_game_animation_pile == piles.size()) { while (!m_new_deck.is_empty()) stack(Stock).push(m_new_deck.take_last()); - stack(Stock).set_dirty(); m_new_game_animation = false; } } } - if (!m_game_over_animation && !m_repaint_all) { - if (!m_focused_cards.is_empty()) { - for (auto& focused_card : m_focused_cards) - focused_card.clear(painter, s_background_color); - } + if (!m_focused_cards.is_empty()) { + for (auto& focused_card : m_focused_cards) + focused_card.clear(painter, s_background_color); + } - for (auto& stack : m_stacks) { - if (stack.is_dirty()) - stack.draw(painter, s_background_color); - } + for (auto& stack : m_stacks) { + stack.draw(painter, s_background_color); + } - if (!m_focused_cards.is_empty()) { - for (auto& focused_card : m_focused_cards) { - focused_card.draw(painter); - focused_card.save_old_position(); - } + if (!m_focused_cards.is_empty()) { + for (auto& focused_card : m_focused_cards) { + focused_card.draw(painter); + focused_card.save_old_position(); } } - m_repaint_all = true; if (!m_mouse_down) { if (!m_focused_cards.is_empty()) { check_for_game_over(); diff --git a/Userland/Games/Solitaire/Game.h b/Userland/Games/Solitaire/Game.h index 011c2368d5..97167dbbcd 100644 --- a/Userland/Games/Solitaire/Game.h +++ b/Userland/Games/Solitaire/Game.h @@ -42,8 +42,19 @@ private: RefPtr card() { return m_animation_card; } - void tick() + void draw(GUI::Painter& painter) { + VERIFY(!m_animation_card.is_null()); + m_animation_card->draw(painter); + m_dirty = false; + } + + bool tick() + { + // Don't move the animation card until the event loop has had a chance to paint its current location. + if (m_dirty) + return false; + VERIFY(!m_animation_card.is_null()); m_y_velocity += m_gravity; @@ -54,6 +65,9 @@ private: } else { m_animation_card->rect().translate_by(m_x_velocity, m_y_velocity); } + + m_dirty = true; + return true; } private: @@ -62,6 +76,7 @@ private: int m_x_velocity { 0 }; float m_y_velocity { 0 }; float m_bouncyness { 0 }; + bool m_dirty { false }; }; enum StackLocation { @@ -110,8 +125,6 @@ private: Gfx::IntPoint m_mouse_down_location; bool m_mouse_down { false }; - bool m_repaint_all { true }; - bool m_has_to_repaint { true }; Animation m_animation; bool m_game_over_animation { false }; diff --git a/Userland/Games/Solitaire/Solitaire.gml b/Userland/Games/Solitaire/Solitaire.gml index e588ef1d58..d72f90c547 100644 --- a/Userland/Games/Solitaire/Solitaire.gml +++ b/Userland/Games/Solitaire/Solitaire.gml @@ -1,10 +1,12 @@ @GUI::Widget { - fill_with_background_color: false + fill_with_background_color: true layout: @GUI::VerticalBoxLayout { } @Solitaire::Game { name: "game" + fill_with_background_color: true + background_color: "green" } }