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