[SOLITAIRE] Add undo feature & fix occasionally invisible cards (#3422)
authorTibor Lajos Füzi <tibor.fuzi@gmail.com>
Sun, 28 Feb 2021 21:31:19 +0000 (22:31 +0100)
committerGitHub <noreply@github.com>
Sun, 28 Feb 2021 21:31:19 +0000 (22:31 +0100)
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.

37 files changed:
base/applications/games/solitaire/CMakeLists.txt
base/applications/games/solitaire/lang/bg-BG.rc
base/applications/games/solitaire/lang/ca-ES.rc
base/applications/games/solitaire/lang/cs-CZ.rc
base/applications/games/solitaire/lang/de-DE.rc
base/applications/games/solitaire/lang/el-GR.rc
base/applications/games/solitaire/lang/en-US.rc
base/applications/games/solitaire/lang/es-ES.rc
base/applications/games/solitaire/lang/eu-ES.rc
base/applications/games/solitaire/lang/fr-FR.rc
base/applications/games/solitaire/lang/he-IL.rc
base/applications/games/solitaire/lang/hu-HU.rc
base/applications/games/solitaire/lang/id-ID.rc
base/applications/games/solitaire/lang/it-IT.rc
base/applications/games/solitaire/lang/ja-JP.rc
base/applications/games/solitaire/lang/ko-KR.rc
base/applications/games/solitaire/lang/lt-LT.rc
base/applications/games/solitaire/lang/nl-NL.rc
base/applications/games/solitaire/lang/no-NO.rc
base/applications/games/solitaire/lang/pl-PL.rc
base/applications/games/solitaire/lang/pt-BR.rc
base/applications/games/solitaire/lang/ro-RO.rc
base/applications/games/solitaire/lang/ru-RU.rc
base/applications/games/solitaire/lang/sk-SK.rc
base/applications/games/solitaire/lang/sq-AL.rc
base/applications/games/solitaire/lang/sv-SE.rc
base/applications/games/solitaire/lang/th-TH.rc
base/applications/games/solitaire/lang/tr-TR.rc
base/applications/games/solitaire/lang/uk-UA.rc
base/applications/games/solitaire/lang/zh-CN.rc
base/applications/games/solitaire/lang/zh-TW.rc
base/applications/games/solitaire/resource.h
base/applications/games/solitaire/solcreate.cpp
base/applications/games/solitaire/solgame.cpp
base/applications/games/solitaire/solitaire.cpp
base/applications/games/solitaire/solitaire.h
base/applications/games/solitaire/solundo.cpp [new file with mode: 0644]

index a0f0837..a229418 100644 (file)
@@ -2,6 +2,7 @@
 list(APPEND SOURCE
     solcreate.cpp
     solgame.cpp
+    solundo.cpp
     solitaire.cpp
     solitaire.h)
 
index a19f9ed..2b8a79b 100644 (file)
@@ -71,6 +71,7 @@ BEGIN
     BEGIN
         MENUITEM "&Раздаване\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "&Тесте...", IDM_GAME_DECK
         MENUITEM "&Настройки...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index 1a8a0d1..231eb55 100644 (file)
@@ -71,6 +71,7 @@ BEGIN
     BEGIN
         MENUITEM "R&eparteix\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "&Barallar...", IDM_GAME_DECK
         MENUITEM "&Opcions...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index 58060af..bb61acd 100644 (file)
@@ -69,6 +69,7 @@ BEGIN
     BEGIN
         MENUITEM "&Rozdat\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "Rub& karet...", IDM_GAME_DECK
         MENUITEM "&Možnosti...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index 331096f..3211c65 100644 (file)
@@ -71,6 +71,7 @@ BEGIN
     BEGIN
         MENUITEM "&Karten geben\t F2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "&Deckblatt...", IDM_GAME_DECK
         MENUITEM "&Optionen...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index 7dbaa6e..08707ff 100644 (file)
@@ -71,6 +71,7 @@ BEGIN
     BEGIN
         MENUITEM "&Deal\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "De&ck...", IDM_GAME_DECK
         MENUITEM "&Επιλογές...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index 46abfe3..b8111ee 100644 (file)
@@ -71,6 +71,7 @@ BEGIN
     BEGIN
         MENUITEM "&Deal\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "De&ck...", IDM_GAME_DECK
         MENUITEM "&Options...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index 3a3222a..16d1944 100644 (file)
@@ -73,6 +73,7 @@ BEGIN
     BEGIN
         MENUITEM "&Repartir\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "&Barajas...", IDM_GAME_DECK
         MENUITEM "&Opciones...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index 8a3c979..064f283 100644 (file)
@@ -71,6 +71,7 @@ BEGIN
     BEGIN
         MENUITEM "&Banatu\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "&Barajak...", IDM_GAME_DECK
         MENUITEM "&Aukerak...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index f1f8747..2f81611 100644 (file)
@@ -71,6 +71,7 @@ BEGIN
     BEGIN
         MENUITEM "&Donne\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "&Jeux...", IDM_GAME_DECK
         MENUITEM "&Options...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index 4912973..dda4254 100644 (file)
@@ -73,6 +73,7 @@ BEGIN
     BEGIN
         MENUITEM "&חלק\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "ח&פיסה...", IDM_GAME_DECK
         MENUITEM "&אפשרויות", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index 2dad675..1363330 100644 (file)
@@ -71,6 +71,7 @@ BEGIN
     BEGIN
         MENUITEM "&Osztás\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Visszavonás", IDM_GAME_UNDO, GRAYED
         MENUITEM "&Hátlap...", IDM_GAME_DECK
         MENUITEM "&Beállítások...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index a2af672..b877e5a 100644 (file)
@@ -71,6 +71,7 @@ BEGIN
     BEGIN
         MENUITEM "&Main Baru\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "&Dek...", IDM_GAME_DECK
         MENUITEM "&Pilihan...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index c767ebf..845f574 100644 (file)
@@ -72,6 +72,7 @@ BEGIN
     BEGIN
         MENUITEM "&Dai carte\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "Maz&zo...", IDM_GAME_DECK
         MENUITEM "O&pzioni...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index cba2e97..fc8bd2f 100644 (file)
@@ -70,6 +70,7 @@ BEGIN
     BEGIN
         MENUITEM "カードを配る(&D)\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "カードの装飾(&C)...", IDM_GAME_DECK
         MENUITEM "オプション(&O)...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index b59f784..5c16be4 100644 (file)
@@ -65,6 +65,7 @@ BEGIN
     BEGIN
         MENUITEM "새 게임(&D)\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "덱(&C)...", IDM_GAME_DECK /* FIXME: is 덱 correct? */
         MENUITEM "옵션(&O)...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index 38b9219..dbb674c 100644 (file)
@@ -71,6 +71,7 @@ BEGIN
     BEGIN
         MENUITEM "&Dalinti\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "&Malka...", IDM_GAME_DECK
         MENUITEM "&Nuostatos...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index d8bed18..6e71cd9 100644 (file)
@@ -71,6 +71,7 @@ BEGIN
     BEGIN
         MENUITEM "&Delen\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "&Kaarten...", IDM_GAME_DECK
         MENUITEM "&Opties...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index c9c9e25..79600a5 100644 (file)
@@ -63,6 +63,7 @@ BEGIN
     BEGIN
         MENUITEM "&Del ut\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "Kort&stokk...", IDM_GAME_DECK
         MENUITEM "&Valg...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index bb046fb..af7615f 100644 (file)
@@ -73,6 +73,7 @@ BEGIN
     BEGIN
         MENUITEM "&Rozdaj\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "&Talia...", IDM_GAME_DECK
         MENUITEM "&Opcje...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index b510545..6f8563b 100644 (file)
@@ -65,6 +65,7 @@ BEGIN
     BEGIN
         MENUITEM "&Novo Jogo\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "Baralho...", IDM_GAME_DECK
         MENUITEM "&Opções...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index fdffea9..4e14430 100644 (file)
@@ -73,6 +73,7 @@ BEGIN
     BEGIN
         MENUITEM "Rundă &nouă\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "Culoare &dorsală…", IDM_GAME_DECK
         MENUITEM "&Opțiuni…", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index 3e97b0c..047ce74 100644 (file)
@@ -65,6 +65,7 @@ BEGIN
     BEGIN
         MENUITEM "Н&овая игра\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "&Обложки...", IDM_GAME_DECK
         MENUITEM "&Настройки...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index 69f57b4..11a3384 100644 (file)
@@ -72,6 +72,7 @@ BEGIN
     BEGIN
         MENUITEM "&Rozdať\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "&Balíček...", IDM_GAME_DECK
         MENUITEM "&Možnosti...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index 1e2f3b0..02400e8 100644 (file)
@@ -72,6 +72,7 @@ BEGIN
     BEGIN
         MENUITEM "&Ndaj Letrat\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "Pa&ko...", IDM_GAME_DECK
         MENUITEM "&Opsione...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index c1d1530..d548ee8 100644 (file)
@@ -63,6 +63,7 @@ BEGIN
     BEGIN
         MENUITEM "&Ge\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "&Kortlek...", IDM_GAME_DECK
         MENUITEM "&Alternativ...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index ebabeab..c5e8486 100644 (file)
@@ -71,6 +71,7 @@ BEGIN
     BEGIN
         MENUITEM "แ&จกไพ่\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "แ&ต่งไพ่...", IDM_GAME_DECK
         MENUITEM "&ตัวเลือก...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index e53de0d..57ce217 100644 (file)
@@ -72,6 +72,7 @@ BEGIN
     BEGIN
         MENUITEM "&Dağıt\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "D&esteler...", IDM_GAME_DECK
         MENUITEM "&Seçenekler...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index 299492e..c654823 100644 (file)
@@ -71,6 +71,7 @@ BEGIN
     BEGIN
         MENUITEM "&Роздати карти\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "&Колода...", IDM_GAME_DECK
         MENUITEM "Па&раметри...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index 5487ba8..18e8a06 100644 (file)
@@ -71,6 +71,7 @@ BEGIN
     BEGIN
         MENUITEM "发牌(&D)\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "背面图案(&C)...", IDM_GAME_DECK
         MENUITEM "选项(&O)...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index ff8c241..8cc1aa8 100644 (file)
@@ -71,6 +71,7 @@ BEGIN
     BEGIN
         MENUITEM "發牌(&D)\tF2", IDM_GAME_NEW
         MENUITEM SEPARATOR
+        MENUITEM "&Undo", IDM_GAME_UNDO, GRAYED
         MENUITEM "背面圖案(&C)...", IDM_GAME_DECK
         MENUITEM "選項(&O)...", IDM_GAME_OPTIONS
         MENUITEM SEPARATOR
index 0b221bc..680ec9f 100644 (file)
@@ -8,11 +8,12 @@
 
 /* Menu */
 #define IDM_GAME_NEW      1001
-#define IDM_GAME_DECK     1002
-#define IDM_GAME_OPTIONS  1003
-#define IDM_GAME_EXIT     1004
-#define IDM_HELP_CONTENTS 1005
-#define IDM_HELP_ABOUT    1006
+#define IDM_GAME_UNDO     1002
+#define IDM_GAME_DECK     1003
+#define IDM_GAME_OPTIONS  1004
+#define IDM_GAME_EXIT     1005
+#define IDM_HELP_CONTENTS 1006
+#define IDM_HELP_ABOUT    1007
 
 /* Dialogs */
 #define IDD_OPTIONS       1200
index 00d29d8..64af9b1 100644 (file)
@@ -5,8 +5,6 @@ CardRegion *pPile;
 CardRegion *pSuitStack[4];
 CardRegion *pRowStack[NUM_ROW_STACKS];
 
-extern CardStack activepile;
-
 HBITMAP hbmBitmap;
 HDC     hdcBitmap;
 int     yRowStackCardOffset;
index f132264..19219b8 100644 (file)
@@ -10,6 +10,7 @@ extern TCHAR MsgWin[128];
 extern TCHAR MsgDeal[128];
 
 CardStack activepile;
+int VisiblePileCards;
 int LastId;
 bool fGameStarted = false;
 bool bAutoroute = false;
@@ -53,6 +54,7 @@ void NewGame(void)
     deck.NewDeck();
     deck.Shuffle();
     activepile.Clear();
+    VisiblePileCards = 0;
 
     //deal to each row stack..
     for(i = 0; i < NUM_ROW_STACKS; i++)
@@ -85,6 +87,7 @@ void NewGame(void)
     dwPrevMode = GetScoreMode();
 
     UpdateStatusBar();
+    ClearUndo();
 
     TRACE("EXIT NewGame()\n");
 
@@ -160,6 +163,8 @@ bool CARDLIBPROC RowStackDropProc(CardRegion &stackobj, CardStack &dragcards)
 
     fGameStarted = true;
 
+    SetUndo(LastId, stackobj.Id(), dragcards.NumCards(), lScore, VisiblePileCards);
+
     if (LastId == PILE_ID)
     {
         if (GetScoreMode() == SCORE_STD)
@@ -245,6 +250,8 @@ bool CARDLIBPROC SuitStackDropProc(CardRegion &stackobj, CardStack &dragcards)
 
     if (b)
     {
+        SetUndo(LastId, stackobj.Id(), 1, lScore, VisiblePileCards);
+
         if ((LastId == PILE_ID) || (LastId >= ROW_ID))
         {
             if (GetScoreMode() == SCORE_VEGAS)
@@ -300,6 +307,7 @@ void CARDLIBPROC RowStackClickProc(CardRegion &stackobj, int iNumClicked)
             lScore = lScore + 5;
             UpdateStatusBar();
         }
+        ClearUndo();
     }
 
     LastId = stackobj.Id();
@@ -363,6 +371,7 @@ void CARDLIBPROC SuitStackAddProc(CardRegion &stackobj, const CardStack &added)
         }
 
         UpdateStatusBar();
+        ClearUndo();
 
         MessageBox(SolWnd, MsgWin, szAppName, MB_OK | MB_ICONINFORMATION);
 
@@ -450,6 +459,22 @@ void CARDLIBPROC PileDblClickProc(CardRegion &stackobj, int iNumClicked)
     TRACE("EXIT PileDblClickProc()\n");
 }
 
+//
+//    Fix for the 3-card play when only 1 card left on the pile.
+//
+void FixIfOneCardLeft(void)
+{
+    // If there is just 1 card left, then modify the
+    // stack to contain ALL the face-up cards. The effect
+    // will be, the next time a card is dragged, all the
+    // previous card-triplets will be available underneath.
+    if ((dwOptions & OPTION_THREE_CARDS) && pPile->NumCards() == 1)
+    {
+        pPile->SetOffsets(0, 0);
+        pPile->SetCardStack(activepile);
+    }
+}
+
 //
 //    What happens when a card is removed from face-up pile?
 //
@@ -462,16 +487,13 @@ void CARDLIBPROC PileRemoveProc(CardRegion &stackobj, int iItems)
     //modify our "virtual" pile by removing the same card
     //that was removed from the physical card stack
     activepile.Pop(iItems);
-
-    //if there is just 1 card left, then modify the
-    //stack to contain ALL the face-up cards..the effect
-    //will be, the next time a card is dragged, all the
-    //previous card-triplets will be available underneath
-    if(stackobj.NumCards() == 1)
+    if ((dwOptions & OPTION_THREE_CARDS) && (VisiblePileCards > 1))
     {
-        stackobj.SetOffsets(0,0);
-        stackobj.SetCardStack(activepile);
+        --VisiblePileCards;
     }
+
+    FixIfOneCardLeft();
+
     TRACE("EXIT PileRemoveProc()\n");
 }
 
@@ -506,10 +528,13 @@ void CARDLIBPROC DeckClickProc(CardRegion &stackobj, int iNumClicked)
                 activepile.Reverse();
                 cardstack.Push(activepile);
                 activepile.Clear();
+                SetUndo(PILE_ID, DECK_ID, cardstack.NumCards(), lScore, VisiblePileCards);
+                VisiblePileCards = 0;
             }
         }
         else if (GetScoreMode() == SCORE_STD)
         {
+            SetUndo(PILE_ID, DECK_ID, activepile.NumCards(), lScore, VisiblePileCards);
             if ((dwWasteCount >= dwWasteTreshold) && (activepile.NumCards() != 0))
             {
                 if (dwOptions & OPTION_THREE_CARDS)
@@ -523,6 +548,7 @@ void CARDLIBPROC DeckClickProc(CardRegion &stackobj, int iNumClicked)
             activepile.Reverse();
             cardstack.Push(activepile);
             activepile.Clear();
+            VisiblePileCards = 0;
 
             UpdateStatusBar();
         }
@@ -533,6 +559,8 @@ void CARDLIBPROC DeckClickProc(CardRegion &stackobj, int iNumClicked)
             activepile.Reverse();
             cardstack.Push(activepile);
             activepile.Clear();
+            SetUndo(PILE_ID, DECK_ID, cardstack.NumCards(), lScore, VisiblePileCards);
+            VisiblePileCards = 0;
         }
 
         dwWasteCount++;
@@ -541,6 +569,8 @@ void CARDLIBPROC DeckClickProc(CardRegion &stackobj, int iNumClicked)
     {
         int numcards = min((dwOptions & OPTION_THREE_CARDS) ? 3 : 1, cardstack.NumCards());
 
+        SetUndo(DECK_ID, PILE_ID, numcards, lScore, VisiblePileCards);
+
         //make a "visible" copy of these cards
         CardStack temp;
         temp = cardstack.Pop(numcards);
@@ -553,6 +583,8 @@ void CARDLIBPROC DeckClickProc(CardRegion &stackobj, int iNumClicked)
 
         //remove the top 3 from deck
         activepile.Push(temp);
+
+        VisiblePileCards = numcards;
     }
 
     activepile.Print();
@@ -560,6 +592,8 @@ void CARDLIBPROC DeckClickProc(CardRegion &stackobj, int iNumClicked)
     pDeck->SetCardStack(cardstack);
     pPile->SetCardStack(pile);
 
+    FixIfOneCardLeft();
+
     SolWnd.Redraw();
     TRACE("EXIT DeckClickProc()\n");
 }
index 84895cb..529f6ba 100644 (file)
@@ -12,6 +12,7 @@ DWORD        dwAppStartTime;
 HWND        hwndMain;
 HWND        hwndStatus;
 HINSTANCE    hInstance;
+HMENU        hGameMenu;
 
 TCHAR szAppName[128];
 TCHAR szScore[64];
@@ -178,6 +179,18 @@ void SetPlayTimer(void)
     }
 }
 
+void SetUndoMenuState(bool enable)
+{
+    if (enable)
+    {
+        EnableMenuItem(hGameMenu, IDM_GAME_UNDO, MF_BYCOMMAND | MF_ENABLED);
+    }
+    else
+    {
+        EnableMenuItem(hGameMenu, IDM_GAME_UNDO, MF_BYCOMMAND | MF_GRAYED);
+    }
+}
+
 //
 //    Main entry point
 //
@@ -248,6 +261,8 @@ int WINAPI _tWinMain(HINSTANCE hInst, HINSTANCE hPrev, LPTSTR szCmdLine, int iCm
 
     hwndMain = hwnd;
 
+    hGameMenu = GetSubMenu(GetMenu(hwndMain), 0);
+
     UpdateStatusBar();
 
     ShowWindow(hwnd, iCmdShow);
@@ -706,6 +721,10 @@ LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
                 NewGame();
                 return 0;
 
+            case IDM_GAME_UNDO:
+                Undo();
+                return 0;
+
             case IDM_GAME_DECK:
                 ShowDeckOptionsDlg(hwnd);
                 return 0;
@@ -753,4 +772,3 @@ LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
 }
 
 
-
index 9905ec5..2254b7a 100644 (file)
@@ -54,15 +54,18 @@ void NewGame(void);
 #define Y_BORDERWITHFRAME        20
 #define Y_ROWSTACK_BORDER        32
 extern int yRowStackCardOffset;
+extern int VisiblePileCards;
 
 extern CardRegion *pDeck;
 extern CardRegion *pPile;
 extern CardRegion *pSuitStack[];
 extern CardRegion *pRowStack[];
+extern CardStack   activepile;
 
 extern void UpdateStatusBar(void);
 extern void SetPlayTimer(void);
 extern int GetScoreMode(void);
+extern void SetUndoMenuState(bool enable);
 
 bool CARDLIBPROC RowStackDragProc(CardRegion &stackobj, int iNumCards);
 bool CARDLIBPROC RowStackDropProc(CardRegion &stackobj,  CardStack &dragcards);
@@ -80,5 +83,9 @@ void CARDLIBPROC PileClickProc(CardRegion &stackobj, int iNumClicked);
 
 void CARDLIBPROC PileRemoveProc(CardRegion &stackobj, int iRemoved);
 
+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);
+void ClearUndo(void);
+void Undo(void);
+
 
 #endif /* _SOL_PCH_ */
diff --git a/base/applications/games/solitaire/solundo.cpp b/base/applications/games/solitaire/solundo.cpp
new file mode 100644 (file)
index 0000000..ef50925
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * 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();
+}