1 #include "solitaire.h"
2 
3 #if 1
4 #define TRACE(s)
5 #else
6 #define TRACE(s) printf("%s(%i): %s",__FILE__,__LINE__,s)
7 #endif
8 
9 extern TCHAR MsgWin[128];
10 extern TCHAR MsgDeal[128];
11 
12 CardStack activepile;
13 int VisiblePileCards;
14 int LastId;
15 bool fGameStarted = false;
16 bool bAutoroute = false;
17 
18 void NewGame(void)
19 {
20     TRACE("ENTER NewGame()\n");
21     int i, j;
22 
23     if (GetScoreMode() == SCORE_VEGAS)
24     {
25         if ((dwOptions & OPTION_KEEP_SCORE) && (dwPrevMode == SCORE_VEGAS))
26             lScore = lScore - 52;
27         else
28             lScore = -52;
29 
30         if (dwOptions & OPTION_THREE_CARDS)
31             dwWasteTreshold = 2;
32         else
33             dwWasteTreshold = 0;
34 
35     }
36     else
37     {
38         if (dwOptions & OPTION_THREE_CARDS)
39             dwWasteTreshold = 3;
40         else
41             dwWasteTreshold = 0;
42 
43         lScore = 0;
44     }
45 
46     dwTime = 0;
47     dwWasteCount = 0;
48     LastId = 0;
49 
50     SolWnd.EmptyStacks();
51 
52     //create a new card-stack
53     CardStack deck;
54     deck.NewDeck();
55     deck.Shuffle();
56     activepile.Clear();
57     VisiblePileCards = 0;
58 
59     //deal to each row stack..
60     for(i = 0; i < NUM_ROW_STACKS; i++)
61     {
62         CardStack temp;
63         temp.Clear();
64 
65         pRowStack[i]->SetFaceDirection(CS_FACE_DOWNUP, i);
66 
67         for(j = 0; j <= i; j++)
68         {
69             temp.Push(deck.Pop());
70         }
71 
72         pRowStack[i]->SetCardStack(temp);
73     }
74 
75     //put the other cards onto the deck
76     pDeck->SetCardStack(deck);
77     pDeck->Update();
78 
79     // For the 1-card-mode, all cards need to be completely overlapped
80     if(!(dwOptions & OPTION_THREE_CARDS))
81         pPile->SetOffsets(0, 0);
82 
83     SolWnd.Redraw();
84 
85     fGameStarted = false;
86 
87     dwPrevMode = GetScoreMode();
88 
89     UpdateStatusBar();
90     ClearUndo();
91 
92     TRACE("EXIT NewGame()\n");
93 
94 }
95 
96 //
97 //    Now follow the stack callback functions. This is where we
98 //  provide the game functionality and rules
99 //
100 
101 //
102 //    Can only drag face-up cards
103 //
104 bool CARDLIBPROC RowStackDragProc(CardRegion &stackobj, int iNumDragCards)
105 {
106     TRACE("ENTER RowStackDragProc()\n");
107     int numfacedown;
108     int numcards;
109 
110     SetPlayTimer();
111 
112     stackobj.GetFaceDirection(&numfacedown);
113 
114     numcards = stackobj.NumCards();
115 
116     TRACE("EXIT RowStackDragProc()\n");
117     if(iNumDragCards <= numcards-numfacedown)
118         return true;
119     else
120         return false;
121 
122 }
123 
124 //
125 //    Row a row-stack, we can only drop cards
126 //    that are lower / different colour
127 //
128 bool CARDLIBPROC RowStackDropProc(CardRegion &stackobj, CardStack &dragcards)
129 {
130     TRACE("ENTER RowStackDropProc()\n");
131     Card dragcard = dragcards[dragcards.NumCards() - 1];
132 
133     SetPlayTimer();
134 
135     //if we are empty, can only drop a stack with a King at bottom
136     if(stackobj.NumCards() == 0)
137     {
138         if(dragcard.LoVal() != 13)
139         {
140             TRACE("EXIT RowStackDropProc(false)\n");
141             return false;
142         }
143     }
144     else
145     {
146         const CardStack &mystack = stackobj.GetCardStack();
147 
148         //can only drop if card is 1 less
149         if(mystack[0].LoVal() != dragcard.LoVal() + 1)
150         {
151             TRACE("EXIT RowStackDropProc(false)\n");
152             return false;
153         }
154 
155         //can only drop if card is different colour
156         if( (mystack[0].IsBlack() && !dragcard.IsRed()) ||
157            (!mystack[0].IsBlack() &&  dragcard.IsRed()) )
158         {
159             TRACE("EXIT RowStackDropProc(false)\n");
160             return false;
161         }
162     }
163 
164     fGameStarted = true;
165 
166     SetUndo(LastId, stackobj.Id(), dragcards.NumCards(), lScore, VisiblePileCards);
167 
168     if (LastId == PILE_ID)
169     {
170         if (GetScoreMode() == SCORE_STD)
171         {
172             lScore = lScore + 5;
173         }
174     }
175     else if ((LastId >= SUIT_ID) && (LastId <= SUIT_ID + 3))
176     {
177         if (GetScoreMode() == SCORE_STD)
178         {
179             lScore = lScore >= 15 ? lScore - 15 : 0;
180         }
181         else if (GetScoreMode() == SCORE_VEGAS)
182         {
183             lScore = lScore >= -47 ? lScore - 5 : -52;
184         }
185     }
186 
187     UpdateStatusBar();
188 
189     TRACE("EXIT RowStackDropProc(true)\n");
190     return true;
191 }
192 
193 //
194 //    Can only drop a card onto a suit-stack if the
195 //  card is 1 higher, and is the same suit
196 //
197 bool CanDrop(CardRegion &stackobj, Card card)
198 {
199     TRACE("ENTER CanDrop()\n");
200     int topval;
201 
202     const CardStack &cardstack = stackobj.GetCardStack();
203 
204     SetPlayTimer();
205 
206     if(cardstack.NumCards() > 0)
207     {
208         if(card.Suit() != cardstack[0].Suit())
209         {
210             TRACE("EXIT CanDrop()\n");
211             return false;
212         }
213 
214         topval = cardstack[0].LoVal();
215     }
216     else
217     {
218         topval = 0;
219     }
220 
221     //make sure 1 higher
222     if(card.LoVal() != (topval + 1))
223     {
224         TRACE("EXIT CanDrop()\n");
225         return false;
226     }
227 
228     TRACE("EXIT CanDrop()\n");
229     return true;
230 }
231 
232 //
233 //    Can only drop a card onto suit stack if it is same suit, and 1 higher
234 //
235 bool CARDLIBPROC SuitStackDropProc(CardRegion &stackobj, CardStack &dragcards)
236 {
237     TRACE("ENTER SuitStackDropProc()\n");
238 
239     SetPlayTimer();
240 
241     //only drop 1 card at a time
242     if (!bAutoroute && dragcards.NumCards() != 1)
243     {
244         TRACE("EXIT SuitStackDropProc()\n");
245         return false;
246     }
247 
248     bool b = CanDrop(stackobj, dragcards[0]);
249     TRACE("EXIT SuitStackDropProc()\n");
250 
251     if (b)
252     {
253         SetUndo(LastId, stackobj.Id(), 1, lScore, VisiblePileCards);
254 
255         if ((LastId == PILE_ID) || (LastId >= ROW_ID))
256         {
257             if (GetScoreMode() == SCORE_VEGAS)
258             {
259                 lScore = lScore + 5;
260             }
261             else if (GetScoreMode() == SCORE_STD)
262             {
263                 lScore = lScore + 10;
264             }
265 
266             UpdateStatusBar();
267         }
268     }
269 
270     return b;
271 }
272 
273 //
274 //    Single-click on one of the suit-stacks
275 //
276 void CARDLIBPROC SuitStackClickProc(CardRegion &stackobj, int iNumClicked)
277 {
278     TRACE("ENTER SuitStackClickProc()\n");
279 
280     fGameStarted = true;
281 
282     LastId = stackobj.Id();
283 
284     TRACE("EXIT SuitStackClickProc()\n");
285 }
286 
287 //
288 //    Single-click on one of the row-stacks
289 //    Turn the top-card over if they are all face-down
290 //
291 void CARDLIBPROC RowStackClickProc(CardRegion &stackobj, int iNumClicked)
292 {
293     TRACE("ENTER RowStackClickProc()\n");
294     int numfacedown;
295 
296     stackobj.GetFaceDirection(&numfacedown);
297 
298     //if all face-down, then make top card face-up
299     if(stackobj.NumCards() == numfacedown)
300     {
301         if(numfacedown > 0) numfacedown--;
302         stackobj.SetFaceDirection(CS_FACE_DOWNUP, numfacedown);
303         stackobj.Redraw();
304 
305         if (GetScoreMode() == SCORE_STD)
306         {
307             lScore = lScore + 5;
308             UpdateStatusBar();
309         }
310         ClearUndo();
311     }
312 
313     LastId = stackobj.Id();
314 
315     fGameStarted = true;
316 
317     TRACE("EXIT RowStackClickProc()\n");
318 }
319 
320 //
321 //    Find the suit-stack that can accept the specified card
322 //
323 CardRegion *FindSuitStackFromCard(Card card)
324 {
325     TRACE("ENTER FindSuitStackFromCard()\n");
326 
327     for(int i = 0; i < 4; i++)
328     {
329         if(CanDrop(*pSuitStack[i], card))
330         {
331             TRACE("EXIT FindSuitStackFromCard()\n");
332             return pSuitStack[i];
333         }
334     }
335 
336     TRACE("EXIT FindSuitStackFromCard()\n");
337     return 0;
338 }
339 
340 //
341 //    What happens when we add a card to one of the suit stacks?
342 //  Well, nothing (it is already added), but we need to
343 //  check all four stacks (not just this one) to see if
344 //  the game has finished.
345 //
346 void CARDLIBPROC SuitStackAddProc(CardRegion &stackobj, const CardStack &added)
347 {
348     TRACE("ENTER SuitStackAddProc()\n");
349     bool fGameOver = true;
350 
351     SetPlayTimer();
352 
353     for(int i = 0; i < 4; i++)
354     {
355         if(pSuitStack[i]->NumCards() != 13)
356         {
357             fGameOver = false;
358 
359             break;
360         }
361     }
362 
363     if(fGameOver)
364     {
365         KillTimer(hwndMain, IDT_PLAYTIMER);
366         PlayTimer = 0;
367 
368         if ((dwOptions & OPTION_SHOW_TIME) && (GetScoreMode() == SCORE_STD))
369         {
370             lScore = lScore + (700000 / dwTime);
371         }
372 
373         UpdateStatusBar();
374         ClearUndo();
375 
376         MessageBox(SolWnd, MsgWin, szAppName, MB_OK | MB_ICONINFORMATION);
377 
378         for(int i = 0; i < 4; i++)
379         {
380             pSuitStack[i]->Flash(11, 100);
381         }
382 
383         if( IDYES == MessageBox(SolWnd, MsgDeal, szAppName, MB_YESNO | MB_ICONQUESTION) )
384         {
385             NewGame();
386         }
387         else
388         {
389             SolWnd.EmptyStacks();
390 
391             fGameStarted = false;
392         }
393     }
394 
395     TRACE("EXIT SuitStackAddProc()\n");
396 }
397 
398 //
399 //    Double-click on one of the row stacks
400 //    The aim is to find a suit-stack to move the
401 //  double-clicked card to.
402 //
403 void CARDLIBPROC RowStackDblClickProc(CardRegion &stackobj, int iNumClicked)
404 {
405     TRACE("ENTER RowStackDblClickProc()\n");
406 
407     SetPlayTimer();
408 
409     //can only move 1 card at a time
410     if(iNumClicked != 1)
411     {
412         TRACE("EXIT RowStackDblClickProc()\n");
413         return;
414     }
415 
416     //find a suit-stack to move the card to...
417     const CardStack &cardstack = stackobj.GetCardStack();
418     CardRegion *pDest = FindSuitStackFromCard(cardstack[0]);
419 
420     if(pDest != 0)
421     {
422         fGameStarted = true;
423         SetPlayTimer();
424 
425         //stackobj.MoveCards(pDest, 1, true);
426         //use the SimulateDrag function, because we get the
427         //AddProc callbacks called for us on the destination stacks...
428         bAutoroute = true;
429         stackobj.SimulateDrag(pDest, 1, true);
430         bAutoroute = false;
431     }
432     TRACE("EXIT RowStackDblClickProc()\n");
433 }
434 
435 //
436 //    Face-up pile single-click
437 //
438 void CARDLIBPROC PileClickProc(CardRegion &stackobj, int iNumClicked)
439 {
440     TRACE("ENTER SuitStackClickProc()\n");
441 
442     fGameStarted = true;
443 
444     LastId = stackobj.Id();
445 
446     TRACE("EXIT SuitStackClickProc()\n");
447 }
448 
449 //
450 //    Face-up pile double-click
451 //
452 void CARDLIBPROC PileDblClickProc(CardRegion &stackobj, int iNumClicked)
453 {
454     TRACE("ENTER PileDblClickProc()\n");
455 
456     SetPlayTimer();
457 
458     RowStackDblClickProc(stackobj, iNumClicked);
459     TRACE("EXIT PileDblClickProc()\n");
460 }
461 
462 //
463 //    Fix for the 3-card play when only 1 card left on the pile.
464 //
465 void FixIfOneCardLeft(void)
466 {
467     // If there is just 1 card left, then modify the
468     // stack to contain ALL the face-up cards. The effect
469     // will be, the next time a card is dragged, all the
470     // previous card-triplets will be available underneath.
471     if ((dwOptions & OPTION_THREE_CARDS) && pPile->NumCards() == 1)
472     {
473         pPile->SetOffsets(0, 0);
474         pPile->SetCardStack(activepile);
475     }
476 }
477 
478 //
479 //    What happens when a card is removed from face-up pile?
480 //
481 void CARDLIBPROC PileRemoveProc(CardRegion &stackobj, int iItems)
482 {
483     TRACE("ENTER PileRemoveProc()\n");
484 
485     SetPlayTimer();
486 
487     //modify our "virtual" pile by removing the same card
488     //that was removed from the physical card stack
489     activepile.Pop(iItems);
490     if ((dwOptions & OPTION_THREE_CARDS) && (VisiblePileCards > 1))
491     {
492         --VisiblePileCards;
493     }
494 
495     FixIfOneCardLeft();
496 
497     TRACE("EXIT PileRemoveProc()\n");
498 }
499 
500 //
501 //    Double-click on the deck
502 //    Move 3 cards to the face-up pile
503 //
504 void CARDLIBPROC DeckClickProc(CardRegion &stackobj, int iNumClicked)
505 {
506     TRACE("ENTER DeckClickProc()\n");
507 
508     SetPlayTimer();
509 
510     CardStack cardstack = stackobj.GetCardStack();
511     CardStack pile      = pPile->GetCardStack();
512 
513     fGameStarted = true;
514     SetPlayTimer();
515 
516     //reset the face-up pile to represent 3 cards
517     if(dwOptions & OPTION_THREE_CARDS)
518         pPile->SetOffsets(CS_DEFXOFF, 1);
519 
520     if(cardstack.NumCards() == 0)
521     {
522         if (GetScoreMode() == SCORE_VEGAS)
523         {
524             if (dwWasteCount < dwWasteTreshold)
525             {
526                 pile.Clear();
527 
528                 activepile.Reverse();
529                 cardstack.Push(activepile);
530                 activepile.Clear();
531                 SetUndo(PILE_ID, DECK_ID, cardstack.NumCards(), lScore, VisiblePileCards);
532                 VisiblePileCards = 0;
533             }
534         }
535         else if (GetScoreMode() == SCORE_STD)
536         {
537             SetUndo(PILE_ID, DECK_ID, activepile.NumCards(), lScore, VisiblePileCards);
538             if ((dwWasteCount >= dwWasteTreshold) && (activepile.NumCards() != 0))
539             {
540                 if (dwOptions & OPTION_THREE_CARDS)
541                     lScore = lScore >= 20 ? lScore - 20 : 0;
542                 else
543                     lScore = lScore >= 100 ? lScore - 100 : 0;
544             }
545 
546             pile.Clear();
547 
548             activepile.Reverse();
549             cardstack.Push(activepile);
550             activepile.Clear();
551             VisiblePileCards = 0;
552 
553             UpdateStatusBar();
554         }
555         else
556         {
557             pile.Clear();
558 
559             activepile.Reverse();
560             cardstack.Push(activepile);
561             activepile.Clear();
562             SetUndo(PILE_ID, DECK_ID, cardstack.NumCards(), lScore, VisiblePileCards);
563             VisiblePileCards = 0;
564         }
565 
566         dwWasteCount++;
567     }
568     else
569     {
570         int numcards = min((dwOptions & OPTION_THREE_CARDS) ? 3 : 1, cardstack.NumCards());
571 
572         SetUndo(DECK_ID, PILE_ID, numcards, lScore, VisiblePileCards);
573 
574         //make a "visible" copy of these cards
575         CardStack temp;
576         temp = cardstack.Pop(numcards);
577         temp.Reverse();
578 
579         if(dwOptions & OPTION_THREE_CARDS)
580             pile.Clear();
581 
582         pile.Push(temp);
583 
584         //remove the top 3 from deck
585         activepile.Push(temp);
586 
587         VisiblePileCards = numcards;
588     }
589 
590     activepile.Print();
591 
592     pDeck->SetCardStack(cardstack);
593     pPile->SetCardStack(pile);
594 
595     FixIfOneCardLeft();
596 
597     SolWnd.Redraw();
598     TRACE("EXIT DeckClickProc()\n");
599 }
600