More tweaks, including a "reversed" property for Grids to show the second player's perspective without turning the pieces upside-down.
1 /* This code is based on Apple's "GeekGameBoard" sample code, version 1.0.
2 http://developer.apple.com/samplecode/GeekGameBoard/
3 Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved.
5 Redistribution and use in source and binary forms, with or without modification, are permitted
6 provided that the following conditions are met:
8 * Redistributions of source code must retain the above copyright notice, this list of conditions
9 and the following disclaimer.
10 * Redistributions in binary form must reproduce the above copyright notice, this list of
11 conditions and the following disclaimer in the documentation and/or other materials provided
12 with the distribution.
14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
15 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
17 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
19 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
20 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
21 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 @implementation HexGrid
30 - (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
31 spacing: (CGSize)spacing
32 position: (CGPoint)pos
34 // Ignore given spacing.height; set it to make the hexagons regular.
35 CGFloat capHeight = spacing.height / 2 * tan(M_PI/6);
36 CGFloat side = spacing.height / 2 / cos(M_PI/6);
37 spacing.height = side + capHeight;
39 self = [super initWithRows: nRows columns: nColumns
43 _capHeight = capHeight;
45 self.bounds = CGRectMake(-1, -1,
46 (nColumns+0.5)*spacing.width + 2,
47 nRows*spacing.height+capHeight + 2);
48 _cellClass = [Hex class];
54 - (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
57 // Compute the horizontal spacing:
58 CGFloat s = floor(MIN( (frame.size.width -2.0)/nColumns,
59 (frame.size.height-2.0)/(nRows+0.5*tan(M_PI/6)) / (0.5*(tan(M_PI/6)+1/cos(M_PI/6))) ));
60 self = [self initWithRows: nRows columns: nColumns
61 spacing: CGSizeMake(s,s)
62 position: frame.origin];
65 CGRect curFrame = self.frame;
66 curFrame.origin.x = round( curFrame.origin.x + (frame.size.width - curFrame.size.width )/2.0f );
67 curFrame.origin.y = round( curFrame.origin.y + (frame.size.height- curFrame.size.height)/2.0f );
68 self.frame = curFrame;
76 CGPathRelease(_cellPath);
81 - (void) addCellsInHexagon
83 int size = _nRows - !(_nRows & 1); // make it odd
84 for( int row=0; row<_nRows; row++ ) {
85 int n; // # of hexes remaining in this row
87 n = size - abs(row-size/2);
90 int c0 = floor(((int)_nRows+1-n -(row&1))/2.0); // col of 1st remaining hex
92 for( int col=0; col<_nColumns; col++ )
93 if( col>=c0 && col<c0+n )
94 [self addCellAtRow: row column: col];
99 - (GridCell*) createCellAtRow: (unsigned)row column: (unsigned)col
100 suggestedFrame: (CGRect)frame
102 // Overridden to stagger the odd-numbered rows
104 frame.origin.x += _spacing.width/2;
105 frame.size.height += _capHeight;
106 return [super createCellAtRow: row column: col suggestedFrame: frame];
110 // Returns a hexagonal CG path defining a cell's outline. Used by cells when drawing & hit-testing.
111 - (CGPathRef) cellPath
114 CGFloat x1 = _spacing.width/2;
115 CGFloat x2 = _spacing.width;
116 CGFloat y1 = _capHeight;
117 CGFloat y2 = y1 + _side;
118 CGFloat y3 = y2 + _capHeight;
119 CGPoint p[6] = { {0,y1}, {x1,0}, {x2,y1}, {x2,y2}, {x1,y3}, {0,y2} };
121 CGMutablePathRef path = CGPathCreateMutable();
122 CGPathAddLines(path, NULL, p, 6);
123 CGPathCloseSubpath(path);
141 - (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
143 CGContextSaveGState(ctx);
144 CGPoint pos = self.position;
145 CGContextTranslateCTM(ctx, pos.x, pos.y);
146 CGContextBeginPath(ctx);
147 CGContextAddPath(ctx, ((HexGrid*)_grid).cellPath);
148 CGContextDrawPath(ctx, (fill ?kCGPathFill :kCGPathStroke));
150 if( !fill && self.highlighted ) {
151 // Highlight by drawing my outline in the highlight color:
152 CGContextSetStrokeColorWithColor(ctx, self.borderColor);
153 CGContextSetLineWidth(ctx,6);
154 CGContextBeginPath(ctx);
155 CGContextAddPath(ctx, ((HexGrid*)_grid).cellPath);
156 CGContextDrawPath(ctx, kCGPathStroke);
158 CGContextRestoreGState(ctx);
162 - (BOOL)containsPoint:(CGPoint)p
164 return [super containsPoint: p]
165 && CGPathContainsPoint( ((HexGrid*)_grid).cellPath, NULL, p, NO );
169 - (void) setHighlighted: (BOOL)highlighted
171 if( highlighted != self.highlighted ) {
172 [super setHighlighted: highlighted];
173 [_grid setNeedsDisplay]; // So I'll be asked to redraw myself
179 #pragma mark NEIGHBORS:
182 - (NSArray*) neighbors
184 NSMutableArray *neighbors = [NSMutableArray arrayWithCapacity: 6];
185 Hex* n[6] = {self.nw, self.ne, self.w, self.e, self.sw, self.se};
186 for( int i=0; i<6; i++ )
188 [neighbors addObject: n[i]];
192 - (Hex*) nw {return (Hex*)[_grid cellAtRow: _row+1 column: _column - ((_row+1)&1)];}
193 - (Hex*) ne {return (Hex*)[_grid cellAtRow: _row+1 column: _column + (_row&1)];}
194 - (Hex*) e {return (Hex*)[_grid cellAtRow: _row column: _column + 1];}
195 - (Hex*) se {return (Hex*)[_grid cellAtRow: _row-1 column: _column + (_row&1)];}
196 - (Hex*) sw {return (Hex*)[_grid cellAtRow: _row-1 column: _column - ((_row-1)&1)];}
197 - (Hex*) w {return (Hex*)[_grid cellAtRow: _row column: _column - 1];}
199 // Directions relative to the current player:
200 - (Hex*) fl {return self.fwdIsN ?self.nw :self.se;}
201 - (Hex*) fr {return self.fwdIsN ?self.ne :self.sw;}
202 - (Hex*) r {return self.fwdIsN ?self.e :self.w;}
203 - (Hex*) br {return self.fwdIsN ?self.se :self.nw;}
204 - (Hex*) bl {return self.fwdIsN ?self.sw :self.ne;}
205 - (Hex*) l {return self.fwdIsN ?self.w :self.e;}