ftp.nice.ch/pub/next/games/card/Solitaire.2.1.s.tar.gz#/Solitaire.2.1.s/Pyramid/Pyramid.m

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"

@implementation Pyramid



/*---------------------------------------------------------------------------
|
|    - startGame:
|
|    returns:    (id)  self
|
|----------------------------------------------------------------------------
|
|    Start a new game.  Get confirmation from the user before aborting a 
|    game in progress.
|			
\----------------------------------------------------------------------------*/

- startGame:sender
{
    if (gameInProgress) [self determineScore];
    [super startGame:sender];
    return [self setupGame:YES];
}


/*---------------------------------------------------------------------------
|
|    - restartGame:
|
|    returns:  (id) self
|
|----------------------------------------------------------------------------
|
|    Restart the game in progress.
|			
\----------------------------------------------------------------------------*/

- restartGame:sender
{
    if (gameInProgress) [self determineScore];
    [super restartGame:sender];
    return [self setupGame:NO];
}


/*---------------------------------------------------------------------------
|
|    - setupGame:(BOOL)redeal
|
|    returns:  (id) self
|
|----------------------------------------------------------------------------
|
|    Setup a new game.  If "redeal" is true, deal a new deck, otherwise
|    use the same cards as the previous game.
|			
\----------------------------------------------------------------------------*/

- setupGame:(BOOL)redeal
{
    int pileIndex;
    id 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 reenableFlushWindow];
    [gameWindow flushWindow];

    /*-----------------------------------------------------------------------
     * 	Initialize the stockCardPileView to have a shuffled deck.
     *---------------------------------------------------------------------*/
    [stockCardPile freeCards];
    if (redeal)
    {
        [[stockCardPile addDeck] shuffle];
	
	// make a copy of the CardPile for restart option
	if (!prevDeck)
	{
	    prevDeck = [[CardPile allocFromZone:[self zone]]
	                           initForCardSize:cardSize];
	}
	else
	{
	    [prevDeck freeCards];
	    [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 freeCards] addDeck] shuffle];
	}
    }

    /*-----------------------------------------------------------------------
     * Initialize the other piles as empty.
     *---------------------------------------------------------------------*/

    [[wasteCardPileView cardPile] freeCards];
    [[discardCardPileViewL cardPile] freeCards];
    [[discardCardPileViewR cardPile] freeCards];
    [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];
    }

    /*-----------------------------------------------------------------------
     * 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++)
    {
        id userPile, 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 freeCards];
	tempCard = [stockCardPile cardAt:CS_TOP];
	[stockCardPile removeCard:tempCard];
	[userPile insertCard:tempCard at:CS_TOP];
	[[userPile cardAt:CS_TOP] flip];
    }
    
    /*-----------------------------------------------------------------------
     * Update all views, then display the window.
     *---------------------------------------------------------------------*/
    [gameWindow display];
    [gameWindow makeKeyAndOrderFront:self];

    return self;
}


/*---------------------------------------------------------------------------
|
|    - endGame:sender
|
|    returns:  (id) self
|
|----------------------------------------------------------------------------
|
|    End the game in progress.  Discard the game window.
|			
\----------------------------------------------------------------------------*/

- endGame:sender
{
    if (gameInProgress) 
    {
        [self determineScore];
	gameInProgress = NO;
    }

    // close the game window
    [super endGame:sender];

    return self;
}

/*---------------------------------------------------------------------------
|
|    - win
|
|     returns: (id) self
|
|----------------------------------------------------------------------------
|
|    Called when the game has been won.  This is where you can insert fancy
|    winning routines, or just call the default (boring) routine.
|
\----------------------------------------------------------------------------*/

- win
{
    [super win];  // replace this with something wonderful
    return self;
}


/*---------------------------------------------------------------------------
|
|    - checkForWin
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    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.
|
\----------------------------------------------------------------------------*/

- checkForWin
{
    int i, j;
    int total;
    id viewList = [[gameWindow contentView] subviews];
    BOOL didMatch;
    
    total = [viewList count];

    /*-----------------------------------------------------------------------
     *
     *    Points for clearing the pyramid...
     *
     *---------------------------------------------------------------------*/
    for (j = 0, didMatch = NO; j < total && !pyramidEmpty && !didMatch; j++)
    {
        id theView = [viewList objectAt:j];
	
        for (i = 0; i < 28 && !didMatch; i++)
	{
	    if (theView == gameCardPiles[i]) 
	        didMatch = YES;
	}
    }
    if (!didMatch && !pyramidEmpty)
    {
	switch ([self dealCount])
	{
	case 1:
	    [prefs setPyramidScore:[prefs pyramidScore] + 50];
	    break;
	case 2:
	    [prefs setPyramidScore:[prefs pyramidScore] + 35];
	    break;
	case 3:
	    [prefs setPyramidScore:[prefs pyramidScore] + 20];
	    break;
	default:
	    break;
	}
	pyramidEmpty = YES;
    }


    /*-----------------------------------------------------------------------
     *
     *    Cleared everything; you win.
     *
     *---------------------------------------------------------------------*/

    if ([[discardCardPileViewL cardPile] cardCount] +
        [[discardCardPileViewR cardPile] cardCount] == 52)
    {
        [self win];
	gameInProgress = NO;
	return self;
    }
    
    /*-----------------------------------------------------------------------
     *
     *    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;
	return self;
    }
    
    gameInProgress = YES;
    return self;
}


/*---------------------------------------------------------------------------
|
|    - (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
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    Set the deal count.  This is the number of passes through the stock.
\----------------------------------------------------------------------------*/

- setDealCount:(int)count
{
    dealCount = count;
    return self;
}


/*---------------------------------------------------------------------------
|
|    - incDealCount
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    Add one to the deal count.
|
\----------------------------------------------------------------------------*/

- incDealCount
{
    dealCount++;
    return self;
}


/*---------------------------------------------------------------------------
|
|    - determineScore
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    Subtract the remaining cards from the current score.
|
\----------------------------------------------------------------------------*/

- 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];

    [prefs setPyramidScore:[prefs pyramidScore] - total];

    return self;
}


@end

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.