19219b8f9f4045855231d1defa732f6a5d39c344
[reactos.git] / base / applications / games / solitaire / solgame.cpp
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 }