Serenity Operating System
at master 142 lines 4.0 kB view raw
1/* 2 * Copyright (c) 2020, Till Mayer <till.mayer@web.de> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#pragma once 8 9#include "Card.h" 10#include <AK/Format.h> 11#include <AK/RefCounted.h> 12#include <AK/Vector.h> 13 14namespace Cards { 15 16class CardStack final : public RefCounted<CardStack> { 17public: 18 enum class Type { 19 Invalid, 20 Stock, 21 Normal, 22 Waste, 23 Play, 24 Foundation 25 }; 26 27 enum class MovementRule { 28 Alternating, 29 Same, 30 Any, 31 }; 32 33 CardStack(); 34 CardStack(Gfx::IntPoint position, Type type, RefPtr<CardStack> covered_stack = nullptr); 35 36 bool is_empty() const { return m_stack.is_empty(); } 37 Type type() const { return m_type; } 38 Vector<NonnullRefPtr<Card>> const& stack() const { return m_stack; } 39 size_t count() const { return m_stack.size(); } 40 Card const& peek() const { return m_stack.last(); } 41 Card& peek() { return m_stack.last(); } 42 Gfx::IntRect const& bounding_box() const { return m_bounding_box; } 43 44 bool make_top_card_visible(); // Returns true if the card was flipped. 45 46 ErrorOr<void> push(NonnullRefPtr<Card>); 47 NonnullRefPtr<Card> pop(); 48 ErrorOr<void> take_all(CardStack&); 49 void rebound_cards(); 50 51 bool is_allowed_to_push(Card const&, size_t stack_size = 1, MovementRule movement_rule = MovementRule::Alternating) const; 52 ErrorOr<void> add_all_grabbed_cards(Gfx::IntPoint click_location, Vector<NonnullRefPtr<Card>>& grabbed, MovementRule movement_rule = MovementRule::Alternating); 53 54 bool preview_card(Gfx::IntPoint click_location); 55 void clear_card_preview(); 56 57 void paint(GUI::Painter&, Gfx::Color background_color); 58 void clear(); 59 60 void set_highlighted(bool highlighted) { m_highlighted = highlighted; } 61 62private: 63 struct StackRules { 64 uint8_t shift_x { 0 }; 65 uint8_t shift_y { 0 }; 66 uint8_t step { 1 }; 67 uint8_t shift_y_upside_down { 0 }; 68 }; 69 70 static constexpr StackRules rules_for_type(Type stack_type) 71 { 72 switch (stack_type) { 73 case Type::Foundation: 74 return { 2, 1, 4, 1 }; 75 case Type::Normal: 76 return { 0, 20, 1, 3 }; 77 case Type::Stock: 78 return { 2, 1, 8, 1 }; 79 case Type::Waste: 80 return { 0, 0, 1, 0 }; 81 case Type::Play: 82 return { 20, 0, 1, 0 }; 83 default: 84 return {}; 85 } 86 } 87 88 void calculate_bounding_box(); 89 90 // An optional stack that this stack is painted on top of. 91 // eg, in Solitaire the Play stack is positioned over the Waste stack. 92 RefPtr<CardStack> m_covered_stack; 93 94 Vector<NonnullRefPtr<Card>> m_stack; 95 Vector<Gfx::IntPoint> m_stack_positions; 96 Gfx::IntPoint m_position; 97 Gfx::IntRect m_bounding_box; 98 Type m_type { Type::Invalid }; 99 StackRules m_rules; 100 Gfx::IntRect m_base; 101 bool m_highlighted { false }; 102}; 103 104} 105 106template<> 107struct AK::Formatter<Cards::CardStack> : Formatter<FormatString> { 108 ErrorOr<void> format(FormatBuilder& builder, Cards::CardStack const& stack) 109 { 110 StringView type; 111 112 switch (stack.type()) { 113 case Cards::CardStack::Type::Stock: 114 type = "Stock"sv; 115 break; 116 case Cards::CardStack::Type::Normal: 117 type = "Normal"sv; 118 break; 119 case Cards::CardStack::Type::Foundation: 120 type = "Foundation"sv; 121 break; 122 case Cards::CardStack::Type::Waste: 123 type = "Waste"sv; 124 break; 125 case Cards::CardStack::Type::Play: 126 type = "Play"sv; 127 break; 128 default: 129 VERIFY_NOT_REACHED(); 130 } 131 132 StringBuilder cards; 133 bool first_card = true; 134 135 for (auto const& card : stack.stack()) { 136 cards.appendff("{}{}", (first_card ? "" : " "), card); 137 first_card = false; 138 } 139 140 return Formatter<FormatString>::format(builder, "{:<10} {:>16}: {}"sv, type, stack.bounding_box(), cards.to_deprecated_string()); 141 } 142};