# HG changeset patch # User Jens Alfke # Date 1212004030 25200 # Node ID 45c82a071acab4309390d05fefcf45a80850d644 # Parent 428a194e3e59ed2afb59af89f58b36802c53e9ad * Got it working with latest iPhone SDK. * Fixed some text alignment issues that showed up on PlayingCards. * Working on persistence and move-tracking for Game. diff -r 428a194e3e59 -r 45c82a071aca English.lproj/MainMenu.nib/designable.nib --- a/English.lproj/MainMenu.nib/designable.nib Sun Mar 16 15:06:47 2008 -0700 +++ b/English.lproj/MainMenu.nib/designable.nib Wed May 28 12:47:10 2008 -0700 @@ -41,7 +41,7 @@ NSImage NSMenuCheckmark - + NSImage NSMenuMixedState @@ -56,7 +56,7 @@ 2147483647 - + @@ -67,7 +67,7 @@ 1048576 2147483647 - + @@ -76,7 +76,7 @@ 1048576 2147483647 - + @@ -87,7 +87,7 @@ 1048576 2147483647 - + @@ -96,7 +96,7 @@ 1048576 2147483647 - + submenuAction: Services @@ -115,7 +115,7 @@ 1048576 2147483647 - + @@ -124,7 +124,7 @@ 1048576 2147483647 - + @@ -133,7 +133,7 @@ 1572864 2147483647 - + @@ -142,7 +142,7 @@ 1048576 2147483647 - + @@ -153,7 +153,7 @@ 1048576 2147483647 - + @@ -162,7 +162,7 @@ 1048576 2147483647 - + _NSAppleMenu @@ -175,7 +175,7 @@ 1048576 2147483647 - + submenuAction: File @@ -188,7 +188,7 @@ 1048576 2147483647 - + @@ -197,7 +197,7 @@ 1048576 2147483647 - + @@ -206,7 +206,7 @@ 1048576 2147483647 - + submenuAction: Open Recent @@ -219,7 +219,7 @@ 1048576 2147483647 - + _NSRecentDocumentsMenu @@ -234,7 +234,7 @@ 1048576 2147483647 - + @@ -243,7 +243,7 @@ 1048576 2147483647 - + @@ -252,7 +252,7 @@ 1048576 2147483647 - + @@ -261,7 +261,7 @@ 1179648 2147483647 - + @@ -269,7 +269,7 @@ 2147483647 - + @@ -280,7 +280,7 @@ 1048576 2147483647 - + @@ -289,7 +289,7 @@ 1179648 2147483647 - + @@ -299,7 +299,7 @@ 1048576 2147483647 - + @@ -311,7 +311,7 @@ 1048576 2147483647 - + submenuAction: Edit @@ -324,7 +324,7 @@ 1048576 2147483647 - + @@ -333,7 +333,7 @@ 1179648 2147483647 - + @@ -344,7 +344,7 @@ 1048576 2147483647 - + @@ -353,7 +353,7 @@ 1048576 2147483647 - + @@ -362,7 +362,7 @@ 1048576 2147483647 - + @@ -371,7 +371,7 @@ 1048576 2147483647 - + @@ -380,7 +380,7 @@ 1048576 2147483647 - + @@ -389,7 +389,7 @@ 1048576 2147483647 - + @@ -400,7 +400,7 @@ 1048576 2147483647 - + @@ -409,7 +409,7 @@ 1048576 2147483647 - + submenuAction: Speech @@ -422,7 +422,7 @@ 1048576 2147483647 - + @@ -431,7 +431,7 @@ 1048576 2147483647 - + @@ -446,7 +446,7 @@ 1048576 2147483647 - + submenuAction: Game @@ -459,7 +459,7 @@ 1048840 2147483647 - + @@ -470,7 +470,7 @@ 1048576 2147483647 - + @@ -479,7 +479,7 @@ 1048840 2147483647 - + 1 @@ -489,7 +489,7 @@ 1048840 2147483647 - + 4 @@ -499,7 +499,7 @@ 1048840 2147483647 - + 2 @@ -509,7 +509,7 @@ 1048840 2147483647 - + 3 @@ -522,7 +522,7 @@ 1048576 2147483647 - + submenuAction: View @@ -535,7 +535,7 @@ 1048576 2147483647 - + @@ -546,7 +546,7 @@ 1048576 2147483647 - + @@ -555,7 +555,7 @@ 1572864 2147483647 - + @@ -564,7 +564,7 @@ 1048576 2147483647 - + @@ -576,7 +576,7 @@ 1048576 2147483647 - + submenuAction: Window @@ -589,7 +589,7 @@ 1048576 2147483647 - + @@ -598,7 +598,7 @@ 1048576 2147483647 - + @@ -609,7 +609,7 @@ 1048576 2147483647 - + @@ -618,7 +618,7 @@ 1048576 2147483647 - + _NSWindowsMenu @@ -631,7 +631,7 @@ 1048576 2147483647 - + submenuAction: Help @@ -664,13 +664,13 @@ YES DemoBoardView - + 290 {{162, 1}, {754, 25}} YES - + -2080244224 0 @@ -679,7 +679,7 @@ 1.200000e+01 16 - + 1.000000e+00 0.000000e+00 0.000000e+00 @@ -975,14 +975,6 @@ - initialFirstResponder - - - - 399 - - - delegate @@ -993,10 +985,18 @@ _turnSlider - + 403 + + + initialFirstResponder + + + + 404 + @@ -1454,7 +1454,7 @@ YES - + @@ -1528,17 +1528,17 @@ 401 - + YES - + 402 - - + + @@ -1877,7 +1877,7 @@ - 403 + 404 @@ -1898,8 +1898,21 @@ DemoBoardView BoardView - startGameFromMenu: - id + YES + + YES + enterFullScreen: + redo: + startGameFromMenu: + undo: + + + YES + id + id + id + id + _turnSlider diff -r 428a194e3e59 -r 45c82a071aca English.lproj/MainMenu.nib/keyedobjects.nib Binary file English.lproj/MainMenu.nib/keyedobjects.nib has changed diff -r 428a194e3e59 -r 45c82a071aca GeekGameBoard-iPhone.xcodeproj/project.pbxproj --- a/GeekGameBoard-iPhone.xcodeproj/project.pbxproj Sun Mar 16 15:06:47 2008 -0700 +++ b/GeekGameBoard-iPhone.xcodeproj/project.pbxproj Wed May 28 12:47:10 2008 -0700 @@ -408,14 +408,15 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ARCHS = "$(ARCHS_STANDARD_32_BIT)"; - "CODE_SIGN_IDENTITY[sdk=aspen*]" = "iPhone Developer"; - GCC_C_LANGUAGE_STANDARD = c99; + CODE_SIGN_IDENTITY = "Laurence ANDERSEN"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Laurence ANDERSEN"; + GCC_C_LANGUAGE_STANDARD = gnu99; GCC_TREAT_WARNINGS_AS_ERRORS = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; ONLY_ACTIVE_ARCH = YES; PREBINDING = NO; - SDKROOT = aspen1.2; + SDKROOT = iphoneos2.0; WARNING_CFLAGS = "-Wall"; }; name = Debug; @@ -424,9 +425,12 @@ isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + CODE_SIGN_IDENTITY = "Laurence ANDERSEN"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Laurence ANDERSEN"; + GCC_C_LANGUAGE_STANDARD = gnu99; GCC_TREAT_WARNINGS_AS_ERRORS = YES; PREBINDING = NO; - SDKROOT = aspen1.2; + SDKROOT = iphoneos2.0; WARNING_CFLAGS = "-Wall"; }; name = Release; diff -r 428a194e3e59 -r 45c82a071aca GeekGameBoard.xcodeproj/project.pbxproj --- a/GeekGameBoard.xcodeproj/project.pbxproj Sun Mar 16 15:06:47 2008 -0700 +++ b/GeekGameBoard.xcodeproj/project.pbxproj Wed May 28 12:47:10 2008 -0700 @@ -36,6 +36,7 @@ 27CCAABD0CB92A9F001CFE24 /* Card.m in Sources */ = {isa = PBXBuildFile; fileRef = 27CCAABC0CB92A9F001CFE24 /* Card.m */; }; 27CCABBF0CB9496B001CFE24 /* Bit.m in Sources */ = {isa = PBXBuildFile; fileRef = 27CCABBE0CB9496B001CFE24 /* Bit.m */; }; 27CCAC750CB95C2B001CFE24 /* PlayingCard.m in Sources */ = {isa = PBXBuildFile; fileRef = 27CCAC740CB95C2B001CFE24 /* PlayingCard.m */; }; + 27D014C00D8DFB4500615ADD /* Game-Persistence.m in Sources */ = {isa = PBXBuildFile; fileRef = 27D014BF0D8DFB4500615ADD /* Game-Persistence.m */; }; 27D4F1260CCF011200923605 /* Stack.m in Sources */ = {isa = PBXBuildFile; fileRef = 27D4F1250CCF011200923605 /* Stack.m */; }; 27DFC4410CCD01B7005E34CE /* GoGame.m in Sources */ = {isa = PBXBuildFile; fileRef = 27DFC4400CCD01B7005E34CE /* GoGame.m */; }; 27F230B90CD1A61B006939C1 /* KlondikeGame.m in Sources */ = {isa = PBXBuildFile; fileRef = 27F230B80CD1A61B006939C1 /* KlondikeGame.m */; }; @@ -99,6 +100,8 @@ 27CCABBE0CB9496B001CFE24 /* Bit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Bit.m; sourceTree = ""; }; 27CCAC730CB95C2B001CFE24 /* PlayingCard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlayingCard.h; sourceTree = ""; }; 27CCAC740CB95C2B001CFE24 /* PlayingCard.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PlayingCard.m; sourceTree = ""; }; + 27D014BE0D8DFB4500615ADD /* Game-Persistence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Game-Persistence.h"; sourceTree = ""; }; + 27D014BF0D8DFB4500615ADD /* Game-Persistence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "Game-Persistence.m"; sourceTree = ""; }; 27D4F1240CCF011200923605 /* Stack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Stack.h; sourceTree = ""; }; 27D4F1250CCF011200923605 /* Stack.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Stack.m; sourceTree = ""; }; 27DFC43F0CCD01B7005E34CE /* GoGame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GoGame.h; sourceTree = ""; }; @@ -216,6 +219,8 @@ children = ( 27275C490CC700F2009C4C6C /* Game.h */, 27275C4A0CC700F2009C4C6C /* Game.m */, + 27D014BE0D8DFB4500615ADD /* Game-Persistence.h */, + 27D014BF0D8DFB4500615ADD /* Game-Persistence.m */, 27275C900CC7C578009C4C6C /* TicTacToeGame.h */, 27275C910CC7C578009C4C6C /* TicTacToeGame.m */, 2734B4EE0CCA5BDB0070C008 /* CheckersGame.h */, @@ -368,6 +373,7 @@ 27C999C30D81185E005AFD4F /* GGBUtils.m in Sources */, 279F4D870D8606C200B32DBF /* GGBLayer.m in Sources */, 279F4D880D8606C200B32DBF /* GGBTextLayer.m in Sources */, + 27D014C00D8DFB4500615ADD /* Game-Persistence.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff -r 428a194e3e59 -r 45c82a071aca Source/Bit.m --- a/Source/Bit.m Sun Mar 16 15:06:47 2008 -0700 +++ b/Source/Bit.m Wed May 28 12:47:10 2008 -0700 @@ -107,7 +107,7 @@ } self.zPosition = z; -#if !TARGET_OS_ASPEN +#if !TARGET_OS_IPHONE self.shadowOpacity = shadow; self.shadowOffset = CGSizeMake(offset,-offset); self.shadowRadius = radius; diff -r 428a194e3e59 -r 45c82a071aca Source/BoardUIView.m --- a/Source/BoardUIView.m Sun Mar 16 15:06:47 2008 -0700 +++ b/Source/BoardUIView.m Wed May 28 12:47:10 2008 -0700 @@ -122,7 +122,7 @@ UITouch *touch = touches.anyObject; BOOL placing = NO; - _dragStartPos = touch.locationInView; + _dragStartPos = [touch locationInView: self]; _dragBit = (Bit*) [self hitTestPoint: _dragStartPos forLayerMatching: layerIsBit offset: &_dragOffset]; @@ -188,7 +188,7 @@ if( _dragBit ) { // Get the mouse position, and see if we've moved 3 pixels since the mouseDown: - CGPoint pos = touch.locationInView; + CGPoint pos = [touch locationInView: self]; if( fabs(pos.x-_dragStartPos.x)>=3 || fabs(pos.y-_dragStartPos.y)>=3 ) _dragMoved = YES; diff -r 428a194e3e59 -r 45c82a071aca Source/Card.m --- a/Source/Card.m Sun Mar 16 15:06:47 2008 -0700 +++ b/Source/Card.m Wed May 28 12:47:10 2008 -0700 @@ -135,7 +135,7 @@ GGBLayer *back = [[GGBLayer alloc] init]; back.bounds = CGRectMake(0,0,size.width,size.height); back.position = CGPointMake(sCardSize.width/2,sCardSize.height/2); -#if TARGET_OS_ASPEN +#if TARGET_OS_IPHONE back.backgroundColor = CreateRGB(0.0,0.5,0.5, 1.0); #else back.contents = (id) GetCGImageNamed(@"/Library/Desktop Pictures/Classic Aqua Blue.jpg"); @@ -148,7 +148,7 @@ back.edgeAntialiasingMask = 0; back.doubleSided = NO; // this makes the layer invisible when it's flipped -#if TARGET_OS_ASPEN +#if TARGET_OS_IPHONE // On iPhone, only Hiragana Kaku includes the coveted snowman glyph... who knows why? UIFont *font = [UIFont fontWithName: @"HiraKakuProN-W3" size: 1*size.width]; #else @@ -167,7 +167,7 @@ #pragma mark DRAG-AND-DROP: -#if ! TARGET_OS_ASPEN +#if ! TARGET_OS_IPHONE // An image from another app can be dragged onto a Card to change its background. */ diff -r 428a194e3e59 -r 45c82a071aca Source/DemoBoardView.h --- a/Source/DemoBoardView.h Sun Mar 16 15:06:47 2008 -0700 +++ b/Source/DemoBoardView.h Wed May 28 12:47:10 2008 -0700 @@ -31,6 +31,9 @@ IBOutlet NSSlider *_turnSlider; } +- (IBAction) undo: (id)sender; +- (IBAction) redo: (id)sender; - (IBAction) startGameFromMenu: (id)sender; +- (IBAction) enterFullScreen: (id)sender; @end diff -r 428a194e3e59 -r 45c82a071aca Source/DemoBoardView.m --- a/Source/DemoBoardView.m Sun Mar 16 15:06:47 2008 -0700 +++ b/Source/DemoBoardView.m Wed May 28 12:47:10 2008 -0700 @@ -72,6 +72,10 @@ } +- (BOOL)canBecomeKeyView {return YES;} +- (BOOL)acceptsFirstResponder {return YES;} + + - (void) awakeFromNib { srandomdev(); @@ -94,7 +98,7 @@ } -- (void) startGameFromMenu: (id)sender +- (IBAction) startGameFromMenu: (id)sender { sCurrentGameName = kMenuGameNames[ [sender tag] ]; [self startGameNamed: sCurrentGameName]; @@ -127,6 +131,24 @@ } +- (IBAction) undo: (id)sender +{ + if( self.game.currentTurn > 0 ) + self.game.currentTurn--; + else + NSBeep(); +} + + +- (IBAction) redo: (id)sender +{ + if( self.game.currentTurn < self.game.maxTurn ) + self.game.currentTurn++; + else + NSBeep(); +} + + - (IBAction) enterFullScreen: (id)sender { [super enterFullScreen: sender]; diff -r 428a194e3e59 -r 45c82a071aca Source/Dispenser.m --- a/Source/Dispenser.m Sun Mar 16 15:06:47 2008 -0700 +++ b/Source/Dispenser.m Wed May 28 12:47:10 2008 -0700 @@ -163,7 +163,7 @@ #pragma mark DRAG-AND-DROP: -#if ! TARGET_OS_ASPEN +#if ! TARGET_OS_IPHONE // An image from another app can be dragged onto a Dispenser to change the Piece's appearance. diff -r 428a194e3e59 -r 45c82a071aca Source/GGBLayer.h --- a/Source/GGBLayer.h Sun Mar 16 15:06:47 2008 -0700 +++ b/Source/GGBLayer.h Wed May 28 12:47:10 2008 -0700 @@ -6,7 +6,8 @@ // Copyright 2008 __MyCompanyName__. All rights reserved. // -#if TARGET_OS_ASPEN + +#if TARGET_OS_IPHONE #import #else #import @@ -17,7 +18,7 @@ { CABasicAnimation *_curAnimation; -#if ! TARGET_OS_ASPEN +#if ! TARGET_OS_IPHONE } #else // For some reason, the CALayer class on iPhone OS doesn't have these! diff -r 428a194e3e59 -r 45c82a071aca Source/GGBLayer.m --- a/Source/GGBLayer.m Sun Mar 16 15:06:47 2008 -0700 +++ b/Source/GGBLayer.m Wed May 28 12:47:10 2008 -0700 @@ -67,7 +67,7 @@ } -#if TARGET_OS_ASPEN +#if TARGET_OS_IPHONE #pragma mark - #pragma mark IPHONE VERSION: diff -r 428a194e3e59 -r 45c82a071aca Source/GGBTextLayer.h --- a/Source/GGBTextLayer.h Sun Mar 16 15:06:47 2008 -0700 +++ b/Source/GGBTextLayer.h Wed May 28 12:47:10 2008 -0700 @@ -9,7 +9,7 @@ #import "GGBLayer.h" -#if TARGET_OS_ASPEN +#if TARGET_OS_IPHONE @interface GGBTextLayer : GGBLayer { NSString *_string; @@ -39,7 +39,7 @@ @end -#if TARGET_OS_ASPEN +#if TARGET_OS_IPHONE /* Bit definitions for `autoresizingMask' property. */ enum CAAutoresizingMask diff -r 428a194e3e59 -r 45c82a071aca Source/GGBTextLayer.m --- a/Source/GGBTextLayer.m Sun Mar 16 15:06:47 2008 -0700 +++ b/Source/GGBTextLayer.m Wed May 28 12:47:10 2008 -0700 @@ -18,7 +18,7 @@ fontSize: (float) fontSize alignment: (enum CAAutoresizingMask) align { -#if TARGET_OS_ASPEN +#if TARGET_OS_IPHONE UIFont *font = [UIFont systemFontOfSize: fontSize]; #else NSFont *font = [NSFont systemFontOfSize: fontSize]; @@ -38,7 +38,7 @@ GGBTextLayer *label = [[self alloc] init]; label.string = text; -#if TARGET_OS_ASPEN +#if TARGET_OS_IPHONE UIFont *font = inputFont; [label setNeedsDisplay]; label.needsDisplayOnBoundsChange = YES; @@ -60,45 +60,53 @@ align |= kCALayerWidthSizable; label.alignmentMode = mode; - CGFloat inset = 3; + // Get the bounds of the interior of the superlayer: + CGFloat inset = round(font.pointSize/8); if( [superlayer respondsToSelector: @selector(borderWidth)] ) inset += ((GGBLayer*)superlayer).borderWidth; CGRect bounds = CGRectInset(superlayer.bounds, inset, inset); - if( mode==@"center" ) + if( mode==@"center" ) { + // horizontal centering: ignore x inset: bounds = CGRectInset(bounds,-inset,0); - CGFloat height = font.ascender; - float descender = font.descender; -#if TARGET_OS_ASPEN - descender = -descender; + } + + // Compute y position of bottom of layer's frame. (Remember, descender is negative!) + CGFloat y = bounds.origin.y; + CGFloat descender=font.descender, height=font.ascender-descender; + if( align & kCALayerHeightSizable ) { + // Vertical centering: + y += (bounds.size.height-height)/2.0; +#if ! TARGET_OS_IPHONE + y += descender/2.0; #endif - CGFloat y = bounds.origin.y; - if( align & kCALayerHeightSizable ) { - y += (bounds.size.height-height)/2.0; -#if TARGET_OS_ASPEN - y -= descender/2.0; -#endif - } else if( align & kCALayerMinYMargin ) + align &= ~kCALayerHeightSizable; + } else if( align & kCALayerMinYMargin ) { + // Top alignment (Mac) or bottom (iPhone): y += bounds.size.height - height; - align &= ~kCALayerHeightSizable; + } + + // Compute label's geometry: label.bounds = CGRectMake(0, descender, - bounds.size.width, height - descender); - label.position = CGPointMake(bounds.origin.x,y+descender); + bounds.size.width, height); label.anchorPoint = CGPointMake(0,0); + label.position = CGPointMake(bounds.origin.x,y); -#if ! TARGET_OS_ASPEN +#if ! TARGET_OS_IPHONE label.autoresizingMask = align; #endif [superlayer addSublayer: label]; [label release]; - //label.borderWidth = 1; - //label.borderColor = kBlackColor; +#if 0 // for debugging layout, border the view + label.borderWidth = 1; + label.borderColor = kBlackColor; +#endif return label; } -#if TARGET_OS_ASPEN +#if TARGET_OS_IPHONE @synthesize string=_string, font=_font, foregroundColor=_foregroundColor, alignmentMode=_alignmentMode; @@ -134,7 +142,9 @@ align = UITextAlignmentLeft; CGRect bounds = self.bounds; - bounds.origin.y += _font.ascender+_font.descender - _font.leading; + //float ascender=_font.ascender, descender=_font.descender, leading=_font.leading; + //bounds.size.height -= ascender-descender - leading; + [_string drawInRect: bounds withFont: _font lineBreakMode: UILineBreakModeClip diff -r 428a194e3e59 -r 45c82a071aca Source/GGBUtils.m --- a/Source/GGBUtils.m Sun Mar 16 15:06:47 2008 -0700 +++ b/Source/GGBUtils.m Wed May 28 12:47:10 2008 -0700 @@ -20,7 +20,7 @@ */ #import "GGBUtils.h" -#if TARGET_OS_ASPEN +#if TARGET_OS_IPHONE #import #endif @@ -44,7 +44,7 @@ void PreloadSound( NSString* name ) { -#if ! TARGET_OS_ASPEN +#if ! TARGET_OS_IPHONE NSSound *sound = [[NSSound soundNamed: @"Pop"] copy]; sound.volume = 0; [sound play]; @@ -55,7 +55,7 @@ void PlaySound( NSString* name ) { -#if TARGET_OS_ASPEN +#if TARGET_OS_IPHONE NSURL *url = [NSURL fileURLWithPath: [@"/Library/Sounds/" stringByAppendingPathComponent: name]]; SystemSoundID soundID; if( AudioServicesCreateSystemSoundID((CFURLRef)url,&soundID) != noErr ) { @@ -70,8 +70,8 @@ void Beep() { -#if TARGET_OS_ASPEN - AudioServicesPlaySystemSound(kSystemSoundID_UserPreferredAlert); +#if TARGET_OS_IPHONE + AudioServicesPlayAlertSound(0x00001000/*kSystemSoundID_UserPreferredAlert*/); #else NSBeep(); #endif diff -r 428a194e3e59 -r 45c82a071aca Source/Game-Persistence.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Game-Persistence.h Wed May 28 12:47:10 2008 -0700 @@ -0,0 +1,20 @@ +// +// Game-Persistence.h +// GeekGameBoard +// +// Created by Jens Alfke on 3/16/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "Game.h" + + +@interface Game (Persistence) + ++ (Game*) gameWithURL: (NSURL*)url; + +- (NSURL*) asURL; + +- (BOOL) addMoveFromURL: (NSURL*)url; + +@end diff -r 428a194e3e59 -r 45c82a071aca Source/Game-Persistence.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Game-Persistence.m Wed May 28 12:47:10 2008 -0700 @@ -0,0 +1,115 @@ +// +// Game-Persistence.m +// GeekGameBoard +// +// Created by Jens Alfke on 3/16/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "Game-Persistence.h" + + +static NSDictionary* parseURLFields( NSURL* url ); + + +@implementation Game (Persistence) + + +static NSMutableDictionary *sPersistentGames; + + +- (id) initWithCoder: (NSCoder*)decoder +{ + self = [self init]; + if( self ) { + _players = [[decoder decodeObjectForKey: @"players"] mutableCopy]; + _states = [[decoder decodeObjectForKey: @"states"] mutableCopy]; + _moves = [[decoder decodeObjectForKey: @"moves"] mutableCopy]; + self.currentTurn = self.maxTurn; + } + return self; +} + + +- (void) encodeWithCoder: (NSCoder*)coder +{ + [coder encodeObject: _players forKey: @"players"]; + [coder encodeObject: _states forKey: @"states"]; + [coder encodeObject: _moves forKey: @"moves"]; +} + + +- (NSURL*) asURL +{ + return [NSURL URLWithString: + [NSString stringWithFormat: @"game:type=%@&id=%@&turn=%u&move=%@", + [[self class] identifier], _uniqueID, self.currentTurn,_moves.lastObject]]; +} + + ++ (Game*) gameWithURL: (NSURL*)url +{ + if( 0 != [@"game" caseInsensitiveCompare: url.scheme] ) + return nil; + NSDictionary *fields = parseURLFields(url); + NSString *type = [fields objectForKey: @"type"]; + NSString *uuid = [fields objectForKey: @"id"]; + int turn = [[fields objectForKey: @"turn"] intValue]; + if( !type || !uuid || turn<=0 ) + return nil; + + Game *game = [sPersistentGames objectForKey: uuid]; + if( game ) { + if( ![type isEqualToString: [[game class] identifier]] ) + return nil; + } else if( turn == 1 ) { + Class gameClass = NSClassFromString( [type stringByAppendingString: @"Game"] ); + if( ! gameClass || ! [gameClass isSubclassOfClass: [Game class]] ) + return nil; + game = [[gameClass alloc] initWithUniqueID: uuid]; + [game setNumberOfPlayers: 2]; + if( ! sPersistentGames ) + sPersistentGames = [[NSMutableDictionary alloc] init]; + [sPersistentGames setObject: game forKey: uuid]; + [game release]; + } + return game; +} + + +- (BOOL) addMoveFromURL: (NSURL*)url +{ + NSDictionary *fields = parseURLFields(url); + NSString *uuid = [fields objectForKey: @"id"]; + NSString *move = [fields objectForKey: @"move"]; + int turn = [[fields objectForKey: @"turn"] intValue]; + return [uuid isEqualToString: self.uniqueID] + && turn==self.currentTurn + && move.length > 0 + && [self applyMoveString: move]; +} + + + +@end + + + +static NSDictionary* parseURLFields( NSURL* url ) +{ + // Parse the URL into key-value pairs: + NSMutableDictionary *fields = [NSMutableDictionary dictionary]; + for( NSString *field in [url.resourceSpecifier componentsSeparatedByString: @"&"] ) { + NSRange e = [field rangeOfString: @"="]; + NSString *key, *value; + if( e.length>0 ) { + key = [field substringToIndex: e.location]; + value = [field substringFromIndex: NSMaxRange(e)]; + } else { + key= field; + value = @""; + } + [fields setObject: value forKey: key]; + } + return fields; +} diff -r 428a194e3e59 -r 45c82a071aca Source/Game.h --- a/Source/Game.h Sun Mar 16 15:06:47 2008 -0700 +++ b/Source/Game.h Wed May 28 12:47:10 2008 -0700 @@ -27,6 +27,7 @@ /** Abstract superclass. Keeps track of the rules and turns of a game. */ @interface Game : NSObject { + NSString *_uniqueID; GGBLayer *_board; NSArray *_players; Player *_currentPlayer, *_winner; @@ -35,6 +36,10 @@ unsigned _currentTurn; } +/** Returns the name used to identify this game in URLs. + (By default it just returns the class name with the "Game" suffix removed.) */ ++ (NSString*) identifier; + /** Returns the human-readable name of this game. (By default it just returns the class name with the "Game" suffix removed.) */ + (NSString*) displayName; @@ -49,9 +54,17 @@ @property unsigned currentTurn; @property (readonly) BOOL isLatestTurn; + +/** A globally-unique string assigned to this game instance, to help networked players identify it. */ +@property (readonly) NSString* uniqueID; + - (BOOL) animateMoveFrom: (BitHolder*)src to: (BitHolder*)dst; +- (id) initWithUniqueID: (NSString*)uniqueID; +- (id) init; + + // Methods for subclasses to implement: /** Designated initializer. After calling the superclass implementation, @@ -87,8 +100,11 @@ Default implementation returns nil. */ - (Player*) checkForWinner; +/** A string describing the current state of the game (the positions of all pieces, + orderings of cards, player scores, ... */ +@property (copy) NSString* stateString; -@property (copy) NSString* stateString; +/** Add a move to the game based on the contents of the string. */ - (BOOL) applyMoveString: (NSString*)move; @@ -111,7 +127,7 @@ /** A mostly-passive object used to represent a player. */ -@interface Player : NSObject +@interface Player : NSObject { Game *_game; NSString *_name; diff -r 428a194e3e59 -r 45c82a071aca Source/Game.m --- a/Source/Game.m Sun Mar 16 15:06:47 2008 -0700 +++ b/Source/Game.m Wed May 28 12:47:10 2008 -0700 @@ -35,7 +35,7 @@ @implementation Game -+ (NSString*) displayName ++ (NSString*) identifier { NSString* name = [self description]; if( [name hasSuffix: @"Game"] ) @@ -44,17 +44,46 @@ } ++ (NSString*) displayName +{ + return [self identifier]; +} + + +- (id) initWithUniqueID: (NSString*)uuid +{ + NSParameterAssert(uuid); + self = [super init]; + if (self != nil) { + _uniqueID = [uuid copy]; + _board = [[GGBLayer alloc] init]; + // Store a pointer to myself as the value of the "Game" property + // of my root layer. (CALayers can have arbitrary KV properties stored into them.) + // This is used by the -[CALayer game] category method defined below, to find the Game. + [_board setValue: self forKey: @"Game"]; + + _currentMove = [[NSMutableString alloc] init]; + } + return self; +} + +- (id) init +{ + CFUUIDRef uuidRef = CFUUIDCreate(NULL); + NSString* uuid = (id) CFUUIDCreateString(NULL,uuidRef); + self = [self initWithUniqueID: uuid]; + CFRelease(uuid); + CFRelease(uuidRef); + return self; +} + - (id) initWithBoard: (GGBLayer*)board { - self = [super init]; + self = [self init]; if (self != nil) { _states = [[NSMutableArray alloc] init]; _moves = [[NSMutableArray alloc] init]; - _currentMove = [[NSMutableString alloc] init]; _board = [board retain]; - // Store a pointer to myself as the value of the "Game" property - // of my root layer. (CALayers can have arbitrary KV properties stored into them.) - // This is used by the -[CALayer game] category method defined below, to find the Game. [board setValue: self forKey: @"Game"]; } return self; @@ -73,7 +102,7 @@ @synthesize players=_players, currentPlayer=_currentPlayer, winner=_winner, - currentMove=_currentMove, states=_states, moves=_moves; + currentMove=_currentMove, states=_states, moves=_moves, uniqueID=_uniqueID; - (void) setNumberOfPlayers: (unsigned)n @@ -129,9 +158,10 @@ { NSLog(@"--- End of turn (move was '%@')", _currentMove); if( self.isLatestTurn ) { + NSString *move = [[_currentMove copy] autorelease]; + [_currentMove setString: @""]; [self willChangeValueForKey: @"maxTurn"]; - [_moves addObject: [[_currentMove copy] autorelease]]; - [_currentMove setString: @""]; + [_moves addObject: move]; [self didChangeValueForKey: @"maxTurn"]; } @@ -145,6 +175,10 @@ } +#pragma mark - +#pragma mark STORED TURNS: + + - (unsigned) maxTurn { return _moves.count; @@ -195,8 +229,13 @@ [bit performSelector: @selector(setPickedUp:) withObject:nil afterDelay: 0.15]; CGPoint endPosition = [dst convertPoint: GetCGRectCenter(dst.bounds) toLayer: bit.superlayer]; [bit animateAndBlock: @"position" +#if TARGET_OS_IPHONE + from: [NSValue valueWithCGPoint: bit.position] + to: [NSValue valueWithCGPoint: endPosition] +#else from: [NSValue valueWithPoint: NSPointFromCGPoint(bit.position)] to: [NSValue valueWithPoint: NSPointFromCGPoint(endPosition)] +#endif duration: 0.25]; dst.bit = bit; dst.highlighted = NO; @@ -273,6 +312,28 @@ return self; } +- (id) initWithCoder: (NSCoder*)decoder +{ + self = [self init]; + if( self ) { + _game = [decoder decodeObjectForKey: @"game"]; + _name = [[decoder decodeObjectForKey: @"name"] copy]; + } + return self; +} + +- (void) encodeWithCoder: (NSCoder*)coder +{ + [coder encodeObject: _game forKey: @"game"]; + [coder encodeObject: _name forKey: @"name"]; +} + +- (void) dealloc +{ + [_name release]; + [super dealloc]; +} + @synthesize game=_game, name=_name; diff -r 428a194e3e59 -r 45c82a071aca Source/Grid.m --- a/Source/Grid.m Sun Mar 16 15:06:47 2008 -0700 +++ b/Source/Grid.m Wed May 28 12:47:10 2008 -0700 @@ -345,7 +345,7 @@ #pragma mark DRAG-AND-DROP: -#if ! TARGET_OS_ASPEN +#if ! TARGET_OS_IPHONE // An image from another app can be dragged onto a Dispenser to change the Piece's appearance. @@ -451,7 +451,7 @@ - (Square*) l {return self.fwdIsN ?self.w :self.e;} -#if ! TARGET_OS_ASPEN +#if ! TARGET_OS_IPHONE - (BOOL)performDragOperation:(id )sender { diff -r 428a194e3e59 -r 45c82a071aca Source/PlayingCard.m --- a/Source/PlayingCard.m Sun Mar 16 15:06:47 2008 -0700 +++ b/Source/PlayingCard.m Wed May 28 12:47:10 2008 -0700 @@ -45,7 +45,7 @@ float cornerFontSize = MAX(18*scale, 14); float centerFontSize = 80*scale; -#if TARGET_OS_ASPEN +#if TARGET_OS_IPHONE UIFont *cornerFont = [UIFont boldSystemFontOfSize: cornerFontSize]; #else NSFont *cornerFont = [NSFont boldSystemFontOfSize: cornerFontSize]; @@ -69,8 +69,6 @@ fontSize: centerFontSize alignment: kCALayerWidthSizable | kCALayerHeightSizable]; label.foregroundColor = suitColor; - //label.borderWidth = 1; - //label.borderColor = kBlackColor; return front; } diff -r 428a194e3e59 -r 45c82a071aca Source/QuartzUtils.h --- a/Source/QuartzUtils.h Sun Mar 16 15:06:47 2008 -0700 +++ b/Source/QuartzUtils.h Wed May 28 12:47:10 2008 -0700 @@ -29,7 +29,7 @@ kAlmostInvisibleWhiteColor, kHighlightColor; -#if TARGET_OS_ASPEN +#if TARGET_OS_IPHONE // These don't exist on iPhone, so I implement them: CGColorRef CreateGray(CGFloat gray, CGFloat alpha); CGColorRef CreateRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha); @@ -56,7 +56,7 @@ CGImageRef GetCGImageNamed( NSString *name ); CGColorRef GetCGPatternNamed( NSString *name ); -#if ! TARGET_OS_ASPEN +#if ! TARGET_OS_IPHONE /** Loads image data from the pasteboard into a CGImage. */ CGImageRef GetCGImageFromPasteboard( NSPasteboard *pb ); #endif diff -r 428a194e3e59 -r 45c82a071aca Source/QuartzUtils.m --- a/Source/QuartzUtils.m Sun Mar 16 15:06:47 2008 -0700 +++ b/Source/QuartzUtils.m Wed May 28 12:47:10 2008 -0700 @@ -42,7 +42,7 @@ } -#if TARGET_OS_ASPEN +#if TARGET_OS_IPHONE CGColorRef CreateGray(CGFloat gray, CGFloat alpha) { CGColorSpaceRef graySpace = CGColorSpaceCreateDeviceGray(); @@ -100,7 +100,7 @@ CGImageRef CreateCGImageFromFile( NSString *path ) { -#if TARGET_OS_ASPEN +#if TARGET_OS_IPHONE UIImage *uiImage = [UIImage imageWithContentsOfFile: path]; if(!uiImage) NSLog(@"Warning: UIImage imageWithContentsOfFile failed on file %@",path); return CGImageRetain(uiImage.CGImage); @@ -120,7 +120,7 @@ CGImageRef GetCGImageNamed( NSString *name ) { -#if TARGET_OS_ASPEN +#if TARGET_OS_IPHONE name = name.lastPathComponent; UIImage *uiImage = [UIImage imageNamed: name]; NSCAssert1(uiImage,@"Couldn't find bundle image resource '%@'",name); @@ -168,7 +168,7 @@ } -#if ! TARGET_OS_ASPEN +#if ! TARGET_OS_IPHONE CGImageRef GetCGImageFromPasteboard( NSPasteboard *pb ) { CGImageSourceRef src = NULL; @@ -197,7 +197,7 @@ float GetPixelAlpha( CGImageRef image, CGSize imageSize, CGPoint pt ) { -#if TARGET_OS_ASPEN +#if TARGET_OS_IPHONE // iPhone uses "flipped" (i.e. normal) coords, so images are wrong-way-up pt.y = imageSize.height - pt.y; #endif diff -r 428a194e3e59 -r 45c82a071aca Source/TicTacToeGame.m --- a/Source/TicTacToeGame.m Sun Mar 16 15:06:47 2008 -0700 +++ b/Source/TicTacToeGame.m Wed May 28 12:47:10 2008 -0700 @@ -57,7 +57,7 @@ for( int playerNumber=0; playerNumber<=1; playerNumber++ ) { Piece *p = [self pieceForPlayer: playerNumber]; CGFloat x = floor(CGRectGetMidX(_board.bounds)); -#if TARGET_OS_ASPEN +#if TARGET_OS_IPHONE x = x - 80 + 160*playerNumber; CGFloat y = 360; #else diff -r 428a194e3e59 -r 45c82a071aca Source/iPhoneAppDelegate.h --- a/Source/iPhoneAppDelegate.h Sun Mar 16 15:06:47 2008 -0700 +++ b/Source/iPhoneAppDelegate.h Wed May 28 12:47:10 2008 -0700 @@ -10,7 +10,7 @@ @class BoardUIView; -@interface GGB_iPhoneAppDelegate : NSObject { +@interface GGB_iPhoneAppDelegate : NSObject { UIWindow *_window; BoardUIView *_contentView; UILabel *_headline; diff -r 428a194e3e59 -r 45c82a071aca Source/iPhoneAppDelegate.m --- a/Source/iPhoneAppDelegate.m Sun Mar 16 15:06:47 2008 -0700 +++ b/Source/iPhoneAppDelegate.m Wed May 28 12:47:10 2008 -0700 @@ -44,7 +44,7 @@ _headline.textAlignment = UITextAlignmentCenter; _headline.font = [UIFont boldSystemFontOfSize: 20]; _headline.minimumFontSize = 14; - _headline.adjustsFontSizeToFit = YES; + _headline.adjustsFontSizeToFitWidth = YES; [_window addSubview: _headline]; // Start game: @@ -71,7 +71,7 @@ [game removeObserver: self forKeyPath: @"winner"]; if( gameClassName == nil ) - gameClassName = [[game class] className]; + gameClassName = [[game class] description]; [_contentView startGameNamed: gameClassName]; @@ -110,8 +110,8 @@ alert = [[UIAlertView alloc] initWithTitle: msg message: @"Congratulations!" delegate:self - defaultButton:@"OK" - cancelButton:nil otherButtons:nil]; + cancelButtonTitle:nil + otherButtonTitles:nil]; [alert show]; [alert release]; } @@ -119,7 +119,7 @@ } -- (void)modalView:(UIModalView *)modalView didDismissWithButtonIndex:(NSInteger)buttonIndex; +- (void)alertView:(UIAlertView *)modalView didDismissWithButtonIndex:(NSInteger)buttonIndex; { // Start new game: [self startGameNamed: nil];