1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 06:57:44 +00:00

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`
This commit is contained in:
Matthew B. Jones 2021-06-03 01:43:28 -06:00 committed by GitHub
parent e7cfa9bf8e
commit ecaae2d10f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 139 additions and 72 deletions

View file

@ -131,6 +131,14 @@ void Game::setup(Mode mode)
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)
{
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)
return;
if (event.shift() && (event.key() == KeyCode::Key_F12))
if (event.shift() && event.key() == KeyCode::Key_F12) {
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();
}
}
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)
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();
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.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()) {
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);
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());
@ -416,6 +363,122 @@ void Game::move_card(CardStack& from, CardStack& to)
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)
{
for (auto& stack : m_stacks) {

View file

@ -160,6 +160,10 @@ private:
void remember_flip_for_undo(Card& card);
void update_score(int to_add);
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 stop_game_over_animation();
void create_new_animation_card();