1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/Source/HexGrid.m Wed May 28 12:47:10 2008 -0700
1.3 @@ -0,0 +1,200 @@
1.4 +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0.
1.5 + http://developer.apple.com/samplecode/GeekGameBoard/
1.6 + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved.
1.7 +
1.8 + Redistribution and use in source and binary forms, with or without modification, are permitted
1.9 + provided that the following conditions are met:
1.10 +
1.11 + * Redistributions of source code must retain the above copyright notice, this list of conditions
1.12 + and the following disclaimer.
1.13 + * Redistributions in binary form must reproduce the above copyright notice, this list of
1.14 + conditions and the following disclaimer in the documentation and/or other materials provided
1.15 + with the distribution.
1.16 +
1.17 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
1.18 + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
1.19 + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
1.20 + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1.21 + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
1.22 + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
1.23 + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
1.24 + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.25 +*/
1.26 +#import "HexGrid.h"
1.27 +#import "Game.h"
1.28 +
1.29 +
1.30 +@implementation HexGrid
1.31 +
1.32 +
1.33 +- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
1.34 + spacing: (CGSize)spacing
1.35 + position: (CGPoint)pos
1.36 +{
1.37 + // Ignore given spacing.height; set it to make the hexagons regular.
1.38 + CGFloat capHeight = spacing.height / 2 * tan(M_PI/6);
1.39 + CGFloat side = spacing.height / 2 / cos(M_PI/6);
1.40 + spacing.height = side + capHeight;
1.41 +
1.42 + self = [super initWithRows: nRows columns: nColumns
1.43 + spacing: spacing
1.44 + position: pos];
1.45 + if( self ) {
1.46 + _capHeight = capHeight;
1.47 + _side = side;
1.48 + self.bounds = CGRectMake(-1, -1,
1.49 + (nColumns+0.5)*spacing.width + 2,
1.50 + nRows*spacing.height+capHeight + 2);
1.51 + _cellClass = [Hex class];
1.52 + }
1.53 + return self;
1.54 +}
1.55 +
1.56 +
1.57 +- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
1.58 + frame: (CGRect)frame;
1.59 +{
1.60 + // Compute the horizontal spacing:
1.61 + CGFloat s = floor(MIN( (frame.size.width -2.0)/nColumns,
1.62 + (frame.size.height-2.0)/(nRows+0.5*tan(M_PI/6)) / (0.5*(tan(M_PI/6)+1/cos(M_PI/6))) ));
1.63 + return [self initWithRows: nRows columns: nColumns
1.64 + spacing: CGSizeMake(s,s)
1.65 + position: frame.origin];
1.66 +}
1.67 +
1.68 +
1.69 +- (void) dealloc
1.70 +{
1.71 + CGPathRelease(_cellPath);
1.72 + [super dealloc];
1.73 +}
1.74 +
1.75 +
1.76 +- (void) addCellsInHexagon
1.77 +{
1.78 + int size = _nRows - !(_nRows & 1); // make it odd
1.79 + for( int row=0; row<_nRows; row++ ) {
1.80 + int n; // # of hexes remaining in this row
1.81 + if( row < size )
1.82 + n = size - abs(row-size/2);
1.83 + else
1.84 + n = 0;
1.85 + int c0 = floor(((int)_nRows+1-n -(row&1))/2.0); // col of 1st remaining hex
1.86 +
1.87 + for( int col=0; col<_nColumns; col++ )
1.88 + if( col>=c0 && col<c0+n )
1.89 + [self addCellAtRow: row column: col];
1.90 + }
1.91 +}
1.92 +
1.93 +
1.94 +- (GridCell*) createCellAtRow: (unsigned)row column: (unsigned)col
1.95 + suggestedFrame: (CGRect)frame
1.96 +{
1.97 + // Overridden to stagger the odd-numbered rows
1.98 + if( row & 1 )
1.99 + frame.origin.x += _spacing.width/2;
1.100 + frame.size.height += _capHeight;
1.101 + return [super createCellAtRow: row column: col suggestedFrame: frame];
1.102 +}
1.103 +
1.104 +
1.105 +// Returns a hexagonal CG path defining a cell's outline. Used by cells when drawing & hit-testing.
1.106 +- (CGPathRef) cellPath
1.107 +{
1.108 + if( ! _cellPath ) {
1.109 + CGFloat x1 = _spacing.width/2;
1.110 + CGFloat x2 = _spacing.width;
1.111 + CGFloat y1 = _capHeight;
1.112 + CGFloat y2 = y1 + _side;
1.113 + CGFloat y3 = y2 + _capHeight;
1.114 + CGPoint p[6] = { {0,y1}, {x1,0}, {x2,y1}, {x2,y2}, {x1,y3}, {0,y2} };
1.115 +
1.116 + CGMutablePathRef path = CGPathCreateMutable();
1.117 + CGPathAddLines(path, NULL, p, 6);
1.118 + CGPathCloseSubpath(path);
1.119 + _cellPath = path;
1.120 + }
1.121 + return _cellPath;
1.122 +}
1.123 +
1.124 +
1.125 +@end
1.126 +
1.127 +
1.128 +
1.129 +
1.130 +
1.131 +#pragma mark -
1.132 +
1.133 +@implementation Hex
1.134 +
1.135 +
1.136 +- (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
1.137 +{
1.138 + CGContextSaveGState(ctx);
1.139 + CGPoint pos = self.position;
1.140 + CGContextTranslateCTM(ctx, pos.x, pos.y);
1.141 + CGContextBeginPath(ctx);
1.142 + CGContextAddPath(ctx, ((HexGrid*)_grid).cellPath);
1.143 + CGContextDrawPath(ctx, (fill ?kCGPathFill :kCGPathStroke));
1.144 +
1.145 + if( !fill && self.highlighted ) {
1.146 + // Highlight by drawing my outline in the highlight color:
1.147 + CGContextSetStrokeColorWithColor(ctx, self.borderColor);
1.148 + CGContextSetLineWidth(ctx,6);
1.149 + CGContextBeginPath(ctx);
1.150 + CGContextAddPath(ctx, ((HexGrid*)_grid).cellPath);
1.151 + CGContextDrawPath(ctx, kCGPathStroke);
1.152 + }
1.153 + CGContextRestoreGState(ctx);
1.154 +}
1.155 +
1.156 +
1.157 +- (BOOL)containsPoint:(CGPoint)p
1.158 +{
1.159 + return [super containsPoint: p]
1.160 + && CGPathContainsPoint( ((HexGrid*)_grid).cellPath, NULL, p, NO );
1.161 +}
1.162 +
1.163 +
1.164 +- (void) setHighlighted: (BOOL)highlighted
1.165 +{
1.166 + if( highlighted != self.highlighted ) {
1.167 + [super setHighlighted: highlighted];
1.168 + [_grid setNeedsDisplay]; // So I'll be asked to redraw myself
1.169 + }
1.170 +}
1.171 +
1.172 +
1.173 +#pragma mark -
1.174 +#pragma mark NEIGHBORS:
1.175 +
1.176 +
1.177 +- (NSArray*) neighbors
1.178 +{
1.179 + NSMutableArray *neighbors = [NSMutableArray arrayWithCapacity: 6];
1.180 + Hex* n[6] = {self.nw, self.ne, self.w, self.e, self.sw, self.se};
1.181 + for( int i=0; i<6; i++ )
1.182 + if( n[i] )
1.183 + [neighbors addObject: n[i]];
1.184 + return neighbors;
1.185 +}
1.186 +
1.187 +- (Hex*) nw {return (Hex*)[_grid cellAtRow: _row+1 column: _column - ((_row+1)&1)];}
1.188 +- (Hex*) ne {return (Hex*)[_grid cellAtRow: _row+1 column: _column + (_row&1)];}
1.189 +- (Hex*) e {return (Hex*)[_grid cellAtRow: _row column: _column + 1];}
1.190 +- (Hex*) se {return (Hex*)[_grid cellAtRow: _row-1 column: _column + (_row&1)];}
1.191 +- (Hex*) sw {return (Hex*)[_grid cellAtRow: _row-1 column: _column - ((_row-1)&1)];}
1.192 +- (Hex*) w {return (Hex*)[_grid cellAtRow: _row column: _column - 1];}
1.193 +
1.194 +// Directions relative to the current player:
1.195 +- (Hex*) fl {return self.fwdIsN ?self.nw :self.se;}
1.196 +- (Hex*) fr {return self.fwdIsN ?self.ne :self.sw;}
1.197 +- (Hex*) r {return self.fwdIsN ?self.e :self.w;}
1.198 +- (Hex*) br {return self.fwdIsN ?self.se :self.nw;}
1.199 +- (Hex*) bl {return self.fwdIsN ?self.sw :self.ne;}
1.200 +- (Hex*) l {return self.fwdIsN ?self.w :self.e;}
1.201 +
1.202 +
1.203 +@end