jens@0
|
1 |
/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0.
|
jens@0
|
2 |
http://developer.apple.com/samplecode/GeekGameBoard/
|
jens@0
|
3 |
Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved.
|
jens@0
|
4 |
|
jens@0
|
5 |
Redistribution and use in source and binary forms, with or without modification, are permitted
|
jens@0
|
6 |
provided that the following conditions are met:
|
jens@0
|
7 |
|
jens@0
|
8 |
* Redistributions of source code must retain the above copyright notice, this list of conditions
|
jens@0
|
9 |
and the following disclaimer.
|
jens@0
|
10 |
* Redistributions in binary form must reproduce the above copyright notice, this list of
|
jens@0
|
11 |
conditions and the following disclaimer in the documentation and/or other materials provided
|
jens@0
|
12 |
with the distribution.
|
jens@0
|
13 |
|
jens@0
|
14 |
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
jens@0
|
15 |
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
jens@0
|
16 |
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
|
jens@0
|
17 |
BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
jens@0
|
18 |
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
jens@0
|
19 |
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
jens@0
|
20 |
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
jens@0
|
21 |
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
jens@0
|
22 |
*/
|
jens@0
|
23 |
#import "HexGrid.h"
|
jens@0
|
24 |
#import "Game.h"
|
jens@0
|
25 |
|
jens@0
|
26 |
|
jens@0
|
27 |
@implementation HexGrid
|
jens@0
|
28 |
|
jens@0
|
29 |
|
jens@0
|
30 |
- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
|
jens@0
|
31 |
spacing: (CGSize)spacing
|
jens@0
|
32 |
position: (CGPoint)pos
|
jens@0
|
33 |
{
|
jens@0
|
34 |
// Ignore given spacing.height; set it to make the hexagons regular.
|
jens@0
|
35 |
CGFloat capHeight = spacing.height / 2 * tan(M_PI/6);
|
jens@0
|
36 |
CGFloat side = spacing.height / 2 / cos(M_PI/6);
|
jens@0
|
37 |
spacing.height = side + capHeight;
|
jens@0
|
38 |
|
jens@0
|
39 |
self = [super initWithRows: nRows columns: nColumns
|
jens@0
|
40 |
spacing: spacing
|
jens@0
|
41 |
position: pos];
|
jens@0
|
42 |
if( self ) {
|
jens@0
|
43 |
_capHeight = capHeight;
|
jens@0
|
44 |
_side = side;
|
jens@0
|
45 |
self.bounds = CGRectMake(-1, -1,
|
jens@0
|
46 |
(nColumns+0.5)*spacing.width + 2,
|
jens@0
|
47 |
nRows*spacing.height+capHeight + 2);
|
jens@0
|
48 |
_cellClass = [Hex class];
|
jens@0
|
49 |
}
|
jens@0
|
50 |
return self;
|
jens@0
|
51 |
}
|
jens@0
|
52 |
|
jens@0
|
53 |
|
jens@0
|
54 |
- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
|
jens@0
|
55 |
frame: (CGRect)frame;
|
jens@0
|
56 |
{
|
jens@0
|
57 |
// Compute the horizontal spacing:
|
jens@0
|
58 |
CGFloat s = floor(MIN( (frame.size.width -2.0)/nColumns,
|
jens@0
|
59 |
(frame.size.height-2.0)/(nRows+0.5*tan(M_PI/6)) / (0.5*(tan(M_PI/6)+1/cos(M_PI/6))) ));
|
jens@10
|
60 |
self = [self initWithRows: nRows columns: nColumns
|
jens@0
|
61 |
spacing: CGSizeMake(s,s)
|
jens@0
|
62 |
position: frame.origin];
|
jens@10
|
63 |
if( self ) {
|
jens@10
|
64 |
// Center in frame:
|
jens@10
|
65 |
CGRect curFrame = self.frame;
|
jens@10
|
66 |
curFrame.origin.x = round( curFrame.origin.x + (frame.size.width - curFrame.size.width )/2.0f );
|
jens@10
|
67 |
curFrame.origin.y = round( curFrame.origin.y + (frame.size.height- curFrame.size.height)/2.0f );
|
jens@10
|
68 |
self.frame = curFrame;
|
jens@10
|
69 |
}
|
jens@10
|
70 |
return self;
|
jens@0
|
71 |
}
|
jens@0
|
72 |
|
jens@0
|
73 |
|
jens@0
|
74 |
- (void) dealloc
|
jens@0
|
75 |
{
|
jens@0
|
76 |
CGPathRelease(_cellPath);
|
jens@0
|
77 |
[super dealloc];
|
jens@0
|
78 |
}
|
jens@0
|
79 |
|
jens@0
|
80 |
|
jens@0
|
81 |
- (void) addCellsInHexagon
|
jens@0
|
82 |
{
|
jens@0
|
83 |
int size = _nRows - !(_nRows & 1); // make it odd
|
jens@0
|
84 |
for( int row=0; row<_nRows; row++ ) {
|
jens@0
|
85 |
int n; // # of hexes remaining in this row
|
jens@0
|
86 |
if( row < size )
|
jens@0
|
87 |
n = size - abs(row-size/2);
|
jens@0
|
88 |
else
|
jens@0
|
89 |
n = 0;
|
jens@0
|
90 |
int c0 = floor(((int)_nRows+1-n -(row&1))/2.0); // col of 1st remaining hex
|
jens@0
|
91 |
|
jens@0
|
92 |
for( int col=0; col<_nColumns; col++ )
|
jens@0
|
93 |
if( col>=c0 && col<c0+n )
|
jens@0
|
94 |
[self addCellAtRow: row column: col];
|
jens@0
|
95 |
}
|
jens@0
|
96 |
}
|
jens@0
|
97 |
|
jens@0
|
98 |
|
jens@0
|
99 |
- (GridCell*) createCellAtRow: (unsigned)row column: (unsigned)col
|
jens@0
|
100 |
suggestedFrame: (CGRect)frame
|
jens@0
|
101 |
{
|
jens@0
|
102 |
// Overridden to stagger the odd-numbered rows
|
jens@16
|
103 |
BOOL stagger = (row & 1) != 0;
|
jens@16
|
104 |
if( _reversed && (_nRows & 1) )
|
jens@16
|
105 |
stagger = !stagger;
|
jens@16
|
106 |
if( stagger )
|
jens@0
|
107 |
frame.origin.x += _spacing.width/2;
|
jens@0
|
108 |
frame.size.height += _capHeight;
|
jens@0
|
109 |
return [super createCellAtRow: row column: col suggestedFrame: frame];
|
jens@0
|
110 |
}
|
jens@0
|
111 |
|
jens@0
|
112 |
|
jens@0
|
113 |
// Returns a hexagonal CG path defining a cell's outline. Used by cells when drawing & hit-testing.
|
jens@0
|
114 |
- (CGPathRef) cellPath
|
jens@0
|
115 |
{
|
jens@0
|
116 |
if( ! _cellPath ) {
|
jens@0
|
117 |
CGFloat x1 = _spacing.width/2;
|
jens@0
|
118 |
CGFloat x2 = _spacing.width;
|
jens@0
|
119 |
CGFloat y1 = _capHeight;
|
jens@0
|
120 |
CGFloat y2 = y1 + _side;
|
jens@0
|
121 |
CGFloat y3 = y2 + _capHeight;
|
jens@0
|
122 |
CGPoint p[6] = { {0,y1}, {x1,0}, {x2,y1}, {x2,y2}, {x1,y3}, {0,y2} };
|
jens@0
|
123 |
|
jens@0
|
124 |
CGMutablePathRef path = CGPathCreateMutable();
|
jens@0
|
125 |
CGPathAddLines(path, NULL, p, 6);
|
jens@0
|
126 |
CGPathCloseSubpath(path);
|
jens@0
|
127 |
_cellPath = path;
|
jens@0
|
128 |
}
|
jens@0
|
129 |
return _cellPath;
|
jens@0
|
130 |
}
|
jens@0
|
131 |
|
jens@0
|
132 |
|
jens@0
|
133 |
@end
|
jens@0
|
134 |
|
jens@0
|
135 |
|
jens@0
|
136 |
|
jens@0
|
137 |
|
jens@0
|
138 |
|
jens@0
|
139 |
#pragma mark -
|
jens@0
|
140 |
|
jens@0
|
141 |
@implementation Hex
|
jens@0
|
142 |
|
jens@0
|
143 |
|
jens@0
|
144 |
- (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
|
jens@0
|
145 |
{
|
jens@0
|
146 |
CGContextSaveGState(ctx);
|
jens@0
|
147 |
CGPoint pos = self.position;
|
jens@0
|
148 |
CGContextTranslateCTM(ctx, pos.x, pos.y);
|
jens@0
|
149 |
CGContextBeginPath(ctx);
|
jens@0
|
150 |
CGContextAddPath(ctx, ((HexGrid*)_grid).cellPath);
|
jens@0
|
151 |
CGContextDrawPath(ctx, (fill ?kCGPathFill :kCGPathStroke));
|
jens@0
|
152 |
|
jens@0
|
153 |
if( !fill && self.highlighted ) {
|
jens@0
|
154 |
// Highlight by drawing my outline in the highlight color:
|
jens@0
|
155 |
CGContextSetStrokeColorWithColor(ctx, self.borderColor);
|
jens@0
|
156 |
CGContextSetLineWidth(ctx,6);
|
jens@0
|
157 |
CGContextBeginPath(ctx);
|
jens@0
|
158 |
CGContextAddPath(ctx, ((HexGrid*)_grid).cellPath);
|
jens@0
|
159 |
CGContextDrawPath(ctx, kCGPathStroke);
|
jens@0
|
160 |
}
|
jens@0
|
161 |
CGContextRestoreGState(ctx);
|
jens@0
|
162 |
}
|
jens@0
|
163 |
|
jens@0
|
164 |
|
jens@0
|
165 |
- (BOOL)containsPoint:(CGPoint)p
|
jens@0
|
166 |
{
|
jens@0
|
167 |
return [super containsPoint: p]
|
jens@0
|
168 |
&& CGPathContainsPoint( ((HexGrid*)_grid).cellPath, NULL, p, NO );
|
jens@0
|
169 |
}
|
jens@0
|
170 |
|
jens@0
|
171 |
|
jens@0
|
172 |
- (void) setHighlighted: (BOOL)highlighted
|
jens@0
|
173 |
{
|
jens@0
|
174 |
if( highlighted != self.highlighted ) {
|
jens@0
|
175 |
[super setHighlighted: highlighted];
|
jens@0
|
176 |
[_grid setNeedsDisplay]; // So I'll be asked to redraw myself
|
jens@0
|
177 |
}
|
jens@0
|
178 |
}
|
jens@0
|
179 |
|
jens@0
|
180 |
|
jens@0
|
181 |
#pragma mark -
|
jens@0
|
182 |
#pragma mark NEIGHBORS:
|
jens@0
|
183 |
|
jens@0
|
184 |
|
jens@0
|
185 |
- (NSArray*) neighbors
|
jens@0
|
186 |
{
|
jens@0
|
187 |
NSMutableArray *neighbors = [NSMutableArray arrayWithCapacity: 6];
|
jens@0
|
188 |
Hex* n[6] = {self.nw, self.ne, self.w, self.e, self.sw, self.se};
|
jens@0
|
189 |
for( int i=0; i<6; i++ )
|
jens@0
|
190 |
if( n[i] )
|
jens@0
|
191 |
[neighbors addObject: n[i]];
|
jens@0
|
192 |
return neighbors;
|
jens@0
|
193 |
}
|
jens@0
|
194 |
|
jens@0
|
195 |
- (Hex*) nw {return (Hex*)[_grid cellAtRow: _row+1 column: _column - ((_row+1)&1)];}
|
jens@0
|
196 |
- (Hex*) ne {return (Hex*)[_grid cellAtRow: _row+1 column: _column + (_row&1)];}
|
jens@0
|
197 |
- (Hex*) e {return (Hex*)[_grid cellAtRow: _row column: _column + 1];}
|
jens@0
|
198 |
- (Hex*) se {return (Hex*)[_grid cellAtRow: _row-1 column: _column + (_row&1)];}
|
jens@0
|
199 |
- (Hex*) sw {return (Hex*)[_grid cellAtRow: _row-1 column: _column - ((_row-1)&1)];}
|
jens@0
|
200 |
- (Hex*) w {return (Hex*)[_grid cellAtRow: _row column: _column - 1];}
|
jens@0
|
201 |
|
jens@0
|
202 |
// Directions relative to the current player:
|
jens@0
|
203 |
- (Hex*) fl {return self.fwdIsN ?self.nw :self.se;}
|
jens@0
|
204 |
- (Hex*) fr {return self.fwdIsN ?self.ne :self.sw;}
|
jens@0
|
205 |
- (Hex*) r {return self.fwdIsN ?self.e :self.w;}
|
jens@0
|
206 |
- (Hex*) br {return self.fwdIsN ?self.se :self.nw;}
|
jens@0
|
207 |
- (Hex*) bl {return self.fwdIsN ?self.sw :self.ne;}
|
jens@0
|
208 |
- (Hex*) l {return self.fwdIsN ?self.w :self.e;}
|
jens@0
|
209 |
|
jens@0
|
210 |
|
jens@0
|
211 |
@end
|