1 /*
2  * PROJECT:      Spider Solitaire
3  * LICENSE:      See COPYING in top level directory
4  * FILE:         base/applications/games/spider/spigame.cpp
5  * PURPOSE:      Spider Solitaire game functions
6  * PROGRAMMER:   Gregor Schneider
7  */
8 
9 #include "spider.h"
10 
11 #define NUM_DECK_CARDS     5
12 #define NUM_SMALLER_STACKS 4
13 #define NUM_CARD_COLORS    4
14 #define NUM_ONECOLOR_CARDS 13
15 #define NUM_STD_CARDS      52
16 #define NUM_SPIDER_CARDS   104
17 
18 CardStack deck;
19 CardRegion *from;
20 CardRegion *pDeck;
21 CardRegion *pStack[NUM_STACKS];
22 bool fGameStarted = false;
23 int  yRowStackCardOffset;
24 int cardsFinished;
25 extern TCHAR MsgDeal[];
26 extern TCHAR MsgWin[];
27 
28 CardStack CreatePlayDeck()
29 {
30     CardStack newStack;
31     int i, colors = 1, num = 0;
32 
33     switch (dwDifficulty)
34     {
35         case IDC_DIF_ONECOLOR:
36             colors = 1;
37             break;
38         case IDC_DIF_TWOCOLORS:
39             colors = 2;
40             break;
41         case IDC_DIF_FOURCOLORS:
42             colors = 4;
43             break;
44     }
45     for (i = 0; i < NUM_SPIDER_CARDS; i++)
46     {
47         num += NUM_CARD_COLORS / colors;
48         Card newCard(num % NUM_STD_CARDS);
49         newStack.Push(newCard);
50     }
51     return newStack;
52 }
53 
54 void NewGame(void)
55 {
56     int i, j;
57     /* First four stack with five, all other with 4 */
58     int covCards = 5;
59     CardStack fakeDeck, temp;
60 
61     SpiderWnd.EmptyStacks();
62 
63     /* Create a new card-deck, fake deck */
64     deck = CreatePlayDeck();
65     deck.Shuffle();
66     fakeDeck.NewDeck();
67     fakeDeck.Shuffle();
68 
69     /* Reset progress value */
70     cardsFinished = 0;
71 
72     /* Deal to each stack */
73     for (i = 0; i < NUM_STACKS; i++)
74     {
75         temp.Clear();
76         if (i == NUM_SMALLER_STACKS)
77         {
78             covCards--;
79         }
80         for (j = 0; j <= covCards; j++)
81         {
82             temp.Push(deck.Pop(1));
83         }
84         pStack[i]->SetFaceDirection(CS_FACE_DOWNUP, covCards);
85         pStack[i]->SetCardStack(temp);
86     }
87     /* Deal five fake cards to the deck */
88     pDeck->SetCardStack(fakeDeck.Pop(5));
89 
90     SpiderWnd.Redraw();
91     fGameStarted = false;
92 }
93 
94 bool stackLookingGood(const CardStack &mystack, int numChecks)
95 {
96     int i;
97     for (i = 0; i < numChecks; i++)
98     {
99         if (mystack[i].LoVal() != mystack[i + 1].LoVal() - 1)
100         {
101             return false;
102         }
103         if (mystack[i].Suit() != mystack[i + 1].Suit())
104         {
105             return false;
106         }
107     }
108     return true;
109 }
110 
111 /* Card to be turned from a stack */
112 void TurnStackCard(CardRegion &stackobj)
113 {
114     int numfacedown;
115 
116     stackobj.GetFaceDirection(&numfacedown);
117     if (stackobj.NumCards() <= numfacedown)
118     {
119         if (numfacedown > 0) numfacedown--;
120         stackobj.SetFaceDirection(CS_FACE_DOWNUP, numfacedown);
121         stackobj.Redraw();
122     }
123 }
124 
125 /* Click on the deck */
126 void CARDLIBPROC DeckClickProc(CardRegion &stackobj, int NumDragCards)
127 {
128     CardStack temp, fakeDeck = pDeck->GetCardStack();
129     fGameStarted = true;
130 
131     if (fakeDeck.NumCards() != 0 && deck.NumCards() != 0)
132     {
133         int i, facedown, faceup;
134         /* Add one card to every stack */
135         for (i = 0; i < NUM_STACKS; i++)
136         {
137             temp = pStack[i]->GetCardStack();
138             temp.Push(deck.Pop());
139 
140             /* Check if we accidentally finished a row */
141             pStack[i]->GetFaceDirection(&facedown);
142             faceup = temp.NumCards() - facedown;
143             if (faceup >= NUM_ONECOLOR_CARDS)
144             {
145                 /* Check stack finished, remove cards if so */
146                 if (stackLookingGood(temp, NUM_ONECOLOR_CARDS - 1))
147                 {
148                     int j;
149                     for (j = 0; j < NUM_ONECOLOR_CARDS; j++)
150                     {
151                         temp.RemoveCard(0);
152                     }
153                     cardsFinished += NUM_ONECOLOR_CARDS;
154                     pStack[i]->SetCardStack(temp);
155                     /* Turn now topmost card */
156                     TurnStackCard(*pStack[i]);
157                 }
158             }
159             pStack[i]->SetCardStack(temp);
160         }
161         /* Remove one card from the fake ones */
162         pDeck->SetCardStack(fakeDeck.Pop(fakeDeck.NumCards() - 1));
163     }
164     pDeck->Update();
165     SpiderWnd.Redraw();
166 }
167 
168 /* Cards dragged from a stack */
169 bool CARDLIBPROC StackDragProc(CardRegion &stackobj, int numDragCards)
170 {
171     int numfacedown, numcards;
172 
173     stackobj.GetFaceDirection(&numfacedown);
174     numcards = stackobj.NumCards();
175 
176     /* Only cards facing up */
177     if (numDragCards <= numcards - numfacedown)
178     {
179         const CardStack &mystack = stackobj.GetCardStack();
180         /* Don't allow to drag unsuited cards */
181         if (!stackLookingGood(mystack, numDragCards - 1))
182         {
183             return false;
184         }
185         /* Remember where the cards come from */
186         from = &stackobj;
187         return true;
188     }
189     else
190     {
191         return false;
192     }
193 }
194 
195 /* Game finished successfully */
196 void GameFinished()
197 {
198     SpiderWnd.EmptyStacks();
199 
200     MessageBox(SpiderWnd, MsgWin, szAppName, MB_OK | MB_ICONINFORMATION);
201     if( IDYES == MessageBox(SpiderWnd, MsgDeal, szAppName, MB_YESNO | MB_ICONQUESTION) )
202     {
203         NewGame();
204     }
205     else
206     {
207         fGameStarted = false;
208     }
209 }
210 
211 /* Card added, check for win situation */
212 void CARDLIBPROC StackAddProc(CardRegion &stackobj, const CardStack &added)
213 {
214     if (cardsFinished == NUM_SPIDER_CARDS)
215     {
216         GameFinished();
217     }
218 }
219 
220 /* Cards dropped to a stack */
221 bool CARDLIBPROC StackDropProc(CardRegion &stackobj, CardStack &dragcards)
222 {
223     Card dragcard = dragcards[dragcards.NumCards() - 1];
224     int faceup, facedown;
225 
226     /* Only drop our cards on other stacks */
227     if (stackobj.Id() == from->Id())
228     {
229         return false;
230     }
231 
232     /* If stack is empty, everything can be dropped */
233     if (stackobj.NumCards() != 0)
234     {
235         const CardStack &mystack = stackobj.GetCardStack();
236 
237         /* Can only drop if card is 1 less */
238         if (mystack[0].LoVal() != dragcard.LoVal() + 1)
239         {
240             return false;
241         }
242 
243         /* Check if stack complete */
244         stackobj.GetFaceDirection(&facedown);
245         faceup = stackobj.NumCards() - facedown;
246 
247         if (faceup + dragcards.NumCards() >= NUM_ONECOLOR_CARDS)
248         {
249             int i, max = NUM_ONECOLOR_CARDS - dragcards.NumCards() - 1;
250 
251             /* Dragged cards have been checked to be in order, check stack cards */
252             if (mystack[0].Suit() == dragcard.Suit() &&
253                 stackLookingGood(mystack, max))
254             {
255                 CardStack s = stackobj.GetCardStack();
256                 CardStack f;
257 
258                 /* Remove from card stack */
259                 for (i = 0; i < max + 1; i++)
260                 {
261                     s.RemoveCard(0);
262                 }
263                 /* Remove dragged cards */
264                 dragcards = f;
265                 stackobj.SetCardStack(s);
266                 cardsFinished += NUM_ONECOLOR_CARDS;
267                 /* Flip top card of the dest stack */
268                 TurnStackCard(stackobj);
269             }
270         }
271     }
272     /* Flip the top card of the source stack */
273     TurnStackCard(*from);
274     fGameStarted = true;
275     return true;
276 }
277 
278 /* Create card regions */
279 void CreateSpider()
280 {
281     int i, pos;
282 
283     /* Compute the value for yRowStackCardOffset based on the height of the card, so the card number and suite isn't hidden on larger cards except Ace */
284     yRowStackCardOffset = (int)(__cardheight / 6.4);
285 
286     pDeck = SpiderWnd.CreateRegion(0, true, 0, 0, -15, 0);
287     pDeck->SetFaceDirection(CS_FACE_DOWN, 0);
288     pDeck->SetEmptyImage(CS_EI_CIRC);
289     pDeck->SetPlacement(CS_XJUST_RIGHT, CS_YJUST_BOTTOM, - X_BORDER, - Y_BORDER);
290     pDeck->SetDragRule(CS_DRAG_NONE, 0);
291     pDeck->SetDropRule(CS_DROP_NONE, 0);
292     pDeck->SetClickReleaseProc(DeckClickProc);
293 
294     /* Create the row stacks */
295     for (i = 0; i < NUM_STACKS; i++)
296     {
297         pStack[i] = SpiderWnd.CreateRegion(NUM_STACKS+i, true, 0, Y_BORDER, 0, yRowStackCardOffset);
298         pStack[i]->SetFaceDirection(CS_FACE_DOWN, 0);
299         pos = i - NUM_STACKS/2;
300         pStack[i]->SetPlacement(CS_XJUST_CENTER, 0,
301             pos * (__cardwidth + X_BORDER) + 6 * (X_BORDER + 1) + 3, 0);
302         pStack[i]->SetEmptyImage(CS_EI_SUNK);
303         pStack[i]->SetDragRule(CS_DRAG_CALLBACK, StackDragProc);
304         pStack[i]->SetDropRule(CS_DROP_CALLBACK, StackDropProc);
305         pStack[i]->SetAddCardProc(StackAddProc);
306     }
307 }
308 
309