Serenity Operating System
at hosted 244 lines 7.4 kB view raw
1/* 2 * Copyright (c) 2020, Till Mayer <till.mayer@web.de> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "CardStack.h" 28 29CardStack::CardStack() 30 : m_position({ 0, 0 }) 31 , m_type(Invalid) 32 , m_shift_x(0) 33 , m_shift_y(0) 34 , m_step(1) 35 , m_base(m_position, { Card::width, Card::height }) 36{ 37} 38 39CardStack::CardStack(const Gfx::Point& position, Type type, uint8_t shift_x, uint8_t shift_y, uint8_t step) 40 : m_position(position) 41 , m_type(type) 42 , m_shift_x(shift_x) 43 , m_shift_y(shift_y) 44 , m_step(step) 45 , m_base(m_position, { Card::width, Card::height }) 46{ 47 ASSERT(step && type != Invalid); 48 calculate_bounding_box(); 49} 50 51void CardStack::clear() 52{ 53 m_stack.clear(); 54 m_stack_positions.clear(); 55} 56 57void CardStack::draw(GUI::Painter& painter, const Gfx::Color& background_color) 58{ 59 switch (m_type) { 60 case Stock: 61 if (is_empty()) { 62 painter.fill_rect(m_base.shrunken(Card::width / 4, Card::height / 4), background_color.lightened(1.5)); 63 painter.fill_rect(m_base.shrunken(Card::width / 2, Card::height / 2), background_color); 64 painter.draw_rect(m_base, background_color.darkened(0.5)); 65 } 66 break; 67 case Foundation: 68 if (is_empty() || (m_stack.size() == 1 && peek().is_moving())) { 69 painter.draw_rect(m_base, background_color.darkened(0.5)); 70 for (int y = 0; y < (m_base.height() - 4) / 8; ++y) { 71 for (int x = 0; x < (m_base.width() - 4) / 5; ++x) { 72 painter.draw_rect({ 4 + m_base.x() + x * 5, 4 + m_base.y() + y * 8, 1, 1 }, background_color.darkened(0.5)); 73 } 74 } 75 } 76 break; 77 case Waste: 78 if (is_empty() || (m_stack.size() == 1 && peek().is_moving())) 79 painter.draw_rect(m_base, background_color.darkened(0.5)); 80 break; 81 case Normal: 82 painter.draw_rect(m_base, background_color.darkened(0.5)); 83 break; 84 default: 85 ASSERT_NOT_REACHED(); 86 } 87 88 if (is_empty()) 89 return; 90 91 if (m_shift_x == 0 && m_shift_y == 0) { 92 auto& card = peek(); 93 card.draw(painter); 94 return; 95 } 96 97 for (auto& card : m_stack) { 98 if (!card.is_moving()) 99 card.draw_complete(painter, background_color); 100 } 101 102 m_dirty = false; 103} 104void CardStack::rebound_cards() 105{ 106 ASSERT(m_stack_positions.size() == m_stack.size()); 107 108 size_t card_index = 0; 109 for (auto& card : m_stack) 110 card.set_position(m_stack_positions.at(card_index++)); 111} 112 113void CardStack::add_all_grabbed_cards(const Gfx::Point& click_location, NonnullRefPtrVector<Card>& grabbed) 114{ 115 ASSERT(grabbed.is_empty()); 116 117 if (m_type != Normal) { 118 auto& top_card = peek(); 119 if (top_card.rect().contains(click_location)) { 120 top_card.set_moving(true); 121 grabbed.append(top_card); 122 } 123 return; 124 } 125 126 RefPtr<Card> last_intersect; 127 128 for (auto& card : m_stack) { 129 if (card.rect().contains(click_location)) { 130 if (card.is_upside_down()) 131 continue; 132 133 last_intersect = card; 134 } else if (!last_intersect.is_null()) { 135 if (grabbed.is_empty()) { 136 grabbed.append(*last_intersect); 137 last_intersect->set_moving(true); 138 } 139 140 if (card.is_upside_down()) { 141 grabbed.clear(); 142 return; 143 } 144 145 card.set_moving(true); 146 grabbed.append(card); 147 } 148 } 149 150 if (grabbed.is_empty() && !last_intersect.is_null()) { 151 grabbed.append(*last_intersect); 152 last_intersect->set_moving(true); 153 } 154} 155 156bool CardStack::is_allowed_to_push(const Card& card) const 157{ 158 if (m_type == Stock || m_type == Waste) 159 return false; 160 161 if (m_type == Normal && is_empty()) 162 return card.value() == 12; 163 164 if (m_type == Foundation && is_empty()) 165 return card.value() == 0; 166 167 if (!is_empty()) { 168 auto& top_card = peek(); 169 if (top_card.is_upside_down()) 170 return false; 171 172 if (m_type == Foundation) { 173 return top_card.type() == card.type() && m_stack.size() == card.value(); 174 } else if (m_type == Normal) { 175 return top_card.color() != card.color() && top_card.value() == card.value() + 1; 176 } 177 178 ASSERT_NOT_REACHED(); 179 } 180 181 return true; 182} 183 184void CardStack::push(NonnullRefPtr<Card> card) 185{ 186 int size = m_stack.size(); 187 int ud_shift = (m_type == Normal) ? 3 : 1; 188 auto top_most_position = m_stack_positions.is_empty() ? m_position : m_stack_positions.last(); 189 190 if (size && size % m_step == 0) { 191 if (peek().is_upside_down()) 192 top_most_position.move_by(m_shift_x, ((m_shift_y == 0) ? 0 : ud_shift)); 193 else 194 top_most_position.move_by(m_shift_x, m_shift_y); 195 } 196 197 if (m_type == Stock) 198 card->set_upside_down(true); 199 200 card->set_position(top_most_position); 201 202 m_stack.append(card); 203 m_stack_positions.append(top_most_position); 204 calculate_bounding_box(); 205} 206 207NonnullRefPtr<Card> CardStack::pop() 208{ 209 auto card = m_stack.take_last(); 210 211 calculate_bounding_box(); 212 if (m_type == Stock) 213 card->set_upside_down(false); 214 215 m_stack_positions.take_last(); 216 return card; 217} 218 219void CardStack::calculate_bounding_box() 220{ 221 m_bounding_box = Gfx::Rect(m_position, { Card::width, Card::height }); 222 223 if (m_stack.is_empty()) 224 return; 225 226 uint16_t width = 0; 227 uint16_t height = 0; 228 int card_position = 0; 229 for (auto& card : m_stack) { 230 if (card_position % m_step == 0 && card_position) { 231 if (card.is_upside_down() && m_type != Stock) { 232 int ud_shift = (m_type == Normal) ? 3 : 1; 233 width += m_shift_x; 234 height += (m_shift_y == 0) ? 0 : ud_shift; 235 } else { 236 width += m_shift_x; 237 height += m_shift_y; 238 } 239 } 240 ++card_position; 241 } 242 243 m_bounding_box.set_size(Card::width + width, Card::height + height); 244}