This is Pyramid.m in view mode; [Download] [Up]
/* indent:4 tabsize:8 font:fixed-width */
#import "Pyramid.h"
#import "PyramidPrefs.h"
#import "DiscardCardPileDelegate.h"
#import "GameCardPileDelegate.h"
#import "StockCardPileDelegate.h"
#import "WasteCardPileDelegate.h"
#import "localstrings.h"
static id _pyramidSharedInstance = nil;
@implementation Pyramid
+ (Pyramid*) sharedInstance
/*"
Instead of the old method of calling SolGameController when
you wanted to message the Pyramid game module, you now
call [Pyramid sharedInstance] and direct messages to
the returned instance. You can use this to check if the game
has been won, among other things.
"*/
{
return _pyramidSharedInstance;
}
- initFromBundle:(NSBundle*)aBundle withName:(NSString*)name
/*"
Extends our superclass method in order to set our shared
instance variable.
"*/
{
[super initFromBundle:aBundle withName:name];
// This is used instead of having a global SolGameController()
// function. It is assumed that this method will be called
// only once and that our +sharedInstance method will return
// the single instance.
_pyramidSharedInstance = self;
return self;
}
/*---------------------------------------------------------------------------
|
| - startGame:
|
|----------------------------------------------------------------------------
|
| Start a new game. Get confirmation from the user before aborting a
| game in progress.
|
\----------------------------------------------------------------------------*/
- (void) startGame:sender
{
if (gameInProgress) [self determineScore];
[super startGame:sender];
[self setupGame:YES];
}
/*---------------------------------------------------------------------------
|
| - restartGame:
|
|----------------------------------------------------------------------------
|
| Restart the game in progress.
|
\----------------------------------------------------------------------------*/
- (void) restartGame:sender
{
if (gameInProgress) [self determineScore];
[super restartGame:sender];
[self setupGame:NO];
}
/*---------------------------------------------------------------------------
|
| - setupGame:(BOOL)redeal
|
|----------------------------------------------------------------------------
|
| Setup a new game. If "redeal" is true, deal a new deck, otherwise
| use the same cards as the previous game.
|
\----------------------------------------------------------------------------*/
- (void) setupGame:(BOOL)redeal
{
int pileIndex;
CardPile* stockCardPile = [stockCardPileView cardPile];
[gameWindow disableFlushWindow];
[self setDealCount:1];
pyramidEmpty = NO;
//----------------------------------------------------------------
// Sync pile id's with current game window and set
// preferences and delegates
//----------------------------------------------------------------
gameCardPiles[0] = gamePileView1;
gameCardPiles[1] = gamePileView2;
gameCardPiles[2] = gamePileView3;
gameCardPiles[3] = gamePileView4;
gameCardPiles[4] = gamePileView5;
gameCardPiles[5] = gamePileView6;
gameCardPiles[6] = gamePileView7;
gameCardPiles[7] = gamePileView8;
gameCardPiles[8] = gamePileView9;
gameCardPiles[9] = gamePileView10;
gameCardPiles[10] = gamePileView11;
gameCardPiles[11] = gamePileView12;
gameCardPiles[12] = gamePileView13;
gameCardPiles[13] = gamePileView14;
gameCardPiles[14] = gamePileView15;
gameCardPiles[15] = gamePileView16;
gameCardPiles[16] = gamePileView17;
gameCardPiles[17] = gamePileView18;
gameCardPiles[18] = gamePileView19;
gameCardPiles[19] = gamePileView20;
gameCardPiles[20] = gamePileView21;
gameCardPiles[21] = gamePileView22;
gameCardPiles[22] = gamePileView23;
gameCardPiles[23] = gamePileView24;
gameCardPiles[24] = gamePileView25;
gameCardPiles[25] = gamePileView26;
gameCardPiles[26] = gamePileView27;
gameCardPiles[27] = gamePileView28;
[stockCardPileView setBackgroundColor:desktopColor];
[stockCardPileView setCardSize:cardSize];
[stockCardPileView setDelegate:stockDelegate];
[wasteCardPileView setBackgroundColor:desktopColor];
[wasteCardPileView setCardSize:cardSize];
[wasteCardPileView setDelegate:wasteDelegate];
[discardCardPileViewL setBackgroundColor:desktopColor];
[discardCardPileViewL setCardSize:cardSize];
[discardCardPileViewL setDelegate:discardDelegate];
[discardCardPileViewR setBackgroundColor:desktopColor];
[discardCardPileViewR setCardSize:cardSize];
[discardCardPileViewR setDelegate:discardDelegate];
[stockDelegate setDiscardLeft:discardCardPileViewL discardRight:discardCardPileViewR waste:wasteCardPileView];
[gameDelegate setDiscardLeft:discardCardPileViewL discardRight:discardCardPileViewR];
[wasteDelegate setDiscardLeft:discardCardPileViewL discardRight:discardCardPileViewR];
[gameWindow display];
[gameWindow enableFlushWindow];
[gameWindow flushWindow];
/*-----------------------------------------------------------------------
* Initialize the stockCardPileView to have a shuffled deck.
*---------------------------------------------------------------------*/
[stockCardPile empty];
if (redeal)
{
[stockCardPile addDeck];
[stockCardPile shuffle];
// make a copy of the CardPile for restart option
if (!prevDeck)
{
prevDeck = [[CardPile allocWithZone:[self zone]]
initForCardSize:cardSize];
}
else
{
[prevDeck empty];
[prevDeck setCardSize:cardSize];
}
[prevDeck addCopyOfPile:stockCardPile];
}
else
{
if (prevDeck)
{
// copy the saved deck back to the game deck
[prevDeck setCardSize:cardSize];
[stockCardPile addCopyOfPile:prevDeck];
}
else
{
// this shouldn't happen, but just in case...
[stockCardPile empty];
[stockCardPile addDeck];
[stockCardPile shuffle];
}
}
/*-----------------------------------------------------------------------
* Initialize the other piles as empty.
*---------------------------------------------------------------------*/
[[wasteCardPileView cardPile] empty];
[[discardCardPileViewL cardPile] empty];
[[discardCardPileViewR cardPile] empty];
[wasteCardPileView display];
[discardCardPileViewL display];
[discardCardPileViewR display];
/*-----------------------------------------------------------------------
* Remove the remaining gameCardPileViews from the view
* hierarchy so they can be put back in correct order.
* As cards are removed from the Pyramid during game play, the
* Pyramid cardPileViews are removed from the view hierarchy to
* allow access to the piles underneath.
*---------------------------------------------------------------------*/
for (pileIndex = 0; pileIndex < 28; pileIndex++)
{
if ([gameCardPiles[pileIndex] superview])
[gameCardPiles[pileIndex] removeFromSuperview];
// Since throughout the game we keep removing these subviews
// from their superview, we have to retain them so they don't
// get released in the middle of the game.
[gameCardPiles[pileIndex] retain];
}
/*-----------------------------------------------------------------------
* Set the attributes of the pyramid cardpiles
*---------------------------------------------------------------------*/
for (pileIndex = 0; pileIndex < 28; pileIndex++)
{
[gameCardPiles[pileIndex] setDelegate:gameDelegate];
[gameCardPiles[pileIndex] setBackgroundColor:desktopColor];
[gameCardPiles[pileIndex] setCardSize:cardSize];
[gameCardPiles[pileIndex] setDelegate:gameDelegate];
[gameCardPiles[pileIndex] setDrawOutline:NO];
[gameCardPiles[pileIndex] resetBacking:self];
if (pileIndex > 0) [gameCardPiles[pileIndex] setCoversOthers:YES];
}
/*-----------------------------------------------------------------------
* Initialize and deal cards to the 28 "game piles"
*---------------------------------------------------------------------*/
for (pileIndex = 0; pileIndex < 28; pileIndex++)
{
CardPile* userPile;
Card* tempCard;
/*-------------------------------------------------------------------
* Reinsert the game pile view in the proper hierachy.
* The views are unlinked during game play.
*-----------------------------------------------------------------*/
[[stockCardPileView superview] addSubview:gameCardPiles[pileIndex]];
/*-------------------------------------------------------------------
* Deal the cards.
*-----------------------------------------------------------------*/
userPile = [gameCardPiles[pileIndex] cardPile];
[userPile empty];
tempCard = [stockCardPile topCard];
[tempCard retain];
[stockCardPile removeCard:tempCard];
[userPile insertCard:tempCard at:CS_TOP];
[tempCard release];
[[userPile topCard] flip];
}
/*-----------------------------------------------------------------------
* Update all views, then display the window.
*---------------------------------------------------------------------*/
[gameWindow display];
[gameWindow makeKeyAndOrderFront:self];
}
/*---------------------------------------------------------------------------
|
| - endGame:sender
|
|----------------------------------------------------------------------------
|
| End the game in progress. Discard the game window.
|
\----------------------------------------------------------------------------*/
- (void) endGame:sender
{
if (gameInProgress)
{
[self determineScore];
gameInProgress = NO;
}
// close the game window
[super endGame:sender];
}
/*---------------------------------------------------------------------------
|
| - win
|
|----------------------------------------------------------------------------
|
| Called when the game has been won. This is where you can insert fancy
| winning routines, or just call the default (boring) routine.
|
\----------------------------------------------------------------------------*/
- (void) win
{
[super win]; // replace this with something wonderful
}
/*---------------------------------------------------------------------------
|
| - checkForWin
|
|----------------------------------------------------------------------------
|
| Called to check the state of the game. Always override (unless your
| game is impossible to win). This is ugly because it handles winning
| and keeping track of the score.
|
\----------------------------------------------------------------------------*/
- (void) checkForWin
{
int i, j;
int total;
NSArray *viewList = [[gameWindow contentView] subviews];
BOOL didMatch;
total = [viewList count];
/*-----------------------------------------------------------------------
*
* Points for clearing the pyramid...
*
*---------------------------------------------------------------------*/
for (j = 0, didMatch = NO; j < total && !pyramidEmpty && !didMatch; j++)
{
CardPileView* theView = [viewList objectAtIndex:j];
for (i = 0; i < 28 && !didMatch; i++)
{
if (theView == gameCardPiles[i])
didMatch = YES;
}
}
if (!didMatch && !pyramidEmpty)
{
switch ([self dealCount])
{
case 1:
[(PyramidPrefs*)prefs setPyramidScore:[(PyramidPrefs*)prefs pyramidScore] + 50];
break;
case 2:
[(PyramidPrefs*)prefs setPyramidScore:[(PyramidPrefs*)prefs pyramidScore] + 35];
break;
case 3:
[(PyramidPrefs*)prefs setPyramidScore:[(PyramidPrefs*)prefs pyramidScore] + 20];
break;
default:
break;
}
pyramidEmpty = YES;
}
/*-----------------------------------------------------------------------
*
* Cleared everything; you win.
*
*---------------------------------------------------------------------*/
if ([[discardCardPileViewL cardPile] cardCount] +
[[discardCardPileViewR cardPile] cardCount] == 52)
{
[self win];
gameInProgress = NO;
}
/*-----------------------------------------------------------------------
*
* If the stock pile is empty and we have used both redeals AND the
* pyramid is empty, then it's time to count the remaining cards.
*
*---------------------------------------------------------------------*/
if (pyramidEmpty && [[stockCardPileView cardPile] cardCount] == 0 &&
[self dealCount] == 3)
{
[self determineScore];
gameInProgress = NO;
}
gameInProgress = YES;
}
/*---------------------------------------------------------------------------
|
| - (int)dealCount
|
| returns: (int) the number of deals so far (1, 2, 3, ...)
|
|----------------------------------------------------------------------------
|
| The deal count is incremented everytime the deck is flipped.
|
\----------------------------------------------------------------------------*/
- (int) dealCount
{
return dealCount;
}
/*---------------------------------------------------------------------------
|
| - setDealCount:(int)count
|
|----------------------------------------------------------------------------
|
| Set the deal count. This is the number of passes through the stock.
\----------------------------------------------------------------------------*/
- (void) setDealCount:(int)count
{
dealCount = count;
}
/*---------------------------------------------------------------------------
|
| - incDealCount
|
|----------------------------------------------------------------------------
|
| Add one to the deal count.
|
\----------------------------------------------------------------------------*/
- (void) incDealCount
{
dealCount++;
}
/*---------------------------------------------------------------------------
|
| - determineScore
|
|----------------------------------------------------------------------------
|
| Subtract the remaining cards from the current score.
|
\----------------------------------------------------------------------------*/
- (void) determineScore
{
int i;
int total = 0;
for (i = 0; i < 28; i++)
{
total += [[gameCardPiles[i] cardPile] cardCount];
}
total += [[stockCardPileView cardPile] cardCount];
total += [[wasteCardPileView cardPile] cardCount];
[(PyramidPrefs*)prefs setPyramidScore:[(PyramidPrefs*)prefs pyramidScore] - total];
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.