reactos/base/applications/games/solitaire/solundo.cpp
Tibor Lajos Füzi 9dff498b21
[SOLITAIRE] Add undo feature & fix occasionally invisible cards (#3422)
Two issues are addressed:

CORE-2189: missing undo feature

Works the same way as solitaire in windows xp:
- only 1 action can be undone;
- the player gets -2 points in standard score mode;
- the undo action resets when the player clicks on a row stack to turn the top card.

CORE-11148: invisible cards

This happens in 3-card mode, when only 1 card left in the deck. The fix for this is to modify the pile stack to contain all the face-up cards. It was actually already in the code somewhere else, so I turned it into a separate function.
2021-02-28 22:31:19 +01:00

172 lines
4.9 KiB
C++

/*
* PROJECT: Solitaire
* LICENSE: See COPYING in top level directory
* FILE: base/applications/games/solitaire/solundo.cpp
* PURPOSE: Undo module for Solitaire
* PROGRAMMER: Tibor Lajos Füzi
*/
#include "solitaire.h"
// source_id and destination_id store the source and destination of the cards
// that were moved. These ids are defined in solitaire.h and can be DECK_ID, PILE_ID,
// [SUIT_ID..SUIT_ID + 3], [ROW_ID..ROW_ID + NUM_ROW_STACKS - 1].
// -1 means that there is no action stored in the undo module.
static int source_id = -1;
static int destination_id = -1;
// Number of cards that were moved.
static int number_of_cards = 0;
// The score before the action was taken.
static int prev_score = 0;
// The number of visible pile cards before the action was taken.
static int prev_visible_pile_cards = 0;
void SetUndo(
int set_source_id,
int set_destination_id,
int set_number_of_cards,
int set_prev_score,
int set_prev_visible_pile_cards)
{
if ((set_source_id == set_destination_id) || (set_number_of_cards == 0))
return;
source_id = set_source_id;
destination_id = set_destination_id;
number_of_cards = set_number_of_cards;
prev_score = set_prev_score;
prev_visible_pile_cards = set_prev_visible_pile_cards;
SetUndoMenuState(true);
}
void ClearUndo(void)
{
source_id = -1;
destination_id = -1;
number_of_cards = 0;
SetUndoMenuState(false);
}
void Undo(void)
{
CardRegion *source = NULL;
CardRegion *destination = NULL;
if ((source_id < 1) ||
(source_id > (ROW_ID + NUM_ROW_STACKS - 1)) ||
(destination_id < 1) ||
(destination_id > (ROW_ID + NUM_ROW_STACKS - 1)) ||
(number_of_cards < 1))
{
ClearUndo();
return;
}
if (source_id >= ROW_ID)
source = pRowStack[source_id - ROW_ID];
else if ((source_id >= SUIT_ID) && (source_id < SUIT_ID + 4))
source = pSuitStack[source_id - SUIT_ID];
else if (source_id == PILE_ID)
source = pPile;
else if (source_id == DECK_ID)
source = pDeck;
if (destination_id >= ROW_ID)
destination = pRowStack[destination_id - ROW_ID];
else if ((destination_id >= SUIT_ID) && (destination_id < SUIT_ID + 4))
destination = pSuitStack[destination_id - SUIT_ID];
else if (destination_id == PILE_ID)
destination = pPile;
else if (destination_id == DECK_ID)
destination = pDeck;
if (destination == NULL || source == NULL)
{
ClearUndo();
return;
}
// If the player clicked on the deck.
if (destination == pPile && source == pDeck)
{
// Put back the cards on the deck in reversed order.
CardStack tmp = activepile.Pop(number_of_cards);
tmp.Reverse();
source->Push(tmp);
// Restore the pile to be the top cards in the active pile.
destination->Clear();
if (prev_visible_pile_cards <= 1)
{
destination->SetOffsets(0,0);
destination->SetCardStack(activepile);
}
else
{
tmp = activepile.Top(prev_visible_pile_cards);
destination->SetOffsets(CS_DEFXOFF, 1);
destination->Push(tmp);
}
VisiblePileCards = prev_visible_pile_cards;
}
// If the player clicked on the empty deck.
else if (source == pPile && destination == pDeck)
{
// Put back all the cards from the deck to the active pile in reversed order.
destination->Reverse();
activepile.Push(destination->GetCardStack());
destination->Clear();
if (prev_visible_pile_cards <= 1)
{
source->SetOffsets(0,0);
source->SetCardStack(activepile);
}
else
{
CardStack tmp = activepile.Top(prev_visible_pile_cards);
source->SetOffsets(CS_DEFXOFF, 1);
source->Push(tmp);
}
VisiblePileCards = prev_visible_pile_cards;
}
// If the player moved one card from the pile.
else if (source == pPile)
{
CardStack tmp = destination->Pop(1);
activepile.Push(tmp);
if (prev_visible_pile_cards <= 1)
{
source->Push(tmp);
}
else
{
source->Clear();
tmp = activepile.Top(prev_visible_pile_cards);
source->Push(tmp);
source->SetOffsets(CS_DEFXOFF, 1);
}
VisiblePileCards = prev_visible_pile_cards;
}
// If the player moved cards between row stacks / suit stacks.
else
{
destination->MoveCard(source, number_of_cards, false);
}
lScore = prev_score;
// -2 points for the undo in standard score mode.
if (GetScoreMode() == SCORE_STD)
lScore = lScore >= 2 ? lScore - 2 : 0;
UpdateStatusBar();
SolWnd.Redraw();
ClearUndo();
}