diff --git a/.gitignore b/.gitignore index e43b0f9..1f14db7 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,25 @@ +# macOS .DS_Store + +# Xcode user-specific files +xcuserdata/ +*.xcuserstate +*.xcscmblueprint + +# Build products +build/ +DerivedData/ + +# Swift Package Manager +.swiftpm/ +.build/ +Package.resolved + +# Build artifacts +*.hmap +*.ipa +*.dSYM.zip +*.dSYM + +# CocoaPods (falls später benötigt) +Pods/ diff --git a/Assets.xcassets/AppIcon.appiconset/AppIcon-1024.png b/Assets.xcassets/AppIcon.appiconset/AppIcon-1024.png new file mode 100644 index 0000000..92a79db Binary files /dev/null and b/Assets.xcassets/AppIcon.appiconset/AppIcon-1024.png differ diff --git a/Assets.xcassets/AppIcon.appiconset/Contents.json b/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..f22e10c --- /dev/null +++ b/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,14 @@ +{ + "images" : [ + { + "filename" : "AppIcon-1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Assets.xcassets/Contents.json b/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/CardEngine/CECard.m b/CardEngine/CECard.m index 44232cf..6dbdacd 100644 --- a/CardEngine/CECard.m +++ b/CardEngine/CECard.m @@ -34,8 +34,8 @@ + (NSUInteger) indexWithRank: (CERank) rank andSuit: (CESuit) suit NSUInteger index = 0; // Param check. - require ((rank >= kCERankAce) && (rank <= kCERankKing), bail); - require ((suit >= kCESuitDiamonds) && (suit <= kCESuitClubs), bail); + __Require ((rank >= kCERankAce) && (rank <= kCERankKing), bail); + __Require ((suit >= kCESuitDiamonds) && (suit <= kCESuitClubs), bail); // Compute index. index = (suit * 13) + rank; @@ -52,7 +52,7 @@ + (CERank) rankFromIndex: (NSUInteger) index CERank rank = 0; // Param check. - require ((index >= 1) && (index <= 52), bail); + __Require ((index >= 1) && (index <= 52), bail); // Modulo 13. rank = ((index - 1) % 13) + 1; @@ -69,7 +69,7 @@ + (CESuit) suitFromIndex: (NSUInteger) index CESuit suit = 0; // Param check. - require ((index >= 1) && (index <= 52), bail); + __Require ((index >= 1) && (index <= 52), bail); // Divide by 13. suit = (index - 1) / 13; @@ -187,7 +187,7 @@ - (id) initWithIndex: (NSUInteger) index // Super. myself = [super init]; - require (myself, bail); + __Require (myself, bail); // Assign instance variable. if (index == 0) @@ -203,14 +203,6 @@ - (id) initWithIndex: (NSUInteger) index return myself; } -// ------------------------------------------------------------------------------------------------------------- dealloc - -- (void) dealloc -{ - // Super. - [super dealloc]; -} - // ---------------------------------------------------------------------------------------------------------------- suit - (CESuit) suit diff --git a/CardEngine/CECardDealer.h b/CardEngine/CECardDealer.h index c78a0e2..2a5a8bf 100644 --- a/CardEngine/CECardDealer.h +++ b/CardEngine/CECardDealer.h @@ -22,15 +22,15 @@ BOOL _dealing; BOOL _enableUndoGrouping; NSTimer *_dealTimer; - id _delegate; + __weak id _delegate; } -@property(nonatomic,retain,readonly) CEStackView *sourceStack; -@property(nonatomic,retain,readonly) CEStackView *destStack; +@property(nonatomic,strong,readonly) CEStackView *sourceStack; +@property(nonatomic,strong,readonly) CEStackView *destStack; @property(nonatomic) NSTimeInterval dealDuration; // Default: 0.25 seconds. @property(nonatomic) NSTimeInterval dealDelay; // Default: 0.20 seconds. @property(nonatomic) BOOL enableUndoGrouping; -@property(nonatomic,assign) id delegate; // Optional delegate. +@property(nonatomic,weak) id delegate; // Optional delegate. @property(nonatomic,readonly) BOOL dealing; - (void) dealCardsFromStackView: (CEStackView *) source toStackView: (CEStackView *) dest count: (NSUInteger) count; diff --git a/CardEngine/CECardDealer.m b/CardEngine/CECardDealer.m index 080b8f7..018faea 100644 --- a/CardEngine/CECardDealer.m +++ b/CardEngine/CECardDealer.m @@ -34,7 +34,7 @@ - (id) init // Super. myself = [super init]; - require (myself, bail); + __Require (myself, bail); // Initialize instance variables. _duration = kDefaultDealAnimationDuration; @@ -54,17 +54,10 @@ - (void) dealloc // Finish any deal in progress. if (_dealing) [self quickComplete]; - - // Release instance var. - [_sourceStack release]; - [_destStack release]; - + // Clean up timer. if (_dealTimer) [_dealTimer invalidate]; - - // Super. - [super dealloc]; } // --------------------------------------------------------------------------------------------------------- dealOneCard @@ -80,8 +73,8 @@ - (void) dealOneCard if (_enableUndoGrouping) [[CETableView sharedCardUndoManager] endUndoGrouping]; _count = 0; - [_sourceStack release]; - [_destStack release]; + _sourceStack = nil; + _destStack = nil; _dealing = NO; } else @@ -118,9 +111,9 @@ - (void) dealCardsFromStackView: (CEStackView *) source toStackView: (CEStackVie if ([source.stack topCard] == nil) return; - // Retain stacks. - _sourceStack = [source retain]; - _destStack = [dest retain]; + // Store stacks. + _sourceStack = source; + _destStack = dest; _count = count; _dealing = YES; @@ -169,8 +162,8 @@ - (void) quickComplete if (_enableUndoGrouping) [[CETableView sharedCardUndoManager] endUndoGrouping]; _count = 0; - [_sourceStack release]; - [_destStack release]; + _sourceStack = nil; + _destStack = nil; _dealing = NO; } } diff --git a/CardEngine/CECardView.h b/CardEngine/CECardView.h index 91ef822..82b87c4 100644 --- a/CardEngine/CECardView.h +++ b/CardEngine/CECardView.h @@ -30,9 +30,9 @@ enum BOOL _hasShadow; // Shadow state. } -@property(nonatomic,retain) CECard *card; // The card object backing the view. This is the card displayed. -@property(nonatomic,retain) UIColor *highlightColor; // Fill color when view is highlighted. Can be set to nil. -@property(nonatomic,retain) NSString *label; // Fill color when view is highlighted. Can be set to nil. +@property(nonatomic,strong) CECard *card; // The card object backing the view. This is the card displayed. +@property(nonatomic,strong) UIColor *highlightColor; // Fill color when view is highlighted. Can be set to nil. +@property(nonatomic,strong) NSString *label; // Fill color when view is highlighted. Can be set to nil. @property(nonatomic) BOOL highlight; // During live drag, view may highlight when stack dragged into. @property(nonatomic) BOOL hasShadow; // Whether to draw a shadow beneath card. Default is NO. diff --git a/CardEngine/CECardView.m b/CardEngine/CECardView.m index 9fbc721..dc94dcf 100644 --- a/CardEngine/CECardView.m +++ b/CardEngine/CECardView.m @@ -83,7 +83,7 @@ - (id) initWithFrame: (CGRect) frame // Super. myself = [super initWithFrame: frame]; - require (myself, bail); + __Require (myself, bail); // Initialize instance variables. _card = nil; @@ -104,18 +104,6 @@ - (id) initWithFrame: (CGRect) frame return self; } -// ------------------------------------------------------------------------------------------------------------- dealloc - -- (void) dealloc -{ - // Release instance var. - [_card release]; - [_highlightColor release]; - - // Super. - [super dealloc]; -} - // ------------------------------------------------------------------------------------------------------------ isOpaque - (BOOL) isOpaque @@ -132,9 +120,8 @@ - (void) setCard: (CECard *) card if (_card == card) return; - // Release, retain, redraw. - [_card release]; - _card = [card retain]; + // Assign, redraw. + _card = card; [self setNeedsDisplay]; } @@ -155,9 +142,8 @@ - (void) setLabel: (NSString *) label if (label == _label) return; - // Release, retain, assign. - [_label release]; - _label = [label retain]; + // Assign. + _label = label; // Redraw. [self setNeedsDisplay]; @@ -272,9 +258,8 @@ - (void) drawRect: (CGRect) rect NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init]; style.lineBreakMode = NSLineBreakByWordWrapping; style.alignment = NSTextAlignmentCenter; - [_label drawInRect: box withAttributes: [NSDictionary dictionaryWithObjectsAndKeys: font, NSFontAttributeName, + [_label drawInRect: box withAttributes: [NSDictionary dictionaryWithObjectsAndKeys: font, NSFontAttributeName, style, NSParagraphStyleAttributeName, nil]]; - [style release]; } } @@ -305,25 +290,25 @@ - (void) drawCardFace // Get image URL from bundle. bundle = CFBundleGetMainBundle (); - require (bundle, bail); + __Require (bundle, bail); base = CFBundleCopyResourcesDirectoryURL (bundle); - require (base, bail); + __Require (base, bail); url = CFURLCreateWithFileSystemPathRelativeToBase (kCFAllocatorDefault, CFSTR ("Cards.pdf"), kCFURLPOSIXPathStyle, false, base); - require (url, bail); + __Require (url, bail); // Get PDF document. gCardPDFDocument = CGPDFDocumentCreateWithURL (url); } // Must have a PDF document by now. - require (gCardPDFDocument, bail); + __Require (gCardPDFDocument, bail); // Get the page of the PDF that corresponds to our card index. cardPage = CGPDFDocumentGetPage (gCardPDFDocument, [_card index]); - require (cardPage, bail); + __Require (cardPage, bail); transform = CGPDFPageGetDrawingTransform (cardPage, kCGPDFCropBox, bounds, 0, true); @@ -372,25 +357,25 @@ - (void) drawCardBack // Get image URL from bundle. bundle = CFBundleGetMainBundle (); - require (bundle, bail); + __Require (bundle, bail); base = CFBundleCopyResourcesDirectoryURL (bundle); - require (base, bail); + __Require (base, bail); url = CFURLCreateWithFileSystemPathRelativeToBase (kCFAllocatorDefault, CFSTR ("Cards.pdf"), kCFURLPOSIXPathStyle, false, base); - require (url, bail); + __Require (url, bail); // Get PDF document. gCardPDFDocument = CGPDFDocumentCreateWithURL (url); } // Must have a PDF document by now. - require (gCardPDFDocument, bail); + __Require (gCardPDFDocument, bail); // Get the page of the PDF that corresponds to our card index. cardPage = CGPDFDocumentGetPage (gCardPDFDocument, 53); - require (cardPage, bail); + __Require (cardPage, bail); transform = CGPDFPageGetDrawingTransform (cardPage, kCGPDFCropBox, bounds, 0, true); diff --git a/CardEngine/CEStack.m b/CardEngine/CEStack.m index dbd0aa0..16d0524 100644 --- a/CardEngine/CEStack.m +++ b/CardEngine/CEStack.m @@ -17,7 +17,7 @@ @interface CardData : NSObject CECard *_card; BOOL _promised; } -@property(nonatomic,retain) CECard *card; +@property(nonatomic,strong) CECard *card; @property(nonatomic,getter=isPromised) BOOL promised; @end @@ -37,8 +37,8 @@ + (id) deckOfCards NSUInteger i; id deck; - deck = [[[CEStack alloc] init] autorelease]; - require (deck, bail); + deck = [[CEStack alloc] init]; + __Require (deck, bail); // Add deck of cards. for (i = 1; i <= 52; i++) @@ -48,7 +48,6 @@ + (id) deckOfCards // Add card. card = [[CECard alloc] initWithIndex: i]; [deck addCardWithoutNotification: card]; - [card release]; } bail: @@ -64,7 +63,7 @@ - (id) init // Super. myself = [super init]; - require (myself, bail); + __Require (myself, bail); // Initialize instance variables. _cards = nil; @@ -74,17 +73,6 @@ - (id) init return myself; } -// ------------------------------------------------------------------------------------------------------------- dealloc - -- (void) dealloc -{ - // Release instance variables. - [_cards release]; - - // Super. - [super dealloc]; -} - #pragma mark ------ card accessors // ------------------------------------------------------------------------------------------------------- numberOfCards @@ -110,8 +98,8 @@ - (CECard *) cardAtIndex: (NSUInteger) index CECard *card = nil; // NOP. - require (_cards, bail); - require (index < [_cards count], bail); + __Require (_cards, bail); + __Require (index < [_cards count], bail); // Return the object in our array at index. card = [(CardData *)[_cards objectAtIndex: index] card]; @@ -129,7 +117,7 @@ - (NSUInteger) indexForCard: (CECard *) card NSUInteger index = NSNotFound; // NOP. - require (card, bail); + __Require (card, bail); // Return the index of the object in our array. count = [self numberOfCards]; @@ -155,7 +143,7 @@ - (BOOL) stackContainsCard: (CECard *) card BOOL contains = NO; // Param check. - require (card, bail); + __Require (card, bail); // Is the card in our array? count = [self numberOfCards]; @@ -180,8 +168,8 @@ - (CECard *) topCard CECard *card = nil; // NOP. - require (_cards, bail); - require ([_cards count] > 0, bail); + __Require (_cards, bail); + __Require ([_cards count] > 0, bail); // Return the last object in our array. card = [(CardData *)[_cards lastObject] card]; @@ -199,7 +187,7 @@ - (NSArray *) cardArrayRepresentation NSUInteger count, i; // Create empty array. - array = [[[NSMutableArray alloc] initWithCapacity: 3] autorelease]; + array = [[NSMutableArray alloc] initWithCapacity: 3]; // For each card, add an NSNumber representing the value of the card to array. count = [self numberOfCards]; @@ -229,7 +217,7 @@ - (NSArray *) cardArrayRepresentation - (void) addCard: (CECard *) card { // Param check. - require (card, bail); + __Require (card, bail); // Add the card. [self addCardWithoutNotification: card]; @@ -249,8 +237,8 @@ - (void) addCardsFromStack: (CEStack *) stack inRange: (NSRange) range NSUInteger i; // Param check. - require (stack, bail); - require ([stack numberOfCards] >= NSMaxRange (range), bail); + __Require (stack, bail); + __Require ([stack numberOfCards] >= NSMaxRange (range), bail); // Add the cards one at a time. for (i = range.location; i < NSMaxRange (range); i++) @@ -271,11 +259,11 @@ - (void) addAllCardsFromStack: (CEStack *) stack faceUp: (BOOL) faceUpOrDown NSUInteger count, i; // Param check. - require (stack, bail); + __Require (stack, bail); // How many cards are in stack passed in? count = [stack numberOfCards]; - require_quiet (count > 0, bail); + __Require_Quiet (count > 0, bail); // Add the cards one at a time. for (i = 0; i < count; i++) @@ -302,7 +290,7 @@ - (void) insertCard: (CECard *) card atIndex: (NSUInteger) index CardData *cardData; // Param check. - require (card, bail); + __Require (card, bail); // Create lazily. if (_cards == nil) @@ -316,7 +304,6 @@ - (void) insertCard: (CECard *) card atIndex: (NSUInteger) index cardData = [[CardData alloc] init]; [cardData setCard: card]; [_cards insertObject: cardData atIndex: index]; - [cardData release]; // Notify observers that the card stack was changed. [[NSNotificationCenter defaultCenter] postNotificationName: @"StackDidChangeCount" object: self userInfo: nil]; @@ -342,7 +329,6 @@ - (void) addDeckWithRandomTransform: (BOOL) random if (random) [card randomizeTransform]; [self addCardWithoutNotification: card]; - [card release]; } // Notify observers that the card stack was changed. @@ -360,11 +346,11 @@ - (void) addCardsFromCardArrayRepresentation: (NSArray *) array randomize: (BOOL NSUInteger count, i; // Param check. - require (array, bail); + __Require (array, bail); // See if there are any cards here. count = [array count]; - require_quiet (count > 0, bail); + __Require_Quiet (count > 0, bail); // For each card, add an NSNumber representing the value of the card to array. for (i = 0; i < count; i++) @@ -379,14 +365,13 @@ - (void) addCardsFromCardArrayRepresentation: (NSArray *) array randomize: (BOOL card = [[CECard alloc] initWithIndex: cardIndex & kCardIndexMask]; if ((cardIndex & kCardIsFaceUpFlag) == kCardIsFaceUpFlag) [card setFaceUp: YES]; - + // BUG: Randomizing the transform needs to be done *before* the card is added to the stack for some reason. if (randomize) [card randomizeTransform]; - + // Add card. [self addCardWithoutNotification: card]; - [card release]; } // Notify observers that the card stack was changed. @@ -405,8 +390,8 @@ - (void) removeCard: (CECard *) card NSUInteger index; // Param check. - require (card, bail); - require (_cards, bail); + __Require (card, bail); + __Require (_cards, bail); // Skip out if this is not our card. if ([self stackContainsCard: card] == NO) @@ -414,7 +399,7 @@ - (void) removeCard: (CECard *) card // Get index for card we are going to remove. index = [self indexForCard: card]; - require (index != NSNotFound, bail); + __Require (index != NSNotFound, bail); // Remove card data object. [_cards removeObjectAtIndex: index]; @@ -431,8 +416,8 @@ - (void) removeCard: (CECard *) card - (void) removeCardsInRange: (NSRange) range { - require (_cards, bail); - require (NSMaxRange (range) <= [_cards count], bail); + __Require (_cards, bail); + __Require (NSMaxRange (range) <= [_cards count], bail); // Remove the card data objects. [_cards removeObjectsInRange: range]; @@ -450,8 +435,8 @@ - (void) removeCardsInRange: (NSRange) range - (void) removeAllCards { // Param check. - require (_cards, bail); - require ([_cards count] > 0, bail); + __Require (_cards, bail); + __Require ([_cards count] > 0, bail); // Remove all cards (empty the array). [_cards removeAllObjects]; @@ -470,8 +455,8 @@ - (void) removeAllCards - (void) flipCard: (CECard *) card faceUp: (BOOL) faceUpOrDown { // Param check. - require (card, bail); - require (_cards, bail); + __Require (card, bail); + __Require (_cards, bail); // Skip out if this is not our card. if ([self stackContainsCard: card] == NO) @@ -502,11 +487,11 @@ - (void) revealAllCards BOOL flippedACard = NO; // Param check. - require (_cards, bail); + __Require (_cards, bail); // How many cards? count = [_cards count]; - require (count > 0, bail); + __Require (count > 0, bail); // Set face up. for (i = 0; i < count; i++) @@ -568,7 +553,7 @@ - (void) shuffleWithSeed: (NSUInteger) seed NSUInteger count, i; // NOP. - require (_cards, bail); + __Require (_cards, bail); // Seed the psuedo-random number generator. _seedUsed = seed; @@ -576,7 +561,7 @@ - (void) shuffleWithSeed: (NSUInteger) seed // Swap each card with a random card. count = [_cards count]; - require (count > 1, bail); + __Require (count > 1, bail); // New shuffle algorithm: Fisher and Yates (also called the Knuth Shuffle). for (i = count - 1; i > 0; i--) @@ -617,14 +602,14 @@ - (void) shuffleDeckWithSeed: (NSUInteger) seed CEStack *tempDeck; // NOP. - require (_cards, bail); + __Require (_cards, bail); // Swap each card with a random card. count = [_cards count]; wLeft = count; // A current restriction is that the stack has exactly 52 cards. - require (count == 52, bail); + __Require (count == 52, bail); // Random seed. winSRand ((unsigned) seed); @@ -668,7 +653,7 @@ - (void) addCardWithoutNotification: (CECard *) card CardData *cardData; // Param check. - require (card, bail); + __Require (card, bail); // Create lazily. if (_cards == nil) @@ -678,10 +663,9 @@ - (void) addCardWithoutNotification: (CECard *) card cardData = [[CardData alloc] init]; [cardData setCard: card]; [_cards addObject: cardData]; - [cardData release]; - + bail: - + return; } @@ -692,7 +676,7 @@ - (void) promiseCard: (CECard *) card CardData *cardData; // Param check. - require (card, bail); + __Require (card, bail); // Create lazily. if (_cards == nil) @@ -703,7 +687,6 @@ - (void) promiseCard: (CECard *) card [cardData setCard: card]; [cardData setPromised: YES]; [_cards addObject: cardData]; - [cardData release]; _cardPromised = YES; @@ -720,7 +703,7 @@ - (void) promiseKeptForCard: (CECard *) card // Get the card index. index = [self indexForCard: card]; - require (index != NSNotFound, bail); + __Require (index != NSNotFound, bail); // Clear promised flag. [[_cards objectAtIndex: index] setPromised: NO]; diff --git a/CardEngine/CEStackView.h b/CardEngine/CEStackView.h index 0455504..b595051 100644 --- a/CardEngine/CEStackView.h +++ b/CardEngine/CEStackView.h @@ -64,7 +64,7 @@ enum NSInteger _highlightedViewIndex; // If our own stack is highlighted, the card view highlighted. CEStackDragPermissions _dragPermissions; // Simple drag permissions. More complex rules done via delegate. BOOL _allowsReordering; // Whether cards within a stack can be re-ordered by the user. - id _delegate; + __weak id _delegate; id _privateDelegate; int _touchState; // State of the touch event. NSUInteger _cardIndexRevealed; // Index of the card that is being revealed. @@ -81,23 +81,23 @@ enum BOOL _enableUndoGrouping; } -@property(nonatomic,retain) CEStack *stack; // The card stack associated with the view. +@property(nonatomic,strong) CEStack *stack; // The card stack associated with the view. @property(nonatomic) CEStackViewLayout layout; // This property determines the layout of the cards displayed. @property(nonatomic) CECardSize cardSize; // Specifies the size of PlayingCardView created. @property(nonatomic) CEStackDragPermissions dragPermissions; // Indicates if cards can be dragged to or from stack. @property(nonatomic) BOOL allowsReordering; // Enables reordering of cards by user. Default = NO. -@property(nonatomic,retain) UIColor *borderColor; // Can be nil to turn off border. -@property(nonatomic,retain) UIColor *fillColor; // Can be nil to turn off rounded-rect fill. -@property(nonatomic,retain) UIColor *highlightColor; // Can be nil to turn off highlighting (not recommended). -@property(nonatomic,retain) NSString *label; // Can be nil to turn off label. -@property(nonatomic,retain) UIFont *labelFont; // Can be nil to turn off label. -@property(nonatomic,retain) UIColor *labelColor; // Can be nil to turn off label. +@property(nonatomic,strong) UIColor *borderColor; // Can be nil to turn off border. +@property(nonatomic,strong) UIColor *fillColor; // Can be nil to turn off rounded-rect fill. +@property(nonatomic,strong) UIColor *highlightColor; // Can be nil to turn off highlighting (not recommended). +@property(nonatomic,strong) NSString *label; // Can be nil to turn off label. +@property(nonatomic,strong) UIFont *labelFont; // Can be nil to turn off label. +@property(nonatomic,strong) UIColor *labelColor; // Can be nil to turn off label. @property(nonatomic) CGFloat cornerRadius; // Set to -1 for automatic. Default is automatic. @property(nonatomic) BOOL displaysCount; // Display count of cards (only for kCEStackViewLayoutStacked). Default = NO. @property(nonatomic) BOOL highlight; // During a live drag, view highlights when dragged into and allowed. -@property(nonatomic,assign) id delegate; // Optional delegate for the view. -@property(nonatomic,retain) NSString *identifier; // An identifier you can choose to associate a StackView with. -@property(nonatomic,retain) NSString *archiveIdentifier; // An identifier used to archive the state of a stack. Default is nil. +@property(nonatomic,weak) id delegate; // Optional delegate for the view. +@property(nonatomic,strong) NSString *identifier; // An identifier you can choose to associate a StackView with. +@property(nonatomic,strong) NSString *archiveIdentifier; // An identifier used to archive the state of a stack. Default is nil. @property(nonatomic,getter=isOrderly) BOOL orderly; // If not orderly cards may have a random rotation or offset applied. @property(nonatomic) BOOL enableUndoGrouping; // Whether to wrap moving multiple cards in an undo group. Default = YES. @property(nonatomic,readonly) BOOL animationInProgress; // Returns YES if there is an animation in progress. diff --git a/CardEngine/CEStackView.m b/CardEngine/CEStackView.m index aeea8c8..b1a8496 100644 --- a/CardEngine/CEStackView.m +++ b/CardEngine/CEStackView.m @@ -58,13 +58,13 @@ - (id) initWithFrame: (CGRect) frame // Super. myself = [super initWithFrame: frame]; - require (myself, bail); + __Require (myself, bail); // Default instance variable values. _borderColor = [[UIColor alloc] initWithRed: 1. green: 1. blue: 1. alpha: 0.5]; _fillColor = [[UIColor alloc] initWithRed: 0.3 green: 0.8 blue: 0.3 alpha: 0.5]; _highlightColor = [[UIColor alloc] initWithRed: 0.0 green: 0.0 blue: 0.0 alpha: 0.25]; - _labelFont = [[UIFont fontWithName: @"Arial" size: 32] retain]; + _labelFont = [UIFont fontWithName: @"Arial" size: 32]; _labelColor = [[UIColor alloc] initWithRed: 0.07 green: 0.34 blue: 0.10 alpha: 1.]; _layout = kCEStackViewLayoutStandardSpread; _cornerRadius = -1.0; @@ -83,7 +83,7 @@ - (id) initWithFrame: (CGRect) frame self.exclusiveTouch = YES; // <--- EXPERIMENT. // Add empty stack by default. - [self setStack: [[[CEStack alloc] init] autorelease]]; + [self setStack: [[CEStack alloc] init]]; bail: @@ -96,19 +96,6 @@ - (void) dealloc { // Stop listening for notifications. [[NSNotificationCenter defaultCenter] removeObserver: self]; - - // Release instance vars. - [_borderColor release]; - [_fillColor release]; - [_highlightColor release]; - [_label release]; - [_labelFont release]; - [_labelColor release]; - [_cardViews release]; - [_stack release]; - - // Super. - [super dealloc]; } // ------------------------------------------------------------------------------------------------------------ isOpaque @@ -147,9 +134,8 @@ - (void) setStack: (CEStack *) stack [[NSNotificationCenter defaultCenter] removeObserver: self name: @"StackDidFlipCard" object: _stack]; [[NSNotificationCenter defaultCenter] removeObserver: self name: @"StackDidChangeOrder" object: _stack]; - // Release, retain, assign. - [_stack release]; - _stack = [stack retain]; + // Assign. + _stack = stack; // Listen for changes in the stack. [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector (stackChangedCount:) @@ -221,9 +207,8 @@ - (void) setBorderColor: (UIColor *) color if (color == _borderColor) return; - // Release, retain, assign. - [_borderColor release]; - _borderColor = [color retain]; + // Assign. + _borderColor = color; // Redraw. [self setNeedsDisplay]; @@ -245,9 +230,8 @@ - (void) setFillColor: (UIColor *) color if (color == _fillColor) return; - // Release, retain, assign. - [_fillColor release]; - _fillColor = [color retain]; + // Assign. + _fillColor = color; // Redraw. [self setNeedsDisplay]; @@ -269,9 +253,8 @@ - (void) setLabel: (NSString *) label if (label == _label) return; - // Release, retain, assign. - [_label release]; - _label = [label retain]; + // Assign. + _label = label; // Redraw. [self setNeedsDisplay]; @@ -293,9 +276,8 @@ - (void) setLabelFont: (UIFont *) font if (font == _labelFont) return; - // Release, retain, assign. - [_labelFont release]; - _labelFont = [font retain]; + // Assign. + _labelFont = font; // Redraw. [self setNeedsDisplay]; @@ -317,9 +299,8 @@ - (void) setLabelColor: (UIColor *) color if (color == _labelColor) return; - // Release, retain, assign. - [_labelColor release]; - _labelColor = [color retain]; + // Assign. + _labelColor = color; // Redraw. [self setNeedsDisplay]; @@ -494,9 +475,9 @@ - (void) dealTopCardToStackView: (CEStackView *) destStack faceUp: (BOOL) faceUp - (void) dealCard: (CECard *) card toStackView: (CEStackView *) stack faceUp: (BOOL) faceUp duration: (NSTimeInterval) duration { // Param check. - require (stack, bail); - require (_stack, bail); - require ([_stack stackContainsCard: card], bail); + __Require (stack, bail); + __Require (_stack, bail); + __Require ([_stack stackContainsCard: card], bail); // Register for 'undo'. [self registerDealCard: card stackView: stack duration: duration]; @@ -509,9 +490,9 @@ - (void) dealCard: (CECard *) card toStackView: (CEStackView *) stack faceUp: (B NSUInteger destNumCards; CGRect destRect; - // Create animation dictionary. Animation completion will release dictionary. + // Create animation dictionary. dictionary = [[NSMutableDictionary alloc] initWithCapacity: 3]; - + // Add animation type key to dictionary. [dictionary setObject: @"moveCardFromPointToPoint" forKey: @"type"]; @@ -523,7 +504,7 @@ - (void) dealCard: (CECard *) card toStackView: (CEStackView *) stack faceUp: (B // Get card view that is going to animate. sourceView = [self cardViewForCard: card]; - require (sourceView, abortAnimation); + __Require (sourceView, abortAnimation); // Determine the coordinate to move from. point = sourceView.center; @@ -550,9 +531,8 @@ - (void) dealCard: (CECard *) card toStackView: (CEStackView *) stack faceUp: (B [sourceView setHidden: YES]; // Remove card from our own stack. - [card retain]; [_stack removeCard: card]; - + // Promise a card. [[stack stack] promiseCard: card]; @@ -564,13 +544,11 @@ - (void) dealCard: (CECard *) card toStackView: (CEStackView *) stack faceUp: (B abortAnimation: // Remove card from our own stack. - [card retain]; [_stack removeCard: card]; - + // Flip and add card to destination stack. [card setFaceUp: faceUp]; [[stack stack] addCard: card]; - [card release]; } // Notify observers that a card is being dragged. @@ -587,8 +565,8 @@ - (void) dealCard: (CECard *) card toStackView: (CEStackView *) stack faceUp: (B - (void) flipCard: (CECard *) card faceUp: (BOOL) faceUp duration: (NSTimeInterval) duration { // Param check. - require (card, bail); - require (_stack, bail); + __Require (card, bail); + __Require (_stack, bail); // Skip out if this is not our card. if ([_stack stackContainsCard: card] == NO) @@ -608,9 +586,9 @@ - (void) flipCard: (CECard *) card faceUp: (BOOL) faceUp duration: (NSTimeInterv CECardView *sourceView; CGRect frame; - // Create animation dictionary. Animation completion will release dictionary. + // Create animation dictionary. dictionary = [[NSMutableDictionary alloc] initWithCapacity: 3]; - + // Add animation type key to dictionary. [dictionary setObject: @"flipCard" forKey: @"type"]; @@ -621,7 +599,7 @@ - (void) flipCard: (CECard *) card faceUp: (BOOL) faceUp duration: (NSTimeInterv // Get the bounds for the flipping card. sourceView = [self cardViewForCard: card]; - require (sourceView, abortAnimation); + __Require (sourceView, abortAnimation); frame = [self convertRect: [sourceView frame] toView: [self superview]]; // Add frame to animation dictionary. @@ -703,9 +681,8 @@ - (void) drawRect: (CGRect) rect NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init]; style.lineBreakMode = NSLineBreakByTruncatingMiddle; - [_label drawAtPoint: origin withAttributes: [NSDictionary dictionaryWithObjectsAndKeys: _labelFont, NSFontAttributeName, + [_label drawAtPoint: origin withAttributes: [NSDictionary dictionaryWithObjectsAndKeys: _labelFont, NSFontAttributeName, style, NSParagraphStyleAttributeName, nil]]; - [style release]; } // Draw border. @@ -1298,7 +1275,6 @@ - (void) createDraggedCardViews: (CGPoint) offset // Add to our array. [_draggedCardViews addObject: draggedCard]; - [draggedCard release]; } bail: @@ -1326,96 +1302,82 @@ - (void) dragDraggedCardViews: (CGPoint) offset - (void) returnDraggedCardsWithAnimation { - NSUInteger i; CECard *card = nil; CETableView *cardTable; - + // Indicate an animation is in progress. _animationRefCount += 1; - - [UIView beginAnimations: nil context: nil]; - [UIView setAnimationDelegate: self]; - [UIView setAnimationDidStopSelector: @selector (draggedCardsReturned:finished:context:)]; - [UIView setAnimationDuration: kCardMoveAnimationSeconds]; - - // Loop over dragged cards - return to original location. - for (i = _cardRangeDragged.location; i < NSMaxRange (_cardRangeDragged); i++) - { - CGRect frame; - - // Get frame from card-view touched - adjust position. - frame = [[self cardViewForCardIndex: i] frame]; - frame = [self convertRect: frame toView: [self superview]]; - - // Sanity check. - if ([_draggedCardViews count] > (i - _cardRangeDragged.location)) + + // Pick first card as representational of stack (for delegate calls below). + if (_cardRangeDragged.length > 0) + card = [_stack cardAtIndex: _cardRangeDragged.location]; + + [UIView animateWithDuration: kCardMoveAnimationSeconds animations: ^{ + // Loop over dragged cards - return to original location. + for (NSUInteger i = self->_cardRangeDragged.location; i < NSMaxRange (self->_cardRangeDragged); i++) + { + CGRect frame; + + frame = [[self cardViewForCardIndex: i] frame]; + frame = [self convertRect: frame toView: [self superview]]; + + if ([self->_draggedCardViews count] > (i - self->_cardRangeDragged.location)) + { + [[self->_draggedCardViews objectAtIndex: i - self->_cardRangeDragged.location] setFrame: frame]; + } + else + { + printf ("returnDraggedCardsWithAnimation - error, card index out range\n"); + } + } + } completion: ^(BOOL finished) { + NSUInteger i; + CECard *completionCard = nil; + CETableView *table; + + // Destroy dragged cards. + [self destroyDraggedCardViews]; + + // Reveal previously hidden cards. + for (i = self->_cardRangeDragged.location; i < NSMaxRange (self->_cardRangeDragged); i++) { - [[_draggedCardViews objectAtIndex: i - _cardRangeDragged.location] setFrame: frame]; + [[self cardViewForCardIndex: i] setHidden: NO]; + + if (completionCard == nil) + completionCard = [self->_stack cardAtIndex: i]; } - else + + // Card dragging complete. + self->_cardRangeDragged = NSMakeRange (0, 0); + + // Un-highlight. + if (self->_highlightedStack) { - printf ("returnDraggedCardsWithAnimation - error, card index out range\n"); + [self->_highlightedStack setHighlight: NO]; + self->_highlightedStack = nil; } - - // Pick first card as representational of stack (see delegate call below). - if (card == nil) - card = [_stack cardAtIndex: i]; - } - - [UIView commitAnimations]; - + + // Call delegate animation completion routine. + if ((self->_delegate) && ([self->_delegate respondsToSelector: @selector (stackView:finishedAnimatingCardMove:)])) + [self->_delegate stackView: self finishedAnimatingCardMove: completionCard]; + + // The enclosing card table keeps track of the animation count. + table = [self enclosingCETableView]; + if (table) + [table stackView: self finishedAnimatingCardMove: completionCard]; + + // Animation complete. + self->_animationRefCount -= 1; + }]; + // Call delegate animation begin routine. if ((_delegate) && ([_delegate respondsToSelector: @selector (stackView:beginAnimatingCardMove:)])) [_delegate stackView: self beginAnimatingCardMove: card]; - - // The enclosing card table keeps track of the animation count. - cardTable = [self enclosingCETableView]; - if (cardTable) - [cardTable stackView: self beginAnimatingCardMove: card]; -} - -// ------------------------------------------------------------------------------- draggedCardsReturned:finished:context -- (void) draggedCardsReturned: (NSString *) animationID finished: (NSNumber *) finished context: (void *) context -{ - NSUInteger i; - CECard *card = nil; - CETableView *cardTable; - - // Destroy dragged cards. - [self destroyDraggedCardViews]; - - // Reveal previously hidden cards. - for (i = _cardRangeDragged.location; i < NSMaxRange (_cardRangeDragged); i++) - { - [[self cardViewForCardIndex: i] setHidden: NO]; - - // Pick first card as representational of stack (see delegate call below). - if (card == nil) - card = [_stack cardAtIndex: i]; - } - - // Card dragging complete. - _cardRangeDragged = NSMakeRange (0, 0); - - // Un-highlight. - if (_highlightedStack) - { - [_highlightedStack setHighlight: NO]; - _highlightedStack = nil; - } - - // Call delegate animation completion routine. - if ((_delegate) && ([_delegate respondsToSelector: @selector (stackView:finishedAnimatingCardMove:)])) - [_delegate stackView: self finishedAnimatingCardMove: card]; - // The enclosing card table keeps track of the animation count. cardTable = [self enclosingCETableView]; if (cardTable) - [cardTable stackView: self finishedAnimatingCardMove: card]; - - // Animation complete. - _animationRefCount -= 1; + [cardTable stackView: self beginAnimatingCardMove: card]; } // --------------------------------------------------------------------------------------------- destroyDraggedCardViews @@ -1677,9 +1639,6 @@ - (void) registerDealCard: (CECard *) card stackView: (CEStackView *) stack dura // Pass the whole dictionary to the undo manager. [[CETableView sharedCardUndoManager] registerUndoWithTarget: stack selector: @selector (undoDealCard:) object: dictionary]; - - // Clean up. - [dictionary release]; } // ------------------------------------------------------------------------------------------- registerFlipCard:duration @@ -1698,9 +1657,6 @@ - (void) registerFlipCard: (CECard *) card duration: (NSTimeInterval) duration // Pass the whole dictionary to the undo manager. [[CETableView sharedCardUndoManager] registerUndoWithTarget: self selector: @selector (undoFlipCard:) object: dictionary]; - - // Clean up. - [dictionary release]; } // -------------------------------------------------------------------------------------------------------- undoDealCard @@ -1822,12 +1778,12 @@ - (BOOL) shouldRevealCardOnTouch: (UITouch *) touch NSUInteger cardIndex; // Sanity check (not needed I think). - require (_stack, bail); - require ([_stack numberOfCards] > 0, bail); + __Require (_stack, bail); + __Require ([_stack numberOfCards] > 0, bail); // Get card index for touch location. cardIndex = [self cardIndexToRevealAtLocation: [touch locationInView: self]]; - require (cardIndex != NSNotFound, bail); + __Require (cardIndex != NSNotFound, bail); // No reveal on top card. Otherwise, yes. if (cardIndex == ([_stack numberOfCards] - 1)) @@ -1912,13 +1868,13 @@ - (CECardView *) cardViewForCardIndex: (NSUInteger) index CECardView *view = nil; // Param check. - require (index != NSNotFound, bail); + __Require (index != NSNotFound, bail); // Stacked layout does not have a one-to-one correlation between views and cards. Special case. if (_layout == kCEStackViewLayoutStacked) { // Sanity check: index ought to correspond to the top card. - require ((index + 1) == [_stack numberOfCards], bail); + __Require ((index + 1) == [_stack numberOfCards], bail); // Top view is for top card. view = [_cardViews lastObject]; @@ -1926,10 +1882,10 @@ - (CECardView *) cardViewForCardIndex: (NSUInteger) index else { // Sanity check: index should be within range. - require (index < [_stack numberOfCards], bail); + __Require (index < [_stack numberOfCards], bail); // Second sanity check, index should be within range of the card views. - require (index < [_cardViews count], bail); + __Require (index < [_cardViews count], bail); // Card index and view order are one-to-one. view = [_cardViews objectAtIndex: index]; @@ -1948,7 +1904,7 @@ - (CECardView *) cardViewForCard: (CECard *) card NSUInteger count, i; // Param check. - require (card, bail); + __Require (card, bail); // Walk through card views looking for the indicated card. count = [_cardViews count]; @@ -1974,7 +1930,7 @@ - (CGRect) boundsForCardAtIndex: (NSUInteger) index forCount: (NSUInteger) count CGRect ourBounds; // Param check. - require (count > 0, bail); + __Require (count > 0, bail); // Get base card bounds. cardBounds.origin = CGPointMake (0.0, 0.0); @@ -2184,9 +2140,8 @@ - (void) layoutForStack if (!_orderly) cardView.transform = cardView.card.transform; [_cardViews addObject: cardView]; - [cardView release]; } - + // Create the top playing card view. frame = [self boundsForCardAtIndex: count - 1 forCount: count]; cardView = [[[self cardViewClass] alloc] initWithFrame: frame]; @@ -2198,8 +2153,7 @@ - (void) layoutForStack else [cardView setLabel: nil]; [_cardViews addObject: cardView]; - [cardView release]; - + // stack, simple bounding rect. _cardBoundingRect = frame; } @@ -2229,8 +2183,7 @@ - (void) layoutForSpread if (!_orderly) cardView.transform = cardView.card.transform; [_cardViews addObject: cardView]; - [cardView release]; - + // Get the union of all card bounds. _cardBoundingRect = CGRectUnion (_cardBoundingRect, frame); } @@ -2261,8 +2214,7 @@ - (void) layoutForColumn if (!_orderly) cardView.transform = cardView.card.transform; [_cardViews addObject: cardView]; - [cardView release]; - + // Get the union of all card bounds. _cardBoundingRect = CGRectUnion (_cardBoundingRect, frame); } @@ -2330,8 +2282,7 @@ - (void) handleAnimation: (NSMutableDictionary *) dictionary [tempView setCard: tempCard]; if (!_orderly) tempView.transform = tempCard.transform; - [tempCard release]; - + // Set card image for view. UIGraphicsBeginImageContext (frame.size); if (cardIsFaceUp) @@ -2340,7 +2291,7 @@ - (void) handleAnimation: (NSMutableDictionary *) dictionary [tempView drawCardBack]; [imageView setImage: UIGraphicsGetImageFromCurrentImageContext ()]; UIGraphicsEndImageContext (); - + // If the card is to flip, create a secondary image view for the card. if (willFlip) { @@ -2389,14 +2340,10 @@ - (void) handleAnimation: (NSMutableDictionary *) dictionary } [dictionary setObject: imageView forKey: @"view"]; - + // Set animation curve. [dictionary setObject: [NSNumber numberWithInt: UIViewAnimationCurveEaseIn] forKey: @"curve"]; - - // Done with temporary views. - [tempView release]; - [imageView release]; - + // Call delegate animation begin routine. if ((_delegate) && ([_delegate respondsToSelector: @selector (stackView:beginAnimatingCardMove:)])) [_delegate stackView: self beginAnimatingCardMove: card]; @@ -2537,7 +2484,6 @@ - (void) handleAnimation: (NSMutableDictionary *) dictionary // Flip and add card to destination stack. [card setFaceUp: faceUp]; [destStack promiseKeptForCard: card]; - [card release]; // Call delegate animation completion routine. if ((_delegate) && ([_delegate respondsToSelector: @selector (stackView:finishedAnimatingCardMove:)])) @@ -2553,11 +2499,8 @@ - (void) handleAnimation: (NSMutableDictionary *) dictionary [cardTable stackView: self finishedAnimatingCardMove: card]; // Notify observers that the card animating has landed. - [[NSNotificationCenter defaultCenter] postNotificationName: StackViewCardReleasedNotification + [[NSNotificationCenter defaultCenter] postNotificationName: StackViewCardReleasedNotification object: self userInfo: nil]; - - // Done. - [dictionary autorelease]; } } else if ([[dictionary objectForKey: @"type"] isEqualToString: @"flipCard"]) @@ -2607,8 +2550,7 @@ - (void) handleAnimation: (NSMutableDictionary *) dictionary [tempView setCard: tempCard]; if (!_orderly) tempView.transform = tempCard.transform; - [tempCard release]; - + // Create an image for the card. UIGraphicsBeginImageContext (frame.size); if (cardIsFaceUp) @@ -2648,10 +2590,6 @@ - (void) handleAnimation: (NSMutableDictionary *) dictionary // Set animation curve. [dictionary setObject: [NSNumber numberWithInt: UIViewAnimationCurveEaseIn] forKey: @"curve"]; - // Done with temporary views. - [tempView release]; - [imageView release]; - // Call delegate animation begin routine. if ((_delegate) && ([_delegate respondsToSelector: @selector (stackView:beginAnimatingCardFlip:)])) [_delegate stackView: self beginAnimatingCardFlip: card]; @@ -2727,9 +2665,6 @@ - (void) handleAnimation: (NSMutableDictionary *) dictionary cardTable = [self enclosingCETableView]; if (cardTable) [cardTable stackView: self finishedAnimatingCardFlip: card]; - - // Done. - [dictionary autorelease]; } } } @@ -2738,51 +2673,40 @@ - (void) handleAnimation: (NSMutableDictionary *) dictionary - (void) animateWithDictionary: (NSDictionary *) dictionary { - UIView *view; - NSNumber *number; - NSValue *value; - + UIView *view; + NSNumber *number; + NSTimeInterval duration = 0.2; + UIViewAnimationOptions options = 0; + // Indicate an animation is in progress. _animationRefCount += 1; - + view = [dictionary objectForKey: @"view"]; - - // Set up animation using the dictionary passed in as a reference. - [UIView beginAnimations: nil context: dictionary]; - [UIView setAnimationDelegate: self]; - [UIView setAnimationDidStopSelector: @selector (animationStopped:finished:context:)]; - + // Duration. number = [dictionary objectForKey: @"duration"]; if (number) - [UIView setAnimationDuration: [number doubleValue]]; - + duration = [number doubleValue]; + // Animation curve. number = [dictionary objectForKey: @"curve"]; if (number) - [UIView setAnimationCurve: [number intValue]]; - - // Transform. - value = [dictionary objectForKey: @"transform"]; - if (value) - [view setTransform: [value CGAffineTransformValue]]; - - value = [dictionary objectForKey: @"wayPt"]; - if (value) - [view setCenter: [value CGPointValue]]; - - [UIView commitAnimations]; -} - -// ----------------------------------------------------------------------------------- animationDidStop:finished:context - -- (void) animationStopped: (NSString *) animationID finished: (NSNumber *) finished context: (void *) context -{ - // Clear animation flag. - _animationRefCount -= 1; - - // Call handle animation again. - [self handleAnimation: (NSMutableDictionary *) context]; + options = (UIViewAnimationOptions) ([number integerValue] << 16); + + [UIView animateWithDuration: duration delay: 0 options: options animations: ^{ + NSValue *value; + + value = [dictionary objectForKey: @"transform"]; + if (value) + [view setTransform: [value CGAffineTransformValue]]; + + value = [dictionary objectForKey: @"wayPt"]; + if (value) + [view setCenter: [value CGPointValue]]; + } completion: ^(BOOL finished) { + self->_animationRefCount -= 1; + [self handleAnimation: (NSMutableDictionary *) dictionary]; + }]; } #pragma mark ------ notifications diff --git a/CardEngine/CEStackViewPrivate.h b/CardEngine/CEStackViewPrivate.h index 911d71a..3034cad 100644 --- a/CardEngine/CEStackViewPrivate.h +++ b/CardEngine/CEStackViewPrivate.h @@ -24,7 +24,6 @@ - (void) createDraggedCardViews: (CGPoint) offset; - (void) dragDraggedCardViews: (CGPoint) offset; - (void) returnDraggedCardsWithAnimation; -- (void) draggedCardsReturned: (NSString *) animationID finished: (NSNumber *) finished context: (void *) context; - (void) destroyDraggedCardViews; - (void) beginCardDrag: (UITouch *) touch; @@ -54,6 +53,5 @@ // ------ animation - (void) handleAnimation: (NSMutableDictionary *) dictionary; - (void) animateWithDictionary: (NSDictionary *) dictionary; -- (void) animationStopped: (NSString *) animationID finished: (NSNumber *) finished context: (void *) context; @end diff --git a/CardEngine/CETableView.h b/CardEngine/CETableView.h index 81ed7ee..621dda7 100644 --- a/CardEngine/CETableView.h +++ b/CardEngine/CETableView.h @@ -13,11 +13,13 @@ { NSString *_portraitImagePath; NSString *_landscapeImagePath; + BOOL _landscape; NSInteger _animatingCount; } -@property(nonatomic,retain) NSString *portraitImagePath; // If nil no image is drawn. -@property(nonatomic,retain) NSString *landscapeImagePath; // If nil no image is drawn. +@property(nonatomic,strong) NSString *portraitImagePath; // If nil no image is drawn. +@property(nonatomic,strong) NSString *landscapeImagePath; // If nil no image is drawn. +@property(nonatomic,assign) BOOL landscape; // Set by view controller; drives image selection in drawRect:. @property(nonatomic,readonly) BOOL animationInProgress; // Returns YES if there is an animation in progress. // Returns a singleton NSUndoManager. All card drags register with this undo manager. diff --git a/CardEngine/CETableView.m b/CardEngine/CETableView.m index 295f0bc..b87d300 100644 --- a/CardEngine/CETableView.m +++ b/CardEngine/CETableView.m @@ -18,6 +18,18 @@ @implementation CETableView @synthesize portraitImagePath = _portraitImagePath; @synthesize landscapeImagePath = _landscapeImagePath; +@synthesize landscape = _landscape; + +// --------------------------------------------------------------------------------------------------------- setLandscape + +- (void) setLandscape: (BOOL) landscape +{ + if (_landscape != landscape) + { + _landscape = landscape; + [self setNeedsDisplay]; + } +} // ----------------------------------------------------------------------------------------------- sharedCardUndoManager @@ -39,7 +51,7 @@ - (id) initForOrientation: (UIInterfaceOrientation) orientation CGRect frame; // Get application frame. - frame = [[UIScreen mainScreen] applicationFrame]; + frame = [[UIScreen mainScreen] bounds]; if (orientation == UIInterfaceOrientationPortrait) { @@ -119,25 +131,13 @@ - (id) initWithFrame: (CGRect) frame // Super. myself = [super initWithFrame: frame]; - require (myself, bail); + __Require (myself, bail); bail: return myself; } -// ------------------------------------------------------------------------------------------------------------- dealloc - -- (void) dealloc -{ - // Release instance variables. - [_portraitImagePath release]; - [_landscapeImagePath release]; - - // Super. - [super dealloc]; -} - // -------------------------------------------------------------------------------------------------------- drawGradient - (void) drawGradient @@ -168,10 +168,7 @@ - (void) drawGradient - (void) drawRect: (CGRect) rect { - UIInterfaceOrientation orientation; - - orientation = [[UIApplication sharedApplication] statusBarOrientation]; - if (UIInterfaceOrientationIsPortrait (orientation)) + if (!_landscape) { if (_portraitImagePath) [[UIImage imageNamed: _portraitImagePath] drawAtPoint: CGPointMake (0.0, 0.0)]; @@ -271,7 +268,6 @@ - (BOOL) archiveStackStateWithIdentifier: (NSString *) identifier if (dictionary) { [defaults setObject: dictionary forKey: identifier]; - [dictionary release]; } return [defaults synchronize]; diff --git a/Classes/LSAudioEngine.h b/Classes/LSAudioEngine.h new file mode 100644 index 0000000..8842fc9 --- /dev/null +++ b/Classes/LSAudioEngine.h @@ -0,0 +1,13 @@ +// ===================================================================================================================== +// LSAudioEngine.h +// ===================================================================================================================== + +#import + +@interface LSAudioEngine : NSObject + ++ (instancetype)sharedEngine; +- (void)preloadEffect:(NSString *)filename; +- (void)playEffect:(NSString *)filename; + +@end diff --git a/Classes/LSAudioEngine.m b/Classes/LSAudioEngine.m new file mode 100644 index 0000000..89c480e --- /dev/null +++ b/Classes/LSAudioEngine.m @@ -0,0 +1,98 @@ +// ===================================================================================================================== +// LSAudioEngine.m +// ===================================================================================================================== + +#import +#import "LSAudioEngine.h" + + +@implementation LSAudioEngine +{ + NSMutableDictionary *_players; +} + ++ (instancetype)sharedEngine +{ + static LSAudioEngine *sharedInstance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedInstance = [[LSAudioEngine alloc] init]; + }); + return sharedInstance; +} + +- (instancetype)init +{ + self = [super init]; + if (self) + { + _players = [[NSMutableDictionary alloc] init]; + + NSError *error = nil; + [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:&error]; + if (error) + NSLog(@"LSAudioEngine: failed to set audio session category: %@", error); + } + return self; +} + +- (NSURL *)urlForFilename:(NSString *)filename +{ + NSString *name = [filename stringByDeletingPathExtension]; + NSString *ext = [filename pathExtension]; + NSURL *url = [[NSBundle mainBundle] URLForResource:name withExtension:ext]; + if (url == nil) + NSLog(@"LSAudioEngine: sound file not found in bundle: %@", filename); + return url; +} + +- (void)preloadEffect:(NSString *)filename +{ + if (_players[filename] != nil) + return; + + NSURL *url = [self urlForFilename:filename]; + if (url == nil) + return; + + NSError *error = nil; + AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error]; + if (player == nil) + { + NSLog(@"LSAudioEngine: failed to create player for %@: %@", filename, error); + return; + } + + [player prepareToPlay]; + _players[filename] = player; +} + +- (void)playEffect:(NSString *)filename +{ + AVAudioPlayer *player = _players[filename]; + + if (player == nil) + { + [self preloadEffect:filename]; + player = _players[filename]; + if (player == nil) + return; + } + + if ([player isPlaying]) + { + NSError *error = nil; + AVAudioPlayer *duplicate = [[AVAudioPlayer alloc] initWithContentsOfURL:[player url] error:&error]; + if (duplicate) + { + [duplicate play]; + } + } + else + { + player.currentTime = 0; + [player play]; + } +} + +@end diff --git a/Classes/LabSolitaireAppDelegate.h b/Classes/LabSolitaireAppDelegate.h index e6031b4..b67898e 100644 --- a/Classes/LabSolitaireAppDelegate.h +++ b/Classes/LabSolitaireAppDelegate.h @@ -6,17 +6,7 @@ #import -@class LabSolitaireViewController; - - -@interface LabSolitaireAppDelegate : NSObject -{ - UIWindow *_window; - LabSolitaireViewController *_viewController; -} - -@property (nonatomic, retain) IBOutlet UIWindow *_window; -@property (nonatomic, retain) IBOutlet LabSolitaireViewController *_viewController; +@interface LabSolitaireAppDelegate : UIResponder @end diff --git a/Classes/LabSolitaireAppDelegate.m b/Classes/LabSolitaireAppDelegate.m index b5d5372..0a4e0e9 100644 --- a/Classes/LabSolitaireAppDelegate.m +++ b/Classes/LabSolitaireAppDelegate.m @@ -4,67 +4,15 @@ #import "LabSolitaireAppDelegate.h" -#import "LabSolitaireViewController.h" @implementation LabSolitaireAppDelegate // ============================================================================================= LabSolitaireAppDelegate -// ---------------------------------------------------------------------------------------------------------- synthesize - -@synthesize _window; -@synthesize _viewController; - // --------------------------------------------------------------------------- application:didFinishLaunchingWithOptions - (BOOL) application: (UIApplication *) application didFinishLaunchingWithOptions: (NSDictionary *) launchOptions -{ - // Override point for customization after app launch. - [_window setRootViewController: _viewController]; - [_window addSubview: _viewController.view]; - [_window makeKeyAndVisible]; - - // Create stacks. - [_viewController createCardTableLayout]; - - // Restore card layout. - [_viewController restoreState]; - - // Display splash (info) view. - [_viewController openSplashAfterDelay]; - - return YES; -} - -// --------------------------------------------------------------------------------------- applicationDidEnterBackground - -- (void) applicationDidEnterBackground: (UIApplication *) application -{ - // Store away game state. - [_viewController saveState]; -} - -// -------------------------------------------------------------------------------------------- applicationWillTerminate - -- (void) applicationWillTerminate: (UIApplication *) application { - // Store away game state. - [_viewController saveState]; -} - -// ---------------------------------------------------------------------------------- applicationDidReceiveMemoryWarning - -- (void) applicationDidReceiveMemoryWarning: (UIApplication *) application -{ - printf ("applicationDidReceiveMemoryWarning\n"); -} - -// ------------------------------------------------------------------------------------------------------------- dealloc - -- (void) dealloc -{ - [_viewController release]; - [_window release]; - [super dealloc]; + return YES; } @end diff --git a/Classes/LabSolitaireSceneDelegate.h b/Classes/LabSolitaireSceneDelegate.h new file mode 100644 index 0000000..a933960 --- /dev/null +++ b/Classes/LabSolitaireSceneDelegate.h @@ -0,0 +1,13 @@ +// ===================================================================================================================== +// LabSolitaireSceneDelegate.h +// ===================================================================================================================== + + +#import + + +@interface LabSolitaireSceneDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + +@end diff --git a/Classes/LabSolitaireSceneDelegate.m b/Classes/LabSolitaireSceneDelegate.m new file mode 100644 index 0000000..314b516 --- /dev/null +++ b/Classes/LabSolitaireSceneDelegate.m @@ -0,0 +1,39 @@ +// ===================================================================================================================== +// LabSolitaireSceneDelegate.m +// ===================================================================================================================== + + +#import "LabSolitaireSceneDelegate.h" +#import "LabSolitaireViewController.h" + + +@implementation LabSolitaireSceneDelegate +{ + LabSolitaireViewController *_viewController; +} + +// -------------------------------------------------------------------------------------------------------- scene:willConnectToSession:options + +- (void) scene: (UIScene *) scene willConnectToSession: (UISceneSession *) session options: (UISceneConnectionOptions *) connectionOptions +{ + UIWindowScene *windowScene = (UIWindowScene *) scene; + + _viewController = [[LabSolitaireViewController alloc] initWithNibName: @"LabSolitaireViewController" bundle: nil]; + + self.window = [[UIWindow alloc] initWithWindowScene: windowScene]; + self.window.rootViewController = _viewController; + [self.window makeKeyAndVisible]; + + [_viewController createCardTableLayout]; + [_viewController restoreState]; + [_viewController openSplashAfterDelay]; +} + +// -------------------------------------------------------------------------------------------------------- sceneDidEnterBackground + +- (void) sceneDidEnterBackground: (UIScene *) scene +{ + [_viewController saveState]; +} + +@end diff --git a/Classes/LabSolitaireViewController.h b/Classes/LabSolitaireViewController.h index 2f6925c..ec66b86 100644 --- a/Classes/LabSolitaireViewController.h +++ b/Classes/LabSolitaireViewController.h @@ -4,6 +4,7 @@ #import +#import #import #import "CardEngine.h" #import "LocalPlayer.h" @@ -16,15 +17,16 @@ @class LSStackView; -@interface LabSolitaireViewController : UIViewController +@interface LabSolitaireViewController : UIViewController { - LSStackView *_cellViews[4]; - LSStackView *_foundationViews[4]; + LSStackView *_cellViews[4]; + LSStackView *_foundationViews[4]; LSStackView *_tableauViews[8]; UIButton *_newButton; UIButton *_undoButton; UIButton *_infoButton; UIInterfaceOrientation _orientation; + BOOL _initialLayoutApplied; NSTimer *_putawayTimer; NSTimer *_undoHeldTimer; BOOL _undoAllAlertOpen; @@ -41,19 +43,13 @@ BOOL _splashDismissed; NSMutableArray *_worriedCards; LocalPlayer *_localPlayer; - NSMutableArray *_leaderboardPlayerIDs; - NSMutableArray *_leaderboardGamesPlayed; - NSMutableArray *_leaderboardGamesWon; - NSArray *_leaderboardAliases; - BOOL _leaderboardFriendsOnly; - NSUInteger _playerLeaderboardIndex; NSUInteger _dealIndex; UIView *_currentInfoView; // Weak reference. UIView *_rotatingView; UIView *_darkView; + UIImageView *_paperBackgroundView; UIView *_infoView; UIView *_overlayingView; // Weak reference. - IBOutlet UIViewController *_aboutViewController; IBOutlet UIView *_aboutView; IBOutlet UIView *_settingsView; IBOutlet UIView *_rulesView; @@ -67,15 +63,8 @@ IBOutlet UILabel *_gamesPlayedLabel; IBOutlet UILabel *_gamesWonLabel; IBOutlet UILabel *_gamesWonPercentageLabel; - IBOutlet UILabel *_displayScopeLabel; - IBOutlet UIButton *_friendScopeButton; - IBOutlet UIButton *_allScopeButton; - IBOutlet UIImageView *_scopeSelectedImage; - IBOutlet UILabel *_globalScoreNameLabel; - IBOutlet UILabel *_globalScorePlayedLabel; - IBOutlet UILabel *_globalScoreWonLabel; - IBOutlet UILabel *_globalScorePercentLabel; - IBOutlet UIImageView *_highlightView; + IBOutlet UILabel *_personalScoreLabel; + IBOutlet UIButton *_gameCenterButton; } - (void) createCardTableLayout; @@ -94,7 +83,7 @@ - (IBAction) toggleAutoPutaway: (id) sender; - (IBAction) selectAutoPutawayMode: (id) sender; - (IBAction) toggleSound: (id) sender; -- (IBAction) selectLeaderboardScope: (id) sender; +- (IBAction) openGameCenter: (id) sender; - (IBAction) openGameOverView: (id) sender; @end diff --git a/Classes/LabSolitaireViewController.m b/Classes/LabSolitaireViewController.m index b1326e8..9c8376c 100644 --- a/Classes/LabSolitaireViewController.m +++ b/Classes/LabSolitaireViewController.m @@ -7,7 +7,7 @@ #import "CEStackViewPrivate.h" #import "LabSolitaireViewController.h" #import "LSStackView.h" -#import "SimpleAudioEngine.h" +#import "LSAudioEngine.h" #define DISPLAY_OUTLINE_IN_TABLEAU 0 @@ -48,38 +48,28 @@ #define kLUndoButtonY 136 // 135 #define kLInfoButtonY 83 // 82 -#define kHighlighterVOffset 328 - // Misc. #define kDealAnimationDuration 0.25 #define kDealAnimationDelay 0.23 #define kPutawayAnimationDuration 0.25 // 0.30 #define kPutawayAnimationDelay 0.25 // 0.30 #define kWaitForTouchToEndDelay 0.02 -#define kResetTableAlertTag 1 -#define kUndoAllAlertTag 2 -#define kMaxLeaderboardScores 15 // 10 +#define kPaperTabletBackgroundTag 42 enum { - kNoDragRestriction = 0, - kDisallowEmptyColumnDragRestriction = 2, + kNoDragRestriction = 0, + kDisallowEmptyColumnDragRestriction = 2, kEmptyColumnOnlyDragRestriction = 3 }; enum { - kAutoPutawayModeSmart = 0, + kAutoPutawayModeSmart = 0, kAutoPutawayModeAll = 1 }; -enum -{ - kLeaderboardMostPlayedMode = 0, - kLeaderboardMostWonMode = 1 -}; - @implementation LabSolitaireViewController // ========================================================================================== LabSolitaireViewController @@ -90,8 +80,9 @@ - (void) adjustLayoutForOrientation: (UIInterfaceOrientation) orientation CGRect mainBounds; CGRect buttonFrame; - mainBounds = [[UIScreen mainScreen] bounds]; - + mainBounds = self.view.bounds; + [(CETableView *) self.view setLandscape: !UIInterfaceOrientationIsPortrait (orientation)]; + if (UIInterfaceOrientationIsPortrait (orientation)) { int i; @@ -155,21 +146,21 @@ - (void) adjustLayoutForOrientation: (UIInterfaceOrientation) orientation _tableauViews[i].frame = CGRectMake (kLLayoutHOffset + (i * (kLTableauHGap + kCardWide)), kLTableauVOffset, kCardWide, kLTableauTall); buttonFrame = _newButton.frame; - buttonFrame.origin = CGPointMake (mainBounds.size.height - kButtonWide, mainBounds.size.width - kLNewButtonY); + buttonFrame.origin = CGPointMake (mainBounds.size.width - kButtonWide, mainBounds.size.height - kLNewButtonY); _newButton.frame = buttonFrame; [_newButton setImage: [UIImage imageNamed: @"NewSelectedL"] forState: UIControlStateHighlighted]; - + buttonFrame = _undoButton.frame; - buttonFrame.origin = CGPointMake (mainBounds.size.height - kButtonWide, mainBounds.size.width - kLUndoButtonY); + buttonFrame.origin = CGPointMake (mainBounds.size.width - kButtonWide, mainBounds.size.height - kLUndoButtonY); _undoButton.frame = buttonFrame; [_undoButton setImage: [UIImage imageNamed: @"UndoSelectedL"] forState: UIControlStateHighlighted]; - + buttonFrame = _infoButton.frame; - buttonFrame.origin = CGPointMake (mainBounds.size.height - kButtonWide, mainBounds.size.width - kLInfoButtonY); + buttonFrame.origin = CGPointMake (mainBounds.size.width - kButtonWide, mainBounds.size.height - kLInfoButtonY); _infoButton.frame = buttonFrame; [_infoButton setImage: [UIImage imageNamed: @"InfoSelectedL"] forState: UIControlStateHighlighted]; - - _darkView.frame = CGRectMake (0.0, 0.0, mainBounds.size.height, mainBounds.size.width); + + _darkView.frame = CGRectMake (0.0, 0.0, mainBounds.size.width, mainBounds.size.height); // if (_infoView) if ((0)) @@ -664,8 +655,10 @@ - (void) createCardTableLayout int i; CGRect mainBounds; - // Store orientation. - _orientation = self.interfaceOrientation; + CGRect viewBounds = self.view.bounds; + _orientation = (viewBounds.size.width > viewBounds.size.height) + ? UIInterfaceOrientationLandscapeRight + : UIInterfaceOrientationPortrait; // Get standard defaults, what is the user preference for auto-putaway. defaults = [NSUserDefaults standardUserDefaults]; @@ -680,7 +673,7 @@ - (void) createCardTableLayout if (number) _autoPutawayMode = [number integerValue]; else - _autoPutawayMode = kAutoPutawayModeAll; + _autoPutawayMode = kAutoPutawayModeSmart; // What is the user preference for sound playback? defaults = [NSUserDefaults standardUserDefaults]; @@ -694,21 +687,10 @@ - (void) createCardTableLayout _playSounds = YES; } - // What is the user preference for leaderboard scope? - defaults = [NSUserDefaults standardUserDefaults]; - number = [defaults objectForKey: @"LeaderboardScope"]; - if (number) - { - _leaderboardFriendsOnly = [number boolValue]; - } - else - { - _leaderboardFriendsOnly = NO; - } - // Assign portrait and landscape images. [(CETableView *) self.view setPortraitImagePath: @"TablePortrait"]; [(CETableView *) self.view setLandscapeImagePath: @"TableLandscape"]; + self.view.contentMode = UIViewContentModeRedraw; // Create cells. for (i = 0; i < 4; i++) @@ -721,13 +703,12 @@ - (void) createCardTableLayout [_cellViews[i] setFillColor: nil]; [_cellViews[i] setLabelColor: [UIColor colorWithWhite: 0.0 alpha: 0.22]]; [_cellViews[i] setLabelFont: [UIFont fontWithName: @"Arial" size: 32.0]]; - [_cellViews[i] setLabel: @"Free"]; + [_cellViews[i] setLabel: NSLocalizedString(@"Free", @"Cell label shown when a free cell is empty")]; [_cellViews[i] setTag: i]; [_cellViews[i] setDelegate: self]; [_cellViews[i] setIdentifier: @"Cell"]; [_cellViews[i] setArchiveIdentifier: [NSString stringWithFormat: @"Cell%d", i]]; [(CETableView *) self.view addSubview: _cellViews[i]]; - [_cellViews[i] release]; } // Create foundations. @@ -753,7 +734,6 @@ - (void) createCardTableLayout [_foundationViews[i] setIdentifier: @"Foundation"]; [_foundationViews[i] setArchiveIdentifier: [NSString stringWithFormat: @"Foundation%d", i]]; [(CETableView *) self.view addSubview: _foundationViews[i]]; - [_foundationViews[i] release]; } // Create tableau. @@ -768,7 +748,7 @@ - (void) createCardTableLayout [_tableauViews[i] setFillColor: nil]; [_tableauViews[i] setLabelColor: [UIColor colorWithWhite: 0.0 alpha: 0.22]]; [_tableauViews[i] setLabelFont: [UIFont fontWithName: @"Arial" size: 32.0]]; - [_tableauViews[i] setLabel: @"Any"]; + [_tableauViews[i] setLabel: NSLocalizedString(@"Any", @"Tableau label")]; #else // DISPLAY_OUTLINE_IN_TABLEAU [_tableauViews[i] setBorderColor: nil]; [_tableauViews[i] setFillColor: nil]; @@ -781,7 +761,6 @@ - (void) createCardTableLayout [_tableauViews[i] setArchiveIdentifier: [NSString stringWithFormat: @"Tableau%d", i]]; [_tableauViews[i] setOrderly: NO]; [(CETableView *) self.view addSubview: _tableauViews[i]]; - [_tableauViews[i] release]; } // Layout the buttons. @@ -821,29 +800,26 @@ - (void) createCardTableLayout _worriedCards = [[NSMutableArray alloc] initWithCapacity: 3]; // Load sounds. - [[SimpleAudioEngine sharedEngine] preloadEffect: @"Shuffle.wav"]; + [[LSAudioEngine sharedEngine] preloadEffect: @"Shuffle.wav"]; for (i = 0; i < kNumCardDrawSounds; i++) { - [[SimpleAudioEngine sharedEngine] preloadEffect: [NSString stringWithFormat: @"CardDraw%d.wav", i]]; + [[LSAudioEngine sharedEngine] preloadEffect: [NSString stringWithFormat: @"CardDraw%d.wav", i]]; } for (i = 0; i < kNumCardPlaceSounds; i++) { - [[SimpleAudioEngine sharedEngine] preloadEffect: [NSString stringWithFormat: @"CardPlace%d.wav", i]]; + [[LSAudioEngine sharedEngine] preloadEffect: [NSString stringWithFormat: @"CardPlace%d.wav", i]]; } - [[SimpleAudioEngine sharedEngine] preloadEffect: @"ClickOpen.wav"]; - [[SimpleAudioEngine sharedEngine] preloadEffect: @"ClickClose.wav"]; - [[SimpleAudioEngine sharedEngine] preloadEffect: @"Blip.wav"]; - [[SimpleAudioEngine sharedEngine] preloadEffect: @"Buzz.wav"]; - [[SimpleAudioEngine sharedEngine] preloadEffect: @"Babip.wav"]; + [[LSAudioEngine sharedEngine] preloadEffect: @"ClickOpen.wav"]; + [[LSAudioEngine sharedEngine] preloadEffect: @"ClickClose.wav"]; + [[LSAudioEngine sharedEngine] preloadEffect: @"Blip.wav"]; + [[LSAudioEngine sharedEngine] preloadEffect: @"Buzz.wav"]; + [[LSAudioEngine sharedEngine] preloadEffect: @"Babip.wav"]; skipAudio: // Create local player object. _localPlayer = [[LocalPlayer alloc] init]; _localPlayer.delegate = self; - _leaderboardPlayerIDs = [[NSMutableArray alloc] initWithCapacity: 3]; - _leaderboardGamesPlayed = [[NSMutableArray alloc] initWithCapacity: 3]; - _leaderboardGamesWon = [[NSMutableArray alloc] initWithCapacity: 3]; // Listen for these. [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector (cardDragged:) @@ -936,14 +912,14 @@ - (void) new: (id) sender { if (_playSounds) { - [[SimpleAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; } if ((_playedAtleastOneCard == NO) || ([self allCardsArePutAway])) { if (_playSounds) { - [[SimpleAudioEngine sharedEngine] playEffect: @"Shuffle.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"Shuffle.wav"]; } // If the game is over, no need for alert. @@ -951,14 +927,19 @@ - (void) new: (id) sender } else { - UIAlertView *alert; - - // A game is in progress, allow the user to cancel the new game. - alert = [[UIAlertView alloc] initWithTitle: NEW_GAME_TITLE message: NEW_GAME_MESSAGE delegate: self - cancelButtonTitle: NEW_GAME_CANCEL_BUTTON otherButtonTitles: NEW_GAME_BUTTON, nil]; - alert.tag = kResetTableAlertTag; - [alert show]; - [alert release]; + UIAlertController *alert = [UIAlertController alertControllerWithTitle: NEW_GAME_TITLE + message: NEW_GAME_MESSAGE preferredStyle: UIAlertControllerStyleAlert]; + [alert addAction: [UIAlertAction actionWithTitle: NEW_GAME_CANCEL_BUTTON + style: UIAlertActionStyleCancel handler: nil]]; + [alert addAction: [UIAlertAction actionWithTitle: NEW_GAME_BUTTON + style: UIAlertActionStyleDestructive handler: ^(UIAlertAction *action) { + if (self->_playSounds) + { + [[LSAudioEngine sharedEngine] playEffect: @"Shuffle.wav"]; + } + [self resetTable: YES]; + }]]; + [self presentViewController: alert animated: YES completion: nil]; } } @@ -983,11 +964,11 @@ - (void) undo: (id) sender { if ([[CETableView sharedCardUndoManager] canUndo]) { - [[SimpleAudioEngine sharedEngine] playEffect: @"Blip.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"Blip.wav"]; } else { - [[SimpleAudioEngine sharedEngine] playEffect: @"Buzz.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"Buzz.wav"]; } } @@ -1005,21 +986,29 @@ - (void) undoAll: (id) sender { if (([[CETableView sharedCardUndoManager] canUndo]) && ([self seedUsedLast] != NSNotFound)) { - UIAlertView *alert; - if (_playSounds) { - [[SimpleAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; } - + _undoAllAlertOpen = YES; - - // Allow the player to decide if they want to Undo to the beginning of the game. - alert = [[UIAlertView alloc] initWithTitle: UNDO_TITLE message: UNDO_MESSAGE delegate: self - cancelButtonTitle: UNDO_CANCEL_BUTTON otherButtonTitles: UNDO_ALL_BUTTON, nil]; - alert.tag = kUndoAllAlertTag; - [alert show]; - [alert release]; + + UIAlertController *alert = [UIAlertController alertControllerWithTitle: UNDO_TITLE + message: UNDO_MESSAGE preferredStyle: UIAlertControllerStyleAlert]; + [alert addAction: [UIAlertAction actionWithTitle: UNDO_CANCEL_BUTTON + style: UIAlertActionStyleCancel handler: ^(UIAlertAction *action) { + self->_undoAllAlertOpen = NO; + }]]; + [alert addAction: [UIAlertAction actionWithTitle: UNDO_ALL_BUTTON + style: UIAlertActionStyleDestructive handler: ^(UIAlertAction *action) { + self->_undoAllAlertOpen = NO; + if (self->_playSounds) + { + [[LSAudioEngine sharedEngine] playEffect: @"Shuffle.wav"]; + } + [self resetTable: NO]; + }]]; + [self presentViewController: alert animated: YES completion: nil]; } } @@ -1054,312 +1043,87 @@ - (void) undoDragOutside: (id) sender _undoHeldTimer = nil; } -// ----------------------------------------------------------------------------------------- updateGlobalScoresInterface +// ---------------------------------------------------------------------------------------------------------------- info -- (void) updateGlobalScoresInterface +- (void) info: (id) sender { - NSMutableString *allNames; - NSMutableString *allPlayed; - NSMutableString *allWon; - NSMutableString *allPercent; - NSUInteger count; - unichar carriageReturn = 0x000D; - CGRect frame; - NSInteger played = 0; - NSInteger won = 0; - BOOL appendedColon = NO; - - // Leaderboard alias's. - allNames = [NSMutableString stringWithCapacity: 80]; - count = 0; - if ((_leaderboardAliases) && ([_leaderboardAliases count] > 0)) - { - for (NSString *name in _leaderboardAliases) - { - [allNames appendString: name]; - [allNames appendString: [NSString stringWithCharacters: &carriageReturn length: 1]]; - count = count + 1; - } - - // Append player if they are not in the list. - if (_playerLeaderboardIndex == NSNotFound) - { - if (count >= 10) - { - appendedColon = YES; - [allNames appendString: @" :"]; - [allNames appendString: [NSString stringWithCharacters: &carriageReturn length: 1]]; - count = count + 1; - } - if (_localPlayer.alias) - [allNames appendString: _localPlayer.alias]; - else - [allNames appendString: @"You"]; - count = count + 1; - } - } - else - { - if (_localPlayer.alias) - [allNames appendString: _localPlayer.alias]; - else - [allNames appendString: @"You"]; - count = count + 1; - } - - _globalScoreNameLabel.numberOfLines = count; - frame = _globalScoreNameLabel.frame; - frame.size.height = count * 20; - _globalScoreNameLabel.frame = frame; - _globalScoreNameLabel.text = allNames; - - // Leaderboard games played. - allPlayed = [NSMutableString stringWithCapacity: 80]; - count = 0; - if ([_leaderboardGamesPlayed count] > 0) - { - for (NSString *number in _leaderboardGamesPlayed) - { - [allPlayed appendString: number]; - [allPlayed appendString: [NSString stringWithCharacters: &carriageReturn length: 1]]; - count = count + 1; - } - - // Append player's games played if they are not in the list. - if (_playerLeaderboardIndex == NSNotFound) - { - if (appendedColon) - { - [allPlayed appendString: @":"]; - [allPlayed appendString: [NSString stringWithCharacters: &carriageReturn length: 1]]; - count = count + 1; - } - - [_localPlayer retrieveLocalScore: &played forCategory: @"com.softdorothy.labsolitaire.games_played"]; - [allPlayed appendString: [NSString stringWithFormat: @"%ld", (long) played]]; - count = count + 1; - } - } - else - { - [_localPlayer retrieveLocalScore: &played forCategory: @"com.softdorothy.labsolitaire.games_played"]; - [allPlayed appendString: [NSString stringWithFormat: @"%ld", (long) played]]; - count = count + 1; - } - - _globalScorePlayedLabel.numberOfLines = count; - frame = _globalScorePlayedLabel.frame; - frame.size.height = count * 20; - _globalScorePlayedLabel.frame = frame; - _globalScorePlayedLabel.text = allPlayed; - - // Leaderboard games won. - allWon = [NSMutableString stringWithCapacity: 80]; - count = 0; - if ([_leaderboardGamesWon count] > 0) - { - for (NSString *number in _leaderboardGamesWon) - { - [allWon appendString: number]; - [allWon appendString: [NSString stringWithCharacters: &carriageReturn length: 1]]; - count = count + 1; - } - - // Append player's games won if they are not in the list. - if (_playerLeaderboardIndex == NSNotFound) - { - if (appendedColon) - { - [allWon appendString: @":"]; - [allWon appendString: [NSString stringWithCharacters: &carriageReturn length: 1]]; - count = count + 1; - } - - [_localPlayer retrieveLocalScore: &won forCategory: @"com.softdorothy.labsolitaire.games_won"]; - [allWon appendString: [NSString stringWithFormat: @"%ld", (long )won]]; - count = count + 1; - } - } - else - { - [_localPlayer retrieveLocalScore: &won forCategory: @"com.softdorothy.labsolitaire.games_won"]; - [allWon appendString: [NSString stringWithFormat: @"%ld", (long) won]]; - count = count + 1; - } - - _globalScoreWonLabel.numberOfLines = count; - frame = _globalScoreWonLabel.frame; - frame.size.height = count * 20; - _globalScoreWonLabel.frame = frame; - _globalScoreWonLabel.text = allWon; - - // Leaderboard percentage games won. - allPercent = [NSMutableString stringWithCapacity: 80]; - count = 0; - if (([_leaderboardGamesPlayed count] > 0) && ([_leaderboardGamesWon count] > 0) && - ([_leaderboardGamesPlayed count] == [_leaderboardGamesWon count])) - { - for (NSString *playedNumber in _leaderboardGamesPlayed) - { - NSInteger gamesPlayed, gamesWon; - - gamesPlayed = [playedNumber integerValue]; - gamesWon = [[_leaderboardGamesWon objectAtIndex: count] integerValue]; - - if (gamesPlayed == 0) - { - [allPercent appendString: @"-"]; - [allPercent appendString: [NSString stringWithCharacters: &carriageReturn length: 1]]; - } - else - { -// [allPercent appendString: [NSString stringWithFormat: @"%d%%", (gamesWon * 100) / gamesPlayed]]; - [allPercent appendString: [NSString stringWithFormat: @"%ld%%", (long) round (((CGFloat) gamesWon * 100.0) / (CGFloat) gamesPlayed)]]; - [allPercent appendString: [NSString stringWithCharacters: &carriageReturn length: 1]]; - } - - count = count + 1; - } - - // Append player's percentage games won if they are not in the list. - if (_playerLeaderboardIndex == NSNotFound) - { - if (appendedColon) - { - [allPercent appendString: @":"]; - [allPercent appendString: [NSString stringWithCharacters: &carriageReturn length: 1]]; - count = count + 1; - } - if (played == 0) - [allPercent appendString: @"-"]; - else -// [allPercent appendString: [NSString stringWithFormat: @"%d%%", (won * 100) / played]]; - [allPercent appendString: [NSString stringWithFormat: @"%ld%%", (long) round (((CGFloat) won * 100.0) / (CGFloat) played)]]; - count = count + 1; - } - } - else - { - if (played == 0) - [allPercent appendString: @"-"]; - else -// [allPercent appendString: [NSString stringWithFormat: @"%d%%", (won * 100) / played]]; - [allPercent appendString: [NSString stringWithFormat: @"%ld%%", (long) round (((CGFloat) won * 100.0) / (CGFloat) played)]]; - count = count + 1; - } - - _globalScorePercentLabel.numberOfLines = count; - frame = _globalScorePercentLabel.frame; - frame.size.height = count * 20; - _globalScorePercentLabel.frame = frame; - _globalScorePercentLabel.text = allPercent; - - // Hide/show leaderboard scope UI. - if (_localPlayer.usingGameCenter) - { - _displayScopeLabel.hidden = NO; - _friendScopeButton.hidden = NO; - _allScopeButton.hidden = NO; - _scopeSelectedImage.hidden = NO; - } - else - { - _displayScopeLabel.hidden = YES; - _friendScopeButton.hidden = YES; - _allScopeButton.hidden = YES; - _scopeSelectedImage.hidden = YES; - } - - // Leaderboard local player highlight. - frame = _highlightView.frame; - if (_leaderboardAliases) - { - if (_playerLeaderboardIndex == NSNotFound) - { - if (appendedColon) - frame.origin.y = kHighlighterVOffset + ((kMaxLeaderboardScores + 1) * 20); - else - frame.origin.y = kHighlighterVOffset + ([_leaderboardAliases count] * 20); - } - else - { - frame.origin.y = kHighlighterVOffset + (_playerLeaderboardIndex * 20); - } - } - else + if (_infoViewIsOpen) { - frame.origin.y = kHighlighterVOffset; + [self _addInfoSubview: _aboutView]; + return; } - _highlightView.frame = frame; -} -// ---------------------------------------------------------------------------------------------------------------- info - -- (void) info: (id) sender -{ if (_playSounds) { - [[SimpleAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; } - + _infoViewIsOpen = YES; _wasAutoPutaway = _autoPutaway; _wasAutoPutawayMode = _autoPutawayMode; - - // Refresh the global scores. - if ((_localPlayer.usingGameCenter) && (_localPlayer.authenticated)) - { - [_localPlayer retrieveLeaderboardScores: kMaxLeaderboardScores forCategory: @"com.softdorothy.labsolitaire.games_won" - friendsOnly: _leaderboardFriendsOnly]; - } - else - { - // Update the UI. - [self updateGlobalScoresInterface]; - } - - [_aboutViewController setModalPresentationStyle: UIModalPresentationOverCurrentContext]; - [self presentViewController: _aboutViewController animated: YES completion: nil]; - + // Initially begin with "about view" being displayed. _currentInfoView = _aboutView; - + + // Create shared paper background if needed. + if (!_paperBackgroundView) + { + _paperBackgroundView = [[UIImageView alloc] initWithImage: [UIImage imageNamed: @"PaperTablet"]]; + _paperBackgroundView.frame = CGRectMake (0, 0, 708, 708); + } + [_darkView addSubview: _paperBackgroundView]; + + // Hide the tab's internal PaperTablet so only the shared one is visible. + [self _ensurePaperTabletTagged: _aboutView]; + [_aboutView viewWithTag: kPaperTabletBackgroundTag].hidden = YES; + _aboutView.alpha = 1.0; [_darkView addSubview: _aboutView]; - + + // Position both views off-screen below before sliding in. + CGRect mainBounds = self.view.bounds; + CGRect frame = _aboutView.frame; + frame.origin.x = (mainBounds.size.width - frame.size.width) / 2.0; + frame.origin.y = mainBounds.size.height; + _aboutView.frame = frame; + _paperBackgroundView.frame = CGRectMake (frame.origin.x, frame.origin.y, 708, 708); + // Capture touch events. _darkView.userInteractionEnabled = YES; - + // Animate-in the view sliding in while the dark view becomes darker. - [UIView beginAnimations: @"SlideInInfoView" context: nil]; - [UIView setAnimationDuration: 0.5]; - _darkView.backgroundColor = [UIColor colorWithWhite: 0.0 alpha: 0.75]; - [self _positionSubviewBottomAndCentered: _aboutView]; - [UIView commitAnimations]; + [UIView animateWithDuration: 0.5 animations: ^{ + self->_darkView.backgroundColor = [UIColor colorWithWhite: 0.0 alpha: 0.75]; + [self _positionSubviewBottomAndCentered: self->_paperBackgroundView]; + [self _positionSubviewBottomAndCentered: self->_aboutView]; + } completion: nil]; } // ------------------------------------------------------------------------------------------ openLabSolitaireInAppStore - (void) openLabSolitaireInAppStore: (id) sender { - [[UIApplication sharedApplication] openURL: - [NSURL URLWithString: @"itms-apps://itunes.apple.com/app/lab-solitaire/id457535509?ls=1&mt=8"]]; + [[UIApplication sharedApplication] openURL: + [NSURL URLWithString: @"itms-apps://itunes.apple.com/app/lab-solitaire/id457535509?ls=1&mt=8"] + options: @{} completionHandler: nil]; } // -------------------------------------------------------------------------------------- openParlourSolitaireInAppStore - (void) openParlourSolitaireInAppStore: (id) sender { - [[UIApplication sharedApplication] openURL: - [NSURL URLWithString: @"itms-apps://itunes.apple.com/app/parlour-solitaire/id465002121?ls=1&mt=8"]]; + [[UIApplication sharedApplication] openURL: + [NSURL URLWithString: @"itms-apps://itunes.apple.com/app/parlour-solitaire/id465002121?ls=1&mt=8"] + options: @{} completionHandler: nil]; } // ------------------------------------------------------------------------------------------------ openGliderInAppStore - (void) openGliderInAppStore: (id) sender { - [[UIApplication sharedApplication] openURL: - [NSURL URLWithString: @"itms-apps://itunes.apple.com/app/glider-classic/id463484447?mt=8"]]; + [[UIApplication sharedApplication] openURL: + [NSURL URLWithString: @"itms-apps://itunes.apple.com/app/glider-classic/id463484447?mt=8"] + options: @{} completionHandler: nil]; } // --------------------------------------------------------------------------------------------- updateSettingsInterface @@ -1414,29 +1178,19 @@ - (void) updateSettingsInterface [_playSoundsButton setImage: [UIImage imageNamed: @"CheckYes"] forState: UIControlStateNormal]; else [_playSoundsButton setImage: [UIImage imageNamed: @"CheckNo"] forState: UIControlStateNormal]; - - // Leaderboard scope. - frame = _scopeSelectedImage.frame; - if (_leaderboardFriendsOnly) - frame.origin.x = CGRectGetMinX (_friendScopeButton.frame) + round ((CGRectGetWidth (_friendScopeButton.frame) - CGRectGetWidth (frame)) / 2.0); - else - frame.origin.x = CGRectGetMinX (_allScopeButton.frame) + round ((CGRectGetWidth (_allScopeButton.frame) - CGRectGetWidth (frame)) / 2.0); - _scopeSelectedImage.frame = frame; - - if (_leaderboardFriendsOnly) - { - [_friendScopeButton setTitleColor: [UIColor colorWithWhite: 0.2 alpha: 1.0] forState: UIControlStateNormal]; - [_friendScopeButton setTitleColor: [UIColor colorWithWhite: 0.2 alpha: 1.0] forState: UIControlStateHighlighted]; - [_allScopeButton setTitleColor: [UIColor colorWithRed: 0.0 green: 0.0 blue: 0.5 alpha: 0.8] forState: UIControlStateNormal]; - [_allScopeButton setTitleColor: [UIColor colorWithRed: 0.72 green: 0.03 blue: 0.09 alpha: 1.0] forState: UIControlStateHighlighted]; - } + + // Personal score. + NSInteger gamesPlayed, gamesWon; + [_localPlayer retrieveLocalScore: &gamesPlayed forCategory: @"com.softdorothy.labsolitaire.games_played"]; + [_localPlayer retrieveLocalScore: &gamesWon forCategory: @"com.softdorothy.labsolitaire.games_won"]; + NSString *percentString; + if (gamesPlayed == 0) + percentString = @"—"; else - { - [_friendScopeButton setTitleColor: [UIColor colorWithRed: 0.0 green: 0.0 blue: 0.5 alpha: 0.8] forState: UIControlStateNormal]; - [_friendScopeButton setTitleColor: [UIColor colorWithRed: 0.72 green: 0.03 blue: 0.09 alpha: 1.0] forState: UIControlStateHighlighted]; - [_allScopeButton setTitleColor: [UIColor colorWithWhite: 0.2 alpha: 1.0] forState: UIControlStateNormal]; - [_allScopeButton setTitleColor: [UIColor colorWithWhite: 0.2 alpha: 1.0] forState: UIControlStateHighlighted]; - } + percentString = [NSString stringWithFormat: @"%ld", (long) lround (((double) gamesWon * 100.0) / (double) gamesPlayed)]; + _personalScoreLabel.text = [NSString stringWithFormat: + NSLocalizedString (@"Games Won: %1$d of %2$d (%3$@%%)", @"Personal score summary in Settings tab"), + (int) gamesWon, (int) gamesPlayed, percentString]; } // -------------------------------------------------------------------------------------- updateLocalStatisticsInterface @@ -1467,20 +1221,29 @@ - (void) updateLocalStatisticsInterface - (void) _positionSubviewBottomAndCentered: (UIView *) subview { - // Get main bounds and orientation. - CGRect mainBounds = [[UIScreen mainScreen] bounds]; - BOOL portrait = UIInterfaceOrientationIsPortrait ([UIApplication sharedApplication].statusBarOrientation); - + CGRect mainBounds = self.view.bounds; + CGRect frame = subview.frame; - if (portrait) - { - frame.origin = CGPointMake ((mainBounds.size.width - frame.size.width) / 2.0, mainBounds.size.height - frame.size.height); - } - else + frame.origin = CGPointMake ((mainBounds.size.width - frame.size.width) / 2.0, + mainBounds.size.height - frame.size.height); + subview.frame = frame; +} + +- (void) _ensurePaperTabletTagged: (UIView *) view +{ + if ([view viewWithTag: kPaperTabletBackgroundTag]) + return; + + for (UIView *sub in view.subviews) { - frame.origin = CGPointMake ((mainBounds.size.height - frame.size.width) / 2.0, mainBounds.size.width - frame.size.height); + if ([sub isKindOfClass: [UIImageView class]] && + sub.bounds.size.width == 708 && sub.bounds.size.height == 708) + { + sub.tag = kPaperTabletBackgroundTag; + return; + } + [self _ensurePaperTabletTagged: sub]; } - subview.frame = frame; } // ------------------------------------------------------------------------------------------------- @@ -1489,21 +1252,29 @@ - (void) _addInfoSubview: (UIView *) subview { if (_playSounds) { - [[SimpleAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; } - + + // Hide the new tab's internal PaperTablet so only the shared one is visible. + [self _ensurePaperTabletTagged: subview]; + [subview viewWithTag: kPaperTabletBackgroundTag].hidden = YES; + // Switch to display the subview. subview.alpha = 0.0; [_darkView addSubview: subview]; [self _positionSubviewBottomAndCentered: subview]; - + // Fade-out the previous view while fading in the new one. - [UIView beginAnimations: @"CrossfadeInfoSubview" context: _aboutView]; - [UIView setAnimationDelegate: self]; - [UIView setAnimationDidStopSelector: @selector (animationStopped:finished:context:)]; - subview.alpha = 1.0; - _currentInfoView.alpha = 0.0; - [UIView commitAnimations]; + UIView *oldView = _currentInfoView; + [UIView animateWithDuration: 0.2 animations: ^{ + subview.alpha = 1.0; + oldView.alpha = 0.0; + } completion: ^(BOOL finished) { + [oldView viewWithTag: kPaperTabletBackgroundTag].hidden = NO; + [oldView removeFromSuperview]; + self->_currentInfoView = subview; + [self->_darkView bringSubviewToFront: self->_currentInfoView]; + }]; } // ------------------------------------------------------------------------------------------------- @@ -1526,7 +1297,7 @@ - (void) rulesInfo: (id) sender - (void) settingsInfo: (id) sender { - // Switch to display the "settings view". + [self updateSettingsInterface]; [self _addInfoSubview: _settingsView]; } @@ -1535,36 +1306,63 @@ - (void) settingsInfo: (id) sender - (void) closeInfo: (id) sender { CGRect mainBounds; - BOOL portrait; CGRect frame; if (_playSounds) { - [[SimpleAudioEngine sharedEngine] playEffect: @"ClickClose.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"ClickClose.wav"]; } - - [_aboutViewController dismissViewControllerAnimated: YES completion: nil]; - - mainBounds = [[UIScreen mainScreen] bounds]; - portrait = UIInterfaceOrientationIsPortrait ([UIApplication sharedApplication].statusBarOrientation); - + + // Disable touch capture immediately, so buttons underneath remain responsive + // even if the animation is disrupted by rotation. + _darkView.userInteractionEnabled = NO; + + mainBounds = self.view.bounds; + // Animate-out the view sliding out while the dark view becomes clear again. - [UIView beginAnimations: @"SlideOutInfoView" context: nil]; - [UIView setAnimationDuration: 0.5]; - [UIView setAnimationDelegate: self]; - [UIView setAnimationDidStopSelector: @selector (animationStopped:finished:context:)]; - _darkView.backgroundColor = [UIColor colorWithWhite: 0.0 alpha: 0.0]; frame = _currentInfoView.frame; - if (portrait) - { - frame.origin = CGPointMake ((mainBounds.size.width - frame.size.width) / 2.0, mainBounds.size.height); - } - else - { - frame.origin = CGPointMake ((mainBounds.size.height - frame.size.width) / 2.0, mainBounds.size.width); - } - _currentInfoView.frame = frame; - [UIView commitAnimations]; + frame.origin = CGPointMake ((mainBounds.size.width - frame.size.width) / 2.0, + mainBounds.size.height); + CGRect bgFrame = _paperBackgroundView.frame; + bgFrame.origin.y = mainBounds.size.height; + [UIView animateWithDuration: 0.5 animations: ^{ + self->_darkView.backgroundColor = [UIColor colorWithWhite: 0.0 alpha: 0.0]; + self->_currentInfoView.frame = frame; + self->_paperBackgroundView.frame = bgFrame; + } completion: ^(BOOL finished) { + self->_infoViewIsOpen = NO; + + if (self->_currentInfoView) + { + [self->_currentInfoView viewWithTag: kPaperTabletBackgroundTag].hidden = NO; + [self->_currentInfoView removeFromSuperview]; + self->_currentInfoView = nil; + } + [self->_paperBackgroundView removeFromSuperview]; + + if (self->_autoPutaway) + { + if ((self->_wasAutoPutawayMode == kAutoPutawayModeAll) && (self->_autoPutawayMode == kAutoPutawayModeSmart)) + { + [self->_worriedCards removeAllObjects]; + } + + if ((self->_wasAutoPutaway == NO) || ((self->_wasAutoPutawayMode == kAutoPutawayModeSmart) && (self->_autoPutawayMode == kAutoPutawayModeAll))) + { + self->_putawayTimer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target: self selector: + @selector (putawayTimer:) userInfo: nil repeats: NO]; + } + } + + if (self->_gameWon) + { + if (self->_playSounds) + { + [[LSAudioEngine sharedEngine] playEffect: @"Shuffle.wav"]; + } + [self resetTable: YES]; + } + }]; // If this is the first time we are dismissing the info view after launching the app. if (_splashDismissed == NO) @@ -1597,12 +1395,12 @@ - (void) toggleAutoPutaway: (id) sender // Sound effect. if ((_playSounds) && (_autoPutaway == YES)) { - [[SimpleAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; } if ((_playSounds) && (_autoPutaway == NO)) { - [[SimpleAudioEngine sharedEngine] playEffect: @"ClickClose.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"ClickClose.wav"]; } // Update UI. @@ -1629,7 +1427,7 @@ - (void) selectAutoPutawayMode: (id) sender if (_playSounds) { - [[SimpleAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; } // Update UI. @@ -1653,7 +1451,7 @@ - (void) toggleSound: (id) sender // Sound effect. if (_playSounds) { - [[SimpleAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; } // Update UI. @@ -1667,47 +1465,21 @@ - (void) toggleSound: (id) sender } } -// ---------------------------------------------------------------------------------------------- selectLeaderboardScope +// ---------------------------------------------------------------------------------------------------- openGameCenter -- (void) selectLeaderboardScope: (id) sender +- (void) openGameCenter: (id) sender { - NSUserDefaults *defaults; - - if ([sender tag] == 0) - { - // NOP. - if (_leaderboardFriendsOnly == YES) - { - return; - } - - _leaderboardFriendsOnly = YES; - } - else + if (_playSounds) { - // NOP. - if (_leaderboardFriendsOnly == NO) - { - return; - } - - _leaderboardFriendsOnly = NO; + [[LSAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; } - - // Store auto-putaway preference. - defaults = [NSUserDefaults standardUserDefaults]; - [defaults setObject: [NSNumber numberWithBool: _leaderboardFriendsOnly] forKey: @"LeaderboardScope"]; - [defaults synchronize]; - - if (_playSounds) + + if (_localPlayer.usingGameCenter && _localPlayer.authenticated) { - [[SimpleAudioEngine sharedEngine] playEffect: @"ClickOpen.wav"]; + GKGameCenterViewController *gcVC = [[GKGameCenterViewController alloc] initWithState: GKGameCenterViewControllerStateLeaderboards]; + gcVC.gameCenterDelegate = self; + [self presentViewController: gcVC animated: YES completion: nil]; } - - // Update UI. - [self updateSettingsInterface]; - [_localPlayer retrieveLeaderboardScores: kMaxLeaderboardScores forCategory: @"com.softdorothy.labsolitaire.games_won" - friendsOnly: _leaderboardFriendsOnly]; } // ---------------------------------------------------------------------------------------------------- openGameOverView @@ -1719,113 +1491,78 @@ - (void) openGameOverView: (id) sender // Player won sound. if (_playSounds) { - [[SimpleAudioEngine sharedEngine] playEffect: @"Babip.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"Babip.wav"]; } - + + // Hide the game over view's internal PaperTablet. + [self _ensurePaperTabletTagged: _gameOverView]; + [_gameOverView viewWithTag: kPaperTabletBackgroundTag].hidden = YES; + // Switch to display the "game over view". _gameOverView.alpha = 0.0; [_darkView addSubview: _gameOverView]; - + [self _positionSubviewBottomAndCentered: _gameOverView]; + // Update statistics. [self updateLocalStatisticsInterface]; - - // Animate-out the view sliding out while the dark view becomes clear again. - [UIView beginAnimations: @"CrossfadeInfoSubview" context: _gameOverView]; - [UIView setAnimationDelegate: self]; - [UIView setAnimationDidStopSelector: @selector (animationStopped:finished:context:)]; - _gameOverView.alpha = 1.0; - _currentInfoView.alpha = 0.0; - [UIView commitAnimations]; + + // Crossfade to the game over view. + UIView *oldView = _currentInfoView; + [UIView animateWithDuration: 0.2 animations: ^{ + self->_gameOverView.alpha = 1.0; + oldView.alpha = 0.0; + } completion: ^(BOOL finished) { + [oldView viewWithTag: kPaperTabletBackgroundTag].hidden = NO; + [oldView removeFromSuperview]; + self->_currentInfoView = self->_gameOverView; + [self->_darkView bringSubviewToFront: self->_currentInfoView]; + }]; } else { _infoViewIsOpen = YES; - + + // Create shared paper background if needed. + if (!_paperBackgroundView) + { + _paperBackgroundView = [[UIImageView alloc] initWithImage: [UIImage imageNamed: @"PaperTablet"]]; + _paperBackgroundView.frame = CGRectMake (0, 0, 708, 708); + } + [_darkView addSubview: _paperBackgroundView]; + + // Hide the game over view's internal PaperTablet. + [self _ensurePaperTabletTagged: _gameOverView]; + [_gameOverView viewWithTag: kPaperTabletBackgroundTag].hidden = YES; + // Add "Game Over" view. [_darkView addSubview: _gameOverView]; _currentInfoView = _gameOverView; - + // Update statistics. [self updateLocalStatisticsInterface]; - + + // Position both views off-screen below before sliding in. + CGRect mainBounds = self.view.bounds; + CGRect frame = _gameOverView.frame; + frame.origin.x = (mainBounds.size.width - frame.size.width) / 2.0; + frame.origin.y = mainBounds.size.height; + _gameOverView.frame = frame; + _paperBackgroundView.frame = CGRectMake (frame.origin.x, frame.origin.y, 708, 708); + // Capture touch events. _darkView.userInteractionEnabled = YES; - - // Animate-in the view sliding in while the dark view becomes darker. - [UIView beginAnimations: @"SlideInInfoView" context: nil]; - [UIView setAnimationDuration: 0.5]; - [UIView setAnimationDelegate: self]; - [UIView setAnimationDidStopSelector: @selector (animationStopped:finished:context:)]; - _darkView.backgroundColor = [UIColor colorWithWhite: 0.0 alpha: 0.75]; - [self _positionSubviewBottomAndCentered: _gameOverView]; - [UIView commitAnimations]; - } -} - -// ----------------------------------------------------------------------------------- animationDidStop:finished:context -- (void) animationStopped: (NSString *) animationID finished: (NSNumber *) finished context: (void *) context -{ - if ([animationID isEqualToString: @"SlideInInfoView"]) - { - if (_currentInfoView == _gameOverView) - { - // Player won sound. - if (_playSounds) - { - [[SimpleAudioEngine sharedEngine] playEffect: @"Babip.wav"]; - } - } - } - else if ([animationID isEqualToString: @"SlideOutInfoView"]) - { - _infoViewIsOpen = NO; - - // No longer capture touch events. - _darkView.userInteractionEnabled = NO; - - if (_currentInfoView) - { - [_currentInfoView removeFromSuperview]; - _currentInfoView = nil; - } - - // Fire off auto-putaway timer if the user enabled it. - if (_autoPutaway) - { - // If the player has 'worried back' cards in "All" putaway mode, we should clear that history and mark the - // cards 'worry free'. - if ((_wasAutoPutawayMode == kAutoPutawayModeAll) && (_autoPutawayMode == kAutoPutawayModeSmart)) - { - [_worriedCards removeAllObjects]; - } - - // Fire off timer to look for cards to put up. - if ((_wasAutoPutaway == NO) || ((_wasAutoPutawayMode == kAutoPutawayModeSmart) && (_autoPutawayMode == kAutoPutawayModeAll))) - { - _putawayTimer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target: self selector: - @selector (putawayTimer:) userInfo: nil repeats: NO]; - } - } - - // Start new game. - if (_gameWon) - { - // Shuffle sound. - if (_playSounds) + // Animate-in the view sliding in while the dark view becomes darker. + [UIView animateWithDuration: 0.5 animations: ^{ + self->_darkView.backgroundColor = [UIColor colorWithWhite: 0.0 alpha: 0.75]; + [self _positionSubviewBottomAndCentered: self->_paperBackgroundView]; + [self _positionSubviewBottomAndCentered: self->_gameOverView]; + } completion: ^(BOOL finished) { + if (self->_playSounds) { - [[SimpleAudioEngine sharedEngine] playEffect: @"Shuffle.wav"]; + [[LSAudioEngine sharedEngine] playEffect: @"Babip.wav"]; } - - // Deal new hand. - [self resetTable: YES]; - } - } - else if ([animationID isEqualToString: @"CrossfadeInfoSubview"]) - { - [_currentInfoView removeFromSuperview]; - _currentInfoView = context; - [_darkView bringSubviewToFront: _currentInfoView]; + }]; } } @@ -1837,6 +1574,25 @@ - (void) willRotateToInterfaceOrientation: (UIInterfaceOrientation) orientation [self adjustLayoutForOrientation: orientation]; } +- (void)viewWillTransitionToSize:(CGSize)size + withTransitionCoordinator:(id)coordinator +{ + [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; + + UIInterfaceOrientation newOrientation = (size.width > size.height) + ? UIInterfaceOrientationLandscapeRight + : UIInterfaceOrientationPortrait; + + [coordinator animateAlongsideTransition:^(id context) { + [self adjustLayoutForOrientation:newOrientation]; + + if (self->_infoViewIsOpen && self->_currentInfoView) { + [self _positionSubviewBottomAndCentered:self->_paperBackgroundView]; + [self _positionSubviewBottomAndCentered:self->_currentInfoView]; + } + } completion:nil]; +} + /* - (void) willAnimateFirstHalfOfRotationToInterfaceOrientation: (UIInterfaceOrientation) toOrientation duration: (NSTimeInterval) duration { @@ -1868,31 +1624,65 @@ - (void) didReceiveMemoryWarning // Release any cached data, images, etc that aren't in use. } +- (void) _localizeViewTree: (UIView *) root +{ + for (UIView *child in root.subviews) + { + NSString *identifier = child.accessibilityIdentifier; + if (identifier.length > 0) + { + NSString *localized = NSLocalizedString (identifier, nil); + if ([child isKindOfClass: [UILabel class]]) + { + ((UILabel *) child).text = localized; + } + else if ([child isKindOfClass: [UIButton class]]) + { + [(UIButton *) child setTitle: localized forState: UIControlStateNormal]; + } + } + [self _localizeViewTree: child]; + } +} + - (void) viewDidLoad { -// if ([self respondsToSelector:@selector(topLayoutGuide)]) -// { -// [self.view removeConstraint: self.containerTopSpaceConstraint]; -// -// self.containerTopSpaceConstraint = [NSLayoutConstraint constraintWithItem: self.contentView -// attribute: NSLayoutAttributeTop relatedBy: NSLayoutRelationEqual toItem: self.topLayoutGuide -// attribute: NSLayoutAttributeBottom multiplier: 1 constant: 0]; -// -// [self.view addConstraint: self.containerTopSpaceConstraint]; -// [self.view setNeedsUpdateConstraints]; -// [self.view layoutIfNeeded]; -// } + [super viewDidLoad]; + [self _localizeViewTree: _aboutView]; + [self _localizeViewTree: _rulesView]; + [self _localizeViewTree: _settingsView]; + [self _localizeViewTree: _gameOverView]; + + NSString *version = [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleShortVersionString"] ?: @"?"; + NSString *year = [@(__DATE__) substringFromIndex: 7]; + UILabel *vcLabel = (UILabel *) [_aboutView viewWithTag: 600]; + vcLabel.text = [NSString stringWithFormat: @"V. %@ ©2011–%@ Soft Dorothy LLC", version, year]; +} + +// ------------------------------------------------------------------------------------------------- viewDidLayoutSubviews + +- (void) viewDidLayoutSubviews +{ + [super viewDidLayoutSubviews]; + + if (!_initialLayoutApplied) + { + _initialLayoutApplied = YES; + CGRect bounds = self.view.bounds; + UIInterfaceOrientation orientation = (bounds.size.width > bounds.size.height) + ? UIInterfaceOrientationLandscapeRight + : UIInterfaceOrientationPortrait; + [self adjustLayoutForOrientation:orientation]; + } } // ------------------------------------------------------------------------------------------------------- viewDidUnload - (void) viewDidUnload { - // Release any retained subviews of the main view. - // e.g. self.myOutlet = nil; - [_newButton release]; - [_undoButton release]; - [_infoButton release]; + _newButton = nil; + _undoButton = nil; + _infoButton = nil; } // ------------------------------------------------------------------------------------------------------------- dealloc @@ -1901,7 +1691,7 @@ - (void) dealloc { // No more observing. [[NSNotificationCenter defaultCenter] removeObserver: self]; - + // Clean up timers. if (_putawayTimer) { @@ -1913,45 +1703,9 @@ - (void) dealloc [_undoHeldTimer invalidate]; } _undoHeldTimer = nil; - - // Super. - [super dealloc]; } -#pragma mark ------ alert view delegate methods -//--------------------------------------------------------------------------------------- alertView:clickedButtonAtIndex - -- (void) alertView: (UIAlertView *) alertView clickedButtonAtIndex: (NSInteger) buttonIndex -{ - if (alertView.tag == kResetTableAlertTag) - { - if (buttonIndex == 1) // New game. - { - if (_playSounds) - { - [[SimpleAudioEngine sharedEngine] playEffect: @"Shuffle.wav"]; - } - - [self resetTable: YES]; - } - } - else if (alertView.tag == kUndoAllAlertTag) - { - _undoAllAlertOpen = NO; - - if (buttonIndex == 1) // Undo all. - { - if (_playSounds) - { - [[SimpleAudioEngine sharedEngine] playEffect: @"Shuffle.wav"]; - } - - [self resetTable: NO]; - } - } -} - #pragma mark ------ stack view delegate methods // --------------------------------------------------------------------------------------------- stackView:allowDragCard @@ -2365,7 +2119,7 @@ - (void) cardPickedUp: (NSNotification *) notification { // Play card drawn sound. if (_playSounds) - [[SimpleAudioEngine sharedEngine] playEffect: [NSString stringWithFormat: @"CardDraw%d.wav", CERandomInt (kNumCardDrawSounds)]]; + [[LSAudioEngine sharedEngine] playEffect: [NSString stringWithFormat: @"CardDraw%d.wav", CERandomInt (kNumCardDrawSounds)]]; } // -------------------------------------------------------------------------------------------------------- cardReleased @@ -2380,7 +2134,7 @@ - (void) cardReleased: (NSNotification *) notification // Play card placed sound. if (_playSounds) - [[SimpleAudioEngine sharedEngine] playEffect: [NSString stringWithFormat: @"CardPlace%d.wav", CERandomInt (kNumCardPlaceSounds)]]; + [[LSAudioEngine sharedEngine] playEffect: [NSString stringWithFormat: @"CardPlace%d.wav", CERandomInt (kNumCardPlaceSounds)]]; } // -------------------------------------------------------------------------------------------------------- putawayTimer @@ -2403,174 +2157,19 @@ - (void) putawayTimer: (NSTimer *) timer - (void) localPlayerAuthenticated: (LocalPlayer *) player { - [_localPlayer retrieveLeaderboardScores: kMaxLeaderboardScores forCategory: @"com.softdorothy.labsolitaire.games_won" - friendsOnly: _leaderboardFriendsOnly]; - - // Fetch player's leaderboard score. - [_localPlayer retrieveLeaderboardScoreForLocalPlayerForCategory: @"com.softdorothy.labsolitaire.games_played"]; - [_localPlayer retrieveLeaderboardScoreForLocalPlayerForCategory: @"com.softdorothy.labsolitaire.games_won"]; } // --------------------------------------------------------------------------- localPlayer:failedAuthenticationWithError -// This can be called if the player disconnects from -// GameCenter while we were in the background. - (void) localPlayer: (LocalPlayer *) player failedAuthenticationWithError: (NSError *) error { - // Empty leaderboard arrays. - [_leaderboardPlayerIDs removeAllObjects]; - [_leaderboardGamesPlayed removeAllObjects]; - [_leaderboardGamesWon removeAllObjects]; - [_leaderboardAliases release]; - _leaderboardAliases = nil; - _playerLeaderboardIndex = NSNotFound; - - // Update the UI. - [self updateGlobalScoresInterface]; -} - -// -------------------------------------------------------------------------------------------- copyPlayerIDs:toOurArray - -- (void) copyPlayerIDs: (NSArray *) players toOurArray: (NSMutableArray *) ourPlayers -{ - // Copy the leaderboard data. - [ourPlayers removeAllObjects]; - if (players) - [ourPlayers addObjectsFromArray: players]; -} - -// ------------------------------------------------------------------------------------ copyLeaderboardScores:toOurArray - -- (void) copyLeaderboardScores: (NSArray *) scores toOurArray: (NSMutableArray *) ourScores -{ - // Copy the leaderboard data. - [ourScores removeAllObjects]; - if (scores) - [ourScores addObjectsFromArray: scores]; -} - -// -------------------------------------------------------------- mergeLocalPlayerScoreWithLeaderboardScores:forCategory - -- (NSUInteger) mergeLocalPlayerScoreWithLeaderboardScores: (NSMutableArray *) leaderboard forCategory: (NSString *) category -{ - NSInteger index = 0; - NSUInteger playerIndex = NSNotFound; - - for (NSString *playerID in _leaderboardPlayerIDs) - { - if ([playerID isEqualToString: _localPlayer.playerID]) - { - NSInteger localScore; - - // Get local score. - [_localPlayer retrieveLocalScore: &localScore forCategory: category]; - if ([leaderboard count] > index) - { - NSInteger leaderboardValue; - - leaderboardValue = [[leaderboard objectAtIndex: index] integerValue]; - if (localScore > leaderboardValue) - [leaderboard replaceObjectAtIndex: index withObject: [NSString stringWithFormat: @"%ld", (long) localScore]]; - else if (leaderboardValue > localScore) - [_localPlayer postLocalScore: leaderboardValue forCategory: category]; - } - else - { - [leaderboard addObject: [NSString stringWithFormat: @"%ld", (long) localScore]]; - } - - playerIndex = index; - break; - } - - index += 1; - } - - return playerIndex; -} - -// -------------------------------------------------------- localPlayer:retrievedLeaderboardScores:playerIDs:forCategory - -- (void) localPlayer: (LocalPlayer *) player retrievedLeaderboardScores: (NSArray *) scores - playerIDs: (NSArray *) players forCategory: (NSString *) category -{ - if ([category isEqualToString: @"com.softdorothy.labsolitaire.games_won"]) - { - // Copy the playerID data. - [self copyPlayerIDs: players toOurArray: _leaderboardPlayerIDs]; - - // Copy the leaderboard data. - [self copyLeaderboardScores: scores toOurArray: _leaderboardGamesWon]; - - // If our local score is greater than the leaderboard score, substitute our local score in the games-won array. - _playerLeaderboardIndex = [self mergeLocalPlayerScoreWithLeaderboardScores: _leaderboardGamesWon forCategory: category]; - - // Fetch the number of games won for the leaderboard players. - [_localPlayer retrieveLeaderboardScoresForPlayerIDs: _leaderboardPlayerIDs forCategory: @"com.softdorothy.labsolitaire.games_played"]; - } - else if ([category isEqualToString: @"com.softdorothy.labsolitaire.games_played"]) - { - // Copy the leaderboard data. - [self copyLeaderboardScores: scores toOurArray: _leaderboardGamesPlayed]; - - // If our local score is greater than the leaderboard score, substitute our local score in the games-played array. - if (_playerLeaderboardIndex != NSNotFound) - [self mergeLocalPlayerScoreWithLeaderboardScores: _leaderboardGamesPlayed forCategory: category]; - - // Fetch the names for the player ID's. - if ((_leaderboardPlayerIDs) && ([_leaderboardPlayerIDs count] > 0)) - { - [_localPlayer retrieveAliasesForPlayerIDs: _leaderboardPlayerIDs]; - } - else - { - [_leaderboardAliases release]; - _leaderboardAliases = nil; - [self updateGlobalScoresInterface]; - } - } -} - -// ----------------------------------------------------------------- retrievedLeaderboardScoreForLocalPlayer:forCategory - -- (void) localPlayer: (LocalPlayer *) player retrievedLeaderboardScoreForLocalPlayer: (int64_t) score forCategory: (NSString *) category -{ - if ([category isEqualToString: @"com.softdorothy.labsolitaire.games_won"]) - { - NSInteger gamesWon; - - [_localPlayer retrieveLocalScore: &gamesWon forCategory: @"com.softdorothy.labsolitaire.games_won"]; - if (score > gamesWon) - [_localPlayer postLocalScore: score forCategory: @"com.softdorothy.labsolitaire.games_won"]; - } - else if ([category isEqualToString: @"com.softdorothy.labsolitaire.games_played"]) - { - NSInteger gamesPlayed; - - [_localPlayer retrieveLocalScore: &gamesPlayed forCategory: @"com.softdorothy.labsolitaire.games_played"]; - if (score > gamesPlayed) - [_localPlayer postLocalScore: score forCategory: @"com.softdorothy.labsolitaire.games_played"]; - } -} - -// ---------------------------------------------------------------------------- localPlayer:retrievedAliasesForPlayerIDs - -- (void) localPlayer: (LocalPlayer *) player retrievedAliasesForPlayerIDs: (NSArray *) aliases -{ - [_leaderboardAliases release]; - _leaderboardAliases = nil; - if (aliases) - _leaderboardAliases = [aliases copy]; - - // Update the UI. - [self updateGlobalScoresInterface]; } -// -------------------------------------------------------------------- localPlayer:failedRetrieveScoreForCategory:error +// --------------------------------------------------------- localPlayer:needsToPresentAuthenticationViewController -- (void) localPlayer: (LocalPlayer *) player failedRetrieveScoreForCategory: (NSString *) category error: (NSError *) error +- (void) localPlayer: (LocalPlayer *) player needsToPresentAuthenticationViewController: (UIViewController *) viewController { - printf ("localPlayer:failedRetrieveScoreForCategory:error: %s\n", [[error description] cStringUsingEncoding: NSUTF8StringEncoding]); + [self presentViewController: viewController animated: YES completion: nil]; } // ------------------------------------------------------------------------ localPlayer:failedPostScoreForCategory:error @@ -2580,11 +2179,11 @@ - (void) localPlayer: (LocalPlayer *) player failedPostScoreForCategory: (NSStri printf ("localPlayer:failedPostScoreForCategory:error: %s\n", [[error description] cStringUsingEncoding: NSUTF8StringEncoding]); } -// ----------------------------------------------------------------------- localPlayer:failedRetrieveAliasesForPlayerIDs +#pragma mark ------ GKGameCenterControllerDelegate -- (void) localPlayer: (LocalPlayer *) player failedRetrieveAliasesForPlayerIDs: (NSError *) error +- (void) gameCenterViewControllerDidFinish: (GKGameCenterViewController *) gameCenterViewController { - printf ("localPlayer:failedRetrieveAliasesForPlayerIDs: %s\n", [[error description] cStringUsingEncoding: NSUTF8StringEncoding]); + [self dismissViewControllerAnimated: YES completion: nil]; } @end diff --git a/Classes/LocalPlayer.h b/Classes/LocalPlayer.h index 706f3be..bad950b 100644 --- a/Classes/LocalPlayer.h +++ b/Classes/LocalPlayer.h @@ -12,49 +12,28 @@ @interface LocalPlayer : NSObject { NSString *_playerID; - NSString *_alias; + NSString *_displayName; BOOL _authenticated; BOOL _usingGameCenter; - id _delegate; + __weak id _delegate; } @property(nonatomic,readonly) NSString *playerID; // Only valid if using Game Center. Must be authenticated. -@property(nonatomic,readonly) NSString *alias; // Only valid if using Game Center. Must be authenticated. +@property(nonatomic,readonly) NSString *displayName; // Only valid if using Game Center. Must be authenticated. @property(nonatomic,readonly) BOOL authenticated; // Returns YES if authenticated (always YES if local). @property(nonatomic,readonly) BOOL usingGameCenter; // Returns YES if LocalPlayer is from Game Center. -@property(nonatomic,assign) id delegate; // Delegate called for asynchronous completions. +@property(nonatomic,weak) id delegate; // Delegate called for asynchronous completions. -// Creates a LocalPlayer object. If Game Center is available, LocalPlayer initialized with the local player. Otherwise -// NSUserDefaults will be used to post and retrieve scores. - (id) init; -// If GameCenter was available, calls into Game Center to post a score for the local player. Otherwise, stores value -// in NSUserDefaults (category becomes the key). Returns NO if player not yet authenticated or if failure. +// Submits a score to the Game Center leaderboard (if authenticated) or stores it locally via NSUserDefaults. - (BOOL) postLeaderboardScore: (NSInteger) score forCategory: (NSString *) category; - (void) postLocalScore: (NSInteger) score forCategory: (NSString *) category; -// Retrieves score from local NSUserDefaults. Uses playerID (for Game Center) or "Local" for key. Returns YES if score -// for category was found in NSUserDefaults. +// Retrieves score from local NSUserDefaults. Returns YES if score for category was found. - (BOOL) retrieveLocalScore: (NSInteger *) score forCategory: (NSString *) category; -// Returns NO if no authenticated local player or no Game Center support. Retrieves leaderboard scores for category -// from Game Center. This is an asynchronous operation and as such, results are passed to delegate (see below). -// A category is required, count must be 75 or less. -- (BOOL) retrieveLeaderboardScores: (NSUInteger) count forCategory: (NSString *) category friendsOnly: (BOOL) friends; - -// Returns NO if no authenticated local player or no Game Center support. Retrieves leaderboard scores for category -// from Game Center. This is an asynchronous operation and as such, results are passed to delegate (see below). -- (BOOL) retrieveLeaderboardScoresForPlayerIDs: (NSArray *) playerIDs forCategory: (NSString *) category; - -// Like -[retrieveLeaderboardScoresForPlayerIDs:forCategory] above but will report leaderboard scores for category -// for the array of playerID's passed in. -- (BOOL) retrieveAliasesForPlayerIDs: (NSArray *) playerIDs; - -// Returns NO if no authenticated local player or no Game Center support. Retrieves leaderboard scores for category -// from Game Center for th elocal player. This is an asynchronous operation and as such, results are passed to delegate (see below). -- (BOOL) retrieveLeaderboardScoreForLocalPlayerForCategory: (NSString *) category; - @end @@ -62,28 +41,9 @@ @optional -// Called when the LocalPlayer has been authenticated. The player may not stay authenticated during the life-cycle of -// the game however. See -[localPlayer:failedAuthenticationWithError] below. - (void) localPlayerAuthenticated: (LocalPlayer *) player; - -// Called either due to an actual error or if the player does not connect to their Game Center account. -// This can be called at any point within the app lifecycle since the game might switch into the background and the -// player log out of Game Center. - (void) localPlayer: (LocalPlayer *) player failedAuthenticationWithError: (NSError *) error; - -// Called when the global leaderboard scores have been retrieved. -- (void) localPlayer: (LocalPlayer *) player retrievedLeaderboardScores: (NSArray *) scores - playerIDs: (NSArray *) players forCategory: (NSString *) category; - -// Called when the playerID aliases have been retrieved. -- (void) localPlayer: (LocalPlayer *) player retrievedAliasesForPlayerIDs: (NSArray *) aliases; - -// Called when the global leaderboard score for the local player has been retrieved. -- (void) localPlayer: (LocalPlayer *) player retrievedLeaderboardScoreForLocalPlayer: (int64_t) score forCategory: (NSString *) category; - -// Optional methods called during error conditions. -- (void) localPlayer: (LocalPlayer *) player failedRetrieveScoreForCategory: (NSString *) category error: (NSError *) error; - (void) localPlayer: (LocalPlayer *) player failedPostScoreForCategory: (NSString *) category error: (NSError *) error; -- (void) localPlayer: (LocalPlayer *) player failedRetrieveAliasesForPlayerIDs: (NSError *) error; +- (void) localPlayer: (LocalPlayer *) player needsToPresentAuthenticationViewController: (UIViewController *) viewController; @end diff --git a/Classes/LocalPlayer.m b/Classes/LocalPlayer.m index ea2cd33..93f65c6 100644 --- a/Classes/LocalPlayer.m +++ b/Classes/LocalPlayer.m @@ -13,7 +13,7 @@ @implementation LocalPlayer // --------------------------------------------------------------------------------------------------------- @synthesize @synthesize playerID = _playerID; -@synthesize alias = _alias; +@synthesize displayName = _displayName; @synthesize authenticated = _authenticated; @synthesize usingGameCenter = _usingGameCenter; @synthesize delegate = _delegate; @@ -23,36 +23,21 @@ @implementation LocalPlayer - (id) init { id myself = nil; - + if ((self = [super init])) { - // Create instance variables. _playerID = nil; - _alias = nil; + _displayName = nil; _authenticated = NO; _usingGameCenter = NO; _delegate = nil; - - // Try to authenticate local player with Game Center (if avail). + [self authenticateLocalPlayer]; - - // Success. + myself = self; } - - return myself; -} - -// ------------------------------------------------------------------------------------------------------------- dealloc -- (void) dealloc -{ - // Release instance vars. - [_playerID release]; - [_alias release]; - - // Super. - [super dealloc]; + return myself; } // ------------------------------------------------------------------------------------------ postLocalScore:forCategory @@ -62,34 +47,29 @@ - (void) postLocalScore: (NSInteger) score forCategory: (NSString *) category NSUserDefaults *defaults; NSDictionary *storedDictionary; NSMutableDictionary *scoreDictionary; - - // Get standard defaults. + defaults = [NSUserDefaults standardUserDefaults]; - - // Fetch the player's local score dictionary (if there is one yet). + if (_playerID) storedDictionary = [defaults dictionaryForKey: _playerID]; else storedDictionary = [defaults dictionaryForKey: @"Local"]; - - // Create a mutable dictionary from stored dictionary (or a new one if required). + if (storedDictionary) scoreDictionary = [NSMutableDictionary dictionaryWithDictionary: storedDictionary]; else scoreDictionary = [NSMutableDictionary dictionaryWithCapacity: 1]; - - // Store score with category as key. + [scoreDictionary setObject: [NSNumber numberWithInteger: score] forKey: category]; - - // Save. + if (_playerID) [defaults setObject: scoreDictionary forKey: _playerID]; else [defaults setObject: scoreDictionary forKey: @"Local"]; [defaults synchronize]; - + bail: - + return; } @@ -98,64 +78,54 @@ - (void) postLocalScore: (NSInteger) score forCategory: (NSString *) category - (BOOL) postLeaderboardScore: (NSInteger) score forCategory: (NSString *) category { BOOL success = NO; - - // We have to have been authenticated already. + if (_authenticated == NO) goto bail; - + if (_usingGameCenter) { NSUserDefaults *defaults; NSDictionary *storedDictionary; NSMutableDictionary *scoreDictionary; - GKScore *scoreReporter; - - // Get standard defaults. + defaults = [NSUserDefaults standardUserDefaults]; - - // Fetch the player's local score dictionary (if there is one yet). storedDictionary = [defaults dictionaryForKey: _playerID]; - - // Store the score using category as the key. + if (storedDictionary) scoreDictionary = [NSMutableDictionary dictionaryWithDictionary: storedDictionary]; else scoreDictionary = [NSMutableDictionary dictionaryWithCapacity: 1]; [scoreDictionary setObject: [NSNumber numberWithInteger: score] forKey: category]; - - // Save with playerID as key. + [defaults setObject: scoreDictionary forKey: _playerID]; success = [defaults synchronize]; - - // Create object to report score. Assign points. - scoreReporter = [[[GKScore alloc] initWithLeaderboardIdentifier: category] autorelease]; - scoreReporter.value = score; - - // Report score. -// [scoreReporter reportScoreWithCompletionHandler: ^(NSError *error) - [GKScore reportScores: [NSArray arrayWithObject: scoreReporter] withCompletionHandler: ^(NSError *error) + + __weak __typeof(self) weakSelf = self; + [GKLeaderboard submitScore: (NSInteger) score + context: 0 + player: [GKLocalPlayer localPlayer] + leaderboardIDs: @[category] + completionHandler: ^(NSError *error) { - if (error != nil) + __typeof(self) strongSelf = weakSelf; + if (error != nil && strongSelf) { - if ([_delegate respondsToSelector: @selector (localPlayer:failedPostScoreForCategory:error:)]) - [_delegate localPlayer: self failedPostScoreForCategory: category error: error]; + if ([strongSelf->_delegate respondsToSelector: @selector (localPlayer:failedPostScoreForCategory:error:)]) + [strongSelf->_delegate localPlayer: strongSelf failedPostScoreForCategory: category error: error]; } }]; } else { NSUserDefaults *defaults; - - // Get standard defaults. + defaults = [NSUserDefaults standardUserDefaults]; - - // Store the score using category as the key. [defaults setObject: [NSNumber numberWithInteger: score] forKey: category]; success = [defaults synchronize]; } - + bail: - + return success; } @@ -166,377 +136,83 @@ - (BOOL) retrieveLocalScore: (NSInteger *) score forCategory: (NSString *) categ NSUserDefaults *defaults; NSDictionary *scoreDictionary; BOOL retrieved = NO; - - // Initialize to zero. + if (score) *score = 0; - - // Param check. - require (category, bail); - - // Get standard user defaults; look for player ID (or 'local') sub-dictionary. + + __Require (category, bail); + defaults = [NSUserDefaults standardUserDefaults]; if (_playerID) scoreDictionary = [defaults dictionaryForKey: _playerID]; else scoreDictionary = [defaults dictionaryForKey: @"Local"]; - - // See if we have a local copy of the score stored here. Return that quickly. + if (scoreDictionary) { NSNumber *scoreNumber; - + scoreNumber = [scoreDictionary objectForKey: category]; if (scoreNumber) { - // Pass back. if (score) *score = [scoreNumber integerValue]; retrieved = YES; } } - + bail: - + return retrieved; } -// ------------------------------------------------------------------- retrieveLeaderboardScores:forCategory:friendsOnly +@end -- (BOOL) retrieveLeaderboardScores: (NSUInteger) count forCategory: (NSString *) category friendsOnly: (BOOL) friends -{ - GKLeaderboard *leaderboardRequest; - BOOL success = NO; - - // Param checking. - require (count <= 75, bail); - require (category, bail); - - // We have to have been authenticated and using Game Center. - if ((_authenticated == NO) || (_usingGameCenter == NO)) - goto bail; - - // Create leaderboard object to request global scores. - leaderboardRequest = [[GKLeaderboard alloc] init]; - require (leaderboardRequest, bail); - - // Leaderboard attributes. - leaderboardRequest.identifier = category; - if (friends) - leaderboardRequest.playerScope = GKLeaderboardPlayerScopeFriendsOnly; - else - leaderboardRequest.playerScope = GKLeaderboardPlayerScopeGlobal; - leaderboardRequest.timeScope = GKLeaderboardTimeScopeAllTime; - - // An odd bug, if a player ID comes back as "G: ANONYMOUS", we will fail to get - // their scores later (in -[retrieveLeaderboardScoresForPlayerIDs:forCategory:] below). - // I'm going to request more than the client asked for and filter out 'anonymous' players. - leaderboardRequest.range = NSMakeRange (1, count + 8); - - // Load the scores. - [leaderboardRequest loadScoresWithCompletionHandler: ^(NSArray *scores, NSError *error) - { - NSMutableArray *values = nil; - NSMutableArray *players = nil; - NSUInteger index = 0; - - // Handle error. Even with an error, there may be a partial list of scores, however. - if (error != nil) - { - if ([_delegate respondsToSelector: @selector (localPlayer:failedRetrieveScoreForCategory:error:)]) - [_delegate localPlayer: self failedRetrieveScoreForCategory: category error: error]; - } - - // See if we have some score data. - if (scores) - { - // Array to hold scores and player ID's. - values = [NSMutableArray arrayWithCapacity: 3]; - players = [NSMutableArray arrayWithCapacity: 3]; - - for (GKScore *oneScore in scores) - { - // Skip over "anonymous" scores. - if ([oneScore.playerID isEqualToString: @"G: ANONYMOUS"] == NO) - { - [values addObject: [NSString stringWithFormat: @"%lld", oneScore.value]]; - [players addObject: oneScore.playerID]; - - // Return only as many scores requested. - index += 1; - if (index == count) - break; - } - } - } - - // Call delegate with the leaderboard scores. - if ([_delegate respondsToSelector: @selector (localPlayer:retrievedLeaderboardScores:playerIDs:forCategory:)]) - [_delegate localPlayer: self retrievedLeaderboardScores: values playerIDs: players forCategory: category]; - }]; - - success = YES; - -bail: - - return success; -} -// ------------------------------------------------------------------- retrieveLeaderboardScoresForPlayerIDs:forCategory +@implementation LocalPlayer (LocalPlayer_priv) +// ====================================================================================== LocalPlayer (LocalPlayer_priv) +// --------------------------------------------------------------------------------------------- authenticateLocalPlayer -- (BOOL) retrieveLeaderboardScoresForPlayerIDs: (NSArray *) playerIDs forCategory: (NSString *) category +- (void) authenticateLocalPlayer { - GKLeaderboard *leaderboardRequest; - BOOL success = NO; - - // Param checking. - require (playerIDs, bail); - require (category, bail); - - // We have to have been authenticated and using Game Center. - if ((_authenticated == NO) || (_usingGameCenter == NO)) - goto bail; - - // Create leaderboard object to request global scores. - leaderboardRequest = [[GKLeaderboard alloc] initWithPlayerIDs: playerIDs]; - require (leaderboardRequest, bail); - - // Leaderboard attributes. - leaderboardRequest.identifier = category; - - // Load the scores. - [leaderboardRequest loadScoresWithCompletionHandler: ^(NSArray *scores, NSError *error) + GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer]; + __weak GKLocalPlayer *weakPlayer = localPlayer; + __weak __typeof(self) weakSelf = self; + localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error) { - NSMutableArray *values = nil; - NSMutableArray *players = nil; - - // Handle error. Even with an error, there may be a partial list of scores, however. - if (error != nil) - { - if ([_delegate respondsToSelector: @selector (localPlayer:failedRetrieveScoreForCategory:error:)]) - [_delegate localPlayer: self failedRetrieveScoreForCategory: category error: error]; - } - - // See if we have some score data. - if (scores) - { - // Array to hold scores. - values = [NSMutableArray arrayWithCapacity: 3]; - players = [NSMutableArray arrayWithCapacity: 3]; - - for (NSString *onePlayerID in playerIDs) - { - BOOL foundMatch = NO; - - for (GKScore *oneScore in scores) - { - if ([onePlayerID isEqualToString: oneScore.playerID]) - { - [values addObject: [NSString stringWithFormat: @"%lld", oneScore.value]]; - [players addObject: onePlayerID]; - - foundMatch = YES; - break; - } - } - - // Insert a placeholder. - if (foundMatch == NO) - { - [values addObject: @"0"]; - [players addObject: onePlayerID]; - } - } - } - - // Call delegate with the leaderboard scores. - if ([_delegate respondsToSelector: @selector (localPlayer:retrievedLeaderboardScores:playerIDs:forCategory:)]) - [_delegate localPlayer: self retrievedLeaderboardScores: values playerIDs: players forCategory: category]; - }]; - - success = YES; - -bail: - - return success; -} - -// ----------------------------------------------------------------------------------------- retrieveAliasesForPlayerIDs + GKLocalPlayer *strongPlayer = weakPlayer; + __typeof(self) strongSelf = weakSelf; + if (strongPlayer == nil || strongSelf == nil) + return; -- (BOOL) retrieveAliasesForPlayerIDs: (NSArray *) playerIDs -{ - BOOL success = NO; - - // We have to have been authenticated and using Game Center. - if ((_authenticated == NO) || (_usingGameCenter == NO)) - goto bail; - - success = YES; - - [GKPlayer loadPlayersForIdentifiers: playerIDs withCompletionHandler: ^(NSArray *players, NSError *error) - { - NSMutableArray *aliases = nil; - - if (error != nil) + if (viewController != nil) { - if ([_delegate respondsToSelector: @selector (localPlayer:failedRetrieveAliasesForPlayerIDs:)]) - [_delegate localPlayer: self failedRetrieveAliasesForPlayerIDs: error]; - } - - if (players) - { - // Array to hold aliases. - aliases = [NSMutableArray arrayWithCapacity: 3]; - - for (NSString *onePlayerID in playerIDs) - { - BOOL foundMatch = NO; - - for (GKPlayer *onePlayer in players) - { - if ([onePlayerID isEqualToString: onePlayer.playerID]) - { - // Add to aliases. - [aliases addObject: onePlayer.alias]; - - foundMatch = YES; - break; - } - } - - // Insert a placeholder. - if (foundMatch == NO) - [aliases addObject: @"???"]; - } + if ([strongSelf->_delegate respondsToSelector: @selector (localPlayer:needsToPresentAuthenticationViewController:)]) + [strongSelf->_delegate localPlayer: strongSelf needsToPresentAuthenticationViewController: viewController]; + return; } - - // Call delegate with the player aliases. - if ([_delegate respondsToSelector: @selector (localPlayer:retrievedAliasesForPlayerIDs:)]) - [_delegate localPlayer: self retrievedAliasesForPlayerIDs: aliases]; - }]; - -bail: - - return success; -} -// ------------------------------------------------------------------- retrieveLeaderboardScoreForLocalPlayerForCategory - -- (BOOL) retrieveLeaderboardScoreForLocalPlayerForCategory: (NSString *) category -{ - GKLeaderboard *leaderboardRequest; - BOOL success = NO; - - // Param checking. - require (category, bail); - - // We have to have been authenticated and using Game Center. - if ((_authenticated == NO) || (_usingGameCenter == NO)) - goto bail; - - // Create leaderboard object to request global scores. - leaderboardRequest = [[GKLeaderboard alloc] initWithPlayerIDs: [NSArray arrayWithObject: _playerID]]; - require (leaderboardRequest, bail); - - // Leaderboard attributes. - leaderboardRequest.identifier = category; - - // Load the scores. - [leaderboardRequest loadScoresWithCompletionHandler: ^(NSArray *scores, NSError *error) - { - // Handle error. Even with an error, there may be a partial list of scores, however. - if (error != nil) + if (strongPlayer.isAuthenticated) { - if ([_delegate respondsToSelector: @selector (localPlayer:failedRetrieveScoreForCategory:error:)]) - [_delegate localPlayer: self failedRetrieveScoreForCategory: category error: error]; + strongSelf->_playerID = [strongPlayer.gamePlayerID copy]; + strongSelf->_displayName = [strongPlayer.displayName copy]; + strongSelf->_authenticated = YES; + strongSelf->_usingGameCenter = YES; + + if ([strongSelf->_delegate respondsToSelector: @selector (localPlayerAuthenticated:)]) + [strongSelf->_delegate localPlayerAuthenticated: strongSelf]; } - - // See if we have some score data. - if (scores) + else { - GKScore *oneScore; - - // Call delegate with the leaderboard score. - oneScore = [scores objectAtIndex: 0]; - if ([_delegate respondsToSelector: @selector (localPlayer:retrievedLeaderboardScoreForLocalPlayer:forCategory:)]) - [_delegate localPlayer: self retrievedLeaderboardScoreForLocalPlayer: oneScore.value forCategory: category]; - } - }]; - - success = YES; - -bail: - - return success; -} - -@end - - -@implementation LocalPlayer (LocalPlayer_priv) -// ====================================================================================== LocalPlayer (LocalPlayer_priv) -// ---------------------------------------------------------------------------------------------- gameCenterAPIAvailable + strongSelf->_playerID = nil; + strongSelf->_displayName = nil; + strongSelf->_authenticated = YES; + strongSelf->_usingGameCenter = NO; -- (BOOL) gameCenterAPIAvailable -{ - BOOL localPlayerClassAvailable; - NSString *requiredSystemVersion = @"4.1"; - NSString *currentSystemVersion; - BOOL osVersionSupported; - - localPlayerClassAvailable = (NSClassFromString (@"GKLocalPlayer") != nil); - currentSystemVersion = [[UIDevice currentDevice] systemVersion]; - osVersionSupported = ([currentSystemVersion compare: requiredSystemVersion options: NSNumericSearch] != NSOrderedAscending); - - return (localPlayerClassAvailable && osVersionSupported); -} - -// --------------------------------------------------------------------------------------------- authenticateLocalPlayer - -- (void) authenticateLocalPlayer -{ - if ([self gameCenterAPIAvailable]) - { - GKLocalPlayer *localPlayer; - - localPlayer = [GKLocalPlayer localPlayer]; - localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error) - { - if (localPlayer.isAuthenticated) - { - _playerID = [[NSString alloc] initWithString: localPlayer.playerID]; - _alias = [[NSString alloc] initWithString: localPlayer.alias]; - _authenticated = YES; - _usingGameCenter = YES; - - if ([_delegate respondsToSelector: @selector (localPlayerAuthenticated:)]) - [_delegate localPlayerAuthenticated: self]; - } - else - { - if (_playerID) - { - [_playerID release]; - _playerID = nil; - } - if (_alias) - { - [_alias release]; - _alias = nil; - } - _authenticated = YES; - _usingGameCenter = NO; - - if ([_delegate respondsToSelector: @selector (localPlayer:failedAuthenticationWithError:)]) - [_delegate localPlayer: self failedAuthenticationWithError: error]; - } - }; - } - else - { - _authenticated = YES; - _usingGameCenter = NO; - } + if ([strongSelf->_delegate respondsToSelector: @selector (localPlayer:failedAuthenticationWithError:)]) + [strongSelf->_delegate localPlayer: strongSelf failedAuthenticationWithError: error]; + } + }; } @end diff --git a/Classes/LocalPlayer_priv.h b/Classes/LocalPlayer_priv.h index f513060..26ef1b0 100644 --- a/Classes/LocalPlayer_priv.h +++ b/Classes/LocalPlayer_priv.h @@ -8,7 +8,6 @@ @interface LocalPlayer (LocalPlayer_priv) -- (BOOL) gameCenterAPIAvailable; - (void) authenticateLocalPlayer; @end diff --git a/CocosDenshion/placeholder.txt b/CocosDenshion/placeholder.txt deleted file mode 100644 index 6123b80..0000000 --- a/CocosDenshion/placeholder.txt +++ /dev/null @@ -1,3 +0,0 @@ -These files from CocosDenshion here: - -SimpleAudioEngine.m SimpleAudioEngine.h CocosDenshion.h CocosDenshion.m CDOpenALSupport.m CDOpenALSupport.h CDAudioManager.m CDConfig.h CDAudioManager.h \ No newline at end of file diff --git a/LabSolitaire-Info.plist b/LabSolitaire-Info.plist index 8e669af..f5684ba 100644 --- a/LabSolitaire-Info.plist +++ b/LabSolitaire-Info.plist @@ -3,35 +3,13 @@ CFBundleDevelopmentRegion - English + $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName ${PRODUCT_NAME} CFBundleDocumentTypes CFBundleExecutable ${EXECUTABLE_NAME} - CFBundleIconFiles - - Icon-72.png - Icon-144.png - Default-Landscape@2x~ipad.png - Default-Portrait@2x~ipad.png - - CFBundleIcons - - CFBundlePrimaryIcon - - CFBundleIconFiles - - Icon-72.png - Icon-144.png - Default-Landscape@2x~ipad.png - Default-Portrait@2x~ipad.png - - UIPrerenderedIcon - - - CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion @@ -41,19 +19,32 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.1.0 + 1.1.1 CFBundleSignature ???? CFBundleURLTypes CFBundleVersion - 1.1.0 + 1.1.1 LSRequiresIPhoneOS - NSMainNibFile - MainWindow - UIPrerenderedIcon - + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + LabSolitaireSceneDelegate + + + + UISupportedInterfaceOrientations UIInterfaceOrientationPortrait diff --git a/LabSolitaire.xcodeproj/project.pbxproj b/LabSolitaire.xcodeproj/project.pbxproj index 57d99ca..6e38a97 100755 --- a/LabSolitaire.xcodeproj/project.pbxproj +++ b/LabSolitaire.xcodeproj/project.pbxproj @@ -75,7 +75,6 @@ 265BB96C14EF6E3200B5D3D4 /* TablePortrait@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 265BB96A14EF6E3200B5D3D4 /* TablePortrait@2x.png */; }; 265BB96E14EF6E3D00B5D3D4 /* GliderAd@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 265BB96D14EF6E3D00B5D3D4 /* GliderAd@2x.png */; }; 265FA4F613DE86E60067ED18 /* CardShadow.png in Resources */ = {isa = PBXBuildFile; fileRef = 265FA4F513DE86E60067ED18 /* CardShadow.png */; }; - 2660DCEE18A210C00023B6DA /* LabSolitaire.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2660DCED18A210C00023B6DA /* LabSolitaire.storyboard */; }; 26640E0F1320114900DA94E4 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26640E0E1320114900DA94E4 /* AVFoundation.framework */; }; 2677A478142BF20900022E23 /* ParlourSolitaireAd.png in Resources */ = {isa = PBXBuildFile; fileRef = 2677A477142BF20900022E23 /* ParlourSolitaireAd.png */; }; 268DE47813230B5300C68035 /* Blip.wav in Resources */ = {isa = PBXBuildFile; fileRef = 268DE47713230B5300C68035 /* Blip.wav */; }; @@ -83,9 +82,7 @@ 268F0EA0131F4525000DC644 /* CheckNo.png in Resources */ = {isa = PBXBuildFile; fileRef = 268F0E9E131F4525000DC644 /* CheckNo.png */; }; 268F0EA1131F4525000DC644 /* CheckYes.png in Resources */ = {isa = PBXBuildFile; fileRef = 268F0E9F131F4525000DC644 /* CheckYes.png */; }; 268F0EE2131F58F0000DC644 /* Dots.png in Resources */ = {isa = PBXBuildFile; fileRef = 268F0EE1131F58F0000DC644 /* Dots.png */; }; - 2690A47F13F860B300984E0C /* LargeSolitaireIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 2690A47E13F860B300984E0C /* LargeSolitaireIcon.png */; }; 26A13F9314438705000D8B18 /* CECardDealer.m in Sources */ = {isa = PBXBuildFile; fileRef = 26A13F9214438705000D8B18 /* CECardDealer.m */; }; - 26A292D91509BE49004F24F2 /* Icon-144.png in Resources */ = {isa = PBXBuildFile; fileRef = 26A292D81509BE49004F24F2 /* Icon-144.png */; }; 26A292FD1509E189004F24F2 /* Default-Landscape@2x~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 26A292FC1509E189004F24F2 /* Default-Landscape@2x~ipad.png */; }; 26A292FF1509E232004F24F2 /* Default-Portrait@2x~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 26A292FE1509E232004F24F2 /* Default-Portrait@2x~ipad.png */; }; 26A81FF2143BFB9A001C21FB /* GameKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26A81FF1143BFB9A001C21FB /* GameKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; @@ -99,12 +96,6 @@ 26AE5BE31442601C00570ABC /* Highlight.png in Resources */ = {isa = PBXBuildFile; fileRef = 26AE5BE21442601C00570ABC /* Highlight.png */; }; 26AE5BF41442926100570ABC /* UnderlineWide.png in Resources */ = {isa = PBXBuildFile; fileRef = 26AE5BF31442926100570ABC /* UnderlineWide.png */; }; 26AE5BF6144292AF00570ABC /* UnderlineNarrow.png in Resources */ = {isa = PBXBuildFile; fileRef = 26AE5BF5144292AF00570ABC /* UnderlineNarrow.png */; }; - 26B1E3E3131D608900DFA4EA /* iTunesArtwork in Resources */ = {isa = PBXBuildFile; fileRef = 26B1E3E1131D608900DFA4EA /* iTunesArtwork */; }; - 26B1E41D131D6E6900DFA4EA /* Icon-72.png in Resources */ = {isa = PBXBuildFile; fileRef = 26B1E41C131D6E6900DFA4EA /* Icon-72.png */; }; - 26B3E29C148404EC00C3459E /* CDAudioManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 26B3E294148404EC00C3459E /* CDAudioManager.m */; }; - 26B3E29D148404EC00C3459E /* CDOpenALSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 26B3E297148404EC00C3459E /* CDOpenALSupport.m */; }; - 26B3E29E148404EC00C3459E /* CocosDenshion.m in Sources */ = {isa = PBXBuildFile; fileRef = 26B3E299148404EC00C3459E /* CocosDenshion.m */; }; - 26B3E29F148404EC00C3459E /* SimpleAudioEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = 26B3E29B148404EC00C3459E /* SimpleAudioEngine.m */; }; 26B3E2A91484084700C3459E /* OpenAL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26B3E2A81484084700C3459E /* OpenAL.framework */; }; 26B3E2AB1484086600C3459E /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26B3E2AA1484086600C3459E /* AudioToolbox.framework */; }; 26B6709914EF69FA00C8E6B2 /* 1C@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 26B6708914EF69FA00C8E6B2 /* 1C@2x.png */; }; @@ -173,7 +164,6 @@ 26C84ECC1301027500EB4D52 /* 4S.png in Resources */ = {isa = PBXBuildFile; fileRef = 26C84EC61301027500EB4D52 /* 4S.png */; }; 26C84ECD1301027500EB4D52 /* 5S.png in Resources */ = {isa = PBXBuildFile; fileRef = 26C84EC71301027500EB4D52 /* 5S.png */; }; 26C84ECE1301027500EB4D52 /* 6S.png in Resources */ = {isa = PBXBuildFile; fileRef = 26C84EC81301027500EB4D52 /* 6S.png */; }; - 26C97421140E84B400D2C373 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 26C97423140E84B400D2C373 /* Localizable.strings */; }; 26CDF5BB143E77C800A19B9F /* LineThin.png in Resources */ = {isa = PBXBuildFile; fileRef = 26CDF5BA143E77C800A19B9F /* LineThin.png */; }; 26DE146912F4902B00FF88C6 /* CECard.m in Sources */ = {isa = PBXBuildFile; fileRef = 26DE145D12F4902B00FF88C6 /* CECard.m */; }; 26DE146A12F4902B00FF88C6 /* CECardView.m in Sources */ = {isa = PBXBuildFile; fileRef = 26DE145F12F4902B00FF88C6 /* CECardView.m */; }; @@ -214,8 +204,11 @@ 26FF236B1303042D005E20CB /* 12D.png in Resources */ = {isa = PBXBuildFile; fileRef = 26FF23671303042D005E20CB /* 12D.png */; }; 26FF236C1303042D005E20CB /* 13D.png in Resources */ = {isa = PBXBuildFile; fileRef = 26FF23681303042D005E20CB /* 13D.png */; }; 288765A50DF7441C002DB57D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288765A40DF7441C002DB57D /* CoreGraphics.framework */; }; - 28AD733F0D9D9553002E5188 /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28AD733E0D9D9553002E5188 /* MainWindow.xib */; }; 28D7ACF80DDB3853001CB0EB /* LabSolitaireViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 28D7ACF70DDB3853001CB0EB /* LabSolitaireViewController.m */; }; + 4C1616F72FA61289002DEE82 /* LSAudioEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C1616F62FA61289002DEE82 /* LSAudioEngine.m */; }; + 4C1616FA2FA67749002DEE82 /* LabSolitaireSceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C1616F92FA67749002DEE82 /* LabSolitaireSceneDelegate.m */; }; + 4C37DCBE2FA79F2D00825C1D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4C37DCBD2FA79F2D00825C1D /* Assets.xcassets */; }; + 4C37DCC02FA7AB1900825C1D /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 4C37DCBF2FA7AB1900825C1D /* Localizable.xcstrings */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -289,7 +282,6 @@ 265BB96A14EF6E3200B5D3D4 /* TablePortrait@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "TablePortrait@2x.png"; sourceTree = ""; }; 265BB96D14EF6E3D00B5D3D4 /* GliderAd@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "GliderAd@2x.png"; sourceTree = ""; }; 265FA4F513DE86E60067ED18 /* CardShadow.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = CardShadow.png; sourceTree = ""; }; - 2660DCED18A210C00023B6DA /* LabSolitaire.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LabSolitaire.storyboard; sourceTree = ""; }; 26640E0E1320114900DA94E4 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; 2677A477142BF20900022E23 /* ParlourSolitaireAd.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = ParlourSolitaireAd.png; sourceTree = ""; }; 268DE47713230B5300C68035 /* Blip.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = Blip.wav; sourceTree = ""; }; @@ -316,17 +308,7 @@ 26AE5BE21442601C00570ABC /* Highlight.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Highlight.png; sourceTree = ""; }; 26AE5BF31442926100570ABC /* UnderlineWide.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = UnderlineWide.png; sourceTree = ""; }; 26AE5BF5144292AF00570ABC /* UnderlineNarrow.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = UnderlineNarrow.png; sourceTree = ""; }; - 26B1E3E1131D608900DFA4EA /* iTunesArtwork */ = {isa = PBXFileReference; lastKnownFileType = file; path = iTunesArtwork; sourceTree = ""; }; 26B1E41C131D6E6900DFA4EA /* Icon-72.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-72.png"; sourceTree = ""; }; - 26B3E293148404EC00C3459E /* CDAudioManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDAudioManager.h; sourceTree = ""; }; - 26B3E294148404EC00C3459E /* CDAudioManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDAudioManager.m; sourceTree = ""; }; - 26B3E295148404EC00C3459E /* CDConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDConfig.h; sourceTree = ""; }; - 26B3E296148404EC00C3459E /* CDOpenALSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDOpenALSupport.h; sourceTree = ""; }; - 26B3E297148404EC00C3459E /* CDOpenALSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDOpenALSupport.m; sourceTree = ""; }; - 26B3E298148404EC00C3459E /* CocosDenshion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CocosDenshion.h; sourceTree = ""; }; - 26B3E299148404EC00C3459E /* CocosDenshion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CocosDenshion.m; sourceTree = ""; }; - 26B3E29A148404EC00C3459E /* SimpleAudioEngine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimpleAudioEngine.h; sourceTree = ""; }; - 26B3E29B148404EC00C3459E /* SimpleAudioEngine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SimpleAudioEngine.m; sourceTree = ""; }; 26B3E2A81484084700C3459E /* OpenAL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenAL.framework; path = System/Library/Frameworks/OpenAL.framework; sourceTree = SDKROOT; }; 26B3E2AA1484086600C3459E /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; 26B6708914EF69FA00C8E6B2 /* 1C@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "1C@2x.png"; sourceTree = ""; }; @@ -397,9 +379,6 @@ 26C84EC61301027500EB4D52 /* 4S.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = 4S.png; sourceTree = ""; }; 26C84EC71301027500EB4D52 /* 5S.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = 5S.png; sourceTree = ""; }; 26C84EC81301027500EB4D52 /* 6S.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = 6S.png; sourceTree = ""; }; - 26C97422140E84B400D2C373 /* en */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; - 26C97424140E84CA00D2C373 /* ja */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = ""; }; - 26C97445140ECCFF00D2C373 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; }; 26CDF5BA143E77C800A19B9F /* LineThin.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = LineThin.png; sourceTree = ""; }; 26DE145B12F4902B00FF88C6 /* Cards.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = Cards.pdf; sourceTree = ""; }; 26DE145C12F4902B00FF88C6 /* CECard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CECard.h; sourceTree = ""; }; @@ -450,10 +429,15 @@ 26FF23671303042D005E20CB /* 12D.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = 12D.png; sourceTree = ""; }; 26FF23681303042D005E20CB /* 13D.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = 13D.png; sourceTree = ""; }; 288765A40DF7441C002DB57D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; - 28AD733E0D9D9553002E5188 /* MainWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainWindow.xib; sourceTree = ""; }; 28D7ACF60DDB3853001CB0EB /* LabSolitaireViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LabSolitaireViewController.h; sourceTree = ""; }; 28D7ACF70DDB3853001CB0EB /* LabSolitaireViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LabSolitaireViewController.m; sourceTree = ""; }; 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 4C1616F52FA61289002DEE82 /* LSAudioEngine.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LSAudioEngine.h; sourceTree = ""; }; + 4C1616F62FA61289002DEE82 /* LSAudioEngine.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LSAudioEngine.m; sourceTree = ""; }; + 4C1616F82FA67749002DEE82 /* LabSolitaireSceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LabSolitaireSceneDelegate.h; sourceTree = ""; }; + 4C1616F92FA67749002DEE82 /* LabSolitaireSceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LabSolitaireSceneDelegate.m; sourceTree = ""; }; + 4C37DCBD2FA79F2D00825C1D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 4C37DCBF2FA7AB1900825C1D /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -477,6 +461,10 @@ 080E96DDFE201D6D7F000001 /* Classes */ = { isa = PBXGroup; children = ( + 4C1616F82FA67749002DEE82 /* LabSolitaireSceneDelegate.h */, + 4C1616F92FA67749002DEE82 /* LabSolitaireSceneDelegate.m */, + 4C1616F52FA61289002DEE82 /* LSAudioEngine.h */, + 4C1616F62FA61289002DEE82 /* LSAudioEngine.m */, 26ED7011143CD2BA00E06F05 /* LocalPlayer.h */, 26ED7010143CD2BA00E06F05 /* LocalPlayer_priv.h */, 26ED7012143CD2BA00E06F05 /* LocalPlayer.m */, @@ -521,22 +509,6 @@ path = Sounds; sourceTree = ""; }; - 26B3E292148404EC00C3459E /* CocosDenshion */ = { - isa = PBXGroup; - children = ( - 26B3E293148404EC00C3459E /* CDAudioManager.h */, - 26B3E294148404EC00C3459E /* CDAudioManager.m */, - 26B3E295148404EC00C3459E /* CDConfig.h */, - 26B3E296148404EC00C3459E /* CDOpenALSupport.h */, - 26B3E297148404EC00C3459E /* CDOpenALSupport.m */, - 26B3E298148404EC00C3459E /* CocosDenshion.h */, - 26B3E299148404EC00C3459E /* CocosDenshion.m */, - 26B3E29A148404EC00C3459E /* SimpleAudioEngine.h */, - 26B3E29B148404EC00C3459E /* SimpleAudioEngine.m */, - ); - path = CocosDenshion; - sourceTree = ""; - }; 26DE145912F4902B00FF88C6 /* CardEngine */ = { isa = PBXGroup; children = ( @@ -740,7 +712,6 @@ 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { isa = PBXGroup; children = ( - 26B3E292148404EC00C3459E /* CocosDenshion */, 26DE145912F4902B00FF88C6 /* CardEngine */, 080E96DDFE201D6D7F000001 /* Classes */, 29B97315FDCFA39411CA2CEA /* Other Sources */, @@ -762,12 +733,10 @@ 29B97317FDCFA39411CA2CEA /* Resources */ = { isa = PBXGroup; children = ( + 4C37DCBF2FA7AB1900825C1D /* Localizable.xcstrings */, 26DE150F12F4A0C600FF88C6 /* Art */, 26640E78132012C100DA94E4 /* Sounds */, - 26C97423140E84B400D2C373 /* Localizable.strings */, - 2660DCED18A210C00023B6DA /* LabSolitaire.storyboard */, 2641ED80140818FD00709D17 /* LabSolitaireViewController.xib */, - 28AD733E0D9D9553002E5188 /* MainWindow.xib */, 26ABC07013C5A0D600ABCE84 /* LabSolitaire-Info.plist */, 26B1E41C131D6E6900DFA4EA /* Icon-72.png */, 26A292D81509BE49004F24F2 /* Icon-144.png */, @@ -775,7 +744,7 @@ 26A292FE1509E232004F24F2 /* Default-Portrait@2x~ipad.png */, 26F27B4C13F4592B00AB6EC1 /* Default-Landscape~ipad.png */, 26A292FC1509E189004F24F2 /* Default-Landscape@2x~ipad.png */, - 26B1E3E1131D608900DFA4EA /* iTunesArtwork */, + 4C37DCBD2FA79F2D00825C1D /* Assets.xcassets */, ); name = Resources; sourceTree = ""; @@ -823,22 +792,19 @@ LastUpgradeCheck = 0710; TargetAttributes = { 1D6058900D05DD3D006BFB54 = { - DevelopmentTeam = FNE3M5XN94; + DevelopmentTeam = 9FFTJDNX47; }; }; }; buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "LabSolitaire" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 1; knownRegions = ( - English, - Japanese, - French, - German, en, fr, ja, + de, ); mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; projectDirPath = ""; @@ -854,7 +820,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 28AD733F0D9D9553002E5188 /* MainWindow.xib in Resources */, 26DE151212F4A0C600FF88C6 /* TableLandscape.png in Resources */, 26DE151312F4A0C600FF88C6 /* TablePortrait.png in Resources */, 26C84EC91301027500EB4D52 /* 1S.png in Resources */, @@ -895,7 +860,6 @@ 26FF23341302F068005E20CB /* 10C.png in Resources */, 26FF23451302FDCA005E20CB /* 11C.png in Resources */, 26FF23461302FDCA005E20CB /* 12C.png in Resources */, - 2660DCEE18A210C00023B6DA /* LabSolitaire.storyboard in Resources */, 26FF23471302FDCA005E20CB /* 13C.png in Resources */, 26FF234B1302FDDA005E20CB /* 1D.png in Resources */, 26FF234C1302FDDA005E20CB /* 2D.png in Resources */, @@ -922,8 +886,6 @@ 2638162B131CDB73004E2983 /* NewSelectedP.png in Resources */, 2638162C131CDB73004E2983 /* UndoSelectedL.png in Resources */, 2638162D131CDB73004E2983 /* UndoSelectedP.png in Resources */, - 26B1E3E3131D608900DFA4EA /* iTunesArtwork in Resources */, - 26B1E41D131D6E6900DFA4EA /* Icon-72.png in Resources */, 268F0E8C131F3E22000DC644 /* Line.png in Resources */, 268F0EA0131F4525000DC644 /* CheckNo.png in Resources */, 268F0EA1131F4525000DC644 /* CheckYes.png in Resources */, @@ -946,8 +908,6 @@ 26F27B4D13F4592B00AB6EC1 /* Default-Landscape~ipad.png in Resources */, 26F27B4F13F4595300AB6EC1 /* Default-Portrait~ipad.png in Resources */, 2629CF1413F730150045FD23 /* Schematic.png in Resources */, - 2690A47F13F860B300984E0C /* LargeSolitaireIcon.png in Resources */, - 26C97421140E84B400D2C373 /* Localizable.strings in Resources */, 2628D34A141D3E8500BAF8F9 /* SelectedIndicator.png in Resources */, 2677A478142BF20900022E23 /* ParlourSolitaireAd.png in Resources */, 26471915143BE93600034152 /* Buzz.wav in Resources */, @@ -969,6 +929,7 @@ 26B670A214EF69FA00C8E6B2 /* 3D@2x.png in Resources */, 26B670A314EF69FA00C8E6B2 /* 3H@2x.png in Resources */, 26B670A414EF69FA00C8E6B2 /* 3S@2x.png in Resources */, + 4C37DCC02FA7AB1900825C1D /* Localizable.xcstrings in Resources */, 26B670A514EF69FA00C8E6B2 /* 4C@2x.png in Resources */, 26B670A614EF69FA00C8E6B2 /* 4D@2x.png in Resources */, 26B670A714EF69FA00C8E6B2 /* 4H@2x.png in Resources */, @@ -999,6 +960,7 @@ 26B670E414EF6AF100C8E6B2 /* 10S@2x.png in Resources */, 26B670E514EF6AF100C8E6B2 /* 11C@2x.png in Resources */, 26B670E614EF6AF100C8E6B2 /* 11D@2x.png in Resources */, + 4C37DCBE2FA79F2D00825C1D /* Assets.xcassets in Resources */, 26B670E714EF6AF100C8E6B2 /* 11H@2x.png in Resources */, 26B670E814EF6AF100C8E6B2 /* 11S@2x.png in Resources */, 26B670E914EF6AF100C8E6B2 /* 12C@2x.png in Resources */, @@ -1037,7 +999,6 @@ 263E7F241503090000BFA8D6 /* Line@2x.png in Resources */, 26E56B04150913CA00E21222 /* PaperTablet@2x.png in Resources */, 26E56B0615091D7400E21222 /* Schematic@2x.png in Resources */, - 26A292D91509BE49004F24F2 /* Icon-144.png in Resources */, 26A292FD1509E189004F24F2 /* Default-Landscape@2x~ipad.png in Resources */, 26A292FF1509E232004F24F2 /* Default-Portrait@2x~ipad.png in Resources */, ); @@ -1054,6 +1015,8 @@ 1D3623260D0F684500981E51 /* LabSolitaireAppDelegate.m in Sources */, 28D7ACF80DDB3853001CB0EB /* LabSolitaireViewController.m in Sources */, 26DE146912F4902B00FF88C6 /* CECard.m in Sources */, + 4C1616FA2FA67749002DEE82 /* LabSolitaireSceneDelegate.m in Sources */, + 4C1616F72FA61289002DEE82 /* LSAudioEngine.m in Sources */, 26DE146A12F4902B00FF88C6 /* CECardView.m in Sources */, 26DE146B12F4902B00FF88C6 /* CEStack.m in Sources */, 26DE146C12F4902B00FF88C6 /* CEStackView.m in Sources */, @@ -1062,10 +1025,6 @@ 26C84EA81301002000EB4D52 /* LSStackView.m in Sources */, 26ED7013143CD2BA00E06F05 /* LocalPlayer.m in Sources */, 26A13F9314438705000D8B18 /* CECardDealer.m in Sources */, - 26B3E29C148404EC00C3459E /* CDAudioManager.m in Sources */, - 26B3E29D148404EC00C3459E /* CDOpenALSupport.m in Sources */, - 26B3E29E148404EC00C3459E /* CocosDenshion.m in Sources */, - 26B3E29F148404EC00C3459E /* SimpleAudioEngine.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1080,16 +1039,6 @@ name = LabSolitaireViewController.xib; sourceTree = ""; }; - 26C97423140E84B400D2C373 /* Localizable.strings */ = { - isa = PBXVariantGroup; - children = ( - 26C97422140E84B400D2C373 /* en */, - 26C97424140E84CA00D2C373 /* ja */, - 26C97445140ECCFF00D2C373 /* fr */, - ); - name = Localizable.strings; - sourceTree = ""; - }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ @@ -1097,16 +1046,21 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_OBJC_ARC = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = 9FFTJDNX47; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = LabSolitaire_Prefix.pch; INFOPLIST_FILE = "LabSolitaire-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - PRODUCT_BUNDLE_IDENTIFIER = com.softdorothy.labsolitaire; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.card-games"; + IPHONEOS_DEPLOYMENT_TARGET = 17.6; + MARKETING_VERSION = 1.1.1; + PRODUCT_BUNDLE_IDENTIFIER = com.github.pullfrom.labsolitaire; PRODUCT_NAME = "Lab Solitaire"; PROVISIONING_PROFILE = ""; }; @@ -1116,14 +1070,19 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_OBJC_ARC = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; + DEVELOPMENT_TEAM = 9FFTJDNX47; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = LabSolitaire_Prefix.pch; INFOPLIST_FILE = "LabSolitaire-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - PRODUCT_BUNDLE_IDENTIFIER = com.softdorothy.labsolitaire; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.card-games"; + IPHONEOS_DEPLOYMENT_TARGET = 17.6; + MARKETING_VERSION = 1.1.1; + PRODUCT_BUNDLE_IDENTIFIER = com.github.pullfrom.labsolitaire; PRODUCT_NAME = "Lab Solitaire"; PROVISIONING_PROFILE = ""; VALIDATE_PRODUCT = YES; diff --git a/LabSolitaire.xcodeproj/xcshareddata/xcschemes/Lab Solitaire.xcscheme b/LabSolitaire.xcodeproj/xcshareddata/xcschemes/Lab Solitaire.xcscheme new file mode 100644 index 0000000..e5214ec --- /dev/null +++ b/LabSolitaire.xcodeproj/xcshareddata/xcschemes/Lab Solitaire.xcscheme @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Localizable.xcstrings b/Localizable.xcstrings new file mode 100644 index 0000000..8933604 --- /dev/null +++ b/Localizable.xcstrings @@ -0,0 +1,1391 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "\"Smart\"" : { + "comment" : "Putaway mode button - Smart", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "\"Smart\"" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "\"Smart\"" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intelligement" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "スマート" + } + } + } + }, + "% Won" : { + "comment" : "Leaderboard column header for win percentage", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "% Gewonnen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "% Won" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "% de Gagnés" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "勝ち率" + } + } + } + }, + "About…" : { + "comment" : "Tab header for About panel", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Info…" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "About…" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "A propos…" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ゲームについて" + } + } + } + }, + "All" : { + "comment" : "Button label for All (putaway mode and leaderboard scope)", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alle" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "All" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tout" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "全部" + } + } + } + }, + "Also available from Soft Dorothy:" : { + "comment" : "Label above app promotions in About panel", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ebenfalls von Soft Dorothy:" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Also available from Soft Dorothy:" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Également disponibles auprès de Soft Dorothy:" + } + } + } + }, + "Any" : { + "comment" : "Tableau label", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jede" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Any" + } + } + } + }, + "Artwork, design, programming: John Calhoun" : { + "comment" : "Credits line in About panel", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Grafik, Design, Programmierung: John Calhoun" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Artwork, design, programming: John Calhoun" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Illustrations, conception et programmation: John Calhoun" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "アート、デザイン、プログラム:John Calhoun" + } + } + } + }, + "Automatically put cards away:" : { + "comment" : "Settings label for auto-putaway toggle", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Karten automatisch ablegen:" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatically put cards away:" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Défausser automatiquement:" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "自動的にカードを除く" + } + } + } + }, + "Cancel" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abbrechen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancel" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annuler" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "キャンセル" + } + } + } + }, + "cells" : { + "comment" : "Diagram label in Rules panel", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zellen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "cells" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "case" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cell" + } + } + } + }, + "Congratulations, you won!" : { + "comment" : "Win message in game-over panel", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glückwunsch, du hast gewonnen!" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Congratulations, you won!" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Félicitations, c'est gagné!" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "おめでとう、あなたの勝ちです!" + } + } + } + }, + "Display:" : { + "comment" : "Settings label for display scope", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anzeige:" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Display:" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afficher:" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ディスプレー" + } + } + } + }, + "foundation" : { + "comment" : "Diagram label in Rules panel", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ablage" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "foundation" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "base" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Foundation" + } + } + } + }, + "Free" : { + "comment" : "Cell label shown when a free cell is empty", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Frei" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Free" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Free" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Free" + } + } + } + }, + "French localization: Laurent Saillard, Thomas Goossens" : { + "comment" : "French translator credits", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Französische Übersetzung: Laurent Saillard, Thomas Goossens" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "French localization: Laurent Saillard, Thomas Goossens" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Localisation Française: Laurent Saillard, Thomas Goossens" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "フランス語翻訳: Laurent Saillard, Thomas Goossens" + } + } + } + }, + "Friends" : { + "comment" : "Leaderboard scope button", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Freunde" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Friends" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Amis" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "友人" + } + } + } + }, + "Game Center" : { + "comment" : "Button label to open the system Game Center UI", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Game Center" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Game Center" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Game Center" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Game Center" + } + } + } + }, + "Games Played" : { + "comment" : "Statistics label", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gespielt" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Games Played" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jeux Joués" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "総ゲーム回数" + } + } + } + }, + "Games Won" : { + "comment" : "Statistics label", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gewonnen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Games Won" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jeux Gagnés" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "勝った回数" + } + } + } + }, + "Games Won: %1$d of %2$d (%3$@%%)" : { + "comment" : "Personal score summary in Settings tab. %1$d = games won, %2$d = games played, %3$@ = win percentage or em-dash", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gewonnen: %1$d von %2$d (%3$@%%)" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Games Won: %1$d of %2$d (%3$@%%)" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gagnées : %1$d sur %2$d (%3$@%%)" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "勝利: %2$d 中 %1$d (%3$@%%)" + } + } + } + }, + "Glider Classic" : { + "comment" : "App promotion button", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glider Classic" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glider Classic" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glider Classic" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Glider Classic" + } + } + } + }, + "If you start a new game this game will count as a loss." : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wenn du ein neues Spiel startest, zählt dieses als Niederlage." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "If you start a new game this game will count as a loss." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Si vous démarrer une nouvelle partie le jeu en cours sera abandonné." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "新しいゲームを始める場合、このゲームは負けとカウントされます。" + } + } + } + }, + "Japanese localization: Mayumi Hepburn" : { + "comment" : "Japanese translator credits", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Japanische Übersetzung: Mayumi Hepburn" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Japanese localization: Mayumi Hepburn" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Localisation Japonais: Mayumi Hepburn" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "日本語翻訳: Mayumi Hepburn" + } + } + } + }, + "Lab Solitaire" : { + "comment" : "App title in About panel", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lab Solitaire" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lab Solitaire" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lab Solitaire" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lab Solitaire" + } + } + } + }, + "Lab Solitaire Rules" : { + "comment" : "Rules panel title", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lab Solitaire Regeln" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lab Solitaire Rules" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rêgles du jeux de Lab Solitaire" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ラボソリタリ ルール" + } + } + } + }, + "Leaderboard Scores" : { + "comment" : "Leaderboard section title in Settings", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bestenliste" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Leaderboard Scores" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Classement" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Leaderboard" + } + } + } + }, + "Name" : { + "comment" : "Leaderboard column header", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Name" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Name" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nom" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "の名前" + } + } + } + }, + "New Game" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Neues Spiel" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "New Game" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nouvelle" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "新しいゲーム" + } + } + } + }, + "New Game Title" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Neues Spiel" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "New Game" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nouvelle partie" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "新しいゲーム" + } + } + } + }, + "Parlour Solitaire (Klondike)" : { + "comment" : "App promotion button", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Parlour Solitaire (Klondike)" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Parlour Solitaire (Klondike)" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Parlour Solitaire (Klondike)" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Parlour Solitaire (Klondike)" + } + } + } + }, + "Percentage Won" : { + "comment" : "Statistics label in game-over panel", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gewinnquote" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Percentage Won" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pourcentage de Gagnés" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "勝ち率" + } + } + } + }, + "Play sounds:" : { + "comment" : "Settings label for sound toggle", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Töne abspielen:" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Play sounds:" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jouer les sons:" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "サウンド" + } + } + } + }, + "Put away:" : { + "comment" : "Settings label for putaway mode", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ablegen:" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Put away:" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Défausser:" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "カードを除く:" + } + } + } + }, + "putaway.help" : { + "comment" : "Help text explaining Smart vs All putaway modes", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "\"Smart\" legt nur Karten ab, die nicht mehr gebraucht werden.\n\"Alle\" legt alle möglichen Karten ab." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "\"Smart\" only puts away cards no longer needed.\n\"All\" puts away all possible cards. " + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "\"Intelligement\" uniquement défausser les cartes qui ne sont plus utiles.\n \"Tout\" défausser toutes les cartes possibles." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "\"スマート\" 必要のないカードだけを除く\n\"全部\" 全部のカードを除く" + } + } + } + }, + "Reset" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zurücksetzen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reset" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Réinitialiser" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "リセットする" + } + } + } + }, + "Reset Statistics" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Statistik zurücksetzen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reset Statistics" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Réinitialisation des statistiques" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "結果をリセットする" + } + } + } + }, + "Reseting the statistics will clear your game history." : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Das Zurücksetzen der Statistik löscht deine gesamte Spielhistorie." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reseting the statistics will clear your game history." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Réinitialiser les statistiques effacera l'historique." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "リセットすると、これまでの結果がすべて消されます。" + } + } + } + }, + "Rules…" : { + "comment" : "Tab header for Rules panel", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Regeln…" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rules…" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rêgles…" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ルール" + } + } + } + }, + "rules.body" : { + "comment" : "Full rules body text in Rules panel", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lab Solitaire ist FreeCell-Solitär.\n\nZiel bei FreeCell ist es, alle Karten von den Tableau-Spalten unten auf die vier Ablagen oben rechts zu bewegen. Die Karten müssen nach Farbe sortiert in der Reihenfolge Ass bis König abgelegt werden. Nur die jeweils oberste Karte kann bewegt werden.\n\nEine Karte kann von einer Tableau-Spalte auf eine andere gelegt werden. Wie beim normalen Solitär darf eine Karte nur auf eine andere gelegt werden, die einen Rang höher und von entgegengesetzter Farbe (rot/schwarz) ist.\n\nFreeCell hat vier freie Zellen oben links, in die jede beliebige Karte vorübergehend gelegt werden kann.\n\nWenn eine Spalte im Tableau leer ist, kann jede beliebige Karte dort abgelegt werden – es muss kein König sein.\n\nTipps:\n – Doppeltippe auf eine Karte, um sie automatisch an die passende Stelle zu legen.\n – Mehrere Karten können gleichzeitig gezogen werden, wenn genug freie Zellen vorhanden sind.\n – Halte „Undo“ gedrückt, um dasselbe Spiel neu zu starten." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lab Solitaire is FreeCell solitaire \n\nIn FreeCell the object is to move all the cards from \nthe tableau columns below to the four foundations \nin the upper right of the layout. They must be placed on the foundations grouped\nby suits and in order from Ace to King. Only the topmost card can be moved.\n\nA card of a tableau column can be moved to another column in the tableau. As in\nstandard solitaire, you can only move a card onto another card that is one \ngreater in rank and opposite in color.\n\nFreeCell has four free “cells” in the top left of the layout where any card may be \ntemporarily moved.\n\nWhen a column is empty in the tableau, any card may be placed in that column –\nit does not have to be a King.\n\nShortcuts:\n – If you double-tap on a card, it will be moved to an appropriate location. \n – Multiple cards may be dragged if you have enough empty cells to have dragged them one at a time.\n – Touch and hold Undo to restart the same game again." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lab Solitaire est un jeux solitaire type FreeCell.\n\nDans FreeCell le but est de déplacer toutes les cartes depuis le tableau-colonne inférieur vers les 4 bases en haut à droite de l'écran. Les cartes doivent être montées dans les bases groupées par couleurs et dans l'ordre As vers Roi. Seule la carte la plus haute peut être déplacée.\n\nUne carte du tableau-colonne peut être déplacée d'une colonne à une autre. Comme dans le solitaire standard, vous ne pouvez superposer une carte que sur une autre carte de valeur juste directement supérieure et de la couleur opposée (noir ou rouge).\n\nFreeCell dispose de quatre cases libres en haut à gauche de l'écran où toute carte peut être placée temporairement.\n\nQuand une colonne est vide dans le tableau, n'importe quelle carte peut y être placée – elle ne doit pas être nécessairement un Roi.\n\nRaccourcis :\n – Si vous double-tapez sur une carte, elle sera déplacée automatiquement vers la position appropriée.\n – Plusieurs cartes peuvent être déplacées simultanément si vous avez assez de place.\n – Touch et tenez Undo pour redémarrer le même jeu de nouveau." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "このゲームは、すべてのカードを右上の\nFoundationにどれだけ早く移動できるか\nを競うゲームです。\n \nFoundationにはダイヤ、スペード、ハート、\nクローバーそれぞれエースからキングまで順番にカードを並べなけれ\nばなりません。\n\n一番上のカードだけを動かすことができます。\nカードの数字がひとつ大きくて、違った色(赤-黒)であればTableauにあるカードを他の列に動かして交換する\nことができます。\n \n左上のCellの部分には一時的に4枚までのカードをおくことができます。\n \nTableauの列のカードを使い果たした時は、\n他の列からカードを移動する事ができます。\n \nショートカット\n-カードを2度クリックして、自動的にカードを動かすことができます。\n-Cellがあいていれば、何枚かのカードを一度に動かすことができます。" + } + } + } + }, + "Settings…" : { + "comment" : "Tab header for Settings panel", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Einstellungen…" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Settings…" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paramètres…" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "設定" + } + } + } + }, + "Statistics" : { + "comment" : "Statistics section title in game-over panel", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Statistik" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Statistics" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Statistiques" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "結果" + } + } + } + }, + "tableau" : { + "comment" : "Diagram label in Rules panel", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tableau" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "tableau" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "tableau" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tableau" + } + } + } + }, + "to leave a review of Lab Solitaire." : { + "comment" : "Review prompt continuation text", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "um Lab Solitaire zu bewerten." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "to leave a review of Lab Solitaire." + } + } + } + }, + "Touch here" : { + "comment" : "Review prompt button in About panel", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hier tippen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Touch here" + } + } + } + }, + "Undo All" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alles zurück" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Undo All" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annuler tout" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "すべてを元に戻す" + } + } + } + }, + "Undo All Actions" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alles zurück" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Undo All" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annuler tout" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "すべてを元に戻す" + } + } + } + }, + "Without reviews, others are less likely to discover this game." : { + "comment" : "Review encouragement text in About panel", + "extractionState" : "manual", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ohne Bewertungen wird dieses Spiel seltener entdeckt." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Without reviews, others are less likely to discover this game." + } + } + } + }, + "You" : { + "comment" : "Leaderboard fallback when no player name is available", + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Du" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "You" + } + } + } + }, + "You can Undo all actions in this game." : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Du kannst alle Züge rückgängig machen und das Spiel mit der gleichen Kartenverteilung neu starten." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "You can Undo all actions in this game and restart it with the same original deal." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vous pouvez annuler toutes les actions dans ce jeu et de le redémarrer avec la même offre originale." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "このゲーム内のすべてのアクションを元に戻すと同じオリジナルの契約でそれを再起動することができます。" + } + } + } + } + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/README.md b/README.md index 84b93bc..40270f6 100644 --- a/README.md +++ b/README.md @@ -3,4 +3,4 @@ An iOS solitaire game (Freecell) for the iPad from 2010. Free Cell was one of my favorite solitaire card games. I wanted to make a very good implementation for the iPad that was also beautiful. -![Screenshot](https://github.com/softdorothy/LabSolitaire/blob/main/Screenshots/Screenshot%201.jpg) +![Screenshot](Screenshots/Screenshot%201.jpg) diff --git a/en.lproj/LabSolitaireViewController.xib b/en.lproj/LabSolitaireViewController.xib index 80c0e20..f80ca1b 100644 --- a/en.lproj/LabSolitaireViewController.xib +++ b/en.lproj/LabSolitaireViewController.xib @@ -1,33 +1,22 @@ - - - - + + - - + - - - - + - - - - - + - @@ -43,30 +32,33 @@ + - + - + + - + @@ -532,6 +401,7 @@ Player - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -867,23 +741,15 @@ Player - - + - + - - - - - - - diff --git a/en.lproj/Localizable.strings b/en.lproj/Localizable.strings deleted file mode 100644 index b0c285e..0000000 Binary files a/en.lproj/Localizable.strings and /dev/null differ diff --git a/fr.lproj/LabSolitaireViewController.xib b/fr.lproj/LabSolitaireViewController.xib deleted file mode 100644 index d905553..0000000 --- a/fr.lproj/LabSolitaireViewController.xib +++ /dev/null @@ -1,3255 +0,0 @@ - - - - 1296 - 11D50b - 2182 - 1138.32 - 568.00 - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 1179 - - - YES - IBUIViewController - IBUIButton - IBUIImageView - IBUIView - IBUILabel - IBProxyObject - - - YES - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - PluginDependencyRecalculationVersion - - - - YES - - IBFilesOwner - IBIPadFramework - - - IBFirstResponder - IBIPadFramework - - - - 274 - {{0, 20}, {768, 1004}} - - - - - 3 - MQA - - 2 - - - - 2 - - IBIPadFramework - - - - 292 - - YES - - - 292 - {{102, 11}, {112, 37}} - - - - NO - YES - 7 - NO - IBIPadFramework - A propos… - - 3 - MC4xOTc1ODA2NDUyAA - - - 3 - MQA - - 1 - 10 - 1 - - MarkerFelt-Thin - Marker Felt - 0 - 28 - - - MarkerFelt-Thin - 28 - 16 - - - - - 292 - {{79, 42}, {159, 14}} - - - - 9 - NO - IBIPadFramework - - NSImage - Underline.png - - - - - 292 - {{253, 121}, {413, 21}} - - - - NO - YES - 7 - NO - IBIPadFramework - Illustrations, conception et programmation: John Calhoun - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - - MarkerFelt-Thin - Marker Felt - 0 - 18 - - - MarkerFelt-Thin - 18 - 16 - - - - - 292 - {{253, 143}, {413, 21}} - - - - NO - YES - 7 - NO - IBIPadFramework - Localisation Française: Laurent Saillard, Thomas Goossens - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - - - - - - 292 - {{253, 165}, {413, 21}} - - - - NO - YES - 7 - NO - IBIPadFramework - Localisation Japonais: Mayumi Hepburn - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - - - - - - 292 - {{565, 187}, {84, 21}} - - - - NO - YES - 7 - NO - IBIPadFramework - v. 1.0.8 - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - 2 - - - - - - 292 - {{252, 81}, {330, 32}} - - - - NO - YES - 7 - NO - IBIPadFramework - Lab Solitaire - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - - - - - - 292 - {{253, 187}, {329, 21}} - - - - NO - YES - 7 - NO - IBIPadFramework - ©2011-2012 Soft Dorothy LLC - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - - - - - - 292 - {{631, 11}, {72, 37}} - - - - NO - IBIPadFramework - 0 - 0 - - 2 - MC43MjE1Njg2Mjc1IDAuMDMxMzcyNTQ5MDIgMC4wOTQxMTc2NDcwNgA - - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - - - 3 - MC41AA - - - NSImage - CloseBoxPressed.png - - - NSImage - CloseBox.png - - - Helvetica-Bold - Helvetica - 2 - 15 - - - Helvetica-Bold - 15 - 16 - - - - - 292 - {{481, 12}, {141, 37}} - - - - NO - IBIPadFramework - 0 - 0 - Paramètres… - - - 1 - MCAwIDAuNDk4MDMxNDk3IDAuODAwMDAwMDExOQA - - - - - - - - 292 - {{281, 12}, {106, 37}} - - - - NO - IBIPadFramework - 0 - 0 - Rêgles… - - - - - - - - - 292 - {{103, 231}, {568, 5}} - - - - NO - IBIPadFramework - - NSImage - Line.png - - - - - 292 - {{102, 381}, {580, 327}} - - - - NO - IBIPadFramework - - NSImage - Schematic.png - - - - - 292 - {{20, 646}, {119, 42}} - - - - 0.5 - NO - IBIPadFramework - - NSImage - SoftDorothy.png - - - - - 292 - {{106, 75}, {138, 138}} - - - - NO - IBIPadFramework - - NSImage - LargeSolitaireIcon.png - - - - - 292 - {{106, 293}, {68, 68}} - - - - NO - IBIPadFramework - 0 - 0 - - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - - - - NSImage - ParlourSolitaireAd.png - - - - - - - 292 - {{182, 317}, {186, 20}} - - - - NO - IBIPadFramework - 0 - 0 - Parlour Solitaire (Klondike) - - - 1 - MCAwIDAuNDk4MDMxNDk3IDAuODAwMDAwMDExOQA - - - - - - - - 292 - {{106, 258}, {323, 21}} - - - - NO - YES - 7 - NO - IBIPadFramework - Également disponibles auprès de Soft Dorothy: - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - - - - - - 292 - {{106, 369}, {69, 69}} - - - - NO - IBIPadFramework - 0 - 0 - - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - - - - NSImage - GliderAd.png - - - - - - - 292 - {{182, 394}, {89, 20}} - - - - NO - IBIPadFramework - 0 - 0 - Glider Classic - - - 1 - MCAwIDAuNDk4MDMxNDk3IDAuODAwMDAwMDExOQA - - - - - - - {708, 708} - - - - - 3 - MCAwAA - - NO - NO - IBIPadFramework - - - - 292 - - YES - - - 292 - {{281, 11}, {106, 37}} - - - - NO - YES - 7 - NO - IBIPadFramework - Rêgles… - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - 1 - - - - - - 292 - {{252, 42}, {159, 14}} - - - - 9 - NO - IBIPadFramework - - - - - 292 - {{103, 151}, {546, 500}} - - - - NO - YES - 7 - NO - IBIPadFramework - TGFiIFNvbGl0YWlyZSBlc3QgdW4gamV1eCBzb2xpdGFpcmUgdHlwZSBGcmVlQ2VsbC4KDURhbnMgRnJl -ZUNlbGwgbGUgYnV0IGVzdCBkZSBkw6lwbGFjZXIgdG91dGVzIGxlcyBjYXJ0ZXMgCmRlcHVpcyBsZSB0 -YWJsZWF1LWNvbG9ubmUgaW5mw6lyaWV1ciB2ZXJzIGxlcyA0IGJhc2VzIAplbiBoYXV0IMOgIGRyb2l0 -ZSBkZSBsJ8OpY3Jhbi4gTGVzIGNhcnRlcyBkb2l2ZW50IMOqdHJlIG1vbnTDqWVzIGRhbnMgbGVzIGJh -c2VzIApncm91cMOpZXMgcGFyIGNvdWxldXJzIGV0IGRhbnMgbCdvcmRyZSBBcyB2ZXJzIFJvaS4gU2V1 -bGUgbGEgY2FydGUgbGEgcGx1cyBoYXV0ZSAKcGV1dCDDqnRyZSBkw6lwbGFjw6llLg0NVW5lIGNhcnRl -IGR1IHRhYmxlYXUtY29sb25uZSBwZXV0IMOqdHJlIGTDqXBsYWPDqWUgZCd1bmUgY29sb25uZSDDoCB1 -bmUgYXV0cmUuIApDb21tZSBkYW5zIGxlIHNvbGl0YWlyZSBzdGFuZGFyZCwgdm91cyBuZSBwb3V2ZXog -c3VwZXJwb3NlciB1bmUgY2FydGUgcXVlIApzdXIgdW5lIGF1dHJlIGNhcnRlIGRlIHZhbGV1ciBqdXN0 -ZSBkaXJlY3RlbWVudCBzdXDDqXJpZXVyZSBldCBkZSBsYSBjb3VsZXVyIApvcHBvc8OpZSAobm9pciBv -dSByb3VnZSkuDQ1GcmVlQ2VsbCBkaXNwb3NlIGRlIHF1YXRyZSBjYXNlcyBsaWJyZXMgZW4gaGF1dCDD -oCBnYXVjaGUgZGUgbCfDqWNyYW4gb8O5IHRvdXRlIApjYXJ0ZSBwZXV0IMOqdHJlIHBsYWPDqWUgdGVt -cG9yYWlyZW1lbnQuDQ1RdWFuZCB1bmUgY29sb25uZSBlc3QgdmlkZSBkYW5zIGxlIHRhYmxlYXUsIG4n -aW1wb3J0ZSBxdWVsbGUgY2FydGUgcGV1dCB5IMOqdHJlIApwbGFjw6llIC0gZWxsZSBuZSBkb2l0IHBh -cyDDqnRyZSBuw6ljZXNzYWlyZW1lbnQgdW4gUm9pLg0NUmFjY291cmNpcyA6DSAtIFNpIHZvdXMgZG91 -YmxlLXRhcGV6IHN1ciB1bmUgY2FydGUsIGVsbGUgc2VyYSAKICAgZMOpcGxhY8OpZSBhdXRvbWF0aXF1 -ZW1lbnQgdmVycyBsYSBwb3NpdGlvbiBhcHByb3ByacOpZS4NIC0gUGx1c2lldXJzIGNhcnRlcyBwZXV2 -ZW50IMOqdHJlIGTDqXBsYWPDqWVzIHNpbXVsdGFuw6ltZW50IAogICBzaSB2b3VzIGF2ZXogYXNzZXog -ZGUgcGxhY2UuCiAtIFRvdWNoIGV0IHRlbmV6IFVuZG8gcG91ciByZWTDqW1hcnJlciBsZSBtw6ptZSBq -ZXUgZGUgbm91dmVhdS4 - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - 25 - 0 - - - NO - - - - 292 - {{103, 86}, {322, 32}} - - - - NO - YES - 7 - NO - IBIPadFramework - Rêgles du jeux de Lab Solitaire - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - - - - - - 292 - {{631, 11}, {72, 37}} - - - - NO - IBIPadFramework - 0 - 0 - - 2 - MC43MjE1Njg2Mjc1IDAuMDMxMzcyNTQ5MDIgMC4wOTQxMTc2NDcwNgA - - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - - - - - - - - - - 292 - {{103, 12}, {112, 37}} - - - - NO - IBIPadFramework - 0 - 0 - A propos… - - - - - - - - - 292 - {{481, 12}, {141, 37}} - - - - NO - IBIPadFramework - 0 - 0 - Paramètres… - - - - - - - - - 292 - {{494, 94}, {163, 104}} - - - - NO - IBIPadFramework - - NSImage - Layout.png - - - - - 292 - {{520, 76}, {26, 21}} - - - - NO - YES - 7 - NO - IBIPadFramework - case - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - 1 - - MarkerFelt-Thin - Marker Felt - 0 - 14 - - - MarkerFelt-Thin - 14 - 16 - - - - - 292 - {{586, 76}, {59, 21}} - - - - NO - YES - 7 - NO - IBIPadFramework - base - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - 1 - - - - - - 292 - {{554, 194}, {42, 20}} - - - - NO - YES - 7 - NO - IBIPadFramework - tableau - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - - - - - {708, 708} - - - - - NO - NO - IBIPadFramework - - - - 292 - - YES - - - 292 - {{481, 11}, {141, 37}} - - - NO - YES - 7 - NO - IBIPadFramework - Paramètres… - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - 1 - - - - - - 292 - {{473, 42}, {159, 14}} - - - 9 - NO - IBIPadFramework - - - - - 292 - {{103, 78}, {253, 21}} - - - NO - YES - 7 - NO - IBIPadFramework - Défausser automatiquement: - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - - - - - - 292 - {{631, 11}, {72, 37}} - - - NO - IBIPadFramework - 0 - 0 - - 2 - MC43MjE1Njg2Mjc1IDAuMDMxMzcyNTQ5MDIgMC4wOTQxMTc2NDcwNgA - - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - - - - - - - - - - 292 - {{103, 12}, {112, 37}} - - - NO - IBIPadFramework - 0 - 0 - A propos… - - - 1 - MCAwIDAuNDk4MDMxNDk3IDAuODAwMDAwMDExOQA - - - - - - - - 292 - {{281, 12}, {106, 37}} - - - NO - IBIPadFramework - 0 - 0 - Rêgles… - - - - - - - - - 292 - {{99, 231}, {568, 5}} - - - NO - IBIPadFramework - - - - - 292 - {{363, 68}, {40, 40}} - - - NO - IBIPadFramework - 2 - - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - - - - NSImage - CheckYes.png - - - - - - - 292 - {{103, 197}, {222, 21}} - - - NO - YES - 7 - NO - IBIPadFramework - Jouer les sons: - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - - - - - - 292 - {{363, 186}, {40, 40}} - - - NO - IBIPadFramework - 2 - - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - - - - - - - - - 292 - {{103, 254}, {229, 32}} - - - NO - YES - 7 - NO - IBIPadFramework - Classement - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - - - - - - 292 - {{134, 113}, {89, 21}} - - - NO - YES - 7 - NO - IBIPadFramework - Défausser: - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - - - - - - 292 - {{131, 147}, {407, 36}} - - - NO - YES - 7 - NO - IBIPadFramework - IkludGVsbGlnZW1lbnQiIHVuaXF1ZW1lbnQgZMOpZmF1c3NlciBsZXMgY2FydGVzIHF1aSBuZSBzb250 -IHBsdXMgdXRpbGVzLgogIlRvdXQiIGTDqWZhdXNzZXIgdG91dGVzIGxlcyBjYXJ0ZXMgcG9zc2libGVz -Lg - - 3 - MC41AA - - - - 1 - 10 - 2 - - MarkerFelt-Thin - Marker Felt - 0 - 15 - - - MarkerFelt-Thin - 15 - 16 - - - - - 292 - {{217, 110}, {102, 28}} - - - NO - IBIPadFramework - 0 - 0 - Intelligement - - - 1 - MCAwIDAuNDk4MDMxNDk2MSAwLjgAA - - - - - - - - 292 - {{327, 110}, {102, 28}} - - - NO - 1 - IBIPadFramework - 0 - 0 - Tout - - - 1 - MCAwIDAuNDk4MDMxNDk2MSAwLjgAA - - - - - - - - 292 - {{208, 106}, {120, 35}} - - - NO - NO - IBIPadFramework - - NSImage - SelectedIndicator.png - - - - - 292 - {{394, 261}, {89, 21}} - - - NO - YES - 7 - NO - IBIPadFramework - Afficher: - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - 2 - - - - - - 292 - {{500, 256}, {60, 32}} - - - NO - IBIPadFramework - 0 - 0 - Amis - - - 1 - MCAwIDAuNDk4MDMxNDk2MSAwLjgAA - - - - - - - - 292 - {{568, 256}, {60, 32}} - - - NO - 1 - IBIPadFramework - 0 - 0 - Tout - - - 1 - MCAwIDAuNDk4MDMxNDk2MSAwLjgAA - - - - - - - - 292 - {{491, 254}, {82, 35}} - - - NO - NO - IBIPadFramework - - - - - 292 - {{99, 328}, {533, 22}} - - - NO - IBIPadFramework - - NSImage - Highlight.png - - - - - 292 - {{110, 296}, {120, 21}} - - - NO - YES - 7 - NO - IBIPadFramework - Nom - - 3 - MC4yNQA - - - - 1 - 10 - - - - - - 292 - {{415, 296}, {120, 21}} - - - NO - YES - 7 - NO - IBIPadFramework - Jeux Joués - - 3 - MC4yNQA - - - - 1 - 10 - 1 - - - - - - 292 - {{299, 296}, {120, 21}} - - - NO - YES - 7 - NO - IBIPadFramework - Jeux Gagnés - - 3 - MC4yNQA - - - - 1 - 10 - 1 - - - - - - 292 - {{110, 329}, {220, 240}} - - - NO - YES - 7 - NO - IBIPadFramework - MQoyCjMKNAo1CjYKNwo4CjkKMTAKIDoKUGxheWVyA - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - 12 - - - - - - 292 - {{435, 329}, {80, 240}} - - - NO - YES - 7 - NO - IBIPadFramework - MQoyCjMKNAo1CjYKNwo4CjkKMTAKIDoKUGxheWVyA - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - 12 - 1 - - - - - - 292 - {{319, 329}, {80, 240}} - - - NO - YES - 7 - NO - IBIPadFramework - MQoyCjMKNAo1CjYKNwo4CjkKMTAKIDoKUGxheWVyA - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - 12 - 1 - - - - - - 292 - {{547, 329}, {80, 240}} - - NO - YES - 7 - NO - IBIPadFramework - MQoyCjMKNAo1CjYKNwo4CjkKMTAKIDoKUGxheWVyA - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - 12 - 1 - - - - - - 292 - {{539, 296}, {96, 21}} - - - NO - YES - 7 - NO - IBIPadFramework - % de Gagnés - - 3 - MC4yNQA - - - - 1 - 10 - 1 - - - - - - 292 - {{102, 320}, {189, 3}} - - - NO - IBIPadFramework - - NSImage - UnderlineWide.png - - - - - 292 - {{536, 320}, {102, 3}} - - - NO - IBIPadFramework - - NSImage - UnderlineNarrow.png - - - - - 292 - {{308, 320}, {102, 3}} - - - NO - IBIPadFramework - - - - - 292 - {{424, 320}, {102, 3}} - - - NO - IBIPadFramework - - - - {708, 708} - - - NO - NO - IBIPadFramework - - - - 292 - - YES - - - 292 - {{176, 50}, {356, 36}} - - - - NO - NO - IBIPadFramework - - NSImage - HighlightFat.png - - - - - 292 - {{134, 198}, {122, 21}} - - - NO - YES - 7 - NO - IBIPadFramework - Jeux Joués - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - - - - - - 292 - {{572, 198}, {88, 21}} - - - NO - YES - 7 - NO - IBIPadFramework - 0 - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - - - - - - 292 - {{572, 231}, {88, 21}} - - - NO - YES - 7 - NO - IBIPadFramework - 0% - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - - - - - - 292 - {{572, 165}, {88, 21}} - - - NO - YES - 7 - NO - IBIPadFramework - 0 - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - - - - - - 292 - {{134, 165}, {122, 21}} - - - NO - YES - 7 - NO - IBIPadFramework - Jeux Gagnés - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - - - - - - 292 - {{134, 231}, {158, 21}} - - - NO - YES - 7 - NO - IBIPadFramework - Pourcentage de Gagnés - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - - - - - - 292 - {{631, 11}, {72, 37}} - - - NO - IBIPadFramework - 0 - 0 - - 2 - MC43MjE1Njg2Mjc1IDAuMDMxMzcyNTQ5MDIgMC4wOTQxMTc2NDcwNgA - - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - - - - - - - - - - 292 - {{99, 96}, {568, 5}} - - - NO - IBIPadFramework - - - - - 292 - {{303, 206}, {255, 13}} - - - NO - IBIPadFramework - - NSImage - Dots.png - - - - - 292 - {{303, 173}, {255, 13}} - - - NO - IBIPadFramework - - - - - 292 - {{303, 239}, {255, 13}} - - - NO - IBIPadFramework - - - - - 292 - {{103, 120}, {229, 32}} - - - NO - YES - 7 - NO - IBIPadFramework - Statistiques - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - - - - - - 292 - {{213, 52}, {281, 32}} - - - NO - YES - 7 - NO - IBIPadFramework - Félicitations, c'est gagné! - - 3 - MC4xOTc1ODA2NDUyAA - - - 1 - 10 - - - - - - 292 - {{102, 381}, {580, 327}} - - NO - IBIPadFramework - - - - {708, 708} - - - NO - NO - IBIPadFramework - - - - 2 - - - 1 - 1 - - IBIPadFramework - NO - - - - - YES - - - view - - - - 3 - - - - aboutView - - - - 13 - - - - settingsView - - - - 39 - - - - rulesView - - - - 57 - - - - autoPutawayButton - - - - 77 - - - - gameOverView - - - - 114 - - - - _smartPutawayButton - - - - 176 - - - - _allPutawayButton - - - - 177 - - - - _putawaySelectedImage - - - - 178 - - - - _smartPutawayModeLabel - - - - 179 - - - - _displayScopeLabel - - - - 208 - - - - _scopeSelectedImage - - - - 209 - - - - _friendScopeButton - - - - 210 - - - - _allScopeButton - - - - 211 - - - - _globalScoreNameLabel - - - - 216 - - - - _globalScorePlayedLabel - - - - 217 - - - - _globalScoreWonLabel - - - - 218 - - - - _globalScorePercentLabel - - - - 219 - - - - _highlightView - - - - 220 - - - - _gamesPlayedLabel - - - - 230 - - - - _gamesWonLabel - - - - 231 - - - - _gamesWonPercentageLabel - - - - 232 - - - - _playSoundsButton - - - - 233 - - - - closeInfo: - - - 7 - - 16 - - - - settingsInfo: - - - 7 - - 18 - - - - rulesInfo: - - - 7 - - 21 - - - - rulesInfo: - - - 7 - - 34 - - - - closeInfo: - - - 7 - - 35 - - - - aboutInfo: - - - 7 - - 38 - - - - aboutInfo: - - - 7 - - 52 - - - - closeInfo: - - - 7 - - 54 - - - - settingsInfo: - - - 7 - - 56 - - - - toggleAutoPutaway: - - - 7 - - 65 - - - - toggleSound: - - - 7 - - 85 - - - - closeInfo: - - - 7 - - 110 - - - - selectAutoPutawayMode: - - - 7 - - 174 - - - - selectAutoPutawayMode: - - - 7 - - 175 - - - - openParlourSolitaireInAppStore: - - - 7 - - 185 - - - - openParlourSolitaireInAppStore: - - - 7 - - 186 - - - - selectLeaderboardScope: - - - 7 - - 227 - - - - selectLeaderboardScope: - - - 7 - - 228 - - - - openGliderInAppStore: - - - 7 - - 236 - - - - openGliderInAppStore: - - - 7 - - 237 - - - - - YES - - 0 - - YES - - - - - - -1 - - - File's Owner - - - -2 - - - - - 2 - - - YES - - - - - 4 - - - YES - - - - - - - - - - - - - - - - - - - - - - - AboutView - - - 6 - - - - - 7 - - - - - 9 - - - - - 12 - - - - - 14 - - - - - 15 - - - - - 17 - - - - - 19 - - - - - 22 - - - YES - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SettingsView - - - 23 - - - - - 25 - - - - - 26 - - - - - 29 - - - - - 30 - - - - - 36 - - - - - 40 - - - YES - - - - - - - - - - - - - - RulesView - - - 41 - - - - - 43 - - - - - 45 - - - - - 48 - - - - - 49 - - - - - 55 - - - - - 47 - - - - - 59 - - - - - 60 - - - - - 61 - - - - - 62 - - - - - 63 - - - - - 64 - - - - - 66 - - - - - 68 - - - - - 81 - - - - - 82 - - - - - 87 - - - YES - - - - - - - - - - - - - - - - - - GameOverView - - - 91 - - - - - 92 - - - - - 93 - - - - - 94 - - - - - 95 - - - - - 96 - - - - - 97 - - - - - 98 - - - - - 99 - - - - - 100 - - - - - 102 - - - - - 106 - - - - - 113 - - - - - 124 - - - - - 157 - - - - - 158 - - - - - 160 - - - - - 161 - - - - - 162 - - - - - 169 - - - - - 170 - - - - - 171 - - - - - 172 - - - - - 173 - - - - - 180 - - - - - 181 - - - - - 182 - - - - - 183 - - - - - 184 - - - - - 187 - - - - - 188 - - - - - 189 - - - - - 190 - - - - - 191 - - - - - 193 - - - - - 194 - - - - - 195 - - - - - 196 - - - - - 197 - - - - - 198 - - - - - 199 - - - - - 200 - - - - - 203 - - - - - 204 - - - - - 206 - - - - - 207 - - - - - 229 - - - - - 234 - - - - - 235 - - - - - - - YES - - YES - -1.CustomClassName - -1.IBPluginDependency - -2.CustomClassName - -2.IBPluginDependency - 100.IBPluginDependency - 102.IBPluginDependency - 106.IBPluginDependency - 113.IBPluginDependency - 12.IBPluginDependency - 124.IBPluginDependency - 14.IBPluginDependency - 15.IBPluginDependency - 157.IBPluginDependency - 158.IBPluginDependency - 160.IBPluginDependency - 161.IBPluginDependency - 162.IBPluginDependency - 169.IBPluginDependency - 17.IBPluginDependency - 17.IBUIButtonInspectorSelectedStateConfigurationMetadataKey - 170.IBPluginDependency - 171.IBPluginDependency - 171.IBUIButtonInspectorSelectedStateConfigurationMetadataKey - 172.IBPluginDependency - 172.IBUIButtonInspectorSelectedStateConfigurationMetadataKey - 173.IBPluginDependency - 180.IBPluginDependency - 181.IBPluginDependency - 182.IBPluginDependency - 183.IBPluginDependency - 183.IBUIButtonInspectorSelectedStateConfigurationMetadataKey - 184.IBPluginDependency - 187.IBPluginDependency - 188.IBPluginDependency - 188.IBUIButtonInspectorSelectedStateConfigurationMetadataKey - 189.IBPluginDependency - 189.IBUIButtonInspectorSelectedStateConfigurationMetadataKey - 19.IBPluginDependency - 19.IBUIButtonInspectorSelectedStateConfigurationMetadataKey - 190.IBPluginDependency - 191.IBPluginDependency - 193.IBPluginDependency - 194.IBPluginDependency - 195.IBPluginDependency - 196.IBPluginDependency - 197.IBPluginDependency - 198.IBPluginDependency - 199.IBPluginDependency - 2.CustomClassName - 2.IBPluginDependency - 200.IBPluginDependency - 203.IBPluginDependency - 204.IBPluginDependency - 206.IBPluginDependency - 207.IBPluginDependency - 22.IBPluginDependency - 229.IBPluginDependency - 23.IBPluginDependency - 23.IBUIButtonInspectorSelectedStateConfigurationMetadataKey - 234.IBPluginDependency - 235.IBPluginDependency - 235.IBUIButtonInspectorSelectedStateConfigurationMetadataKey - 25.IBPluginDependency - 26.IBPluginDependency - 29.IBPluginDependency - 30.IBPluginDependency - 36.IBPluginDependency - 36.IBUIButtonInspectorSelectedStateConfigurationMetadataKey - 4.IBPluginDependency - 40.IBPluginDependency - 41.IBPluginDependency - 41.IBUIButtonInspectorSelectedStateConfigurationMetadataKey - 43.IBPluginDependency - 45.IBPluginDependency - 47.IBPluginDependency - 48.IBPluginDependency - 49.IBPluginDependency - 49.IBUIButtonInspectorSelectedStateConfigurationMetadataKey - 55.IBPluginDependency - 59.IBPluginDependency - 6.IBPluginDependency - 60.IBPluginDependency - 61.IBPluginDependency - 62.IBPluginDependency - 63.IBPluginDependency - 64.IBPluginDependency - 66.IBPluginDependency - 68.IBPluginDependency - 7.IBPluginDependency - 81.IBPluginDependency - 82.IBPluginDependency - 87.IBPluginDependency - 9.IBPluginDependency - 91.IBPluginDependency - 92.IBPluginDependency - 93.IBPluginDependency - 94.IBPluginDependency - 95.IBPluginDependency - 96.IBPluginDependency - 97.IBPluginDependency - 98.IBPluginDependency - 99.IBPluginDependency - - - YES - LabSolitaireViewController - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - UIResponder - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - CETableView - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - - YES - - - - - - YES - - - - - 237 - - - - YES - - CETableView - UIView - - IBProjectSource - ./Classes/CETableView.h - - - - LabSolitaireViewController - UIViewController - - YES - - YES - aboutInfo: - closeInfo: - info: - openGameOverView: - openGliderInAppStore: - openLabSolitaireInAppStore: - openParlourSolitaireInAppStore: - rulesInfo: - selectAutoPutawayMode: - selectLeaderboardScope: - settingsInfo: - toggleAutoPutaway: - toggleSound: - - - YES - id - id - id - id - id - id - id - id - id - id - id - id - id - - - - YES - - YES - aboutInfo: - closeInfo: - info: - openGameOverView: - openGliderInAppStore: - openLabSolitaireInAppStore: - openParlourSolitaireInAppStore: - rulesInfo: - selectAutoPutawayMode: - selectLeaderboardScope: - settingsInfo: - toggleAutoPutaway: - toggleSound: - - - YES - - aboutInfo: - id - - - closeInfo: - id - - - info: - id - - - openGameOverView: - id - - - openGliderInAppStore: - id - - - openLabSolitaireInAppStore: - id - - - openParlourSolitaireInAppStore: - id - - - rulesInfo: - id - - - selectAutoPutawayMode: - id - - - selectLeaderboardScope: - id - - - settingsInfo: - id - - - toggleAutoPutaway: - id - - - toggleSound: - id - - - - - YES - - YES - _aboutView - _allPutawayButton - _allScopeButton - _autoPutawayButton - _displayScopeLabel - _friendScopeButton - _gameOverView - _gamesPlayedLabel - _gamesWonLabel - _gamesWonPercentageLabel - _globalScoreNameLabel - _globalScorePercentLabel - _globalScorePlayedLabel - _globalScoreWonLabel - _highlightView - _playSoundsButton - _putawaySelectedImage - _rulesView - _scopeSelectedImage - _settingsView - _smartPutawayButton - _smartPutawayModeLabel - - - YES - UIView - UIButton - UIButton - UIButton - UILabel - UIButton - UIView - UILabel - UILabel - UILabel - UILabel - UILabel - UILabel - UILabel - UIImageView - UIButton - UIImageView - UIView - UIImageView - UIView - UIButton - UILabel - - - - YES - - YES - _aboutView - _allPutawayButton - _allScopeButton - _autoPutawayButton - _displayScopeLabel - _friendScopeButton - _gameOverView - _gamesPlayedLabel - _gamesWonLabel - _gamesWonPercentageLabel - _globalScoreNameLabel - _globalScorePercentLabel - _globalScorePlayedLabel - _globalScoreWonLabel - _highlightView - _playSoundsButton - _putawaySelectedImage - _rulesView - _scopeSelectedImage - _settingsView - _smartPutawayButton - _smartPutawayModeLabel - - - YES - - _aboutView - UIView - - - _allPutawayButton - UIButton - - - _allScopeButton - UIButton - - - _autoPutawayButton - UIButton - - - _displayScopeLabel - UILabel - - - _friendScopeButton - UIButton - - - _gameOverView - UIView - - - _gamesPlayedLabel - UILabel - - - _gamesWonLabel - UILabel - - - _gamesWonPercentageLabel - UILabel - - - _globalScoreNameLabel - UILabel - - - _globalScorePercentLabel - UILabel - - - _globalScorePlayedLabel - UILabel - - - _globalScoreWonLabel - UILabel - - - _highlightView - UIImageView - - - _playSoundsButton - UIButton - - - _putawaySelectedImage - UIImageView - - - _rulesView - UIView - - - _scopeSelectedImage - UIImageView - - - _settingsView - UIView - - - _smartPutawayButton - UIButton - - - _smartPutawayModeLabel - UILabel - - - - - IBProjectSource - ./Classes/LabSolitaireViewController.h - - - - - 0 - IBIPadFramework - - com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS - - - - com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 - - - YES - 3 - - YES - - YES - CheckYes.png - CloseBox.png - CloseBoxPressed.png - Dots.png - GliderAd.png - Highlight.png - HighlightFat.png - LargeSolitaireIcon.png - Layout.png - Line.png - ParlourSolitaireAd.png - Schematic.png - SelectedIndicator.png - SoftDorothy.png - Underline.png - UnderlineNarrow.png - UnderlineWide.png - - - YES - {36, 32} - {27, 28} - {27, 28} - {255, 13} - {69, 69} - {533, 22} - {356, 36} - {138, 138} - {163, 104} - {568, 5} - {68, 68} - {580, 327} - {82, 35} - {119, 42} - {159, 14} - {102, 3} - {189, 3} - - - 1179 - - diff --git a/fr.lproj/Localizable.strings b/fr.lproj/Localizable.strings deleted file mode 100644 index 47a1656..0000000 Binary files a/fr.lproj/Localizable.strings and /dev/null differ diff --git a/iTunesArtwork-1024.png b/iTunesArtwork-1024.png new file mode 100644 index 0000000..92a79db Binary files /dev/null and b/iTunesArtwork-1024.png differ diff --git a/ja.lproj/LabSolitaireViewController.xib b/ja.lproj/LabSolitaireViewController.xib deleted file mode 100644 index 1475bf0..0000000 --- a/ja.lproj/LabSolitaireViewController.xib +++ /dev/null @@ -1,817 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/ja.lproj/Localizable.strings b/ja.lproj/Localizable.strings deleted file mode 100644 index 574c84f..0000000 Binary files a/ja.lproj/Localizable.strings and /dev/null differ diff --git a/main.m b/main.m index 67ee62b..0b88738 100644 --- a/main.m +++ b/main.m @@ -4,15 +4,14 @@ #import +#import "LabSolitaireAppDelegate.h" int main (int argc, char *argv[]) -{ - NSAutoreleasePool *pool; - - pool = [[NSAutoreleasePool alloc] init]; - int retVal = UIApplicationMain (argc, argv, nil, @"LabSolitaireAppDelegate"); - [pool release]; - - return retVal; +{ + @autoreleasepool + { + int retVal = UIApplicationMain (argc, argv, nil, NSStringFromClass ([LabSolitaireAppDelegate class])); + return retVal; + } }