diff options
| author | Starla Insigna <starla4444@gmail.com> | 2011-07-30 11:19:14 -0400 |
|---|---|---|
| committer | Starla Insigna <starla4444@gmail.com> | 2011-07-30 11:19:14 -0400 |
| commit | 9cd57b731ab1c666d4a1cb725538fdc137763d12 (patch) | |
| tree | 5bac45ae5157a1cb10c6e45500cbf72789917980 /libs/cocos2d/CCTMXLayer.m | |
| download | cartcollect-9cd57b731ab1c666d4a1cb725538fdc137763d12.tar.gz cartcollect-9cd57b731ab1c666d4a1cb725538fdc137763d12.tar.bz2 cartcollect-9cd57b731ab1c666d4a1cb725538fdc137763d12.zip | |
Initial commit (version 0.2.1)
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 | |||
