diff options
Diffstat (limited to 'libs/cocos2d/CCTMXLayer.m')
-rwxr-xr-x | libs/cocos2d/CCTMXLayer.m | 670 |
1 files changed, 670 insertions, 0 deletions
diff --git a/libs/cocos2d/CCTMXLayer.m b/libs/cocos2d/CCTMXLayer.m new file mode 100755 index 0000000..bb2ba60 --- /dev/null +++ b/libs/cocos2d/CCTMXLayer.m | |||
@@ -0,0 +1,670 @@ | |||
1 | /* | ||
2 | * cocos2d for iPhone: http://www.cocos2d-iphone.org | ||
3 | * | ||
4 | * Copyright (c) 2009-2010 Ricardo Quesada | ||
5 | * Copyright (c) 2011 Zynga Inc. | ||
6 | * | ||
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
8 | * of this software and associated documentation files (the "Software"), to deal | ||
9 | * in the Software without restriction, including without limitation the rights | ||
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
11 | * copies of the Software, and to permit persons to whom the Software is | ||
12 | * furnished to do so, subject to the following conditions: | ||
13 | * | ||
14 | * The above copyright notice and this permission notice shall be included in | ||
15 | * all copies or substantial portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
23 | * THE SOFTWARE. | ||
24 | * | ||
25 | * | ||
26 | * TMX Tiled Map support: | ||
27 | * http://www.mapeditor.org | ||
28 | * | ||
29 | */ | ||
30 | |||
31 | #import "CCTMXLayer.h" | ||
32 | #import "CCTMXTiledMap.h" | ||
33 | #import "CCTMXXMLParser.h" | ||
34 | #import "CCSprite.h" | ||
35 | #import "CCSpriteBatchNode.h" | ||
36 | #import "CCTextureCache.h" | ||
37 | #import "Support/CGPointExtension.h" | ||
38 | |||
39 | #pragma mark - | ||
40 | #pragma mark CCSpriteBatchNode Extension | ||
41 | |||
42 | @interface CCSpriteBatchNode (TMXTiledMapExtensions) | ||
43 | -(id) addSpriteWithoutQuad:(CCSprite*)child z:(NSUInteger)z tag:(NSInteger)aTag; | ||
44 | -(void) addQuadFromSprite:(CCSprite*)sprite quadIndex:(NSUInteger)index; | ||
45 | @end | ||
46 | |||
47 | /* IMPORTANT XXX IMPORTNAT: | ||
48 | * These 2 methods can't be part of CCTMXLayer since they call [super add...], and CCSpriteBatchNode#add SHALL not be called | ||
49 | */ | ||
50 | @implementation CCSpriteBatchNode (TMXTiledMapExtension) | ||
51 | |||
52 | /* Adds a quad into the texture atlas but it won't be added into the children array. | ||
53 | This method should be called only when you are dealing with very big AtlasSrite and when most of the CCSprite won't be updated. | ||
54 | For example: a tile map (CCTMXMap) or a label with lots of characgers (CCLabelBMFont) | ||
55 | */ | ||
56 | -(void) addQuadFromSprite:(CCSprite*)sprite quadIndex:(NSUInteger)index | ||
57 | { | ||
58 | NSAssert( sprite != nil, @"Argument must be non-nil"); | ||
59 | NSAssert( [sprite isKindOfClass:[CCSprite class]], @"CCSpriteBatchNode only supports CCSprites as children"); | ||
60 | |||
61 | |||
62 | while(index >= textureAtlas_.capacity || textureAtlas_.capacity == textureAtlas_.totalQuads ) | ||
63 | [self increaseAtlasCapacity]; | ||
64 | |||
65 | // | ||
66 | // update the quad directly. Don't add the sprite to the scene graph | ||
67 | // | ||
68 | |||
69 | [sprite useBatchNode:self]; | ||
70 | [sprite setAtlasIndex:index]; | ||
71 | |||
72 | ccV3F_C4B_T2F_Quad quad = [sprite quad]; | ||
73 | [textureAtlas_ insertQuad:&quad atIndex:index]; | ||
74 | |||
75 | // XXX: updateTransform will update the textureAtlas too using updateQuad. | ||
76 | // XXX: so, it should be AFTER the insertQuad | ||
77 | [sprite setDirty:YES]; | ||
78 | [sprite updateTransform]; | ||
79 | } | ||
80 | |||
81 | /* This is the opposite of "addQuadFromSprite. | ||
82 | It add the sprite to the children and descendants array, but it doesn't update add it to the texture atlas | ||
83 | */ | ||
84 | -(id) addSpriteWithoutQuad:(CCSprite*)child z:(NSUInteger)z tag:(NSInteger)aTag | ||
85 | { | ||
86 | NSAssert( child != nil, @"Argument must be non-nil"); | ||
87 | NSAssert( [child isKindOfClass:[CCSprite class]], @"CCSpriteBatchNode only supports CCSprites as children"); | ||
88 | |||
89 | // quad index is Z | ||
90 | [child setAtlasIndex:z]; | ||
91 | |||
92 | // XXX: optimize with a binary search | ||
93 | int i=0; | ||
94 | for( CCSprite *c in descendants_ ) { | ||
95 | if( c.atlasIndex >= z ) | ||
96 | break; | ||
97 | i++; | ||
98 | } | ||
99 | [descendants_ insertObject:child atIndex:i]; | ||
100 | |||
101 | |||
102 | // IMPORTANT: Call super, and not self. Avoid adding it to the texture atlas array | ||
103 | [super addChild:child z:z tag:aTag]; | ||
104 | return self; | ||
105 | } | ||
106 | @end | ||
107 | |||
108 | |||
109 | #pragma mark - | ||
110 | #pragma mark CCTMXLayer | ||
111 | |||
112 | int compareInts (const void * a, const void * b); | ||
113 | |||
114 | |||
115 | @interface CCTMXLayer () | ||
116 | -(CGPoint) positionForIsoAt:(CGPoint)pos; | ||
117 | -(CGPoint) positionForOrthoAt:(CGPoint)pos; | ||
118 | -(CGPoint) positionForHexAt:(CGPoint)pos; | ||
119 | |||
120 | -(CGPoint) calculateLayerOffset:(CGPoint)offset; | ||
121 | |||
122 | /* optimization methos */ | ||
123 | -(CCSprite*) appendTileForGID:(uint32_t)gid at:(CGPoint)pos; | ||
124 | -(CCSprite*) insertTileForGID:(uint32_t)gid at:(CGPoint)pos; | ||
125 | -(CCSprite*) updateTileForGID:(uint32_t)gid at:(CGPoint)pos; | ||
126 | |||
127 | /* The layer recognizes some special properties, like cc_vertez */ | ||
128 | -(void) parseInternalProperties; | ||
129 | |||
130 | -(NSInteger) vertexZForPos:(CGPoint)pos; | ||
131 | |||
132 | // index | ||
133 | -(NSUInteger) atlasIndexForExistantZ:(NSUInteger)z; | ||
134 | -(NSUInteger) atlasIndexForNewZ:(NSUInteger)z; | ||
135 | @end | ||
136 | |||
137 | @implementation CCTMXLayer | ||
138 | @synthesize layerSize = layerSize_, layerName = layerName_, tiles = tiles_; | ||
139 | @synthesize tileset = tileset_; | ||
140 | @synthesize layerOrientation = layerOrientation_; | ||
141 | @synthesize mapTileSize = mapTileSize_; | ||
142 | @synthesize properties = properties_; | ||
143 | |||
144 | #pragma mark CCTMXLayer - init & alloc & dealloc | ||
145 | |||
146 | +(id) layerWithTilesetInfo:(CCTMXTilesetInfo*)tilesetInfo layerInfo:(CCTMXLayerInfo*)layerInfo mapInfo:(CCTMXMapInfo*)mapInfo | ||
147 | { | ||
148 | return [[[self alloc] initWithTilesetInfo:tilesetInfo layerInfo:layerInfo mapInfo:mapInfo] autorelease]; | ||
149 | } | ||
150 | |||
151 | -(id) initWithTilesetInfo:(CCTMXTilesetInfo*)tilesetInfo layerInfo:(CCTMXLayerInfo*)layerInfo mapInfo:(CCTMXMapInfo*)mapInfo | ||
152 | { | ||
153 | // XXX: is 35% a good estimate ? | ||
154 | CGSize size = layerInfo.layerSize; | ||
155 | float totalNumberOfTiles = size.width * size.height; | ||
156 | float capacity = totalNumberOfTiles * 0.35f + 1; // 35 percent is occupied ? | ||
157 | |||
158 | CCTexture2D *tex = nil; | ||
159 | if( tilesetInfo ) | ||
160 | tex = [[CCTextureCache sharedTextureCache] addImage:tilesetInfo.sourceImage]; | ||
161 | |||
162 | if((self = [super initWithTexture:tex capacity:capacity])) { | ||
163 | |||
164 | // layerInfo | ||
165 | self.layerName = layerInfo.name; | ||
166 | layerSize_ = layerInfo.layerSize; | ||
167 | tiles_ = layerInfo.tiles; | ||
168 | minGID_ = layerInfo.minGID; | ||
169 | maxGID_ = layerInfo.maxGID; | ||
170 | opacity_ = layerInfo.opacity; | ||
171 | self.properties = [NSMutableDictionary dictionaryWithDictionary:layerInfo.properties]; | ||
172 | |||
173 | // tilesetInfo | ||
174 | self.tileset = tilesetInfo; | ||
175 | |||
176 | // mapInfo | ||
177 | mapTileSize_ = mapInfo.tileSize; | ||
178 | layerOrientation_ = mapInfo.orientation; | ||
179 | |||
180 | // offset (after layer orientation is set); | ||
181 | CGPoint offset = [self calculateLayerOffset:layerInfo.offset]; | ||
182 | [self setPositionInPixels:offset]; | ||
183 | |||
184 | atlasIndexArray_ = ccCArrayNew(totalNumberOfTiles); | ||
185 | |||
186 | [self setContentSizeInPixels: CGSizeMake( layerSize_.width * mapTileSize_.width, layerSize_.height * mapTileSize_.height )]; | ||
187 | |||
188 | useAutomaticVertexZ_= NO; | ||
189 | vertexZvalue_ = 0; | ||
190 | alphaFuncValue_ = 0; | ||
191 | |||
192 | } | ||
193 | return self; | ||
194 | } | ||
195 | |||
196 | - (void) dealloc | ||
197 | { | ||
198 | [layerName_ release]; | ||
199 | [tileset_ release]; | ||
200 | [reusedTile_ release]; | ||
201 | [properties_ release]; | ||
202 | |||
203 | if( atlasIndexArray_ ) { | ||
204 | ccCArrayFree(atlasIndexArray_); | ||
205 | atlasIndexArray_ = NULL; | ||
206 | } | ||
207 | |||
208 | if( tiles_ ) { | ||
209 | free(tiles_); | ||
210 | tiles_ = NULL; | ||
211 | } | ||
212 | |||
213 | [super dealloc]; | ||
214 | } | ||
215 | |||
216 | -(void) releaseMap | ||
217 | { | ||
218 | if( tiles_) { | ||
219 | free( tiles_); | ||
220 | tiles_ = NULL; | ||
221 | } | ||
222 | |||
223 | if( atlasIndexArray_ ) { | ||
224 | ccCArrayFree(atlasIndexArray_); | ||
225 | atlasIndexArray_ = NULL; | ||
226 | } | ||
227 | } | ||
228 | |||
229 | #pragma mark CCTMXLayer - setup Tiles | ||
230 | |||
231 | -(void) setupTiles | ||
232 | { | ||
233 | // Optimization: quick hack that sets the image size on the tileset | ||
234 | tileset_.imageSize = [textureAtlas_.texture contentSizeInPixels]; | ||
235 | |||
236 | // By default all the tiles are aliased | ||
237 | // pros: | ||
238 | // - easier to render | ||
239 | // cons: | ||
240 | // - difficult to scale / rotate / etc. | ||
241 | [textureAtlas_.texture setAliasTexParameters]; | ||
242 | |||
243 | CFByteOrder o = CFByteOrderGetCurrent(); | ||
244 | |||
245 | // Parse cocos2d properties | ||
246 | [self parseInternalProperties]; | ||
247 | |||
248 | for( NSUInteger y=0; y < layerSize_.height; y++ ) { | ||
249 | for( NSUInteger x=0; x < layerSize_.width; x++ ) { | ||
250 | |||
251 | NSUInteger pos = x + layerSize_.width * y; | ||
252 | uint32_t gid = tiles_[ pos ]; | ||
253 | |||
254 | // gid are stored in little endian. | ||
255 | // if host is big endian, then swap | ||
256 | if( o == CFByteOrderBigEndian ) | ||
257 | gid = CFSwapInt32( gid ); | ||
258 | |||
259 | // XXX: gid == 0 --> empty tile | ||
260 | if( gid != 0 ) { | ||
261 | [self appendTileForGID:gid at:ccp(x,y)]; | ||
262 | |||
263 | // Optimization: update min and max GID rendered by the layer | ||
264 | minGID_ = MIN(gid, minGID_); | ||
265 | maxGID_ = MAX(gid, maxGID_); | ||
266 | } | ||
267 | } | ||
268 | } | ||
269 | |||
270 | NSAssert( maxGID_ >= tileset_.firstGid && | ||
271 | minGID_ >= tileset_.firstGid, @"TMX: Only 1 tilset per layer is supported"); | ||
272 | } | ||
273 | |||
274 | #pragma mark CCTMXLayer - Properties | ||
275 | |||
276 | -(id) propertyNamed:(NSString *)propertyName | ||
277 | { | ||
278 | return [properties_ valueForKey:propertyName]; | ||
279 | } | ||
280 | |||
281 | -(void) parseInternalProperties | ||
282 | { | ||
283 | // if cc_vertex=automatic, then tiles will be rendered using vertexz | ||
284 | |||
285 | NSString *vertexz = [self propertyNamed:@"cc_vertexz"]; | ||
286 | if( vertexz ) { | ||
287 | if( [vertexz isEqualToString:@"automatic"] ) | ||
288 | useAutomaticVertexZ_ = YES; | ||
289 | else | ||
290 | vertexZvalue_ = [vertexz intValue]; | ||
291 | } | ||
292 | |||
293 | NSString *alphaFuncVal = [self propertyNamed:@"cc_alpha_func"]; | ||
294 | alphaFuncValue_ = [alphaFuncVal floatValue]; | ||
295 | } | ||
296 | |||
297 | #pragma mark CCTMXLayer - obtaining tiles/gids | ||
298 | |||
299 | -(CCSprite*) tileAt:(CGPoint)pos | ||
300 | { | ||
301 | NSAssert( pos.x < layerSize_.width && pos.y < layerSize_.height && pos.x >=0 && pos.y >=0, @"TMXLayer: invalid position"); | ||
302 | NSAssert( tiles_ && atlasIndexArray_, @"TMXLayer: the tiles map has been released"); | ||
303 | |||
304 | CCSprite *tile = nil; | ||
305 | uint32_t gid = [self tileGIDAt:pos]; | ||
306 | |||
307 | // if GID == 0, then no tile is present | ||
308 | if( gid ) { | ||
309 | int z = pos.x + pos.y * layerSize_.width; | ||
310 | tile = (CCSprite*) [self getChildByTag:z]; | ||
311 | |||
312 | // tile not created yet. create it | ||
313 | if( ! tile ) { | ||
314 | CGRect rect = [tileset_ rectForGID:gid]; | ||
315 | tile = [[CCSprite alloc] initWithBatchNode:self rectInPixels:rect]; | ||
316 | [tile setPositionInPixels: [self positionAt:pos]]; | ||
317 | [tile setVertexZ: [self vertexZForPos:pos]]; | ||
318 | tile.anchorPoint = CGPointZero; | ||
319 | [tile setOpacity:opacity_]; | ||
320 | |||
321 | NSUInteger indexForZ = [self atlasIndexForExistantZ:z]; | ||
322 | [self addSpriteWithoutQuad:tile z:indexForZ tag:z]; | ||
323 | [tile release]; | ||
324 | } | ||
325 | } | ||
326 | return tile; | ||
327 | } | ||
328 | |||
329 | -(uint32_t) tileGIDAt:(CGPoint)pos | ||
330 | { | ||
331 | NSAssert( pos.x < layerSize_.width && pos.y < layerSize_.height && pos.x >=0 && pos.y >=0, @"TMXLayer: invalid position"); | ||
332 | NSAssert( tiles_ && atlasIndexArray_, @"TMXLayer: the tiles map has been released"); | ||
333 | |||
334 | NSInteger idx = pos.x + pos.y * layerSize_.width; | ||
335 | return tiles_[ idx ]; | ||
336 | } | ||
337 | |||
338 | #pragma mark CCTMXLayer - adding helper methods | ||
339 | |||
340 | -(CCSprite*) insertTileForGID:(uint32_t)gid at:(CGPoint)pos | ||
341 | { | ||
342 | CGRect rect = [tileset_ rectForGID:gid]; | ||
343 | |||
344 | NSInteger z = pos.x + pos.y * layerSize_.width; | ||
345 | |||
346 | if( ! reusedTile_ ) | ||
347 | reusedTile_ = [[CCSprite alloc] initWithBatchNode:self rectInPixels:rect]; | ||
348 | else | ||
349 | [reusedTile_ initWithBatchNode:self rectInPixels:rect]; | ||
350 | |||
351 | [reusedTile_ setPositionInPixels: [self positionAt:pos]]; | ||
352 | [reusedTile_ setVertexZ: [self vertexZForPos:pos]]; | ||
353 | reusedTile_.anchorPoint = CGPointZero; | ||
354 | [reusedTile_ setOpacity:opacity_]; | ||
355 | |||
356 | // get atlas index | ||
357 | NSUInteger indexForZ = [self atlasIndexForNewZ:z]; | ||
358 | |||
359 | // Optimization: add the quad without adding a child | ||
360 | [self addQuadFromSprite:reusedTile_ quadIndex:indexForZ]; | ||
361 | |||
362 | // insert it into the local atlasindex array | ||
363 | ccCArrayInsertValueAtIndex(atlasIndexArray_, (void*)z, indexForZ); | ||
364 | |||
365 | // update possible children | ||
366 | CCSprite *sprite; | ||
367 | CCARRAY_FOREACH(children_, sprite) { | ||
368 | NSUInteger ai = [sprite atlasIndex]; | ||
369 | if( ai >= indexForZ) | ||
370 | [sprite setAtlasIndex: ai+1]; | ||
371 | } | ||
372 | |||
373 | tiles_[z] = gid; | ||
374 | |||
375 | return reusedTile_; | ||
376 | } | ||
377 | |||
378 | -(CCSprite*) updateTileForGID:(uint32_t)gid at:(CGPoint)pos | ||
379 | { | ||
380 | CGRect rect = [tileset_ rectForGID:gid]; | ||
381 | |||
382 | int z = pos.x + pos.y * layerSize_.width; | ||
383 | |||
384 | if( ! reusedTile_ ) | ||
385 | reusedTile_ = [[CCSprite alloc] initWithBatchNode:self rectInPixels:rect]; | ||
386 | else | ||
387 | [reusedTile_ initWithBatchNode:self rectInPixels:rect]; | ||
388 | |||
389 | [reusedTile_ setPositionInPixels: [self positionAt:pos]]; | ||
390 | [reusedTile_ setVertexZ: [self vertexZForPos:pos]]; | ||
391 | reusedTile_.anchorPoint = CGPointZero; | ||
392 | [reusedTile_ setOpacity:opacity_]; | ||
393 | |||
394 | // get atlas index | ||
395 | NSUInteger indexForZ = [self atlasIndexForExistantZ:z]; | ||
396 | |||
397 | [reusedTile_ setAtlasIndex:indexForZ]; | ||
398 | [reusedTile_ setDirty:YES]; | ||
399 | [reusedTile_ updateTransform]; | ||
400 | tiles_[z] = gid; | ||
401 | |||
402 | return reusedTile_; | ||
403 | } | ||
404 | |||
405 | |||
406 | // used only when parsing the map. useless after the map was parsed | ||
407 | // since lot's of assumptions are no longer true | ||
408 | -(CCSprite*) appendTileForGID:(uint32_t)gid at:(CGPoint)pos | ||
409 | { | ||
410 | CGRect rect = [tileset_ rectForGID:gid]; | ||
411 | |||
412 | NSInteger z = pos.x + pos.y * layerSize_.width; | ||
413 | |||
414 | if( ! reusedTile_ ) | ||
415 | reusedTile_ = [[CCSprite alloc] initWithBatchNode:self rectInPixels:rect]; | ||
416 | else | ||
417 | [reusedTile_ initWithBatchNode:self rectInPixels:rect]; | ||
418 | |||
419 | [reusedTile_ setPositionInPixels: [self positionAt:pos]]; | ||
420 | [reusedTile_ setVertexZ: [self vertexZForPos:pos]]; | ||
421 | reusedTile_.anchorPoint = CGPointZero; | ||
422 | [reusedTile_ setOpacity:opacity_]; | ||
423 | |||
424 | // optimization: | ||
425 | // The difference between appendTileForGID and insertTileforGID is that append is faster, since | ||
426 | // it appends the tile at the end of the texture atlas | ||
427 | NSUInteger indexForZ = atlasIndexArray_->num; | ||
428 | |||
429 | |||
430 | // don't add it using the "standard" way. | ||
431 | [self addQuadFromSprite:reusedTile_ quadIndex:indexForZ]; | ||
432 | |||
433 | |||
434 | // append should be after addQuadFromSprite since it modifies the quantity values | ||
435 | ccCArrayInsertValueAtIndex(atlasIndexArray_, (void*)z, indexForZ); | ||
436 | |||
437 | return reusedTile_; | ||
438 | } | ||
439 | |||
440 | #pragma mark CCTMXLayer - atlasIndex and Z | ||
441 | |||
442 | int compareInts (const void * a, const void * b) | ||
443 | { | ||
444 | return ( *(int*)a - *(int*)b ); | ||
445 | } | ||
446 | |||
447 | -(NSUInteger) atlasIndexForExistantZ:(NSUInteger)z | ||
448 | { | ||
449 | NSInteger key = z; | ||
450 | NSInteger *item = bsearch((void*)&key, (void*)&atlasIndexArray_->arr[0], atlasIndexArray_->num, sizeof(void*), compareInts); | ||
451 | |||
452 | NSAssert( item, @"TMX atlas index not found. Shall not happen"); | ||
453 | |||
454 | NSUInteger index = ((NSInteger)item - (NSInteger)atlasIndexArray_->arr) / sizeof(void*); | ||
455 | return index; | ||
456 | } | ||
457 | |||
458 | -(NSUInteger)atlasIndexForNewZ:(NSUInteger)z | ||
459 | { | ||
460 | // XXX: This can be improved with a sort of binary search | ||
461 | NSUInteger i = 0; | ||
462 | for(i = 0; i< atlasIndexArray_->num; i++) { | ||
463 | NSUInteger val = (NSUInteger) atlasIndexArray_->arr[i]; | ||
464 | if( z < val ) | ||
465 | break; | ||
466 | } | ||
467 | return i; | ||
468 | } | ||
469 | |||
470 | #pragma mark CCTMXLayer - adding / remove tiles | ||
471 | |||
472 | -(void) setTileGID:(uint32_t)gid at:(CGPoint)pos | ||
473 | { | ||
474 | NSAssert( pos.x < layerSize_.width && pos.y < layerSize_.height && pos.x >=0 && pos.y >=0, @"TMXLayer: invalid position"); | ||
475 | NSAssert( tiles_ && atlasIndexArray_, @"TMXLayer: the tiles map has been released"); | ||
476 | NSAssert( gid == 0 || gid >= tileset_.firstGid, @"TMXLayer: invalid gid" ); | ||
477 | |||
478 | uint32_t currentGID = [self tileGIDAt:pos]; | ||
479 | |||
480 | if( currentGID != gid ) { | ||
481 | |||
482 | // setting gid=0 is equal to remove the tile | ||
483 | if( gid == 0 ) | ||
484 | [self removeTileAt:pos]; | ||
485 | |||
486 | // empty tile. create a new one | ||
487 | else if( currentGID == 0 ) | ||
488 | [self insertTileForGID:gid at:pos]; | ||
489 | |||
490 | // modifying an existing tile with a non-empty tile | ||
491 | else { | ||
492 | |||
493 | NSUInteger z = pos.x + pos.y * layerSize_.width; | ||
494 | id sprite = [self getChildByTag:z]; | ||
495 | if( sprite ) { | ||
496 | CGRect rect = [tileset_ rectForGID:gid]; | ||
497 | [sprite setTextureRectInPixels:rect rotated:NO untrimmedSize:rect.size]; | ||
498 | tiles_[z] = gid; | ||
499 | } else | ||
500 | [self updateTileForGID:gid at:pos]; | ||
501 | } | ||
502 | } | ||
503 | } | ||
504 | |||
505 | -(void) addChild: (CCNode*)node z:(NSInteger)z tag:(NSInteger)tag | ||
506 | { | ||
507 | NSAssert(NO, @"addChild: is not supported on CCTMXLayer. Instead use setTileGID:at:/tileAt:"); | ||
508 | } | ||
509 | |||
510 | -(void) removeChild:(CCSprite*)sprite cleanup:(BOOL)cleanup | ||
511 | { | ||
512 | // allows removing nil objects | ||
513 | if( ! sprite ) | ||
514 | return; | ||
515 | |||
516 | NSAssert( [children_ containsObject:sprite], @"Tile does not belong to TMXLayer"); | ||
517 | |||
518 | NSUInteger atlasIndex = [sprite atlasIndex]; | ||
519 | NSUInteger zz = (NSUInteger) atlasIndexArray_->arr[atlasIndex]; | ||
520 | tiles_[zz] = 0; | ||
521 | ccCArrayRemoveValueAtIndex(atlasIndexArray_, atlasIndex); | ||
522 | [super removeChild:sprite cleanup:cleanup]; | ||
523 | } | ||
524 | |||
525 | -(void) removeTileAt:(CGPoint)pos | ||
526 | { | ||
527 | NSAssert( pos.x < layerSize_.width && pos.y < layerSize_.height && pos.x >=0 && pos.y >=0, @"TMXLayer: invalid position"); | ||
528 | NSAssert( tiles_ && atlasIndexArray_, @"TMXLayer: the tiles map has been released"); | ||
529 | |||
530 | uint32_t gid = [self tileGIDAt:pos]; | ||
531 | |||
532 | if( gid ) { | ||
533 | |||
534 | NSUInteger z = pos.x + pos.y * layerSize_.width; | ||
535 | NSUInteger atlasIndex = [self atlasIndexForExistantZ:z]; | ||
536 | |||
537 | // remove tile from GID map | ||
538 | tiles_[z] = 0; | ||
539 | |||
540 | // remove tile from atlas position array | ||
541 | ccCArrayRemoveValueAtIndex(atlasIndexArray_, atlasIndex); | ||
542 | |||
543 | // remove it from sprites and/or texture atlas | ||
544 | id sprite = [self getChildByTag:z]; | ||
545 | if( sprite ) | ||
546 | [super removeChild:sprite cleanup:YES]; | ||
547 | else { | ||
548 | [textureAtlas_ removeQuadAtIndex:atlasIndex]; | ||
549 | |||
550 | // update possible children | ||
551 | CCARRAY_FOREACH(children_, sprite) { | ||
552 | NSUInteger ai = [sprite atlasIndex]; | ||
553 | if( ai >= atlasIndex) { | ||
554 | [sprite setAtlasIndex: ai-1]; | ||
555 | } | ||
556 | } | ||
557 | } | ||
558 | } | ||
559 | } | ||
560 | |||
561 | #pragma mark CCTMXLayer - obtaining positions, offset | ||
562 | |||
563 | -(CGPoint) calculateLayerOffset:(CGPoint)pos | ||
564 | { | ||
565 | CGPoint ret = CGPointZero; | ||
566 | switch( layerOrientation_ ) { | ||
567 | case CCTMXOrientationOrtho: | ||
568 | ret = ccp( pos.x * mapTileSize_.width, -pos.y *mapTileSize_.height); | ||
569 | break; | ||
570 | case CCTMXOrientationIso: | ||
571 | ret = ccp( (mapTileSize_.width /2) * (pos.x - pos.y), | ||
572 | (mapTileSize_.height /2 ) * (-pos.x - pos.y) ); | ||
573 | break; | ||
574 | case CCTMXOrientationHex: | ||
575 | NSAssert(CGPointEqualToPoint(pos, CGPointZero), @"offset for hexagonal map not implemented yet"); | ||
576 | break; | ||
577 | } | ||
578 | return ret; | ||
579 | } | ||
580 | |||
581 | -(CGPoint) positionAt:(CGPoint)pos | ||
582 | { | ||
583 | CGPoint ret = CGPointZero; | ||
584 | switch( layerOrientation_ ) { | ||
585 | case CCTMXOrientationOrtho: | ||
586 | ret = [self positionForOrthoAt:pos]; | ||
587 | break; | ||
588 | case CCTMXOrientationIso: | ||
589 | ret = [self positionForIsoAt:pos]; | ||
590 | break; | ||
591 | case CCTMXOrientationHex: | ||
592 | ret = [self positionForHexAt:pos]; | ||
593 | break; | ||
594 | } | ||
595 | return ret; | ||
596 | } | ||
597 | |||
598 | -(CGPoint) positionForOrthoAt:(CGPoint)pos | ||
599 | { | ||
600 | CGPoint xy = { | ||
601 | pos.x * mapTileSize_.width, | ||
602 | (layerSize_.height - pos.y - 1) * mapTileSize_.height, | ||
603 | }; | ||
604 | return xy; | ||
605 | } | ||
606 | |||
607 | -(CGPoint) positionForIsoAt:(CGPoint)pos | ||
608 | { | ||
609 | CGPoint xy = { | ||
610 | mapTileSize_.width /2 * ( layerSize_.width + pos.x - pos.y - 1), | ||
611 | mapTileSize_.height /2 * (( layerSize_.height * 2 - pos.x - pos.y) - 2), | ||
612 | }; | ||
613 | return xy; | ||
614 | } | ||
615 | |||
616 | -(CGPoint) positionForHexAt:(CGPoint)pos | ||
617 | { | ||
618 | float diffY = 0; | ||
619 | if( (int)pos.x % 2 == 1 ) | ||
620 | diffY = -mapTileSize_.height/2 ; | ||
621 | |||
622 | CGPoint xy = { | ||
623 | pos.x * mapTileSize_.width*3/4, | ||
624 | (layerSize_.height - pos.y - 1) * mapTileSize_.height + diffY | ||
625 | }; | ||
626 | return xy; | ||
627 | } | ||
628 | |||
629 | -(NSInteger) vertexZForPos:(CGPoint)pos | ||
630 | { | ||
631 | NSInteger ret = 0; | ||
632 | NSUInteger maxVal = 0; | ||
633 | if( useAutomaticVertexZ_ ) { | ||
634 | switch( layerOrientation_ ) { | ||
635 | case CCTMXOrientationIso: | ||
636 | maxVal = layerSize_.width + layerSize_.height; | ||
637 | ret = -(maxVal - (pos.x + pos.y)); | ||
638 | break; | ||
639 | case CCTMXOrientationOrtho: | ||
640 | ret = -(layerSize_.height-pos.y); | ||
641 | break; | ||
642 | case CCTMXOrientationHex: | ||
643 | NSAssert(NO,@"TMX Hexa zOrder not supported"); | ||
644 | break; | ||
645 | default: | ||
646 | NSAssert(NO,@"TMX invalid value"); | ||
647 | break; | ||
648 | } | ||
649 | } else | ||
650 | ret = vertexZvalue_; | ||
651 | |||
652 | return ret; | ||
653 | } | ||
654 | |||
655 | #pragma mark CCTMXLayer - draw | ||
656 | |||
657 | -(void) draw | ||
658 | { | ||
659 | if( useAutomaticVertexZ_ ) { | ||
660 | glEnable(GL_ALPHA_TEST); | ||
661 | glAlphaFunc(GL_GREATER, alphaFuncValue_); | ||
662 | } | ||
663 | |||
664 | [super draw]; | ||
665 | |||
666 | if( useAutomaticVertexZ_ ) | ||
667 | glDisable(GL_ALPHA_TEST); | ||
668 | } | ||
669 | @end | ||
670 | |||