--- /dev/null
+/*
+ Copyright (c) 2010 Steve Oldmeadow
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+
+ $Id$
+ */
+
+#import "CocosDenshion.h"
+#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 30000
+ #import <AVFoundation/AVFoundation.h>
+#else
+ #import "CDXMacOSXSupport.h"
+#endif
+
+/** Different modes of the engine */
+typedef enum {
+ kAMM_FxOnly, //!Other apps will be able to play audio
+ kAMM_FxPlusMusic, //!Only this app will play audio
+ kAMM_FxPlusMusicIfNoOtherAudio, //!If another app is playing audio at start up then allow it to continue and don't play music
+ kAMM_MediaPlayback, //!This app takes over audio e.g music player app
+ kAMM_PlayAndRecord //!App takes over audio and has input and output
+} tAudioManagerMode;
+
+/** Possible states of the engine */
+typedef enum {
+ kAMStateUninitialised, //!Audio manager has not been initialised - do not use
+ kAMStateInitialising, //!Audio manager is in the process of initialising - do not use
+ kAMStateInitialised //!Audio manager is initialised - safe to use
+} tAudioManagerState;
+
+typedef enum {
+ kAMRBDoNothing, //Audio manager will not do anything on resign or becoming active
+ kAMRBStopPlay, //Background music is stopped on resign and resumed on become active
+ kAMRBStop //Background music is stopped on resign but not resumed - maybe because you want to do this from within your game
+} tAudioManagerResignBehavior;
+
+/** Notifications */
+extern NSString * const kCDN_AudioManagerInitialised;
+
+@interface CDAsynchInitialiser : NSOperation {}
+@end
+
+/** CDAudioManager supports two long audio source channels called left and right*/
+typedef enum {
+ kASC_Left = 0,
+ kASC_Right = 1
+} tAudioSourceChannel;
+
+typedef enum {
+ kLAS_Init,
+ kLAS_Loaded,
+ kLAS_Playing,
+ kLAS_Paused,
+ kLAS_Stopped,
+} tLongAudioSourceState;
+
+@class CDLongAudioSource;
+@protocol CDLongAudioSourceDelegate <NSObject>
+@optional
+/** The audio source completed playing */
+- (void) cdAudioSourceDidFinishPlaying:(CDLongAudioSource *) audioSource;
+/** The file used to load the audio source has changed */
+- (void) cdAudioSourceFileDidChange:(CDLongAudioSource *) audioSource;
+@end
+
+/**
+ CDLongAudioSource represents an audio source that has a long duration which makes
+ it costly to load into memory for playback as an effect using CDSoundEngine. Examples
+ include background music and narration tracks. The audio file may or may not be compressed.
+ Bear in mind that current iDevices can only use hardware to decode a single compressed
+ audio file at a time and playing multiple compressed files will result in a performance drop
+ as software decompression will take place.
+ @since v0.99
+ */
+@interface CDLongAudioSource : NSObject <AVAudioPlayerDelegate, CDAudioInterruptProtocol>{
+ AVAudioPlayer *audioSourcePlayer;
+ NSString *audioSourceFilePath;
+ NSInteger numberOfLoops;
+ float volume;
+ id<CDLongAudioSourceDelegate> delegate;
+ BOOL mute;
+ BOOL enabled_;
+ BOOL backgroundMusic;
+@public
+ BOOL systemPaused;//Used for auto resign handling
+ NSTimeInterval systemPauseLocation;//Used for auto resign handling
+@protected
+ tLongAudioSourceState state;
+}
+@property (readonly) AVAudioPlayer *audioSourcePlayer;
+@property (readonly) NSString *audioSourceFilePath;
+@property (readwrite, nonatomic) NSInteger numberOfLoops;
+@property (readwrite, nonatomic) float volume;
+@property (assign) id<CDLongAudioSourceDelegate> delegate;
+/* This long audio source functions as background music */
+@property (readwrite, nonatomic) BOOL backgroundMusic;
+
+/** Loads the file into the audio source */
+-(void) load:(NSString*) filePath;
+/** Plays the audio source */
+-(void) play;
+/** Stops playing the audio soruce */
+-(void) stop;
+/** Pauses the audio source */
+-(void) pause;
+/** Rewinds the audio source */
+-(void) rewind;
+/** Resumes playing the audio source if it was paused */
+-(void) resume;
+/** Returns whether or not the audio source is playing */
+-(BOOL) isPlaying;
+
+@end
+
+/**
+ CDAudioManager manages audio requirements for a game. It provides access to a CDSoundEngine object
+ for playing sound effects. It provides access to two CDLongAudioSource object (left and right channel)
+ for playing long duration audio such as background music and narration tracks. Additionally it manages
+ the audio session to take care of things like audio session interruption and interacting with the audio
+ of other apps that are running on the device.
+
+ Requirements:
+ - Firmware: OS 2.2 or greater
+ - Files: CDAudioManager.*, CocosDenshion.*
+ - Frameworks: OpenAL, AudioToolbox, AVFoundation
+ @since v0.8
+ */
+@interface CDAudioManager : NSObject <CDLongAudioSourceDelegate, CDAudioInterruptProtocol, AVAudioSessionDelegate> {
+ CDSoundEngine *soundEngine;
+ CDLongAudioSource *backgroundMusic;
+ NSMutableArray *audioSourceChannels;
+ NSString* _audioSessionCategory;
+ BOOL _audioWasPlayingAtStartup;
+ tAudioManagerMode _mode;
+ SEL backgroundMusicCompletionSelector;
+ id backgroundMusicCompletionListener;
+ BOOL willPlayBackgroundMusic;
+ BOOL _mute;
+ BOOL _resigned;
+ BOOL _interrupted;
+ BOOL _audioSessionActive;
+ BOOL enabled_;
+
+ //For handling resign/become active
+ BOOL _isObservingAppEvents;
+ tAudioManagerResignBehavior _resignBehavior;
+}
+
+@property (readonly) CDSoundEngine *soundEngine;
+@property (readonly) CDLongAudioSource *backgroundMusic;
+@property (readonly) BOOL willPlayBackgroundMusic;
+
+/** Returns the shared singleton */
++ (CDAudioManager *) sharedManager;
++ (tAudioManagerState) sharedManagerState;
+/** Configures the shared singleton with a mode*/
++ (void) configure: (tAudioManagerMode) mode;
+/** Initializes the engine asynchronously with a mode */
++ (void) initAsynchronously: (tAudioManagerMode) mode;
+/** Initializes the engine synchronously with a mode, channel definition and a total number of channels */
+- (id) init: (tAudioManagerMode) mode;
+-(void) audioSessionInterrupted;
+-(void) audioSessionResumed;
+-(void) setResignBehavior:(tAudioManagerResignBehavior) resignBehavior autoHandle:(BOOL) autoHandle;
+/** Returns true is audio is muted at a hardware level e.g user has ringer switch set to off */
+-(BOOL) isDeviceMuted;
+/** Returns true if another app is playing audio such as the iPod music player */
+-(BOOL) isOtherAudioPlaying;
+/** Sets the way the audio manager interacts with the operating system such as whether it shares output with other apps or obeys the mute switch */
+-(void) setMode:(tAudioManagerMode) mode;
+/** Shuts down the shared audio manager instance so that it can be reinitialised */
++(void) end;
+
+/** Call if you want to use built in resign behavior but need to do some additional audio processing on resign active. */
+- (void) applicationWillResignActive;
+/** Call if you want to use built in resign behavior but need to do some additional audio processing on become active. */
+- (void) applicationDidBecomeActive;
+
+//New AVAudioPlayer API
+/** Loads the data from the specified file path to the channel's audio source */
+-(CDLongAudioSource*) audioSourceLoad:(NSString*) filePath channel:(tAudioSourceChannel) channel;
+/** Retrieves the audio source for the specified channel */
+-(CDLongAudioSource*) audioSourceForChannel:(tAudioSourceChannel) channel;
+
+//Legacy AVAudioPlayer API
+/** Plays music in background. The music can be looped or not
+ It is recommended to use .aac files as background music since they are decoded by the device (hardware).
+ */
+-(void) playBackgroundMusic:(NSString*) filePath loop:(BOOL) loop;
+/** Preloads a background music */
+-(void) preloadBackgroundMusic:(NSString*) filePath;
+/** Stops playing the background music */
+-(void) stopBackgroundMusic;
+/** Pauses the background music */
+-(void) pauseBackgroundMusic;
+/** Rewinds the background music */
+-(void) rewindBackgroundMusic;
+/** Resumes playing the background music */
+-(void) resumeBackgroundMusic;
+/** Returns whether or not the background music is playing */
+-(BOOL) isBackgroundMusicPlaying;
+
+-(void) setBackgroundMusicCompletionListener:(id) listener selector:(SEL) selector;
+
+@end
+
+/** Fader for long audio source objects */
+@interface CDLongAudioSourceFader : CDPropertyModifier{}
+@end
+
+static const int kCDNoBuffer = -1;
+
+/** Allows buffers to be associated with file names */
+@interface CDBufferManager:NSObject{
+ NSMutableDictionary* loadedBuffers;
+ NSMutableArray *freedBuffers;
+ CDSoundEngine *soundEngine;
+ int nextBufferId;
+}
+
+-(id) initWithEngine:(CDSoundEngine *) theSoundEngine;
+-(int) bufferForFile:(NSString*) filePath create:(BOOL) create;
+-(void) releaseBufferForFile:(NSString *) filePath;
+
+@end
+
--- /dev/null
+/*
+ Copyright (c) 2010 Steve Oldmeadow
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+
+ $Id$
+ */
+
+
+#import "CDAudioManager.h"
+
+NSString * const kCDN_AudioManagerInitialised = @"kCDN_AudioManagerInitialised";
+
+//NSOperation object used to asynchronously initialise
+@implementation CDAsynchInitialiser
+
+-(void) main {
+ [super main];
+ [CDAudioManager sharedManager];
+}
+
+@end
+
+@implementation CDLongAudioSource
+
+@synthesize audioSourcePlayer, audioSourceFilePath, delegate, backgroundMusic;
+
+-(id) init {
+ if ((self = [super init])) {
+ state = kLAS_Init;
+ volume = 1.0f;
+ mute = NO;
+ enabled_ = YES;
+ }
+ return self;
+}
+
+-(void) dealloc {
+ CDLOGINFO(@"Denshion::CDLongAudioSource - deallocating %@", self);
+ [audioSourcePlayer release];
+ [audioSourceFilePath release];
+ [super dealloc];
+}
+
+-(void) load:(NSString*) filePath {
+ //We have alread loaded a file previously, check if we are being asked to load the same file
+ if (state == kLAS_Init || ![filePath isEqualToString:audioSourceFilePath]) {
+ CDLOGINFO(@"Denshion::CDLongAudioSource - Loading new audio source %@",filePath);
+ //New file
+ if (state != kLAS_Init) {
+ [audioSourceFilePath release];//Release old file path
+ [audioSourcePlayer release];//Release old AVAudioPlayer, they can't be reused
+ }
+ audioSourceFilePath = [filePath copy];
+ NSError *error = nil;
+ NSString *path = [CDUtilities fullPathFromRelativePath:audioSourceFilePath];
+ audioSourcePlayer = [(AVAudioPlayer*)[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&error];
+ if (error == nil) {
+ [audioSourcePlayer prepareToPlay];
+ audioSourcePlayer.delegate = self;
+ if (delegate && [delegate respondsToSelector:@selector(cdAudioSourceFileDidChange:)]) {
+ //Tell our delegate the file has changed
+ [delegate cdAudioSourceFileDidChange:self];
+ }
+ } else {
+ CDLOG(@"Denshion::CDLongAudioSource - Error initialising audio player: %@",error);
+ }
+ } else {
+ //Same file - just return it to a consistent state
+ [self pause];
+ [self rewind];
+ }
+ audioSourcePlayer.volume = volume;
+ audioSourcePlayer.numberOfLoops = numberOfLoops;
+ state = kLAS_Loaded;
+}
+
+-(void) play {
+ if (enabled_) {
+ self->systemPaused = NO;
+ [audioSourcePlayer play];
+ } else {
+ CDLOGINFO(@"Denshion::CDLongAudioSource long audio source didn't play because it is disabled");
+ }
+}
+
+-(void) stop {
+ [audioSourcePlayer stop];
+}
+
+-(void) pause {
+ [audioSourcePlayer pause];
+}
+
+-(void) rewind {
+ [audioSourcePlayer setCurrentTime:0];
+}
+
+-(void) resume {
+ [audioSourcePlayer play];
+}
+
+-(BOOL) isPlaying {
+ if (state != kLAS_Init) {
+ return [audioSourcePlayer isPlaying];
+ } else {
+ return NO;
+ }
+}
+
+-(void) setVolume:(float) newVolume
+{
+ volume = newVolume;
+ if (state != kLAS_Init && !mute) {
+ audioSourcePlayer.volume = newVolume;
+ }
+}
+
+-(float) volume
+{
+ return volume;
+}
+
+#pragma mark Audio Interrupt Protocol
+-(BOOL) mute
+{
+ return mute;
+}
+
+-(void) setMute:(BOOL) muteValue
+{
+ if (mute != muteValue) {
+ if (mute) {
+ //Turn sound back on
+ audioSourcePlayer.volume = volume;
+ } else {
+ audioSourcePlayer.volume = 0.0f;
+ }
+ mute = muteValue;
+ }
+}
+
+-(BOOL) enabled
+{
+ return enabled_;
+}
+
+-(void) setEnabled:(BOOL)enabledValue
+{
+ if (enabledValue != enabled_) {
+ enabled_ = enabledValue;
+ if (!enabled_) {
+ //"Stop" the sounds
+ [self pause];
+ [self rewind];
+ }
+ }
+}
+
+-(NSInteger) numberOfLoops {
+ return numberOfLoops;
+}
+
+-(void) setNumberOfLoops:(NSInteger) loopCount
+{
+ audioSourcePlayer.numberOfLoops = loopCount;
+ numberOfLoops = loopCount;
+}
+
+- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
+ CDLOGINFO(@"Denshion::CDLongAudioSource - audio player finished");
+#if TARGET_IPHONE_SIMULATOR
+ CDLOGINFO(@"Denshion::CDLongAudioSource - workaround for OpenAL clobbered audio issue");
+ //This is a workaround for an issue in all simulators (tested to 3.1.2). Problem is
+ //that OpenAL audio playback is clobbered when an AVAudioPlayer stops. Workaround
+ //is to keep the player playing on an endless loop with 0 volume and then when
+ //it is played again reset the volume and set loop count appropriately.
+ //NB: this workaround is not foolproof but it is good enough for most situations.
+ player.numberOfLoops = -1;
+ player.volume = 0;
+ [player play];
+#endif
+ if (delegate && [delegate respondsToSelector:@selector(cdAudioSourceDidFinishPlaying:)]) {
+ [delegate cdAudioSourceDidFinishPlaying:self];
+ }
+}
+
+-(void)audioPlayerBeginInterruption:(AVAudioPlayer *)player {
+ CDLOGINFO(@"Denshion::CDLongAudioSource - audio player interrupted");
+}
+
+-(void)audioPlayerEndInterruption:(AVAudioPlayer *)player {
+ CDLOGINFO(@"Denshion::CDLongAudioSource - audio player resumed");
+ if (self.backgroundMusic) {
+ //Check if background music can play as rules may have changed during
+ //the interruption. This is to address a specific issue in 4.x when
+ //fast task switching
+ if([CDAudioManager sharedManager].willPlayBackgroundMusic) {
+ [player play];
+ }
+ } else {
+ [player play];
+ }
+}
+
+@end
+
+
+@interface CDAudioManager (PrivateMethods)
+-(BOOL) audioSessionSetActive:(BOOL) active;
+-(BOOL) audioSessionSetCategory:(NSString*) category;
+-(void) badAlContextHandler;
+@end
+
+
+@implementation CDAudioManager
+#define BACKGROUND_MUSIC_CHANNEL kASC_Left
+
+@synthesize soundEngine, willPlayBackgroundMusic;
+static CDAudioManager *sharedManager;
+static tAudioManagerState _sharedManagerState = kAMStateUninitialised;
+static tAudioManagerMode configuredMode;
+static BOOL configured = FALSE;
+
+-(BOOL) audioSessionSetActive:(BOOL) active {
+ NSError *activationError = nil;
+ if ([[AVAudioSession sharedInstance] setActive:active error:&activationError]) {
+ _audioSessionActive = active;
+ CDLOGINFO(@"Denshion::CDAudioManager - Audio session set active %i succeeded", active);
+ return YES;
+ } else {
+ //Failed
+ CDLOG(@"Denshion::CDAudioManager - Audio session set active %i failed with error %@", active, activationError);
+ return NO;
+ }
+}
+
+-(BOOL) audioSessionSetCategory:(NSString*) category {
+ NSError *categoryError = nil;
+ if ([[AVAudioSession sharedInstance] setCategory:category error:&categoryError]) {
+ CDLOGINFO(@"Denshion::CDAudioManager - Audio session set category %@ succeeded", category);
+ return YES;
+ } else {
+ //Failed
+ CDLOG(@"Denshion::CDAudioManager - Audio session set category %@ failed with error %@", category, categoryError);
+ return NO;
+ }
+}
+
+// Init
++ (CDAudioManager *) sharedManager
+{
+ @synchronized(self) {
+ if (!sharedManager) {
+ if (!configured) {
+ //Set defaults here
+ configuredMode = kAMM_FxPlusMusicIfNoOtherAudio;
+ }
+ sharedManager = [[CDAudioManager alloc] init:configuredMode];
+ _sharedManagerState = kAMStateInitialised;//This is only really relevant when using asynchronous initialisation
+ [[NSNotificationCenter defaultCenter] postNotificationName:kCDN_AudioManagerInitialised object:nil];
+ }
+ }
+ return sharedManager;
+}
+
++ (tAudioManagerState) sharedManagerState {
+ return _sharedManagerState;
+}
+
+/**
+ * Call this to set up audio manager asynchronously. Initialisation is finished when sharedManagerState == kAMStateInitialised
+ */
++ (void) initAsynchronously: (tAudioManagerMode) mode {
+ @synchronized(self) {
+ if (_sharedManagerState == kAMStateUninitialised) {
+ _sharedManagerState = kAMStateInitialising;
+ [CDAudioManager configure:mode];
+ CDAsynchInitialiser *initOp = [[[CDAsynchInitialiser alloc] init] autorelease];
+ NSOperationQueue *opQ = [[[NSOperationQueue alloc] init] autorelease];
+ [opQ addOperation:initOp];
+ }
+ }
+}
+
++ (id) alloc
+{
+ @synchronized(self) {
+ NSAssert(sharedManager == nil, @"Attempted to allocate a second instance of a singleton.");
+ return [super alloc];
+ }
+ return nil;
+}
+
+/*
+ * Call this method before accessing the shared manager in order to configure the shared audio manager
+ */
++ (void) configure: (tAudioManagerMode) mode {
+ configuredMode = mode;
+ configured = TRUE;
+}
+
+-(BOOL) isOtherAudioPlaying {
+ UInt32 isPlaying = 0;
+ UInt32 varSize = sizeof(isPlaying);
+ AudioSessionGetProperty (kAudioSessionProperty_OtherAudioIsPlaying, &varSize, &isPlaying);
+ return (isPlaying != 0);
+}
+
+-(void) setMode:(tAudioManagerMode) mode {
+
+ _mode = mode;
+ switch (_mode) {
+
+ case kAMM_FxOnly:
+ //Share audio with other app
+ CDLOGINFO(@"Denshion::CDAudioManager - Audio will be shared");
+ //_audioSessionCategory = kAudioSessionCategory_AmbientSound;
+ _audioSessionCategory = AVAudioSessionCategoryAmbient;
+ willPlayBackgroundMusic = NO;
+ break;
+
+ case kAMM_FxPlusMusic:
+ //Use audio exclusively - if other audio is playing it will be stopped
+ CDLOGINFO(@"Denshion::CDAudioManager - Audio will be exclusive");
+ //_audioSessionCategory = kAudioSessionCategory_SoloAmbientSound;
+ _audioSessionCategory = AVAudioSessionCategorySoloAmbient;
+ willPlayBackgroundMusic = YES;
+ break;
+
+ case kAMM_MediaPlayback:
+ //Use audio exclusively, ignore mute switch and sleep
+ CDLOGINFO(@"Denshion::CDAudioManager - Media playback mode, audio will be exclusive");
+ //_audioSessionCategory = kAudioSessionCategory_MediaPlayback;
+ _audioSessionCategory = AVAudioSessionCategoryPlayback;
+ willPlayBackgroundMusic = YES;
+ break;
+
+ case kAMM_PlayAndRecord:
+ //Use audio exclusively, ignore mute switch and sleep, has inputs and outputs
+ CDLOGINFO(@"Denshion::CDAudioManager - Play and record mode, audio will be exclusive");
+ //_audioSessionCategory = kAudioSessionCategory_PlayAndRecord;
+ _audioSessionCategory = AVAudioSessionCategoryPlayAndRecord;
+ willPlayBackgroundMusic = YES;
+ break;
+
+ default:
+ //kAudioManagerFxPlusMusicIfNoOtherAudio
+ if ([self isOtherAudioPlaying]) {
+ CDLOGINFO(@"Denshion::CDAudioManager - Other audio is playing audio will be shared");
+ //_audioSessionCategory = kAudioSessionCategory_AmbientSound;
+ _audioSessionCategory = AVAudioSessionCategoryAmbient;
+ willPlayBackgroundMusic = NO;
+ } else {
+ CDLOGINFO(@"Denshion::CDAudioManager - Other audio is not playing audio will be exclusive");
+ //_audioSessionCategory = kAudioSessionCategory_SoloAmbientSound;
+ _audioSessionCategory = AVAudioSessionCategorySoloAmbient;
+ willPlayBackgroundMusic = YES;
+ }
+
+ break;
+ }
+
+ [self audioSessionSetCategory:_audioSessionCategory];
+
+}
+
+/**
+ * This method is used to work around various bugs introduced in 4.x OS versions. In some circumstances the
+ * audio session is interrupted but never resumed, this results in the loss of OpenAL audio when following
+ * standard practices. If we detect this situation then we will attempt to resume the audio session ourselves.
+ * Known triggers: lock the device then unlock it (iOS 4.2 gm), playback a song using MPMediaPlayer (iOS 4.0)
+ */
+- (void) badAlContextHandler {
+ if (_interrupted && alcGetCurrentContext() == NULL) {
+ CDLOG(@"Denshion::CDAudioManager - bad OpenAL context detected, attempting to resume audio session");
+ [self audioSessionResumed];
+ }
+}
+
+- (id) init: (tAudioManagerMode) mode {
+ if ((self = [super init])) {
+
+ //Initialise the audio session
+ AVAudioSession* session = [AVAudioSession sharedInstance];
+ session.delegate = self;
+
+ _mode = mode;
+ backgroundMusicCompletionSelector = nil;
+ _isObservingAppEvents = FALSE;
+ _mute = NO;
+ _resigned = NO;
+ _interrupted = NO;
+ enabled_ = YES;
+ _audioSessionActive = NO;
+ [self setMode:mode];
+ soundEngine = [[CDSoundEngine alloc] init];
+
+ //Set up audioSource channels
+ audioSourceChannels = [[NSMutableArray alloc] init];
+ CDLongAudioSource *leftChannel = [[CDLongAudioSource alloc] init];
+ leftChannel.backgroundMusic = YES;
+ CDLongAudioSource *rightChannel = [[CDLongAudioSource alloc] init];
+ rightChannel.backgroundMusic = NO;
+ [audioSourceChannels insertObject:leftChannel atIndex:kASC_Left];
+ [audioSourceChannels insertObject:rightChannel atIndex:kASC_Right];
+ [leftChannel release];
+ [rightChannel release];
+ //Used to support legacy APIs
+ backgroundMusic = [self audioSourceForChannel:BACKGROUND_MUSIC_CHANNEL];
+ backgroundMusic.delegate = self;
+
+ //Add handler for bad al context messages, these are posted by the sound engine.
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(badAlContextHandler) name:kCDN_BadAlContext object:nil];
+
+ }
+ return self;
+}
+
+-(void) dealloc {
+ CDLOGINFO(@"Denshion::CDAudioManager - deallocating");
+ [self stopBackgroundMusic];
+ [soundEngine release];
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ [self audioSessionSetActive:NO];
+ [audioSourceChannels release];
+ [super dealloc];
+}
+
+/** Retrieves the audio source for the specified channel */
+-(CDLongAudioSource*) audioSourceForChannel:(tAudioSourceChannel) channel
+{
+ return (CDLongAudioSource*)[audioSourceChannels objectAtIndex:channel];
+}
+
+/** Loads the data from the specified file path to the channel's audio source */
+-(CDLongAudioSource*) audioSourceLoad:(NSString*) filePath channel:(tAudioSourceChannel) channel
+{
+ CDLongAudioSource *audioSource = [self audioSourceForChannel:channel];
+ if (audioSource) {
+ [audioSource load:filePath];
+ }
+ return audioSource;
+}
+
+-(BOOL) isBackgroundMusicPlaying {
+ return [self.backgroundMusic isPlaying];
+}
+
+//NB: originally I tried using a route change listener and intended to store the current route,
+//however, on a 3gs running 3.1.2 no route change is generated when the user switches the
+//ringer mute switch to off (i.e. enables sound) therefore polling is the only reliable way to
+//determine ringer switch state
+-(BOOL) isDeviceMuted {
+
+#if TARGET_IPHONE_SIMULATOR
+ //Calling audio route stuff on the simulator causes problems
+ return NO;
+#else
+ CFStringRef newAudioRoute;
+ UInt32 propertySize = sizeof (CFStringRef);
+
+ AudioSessionGetProperty (
+ kAudioSessionProperty_AudioRoute,
+ &propertySize,
+ &newAudioRoute
+ );
+
+ if (newAudioRoute == NULL) {
+ //Don't expect this to happen but playing safe otherwise a null in the CFStringCompare will cause a crash
+ return YES;
+ } else {
+ CFComparisonResult newDeviceIsMuted = CFStringCompare (
+ newAudioRoute,
+ (CFStringRef) @"",
+ 0
+ );
+
+ return (newDeviceIsMuted == kCFCompareEqualTo);
+ }
+#endif
+}
+
+#pragma mark Audio Interrupt Protocol
+
+-(BOOL) mute {
+ return _mute;
+}
+
+-(void) setMute:(BOOL) muteValue {
+ if (muteValue != _mute) {
+ _mute = muteValue;
+ [soundEngine setMute:muteValue];
+ for( CDLongAudioSource *audioSource in audioSourceChannels) {
+ audioSource.mute = muteValue;
+ }
+ }
+}
+
+-(BOOL) enabled {
+ return enabled_;
+}
+
+-(void) setEnabled:(BOOL) enabledValue {
+ if (enabledValue != enabled_) {
+ enabled_ = enabledValue;
+ [soundEngine setEnabled:enabled_];
+ for( CDLongAudioSource *audioSource in audioSourceChannels) {
+ audioSource.enabled = enabled_;
+ }
+ }
+}
+
+-(CDLongAudioSource*) backgroundMusic
+{
+ return backgroundMusic;
+}
+
+//Load background music ready for playing
+-(void) preloadBackgroundMusic:(NSString*) filePath
+{
+ [self.backgroundMusic load:filePath];
+}
+
+-(void) playBackgroundMusic:(NSString*) filePath loop:(BOOL) loop
+{
+ [self.backgroundMusic load:filePath];
+
+ if (!willPlayBackgroundMusic || _mute) {
+ CDLOGINFO(@"Denshion::CDAudioManager - play bgm aborted because audio is not exclusive or sound is muted");
+ return;
+ }
+
+ if (loop) {
+ [self.backgroundMusic setNumberOfLoops:-1];
+ } else {
+ [self.backgroundMusic setNumberOfLoops:0];
+ }
+ [self.backgroundMusic play];
+}
+
+-(void) stopBackgroundMusic
+{
+ [self.backgroundMusic stop];
+}
+
+-(void) pauseBackgroundMusic
+{
+ [self.backgroundMusic pause];
+}
+
+-(void) resumeBackgroundMusic
+{
+ if (!willPlayBackgroundMusic || _mute) {
+ CDLOGINFO(@"Denshion::CDAudioManager - resume bgm aborted because audio is not exclusive or sound is muted");
+ return;
+ }
+
+ [self.backgroundMusic resume];
+}
+
+-(void) rewindBackgroundMusic
+{
+ [self.backgroundMusic rewind];
+}
+
+-(void) setBackgroundMusicCompletionListener:(id) listener selector:(SEL) selector {
+ backgroundMusicCompletionListener = listener;
+ backgroundMusicCompletionSelector = selector;
+}
+
+/*
+ * Call this method to have the audio manager automatically handle application resign and
+ * become active. Pass a tAudioManagerResignBehavior to indicate the desired behavior
+ * for resigning and becoming active again.
+ *
+ * If autohandle is YES then the applicationWillResignActive and applicationDidBecomActive
+ * methods are automatically called, otherwise you must call them yourself at the appropriate time.
+ *
+ * Based on idea of Dominique Bongard
+ */
+-(void) setResignBehavior:(tAudioManagerResignBehavior) resignBehavior autoHandle:(BOOL) autoHandle {
+
+ if (!_isObservingAppEvents && autoHandle) {
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive:) name:@"UIApplicationWillResignActiveNotification" object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:) name:@"UIApplicationDidBecomeActiveNotification" object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillTerminate:) name:@"UIApplicationWillTerminateNotification" object:nil];
+ _isObservingAppEvents = TRUE;
+ }
+ _resignBehavior = resignBehavior;
+}
+
+- (void) applicationWillResignActive {
+ self->_resigned = YES;
+
+ //Set the audio sesssion to one that allows sharing so that other audio won't be clobbered on resume
+ [self audioSessionSetCategory:AVAudioSessionCategoryAmbient];
+
+ switch (_resignBehavior) {
+
+ case kAMRBStopPlay:
+
+ for( CDLongAudioSource *audioSource in audioSourceChannels) {
+ if (audioSource.isPlaying) {
+ audioSource->systemPaused = YES;
+ audioSource->systemPauseLocation = audioSource.audioSourcePlayer.currentTime;
+ [audioSource stop];
+ } else {
+ //Music is either paused or stopped, if it is paused it will be restarted
+ //by OS so we will stop it.
+ audioSource->systemPaused = NO;
+ [audioSource stop];
+ }
+ }
+ break;
+
+ case kAMRBStop:
+ //Stop music regardless of whether it is playing or not because if it was paused
+ //then the OS would resume it
+ for( CDLongAudioSource *audioSource in audioSourceChannels) {
+ [audioSource stop];
+ }
+
+ default:
+ break;
+
+ }
+ CDLOGINFO(@"Denshion::CDAudioManager - handled resign active");
+}
+
+//Called when application resigns active only if setResignBehavior has been called
+- (void) applicationWillResignActive:(NSNotification *) notification
+{
+ [self applicationWillResignActive];
+}
+
+- (void) applicationDidBecomeActive {
+
+ if (self->_resigned) {
+ _resigned = NO;
+ //Reset the mode incase something changed with audio while we were inactive
+ [self setMode:_mode];
+ switch (_resignBehavior) {
+
+ case kAMRBStopPlay:
+
+ //Music had been stopped but stop maintains current time
+ //so playing again will continue from where music was before resign active.
+ //We check if music can be played because while we were inactive the user might have
+ //done something that should force music to not play such as starting a track in the iPod
+ if (self.willPlayBackgroundMusic) {
+ for( CDLongAudioSource *audioSource in audioSourceChannels) {
+ if (audioSource->systemPaused) {
+ [audioSource resume];
+ audioSource->systemPaused = NO;
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+
+ }
+ CDLOGINFO(@"Denshion::CDAudioManager - audio manager handled become active");
+ }
+}
+
+//Called when application becomes active only if setResignBehavior has been called
+- (void) applicationDidBecomeActive:(NSNotification *) notification
+{
+ [self applicationDidBecomeActive];
+}
+
+//Called when application terminates only if setResignBehavior has been called
+- (void) applicationWillTerminate:(NSNotification *) notification
+{
+ CDLOGINFO(@"Denshion::CDAudioManager - audio manager handling terminate");
+ [self stopBackgroundMusic];
+}
+
+/** The audio source completed playing */
+- (void) cdAudioSourceDidFinishPlaying:(CDLongAudioSource *) audioSource {
+ CDLOGINFO(@"Denshion::CDAudioManager - audio manager got told background music finished");
+ if (backgroundMusicCompletionSelector != nil) {
+ [backgroundMusicCompletionListener performSelector:backgroundMusicCompletionSelector];
+ }
+}
+
+-(void) beginInterruption {
+ CDLOGINFO(@"Denshion::CDAudioManager - begin interruption");
+ [self audioSessionInterrupted];
+}
+
+-(void) endInterruption {
+ CDLOGINFO(@"Denshion::CDAudioManager - end interruption");
+ [self audioSessionResumed];
+}
+
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 40000
+-(void) endInterruptionWithFlags:(NSUInteger)flags {
+ CDLOGINFO(@"Denshion::CDAudioManager - interruption ended with flags %i",flags);
+ if (flags == AVAudioSessionInterruptionFlags_ShouldResume) {
+ [self audioSessionResumed];
+ }
+}
+#endif
+
+-(void)audioSessionInterrupted
+{
+ if (!_interrupted) {
+ CDLOGINFO(@"Denshion::CDAudioManager - Audio session interrupted");
+ _interrupted = YES;
+
+ // Deactivate the current audio session
+ [self audioSessionSetActive:NO];
+
+ if (alcGetCurrentContext() != NULL) {
+ CDLOGINFO(@"Denshion::CDAudioManager - Setting OpenAL context to NULL");
+
+ ALenum error = AL_NO_ERROR;
+
+ // set the current context to NULL will 'shutdown' openAL
+ alcMakeContextCurrent(NULL);
+
+ if((error = alGetError()) != AL_NO_ERROR) {
+ CDLOG(@"Denshion::CDAudioManager - Error making context current %x\n", error);
+ }
+ #pragma unused(error)
+ }
+ }
+}
+
+-(void)audioSessionResumed
+{
+ if (_interrupted) {
+ CDLOGINFO(@"Denshion::CDAudioManager - Audio session resumed");
+ _interrupted = NO;
+
+ BOOL activationResult = NO;
+ // Reactivate the current audio session
+ activationResult = [self audioSessionSetActive:YES];
+
+ //This code is to handle a problem with iOS 4.0 and 4.01 where reactivating the session can fail if
+ //task switching is performed too rapidly. A test case that reliably reproduces the issue is to call the
+ //iPhone and then hang up after two rings (timing may vary ;))
+ //Basically we keep waiting and trying to let the OS catch up with itself but the number of tries is
+ //limited.
+ if (!activationResult) {
+ CDLOG(@"Denshion::CDAudioManager - Failure reactivating audio session, will try wait-try cycle");
+ int activateCount = 0;
+ while (!activationResult && activateCount < 10) {
+ [NSThread sleepForTimeInterval:0.5];
+ activationResult = [self audioSessionSetActive:YES];
+ activateCount++;
+ CDLOGINFO(@"Denshion::CDAudioManager - Reactivation attempt %i status = %i",activateCount,activationResult);
+ }
+ }
+
+ if (alcGetCurrentContext() == NULL) {
+ CDLOGINFO(@"Denshion::CDAudioManager - Restoring OpenAL context");
+ ALenum error = AL_NO_ERROR;
+ // Restore open al context
+ alcMakeContextCurrent([soundEngine openALContext]);
+ if((error = alGetError()) != AL_NO_ERROR) {
+ CDLOG(@"Denshion::CDAudioManager - Error making context current%x\n", error);
+ }
+ #pragma unused(error)
+ }
+ }
+}
+
++(void) end {
+ [sharedManager release];
+ sharedManager = nil;
+}
+
+@end
+
+///////////////////////////////////////////////////////////////////////////////////////
+@implementation CDLongAudioSourceFader
+
+-(void) _setTargetProperty:(float) newVal {
+ ((CDLongAudioSource*)target).volume = newVal;
+}
+
+-(float) _getTargetProperty {
+ return ((CDLongAudioSource*)target).volume;
+}
+
+-(void) _stopTarget {
+ //Pause instead of stop as stop releases resources and causes problems in the simulator
+ [((CDLongAudioSource*)target) pause];
+}
+
+-(Class) _allowableType {
+ return [CDLongAudioSource class];
+}
+
+@end
+///////////////////////////////////////////////////////////////////////////////////////
+@implementation CDBufferManager
+
+-(id) initWithEngine:(CDSoundEngine *) theSoundEngine {
+ if ((self = [super init])) {
+ soundEngine = theSoundEngine;
+ loadedBuffers = [[NSMutableDictionary alloc] initWithCapacity:CD_BUFFERS_START];
+ freedBuffers = [[NSMutableArray alloc] init];
+ nextBufferId = 0;
+ }
+ return self;
+}
+
+-(void) dealloc {
+ [loadedBuffers release];
+ [freedBuffers release];
+ [super dealloc];
+}
+
+-(int) bufferForFile:(NSString*) filePath create:(BOOL) create {
+
+ NSNumber* soundId = (NSNumber*)[loadedBuffers objectForKey:filePath];
+ if(soundId == nil)
+ {
+ if (create) {
+ NSNumber* bufferId = nil;
+ //First try to get a buffer from the free buffers
+ if ([freedBuffers count] > 0) {
+ bufferId = [[[freedBuffers lastObject] retain] autorelease];
+ [freedBuffers removeLastObject];
+ CDLOGINFO(@"Denshion::CDBufferManager reusing buffer id %i",[bufferId intValue]);
+ } else {
+ bufferId = [[NSNumber alloc] initWithInt:nextBufferId];
+ [bufferId autorelease];
+ CDLOGINFO(@"Denshion::CDBufferManager generating new buffer id %i",[bufferId intValue]);
+ nextBufferId++;
+ }
+
+ if ([soundEngine loadBuffer:[bufferId intValue] filePath:filePath]) {
+ //File successfully loaded
+ CDLOGINFO(@"Denshion::CDBufferManager buffer loaded %@ %@",bufferId,filePath);
+ [loadedBuffers setObject:bufferId forKey:filePath];
+ return [bufferId intValue];
+ } else {
+ //File didn't load, put buffer id on free list
+ [freedBuffers addObject:bufferId];
+ return kCDNoBuffer;
+ }
+ } else {
+ //No matching buffer was found
+ return kCDNoBuffer;
+ }
+ } else {
+ return [soundId intValue];
+ }
+}
+
+-(void) releaseBufferForFile:(NSString *) filePath {
+ int bufferId = [self bufferForFile:filePath create:NO];
+ if (bufferId != kCDNoBuffer) {
+ [soundEngine unloadBuffer:bufferId];
+ [loadedBuffers removeObjectForKey:filePath];
+ NSNumber *freedBufferId = [[NSNumber alloc] initWithInt:bufferId];
+ [freedBufferId autorelease];
+ [freedBuffers addObject:freedBufferId];
+ }
+}
+@end
+
+
+
--- /dev/null
+/*
+ Copyright (c) 2010 Steve Oldmeadow
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+
+ $Id$
+ */
+#define COCOSDENSHION_VERSION "Aphex.rc"
+
+
+/**
+ If enabled code useful for debugging such as parameter check assertions will be performed.
+ If you experience any problems you should enable this and test your code with a debug build.
+ */
+//#define CD_DEBUG 1
+
+/**
+ The total number of sounds/buffers that can be loaded assuming memory is sufficient
+ */
+//Number of buffers slots that will be initially created
+#define CD_BUFFERS_START 64
+//Number of buffers that will be added
+#define CD_BUFFERS_INCREMENT 16
+
+/**
+ If enabled, OpenAL code will use static buffers. When static buffers are used the audio
+ data is managed outside of OpenAL, this eliminates a memcpy operation which leads to
+ higher performance when loading sounds.
+
+ However, the downside is that when the audio data is freed you must
+ be certain that it is no longer being accessed otherwise your app will crash. Testing on OS 2.2.1
+ and 3.1.2 has shown that this may occur if a buffer is being used by a source with state = AL_PLAYING
+ when the buffer is deleted. If the data is freed too quickly after the source is stopped then
+ a crash will occur. The implemented workaround is that when static buffers are used the unloadBuffer code will wait for
+ any playing sources to finish playing before the associated buffer and data are deleted, however, this delay may negate any
+ performance gains that are achieved during loading.
+
+ Performance tests on a 1st gen iPod running OS 2.2.1 loading the CocosDenshionDemo sounds were ~0.14 seconds without
+ static buffers and ~0.12 seconds when using static buffers.
+
+ */
+//#define CD_USE_STATIC_BUFFERS 1
+
+
--- /dev/null
+/*
+
+ Disclaimer: IMPORTANT: This Apple software is supplied to you by
+ Apple Inc. ("Apple") in consideration of your agreement to the
+ following terms, and your use, installation, modification or
+ redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use,
+ install, modify or redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and
+ subject to these terms, Apple grants you a personal, non-exclusive
+ license, under Apple's copyrights in this original Apple software (the
+ "Apple Software"), to use, reproduce, modify and redistribute the Apple
+ Software, with or without modifications, in source and/or binary forms;
+ provided that if you redistribute the Apple Software in its entirety and
+ without modifications, you must retain this notice and the following
+ text and disclaimers in all such redistributions of the Apple Software.
+ Neither the name, trademarks, service marks or logos of Apple Inc.
+ may be used to endorse or promote products derived from the Apple
+ Software without specific prior written permission from Apple. Except
+ as expressly stated in this notice, no other rights or licenses, express
+ or implied, are granted by Apple herein, including but not limited to
+ any patent rights that may be infringed by your derivative works or by
+ other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE
+ MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
+ THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
+ FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
+ OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
+ MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
+ AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
+ STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+ Copyright (C) 2009 Apple Inc. All Rights Reserved.
+
+ $Id$
+ */
+
+/*
+ This file contains code from version 1.1 and 1.4 of MyOpenALSupport.h taken from Apple's oalTouch version.
+ The 1.4 version code is used for loading IMA4 files, however, this code causes very noticeable clicking
+ when used to load wave files that are looped so the 1.1 version code is used specifically for loading
+ wav files.
+ */
+
+#ifndef __CD_OPENAL_H
+#define __CD_OPENAL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#import <OpenAL/al.h>
+#import <OpenAL/alc.h>
+#import <CoreFoundation/CFURL.h>
+
+
+//Taken from oalTouch MyOpenALSupport 1.1
+void* CDloadWaveAudioData(CFURLRef inFileURL, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei* outSampleRate);
+void* CDloadCafAudioData(CFURLRef inFileURL, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei* outSampleRate);
+void* CDGetOpenALAudioData(CFURLRef inFileURL, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei* outSampleRate);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+
--- /dev/null
+/*
+
+ Disclaimer: IMPORTANT: This Apple software is supplied to you by
+ Apple Inc. ("Apple") in consideration of your agreement to the
+ following terms, and your use, installation, modification or
+ redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use,
+ install, modify or redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and
+ subject to these terms, Apple grants you a personal, non-exclusive
+ license, under Apple's copyrights in this original Apple software (the
+ "Apple Software"), to use, reproduce, modify and redistribute the Apple
+ Software, with or without modifications, in source and/or binary forms;
+ provided that if you redistribute the Apple Software in its entirety and
+ without modifications, you must retain this notice and the following
+ text and disclaimers in all such redistributions of the Apple Software.
+ Neither the name, trademarks, service marks or logos of Apple Inc.
+ may be used to endorse or promote products derived from the Apple
+ Software without specific prior written permission from Apple. Except
+ as expressly stated in this notice, no other rights or licenses, express
+ or implied, are granted by Apple herein, including but not limited to
+ any patent rights that may be infringed by your derivative works or by
+ other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE
+ MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
+ THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
+ FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
+ OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
+ MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
+ AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
+ STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+ Copyright (C) 2009 Apple Inc. All Rights Reserved.
+
+ $Id: CDOpenALSupport.h 16 2010-03-11 06:22:10Z steveoldmeadow $
+ */
+
+#import "CDOpenALSupport.h"
+#import "CocosDenshion.h"
+#import <AudioToolbox/AudioToolbox.h>
+#import <AudioToolbox/ExtendedAudioFile.h>
+
+//Taken from oalTouch MyOpenALSupport 1.1
+void* CDloadWaveAudioData(CFURLRef inFileURL, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei* outSampleRate)
+{
+ OSStatus err = noErr;
+ UInt64 fileDataSize = 0;
+ AudioStreamBasicDescription theFileFormat;
+ UInt32 thePropertySize = sizeof(theFileFormat);
+ AudioFileID afid = 0;
+ void* theData = NULL;
+
+ // Open a file with ExtAudioFileOpen()
+ err = AudioFileOpenURL(inFileURL, kAudioFileReadPermission, 0, &afid);
+ if(err) { CDLOG(@"MyGetOpenALAudioData: AudioFileOpenURL FAILED, Error = %ld\n", err); goto Exit; }
+
+ // Get the audio data format
+ err = AudioFileGetProperty(afid, kAudioFilePropertyDataFormat, &thePropertySize, &theFileFormat);
+ if(err) { CDLOG(@"MyGetOpenALAudioData: AudioFileGetProperty(kAudioFileProperty_DataFormat) FAILED, Error = %ld\n", err); goto Exit; }
+
+ if (theFileFormat.mChannelsPerFrame > 2) {
+ CDLOG(@"MyGetOpenALAudioData - Unsupported Format, channel count is greater than stereo\n"); goto Exit;
+ }
+
+ if ((theFileFormat.mFormatID != kAudioFormatLinearPCM) || (!TestAudioFormatNativeEndian(theFileFormat))) {
+ CDLOG(@"MyGetOpenALAudioData - Unsupported Format, must be little-endian PCM\n"); goto Exit;
+ }
+
+ if ((theFileFormat.mBitsPerChannel != 8) && (theFileFormat.mBitsPerChannel != 16)) {
+ CDLOG(@"MyGetOpenALAudioData - Unsupported Format, must be 8 or 16 bit PCM\n"); goto Exit;
+ }
+
+
+ thePropertySize = sizeof(fileDataSize);
+ err = AudioFileGetProperty(afid, kAudioFilePropertyAudioDataByteCount, &thePropertySize, &fileDataSize);
+ if(err) { CDLOG(@"MyGetOpenALAudioData: AudioFileGetProperty(kAudioFilePropertyAudioDataByteCount) FAILED, Error = %ld\n", err); goto Exit; }
+
+ // Read all the data into memory
+ UInt32 dataSize = (UInt32)fileDataSize;
+ theData = malloc(dataSize);
+ if (theData)
+ {
+ AudioFileReadBytes(afid, false, 0, &dataSize, theData);
+ if(err == noErr)
+ {
+ // success
+ *outDataSize = (ALsizei)dataSize;
+ //This fix was added by me, however, 8 bit sounds have a clipping sound at the end so aren't really usable (SO)
+ if (theFileFormat.mBitsPerChannel == 16) {
+ *outDataFormat = (theFileFormat.mChannelsPerFrame > 1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16;
+ } else {
+ *outDataFormat = (theFileFormat.mChannelsPerFrame > 1) ? AL_FORMAT_STEREO8 : AL_FORMAT_MONO8;
+ }
+ *outSampleRate = (ALsizei)theFileFormat.mSampleRate;
+ }
+ else
+ {
+ // failure
+ free (theData);
+ theData = NULL; // make sure to return NULL
+ CDLOG(@"MyGetOpenALAudioData: ExtAudioFileRead FAILED, Error = %ld\n", err); goto Exit;
+ }
+ }
+
+Exit:
+ // Dispose the ExtAudioFileRef, it is no longer needed
+ if (afid) AudioFileClose(afid);
+ return theData;
+}
+
+//Taken from oalTouch MyOpenALSupport 1.4
+void* CDloadCafAudioData(CFURLRef inFileURL, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei* outSampleRate)
+{
+ OSStatus status = noErr;
+ BOOL abort = NO;
+ SInt64 theFileLengthInFrames = 0;
+ AudioStreamBasicDescription theFileFormat;
+ UInt32 thePropertySize = sizeof(theFileFormat);
+ ExtAudioFileRef extRef = NULL;
+ void* theData = NULL;
+ AudioStreamBasicDescription theOutputFormat;
+ UInt32 dataSize = 0;
+
+ // Open a file with ExtAudioFileOpen()
+ status = ExtAudioFileOpenURL(inFileURL, &extRef);
+ if (status != noErr)
+ {
+ CDLOG(@"MyGetOpenALAudioData: ExtAudioFileOpenURL FAILED, Error = %ld\n", status);
+ abort = YES;
+ }
+ if (abort)
+ goto Exit;
+
+ // Get the audio data format
+ status = ExtAudioFileGetProperty(extRef, kExtAudioFileProperty_FileDataFormat, &thePropertySize, &theFileFormat);
+ if (status != noErr)
+ {
+ CDLOG(@"MyGetOpenALAudioData: ExtAudioFileGetProperty(kExtAudioFileProperty_FileDataFormat) FAILED, Error = %ld\n", status);
+ abort = YES;
+ }
+ if (abort)
+ goto Exit;
+
+ if (theFileFormat.mChannelsPerFrame > 2)
+ {
+ CDLOG(@"MyGetOpenALAudioData - Unsupported Format, channel count is greater than stereo\n");
+ abort = YES;
+ }
+ if (abort)
+ goto Exit;
+
+ // Set the client format to 16 bit signed integer (native-endian) data
+ // Maintain the channel count and sample rate of the original source format
+ theOutputFormat.mSampleRate = theFileFormat.mSampleRate;
+ theOutputFormat.mChannelsPerFrame = theFileFormat.mChannelsPerFrame;
+
+ theOutputFormat.mFormatID = kAudioFormatLinearPCM;
+ theOutputFormat.mBytesPerPacket = 2 * theOutputFormat.mChannelsPerFrame;
+ theOutputFormat.mFramesPerPacket = 1;
+ theOutputFormat.mBytesPerFrame = 2 * theOutputFormat.mChannelsPerFrame;
+ theOutputFormat.mBitsPerChannel = 16;
+ theOutputFormat.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger;
+
+ // Set the desired client (output) data format
+ status = ExtAudioFileSetProperty(extRef, kExtAudioFileProperty_ClientDataFormat, sizeof(theOutputFormat), &theOutputFormat);
+ if (status != noErr)
+ {
+ CDLOG(@"MyGetOpenALAudioData: ExtAudioFileSetProperty(kExtAudioFileProperty_ClientDataFormat) FAILED, Error = %ld\n", status);
+ abort = YES;
+ }
+ if (abort)
+ goto Exit;
+
+ // Get the total frame count
+ thePropertySize = sizeof(theFileLengthInFrames);
+ status = ExtAudioFileGetProperty(extRef, kExtAudioFileProperty_FileLengthFrames, &thePropertySize, &theFileLengthInFrames);
+ if (status != noErr)
+ {
+ CDLOG(@"MyGetOpenALAudioData: ExtAudioFileGetProperty(kExtAudioFileProperty_FileLengthFrames) FAILED, Error = %ld\n", status);
+ abort = YES;
+ }
+ if (abort)
+ goto Exit;
+
+ // Read all the data into memory
+ dataSize = (UInt32) theFileLengthInFrames * theOutputFormat.mBytesPerFrame;
+ theData = malloc(dataSize);
+ if (theData)
+ {
+ AudioBufferList theDataBuffer;
+ theDataBuffer.mNumberBuffers = 1;
+ theDataBuffer.mBuffers[0].mDataByteSize = dataSize;
+ theDataBuffer.mBuffers[0].mNumberChannels = theOutputFormat.mChannelsPerFrame;
+ theDataBuffer.mBuffers[0].mData = theData;
+
+ // Read the data into an AudioBufferList
+ status = ExtAudioFileRead(extRef, (UInt32*)&theFileLengthInFrames, &theDataBuffer);
+ if(status == noErr)
+ {
+ // success
+ *outDataSize = (ALsizei)dataSize;
+ *outDataFormat = (theOutputFormat.mChannelsPerFrame > 1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16;
+ *outSampleRate = (ALsizei)theOutputFormat.mSampleRate;
+ }
+ else
+ {
+ // failure
+ free (theData);
+ theData = NULL; // make sure to return NULL
+ CDLOG(@"MyGetOpenALAudioData: ExtAudioFileRead FAILED, Error = %ld\n", status);
+ abort = YES;
+ }
+ }
+ if (abort)
+ goto Exit;
+
+Exit:
+ // Dispose the ExtAudioFileRef, it is no longer needed
+ if (extRef) ExtAudioFileDispose(extRef);
+ return theData;
+}
+
+void* CDGetOpenALAudioData(CFURLRef inFileURL, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei* outSampleRate) {
+
+ CFStringRef extension = CFURLCopyPathExtension(inFileURL);
+ CFComparisonResult isWavFile = 0;
+ if (extension != NULL) {
+ isWavFile = CFStringCompare (extension,(CFStringRef)@"wav", kCFCompareCaseInsensitive);
+ CFRelease(extension);
+ }
+
+ if (isWavFile == kCFCompareEqualTo) {
+ return CDloadWaveAudioData(inFileURL, outDataSize, outDataFormat, outSampleRate);
+ } else {
+ return CDloadCafAudioData(inFileURL, outDataSize, outDataFormat, outSampleRate);
+ }
+}
+
--- /dev/null
+/*
+ Copyright (c) 2010 Steve Oldmeadow
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+
+ $Id$
+ */
+
+
+
+/**
+@file
+@b IMPORTANT
+There are 3 different ways of using CocosDenshion. Depending on which you choose you
+will need to include different files and frameworks.
+
+@par SimpleAudioEngine
+This is recommended for basic audio requirements. If you just want to play some sound fx
+and some background music and have no interest in learning the lower level workings then
+this is the interface to use.
+
+Requirements:
+ - Firmware: OS 2.2 or greater
+ - Files: SimpleAudioEngine.*, CocosDenshion.*
+ - Frameworks: OpenAL, AudioToolbox, AVFoundation
+
+@par CDAudioManager
+CDAudioManager is basically a thin wrapper around an AVAudioPlayer object used for playing
+background music and a CDSoundEngine object used for playing sound effects. It manages the
+audio session for you deals with audio session interruption. It is fairly low level and it
+is expected you have some understanding of the underlying technologies. For example, for
+many use cases regarding background music it is expected you will work directly with the
+backgroundMusic AVAudioPlayer which is exposed as a property.
+
+Requirements:
+ - Firmware: OS 2.2 or greater
+ - Files: CDAudioManager.*, CocosDenshion.*
+ - Frameworks: OpenAL, AudioToolbox, AVFoundation
+
+@par CDSoundEngine
+CDSoundEngine is a sound engine built upon OpenAL and derived from Apple's oalTouch
+example. It can playback up to 32 sounds simultaneously with control over pitch, pan
+and gain. It can be set up to handle audio session interruption automatically. You
+may decide to use CDSoundEngine directly instead of CDAudioManager or SimpleAudioEngine
+because you require OS 2.0 compatibility.
+
+Requirements:
+ - Firmware: OS 2.0 or greater
+ - Files: CocosDenshion.*
+ - Frameworks: OpenAL, AudioToolbox
+
+*/
+
+#import <OpenAL/al.h>
+#import <OpenAL/alc.h>
+#import <AudioToolbox/AudioToolbox.h>
+#import <Foundation/Foundation.h>
+#import "CDConfig.h"
+
+
+#if !defined(CD_DEBUG) || CD_DEBUG == 0
+#define CDLOG(...) do {} while (0)
+#define CDLOGINFO(...) do {} while (0)
+
+#elif CD_DEBUG == 1
+#define CDLOG(...) NSLog(__VA_ARGS__)
+#define CDLOGINFO(...) do {} while (0)
+
+#elif CD_DEBUG > 1
+#define CDLOG(...) NSLog(__VA_ARGS__)
+#define CDLOGINFO(...) NSLog(__VA_ARGS__)
+#endif // CD_DEBUG
+
+
+#import "CDOpenALSupport.h"
+
+//Tested source limit on 2.2.1 and 3.1.2 with up to 128 sources and appears to work. Older OS versions e.g 2.2 may support only 32
+#define CD_SOURCE_LIMIT 32 //Total number of sources we will ever want, may actually get less
+#define CD_NO_SOURCE 0xFEEDFAC //Return value indicating playback failed i.e. no source
+#define CD_IGNORE_AUDIO_SESSION 0xBEEFBEE //Used internally to indicate audio session will not be handled
+#define CD_MUTE 0xFEEDBAB //Return value indicating sound engine is muted or non functioning
+#define CD_NO_SOUND = -1;
+
+#define CD_SAMPLE_RATE_HIGH 44100
+#define CD_SAMPLE_RATE_MID 22050
+#define CD_SAMPLE_RATE_LOW 16000
+#define CD_SAMPLE_RATE_BASIC 8000
+#define CD_SAMPLE_RATE_DEFAULT 44100
+
+extern NSString * const kCDN_BadAlContext;
+extern NSString * const kCDN_AsynchLoadComplete;
+
+extern float const kCD_PitchDefault;
+extern float const kCD_PitchLowerOneOctave;
+extern float const kCD_PitchHigherOneOctave;
+extern float const kCD_PanDefault;
+extern float const kCD_PanFullLeft;
+extern float const kCD_PanFullRight;
+extern float const kCD_GainDefault;
+
+enum bufferState {
+ CD_BS_EMPTY = 0,
+ CD_BS_LOADED = 1,
+ CD_BS_FAILED = 2
+};
+
+typedef struct _sourceGroup {
+ int startIndex;
+ int currentIndex;
+ int totalSources;
+ bool enabled;
+ bool nonInterruptible;
+ int *sourceStatuses;//pointer into array of source status information
+} sourceGroup;
+
+typedef struct _bufferInfo {
+ ALuint bufferId;
+ int bufferState;
+ void* bufferData;
+ ALenum format;
+ ALsizei sizeInBytes;
+ ALsizei frequencyInHertz;
+} bufferInfo;
+
+typedef struct _sourceInfo {
+ bool usable;
+ ALuint sourceId;
+ ALuint attachedBufferId;
+} sourceInfo;
+
+#pragma mark CDAudioTransportProtocol
+
+@protocol CDAudioTransportProtocol <NSObject>
+/** Play the audio */
+-(BOOL) play;
+/** Pause the audio, retain resources */
+-(BOOL) pause;
+/** Stop the audio, release resources */
+-(BOOL) stop;
+/** Return playback to beginning */
+-(BOOL) rewind;
+@end
+
+#pragma mark CDAudioInterruptProtocol
+
+@protocol CDAudioInterruptProtocol <NSObject>
+/** Is audio mute */
+-(BOOL) mute;
+/** If YES then audio is silenced but not stopped, calls to start new audio will proceed but silently */
+-(void) setMute:(BOOL) muteValue;
+/** Is audio enabled */
+-(BOOL) enabled;
+/** If NO then all audio is stopped and any calls to start new audio will be ignored */
+-(void) setEnabled:(BOOL) enabledValue;
+@end
+
+#pragma mark CDUtilities
+/**
+ Collection of utilities required by CocosDenshion
+ */
+@interface CDUtilities : NSObject
+{
+}
+
+/** Fundamentally the same as the corresponding method is CCFileUtils but added to break binding to cocos2d */
++(NSString*) fullPathFromRelativePath:(NSString*) relPath;
+
+@end
+
+
+#pragma mark CDSoundEngine
+
+/** CDSoundEngine is built upon OpenAL and works with SDK 2.0.
+ CDSoundEngine is a sound engine built upon OpenAL and derived from Apple's oalTouch
+ example. It can playback up to 32 sounds simultaneously with control over pitch, pan
+ and gain. It can be set up to handle audio session interruption automatically. You
+ may decide to use CDSoundEngine directly instead of CDAudioManager or SimpleAudioEngine
+ because you require OS 2.0 compatibility.
+
+ Requirements:
+ - Firmware: OS 2.0 or greater
+ - Files: CocosDenshion.*
+ - Frameworks: OpenAL, AudioToolbox
+
+ @since v0.8
+ */
+@class CDSoundSource;
+@interface CDSoundEngine : NSObject <CDAudioInterruptProtocol> {
+
+ bufferInfo *_buffers;
+ sourceInfo *_sources;
+ sourceGroup *_sourceGroups;
+ ALCcontext *context;
+ NSUInteger _sourceGroupTotal;
+ UInt32 _audioSessionCategory;
+ BOOL _handleAudioSession;
+ ALfloat _preMuteGain;
+ NSObject *_mutexBufferLoad;
+ BOOL mute_;
+ BOOL enabled_;
+
+ ALenum lastErrorCode_;
+ BOOL functioning_;
+ float asynchLoadProgress_;
+ BOOL getGainWorks_;
+
+ //For managing dynamic allocation of sources and buffers
+ int sourceTotal_;
+ int bufferTotal;
+
+}
+
+@property (readwrite, nonatomic) ALfloat masterGain;
+@property (readonly) ALenum lastErrorCode;//Last OpenAL error code that was generated
+@property (readonly) BOOL functioning;//Is the sound engine functioning
+@property (readwrite) float asynchLoadProgress;
+@property (readonly) BOOL getGainWorks;//Does getting the gain for a source work
+/** Total number of sources available */
+@property (readonly) int sourceTotal;
+/** Total number of source groups that have been defined */
+@property (readonly) NSUInteger sourceGroupTotal;
+
+/** Sets the sample rate for the audio mixer. For best performance this should match the sample rate of your audio content */
++(void) setMixerSampleRate:(Float32) sampleRate;
+
+/** Initializes the engine with a group definition and a total number of groups */
+-(id)init;
+
+/** Plays a sound in a channel group with a pitch, pan and gain. The sound could played looped or not */
+-(ALuint) playSound:(int) soundId sourceGroupId:(int)sourceGroupId pitch:(float) pitch pan:(float) pan gain:(float) gain loop:(BOOL) loop;
+
+/** Creates and returns a sound source object for the specified sound within the specified source group.
+ */
+-(CDSoundSource *) soundSourceForSound:(int) soundId sourceGroupId:(int) sourceGroupId;
+
+/** Stops playing a sound */
+- (void) stopSound:(ALuint) sourceId;
+/** Stops playing a source group */
+- (void) stopSourceGroup:(int) sourceGroupId;
+/** Stops all playing sounds */
+-(void) stopAllSounds;
+-(void) defineSourceGroups:(NSArray*) sourceGroupDefinitions;
+-(void) defineSourceGroups:(int[]) sourceGroupDefinitions total:(NSUInteger) total;
+-(void) setSourceGroupNonInterruptible:(int) sourceGroupId isNonInterruptible:(BOOL) isNonInterruptible;
+-(void) setSourceGroupEnabled:(int) sourceGroupId enabled:(BOOL) enabled;
+-(BOOL) sourceGroupEnabled:(int) sourceGroupId;
+-(BOOL) loadBufferFromData:(int) soundId soundData:(ALvoid*) soundData format:(ALenum) format size:(ALsizei) size freq:(ALsizei) freq;
+-(BOOL) loadBuffer:(int) soundId filePath:(NSString*) filePath;
+-(void) loadBuffersAsynchronously:(NSArray *) loadRequests;
+-(BOOL) unloadBuffer:(int) soundId;
+-(ALCcontext *) openALContext;
+
+/** Returns the duration of the buffer in seconds or a negative value if the buffer id is invalid */
+-(float) bufferDurationInSeconds:(int) soundId;
+/** Returns the size of the buffer in bytes or a negative value if the buffer id is invalid */
+-(ALsizei) bufferSizeInBytes:(int) soundId;
+/** Returns the sampling frequency of the buffer in hertz or a negative value if the buffer id is invalid */
+-(ALsizei) bufferFrequencyInHertz:(int) soundId;
+
+/** Used internally, never call unless you know what you are doing */
+-(void) _soundSourcePreRelease:(CDSoundSource *) soundSource;
+
+@end
+
+#pragma mark CDSoundSource
+/** CDSoundSource is a wrapper around an OpenAL sound source.
+ It allows you to manipulate properties such as pitch, gain, pan and looping while the
+ sound is playing. CDSoundSource is based on the old CDSourceWrapper class but with much
+ added functionality.
+
+ @since v1.0
+ */
+@interface CDSoundSource : NSObject <CDAudioTransportProtocol, CDAudioInterruptProtocol> {
+ ALenum lastError;
+@public
+ ALuint _sourceId;
+ ALuint _sourceIndex;
+ CDSoundEngine* _engine;
+ int _soundId;
+ float _preMuteGain;
+ BOOL enabled_;
+ BOOL mute_;
+}
+@property (readwrite, nonatomic) float pitch;
+@property (readwrite, nonatomic) float gain;
+@property (readwrite, nonatomic) float pan;
+@property (readwrite, nonatomic) BOOL looping;
+@property (readonly) BOOL isPlaying;
+@property (readwrite, nonatomic) int soundId;
+/** Returns the duration of the attached buffer in seconds or a negative value if the buffer is invalid */
+@property (readonly) float durationInSeconds;
+
+/** Stores the last error code that occurred. Check against AL_NO_ERROR */
+@property (readonly) ALenum lastError;
+/** Do not init yourself, get an instance from the sourceForSound factory method on CDSoundEngine */
+-(id)init:(ALuint) theSourceId sourceIndex:(int) index soundEngine:(CDSoundEngine*) engine;
+
+@end
+
+#pragma mark CDAudioInterruptTargetGroup
+
+/** Container for objects that implement audio interrupt protocol i.e. they can be muted and enabled.
+ Setting mute and enabled for the group propagates to all children.
+ Designed to be used with your CDSoundSource objects to get them to comply with global enabled and mute settings
+ if that is what you want to do.*/
+@interface CDAudioInterruptTargetGroup : NSObject <CDAudioInterruptProtocol> {
+ BOOL mute_;
+ BOOL enabled_;
+ NSMutableArray *children_;
+}
+-(void) addAudioInterruptTarget:(NSObject<CDAudioInterruptProtocol>*) interruptibleTarget;
+@end
+
+#pragma mark CDAsynchBufferLoader
+
+/** CDAsynchBufferLoader
+ TODO
+ */
+@interface CDAsynchBufferLoader : NSOperation {
+ NSArray *_loadRequests;
+ CDSoundEngine *_soundEngine;
+}
+
+-(id) init:(NSArray *)loadRequests soundEngine:(CDSoundEngine *) theSoundEngine;
+
+@end
+
+#pragma mark CDBufferLoadRequest
+
+/** CDBufferLoadRequest */
+@interface CDBufferLoadRequest: NSObject
+{
+ NSString *filePath;
+ int soundId;
+ //id loader;
+}
+
+@property (readonly) NSString *filePath;
+@property (readonly) int soundId;
+
+- (id)init:(int) theSoundId filePath:(const NSString *) theFilePath;
+@end
+
+/** Interpolation type */
+typedef enum {
+ kIT_Linear, //!Straight linear interpolation fade
+ kIT_SCurve, //!S curved interpolation
+ kIT_Exponential //!Exponential interpolation
+} tCDInterpolationType;
+
+#pragma mark CDFloatInterpolator
+@interface CDFloatInterpolator: NSObject
+{
+ float start;
+ float end;
+ float lastValue;
+ tCDInterpolationType interpolationType;
+}
+@property (readwrite, nonatomic) float start;
+@property (readwrite, nonatomic) float end;
+@property (readwrite, nonatomic) tCDInterpolationType interpolationType;
+
+/** Return a value between min and max based on t which represents fractional progress where 0 is the start
+ and 1 is the end */
+-(float) interpolate:(float) t;
+-(id) init:(tCDInterpolationType) type startVal:(float) startVal endVal:(float) endVal;
+
+@end
+
+#pragma mark CDPropertyModifier
+
+/** Base class for classes that modify properties such as pitch, pan and gain */
+@interface CDPropertyModifier: NSObject
+{
+ CDFloatInterpolator *interpolator;
+ float startValue;
+ float endValue;
+ id target;
+ BOOL stopTargetWhenComplete;
+
+}
+@property (readwrite, nonatomic) BOOL stopTargetWhenComplete;
+@property (readwrite, nonatomic) float startValue;
+@property (readwrite, nonatomic) float endValue;
+@property (readwrite, nonatomic) tCDInterpolationType interpolationType;
+
+-(id) init:(id) theTarget interpolationType:(tCDInterpolationType) type startVal:(float) startVal endVal:(float) endVal;
+/** Set to a fractional value between 0 and 1 where 0 equals the start and 1 equals the end*/
+-(void) modify:(float) t;
+
+-(void) _setTargetProperty:(float) newVal;
+-(float) _getTargetProperty;
+-(void) _stopTarget;
+-(Class) _allowableType;
+
+@end
+
+#pragma mark CDSoundSourceFader
+
+/** Fader for CDSoundSource objects */
+@interface CDSoundSourceFader : CDPropertyModifier{}
+@end
+
+#pragma mark CDSoundSourcePanner
+
+/** Panner for CDSoundSource objects */
+@interface CDSoundSourcePanner : CDPropertyModifier{}
+@end
+
+#pragma mark CDSoundSourcePitchBender
+
+/** Pitch bender for CDSoundSource objects */
+@interface CDSoundSourcePitchBender : CDPropertyModifier{}
+@end
+
+#pragma mark CDSoundEngineFader
+
+/** Fader for CDSoundEngine objects */
+@interface CDSoundEngineFader : CDPropertyModifier{}
+@end
+
+
+
+
--- /dev/null
+/*
+ Copyright (c) 2010 Steve Oldmeadow
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+
+ $Id$
+ */
+
+#import "CocosDenshion.h"
+
+typedef ALvoid AL_APIENTRY (*alBufferDataStaticProcPtr) (const ALint bid, ALenum format, ALvoid* data, ALsizei size, ALsizei freq);
+ALvoid alBufferDataStaticProc(const ALint bid, ALenum format, ALvoid* data, ALsizei size, ALsizei freq)
+{
+ static alBufferDataStaticProcPtr proc = NULL;
+
+ if (proc == NULL) {
+ proc = (alBufferDataStaticProcPtr) alcGetProcAddress(NULL, (const ALCchar*) "alBufferDataStatic");
+ }
+
+ if (proc)
+ proc(bid, format, data, size, freq);
+
+ return;
+}
+
+typedef ALvoid AL_APIENTRY (*alcMacOSXMixerOutputRateProcPtr) (const ALdouble value);
+ALvoid alcMacOSXMixerOutputRateProc(const ALdouble value)
+{
+ static alcMacOSXMixerOutputRateProcPtr proc = NULL;
+
+ if (proc == NULL) {
+ proc = (alcMacOSXMixerOutputRateProcPtr) alcGetProcAddress(NULL, (const ALCchar*) "alcMacOSXMixerOutputRate");
+ }
+
+ if (proc)
+ proc(value);
+
+ return;
+}
+
+NSString * const kCDN_BadAlContext = @"kCDN_BadAlContext";
+NSString * const kCDN_AsynchLoadComplete = @"kCDN_AsynchLoadComplete";
+float const kCD_PitchDefault = 1.0f;
+float const kCD_PitchLowerOneOctave = 0.5f;
+float const kCD_PitchHigherOneOctave = 2.0f;
+float const kCD_PanDefault = 0.0f;
+float const kCD_PanFullLeft = -1.0f;
+float const kCD_PanFullRight = 1.0f;
+float const kCD_GainDefault = 1.0f;
+
+@interface CDSoundEngine (PrivateMethods)
+-(BOOL) _initOpenAL;
+-(void) _testGetGain;
+-(void) _dumpSourceGroupsInfo;
+-(void) _getSourceIndexForSourceGroup;
+-(void) _freeSourceGroups;
+-(BOOL) _setUpSourceGroups:(int[]) definitions total:(NSUInteger) total;
+@end
+
+#pragma mark -
+#pragma mark CDUtilities
+
+@implementation CDUtilities
+
++(NSString*) fullPathFromRelativePath:(NSString*) relPath
+{
+ // do not convert an absolute path (starting with '/')
+ if(([relPath length] > 0) && ([relPath characterAtIndex:0] == '/'))
+ {
+ return relPath;
+ }
+
+ NSMutableArray *imagePathComponents = [NSMutableArray arrayWithArray:[relPath pathComponents]];
+ NSString *file = [imagePathComponents lastObject];
+
+ [imagePathComponents removeLastObject];
+ NSString *imageDirectory = [NSString pathWithComponents:imagePathComponents];
+
+ NSString *fullpath = [[NSBundle mainBundle] pathForResource:file ofType:nil inDirectory:imageDirectory];
+ if (fullpath == nil)
+ fullpath = relPath;
+
+ return fullpath;
+}
+
+@end
+
+#pragma mark -
+#pragma mark CDSoundEngine
+
+@implementation CDSoundEngine
+
+static Float32 _mixerSampleRate;
+static BOOL _mixerRateSet = NO;
+
+@synthesize lastErrorCode = lastErrorCode_;
+@synthesize functioning = functioning_;
+@synthesize asynchLoadProgress = asynchLoadProgress_;
+@synthesize getGainWorks = getGainWorks_;
+@synthesize sourceTotal = sourceTotal_;
+
++ (void) setMixerSampleRate:(Float32) sampleRate {
+ _mixerRateSet = YES;
+ _mixerSampleRate = sampleRate;
+}
+
+- (void) _testGetGain {
+ float testValue = 0.7f;
+ ALuint testSourceId = _sources[0].sourceId;
+ alSourcef(testSourceId, AL_GAIN, 0.0f);//Start from know value
+ alSourcef(testSourceId, AL_GAIN, testValue);
+ ALfloat gainVal;
+ alGetSourcef(testSourceId, AL_GAIN, &gainVal);
+ getGainWorks_ = (gainVal == testValue);
+}
+
+//Generate sources one at a time until we fail
+-(void) _generateSources {
+
+ _sources = (sourceInfo*)malloc( sizeof(_sources[0]) * CD_SOURCE_LIMIT);
+ BOOL hasFailed = NO;
+ sourceTotal_ = 0;
+ alGetError();//Clear error
+ while (!hasFailed && sourceTotal_ < CD_SOURCE_LIMIT) {
+ alGenSources(1, &(_sources[sourceTotal_].sourceId));
+ if (alGetError() == AL_NO_ERROR) {
+ //Now try attaching source to null buffer
+ alSourcei(_sources[sourceTotal_].sourceId, AL_BUFFER, 0);
+ if (alGetError() == AL_NO_ERROR) {
+ _sources[sourceTotal_].usable = true;
+ sourceTotal_++;
+ } else {
+ hasFailed = YES;
+ }
+ } else {
+ _sources[sourceTotal_].usable = false;
+ hasFailed = YES;
+ }
+ }
+ //Mark the rest of the sources as not usable
+ for (int i=sourceTotal_; i < CD_SOURCE_LIMIT; i++) {
+ _sources[i].usable = false;
+ }
+}
+
+-(void) _generateBuffers:(int) startIndex endIndex:(int) endIndex {
+ if (_buffers) {
+ alGetError();
+ for (int i=startIndex; i <= endIndex; i++) {
+ alGenBuffers(1, &_buffers[i].bufferId);
+ _buffers[i].bufferData = NULL;
+ if (alGetError() == AL_NO_ERROR) {
+ _buffers[i].bufferState = CD_BS_EMPTY;
+ } else {
+ _buffers[i].bufferState = CD_BS_FAILED;
+ CDLOG(@"Denshion::CDSoundEngine - buffer creation failed %i",i);
+ }
+ }
+ }
+}
+
+/**
+ * Internal method called during init
+ */
+- (BOOL) _initOpenAL
+{
+ //ALenum error;
+ context = NULL;
+ ALCdevice *newDevice = NULL;
+
+ //Set the mixer rate for the audio mixer
+ if (!_mixerRateSet) {
+ _mixerSampleRate = CD_SAMPLE_RATE_DEFAULT;
+ }
+ alcMacOSXMixerOutputRateProc(_mixerSampleRate);
+ CDLOGINFO(@"Denshion::CDSoundEngine - mixer output rate set to %0.2f",_mixerSampleRate);
+
+ // Create a new OpenAL Device
+ // Pass NULL to specify the system's default output device
+ newDevice = alcOpenDevice(NULL);
+ if (newDevice != NULL)
+ {
+ // Create a new OpenAL Context
+ // The new context will render to the OpenAL Device just created
+ context = alcCreateContext(newDevice, 0);
+ if (context != NULL)
+ {
+ // Make the new context the Current OpenAL Context
+ alcMakeContextCurrent(context);
+
+ // Create some OpenAL Buffer Objects
+ [self _generateBuffers:0 endIndex:bufferTotal-1];
+
+ // Create some OpenAL Source Objects
+ [self _generateSources];
+
+ }
+ } else {
+ return FALSE;//No device
+ }
+ alGetError();//Clear error
+ return TRUE;
+}
+
+- (void) dealloc {
+
+ ALCcontext *currentContext = NULL;
+ ALCdevice *device = NULL;
+
+ [self stopAllSounds];
+
+ CDLOGINFO(@"Denshion::CDSoundEngine - Deallocing sound engine.");
+ [self _freeSourceGroups];
+
+ // Delete the Sources
+ CDLOGINFO(@"Denshion::CDSoundEngine - deleting sources.");
+ for (int i=0; i < sourceTotal_; i++) {
+ alSourcei(_sources[i].sourceId, AL_BUFFER, 0);//Detach from current buffer
+ alDeleteSources(1, &(_sources[i].sourceId));
+ if((lastErrorCode_ = alGetError()) != AL_NO_ERROR) {
+ CDLOG(@"Denshion::CDSoundEngine - Error deleting source! %x\n", lastErrorCode_);
+ }
+ }
+
+ // Delete the Buffers
+ CDLOGINFO(@"Denshion::CDSoundEngine - deleting buffers.");
+ for (int i=0; i < bufferTotal; i++) {
+ alDeleteBuffers(1, &_buffers[i].bufferId);
+#ifdef CD_USE_STATIC_BUFFERS
+ if (_buffers[i].bufferData) {
+ free(_buffers[i].bufferData);
+ }
+#endif
+ }
+ CDLOGINFO(@"Denshion::CDSoundEngine - free buffers.");
+ free(_buffers);
+ currentContext = alcGetCurrentContext();
+ //Get device for active context
+ device = alcGetContextsDevice(currentContext);
+ //Release context
+ CDLOGINFO(@"Denshion::CDSoundEngine - destroy context.");
+ alcDestroyContext(currentContext);
+ //Close device
+ CDLOGINFO(@"Denshion::CDSoundEngine - close device.");
+ alcCloseDevice(device);
+ CDLOGINFO(@"Denshion::CDSoundEngine - free sources.");
+ free(_sources);
+
+ //Release mutexes
+ [_mutexBufferLoad release];
+
+ [super dealloc];
+}
+
+-(NSUInteger) sourceGroupTotal {
+ return _sourceGroupTotal;
+}
+
+-(void) _freeSourceGroups
+{
+ CDLOGINFO(@"Denshion::CDSoundEngine freeing source groups");
+ if(_sourceGroups) {
+ for (int i=0; i < _sourceGroupTotal; i++) {
+ if (_sourceGroups[i].sourceStatuses) {
+ free(_sourceGroups[i].sourceStatuses);
+ CDLOGINFO(@"Denshion::CDSoundEngine freed source statuses %i",i);
+ }
+ }
+ free(_sourceGroups);
+ }
+}
+
+-(BOOL) _redefineSourceGroups:(int[]) definitions total:(NSUInteger) total
+{
+ if (_sourceGroups) {
+ //Stop all sounds
+ [self stopAllSounds];
+ //Need to free source groups
+ [self _freeSourceGroups];
+ }
+ return [self _setUpSourceGroups:definitions total:total];
+}
+
+-(BOOL) _setUpSourceGroups:(int[]) definitions total:(NSUInteger) total
+{
+ _sourceGroups = (sourceGroup *)malloc( sizeof(_sourceGroups[0]) * total);
+ if(!_sourceGroups) {
+ CDLOG(@"Denshion::CDSoundEngine - source groups memory allocation failed");
+ return NO;
+ }
+
+ _sourceGroupTotal = total;
+ int sourceCount = 0;
+ for (int i=0; i < _sourceGroupTotal; i++) {
+
+ _sourceGroups[i].startIndex = 0;
+ _sourceGroups[i].currentIndex = _sourceGroups[i].startIndex;
+ _sourceGroups[i].enabled = false;
+ _sourceGroups[i].nonInterruptible = false;
+ _sourceGroups[i].totalSources = definitions[i];
+ _sourceGroups[i].sourceStatuses = malloc(sizeof(_sourceGroups[i].sourceStatuses[0]) * _sourceGroups[i].totalSources);
+ if (_sourceGroups[i].sourceStatuses) {
+ for (int j=0; j < _sourceGroups[i].totalSources; j++) {
+ //First bit is used to indicate whether source is locked, index is shifted back 1 bit
+ _sourceGroups[i].sourceStatuses[j] = (sourceCount + j) << 1;
+ }
+ }
+ sourceCount += definitions[i];
+ }
+ return YES;
+}
+
+-(void) defineSourceGroups:(int[]) sourceGroupDefinitions total:(NSUInteger) total {
+ [self _redefineSourceGroups:sourceGroupDefinitions total:total];
+}
+
+-(void) defineSourceGroups:(NSArray*) sourceGroupDefinitions {
+ CDLOGINFO(@"Denshion::CDSoundEngine - source groups defined by NSArray.");
+ NSUInteger totalDefs = [sourceGroupDefinitions count];
+ int* defs = (int *)malloc( sizeof(int) * totalDefs);
+ int currentIndex = 0;
+ for (id currentDef in sourceGroupDefinitions) {
+ if ([currentDef isKindOfClass:[NSNumber class]]) {
+ defs[currentIndex] = (int)[(NSNumber*)currentDef integerValue];
+ CDLOGINFO(@"Denshion::CDSoundEngine - found definition %i.",defs[currentIndex]);
+ } else {
+ CDLOG(@"Denshion::CDSoundEngine - warning, did not understand source definition.");
+ defs[currentIndex] = 0;
+ }
+ currentIndex++;
+ }
+ [self _redefineSourceGroups:defs total:totalDefs];
+ free(defs);
+}
+
+- (id)init
+{
+ if ((self = [super init])) {
+
+ //Create mutexes
+ _mutexBufferLoad = [[NSObject alloc] init];
+
+ asynchLoadProgress_ = 0.0f;
+
+ bufferTotal = CD_BUFFERS_START;
+ _buffers = (bufferInfo *)malloc( sizeof(_buffers[0]) * bufferTotal);
+
+ // Initialize our OpenAL environment
+ if ([self _initOpenAL]) {
+ //Set up the default source group - a single group that contains all the sources
+ int sourceDefs[1];
+ sourceDefs[0] = self.sourceTotal;
+ [self _setUpSourceGroups:sourceDefs total:1];
+
+ functioning_ = YES;
+ //Synchronize premute gain
+ _preMuteGain = self.masterGain;
+ mute_ = NO;
+ enabled_ = YES;
+ //Test whether get gain works for sources
+ [self _testGetGain];
+ } else {
+ //Something went wrong with OpenAL
+ functioning_ = NO;
+ }
+ }
+
+ return self;
+}
+
+/**
+ * Delete the buffer identified by soundId
+ * @return true if buffer deleted successfully, otherwise false
+ */
+- (BOOL) unloadBuffer:(int) soundId
+{
+ //Ensure soundId is within array bounds otherwise memory corruption will occur
+ if (soundId < 0 || soundId >= bufferTotal) {
+ CDLOG(@"Denshion::CDSoundEngine - soundId is outside array bounds, maybe you need to increase CD_MAX_BUFFERS");
+ return FALSE;
+ }
+
+ //Before a buffer can be deleted any sources that are attached to it must be stopped
+ for (int i=0; i < sourceTotal_; i++) {
+ //Note: tried getting the AL_BUFFER attribute of the source instead but doesn't
+ //appear to work on a device - just returned zero.
+ if (_buffers[soundId].bufferId == _sources[i].attachedBufferId) {
+
+ CDLOG(@"Denshion::CDSoundEngine - Found attached source %i %i %i",i,_buffers[soundId].bufferId,_sources[i].sourceId);
+#ifdef CD_USE_STATIC_BUFFERS
+ //When using static buffers a crash may occur if a source is playing with a buffer that is about
+ //to be deleted even though we stop the source and successfully delete the buffer. Crash is confirmed
+ //on 2.2.1 and 3.1.2, however, it will only occur if a source is used rapidly after having its prior
+ //data deleted. To avoid any possibility of the crash we wait for the source to finish playing.
+ ALint state;
+
+ alGetSourcei(_sources[i].sourceId, AL_SOURCE_STATE, &state);
+
+ if (state == AL_PLAYING) {
+ CDLOG(@"Denshion::CDSoundEngine - waiting for source to complete playing before removing buffer data");
+ alSourcei(_sources[i].sourceId, AL_LOOPING, FALSE);//Turn off looping otherwise loops will never end
+ while (state == AL_PLAYING) {
+ alGetSourcei(_sources[i].sourceId, AL_SOURCE_STATE, &state);
+ usleep(10000);
+ }
+ }
+#endif
+ //Stop source and detach
+ alSourceStop(_sources[i].sourceId);
+ if((lastErrorCode_ = alGetError()) != AL_NO_ERROR) {
+ CDLOG(@"Denshion::CDSoundEngine - error stopping source: %x\n", lastErrorCode_);
+ }
+
+ alSourcei(_sources[i].sourceId, AL_BUFFER, 0);//Attach to "NULL" buffer to detach
+ if((lastErrorCode_ = alGetError()) != AL_NO_ERROR) {
+ CDLOG(@"Denshion::CDSoundEngine - error detaching buffer: %x\n", lastErrorCode_);
+ } else {
+ //Record that source is now attached to nothing
+ _sources[i].attachedBufferId = 0;
+ }
+ }
+ }
+
+ alDeleteBuffers(1, &_buffers[soundId].bufferId);
+ if((lastErrorCode_ = alGetError()) != AL_NO_ERROR) {
+ CDLOG(@"Denshion::CDSoundEngine - error deleting buffer: %x\n", lastErrorCode_);
+ _buffers[soundId].bufferState = CD_BS_FAILED;
+ return FALSE;
+ } else {
+#ifdef CD_USE_STATIC_BUFFERS
+ //Free previous data, if alDeleteBuffer has returned without error then no
+ if (_buffers[soundId].bufferData) {
+ CDLOGINFO(@"Denshion::CDSoundEngine - freeing static data for soundId %i @ %i",soundId,_buffers[soundId].bufferData);
+ free(_buffers[soundId].bufferData);//Free the old data
+ _buffers[soundId].bufferData = NULL;
+ }
+#endif
+ }
+
+ alGenBuffers(1, &_buffers[soundId].bufferId);
+ if((lastErrorCode_ = alGetError()) != AL_NO_ERROR) {
+ CDLOG(@"Denshion::CDSoundEngine - error regenerating buffer: %x\n", lastErrorCode_);
+ _buffers[soundId].bufferState = CD_BS_FAILED;
+ return FALSE;
+ } else {
+ //We now have an empty buffer
+ _buffers[soundId].bufferState = CD_BS_EMPTY;
+ CDLOGINFO(@"Denshion::CDSoundEngine - buffer %i successfully unloaded\n",soundId);
+ return TRUE;
+ }
+}
+
+/**
+ * Load buffers asynchronously
+ * Check asynchLoadProgress for progress. asynchLoadProgress represents fraction of completion. When it equals 1.0 loading
+ * is complete. NB: asynchLoadProgress is simply based on the number of load requests, it does not take into account
+ * file sizes.
+ * @param An array of CDBufferLoadRequest objects
+ */
+- (void) loadBuffersAsynchronously:(NSArray *) loadRequests {
+ @synchronized(self) {
+ asynchLoadProgress_ = 0.0f;
+ CDAsynchBufferLoader *loaderOp = [[[CDAsynchBufferLoader alloc] init:loadRequests soundEngine:self] autorelease];
+ NSOperationQueue *opQ = [[[NSOperationQueue alloc] init] autorelease];
+ [opQ addOperation:loaderOp];
+ }
+}
+
+-(BOOL) _resizeBuffers:(int) increment {
+
+ void * tmpBufferInfos = realloc( _buffers, sizeof(_buffers[0]) * (bufferTotal + increment) );
+
+ if(!tmpBufferInfos) {
+ free(tmpBufferInfos);
+ return NO;
+ } else {
+ _buffers = tmpBufferInfos;
+ int oldBufferTotal = bufferTotal;
+ bufferTotal = bufferTotal + increment;
+ [self _generateBuffers:oldBufferTotal endIndex:bufferTotal-1];
+ return YES;
+ }
+}
+
+-(BOOL) loadBufferFromData:(int) soundId soundData:(ALvoid*) soundData format:(ALenum) format size:(ALsizei) size freq:(ALsizei) freq {
+
+ @synchronized(_mutexBufferLoad) {
+
+ if (!functioning_) {
+ //OpenAL initialisation has previously failed
+ CDLOG(@"Denshion::CDSoundEngine - Loading buffer failed because sound engine state != functioning");
+ return FALSE;
+ }
+
+ //Ensure soundId is within array bounds otherwise memory corruption will occur
+ if (soundId < 0) {
+ CDLOG(@"Denshion::CDSoundEngine - soundId is negative");
+ return FALSE;
+ }
+
+ if (soundId >= bufferTotal) {
+ //Need to resize the buffers
+ int requiredIncrement = CD_BUFFERS_INCREMENT;
+ while (bufferTotal + requiredIncrement < soundId) {
+ requiredIncrement += CD_BUFFERS_INCREMENT;
+ }
+ CDLOGINFO(@"Denshion::CDSoundEngine - attempting to resize buffers by %i for sound %i",requiredIncrement,soundId);
+ if (![self _resizeBuffers:requiredIncrement]) {
+ CDLOG(@"Denshion::CDSoundEngine - buffer resize failed");
+ return FALSE;
+ }
+ }
+
+ if (soundData)
+ {
+ if (_buffers[soundId].bufferState != CD_BS_EMPTY) {
+ CDLOGINFO(@"Denshion::CDSoundEngine - non empty buffer, regenerating");
+ if (![self unloadBuffer:soundId]) {
+ //Deletion of buffer failed, delete buffer routine has set buffer state and lastErrorCode
+ return NO;
+ }
+ }
+
+#ifdef CD_DEBUG
+ //Check that sample rate matches mixer rate and warn if they do not
+ if (freq != (int)_mixerSampleRate) {
+ CDLOGINFO(@"Denshion::CDSoundEngine - WARNING sample rate does not match mixer sample rate performance may not be optimal.");
+ }
+#endif
+
+#ifdef CD_USE_STATIC_BUFFERS
+ alBufferDataStaticProc(_buffers[soundId].bufferId, format, soundData, size, freq);
+ _buffers[soundId].bufferData = data;//Save the pointer to the new data
+#else
+ alBufferData(_buffers[soundId].bufferId, format, soundData, size, freq);
+#endif
+ if((lastErrorCode_ = alGetError()) != AL_NO_ERROR) {
+ CDLOG(@"Denshion::CDSoundEngine - error attaching audio to buffer: %x", lastErrorCode_);
+ _buffers[soundId].bufferState = CD_BS_FAILED;
+ return FALSE;
+ }
+ } else {
+ CDLOG(@"Denshion::CDSoundEngine Buffer data is null!");
+ _buffers[soundId].bufferState = CD_BS_FAILED;
+ return FALSE;
+ }
+
+ _buffers[soundId].format = format;
+ _buffers[soundId].sizeInBytes = size;
+ _buffers[soundId].frequencyInHertz = freq;
+ _buffers[soundId].bufferState = CD_BS_LOADED;
+ CDLOGINFO(@"Denshion::CDSoundEngine Buffer %i loaded format:%i freq:%i size:%i",soundId,format,freq,size);
+ return TRUE;
+ }//end mutex
+}
+
+/**
+ * Load sound data for later play back.
+ * @return TRUE if buffer loaded okay for play back otherwise false
+ */
+- (BOOL) loadBuffer:(int) soundId filePath:(NSString*) filePath
+{
+
+ ALvoid* data;
+ ALenum format;
+ ALsizei size;
+ ALsizei freq;
+
+ CDLOGINFO(@"Denshion::CDSoundEngine - Loading openAL buffer %i %@", soundId, filePath);
+
+ CFURLRef fileURL = nil;
+ NSString *path = [CDUtilities fullPathFromRelativePath:filePath];
+ if (path) {
+ fileURL = (CFURLRef)[[NSURL fileURLWithPath:path] retain];
+ }
+
+ if (fileURL)
+ {
+ data = CDGetOpenALAudioData(fileURL, &size, &format, &freq);
+ CFRelease(fileURL);
+ BOOL result = [self loadBufferFromData:soundId soundData:data format:format size:size freq:freq];
+#ifndef CD_USE_STATIC_BUFFERS
+ free(data);//Data can be freed here because alBufferData performs a memcpy
+#endif
+ return result;
+ } else {
+ CDLOG(@"Denshion::CDSoundEngine Could not find file!\n");
+ //Don't change buffer state here as it will be the same as before method was called
+ return FALSE;
+ }
+}
+
+-(BOOL) validateBufferId:(int) soundId {
+ if (soundId < 0 || soundId >= bufferTotal) {
+ CDLOGINFO(@"Denshion::CDSoundEngine - validateBufferId buffer outside range %i",soundId);
+ return NO;
+ } else if (_buffers[soundId].bufferState != CD_BS_LOADED) {
+ CDLOGINFO(@"Denshion::CDSoundEngine - validateBufferId invalide buffer state %i",soundId);
+ return NO;
+ } else {
+ return YES;
+ }
+}
+
+-(float) bufferDurationInSeconds:(int) soundId {
+ if ([self validateBufferId:soundId]) {
+ float factor = 0.0f;
+ switch (_buffers[soundId].format) {
+ case AL_FORMAT_MONO8:
+ factor = 1.0f;
+ break;
+ case AL_FORMAT_MONO16:
+ factor = 0.5f;
+ break;
+ case AL_FORMAT_STEREO8:
+ factor = 0.5f;
+ break;
+ case AL_FORMAT_STEREO16:
+ factor = 0.25f;
+ break;
+ }
+ return (float)_buffers[soundId].sizeInBytes/(float)_buffers[soundId].frequencyInHertz * factor;
+ } else {
+ return -1.0f;
+ }
+}
+
+-(ALsizei) bufferSizeInBytes:(int) soundId {
+ if ([self validateBufferId:soundId]) {
+ return _buffers[soundId].sizeInBytes;
+ } else {
+ return -1.0f;
+ }
+}
+
+-(ALsizei) bufferFrequencyInHertz:(int) soundId {
+ if ([self validateBufferId:soundId]) {
+ return _buffers[soundId].frequencyInHertz;
+ } else {
+ return -1.0f;
+ }
+}
+
+- (ALfloat) masterGain {
+ if (mute_) {
+ //When mute the real gain will always be 0 therefore return the preMuteGain value
+ return _preMuteGain;
+ } else {
+ ALfloat gain;
+ alGetListenerf(AL_GAIN, &gain);
+ return gain;
+ }
+}
+
+/**
+ * Overall gain setting multiplier. e.g 0.5 is half the gain.
+ */
+- (void) setMasterGain:(ALfloat) newGainValue {
+ if (mute_) {
+ _preMuteGain = newGainValue;
+ } else {
+ alListenerf(AL_GAIN, newGainValue);
+ }
+}
+
+#pragma mark CDSoundEngine AudioInterrupt protocol
+- (BOOL) mute {
+ return mute_;
+}
+
+/**
+ * Setting mute silences all sounds but playing sounds continue to advance playback
+ */
+- (void) setMute:(BOOL) newMuteValue {
+
+ if (newMuteValue == mute_) {
+ return;
+ }
+
+ mute_ = newMuteValue;
+ if (mute_) {
+ //Remember what the gain was
+ _preMuteGain = self.masterGain;
+ //Set gain to 0 - do not use the property as this will adjust preMuteGain when muted
+ alListenerf(AL_GAIN, 0.0f);
+ } else {
+ //Restore gain to what it was before being muted
+ self.masterGain = _preMuteGain;
+ }
+}
+
+- (BOOL) enabled {
+ return enabled_;
+}
+
+- (void) setEnabled:(BOOL)enabledValue
+{
+ if (enabled_ == enabledValue) {
+ return;
+ }
+ enabled_ = enabledValue;
+ if (enabled_ == NO) {
+ [self stopAllSounds];
+ }
+}
+
+-(void) _lockSource:(int) sourceIndex lock:(BOOL) lock {
+ BOOL found = NO;
+ for (int i=0; i < _sourceGroupTotal && !found; i++) {
+ if (_sourceGroups[i].sourceStatuses) {
+ for (int j=0; j < _sourceGroups[i].totalSources && !found; j++) {
+ //First bit is used to indicate whether source is locked, index is shifted back 1 bit
+ if((_sourceGroups[i].sourceStatuses[j] >> 1)==sourceIndex) {
+ if (lock) {
+ //Set first bit to lock this source
+ _sourceGroups[i].sourceStatuses[j] |= 1;
+ } else {
+ //Unset first bit to unlock this source
+ _sourceGroups[i].sourceStatuses[j] &= ~1;
+ }
+ found = YES;
+ }
+ }
+ }
+ }
+}
+
+-(int) _getSourceIndexForSourceGroup:(int)sourceGroupId
+{
+ //Ensure source group id is valid to prevent memory corruption
+ if (sourceGroupId < 0 || sourceGroupId >= _sourceGroupTotal) {
+ CDLOG(@"Denshion::CDSoundEngine invalid source group id %i",sourceGroupId);
+ return CD_NO_SOURCE;
+ }
+
+ int sourceIndex = -1;//Using -1 to indicate no source found
+ BOOL complete = NO;
+ ALint sourceState = 0;
+ sourceGroup *thisSourceGroup = &_sourceGroups[sourceGroupId];
+ thisSourceGroup->currentIndex = thisSourceGroup->startIndex;
+ while (!complete) {
+ //Iterate over sources looking for one that is not locked, first bit indicates if source is locked
+ if ((thisSourceGroup->sourceStatuses[thisSourceGroup->currentIndex] & 1) == 0) {
+ //This source is not locked
+ sourceIndex = thisSourceGroup->sourceStatuses[thisSourceGroup->currentIndex] >> 1;//shift back to get the index
+ if (thisSourceGroup->nonInterruptible) {
+ //Check if this source is playing, if so it can't be interrupted
+ alGetSourcei(_sources[sourceIndex].sourceId, AL_SOURCE_STATE, &sourceState);
+ if (sourceState != AL_PLAYING) {
+ //complete = YES;
+ //Set start index so next search starts at the next position
+ thisSourceGroup->startIndex = thisSourceGroup->currentIndex + 1;
+ break;
+ } else {
+ sourceIndex = -1;//The source index was no good because the source was playing
+ }
+ } else {
+ //complete = YES;
+ //Set start index so next search starts at the next position
+ thisSourceGroup->startIndex = thisSourceGroup->currentIndex + 1;
+ break;
+ }
+ }
+ thisSourceGroup->currentIndex++;
+ if (thisSourceGroup->currentIndex >= thisSourceGroup->totalSources) {
+ //Reset to the beginning
+ thisSourceGroup->currentIndex = 0;
+ }
+ if (thisSourceGroup->currentIndex == thisSourceGroup->startIndex) {
+ //We have looped around and got back to the start
+ complete = YES;
+ }
+ }
+
+ //Reset start index to beginning if beyond bounds
+ if (thisSourceGroup->startIndex >= thisSourceGroup->totalSources) {
+ thisSourceGroup->startIndex = 0;
+ }
+
+ if (sourceIndex >= 0) {
+ return sourceIndex;
+ } else {
+ return CD_NO_SOURCE;
+ }
+
+}
+
+/**
+ * Play a sound.
+ * @param soundId the id of the sound to play (buffer id).
+ * @param SourceGroupId the source group that will be used to play the sound.
+ * @param pitch pitch multiplier. e.g 1.0 is unaltered, 0.5 is 1 octave lower.
+ * @param pan stereo position. -1 is fully left, 0 is centre and 1 is fully right.
+ * @param gain gain multiplier. e.g. 1.0 is unaltered, 0.5 is half the gain
+ * @param loop should the sound be looped or one shot.
+ * @return the id of the source being used to play the sound or CD_MUTE if the sound engine is muted or non functioning
+ * or CD_NO_SOURCE if a problem occurs setting up the source
+ *
+ */
+- (ALuint)playSound:(int) soundId sourceGroupId:(int)sourceGroupId pitch:(float) pitch pan:(float) pan gain:(float) gain loop:(BOOL) loop {
+
+#ifdef CD_DEBUG
+ //Sanity check parameters - only in DEBUG
+ NSAssert(soundId >= 0, @"soundId can not be negative");
+ NSAssert(soundId < bufferTotal, @"soundId exceeds limit");
+ NSAssert(sourceGroupId >= 0, @"sourceGroupId can not be negative");
+ NSAssert(sourceGroupId < _sourceGroupTotal, @"sourceGroupId exceeds limit");
+ NSAssert(pitch > 0, @"pitch must be greater than zero");
+ NSAssert(pan >= -1 && pan <= 1, @"pan must be between -1 and 1");
+ NSAssert(gain >= 0, @"gain can not be negative");
+#endif
+ //If mute or initialisation has failed or buffer is not loaded then do nothing
+ if (!enabled_ || !functioning_ || _buffers[soundId].bufferState != CD_BS_LOADED || _sourceGroups[sourceGroupId].enabled) {
+#ifdef CD_DEBUG
+ if (!functioning_) {
+ CDLOGINFO(@"Denshion::CDSoundEngine - sound playback aborted because sound engine is not functioning");
+ } else if (_buffers[soundId].bufferState != CD_BS_LOADED) {
+ CDLOGINFO(@"Denshion::CDSoundEngine - sound playback aborted because buffer %i is not loaded", soundId);
+ }
+#endif
+ return CD_MUTE;
+ }
+
+ int sourceIndex = [self _getSourceIndexForSourceGroup:sourceGroupId];//This method ensures sourceIndex is valid
+
+ if (sourceIndex != CD_NO_SOURCE) {
+ ALint state;
+ ALuint source = _sources[sourceIndex].sourceId;
+ ALuint buffer = _buffers[soundId].bufferId;
+ alGetError();//Clear the error code
+ alGetSourcei(source, AL_SOURCE_STATE, &state);
+ if (state == AL_PLAYING) {
+ alSourceStop(source);
+ }
+ alSourcei(source, AL_BUFFER, buffer);//Attach to sound
+ alSourcef(source, AL_PITCH, pitch);//Set pitch
+ alSourcei(source, AL_LOOPING, loop);//Set looping
+ alSourcef(source, AL_GAIN, gain);//Set gain/volume
+ float sourcePosAL[] = {pan, 0.0f, 0.0f};//Set position - just using left and right panning
+ alSourcefv(source, AL_POSITION, sourcePosAL);
+ alGetError();//Clear the error code
+ alSourcePlay(source);
+ if((lastErrorCode_ = alGetError()) == AL_NO_ERROR) {
+ //Everything was okay
+ _sources[sourceIndex].attachedBufferId = buffer;
+ return source;
+ } else {
+ if (alcGetCurrentContext() == NULL) {
+ CDLOGINFO(@"Denshion::CDSoundEngine - posting bad OpenAL context message");
+ [[NSNotificationCenter defaultCenter] postNotificationName:kCDN_BadAlContext object:nil];
+ }
+ return CD_NO_SOURCE;
+ }
+ } else {
+ return CD_NO_SOURCE;
+ }
+}
+
+-(BOOL) _soundSourceAttachToBuffer:(CDSoundSource*) soundSource soundId:(int) soundId {
+ //Attach the source to the buffer
+ ALint state;
+ ALuint source = soundSource->_sourceId;
+ ALuint buffer = _buffers[soundId].bufferId;
+ alGetSourcei(source, AL_SOURCE_STATE, &state);
+ if (state == AL_PLAYING) {
+ alSourceStop(source);
+ }
+ alGetError();//Clear the error code
+ alSourcei(source, AL_BUFFER, buffer);//Attach to sound data
+ if((lastErrorCode_ = alGetError()) == AL_NO_ERROR) {
+ _sources[soundSource->_sourceIndex].attachedBufferId = buffer;
+ //_sourceBufferAttachments[soundSource->_sourceIndex] = buffer;//Keep track of which
+ soundSource->_soundId = soundId;
+ return YES;
+ } else {
+ return NO;
+ }
+}
+
+/**
+ * Get a sound source for the specified sound in the specified source group
+ */
+-(CDSoundSource *) soundSourceForSound:(int) soundId sourceGroupId:(int) sourceGroupId
+{
+ if (!functioning_) {
+ return nil;
+ }
+ //Check if a source is available
+ int sourceIndex = [self _getSourceIndexForSourceGroup:sourceGroupId];
+ if (sourceIndex != CD_NO_SOURCE) {
+ CDSoundSource *result = [[CDSoundSource alloc] init:_sources[sourceIndex].sourceId sourceIndex:sourceIndex soundEngine:self];
+ [self _lockSource:sourceIndex lock:YES];
+ //Try to attach to the buffer
+ if ([self _soundSourceAttachToBuffer:result soundId:soundId]) {
+ //Set to a known state
+ result.pitch = 1.0f;
+ result.pan = 0.0f;
+ result.gain = 1.0f;
+ result.looping = NO;
+ return [result autorelease];
+ } else {
+ //Release the sound source we just created, this will also unlock the source
+ [result release];
+ return nil;
+ }
+ } else {
+ //No available source within that source group
+ return nil;
+ }
+}
+
+-(void) _soundSourcePreRelease:(CDSoundSource *) soundSource {
+ CDLOGINFO(@"Denshion::CDSoundEngine _soundSourcePreRelease %i",soundSource->_sourceIndex);
+ //Unlock the sound source's source
+ [self _lockSource:soundSource->_sourceIndex lock:NO];
+}
+
+/**
+ * Stop all sounds playing within a source group
+ */
+- (void) stopSourceGroup:(int) sourceGroupId {
+
+ if (!functioning_ || sourceGroupId >= _sourceGroupTotal || sourceGroupId < 0) {
+ return;
+ }
+ int sourceCount = _sourceGroups[sourceGroupId].totalSources;
+ for (int i=0; i < sourceCount; i++) {
+ int sourceIndex = _sourceGroups[sourceGroupId].sourceStatuses[i] >> 1;
+ alSourceStop(_sources[sourceIndex].sourceId);
+ }
+ alGetError();//Clear error in case we stopped any sounds that couldn't be stopped
+}
+
+/**
+ * Stop a sound playing.
+ * @param sourceId an OpenAL source identifier i.e. the return value of playSound
+ */
+- (void)stopSound:(ALuint) sourceId {
+ if (!functioning_) {
+ return;
+ }
+ alSourceStop(sourceId);
+ alGetError();//Clear error in case we stopped any sounds that couldn't be stopped
+}
+
+- (void) stopAllSounds {
+ for (int i=0; i < sourceTotal_; i++) {
+ alSourceStop(_sources[i].sourceId);
+ }
+ alGetError();//Clear error in case we stopped any sounds that couldn't be stopped
+}
+
+/**
+ * Set a source group as non interruptible. Default is that source groups are interruptible.
+ * Non interruptible means that if a request to play a sound is made for a source group and there are
+ * no free sources available then the play request will be ignored and CD_NO_SOURCE will be returned.
+ */
+- (void) setSourceGroupNonInterruptible:(int) sourceGroupId isNonInterruptible:(BOOL) isNonInterruptible {
+ //Ensure source group id is valid to prevent memory corruption
+ if (sourceGroupId < 0 || sourceGroupId >= _sourceGroupTotal) {
+ CDLOG(@"Denshion::CDSoundEngine setSourceGroupNonInterruptible invalid source group id %i",sourceGroupId);
+ return;
+ }
+
+ if (isNonInterruptible) {
+ _sourceGroups[sourceGroupId].nonInterruptible = true;
+ } else {
+ _sourceGroups[sourceGroupId].nonInterruptible = false;
+ }
+}
+
+/**
+ * Set the mute property for a source group. If mute is turned on any sounds in that source group
+ * will be stopped and further sounds in that source group will play. However, turning mute off
+ * will not restart any sounds that were playing when mute was turned on. Also the mute setting
+ * for the sound engine must be taken into account. If the sound engine is mute no sounds will play
+ * no matter what the source group mute setting is.
+ */
+- (void) setSourceGroupEnabled:(int) sourceGroupId enabled:(BOOL) enabled {
+ //Ensure source group id is valid to prevent memory corruption
+ if (sourceGroupId < 0 || sourceGroupId >= _sourceGroupTotal) {
+ CDLOG(@"Denshion::CDSoundEngine setSourceGroupEnabled invalid source group id %i",sourceGroupId);
+ return;
+ }
+
+ if (enabled) {
+ _sourceGroups[sourceGroupId].enabled = true;
+ [self stopSourceGroup:sourceGroupId];
+ } else {
+ _sourceGroups[sourceGroupId].enabled = false;
+ }
+}
+
+/**
+ * Return the mute property for the source group identified by sourceGroupId
+ */
+- (BOOL) sourceGroupEnabled:(int) sourceGroupId {
+ return _sourceGroups[sourceGroupId].enabled;
+}
+
+-(ALCcontext *) openALContext {
+ return context;
+}
+
+- (void) _dumpSourceGroupsInfo {
+#ifdef CD_DEBUG
+ CDLOGINFO(@"-------------- source Group Info --------------");
+ for (int i=0; i < _sourceGroupTotal; i++) {
+ CDLOGINFO(@"Group: %i start:%i total:%i",i,_sourceGroups[i].startIndex, _sourceGroups[i].totalSources);
+ CDLOGINFO(@"----- mute:%i nonInterruptible:%i",_sourceGroups[i].enabled, _sourceGroups[i].nonInterruptible);
+ CDLOGINFO(@"----- Source statuses ----");
+ for (int j=0; j < _sourceGroups[i].totalSources; j++) {
+ CDLOGINFO(@"Source status:%i index=%i locked=%i",j,_sourceGroups[i].sourceStatuses[j] >> 1, _sourceGroups[i].sourceStatuses[j] & 1);
+ }
+ }
+#endif
+}
+
+@end
+
+///////////////////////////////////////////////////////////////////////////////////////
+@implementation CDSoundSource
+
+@synthesize lastError;
+
+//Macro for handling the al error code
+#define CDSOUNDSOURCE_UPDATE_LAST_ERROR (lastError = alGetError())
+#define CDSOUNDSOURCE_ERROR_HANDLER ( CDSOUNDSOURCE_UPDATE_LAST_ERROR == AL_NO_ERROR)
+
+-(id)init:(ALuint) theSourceId sourceIndex:(int) index soundEngine:(CDSoundEngine*) engine {
+ if ((self = [super init])) {
+ _sourceId = theSourceId;
+ _engine = engine;
+ _sourceIndex = index;
+ enabled_ = YES;
+ mute_ = NO;
+ _preMuteGain = self.gain;
+ }
+ return self;
+}
+
+-(void) dealloc
+{
+ CDLOGINFO(@"Denshion::CDSoundSource deallocated %i",self->_sourceIndex);
+
+ //Notify sound engine we are about to release
+ [_engine _soundSourcePreRelease:self];
+ [super dealloc];
+}
+
+- (void) setPitch:(float) newPitchValue {
+ alSourcef(_sourceId, AL_PITCH, newPitchValue);
+ CDSOUNDSOURCE_UPDATE_LAST_ERROR;
+}
+
+- (void) setGain:(float) newGainValue {
+ if (!mute_) {
+ alSourcef(_sourceId, AL_GAIN, newGainValue);
+ } else {
+ _preMuteGain = newGainValue;
+ }
+ CDSOUNDSOURCE_UPDATE_LAST_ERROR;
+}
+
+- (void) setPan:(float) newPanValue {
+ float sourcePosAL[] = {newPanValue, 0.0f, 0.0f};//Set position - just using left and right panning
+ alSourcefv(_sourceId, AL_POSITION, sourcePosAL);
+ CDSOUNDSOURCE_UPDATE_LAST_ERROR;
+
+}
+
+- (void) setLooping:(BOOL) newLoopingValue {
+ alSourcei(_sourceId, AL_LOOPING, newLoopingValue);
+ CDSOUNDSOURCE_UPDATE_LAST_ERROR;
+
+}
+
+- (BOOL) isPlaying {
+ ALint state;
+ alGetSourcei(_sourceId, AL_SOURCE_STATE, &state);
+ CDSOUNDSOURCE_UPDATE_LAST_ERROR;
+ return (state == AL_PLAYING);
+}
+
+- (float) pitch {
+ ALfloat pitchVal;
+ alGetSourcef(_sourceId, AL_PITCH, &pitchVal);
+ CDSOUNDSOURCE_UPDATE_LAST_ERROR;
+ return pitchVal;
+}
+
+- (float) pan {
+ ALfloat sourcePosAL[] = {0.0f,0.0f,0.0f};
+ alGetSourcefv(_sourceId, AL_POSITION, sourcePosAL);
+ CDSOUNDSOURCE_UPDATE_LAST_ERROR;
+ return sourcePosAL[0];
+}
+
+- (float) gain {
+ if (!mute_) {
+ ALfloat val;
+ alGetSourcef(_sourceId, AL_GAIN, &val);
+ CDSOUNDSOURCE_UPDATE_LAST_ERROR;
+ return val;
+ } else {
+ return _preMuteGain;
+ }
+}
+
+- (BOOL) looping {
+ ALfloat val;
+ alGetSourcef(_sourceId, AL_LOOPING, &val);
+ CDSOUNDSOURCE_UPDATE_LAST_ERROR;
+ return val;
+}
+
+-(BOOL) stop {
+ alSourceStop(_sourceId);
+ return CDSOUNDSOURCE_ERROR_HANDLER;
+}
+
+-(BOOL) play {
+ if (enabled_) {
+ alSourcePlay(_sourceId);
+ CDSOUNDSOURCE_UPDATE_LAST_ERROR;
+ if (lastError != AL_NO_ERROR) {
+ if (alcGetCurrentContext() == NULL) {
+ CDLOGINFO(@"Denshion::CDSoundSource - posting bad OpenAL context message");
+ [[NSNotificationCenter defaultCenter] postNotificationName:kCDN_BadAlContext object:nil];
+ }
+ return NO;
+ } else {
+ return YES;
+ }
+ } else {
+ return NO;
+ }
+}
+
+-(BOOL) pause {
+ alSourcePause(_sourceId);
+ return CDSOUNDSOURCE_ERROR_HANDLER;
+}
+
+-(BOOL) rewind {
+ alSourceRewind(_sourceId);
+ return CDSOUNDSOURCE_ERROR_HANDLER;
+}
+
+-(void) setSoundId:(int) soundId {
+ [_engine _soundSourceAttachToBuffer:self soundId:soundId];
+}
+
+-(int) soundId {
+ return _soundId;
+}
+
+-(float) durationInSeconds {
+ return [_engine bufferDurationInSeconds:_soundId];
+}
+
+#pragma mark CDSoundSource AudioInterrupt protocol
+- (BOOL) mute {
+ return mute_;
+}
+
+/**
+ * Setting mute silences all sounds but playing sounds continue to advance playback
+ */
+- (void) setMute:(BOOL) newMuteValue {
+
+ if (newMuteValue == mute_) {
+ return;
+ }
+
+ if (newMuteValue) {
+ //Remember what the gain was
+ _preMuteGain = self.gain;
+ self.gain = 0.0f;
+ mute_ = newMuteValue;//Make sure this is done after setting the gain property as the setter behaves differently depending on mute value
+ } else {
+ //Restore gain to what it was before being muted
+ mute_ = newMuteValue;
+ self.gain = _preMuteGain;
+ }
+}
+
+- (BOOL) enabled {
+ return enabled_;
+}
+
+- (void) setEnabled:(BOOL)enabledValue
+{
+ if (enabled_ == enabledValue) {
+ return;
+ }
+ enabled_ = enabledValue;
+ if (enabled_ == NO) {
+ [self stop];
+ }
+}
+
+@end
+
+////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark CDAudioInterruptTargetGroup
+
+@implementation CDAudioInterruptTargetGroup
+
+-(id) init {
+ if ((self = [super init])) {
+ children_ = [[NSMutableArray alloc] initWithCapacity:32];
+ enabled_ = YES;
+ mute_ = NO;
+ }
+ return self;
+}
+
+-(void) addAudioInterruptTarget:(NSObject<CDAudioInterruptProtocol>*) interruptibleTarget {
+ //Synchronize child with group settings;
+ [interruptibleTarget setMute:mute_];
+ [interruptibleTarget setEnabled:enabled_];
+ [children_ addObject:interruptibleTarget];
+}
+
+-(void) removeAudioInterruptTarget:(NSObject<CDAudioInterruptProtocol>*) interruptibleTarget {
+ [children_ removeObjectIdenticalTo:interruptibleTarget];
+}
+
+- (BOOL) mute {
+ return mute_;
+}
+
+/**
+ * Setting mute silences all sounds but playing sounds continue to advance playback
+ */
+- (void) setMute:(BOOL) newMuteValue {
+
+ if (newMuteValue == mute_) {
+ return;
+ }
+
+ for (NSObject<CDAudioInterruptProtocol>* target in children_) {
+ [target setMute:newMuteValue];
+ }
+}
+
+- (BOOL) enabled {
+ return enabled_;
+}
+
+- (void) setEnabled:(BOOL)enabledValue
+{
+ if (enabledValue == enabled_) {
+ return;
+ }
+
+ for (NSObject<CDAudioInterruptProtocol>* target in children_) {
+ [target setEnabled:enabledValue];
+ }
+}
+
+@end
+
+
+
+////////////////////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark CDAsynchBufferLoader
+
+@implementation CDAsynchBufferLoader
+
+-(id) init:(NSArray *)loadRequests soundEngine:(CDSoundEngine *) theSoundEngine {
+ if ((self = [super init])) {
+ _loadRequests = loadRequests;
+ [_loadRequests retain];
+ _soundEngine = theSoundEngine;
+ [_soundEngine retain];
+ }
+ return self;
+}
+
+-(void) main {
+ CDLOGINFO(@"Denshion::CDAsynchBufferLoader - loading buffers");
+ [super main];
+ _soundEngine.asynchLoadProgress = 0.0f;
+
+ if ([_loadRequests count] > 0) {
+ float increment = 1.0f / [_loadRequests count];
+ //Iterate over load request and load
+ for (CDBufferLoadRequest *loadRequest in _loadRequests) {
+ [_soundEngine loadBuffer:loadRequest.soundId filePath:loadRequest.filePath];
+ _soundEngine.asynchLoadProgress += increment;
+ }
+ }
+
+ //Completed
+ _soundEngine.asynchLoadProgress = 1.0f;
+ [[NSNotificationCenter defaultCenter] postNotificationName:kCDN_AsynchLoadComplete object:nil];
+
+}
+
+-(void) dealloc {
+ [_loadRequests release];
+ [_soundEngine release];
+ [super dealloc];
+}
+
+@end
+
+
+///////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark CDBufferLoadRequest
+
+@implementation CDBufferLoadRequest
+
+@synthesize filePath, soundId;
+
+-(id) init:(int) theSoundId filePath:(const NSString *) theFilePath {
+ if ((self = [super init])) {
+ soundId = theSoundId;
+ filePath = [theFilePath copy];//TODO: is retain necessary or does copy set retain count
+ [filePath retain];
+ }
+ return self;
+}
+
+-(void) dealloc {
+ [filePath release];
+ [super dealloc];
+}
+
+@end
+
+///////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark CDFloatInterpolator
+
+@implementation CDFloatInterpolator
+@synthesize start,end,interpolationType;
+
+-(float) interpolate:(float) t {
+
+ if (t < 1.0f) {
+ switch (interpolationType) {
+ case kIT_Linear:
+ //Linear interpolation
+ return ((end - start) * t) + start;
+
+ case kIT_SCurve:
+ //Cubic s curve t^2 * (3 - 2t)
+ return ((float)(t * t * (3.0 - (2.0 * t))) * (end - start)) + start;
+
+ case kIT_Exponential:
+ //Formulas taken from EaseAction
+ if (end > start) {
+ //Fade in
+ float logDelta = (t==0) ? 0 : powf(2, 10 * (t/1 - 1)) - 1 * 0.001f;
+ return ((end - start) * logDelta) + start;
+ } else {
+ //Fade Out
+ float logDelta = (-powf(2, -10 * t/1) + 1);
+ return ((end - start) * logDelta) + start;
+ }
+ default:
+ return 0.0f;
+ }
+ } else {
+ return end;
+ }
+}
+
+-(id) init:(tCDInterpolationType) type startVal:(float) startVal endVal:(float) endVal {
+ if ((self = [super init])) {
+ start = startVal;
+ end = endVal;
+ interpolationType = type;
+ }
+ return self;
+}
+
+@end
+
+///////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark CDPropertyModifier
+
+@implementation CDPropertyModifier
+
+@synthesize stopTargetWhenComplete;
+
+-(id) init:(id) theTarget interpolationType:(tCDInterpolationType) type startVal:(float) startVal endVal:(float) endVal {
+ if ((self = [super init])) {
+ if (target) {
+ //Release the previous target if there is one
+ [target release];
+ }
+ target = theTarget;
+#if CD_DEBUG
+ //Check target is of the required type
+ if (![theTarget isMemberOfClass:[self _allowableType]] ) {
+ CDLOG(@"Denshion::CDPropertyModifier target is not of type %@",[self _allowableType]);
+ NSAssert([theTarget isKindOfClass:[CDSoundEngine class]], @"CDPropertyModifier target not of required type");
+ }
+#endif
+ [target retain];
+ startValue = startVal;
+ endValue = endVal;
+ if (interpolator) {
+ //Release previous interpolator if there is one
+ [interpolator release];
+ }
+ interpolator = [[CDFloatInterpolator alloc] init:type startVal:startVal endVal:endVal];
+ stopTargetWhenComplete = NO;
+ }
+ return self;
+}
+
+-(void) dealloc {
+ CDLOGINFO(@"Denshion::CDPropertyModifier deallocated %@",self);
+ [target release];
+ [interpolator release];
+ [super dealloc];
+}
+
+-(void) modify:(float) t {
+ if (t < 1.0) {
+ [self _setTargetProperty:[interpolator interpolate:t]];
+ } else {
+ //At the end
+ [self _setTargetProperty:endValue];
+ if (stopTargetWhenComplete) {
+ [self _stopTarget];
+ }
+ }
+}
+
+-(float) startValue {
+ return startValue;
+}
+
+-(void) setStartValue:(float) startVal
+{
+ startValue = startVal;
+ interpolator.start = startVal;
+}
+
+-(float) endValue {
+ return startValue;
+}
+
+-(void) setEndValue:(float) endVal
+{
+ endValue = endVal;
+ interpolator.end = endVal;
+}
+
+-(tCDInterpolationType) interpolationType {
+ return interpolator.interpolationType;
+}
+
+-(void) setInterpolationType:(tCDInterpolationType) interpolationType {
+ interpolator.interpolationType = interpolationType;
+}
+
+-(void) _setTargetProperty:(float) newVal {
+
+}
+
+-(float) _getTargetProperty {
+ return 0.0f;
+}
+
+-(void) _stopTarget {
+
+}
+
+-(Class) _allowableType {
+ return [NSObject class];
+}
+@end
+
+///////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark CDSoundSourceFader
+
+@implementation CDSoundSourceFader
+
+-(void) _setTargetProperty:(float) newVal {
+ ((CDSoundSource*)target).gain = newVal;
+}
+
+-(float) _getTargetProperty {
+ return ((CDSoundSource*)target).gain;
+}
+
+-(void) _stopTarget {
+ [((CDSoundSource*)target) stop];
+}
+
+-(Class) _allowableType {
+ return [CDSoundSource class];
+}
+
+@end
+
+///////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark CDSoundSourcePanner
+
+@implementation CDSoundSourcePanner
+
+-(void) _setTargetProperty:(float) newVal {
+ ((CDSoundSource*)target).pan = newVal;
+}
+
+-(float) _getTargetProperty {
+ return ((CDSoundSource*)target).pan;
+}
+
+-(void) _stopTarget {
+ [((CDSoundSource*)target) stop];
+}
+
+-(Class) _allowableType {
+ return [CDSoundSource class];
+}
+
+@end
+
+///////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark CDSoundSourcePitchBender
+
+@implementation CDSoundSourcePitchBender
+
+-(void) _setTargetProperty:(float) newVal {
+ ((CDSoundSource*)target).pitch = newVal;
+}
+
+-(float) _getTargetProperty {
+ return ((CDSoundSource*)target).pitch;
+}
+
+-(void) _stopTarget {
+ [((CDSoundSource*)target) stop];
+}
+
+-(Class) _allowableType {
+ return [CDSoundSource class];
+}
+
+@end
+
+///////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark CDSoundEngineFader
+
+@implementation CDSoundEngineFader
+
+-(void) _setTargetProperty:(float) newVal {
+ ((CDSoundEngine*)target).masterGain = newVal;
+}
+
+-(float) _getTargetProperty {
+ return ((CDSoundEngine*)target).masterGain;
+}
+
+-(void) _stopTarget {
+ [((CDSoundEngine*)target) stopAllSounds];
+}
+
+-(Class) _allowableType {
+ return [CDSoundEngine class];
+}
+
+@end
+
+
--- /dev/null
+/*
+ Copyright (c) 2010 Steve Oldmeadow
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+
+ $Id$
+ */
+
+
+#import "CDAudioManager.h"
+
+/**
+ A wrapper to the CDAudioManager object.
+ This is recommended for basic audio requirements. If you just want to play some sound fx
+ and some background music and have no interest in learning the lower level workings then
+ this is the interface to use.
+
+ Requirements:
+ - Firmware: OS 2.2 or greater
+ - Files: SimpleAudioEngine.*, CocosDenshion.*
+ - Frameworks: OpenAL, AudioToolbox, AVFoundation
+ @since v0.8
+ */
+@interface SimpleAudioEngine : NSObject <CDAudioInterruptProtocol> {
+
+ BOOL mute_;
+ BOOL enabled_;
+}
+
+/** Background music volume. Range is 0.0f to 1.0f. This will only have an effect if willPlayBackgroundMusic returns YES */
+@property (readwrite) float backgroundMusicVolume;
+/** Effects volume. Range is 0.0f to 1.0f */
+@property (readwrite) float effectsVolume;
+/** If NO it indicates background music will not be played either because no background music is loaded or the audio session does not permit it.*/
+@property (readonly) BOOL willPlayBackgroundMusic;
+
+/** returns the shared instance of the SimpleAudioEngine object */
++ (SimpleAudioEngine*) sharedEngine;
+
+/** Preloads a music file so it will be ready to play as background music */
+-(void) preloadBackgroundMusic:(NSString*) filePath;
+
+/** plays background music in a loop*/
+-(void) playBackgroundMusic:(NSString*) filePath;
+/** plays background music, if loop is true the music will repeat otherwise it will be played once */
+-(void) playBackgroundMusic:(NSString*) filePath loop:(BOOL) loop;
+/** stops playing background music */
+-(void) stopBackgroundMusic;
+/** pauses the background music */
+-(void) pauseBackgroundMusic;
+/** resume background music that has been paused */
+-(void) resumeBackgroundMusic;
+/** rewind the background music */
+-(void) rewindBackgroundMusic;
+/** returns whether or not the background music is playing */
+-(BOOL) isBackgroundMusicPlaying;
+
+/** plays an audio effect with a file path*/
+-(ALuint) playEffect:(NSString*) filePath;
+/** stop a sound that is playing, note you must pass in the soundId that is returned when you started playing the sound with playEffect */
+-(void) stopEffect:(ALuint) soundId;
+/** plays an audio effect with a file path, pitch, pan and gain */
+-(ALuint) playEffect:(NSString*) filePath pitch:(Float32) pitch pan:(Float32) pan gain:(Float32) gain;
+/** preloads an audio effect */
+-(void) preloadEffect:(NSString*) filePath;
+/** unloads an audio effect from memory */
+-(void) unloadEffect:(NSString*) filePath;
+/** Gets a CDSoundSource object set up to play the specified file. */
+-(CDSoundSource *) soundSourceForFile:(NSString*) filePath;
+
+/** Shuts down the shared audio engine instance so that it can be reinitialised */
++(void) end;
+
+@end
--- /dev/null
+/*
+ Copyright (c) 2010 Steve Oldmeadow
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+
+ $Id$
+ */
+
+#import "SimpleAudioEngine.h"
+
+@implementation SimpleAudioEngine
+
+static SimpleAudioEngine *sharedEngine = nil;
+static CDSoundEngine* soundEngine = nil;
+static CDAudioManager *am = nil;
+static CDBufferManager *bufferManager = nil;
+
+// Init
++ (SimpleAudioEngine *) sharedEngine
+{
+ @synchronized(self) {
+ if (!sharedEngine)
+ sharedEngine = [[SimpleAudioEngine alloc] init];
+ }
+ return sharedEngine;
+}
+
++ (id) alloc
+{
+ @synchronized(self) {
+ NSAssert(sharedEngine == nil, @"Attempted to allocate a second instance of a singleton.");
+ return [super alloc];
+ }
+ return nil;
+}
+
+-(id) init
+{
+ if((self=[super init])) {
+ am = [CDAudioManager sharedManager];
+ soundEngine = am.soundEngine;
+ bufferManager = [[CDBufferManager alloc] initWithEngine:soundEngine];
+ mute_ = NO;
+ enabled_ = YES;
+ }
+ return self;
+}
+
+// Memory
+- (void) dealloc
+{
+ am = nil;
+ soundEngine = nil;
+ bufferManager = nil;
+ [super dealloc];
+}
+
++(void) end
+{
+ am = nil;
+ [CDAudioManager end];
+ [bufferManager release];
+ [sharedEngine release];
+ sharedEngine = nil;
+}
+
+#pragma mark SimpleAudioEngine - background music
+
+-(void) preloadBackgroundMusic:(NSString*) filePath {
+ [am preloadBackgroundMusic:filePath];
+}
+
+-(void) playBackgroundMusic:(NSString*) filePath
+{
+ [am playBackgroundMusic:filePath loop:TRUE];
+}
+
+-(void) playBackgroundMusic:(NSString*) filePath loop:(BOOL) loop
+{
+ [am playBackgroundMusic:filePath loop:loop];
+}
+
+-(void) stopBackgroundMusic
+{
+ [am stopBackgroundMusic];
+}
+
+-(void) pauseBackgroundMusic {
+ [am pauseBackgroundMusic];
+}
+
+-(void) resumeBackgroundMusic {
+ [am resumeBackgroundMusic];
+}
+
+-(void) rewindBackgroundMusic {
+ [am rewindBackgroundMusic];
+}
+
+-(BOOL) isBackgroundMusicPlaying {
+ return [am isBackgroundMusicPlaying];
+}
+
+-(BOOL) willPlayBackgroundMusic {
+ return [am willPlayBackgroundMusic];
+}
+
+#pragma mark SimpleAudioEngine - sound effects
+
+-(ALuint) playEffect:(NSString*) filePath
+{
+ return [self playEffect:filePath pitch:1.0f pan:0.0f gain:1.0f];
+}
+
+-(ALuint) playEffect:(NSString*) filePath pitch:(Float32) pitch pan:(Float32) pan gain:(Float32) gain
+{
+ int soundId = [bufferManager bufferForFile:filePath create:YES];
+ if (soundId != kCDNoBuffer) {
+ return [soundEngine playSound:soundId sourceGroupId:0 pitch:pitch pan:pan gain:gain loop:false];
+ } else {
+ return CD_MUTE;
+ }
+}
+
+-(void) stopEffect:(ALuint) soundId {
+ [soundEngine stopSound:soundId];
+}
+
+-(void) preloadEffect:(NSString*) filePath
+{
+ int soundId = [bufferManager bufferForFile:filePath create:YES];
+ if (soundId == kCDNoBuffer) {
+ CDLOG(@"Denshion::SimpleAudioEngine sound failed to preload %@",filePath);
+ }
+}
+
+-(void) unloadEffect:(NSString*) filePath
+{
+ CDLOGINFO(@"Denshion::SimpleAudioEngine unloadedEffect %@",filePath);
+ [bufferManager releaseBufferForFile:filePath];
+}
+
+#pragma mark Audio Interrupt Protocol
+-(BOOL) mute
+{
+ return mute_;
+}
+
+-(void) setMute:(BOOL) muteValue
+{
+ if (mute_ != muteValue) {
+ mute_ = muteValue;
+ am.mute = mute_;
+ }
+}
+
+-(BOOL) enabled
+{
+ return enabled_;
+}
+
+-(void) setEnabled:(BOOL) enabledValue
+{
+ if (enabled_ != enabledValue) {
+ enabled_ = enabledValue;
+ am.enabled = enabled_;
+ }
+}
+
+
+#pragma mark SimpleAudioEngine - BackgroundMusicVolume
+-(float) backgroundMusicVolume
+{
+ return am.backgroundMusic.volume;
+}
+
+-(void) setBackgroundMusicVolume:(float) volume
+{
+ am.backgroundMusic.volume = volume;
+}
+
+#pragma mark SimpleAudioEngine - EffectsVolume
+-(float) effectsVolume
+{
+ return am.soundEngine.masterGain;
+}
+
+-(void) setEffectsVolume:(float) volume
+{
+ am.soundEngine.masterGain = volume;
+}
+
+-(CDSoundSource *) soundSourceForFile:(NSString*) filePath {
+ int soundId = [bufferManager bufferForFile:filePath create:YES];
+ if (soundId != kCDNoBuffer) {
+ CDSoundSource *result = [soundEngine soundSourceForSound:soundId sourceGroupId:0];
+ CDLOGINFO(@"Denshion::SimpleAudioEngine sound source created for %@",filePath);
+ return result;
+ } else {
+ return nil;
+ }
+}
+
+@end
--- /dev/null
+//
+// FontLabel.h
+// FontLabel
+//
+// Created by Kevin Ballard on 5/8/09.
+// Copyright © 2009 Zynga Game Networks
+//
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+
+@class ZFont;
+@class ZAttributedString;
+
+@interface FontLabel : UILabel {
+ void *reserved; // works around a bug in UILabel
+ ZFont *zFont;
+ ZAttributedString *zAttributedText;
+}
+@property (nonatomic, setter=setCGFont:) CGFontRef cgFont __AVAILABILITY_INTERNAL_DEPRECATED;
+@property (nonatomic, assign) CGFloat pointSize __AVAILABILITY_INTERNAL_DEPRECATED;
+@property (nonatomic, retain, setter=setZFont:) ZFont *zFont;
+// if attributedText is nil, fall back on using the inherited UILabel properties
+// if attributedText is non-nil, the font/text/textColor
+// in addition, adjustsFontSizeToFitWidth does not work with attributed text
+@property (nonatomic, copy) ZAttributedString *zAttributedText;
+// -initWithFrame:fontName:pointSize: uses FontManager to look up the font name
+- (id)initWithFrame:(CGRect)frame fontName:(NSString *)fontName pointSize:(CGFloat)pointSize;
+- (id)initWithFrame:(CGRect)frame zFont:(ZFont *)font;
+- (id)initWithFrame:(CGRect)frame font:(CGFontRef)font pointSize:(CGFloat)pointSize __AVAILABILITY_INTERNAL_DEPRECATED;
+@end
--- /dev/null
+//
+// FontLabel.m
+// FontLabel
+//
+// Created by Kevin Ballard on 5/8/09.
+// Copyright © 2009 Zynga Game Networks
+//
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "FontLabel.h"
+#import "FontManager.h"
+#import "FontLabelStringDrawing.h"
+#import "ZFont.h"
+
+@interface ZFont (ZFontPrivate)
+@property (nonatomic, readonly) CGFloat ratio;
+@end
+
+@implementation FontLabel
+@synthesize zFont;
+@synthesize zAttributedText;
+
+- (id)initWithFrame:(CGRect)frame fontName:(NSString *)fontName pointSize:(CGFloat)pointSize {
+ return [self initWithFrame:frame zFont:[[FontManager sharedManager] zFontWithName:fontName pointSize:pointSize]];
+}
+
+- (id)initWithFrame:(CGRect)frame zFont:(ZFont *)font {
+ if ((self = [super initWithFrame:frame])) {
+ zFont = [font retain];
+ }
+ return self;
+}
+
+- (id)initWithFrame:(CGRect)frame font:(CGFontRef)font pointSize:(CGFloat)pointSize {
+ return [self initWithFrame:frame zFont:[ZFont fontWithCGFont:font size:pointSize]];
+}
+
+- (CGFontRef)cgFont {
+ return self.zFont.cgFont;
+}
+
+- (void)setCGFont:(CGFontRef)font {
+ if (self.zFont.cgFont != font) {
+ self.zFont = [ZFont fontWithCGFont:font size:self.zFont.pointSize];
+ }
+}
+
+- (CGFloat)pointSize {
+ return self.zFont.pointSize;
+}
+
+- (void)setPointSize:(CGFloat)pointSize {
+ if (self.zFont.pointSize != pointSize) {
+ self.zFont = [ZFont fontWithCGFont:self.zFont.cgFont size:pointSize];
+ }
+}
+
+- (void)setZAttributedText:(ZAttributedString *)attStr {
+ if (zAttributedText != attStr) {
+ [zAttributedText release];
+ zAttributedText = [attStr copy];
+ [self setNeedsDisplay];
+ }
+}
+
+- (void)drawTextInRect:(CGRect)rect {
+ if (self.zFont == NULL && self.zAttributedText == nil) {
+ [super drawTextInRect:rect];
+ return;
+ }
+
+ if (self.zAttributedText == nil) {
+ // this method is documented as setting the text color for us, but that doesn't appear to be the case
+ if (self.highlighted) {
+ [(self.highlightedTextColor ?: [UIColor whiteColor]) setFill];
+ } else {
+ [(self.textColor ?: [UIColor blackColor]) setFill];
+ }
+
+ ZFont *actualFont = self.zFont;
+ CGSize origSize = rect.size;
+ if (self.numberOfLines == 1) {
+ origSize.height = actualFont.leading;
+ CGPoint point = CGPointMake(rect.origin.x,
+ rect.origin.y + roundf(((rect.size.height - actualFont.leading) / 2.0f)));
+ CGSize size = [self.text sizeWithZFont:actualFont];
+ if (self.adjustsFontSizeToFitWidth && self.minimumFontSize < actualFont.pointSize) {
+ if (size.width > origSize.width) {
+ CGFloat desiredRatio = (origSize.width * actualFont.ratio) / size.width;
+ CGFloat desiredPointSize = desiredRatio * actualFont.pointSize / actualFont.ratio;
+ actualFont = [actualFont fontWithSize:MAX(MAX(desiredPointSize, self.minimumFontSize), 1.0f)];
+ size = [self.text sizeWithZFont:actualFont];
+ }
+ if (!CGSizeEqualToSize(origSize, size)) {
+ switch (self.baselineAdjustment) {
+ case UIBaselineAdjustmentAlignCenters:
+ point.y += roundf((origSize.height - size.height) / 2.0f);
+ break;
+ case UIBaselineAdjustmentAlignBaselines:
+ point.y += (self.zFont.ascender - actualFont.ascender);
+ break;
+ case UIBaselineAdjustmentNone:
+ break;
+ }
+ }
+ }
+ size.width = MIN(size.width, origSize.width);
+ // adjust the point for alignment
+ switch (self.textAlignment) {
+ case UITextAlignmentLeft:
+ break;
+ case UITextAlignmentCenter:
+ point.x += (origSize.width - size.width) / 2.0f;
+ break;
+ case UITextAlignmentRight:
+ point.x += origSize.width - size.width;
+ break;
+ }
+ [self.text drawAtPoint:point forWidth:size.width withZFont:actualFont lineBreakMode:self.lineBreakMode];
+ } else {
+ CGSize size = [self.text sizeWithZFont:actualFont constrainedToSize:origSize lineBreakMode:self.lineBreakMode numberOfLines:self.numberOfLines];
+ CGPoint point = rect.origin;
+ point.y += roundf((rect.size.height - size.height) / 2.0f);
+ rect = (CGRect){point, CGSizeMake(rect.size.width, size.height)};
+ [self.text drawInRect:rect withZFont:actualFont lineBreakMode:self.lineBreakMode alignment:self.textAlignment numberOfLines:self.numberOfLines];
+ }
+ } else {
+ ZAttributedString *attStr = self.zAttributedText;
+ if (self.highlighted) {
+ // modify the string to change the base color
+ ZMutableAttributedString *mutStr = [[attStr mutableCopy] autorelease];
+ NSRange activeRange = NSMakeRange(0, attStr.length);
+ while (activeRange.length > 0) {
+ NSRange effective;
+ UIColor *color = [attStr attribute:ZForegroundColorAttributeName atIndex:activeRange.location
+ longestEffectiveRange:&effective inRange:activeRange];
+ if (color == nil) {
+ [mutStr addAttribute:ZForegroundColorAttributeName value:[UIColor whiteColor] range:effective];
+ }
+ activeRange.location += effective.length, activeRange.length -= effective.length;
+ }
+ attStr = mutStr;
+ }
+ CGSize size = [attStr sizeConstrainedToSize:rect.size lineBreakMode:self.lineBreakMode numberOfLines:self.numberOfLines];
+ CGPoint point = rect.origin;
+ point.y += roundf((rect.size.height - size.height) / 2.0f);
+ rect = (CGRect){point, CGSizeMake(rect.size.width, size.height)};
+ [attStr drawInRect:rect withLineBreakMode:self.lineBreakMode alignment:self.textAlignment numberOfLines:self.numberOfLines];
+ }
+}
+
+- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines {
+ if (self.zFont == NULL && self.zAttributedText == nil) {
+ return [super textRectForBounds:bounds limitedToNumberOfLines:numberOfLines];
+ }
+
+ if (numberOfLines == 1) {
+ // if numberOfLines == 1 we need to use the version that converts spaces
+ CGSize size;
+ if (self.zAttributedText == nil) {
+ size = [self.text sizeWithZFont:self.zFont];
+ } else {
+ size = [self.zAttributedText size];
+ }
+ bounds.size.width = MIN(bounds.size.width, size.width);
+ bounds.size.height = MIN(bounds.size.height, size.height);
+ } else {
+ if (numberOfLines > 0) bounds.size.height = MIN(bounds.size.height, self.zFont.leading * numberOfLines);
+ if (self.zAttributedText == nil) {
+ bounds.size = [self.text sizeWithZFont:self.zFont constrainedToSize:bounds.size lineBreakMode:self.lineBreakMode];
+ } else {
+ bounds.size = [self.zAttributedText sizeConstrainedToSize:bounds.size lineBreakMode:self.lineBreakMode];
+ }
+ }
+ return bounds;
+}
+
+- (void)dealloc {
+ [zFont release];
+ [zAttributedText release];
+ [super dealloc];
+}
+@end
--- /dev/null
+//
+// FontLabelStringDrawing.h
+// FontLabel
+//
+// Created by Kevin Ballard on 5/5/09.
+// Copyright © 2009 Zynga Game Networks
+//
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import <UIKit/UIKit.h>
+#import "ZAttributedString.h"
+
+@class ZFont;
+
+@interface NSString (FontLabelStringDrawing)
+// CGFontRef-based methods
+- (CGSize)sizeWithCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize __AVAILABILITY_INTERNAL_DEPRECATED;
+- (CGSize)sizeWithCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize constrainedToSize:(CGSize)size __AVAILABILITY_INTERNAL_DEPRECATED;
+- (CGSize)sizeWithCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize constrainedToSize:(CGSize)size
+ lineBreakMode:(UILineBreakMode)lineBreakMode __AVAILABILITY_INTERNAL_DEPRECATED;
+- (CGSize)drawAtPoint:(CGPoint)point withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize __AVAILABILITY_INTERNAL_DEPRECATED;
+- (CGSize)drawInRect:(CGRect)rect withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize __AVAILABILITY_INTERNAL_DEPRECATED;
+- (CGSize)drawInRect:(CGRect)rect withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize
+ lineBreakMode:(UILineBreakMode)lineBreakMode __AVAILABILITY_INTERNAL_DEPRECATED;
+- (CGSize)drawInRect:(CGRect)rect withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize
+ lineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment __AVAILABILITY_INTERNAL_DEPRECATED;
+
+// ZFont-based methods
+- (CGSize)sizeWithZFont:(ZFont *)font;
+- (CGSize)sizeWithZFont:(ZFont *)font constrainedToSize:(CGSize)size;
+- (CGSize)sizeWithZFont:(ZFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode;
+- (CGSize)sizeWithZFont:(ZFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode
+ numberOfLines:(NSUInteger)numberOfLines;
+- (CGSize)drawAtPoint:(CGPoint)point withZFont:(ZFont *)font;
+- (CGSize)drawAtPoint:(CGPoint)point forWidth:(CGFloat)width withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode;
+- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font;
+- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode;
+- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode
+ alignment:(UITextAlignment)alignment;
+- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode
+ alignment:(UITextAlignment)alignment numberOfLines:(NSUInteger)numberOfLines;
+@end
+
+@interface ZAttributedString (ZAttributedStringDrawing)
+- (CGSize)size;
+- (CGSize)sizeConstrainedToSize:(CGSize)size;
+- (CGSize)sizeConstrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode;
+- (CGSize)sizeConstrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode
+ numberOfLines:(NSUInteger)numberOfLines;
+- (CGSize)drawAtPoint:(CGPoint)point;
+- (CGSize)drawAtPoint:(CGPoint)point forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode;
+- (CGSize)drawInRect:(CGRect)rect;
+- (CGSize)drawInRect:(CGRect)rect withLineBreakMode:(UILineBreakMode)lineBreakMode;
+- (CGSize)drawInRect:(CGRect)rect withLineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment;
+- (CGSize)drawInRect:(CGRect)rect withLineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment
+ numberOfLines:(NSUInteger)numberOfLines;
+@end
--- /dev/null
+//
+// FontLabelStringDrawing.m
+// FontLabel
+//
+// Created by Kevin Ballard on 5/5/09.
+// Copyright © 2009 Zynga Game Networks
+//
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "FontLabelStringDrawing.h"
+#import "ZFont.h"
+#import "ZAttributedStringPrivate.h"
+
+@interface ZFont (ZFontPrivate)
+@property (nonatomic, readonly) CGFloat ratio;
+@end
+
+#define kUnicodeHighSurrogateStart 0xD800
+#define kUnicodeHighSurrogateEnd 0xDBFF
+#define kUnicodeHighSurrogateMask kUnicodeHighSurrogateStart
+#define kUnicodeLowSurrogateStart 0xDC00
+#define kUnicodeLowSurrogateEnd 0xDFFF
+#define kUnicodeLowSurrogateMask kUnicodeLowSurrogateStart
+#define kUnicodeSurrogateTypeMask 0xFC00
+#define UnicharIsHighSurrogate(c) ((c & kUnicodeSurrogateTypeMask) == kUnicodeHighSurrogateMask)
+#define UnicharIsLowSurrogate(c) ((c & kUnicodeSurrogateTypeMask) == kUnicodeLowSurrogateMask)
+#define ConvertSurrogatePairToUTF32(high, low) ((UInt32)((high - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000))
+
+typedef enum {
+ kFontTableFormat4 = 4,
+ kFontTableFormat12 = 12,
+} FontTableFormat;
+
+typedef struct fontTable {
+ NSUInteger retainCount;
+ CFDataRef cmapTable;
+ FontTableFormat format;
+ union {
+ struct {
+ UInt16 segCountX2;
+ UInt16 *endCodes;
+ UInt16 *startCodes;
+ UInt16 *idDeltas;
+ UInt16 *idRangeOffsets;
+ } format4;
+ struct {
+ UInt32 nGroups;
+ struct {
+ UInt32 startCharCode;
+ UInt32 endCharCode;
+ UInt32 startGlyphCode;
+ } *groups;
+ } format12;
+ } cmap;
+} fontTable;
+
+static FontTableFormat supportedFormats[] = { kFontTableFormat4, kFontTableFormat12 };
+static size_t supportedFormatsCount = sizeof(supportedFormats) / sizeof(FontTableFormat);
+
+static fontTable *newFontTable(CFDataRef cmapTable, FontTableFormat format) {
+ fontTable *table = (struct fontTable *)malloc(sizeof(struct fontTable));
+ table->retainCount = 1;
+ table->cmapTable = CFRetain(cmapTable);
+ table->format = format;
+ return table;
+}
+
+static fontTable *retainFontTable(fontTable *table) {
+ if (table != NULL) {
+ table->retainCount++;
+ }
+ return table;
+}
+
+static void releaseFontTable(fontTable *table) {
+ if (table != NULL) {
+ if (table->retainCount <= 1) {
+ CFRelease(table->cmapTable);
+ free(table);
+ } else {
+ table->retainCount--;
+ }
+ }
+}
+
+static const void *fontTableRetainCallback(CFAllocatorRef allocator, const void *value) {
+ return retainFontTable((fontTable *)value);
+}
+
+static void fontTableReleaseCallback(CFAllocatorRef allocator, const void *value) {
+ releaseFontTable((fontTable *)value);
+}
+
+static const CFDictionaryValueCallBacks kFontTableDictionaryValueCallBacks = {
+ .version = 0,
+ .retain = &fontTableRetainCallback,
+ .release = &fontTableReleaseCallback,
+ .copyDescription = NULL,
+ .equal = NULL
+};
+
+// read the cmap table from the font
+// we only know how to understand some of the table formats at the moment
+static fontTable *readFontTableFromCGFont(CGFontRef font) {
+ CFDataRef cmapTable = CGFontCopyTableForTag(font, 'cmap');
+ NSCAssert1(cmapTable != NULL, @"CGFontCopyTableForTag returned NULL for 'cmap' tag in font %@",
+ (font ? [(id)CFCopyDescription(font) autorelease] : @"(null)"));
+ const UInt8 * const bytes = CFDataGetBytePtr(cmapTable);
+ NSCAssert1(OSReadBigInt16(bytes, 0) == 0, @"cmap table for font %@ has bad version number",
+ (font ? [(id)CFCopyDescription(font) autorelease] : @"(null)"));
+ UInt16 numberOfSubtables = OSReadBigInt16(bytes, 2);
+ const UInt8 *unicodeSubtable = NULL;
+ //UInt16 unicodeSubtablePlatformID;
+ UInt16 unicodeSubtablePlatformSpecificID;
+ FontTableFormat unicodeSubtableFormat;
+ const UInt8 * const encodingSubtables = &bytes[4];
+ for (UInt16 i = 0; i < numberOfSubtables; i++) {
+ const UInt8 * const encodingSubtable = &encodingSubtables[8 * i];
+ UInt16 platformID = OSReadBigInt16(encodingSubtable, 0);
+ UInt16 platformSpecificID = OSReadBigInt16(encodingSubtable, 2);
+ // find the best subtable
+ // best is defined by a combination of encoding and format
+ // At the moment we only support format 4, so ignore all other format tables
+ // We prefer platformID == 0, but we will also accept Microsoft's unicode format
+ if (platformID == 0 || (platformID == 3 && platformSpecificID == 1)) {
+ BOOL preferred = NO;
+ if (unicodeSubtable == NULL) {
+ preferred = YES;
+ } else if (platformID == 0 && platformSpecificID > unicodeSubtablePlatformSpecificID) {
+ preferred = YES;
+ }
+ if (preferred) {
+ UInt32 offset = OSReadBigInt32(encodingSubtable, 4);
+ const UInt8 *subtable = &bytes[offset];
+ UInt16 format = OSReadBigInt16(subtable, 0);
+ for (size_t i = 0; i < supportedFormatsCount; i++) {
+ if (format == supportedFormats[i]) {
+ if (format >= 8) {
+ // the version is a fixed-point
+ UInt16 formatFrac = OSReadBigInt16(subtable, 2);
+ if (formatFrac != 0) {
+ // all the current formats with a Fixed version are always *.0
+ continue;
+ }
+ }
+ unicodeSubtable = subtable;
+ //unicodeSubtablePlatformID = platformID;
+ unicodeSubtablePlatformSpecificID = platformSpecificID;
+ unicodeSubtableFormat = format;
+ break;
+ }
+ }
+ }
+ }
+ }
+ fontTable *table = NULL;
+ if (unicodeSubtable != NULL) {
+ table = newFontTable(cmapTable, unicodeSubtableFormat);
+ switch (unicodeSubtableFormat) {
+ case kFontTableFormat4:
+ // subtable format 4
+ //UInt16 length = OSReadBigInt16(unicodeSubtable, 2);
+ //UInt16 language = OSReadBigInt16(unicodeSubtable, 4);
+ table->cmap.format4.segCountX2 = OSReadBigInt16(unicodeSubtable, 6);
+ //UInt16 searchRange = OSReadBigInt16(unicodeSubtable, 8);
+ //UInt16 entrySelector = OSReadBigInt16(unicodeSubtable, 10);
+ //UInt16 rangeShift = OSReadBigInt16(unicodeSubtable, 12);
+ table->cmap.format4.endCodes = (UInt16*)&unicodeSubtable[14];
+ table->cmap.format4.startCodes = (UInt16*)&((UInt8*)table->cmap.format4.endCodes)[table->cmap.format4.segCountX2+2];
+ table->cmap.format4.idDeltas = (UInt16*)&((UInt8*)table->cmap.format4.startCodes)[table->cmap.format4.segCountX2];
+ table->cmap.format4.idRangeOffsets = (UInt16*)&((UInt8*)table->cmap.format4.idDeltas)[table->cmap.format4.segCountX2];
+ //UInt16 *glyphIndexArray = &idRangeOffsets[segCountX2];
+ break;
+ case kFontTableFormat12:
+ table->cmap.format12.nGroups = OSReadBigInt32(unicodeSubtable, 12);
+ table->cmap.format12.groups = (void *)&unicodeSubtable[16];
+ break;
+ default:
+ releaseFontTable(table);
+ table = NULL;
+ }
+ }
+ CFRelease(cmapTable);
+ return table;
+}
+
+// outGlyphs must be at least size n
+static void mapCharactersToGlyphsInFont(const fontTable *table, unichar characters[], size_t charLen, CGGlyph outGlyphs[], size_t *outGlyphLen) {
+ if (table != NULL) {
+ NSUInteger j = 0;
+ switch (table->format) {
+ case kFontTableFormat4: {
+ for (NSUInteger i = 0; i < charLen; i++, j++) {
+ unichar c = characters[i];
+ UInt16 segOffset;
+ BOOL foundSegment = NO;
+ for (segOffset = 0; segOffset < table->cmap.format4.segCountX2; segOffset += 2) {
+ UInt16 endCode = OSReadBigInt16(table->cmap.format4.endCodes, segOffset);
+ if (endCode >= c) {
+ foundSegment = YES;
+ break;
+ }
+ }
+ if (!foundSegment) {
+ // no segment
+ // this is an invalid font
+ outGlyphs[j] = 0;
+ } else {
+ UInt16 startCode = OSReadBigInt16(table->cmap.format4.startCodes, segOffset);
+ if (!(startCode <= c)) {
+ // the code falls in a hole between segments
+ outGlyphs[j] = 0;
+ } else {
+ UInt16 idRangeOffset = OSReadBigInt16(table->cmap.format4.idRangeOffsets, segOffset);
+ if (idRangeOffset == 0) {
+ UInt16 idDelta = OSReadBigInt16(table->cmap.format4.idDeltas, segOffset);
+ outGlyphs[j] = (c + idDelta) % 65536;
+ } else {
+ // use the glyphIndexArray
+ UInt16 glyphOffset = idRangeOffset + 2 * (c - startCode);
+ outGlyphs[j] = OSReadBigInt16(&((UInt8*)table->cmap.format4.idRangeOffsets)[segOffset], glyphOffset);
+ }
+ }
+ }
+ }
+ break;
+ }
+ case kFontTableFormat12: {
+ UInt32 lastSegment = UINT32_MAX;
+ for (NSUInteger i = 0; i < charLen; i++, j++) {
+ unichar c = characters[i];
+ UInt32 c32 = c;
+ if (UnicharIsHighSurrogate(c)) {
+ if (i+1 < charLen) { // do we have another character after this one?
+ unichar cc = characters[i+1];
+ if (UnicharIsLowSurrogate(cc)) {
+ c32 = ConvertSurrogatePairToUTF32(c, cc);
+ i++;
+ }
+ }
+ }
+ // Start the heuristic search
+ // If this is an ASCII char, just do a linear search
+ // Otherwise do a hinted, modified binary search
+ // Start the first pivot at the last range found
+ // And when moving the pivot, limit the movement by increasing
+ // powers of two. This should help with locality
+ __typeof__(table->cmap.format12.groups[0]) *foundGroup = NULL;
+ if (c32 <= 0x7F) {
+ // ASCII
+ for (UInt32 idx = 0; idx < table->cmap.format12.nGroups; idx++) {
+ __typeof__(table->cmap.format12.groups[idx]) *group = &table->cmap.format12.groups[idx];
+ if (c32 < OSSwapBigToHostInt32(group->startCharCode)) {
+ // we've fallen into a hole
+ break;
+ } else if (c32 <= OSSwapBigToHostInt32(group->endCharCode)) {
+ // this is the range
+ foundGroup = group;
+ break;
+ }
+ }
+ } else {
+ // heuristic search
+ UInt32 maxJump = (lastSegment == UINT32_MAX ? UINT32_MAX / 2 : 8);
+ UInt32 lowIdx = 0, highIdx = table->cmap.format12.nGroups; // highIdx is the first invalid idx
+ UInt32 pivot = (lastSegment == UINT32_MAX ? lowIdx + (highIdx - lowIdx) / 2 : lastSegment);
+ while (highIdx > lowIdx) {
+ __typeof__(table->cmap.format12.groups[pivot]) *group = &table->cmap.format12.groups[pivot];
+ if (c32 < OSSwapBigToHostInt32(group->startCharCode)) {
+ highIdx = pivot;
+ } else if (c32 > OSSwapBigToHostInt32(group->endCharCode)) {
+ lowIdx = pivot + 1;
+ } else {
+ // we've hit the range
+ foundGroup = group;
+ break;
+ }
+ if (highIdx - lowIdx > maxJump * 2) {
+ if (highIdx == pivot) {
+ pivot -= maxJump;
+ } else {
+ pivot += maxJump;
+ }
+ maxJump *= 2;
+ } else {
+ pivot = lowIdx + (highIdx - lowIdx) / 2;
+ }
+ }
+ if (foundGroup != NULL) lastSegment = pivot;
+ }
+ if (foundGroup == NULL) {
+ outGlyphs[j] = 0;
+ } else {
+ outGlyphs[j] = (CGGlyph)(OSSwapBigToHostInt32(foundGroup->startGlyphCode) +
+ (c32 - OSSwapBigToHostInt32(foundGroup->startCharCode)));
+ }
+ }
+ break;
+ }
+ }
+ if (outGlyphLen != NULL) *outGlyphLen = j;
+ } else {
+ // we have no table, so just null out the glyphs
+ bzero(outGlyphs, charLen*sizeof(CGGlyph));
+ if (outGlyphLen != NULL) *outGlyphLen = 0;
+ }
+}
+
+static BOOL mapGlyphsToAdvancesInFont(ZFont *font, size_t n, CGGlyph glyphs[], CGFloat outAdvances[]) {
+ int advances[n];
+ if (CGFontGetGlyphAdvances(font.cgFont, glyphs, n, advances)) {
+ CGFloat ratio = font.ratio;
+
+ for (size_t i = 0; i < n; i++) {
+ outAdvances[i] = advances[i]*ratio;
+ }
+ return YES;
+ } else {
+ bzero(outAdvances, n*sizeof(CGFloat));
+ }
+ return NO;
+}
+
+static id getValueOrDefaultForRun(ZAttributeRun *run, NSString *key) {
+ id value = [run.attributes objectForKey:key];
+ if (value == nil) {
+ static NSDictionary *defaultValues = nil;
+ if (defaultValues == nil) {
+ defaultValues = [[NSDictionary alloc] initWithObjectsAndKeys:
+ [ZFont fontWithUIFont:[UIFont systemFontOfSize:12]], ZFontAttributeName,
+ [UIColor blackColor], ZForegroundColorAttributeName,
+ [UIColor clearColor], ZBackgroundColorAttributeName,
+ [NSNumber numberWithInt:ZUnderlineStyleNone], ZUnderlineStyleAttributeName,
+ nil];
+ }
+ value = [defaultValues objectForKey:key];
+ }
+ return value;
+}
+
+static void readRunInformation(NSArray *attributes, NSUInteger len, CFMutableDictionaryRef fontTableMap,
+ NSUInteger index, ZAttributeRun **currentRun, NSUInteger *nextRunStart,
+ ZFont **currentFont, fontTable **currentTable) {
+ *currentRun = [attributes objectAtIndex:index];
+ *nextRunStart = ([attributes count] > index+1 ? [[attributes objectAtIndex:index+1] index] : len);
+ *currentFont = getValueOrDefaultForRun(*currentRun, ZFontAttributeName);
+ if (!CFDictionaryGetValueIfPresent(fontTableMap, (*currentFont).cgFont, (const void **)currentTable)) {
+ *currentTable = readFontTableFromCGFont((*currentFont).cgFont);
+ CFDictionarySetValue(fontTableMap, (*currentFont).cgFont, *currentTable);
+ releaseFontTable(*currentTable);
+ }
+}
+
+static CGSize drawOrSizeTextConstrainedToSize(BOOL performDraw, NSString *string, NSArray *attributes, CGSize constrainedSize, NSUInteger maxLines,
+ UILineBreakMode lineBreakMode, UITextAlignment alignment, BOOL ignoreColor) {
+ NSUInteger len = [string length];
+ NSUInteger idx = 0;
+ CGPoint drawPoint = CGPointZero;
+ CGSize retValue = CGSizeZero;
+ CGContextRef ctx = (performDraw ? UIGraphicsGetCurrentContext() : NULL);
+
+ BOOL convertNewlines = (maxLines == 1);
+
+ // Extract the characters from the string
+ // Convert newlines to spaces if necessary
+ unichar *characters = (unichar *)malloc(sizeof(unichar) * len);
+ if (convertNewlines) {
+ NSCharacterSet *charset = [NSCharacterSet newlineCharacterSet];
+ NSRange range = NSMakeRange(0, len);
+ size_t cIdx = 0;
+ while (range.length > 0) {
+ NSRange newlineRange = [string rangeOfCharacterFromSet:charset options:0 range:range];
+ if (newlineRange.location == NSNotFound) {
+ [string getCharacters:&characters[cIdx] range:range];
+ cIdx += range.length;
+ break;
+ } else {
+ NSUInteger delta = newlineRange.location - range.location;
+ if (newlineRange.location > range.location) {
+ [string getCharacters:&characters[cIdx] range:NSMakeRange(range.location, delta)];
+ }
+ cIdx += delta;
+ characters[cIdx] = (unichar)' ';
+ cIdx++;
+ delta += newlineRange.length;
+ range.location += delta, range.length -= delta;
+ if (newlineRange.length == 1 && range.length >= 1 &&
+ [string characterAtIndex:newlineRange.location] == (unichar)'\r' &&
+ [string characterAtIndex:range.location] == (unichar)'\n') {
+ // CRLF sequence, skip the LF
+ range.location += 1, range.length -= 1;
+ }
+ }
+ }
+ len = cIdx;
+ } else {
+ [string getCharacters:characters range:NSMakeRange(0, len)];
+ }
+
+ // Create storage for glyphs and advances
+ CGGlyph *glyphs;
+ CGFloat *advances;
+ {
+ NSUInteger maxRunLength = 0;
+ ZAttributeRun *a = [attributes objectAtIndex:0];
+ for (NSUInteger i = 1; i < [attributes count]; i++) {
+ ZAttributeRun *b = [attributes objectAtIndex:i];
+ maxRunLength = MAX(maxRunLength, b.index - a.index);
+ a = b;
+ }
+ maxRunLength = MAX(maxRunLength, len - a.index);
+ maxRunLength++; // for a potential ellipsis
+ glyphs = (CGGlyph *)malloc(sizeof(CGGlyph) * maxRunLength);
+ advances = (CGFloat *)malloc(sizeof(CGFloat) * maxRunLength);
+ }
+
+ // Use this table to cache all fontTable objects
+ CFMutableDictionaryRef fontTableMap = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks,
+ &kFontTableDictionaryValueCallBacks);
+
+ // Fetch initial style values
+ NSUInteger currentRunIdx = 0;
+ ZAttributeRun *currentRun;
+ NSUInteger nextRunStart;
+ ZFont *currentFont;
+ fontTable *currentTable;
+
+#define READ_RUN() readRunInformation(attributes, len, fontTableMap, \
+ currentRunIdx, ¤tRun, &nextRunStart, \
+ ¤tFont, ¤tTable)
+
+ READ_RUN();
+
+ // fetch the glyphs for the first run
+ size_t glyphCount;
+ NSUInteger glyphIdx;
+
+#define READ_GLYPHS() do { \
+ mapCharactersToGlyphsInFont(currentTable, &characters[currentRun.index], (nextRunStart - currentRun.index), glyphs, &glyphCount); \
+ mapGlyphsToAdvancesInFont(currentFont, (nextRunStart - currentRun.index), glyphs, advances); \
+ glyphIdx = 0; \
+ } while (0)
+
+ READ_GLYPHS();
+
+ NSMutableCharacterSet *alphaCharset = [NSMutableCharacterSet alphanumericCharacterSet];
+ [alphaCharset addCharactersInString:@"([{'\"\u2019\u02BC"];
+
+ // scan left-to-right looking for newlines or until we hit the width constraint
+ // When we hit a wrapping point, calculate truncation as follows:
+ // If we have room to draw at least one more character on the next line, no truncation
+ // Otherwise apply the truncation algorithm to the current line.
+ // After calculating any truncation, draw.
+ // Each time we hit the end of an attribute run, calculate the new font and make sure
+ // it fits (vertically) within the size constraint. If not, truncate this line.
+ // When we draw, iterate over the attribute runs for this line and draw each run separately
+ BOOL lastLine = NO; // used to indicate truncation and to stop the iterating
+ NSUInteger lineCount = 1;
+ while (idx < len && !lastLine) {
+ if (maxLines > 0 && lineCount == maxLines) {
+ lastLine = YES;
+ }
+ // scan left-to-right
+ struct {
+ NSUInteger index;
+ NSUInteger glyphIndex;
+ NSUInteger currentRunIdx;
+ } indexCache = { idx, glyphIdx, currentRunIdx };
+ CGSize lineSize = CGSizeMake(0, currentFont.leading);
+ CGFloat lineAscender = currentFont.ascender;
+ struct {
+ NSUInteger index;
+ NSUInteger glyphIndex;
+ NSUInteger currentRunIdx;
+ CGSize lineSize;
+ } lastWrapCache = {0, 0, 0, CGSizeZero};
+ BOOL inAlpha = NO; // used for calculating wrap points
+
+ BOOL finishLine = NO;
+ for (;idx <= len && !finishLine;) {
+ NSUInteger skipCount = 0;
+ if (idx == len) {
+ finishLine = YES;
+ lastLine = YES;
+ } else {
+ if (idx >= nextRunStart) {
+ // cycle the font and table and grab the next set of glyphs
+ do {
+ currentRunIdx++;
+ READ_RUN();
+ } while (idx >= nextRunStart);
+ READ_GLYPHS();
+ // re-scan the characters to synchronize the glyph index
+ for (NSUInteger j = currentRun.index; j < idx; j++) {
+ if (UnicharIsHighSurrogate(characters[j]) && j+1<len && UnicharIsLowSurrogate(characters[j+1])) {
+ j++;
+ }
+ glyphIdx++;
+ }
+ if (currentFont.leading > lineSize.height) {
+ lineSize.height = currentFont.leading;
+ if (retValue.height + currentFont.ascender > constrainedSize.height) {
+ lastLine = YES;
+ finishLine = YES;
+ }
+ }
+ lineAscender = MAX(lineAscender, currentFont.ascender);
+ }
+ unichar c = characters[idx];
+ // Mark a wrap point before spaces and after any stretch of non-alpha characters
+ BOOL markWrap = NO;
+ if (c == (unichar)' ') {
+ markWrap = YES;
+ } else if ([alphaCharset characterIsMember:c]) {
+ if (!inAlpha) {
+ markWrap = YES;
+ inAlpha = YES;
+ }
+ } else {
+ inAlpha = NO;
+ }
+ if (markWrap) {
+ lastWrapCache = (__typeof__(lastWrapCache)){
+ .index = idx,
+ .glyphIndex = glyphIdx,
+ .currentRunIdx = currentRunIdx,
+ .lineSize = lineSize
+ };
+ }
+ // process the line
+ if (c == (unichar)'\n' || c == 0x0085) { // U+0085 is the NEXT_LINE unicode character
+ finishLine = YES;
+ skipCount = 1;
+ } else if (c == (unichar)'\r') {
+ finishLine = YES;
+ // check for CRLF
+ if (idx+1 < len && characters[idx+1] == (unichar)'\n') {
+ skipCount = 2;
+ } else {
+ skipCount = 1;
+ }
+ } else if (lineSize.width + advances[glyphIdx] > constrainedSize.width) {
+ finishLine = YES;
+ if (retValue.height + lineSize.height + currentFont.ascender > constrainedSize.height) {
+ lastLine = YES;
+ }
+ // walk backwards if wrapping is necessary
+ if (lastWrapCache.index > indexCache.index && lineBreakMode != UILineBreakModeCharacterWrap &&
+ (!lastLine || lineBreakMode != UILineBreakModeClip)) {
+ // we're doing some sort of word wrapping
+ idx = lastWrapCache.index;
+ lineSize = lastWrapCache.lineSize;
+ if (!lastLine) {
+ // re-check if this is the last line
+ if (lastWrapCache.currentRunIdx != currentRunIdx) {
+ currentRunIdx = lastWrapCache.currentRunIdx;
+ READ_RUN();
+ READ_GLYPHS();
+ }
+ if (retValue.height + lineSize.height + currentFont.ascender > constrainedSize.height) {
+ lastLine = YES;
+ }
+ }
+ glyphIdx = lastWrapCache.glyphIndex;
+ // skip any spaces
+ for (NSUInteger j = idx; j < len && characters[j] == (unichar)' '; j++) {
+ skipCount++;
+ }
+ }
+ }
+ }
+ if (finishLine) {
+ // TODO: support head/middle truncation
+ if (lastLine && idx < len && lineBreakMode == UILineBreakModeTailTruncation) {
+ // truncate
+ unichar ellipsis = 0x2026; // ellipsis (…)
+ CGGlyph ellipsisGlyph;
+ mapCharactersToGlyphsInFont(currentTable, &ellipsis, 1, &ellipsisGlyph, NULL);
+ CGFloat ellipsisWidth;
+ mapGlyphsToAdvancesInFont(currentFont, 1, &ellipsisGlyph, &ellipsisWidth);
+ while ((idx - indexCache.index) > 1 && lineSize.width + ellipsisWidth > constrainedSize.width) {
+ // we have more than 1 character and we're too wide, so back up
+ idx--;
+ if (UnicharIsHighSurrogate(characters[idx]) && UnicharIsLowSurrogate(characters[idx+1])) {
+ idx--;
+ }
+ if (idx < currentRun.index) {
+ ZFont *oldFont = currentFont;
+ do {
+ currentRunIdx--;
+ READ_RUN();
+ } while (idx < currentRun.index);
+ READ_GLYPHS();
+ glyphIdx = glyphCount-1;
+ if (oldFont != currentFont) {
+ mapCharactersToGlyphsInFont(currentTable, &ellipsis, 1, &ellipsisGlyph, NULL);
+ mapGlyphsToAdvancesInFont(currentFont, 1, &ellipsisGlyph, &ellipsisWidth);
+ }
+ } else {
+ glyphIdx--;
+ }
+ lineSize.width -= advances[glyphIdx];
+ }
+ // skip any spaces before truncating
+ while ((idx - indexCache.index) > 1 && characters[idx-1] == (unichar)' ') {
+ idx--;
+ if (idx < currentRun.index) {
+ currentRunIdx--;
+ READ_RUN();
+ READ_GLYPHS();
+ glyphIdx = glyphCount-1;
+ } else {
+ glyphIdx--;
+ }
+ lineSize.width -= advances[glyphIdx];
+ }
+ lineSize.width += ellipsisWidth;
+ glyphs[glyphIdx] = ellipsisGlyph;
+ idx++;
+ glyphIdx++;
+ }
+ retValue.width = MAX(retValue.width, lineSize.width);
+ retValue.height += lineSize.height;
+
+ // draw
+ if (performDraw) {
+ switch (alignment) {
+ case UITextAlignmentLeft:
+ drawPoint.x = 0;
+ break;
+ case UITextAlignmentCenter:
+ drawPoint.x = (constrainedSize.width - lineSize.width) / 2.0f;
+ break;
+ case UITextAlignmentRight:
+ drawPoint.x = constrainedSize.width - lineSize.width;
+ break;
+ }
+ NSUInteger stopGlyphIdx = glyphIdx;
+ NSUInteger lastRunIdx = currentRunIdx;
+ NSUInteger stopCharIdx = idx;
+ idx = indexCache.index;
+ if (currentRunIdx != indexCache.currentRunIdx) {
+ currentRunIdx = indexCache.currentRunIdx;
+ READ_RUN();
+ READ_GLYPHS();
+ }
+ glyphIdx = indexCache.glyphIndex;
+ for (NSUInteger drawIdx = currentRunIdx; drawIdx <= lastRunIdx; drawIdx++) {
+ if (drawIdx != currentRunIdx) {
+ currentRunIdx = drawIdx;
+ READ_RUN();
+ READ_GLYPHS();
+ }
+ NSUInteger numGlyphs;
+ if (drawIdx == lastRunIdx) {
+ numGlyphs = stopGlyphIdx - glyphIdx;
+ idx = stopCharIdx;
+ } else {
+ numGlyphs = glyphCount - glyphIdx;
+ idx = nextRunStart;
+ }
+ CGContextSetFont(ctx, currentFont.cgFont);
+ CGContextSetFontSize(ctx, currentFont.pointSize);
+ // calculate the fragment size
+ CGFloat fragmentWidth = 0;
+ for (NSUInteger g = 0; g < numGlyphs; g++) {
+ fragmentWidth += advances[glyphIdx + g];
+ }
+
+ if (!ignoreColor) {
+ UIColor *foregroundColor = getValueOrDefaultForRun(currentRun, ZForegroundColorAttributeName);
+ UIColor *backgroundColor = getValueOrDefaultForRun(currentRun, ZBackgroundColorAttributeName);
+ if (backgroundColor != nil && ![backgroundColor isEqual:[UIColor clearColor]]) {
+ [backgroundColor setFill];
+ UIRectFillUsingBlendMode((CGRect){ drawPoint, { fragmentWidth, lineSize.height } }, kCGBlendModeNormal);
+ }
+ [foregroundColor setFill];
+ }
+
+ CGContextShowGlyphsAtPoint(ctx, drawPoint.x, drawPoint.y + lineAscender, &glyphs[glyphIdx], numGlyphs);
+ NSNumber *underlineStyle = getValueOrDefaultForRun(currentRun, ZUnderlineStyleAttributeName);
+ if ([underlineStyle integerValue] & ZUnderlineStyleMask) {
+ // we only support single for the time being
+ UIRectFill(CGRectMake(drawPoint.x, drawPoint.y + lineAscender, fragmentWidth, 1));
+ }
+ drawPoint.x += fragmentWidth;
+ glyphIdx += numGlyphs;
+ }
+ drawPoint.y += lineSize.height;
+ }
+ idx += skipCount;
+ glyphIdx += skipCount;
+ lineCount++;
+ } else {
+ lineSize.width += advances[glyphIdx];
+ glyphIdx++;
+ idx++;
+ if (idx < len && UnicharIsHighSurrogate(characters[idx-1]) && UnicharIsLowSurrogate(characters[idx])) {
+ // skip the second half of the surrogate pair
+ idx++;
+ }
+ }
+ }
+ }
+ CFRelease(fontTableMap);
+ free(glyphs);
+ free(advances);
+ free(characters);
+
+#undef READ_GLYPHS
+#undef READ_RUN
+
+ return retValue;
+}
+
+static NSArray *attributeRunForFont(ZFont *font) {
+ return [NSArray arrayWithObject:[ZAttributeRun attributeRunWithIndex:0
+ attributes:[NSDictionary dictionaryWithObject:font
+ forKey:ZFontAttributeName]]];
+}
+
+static CGSize drawTextInRect(CGRect rect, NSString *text, NSArray *attributes, UILineBreakMode lineBreakMode,
+ UITextAlignment alignment, NSUInteger numberOfLines, BOOL ignoreColor) {
+ CGContextRef ctx = UIGraphicsGetCurrentContext();
+
+ CGContextSaveGState(ctx);
+
+ // flip it upside-down because our 0,0 is upper-left, whereas ttfs are for screens where 0,0 is lower-left
+ CGAffineTransform textTransform = CGAffineTransformMake(1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f);
+ CGContextSetTextMatrix(ctx, textTransform);
+
+ CGContextTranslateCTM(ctx, rect.origin.x, rect.origin.y);
+
+ CGContextSetTextDrawingMode(ctx, kCGTextFill);
+ CGSize size = drawOrSizeTextConstrainedToSize(YES, text, attributes, rect.size, numberOfLines, lineBreakMode, alignment, ignoreColor);
+
+ CGContextRestoreGState(ctx);
+
+ return size;
+}
+
+@implementation NSString (FontLabelStringDrawing)
+// CGFontRef-based methods
+- (CGSize)sizeWithCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize {
+ return [self sizeWithZFont:[ZFont fontWithCGFont:font size:pointSize]];
+}
+
+- (CGSize)sizeWithCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize constrainedToSize:(CGSize)size {
+ return [self sizeWithZFont:[ZFont fontWithCGFont:font size:pointSize] constrainedToSize:size];
+}
+
+- (CGSize)sizeWithCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize constrainedToSize:(CGSize)size
+ lineBreakMode:(UILineBreakMode)lineBreakMode {
+ return [self sizeWithZFont:[ZFont fontWithCGFont:font size:pointSize] constrainedToSize:size lineBreakMode:lineBreakMode];
+}
+
+- (CGSize)drawAtPoint:(CGPoint)point withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize {
+ return [self drawAtPoint:point withZFont:[ZFont fontWithCGFont:font size:pointSize]];
+}
+
+- (CGSize)drawInRect:(CGRect)rect withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize {
+ return [self drawInRect:rect withZFont:[ZFont fontWithCGFont:font size:pointSize]];
+}
+
+- (CGSize)drawInRect:(CGRect)rect withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize lineBreakMode:(UILineBreakMode)lineBreakMode {
+ return [self drawInRect:rect withZFont:[ZFont fontWithCGFont:font size:pointSize] lineBreakMode:lineBreakMode];
+}
+
+- (CGSize)drawInRect:(CGRect)rect withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize
+ lineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment {
+ return [self drawInRect:rect withZFont:[ZFont fontWithCGFont:font size:pointSize] lineBreakMode:lineBreakMode alignment:alignment];
+}
+
+// ZFont-based methods
+- (CGSize)sizeWithZFont:(ZFont *)font {
+ CGSize size = drawOrSizeTextConstrainedToSize(NO, self, attributeRunForFont(font), CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX), 1,
+ UILineBreakModeClip, UITextAlignmentLeft, YES);
+ return CGSizeMake(ceilf(size.width), ceilf(size.height));
+}
+
+- (CGSize)sizeWithZFont:(ZFont *)font constrainedToSize:(CGSize)size {
+ return [self sizeWithZFont:font constrainedToSize:size lineBreakMode:UILineBreakModeWordWrap];
+}
+
+/*
+ According to experimentation with UIStringDrawing, this can actually return a CGSize whose height is greater
+ than the one passed in. The two cases are as follows:
+ 1. If the given size parameter's height is smaller than a single line, the returned value will
+ be the height of one line.
+ 2. If the given size parameter's height falls between multiples of a line height, and the wrapped string
+ actually extends past the size.height, and the difference between size.height and the previous multiple
+ of a line height is >= the font's ascender, then the returned size's height is extended to the next line.
+ To put it simply, if the baseline point of a given line falls in the given size, the entire line will
+ be present in the output size.
+ */
+- (CGSize)sizeWithZFont:(ZFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode {
+ size = drawOrSizeTextConstrainedToSize(NO, self, attributeRunForFont(font), size, 0, lineBreakMode, UITextAlignmentLeft, YES);
+ return CGSizeMake(ceilf(size.width), ceilf(size.height));
+}
+
+- (CGSize)sizeWithZFont:(ZFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode
+ numberOfLines:(NSUInteger)numberOfLines {
+ size = drawOrSizeTextConstrainedToSize(NO, self, attributeRunForFont(font), size, numberOfLines, lineBreakMode, UITextAlignmentLeft, YES);
+ return CGSizeMake(ceilf(size.width), ceilf(size.height));
+}
+
+- (CGSize)drawAtPoint:(CGPoint)point withZFont:(ZFont *)font {
+ return [self drawAtPoint:point forWidth:CGFLOAT_MAX withZFont:font lineBreakMode:UILineBreakModeClip];
+}
+
+- (CGSize)drawAtPoint:(CGPoint)point forWidth:(CGFloat)width withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode {
+ return drawTextInRect((CGRect){ point, { width, CGFLOAT_MAX } }, self, attributeRunForFont(font), lineBreakMode, UITextAlignmentLeft, 1, YES);
+}
+
+- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font {
+ return [self drawInRect:rect withZFont:font lineBreakMode:UILineBreakModeWordWrap];
+}
+
+- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode {
+ return [self drawInRect:rect withZFont:font lineBreakMode:lineBreakMode alignment:UITextAlignmentLeft];
+}
+
+- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode
+ alignment:(UITextAlignment)alignment {
+ return drawTextInRect(rect, self, attributeRunForFont(font), lineBreakMode, alignment, 0, YES);
+}
+
+- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode
+ alignment:(UITextAlignment)alignment numberOfLines:(NSUInteger)numberOfLines {
+ return drawTextInRect(rect, self, attributeRunForFont(font), lineBreakMode, alignment, numberOfLines, YES);
+}
+@end
+
+@implementation ZAttributedString (ZAttributedStringDrawing)
+- (CGSize)size {
+ CGSize size = drawOrSizeTextConstrainedToSize(NO, self.string, self.attributes, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX), 1,
+ UILineBreakModeClip, UITextAlignmentLeft, NO);
+ return CGSizeMake(ceilf(size.width), ceilf(size.height));
+}
+
+- (CGSize)sizeConstrainedToSize:(CGSize)size {
+ return [self sizeConstrainedToSize:size lineBreakMode:UILineBreakModeWordWrap];
+}
+
+- (CGSize)sizeConstrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode {
+ size = drawOrSizeTextConstrainedToSize(NO, self.string, self.attributes, size, 0, lineBreakMode, UITextAlignmentLeft, NO);
+ return CGSizeMake(ceilf(size.width), ceilf(size.height));
+}
+
+- (CGSize)sizeConstrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode
+ numberOfLines:(NSUInteger)numberOfLines {
+ size = drawOrSizeTextConstrainedToSize(NO, self.string, self.attributes, size, numberOfLines, lineBreakMode, UITextAlignmentLeft, NO);
+ return CGSizeMake(ceilf(size.width), ceilf(size.height));
+}
+
+- (CGSize)drawAtPoint:(CGPoint)point {
+ return [self drawAtPoint:point forWidth:CGFLOAT_MAX lineBreakMode:UILineBreakModeClip];
+}
+
+- (CGSize)drawAtPoint:(CGPoint)point forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode {
+ return drawTextInRect((CGRect){ point, { width, CGFLOAT_MAX } }, self.string, self.attributes, lineBreakMode, UITextAlignmentLeft, 1, NO);
+}
+
+- (CGSize)drawInRect:(CGRect)rect {
+ return [self drawInRect:rect withLineBreakMode:UILineBreakModeWordWrap];
+}
+
+- (CGSize)drawInRect:(CGRect)rect withLineBreakMode:(UILineBreakMode)lineBreakMode {
+ return [self drawInRect:rect withLineBreakMode:lineBreakMode alignment:UITextAlignmentLeft];
+}
+
+- (CGSize)drawInRect:(CGRect)rect withLineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment {
+ return drawTextInRect(rect, self.string, self.attributes, lineBreakMode, alignment, 0, NO);
+}
+
+- (CGSize)drawInRect:(CGRect)rect withLineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment
+ numberOfLines:(NSUInteger)numberOfLines {
+ return drawTextInRect(rect, self.string, self.attributes, lineBreakMode, alignment, numberOfLines, NO);
+}
+@end
--- /dev/null
+//
+// FontManager.h
+// FontLabel
+//
+// Created by Kevin Ballard on 5/5/09.
+// Copyright © 2009 Zynga Game Networks
+//
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import <Foundation/Foundation.h>
+#import <CoreGraphics/CoreGraphics.h>
+
+@class ZFont;
+
+@interface FontManager : NSObject {
+ CFMutableDictionaryRef fonts;
+ NSMutableDictionary *urls;
+}
++ (FontManager *)sharedManager;
+/*!
+ @method
+ @abstract Loads a TTF font from the main bundle
+ @param filename The name of the font file to load (with or without extension).
+ @return YES if the font was loaded, NO if an error occurred
+ @discussion If the font has already been loaded, this method does nothing and returns YES.
+ This method first attempts to load the font by appending .ttf to the filename.
+ If that file does not exist, it tries the filename exactly as given.
+*/
+- (BOOL)loadFont:(NSString *)filename;
+/*!
+ @method
+ @abstract Loads a font from the given file URL
+ @param url A file URL that points to a font file
+ @return YES if the font was loaded, NO if an error occurred
+ @discussion If the font has already been loaded, this method does nothing and returns YES.
+*/
+- (BOOL)loadFontURL:(NSURL *)url;
+/*!
+ @method
+ @abstract Returns the loaded font with the given filename
+ @param filename The name of the font file that was given to -loadFont:
+ @return A CGFontRef, or NULL if the specified font cannot be found
+ @discussion If the font has not been loaded yet, -loadFont: will be
+ called with the given name first.
+*/
+- (CGFontRef)fontWithName:(NSString *)filename __AVAILABILITY_INTERNAL_DEPRECATED;
+/*!
+ @method
+ @abstract Returns a ZFont object corresponding to the loaded font with the given filename and point size
+ @param filename The name of the font file that was given to -loadFont:
+ @param pointSize The point size of the font
+ @return A ZFont, or NULL if the specified font cannot be found
+ @discussion If the font has not been loaded yet, -loadFont: will be
+ called with the given name first.
+*/
+- (ZFont *)zFontWithName:(NSString *)filename pointSize:(CGFloat)pointSize;
+/*!
+ @method
+ @abstract Returns a ZFont object corresponding to the loaded font with the given file URL and point size
+ @param url A file URL that points to a font file
+ @param pointSize The point size of the font
+ @return A ZFont, or NULL if the specified font cannot be loaded
+ @discussion If the font has not been loaded yet, -loadFontURL: will be called with the given URL first.
+*/
+- (ZFont *)zFontWithURL:(NSURL *)url pointSize:(CGFloat)pointSize;
+/*!
+ @method
+ @abstract Returns a CFArrayRef of all loaded CGFont objects
+ @return A CFArrayRef of all loaded CGFont objects
+ @description You are responsible for releasing the CFArrayRef
+*/
+- (CFArrayRef)copyAllFonts;
+@end
--- /dev/null
+//
+// FontManager.m
+// FontLabel
+//
+// Created by Kevin Ballard on 5/5/09.
+// Copyright © 2009 Zynga Game Networks
+//
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "FontManager.h"
+#import "ZFont.h"
+
+static FontManager *sharedFontManager = nil;
+
+@implementation FontManager
++ (FontManager *)sharedManager {
+ @synchronized(self) {
+ if (sharedFontManager == nil) {
+ sharedFontManager = [[self alloc] init];
+ }
+ }
+ return sharedFontManager;
+}
+
+- (id)init {
+ if ((self = [super init])) {
+ fonts = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ urls = [[NSMutableDictionary alloc] init];
+ }
+ return self;
+}
+
+- (BOOL)loadFont:(NSString *)filename {
+ NSString *fontPath = [[NSBundle mainBundle] pathForResource:filename ofType:@"ttf"];
+ if (fontPath == nil) {
+ fontPath = [[NSBundle mainBundle] pathForResource:filename ofType:nil];
+ }
+ if (fontPath == nil) return NO;
+
+ NSURL *url = [NSURL fileURLWithPath:fontPath];
+ if ([self loadFontURL:url]) {
+ [urls setObject:url forKey:filename];
+ return YES;
+ }
+ return NO;
+}
+
+- (BOOL)loadFontURL:(NSURL *)url {
+ CGDataProviderRef fontDataProvider = CGDataProviderCreateWithURL((CFURLRef)url);
+ if (fontDataProvider == NULL) return NO;
+ CGFontRef newFont = CGFontCreateWithDataProvider(fontDataProvider);
+ CGDataProviderRelease(fontDataProvider);
+ if (newFont == NULL) return NO;
+
+ CFDictionarySetValue(fonts, url, newFont);
+ CGFontRelease(newFont);
+ return YES;
+}
+
+- (CGFontRef)fontWithName:(NSString *)filename {
+ CGFontRef font = NULL;
+ NSURL *url = [urls objectForKey:filename];
+ if (url == nil && [self loadFont:filename]) {
+ url = [urls objectForKey:filename];
+ }
+ if (url != nil) {
+ font = (CGFontRef)CFDictionaryGetValue(fonts, url);
+ }
+ return font;
+}
+
+- (ZFont *)zFontWithName:(NSString *)filename pointSize:(CGFloat)pointSize {
+ NSURL *url = [urls objectForKey:filename];
+ if (url == nil && [self loadFont:filename]) {
+ url = [urls objectForKey:filename];
+ }
+ if (url != nil) {
+ CGFontRef cgFont = (CGFontRef)CFDictionaryGetValue(fonts, url);
+ if (cgFont != NULL) {
+ return [ZFont fontWithCGFont:cgFont size:pointSize];
+ }
+ }
+ return nil;
+}
+
+- (ZFont *)zFontWithURL:(NSURL *)url pointSize:(CGFloat)pointSize {
+ CGFontRef cgFont = (CGFontRef)CFDictionaryGetValue(fonts, url);
+ if (cgFont == NULL && [self loadFontURL:url]) {
+ cgFont = (CGFontRef)CFDictionaryGetValue(fonts, url);
+ }
+ if (cgFont != NULL) {
+ return [ZFont fontWithCGFont:cgFont size:pointSize];
+ }
+ return nil;
+}
+
+- (CFArrayRef)copyAllFonts {
+ CFIndex count = CFDictionaryGetCount(fonts);
+ CGFontRef *values = (CGFontRef *)malloc(sizeof(CGFontRef) * count);
+ CFDictionaryGetKeysAndValues(fonts, NULL, (const void **)values);
+ CFArrayRef array = CFArrayCreate(NULL, (const void **)values, count, &kCFTypeArrayCallBacks);
+ free(values);
+ return array;
+}
+
+- (void)dealloc {
+ CFRelease(fonts);
+ [urls release];
+ [super dealloc];
+}
+@end
--- /dev/null
+//
+// ZAttributedString.h
+// FontLabel
+//
+// Created by Kevin Ballard on 9/22/09.
+// Copyright 2009 Zynga Game Networks. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+#if NS_BLOCKS_AVAILABLE
+#define Z_BLOCKS 1
+#else
+// set this to 1 if you are using PLBlocks
+#define Z_BLOCKS 0
+#endif
+
+#if Z_BLOCKS
+enum {
+ ZAttributedStringEnumerationReverse = (1UL << 1),
+ ZAttributedStringEnumerationLongestEffectiveRangeNotRequired = (1UL << 20)
+};
+typedef NSUInteger ZAttributedStringEnumerationOptions;
+#endif
+
+@interface ZAttributedString : NSObject <NSCoding, NSCopying, NSMutableCopying> {
+ NSMutableString *_buffer;
+ NSMutableArray *_attributes;
+}
+@property (nonatomic, readonly) NSUInteger length;
+@property (nonatomic, readonly) NSString *string;
+- (id)initWithAttributedString:(ZAttributedString *)attr;
+- (id)initWithString:(NSString *)str;
+- (id)initWithString:(NSString *)str attributes:(NSDictionary *)attributes;
+- (id)attribute:(NSString *)attributeName atIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange;
+- (id)attribute:(NSString *)attributeName atIndex:(NSUInteger)index longestEffectiveRange:(NSRangePointer)aRange inRange:(NSRange)rangeLimit;
+- (ZAttributedString *)attributedSubstringFromRange:(NSRange)aRange;
+- (NSDictionary *)attributesAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange;
+- (NSDictionary *)attributesAtIndex:(NSUInteger)index longestEffectiveRange:(NSRangePointer)aRange inRange:(NSRange)rangeLimit;
+#if Z_BLOCKS
+- (void)enumerateAttribute:(NSString *)attrName inRange:(NSRange)enumerationRange options:(ZAttributedStringEnumerationOptions)opts
+ usingBlock:(void (^)(id value, NSRange range, BOOL *stop))block;
+- (void)enumerateAttributesInRange:(NSRange)enumerationRange options:(ZAttributedStringEnumerationOptions)opts
+ usingBlock:(void (^)(NSDictionary *attrs, NSRange range, BOOL *stop))block;
+#endif
+- (BOOL)isEqualToAttributedString:(ZAttributedString *)otherString;
+@end
+
+@interface ZMutableAttributedString : ZAttributedString {
+}
+- (void)addAttribute:(NSString *)name value:(id)value range:(NSRange)range;
+- (void)addAttributes:(NSDictionary *)attributes range:(NSRange)range;
+- (void)appendAttributedString:(ZAttributedString *)str;
+- (void)deleteCharactersInRange:(NSRange)range;
+- (void)insertAttributedString:(ZAttributedString *)str atIndex:(NSUInteger)idx;
+- (void)removeAttribute:(NSString *)name range:(NSRange)range;
+- (void)replaceCharactersInRange:(NSRange)range withAttributedString:(ZAttributedString *)str;
+- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str;
+- (void)setAttributedString:(ZAttributedString *)str;
+- (void)setAttributes:(NSDictionary *)attributes range:(NSRange)range;
+@end
+
+extern NSString * const ZFontAttributeName;
+extern NSString * const ZForegroundColorAttributeName;
+extern NSString * const ZBackgroundColorAttributeName;
+extern NSString * const ZUnderlineStyleAttributeName;
+
+enum {
+ ZUnderlineStyleNone = 0x00,
+ ZUnderlineStyleSingle = 0x01
+};
+#define ZUnderlineStyleMask 0x00FF
+
+enum {
+ ZUnderlinePatternSolid = 0x0000
+};
+#define ZUnderlinePatternMask 0xFF00
--- /dev/null
+//
+// ZAttributedString.m
+// FontLabel
+//
+// Created by Kevin Ballard on 9/22/09.
+// Copyright 2009 Zynga Game Networks. All rights reserved.
+//
+
+#import "ZAttributedString.h"
+#import "ZAttributedStringPrivate.h"
+
+@interface ZAttributedString ()
+- (NSUInteger)indexOfEffectiveAttributeRunForIndex:(NSUInteger)index;
+- (NSDictionary *)attributesAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange uniquingOnName:(NSString *)attributeName;
+- (NSDictionary *)attributesAtIndex:(NSUInteger)index longestEffectiveRange:(NSRangePointer)aRange
+ inRange:(NSRange)rangeLimit uniquingOnName:(NSString *)attributeName;
+@end
+
+@interface ZAttributedString ()
+@property (nonatomic, readonly) NSArray *attributes;
+@end
+
+@implementation ZAttributedString
+@synthesize string = _buffer;
+@synthesize attributes = _attributes;
+
+- (id)initWithAttributedString:(ZAttributedString *)attr {
+ NSParameterAssert(attr != nil);
+ if ((self = [super init])) {
+ _buffer = [attr->_buffer mutableCopy];
+ _attributes = [[NSMutableArray alloc] initWithArray:attr->_attributes copyItems:YES];
+ }
+ return self;
+}
+
+- (id)initWithString:(NSString *)str {
+ return [self initWithString:str attributes:nil];
+}
+
+- (id)initWithString:(NSString *)str attributes:(NSDictionary *)attributes {
+ if ((self = [super init])) {
+ _buffer = [str mutableCopy];
+ _attributes = [[NSMutableArray alloc] initWithObjects:[ZAttributeRun attributeRunWithIndex:0 attributes:attributes], nil];
+ }
+ return self;
+}
+
+- (id)init {
+ return [self initWithString:@"" attributes:nil];
+}
+
+- (id)initWithCoder:(NSCoder *)decoder {
+ if ((self = [super init])) {
+ _buffer = [[decoder decodeObjectForKey:@"buffer"] mutableCopy];
+ _attributes = [[decoder decodeObjectForKey:@"attributes"] mutableCopy];
+ }
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder {
+ [aCoder encodeObject:_buffer forKey:@"buffer"];
+ [aCoder encodeObject:_attributes forKey:@"attributes"];
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+ return [self retain];
+}
+
+- (id)mutableCopyWithZone:(NSZone *)zone {
+ return [(ZMutableAttributedString *)[ZMutableAttributedString allocWithZone:zone] initWithAttributedString:self];
+}
+
+- (NSUInteger)length {
+ return [_buffer length];
+}
+
+- (NSString *)description {
+ NSMutableArray *components = [NSMutableArray arrayWithCapacity:[_attributes count]*2];
+ NSRange range = NSMakeRange(0, 0);
+ for (NSUInteger i = 0; i <= [_attributes count]; i++) {
+ range.location = NSMaxRange(range);
+ ZAttributeRun *run;
+ if (i < [_attributes count]) {
+ run = [_attributes objectAtIndex:i];
+ range.length = run.index - range.location;
+ } else {
+ run = nil;
+ range.length = [_buffer length] - range.location;
+ }
+ if (range.length > 0) {
+ [components addObject:[NSString stringWithFormat:@"\"%@\"", [_buffer substringWithRange:range]]];
+ }
+ if (run != nil) {
+ NSMutableArray *attrDesc = [NSMutableArray arrayWithCapacity:[run.attributes count]];
+ for (id key in run.attributes) {
+ [attrDesc addObject:[NSString stringWithFormat:@"%@: %@", key, [run.attributes objectForKey:key]]];
+ }
+ [components addObject:[NSString stringWithFormat:@"{%@}", [attrDesc componentsJoinedByString:@", "]]];
+ }
+ }
+ return [NSString stringWithFormat:@"%@", [components componentsJoinedByString:@" "]];
+}
+
+- (id)attribute:(NSString *)attributeName atIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange {
+ NSParameterAssert(attributeName != nil);
+ return [[self attributesAtIndex:index effectiveRange:aRange uniquingOnName:attributeName] objectForKey:attributeName];
+}
+
+- (id)attribute:(NSString *)attributeName atIndex:(NSUInteger)index longestEffectiveRange:(NSRangePointer)aRange inRange:(NSRange)rangeLimit {
+ NSParameterAssert(attributeName != nil);
+ return [[self attributesAtIndex:index longestEffectiveRange:aRange inRange:rangeLimit uniquingOnName:attributeName] objectForKey:attributeName];
+}
+
+- (ZAttributedString *)attributedSubstringFromRange:(NSRange)aRange {
+ if (NSMaxRange(aRange) > [_buffer length]) {
+ @throw [NSException exceptionWithName:NSRangeException reason:@"range was outisde of the attributed string" userInfo:nil];
+ }
+ ZMutableAttributedString *newStr = [self mutableCopy];
+ if (aRange.location > 0) {
+ [newStr deleteCharactersInRange:NSMakeRange(0, aRange.location)];
+ }
+ if (NSMaxRange(aRange) < [_buffer length]) {
+ [newStr deleteCharactersInRange:NSMakeRange(aRange.length, [_buffer length] - NSMaxRange(aRange))];
+ }
+ return [newStr autorelease];
+}
+
+- (NSDictionary *)attributesAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange {
+ return [NSDictionary dictionaryWithDictionary:[self attributesAtIndex:index effectiveRange:aRange uniquingOnName:nil]];
+}
+
+- (NSDictionary *)attributesAtIndex:(NSUInteger)index longestEffectiveRange:(NSRangePointer)aRange inRange:(NSRange)rangeLimit {
+ return [NSDictionary dictionaryWithDictionary:[self attributesAtIndex:index longestEffectiveRange:aRange inRange:rangeLimit uniquingOnName:nil]];
+}
+
+#if Z_BLOCKS
+// Warning: this code has not been tested. The only guarantee is that it compiles.
+- (void)enumerateAttribute:(NSString *)attrName inRange:(NSRange)enumerationRange options:(ZAttributedStringEnumerationOptions)opts
+ usingBlock:(void (^)(id, NSRange, BOOL*))block {
+ if (opts & ZAttributedStringEnumerationLongestEffectiveRangeNotRequired) {
+ [self enumerateAttributesInRange:enumerationRange options:opts usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) {
+ id value = [attrs objectForKey:attrName];
+ if (value != nil) {
+ block(value, range, stop);
+ }
+ }];
+ } else {
+ __block id oldValue = nil;
+ __block NSRange effectiveRange = NSMakeRange(0, 0);
+ [self enumerateAttributesInRange:enumerationRange options:opts usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) {
+ id value = [attrs objectForKey:attrName];
+ if (oldValue == nil) {
+ oldValue = value;
+ effectiveRange = range;
+ } else if (value != nil && [oldValue isEqual:value]) {
+ // combine the attributes
+ effectiveRange = NSUnionRange(effectiveRange, range);
+ } else {
+ BOOL innerStop = NO;
+ block(oldValue, effectiveRange, &innerStop);
+ if (innerStop) {
+ *stop = YES;
+ oldValue = nil;
+ } else {
+ oldValue = value;
+ }
+ }
+ }];
+ if (oldValue != nil) {
+ BOOL innerStop = NO; // necessary for the block, but unused
+ block(oldValue, effectiveRange, &innerStop);
+ }
+ }
+}
+
+- (void)enumerateAttributesInRange:(NSRange)enumerationRange options:(ZAttributedStringEnumerationOptions)opts
+ usingBlock:(void (^)(NSDictionary*, NSRange, BOOL*))block {
+ // copy the attributes so we can mutate the string if necessary during enumeration
+ // also clip the array during copy to only the subarray of attributes that cover the requested range
+ NSArray *attrs;
+ if (NSEqualRanges(enumerationRange, NSMakeRange(0, 0))) {
+ attrs = [NSArray arrayWithArray:_attributes];
+ } else {
+ // in this binary search, last is the first run after the range
+ NSUInteger first = 0, last = [_attributes count];
+ while (last > first+1) {
+ NSUInteger pivot = (last + first) / 2;
+ ZAttributeRun *run = [_attributes objectAtIndex:pivot];
+ if (run.index < enumerationRange.location) {
+ first = pivot;
+ } else if (run.index >= NSMaxRange(enumerationRange)) {
+ last = pivot;
+ }
+ }
+ attrs = [_attributes subarrayWithRange:NSMakeRange(first, last-first)];
+ }
+ if (opts & ZAttributedStringEnumerationReverse) {
+ NSUInteger end = [_buffer length];
+ for (ZAttributeRun *run in [attrs reverseObjectEnumerator]) {
+ BOOL stop = NO;
+ NSUInteger start = run.index;
+ // clip to enumerationRange
+ start = MAX(start, enumerationRange.location);
+ end = MIN(end, NSMaxRange(enumerationRange));
+ block(run.attributes, NSMakeRange(start, end - start), &stop);
+ if (stop) break;
+ end = run.index;
+ }
+ } else {
+ NSUInteger start = 0;
+ ZAttributeRun *run = [attrs objectAtIndex:0];
+ NSInteger offset = 0;
+ NSInteger oldLength = [_buffer length];
+ for (NSUInteger i = 1;;i++) {
+ NSUInteger end;
+ if (i >= [attrs count]) {
+ end = oldLength;
+ } else {
+ end = [[attrs objectAtIndex:i] index];
+ }
+ BOOL stop = NO;
+ NSUInteger clippedStart = MAX(start, enumerationRange.location);
+ NSUInteger clippedEnd = MIN(end, NSMaxRange(enumerationRange));
+ block(run.attributes, NSMakeRange(clippedStart + offset, clippedEnd - start), &stop);
+ if (stop || i >= [attrs count]) break;
+ start = end;
+ NSUInteger newLength = [_buffer length];
+ offset += (newLength - oldLength);
+ oldLength = newLength;
+ }
+ }
+}
+#endif
+
+- (BOOL)isEqualToAttributedString:(ZAttributedString *)otherString {
+ return ([_buffer isEqualToString:otherString->_buffer] && [_attributes isEqualToArray:otherString->_attributes]);
+}
+
+- (BOOL)isEqual:(id)object {
+ return [object isKindOfClass:[ZAttributedString class]] && [self isEqualToAttributedString:(ZAttributedString *)object];
+}
+
+#pragma mark -
+
+- (NSUInteger)indexOfEffectiveAttributeRunForIndex:(NSUInteger)index {
+ NSUInteger first = 0, last = [_attributes count];
+ while (last > first + 1) {
+ NSUInteger pivot = (last + first) / 2;
+ ZAttributeRun *run = [_attributes objectAtIndex:pivot];
+ if (run.index > index) {
+ last = pivot;
+ } else if (run.index < index) {
+ first = pivot;
+ } else {
+ first = pivot;
+ break;
+ }
+ }
+ return first;
+}
+
+- (NSDictionary *)attributesAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange uniquingOnName:(NSString *)attributeName {
+ if (index >= [_buffer length]) {
+ @throw [NSException exceptionWithName:NSRangeException reason:@"index beyond range of attributed string" userInfo:nil];
+ }
+ NSUInteger runIndex = [self indexOfEffectiveAttributeRunForIndex:index];
+ ZAttributeRun *run = [_attributes objectAtIndex:runIndex];
+ if (aRange != NULL) {
+ aRange->location = run.index;
+ runIndex++;
+ if (runIndex < [_attributes count]) {
+ aRange->length = [[_attributes objectAtIndex:runIndex] index] - aRange->location;
+ } else {
+ aRange->length = [_buffer length] - aRange->location;
+ }
+ }
+ return run.attributes;
+}
+- (NSDictionary *)attributesAtIndex:(NSUInteger)index longestEffectiveRange:(NSRangePointer)aRange
+ inRange:(NSRange)rangeLimit uniquingOnName:(NSString *)attributeName {
+ if (index >= [_buffer length]) {
+ @throw [NSException exceptionWithName:NSRangeException reason:@"index beyond range of attributed string" userInfo:nil];
+ } else if (NSMaxRange(rangeLimit) > [_buffer length]) {
+ @throw [NSException exceptionWithName:NSRangeException reason:@"rangeLimit beyond range of attributed string" userInfo:nil];
+ }
+ NSUInteger runIndex = [self indexOfEffectiveAttributeRunForIndex:index];
+ ZAttributeRun *run = [_attributes objectAtIndex:runIndex];
+ if (aRange != NULL) {
+ if (attributeName != nil) {
+ id value = [run.attributes objectForKey:attributeName];
+ NSUInteger endRunIndex = runIndex+1;
+ runIndex--;
+ // search backwards
+ while (1) {
+ if (run.index <= rangeLimit.location) {
+ break;
+ }
+ ZAttributeRun *prevRun = [_attributes objectAtIndex:runIndex];
+ id prevValue = [prevRun.attributes objectForKey:attributeName];
+ if (prevValue == value || (value != nil && [prevValue isEqual:value])) {
+ runIndex--;
+ run = prevRun;
+ } else {
+ break;
+ }
+ }
+ // search forwards
+ ZAttributeRun *endRun = nil;
+ while (endRunIndex < [_attributes count]) {
+ ZAttributeRun *nextRun = [_attributes objectAtIndex:endRunIndex];
+ if (nextRun.index >= NSMaxRange(rangeLimit)) {
+ endRun = nextRun;
+ break;
+ }
+ id nextValue = [nextRun.attributes objectForKey:attributeName];
+ if (nextValue == value || (value != nil && [nextValue isEqual:value])) {
+ endRunIndex++;
+ } else {
+ endRun = nextRun;
+ break;
+ }
+ }
+ aRange->location = MAX(run.index, rangeLimit.location);
+ aRange->length = MIN((endRun ? endRun.index : [_buffer length]), NSMaxRange(rangeLimit)) - aRange->location;
+ } else {
+ // with no attribute name, we don't need to do any real searching,
+ // as we already guarantee each run has unique attributes.
+ // just make sure to clip the range to the rangeLimit
+ aRange->location = MAX(run.index, rangeLimit.location);
+ ZAttributeRun *endRun = (runIndex+1 < [_attributes count] ? [_attributes objectAtIndex:runIndex+1] : nil);
+ aRange->length = MIN((endRun ? endRun.index : [_buffer length]), NSMaxRange(rangeLimit)) - aRange->location;
+ }
+ }
+ return run.attributes;
+}
+
+- (void)dealloc {
+ [_buffer release];
+ [_attributes release];
+ [super dealloc];
+}
+@end
+
+@interface ZMutableAttributedString ()
+- (void)cleanupAttributesInRange:(NSRange)range;
+- (NSRange)rangeOfAttributeRunsForRange:(NSRange)range;
+- (void)offsetRunsInRange:(NSRange )range byOffset:(NSInteger)offset;
+@end
+
+@implementation ZMutableAttributedString
+- (id)copyWithZone:(NSZone *)zone {
+ return [(ZAttributedString *)[ZAttributedString allocWithZone:zone] initWithAttributedString:self];
+}
+
+- (void)addAttribute:(NSString *)name value:(id)value range:(NSRange)range {
+ range = [self rangeOfAttributeRunsForRange:range];
+ for (ZAttributeRun *run in [_attributes subarrayWithRange:range]) {
+ [run.attributes setObject:value forKey:name];
+ }
+ [self cleanupAttributesInRange:range];
+}
+
+- (void)addAttributes:(NSDictionary *)attributes range:(NSRange)range {
+ range = [self rangeOfAttributeRunsForRange:range];
+ for (ZAttributeRun *run in [_attributes subarrayWithRange:range]) {
+ [run.attributes addEntriesFromDictionary:attributes];
+ }
+ [self cleanupAttributesInRange:range];
+}
+
+- (void)appendAttributedString:(ZAttributedString *)str {
+ [self insertAttributedString:str atIndex:[_buffer length]];
+}
+
+- (void)deleteCharactersInRange:(NSRange)range {
+ NSRange runRange = [self rangeOfAttributeRunsForRange:range];
+ [_buffer replaceCharactersInRange:range withString:@""];
+ [_attributes removeObjectsInRange:runRange];
+ for (NSUInteger i = runRange.location; i < [_attributes count]; i++) {
+ ZAttributeRun *run = [_attributes objectAtIndex:i];
+ ZAttributeRun *newRun = [[ZAttributeRun alloc] initWithIndex:(run.index - range.length) attributes:run.attributes];
+ [_attributes replaceObjectAtIndex:i withObject:newRun];
+ [newRun release];
+ }
+ [self cleanupAttributesInRange:NSMakeRange(runRange.location, 0)];
+}
+
+- (void)insertAttributedString:(ZAttributedString *)str atIndex:(NSUInteger)idx {
+ [self replaceCharactersInRange:NSMakeRange(idx, 0) withAttributedString:str];
+}
+
+- (void)removeAttribute:(NSString *)name range:(NSRange)range {
+ range = [self rangeOfAttributeRunsForRange:range];
+ for (ZAttributeRun *run in [_attributes subarrayWithRange:range]) {
+ [run.attributes removeObjectForKey:name];
+ }
+ [self cleanupAttributesInRange:range];
+}
+
+- (void)replaceCharactersInRange:(NSRange)range withAttributedString:(ZAttributedString *)str {
+ NSRange replaceRange = [self rangeOfAttributeRunsForRange:range];
+ NSInteger offset = [str->_buffer length] - range.length;
+ [_buffer replaceCharactersInRange:range withString:str->_buffer];
+ [_attributes replaceObjectsInRange:replaceRange withObjectsFromArray:str->_attributes];
+ NSRange newRange = NSMakeRange(replaceRange.location, [str->_attributes count]);
+ [self offsetRunsInRange:newRange byOffset:range.location];
+ [self offsetRunsInRange:NSMakeRange(NSMaxRange(newRange), [_attributes count] - NSMaxRange(newRange)) byOffset:offset];
+ [self cleanupAttributesInRange:NSMakeRange(newRange.location, 0)];
+ [self cleanupAttributesInRange:NSMakeRange(NSMaxRange(newRange), 0)];
+}
+
+- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str {
+ [self replaceCharactersInRange:range withAttributedString:[[[ZAttributedString alloc] initWithString:str] autorelease]];
+}
+
+- (void)setAttributedString:(ZAttributedString *)str {
+ [_buffer release], _buffer = [str->_buffer mutableCopy];
+ [_attributes release], _attributes = [str->_attributes mutableCopy];
+}
+
+- (void)setAttributes:(NSDictionary *)attributes range:(NSRange)range {
+ range = [self rangeOfAttributeRunsForRange:range];
+ for (ZAttributeRun *run in [_attributes subarrayWithRange:range]) {
+ [run.attributes setDictionary:attributes];
+ }
+ [self cleanupAttributesInRange:range];
+}
+
+#pragma mark -
+
+// splits the existing runs to provide one or more new runs for the given range
+- (NSRange)rangeOfAttributeRunsForRange:(NSRange)range {
+ NSParameterAssert(NSMaxRange(range) <= [_buffer length]);
+
+ // find (or create) the first run
+ NSUInteger first = 0;
+ ZAttributeRun *lastRun = nil;
+ for (;;first++) {
+ if (first >= [_attributes count]) {
+ // we didn't find a run
+ first = [_attributes count];
+ ZAttributeRun *newRun = [[ZAttributeRun alloc] initWithIndex:range.location attributes:lastRun.attributes];
+ [_attributes addObject:newRun];
+ [newRun release];
+ break;
+ }
+ ZAttributeRun *run = [_attributes objectAtIndex:first];
+ if (run.index == range.location) {
+ break;
+ } else if (run.index > range.location) {
+ ZAttributeRun *newRun = [[ZAttributeRun alloc] initWithIndex:range.location attributes:lastRun.attributes];
+ [_attributes insertObject:newRun atIndex:first];
+ [newRun release];
+ break;
+ }
+ lastRun = run;
+ }
+
+ if (((ZAttributeRun *)[_attributes lastObject]).index < NSMaxRange(range)) {
+ NSRange subrange = NSMakeRange(first, [_attributes count] - first);
+ if (NSMaxRange(range) < [_buffer length]) {
+ ZAttributeRun *newRun = [[ZAttributeRun alloc] initWithIndex:NSMaxRange(range) attributes:[[_attributes lastObject] attributes]];
+ [_attributes addObject:newRun];
+ [newRun release];
+ }
+ return subrange;
+ } else {
+ // find the last run within and the first run after the range
+ NSUInteger lastIn = first, firstAfter = [_attributes count]-1;
+ while (firstAfter > lastIn + 1) {
+ NSUInteger idx = (firstAfter + lastIn) / 2;
+ ZAttributeRun *run = [_attributes objectAtIndex:idx];
+ if (run.index < range.location) {
+ lastIn = idx;
+ } else if (run.index > range.location) {
+ firstAfter = idx;
+ } else {
+ // this is definitively the first run after the range
+ firstAfter = idx;
+ break;
+ }
+ }
+ if ([[_attributes objectAtIndex:firstAfter] index] > NSMaxRange(range)) {
+ // the first after is too far after, insert another run!
+ ZAttributeRun *newRun = [[ZAttributeRun alloc] initWithIndex:NSMaxRange(range)
+ attributes:[[_attributes objectAtIndex:firstAfter-1] attributes]];
+ [_attributes insertObject:newRun atIndex:firstAfter];
+ [newRun release];
+ }
+ return NSMakeRange(lastIn, firstAfter - lastIn);
+ }
+}
+
+- (void)cleanupAttributesInRange:(NSRange)range {
+ // expand the range to include one surrounding attribute on each side
+ if (range.location > 0) {
+ range.location -= 1;
+ range.length += 1;
+ }
+ if (NSMaxRange(range) < [_attributes count]) {
+ range.length += 1;
+ } else {
+ // make sure the range is capped to the attributes count
+ range.length = [_attributes count] - range.location;
+ }
+ if (range.length == 0) return;
+ ZAttributeRun *lastRun = [_attributes objectAtIndex:range.location];
+ for (NSUInteger i = range.location+1; i < NSMaxRange(range);) {
+ ZAttributeRun *run = [_attributes objectAtIndex:i];
+ if ([lastRun.attributes isEqualToDictionary:run.attributes]) {
+ [_attributes removeObjectAtIndex:i];
+ range.length -= 1;
+ } else {
+ lastRun = run;
+ i++;
+ }
+ }
+}
+
+- (void)offsetRunsInRange:(NSRange)range byOffset:(NSInteger)offset {
+ for (NSUInteger i = range.location; i < NSMaxRange(range); i++) {
+ ZAttributeRun *run = [_attributes objectAtIndex:i];
+ ZAttributeRun *newRun = [[ZAttributeRun alloc] initWithIndex:run.index + offset attributes:run.attributes];
+ [_attributes replaceObjectAtIndex:i withObject:newRun];
+ [newRun release];
+ }
+}
+@end
+
+@implementation ZAttributeRun
+@synthesize index = _index;
+@synthesize attributes = _attributes;
+
++ (id)attributeRunWithIndex:(NSUInteger)idx attributes:(NSDictionary *)attrs {
+ return [[[self alloc] initWithIndex:idx attributes:attrs] autorelease];
+}
+
+- (id)initWithIndex:(NSUInteger)idx attributes:(NSDictionary *)attrs {
+ NSParameterAssert(idx >= 0);
+ if ((self = [super init])) {
+ _index = idx;
+ if (attrs == nil) {
+ _attributes = [[NSMutableDictionary alloc] init];
+ } else {
+ _attributes = [attrs mutableCopy];
+ }
+ }
+ return self;
+}
+
+- (id)initWithCoder:(NSCoder *)decoder {
+ if ((self = [super init])) {
+ _index = [[decoder decodeObjectForKey:@"index"] unsignedIntegerValue];
+ _attributes = [[decoder decodeObjectForKey:@"attributes"] mutableCopy];
+ }
+ return self;
+}
+
+- (id)init {
+ return [self initWithIndex:0 attributes:[NSDictionary dictionary]];
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+ return [[ZAttributeRun allocWithZone:zone] initWithIndex:_index attributes:_attributes];
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder {
+ [aCoder encodeObject:[NSNumber numberWithUnsignedInteger:_index] forKey:@"index"];
+ [aCoder encodeObject:_attributes forKey:@"attributes"];
+}
+
+- (NSString *)description {
+ NSMutableArray *components = [NSMutableArray arrayWithCapacity:[_attributes count]];
+ for (id key in _attributes) {
+ [components addObject:[NSString stringWithFormat:@"%@=%@", key, [_attributes objectForKey:key]]];
+ }
+ return [NSString stringWithFormat:@"<%@: %p index=%lu attributes={%@}>",
+ NSStringFromClass([self class]), self, (unsigned long)_index, [components componentsJoinedByString:@" "]];
+}
+
+- (BOOL)isEqual:(id)object {
+ if (![object isKindOfClass:[ZAttributeRun class]]) return NO;
+ ZAttributeRun *other = (ZAttributeRun *)object;
+ return _index == other->_index && [_attributes isEqualToDictionary:other->_attributes];
+}
+
+- (void)dealloc {
+ [_attributes release];
+ [super dealloc];
+}
+@end
+
+NSString * const ZFontAttributeName = @"ZFontAttributeName";
+NSString * const ZForegroundColorAttributeName = @"ZForegroundColorAttributeName";
+NSString * const ZBackgroundColorAttributeName = @"ZBackgroundColorAttributeName";
+NSString * const ZUnderlineStyleAttributeName = @"ZUnderlineStyleAttributeName";
--- /dev/null
+//
+// ZAttributedStringPrivate.h
+// FontLabel
+//
+// Created by Kevin Ballard on 9/23/09.
+// Copyright 2009 Zynga Game Networks. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "ZAttributedString.h"
+
+@interface ZAttributeRun : NSObject <NSCopying, NSCoding> {
+ NSUInteger _index;
+ NSMutableDictionary *_attributes;
+}
+@property (nonatomic, readonly) NSUInteger index;
+@property (nonatomic, readonly) NSMutableDictionary *attributes;
++ (id)attributeRunWithIndex:(NSUInteger)idx attributes:(NSDictionary *)attrs;
+- (id)initWithIndex:(NSUInteger)idx attributes:(NSDictionary *)attrs;
+@end
+
+@interface ZAttributedString (ZAttributedStringPrivate)
+@property (nonatomic, readonly) NSArray *attributes;
+@end
--- /dev/null
+//
+// ZFont.h
+// FontLabel
+//
+// Created by Kevin Ballard on 7/2/09.
+// Copyright © 2009 Zynga Game Networks
+//
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+
+@interface ZFont : NSObject {
+ CGFontRef _cgFont;
+ CGFloat _pointSize;
+ CGFloat _ratio;
+ NSString *_familyName;
+ NSString *_fontName;
+ NSString *_postScriptName;
+}
+@property (nonatomic, readonly) CGFontRef cgFont;
+@property (nonatomic, readonly) CGFloat pointSize;
+@property (nonatomic, readonly) CGFloat ascender;
+@property (nonatomic, readonly) CGFloat descender;
+@property (nonatomic, readonly) CGFloat leading;
+@property (nonatomic, readonly) CGFloat xHeight;
+@property (nonatomic, readonly) CGFloat capHeight;
+@property (nonatomic, readonly) NSString *familyName;
+@property (nonatomic, readonly) NSString *fontName;
+@property (nonatomic, readonly) NSString *postScriptName;
++ (ZFont *)fontWithCGFont:(CGFontRef)cgFont size:(CGFloat)fontSize;
++ (ZFont *)fontWithUIFont:(UIFont *)uiFont;
+- (id)initWithCGFont:(CGFontRef)cgFont size:(CGFloat)fontSize;
+- (ZFont *)fontWithSize:(CGFloat)fontSize;
+@end
--- /dev/null
+//
+// ZFont.m
+// FontLabel
+//
+// Created by Kevin Ballard on 7/2/09.
+// Copyright © 2009 Zynga Game Networks
+//
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "ZFont.h"
+
+@interface ZFont ()
+@property (nonatomic, readonly) CGFloat ratio;
+- (NSString *)copyNameTableEntryForID:(UInt16)nameID;
+@end
+
+@implementation ZFont
+@synthesize cgFont=_cgFont, pointSize=_pointSize, ratio=_ratio;
+
++ (ZFont *)fontWithCGFont:(CGFontRef)cgFont size:(CGFloat)fontSize {
+ return [[[self alloc] initWithCGFont:cgFont size:fontSize] autorelease];
+}
+
++ (ZFont *)fontWithUIFont:(UIFont *)uiFont {
+ NSParameterAssert(uiFont != nil);
+ CGFontRef cgFont = CGFontCreateWithFontName((CFStringRef)uiFont.fontName);
+ ZFont *zFont = [[self alloc] initWithCGFont:cgFont size:uiFont.pointSize];
+ CGFontRelease(cgFont);
+ return [zFont autorelease];
+}
+
+- (id)initWithCGFont:(CGFontRef)cgFont size:(CGFloat)fontSize {
+ if ((self = [super init])) {
+ _cgFont = CGFontRetain(cgFont);
+ _pointSize = fontSize;
+ _ratio = fontSize/CGFontGetUnitsPerEm(cgFont);
+ }
+ return self;
+}
+
+- (id)init {
+ NSAssert(NO, @"-init is not valid for ZFont");
+ return nil;
+}
+
+- (CGFloat)ascender {
+ return ceilf(self.ratio * CGFontGetAscent(self.cgFont));
+}
+
+- (CGFloat)descender {
+ return floorf(self.ratio * CGFontGetDescent(self.cgFont));
+}
+
+- (CGFloat)leading {
+ return (self.ascender - self.descender);
+}
+
+- (CGFloat)capHeight {
+ return ceilf(self.ratio * CGFontGetCapHeight(self.cgFont));
+}
+
+- (CGFloat)xHeight {
+ return ceilf(self.ratio * CGFontGetXHeight(self.cgFont));
+}
+
+- (NSString *)familyName {
+ if (_familyName == nil) {
+ _familyName = [self copyNameTableEntryForID:1];
+ }
+ return _familyName;
+}
+
+- (NSString *)fontName {
+ if (_fontName == nil) {
+ _fontName = [self copyNameTableEntryForID:4];
+ }
+ return _fontName;
+}
+
+- (NSString *)postScriptName {
+ if (_postScriptName == nil) {
+ _postScriptName = [self copyNameTableEntryForID:6];
+ }
+ return _postScriptName;
+}
+
+- (ZFont *)fontWithSize:(CGFloat)fontSize {
+ if (fontSize == self.pointSize) return self;
+ NSParameterAssert(fontSize > 0.0);
+ return [[[ZFont alloc] initWithCGFont:self.cgFont size:fontSize] autorelease];
+}
+
+- (BOOL)isEqual:(id)object {
+ if (![object isKindOfClass:[ZFont class]]) return NO;
+ ZFont *font = (ZFont *)object;
+ return (font.cgFont == self.cgFont && font.pointSize == self.pointSize);
+}
+
+- (NSString *)copyNameTableEntryForID:(UInt16)aNameID {
+ CFDataRef nameTable = CGFontCopyTableForTag(self.cgFont, 'name');
+ NSAssert1(nameTable != NULL, @"CGFontCopyTableForTag returned NULL for 'name' tag in font %@",
+ [(id)CFCopyDescription(self.cgFont) autorelease]);
+ const UInt8 * const bytes = CFDataGetBytePtr(nameTable);
+ NSAssert1(OSReadBigInt16(bytes, 0) == 0, @"name table for font %@ has bad version number",
+ [(id)CFCopyDescription(self.cgFont) autorelease]);
+ const UInt16 count = OSReadBigInt16(bytes, 2);
+ const UInt16 stringOffset = OSReadBigInt16(bytes, 4);
+ const UInt8 * const nameRecords = &bytes[6];
+ UInt16 nameLength = 0;
+ UInt16 nameOffset = 0;
+ NSStringEncoding encoding = 0;
+ for (UInt16 idx = 0; idx < count; idx++) {
+ const uintptr_t recordOffset = 12 * idx;
+ const UInt16 nameID = OSReadBigInt16(nameRecords, recordOffset + 6);
+ if (nameID != aNameID) continue;
+ const UInt16 platformID = OSReadBigInt16(nameRecords, recordOffset + 0);
+ const UInt16 platformSpecificID = OSReadBigInt16(nameRecords, recordOffset + 2);
+ encoding = 0;
+ // for now, we only support a subset of encodings
+ switch (platformID) {
+ case 0: // Unicode
+ encoding = NSUTF16StringEncoding;
+ break;
+ case 1: // Macintosh
+ switch (platformSpecificID) {
+ case 0:
+ encoding = NSMacOSRomanStringEncoding;
+ break;
+ }
+ case 3: // Microsoft
+ switch (platformSpecificID) {
+ case 1:
+ encoding = NSUTF16StringEncoding;
+ break;
+ }
+ }
+ if (encoding == 0) continue;
+ nameLength = OSReadBigInt16(nameRecords, recordOffset + 8);
+ nameOffset = OSReadBigInt16(nameRecords, recordOffset + 10);
+ break;
+ }
+ NSString *result = nil;
+ if (nameOffset > 0) {
+ const UInt8 *nameBytes = &bytes[stringOffset + nameOffset];
+ result = [[NSString alloc] initWithBytes:nameBytes length:nameLength encoding:encoding];
+ }
+ CFRelease(nameTable);
+ return result;
+}
+
+- (void)dealloc {
+ CGFontRelease(_cgFont);
+ [_familyName release];
+ [_fontName release];
+ [_postScriptName release];
+ [super dealloc];
+}
+@end
--- /dev/null
+//
+// CDataScanner.h
+// TouchCode
+//
+// Created by Jonathan Wight on 04/16/08.
+// Copyright 2008 toxicsoftware.com. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <Foundation/Foundation.h>
+
+// NSScanner
+
+@interface CDataScanner : NSObject {
+ NSData *data;
+
+ u_int8_t *start;
+ u_int8_t *end;
+ u_int8_t *current;
+ NSUInteger length;
+}
+
+@property (readwrite, nonatomic, retain) NSData *data;
+@property (readwrite, nonatomic, assign) NSUInteger scanLocation;
+@property (readonly, nonatomic, assign) NSUInteger bytesRemaining;
+@property (readonly, nonatomic, assign) BOOL isAtEnd;
+
+- (id)initWithData:(NSData *)inData;
+
+- (unichar)currentCharacter;
+- (unichar)scanCharacter;
+- (BOOL)scanCharacter:(unichar)inCharacter;
+
+- (BOOL)scanUTF8String:(const char *)inString intoString:(NSString **)outValue;
+- (BOOL)scanString:(NSString *)inString intoString:(NSString **)outValue;
+- (BOOL)scanCharactersFromSet:(NSCharacterSet *)inSet intoString:(NSString **)outValue; // inSet must only contain 7-bit ASCII characters
+
+- (BOOL)scanUpToString:(NSString *)string intoString:(NSString **)outValue;
+- (BOOL)scanUpToCharactersFromSet:(NSCharacterSet *)set intoString:(NSString **)outValue; // inSet must only contain 7-bit ASCII characters
+
+- (BOOL)scanNumber:(NSNumber **)outValue;
+- (BOOL)scanDecimalNumber:(NSDecimalNumber **)outValue;
+
+- (BOOL)scanDataOfLength:(NSUInteger)inLength intoData:(NSData **)outData;
+
+- (void)skipWhitespace;
+
+- (NSString *)remainingString;
+- (NSData *)remainingData;
+
+@end
--- /dev/null
+//
+// CDataScanner.m
+// TouchCode
+//
+// Created by Jonathan Wight on 04/16/08.
+// Copyright 2008 toxicsoftware.com. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "CDataScanner.h"
+
+#import "CDataScanner_Extensions.h"
+
+@interface CDataScanner ()
+@end
+
+#pragma mark -
+
+inline static unichar CharacterAtPointer(void *start, void *end)
+ {
+ #pragma unused(end)
+
+ const u_int8_t theByte = *(u_int8_t *)start;
+ if (theByte & 0x80)
+ {
+ // TODO -- UNICODE!!!! (well in theory nothing todo here)
+ }
+ const unichar theCharacter = theByte;
+ return(theCharacter);
+ }
+
+ static NSCharacterSet *sDoubleCharacters = NULL;
+
+ @implementation CDataScanner
+
+- (id)init
+ {
+ if ((self = [super init]) != NULL)
+ {
+ }
+ return(self);
+ }
+
+- (id)initWithData:(NSData *)inData;
+ {
+ if ((self = [self init]) != NULL)
+ {
+ [self setData:inData];
+ }
+ return(self);
+ }
+
+ + (void)initialize
+ {
+ if (sDoubleCharacters == NULL)
+ {
+ sDoubleCharacters = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789eE-+."] retain];
+ }
+ }
+
+- (void)dealloc
+ {
+ [data release];
+ data = NULL;
+ //
+ [super dealloc];
+ }
+
+- (NSUInteger)scanLocation
+ {
+ return(current - start);
+ }
+
+- (NSUInteger)bytesRemaining
+ {
+ return(end - current);
+ }
+
+- (NSData *)data
+ {
+ return(data);
+ }
+
+- (void)setData:(NSData *)inData
+ {
+ if (data != inData)
+ {
+ [data release];
+ data = [inData retain];
+ }
+
+ if (data)
+ {
+ start = (u_int8_t *)data.bytes;
+ end = start + data.length;
+ current = start;
+ length = data.length;
+ }
+ else
+ {
+ start = NULL;
+ end = NULL;
+ current = NULL;
+ length = 0;
+ }
+ }
+
+- (void)setScanLocation:(NSUInteger)inScanLocation
+ {
+ current = start + inScanLocation;
+ }
+
+- (BOOL)isAtEnd
+ {
+ return(self.scanLocation >= length);
+ }
+
+- (unichar)currentCharacter
+ {
+ return(CharacterAtPointer(current, end));
+ }
+
+#pragma mark -
+
+- (unichar)scanCharacter
+ {
+ const unichar theCharacter = CharacterAtPointer(current++, end);
+ return(theCharacter);
+ }
+
+- (BOOL)scanCharacter:(unichar)inCharacter
+ {
+ unichar theCharacter = CharacterAtPointer(current, end);
+ if (theCharacter == inCharacter)
+ {
+ ++current;
+ return(YES);
+ }
+ else
+ return(NO);
+ }
+
+- (BOOL)scanUTF8String:(const char *)inString intoString:(NSString **)outValue
+ {
+ const size_t theLength = strlen(inString);
+ if ((size_t)(end - current) < theLength)
+ return(NO);
+ if (strncmp((char *)current, inString, theLength) == 0)
+ {
+ current += theLength;
+ if (outValue)
+ *outValue = [NSString stringWithUTF8String:inString];
+ return(YES);
+ }
+ return(NO);
+ }
+
+- (BOOL)scanString:(NSString *)inString intoString:(NSString **)outValue
+ {
+ if ((size_t)(end - current) < inString.length)
+ return(NO);
+ if (strncmp((char *)current, [inString UTF8String], inString.length) == 0)
+ {
+ current += inString.length;
+ if (outValue)
+ *outValue = inString;
+ return(YES);
+ }
+ return(NO);
+ }
+
+- (BOOL)scanCharactersFromSet:(NSCharacterSet *)inSet intoString:(NSString **)outValue
+ {
+ u_int8_t *P;
+ for (P = current; P < end && [inSet characterIsMember:*P] == YES; ++P)
+ ;
+
+ if (P == current)
+ {
+ return(NO);
+ }
+
+ if (outValue)
+ {
+ *outValue = [[[NSString alloc] initWithBytes:current length:P - current encoding:NSUTF8StringEncoding] autorelease];
+ }
+
+ current = P;
+
+ return(YES);
+ }
+
+- (BOOL)scanUpToString:(NSString *)inString intoString:(NSString **)outValue
+ {
+ const char *theToken = [inString UTF8String];
+ const char *theResult = strnstr((char *)current, theToken, end - current);
+ if (theResult == NULL)
+ {
+ return(NO);
+ }
+
+ if (outValue)
+ {
+ *outValue = [[[NSString alloc] initWithBytes:current length:theResult - (char *)current encoding:NSUTF8StringEncoding] autorelease];
+ }
+
+ current = (u_int8_t *)theResult;
+
+ return(YES);
+ }
+
+- (BOOL)scanUpToCharactersFromSet:(NSCharacterSet *)inSet intoString:(NSString **)outValue
+ {
+ u_int8_t *P;
+ for (P = current; P < end && [inSet characterIsMember:*P] == NO; ++P)
+ ;
+
+ if (P == current)
+ {
+ return(NO);
+ }
+
+ if (outValue)
+ {
+ *outValue = [[[NSString alloc] initWithBytes:current length:P - current encoding:NSUTF8StringEncoding] autorelease];
+ }
+
+ current = P;
+
+ return(YES);
+ }
+
+- (BOOL)scanNumber:(NSNumber **)outValue
+ {
+ NSString *theString = NULL;
+ if ([self scanCharactersFromSet:sDoubleCharacters intoString:&theString])
+ {
+ if ([theString rangeOfString:@"."].location != NSNotFound)
+ {
+ if (outValue)
+ {
+ *outValue = [NSDecimalNumber decimalNumberWithString:theString];
+ }
+ return(YES);
+ }
+ else if ([theString rangeOfString:@"-"].location != NSNotFound)
+ {
+ if (outValue != NULL)
+ {
+ *outValue = [NSNumber numberWithLongLong:[theString longLongValue]];
+ }
+ return(YES);
+ }
+ else
+ {
+ if (outValue != NULL)
+ {
+ *outValue = [NSNumber numberWithUnsignedLongLong:strtoull([theString UTF8String], NULL, 0)];
+ }
+ return(YES);
+ }
+
+ }
+ return(NO);
+ }
+
+- (BOOL)scanDecimalNumber:(NSDecimalNumber **)outValue;
+ {
+ NSString *theString = NULL;
+ if ([self scanCharactersFromSet:sDoubleCharacters intoString:&theString])
+ {
+ if (outValue)
+ {
+ *outValue = [NSDecimalNumber decimalNumberWithString:theString];
+ }
+ return(YES);
+ }
+ return(NO);
+ }
+
+- (BOOL)scanDataOfLength:(NSUInteger)inLength intoData:(NSData **)outData;
+ {
+ if (self.bytesRemaining < inLength)
+ {
+ return(NO);
+ }
+
+ if (outData)
+ {
+ *outData = [NSData dataWithBytes:current length:inLength];
+ }
+
+ current += inLength;
+ return(YES);
+ }
+
+
+- (void)skipWhitespace
+ {
+ u_int8_t *P;
+ for (P = current; P < end && (isspace(*P)); ++P)
+ ;
+
+ current = P;
+ }
+
+- (NSString *)remainingString
+ {
+ NSData *theRemainingData = [NSData dataWithBytes:current length:end - current];
+ NSString *theString = [[[NSString alloc] initWithData:theRemainingData encoding:NSUTF8StringEncoding] autorelease];
+ return(theString);
+ }
+
+- (NSData *)remainingData;
+ {
+ NSData *theRemainingData = [NSData dataWithBytes:current length:end - current];
+ return(theRemainingData);
+ }
+
+ @end
--- /dev/null
+//
+// CDataScanner_Extensions.h
+// TouchCode
+//
+// Created by Jonathan Wight on 12/08/2005.
+// Copyright 2005 toxicsoftware.com. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "CDataScanner.h"
+
+@interface CDataScanner (CDataScanner_Extensions)
+
+- (BOOL)scanCStyleComment:(NSString **)outComment;
+- (BOOL)scanCPlusPlusStyleComment:(NSString **)outComment;
+
+- (NSUInteger)lineOfScanLocation;
+- (NSDictionary *)userInfoForScanLocation;
+
+@end
--- /dev/null
+//
+// CDataScanner_Extensions.m
+// TouchCode
+//
+// Created by Jonathan Wight on 12/08/2005.
+// Copyright 2005 toxicsoftware.com. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "CDataScanner_Extensions.h"
+
+#define LF 0x000a // Line Feed
+#define FF 0x000c // Form Feed
+#define CR 0x000d // Carriage Return
+#define NEL 0x0085 // Next Line
+#define LS 0x2028 // Line Separator
+#define PS 0x2029 // Paragraph Separator
+
+@implementation CDataScanner (CDataScanner_Extensions)
+
+- (BOOL)scanCStyleComment:(NSString **)outComment
+{
+if ([self scanString:@"/*" intoString:NULL] == YES)
+ {
+ NSString *theComment = NULL;
+ if ([self scanUpToString:@"*/" intoString:&theComment] == NO)
+ [NSException raise:NSGenericException format:@"Started to scan a C style comment but it wasn't terminated."];
+
+ if ([theComment rangeOfString:@"/*"].location != NSNotFound)
+ [NSException raise:NSGenericException format:@"C style comments should not be nested."];
+
+ if ([self scanString:@"*/" intoString:NULL] == NO)
+ [NSException raise:NSGenericException format:@"C style comment did not end correctly."];
+
+ if (outComment != NULL)
+ *outComment = theComment;
+
+ return(YES);
+ }
+else
+ {
+ return(NO);
+ }
+}
+
+- (BOOL)scanCPlusPlusStyleComment:(NSString **)outComment
+ {
+ if ([self scanString:@"//" intoString:NULL] == YES)
+ {
+ unichar theCharacters[] = { LF, FF, CR, NEL, LS, PS, };
+ NSCharacterSet *theLineBreaksCharacterSet = [NSCharacterSet characterSetWithCharactersInString:[NSString stringWithCharacters:theCharacters length:sizeof(theCharacters) / sizeof(*theCharacters)]];
+
+ NSString *theComment = NULL;
+ [self scanUpToCharactersFromSet:theLineBreaksCharacterSet intoString:&theComment];
+ [self scanCharactersFromSet:theLineBreaksCharacterSet intoString:NULL];
+
+ if (outComment != NULL)
+ *outComment = theComment;
+
+ return(YES);
+ }
+ else
+ {
+ return(NO);
+ }
+ }
+
+- (NSUInteger)lineOfScanLocation
+ {
+ NSUInteger theLine = 0;
+ for (const u_int8_t *C = start; C < current; ++C)
+ {
+ // TODO: JIW What about MS-DOS line endings you bastard! (Also other unicode line endings)
+ if (*C == '\n' || *C == '\r')
+ {
+ ++theLine;
+ }
+ }
+ return(theLine);
+ }
+
+- (NSDictionary *)userInfoForScanLocation
+ {
+ NSUInteger theLine = 0;
+ const u_int8_t *theLineStart = start;
+ for (const u_int8_t *C = start; C < current; ++C)
+ {
+ if (*C == '\n' || *C == '\r')
+ {
+ theLineStart = C - 1;
+ ++theLine;
+ }
+ }
+
+ NSUInteger theCharacter = current - theLineStart;
+
+ NSRange theStartRange = NSIntersectionRange((NSRange){ .location = MAX((NSInteger)self.scanLocation - 20, 0), .length = 20 + (NSInteger)self.scanLocation - 20 }, (NSRange){ .location = 0, .length = self.data.length });
+ NSRange theEndRange = NSIntersectionRange((NSRange){ .location = self.scanLocation, .length = 20 }, (NSRange){ .location = 0, .length = self.data.length });
+
+
+ NSString *theSnippet = [NSString stringWithFormat:@"%@!HERE>!%@",
+ [[[NSString alloc] initWithData:[self.data subdataWithRange:theStartRange] encoding:NSUTF8StringEncoding] autorelease],
+ [[[NSString alloc] initWithData:[self.data subdataWithRange:theEndRange] encoding:NSUTF8StringEncoding] autorelease]
+ ];
+
+ NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithUnsignedInteger:theLine], @"line",
+ [NSNumber numberWithUnsignedInteger:theCharacter], @"character",
+ [NSNumber numberWithUnsignedInteger:self.scanLocation], @"location",
+ theSnippet, @"snippet",
+ NULL];
+ return(theUserInfo);
+ }
+
+@end
--- /dev/null
+//
+// NSDictionary_JSONExtensions.h
+// TouchCode
+//
+// Created by Jonathan Wight on 04/17/08.
+// Copyright 2008 toxicsoftware.com. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface NSDictionary (NSDictionary_JSONExtensions)
+
++ (id)dictionaryWithJSONData:(NSData *)inData error:(NSError **)outError;
++ (id)dictionaryWithJSONString:(NSString *)inJSON error:(NSError **)outError;
+
+@end
--- /dev/null
+//
+// NSDictionary_JSONExtensions.m
+// TouchCode
+//
+// Created by Jonathan Wight on 04/17/08.
+// Copyright 2008 toxicsoftware.com. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "NSDictionary_JSONExtensions.h"
+
+#import "CJSONDeserializer.h"
+
+@implementation NSDictionary (NSDictionary_JSONExtensions)
+
++ (id)dictionaryWithJSONData:(NSData *)inData error:(NSError **)outError
+ {
+ return([[CJSONDeserializer deserializer] deserialize:inData error:outError]);
+ }
+
++ (id)dictionaryWithJSONString:(NSString *)inJSON error:(NSError **)outError;
+ {
+ NSData *theData = [inJSON dataUsingEncoding:NSUTF8StringEncoding];
+ return([self dictionaryWithJSONData:theData error:outError]);
+ }
+
+@end
--- /dev/null
+//
+// CJSONDeserializer.h
+// TouchCode
+//
+// Created by Jonathan Wight on 12/15/2005.
+// Copyright 2005 toxicsoftware.com. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <Foundation/Foundation.h>
+
+#import "CJSONScanner.h"
+
+extern NSString *const kJSONDeserializerErrorDomain /* = @"CJSONDeserializerErrorDomain" */;
+
+enum {
+ kJSONDeserializationOptions_MutableContainers = kJSONScannerOptions_MutableContainers,
+ kJSONDeserializationOptions_MutableLeaves = kJSONScannerOptions_MutableLeaves,
+};
+typedef NSUInteger EJSONDeserializationOptions;
+
+@class CJSONScanner;
+
+@interface CJSONDeserializer : NSObject {
+ CJSONScanner *scanner;
+ EJSONDeserializationOptions options;
+}
+
+@property (readwrite, nonatomic, retain) CJSONScanner *scanner;
+/// Object to return instead when a null encountered in the JSON. Defaults to NSNull. Setting to null causes the scanner to skip null values.
+@property (readwrite, nonatomic, retain) id nullObject;
+/// JSON must be encoded in Unicode (UTF-8, UTF-16 or UTF-32). Use this if you expect to get the JSON in another encoding.
+@property (readwrite, nonatomic, assign) NSStringEncoding allowedEncoding;
+@property (readwrite, nonatomic, assign) EJSONDeserializationOptions options;
+
++ (id)deserializer;
+
+- (id)deserialize:(NSData *)inData error:(NSError **)outError;
+
+- (id)deserializeAsDictionary:(NSData *)inData error:(NSError **)outError;
+- (id)deserializeAsArray:(NSData *)inData error:(NSError **)outError;
+
+@end
--- /dev/null
+//
+// CJSONDeserializer.m
+// TouchCode
+//
+// Created by Jonathan Wight on 12/15/2005.
+// Copyright 2005 toxicsoftware.com. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "CJSONDeserializer.h"
+
+#import "CJSONScanner.h"
+#import "CDataScanner.h"
+
+NSString *const kJSONDeserializerErrorDomain = @"CJSONDeserializerErrorDomain";
+
+@interface CJSONDeserializer ()
+@end
+
+@implementation CJSONDeserializer
+
+@synthesize scanner;
+@synthesize options;
+
++ (id)deserializer
+ {
+ return([[[self alloc] init] autorelease]);
+ }
+
+- (id)init
+ {
+ if ((self = [super init]) != NULL)
+ {
+ }
+ return(self);
+ }
+
+- (void)dealloc
+ {
+ [scanner release];
+ scanner = NULL;
+ //
+ [super dealloc];
+ }
+
+#pragma mark -
+
+- (CJSONScanner *)scanner
+ {
+ if (scanner == NULL)
+ {
+ scanner = [[CJSONScanner alloc] init];
+ }
+ return(scanner);
+ }
+
+- (id)nullObject
+ {
+ return(self.scanner.nullObject);
+ }
+
+- (void)setNullObject:(id)inNullObject
+ {
+ self.scanner.nullObject = inNullObject;
+ }
+
+#pragma mark -
+
+- (NSStringEncoding)allowedEncoding
+ {
+ return(self.scanner.allowedEncoding);
+ }
+
+- (void)setAllowedEncoding:(NSStringEncoding)inAllowedEncoding
+ {
+ self.scanner.allowedEncoding = inAllowedEncoding;
+ }
+
+#pragma mark -
+
+- (id)deserialize:(NSData *)inData error:(NSError **)outError
+ {
+ if (inData == NULL || [inData length] == 0)
+ {
+ if (outError)
+ *outError = [NSError errorWithDomain:kJSONDeserializerErrorDomain code:kJSONScannerErrorCode_NothingToScan userInfo:NULL];
+
+ return(NULL);
+ }
+ if ([self.scanner setData:inData error:outError] == NO)
+ {
+ return(NULL);
+ }
+ id theObject = NULL;
+ if ([self.scanner scanJSONObject:&theObject error:outError] == YES)
+ return(theObject);
+ else
+ return(NULL);
+ }
+
+- (id)deserializeAsDictionary:(NSData *)inData error:(NSError **)outError
+ {
+ if (inData == NULL || [inData length] == 0)
+ {
+ if (outError)
+ *outError = [NSError errorWithDomain:kJSONDeserializerErrorDomain code:kJSONScannerErrorCode_NothingToScan userInfo:NULL];
+
+ return(NULL);
+ }
+ if ([self.scanner setData:inData error:outError] == NO)
+ {
+ return(NULL);
+ }
+ NSDictionary *theDictionary = NULL;
+ if ([self.scanner scanJSONDictionary:&theDictionary error:outError] == YES)
+ return(theDictionary);
+ else
+ return(NULL);
+ }
+
+- (id)deserializeAsArray:(NSData *)inData error:(NSError **)outError
+ {
+ if (inData == NULL || [inData length] == 0)
+ {
+ if (outError)
+ *outError = [NSError errorWithDomain:kJSONDeserializerErrorDomain code:kJSONScannerErrorCode_NothingToScan userInfo:NULL];
+
+ return(NULL);
+ }
+ if ([self.scanner setData:inData error:outError] == NO)
+ {
+ return(NULL);
+ }
+ NSArray *theArray = NULL;
+ if ([self.scanner scanJSONArray:&theArray error:outError] == YES)
+ return(theArray);
+ else
+ return(NULL);
+ }
+
+@end
--- /dev/null
+//
+// CJSONScanner.h
+// TouchCode
+//
+// Created by Jonathan Wight on 12/07/2005.
+// Copyright 2005 toxicsoftware.com. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "CDataScanner.h"
+
+enum {
+ kJSONScannerOptions_MutableContainers = 0x1,
+ kJSONScannerOptions_MutableLeaves = 0x2,
+};
+typedef NSUInteger EJSONScannerOptions;
+
+/// CDataScanner subclass that understands JSON syntax natively. You should generally use CJSONDeserializer instead of this class. (TODO - this could have been a category?)
+@interface CJSONScanner : CDataScanner {
+ BOOL strictEscapeCodes;
+ id nullObject;
+ NSStringEncoding allowedEncoding;
+ EJSONScannerOptions options;
+}
+
+@property (readwrite, nonatomic, assign) BOOL strictEscapeCodes;
+@property (readwrite, nonatomic, retain) id nullObject;
+@property (readwrite, nonatomic, assign) NSStringEncoding allowedEncoding;
+@property (readwrite, nonatomic, assign) EJSONScannerOptions options;
+
+- (BOOL)setData:(NSData *)inData error:(NSError **)outError;
+
+- (BOOL)scanJSONObject:(id *)outObject error:(NSError **)outError;
+- (BOOL)scanJSONDictionary:(NSDictionary **)outDictionary error:(NSError **)outError;
+- (BOOL)scanJSONArray:(NSArray **)outArray error:(NSError **)outError;
+- (BOOL)scanJSONStringConstant:(NSString **)outStringConstant error:(NSError **)outError;
+- (BOOL)scanJSONNumberConstant:(NSNumber **)outNumberConstant error:(NSError **)outError;
+
+@end
+
+extern NSString *const kJSONScannerErrorDomain /* = @"kJSONScannerErrorDomain" */;
+
+typedef enum {
+
+ // Fundamental scanning errors
+ kJSONScannerErrorCode_NothingToScan = -11,
+ kJSONScannerErrorCode_CouldNotDecodeData = -12,
+ kJSONScannerErrorCode_CouldNotSerializeData = -13,
+ kJSONScannerErrorCode_CouldNotSerializeObject = -14,
+ kJSONScannerErrorCode_CouldNotScanObject = -15,
+
+ // Dictionary scanning
+ kJSONScannerErrorCode_DictionaryStartCharacterMissing = -101,
+ kJSONScannerErrorCode_DictionaryKeyScanFailed = -102,
+ kJSONScannerErrorCode_DictionaryKeyNotTerminated = -103,
+ kJSONScannerErrorCode_DictionaryValueScanFailed = -104,
+ kJSONScannerErrorCode_DictionaryKeyValuePairNoDelimiter = -105,
+ kJSONScannerErrorCode_DictionaryNotTerminated = -106,
+
+ // Array scanning
+ kJSONScannerErrorCode_ArrayStartCharacterMissing = -201,
+ kJSONScannerErrorCode_ArrayValueScanFailed = -202,
+ kJSONScannerErrorCode_ArrayValueIsNull = -203,
+ kJSONScannerErrorCode_ArrayNotTerminated = -204,
+
+ // String scanning
+ kJSONScannerErrorCode_StringNotStartedWithBackslash = -301,
+ kJSONScannerErrorCode_StringUnicodeNotDecoded = -302,
+ kJSONScannerErrorCode_StringUnknownEscapeCode = -303,
+ kJSONScannerErrorCode_StringNotTerminated = -304,
+
+ // Number scanning
+ kJSONScannerErrorCode_NumberNotScannable = -401
+
+} EJSONScannerErrorCode;
--- /dev/null
+//
+// CJSONScanner.m
+// TouchCode
+//
+// Created by Jonathan Wight on 12/07/2005.
+// Copyright 2005 toxicsoftware.com. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "CJSONScanner.h"
+
+#import "CDataScanner_Extensions.h"
+
+#if !defined(TREAT_COMMENTS_AS_WHITESPACE)
+#define TREAT_COMMENTS_AS_WHITESPACE 0
+#endif // !defined(TREAT_COMMENTS_AS_WHITESPACE)
+
+NSString *const kJSONScannerErrorDomain = @"kJSONScannerErrorDomain";
+
+inline static int HexToInt(char inCharacter)
+ {
+ int theValues[] = { 0x0 /* 48 '0' */, 0x1 /* 49 '1' */, 0x2 /* 50 '2' */, 0x3 /* 51 '3' */, 0x4 /* 52 '4' */, 0x5 /* 53 '5' */, 0x6 /* 54 '6' */, 0x7 /* 55 '7' */, 0x8 /* 56 '8' */, 0x9 /* 57 '9' */, -1 /* 58 ':' */, -1 /* 59 ';' */, -1 /* 60 '<' */, -1 /* 61 '=' */, -1 /* 62 '>' */, -1 /* 63 '?' */, -1 /* 64 '@' */, 0xa /* 65 'A' */, 0xb /* 66 'B' */, 0xc /* 67 'C' */, 0xd /* 68 'D' */, 0xe /* 69 'E' */, 0xf /* 70 'F' */, -1 /* 71 'G' */, -1 /* 72 'H' */, -1 /* 73 'I' */, -1 /* 74 'J' */, -1 /* 75 'K' */, -1 /* 76 'L' */, -1 /* 77 'M' */, -1 /* 78 'N' */, -1 /* 79 'O' */, -1 /* 80 'P' */, -1 /* 81 'Q' */, -1 /* 82 'R' */, -1 /* 83 'S' */, -1 /* 84 'T' */, -1 /* 85 'U' */, -1 /* 86 'V' */, -1 /* 87 'W' */, -1 /* 88 'X' */, -1 /* 89 'Y' */, -1 /* 90 'Z' */, -1 /* 91 '[' */, -1 /* 92 '\' */, -1 /* 93 ']' */, -1 /* 94 '^' */, -1 /* 95 '_' */, -1 /* 96 '`' */, 0xa /* 97 'a' */, 0xb /* 98 'b' */, 0xc /* 99 'c' */, 0xd /* 100 'd' */, 0xe /* 101 'e' */, 0xf /* 102 'f' */, };
+ if (inCharacter >= '0' && inCharacter <= 'f')
+ return(theValues[inCharacter - '0']);
+ else
+ return(-1);
+ }
+
+@interface CJSONScanner ()
+- (BOOL)scanNotQuoteCharactersIntoString:(NSString **)outValue;
+@end
+
+#pragma mark -
+
+@implementation CJSONScanner
+
+@synthesize strictEscapeCodes;
+@synthesize nullObject;
+@synthesize allowedEncoding;
+@synthesize options;
+
+- (id)init
+ {
+ if ((self = [super init]) != NULL)
+ {
+ strictEscapeCodes = NO;
+ nullObject = [[NSNull null] retain];
+ }
+ return(self);
+ }
+
+- (void)dealloc
+ {
+ [nullObject release];
+ nullObject = NULL;
+ //
+ [super dealloc];
+ }
+
+#pragma mark -
+
+- (BOOL)setData:(NSData *)inData error:(NSError **)outError;
+ {
+ NSData *theData = inData;
+ if (theData && theData.length >= 4)
+ {
+ // This code is lame, but it works. Because the first character of any JSON string will always be a (ascii) control character we can work out the Unicode encoding by the bit pattern. See section 3 of http://www.ietf.org/rfc/rfc4627.txt
+ const char *theChars = theData.bytes;
+ NSStringEncoding theEncoding = NSUTF8StringEncoding;
+ if (theChars[0] != 0 && theChars[1] == 0)
+ {
+ if (theChars[2] != 0 && theChars[3] == 0)
+ theEncoding = NSUTF16LittleEndianStringEncoding;
+ else if (theChars[2] == 0 && theChars[3] == 0)
+ theEncoding = NSUTF32LittleEndianStringEncoding;
+ }
+ else if (theChars[0] == 0 && theChars[2] == 0 && theChars[3] != 0)
+ {
+ if (theChars[1] == 0)
+ theEncoding = NSUTF32BigEndianStringEncoding;
+ else if (theChars[1] != 0)
+ theEncoding = NSUTF16BigEndianStringEncoding;
+ }
+
+ NSString *theString = [[NSString alloc] initWithData:theData encoding:theEncoding];
+ if (theString == NULL && self.allowedEncoding != 0)
+ {
+ theString = [[NSString alloc] initWithData:theData encoding:self.allowedEncoding];
+ }
+ theData = [theString dataUsingEncoding:NSUTF8StringEncoding];
+ [theString release];
+ }
+
+ if (theData)
+ {
+ [super setData:theData];
+ return(YES);
+ }
+ else
+ {
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan data. Data wasn't encoded properly?", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_CouldNotDecodeData userInfo:theUserInfo];
+ }
+ return(NO);
+ }
+ }
+
+- (void)setData:(NSData *)inData
+ {
+ [self setData:inData error:NULL];
+ }
+
+#pragma mark -
+
+- (BOOL)scanJSONObject:(id *)outObject error:(NSError **)outError
+ {
+ BOOL theResult = YES;
+
+ [self skipWhitespace];
+
+ id theObject = NULL;
+
+ const unichar C = [self currentCharacter];
+ switch (C)
+ {
+ case 't':
+ if ([self scanUTF8String:"true" intoString:NULL])
+ {
+ theObject = [NSNumber numberWithBool:YES];
+ }
+ break;
+ case 'f':
+ if ([self scanUTF8String:"false" intoString:NULL])
+ {
+ theObject = [NSNumber numberWithBool:NO];
+ }
+ break;
+ case 'n':
+ if ([self scanUTF8String:"null" intoString:NULL])
+ {
+ theObject = self.nullObject;
+ }
+ break;
+ case '\"':
+ case '\'':
+ theResult = [self scanJSONStringConstant:&theObject error:outError];
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '-':
+ theResult = [self scanJSONNumberConstant:&theObject error:outError];
+ break;
+ case '{':
+ theResult = [self scanJSONDictionary:&theObject error:outError];
+ break;
+ case '[':
+ theResult = [self scanJSONArray:&theObject error:outError];
+ break;
+ default:
+ theResult = NO;
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan object. Character not a valid JSON character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_CouldNotScanObject userInfo:theUserInfo];
+ }
+ break;
+ }
+
+ if (outObject != NULL)
+ *outObject = theObject;
+
+ return(theResult);
+ }
+
+- (BOOL)scanJSONDictionary:(NSDictionary **)outDictionary error:(NSError **)outError
+ {
+ NSUInteger theScanLocation = [self scanLocation];
+
+ [self skipWhitespace];
+
+ if ([self scanCharacter:'{'] == NO)
+ {
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan dictionary. Dictionary that does not start with '{' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryStartCharacterMissing userInfo:theUserInfo];
+ }
+ return(NO);
+ }
+
+ NSMutableDictionary *theDictionary = [[NSMutableDictionary alloc] init];
+
+ while ([self currentCharacter] != '}')
+ {
+ [self skipWhitespace];
+
+ if ([self currentCharacter] == '}')
+ break;
+
+ NSString *theKey = NULL;
+ if ([self scanJSONStringConstant:&theKey error:outError] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan dictionary. Failed to scan a key.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryKeyScanFailed userInfo:theUserInfo];
+ }
+ [theDictionary release];
+ return(NO);
+ }
+
+ [self skipWhitespace];
+
+ if ([self scanCharacter:':'] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan dictionary. Key was not terminated with a ':' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryKeyNotTerminated userInfo:theUserInfo];
+ }
+ [theDictionary release];
+ return(NO);
+ }
+
+ id theValue = NULL;
+ if ([self scanJSONObject:&theValue error:outError] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan dictionary. Failed to scan a value.", NSLocalizedDescriptionKey,
+ NULL];
+
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryValueScanFailed userInfo:theUserInfo];
+ }
+ [theDictionary release];
+ return(NO);
+ }
+
+ if (theValue == NULL && self.nullObject == NULL)
+ {
+ // If the value is a null and nullObject is also null then we're skipping this key/value pair.
+ }
+ else
+ {
+ [theDictionary setValue:theValue forKey:theKey];
+ }
+
+ [self skipWhitespace];
+ if ([self scanCharacter:','] == NO)
+ {
+ if ([self currentCharacter] != '}')
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan dictionary. Key value pairs not delimited with a ',' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryKeyValuePairNoDelimiter userInfo:theUserInfo];
+ }
+ [theDictionary release];
+ return(NO);
+ }
+ break;
+ }
+ else
+ {
+ [self skipWhitespace];
+ if ([self currentCharacter] == '}')
+ break;
+ }
+ }
+
+ if ([self scanCharacter:'}'] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan dictionary. Dictionary not terminated by a '}' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryNotTerminated userInfo:theUserInfo];
+ }
+ [theDictionary release];
+ return(NO);
+ }
+
+ if (outDictionary != NULL)
+ {
+ if (self.options & kJSONScannerOptions_MutableContainers)
+ {
+ *outDictionary = [theDictionary autorelease];
+ }
+ else
+ {
+ *outDictionary = [[theDictionary copy] autorelease];
+ [theDictionary release];
+ }
+ }
+ else
+ {
+ [theDictionary release];
+ }
+
+ return(YES);
+ }
+
+- (BOOL)scanJSONArray:(NSArray **)outArray error:(NSError **)outError
+ {
+ NSUInteger theScanLocation = [self scanLocation];
+
+ [self skipWhitespace];
+
+ if ([self scanCharacter:'['] == NO)
+ {
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan array. Array not started by a '[' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayStartCharacterMissing userInfo:theUserInfo];
+ }
+ return(NO);
+ }
+
+ NSMutableArray *theArray = [[NSMutableArray alloc] init];
+
+ [self skipWhitespace];
+ while ([self currentCharacter] != ']')
+ {
+ NSString *theValue = NULL;
+ if ([self scanJSONObject:&theValue error:outError] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan array. Could not scan a value.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayValueScanFailed userInfo:theUserInfo];
+ }
+ [theArray release];
+ return(NO);
+ }
+
+ if (theValue == NULL)
+ {
+ if (self.nullObject != NULL)
+ {
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan array. Value is NULL.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayValueIsNull userInfo:theUserInfo];
+ }
+ [theArray release];
+ return(NO);
+ }
+ }
+ else
+ {
+ [theArray addObject:theValue];
+ }
+
+ [self skipWhitespace];
+ if ([self scanCharacter:','] == NO)
+ {
+ [self skipWhitespace];
+ if ([self currentCharacter] != ']')
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan array. Array not terminated by a ']' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayNotTerminated userInfo:theUserInfo];
+ }
+ [theArray release];
+ return(NO);
+ }
+
+ break;
+ }
+ [self skipWhitespace];
+ }
+
+ [self skipWhitespace];
+
+ if ([self scanCharacter:']'] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan array. Array not terminated by a ']' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayNotTerminated userInfo:theUserInfo];
+ }
+ [theArray release];
+ return(NO);
+ }
+
+ if (outArray != NULL)
+ {
+ if (self.options & kJSONScannerOptions_MutableContainers)
+ {
+ *outArray = [theArray autorelease];
+ }
+ else
+ {
+ *outArray = [[theArray copy] autorelease];
+ [theArray release];
+ }
+ }
+ else
+ {
+ [theArray release];
+ }
+ return(YES);
+ }
+
+- (BOOL)scanJSONStringConstant:(NSString **)outStringConstant error:(NSError **)outError
+ {
+ NSUInteger theScanLocation = [self scanLocation];
+
+ [self skipWhitespace];
+
+ NSMutableString *theString = [[NSMutableString alloc] init];
+
+ if ([self scanCharacter:'"'] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan string constant. String not started by a '\"' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_StringNotStartedWithBackslash userInfo:theUserInfo];
+ }
+ [theString release];
+ return(NO);
+ }
+
+ while ([self scanCharacter:'"'] == NO)
+ {
+ NSString *theStringChunk = NULL;
+ if ([self scanNotQuoteCharactersIntoString:&theStringChunk])
+ {
+ CFStringAppend((CFMutableStringRef)theString, (CFStringRef)theStringChunk);
+ }
+ else if ([self scanCharacter:'\\'] == YES)
+ {
+ unichar theCharacter = [self scanCharacter];
+ switch (theCharacter)
+ {
+ case '"':
+ case '\\':
+ case '/':
+ break;
+ case 'b':
+ theCharacter = '\b';
+ break;
+ case 'f':
+ theCharacter = '\f';
+ break;
+ case 'n':
+ theCharacter = '\n';
+ break;
+ case 'r':
+ theCharacter = '\r';
+ break;
+ case 't':
+ theCharacter = '\t';
+ break;
+ case 'u':
+ {
+ theCharacter = 0;
+
+ int theShift;
+ for (theShift = 12; theShift >= 0; theShift -= 4)
+ {
+ const int theDigit = HexToInt([self scanCharacter]);
+ if (theDigit == -1)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan string constant. Unicode character could not be decoded.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_StringUnicodeNotDecoded userInfo:theUserInfo];
+ }
+ [theString release];
+ return(NO);
+ }
+ theCharacter |= (theDigit << theShift);
+ }
+ }
+ break;
+ default:
+ {
+ if (strictEscapeCodes == YES)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan string constant. Unknown escape code.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_StringUnknownEscapeCode userInfo:theUserInfo];
+ }
+ [theString release];
+ return(NO);
+ }
+ }
+ break;
+ }
+ CFStringAppendCharacters((CFMutableStringRef)theString, &theCharacter, 1);
+ }
+ else
+ {
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan string constant. No terminating double quote character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_StringNotTerminated userInfo:theUserInfo];
+ }
+ [theString release];
+ return(NO);
+ }
+ }
+
+ if (outStringConstant != NULL)
+ {
+ if (self.options & kJSONScannerOptions_MutableLeaves)
+ {
+ *outStringConstant = [theString autorelease];
+ }
+ else
+ {
+ *outStringConstant = [[theString copy] autorelease];
+ [theString release];
+ }
+ }
+ else
+ {
+ [theString release];
+ }
+
+ return(YES);
+ }
+
+- (BOOL)scanJSONNumberConstant:(NSNumber **)outNumberConstant error:(NSError **)outError
+ {
+ NSNumber *theNumber = NULL;
+
+ [self skipWhitespace];
+
+ if ([self scanNumber:&theNumber] == YES)
+ {
+ if (outNumberConstant != NULL)
+ *outNumberConstant = theNumber;
+ return(YES);
+ }
+ else
+ {
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan number constant.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_NumberNotScannable userInfo:theUserInfo];
+ }
+ return(NO);
+ }
+ }
+
+#if TREAT_COMMENTS_AS_WHITESPACE
+- (void)skipWhitespace
+ {
+ [super skipWhitespace];
+ [self scanCStyleComment:NULL];
+ [self scanCPlusPlusStyleComment:NULL];
+ [super skipWhitespace];
+ }
+#endif // TREAT_COMMENTS_AS_WHITESPACE
+
+#pragma mark -
+
+- (BOOL)scanNotQuoteCharactersIntoString:(NSString **)outValue
+ {
+ u_int8_t *P;
+ for (P = current; P < end && *P != '\"' && *P != '\\'; ++P)
+ ;
+
+ if (P == current)
+ {
+ return(NO);
+ }
+
+ if (outValue)
+ {
+ *outValue = [[[NSString alloc] initWithBytes:current length:P - current encoding:NSUTF8StringEncoding] autorelease];
+ }
+
+ current = P;
+
+ return(YES);
+ }
+
+@end
--- /dev/null
+//
+// CJSONSerializer.h
+// TouchCode
+//
+// Created by Jonathan Wight on 12/07/2005.
+// Copyright 2005 toxicsoftware.com. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface CJSONSerializer : NSObject {
+}
+
++ (id)serializer;
+
+- (BOOL)isValidJSONObject:(id)inObject;
+
+/// Take any JSON compatible object (generally NSNull, NSNumber, NSString, NSArray and NSDictionary) and produce an NSData containing the serialized JSON.
+- (NSData *)serializeObject:(id)inObject error:(NSError **)outError;
+
+- (NSData *)serializeNull:(NSNull *)inNull error:(NSError **)outError;
+- (NSData *)serializeNumber:(NSNumber *)inNumber error:(NSError **)outError;
+- (NSData *)serializeString:(NSString *)inString error:(NSError **)outError;
+- (NSData *)serializeArray:(NSArray *)inArray error:(NSError **)outError;
+- (NSData *)serializeDictionary:(NSDictionary *)inDictionary error:(NSError **)outError;
+
+@end
+
+typedef enum {
+ CJSONSerializerErrorCouldNotSerializeDataType = -1,
+ CJSONSerializerErrorCouldNotSerializeObject = -1
+} CJSONSerializerError;
--- /dev/null
+//
+// CJSONSerializer.m
+// TouchCode
+//
+// Created by Jonathan Wight on 12/07/2005.
+// Copyright 2005 toxicsoftware.com. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "CJSONSerializer.h"
+
+#import "JSONRepresentation.h"
+
+static NSData *kNULL = NULL;
+static NSData *kFalse = NULL;
+static NSData *kTrue = NULL;
+
+@implementation CJSONSerializer
+
++ (void)initialize
+ {
+ NSAutoreleasePool *thePool = [[NSAutoreleasePool alloc] init];
+
+ if (self == [CJSONSerializer class])
+ {
+ if (kNULL == NULL)
+ kNULL = [[NSData alloc] initWithBytesNoCopy:(void *)"null" length:4 freeWhenDone:NO];
+ if (kFalse == NULL)
+ kFalse = [[NSData alloc] initWithBytesNoCopy:(void *)"false" length:5 freeWhenDone:NO];
+ if (kTrue == NULL)
+ kTrue = [[NSData alloc] initWithBytesNoCopy:(void *)"true" length:4 freeWhenDone:NO];
+
+ [thePool release];
+ }
+ }
+
++ (id)serializer
+ {
+ return([[[self alloc] init] autorelease]);
+ }
+
+- (BOOL)isValidJSONObject:(id)inObject
+ {
+ if ([inObject isKindOfClass:[NSNull class]])
+ {
+ return(YES);
+ }
+ else if ([inObject isKindOfClass:[NSNumber class]])
+ {
+ return(YES);
+ }
+ else if ([inObject isKindOfClass:[NSString class]])
+ {
+ return(YES);
+ }
+ else if ([inObject isKindOfClass:[NSArray class]])
+ {
+ return(YES);
+ }
+ else if ([inObject isKindOfClass:[NSDictionary class]])
+ {
+ return(YES);
+ }
+ else if ([inObject isKindOfClass:[NSData class]])
+ {
+ return(YES);
+ }
+ else if ([inObject respondsToSelector:@selector(JSONDataRepresentation)])
+ {
+ return(YES);
+ }
+ else
+ {
+ return(NO);
+ }
+ }
+
+- (NSData *)serializeObject:(id)inObject error:(NSError **)outError
+ {
+ NSData *theResult = NULL;
+
+ if ([inObject isKindOfClass:[NSNull class]])
+ {
+ theResult = [self serializeNull:inObject error:outError];
+ }
+ else if ([inObject isKindOfClass:[NSNumber class]])
+ {
+ theResult = [self serializeNumber:inObject error:outError];
+ }
+ else if ([inObject isKindOfClass:[NSString class]])
+ {
+ theResult = [self serializeString:inObject error:outError];
+ }
+ else if ([inObject isKindOfClass:[NSArray class]])
+ {
+ theResult = [self serializeArray:inObject error:outError];
+ }
+ else if ([inObject isKindOfClass:[NSDictionary class]])
+ {
+ theResult = [self serializeDictionary:inObject error:outError];
+ }
+ else if ([inObject isKindOfClass:[NSData class]])
+ {
+ NSString *theString = [[[NSString alloc] initWithData:inObject encoding:NSUTF8StringEncoding] autorelease];
+ theResult = [self serializeString:theString error:outError];
+ }
+ else if ([inObject respondsToSelector:@selector(JSONDataRepresentation)])
+ {
+ theResult = [inObject JSONDataRepresentation];
+ }
+ else
+ {
+ if (outError)
+ {
+ NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSString stringWithFormat:@"Cannot serialize data of type '%@'", NSStringFromClass([inObject class])], NSLocalizedDescriptionKey,
+ NULL];
+ *outError = [NSError errorWithDomain:@"TODO_DOMAIN" code:CJSONSerializerErrorCouldNotSerializeDataType userInfo:theUserInfo];
+ }
+ return(NULL);
+ }
+ if (theResult == NULL)
+ {
+ if (outError)
+ {
+ NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSString stringWithFormat:@"Could not serialize object '%@'", inObject], NSLocalizedDescriptionKey,
+ NULL];
+ *outError = [NSError errorWithDomain:@"TODO_DOMAIN" code:CJSONSerializerErrorCouldNotSerializeObject userInfo:theUserInfo];
+ }
+ return(NULL);
+ }
+ return(theResult);
+ }
+
+- (NSData *)serializeNull:(NSNull *)inNull error:(NSError **)outError
+ {
+ #pragma unused (inNull, outError)
+ return(kNULL);
+ }
+
+- (NSData *)serializeNumber:(NSNumber *)inNumber error:(NSError **)outError
+ {
+ #pragma unused (outError)
+ NSData *theResult = NULL;
+ switch (CFNumberGetType((CFNumberRef)inNumber))
+ {
+ case kCFNumberCharType:
+ {
+ int theValue = [inNumber intValue];
+ if (theValue == 0)
+ theResult = kFalse;
+ else if (theValue == 1)
+ theResult = kTrue;
+ else
+ theResult = [[inNumber stringValue] dataUsingEncoding:NSASCIIStringEncoding];
+ }
+ break;
+ case kCFNumberFloat32Type:
+ case kCFNumberFloat64Type:
+ case kCFNumberFloatType:
+ case kCFNumberDoubleType:
+ case kCFNumberSInt8Type:
+ case kCFNumberSInt16Type:
+ case kCFNumberSInt32Type:
+ case kCFNumberSInt64Type:
+ case kCFNumberShortType:
+ case kCFNumberIntType:
+ case kCFNumberLongType:
+ case kCFNumberLongLongType:
+ case kCFNumberCFIndexType:
+ default:
+ theResult = [[inNumber stringValue] dataUsingEncoding:NSASCIIStringEncoding];
+ break;
+ }
+ return(theResult);
+ }
+
+- (NSData *)serializeString:(NSString *)inString error:(NSError **)outError
+ {
+ #pragma unused (outError)
+
+ const char *theUTF8String = [inString UTF8String];
+
+ NSMutableData *theData = [NSMutableData dataWithLength:strlen(theUTF8String) * 2 + 2];
+
+ char *theOutputStart = [theData mutableBytes];
+ char *OUT = theOutputStart;
+
+ *OUT++ = '"';
+
+ for (const char *IN = theUTF8String; IN && *IN != '\0'; ++IN)
+ {
+ switch (*IN)
+ {
+ case '\\':
+ {
+ *OUT++ = '\\';
+ *OUT++ = '\\';
+ }
+ break;
+ case '\"':
+ {
+ *OUT++ = '\\';
+ *OUT++ = '\"';
+ }
+ break;
+ case '/':
+ {
+ *OUT++ = '\\';
+ *OUT++ = '/';
+ }
+ break;
+ case '\b':
+ {
+ *OUT++ = '\\';
+ *OUT++ = 'b';
+ }
+ break;
+ case '\f':
+ {
+ *OUT++ = '\\';
+ *OUT++ = 'f';
+ }
+ break;
+ case '\n':
+ {
+ *OUT++ = '\\';
+ *OUT++ = 'n';
+ }
+ break;
+ case '\r':
+ {
+ *OUT++ = '\\';
+ *OUT++ = 'r';
+ }
+ break;
+ case '\t':
+ {
+ *OUT++ = '\\';
+ *OUT++ = 't';
+ }
+ break;
+ default:
+ {
+ *OUT++ = *IN;
+ }
+ break;
+ }
+ }
+
+ *OUT++ = '"';
+
+ theData.length = OUT - theOutputStart;
+ return(theData);
+ }
+
+- (NSData *)serializeArray:(NSArray *)inArray error:(NSError **)outError
+ {
+ NSMutableData *theData = [NSMutableData data];
+
+ [theData appendBytes:"[" length:1];
+
+ NSEnumerator *theEnumerator = [inArray objectEnumerator];
+ id theValue = NULL;
+ NSUInteger i = 0;
+ while ((theValue = [theEnumerator nextObject]) != NULL)
+ {
+ NSData *theValueData = [self serializeObject:theValue error:outError];
+ if (theValueData == NULL)
+ {
+ return(NULL);
+ }
+ [theData appendData:theValueData];
+ if (++i < [inArray count])
+ [theData appendBytes:"," length:1];
+ }
+
+ [theData appendBytes:"]" length:1];
+
+ return(theData);
+ }
+
+- (NSData *)serializeDictionary:(NSDictionary *)inDictionary error:(NSError **)outError
+ {
+ NSMutableData *theData = [NSMutableData data];
+
+ [theData appendBytes:"{" length:1];
+
+ NSArray *theKeys = [inDictionary allKeys];
+ NSEnumerator *theEnumerator = [theKeys objectEnumerator];
+ NSString *theKey = NULL;
+ while ((theKey = [theEnumerator nextObject]) != NULL)
+ {
+ id theValue = [inDictionary objectForKey:theKey];
+
+ NSData *theKeyData = [self serializeString:theKey error:outError];
+ if (theKeyData == NULL)
+ {
+ return(NULL);
+ }
+ NSData *theValueData = [self serializeObject:theValue error:outError];
+ if (theValueData == NULL)
+ {
+ return(NULL);
+ }
+
+
+ [theData appendData:theKeyData];
+ [theData appendBytes:":" length:1];
+ [theData appendData:theValueData];
+
+ if (theKey != [theKeys lastObject])
+ [theData appendData:[@"," dataUsingEncoding:NSASCIIStringEncoding]];
+ }
+
+ [theData appendBytes:"}" length:1];
+
+ return(theData);
+ }
+
+@end
--- /dev/null
+//
+// JSONRepresentation.h
+// TouchJSON
+//
+// Created by Jonathan Wight on 10/15/10.
+// Copyright 2010 toxicsoftware.com. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@protocol JSONRepresentation
+
+@optional
+- (id)initWithJSONDataRepresentation:(NSData *)inJSONData;
+
+- (NSData *)JSONDataRepresentation;
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#include <sys/time.h>
+#import <Foundation/Foundation.h>
+
+#import "ccTypes.h"
+
+enum {
+ //! Default tag
+ kCCActionTagInvalid = -1,
+};
+
+/** Base class for CCAction objects.
+ */
+@interface CCAction : NSObject <NSCopying>
+{
+ id originalTarget_;
+ id target_;
+ NSInteger tag_;
+}
+
+/** The "target". The action will modify the target properties.
+ The target will be set with the 'startWithTarget' method.
+ When the 'stop' method is called, target will be set to nil.
+ The target is 'assigned', it is not 'retained'.
+ */
+@property (nonatomic,readonly,assign) id target;
+
+/** The original target, since target can be nil.
+ Is the target that were used to run the action. Unless you are doing something complex, like CCActionManager, you should NOT call this method.
+ @since v0.8.2
+*/
+@property (nonatomic,readonly,assign) id originalTarget;
+
+
+/** The action tag. An identifier of the action */
+@property (nonatomic,readwrite,assign) NSInteger tag;
+
+/** Allocates and initializes the action */
++(id) action;
+
+/** Initializes the action */
+-(id) init;
+
+-(id) copyWithZone: (NSZone*) zone;
+
+//! return YES if the action has finished
+-(BOOL) isDone;
+//! called before the action start. It will also set the target.
+-(void) startWithTarget:(id)target;
+//! called after the action has finished. It will set the 'target' to nil.
+//! IMPORTANT: You should never call "[action stop]" manually. Instead, use: "[target stopAction:action];"
+-(void) stop;
+//! called every frame with it's delta time. DON'T override unless you know what you are doing.
+-(void) step: (ccTime) dt;
+//! called once per frame. time a value between 0 and 1
+//! For example:
+//! * 0 means that the action just started
+//! * 0.5 means that the action is in the middle
+//! * 1 means that the action is over
+-(void) update: (ccTime) time;
+
+@end
+
+/** Base class actions that do have a finite time duration.
+ Possible actions:
+ - An action with a duration of 0 seconds
+ - An action with a duration of 35.5 seconds
+ Infitite time actions are valid
+ */
+@interface CCFiniteTimeAction : CCAction <NSCopying>
+{
+ //! duration in seconds
+ ccTime duration_;
+}
+//! duration in seconds of the action
+@property (nonatomic,readwrite) ccTime duration;
+
+/** returns a reversed action */
+- (CCFiniteTimeAction*) reverse;
+@end
+
+
+@class CCActionInterval;
+/** Repeats an action for ever.
+ To repeat the an action for a limited number of times use the Repeat action.
+ @warning This action can't be Sequenceable because it is not an IntervalAction
+ */
+@interface CCRepeatForever : CCAction <NSCopying>
+{
+ CCActionInterval *innerAction_;
+}
+/** Inner action */
+@property (nonatomic, readwrite, retain) CCActionInterval *innerAction;
+
+/** creates the action */
++(id) actionWithAction: (CCActionInterval*) action;
+/** initializes the action */
+-(id) initWithAction: (CCActionInterval*) action;
+@end
+
+/** Changes the speed of an action, making it take longer (speed>1)
+ or less (speed<1) time.
+ Useful to simulate 'slow motion' or 'fast forward' effect.
+ @warning This action can't be Sequenceable because it is not an CCIntervalAction
+ */
+@interface CCSpeed : CCAction <NSCopying>
+{
+ CCActionInterval *innerAction_;
+ float speed_;
+}
+/** alter the speed of the inner function in runtime */
+@property (nonatomic,readwrite) float speed;
+/** Inner action of CCSpeed */
+@property (nonatomic, readwrite, retain) CCActionInterval *innerAction;
+
+/** creates the action */
++(id) actionWithAction: (CCActionInterval*) action speed:(float)rate;
+/** initializes the action */
+-(id) initWithAction: (CCActionInterval*) action speed:(float)rate;
+@end
+
+@class CCNode;
+/** CCFollow is an action that "follows" a node.
+
+ Eg:
+ [layer runAction: [CCFollow actionWithTarget:hero]];
+
+ Instead of using CCCamera as a "follower", use this action instead.
+ @since v0.99.2
+ */
+@interface CCFollow : CCAction <NSCopying>
+{
+ /* node to follow */
+ CCNode *followedNode_;
+
+ /* whether camera should be limited to certain area */
+ BOOL boundarySet;
+
+ /* if screensize is bigger than the boundary - update not needed */
+ BOOL boundaryFullyCovered;
+
+ /* fast access to the screen dimensions */
+ CGPoint halfScreenSize;
+ CGPoint fullScreenSize;
+
+ /* world boundaries */
+ float leftBoundary;
+ float rightBoundary;
+ float topBoundary;
+ float bottomBoundary;
+}
+
+/** alter behavior - turn on/off boundary */
+@property (nonatomic,readwrite) BOOL boundarySet;
+
+/** creates the action with no boundary set */
++(id) actionWithTarget:(CCNode *)followedNode;
+
+/** creates the action with a set boundary */
++(id) actionWithTarget:(CCNode *)followedNode worldBoundary:(CGRect)rect;
+
+/** initializes the action */
+-(id) initWithTarget:(CCNode *)followedNode;
+
+/** initializes the action with a set boundary */
+-(id) initWithTarget:(CCNode *)followedNode worldBoundary:(CGRect)rect;
+
+@end
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+
+#import <Availability.h>
+#import "CCDirector.h"
+#import "ccMacros.h"
+#import "CCAction.h"
+#import "CCActionInterval.h"
+#import "Support/CGPointExtension.h"
+
+//
+// Action Base Class
+//
+#pragma mark -
+#pragma mark Action
+@implementation CCAction
+
+@synthesize tag = tag_, target = target_, originalTarget = originalTarget_;
+
++(id) action
+{
+ return [[[self alloc] init] autorelease];
+}
+
+-(id) init
+{
+ if( (self=[super init]) ) {
+ originalTarget_ = target_ = nil;
+ tag_ = kCCActionTagInvalid;
+ }
+ return self;
+}
+
+-(void) dealloc
+{
+ CCLOGINFO(@"cocos2d: deallocing %@", self);
+ [super dealloc];
+}
+
+-(NSString*) description
+{
+ return [NSString stringWithFormat:@"<%@ = %08X | Tag = %i>", [self class], self, tag_];
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCAction *copy = [[[self class] allocWithZone: zone] init];
+ copy.tag = tag_;
+ return copy;
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+ originalTarget_ = target_ = aTarget;
+}
+
+-(void) stop
+{
+ target_ = nil;
+}
+
+-(BOOL) isDone
+{
+ return YES;
+}
+
+-(void) step: (ccTime) dt
+{
+ NSLog(@"[Action step]. override me");
+}
+
+-(void) update: (ccTime) time
+{
+ NSLog(@"[Action update]. override me");
+}
+@end
+
+//
+// FiniteTimeAction
+//
+#pragma mark -
+#pragma mark FiniteTimeAction
+@implementation CCFiniteTimeAction
+@synthesize duration = duration_;
+
+- (CCFiniteTimeAction*) reverse
+{
+ CCLOG(@"cocos2d: FiniteTimeAction#reverse: Implement me");
+ return nil;
+}
+@end
+
+
+//
+// RepeatForever
+//
+#pragma mark -
+#pragma mark RepeatForever
+@implementation CCRepeatForever
+@synthesize innerAction=innerAction_;
++(id) actionWithAction: (CCActionInterval*) action
+{
+ return [[[self alloc] initWithAction: action] autorelease];
+}
+
+-(id) initWithAction: (CCActionInterval*) action
+{
+ if( (self=[super init]) )
+ self.innerAction = action;
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCAction *copy = [[[self class] allocWithZone: zone] initWithAction:[[innerAction_ copy] autorelease] ];
+ return copy;
+}
+
+-(void) dealloc
+{
+ [innerAction_ release];
+ [super dealloc];
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ [innerAction_ startWithTarget:target_];
+}
+
+-(void) step:(ccTime) dt
+{
+ [innerAction_ step: dt];
+ if( [innerAction_ isDone] ) {
+ ccTime diff = dt + innerAction_.duration - innerAction_.elapsed;
+ [innerAction_ startWithTarget:target_];
+
+ // to prevent jerk. issue #390
+ [innerAction_ step: diff];
+ }
+}
+
+
+-(BOOL) isDone
+{
+ return NO;
+}
+
+- (CCActionInterval *) reverse
+{
+ return [CCRepeatForever actionWithAction:[innerAction_ reverse]];
+}
+@end
+
+//
+// Speed
+//
+#pragma mark -
+#pragma mark Speed
+@implementation CCSpeed
+@synthesize speed=speed_;
+@synthesize innerAction=innerAction_;
+
++(id) actionWithAction: (CCActionInterval*) action speed:(float)r
+{
+ return [[[self alloc] initWithAction: action speed:r] autorelease];
+}
+
+-(id) initWithAction: (CCActionInterval*) action speed:(float)r
+{
+ if( (self=[super init]) ) {
+ self.innerAction = action;
+ speed_ = r;
+ }
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCAction *copy = [[[self class] allocWithZone: zone] initWithAction:[[innerAction_ copy] autorelease] speed:speed_];
+ return copy;
+}
+
+-(void) dealloc
+{
+ [innerAction_ release];
+ [super dealloc];
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ [innerAction_ startWithTarget:target_];
+}
+
+-(void) stop
+{
+ [innerAction_ stop];
+ [super stop];
+}
+
+-(void) step:(ccTime) dt
+{
+ [innerAction_ step: dt * speed_];
+}
+
+-(BOOL) isDone
+{
+ return [innerAction_ isDone];
+}
+
+- (CCActionInterval *) reverse
+{
+ return [CCSpeed actionWithAction:[innerAction_ reverse] speed:speed_];
+}
+@end
+
+//
+// Follow
+//
+#pragma mark -
+#pragma mark Follow
+@implementation CCFollow
+
+@synthesize boundarySet;
+
++(id) actionWithTarget:(CCNode *) fNode
+{
+ return [[[self alloc] initWithTarget:fNode] autorelease];
+}
+
++(id) actionWithTarget:(CCNode *) fNode worldBoundary:(CGRect)rect
+{
+ return [[[self alloc] initWithTarget:fNode worldBoundary:rect] autorelease];
+}
+
+-(id) initWithTarget:(CCNode *)fNode
+{
+ if( (self=[super init]) ) {
+
+ followedNode_ = [fNode retain];
+ boundarySet = FALSE;
+ boundaryFullyCovered = FALSE;
+
+ CGSize s = [[CCDirector sharedDirector] winSize];
+ fullScreenSize = CGPointMake(s.width, s.height);
+ halfScreenSize = ccpMult(fullScreenSize, .5f);
+ }
+
+ return self;
+}
+
+-(id) initWithTarget:(CCNode *)fNode worldBoundary:(CGRect)rect
+{
+ if( (self=[super init]) ) {
+
+ followedNode_ = [fNode retain];
+ boundarySet = TRUE;
+ boundaryFullyCovered = FALSE;
+
+ CGSize winSize = [[CCDirector sharedDirector] winSize];
+ fullScreenSize = CGPointMake(winSize.width, winSize.height);
+ halfScreenSize = ccpMult(fullScreenSize, .5f);
+
+ leftBoundary = -((rect.origin.x+rect.size.width) - fullScreenSize.x);
+ rightBoundary = -rect.origin.x ;
+ topBoundary = -rect.origin.y;
+ bottomBoundary = -((rect.origin.y+rect.size.height) - fullScreenSize.y);
+
+ if(rightBoundary < leftBoundary)
+ {
+ // screen width is larger than world's boundary width
+ //set both in the middle of the world
+ rightBoundary = leftBoundary = (leftBoundary + rightBoundary) / 2;
+ }
+ if(topBoundary < bottomBoundary)
+ {
+ // screen width is larger than world's boundary width
+ //set both in the middle of the world
+ topBoundary = bottomBoundary = (topBoundary + bottomBoundary) / 2;
+ }
+
+ if( (topBoundary == bottomBoundary) && (leftBoundary == rightBoundary) )
+ boundaryFullyCovered = TRUE;
+ }
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCAction *copy = [[[self class] allocWithZone: zone] init];
+ copy.tag = tag_;
+ return copy;
+}
+
+-(void) step:(ccTime) dt
+{
+#define CLAMP(x,y,z) MIN(MAX(x,y),z)
+
+ if(boundarySet)
+ {
+ // whole map fits inside a single screen, no need to modify the position - unless map boundaries are increased
+ if(boundaryFullyCovered)
+ return;
+
+ CGPoint tempPos = ccpSub( halfScreenSize, followedNode_.position);
+ [target_ setPosition:ccp(CLAMP(tempPos.x,leftBoundary,rightBoundary), CLAMP(tempPos.y,bottomBoundary,topBoundary))];
+ }
+ else
+ [target_ setPosition:ccpSub( halfScreenSize, followedNode_.position )];
+
+#undef CLAMP
+}
+
+
+-(BOOL) isDone
+{
+ return !followedNode_.isRunning;
+}
+
+-(void) stop
+{
+ target_ = nil;
+ [super stop];
+}
+
+-(void) dealloc
+{
+ [followedNode_ release];
+ [super dealloc];
+}
+
+@end
+
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "CCActionInterval.h"
+
+@class CCCamera;
+
+/** Base class for CCCamera actions
+ */
+@interface CCActionCamera : CCActionInterval <NSCopying>
+{
+ float centerXOrig_;
+ float centerYOrig_;
+ float centerZOrig_;
+
+ float eyeXOrig_;
+ float eyeYOrig_;
+ float eyeZOrig_;
+
+ float upXOrig_;
+ float upYOrig_;
+ float upZOrig_;
+}
+@end
+
+/** CCOrbitCamera action
+ Orbits the camera around the center of the screen using spherical coordinates
+ */
+@interface CCOrbitCamera : CCActionCamera <NSCopying>
+{
+ float radius_;
+ float deltaRadius_;
+ float angleZ_;
+ float deltaAngleZ_;
+ float angleX_;
+ float deltaAngleX_;
+
+ float radZ_;
+ float radDeltaZ_;
+ float radX_;
+ float radDeltaX_;
+
+}
+/** creates a CCOrbitCamera action with radius, delta-radius, z, deltaZ, x, deltaX */
++(id) actionWithDuration:(float) t radius:(float)r deltaRadius:(float) dr angleZ:(float)z deltaAngleZ:(float)dz angleX:(float)x deltaAngleX:(float)dx;
+/** initializes a CCOrbitCamera action with radius, delta-radius, z, deltaZ, x, deltaX */
+-(id) initWithDuration:(float) t radius:(float)r deltaRadius:(float) dr angleZ:(float)z deltaAngleZ:(float)dz angleX:(float)x deltaAngleX:(float)dx;
+/** positions the camera according to spherical coordinates */
+-(void) sphericalRadius:(float*) r zenith:(float*) zenith azimuth:(float*) azimuth;
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+
+#import "CCActionCamera.h"
+#import "CCNode.h"
+#import "CCCamera.h"
+#import "ccMacros.h"
+
+//
+// CameraAction
+//
+@implementation CCActionCamera
+-(void) startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ CCCamera *camera = [target_ camera];
+ [camera centerX:¢erXOrig_ centerY:¢erYOrig_ centerZ:¢erZOrig_];
+ [camera eyeX:&eyeXOrig_ eyeY:&eyeYOrig_ eyeZ:&eyeZOrig_];
+ [camera upX:&upXOrig_ upY:&upYOrig_ upZ: &upZOrig_];
+}
+
+-(id) reverse
+{
+ return [CCReverseTime actionWithAction:self];
+}
+@end
+
+@implementation CCOrbitCamera
++(id) actionWithDuration:(float)t radius:(float)r deltaRadius:(float) dr angleZ:(float)z deltaAngleZ:(float)dz angleX:(float)x deltaAngleX:(float)dx
+{
+ return [[[self alloc] initWithDuration:t radius:r deltaRadius:dr angleZ:z deltaAngleZ:dz angleX:x deltaAngleX:dx] autorelease];
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ return [[[self class] allocWithZone: zone] initWithDuration:duration_ radius:radius_ deltaRadius:deltaRadius_ angleZ:angleZ_ deltaAngleZ:deltaAngleZ_ angleX:angleX_ deltaAngleX:deltaAngleX_];
+}
+
+
+-(id) initWithDuration:(float)t radius:(float)r deltaRadius:(float) dr angleZ:(float)z deltaAngleZ:(float)dz angleX:(float)x deltaAngleX:(float)dx
+{
+ if((self=[super initWithDuration:t]) ) {
+
+ radius_ = r;
+ deltaRadius_ = dr;
+ angleZ_ = z;
+ deltaAngleZ_ = dz;
+ angleX_ = x;
+ deltaAngleX_ = dx;
+
+ radDeltaZ_ = (CGFloat)CC_DEGREES_TO_RADIANS(dz);
+ radDeltaX_ = (CGFloat)CC_DEGREES_TO_RADIANS(dx);
+ }
+
+ return self;
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ float r, zenith, azimuth;
+
+ [self sphericalRadius: &r zenith:&zenith azimuth:&azimuth];
+
+#if 0 // isnan() is not supported on the simulator, and isnan() always returns false.
+ if( isnan(radius_) )
+ radius_ = r;
+
+ if( isnan( angleZ_) )
+ angleZ_ = (CGFloat)CC_RADIANS_TO_DEGREES(zenith);
+
+ if( isnan( angleX_ ) )
+ angleX_ = (CGFloat)CC_RADIANS_TO_DEGREES(azimuth);
+#endif
+
+ radZ_ = (CGFloat)CC_DEGREES_TO_RADIANS(angleZ_);
+ radX_ = (CGFloat)CC_DEGREES_TO_RADIANS(angleX_);
+}
+
+-(void) update: (ccTime) dt
+{
+ float r = (radius_ + deltaRadius_ * dt) *[CCCamera getZEye];
+ float za = radZ_ + radDeltaZ_ * dt;
+ float xa = radX_ + radDeltaX_ * dt;
+
+ float i = sinf(za) * cosf(xa) * r + centerXOrig_;
+ float j = sinf(za) * sinf(xa) * r + centerYOrig_;
+ float k = cosf(za) * r + centerZOrig_;
+
+ [[target_ camera] setEyeX:i eyeY:j eyeZ:k];
+}
+
+-(void) sphericalRadius:(float*) newRadius zenith:(float*) zenith azimuth:(float*) azimuth
+{
+ float ex, ey, ez, cx, cy, cz, x, y, z;
+ float r; // radius
+ float s;
+
+ CCCamera *camera = [target_ camera];
+ [camera eyeX:&ex eyeY:&ey eyeZ:&ez];
+ [camera centerX:&cx centerY:&cy centerZ:&cz];
+
+ x = ex-cx;
+ y = ey-cy;
+ z = ez-cz;
+
+ r = sqrtf( x*x + y*y + z*z);
+ s = sqrtf( x*x + y*y);
+ if(s==0.0f)
+ s = FLT_EPSILON;
+ if(r==0.0f)
+ r = FLT_EPSILON;
+
+ *zenith = acosf( z/r);
+ if( x < 0 )
+ *azimuth = (float)M_PI - asinf(y/s);
+ else
+ *azimuth = asinf(y/s);
+
+ *newRadius = r / [CCCamera getZEye];
+}
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2009 Jason Booth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCActionInterval.h"
+
+/** Base class for Easing actions
+ */
+@interface CCActionEase : CCActionInterval <NSCopying>
+{
+ CCActionInterval * other;
+}
+/** creates the action */
++(id) actionWithAction: (CCActionInterval*) action;
+/** initializes the action */
+-(id) initWithAction: (CCActionInterval*) action;
+@end
+
+/** Base class for Easing actions with rate parameters
+ */
+@interface CCEaseRateAction : CCActionEase <NSCopying>
+{
+ float rate;
+}
+/** rate value for the actions */
+@property (nonatomic,readwrite,assign) float rate;
+/** Creates the action with the inner action and the rate parameter */
++(id) actionWithAction: (CCActionInterval*) action rate:(float)rate;
+/** Initializes the action with the inner action and the rate parameter */
+-(id) initWithAction: (CCActionInterval*) action rate:(float)rate;
+@end
+
+/** CCEaseIn action with a rate
+ */
+@interface CCEaseIn : CCEaseRateAction <NSCopying> {} @end
+
+/** CCEaseOut action with a rate
+ */
+@interface CCEaseOut : CCEaseRateAction <NSCopying> {} @end
+
+/** CCEaseInOut action with a rate
+ */
+@interface CCEaseInOut : CCEaseRateAction <NSCopying> {} @end
+
+/** CCEase Exponential In
+ */
+@interface CCEaseExponentialIn : CCActionEase <NSCopying> {} @end
+/** Ease Exponential Out
+ */
+@interface CCEaseExponentialOut : CCActionEase <NSCopying> {} @end
+/** Ease Exponential InOut
+ */
+@interface CCEaseExponentialInOut : CCActionEase <NSCopying> {} @end
+/** Ease Sine In
+ */
+@interface CCEaseSineIn : CCActionEase <NSCopying> {} @end
+/** Ease Sine Out
+ */
+@interface CCEaseSineOut : CCActionEase <NSCopying> {} @end
+/** Ease Sine InOut
+ */
+@interface CCEaseSineInOut : CCActionEase <NSCopying> {} @end
+
+/** Ease Elastic abstract class
+ @since v0.8.2
+ */
+@interface CCEaseElastic : CCActionEase <NSCopying>
+{
+ float period_;
+}
+
+/** period of the wave in radians. default is 0.3 */
+@property (nonatomic,readwrite) float period;
+
+/** Creates the action with the inner action and the period in radians (default is 0.3) */
++(id) actionWithAction: (CCActionInterval*) action period:(float)period;
+/** Initializes the action with the inner action and the period in radians (default is 0.3) */
+-(id) initWithAction: (CCActionInterval*) action period:(float)period;
+@end
+
+/** Ease Elastic In action.
+ @warning This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action.
+ @since v0.8.2
+ */
+@interface CCEaseElasticIn : CCEaseElastic <NSCopying> {} @end
+/** Ease Elastic Out action.
+ @warning This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action.
+ @since v0.8.2
+ */
+@interface CCEaseElasticOut : CCEaseElastic <NSCopying> {} @end
+/** Ease Elastic InOut action.
+ @warning This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action.
+ @since v0.8.2
+ */
+@interface CCEaseElasticInOut : CCEaseElastic <NSCopying> {} @end
+
+/** CCEaseBounce abstract class.
+ @since v0.8.2
+*/
+@interface CCEaseBounce : CCActionEase <NSCopying> {} @end
+
+/** CCEaseBounceIn action.
+ @warning This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action.
+ @since v0.8.2
+*/
+@interface CCEaseBounceIn : CCEaseBounce <NSCopying> {} @end
+
+/** EaseBounceOut action.
+ @warning This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action.
+ @since v0.8.2
+ */
+@interface CCEaseBounceOut : CCEaseBounce <NSCopying> {} @end
+
+/** CCEaseBounceInOut action.
+ @warning This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action.
+ @since v0.8.2
+ */
+@interface CCEaseBounceInOut : CCEaseBounce <NSCopying> {} @end
+
+/** CCEaseBackIn action.
+ @warning This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action.
+ @since v0.8.2
+ */
+@interface CCEaseBackIn : CCActionEase <NSCopying> {} @end
+
+/** CCEaseBackOut action.
+ @warning This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action.
+ @since v0.8.2
+ */
+@interface CCEaseBackOut : CCActionEase <NSCopying> {} @end
+
+/** CCEaseBackInOut action.
+ @warning This action doesn't use a bijective fucntion. Actions like Sequence might have an unexpected result when used with this action.
+ @since v0.8.2
+ */
+@interface CCEaseBackInOut : CCActionEase <NSCopying> {} @end
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2009 Jason Booth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+/*
+ * Elastic, Back and Bounce actions based on code from:
+ * http://github.com/NikhilK/silverlightfx/
+ *
+ * by http://github.com/NikhilK
+ */
+
+#import "CCActionEase.h"
+
+#ifndef M_PI_X_2
+#define M_PI_X_2 (float)M_PI * 2.0f
+#endif
+
+#pragma mark EaseAction
+
+//
+// EaseAction
+//
+@implementation CCActionEase
+
++(id) actionWithAction: (CCActionInterval*) action
+{
+ return [[[self alloc] initWithAction: action] autorelease ];
+}
+
+-(id) initWithAction: (CCActionInterval*) action
+{
+ NSAssert( action!=nil, @"Ease: arguments must be non-nil");
+
+ if( (self=[super initWithDuration: action.duration]) )
+ other = [action retain];
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCAction *copy = [[[self class] allocWithZone:zone] initWithAction:[[other copy] autorelease]];
+ return copy;
+}
+
+-(void) dealloc
+{
+ [other release];
+ [super dealloc];
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ [other startWithTarget:target_];
+}
+
+-(void) stop
+{
+ [other stop];
+ [super stop];
+}
+
+-(void) update: (ccTime) t
+{
+ [other update: t];
+}
+
+-(CCActionInterval*) reverse
+{
+ return [[self class] actionWithAction: [other reverse]];
+}
+@end
+
+
+#pragma mark -
+#pragma mark EaseRate
+
+//
+// EaseRateAction
+//
+@implementation CCEaseRateAction
+@synthesize rate;
++(id) actionWithAction: (CCActionInterval*) action rate:(float)aRate
+{
+ return [[[self alloc] initWithAction: action rate:aRate] autorelease ];
+}
+
+-(id) initWithAction: (CCActionInterval*) action rate:(float)aRate
+{
+ if( (self=[super initWithAction:action ]) )
+ self.rate = aRate;
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCAction *copy = [[[self class] allocWithZone:zone] initWithAction:[[other copy] autorelease] rate:rate];
+ return copy;
+}
+
+-(void) dealloc
+{
+ [super dealloc];
+}
+
+-(CCActionInterval*) reverse
+{
+ return [[self class] actionWithAction: [other reverse] rate:1/rate];
+}
+@end
+
+//
+// EeseIn
+//
+@implementation CCEaseIn
+-(void) update: (ccTime) t
+{
+ [other update: powf(t,rate)];
+}
+@end
+
+//
+// EaseOut
+//
+@implementation CCEaseOut
+-(void) update: (ccTime) t
+{
+ [other update: powf(t,1/rate)];
+}
+@end
+
+//
+// EaseInOut
+//
+@implementation CCEaseInOut
+-(void) update: (ccTime) t
+{
+ int sign =1;
+ int r = (int) rate;
+ if (r % 2 == 0)
+ sign = -1;
+ t *= 2;
+ if (t < 1)
+ [other update: 0.5f * powf (t, rate)];
+ else
+ [other update: sign*0.5f * (powf (t-2, rate) + sign*2)];
+}
+
+// InOut and OutIn are symmetrical
+-(CCActionInterval*) reverse
+{
+ return [[self class] actionWithAction: [other reverse] rate:rate];
+}
+
+@end
+
+#pragma mark -
+#pragma mark EaseExponential
+
+//
+// EaseExponentialIn
+//
+@implementation CCEaseExponentialIn
+-(void) update: (ccTime) t
+{
+ [other update: (t==0) ? 0 : powf(2, 10 * (t/1 - 1)) - 1 * 0.001f];
+}
+
+- (CCActionInterval*) reverse
+{
+ return [CCEaseExponentialOut actionWithAction: [other reverse]];
+}
+@end
+
+//
+// EaseExponentialOut
+//
+@implementation CCEaseExponentialOut
+-(void) update: (ccTime) t
+{
+ [other update: (t==1) ? 1 : (-powf(2, -10 * t/1) + 1)];
+}
+
+- (CCActionInterval*) reverse
+{
+ return [CCEaseExponentialIn actionWithAction: [other reverse]];
+}
+@end
+
+//
+// EaseExponentialInOut
+//
+@implementation CCEaseExponentialInOut
+-(void) update: (ccTime) t
+{
+ t /= 0.5f;
+ if (t < 1)
+ t = 0.5f * powf(2, 10 * (t - 1));
+ else
+ t = 0.5f * (-powf(2, -10 * (t -1) ) + 2);
+
+ [other update:t];
+}
+@end
+
+
+#pragma mark -
+#pragma mark EaseSin actions
+
+//
+// EaseSineIn
+//
+@implementation CCEaseSineIn
+-(void) update: (ccTime) t
+{
+ [other update:-1*cosf(t * (float)M_PI_2) +1];
+}
+
+- (CCActionInterval*) reverse
+{
+ return [CCEaseSineOut actionWithAction: [other reverse]];
+}
+@end
+
+//
+// EaseSineOut
+//
+@implementation CCEaseSineOut
+-(void) update: (ccTime) t
+{
+ [other update:sinf(t * (float)M_PI_2)];
+}
+
+- (CCActionInterval*) reverse
+{
+ return [CCEaseSineIn actionWithAction: [other reverse]];
+}
+@end
+
+//
+// EaseSineInOut
+//
+@implementation CCEaseSineInOut
+-(void) update: (ccTime) t
+{
+ [other update:-0.5f*(cosf( (float)M_PI*t) - 1)];
+}
+@end
+
+#pragma mark -
+#pragma mark EaseElastic actions
+
+//
+// EaseElastic
+//
+@implementation CCEaseElastic
+
+@synthesize period = period_;
+
++(id) actionWithAction: (CCActionInterval*) action
+{
+ return [[[self alloc] initWithAction:action period:0.3f] autorelease];
+}
+
++(id) actionWithAction: (CCActionInterval*) action period:(float)period
+{
+ return [[[self alloc] initWithAction:action period:period] autorelease];
+}
+
+-(id) initWithAction: (CCActionInterval*) action
+{
+ return [self initWithAction:action period:0.3f];
+}
+
+-(id) initWithAction: (CCActionInterval*) action period:(float)period
+{
+ if( (self=[super initWithAction:action]) )
+ period_ = period;
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCAction *copy = [[[self class] allocWithZone:zone] initWithAction:[[other copy] autorelease] period:period_];
+ return copy;
+}
+
+-(CCActionInterval*) reverse
+{
+ NSAssert(NO,@"Override me");
+ return nil;
+}
+
+@end
+
+//
+// EaseElasticIn
+//
+
+@implementation CCEaseElasticIn
+-(void) update: (ccTime) t
+{
+ ccTime newT = 0;
+ if (t == 0 || t == 1)
+ newT = t;
+
+ else {
+ float s = period_ / 4;
+ t = t - 1;
+ newT = -powf(2, 10 * t) * sinf( (t-s) *M_PI_X_2 / period_);
+ }
+ [other update:newT];
+}
+
+- (CCActionInterval*) reverse
+{
+ return [CCEaseElasticOut actionWithAction: [other reverse] period:period_];
+}
+
+@end
+
+//
+// EaseElasticOut
+//
+@implementation CCEaseElasticOut
+
+-(void) update: (ccTime) t
+{
+ ccTime newT = 0;
+ if (t == 0 || t == 1) {
+ newT = t;
+
+ } else {
+ float s = period_ / 4;
+ newT = powf(2, -10 * t) * sinf( (t-s) *M_PI_X_2 / period_) + 1;
+ }
+ [other update:newT];
+}
+
+- (CCActionInterval*) reverse
+{
+ return [CCEaseElasticIn actionWithAction: [other reverse] period:period_];
+}
+
+@end
+
+//
+// EaseElasticInOut
+//
+@implementation CCEaseElasticInOut
+-(void) update: (ccTime) t
+{
+ ccTime newT = 0;
+
+ if( t == 0 || t == 1 )
+ newT = t;
+ else {
+ t = t * 2;
+ if(! period_ )
+ period_ = 0.3f * 1.5f;
+ ccTime s = period_ / 4;
+
+ t = t -1;
+ if( t < 0 )
+ newT = -0.5f * powf(2, 10 * t) * sinf((t - s) * M_PI_X_2 / period_);
+ else
+ newT = powf(2, -10 * t) * sinf((t - s) * M_PI_X_2 / period_) * 0.5f + 1;
+ }
+ [other update:newT];
+}
+
+- (CCActionInterval*) reverse
+{
+ return [CCEaseElasticInOut actionWithAction: [other reverse] period:period_];
+}
+
+@end
+
+#pragma mark -
+#pragma mark EaseBounce actions
+
+//
+// EaseBounce
+//
+@implementation CCEaseBounce
+-(ccTime) bounceTime:(ccTime) t
+{
+ if (t < 1 / 2.75) {
+ return 7.5625f * t * t;
+ }
+ else if (t < 2 / 2.75) {
+ t -= 1.5f / 2.75f;
+ return 7.5625f * t * t + 0.75f;
+ }
+ else if (t < 2.5 / 2.75) {
+ t -= 2.25f / 2.75f;
+ return 7.5625f * t * t + 0.9375f;
+ }
+
+ t -= 2.625f / 2.75f;
+ return 7.5625f * t * t + 0.984375f;
+}
+@end
+
+//
+// EaseBounceIn
+//
+
+@implementation CCEaseBounceIn
+
+-(void) update: (ccTime) t
+{
+ ccTime newT = 1 - [self bounceTime:1-t];
+ [other update:newT];
+}
+
+- (CCActionInterval*) reverse
+{
+ return [CCEaseBounceOut actionWithAction: [other reverse]];
+}
+
+@end
+
+@implementation CCEaseBounceOut
+
+-(void) update: (ccTime) t
+{
+ ccTime newT = [self bounceTime:t];
+ [other update:newT];
+}
+
+- (CCActionInterval*) reverse
+{
+ return [CCEaseBounceIn actionWithAction: [other reverse]];
+}
+
+@end
+
+@implementation CCEaseBounceInOut
+
+-(void) update: (ccTime) t
+{
+ ccTime newT = 0;
+ if (t < 0.5) {
+ t = t * 2;
+ newT = (1 - [self bounceTime:1-t] ) * 0.5f;
+ } else
+ newT = [self bounceTime:t * 2 - 1] * 0.5f + 0.5f;
+
+ [other update:newT];
+}
+@end
+
+#pragma mark -
+#pragma mark Ease Back actions
+
+//
+// EaseBackIn
+//
+@implementation CCEaseBackIn
+
+-(void) update: (ccTime) t
+{
+ ccTime overshoot = 1.70158f;
+ [other update: t * t * ((overshoot + 1) * t - overshoot)];
+}
+
+- (CCActionInterval*) reverse
+{
+ return [CCEaseBackOut actionWithAction: [other reverse]];
+}
+@end
+
+//
+// EaseBackOut
+//
+@implementation CCEaseBackOut
+-(void) update: (ccTime) t
+{
+ ccTime overshoot = 1.70158f;
+
+ t = t - 1;
+ [other update: t * t * ((overshoot + 1) * t + overshoot) + 1];
+}
+
+- (CCActionInterval*) reverse
+{
+ return [CCEaseBackIn actionWithAction: [other reverse]];
+}
+@end
+
+//
+// EaseBackInOut
+//
+@implementation CCEaseBackInOut
+
+-(void) update: (ccTime) t
+{
+ ccTime overshoot = 1.70158f * 1.525f;
+
+ t = t * 2;
+ if (t < 1)
+ [other update: (t * t * ((overshoot + 1) * t - overshoot)) / 2];
+ else {
+ t = t - 2;
+ [other update: (t * t * ((overshoot + 1) * t + overshoot)) / 2 + 1];
+ }
+}
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 On-Core
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCActionInterval.h"
+#import "CCActionInstant.h"
+#import "CCGrid.h"
+
+@class CCGridBase;
+
+/** Base class for Grid actions */
+@interface CCGridAction : CCActionInterval
+{
+ ccGridSize gridSize_;
+}
+
+/** size of the grid */
+@property (nonatomic,readwrite) ccGridSize gridSize;
+
+/** creates the action with size and duration */
++(id) actionWithSize:(ccGridSize)size duration:(ccTime)d;
+/** initializes the action with size and duration */
+-(id) initWithSize:(ccGridSize)gridSize duration:(ccTime)d;
+/** returns the grid */
+-(CCGridBase *)grid;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** Base class for CCGrid3D actions.
+ Grid3D actions can modify a non-tiled grid.
+ */
+@interface CCGrid3DAction : CCGridAction
+{
+}
+
+/** returns the vertex than belongs to certain position in the grid */
+-(ccVertex3F)vertex:(ccGridSize)pos;
+/** returns the non-transformed vertex than belongs to certain position in the grid */
+-(ccVertex3F)originalVertex:(ccGridSize)pos;
+/** sets a new vertex to a certain position of the grid */
+-(void)setVertex:(ccGridSize)pos vertex:(ccVertex3F)vertex;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** Base class for CCTiledGrid3D actions */
+@interface CCTiledGrid3DAction : CCGridAction
+{
+}
+
+/** returns the tile that belongs to a certain position of the grid */
+-(ccQuad3)tile:(ccGridSize)pos;
+/** returns the non-transformed tile that belongs to a certain position of the grid */
+-(ccQuad3)originalTile:(ccGridSize)pos;
+/** sets a new tile to a certain position of the grid */
+-(void)setTile:(ccGridSize)pos coords:(ccQuad3)coords;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCAccelDeccelAmplitude action */
+@interface CCAccelDeccelAmplitude : CCActionInterval
+{
+ float rate;
+ CCActionInterval *other;
+}
+
+/** amplitude rate */
+@property (nonatomic,readwrite) float rate;
+
+/** creates the action with an inner action that has the amplitude property, and a duration time */
++(id)actionWithAction:(CCAction*)action duration:(ccTime)d;
+/** initializes the action with an inner action that has the amplitude property, and a duration time */
+-(id)initWithAction:(CCAction*)action duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCAccelAmplitude action */
+@interface CCAccelAmplitude : CCActionInterval
+{
+ float rate;
+ CCActionInterval *other;
+}
+
+/** amplitude rate */
+@property (nonatomic,readwrite) float rate;
+
+/** creates the action with an inner action that has the amplitude property, and a duration time */
++(id)actionWithAction:(CCAction*)action duration:(ccTime)d;
+/** initializes the action with an inner action that has the amplitude property, and a duration time */
+-(id)initWithAction:(CCAction*)action duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCDeccelAmplitude action */
+@interface CCDeccelAmplitude : CCActionInterval
+{
+ float rate;
+ CCActionInterval *other;
+}
+
+/** amplitude rate */
+@property (nonatomic,readwrite) float rate;
+
+/** creates the action with an inner action that has the amplitude property, and a duration time */
++(id)actionWithAction:(CCAction*)action duration:(ccTime)d;
+/** initializes the action with an inner action that has the amplitude property, and a duration time */
+-(id)initWithAction:(CCAction*)action duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCStopGrid action.
+ Don't call this action if another grid action is active.
+ Call if you want to remove the the grid effect. Example:
+ [Sequence actions:[Lens ...], [StopGrid action], nil];
+ */
+@interface CCStopGrid : CCActionInstant
+{
+}
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCReuseGrid action */
+@interface CCReuseGrid : CCActionInstant
+{
+ int t;
+}
+/** creates an action with the number of times that the current grid will be reused */
++(id) actionWithTimes: (int) times;
+/** initializes an action with the number of times that the current grid will be reused */
+-(id) initWithTimes: (int) times;
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 On-Core
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCActionGrid.h"
+#import "CCDirector.h"
+
+#pragma mark -
+#pragma mark GridAction
+
+@implementation CCGridAction
+
+@synthesize gridSize = gridSize_;
+
++(id) actionWithSize:(ccGridSize)size duration:(ccTime)d
+{
+ return [[[self alloc] initWithSize:size duration:d ] autorelease];
+}
+
+-(id) initWithSize:(ccGridSize)gSize duration:(ccTime)d
+{
+ if ( (self = [super initWithDuration:d]) )
+ {
+ gridSize_ = gSize;
+ }
+
+ return self;
+}
+
+-(void)startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+
+ CCGridBase *newgrid = [self grid];
+
+ CCNode *t = (CCNode*) target_;
+ CCGridBase *targetGrid = [t grid];
+
+ if ( targetGrid && targetGrid.reuseGrid > 0 )
+ {
+ if ( targetGrid.active && targetGrid.gridSize.x == gridSize_.x && targetGrid.gridSize.y == gridSize_.y && [targetGrid isKindOfClass:[newgrid class]] )
+ [targetGrid reuse];
+ else
+ [NSException raise:@"GridBase" format:@"Cannot reuse grid"];
+ }
+ else
+ {
+ if ( targetGrid && targetGrid.active )
+ targetGrid.active = NO;
+
+ t.grid = newgrid;
+ t.grid.active = YES;
+ }
+}
+
+-(CCGridBase *)grid
+{
+ [NSException raise:@"GridBase" format:@"Abstract class needs implementation"];
+ return nil;
+}
+
+- (CCActionInterval*) reverse
+{
+ return [CCReverseTime actionWithAction:self];
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCGridAction *copy = [[[self class] allocWithZone:zone] initWithSize:gridSize_ duration:duration_];
+ return copy;
+}
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark Grid3DAction
+
+@implementation CCGrid3DAction
+
+-(CCGridBase *)grid
+{
+ return [CCGrid3D gridWithSize:gridSize_];
+}
+
+-(ccVertex3F)vertex:(ccGridSize)pos
+{
+ CCGrid3D *g = (CCGrid3D *)[target_ grid];
+ return [g vertex:pos];
+}
+
+-(ccVertex3F)originalVertex:(ccGridSize)pos
+{
+ CCGrid3D *g = (CCGrid3D *)[target_ grid];
+ return [g originalVertex:pos];
+}
+
+-(void)setVertex:(ccGridSize)pos vertex:(ccVertex3F)vertex
+{
+ CCGrid3D *g = (CCGrid3D *)[target_ grid];
+ return [g setVertex:pos vertex:vertex];
+}
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark TiledGrid3DAction
+
+@implementation CCTiledGrid3DAction
+
+-(CCGridBase *)grid
+{
+ return [CCTiledGrid3D gridWithSize:gridSize_];
+}
+
+-(ccQuad3)tile:(ccGridSize)pos
+{
+ CCTiledGrid3D *g = (CCTiledGrid3D *)[target_ grid];
+ return [g tile:pos];
+}
+
+-(ccQuad3)originalTile:(ccGridSize)pos
+{
+ CCTiledGrid3D *g = (CCTiledGrid3D *)[target_ grid];
+ return [g originalTile:pos];
+}
+
+-(void)setTile:(ccGridSize)pos coords:(ccQuad3)coords
+{
+ CCTiledGrid3D *g = (CCTiledGrid3D *)[target_ grid];
+ [g setTile:pos coords:coords];
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+@interface CCActionInterval (Amplitude)
+-(void)setAmplitudeRate:(CGFloat)amp;
+-(CGFloat)getAmplitudeRate;
+@end
+
+@implementation CCActionInterval (Amplitude)
+-(void)setAmplitudeRate:(CGFloat)amp
+{
+ [NSException raise:@"IntervalAction (Amplitude)" format:@"Abstract class needs implementation"];
+}
+
+-(CGFloat)getAmplitudeRate
+{
+ [NSException raise:@"IntervalAction (Amplitude)" format:@"Abstract class needs implementation"];
+ return 0;
+}
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark AccelDeccelAmplitude
+
+@implementation CCAccelDeccelAmplitude
+
+@synthesize rate;
+
++(id)actionWithAction:(CCAction*)action duration:(ccTime)d
+{
+ return [[[self alloc] initWithAction:action duration:d ] autorelease];
+}
+
+-(id)initWithAction:(CCAction *)action duration:(ccTime)d
+{
+ if ( (self = [super initWithDuration:d]) )
+ {
+ rate = 1.0f;
+ other = [action retain];
+ }
+
+ return self;
+}
+
+-(void)dealloc
+{
+ [other release];
+ [super dealloc];
+}
+
+-(void)startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ [other startWithTarget:target_];
+}
+
+-(void) update: (ccTime) time
+{
+ float f = time*2;
+
+ if (f > 1)
+ {
+ f -= 1;
+ f = 1 - f;
+ }
+
+ [other setAmplitudeRate:powf(f, rate)];
+ [other update:time];
+}
+
+- (CCActionInterval*) reverse
+{
+ return [CCAccelDeccelAmplitude actionWithAction:[other reverse] duration:duration_];
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark AccelAmplitude
+
+@implementation CCAccelAmplitude
+
+@synthesize rate;
+
++(id)actionWithAction:(CCAction*)action duration:(ccTime)d
+{
+ return [[[self alloc] initWithAction:action duration:d ] autorelease];
+}
+
+-(id)initWithAction:(CCAction *)action duration:(ccTime)d
+{
+ if ( (self = [super initWithDuration:d]) )
+ {
+ rate = 1.0f;
+ other = [action retain];
+ }
+
+ return self;
+}
+
+-(void)dealloc
+{
+ [other release];
+ [super dealloc];
+}
+
+-(void)startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ [other startWithTarget:target_];
+}
+
+-(void) update: (ccTime) time
+{
+ [other setAmplitudeRate:powf(time, rate)];
+ [other update:time];
+}
+
+- (CCActionInterval*) reverse
+{
+ return [CCAccelAmplitude actionWithAction:[other reverse] duration:self.duration];
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark DeccelAmplitude
+
+@implementation CCDeccelAmplitude
+
+@synthesize rate;
+
++(id)actionWithAction:(CCAction*)action duration:(ccTime)d
+{
+ return [[[self alloc] initWithAction:action duration:d ] autorelease];
+}
+
+-(id)initWithAction:(CCAction *)action duration:(ccTime)d
+{
+ if ( (self = [super initWithDuration:d]) )
+ {
+ rate = 1.0f;
+ other = [action retain];
+ }
+
+ return self;
+}
+
+-(void)dealloc
+{
+ [other release];
+ [super dealloc];
+}
+
+-(void)startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ [other startWithTarget:target_];
+}
+
+-(void) update: (ccTime) time
+{
+ [other setAmplitudeRate:powf((1-time), rate)];
+ [other update:time];
+}
+
+- (CCActionInterval*) reverse
+{
+ return [CCDeccelAmplitude actionWithAction:[other reverse] duration:self.duration];
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark StopGrid
+
+@implementation CCStopGrid
+
+-(void)startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+
+ if ( [[self target] grid] && [[[self target] grid] active] ) {
+ [[[self target] grid] setActive: NO];
+
+// [[self target] setGrid: nil];
+ }
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark ReuseGrid
+
+@implementation CCReuseGrid
+
++(id)actionWithTimes:(int)times
+{
+ return [[[self alloc] initWithTimes:times ] autorelease];
+}
+
+-(id)initWithTimes:(int)times
+{
+ if ( (self = [super init]) )
+ t = times;
+
+ return self;
+}
+
+-(void)startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+
+ CCNode *myTarget = (CCNode*) [self target];
+ if ( myTarget.grid && myTarget.grid.active )
+ myTarget.grid.reuseGrid += t;
+}
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 On-Core
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCActionGrid.h"
+
+/** CCWaves3D action */
+@interface CCWaves3D : CCGrid3DAction
+{
+ int waves;
+ float amplitude;
+ float amplitudeRate;
+}
+
+/** amplitude of the wave */
+@property (nonatomic,readwrite) float amplitude;
+/** amplitude rate of the wave */
+@property (nonatomic,readwrite) float amplitudeRate;
+
++(id)actionWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d;
+-(id)initWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCFlipX3D action */
+@interface CCFlipX3D : CCGrid3DAction
+{
+}
+
+/** creates the action with duration */
++(id) actionWithDuration:(ccTime)d;
+/** initizlies the action with duration */
+-(id) initWithDuration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCFlipY3D action */
+@interface CCFlipY3D : CCFlipX3D
+{
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCLens3D action */
+@interface CCLens3D : CCGrid3DAction
+{
+ CGPoint position_;
+ CGPoint positionInPixels_;
+ float radius_;
+ float lensEffect_;
+ BOOL dirty_;
+}
+
+/** lens effect. Defaults to 0.7 - 0 means no effect, 1 is very strong effect */
+@property (nonatomic,readwrite) float lensEffect;
+/** lens center position in Points */
+@property (nonatomic,readwrite) CGPoint position;
+
+/** creates the action with center position in Points, radius, a grid size and duration */
++(id)actionWithPosition:(CGPoint)pos radius:(float)r grid:(ccGridSize)gridSize duration:(ccTime)d;
+/** initializes the action with center position in Points, radius, a grid size and duration */
+-(id)initWithPosition:(CGPoint)pos radius:(float)r grid:(ccGridSize)gridSize duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCRipple3D action */
+@interface CCRipple3D : CCGrid3DAction
+{
+ CGPoint position_;
+ CGPoint positionInPixels_;
+ float radius_;
+ int waves_;
+ float amplitude_;
+ float amplitudeRate_;
+}
+
+/** center position in Points */
+@property (nonatomic,readwrite) CGPoint position;
+/** amplitude */
+@property (nonatomic,readwrite) float amplitude;
+/** amplitude rate */
+@property (nonatomic,readwrite) float amplitudeRate;
+
+/** creates the action with a position in points, radius, number of waves, amplitude, a grid size and duration */
++(id)actionWithPosition:(CGPoint)pos radius:(float)r waves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d;
+/** initializes the action with a position in points, radius, number of waves, amplitude, a grid size and duration */
+-(id)initWithPosition:(CGPoint)pos radius:(float)r waves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCShaky3D action */
+@interface CCShaky3D : CCGrid3DAction
+{
+ int randrange;
+ BOOL shakeZ;
+}
+
+/** creates the action with a range, shake Z vertices, a grid and duration */
++(id)actionWithRange:(int)range shakeZ:(BOOL)shakeZ grid:(ccGridSize)gridSize duration:(ccTime)d;
+/** initializes the action with a range, shake Z vertices, a grid and duration */
+-(id)initWithRange:(int)range shakeZ:(BOOL)shakeZ grid:(ccGridSize)gridSize duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCLiquid action */
+@interface CCLiquid : CCGrid3DAction
+{
+ int waves;
+ float amplitude;
+ float amplitudeRate;
+
+}
+
+/** amplitude */
+@property (nonatomic,readwrite) float amplitude;
+/** amplitude rate */
+@property (nonatomic,readwrite) float amplitudeRate;
+
+/** creates the action with amplitude, a grid and duration */
++(id)actionWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d;
+/** initializes the action with amplitude, a grid and duration */
+-(id)initWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCWaves action */
+@interface CCWaves : CCGrid3DAction
+{
+ int waves;
+ float amplitude;
+ float amplitudeRate;
+ BOOL vertical;
+ BOOL horizontal;
+}
+
+/** amplitude */
+@property (nonatomic,readwrite) float amplitude;
+/** amplitude rate */
+@property (nonatomic,readwrite) float amplitudeRate;
+
+/** initializes the action with amplitude, horizontal sin, vertical sin, a grid and duration */
++(id)actionWithWaves:(int)wav amplitude:(float)amp horizontal:(BOOL)h vertical:(BOOL)v grid:(ccGridSize)gridSize duration:(ccTime)d;
+/** creates the action with amplitude, horizontal sin, vertical sin, a grid and duration */
+-(id)initWithWaves:(int)wav amplitude:(float)amp horizontal:(BOOL)h vertical:(BOOL)v grid:(ccGridSize)gridSize duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCTwirl action */
+@interface CCTwirl : CCGrid3DAction
+{
+ CGPoint position_;
+ CGPoint positionInPixels_;
+ int twirls_;
+ float amplitude_;
+ float amplitudeRate_;
+}
+
+/** twirl center */
+@property (nonatomic,readwrite) CGPoint position;
+/** amplitude */
+@property (nonatomic,readwrite) float amplitude;
+/** amplitude rate */
+@property (nonatomic,readwrite) float amplitudeRate;
+
+/** creates the action with center position, number of twirls, amplitude, a grid size and duration */
++(id)actionWithPosition:(CGPoint)pos twirls:(int)t amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d;
+/** initializes the action with center position, number of twirls, amplitude, a grid size and duration */
+-(id)initWithPosition:(CGPoint)pos twirls:(int)t amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d;
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 On-Core
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCActionGrid3D.h"
+#import "ccMacros.h"
+#import "Support/CGPointExtension.h"
+
+#pragma mark -
+#pragma mark Waves3D
+
+@implementation CCWaves3D
+
+@synthesize amplitude;
+@synthesize amplitudeRate;
+
++(id)actionWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d
+{
+ return [[[self alloc] initWithWaves:wav amplitude:amp grid:gridSize duration:d] autorelease];
+}
+
+-(id)initWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gSize duration:(ccTime)d
+{
+ if ( (self = [super initWithSize:gSize duration:d]) )
+ {
+ waves = wav;
+ amplitude = amp;
+ amplitudeRate = 1.0f;
+ }
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCGridAction *copy = [[[self class] allocWithZone:zone] initWithWaves:waves amplitude:amplitude grid:gridSize_ duration:duration_];
+ return copy;
+}
+
+
+-(void)update:(ccTime)time
+{
+ int i, j;
+
+ for( i = 0; i < (gridSize_.x+1); i++ )
+ {
+ for( j = 0; j < (gridSize_.y+1); j++ )
+ {
+ ccVertex3F v = [self originalVertex:ccg(i,j)];
+ v.z += (sinf((CGFloat)M_PI*time*waves*2 + (v.y+v.x) * .01f) * amplitude * amplitudeRate);
+ [self setVertex:ccg(i,j) vertex:v];
+ }
+ }
+}
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark FlipX3D
+
+@implementation CCFlipX3D
+
++(id) actionWithDuration:(ccTime)d
+{
+ return [[[self alloc] initWithSize:ccg(1,1) duration:d] autorelease];
+}
+
+-(id) initWithDuration:(ccTime)d
+{
+ return [super initWithSize:ccg(1,1) duration:d];
+}
+
+-(id)initWithSize:(ccGridSize)gSize duration:(ccTime)d
+{
+ if ( gSize.x != 1 || gSize.y != 1 )
+ {
+ [NSException raise:@"FlipX3D" format:@"Grid size must be (1,1)"];
+ }
+
+ return [super initWithSize:gSize duration:d];
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCGridAction *copy = [[[self class] allocWithZone:zone] initWithSize:gridSize_ duration:duration_];
+ return copy;
+}
+
+
+-(void)update:(ccTime)time
+{
+ CGFloat angle = (CGFloat)M_PI * time; // 180 degrees
+ CGFloat mz = sinf( angle );
+ angle = angle / 2.0f; // x calculates degrees from 0 to 90
+ CGFloat mx = cosf( angle );
+
+ ccVertex3F v0, v1, v, diff;
+
+ v0 = [self originalVertex:ccg(1,1)];
+ v1 = [self originalVertex:ccg(0,0)];
+
+ CGFloat x0 = v0.x;
+ CGFloat x1 = v1.x;
+ CGFloat x;
+ ccGridSize a, b, c, d;
+
+ if ( x0 > x1 )
+ {
+ // Normal Grid
+ a = ccg(0,0);
+ b = ccg(0,1);
+ c = ccg(1,0);
+ d = ccg(1,1);
+ x = x0;
+ }
+ else
+ {
+ // Reversed Grid
+ c = ccg(0,0);
+ d = ccg(0,1);
+ a = ccg(1,0);
+ b = ccg(1,1);
+ x = x1;
+ }
+
+ diff.x = ( x - x * mx );
+ diff.z = fabsf( floorf( (x * mz) / 4.0f ) );
+
+// bottom-left
+ v = [self originalVertex:a];
+ v.x = diff.x;
+ v.z += diff.z;
+ [self setVertex:a vertex:v];
+
+// upper-left
+ v = [self originalVertex:b];
+ v.x = diff.x;
+ v.z += diff.z;
+ [self setVertex:b vertex:v];
+
+// bottom-right
+ v = [self originalVertex:c];
+ v.x -= diff.x;
+ v.z -= diff.z;
+ [self setVertex:c vertex:v];
+
+// upper-right
+ v = [self originalVertex:d];
+ v.x -= diff.x;
+ v.z -= diff.z;
+ [self setVertex:d vertex:v];
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark FlipY3D
+
+@implementation CCFlipY3D
+
+-(void)update:(ccTime)time
+{
+ CGFloat angle = (CGFloat)M_PI * time; // 180 degrees
+ CGFloat mz = sinf( angle );
+ angle = angle / 2.0f; // x calculates degrees from 0 to 90
+ CGFloat my = cosf( angle );
+
+ ccVertex3F v0, v1, v, diff;
+
+ v0 = [self originalVertex:ccg(1,1)];
+ v1 = [self originalVertex:ccg(0,0)];
+
+ CGFloat y0 = v0.y;
+ CGFloat y1 = v1.y;
+ CGFloat y;
+ ccGridSize a, b, c, d;
+
+ if ( y0 > y1 )
+ {
+ // Normal Grid
+ a = ccg(0,0);
+ b = ccg(0,1);
+ c = ccg(1,0);
+ d = ccg(1,1);
+ y = y0;
+ }
+ else
+ {
+ // Reversed Grid
+ b = ccg(0,0);
+ a = ccg(0,1);
+ d = ccg(1,0);
+ c = ccg(1,1);
+ y = y1;
+ }
+
+ diff.y = y - y * my;
+ diff.z = fabsf( floorf( (y * mz) / 4.0f ) );
+
+ // bottom-left
+ v = [self originalVertex:a];
+ v.y = diff.y;
+ v.z += diff.z;
+ [self setVertex:a vertex:v];
+
+ // upper-left
+ v = [self originalVertex:b];
+ v.y -= diff.y;
+ v.z -= diff.z;
+ [self setVertex:b vertex:v];
+
+ // bottom-right
+ v = [self originalVertex:c];
+ v.y = diff.y;
+ v.z += diff.z;
+ [self setVertex:c vertex:v];
+
+ // upper-right
+ v = [self originalVertex:d];
+ v.y -= diff.y;
+ v.z -= diff.z;
+ [self setVertex:d vertex:v];
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark Lens3D
+
+@implementation CCLens3D
+
+@synthesize lensEffect=lensEffect_;
+
++(id)actionWithPosition:(CGPoint)pos radius:(float)r grid:(ccGridSize)gridSize duration:(ccTime)d
+{
+ return [[[self alloc] initWithPosition:pos radius:r grid:gridSize duration:d] autorelease];
+}
+
+-(id)initWithPosition:(CGPoint)pos radius:(float)r grid:(ccGridSize)gSize duration:(ccTime)d
+{
+ if ( (self = [super initWithSize:gSize duration:d]) )
+ {
+ position_ = ccp(-1,-1);
+ self.position = pos;
+ radius_ = r;
+ lensEffect_ = 0.7f;
+ dirty_ = YES;
+ }
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCGridAction *copy = [[[self class] allocWithZone:zone] initWithPosition:position_ radius:radius_ grid:gridSize_ duration:duration_];
+ return copy;
+}
+
+-(void) setPosition:(CGPoint)pos
+{
+ if( ! CGPointEqualToPoint(pos, position_) ) {
+ position_ = pos;
+ positionInPixels_.x = pos.x * CC_CONTENT_SCALE_FACTOR();
+ positionInPixels_.y = pos.y * CC_CONTENT_SCALE_FACTOR();
+
+ dirty_ = YES;
+ }
+}
+
+-(CGPoint) position
+{
+ return position_;
+}
+
+-(void)update:(ccTime)time
+{
+ if ( dirty_ )
+ {
+ int i, j;
+
+ for( i = 0; i < gridSize_.x+1; i++ )
+ {
+ for( j = 0; j < gridSize_.y+1; j++ )
+ {
+ ccVertex3F v = [self originalVertex:ccg(i,j)];
+ CGPoint vect = ccpSub(positionInPixels_, ccp(v.x,v.y));
+ CGFloat r = ccpLength(vect);
+
+ if ( r < radius_ )
+ {
+ r = radius_ - r;
+ CGFloat pre_log = r / radius_;
+ if ( pre_log == 0 ) pre_log = 0.001f;
+ float l = logf(pre_log) * lensEffect_;
+ float new_r = expf( l ) * radius_;
+
+ if ( ccpLength(vect) > 0 )
+ {
+ vect = ccpNormalize(vect);
+ CGPoint new_vect = ccpMult(vect, new_r);
+ v.z += ccpLength(new_vect) * lensEffect_;
+ }
+ }
+
+ [self setVertex:ccg(i,j) vertex:v];
+ }
+ }
+
+ dirty_ = NO;
+ }
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark Ripple3D
+
+@implementation CCRipple3D
+
+@synthesize amplitude = amplitude_;
+@synthesize amplitudeRate = amplitudeRate_;
+
++(id)actionWithPosition:(CGPoint)pos radius:(float)r waves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d
+{
+ return [[[self alloc] initWithPosition:pos radius:r waves:wav amplitude:amp grid:gridSize duration:d] autorelease];
+}
+
+-(id)initWithPosition:(CGPoint)pos radius:(float)r waves:(int)wav amplitude:(float)amp grid:(ccGridSize)gSize duration:(ccTime)d
+{
+ if ( (self = [super initWithSize:gSize duration:d]) )
+ {
+ self.position = pos;
+ radius_ = r;
+ waves_ = wav;
+ amplitude_ = amp;
+ amplitudeRate_ = 1.0f;
+ }
+
+ return self;
+}
+
+-(CGPoint) position
+{
+ return position_;
+}
+
+-(void) setPosition:(CGPoint)pos
+{
+ position_ = pos;
+ positionInPixels_.x = pos.x * CC_CONTENT_SCALE_FACTOR();
+ positionInPixels_.y = pos.y * CC_CONTENT_SCALE_FACTOR();
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCGridAction *copy = [[[self class] allocWithZone:zone] initWithPosition:position_ radius:radius_ waves:waves_ amplitude:amplitude_ grid:gridSize_ duration:duration_];
+ return copy;
+}
+
+
+-(void)update:(ccTime)time
+{
+ int i, j;
+
+ for( i = 0; i < (gridSize_.x+1); i++ )
+ {
+ for( j = 0; j < (gridSize_.y+1); j++ )
+ {
+ ccVertex3F v = [self originalVertex:ccg(i,j)];
+ CGPoint vect = ccpSub(positionInPixels_, ccp(v.x,v.y));
+ CGFloat r = ccpLength(vect);
+
+ if ( r < radius_ )
+ {
+ r = radius_ - r;
+ CGFloat rate = powf( r / radius_, 2);
+ v.z += (sinf( time*(CGFloat)M_PI*waves_*2 + r * 0.1f) * amplitude_ * amplitudeRate_ * rate );
+ }
+
+ [self setVertex:ccg(i,j) vertex:v];
+ }
+ }
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark Shaky3D
+
+@implementation CCShaky3D
+
++(id)actionWithRange:(int)range shakeZ:(BOOL)sz grid:(ccGridSize)gridSize duration:(ccTime)d
+{
+ return [[[self alloc] initWithRange:range shakeZ:sz grid:gridSize duration:d] autorelease];
+}
+
+-(id)initWithRange:(int)range shakeZ:(BOOL)sz grid:(ccGridSize)gSize duration:(ccTime)d
+{
+ if ( (self = [super initWithSize:gSize duration:d]) )
+ {
+ randrange = range;
+ shakeZ = sz;
+ }
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCGridAction *copy = [[[self class] allocWithZone:zone] initWithRange:randrange shakeZ:shakeZ grid:gridSize_ duration:duration_];
+ return copy;
+}
+
+-(void)update:(ccTime)time
+{
+ int i, j;
+
+ for( i = 0; i < (gridSize_.x+1); i++ )
+ {
+ for( j = 0; j < (gridSize_.y+1); j++ )
+ {
+ ccVertex3F v = [self originalVertex:ccg(i,j)];
+ v.x += ( rand() % (randrange*2) ) - randrange;
+ v.y += ( rand() % (randrange*2) ) - randrange;
+ if( shakeZ )
+ v.z += ( rand() % (randrange*2) ) - randrange;
+
+ [self setVertex:ccg(i,j) vertex:v];
+ }
+ }
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark Liquid
+
+@implementation CCLiquid
+
+@synthesize amplitude;
+@synthesize amplitudeRate;
+
++(id)actionWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d
+{
+ return [[[self alloc] initWithWaves:wav amplitude:amp grid:gridSize duration:d] autorelease];
+}
+
+-(id)initWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gSize duration:(ccTime)d
+{
+ if ( (self = [super initWithSize:gSize duration:d]) )
+ {
+ waves = wav;
+ amplitude = amp;
+ amplitudeRate = 1.0f;
+ }
+
+ return self;
+}
+
+-(void)update:(ccTime)time
+{
+ int i, j;
+
+ for( i = 1; i < gridSize_.x; i++ )
+ {
+ for( j = 1; j < gridSize_.y; j++ )
+ {
+ ccVertex3F v = [self originalVertex:ccg(i,j)];
+ v.x = (v.x + (sinf(time*(CGFloat)M_PI*waves*2 + v.x * .01f) * amplitude * amplitudeRate));
+ v.y = (v.y + (sinf(time*(CGFloat)M_PI*waves*2 + v.y * .01f) * amplitude * amplitudeRate));
+ [self setVertex:ccg(i,j) vertex:v];
+ }
+ }
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCGridAction *copy = [[[self class] allocWithZone:zone] initWithWaves:waves amplitude:amplitude grid:gridSize_ duration:duration_];
+ return copy;
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark Waves
+
+@implementation CCWaves
+
+@synthesize amplitude;
+@synthesize amplitudeRate;
+
++(id)actionWithWaves:(int)wav amplitude:(float)amp horizontal:(BOOL)h vertical:(BOOL)v grid:(ccGridSize)gridSize duration:(ccTime)d
+{
+ return [[[self alloc] initWithWaves:wav amplitude:amp horizontal:h vertical:v grid:gridSize duration:d] autorelease];
+}
+
+-(id)initWithWaves:(int)wav amplitude:(float)amp horizontal:(BOOL)h vertical:(BOOL)v grid:(ccGridSize)gSize duration:(ccTime)d
+{
+ if ( (self = [super initWithSize:gSize duration:d]) )
+ {
+ waves = wav;
+ amplitude = amp;
+ amplitudeRate = 1.0f;
+ horizontal = h;
+ vertical = v;
+ }
+
+ return self;
+}
+
+-(void)update:(ccTime)time
+{
+ int i, j;
+
+ for( i = 0; i < (gridSize_.x+1); i++ )
+ {
+ for( j = 0; j < (gridSize_.y+1); j++ )
+ {
+ ccVertex3F v = [self originalVertex:ccg(i,j)];
+
+ if ( vertical )
+ v.x = (v.x + (sinf(time*(CGFloat)M_PI*waves*2 + v.y * .01f) * amplitude * amplitudeRate));
+
+ if ( horizontal )
+ v.y = (v.y + (sinf(time*(CGFloat)M_PI*waves*2 + v.x * .01f) * amplitude * amplitudeRate));
+
+ [self setVertex:ccg(i,j) vertex:v];
+ }
+ }
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCGridAction *copy = [[[self class] allocWithZone:zone] initWithWaves:waves amplitude:amplitude horizontal:horizontal vertical:vertical grid:gridSize_ duration:duration_];
+ return copy;
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark Twirl
+
+@implementation CCTwirl
+
+@synthesize amplitude = amplitude_;
+@synthesize amplitudeRate = amplitudeRate_;
+
++(id)actionWithPosition:(CGPoint)pos twirls:(int)t amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d
+{
+ return [[[self alloc] initWithPosition:pos twirls:t amplitude:amp grid:gridSize duration:d] autorelease];
+}
+
+-(id)initWithPosition:(CGPoint)pos twirls:(int)t amplitude:(float)amp grid:(ccGridSize)gSize duration:(ccTime)d
+{
+ if ( (self = [super initWithSize:gSize duration:d]) )
+ {
+ self.position = pos;
+ twirls_ = t;
+ amplitude_ = amp;
+ amplitudeRate_ = 1.0f;
+ }
+
+ return self;
+}
+
+-(void) setPosition:(CGPoint)pos
+{
+ position_ = pos;
+ positionInPixels_.x = pos.x * CC_CONTENT_SCALE_FACTOR();
+ positionInPixels_.y = pos.y * CC_CONTENT_SCALE_FACTOR();
+}
+
+-(CGPoint) position
+{
+ return position_;
+}
+
+-(void)update:(ccTime)time
+{
+ int i, j;
+ CGPoint c = positionInPixels_;
+
+ for( i = 0; i < (gridSize_.x+1); i++ )
+ {
+ for( j = 0; j < (gridSize_.y+1); j++ )
+ {
+ ccVertex3F v = [self originalVertex:ccg(i,j)];
+
+ CGPoint avg = ccp(i-(gridSize_.x/2.0f), j-(gridSize_.y/2.0f));
+ CGFloat r = ccpLength( avg );
+
+ CGFloat amp = 0.1f * amplitude_ * amplitudeRate_;
+ CGFloat a = r * cosf( (CGFloat)M_PI/2.0f + time * (CGFloat)M_PI * twirls_ * 2 ) * amp;
+
+ float cosA = cosf(a);
+ float sinA = sinf(a);
+
+ CGPoint d = {
+ sinA * (v.y-c.y) + cosA * (v.x-c.x),
+ cosA * (v.y-c.y) - sinA * (v.x-c.x)
+ };
+
+ v.x = c.x + d.x;
+ v.y = c.y + d.y;
+
+ [self setVertex:ccg(i,j) vertex:v];
+ }
+ }
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCGridAction *copy = [[[self class] allocWithZone:zone] initWithPosition:position_
+ twirls:twirls_
+ amplitude:amplitude_
+ grid:gridSize_
+ duration:duration_];
+ return copy;
+}
+
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCAction.h"
+
+/** Instant actions are immediate actions. They don't have a duration like
+ the CCIntervalAction actions.
+*/
+@interface CCActionInstant : CCFiniteTimeAction <NSCopying>
+{
+}
+@end
+
+/** Show the node
+ */
+ @interface CCShow : CCActionInstant
+{
+}
+@end
+
+/** Hide the node
+ */
+@interface CCHide : CCActionInstant
+{
+}
+@end
+
+/** Toggles the visibility of a node
+ */
+@interface CCToggleVisibility : CCActionInstant
+{
+}
+@end
+
+/** Flips the sprite horizontally
+ @since v0.99.0
+ */
+@interface CCFlipX : CCActionInstant
+{
+ BOOL flipX;
+}
++(id) actionWithFlipX:(BOOL)x;
+-(id) initWithFlipX:(BOOL)x;
+@end
+
+/** Flips the sprite vertically
+ @since v0.99.0
+ */
+@interface CCFlipY : CCActionInstant
+{
+ BOOL flipY;
+}
++(id) actionWithFlipY:(BOOL)y;
+-(id) initWithFlipY:(BOOL)y;
+@end
+
+/** Places the node in a certain position
+ */
+@interface CCPlace : CCActionInstant <NSCopying>
+{
+ CGPoint position;
+}
+/** creates a Place action with a position */
++(id) actionWithPosition: (CGPoint) pos;
+/** Initializes a Place action with a position */
+-(id) initWithPosition: (CGPoint) pos;
+@end
+
+/** Calls a 'callback'
+ */
+@interface CCCallFunc : CCActionInstant <NSCopying>
+{
+ id targetCallback_;
+ SEL selector_;
+}
+
+/** Target that will be called */
+@property (nonatomic, readwrite, retain) id targetCallback;
+
+/** creates the action with the callback */
++(id) actionWithTarget: (id) t selector:(SEL) s;
+/** initializes the action with the callback */
+-(id) initWithTarget: (id) t selector:(SEL) s;
+/** exeuctes the callback */
+-(void) execute;
+@end
+
+/** Calls a 'callback' with the node as the first argument.
+ N means Node
+ */
+@interface CCCallFuncN : CCCallFunc
+{
+}
+@end
+
+typedef void (*CC_CALLBACK_ND)(id, SEL, id, void *);
+/** Calls a 'callback' with the node as the first argument and the 2nd argument is data.
+ * ND means: Node and Data. Data is void *, so it could be anything.
+ */
+@interface CCCallFuncND : CCCallFuncN
+{
+ void *data_;
+ CC_CALLBACK_ND callbackMethod_;
+}
+
+/** Invocation object that has the target#selector and the parameters */
+@property (nonatomic,readwrite) CC_CALLBACK_ND callbackMethod;
+
+/** creates the action with the callback and the data to pass as an argument */
++(id) actionWithTarget: (id) t selector:(SEL) s data:(void*)d;
+/** initializes the action with the callback and the data to pass as an argument */
+-(id) initWithTarget:(id) t selector:(SEL) s data:(void*) d;
+@end
+
+/** Calls a 'callback' with an object as the first argument.
+ O means Object.
+ @since v0.99.5
+ */
+@interface CCCallFuncO : CCCallFunc
+{
+ id object_;
+}
+/** object to be passed as argument */
+@property (nonatomic, readwrite, retain) id object;
+
+/** creates the action with the callback and the object to pass as an argument */
++(id) actionWithTarget: (id) t selector:(SEL) s object:(id)object;
+/** initializes the action with the callback and the object to pass as an argument */
+-(id) initWithTarget:(id) t selector:(SEL) s object:(id)object;
+
+@end
+
+#pragma mark Blocks Support
+
+#if NS_BLOCKS_AVAILABLE
+
+/** Executes a callback using a block.
+ */
+@interface CCCallBlock : CCActionInstant<NSCopying>
+{
+ void (^block_)();
+}
+
+/** creates the action with the specified block, to be used as a callback.
+ The block will be "copied".
+ */
++(id) actionWithBlock:(void(^)())block;
+
+/** initialized the action with the specified block, to be used as a callback.
+ The block will be "copied".
+ */
+-(id) initWithBlock:(void(^)())block;
+
+/** executes the callback */
+-(void) execute;
+@end
+
+@class CCNode;
+
+/** Executes a callback using a block with a single CCNode parameter.
+ */
+@interface CCCallBlockN : CCActionInstant<NSCopying>
+{
+ void (^block_)(CCNode *);
+}
+
+/** creates the action with the specified block, to be used as a callback.
+ The block will be "copied".
+ */
++(id) actionWithBlock:(void(^)(CCNode *node))block;
+
+/** initialized the action with the specified block, to be used as a callback.
+ The block will be "copied".
+ */
+-(id) initWithBlock:(void(^)(CCNode *node))block;
+
+/** executes the callback */
+-(void) execute;
+@end
+
+#endif
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCBlockSupport.h"
+#import "CCActionInstant.h"
+#import "CCNode.h"
+#import "CCSprite.h"
+
+
+//
+// InstantAction
+//
+#pragma mark CCActionInstant
+
+@implementation CCActionInstant
+
+-(id) init
+{
+ if( (self=[super init]) )
+ duration_ = 0;
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCActionInstant *copy = [[[self class] allocWithZone: zone] init];
+ return copy;
+}
+
+- (BOOL) isDone
+{
+ return YES;
+}
+
+-(void) step: (ccTime) dt
+{
+ [self update: 1];
+}
+
+-(void) update: (ccTime) t
+{
+ // ignore
+}
+
+-(CCFiniteTimeAction*) reverse
+{
+ return [[self copy] autorelease];
+}
+@end
+
+//
+// Show
+//
+#pragma mark CCShow
+
+@implementation CCShow
+-(void) startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ ((CCNode *)target_).visible = YES;
+}
+
+-(CCFiniteTimeAction*) reverse
+{
+ return [CCHide action];
+}
+@end
+
+//
+// Hide
+//
+#pragma mark CCHide
+
+@implementation CCHide
+-(void) startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ ((CCNode *)target_).visible = NO;
+}
+
+-(CCFiniteTimeAction*) reverse
+{
+ return [CCShow action];
+}
+@end
+
+//
+// ToggleVisibility
+//
+#pragma mark CCToggleVisibility
+
+@implementation CCToggleVisibility
+-(void) startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ ((CCNode *)target_).visible = !((CCNode *)target_).visible;
+}
+@end
+
+//
+// FlipX
+//
+#pragma mark CCFlipX
+
+@implementation CCFlipX
++(id) actionWithFlipX:(BOOL)x
+{
+ return [[[self alloc] initWithFlipX:x] autorelease];
+}
+
+-(id) initWithFlipX:(BOOL)x
+{
+ if(( self=[super init]))
+ flipX = x;
+
+ return self;
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ [(CCSprite*)aTarget setFlipX:flipX];
+}
+
+-(CCFiniteTimeAction*) reverse
+{
+ return [CCFlipX actionWithFlipX:!flipX];
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCActionInstant *copy = [[[self class] allocWithZone: zone] initWithFlipX:flipX];
+ return copy;
+}
+@end
+
+//
+// FlipY
+//
+#pragma mark CCFlipY
+
+@implementation CCFlipY
++(id) actionWithFlipY:(BOOL)y
+{
+ return [[[self alloc] initWithFlipY:y] autorelease];
+}
+
+-(id) initWithFlipY:(BOOL)y
+{
+ if(( self=[super init]))
+ flipY = y;
+
+ return self;
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ [(CCSprite*)aTarget setFlipY:flipY];
+}
+
+-(CCFiniteTimeAction*) reverse
+{
+ return [CCFlipY actionWithFlipY:!flipY];
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCActionInstant *copy = [[[self class] allocWithZone: zone] initWithFlipY:flipY];
+ return copy;
+}
+@end
+
+
+//
+// Place
+//
+#pragma mark CCPlace
+
+@implementation CCPlace
++(id) actionWithPosition: (CGPoint) pos
+{
+ return [[[self alloc]initWithPosition:pos]autorelease];
+}
+
+-(id) initWithPosition: (CGPoint) pos
+{
+ if( (self=[super init]) )
+ position = pos;
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCActionInstant *copy = [[[self class] allocWithZone: zone] initWithPosition: position];
+ return copy;
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ ((CCNode *)target_).position = position;
+}
+
+@end
+
+//
+// CallFunc
+//
+#pragma mark CCCallFunc
+
+@implementation CCCallFunc
+
+@synthesize targetCallback = targetCallback_;
+
++(id) actionWithTarget: (id) t selector:(SEL) s
+{
+ return [[[self alloc] initWithTarget: t selector: s] autorelease];
+}
+
+-(id) initWithTarget: (id) t selector:(SEL) s
+{
+ if( (self=[super init]) ) {
+ self.targetCallback = t;
+ selector_ = s;
+ }
+ return self;
+}
+
+-(NSString*) description
+{
+ return [NSString stringWithFormat:@"<%@ = %08X | Tag = %i | target = %@ | selector = %@>",
+ [self class],
+ self,
+ tag_,
+ [targetCallback_ class],
+ NSStringFromSelector(selector_)
+ ];
+}
+
+-(void) dealloc
+{
+ [targetCallback_ release];
+ [super dealloc];
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCActionInstant *copy = [[[self class] allocWithZone: zone] initWithTarget:targetCallback_ selector:selector_];
+ return copy;
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ [self execute];
+}
+
+-(void) execute
+{
+ [targetCallback_ performSelector:selector_];
+}
+@end
+
+//
+// CallFuncN
+//
+#pragma mark CCCallFuncN
+
+@implementation CCCallFuncN
+
+-(void) execute
+{
+ [targetCallback_ performSelector:selector_ withObject:target_];
+}
+@end
+
+//
+// CallFuncND
+//
+#pragma mark CCCallFuncND
+
+@implementation CCCallFuncND
+
+@synthesize callbackMethod = callbackMethod_;
+
++(id) actionWithTarget:(id)t selector:(SEL)s data:(void*)d
+{
+ return [[[self alloc] initWithTarget:t selector:s data:d] autorelease];
+}
+
+-(id) initWithTarget:(id)t selector:(SEL)s data:(void*)d
+{
+ if( (self=[super initWithTarget:t selector:s]) ) {
+ data_ = d;
+
+#if COCOS2D_DEBUG
+ NSMethodSignature * sig = [t methodSignatureForSelector:s]; // added
+ NSAssert(sig !=0 , @"Signature not found for selector - does it have the following form? -(void)name:(id)sender data:(void*)data");
+#endif
+ callbackMethod_ = (CC_CALLBACK_ND) [t methodForSelector:s];
+ }
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCActionInstant *copy = [[[self class] allocWithZone: zone] initWithTarget:targetCallback_ selector:selector_ data:data_];
+ return copy;
+}
+
+-(void) dealloc
+{
+ // nothing to dealloc really. Everything is dealloc on super (CCCallFuncN)
+ [super dealloc];
+}
+
+-(void) execute
+{
+ callbackMethod_(targetCallback_,selector_,target_, data_);
+}
+@end
+
+@implementation CCCallFuncO
+@synthesize object = object_;
+
++(id) actionWithTarget: (id) t selector:(SEL) s object:(id)object
+{
+ return [[[self alloc] initWithTarget:t selector:s object:object] autorelease];
+}
+
+-(id) initWithTarget:(id) t selector:(SEL) s object:(id)object
+{
+ if( (self=[super initWithTarget:t selector:s] ) )
+ self.object = object;
+
+ return self;
+}
+
+- (void) dealloc
+{
+ [object_ release];
+ [super dealloc];
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCActionInstant *copy = [[[self class] allocWithZone: zone] initWithTarget:targetCallback_ selector:selector_ object:object_];
+ return copy;
+}
+
+
+-(void) execute
+{
+ [targetCallback_ performSelector:selector_ withObject:object_];
+}
+
+@end
+
+
+#pragma mark -
+#pragma mark Blocks
+
+#if NS_BLOCKS_AVAILABLE
+
+#pragma mark CCCallBlock
+
+@implementation CCCallBlock
+
++(id) actionWithBlock:(void(^)())block
+{
+ return [[[self alloc] initWithBlock:block] autorelease];
+}
+
+-(id) initWithBlock:(void(^)())block
+{
+ if ((self = [super init]))
+ block_ = [block copy];
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCActionInstant *copy = [[[self class] allocWithZone: zone] initWithBlock:block_];
+ return copy;
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ [self execute];
+}
+
+-(void) execute
+{
+ block_();
+}
+
+-(void) dealloc
+{
+ [block_ release];
+ [super dealloc];
+}
+
+@end
+
+#pragma mark CCCallBlockN
+
+@implementation CCCallBlockN
+
++(id) actionWithBlock:(void(^)(CCNode *node))block
+{
+ return [[[self alloc] initWithBlock:block] autorelease];
+}
+
+-(id) initWithBlock:(void(^)(CCNode *node))block
+{
+ if ((self = [super init]))
+ block_ = [block copy];
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCActionInstant *copy = [[[self class] allocWithZone: zone] initWithBlock:block_];
+ return copy;
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ [self execute];
+}
+
+-(void) execute
+{
+ block_(target_);
+}
+
+-(void) dealloc
+{
+ [block_ release];
+ [super dealloc];
+}
+
+@end
+
+
+#endif // NS_BLOCKS_AVAILABLE
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2011 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCNode.h"
+#import "CCAction.h"
+#import "CCProtocols.h"
+
+#include <sys/time.h>
+
+/** An interval action is an action that takes place within a certain period of time.
+It has an start time, and a finish time. The finish time is the parameter
+duration plus the start time.
+
+These CCActionInterval actions have some interesting properties, like:
+ - They can run normally (default)
+ - They can run reversed with the reverse method
+ - They can run with the time altered with the Accelerate, AccelDeccel and Speed actions.
+
+For example, you can simulate a Ping Pong effect running the action normally and
+then running it again in Reverse mode.
+
+Example:
+
+ CCAction * pingPongAction = [CCSequence actions: action, [action reverse], nil];
+*/
+@interface CCActionInterval: CCFiniteTimeAction <NSCopying>
+{
+ ccTime elapsed_;
+ BOOL firstTick_;
+}
+
+/** how many seconds had elapsed since the actions started to run. */
+@property (nonatomic,readonly) ccTime elapsed;
+
+/** creates the action */
++(id) actionWithDuration: (ccTime) d;
+/** initializes the action */
+-(id) initWithDuration: (ccTime) d;
+/** returns YES if the action has finished */
+-(BOOL) isDone;
+/** returns a reversed action */
+- (CCActionInterval*) reverse;
+@end
+
+/** Runs actions sequentially, one after another
+ */
+@interface CCSequence : CCActionInterval <NSCopying>
+{
+ CCFiniteTimeAction *actions_[2];
+ ccTime split_;
+ int last_;
+}
+/** helper contructor to create an array of sequenceable actions */
++(id) actions: (CCFiniteTimeAction*) action1, ... NS_REQUIRES_NIL_TERMINATION;
+/** helper contructor to create an array of sequenceable actions given an array */
++(id) actionsWithArray: (NSArray*) actions;
+/** creates the action */
++(id) actionOne:(CCFiniteTimeAction*)actionOne two:(CCFiniteTimeAction*)actionTwo;
+/** initializes the action */
+-(id) initOne:(CCFiniteTimeAction*)actionOne two:(CCFiniteTimeAction*)actionTwo;
+@end
+
+
+/** Repeats an action a number of times.
+ * To repeat an action forever use the CCRepeatForever action.
+ */
+@interface CCRepeat : CCActionInterval <NSCopying>
+{
+ NSUInteger times_;
+ NSUInteger total_;
+ CCFiniteTimeAction *innerAction_;
+}
+
+/** Inner action */
+@property (nonatomic,readwrite,retain) CCFiniteTimeAction *innerAction;
+
+/** creates a CCRepeat action. Times is an unsigned integer between 1 and MAX_UINT */
++(id) actionWithAction:(CCFiniteTimeAction*)action times: (NSUInteger)times;
+/** initializes a CCRepeat action. Times is an unsigned integer between 1 and MAX_UINT */
+-(id) initWithAction:(CCFiniteTimeAction*)action times: (NSUInteger)times;
+@end
+
+/** Spawn a new action immediately
+ */
+@interface CCSpawn : CCActionInterval <NSCopying>
+{
+ CCFiniteTimeAction *one_;
+ CCFiniteTimeAction *two_;
+}
+/** helper constructor to create an array of spawned actions */
++(id) actions: (CCFiniteTimeAction*) action1, ... NS_REQUIRES_NIL_TERMINATION;
+/** helper contructor to create an array of spawned actions given an array */
++(id) actionsWithArray: (NSArray*) actions;
+/** creates the Spawn action */
++(id) actionOne: (CCFiniteTimeAction*) one two:(CCFiniteTimeAction*) two;
+/** initializes the Spawn action with the 2 actions to spawn */
+-(id) initOne: (CCFiniteTimeAction*) one two:(CCFiniteTimeAction*) two;
+@end
+
+/** Rotates a CCNode object to a certain angle by modifying it's
+ rotation attribute.
+ The direction will be decided by the shortest angle.
+*/
+@interface CCRotateTo : CCActionInterval <NSCopying>
+{
+ float dstAngle_;
+ float startAngle_;
+ float diffAngle_;
+}
+/** creates the action */
++(id) actionWithDuration:(ccTime)duration angle:(float)angle;
+/** initializes the action */
+-(id) initWithDuration:(ccTime)duration angle:(float)angle;
+@end
+
+/** Rotates a CCNode object clockwise a number of degrees by modiying it's rotation attribute.
+*/
+@interface CCRotateBy : CCActionInterval <NSCopying>
+{
+ float angle_;
+ float startAngle_;
+}
+/** creates the action */
++(id) actionWithDuration:(ccTime)duration angle:(float)deltaAngle;
+/** initializes the action */
+-(id) initWithDuration:(ccTime)duration angle:(float)deltaAngle;
+@end
+
+/** Moves a CCNode object to the position x,y. x and y are absolute coordinates by modifying it's position attribute.
+*/
+@interface CCMoveTo : CCActionInterval <NSCopying>
+{
+ CGPoint endPosition_;
+ CGPoint startPosition_;
+ CGPoint delta_;
+}
+/** creates the action */
++(id) actionWithDuration:(ccTime)duration position:(CGPoint)position;
+/** initializes the action */
+-(id) initWithDuration:(ccTime)duration position:(CGPoint)position;
+@end
+
+/** Moves a CCNode object x,y pixels by modifying it's position attribute.
+ x and y are relative to the position of the object.
+ Duration is is seconds.
+*/
+@interface CCMoveBy : CCMoveTo <NSCopying>
+{
+}
+/** creates the action */
++(id) actionWithDuration: (ccTime)duration position:(CGPoint)deltaPosition;
+/** initializes the action */
+-(id) initWithDuration: (ccTime)duration position:(CGPoint)deltaPosition;
+@end
+
+/** Skews a CCNode object to given angles by modifying it's skewX and skewY attributes
+ @since v1.0
+ */
+@interface CCSkewTo : CCActionInterval <NSCopying>
+{
+ float skewX_;
+ float skewY_;
+ float startSkewX_;
+ float startSkewY_;
+ float endSkewX_;
+ float endSkewY_;
+ float deltaX_;
+ float deltaY_;
+}
+/** creates the action */
++(id) actionWithDuration:(ccTime)t skewX:(float)sx skewY:(float)sy;
+/** initializes the action */
+-(id) initWithDuration:(ccTime)t skewX:(float)sx skewY:(float)sy;
+@end
+
+/** Skews a CCNode object by skewX and skewY degrees
+ @since v1.0
+ */
+@interface CCSkewBy : CCSkewTo <NSCopying>
+{
+}
+@end
+
+/** Moves a CCNode object simulating a parabolic jump movement by modifying it's position attribute.
+*/
+ @interface CCJumpBy : CCActionInterval <NSCopying>
+{
+ CGPoint startPosition_;
+ CGPoint delta_;
+ ccTime height_;
+ NSUInteger jumps_;
+}
+/** creates the action */
++(id) actionWithDuration: (ccTime)duration position:(CGPoint)position height:(ccTime)height jumps:(NSUInteger)jumps;
+/** initializes the action */
+-(id) initWithDuration: (ccTime)duration position:(CGPoint)position height:(ccTime)height jumps:(NSUInteger)jumps;
+@end
+
+/** Moves a CCNode object to a parabolic position simulating a jump movement by modifying it's position attribute.
+*/
+ @interface CCJumpTo : CCJumpBy <NSCopying>
+{
+}
+@end
+
+/** bezier configuration structure
+ */
+typedef struct _ccBezierConfig {
+ //! end position of the bezier
+ CGPoint endPosition;
+ //! Bezier control point 1
+ CGPoint controlPoint_1;
+ //! Bezier control point 2
+ CGPoint controlPoint_2;
+} ccBezierConfig;
+
+/** An action that moves the target with a cubic Bezier curve by a certain distance.
+ */
+@interface CCBezierBy : CCActionInterval <NSCopying>
+{
+ ccBezierConfig config_;
+ CGPoint startPosition_;
+}
+
+/** creates the action with a duration and a bezier configuration */
++(id) actionWithDuration: (ccTime) t bezier:(ccBezierConfig) c;
+
+/** initializes the action with a duration and a bezier configuration */
+-(id) initWithDuration: (ccTime) t bezier:(ccBezierConfig) c;
+@end
+
+/** An action that moves the target with a cubic Bezier curve to a destination point.
+ @since v0.8.2
+ */
+@interface CCBezierTo : CCBezierBy
+{
+}
+@end
+
+/** Scales a CCNode object to a zoom factor by modifying it's scale attribute.
+ @warning This action doesn't support "reverse"
+ */
+@interface CCScaleTo : CCActionInterval <NSCopying>
+{
+ float scaleX_;
+ float scaleY_;
+ float startScaleX_;
+ float startScaleY_;
+ float endScaleX_;
+ float endScaleY_;
+ float deltaX_;
+ float deltaY_;
+}
+/** creates the action with the same scale factor for X and Y */
++(id) actionWithDuration: (ccTime)duration scale:(float) s;
+/** initializes the action with the same scale factor for X and Y */
+-(id) initWithDuration: (ccTime)duration scale:(float) s;
+/** creates the action with and X factor and a Y factor */
++(id) actionWithDuration: (ccTime)duration scaleX:(float) sx scaleY:(float)sy;
+/** initializes the action with and X factor and a Y factor */
+-(id) initWithDuration: (ccTime)duration scaleX:(float) sx scaleY:(float)sy;
+@end
+
+/** Scales a CCNode object a zoom factor by modifying it's scale attribute.
+*/
+@interface CCScaleBy : CCScaleTo <NSCopying>
+{
+}
+@end
+
+/** Blinks a CCNode object by modifying it's visible attribute
+*/
+@interface CCBlink : CCActionInterval <NSCopying>
+{
+ NSUInteger times_;
+}
+/** creates the action */
++(id) actionWithDuration: (ccTime)duration blinks:(NSUInteger)blinks;
+/** initilizes the action */
+-(id) initWithDuration: (ccTime)duration blinks:(NSUInteger)blinks;
+@end
+
+/** Fades In an object that implements the CCRGBAProtocol protocol. It modifies the opacity from 0 to 255.
+ The "reverse" of this action is FadeOut
+ */
+@interface CCFadeIn : CCActionInterval <NSCopying>
+{
+}
+@end
+
+/** Fades Out an object that implements the CCRGBAProtocol protocol. It modifies the opacity from 255 to 0.
+ The "reverse" of this action is FadeIn
+*/
+@interface CCFadeOut : CCActionInterval <NSCopying>
+{
+}
+@end
+
+/** Fades an object that implements the CCRGBAProtocol protocol. It modifies the opacity from the current value to a custom one.
+ @warning This action doesn't support "reverse"
+ */
+@interface CCFadeTo : CCActionInterval <NSCopying>
+{
+ GLubyte toOpacity_;
+ GLubyte fromOpacity_;
+}
+/** creates an action with duration and opactiy */
++(id) actionWithDuration:(ccTime)duration opacity:(GLubyte)opactiy;
+/** initializes the action with duration and opacity */
+-(id) initWithDuration:(ccTime)duration opacity:(GLubyte)opacity;
+@end
+
+/** Tints a CCNode that implements the CCNodeRGB protocol from current tint to a custom one.
+ @warning This action doesn't support "reverse"
+ @since v0.7.2
+*/
+@interface CCTintTo : CCActionInterval <NSCopying>
+{
+ ccColor3B to_;
+ ccColor3B from_;
+}
+/** creates an action with duration and color */
++(id) actionWithDuration:(ccTime)duration red:(GLubyte)red green:(GLubyte)green blue:(GLubyte)blue;
+/** initializes the action with duration and color */
+-(id) initWithDuration:(ccTime)duration red:(GLubyte)red green:(GLubyte)green blue:(GLubyte)blue;
+@end
+
+/** Tints a CCNode that implements the CCNodeRGB protocol from current tint to a custom one.
+ @since v0.7.2
+ */
+@interface CCTintBy : CCActionInterval <NSCopying>
+{
+ GLshort deltaR_, deltaG_, deltaB_;
+ GLshort fromR_, fromG_, fromB_;
+}
+/** creates an action with duration and color */
++(id) actionWithDuration:(ccTime)duration red:(GLshort)deltaRed green:(GLshort)deltaGreen blue:(GLshort)deltaBlue;
+/** initializes the action with duration and color */
+-(id) initWithDuration:(ccTime)duration red:(GLshort)deltaRed green:(GLshort)deltaGreen blue:(GLshort)deltaBlue;
+@end
+
+/** Delays the action a certain amount of seconds
+*/
+@interface CCDelayTime : CCActionInterval <NSCopying>
+{
+}
+@end
+
+/** Executes an action in reverse order, from time=duration to time=0
+
+ @warning Use this action carefully. This action is not
+ sequenceable. Use it as the default "reversed" method
+ of your own actions, but using it outside the "reversed"
+ scope is not recommended.
+*/
+@interface CCReverseTime : CCActionInterval <NSCopying>
+{
+ CCFiniteTimeAction * other_;
+}
+/** creates the action */
++(id) actionWithAction: (CCFiniteTimeAction*) action;
+/** initializes the action */
+-(id) initWithAction: (CCFiniteTimeAction*) action;
+@end
+
+
+@class CCAnimation;
+@class CCTexture2D;
+/** Animates a sprite given the name of an Animation */
+@interface CCAnimate : CCActionInterval <NSCopying>
+{
+ CCAnimation *animation_;
+ id origFrame_;
+ BOOL restoreOriginalFrame_;
+}
+/** animation used for the animage */
+@property (readwrite,nonatomic,retain) CCAnimation * animation;
+
+/** creates the action with an Animation and will restore the original frame when the animation is over */
++(id) actionWithAnimation:(CCAnimation*) a;
+/** initializes the action with an Animation and will restore the original frame when the animtion is over */
+-(id) initWithAnimation:(CCAnimation*) a;
+/** creates the action with an Animation */
++(id) actionWithAnimation:(CCAnimation*) a restoreOriginalFrame:(BOOL)b;
+/** initializes the action with an Animation */
+-(id) initWithAnimation:(CCAnimation*) a restoreOriginalFrame:(BOOL)b;
+/** creates an action with a duration, animation and depending of the restoreOriginalFrame, it will restore the original frame or not.
+ The 'delay' parameter of the animation will be overrided by the duration parameter.
+ @since v0.99.0
+ */
++(id) actionWithDuration:(ccTime)duration animation:(CCAnimation*)animation restoreOriginalFrame:(BOOL)b;
+/** initializes an action with a duration, animation and depending of the restoreOriginalFrame, it will restore the original frame or not.
+ The 'delay' parameter of the animation will be overrided by the duration parameter.
+ @since v0.99.0
+ */
+-(id) initWithDuration:(ccTime)duration animation:(CCAnimation*)animation restoreOriginalFrame:(BOOL)b;
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2011 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+
+#import "CCActionInterval.h"
+#import "CCSprite.h"
+#import "CCSpriteFrame.h"
+#import "CCAnimation.h"
+#import "CCNode.h"
+#import "Support/CGPointExtension.h"
+
+//
+// IntervalAction
+//
+#pragma mark -
+#pragma mark IntervalAction
+@implementation CCActionInterval
+
+@synthesize elapsed = elapsed_;
+
+-(id) init
+{
+ NSAssert(NO, @"IntervalActionInit: Init not supported. Use InitWithDuration");
+ [self release];
+ return nil;
+}
+
++(id) actionWithDuration: (ccTime) d
+{
+ return [[[self alloc] initWithDuration:d ] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) d
+{
+ if( (self=[super init]) ) {
+ duration_ = d;
+
+ // prevent division by 0
+ // This comparison could be in step:, but it might decrease the performance
+ // by 3% in heavy based action games.
+ if( duration_ == 0 )
+ duration_ = FLT_EPSILON;
+ elapsed_ = 0;
+ firstTick_ = YES;
+ }
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] ];
+ return copy;
+}
+
+- (BOOL) isDone
+{
+ return (elapsed_ >= duration_);
+}
+
+-(void) step: (ccTime) dt
+{
+ if( firstTick_ ) {
+ firstTick_ = NO;
+ elapsed_ = 0;
+ } else
+ elapsed_ += dt;
+
+ [self update: MIN(1, elapsed_/duration_)];
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ elapsed_ = 0.0f;
+ firstTick_ = YES;
+}
+
+- (CCActionInterval*) reverse
+{
+ NSAssert(NO, @"CCIntervalAction: reverse not implemented.");
+ return nil;
+}
+@end
+
+//
+// Sequence
+//
+#pragma mark -
+#pragma mark Sequence
+@implementation CCSequence
++(id) actions: (CCFiniteTimeAction*) action1, ...
+{
+ va_list params;
+ va_start(params,action1);
+
+ CCFiniteTimeAction *now;
+ CCFiniteTimeAction *prev = action1;
+
+ while( action1 ) {
+ now = va_arg(params,CCFiniteTimeAction*);
+ if ( now )
+ prev = [self actionOne: prev two: now];
+ else
+ break;
+ }
+ va_end(params);
+ return prev;
+}
+
++(id) actionsWithArray: (NSArray*) actions
+{
+ CCFiniteTimeAction *prev = [actions objectAtIndex:0];
+
+ for (int i = 1; i < [actions count]; i++)
+ prev = [self actionOne:prev two:[actions objectAtIndex:i]];
+
+ return prev;
+}
+
++(id) actionOne: (CCFiniteTimeAction*) one two: (CCFiniteTimeAction*) two
+{
+ return [[[self alloc] initOne:one two:two ] autorelease];
+}
+
+-(id) initOne: (CCFiniteTimeAction*) one two: (CCFiniteTimeAction*) two
+{
+ NSAssert( one!=nil && two!=nil, @"Sequence: arguments must be non-nil");
+ NSAssert( one!=actions_[0] && one!=actions_[1], @"Sequence: re-init using the same parameters is not supported");
+ NSAssert( two!=actions_[1] && two!=actions_[0], @"Sequence: re-init using the same parameters is not supported");
+
+ ccTime d = [one duration] + [two duration];
+
+ if( (self=[super initWithDuration: d]) ) {
+
+ // XXX: Supports re-init without leaking. Fails if one==one_ || two==two_
+ [actions_[0] release];
+ [actions_[1] release];
+
+ actions_[0] = [one retain];
+ actions_[1] = [two retain];
+ }
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCAction *copy = [[[self class] allocWithZone:zone] initOne:[[actions_[0] copy] autorelease] two:[[actions_[1] copy] autorelease] ];
+ return copy;
+}
+
+-(void) dealloc
+{
+ [actions_[0] release];
+ [actions_[1] release];
+ [super dealloc];
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ split_ = [actions_[0] duration] / duration_;
+ last_ = -1;
+}
+
+-(void) stop
+{
+ [actions_[0] stop];
+ [actions_[1] stop];
+ [super stop];
+}
+
+-(void) update: (ccTime) t
+{
+ int found = 0;
+ ccTime new_t = 0.0f;
+
+ if( t >= split_ ) {
+ found = 1;
+ if ( split_ == 1 )
+ new_t = 1;
+ else
+ new_t = (t-split_) / (1 - split_ );
+ } else {
+ found = 0;
+ if( split_ != 0 )
+ new_t = t / split_;
+ else
+ new_t = 1;
+ }
+
+ if (last_ == -1 && found==1) {
+ [actions_[0] startWithTarget:target_];
+ [actions_[0] update:1.0f];
+ [actions_[0] stop];
+ }
+
+ if (last_ != found ) {
+ if( last_ != -1 ) {
+ [actions_[last_] update: 1.0f];
+ [actions_[last_] stop];
+ }
+ [actions_[found] startWithTarget:target_];
+ }
+ [actions_[found] update: new_t];
+ last_ = found;
+}
+
+- (CCActionInterval *) reverse
+{
+ return [[self class] actionOne: [actions_[1] reverse] two: [actions_[0] reverse ] ];
+}
+@end
+
+//
+// Repeat
+//
+#pragma mark -
+#pragma mark CCRepeat
+@implementation CCRepeat
+@synthesize innerAction=innerAction_;
+
++(id) actionWithAction:(CCFiniteTimeAction*)action times:(NSUInteger)times
+{
+ return [[[self alloc] initWithAction:action times:times] autorelease];
+}
+
+-(id) initWithAction:(CCFiniteTimeAction*)action times:(NSUInteger)times
+{
+ ccTime d = [action duration] * times;
+
+ if( (self=[super initWithDuration: d ]) ) {
+ times_ = times;
+ self.innerAction = action;
+
+ total_ = 0;
+ }
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCAction *copy = [[[self class] allocWithZone:zone] initWithAction:[[innerAction_ copy] autorelease] times:times_];
+ return copy;
+}
+
+-(void) dealloc
+{
+ [innerAction_ release];
+ [super dealloc];
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+ total_ = 0;
+ [super startWithTarget:aTarget];
+ [innerAction_ startWithTarget:aTarget];
+}
+
+-(void) stop
+{
+ [innerAction_ stop];
+ [super stop];
+}
+
+
+// issue #80. Instead of hooking step:, hook update: since it can be called by any
+// container action like Repeat, Sequence, AccelDeccel, etc..
+-(void) update:(ccTime) dt
+{
+ ccTime t = dt * times_;
+ if( t > total_+1 ) {
+ [innerAction_ update:1.0f];
+ total_++;
+ [innerAction_ stop];
+ [innerAction_ startWithTarget:target_];
+
+ // repeat is over ?
+ if( total_== times_ )
+ // so, set it in the original position
+ [innerAction_ update:0];
+ else {
+ // no ? start next repeat with the right update
+ // to prevent jerk (issue #390)
+ [innerAction_ update: t-total_];
+ }
+
+ } else {
+
+ float r = fmodf(t, 1.0f);
+
+ // fix last repeat position
+ // else it could be 0.
+ if( dt== 1.0f) {
+ r = 1.0f;
+ total_++; // this is the added line
+ }
+ [innerAction_ update: MIN(r,1)];
+ }
+}
+
+-(BOOL) isDone
+{
+ return ( total_ == times_ );
+}
+
+- (CCActionInterval *) reverse
+{
+ return [[self class] actionWithAction:[innerAction_ reverse] times:times_];
+}
+@end
+
+//
+// Spawn
+//
+#pragma mark -
+#pragma mark Spawn
+
+@implementation CCSpawn
++(id) actions: (CCFiniteTimeAction*) action1, ...
+{
+ va_list params;
+ va_start(params,action1);
+
+ CCFiniteTimeAction *now;
+ CCFiniteTimeAction *prev = action1;
+
+ while( action1 ) {
+ now = va_arg(params,CCFiniteTimeAction*);
+ if ( now )
+ prev = [self actionOne: prev two: now];
+ else
+ break;
+ }
+ va_end(params);
+ return prev;
+}
+
++(id) actionsWithArray: (NSArray*) actions
+{
+ CCFiniteTimeAction *prev = [actions objectAtIndex:0];
+
+ for (int i = 1; i < [actions count]; i++)
+ prev = [self actionOne:prev two:[actions objectAtIndex:i]];
+
+ return prev;
+}
+
++(id) actionOne: (CCFiniteTimeAction*) one two: (CCFiniteTimeAction*) two
+{
+ return [[[self alloc] initOne:one two:two ] autorelease];
+}
+
+-(id) initOne: (CCFiniteTimeAction*) one two: (CCFiniteTimeAction*) two
+{
+ NSAssert( one!=nil && two!=nil, @"Spawn: arguments must be non-nil");
+ NSAssert( one!=one_ && one!=two_, @"Spawn: reinit using same parameters is not supported");
+ NSAssert( two!=two_ && two!=one_, @"Spawn: reinit using same parameters is not supported");
+
+ ccTime d1 = [one duration];
+ ccTime d2 = [two duration];
+
+ [super initWithDuration: MAX(d1,d2)];
+
+ // XXX: Supports re-init without leaking. Fails if one==one_ || two==two_
+ [one_ release];
+ [two_ release];
+
+ one_ = one;
+ two_ = two;
+
+ if( d1 > d2 )
+ two_ = [CCSequence actionOne:two two:[CCDelayTime actionWithDuration: (d1-d2)] ];
+ else if( d1 < d2)
+ one_ = [CCSequence actionOne:one two: [CCDelayTime actionWithDuration: (d2-d1)] ];
+
+ [one_ retain];
+ [two_ retain];
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCAction *copy = [[[self class] allocWithZone: zone] initOne: [[one_ copy] autorelease] two: [[two_ copy] autorelease] ];
+ return copy;
+}
+
+-(void) dealloc
+{
+ [one_ release];
+ [two_ release];
+ [super dealloc];
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ [one_ startWithTarget:target_];
+ [two_ startWithTarget:target_];
+}
+
+-(void) stop
+{
+ [one_ stop];
+ [two_ stop];
+ [super stop];
+}
+
+-(void) update: (ccTime) t
+{
+ [one_ update:t];
+ [two_ update:t];
+}
+
+- (CCActionInterval *) reverse
+{
+ return [[self class] actionOne: [one_ reverse] two: [two_ reverse ] ];
+}
+@end
+
+//
+// RotateTo
+//
+#pragma mark -
+#pragma mark RotateTo
+
+@implementation CCRotateTo
++(id) actionWithDuration: (ccTime) t angle:(float) a
+{
+ return [[[self alloc] initWithDuration:t angle:a ] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) t angle:(float) a
+{
+ if( (self=[super initWithDuration: t]) )
+ dstAngle_ = a;
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration:[self duration] angle:dstAngle_];
+ return copy;
+}
+
+-(void) startWithTarget:(CCNode *)aTarget
+{
+ [super startWithTarget:aTarget];
+
+ startAngle_ = [target_ rotation];
+ if (startAngle_ > 0)
+ startAngle_ = fmodf(startAngle_, 360.0f);
+ else
+ startAngle_ = fmodf(startAngle_, -360.0f);
+
+ diffAngle_ =dstAngle_ - startAngle_;
+ if (diffAngle_ > 180)
+ diffAngle_ -= 360;
+ if (diffAngle_ < -180)
+ diffAngle_ += 360;
+}
+-(void) update: (ccTime) t
+{
+ [target_ setRotation: startAngle_ + diffAngle_ * t];
+}
+@end
+
+
+//
+// RotateBy
+//
+#pragma mark -
+#pragma mark RotateBy
+
+@implementation CCRotateBy
++(id) actionWithDuration: (ccTime) t angle:(float) a
+{
+ return [[[self alloc] initWithDuration:t angle:a ] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) t angle:(float) a
+{
+ if( (self=[super initWithDuration: t]) )
+ angle_ = a;
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] angle: angle_];
+ return copy;
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ startAngle_ = [target_ rotation];
+}
+
+-(void) update: (ccTime) t
+{
+ // XXX: shall I add % 360
+ [target_ setRotation: (startAngle_ +angle_ * t )];
+}
+
+-(CCActionInterval*) reverse
+{
+ return [[self class] actionWithDuration:duration_ angle:-angle_];
+}
+
+@end
+
+//
+// MoveTo
+//
+#pragma mark -
+#pragma mark MoveTo
+
+@implementation CCMoveTo
++(id) actionWithDuration: (ccTime) t position: (CGPoint) p
+{
+ return [[[self alloc] initWithDuration:t position:p ] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) t position: (CGPoint) p
+{
+ if( (self=[super initWithDuration: t]) )
+ endPosition_ = p;
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] position: endPosition_];
+ return copy;
+}
+
+-(void) startWithTarget:(CCNode *)aTarget
+{
+ [super startWithTarget:aTarget];
+ startPosition_ = [(CCNode*)target_ position];
+ delta_ = ccpSub( endPosition_, startPosition_ );
+}
+
+-(void) update: (ccTime) t
+{
+ [target_ setPosition: ccp( (startPosition_.x + delta_.x * t ), (startPosition_.y + delta_.y * t ) )];
+}
+@end
+
+//
+// MoveBy
+//
+#pragma mark -
+#pragma mark MoveBy
+
+@implementation CCMoveBy
++(id) actionWithDuration: (ccTime) t position: (CGPoint) p
+{
+ return [[[self alloc] initWithDuration:t position:p ] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) t position: (CGPoint) p
+{
+ if( (self=[super initWithDuration: t]) )
+ delta_ = p;
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] position: delta_];
+ return copy;
+}
+
+-(void) startWithTarget:(CCNode *)aTarget
+{
+ CGPoint dTmp = delta_;
+ [super startWithTarget:aTarget];
+ delta_ = dTmp;
+}
+
+-(CCActionInterval*) reverse
+{
+ return [[self class] actionWithDuration:duration_ position:ccp( -delta_.x, -delta_.y)];
+}
+@end
+
+
+//
+// SkewTo
+//
+#pragma mark -
+#pragma mark SkewTo
+
+@implementation CCSkewTo
++(id) actionWithDuration:(ccTime)t skewX:(float)sx skewY:(float)sy
+{
+ return [[[self alloc] initWithDuration: t skewX:sx skewY:sy] autorelease];
+}
+
+-(id) initWithDuration:(ccTime)t skewX:(float)sx skewY:(float)sy
+{
+ if( (self=[super initWithDuration:t]) ) {
+ endSkewX_ = sx;
+ endSkewY_ = sy;
+ }
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration:[self duration] skewX:endSkewX_ skewY:endSkewY_];
+ return copy;
+}
+
+-(void) startWithTarget:(CCNode *)aTarget
+{
+ [super startWithTarget:aTarget];
+
+ startSkewX_ = [target_ skewX];
+
+ if (startSkewX_ > 0)
+ startSkewX_ = fmodf(startSkewX_, 180.0f);
+ else
+ startSkewX_ = fmodf(startSkewX_, -180.0f);
+
+ deltaX_ = endSkewX_ - startSkewX_;
+
+ if ( deltaX_ > 180 ) {
+ deltaX_ -= 360;
+ }
+ if ( deltaX_ < -180 ) {
+ deltaX_ += 360;
+ }
+
+ startSkewY_ = [target_ skewY];
+
+ if (startSkewY_ > 0)
+ startSkewY_ = fmodf(startSkewY_, 360.0f);
+ else
+ startSkewY_ = fmodf(startSkewY_, -360.0f);
+
+ deltaY_ = endSkewY_ - startSkewY_;
+
+ if ( deltaY_ > 180 ) {
+ deltaY_ -= 360;
+ }
+ if ( deltaY_ < -180 ) {
+ deltaY_ += 360;
+ }
+}
+
+-(void) update: (ccTime) t
+{
+ [target_ setSkewX: (startSkewX_ + deltaX_ * t ) ];
+ [target_ setSkewY: (startSkewY_ + deltaY_ * t ) ];
+}
+
+@end
+
+//
+// CCSkewBy
+//
+@implementation CCSkewBy
+
+-(id) initWithDuration:(ccTime)t skewX:(float)deltaSkewX skewY:(float)deltaSkewY
+{
+ if( (self=[super initWithDuration:t skewX:deltaSkewX skewY:deltaSkewY]) ) {
+ skewX_ = deltaSkewX;
+ skewY_ = deltaSkewY;
+ }
+ return self;
+}
+
+-(void) startWithTarget:(CCNode *)aTarget
+{
+ [super startWithTarget:aTarget];
+ deltaX_ = skewX_;
+ deltaY_ = skewY_;
+ endSkewX_ = startSkewX_ + deltaX_;
+ endSkewY_ = startSkewY_ + deltaY_;
+}
+
+-(CCActionInterval*) reverse
+{
+ return [[self class] actionWithDuration:duration_ skewX:-skewX_ skewY:-skewY_];
+}
+@end
+
+
+//
+// JumpBy
+//
+#pragma mark -
+#pragma mark JumpBy
+
+@implementation CCJumpBy
++(id) actionWithDuration: (ccTime) t position: (CGPoint) pos height: (ccTime) h jumps:(NSUInteger)j
+{
+ return [[[self alloc] initWithDuration: t position: pos height: h jumps:j] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) t position: (CGPoint) pos height: (ccTime) h jumps:(NSUInteger)j
+{
+ if( (self=[super initWithDuration:t]) ) {
+ delta_ = pos;
+ height_ = h;
+ jumps_ = j;
+ }
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration:[self duration] position:delta_ height:height_ jumps:jumps_];
+ return copy;
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ startPosition_ = [(CCNode*)target_ position];
+}
+
+-(void) update: (ccTime) t
+{
+ // Sin jump. Less realistic
+// ccTime y = height * fabsf( sinf(t * (CGFloat)M_PI * jumps ) );
+// y += delta.y * t;
+// ccTime x = delta.x * t;
+// [target setPosition: ccp( startPosition.x + x, startPosition.y + y )];
+
+ // parabolic jump (since v0.8.2)
+ ccTime frac = fmodf( t * jumps_, 1.0f );
+ ccTime y = height_ * 4 * frac * (1 - frac);
+ y += delta_.y * t;
+ ccTime x = delta_.x * t;
+ [target_ setPosition: ccp( startPosition_.x + x, startPosition_.y + y )];
+
+}
+
+-(CCActionInterval*) reverse
+{
+ return [[self class] actionWithDuration:duration_ position: ccp(-delta_.x,-delta_.y) height:height_ jumps:jumps_];
+}
+@end
+
+//
+// JumpTo
+//
+#pragma mark -
+#pragma mark JumpTo
+
+@implementation CCJumpTo
+-(void) startWithTarget:(CCNode *)aTarget
+{
+ [super startWithTarget:aTarget];
+ delta_ = ccp( delta_.x - startPosition_.x, delta_.y - startPosition_.y );
+}
+@end
+
+
+#pragma mark -
+#pragma mark BezierBy
+
+// Bezier cubic formula:
+// ((1 - t) + t)3 = 1
+// Expands to…
+// (1 - t)3 + 3t(1-t)2 + 3t2(1 - t) + t3 = 1
+static inline float bezierat( float a, float b, float c, float d, ccTime t )
+{
+ return (powf(1-t,3) * a +
+ 3*t*(powf(1-t,2))*b +
+ 3*powf(t,2)*(1-t)*c +
+ powf(t,3)*d );
+}
+
+//
+// BezierBy
+//
+@implementation CCBezierBy
++(id) actionWithDuration: (ccTime) t bezier:(ccBezierConfig) c
+{
+ return [[[self alloc] initWithDuration:t bezier:c ] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) t bezier:(ccBezierConfig) c
+{
+ if( (self=[super initWithDuration: t]) ) {
+ config_ = c;
+ }
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration:[self duration] bezier:config_];
+ return copy;
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ startPosition_ = [(CCNode*)target_ position];
+}
+
+-(void) update: (ccTime) t
+{
+ float xa = 0;
+ float xb = config_.controlPoint_1.x;
+ float xc = config_.controlPoint_2.x;
+ float xd = config_.endPosition.x;
+
+ float ya = 0;
+ float yb = config_.controlPoint_1.y;
+ float yc = config_.controlPoint_2.y;
+ float yd = config_.endPosition.y;
+
+ float x = bezierat(xa, xb, xc, xd, t);
+ float y = bezierat(ya, yb, yc, yd, t);
+ [target_ setPosition: ccpAdd( startPosition_, ccp(x,y))];
+}
+
+- (CCActionInterval*) reverse
+{
+ ccBezierConfig r;
+
+ r.endPosition = ccpNeg(config_.endPosition);
+ r.controlPoint_1 = ccpAdd(config_.controlPoint_2, ccpNeg(config_.endPosition));
+ r.controlPoint_2 = ccpAdd(config_.controlPoint_1, ccpNeg(config_.endPosition));
+
+ CCBezierBy *action = [[self class] actionWithDuration:[self duration] bezier:r];
+ return action;
+}
+@end
+
+//
+// BezierTo
+//
+#pragma mark -
+#pragma mark BezierTo
+@implementation CCBezierTo
+-(void) startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ config_.controlPoint_1 = ccpSub(config_.controlPoint_1, startPosition_);
+ config_.controlPoint_2 = ccpSub(config_.controlPoint_2, startPosition_);
+ config_.endPosition = ccpSub(config_.endPosition, startPosition_);
+}
+@end
+
+
+//
+// ScaleTo
+//
+#pragma mark -
+#pragma mark ScaleTo
+@implementation CCScaleTo
++(id) actionWithDuration: (ccTime) t scale:(float) s
+{
+ return [[[self alloc] initWithDuration: t scale:s] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) t scale:(float) s
+{
+ if( (self=[super initWithDuration: t]) ) {
+ endScaleX_ = s;
+ endScaleY_ = s;
+ }
+ return self;
+}
+
++(id) actionWithDuration: (ccTime) t scaleX:(float)sx scaleY:(float)sy
+{
+ return [[[self alloc] initWithDuration: t scaleX:sx scaleY:sy] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) t scaleX:(float)sx scaleY:(float)sy
+{
+ if( (self=[super initWithDuration: t]) ) {
+ endScaleX_ = sx;
+ endScaleY_ = sy;
+ }
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration:[self duration] scaleX:endScaleX_ scaleY:endScaleY_];
+ return copy;
+}
+
+-(void) startWithTarget:(CCNode *)aTarget
+{
+ [super startWithTarget:aTarget];
+ startScaleX_ = [target_ scaleX];
+ startScaleY_ = [target_ scaleY];
+ deltaX_ = endScaleX_ - startScaleX_;
+ deltaY_ = endScaleY_ - startScaleY_;
+}
+
+-(void) update: (ccTime) t
+{
+ [target_ setScaleX: (startScaleX_ + deltaX_ * t ) ];
+ [target_ setScaleY: (startScaleY_ + deltaY_ * t ) ];
+}
+@end
+
+//
+// ScaleBy
+//
+#pragma mark -
+#pragma mark ScaleBy
+@implementation CCScaleBy
+-(void) startWithTarget:(CCNode *)aTarget
+{
+ [super startWithTarget:aTarget];
+ deltaX_ = startScaleX_ * endScaleX_ - startScaleX_;
+ deltaY_ = startScaleY_ * endScaleY_ - startScaleY_;
+}
+
+-(CCActionInterval*) reverse
+{
+ return [[self class] actionWithDuration:duration_ scaleX:1/endScaleX_ scaleY:1/endScaleY_];
+}
+@end
+
+//
+// Blink
+//
+#pragma mark -
+#pragma mark Blink
+@implementation CCBlink
++(id) actionWithDuration: (ccTime) t blinks: (NSUInteger) b
+{
+ return [[[ self alloc] initWithDuration: t blinks: b] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) t blinks: (NSUInteger) b
+{
+ if( (self=[super initWithDuration: t] ) )
+ times_ = b;
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration: [self duration] blinks: times_];
+ return copy;
+}
+
+-(void) update: (ccTime) t
+{
+ if( ! [self isDone] ) {
+ ccTime slice = 1.0f / times_;
+ ccTime m = fmodf(t, slice);
+ [target_ setVisible: (m > slice/2) ? YES : NO];
+ }
+}
+
+-(CCActionInterval*) reverse
+{
+ // return 'self'
+ return [[self class] actionWithDuration:duration_ blinks: times_];
+}
+@end
+
+//
+// FadeIn
+//
+#pragma mark -
+#pragma mark FadeIn
+@implementation CCFadeIn
+-(void) update: (ccTime) t
+{
+ [(id<CCRGBAProtocol>) target_ setOpacity: 255 *t];
+}
+
+-(CCActionInterval*) reverse
+{
+ return [CCFadeOut actionWithDuration:duration_];
+}
+@end
+
+//
+// FadeOut
+//
+#pragma mark -
+#pragma mark FadeOut
+@implementation CCFadeOut
+-(void) update: (ccTime) t
+{
+ [(id<CCRGBAProtocol>) target_ setOpacity: 255 *(1-t)];
+}
+
+-(CCActionInterval*) reverse
+{
+ return [CCFadeIn actionWithDuration:duration_];
+}
+@end
+
+//
+// FadeTo
+//
+#pragma mark -
+#pragma mark FadeTo
+@implementation CCFadeTo
++(id) actionWithDuration: (ccTime) t opacity: (GLubyte) o
+{
+ return [[[ self alloc] initWithDuration: t opacity: o] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) t opacity: (GLubyte) o
+{
+ if( (self=[super initWithDuration: t] ) )
+ toOpacity_ = o;
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration:[self duration] opacity:toOpacity_];
+ return copy;
+}
+
+-(void) startWithTarget:(CCNode *)aTarget
+{
+ [super startWithTarget:aTarget];
+ fromOpacity_ = [(id<CCRGBAProtocol>)target_ opacity];
+}
+
+-(void) update: (ccTime) t
+{
+ [(id<CCRGBAProtocol>)target_ setOpacity:fromOpacity_ + ( toOpacity_ - fromOpacity_ ) * t];
+}
+@end
+
+//
+// TintTo
+//
+#pragma mark -
+#pragma mark TintTo
+@implementation CCTintTo
++(id) actionWithDuration:(ccTime)t red:(GLubyte)r green:(GLubyte)g blue:(GLubyte)b
+{
+ return [[(CCTintTo*)[ self alloc] initWithDuration:t red:r green:g blue:b] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) t red:(GLubyte)r green:(GLubyte)g blue:(GLubyte)b
+{
+ if( (self=[super initWithDuration:t] ) )
+ to_ = ccc3(r,g,b);
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCAction *copy = [(CCTintTo*)[[self class] allocWithZone: zone] initWithDuration:[self duration] red:to_.r green:to_.g blue:to_.b];
+ return copy;
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+
+ id<CCRGBAProtocol> tn = (id<CCRGBAProtocol>) target_;
+ from_ = [tn color];
+}
+
+-(void) update: (ccTime) t
+{
+ id<CCRGBAProtocol> tn = (id<CCRGBAProtocol>) target_;
+ [tn setColor:ccc3(from_.r + (to_.r - from_.r) * t, from_.g + (to_.g - from_.g) * t, from_.b + (to_.b - from_.b) * t)];
+}
+@end
+
+//
+// TintBy
+//
+#pragma mark -
+#pragma mark TintBy
+@implementation CCTintBy
++(id) actionWithDuration:(ccTime)t red:(GLshort)r green:(GLshort)g blue:(GLshort)b
+{
+ return [[(CCTintBy*)[ self alloc] initWithDuration:t red:r green:g blue:b] autorelease];
+}
+
+-(id) initWithDuration:(ccTime)t red:(GLshort)r green:(GLshort)g blue:(GLshort)b
+{
+ if( (self=[super initWithDuration: t] ) ) {
+ deltaR_ = r;
+ deltaG_ = g;
+ deltaB_ = b;
+ }
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ return[(CCTintBy*)[[self class] allocWithZone: zone] initWithDuration: [self duration] red:deltaR_ green:deltaG_ blue:deltaB_];
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+
+ id<CCRGBAProtocol> tn = (id<CCRGBAProtocol>) target_;
+ ccColor3B color = [tn color];
+ fromR_ = color.r;
+ fromG_ = color.g;
+ fromB_ = color.b;
+}
+
+-(void) update: (ccTime) t
+{
+ id<CCRGBAProtocol> tn = (id<CCRGBAProtocol>) target_;
+ [tn setColor:ccc3( fromR_ + deltaR_ * t, fromG_ + deltaG_ * t, fromB_ + deltaB_ * t)];
+}
+
+- (CCActionInterval*) reverse
+{
+ return [CCTintBy actionWithDuration:duration_ red:-deltaR_ green:-deltaG_ blue:-deltaB_];
+}
+@end
+
+//
+// DelayTime
+//
+#pragma mark -
+#pragma mark DelayTime
+@implementation CCDelayTime
+-(void) update: (ccTime) t
+{
+ return;
+}
+
+-(id)reverse
+{
+ return [[self class] actionWithDuration:duration_];
+}
+@end
+
+//
+// ReverseTime
+//
+#pragma mark -
+#pragma mark ReverseTime
+@implementation CCReverseTime
++(id) actionWithAction: (CCFiniteTimeAction*) action
+{
+ // casting to prevent warnings
+ CCReverseTime *a = [super alloc];
+ return [[a initWithAction:action] autorelease];
+}
+
+-(id) initWithAction: (CCFiniteTimeAction*) action
+{
+ NSAssert(action != nil, @"CCReverseTime: action should not be nil");
+ NSAssert(action != other_, @"CCReverseTime: re-init doesn't support using the same arguments");
+
+ if( (self=[super initWithDuration: [action duration]]) ) {
+ // Don't leak if action is reused
+ [other_ release];
+ other_ = [action retain];
+ }
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ return [[[self class] allocWithZone: zone] initWithAction:[[other_ copy] autorelease] ];
+}
+
+-(void) dealloc
+{
+ [other_ release];
+ [super dealloc];
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ [other_ startWithTarget:target_];
+}
+
+-(void) stop
+{
+ [other_ stop];
+ [super stop];
+}
+
+-(void) update:(ccTime)t
+{
+ [other_ update:1-t];
+}
+
+-(CCActionInterval*) reverse
+{
+ return [[other_ copy] autorelease];
+}
+@end
+
+//
+// Animate
+//
+
+#pragma mark -
+#pragma mark Animate
+@implementation CCAnimate
+
+@synthesize animation = animation_;
+
++(id) actionWithAnimation: (CCAnimation*)anim
+{
+ return [[[self alloc] initWithAnimation:anim restoreOriginalFrame:YES] autorelease];
+}
+
++(id) actionWithAnimation: (CCAnimation*)anim restoreOriginalFrame:(BOOL)b
+{
+ return [[[self alloc] initWithAnimation:anim restoreOriginalFrame:b] autorelease];
+}
+
++(id) actionWithDuration:(ccTime)duration animation: (CCAnimation*)anim restoreOriginalFrame:(BOOL)b
+{
+ return [[[self alloc] initWithDuration:duration animation:anim restoreOriginalFrame:b] autorelease];
+}
+
+-(id) initWithAnimation: (CCAnimation*)anim
+{
+ NSAssert( anim!=nil, @"Animate: argument Animation must be non-nil");
+ return [self initWithAnimation:anim restoreOriginalFrame:YES];
+}
+
+-(id) initWithAnimation: (CCAnimation*)anim restoreOriginalFrame:(BOOL) b
+{
+ NSAssert( anim!=nil, @"Animate: argument Animation must be non-nil");
+
+ if( (self=[super initWithDuration: [[anim frames] count] * [anim delay]]) ) {
+
+ restoreOriginalFrame_ = b;
+ self.animation = anim;
+ origFrame_ = nil;
+ }
+ return self;
+}
+
+-(id) initWithDuration:(ccTime)aDuration animation: (CCAnimation*)anim restoreOriginalFrame:(BOOL) b
+{
+ NSAssert( anim!=nil, @"Animate: argument Animation must be non-nil");
+
+ if( (self=[super initWithDuration:aDuration] ) ) {
+
+ restoreOriginalFrame_ = b;
+ self.animation = anim;
+ origFrame_ = nil;
+ }
+ return self;
+}
+
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ return [[[self class] allocWithZone: zone] initWithDuration:duration_ animation:animation_ restoreOriginalFrame:restoreOriginalFrame_];
+}
+
+-(void) dealloc
+{
+ [animation_ release];
+ [origFrame_ release];
+ [super dealloc];
+}
+
+-(void) startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ CCSprite *sprite = target_;
+
+ [origFrame_ release];
+
+ if( restoreOriginalFrame_ )
+ origFrame_ = [[sprite displayedFrame] retain];
+}
+
+-(void) stop
+{
+ if( restoreOriginalFrame_ ) {
+ CCSprite *sprite = target_;
+ [sprite setDisplayFrame:origFrame_];
+ }
+
+ [super stop];
+}
+
+-(void) update: (ccTime) t
+{
+ NSArray *frames = [animation_ frames];
+ NSUInteger numberOfFrames = [frames count];
+
+ NSUInteger idx = t * numberOfFrames;
+
+ if( idx >= numberOfFrames )
+ idx = numberOfFrames -1;
+
+ CCSprite *sprite = target_;
+ if (! [sprite isFrameDisplayed: [frames objectAtIndex: idx]] )
+ [sprite setDisplayFrame: [frames objectAtIndex:idx]];
+}
+
+- (CCActionInterval *) reverse
+{
+ NSArray *oldArray = [animation_ frames];
+ NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:[oldArray count]];
+ NSEnumerator *enumerator = [oldArray reverseObjectEnumerator];
+ for (id element in enumerator)
+ [newArray addObject:[[element copy] autorelease]];
+
+ CCAnimation *newAnim = [CCAnimation animationWithFrames:newArray delay:animation_.delay];
+ return [[self class] actionWithDuration:duration_ animation:newAnim restoreOriginalFrame:restoreOriginalFrame_];
+}
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Valentin Milea
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCAction.h"
+#import "Support/ccCArray.h"
+#import "Support/uthash.h"
+
+typedef struct _hashElement
+{
+ struct ccArray *actions;
+ id target;
+ NSUInteger actionIndex;
+ CCAction *currentAction;
+ BOOL currentActionSalvaged;
+ BOOL paused;
+ UT_hash_handle hh;
+} tHashElement;
+
+
+/** CCActionManager is a singleton that manages all the actions.
+ Normally you won't need to use this singleton directly. 99% of the cases you will use the CCNode interface,
+ which uses this singleton.
+ But there are some cases where you might need to use this singleton.
+ Examples:
+ - When you want to run an action where the target is different from a CCNode.
+ - When you want to pause / resume the actions
+
+ @since v0.8
+ */
+@interface CCActionManager : NSObject
+{
+ tHashElement *targets;
+ tHashElement *currentTarget;
+ BOOL currentTargetSalvaged;
+}
+
+/** returns a shared instance of the CCActionManager */
++ (CCActionManager *)sharedManager;
+
+/** purges the shared action manager. It releases the retained instance.
+ @since v0.99.0
+ */
++(void)purgeSharedManager;
+
+// actions
+
+/** Adds an action with a target.
+ If the target is already present, then the action will be added to the existing target.
+ If the target is not present, a new instance of this target will be created either paused or paused, and the action will be added to the newly created target.
+ When the target is paused, the queued actions won't be 'ticked'.
+ */
+-(void) addAction: (CCAction*) action target:(id)target paused:(BOOL)paused;
+/** Removes all actions from all the targers.
+ */
+-(void) removeAllActions;
+
+/** Removes all actions from a certain target.
+ All the actions that belongs to the target will be removed.
+ */
+-(void) removeAllActionsFromTarget:(id)target;
+/** Removes an action given an action reference.
+ */
+-(void) removeAction: (CCAction*) action;
+/** Removes an action given its tag and the target */
+-(void) removeActionByTag:(NSInteger)tag target:(id)target;
+/** Gets an action given its tag an a target
+ @return the Action the with the given tag
+ */
+-(CCAction*) getActionByTag:(NSInteger) tag target:(id)target;
+/** Returns the numbers of actions that are running in a certain target
+ * Composable actions are counted as 1 action. Example:
+ * If you are running 1 Sequence of 7 actions, it will return 1.
+ * If you are running 7 Sequences of 2 actions, it will return 7.
+ */
+-(NSUInteger) numberOfRunningActionsInTarget:(id)target;
+
+/** Pauses the target: all running actions and newly added actions will be paused.
+ */
+-(void) pauseTarget:(id)target;
+/** Resumes the target. All queued actions will be resumed.
+ */
+-(void) resumeTarget:(id)target;
+
+@end
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Valentin Milea
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCActionManager.h"
+#import "CCScheduler.h"
+#import "ccMacros.h"
+
+
+//
+// singleton stuff
+//
+static CCActionManager *sharedManager_ = nil;
+
+@interface CCActionManager (Private)
+-(void) removeActionAtIndex:(NSUInteger)index hashElement:(tHashElement*)element;
+-(void) deleteHashElement:(tHashElement*)element;
+-(void) actionAllocWithHashElement:(tHashElement*)element;
+@end
+
+
+@implementation CCActionManager
+
+#pragma mark ActionManager - init
++ (CCActionManager *)sharedManager
+{
+ if (!sharedManager_)
+ sharedManager_ = [[self alloc] init];
+
+ return sharedManager_;
+}
+
++(id)alloc
+{
+ NSAssert(sharedManager_ == nil, @"Attempted to allocate a second instance of a singleton.");
+ return [super alloc];
+}
+
++(void)purgeSharedManager
+{
+ [[CCScheduler sharedScheduler] unscheduleUpdateForTarget:self];
+ [sharedManager_ release];
+ sharedManager_ = nil;
+}
+
+-(id) init
+{
+ if ((self=[super init]) ) {
+ [[CCScheduler sharedScheduler] scheduleUpdateForTarget:self priority:0 paused:NO];
+ targets = NULL;
+ }
+
+ return self;
+}
+
+- (void) dealloc
+{
+ CCLOGINFO( @"cocos2d: deallocing %@", self);
+
+ [self removeAllActions];
+
+ sharedManager_ = nil;
+
+ [super dealloc];
+}
+
+#pragma mark ActionManager - Private
+
+-(void) deleteHashElement:(tHashElement*)element
+{
+ ccArrayFree(element->actions);
+ HASH_DEL(targets, element);
+// CCLOG(@"cocos2d: ---- buckets: %d/%d - %@", targets->entries, targets->size, element->target);
+ [element->target release];
+ free(element);
+}
+
+-(void) actionAllocWithHashElement:(tHashElement*)element
+{
+ // 4 actions per Node by default
+ if( element->actions == nil )
+ element->actions = ccArrayNew(4);
+ else if( element->actions->num == element->actions->max )
+ ccArrayDoubleCapacity(element->actions);
+}
+
+-(void) removeActionAtIndex:(NSUInteger)index hashElement:(tHashElement*)element
+{
+ id action = element->actions->arr[index];
+
+ if( action == element->currentAction && !element->currentActionSalvaged ) {
+ [element->currentAction retain];
+ element->currentActionSalvaged = YES;
+ }
+
+ ccArrayRemoveObjectAtIndex(element->actions, index);
+
+ // update actionIndex in case we are in tick:, looping over the actions
+ if( element->actionIndex >= index )
+ element->actionIndex--;
+
+ if( element->actions->num == 0 ) {
+ if( currentTarget == element )
+ currentTargetSalvaged = YES;
+ else
+ [self deleteHashElement: element];
+ }
+}
+
+#pragma mark ActionManager - Pause / Resume
+
+-(void) pauseTarget:(id)target
+{
+ tHashElement *element = NULL;
+ HASH_FIND_INT(targets, &target, element);
+ if( element )
+ element->paused = YES;
+// else
+// CCLOG(@"cocos2d: pauseAllActions: Target not found");
+}
+
+-(void) resumeTarget:(id)target
+{
+ tHashElement *element = NULL;
+ HASH_FIND_INT(targets, &target, element);
+ if( element )
+ element->paused = NO;
+// else
+// CCLOG(@"cocos2d: resumeAllActions: Target not found");
+}
+
+#pragma mark ActionManager - run
+
+-(void) addAction:(CCAction*)action target:(id)target paused:(BOOL)paused
+{
+ NSAssert( action != nil, @"Argument action must be non-nil");
+ NSAssert( target != nil, @"Argument target must be non-nil");
+
+ tHashElement *element = NULL;
+ HASH_FIND_INT(targets, &target, element);
+ if( ! element ) {
+ element = calloc( sizeof( *element ), 1 );
+ element->paused = paused;
+ element->target = [target retain];
+ HASH_ADD_INT(targets, target, element);
+// CCLOG(@"cocos2d: ---- buckets: %d/%d - %@", targets->entries, targets->size, element->target);
+
+ }
+
+ [self actionAllocWithHashElement:element];
+
+ NSAssert( !ccArrayContainsObject(element->actions, action), @"runAction: Action already running");
+ ccArrayAppendObject(element->actions, action);
+
+ [action startWithTarget:target];
+}
+
+#pragma mark ActionManager - remove
+
+-(void) removeAllActions
+{
+ for(tHashElement *element=targets; element != NULL; ) {
+ id target = element->target;
+ element = element->hh.next;
+ [self removeAllActionsFromTarget:target];
+ }
+}
+-(void) removeAllActionsFromTarget:(id)target
+{
+ // explicit nil handling
+ if( target == nil )
+ return;
+
+ tHashElement *element = NULL;
+ HASH_FIND_INT(targets, &target, element);
+ if( element ) {
+ if( ccArrayContainsObject(element->actions, element->currentAction) && !element->currentActionSalvaged ) {
+ [element->currentAction retain];
+ element->currentActionSalvaged = YES;
+ }
+ ccArrayRemoveAllObjects(element->actions);
+ if( currentTarget == element )
+ currentTargetSalvaged = YES;
+ else
+ [self deleteHashElement:element];
+ }
+// else {
+// CCLOG(@"cocos2d: removeAllActionsFromTarget: Target not found");
+// }
+}
+
+-(void) removeAction: (CCAction*) action
+{
+ // explicit nil handling
+ if (action == nil)
+ return;
+
+ tHashElement *element = NULL;
+ id target = [action originalTarget];
+ HASH_FIND_INT(targets, &target, element );
+ if( element ) {
+ NSUInteger i = ccArrayGetIndexOfObject(element->actions, action);
+ if( i != NSNotFound )
+ [self removeActionAtIndex:i hashElement:element];
+ }
+// else {
+// CCLOG(@"cocos2d: removeAction: Target not found");
+// }
+}
+
+-(void) removeActionByTag:(NSInteger)aTag target:(id)target
+{
+ NSAssert( aTag != kCCActionTagInvalid, @"Invalid tag");
+ NSAssert( target != nil, @"Target should be ! nil");
+
+ tHashElement *element = NULL;
+ HASH_FIND_INT(targets, &target, element);
+
+ if( element ) {
+ NSUInteger limit = element->actions->num;
+ for( NSUInteger i = 0; i < limit; i++) {
+ CCAction *a = element->actions->arr[i];
+
+ if( a.tag == aTag && [a originalTarget]==target)
+ return [self removeActionAtIndex:i hashElement:element];
+ }
+// CCLOG(@"cocos2d: removeActionByTag: Action not found!");
+ }
+// else {
+// CCLOG(@"cocos2d: removeActionByTag: Target not found!");
+// }
+}
+
+#pragma mark ActionManager - get
+
+-(CCAction*) getActionByTag:(NSInteger)aTag target:(id)target
+{
+ NSAssert( aTag != kCCActionTagInvalid, @"Invalid tag");
+
+ tHashElement *element = NULL;
+ HASH_FIND_INT(targets, &target, element);
+
+ if( element ) {
+ if( element->actions != nil ) {
+ NSUInteger limit = element->actions->num;
+ for( NSUInteger i = 0; i < limit; i++) {
+ CCAction *a = element->actions->arr[i];
+
+ if( a.tag == aTag )
+ return a;
+ }
+ }
+// CCLOG(@"cocos2d: getActionByTag: Action not found");
+ }
+// else {
+// CCLOG(@"cocos2d: getActionByTag: Target not found");
+// }
+ return nil;
+}
+
+-(NSUInteger) numberOfRunningActionsInTarget:(id) target
+{
+ tHashElement *element = NULL;
+ HASH_FIND_INT(targets, &target, element);
+ if( element )
+ return element->actions ? element->actions->num : 0;
+
+// CCLOG(@"cocos2d: numberOfRunningActionsInTarget: Target not found");
+ return 0;
+}
+
+#pragma mark ActionManager - main loop
+
+-(void) update: (ccTime) dt
+{
+ for(tHashElement *elt = targets; elt != NULL; ) {
+
+ currentTarget = elt;
+ currentTargetSalvaged = NO;
+
+ if( ! currentTarget->paused ) {
+
+ // The 'actions' ccArray may change while inside this loop.
+ for( currentTarget->actionIndex = 0; currentTarget->actionIndex < currentTarget->actions->num; currentTarget->actionIndex++) {
+ currentTarget->currentAction = currentTarget->actions->arr[currentTarget->actionIndex];
+ currentTarget->currentActionSalvaged = NO;
+
+ [currentTarget->currentAction step: dt];
+
+ if( currentTarget->currentActionSalvaged ) {
+ // The currentAction told the node to remove it. To prevent the action from
+ // accidentally deallocating itself before finishing its step, we retained
+ // it. Now that step is done, it's safe to release it.
+ [currentTarget->currentAction release];
+
+ } else if( [currentTarget->currentAction isDone] ) {
+ [currentTarget->currentAction stop];
+
+ CCAction *a = currentTarget->currentAction;
+ // Make currentAction nil to prevent removeAction from salvaging it.
+ currentTarget->currentAction = nil;
+ [self removeAction:a];
+ }
+
+ currentTarget->currentAction = nil;
+ }
+ }
+
+ // elt, at this moment, is still valid
+ // so it is safe to ask this here (issue #490)
+ elt = elt->hh.next;
+
+ // only delete currentTarget if no actions were scheduled during the cycle (issue #481)
+ if( currentTargetSalvaged && currentTarget->actions->num == 0 )
+ [self deleteHashElement:currentTarget];
+ }
+
+ // issue #635
+ currentTarget = nil;
+}
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Sindesso Pty Ltd http://www.sindesso.com/
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCActionGrid3D.h"
+
+/**
+ * This action simulates a page turn from the bottom right hand corner of the screen
+ * It's not much use by itself but is used by the PageTurnTransition.
+ *
+ * Based on an original paper by L Hong et al.
+ * http://www.parc.com/publication/1638/turning-pages-of-3d-electronic-books.html
+ *
+ * @since v0.8.2
+ */
+@interface CCPageTurn3D : CCGrid3DAction
+{
+}
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Sindesso Pty Ltd http://www.sindesso.com/
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "CCActionPageTurn3D.h"
+
+@implementation CCPageTurn3D
+
+/*
+ * Update each tick
+ * Time is the percentage of the way through the duration
+ */
+-(void)update:(ccTime)time
+{
+ float tt = MAX( 0, time - 0.25f );
+ float deltaAy = ( tt * tt * 500);
+ float ay = -100 - deltaAy;
+
+ float deltaTheta = - (float) M_PI_2 * sqrtf( time) ;
+ float theta = /*0.01f*/ + (float) M_PI_2 +deltaTheta;
+
+ float sinTheta = sinf(theta);
+ float cosTheta = cosf(theta);
+
+ for( int i = 0; i <=gridSize_.x; i++ )
+ {
+ for( int j = 0; j <= gridSize_.y; j++ )
+ {
+ // Get original vertex
+ ccVertex3F p = [self originalVertex:ccg(i,j)];
+
+ float R = sqrtf(p.x*p.x + (p.y - ay) * (p.y - ay));
+ float r = R * sinTheta;
+ float alpha = asinf( p.x / R );
+ float beta = alpha / sinTheta;
+ float cosBeta = cosf( beta );
+
+ // If beta > PI then we've wrapped around the cone
+ // Reduce the radius to stop these points interfering with others
+ if( beta <= M_PI)
+ p.x = ( r * sinf(beta));
+ else
+ {
+ // Force X = 0 to stop wrapped
+ // points
+ p.x = 0;
+ }
+
+ p.y = ( R + ay - ( r*(1 - cosBeta)*sinTheta));
+
+ // We scale z here to avoid the animation being
+ // too much bigger than the screen due to perspectve transform
+ p.z = (r * ( 1 - cosBeta ) * cosTheta) / 7; // "100" didn't work for
+
+ // Stop z coord from dropping beneath underlying page in a transition
+ // issue #751
+ if( p.z<0.5f )
+ p.z = 0.5f;
+
+ // Set new coords
+ [self setVertex:ccg(i,j) vertex:p];
+ }
+ }
+}
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (C) 2010 Lam Pham
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import <Foundation/Foundation.h>
+#import "CCProgressTimer.h"
+#import "CCActionInterval.h"
+
+/**
+ Progress to percentage
+@since v0.99.1
+*/
+@interface CCProgressTo : CCActionInterval <NSCopying>
+{
+ float to_;
+ float from_;
+}
+/** Creates and initializes with a duration and a percent */
++(id) actionWithDuration:(ccTime)duration percent:(float)percent;
+/** Initializes with a duration and a percent */
+-(id) initWithDuration:(ccTime)duration percent:(float)percent;
+@end
+
+/**
+ Progress from a percentage to another percentage
+ @since v0.99.1
+ */
+@interface CCProgressFromTo : CCActionInterval <NSCopying>
+{
+ float to_;
+ float from_;
+}
+/** Creates and initializes the action with a duration, a "from" percentage and a "to" percentage */
++(id) actionWithDuration:(ccTime)duration from:(float)fromPercentage to:(float) toPercentage;
+/** Initializes the action with a duration, a "from" percentage and a "to" percentage */
+-(id) initWithDuration:(ccTime)duration from:(float)fromPercentage to:(float) toPercentage;
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (C) 2010 Lam Pham
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCActionProgressTimer.h"
+
+#define kProgressTimerCast CCProgressTimer*
+
+@implementation CCProgressTo
++(id) actionWithDuration: (ccTime) t percent: (float) v
+{
+ return [[[ self alloc] initWithDuration: t percent: v] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) t percent: (float) v
+{
+ if( (self=[super initWithDuration: t] ) )
+ to_ = v;
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration:duration_ percent:to_];
+ return copy;
+}
+
+-(void) startWithTarget:(id) aTarget;
+{
+ [super startWithTarget:aTarget];
+ from_ = [(kProgressTimerCast)target_ percentage];
+
+ // XXX: Is this correct ?
+ // Adding it to support CCRepeat
+ if( from_ == 100)
+ from_ = 0;
+}
+
+-(void) update: (ccTime) t
+{
+ [(kProgressTimerCast)target_ setPercentage: from_ + ( to_ - from_ ) * t];
+}
+@end
+
+@implementation CCProgressFromTo
++(id) actionWithDuration: (ccTime) t from:(float)fromPercentage to:(float) toPercentage
+{
+ return [[[self alloc] initWithDuration: t from: fromPercentage to: toPercentage] autorelease];
+}
+
+-(id) initWithDuration: (ccTime) t from:(float)fromPercentage to:(float) toPercentage
+{
+ if( (self=[super initWithDuration: t] ) ){
+ to_ = toPercentage;
+ from_ = fromPercentage;
+ }
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration:duration_ from:from_ to:to_];
+ return copy;
+}
+
+- (CCActionInterval *) reverse
+{
+ return [[self class] actionWithDuration:duration_ from:to_ to:from_];
+}
+
+-(void) startWithTarget:(id) aTarget;
+{
+ [super startWithTarget:aTarget];
+}
+
+-(void) update: (ccTime) t
+{
+ [(kProgressTimerCast)target_ setPercentage: from_ + ( to_ - from_ ) * t];
+}
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 On-Core
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "CCActionGrid.h"
+
+/** CCShakyTiles3D action */
+@interface CCShakyTiles3D : CCTiledGrid3DAction
+{
+ int randrange;
+ BOOL shakeZ;
+}
+
+/** creates the action with a range, whether or not to shake Z vertices, a grid size, and duration */
++(id)actionWithRange:(int)range shakeZ:(BOOL)shakeZ grid:(ccGridSize)gridSize duration:(ccTime)d;
+/** initializes the action with a range, whether or not to shake Z vertices, a grid size, and duration */
+-(id)initWithRange:(int)range shakeZ:(BOOL)shakeZ grid:(ccGridSize)gridSize duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCShatteredTiles3D action */
+@interface CCShatteredTiles3D : CCTiledGrid3DAction
+{
+ int randrange;
+ BOOL once;
+ BOOL shatterZ;
+}
+
+/** creates the action with a range, whether of not to shatter Z vertices, a grid size and duration */
++(id)actionWithRange:(int)range shatterZ:(BOOL)shatterZ grid:(ccGridSize)gridSize duration:(ccTime)d;
+/** initializes the action with a range, whether or not to shatter Z vertices, a grid size and duration */
+-(id)initWithRange:(int)range shatterZ:(BOOL)shatterZ grid:(ccGridSize)gridSize duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCShuffleTiles action
+ Shuffle the tiles in random order
+ */
+@interface CCShuffleTiles : CCTiledGrid3DAction
+{
+ int seed;
+ NSUInteger tilesCount;
+ int *tilesOrder;
+ void *tiles;
+}
+
+/** creates the action with a random seed, the grid size and the duration */
++(id)actionWithSeed:(int)s grid:(ccGridSize)gridSize duration:(ccTime)d;
+/** initializes the action with a random seed, the grid size and the duration */
+-(id)initWithSeed:(int)s grid:(ccGridSize)gridSize duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCFadeOutTRTiles action
+ Fades out the tiles in a Top-Right direction
+ */
+@interface CCFadeOutTRTiles : CCTiledGrid3DAction
+{
+}
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCFadeOutBLTiles action.
+ Fades out the tiles in a Bottom-Left direction
+ */
+@interface CCFadeOutBLTiles : CCFadeOutTRTiles
+{
+}
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCFadeOutUpTiles action.
+ Fades out the tiles in upwards direction
+ */
+@interface CCFadeOutUpTiles : CCFadeOutTRTiles
+{
+}
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCFadeOutDownTiles action.
+ Fades out the tiles in downwards direction
+ */
+@interface CCFadeOutDownTiles : CCFadeOutUpTiles
+{
+}
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCTurnOffTiles action.
+ Turn off the files in random order
+ */
+@interface CCTurnOffTiles : CCTiledGrid3DAction
+{
+ int seed;
+ NSUInteger tilesCount;
+ int *tilesOrder;
+}
+
+/** creates the action with a random seed, the grid size and the duration */
++(id)actionWithSeed:(int)s grid:(ccGridSize)gridSize duration:(ccTime)d;
+/** initializes the action with a random seed, the grid size and the duration */
+-(id)initWithSeed:(int)s grid:(ccGridSize)gridSize duration:(ccTime)d;
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCWavesTiles3D action. */
+@interface CCWavesTiles3D : CCTiledGrid3DAction
+{
+ int waves;
+ float amplitude;
+ float amplitudeRate;
+}
+
+/** waves amplitude */
+@property (nonatomic,readwrite) float amplitude;
+/** waves amplitude rate */
+@property (nonatomic,readwrite) float amplitudeRate;
+
+/** creates the action with a number of waves, the waves amplitude, the grid size and the duration */
++(id)actionWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d;
+/** initializes the action with a number of waves, the waves amplitude, the grid size and the duration */
+-(id)initWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCJumpTiles3D action.
+ A sin function is executed to move the tiles across the Z axis
+ */
+@interface CCJumpTiles3D : CCTiledGrid3DAction
+{
+ int jumps;
+ float amplitude;
+ float amplitudeRate;
+}
+
+/** amplitude of the sin*/
+@property (nonatomic,readwrite) float amplitude;
+/** amplitude rate */
+@property (nonatomic,readwrite) float amplitudeRate;
+
+/** creates the action with the number of jumps, the sin amplitude, the grid size and the duration */
++(id)actionWithJumps:(int)j amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d;
+/** initializes the action with the number of jumps, the sin amplitude, the grid size and the duration */
+-(id)initWithJumps:(int)j amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCSplitRows action */
+@interface CCSplitRows : CCTiledGrid3DAction
+{
+ int rows;
+ CGSize winSize;
+}
+/** creates the action with the number of rows to split and the duration */
++(id)actionWithRows:(int)rows duration:(ccTime)duration;
+/** initializes the action with the number of rows to split and the duration */
+-(id)initWithRows:(int)rows duration:(ccTime)duration;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/** CCSplitCols action */
+@interface CCSplitCols : CCTiledGrid3DAction
+{
+ int cols;
+ CGSize winSize;
+}
+/** creates the action with the number of columns to split and the duration */
++(id)actionWithCols:(int)cols duration:(ccTime)duration;
+/** initializes the action with the number of columns to split and the duration */
+-(id)initWithCols:(int)cols duration:(ccTime)duration;
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 On-Core
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCActionTiledGrid.h"
+#import "CCDirector.h"
+#import "ccMacros.h"
+#import "Support/CGPointExtension.h"
+
+typedef struct
+{
+ CGPoint position;
+ CGPoint startPosition;
+ ccGridSize delta;
+} Tile;
+
+#pragma mark -
+#pragma mark ShakyTiles3D
+
+@implementation CCShakyTiles3D
+
++(id)actionWithRange:(int)range shakeZ:(BOOL)shakeZ grid:(ccGridSize)gridSize duration:(ccTime)d
+{
+ return [[[self alloc] initWithRange:range shakeZ:shakeZ grid:gridSize duration:d] autorelease];
+}
+
+-(id)initWithRange:(int)range shakeZ:(BOOL)sz grid:(ccGridSize)gSize duration:(ccTime)d
+{
+ if ( (self = [super initWithSize:gSize duration:d]) )
+ {
+ randrange = range;
+ shakeZ = sz;
+ }
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCGridAction *copy = [[[self class] allocWithZone:zone] initWithRange:randrange shakeZ:shakeZ grid:gridSize_ duration:duration_];
+ return copy;
+}
+
+
+-(void)update:(ccTime)time
+{
+ int i, j;
+
+ for( i = 0; i < gridSize_.x; i++ )
+ {
+ for( j = 0; j < gridSize_.y; j++ )
+ {
+ ccQuad3 coords = [self originalTile:ccg(i,j)];
+
+ // X
+ coords.bl.x += ( rand() % (randrange*2) ) - randrange;
+ coords.br.x += ( rand() % (randrange*2) ) - randrange;
+ coords.tl.x += ( rand() % (randrange*2) ) - randrange;
+ coords.tr.x += ( rand() % (randrange*2) ) - randrange;
+
+ // Y
+ coords.bl.y += ( rand() % (randrange*2) ) - randrange;
+ coords.br.y += ( rand() % (randrange*2) ) - randrange;
+ coords.tl.y += ( rand() % (randrange*2) ) - randrange;
+ coords.tr.y += ( rand() % (randrange*2) ) - randrange;
+
+ if( shakeZ ) {
+ coords.bl.z += ( rand() % (randrange*2) ) - randrange;
+ coords.br.z += ( rand() % (randrange*2) ) - randrange;
+ coords.tl.z += ( rand() % (randrange*2) ) - randrange;
+ coords.tr.z += ( rand() % (randrange*2) ) - randrange;
+ }
+
+ [self setTile:ccg(i,j) coords:coords];
+ }
+ }
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark CCShatteredTiles3D
+
+@implementation CCShatteredTiles3D
+
++(id)actionWithRange:(int)range shatterZ:(BOOL)sz grid:(ccGridSize)gridSize duration:(ccTime)d
+{
+ return [[[self alloc] initWithRange:range shatterZ:sz grid:gridSize duration:d] autorelease];
+}
+
+-(id)initWithRange:(int)range shatterZ:(BOOL)sz grid:(ccGridSize)gSize duration:(ccTime)d
+{
+ if ( (self = [super initWithSize:gSize duration:d]) )
+ {
+ once = NO;
+ randrange = range;
+ shatterZ = sz;
+ }
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCGridAction *copy = [[[self class] allocWithZone:zone] initWithRange:randrange shatterZ:shatterZ grid:gridSize_ duration:duration_];
+ return copy;
+}
+
+
+-(void)update:(ccTime)time
+{
+ int i, j;
+
+ if ( once == NO )
+ {
+ for( i = 0; i < gridSize_.x; i++ )
+ {
+ for( j = 0; j < gridSize_.y; j++ )
+ {
+ ccQuad3 coords = [self originalTile:ccg(i,j)];
+
+ // X
+ coords.bl.x += ( rand() % (randrange*2) ) - randrange;
+ coords.br.x += ( rand() % (randrange*2) ) - randrange;
+ coords.tl.x += ( rand() % (randrange*2) ) - randrange;
+ coords.tr.x += ( rand() % (randrange*2) ) - randrange;
+
+ // Y
+ coords.bl.y += ( rand() % (randrange*2) ) - randrange;
+ coords.br.y += ( rand() % (randrange*2) ) - randrange;
+ coords.tl.y += ( rand() % (randrange*2) ) - randrange;
+ coords.tr.y += ( rand() % (randrange*2) ) - randrange;
+
+ if( shatterZ ) {
+ coords.bl.z += ( rand() % (randrange*2) ) - randrange;
+ coords.br.z += ( rand() % (randrange*2) ) - randrange;
+ coords.tl.z += ( rand() % (randrange*2) ) - randrange;
+ coords.tr.z += ( rand() % (randrange*2) ) - randrange;
+ }
+
+ [self setTile:ccg(i,j) coords:coords];
+ }
+ }
+
+ once = YES;
+ }
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark CCShuffleTiles
+
+@implementation CCShuffleTiles
+
++(id)actionWithSeed:(int)s grid:(ccGridSize)gridSize duration:(ccTime)d
+{
+ return [[[self alloc] initWithSeed:s grid:gridSize duration:d] autorelease];
+}
+
+-(id)initWithSeed:(int)s grid:(ccGridSize)gSize duration:(ccTime)d
+{
+ if ( (self = [super initWithSize:gSize duration:d]) )
+ {
+ seed = s;
+ tilesOrder = nil;
+ tiles = nil;
+ }
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCGridAction *copy = [[[self class] allocWithZone:zone] initWithSeed:seed grid:gridSize_ duration:duration_];
+ return copy;
+}
+
+
+-(void)dealloc
+{
+ if ( tilesOrder ) free(tilesOrder);
+ if ( tiles ) free(tiles);
+ [super dealloc];
+}
+
+-(void)shuffle:(int*)array count:(NSUInteger)len
+{
+ NSInteger i;
+ for( i = len - 1; i >= 0; i-- )
+ {
+ NSInteger j = rand() % (i+1);
+ int v = array[i];
+ array[i] = array[j];
+ array[j] = v;
+ }
+}
+
+-(ccGridSize)getDelta:(ccGridSize)pos
+{
+ CGPoint pos2;
+
+ NSInteger idx = pos.x * gridSize_.y + pos.y;
+
+ pos2.x = tilesOrder[idx] / (int)gridSize_.y;
+ pos2.y = tilesOrder[idx] % (int)gridSize_.y;
+
+ return ccg(pos2.x - pos.x, pos2.y - pos.y);
+}
+
+-(void)placeTile:(ccGridSize)pos tile:(Tile)t
+{
+ ccQuad3 coords = [self originalTile:pos];
+
+ CGPoint step = [[target_ grid] step];
+ coords.bl.x += (int)(t.position.x * step.x);
+ coords.bl.y += (int)(t.position.y * step.y);
+
+ coords.br.x += (int)(t.position.x * step.x);
+ coords.br.y += (int)(t.position.y * step.y);
+
+ coords.tl.x += (int)(t.position.x * step.x);
+ coords.tl.y += (int)(t.position.y * step.y);
+
+ coords.tr.x += (int)(t.position.x * step.x);
+ coords.tr.y += (int)(t.position.y * step.y);
+
+ [self setTile:pos coords:coords];
+}
+
+-(void)startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+
+ if ( seed != -1 )
+ srand(seed);
+
+ tilesCount = gridSize_.x * gridSize_.y;
+ tilesOrder = (int*)malloc(tilesCount*sizeof(int));
+ int i, j;
+
+ for( i = 0; i < tilesCount; i++ )
+ tilesOrder[i] = i;
+
+ [self shuffle:tilesOrder count:tilesCount];
+
+ tiles = malloc(tilesCount*sizeof(Tile));
+ Tile *tileArray = (Tile*)tiles;
+
+ for( i = 0; i < gridSize_.x; i++ )
+ {
+ for( j = 0; j < gridSize_.y; j++ )
+ {
+ tileArray->position = ccp(i,j);
+ tileArray->startPosition = ccp(i,j);
+ tileArray->delta = [self getDelta:ccg(i,j)];
+ tileArray++;
+ }
+ }
+}
+
+-(void)update:(ccTime)time
+{
+ int i, j;
+
+ Tile *tileArray = (Tile*)tiles;
+
+ for( i = 0; i < gridSize_.x; i++ )
+ {
+ for( j = 0; j < gridSize_.y; j++ )
+ {
+ tileArray->position = ccpMult( ccp(tileArray->delta.x, tileArray->delta.y), time);
+ [self placeTile:ccg(i,j) tile:*tileArray];
+ tileArray++;
+ }
+ }
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark CCFadeOutTRTiles
+
+@implementation CCFadeOutTRTiles
+
+-(float)testFunc:(ccGridSize)pos time:(ccTime)time
+{
+ CGPoint n = ccpMult( ccp(gridSize_.x,gridSize_.y), time);
+ if ( (n.x+n.y) == 0.0f )
+ return 1.0f;
+
+ return powf( (pos.x+pos.y) / (n.x+n.y), 6 );
+}
+
+-(void)turnOnTile:(ccGridSize)pos
+{
+ [self setTile:pos coords:[self originalTile:pos]];
+}
+
+-(void)turnOffTile:(ccGridSize)pos
+{
+ ccQuad3 coords;
+ bzero(&coords, sizeof(ccQuad3));
+ [self setTile:pos coords:coords];
+}
+
+-(void)transformTile:(ccGridSize)pos distance:(float)distance
+{
+ ccQuad3 coords = [self originalTile:pos];
+ CGPoint step = [[target_ grid] step];
+
+ coords.bl.x += (step.x / 2) * (1.0f - distance);
+ coords.bl.y += (step.y / 2) * (1.0f - distance);
+
+ coords.br.x -= (step.x / 2) * (1.0f - distance);
+ coords.br.y += (step.y / 2) * (1.0f - distance);
+
+ coords.tl.x += (step.x / 2) * (1.0f - distance);
+ coords.tl.y -= (step.y / 2) * (1.0f - distance);
+
+ coords.tr.x -= (step.x / 2) * (1.0f - distance);
+ coords.tr.y -= (step.y / 2) * (1.0f - distance);
+
+ [self setTile:pos coords:coords];
+}
+
+-(void)update:(ccTime)time
+{
+ int i, j;
+
+ for( i = 0; i < gridSize_.x; i++ )
+ {
+ for( j = 0; j < gridSize_.y; j++ )
+ {
+ float distance = [self testFunc:ccg(i,j) time:time];
+ if ( distance == 0 )
+ [self turnOffTile:ccg(i,j)];
+ else if ( distance < 1 )
+ [self transformTile:ccg(i,j) distance:distance];
+ else
+ [self turnOnTile:ccg(i,j)];
+ }
+ }
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark CCFadeOutBLTiles
+
+@implementation CCFadeOutBLTiles
+
+-(float)testFunc:(ccGridSize)pos time:(ccTime)time
+{
+ CGPoint n = ccpMult(ccp(gridSize_.x, gridSize_.y), (1.0f-time));
+ if ( (pos.x+pos.y) == 0 )
+ return 1.0f;
+
+ return powf( (n.x+n.y) / (pos.x+pos.y), 6 );
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark CCFadeOutUpTiles
+
+@implementation CCFadeOutUpTiles
+
+-(float)testFunc:(ccGridSize)pos time:(ccTime)time
+{
+ CGPoint n = ccpMult(ccp(gridSize_.x, gridSize_.y), time);
+ if ( n.y == 0 )
+ return 1.0f;
+
+ return powf( pos.y / n.y, 6 );
+}
+
+-(void)transformTile:(ccGridSize)pos distance:(float)distance
+{
+ ccQuad3 coords = [self originalTile:pos];
+ CGPoint step = [[target_ grid] step];
+
+ coords.bl.y += (step.y / 2) * (1.0f - distance);
+ coords.br.y += (step.y / 2) * (1.0f - distance);
+ coords.tl.y -= (step.y / 2) * (1.0f - distance);
+ coords.tr.y -= (step.y / 2) * (1.0f - distance);
+
+ [self setTile:pos coords:coords];
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark CCFadeOutDownTiles
+
+@implementation CCFadeOutDownTiles
+
+-(float)testFunc:(ccGridSize)pos time:(ccTime)time
+{
+ CGPoint n = ccpMult(ccp(gridSize_.x,gridSize_.y), (1.0f - time));
+ if ( pos.y == 0 )
+ return 1.0f;
+
+ return powf( n.y / pos.y, 6 );
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark TurnOffTiles
+
+@implementation CCTurnOffTiles
+
++(id)actionWithSeed:(int)s grid:(ccGridSize)gridSize duration:(ccTime)d
+{
+ return [[[self alloc] initWithSeed:s grid:gridSize duration:d] autorelease];
+}
+
+-(id)initWithSeed:(int)s grid:(ccGridSize)gSize duration:(ccTime)d
+{
+ if ( (self = [super initWithSize:gSize duration:d]) )
+ {
+ seed = s;
+ tilesOrder = nil;
+ }
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCGridAction *copy = [[[self class] allocWithZone:zone] initWithSeed:seed grid:gridSize_ duration:duration_];
+ return copy;
+}
+
+-(void)dealloc
+{
+ if ( tilesOrder ) free(tilesOrder);
+ [super dealloc];
+}
+
+-(void)shuffle:(int*)array count:(NSUInteger)len
+{
+ NSInteger i;
+ for( i = len - 1; i >= 0; i-- )
+ {
+ NSInteger j = rand() % (i+1);
+ int v = array[i];
+ array[i] = array[j];
+ array[j] = v;
+ }
+}
+
+-(void)turnOnTile:(ccGridSize)pos
+{
+ [self setTile:pos coords:[self originalTile:pos]];
+}
+
+-(void)turnOffTile:(ccGridSize)pos
+{
+ ccQuad3 coords;
+
+ bzero(&coords, sizeof(ccQuad3));
+ [self setTile:pos coords:coords];
+}
+
+-(void)startWithTarget:(id)aTarget
+{
+ int i;
+
+ [super startWithTarget:aTarget];
+
+ if ( seed != -1 )
+ srand(seed);
+
+ tilesCount = gridSize_.x * gridSize_.y;
+ tilesOrder = (int*)malloc(tilesCount*sizeof(int));
+
+ for( i = 0; i < tilesCount; i++ )
+ tilesOrder[i] = i;
+
+ [self shuffle:tilesOrder count:tilesCount];
+}
+
+-(void)update:(ccTime)time
+{
+ int i, l, t;
+
+ l = (int)(time * (float)tilesCount);
+
+ for( i = 0; i < tilesCount; i++ )
+ {
+ t = tilesOrder[i];
+ ccGridSize tilePos = ccg( t / gridSize_.y, t % gridSize_.y );
+
+ if ( i < l )
+ [self turnOffTile:tilePos];
+ else
+ [self turnOnTile:tilePos];
+ }
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark CCWavesTiles3D
+
+@implementation CCWavesTiles3D
+
+@synthesize amplitude;
+@synthesize amplitudeRate;
+
++(id)actionWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d
+{
+ return [[[self alloc] initWithWaves:wav amplitude:amp grid:gridSize duration:d] autorelease];
+}
+
+-(id)initWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gSize duration:(ccTime)d
+{
+ if ( (self = [super initWithSize:gSize duration:d]) )
+ {
+ waves = wav;
+ amplitude = amp;
+ amplitudeRate = 1.0f;
+ }
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCGridAction *copy = [[[self class] allocWithZone:zone] initWithWaves:waves amplitude:amplitude grid:gridSize_ duration:duration_];
+ return copy;
+}
+
+
+-(void)update:(ccTime)time
+{
+ int i, j;
+
+ for( i = 0; i < gridSize_.x; i++ )
+ {
+ for( j = 0; j < gridSize_.y; j++ )
+ {
+ ccQuad3 coords = [self originalTile:ccg(i,j)];
+
+ coords.bl.z = (sinf(time*(CGFloat)M_PI*waves*2 + (coords.bl.y+coords.bl.x) * .01f) * amplitude * amplitudeRate );
+ coords.br.z = coords.bl.z;
+ coords.tl.z = coords.bl.z;
+ coords.tr.z = coords.bl.z;
+
+ [self setTile:ccg(i,j) coords:coords];
+ }
+ }
+}
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark CCJumpTiles3D
+
+@implementation CCJumpTiles3D
+
+@synthesize amplitude;
+@synthesize amplitudeRate;
+
++(id)actionWithJumps:(int)j amplitude:(float)amp grid:(ccGridSize)gridSize duration:(ccTime)d
+{
+ return [[[self alloc] initWithJumps:j amplitude:amp grid:gridSize duration:d] autorelease];
+}
+
+-(id)initWithJumps:(int)j amplitude:(float)amp grid:(ccGridSize)gSize duration:(ccTime)d
+{
+ if ( (self = [super initWithSize:gSize duration:d]) )
+ {
+ jumps = j;
+ amplitude = amp;
+ amplitudeRate = 1.0f;
+ }
+
+ return self;
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCGridAction *copy = [[[self class] allocWithZone:zone] initWithJumps:jumps amplitude:amplitude grid:gridSize_ duration:duration_];
+ return copy;
+}
+
+
+-(void)update:(ccTime)time
+{
+ int i, j;
+
+ float sinz = (sinf((CGFloat)M_PI*time*jumps*2) * amplitude * amplitudeRate );
+ float sinz2 = (sinf((CGFloat)M_PI*(time*jumps*2 + 1)) * amplitude * amplitudeRate );
+
+ for( i = 0; i < gridSize_.x; i++ )
+ {
+ for( j = 0; j < gridSize_.y; j++ )
+ {
+ ccQuad3 coords = [self originalTile:ccg(i,j)];
+
+ if ( ((i+j) % 2) == 0 )
+ {
+ coords.bl.z += sinz;
+ coords.br.z += sinz;
+ coords.tl.z += sinz;
+ coords.tr.z += sinz;
+ }
+ else
+ {
+ coords.bl.z += sinz2;
+ coords.br.z += sinz2;
+ coords.tl.z += sinz2;
+ coords.tr.z += sinz2;
+ }
+
+ [self setTile:ccg(i,j) coords:coords];
+ }
+ }
+}
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark SplitRows
+
+@implementation CCSplitRows
+
++(id)actionWithRows:(int)r duration:(ccTime)d
+{
+ return [[[self alloc] initWithRows:r duration:d] autorelease];
+}
+
+-(id)initWithRows:(int)r duration:(ccTime)d
+{
+ rows = r;
+ return [super initWithSize:ccg(1,r) duration:d];
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCGridAction *copy = [[[self class] allocWithZone:zone] initWithRows:rows duration:duration_];
+ return copy;
+}
+
+-(void)startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ winSize = [[CCDirector sharedDirector] winSizeInPixels];
+}
+
+-(void)update:(ccTime)time
+{
+ int j;
+
+ for( j = 0; j < gridSize_.y; j++ )
+ {
+ ccQuad3 coords = [self originalTile:ccg(0,j)];
+ float direction = 1;
+
+ if ( (j % 2 ) == 0 )
+ direction = -1;
+
+ coords.bl.x += direction * winSize.width * time;
+ coords.br.x += direction * winSize.width * time;
+ coords.tl.x += direction * winSize.width * time;
+ coords.tr.x += direction * winSize.width * time;
+
+ [self setTile:ccg(0,j) coords:coords];
+ }
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark CCSplitCols
+
+@implementation CCSplitCols
+
++(id)actionWithCols:(int)c duration:(ccTime)d
+{
+ return [[[self alloc] initWithCols:c duration:d] autorelease];
+}
+
+-(id)initWithCols:(int)c duration:(ccTime)d
+{
+ cols = c;
+ return [super initWithSize:ccg(c,1) duration:d];
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCGridAction *copy = [[[self class] allocWithZone:zone] initWithCols:cols duration:duration_];
+ return copy;
+}
+
+-(void)startWithTarget:(id)aTarget
+{
+ [super startWithTarget:aTarget];
+ winSize = [[CCDirector sharedDirector] winSizeInPixels];
+}
+
+-(void)update:(ccTime)time
+{
+ int i;
+
+ for( i = 0; i < gridSize_.x; i++ )
+ {
+ ccQuad3 coords = [self originalTile:ccg(i,0)];
+ float direction = 1;
+
+ if ( (i % 2 ) == 0 )
+ direction = -1;
+
+ coords.bl.y += direction * winSize.height * time;
+ coords.br.y += direction * winSize.height * time;
+ coords.tl.y += direction * winSize.height * time;
+ coords.tr.y += direction * winSize.height * time;
+
+ [self setTile:ccg(i,0) coords:coords];
+ }
+}
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright 2009 lhunath (Maarten Billemont)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import <Foundation/Foundation.h>
+#import "CCActionInterval.h"
+
+/** CCActionTween
+
+ CCActionTween is an action that lets you update any property of an object.
+ For example, if you want to modify the "width" property of a target from 200 to 300 in 2 senconds, then:
+
+ id modifyWidth = [CCActionTween actionWithDuration:2 key:@"width" from:200 to:300];
+ [target runAction:modifyWidth];
+
+
+ Another example: CCScaleTo action could be rewriten using CCPropertyAction:
+
+ // scaleA and scaleB are equivalents
+ id scaleA = [CCScaleTo actionWithDuration:2 scale:3];
+ id scaleB = [CCActionTween actionWithDuration:2 key:@"scale" from:1 to:3];
+
+
+ @since v0.99.2
+ */
+@interface CCActionTween : CCActionInterval
+{
+ NSString *key_;
+
+ float from_, to_;
+ float delta_;
+}
+
+/** creates an initializes the action with the property name (key), and the from and to parameters. */
++ (id)actionWithDuration:(ccTime)aDuration key:(NSString *)key from:(float)from to:(float)to;
+
+/** initializes the action with the property name (key), and the from and to parameters. */
+- (id)initWithDuration:(ccTime)aDuration key:(NSString *)key from:(float)from to:(float)to;
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright 2009 lhunath (Maarten Billemont)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "CCActionTween.h"
+
+
+@implementation CCActionTween
+
++ (id)actionWithDuration:(ccTime)aDuration key:(NSString *)aKey from:(float)aFrom to:(float)aTo {
+
+ return [[[[self class] alloc] initWithDuration:aDuration key:aKey from:aFrom to:aTo] autorelease];
+}
+
+- (id)initWithDuration:(ccTime)aDuration key:(NSString *)key from:(float)from to:(float)to {
+
+ if ((self = [super initWithDuration:aDuration])) {
+
+ key_ = [key copy];
+ to_ = to;
+ from_ = from;
+
+ }
+
+ return self;
+}
+
+- (void) dealloc
+{
+ [key_ release];
+ [super dealloc];
+}
+
+- (void)startWithTarget:aTarget
+{
+ [super startWithTarget:aTarget];
+ delta_ = to_ - from_;
+}
+
+- (void) update:(ccTime) dt
+{
+ [target_ setValue:[NSNumber numberWithFloat:to_ - delta_ * (1 - dt)] forKey:key_];
+}
+
+- (CCActionInterval *) reverse
+{
+ return [[self class] actionWithDuration:duration_ key:key_ from:to_ to:from_];
+}
+
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import <Foundation/Foundation.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import <CoreGraphics/CoreGraphics.h>
+#endif // IPHONE
+
+@class CCSpriteFrame;
+@class CCTexture2D;
+
+/** A CCAnimation object is used to perform animations on the CCSprite objects.
+
+ The CCAnimation object contains CCSpriteFrame objects, and a possible delay between the frames.
+ You can animate a CCAnimation object by using the CCAnimate action. Example:
+
+ [sprite runAction:[CCAnimate actionWithAnimation:animation]];
+
+ */
+@interface CCAnimation : NSObject
+{
+ NSString *name_;
+ float delay_;
+ NSMutableArray *frames_;
+}
+
+/** name of the animation */
+@property (nonatomic,readwrite,retain) NSString *name;
+/** delay between frames in seconds. */
+@property (nonatomic,readwrite,assign) float delay;
+/** array of frames */
+@property (nonatomic,readwrite,retain) NSMutableArray *frames;
+
+/** Creates an animation
+ @since v0.99.5
+ */
++(id) animation;
+
+/** Creates an animation with frames.
+ @since v0.99.5
+ */
++(id) animationWithFrames:(NSArray*)frames;
+
+/* Creates an animation with frames and a delay between frames.
+ @since v0.99.5
+ */
++(id) animationWithFrames:(NSArray*)frames delay:(float)delay;
+
+/** Creates a CCAnimation with a name
+ @since v0.99.3
+ @deprecated Will be removed in 1.0.1. Use "animation" instead.
+ */
++(id) animationWithName:(NSString*)name DEPRECATED_ATTRIBUTE;
+
+/** Creates a CCAnimation with a name and frames
+ @since v0.99.3
+ @deprecated Will be removed in 1.0.1. Use "animationWithFrames" instead.
+ */
++(id) animationWithName:(NSString*)name frames:(NSArray*)frames DEPRECATED_ATTRIBUTE;
+
+/** Creates a CCAnimation with a name and delay between frames. */
++(id) animationWithName:(NSString*)name delay:(float)delay DEPRECATED_ATTRIBUTE;
+
+/** Creates a CCAnimation with a name, delay and an array of CCSpriteFrames. */
++(id) animationWithName:(NSString*)name delay:(float)delay frames:(NSArray*)frames DEPRECATED_ATTRIBUTE;
+
+
+/** Initializes a CCAnimation with frames.
+ @since v0.99.5
+*/
+-(id) initWithFrames:(NSArray*)frames;
+
+/** Initializes a CCAnimation with frames and a delay between frames
+ @since v0.99.5
+ */
+-(id) initWithFrames:(NSArray *)frames delay:(float)delay;
+
+/** Initializes a CCAnimation with a name
+ @since v0.99.3
+ @deprecated Will be removed in 1.0.1. Use "init" instead.
+ */
+-(id) initWithName:(NSString*)name DEPRECATED_ATTRIBUTE;
+
+/** Initializes a CCAnimation with a name and frames
+ @since v0.99.3
+ @deprecated Will be removed in 1.0.1. Use "initWithFrames" instead.
+ */
+-(id) initWithName:(NSString*)name frames:(NSArray*)frames DEPRECATED_ATTRIBUTE;
+
+/** Initializes a CCAnimation with a name and delay between frames.
+ @deprecated Will be removed in 1.0.1. Use "initWithFrames:nil delay:delay" instead.
+*/
+-(id) initWithName:(NSString*)name delay:(float)delay DEPRECATED_ATTRIBUTE;
+
+/** Initializes a CCAnimation with a name, delay and an array of CCSpriteFrames.
+ @deprecated Will be removed in 1.0.1. Use "initWithFrames:frames delay:delay" instead.
+*/
+-(id) initWithName:(NSString*)name delay:(float)delay frames:(NSArray*)frames DEPRECATED_ATTRIBUTE;
+
+/** Adds a frame to a CCAnimation. */
+-(void) addFrame:(CCSpriteFrame*)frame;
+
+/** Adds a frame with an image filename. Internally it will create a CCSpriteFrame and it will add it.
+ Added to facilitate the migration from v0.8 to v0.9.
+ */
+-(void) addFrameWithFilename:(NSString*)filename;
+
+/** Adds a frame with a texture and a rect. Internally it will create a CCSpriteFrame and it will add it.
+ Added to facilitate the migration from v0.8 to v0.9.
+ */
+-(void) addFrameWithTexture:(CCTexture2D*)texture rect:(CGRect)rect;
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "ccMacros.h"
+#import "CCAnimation.h"
+#import "CCSpriteFrame.h"
+#import "CCTexture2D.h"
+#import "CCTextureCache.h"
+
+@implementation CCAnimation
+@synthesize name = name_, delay = delay_, frames = frames_;
+
++(id) animation
+{
+ return [[[self alloc] init] autorelease];
+}
+
++(id) animationWithFrames:(NSArray*)frames
+{
+ return [[[self alloc] initWithFrames:frames] autorelease];
+}
+
++(id) animationWithFrames:(NSArray*)frames delay:(float)delay
+{
+ return [[[self alloc] initWithFrames:frames delay:delay] autorelease];
+}
+
++(id) animationWithName:(NSString*)name
+{
+ return [[[self alloc] initWithName:name] autorelease];
+}
+
++(id) animationWithName:(NSString*)name frames:(NSArray*)frames
+{
+ return [[[self alloc] initWithName:name frames:frames] autorelease];
+}
+
++(id) animationWithName:(NSString*)aname delay:(float)d frames:(NSArray*)array
+{
+ return [[[self alloc] initWithName:aname delay:d frames:array] autorelease];
+}
+
++(id) animationWithName:(NSString*)aname delay:(float)d
+{
+ return [[[self alloc] initWithName:aname delay:d] autorelease];
+}
+
+-(id) init
+{
+ return [self initWithFrames:nil delay:0];
+}
+
+-(id) initWithFrames:(NSArray*)frames
+{
+ return [self initWithFrames:frames delay:0];
+}
+
+-(id) initWithFrames:(NSArray*)array delay:(float)delay
+{
+ if( (self=[super init]) ) {
+
+ delay_ = delay;
+ self.frames = [NSMutableArray arrayWithArray:array];
+ }
+ return self;
+}
+
+-(id) initWithName:(NSString*)name
+{
+ return [self initWithName:name delay:0 frames:nil];
+}
+
+-(id) initWithName:(NSString*)name frames:(NSArray*)frames
+{
+ return [self initWithName:name delay:0 frames:frames];
+}
+
+-(id) initWithName:(NSString*)t delay:(float)d
+{
+ return [self initWithName:t delay:d frames:nil];
+}
+
+-(id) initWithName:(NSString*)name delay:(float)delay frames:(NSArray*)array
+{
+ if( (self=[super init]) ) {
+
+ delay_ = delay;
+ self.name = name;
+ self.frames = [NSMutableArray arrayWithArray:array];
+ }
+ return self;
+}
+
+- (NSString*) description
+{
+ return [NSString stringWithFormat:@"<%@ = %08X | frames=%d, delay:%f>", [self class], self,
+ [frames_ count],
+ delay_
+ ];
+}
+
+-(void) dealloc
+{
+ CCLOGINFO( @"cocos2d: deallocing %@",self);
+ [name_ release];
+ [frames_ release];
+ [super dealloc];
+}
+
+-(void) addFrame:(CCSpriteFrame*)frame
+{
+ [frames_ addObject:frame];
+}
+
+-(void) addFrameWithFilename:(NSString*)filename
+{
+ CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage:filename];
+ CGRect rect = CGRectZero;
+ rect.size = texture.contentSize;
+ CCSpriteFrame *frame = [CCSpriteFrame frameWithTexture:texture rect:rect];
+ [frames_ addObject:frame];
+}
+
+-(void) addFrameWithTexture:(CCTexture2D*)texture rect:(CGRect)rect
+{
+ CCSpriteFrame *frame = [CCSpriteFrame frameWithTexture:texture rect:rect];
+ [frames_ addObject:frame];
+}
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import <Foundation/Foundation.h>
+
+@class CCAnimation;
+
+/** Singleton that manages the Animations.
+ It saves in a cache the animations. You should use this class if you want to save your animations in a cache.
+
+ Before v0.99.5, the recommend way was to save them on the CCSprite. Since v0.99.5, you should use this class instead.
+
+ @since v0.99.5
+ */
+@interface CCAnimationCache : NSObject
+{
+ NSMutableDictionary *animations_;
+}
+
+/** Retruns ths shared instance of the Animation cache */
++ (CCAnimationCache *) sharedAnimationCache;
+
+/** Purges the cache. It releases all the CCAnimation objects and the shared instance.
+ */
++(void)purgeSharedAnimationCache;
+
+/** Adds a CCAnimation with a name.
+ */
+-(void) addAnimation:(CCAnimation*)animation name:(NSString*)name;
+
+/** Deletes a CCAnimation from the cache.
+ */
+-(void) removeAnimationByName:(NSString*)name;
+
+/** Returns a CCAnimation that was previously added.
+ If the name is not found it will return nil.
+ You should retain the returned copy if you are going to use it.
+ */
+-(CCAnimation*) animationByName:(NSString*)name;
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "ccMacros.h"
+#import "CCAnimationCache.h"
+#import "CCAnimation.h"
+#import "CCSprite.h"
+
+
+@implementation CCAnimationCache
+
+#pragma mark CCAnimationCache - Alloc, Init & Dealloc
+
+static CCAnimationCache *sharedAnimationCache_=nil;
+
++ (CCAnimationCache *)sharedAnimationCache
+{
+ if (!sharedAnimationCache_)
+ sharedAnimationCache_ = [[CCAnimationCache alloc] init];
+
+ return sharedAnimationCache_;
+}
+
++(id)alloc
+{
+ NSAssert(sharedAnimationCache_ == nil, @"Attempted to allocate a second instance of a singleton.");
+ return [super alloc];
+}
+
++(void)purgeSharedAnimationCache
+{
+ [sharedAnimationCache_ release];
+ sharedAnimationCache_ = nil;
+}
+
+-(id) init
+{
+ if( (self=[super init]) ) {
+ animations_ = [[NSMutableDictionary alloc] initWithCapacity: 20];
+ }
+
+ return self;
+}
+
+- (NSString*) description
+{
+ return [NSString stringWithFormat:@"<%@ = %08X | num of animations = %i>", [self class], self, [animations_ count]];
+}
+
+-(void) dealloc
+{
+ CCLOGINFO(@"cocos2d: deallocing %@", self);
+
+ [animations_ release];
+ [super dealloc];
+}
+
+#pragma mark CCAnimationCache - load/get/del
+
+-(void) addAnimation:(CCAnimation*)animation name:(NSString*)name
+{
+ [animations_ setObject:animation forKey:name];
+}
+
+-(void) removeAnimationByName:(NSString*)name
+{
+ if( ! name )
+ return;
+
+ [animations_ removeObjectForKey:name];
+}
+
+-(CCAnimation*) animationByName:(NSString*)name
+{
+ return [animations_ objectForKey:name];
+}
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#import "CCTextureAtlas.h"
+#import "CCNode.h"
+#import "CCProtocols.h"
+
+/** CCAtlasNode is a subclass of CCNode that implements the CCRGBAProtocol and
+ CCTextureProtocol protocol
+
+ It knows how to render a TextureAtlas object.
+ If you are going to render a TextureAtlas consider subclassing CCAtlasNode (or a subclass of CCAtlasNode)
+
+ All features from CCNode are valid, plus the following features:
+ - opacity and RGB colors
+ */
+@interface CCAtlasNode : CCNode <CCRGBAProtocol, CCTextureProtocol>
+{
+ // texture atlas
+ CCTextureAtlas *textureAtlas_;
+
+ // chars per row
+ NSUInteger itemsPerRow_;
+ // chars per column
+ NSUInteger itemsPerColumn_;
+
+ // width of each char
+ NSUInteger itemWidth_;
+ // height of each char
+ NSUInteger itemHeight_;
+
+ // blend function
+ ccBlendFunc blendFunc_;
+
+ // texture RGBA.
+ GLubyte opacity_;
+ ccColor3B color_;
+ ccColor3B colorUnmodified_;
+ BOOL opacityModifyRGB_;
+}
+
+/** conforms to CCTextureProtocol protocol */
+@property (nonatomic,readwrite,retain) CCTextureAtlas *textureAtlas;
+
+/** conforms to CCTextureProtocol protocol */
+@property (nonatomic,readwrite) ccBlendFunc blendFunc;
+
+/** conforms to CCRGBAProtocol protocol */
+@property (nonatomic,readwrite) GLubyte opacity;
+/** conforms to CCRGBAProtocol protocol */
+@property (nonatomic,readwrite) ccColor3B color;
+
+
+/** creates a CCAtlasNode with an Atlas file the width and height of each item measured in points and the quantity of items to render*/
++(id) atlasWithTileFile:(NSString*)tile tileWidth:(NSUInteger)w tileHeight:(NSUInteger)h itemsToRender: (NSUInteger) c;
+
+/** initializes an CCAtlasNode with an Atlas file the width and height of each item measured in points and the quantity of items to render*/
+-(id) initWithTileFile:(NSString*)tile tileWidth:(NSUInteger)w tileHeight:(NSUInteger)h itemsToRender: (NSUInteger) c;
+
+/** updates the Atlas (indexed vertex array).
+ * Shall be overriden in subclasses
+ */
+-(void) updateAtlasValues;
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#import "CCAtlasNode.h"
+#import "ccMacros.h"
+
+
+@interface CCAtlasNode ()
+-(void) calculateMaxItems;
+-(void) updateBlendFunc;
+-(void) updateOpacityModifyRGB;
+@end
+
+@implementation CCAtlasNode
+
+@synthesize textureAtlas = textureAtlas_;
+@synthesize blendFunc = blendFunc_;
+
+#pragma mark CCAtlasNode - Creation & Init
++(id) atlasWithTileFile:(NSString*)tile tileWidth:(NSUInteger)w tileHeight:(NSUInteger)h itemsToRender: (NSUInteger) c
+{
+ return [[[self alloc] initWithTileFile:tile tileWidth:w tileHeight:h itemsToRender:c] autorelease];
+}
+
+-(id) initWithTileFile:(NSString*)tile tileWidth:(NSUInteger)w tileHeight:(NSUInteger)h itemsToRender: (NSUInteger) c
+{
+ if( (self=[super init]) ) {
+
+ itemWidth_ = w * CC_CONTENT_SCALE_FACTOR();
+ itemHeight_ = h * CC_CONTENT_SCALE_FACTOR();
+
+ opacity_ = 255;
+ color_ = colorUnmodified_ = ccWHITE;
+ opacityModifyRGB_ = YES;
+
+ blendFunc_.src = CC_BLEND_SRC;
+ blendFunc_.dst = CC_BLEND_DST;
+
+ // double retain to avoid the autorelease pool
+ // also, using: self.textureAtlas supports re-initialization without leaking
+ self.textureAtlas = [[CCTextureAtlas alloc] initWithFile:tile capacity:c];
+ [textureAtlas_ release];
+
+ if( ! textureAtlas_ ) {
+ CCLOG(@"cocos2d: Could not initialize CCAtlasNode. Invalid Texture");
+ [self release];
+ return nil;
+ }
+
+ [self updateBlendFunc];
+ [self updateOpacityModifyRGB];
+
+ [self calculateMaxItems];
+
+ }
+ return self;
+}
+
+-(void) dealloc
+{
+ [textureAtlas_ release];
+
+ [super dealloc];
+}
+
+#pragma mark CCAtlasNode - Atlas generation
+
+-(void) calculateMaxItems
+{
+ CGSize s = [[textureAtlas_ texture] contentSizeInPixels];
+ itemsPerColumn_ = s.height / itemHeight_;
+ itemsPerRow_ = s.width / itemWidth_;
+}
+
+-(void) updateAtlasValues
+{
+ [NSException raise:@"CCAtlasNode:Abstract" format:@"updateAtlasValue not overriden"];
+}
+
+#pragma mark CCAtlasNode - draw
+- (void) draw
+{
+ // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Unneeded states: GL_COLOR_ARRAY
+ glDisableClientState(GL_COLOR_ARRAY);
+
+ glColor4ub( color_.r, color_.g, color_.b, opacity_);
+
+ BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST;
+ if( newBlend )
+ glBlendFunc( blendFunc_.src, blendFunc_.dst );
+
+ [textureAtlas_ drawQuads];
+
+ if( newBlend )
+ glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
+
+ // is this chepear than saving/restoring color state ?
+ // XXX: There is no need to restore the color to (255,255,255,255). Objects should use the color
+ // XXX: that they need
+// glColor4ub( 255, 255, 255, 255);
+
+ // restore default GL state
+ glEnableClientState(GL_COLOR_ARRAY);
+
+}
+
+#pragma mark CCAtlasNode - RGBA protocol
+
+- (ccColor3B) color
+{
+ if(opacityModifyRGB_)
+ return colorUnmodified_;
+
+ return color_;
+}
+
+-(void) setColor:(ccColor3B)color3
+{
+ color_ = colorUnmodified_ = color3;
+
+ if( opacityModifyRGB_ ){
+ color_.r = color3.r * opacity_/255;
+ color_.g = color3.g * opacity_/255;
+ color_.b = color3.b * opacity_/255;
+ }
+}
+
+-(GLubyte) opacity
+{
+ return opacity_;
+}
+
+-(void) setOpacity:(GLubyte) anOpacity
+{
+ opacity_ = anOpacity;
+
+ // special opacity for premultiplied textures
+ if( opacityModifyRGB_ )
+ [self setColor: colorUnmodified_];
+}
+
+-(void) setOpacityModifyRGB:(BOOL)modify
+{
+ ccColor3B oldColor = self.color;
+ opacityModifyRGB_ = modify;
+ self.color = oldColor;
+}
+
+-(BOOL) doesOpacityModifyRGB
+{
+ return opacityModifyRGB_;
+}
+
+-(void) updateOpacityModifyRGB
+{
+ opacityModifyRGB_ = [textureAtlas_.texture hasPremultipliedAlpha];
+}
+
+#pragma mark CCAtlasNode - CocosNodeTexture protocol
+
+-(void) updateBlendFunc
+{
+ if( ! [textureAtlas_.texture hasPremultipliedAlpha] ) {
+ blendFunc_.src = GL_SRC_ALPHA;
+ blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA;
+ }
+}
+
+-(void) setTexture:(CCTexture2D*)texture
+{
+ textureAtlas_.texture = texture;
+ [self updateBlendFunc];
+ [self updateOpacityModifyRGB];
+}
+
+-(CCTexture2D*) texture
+{
+ return textureAtlas_.texture;
+}
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Stuart Carnie
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import <Foundation/Foundation.h>
+
+/** @file
+ cocos2d blocks support
+ */
+
+// To comply with Apple Objective C runtime (this is defined in NSObjCRuntime.h)
+#if !defined(NS_BLOCKS_AVAILABLE)
+ #if __BLOCKS__
+ #define NS_BLOCKS_AVAILABLE 1
+ #else
+ #define NS_BLOCKS_AVAILABLE 0
+ #endif
+#endif
+
+#if NS_BLOCKS_AVAILABLE
+
+@interface NSObject(CCBlocksAdditions)
+
+- (void)ccCallbackBlock;
+- (void)ccCallbackBlockWithSender:(id)sender;
+
+@end
+
+#endif // NS_BLOCKS_AVAILABLE
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Stuart Carnie
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCBlockSupport.h"
+
+#if NS_BLOCKS_AVAILABLE
+
+@implementation NSObject(CCBlocksAdditions)
+
+- (void)ccCallbackBlock {
+ void (^block)(void) = (id)self;
+ block();
+}
+
+- (void)ccCallbackBlockWithSender:(id)sender {
+ void (^block)(id) = (id)self;
+ block(sender);
+}
+
+
+@end
+
+#endif
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+
+#import "CCNode.h"
+
+/**
+ A CCCamera is used in every CCNode.
+ Useful to look at the object from different views.
+ The OpenGL gluLookAt() function is used to locate the
+ camera.
+
+ If the object is transformed by any of the scale, rotation or
+ position attributes, then they will override the camera.
+
+ IMPORTANT: Either your use the camera or the rotation/scale/position properties. You can't use both.
+ World coordinates won't work if you use the camera.
+
+ Limitations:
+
+ - Some nodes, like CCParallaxNode, CCParticle uses world node coordinates, and they won't work properly if you move them (or any of their ancestors)
+ using the camera.
+
+ - It doesn't work on batched nodes like CCSprite objects when they are parented to a CCSpriteBatchNode object.
+
+ - It is recommended to use it ONLY if you are going to create 3D effects. For 2D effecs, use the action CCFollow or position/scale/rotate.
+
+*/
+
+@interface CCCamera : NSObject
+{
+ float eyeX_;
+ float eyeY_;
+ float eyeZ_;
+
+ float centerX_;
+ float centerY_;
+ float centerZ_;
+
+ float upX_;
+ float upY_;
+ float upZ_;
+
+ BOOL dirty_;
+}
+
+/** whether of not the camera is dirty */
+@property (nonatomic,readwrite) BOOL dirty;
+
+/** returns the Z eye */
++(float) getZEye;
+
+/** sets the camera in the defaul position */
+-(void) restore;
+/** Sets the camera using gluLookAt using its eye, center and up_vector */
+-(void) locate;
+/** sets the eye values in points */
+-(void) setEyeX: (float)x eyeY:(float)y eyeZ:(float)z;
+/** sets the center values in points */
+-(void) setCenterX: (float)x centerY:(float)y centerZ:(float)z;
+/** sets the up values */
+-(void) setUpX: (float)x upY:(float)y upZ:(float)z;
+
+/** get the eye vector values in points */
+-(void) eyeX:(float*)x eyeY:(float*)y eyeZ:(float*)z;
+/** get the center vector values in points */
+-(void) centerX:(float*)x centerY:(float*)y centerZ:(float*)z;
+/** get the up vector values */
+-(void) upX:(float*)x upY:(float*)y upZ:(float*)z;
+
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#import "Platforms/CCGL.h"
+#import "CCCamera.h"
+#import "ccMacros.h"
+#import "CCDrawingPrimitives.h"
+
+@implementation CCCamera
+
+@synthesize dirty = dirty_;
+
+-(id) init
+{
+ if( (self=[super init]) )
+ [self restore];
+
+ return self;
+}
+
+- (NSString*) description
+{
+ return [NSString stringWithFormat:@"<%@ = %08X | center = (%.2f,%.2f,%.2f)>", [self class], self, centerX_, centerY_, centerZ_];
+}
+
+
+- (void) dealloc
+{
+ CCLOGINFO(@"cocos2d: deallocing %@", self);
+ [super dealloc];
+}
+
+-(void) restore
+{
+ eyeX_ = eyeY_ = 0;
+ eyeZ_ = [CCCamera getZEye];
+
+ centerX_ = centerY_ = centerZ_ = 0;
+
+ upX_ = 0.0f;
+ upY_ = 1.0f;
+ upZ_ = 0.0f;
+
+ dirty_ = NO;
+}
+
+-(void) locate
+{
+ if( dirty_ )
+ gluLookAt( eyeX_, eyeY_, eyeZ_,
+ centerX_, centerY_, centerZ_,
+ upX_, upY_, upZ_
+ );
+}
+
++(float) getZEye
+{
+ return FLT_EPSILON;
+// CGSize s = [[CCDirector sharedDirector] displaySize];
+// return ( s.height / 1.1566f );
+}
+
+-(void) setEyeX: (float)x eyeY:(float)y eyeZ:(float)z
+{
+ eyeX_ = x * CC_CONTENT_SCALE_FACTOR();
+ eyeY_ = y * CC_CONTENT_SCALE_FACTOR();
+ eyeZ_ = z * CC_CONTENT_SCALE_FACTOR();
+ dirty_ = YES;
+}
+
+-(void) setCenterX: (float)x centerY:(float)y centerZ:(float)z
+{
+ centerX_ = x * CC_CONTENT_SCALE_FACTOR();
+ centerY_ = y * CC_CONTENT_SCALE_FACTOR();
+ centerZ_ = z * CC_CONTENT_SCALE_FACTOR();
+ dirty_ = YES;
+}
+
+-(void) setUpX: (float)x upY:(float)y upZ:(float)z
+{
+ upX_ = x;
+ upY_ = y;
+ upZ_ = z;
+ dirty_ = YES;
+}
+
+-(void) eyeX: (float*)x eyeY:(float*)y eyeZ:(float*)z
+{
+ *x = eyeX_ / CC_CONTENT_SCALE_FACTOR();
+ *y = eyeY_ / CC_CONTENT_SCALE_FACTOR();
+ *z = eyeZ_ / CC_CONTENT_SCALE_FACTOR();
+}
+
+-(void) centerX: (float*)x centerY:(float*)y centerZ:(float*)z
+{
+ *x = centerX_ / CC_CONTENT_SCALE_FACTOR();
+ *y = centerY_ / CC_CONTENT_SCALE_FACTOR();
+ *z = centerZ_ / CC_CONTENT_SCALE_FACTOR();
+}
+
+-(void) upX: (float*)x upY:(float*)y upZ:(float*)z
+{
+ *x = upX_;
+ *y = upY_;
+ *z = upZ_;
+}
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#import <Foundation/Foundation.h>
+
+#import "Platforms/CCGL.h"
+
+/** OS version definitions. Includes both iOS and Mac OS versions
+ */
+enum {
+ kCCiOSVersion_3_0 = 0x03000000,
+ kCCiOSVersion_3_1 = 0x03010000,
+ kCCiOSVersion_3_1_1 = 0x03010100,
+ kCCiOSVersion_3_1_2 = 0x03010200,
+ kCCiOSVersion_3_1_3 = 0x03010300,
+ kCCiOSVersion_3_2 = 0x03020000,
+ kCCiOSVersion_3_2_1 = 0x03020100,
+ kCCiOSVersion_4_0 = 0x04000000,
+ kCCiOSVersion_4_0_1 = 0x04000100,
+ kCCiOSVersion_4_1 = 0x04010000,
+ kCCiOSVersion_4_2 = 0x04020000,
+ kCCiOSVersion_4_3 = 0x04030000,
+ kCCiOSVersion_4_3_1 = 0x04030100,
+ kCCiOSVersion_4_3_2 = 0x04030200,
+ kCCiOSVersion_4_3_3 = 0x04030300,
+
+ kCCMacVersion_10_5 = 0x0a050000,
+ kCCMacVersion_10_6 = 0x0a060000,
+ kCCMacVersion_10_7 = 0x0a070000,
+};
+
+/**
+ CCConfiguration contains some openGL variables
+ @since v0.99.0
+ */
+@interface CCConfiguration : NSObject {
+
+ GLint maxTextureSize_;
+ GLint maxModelviewStackDepth_;
+ BOOL supportsPVRTC_;
+ BOOL supportsNPOT_;
+ BOOL supportsBGRA8888_;
+ BOOL supportsDiscardFramebuffer_;
+ unsigned int OSVersion_;
+ GLint maxSamplesAllowed_;
+}
+
+/** OpenGL Max texture size. */
+@property (nonatomic, readonly) GLint maxTextureSize;
+
+/** OpenGL Max Modelview Stack Depth. */
+@property (nonatomic, readonly) GLint maxModelviewStackDepth;
+
+/** Whether or not the GPU supports NPOT (Non Power Of Two) textures.
+ NPOT textures have the following limitations:
+ - They can't have mipmaps
+ - They only accept GL_CLAMP_TO_EDGE in GL_TEXTURE_WRAP_{S,T}
+
+ @since v0.99.2
+ */
+@property (nonatomic, readonly) BOOL supportsNPOT;
+
+/** Whether or not PVR Texture Compressed is supported */
+@property (nonatomic, readonly) BOOL supportsPVRTC;
+
+/** Whether or not BGRA8888 textures are supported.
+
+ @since v0.99.2
+ */
+@property (nonatomic, readonly) BOOL supportsBGRA8888;
+
+/** Whether or not glDiscardFramebufferEXT is supported
+
+ @since v0.99.2
+ */
+@property (nonatomic, readonly) BOOL supportsDiscardFramebuffer;
+
+/** returns the OS version.
+ - On iOS devices it returns the firmware version.
+ - On Mac returns the OS version
+
+ @since v0.99.5
+ */
+@property (nonatomic, readonly) unsigned int OSVersion;
+
+/** returns a shared instance of the CCConfiguration */
++(CCConfiguration *) sharedConfiguration;
+
+/** returns whether or not an OpenGL is supported */
+- (BOOL) checkForGLExtension:(NSString *)searchName;
+
+
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#import <Availability.h>
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import <UIKit/UIKit.h> // Needed for UIDevice
+#endif
+
+#import "Platforms/CCGL.h"
+#import "CCBlockSupport.h"
+#import "CCConfiguration.h"
+#import "ccMacros.h"
+#import "ccConfig.h"
+#import "Support/OpenGL_Internal.h"
+
+@implementation CCConfiguration
+
+@synthesize maxTextureSize = maxTextureSize_;
+@synthesize supportsPVRTC = supportsPVRTC_;
+@synthesize maxModelviewStackDepth = maxModelviewStackDepth_;
+@synthesize supportsNPOT = supportsNPOT_;
+@synthesize supportsBGRA8888 = supportsBGRA8888_;
+@synthesize supportsDiscardFramebuffer = supportsDiscardFramebuffer_;
+@synthesize OSVersion = OSVersion_;
+
+//
+// singleton stuff
+//
+static CCConfiguration *_sharedConfiguration = nil;
+
+static char * glExtensions;
+
++ (CCConfiguration *)sharedConfiguration
+{
+ if (!_sharedConfiguration)
+ _sharedConfiguration = [[self alloc] init];
+
+ return _sharedConfiguration;
+}
+
++(id)alloc
+{
+ NSAssert(_sharedConfiguration == nil, @"Attempted to allocate a second instance of a singleton.");
+ return [super alloc];
+}
+
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+- (NSString*)getMacVersion
+{
+ SInt32 versionMajor, versionMinor, versionBugFix;
+ Gestalt(gestaltSystemVersionMajor, &versionMajor);
+ Gestalt(gestaltSystemVersionMinor, &versionMinor);
+ Gestalt(gestaltSystemVersionBugFix, &versionBugFix);
+
+ return [NSString stringWithFormat:@"%d.%d.%d", versionMajor, versionMinor, versionBugFix];
+}
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
+
+-(id) init
+{
+ if( (self=[super init])) {
+
+ // Obtain iOS version
+ OSVersion_ = 0;
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+ NSString *OSVer = [[UIDevice currentDevice] systemVersion];
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+ NSString *OSVer = [self getMacVersion];
+#endif
+ NSArray *arr = [OSVer componentsSeparatedByString:@"."];
+ int idx=0x01000000;
+ for( NSString *str in arr ) {
+ int value = [str intValue];
+ OSVersion_ += value * idx;
+ idx = idx >> 8;
+ }
+ CCLOG(@"cocos2d: OS version: %@ (0x%08x)", OSVer, OSVersion_);
+
+ CCLOG(@"cocos2d: GL_VENDOR: %s", glGetString(GL_VENDOR) );
+ CCLOG(@"cocos2d: GL_RENDERER: %s", glGetString ( GL_RENDERER ) );
+ CCLOG(@"cocos2d: GL_VERSION: %s", glGetString ( GL_VERSION ) );
+
+ glExtensions = (char*) glGetString(GL_EXTENSIONS);
+
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize_);
+ glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH, &maxModelviewStackDepth_);
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+ if( OSVersion_ >= kCCiOSVersion_4_0 )
+ glGetIntegerv(GL_MAX_SAMPLES_APPLE, &maxSamplesAllowed_);
+ else
+ maxSamplesAllowed_ = 0;
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+ glGetIntegerv(GL_MAX_SAMPLES, &maxSamplesAllowed_);
+#endif
+
+ supportsPVRTC_ = [self checkForGLExtension:@"GL_IMG_texture_compression_pvrtc"];
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+ supportsNPOT_ = [self checkForGLExtension:@"GL_APPLE_texture_2D_limited_npot"];
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+ supportsNPOT_ = [self checkForGLExtension:@"GL_ARB_texture_non_power_of_two"];
+#endif
+ // It seems that somewhere between firmware iOS 3.0 and 4.2 Apple renamed
+ // GL_IMG_... to GL_APPLE.... So we should check both names
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+ BOOL bgra8a = [self checkForGLExtension:@"GL_IMG_texture_format_BGRA8888"];
+ BOOL bgra8b = [self checkForGLExtension:@"GL_APPLE_texture_format_BGRA8888"];
+ supportsBGRA8888_ = bgra8a | bgra8b;
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+ supportsBGRA8888_ = [self checkForGLExtension:@"GL_EXT_bgra"];
+#endif
+
+ supportsDiscardFramebuffer_ = [self checkForGLExtension:@"GL_EXT_discard_framebuffer"];
+
+ CCLOG(@"cocos2d: GL_MAX_TEXTURE_SIZE: %d", maxTextureSize_);
+ CCLOG(@"cocos2d: GL_MAX_MODELVIEW_STACK_DEPTH: %d",maxModelviewStackDepth_);
+ CCLOG(@"cocos2d: GL_MAX_SAMPLES: %d", maxSamplesAllowed_);
+ CCLOG(@"cocos2d: GL supports PVRTC: %s", (supportsPVRTC_ ? "YES" : "NO") );
+ CCLOG(@"cocos2d: GL supports BGRA8888 textures: %s", (supportsBGRA8888_ ? "YES" : "NO") );
+ CCLOG(@"cocos2d: GL supports NPOT textures: %s", (supportsNPOT_ ? "YES" : "NO") );
+ CCLOG(@"cocos2d: GL supports discard_framebuffer: %s", (supportsDiscardFramebuffer_ ? "YES" : "NO") );
+ CCLOG(@"cocos2d: compiled with NPOT support: %s",
+#if CC_TEXTURE_NPOT_SUPPORT
+ "YES"
+#else
+ "NO"
+#endif
+ );
+ CCLOG(@"cocos2d: compiled with VBO support in TextureAtlas : %s",
+#if CC_USES_VBO
+ "YES"
+#else
+ "NO"
+#endif
+ );
+
+ CCLOG(@"cocos2d: compiled with Affine Matrix transformation in CCNode : %s",
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+ "YES"
+#else
+ "NO"
+#endif
+ );
+
+ CCLOG(@"cocos2d: compiled with Profiling Support: %s",
+#if CC_ENABLE_PROFILERS
+
+ "YES - *** Disable it when you finish profiling ***"
+#else
+ "NO"
+#endif
+ );
+
+ CHECK_GL_ERROR();
+ }
+
+ return self;
+}
+
+- (BOOL) checkForGLExtension:(NSString *)searchName
+{
+ // For best results, extensionsNames should be stored in your renderer so that it does not
+ // need to be recreated on each invocation.
+ NSString *extensionsString = [NSString stringWithCString:glExtensions encoding: NSASCIIStringEncoding];
+ NSArray *extensionsNames = [extensionsString componentsSeparatedByString:@" "];
+ return [extensionsNames containsObject: searchName];
+}
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#import "ccConfig.h"
+#import "ccTypes.h"
+
+// OpenGL related
+#import "Platforms/CCGL.h"
+#import "CCProtocols.h"
+
+/** @typedef ccDirectorProjection
+ Possible OpenGL projections used by director
+ */
+typedef enum {
+ /// sets a 2D projection (orthogonal projection).
+ kCCDirectorProjection2D,
+
+ /// sets a 3D projection with a fovy=60, znear=0.5f and zfar=1500.
+ kCCDirectorProjection3D,
+
+ /// it calls "updateProjection" on the projection delegate.
+ kCCDirectorProjectionCustom,
+
+ /// Detault projection is 3D projection
+ kCCDirectorProjectionDefault = kCCDirectorProjection3D,
+
+ // backward compatibility stuff
+ CCDirectorProjection2D = kCCDirectorProjection2D,
+ CCDirectorProjection3D = kCCDirectorProjection3D,
+ CCDirectorProjectionCustom = kCCDirectorProjectionCustom,
+
+} ccDirectorProjection;
+
+
+@class CCLabelAtlas;
+@class CCScene;
+
+/**Class that creates and handle the main Window and manages how
+and when to execute the Scenes.
+
+ The CCDirector is also resposible for:
+ - initializing the OpenGL ES context
+ - setting the OpenGL pixel format (default on is RGB565)
+ - setting the OpenGL buffer depth (default one is 0-bit)
+ - setting the projection (default one is 3D)
+ - setting the orientation (default one is Protrait)
+
+ Since the CCDirector is a singleton, the standard way to use it is by calling:
+ - [[CCDirector sharedDirector] methodName];
+
+ The CCDirector also sets the default OpenGL context:
+ - GL_TEXTURE_2D is enabled
+ - GL_VERTEX_ARRAY is enabled
+ - GL_COLOR_ARRAY is enabled
+ - GL_TEXTURE_COORD_ARRAY is enabled
+*/
+@interface CCDirector : NSObject
+{
+ CC_GLVIEW *openGLView_;
+
+ // internal timer
+ NSTimeInterval animationInterval_;
+ NSTimeInterval oldAnimationInterval_;
+
+ /* display FPS ? */
+ BOOL displayFPS_;
+
+ NSUInteger frames_;
+ ccTime accumDt_;
+ ccTime frameRate_;
+#if CC_DIRECTOR_FAST_FPS
+ CCLabelAtlas *FPSLabel_;
+#endif
+
+ /* is the running scene paused */
+ BOOL isPaused_;
+
+ /* The running scene */
+ CCScene *runningScene_;
+
+ /* This object will be visited after the scene. Useful to hook a notification node */
+ id notificationNode_;
+
+ /* will be the next 'runningScene' in the next frame
+ nextScene is a weak reference. */
+ CCScene *nextScene_;
+
+ /* If YES, then "old" scene will receive the cleanup message */
+ BOOL sendCleanupToScene_;
+
+ /* scheduled scenes */
+ NSMutableArray *scenesStack_;
+
+ /* last time the main loop was updated */
+ struct timeval lastUpdate_;
+ /* delta time since last tick to main loop */
+ ccTime dt;
+ /* whether or not the next delta time will be zero */
+ BOOL nextDeltaTimeZero_;
+
+ /* projection used */
+ ccDirectorProjection projection_;
+
+ /* Projection protocol delegate */
+ id<CCProjectionProtocol> projectionDelegate_;
+
+ /* window size in points */
+ CGSize winSizeInPoints_;
+
+ /* window size in pixels */
+ CGSize winSizeInPixels_;
+
+ /* the cocos2d running thread */
+ NSThread *runningThread_;
+
+ // profiler
+#if CC_ENABLE_PROFILERS
+ ccTime accumDtForProfiler_;
+#endif
+}
+
+/** returns the cocos2d thread.
+ If you want to run any cocos2d task, run it in this thread.
+ On iOS usually it is the main thread.
+ @since v0.99.5
+ */
+@property (readonly, nonatomic ) NSThread *runningThread;
+/** The current running Scene. Director can only run one Scene at the time */
+@property (nonatomic,readonly) CCScene* runningScene;
+/** The FPS value */
+@property (nonatomic,readwrite, assign) NSTimeInterval animationInterval;
+/** Whether or not to display the FPS on the bottom-left corner */
+@property (nonatomic,readwrite, assign) BOOL displayFPS;
+/** The OpenGLView, where everything is rendered */
+@property (nonatomic,readwrite,retain) CC_GLVIEW *openGLView;
+/** whether or not the next delta time will be zero */
+@property (nonatomic,readwrite,assign) BOOL nextDeltaTimeZero;
+/** Whether or not the Director is paused */
+@property (nonatomic,readonly) BOOL isPaused;
+/** Sets an OpenGL projection
+ @since v0.8.2
+ */
+@property (nonatomic,readwrite) ccDirectorProjection projection;
+
+/** Whether or not the replaced scene will receive the cleanup message.
+ If the new scene is pushed, then the old scene won't receive the "cleanup" message.
+ If the new scene replaces the old one, the it will receive the "cleanup" message.
+ @since v0.99.0
+ */
+@property (nonatomic, readonly) BOOL sendCleanupToScene;
+
+/** This object will be visited after the main scene is visited.
+ This object MUST implement the "visit" selector.
+ Useful to hook a notification object, like CCNotifications (http://github.com/manucorporat/CCNotifications)
+ @since v0.99.5
+ */
+@property (nonatomic, readwrite, retain) id notificationNode;
+
+/** This object will be called when the OpenGL projection is udpated and only when the kCCDirectorProjectionCustom projection is used.
+ @since v0.99.5
+ */
+@property (nonatomic, readwrite, retain) id<CCProjectionProtocol> projectionDelegate;
+
+/** returns a shared instance of the director */
++(CCDirector *)sharedDirector;
+
+
+
+// Window size
+
+/** returns the size of the OpenGL view in points.
+ It takes into account any possible rotation (device orientation) of the window
+ */
+- (CGSize) winSize;
+
+/** returns the size of the OpenGL view in pixels.
+ It takes into account any possible rotation (device orientation) of the window.
+ On Mac winSize and winSizeInPixels return the same value.
+ */
+- (CGSize) winSizeInPixels;
+/** returns the display size of the OpenGL view in pixels.
+ It doesn't take into account any possible rotation of the window.
+ */
+-(CGSize) displaySizeInPixels;
+/** changes the projection size */
+-(void) reshapeProjection:(CGSize)newWindowSize;
+
+/** converts a UIKit coordinate to an OpenGL coordinate
+ Useful to convert (multi) touchs coordinates to the current layout (portrait or landscape)
+ */
+-(CGPoint) convertToGL: (CGPoint) p;
+/** converts an OpenGL coordinate to a UIKit coordinate
+ Useful to convert node points to window points for calls such as glScissor
+ */
+-(CGPoint) convertToUI:(CGPoint)p;
+
+/// XXX: missing description
+-(float) getZEye;
+
+// Scene Management
+
+/**Enters the Director's main loop with the given Scene.
+ * Call it to run only your FIRST scene.
+ * Don't call it if there is already a running scene.
+ */
+- (void) runWithScene:(CCScene*) scene;
+
+/**Suspends the execution of the running scene, pushing it on the stack of suspended scenes.
+ * The new scene will be executed.
+ * Try to avoid big stacks of pushed scenes to reduce memory allocation.
+ * ONLY call it if there is a running scene.
+ */
+- (void) pushScene:(CCScene*) scene;
+
+/**Pops out a scene from the queue.
+ * This scene will replace the running one.
+ * The running scene will be deleted. If there are no more scenes in the stack the execution is terminated.
+ * ONLY call it if there is a running scene.
+ */
+- (void) popScene;
+
+/** Replaces the running scene with a new one. The running scene is terminated.
+ * ONLY call it if there is a running scene.
+ */
+-(void) replaceScene: (CCScene*) scene;
+
+/** Ends the execution, releases the running scene.
+ It doesn't remove the OpenGL view from its parent. You have to do it manually.
+ */
+-(void) end;
+
+/** Pauses the running scene.
+ The running scene will be _drawed_ but all scheduled timers will be paused
+ While paused, the draw rate will be 4 FPS to reduce CPU consuption
+ */
+-(void) pause;
+
+/** Resumes the paused scene
+ The scheduled timers will be activated again.
+ The "delta time" will be 0 (as if the game wasn't paused)
+ */
+-(void) resume;
+
+/** Stops the animation. Nothing will be drawn. The main loop won't be triggered anymore.
+ If you wan't to pause your animation call [pause] instead.
+ */
+-(void) stopAnimation;
+
+/** The main loop is triggered again.
+ Call this function only if [stopAnimation] was called earlier
+ @warning Dont' call this function to start the main loop. To run the main loop call runWithScene
+ */
+-(void) startAnimation;
+
+/** Draw the scene.
+ This method is called every frame. Don't call it manually.
+ */
+-(void) drawScene;
+
+// Memory Helper
+
+/** Removes all the cocos2d data that was cached automatically.
+ It will purge the CCTextureCache, CCBitmapFont cache.
+ IMPORTANT: The CCSpriteFrameCache won't be purged. If you want to purge it, you have to purge it manually.
+ @since v0.99.3
+ */
+-(void) purgeCachedData;
+
+// OpenGL Helper
+
+/** sets the OpenGL default values */
+-(void) setGLDefaultValues;
+
+/** enables/disables OpenGL alpha blending */
+- (void) setAlphaBlending: (BOOL) on;
+/** enables/disables OpenGL depth test */
+- (void) setDepthTest: (BOOL) on;
+
+// Profiler
+-(void) showProfilers;
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+/* Idea of decoupling Window from Director taken from OC3D project: http://code.google.com/p/oc3d/
+ */
+
+#import <unistd.h>
+#import <Availability.h>
+
+// cocos2d imports
+#import "CCDirector.h"
+#import "CCScheduler.h"
+#import "CCActionManager.h"
+#import "CCTextureCache.h"
+#import "CCAnimationCache.h"
+#import "CCLabelAtlas.h"
+#import "ccMacros.h"
+#import "CCTransition.h"
+#import "CCScene.h"
+#import "CCSpriteFrameCache.h"
+#import "CCTexture2D.h"
+#import "CCLabelBMFont.h"
+#import "CCLayer.h"
+
+// support imports
+#import "Platforms/CCGL.h"
+#import "Platforms/CCNS.h"
+
+#import "Support/OpenGL_Internal.h"
+#import "Support/CGPointExtension.h"
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import "Platforms/iOS/CCDirectorIOS.h"
+#define CC_DIRECTOR_DEFAULT CCDirectorTimer
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+#import "Platforms/Mac/CCDirectorMac.h"
+#define CC_DIRECTOR_DEFAULT CCDirectorDisplayLink
+#endif
+
+#import "Support/CCProfiling.h"
+
+#define kDefaultFPS 60.0 // 60 frames per second
+
+extern NSString * cocos2dVersion(void);
+
+
+@interface CCDirector (Private)
+-(void) setNextScene;
+// shows the FPS in the screen
+-(void) showFPS;
+// calculates delta time since last time it was called
+-(void) calculateDeltaTime;
+@end
+
+@implementation CCDirector
+
+@synthesize animationInterval = animationInterval_;
+@synthesize runningScene = runningScene_;
+@synthesize displayFPS = displayFPS_;
+@synthesize nextDeltaTimeZero = nextDeltaTimeZero_;
+@synthesize isPaused = isPaused_;
+@synthesize sendCleanupToScene = sendCleanupToScene_;
+@synthesize runningThread = runningThread_;
+@synthesize notificationNode = notificationNode_;
+@synthesize projectionDelegate = projectionDelegate_;
+//
+// singleton stuff
+//
+static CCDirector *_sharedDirector = nil;
+
++ (CCDirector *)sharedDirector
+{
+ if (!_sharedDirector) {
+
+ //
+ // Default Director is TimerDirector
+ //
+ if( [ [CCDirector class] isEqual:[self class]] )
+ _sharedDirector = [[CC_DIRECTOR_DEFAULT alloc] init];
+ else
+ _sharedDirector = [[self alloc] init];
+ }
+
+ return _sharedDirector;
+}
+
++(id)alloc
+{
+ NSAssert(_sharedDirector == nil, @"Attempted to allocate a second instance of a singleton.");
+ return [super alloc];
+}
+
+- (id) init
+{
+ CCLOG(@"cocos2d: %@", cocos2dVersion() );
+
+ if( (self=[super init]) ) {
+
+ CCLOG(@"cocos2d: Using Director Type:%@", [self class]);
+
+ // scenes
+ runningScene_ = nil;
+ nextScene_ = nil;
+
+ notificationNode_ = nil;
+
+ oldAnimationInterval_ = animationInterval_ = 1.0 / kDefaultFPS;
+ scenesStack_ = [[NSMutableArray alloc] initWithCapacity:10];
+
+ // Set default projection (3D)
+ projection_ = kCCDirectorProjectionDefault;
+
+ // projection delegate if "Custom" projection is used
+ projectionDelegate_ = nil;
+
+ // FPS
+ displayFPS_ = NO;
+ frames_ = 0;
+
+ // paused ?
+ isPaused_ = NO;
+
+ // running thread
+ runningThread_ = nil;
+
+ winSizeInPixels_ = winSizeInPoints_ = CGSizeZero;
+ }
+
+ return self;
+}
+
+- (void) dealloc
+{
+ CCLOGINFO(@"cocos2d: deallocing %@", self);
+
+#if CC_DIRECTOR_FAST_FPS
+ [FPSLabel_ release];
+#endif
+ [runningScene_ release];
+ [notificationNode_ release];
+ [scenesStack_ release];
+
+ [projectionDelegate_ release];
+
+ _sharedDirector = nil;
+
+ [super dealloc];
+}
+
+-(void) setGLDefaultValues
+{
+ // This method SHOULD be called only after openGLView_ was initialized
+ NSAssert( openGLView_, @"openGLView_ must be initialized");
+
+ [self setAlphaBlending: YES];
+ [self setDepthTest: YES];
+ [self setProjection: projection_];
+
+ // set other opengl default values
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+
+#if CC_DIRECTOR_FAST_FPS
+ if (!FPSLabel_) {
+ CCTexture2DPixelFormat currentFormat = [CCTexture2D defaultAlphaPixelFormat];
+ [CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA4444];
+ FPSLabel_ = [[CCLabelAtlas labelWithString:@"00.0" charMapFile:@"fps_images.png" itemWidth:16 itemHeight:24 startCharMap:'.'] retain];
+ [CCTexture2D setDefaultAlphaPixelFormat:currentFormat];
+ }
+#endif // CC_DIRECTOR_FAST_FPS
+}
+
+//
+// Draw the Scene
+//
+- (void) drawScene
+{
+ // Override me
+}
+
+-(void) calculateDeltaTime
+{
+ struct timeval now;
+
+ if( gettimeofday( &now, NULL) != 0 ) {
+ CCLOG(@"cocos2d: error in gettimeofday");
+ dt = 0;
+ return;
+ }
+
+ // new delta time
+ if( nextDeltaTimeZero_ ) {
+ dt = 0;
+ nextDeltaTimeZero_ = NO;
+ } else {
+ dt = (now.tv_sec - lastUpdate_.tv_sec) + (now.tv_usec - lastUpdate_.tv_usec) / 1000000.0f;
+ dt = MAX(0,dt);
+ }
+
+#ifdef DEBUG
+ // If we are debugging our code, prevent big delta time
+ if( dt > 0.2f )
+ dt = 1/60.0f;
+#endif
+
+ lastUpdate_ = now;
+}
+
+#pragma mark Director - Memory Helper
+
+-(void) purgeCachedData
+{
+ [CCLabelBMFont purgeCachedData];
+ [[CCTextureCache sharedTextureCache] removeUnusedTextures];
+}
+
+#pragma mark Director - Scene OpenGL Helper
+
+-(ccDirectorProjection) projection
+{
+ return projection_;
+}
+
+-(float) getZEye
+{
+ return ( winSizeInPixels_.height / 1.1566f );
+}
+
+-(void) setProjection:(ccDirectorProjection)projection
+{
+ CCLOG(@"cocos2d: override me");
+}
+
+- (void) setAlphaBlending: (BOOL) on
+{
+ if (on) {
+ glEnable(GL_BLEND);
+ glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
+
+ } else
+ glDisable(GL_BLEND);
+}
+
+- (void) setDepthTest: (BOOL) on
+{
+ if (on) {
+ ccglClearDepth(1.0f);
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LEQUAL);
+// glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
+ } else
+ glDisable( GL_DEPTH_TEST );
+}
+
+#pragma mark Director Integration with a UIKit view
+
+-(CC_GLVIEW*) openGLView
+{
+ return openGLView_;
+}
+
+-(void) setOpenGLView:(CC_GLVIEW *)view
+{
+ NSAssert( view, @"OpenGLView must be non-nil");
+
+ if( view != openGLView_ ) {
+ [openGLView_ release];
+ openGLView_ = [view retain];
+
+ // set size
+ winSizeInPixels_ = winSizeInPoints_ = CCNSSizeToCGSize( [view bounds].size );
+
+ [self setGLDefaultValues];
+ }
+}
+
+#pragma mark Director Scene Landscape
+
+-(CGPoint)convertToGL:(CGPoint)uiPoint
+{
+ CCLOG(@"CCDirector#convertToGL: OVERRIDE ME.");
+ return CGPointZero;
+}
+
+-(CGPoint)convertToUI:(CGPoint)glPoint
+{
+ CCLOG(@"CCDirector#convertToUI: OVERRIDE ME.");
+ return CGPointZero;
+}
+
+-(CGSize)winSize
+{
+ return winSizeInPoints_;
+}
+
+-(CGSize)winSizeInPixels
+{
+ return winSizeInPixels_;
+}
+
+-(CGSize)displaySizeInPixels
+{
+ return winSizeInPixels_;
+}
+
+-(void) reshapeProjection:(CGSize)newWindowSize
+{
+ winSizeInPixels_ = winSizeInPoints_ = newWindowSize;
+ [self setProjection:projection_];
+}
+
+#pragma mark Director Scene Management
+
+- (void)runWithScene:(CCScene*) scene
+{
+ NSAssert( scene != nil, @"Argument must be non-nil");
+ NSAssert( runningScene_ == nil, @"You can't run an scene if another Scene is running. Use replaceScene or pushScene instead");
+
+ [self pushScene:scene];
+ [self startAnimation];
+}
+
+-(void) replaceScene: (CCScene*) scene
+{
+ NSAssert( scene != nil, @"Argument must be non-nil");
+
+ NSUInteger index = [scenesStack_ count];
+
+ sendCleanupToScene_ = YES;
+ [scenesStack_ replaceObjectAtIndex:index-1 withObject:scene];
+ nextScene_ = scene; // nextScene_ is a weak ref
+}
+
+- (void) pushScene: (CCScene*) scene
+{
+ NSAssert( scene != nil, @"Argument must be non-nil");
+
+ sendCleanupToScene_ = NO;
+
+ [scenesStack_ addObject: scene];
+ nextScene_ = scene; // nextScene_ is a weak ref
+}
+
+-(void) popScene
+{
+ NSAssert( runningScene_ != nil, @"A running Scene is needed");
+
+ [scenesStack_ removeLastObject];
+ NSUInteger c = [scenesStack_ count];
+
+ if( c == 0 )
+ [self end];
+ else {
+ sendCleanupToScene_ = YES;
+ nextScene_ = [scenesStack_ objectAtIndex:c-1];
+ }
+}
+
+-(void) end
+{
+ [runningScene_ onExit];
+ [runningScene_ cleanup];
+ [runningScene_ release];
+
+ runningScene_ = nil;
+ nextScene_ = nil;
+
+ // remove all objects, but don't release it.
+ // runWithScene might be executed after 'end'.
+ [scenesStack_ removeAllObjects];
+
+ [self stopAnimation];
+
+#if CC_DIRECTOR_FAST_FPS
+ [FPSLabel_ release];
+ FPSLabel_ = nil;
+#endif
+
+ [projectionDelegate_ release];
+ projectionDelegate_ = nil;
+
+ // Purge bitmap cache
+ [CCLabelBMFont purgeCachedData];
+
+ // Purge all managers
+ [CCAnimationCache purgeSharedAnimationCache];
+ [CCSpriteFrameCache purgeSharedSpriteFrameCache];
+ [CCScheduler purgeSharedScheduler];
+ [CCActionManager purgeSharedManager];
+ [CCTextureCache purgeSharedTextureCache];
+
+
+ // OpenGL view
+
+ // Since the director doesn't attach the openglview to the window
+ // it shouldn't remove it from the window too.
+// [openGLView_ removeFromSuperview];
+
+ [openGLView_ release];
+ openGLView_ = nil;
+}
+
+-(void) setNextScene
+{
+ Class transClass = [CCTransitionScene class];
+ BOOL runningIsTransition = [runningScene_ isKindOfClass:transClass];
+ BOOL newIsTransition = [nextScene_ isKindOfClass:transClass];
+
+ // If it is not a transition, call onExit/cleanup
+ if( ! newIsTransition ) {
+ [runningScene_ onExit];
+
+ // issue #709. the root node (scene) should receive the cleanup message too
+ // otherwise it might be leaked.
+ if( sendCleanupToScene_)
+ [runningScene_ cleanup];
+ }
+
+ [runningScene_ release];
+
+ runningScene_ = [nextScene_ retain];
+ nextScene_ = nil;
+
+ if( ! runningIsTransition ) {
+ [runningScene_ onEnter];
+ [runningScene_ onEnterTransitionDidFinish];
+ }
+}
+
+-(void) pause
+{
+ if( isPaused_ )
+ return;
+
+ oldAnimationInterval_ = animationInterval_;
+
+ // when paused, don't consume CPU
+ [self setAnimationInterval:1/4.0];
+ isPaused_ = YES;
+}
+
+-(void) resume
+{
+ if( ! isPaused_ )
+ return;
+
+ [self setAnimationInterval: oldAnimationInterval_];
+
+ if( gettimeofday( &lastUpdate_, NULL) != 0 ) {
+ CCLOG(@"cocos2d: Director: Error in gettimeofday");
+ }
+
+ isPaused_ = NO;
+ dt = 0;
+}
+
+- (void)startAnimation
+{
+ CCLOG(@"cocos2d: Director#startAnimation. Override me");
+}
+
+- (void)stopAnimation
+{
+ CCLOG(@"cocos2d: Director#stopAnimation. Override me");
+}
+
+- (void)setAnimationInterval:(NSTimeInterval)interval
+{
+ CCLOG(@"cocos2d: Director#setAnimationInterval. Override me");
+}
+
+#if CC_DIRECTOR_FAST_FPS
+
+// display the FPS using a LabelAtlas
+// updates the FPS every frame
+-(void) showFPS
+{
+ frames_++;
+ accumDt_ += dt;
+
+ if ( accumDt_ > CC_DIRECTOR_FPS_INTERVAL) {
+ frameRate_ = frames_/accumDt_;
+ frames_ = 0;
+ accumDt_ = 0;
+
+// sprintf(format,"%.1f",frameRate);
+// [FPSLabel setCString:format];
+
+ NSString *str = [[NSString alloc] initWithFormat:@"%.1f", frameRate_];
+ [FPSLabel_ setString:str];
+ [str release];
+ }
+
+ [FPSLabel_ draw];
+}
+#else
+// display the FPS using a manually generated Texture (very slow)
+// updates the FPS 3 times per second aprox.
+-(void) showFPS
+{
+ frames_++;
+ accumDt_ += dt;
+
+ if ( accumDt_ > CC_DIRECTOR_FPS_INTERVAL) {
+ frameRate_ = frames_/accumDt_;
+ frames_ = 0;
+ accumDt_ = 0;
+ }
+
+ NSString *str = [NSString stringWithFormat:@"%.2f",frameRate_];
+ CCTexture2D *texture = [[CCTexture2D alloc] initWithString:str dimensions:CGSizeMake(100,30) alignment:CCTextAlignmentLeft fontName:@"Arial" fontSize:24];
+
+ // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Unneeded states: GL_COLOR_ARRAY
+ glDisableClientState(GL_COLOR_ARRAY);
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ glColor4ub(224,224,244,200);
+ [texture drawAtPoint: ccp(5,2)];
+ [texture release];
+
+ glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
+
+ // restore default GL state
+ glEnableClientState(GL_COLOR_ARRAY);
+}
+#endif
+
+- (void) showProfilers {
+#if CC_ENABLE_PROFILERS
+ accumDtForProfiler_ += dt;
+ if (accumDtForProfiler_ > 1.0f) {
+ accumDtForProfiler_ = 0;
+ [[CCProfiler sharedProfiler] displayTimers];
+ }
+#endif // CC_ENABLE_PROFILERS
+}
+
+@end
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef __CC_DRAWING_PRIMITIVES_H
+#define __CC_DRAWING_PRIMITIVES_H
+
+#import <Availability.h>
+#import <Foundation/Foundation.h>
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import <CoreGraphics/CGGeometry.h> // for CGPoint
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ @file
+ Drawing OpenGL ES primitives.
+ - ccDrawPoint
+ - ccDrawLine
+ - ccDrawPoly
+ - ccDrawCircle
+ - ccDrawQuadBezier
+ - ccDrawCubicBezier
+
+ You can change the color, width and other property by calling the
+ glColor4ub(), glLineWidth(), glPointSize().
+
+ @warning These functions draws the Line, Point, Polygon, immediately. They aren't batched. If you are going to make a game that depends on these primitives, I suggest creating a batch.
+ */
+
+
+/** draws a point given x and y coordinate measured in points. */
+void ccDrawPoint( CGPoint point );
+
+/** draws an array of points.
+ @since v0.7.2
+ */
+void ccDrawPoints( const CGPoint *points, NSUInteger numberOfPoints );
+
+/** draws a line given the origin and destination point measured in points. */
+void ccDrawLine( CGPoint origin, CGPoint destination );
+
+/** draws a poligon given a pointer to CGPoint coordiantes and the number of vertices measured in points.
+ The polygon can be closed or open
+ */
+void ccDrawPoly( const CGPoint *vertices, NSUInteger numOfVertices, BOOL closePolygon );
+
+/** draws a circle given the center, radius and number of segments measured in points */
+void ccDrawCircle( CGPoint center, float radius, float angle, NSUInteger segments, BOOL drawLineToCenter);
+
+/** draws a quad bezier path measured in points.
+ @since v0.8
+ */
+void ccDrawQuadBezier(CGPoint origin, CGPoint control, CGPoint destination, NSUInteger segments);
+
+/** draws a cubic bezier path measured in points.
+ @since v0.8
+ */
+void ccDrawCubicBezier(CGPoint origin, CGPoint control1, CGPoint control2, CGPoint destination, NSUInteger segments);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __CC_DRAWING_PRIMITIVES_H
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#import <math.h>
+#import <stdlib.h>
+#import <string.h>
+
+#import "CCDrawingPrimitives.h"
+#import "ccTypes.h"
+#import "ccMacros.h"
+#import "Platforms/CCGL.h"
+
+void ccDrawPoint( CGPoint point )
+{
+ ccVertex2F p = (ccVertex2F) {point.x * CC_CONTENT_SCALE_FACTOR(), point.y * CC_CONTENT_SCALE_FACTOR() };
+
+ // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Needed states: GL_VERTEX_ARRAY,
+ // Unneeded states: GL_TEXTURE_2D, GL_TEXTURE_COORD_ARRAY, GL_COLOR_ARRAY
+ glDisable(GL_TEXTURE_2D);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_COLOR_ARRAY);
+
+ glVertexPointer(2, GL_FLOAT, 0, &p);
+ glDrawArrays(GL_POINTS, 0, 1);
+
+ // restore default state
+ glEnableClientState(GL_COLOR_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_2D);
+}
+
+void ccDrawPoints( const CGPoint *points, NSUInteger numberOfPoints )
+{
+ // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Needed states: GL_VERTEX_ARRAY,
+ // Unneeded states: GL_TEXTURE_2D, GL_TEXTURE_COORD_ARRAY, GL_COLOR_ARRAY
+ glDisable(GL_TEXTURE_2D);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_COLOR_ARRAY);
+
+ ccVertex2F newPoints[numberOfPoints];
+
+ // iPhone and 32-bit machines optimization
+ if( sizeof(CGPoint) == sizeof(ccVertex2F) ) {
+
+ // points ?
+ if( CC_CONTENT_SCALE_FACTOR() != 1 ) {
+ for( NSUInteger i=0; i<numberOfPoints;i++)
+ newPoints[i] = (ccVertex2F){ points[i].x * CC_CONTENT_SCALE_FACTOR(), points[i].y * CC_CONTENT_SCALE_FACTOR() };
+
+ glVertexPointer(2, GL_FLOAT, 0, newPoints);
+
+ } else
+ glVertexPointer(2, GL_FLOAT, 0, points);
+
+ glDrawArrays(GL_POINTS, 0, (GLsizei) numberOfPoints);
+
+ } else {
+
+ // Mac on 64-bit
+ for( NSUInteger i=0; i<numberOfPoints;i++)
+ newPoints[i] = (ccVertex2F) { points[i].x, points[i].y };
+
+ glVertexPointer(2, GL_FLOAT, 0, newPoints);
+ glDrawArrays(GL_POINTS, 0, (GLsizei) numberOfPoints);
+
+ }
+
+
+ // restore default state
+ glEnableClientState(GL_COLOR_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_2D);
+}
+
+
+void ccDrawLine( CGPoint origin, CGPoint destination )
+{
+ ccVertex2F vertices[2] = {
+ {origin.x * CC_CONTENT_SCALE_FACTOR(), origin.y * CC_CONTENT_SCALE_FACTOR() },
+ {destination.x * CC_CONTENT_SCALE_FACTOR(), destination.y * CC_CONTENT_SCALE_FACTOR() }
+ };
+
+ // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Needed states: GL_VERTEX_ARRAY,
+ // Unneeded states: GL_TEXTURE_2D, GL_TEXTURE_COORD_ARRAY, GL_COLOR_ARRAY
+ glDisable(GL_TEXTURE_2D);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_COLOR_ARRAY);
+
+ glVertexPointer(2, GL_FLOAT, 0, vertices);
+ glDrawArrays(GL_LINES, 0, 2);
+
+ // restore default state
+ glEnableClientState(GL_COLOR_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_2D);
+}
+
+
+void ccDrawPoly( const CGPoint *poli, NSUInteger numberOfPoints, BOOL closePolygon )
+{
+ ccVertex2F newPoint[numberOfPoints];
+
+ // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Needed states: GL_VERTEX_ARRAY,
+ // Unneeded states: GL_TEXTURE_2D, GL_TEXTURE_COORD_ARRAY, GL_COLOR_ARRAY
+ glDisable(GL_TEXTURE_2D);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_COLOR_ARRAY);
+
+
+ // iPhone and 32-bit machines
+ if( sizeof(CGPoint) == sizeof(ccVertex2F) ) {
+
+ // convert to pixels ?
+ if( CC_CONTENT_SCALE_FACTOR() != 1 ) {
+ memcpy( newPoint, poli, numberOfPoints * sizeof(ccVertex2F) );
+ for( NSUInteger i=0; i<numberOfPoints;i++)
+ newPoint[i] = (ccVertex2F) { poli[i].x * CC_CONTENT_SCALE_FACTOR(), poli[i].y * CC_CONTENT_SCALE_FACTOR() };
+
+ glVertexPointer(2, GL_FLOAT, 0, newPoint);
+
+ } else
+ glVertexPointer(2, GL_FLOAT, 0, poli);
+
+
+ } else {
+ // 64-bit machines (Mac)
+
+ for( NSUInteger i=0; i<numberOfPoints;i++)
+ newPoint[i] = (ccVertex2F) { poli[i].x, poli[i].y };
+
+ glVertexPointer(2, GL_FLOAT, 0, newPoint );
+
+ }
+
+ if( closePolygon )
+ glDrawArrays(GL_LINE_LOOP, 0, (GLsizei) numberOfPoints);
+ else
+ glDrawArrays(GL_LINE_STRIP, 0, (GLsizei) numberOfPoints);
+
+ // restore default state
+ glEnableClientState(GL_COLOR_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_2D);
+}
+
+void ccDrawCircle( CGPoint center, float r, float a, NSUInteger segs, BOOL drawLineToCenter)
+{
+ int additionalSegment = 1;
+ if (drawLineToCenter)
+ additionalSegment++;
+
+ const float coef = 2.0f * (float)M_PI/segs;
+
+ GLfloat *vertices = calloc( sizeof(GLfloat)*2*(segs+2), 1);
+ if( ! vertices )
+ return;
+
+ for(NSUInteger i=0;i<=segs;i++)
+ {
+ float rads = i*coef;
+ GLfloat j = r * cosf(rads + a) + center.x;
+ GLfloat k = r * sinf(rads + a) + center.y;
+
+ vertices[i*2] = j * CC_CONTENT_SCALE_FACTOR();
+ vertices[i*2+1] =k * CC_CONTENT_SCALE_FACTOR();
+ }
+ vertices[(segs+1)*2] = center.x * CC_CONTENT_SCALE_FACTOR();
+ vertices[(segs+1)*2+1] = center.y * CC_CONTENT_SCALE_FACTOR();
+
+ // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Needed states: GL_VERTEX_ARRAY,
+ // Unneeded states: GL_TEXTURE_2D, GL_TEXTURE_COORD_ARRAY, GL_COLOR_ARRAY
+ glDisable(GL_TEXTURE_2D);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_COLOR_ARRAY);
+
+ glVertexPointer(2, GL_FLOAT, 0, vertices);
+ glDrawArrays(GL_LINE_STRIP, 0, (GLsizei) segs+additionalSegment);
+
+ // restore default state
+ glEnableClientState(GL_COLOR_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_2D);
+
+ free( vertices );
+}
+
+void ccDrawQuadBezier(CGPoint origin, CGPoint control, CGPoint destination, NSUInteger segments)
+{
+ ccVertex2F vertices[segments + 1];
+
+ float t = 0.0f;
+ for(NSUInteger i = 0; i < segments; i++)
+ {
+ GLfloat x = powf(1 - t, 2) * origin.x + 2.0f * (1 - t) * t * control.x + t * t * destination.x;
+ GLfloat y = powf(1 - t, 2) * origin.y + 2.0f * (1 - t) * t * control.y + t * t * destination.y;
+ vertices[i] = (ccVertex2F) {x * CC_CONTENT_SCALE_FACTOR(), y * CC_CONTENT_SCALE_FACTOR() };
+ t += 1.0f / segments;
+ }
+ vertices[segments] = (ccVertex2F) {destination.x * CC_CONTENT_SCALE_FACTOR(), destination.y * CC_CONTENT_SCALE_FACTOR() };
+
+ // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Needed states: GL_VERTEX_ARRAY,
+ // Unneeded states: GL_TEXTURE_2D, GL_TEXTURE_COORD_ARRAY, GL_COLOR_ARRAY
+ glDisable(GL_TEXTURE_2D);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_COLOR_ARRAY);
+
+ glVertexPointer(2, GL_FLOAT, 0, vertices);
+ glDrawArrays(GL_LINE_STRIP, 0, (GLsizei) segments + 1);
+
+ // restore default state
+ glEnableClientState(GL_COLOR_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_2D);
+}
+
+void ccDrawCubicBezier(CGPoint origin, CGPoint control1, CGPoint control2, CGPoint destination, NSUInteger segments)
+{
+ ccVertex2F vertices[segments + 1];
+
+ float t = 0;
+ for(NSUInteger i = 0; i < segments; i++)
+ {
+ GLfloat x = powf(1 - t, 3) * origin.x + 3.0f * powf(1 - t, 2) * t * control1.x + 3.0f * (1 - t) * t * t * control2.x + t * t * t * destination.x;
+ GLfloat y = powf(1 - t, 3) * origin.y + 3.0f * powf(1 - t, 2) * t * control1.y + 3.0f * (1 - t) * t * t * control2.y + t * t * t * destination.y;
+ vertices[i] = (ccVertex2F) {x * CC_CONTENT_SCALE_FACTOR(), y * CC_CONTENT_SCALE_FACTOR() };
+ t += 1.0f / segments;
+ }
+ vertices[segments] = (ccVertex2F) {destination.x * CC_CONTENT_SCALE_FACTOR(), destination.y * CC_CONTENT_SCALE_FACTOR() };
+
+ // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Needed states: GL_VERTEX_ARRAY,
+ // Unneeded states: GL_TEXTURE_2D, GL_TEXTURE_COORD_ARRAY, GL_COLOR_ARRAY
+ glDisable(GL_TEXTURE_2D);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_COLOR_ARRAY);
+
+ glVertexPointer(2, GL_FLOAT, 0, vertices);
+ glDrawArrays(GL_LINE_STRIP, 0, (GLsizei) segments + 1);
+
+ // restore default state
+ glEnableClientState(GL_COLOR_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_2D);
+}
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 On-Core
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "Platforms/CCGL.h"
+#import <Foundation/Foundation.h>
+
+@class CCTexture2D;
+
+/** FBO class that grabs the the contents of the screen */
+@interface CCGrabber : NSObject
+{
+ GLuint fbo;
+ GLint oldFBO;
+}
+
+-(void)grab:(CCTexture2D*)texture;
+-(void)beforeRender:(CCTexture2D*)texture;
+-(void)afterRender:(CCTexture2D*)texture;
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 On-Core
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "Platforms/CCGL.h"
+#import "CCGrabber.h"
+#import "ccMacros.h"
+#import "CCTexture2D.h"
+#import "Support/OpenGL_Internal.h"
+
+@implementation CCGrabber
+
+-(id) init
+{
+ if(( self = [super init] )) {
+ // generate FBO
+ ccglGenFramebuffers(1, &fbo);
+ }
+ return self;
+}
+
+-(void)grab:(CCTexture2D*)texture
+{
+ glGetIntegerv(CC_GL_FRAMEBUFFER_BINDING, &oldFBO);
+
+ // bind
+ ccglBindFramebuffer(CC_GL_FRAMEBUFFER, fbo);
+
+ // associate texture with FBO
+ ccglFramebufferTexture2D(CC_GL_FRAMEBUFFER, CC_GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.name, 0);
+
+ // check if it worked (probably worth doing :) )
+ GLuint status = ccglCheckFramebufferStatus(CC_GL_FRAMEBUFFER);
+ if (status != CC_GL_FRAMEBUFFER_COMPLETE)
+ [NSException raise:@"Frame Grabber" format:@"Could not attach texture to framebuffer"];
+
+ ccglBindFramebuffer(CC_GL_FRAMEBUFFER, oldFBO);
+}
+
+-(void)beforeRender:(CCTexture2D*)texture
+{
+ glGetIntegerv(CC_GL_FRAMEBUFFER_BINDING, &oldFBO);
+ ccglBindFramebuffer(CC_GL_FRAMEBUFFER, fbo);
+
+ // BUG XXX: doesn't work with RGB565.
+
+
+ glClearColor(0,0,0,0);
+
+ // BUG #631: To fix #631, uncomment the lines with #631
+ // Warning: But it CCGrabber won't work with 2 effects at the same time
+// glClearColor(0.0f,0.0f,0.0f,1.0f); // #631
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+// glColorMask(TRUE, TRUE, TRUE, FALSE); // #631
+
+}
+
+-(void)afterRender:(CCTexture2D*)texture
+{
+ ccglBindFramebuffer(CC_GL_FRAMEBUFFER, oldFBO);
+// glColorMask(TRUE, TRUE, TRUE, TRUE); // #631
+}
+
+- (void) dealloc
+{
+ CCLOGINFO(@"cocos2d: deallocing %@", self);
+ ccglDeleteFramebuffers(1, &fbo);
+ [super dealloc];
+}
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 On-Core
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import <Foundation/Foundation.h>
+
+#import "CCNode.h"
+#import "CCCamera.h"
+#import "ccTypes.h"
+
+@class CCTexture2D;
+@class CCGrabber;
+
+/** Base class for other
+ */
+@interface CCGridBase : NSObject
+{
+ BOOL active_;
+ int reuseGrid_;
+ ccGridSize gridSize_;
+ CCTexture2D *texture_;
+ CGPoint step_;
+ CCGrabber *grabber_;
+ BOOL isTextureFlipped_;
+}
+
+/** wheter or not the grid is active */
+@property (nonatomic,readwrite) BOOL active;
+/** number of times that the grid will be reused */
+@property (nonatomic,readwrite) int reuseGrid;
+/** size of the grid */
+@property (nonatomic,readonly) ccGridSize gridSize;
+/** pixels between the grids */
+@property (nonatomic,readwrite) CGPoint step;
+/** texture used */
+@property (nonatomic, retain) CCTexture2D *texture;
+/** grabber used */
+@property (nonatomic, retain) CCGrabber *grabber;
+/** is texture flipped */
+@property (nonatomic, readwrite) BOOL isTextureFlipped;
+
++(id) gridWithSize:(ccGridSize)gridSize texture:(CCTexture2D*)texture flippedTexture:(BOOL)flipped;
++(id) gridWithSize:(ccGridSize)gridSize;
+
+-(id) initWithSize:(ccGridSize)gridSize texture:(CCTexture2D*)texture flippedTexture:(BOOL)flipped;
+-(id)initWithSize:(ccGridSize)gridSize;
+-(void)beforeDraw;
+-(void)afterDraw:(CCNode*)target;
+-(void)blit;
+-(void)reuse;
+
+-(void)calculateVertexPoints;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/**
+ CCGrid3D is a 3D grid implementation. Each vertex has 3 dimensions: x,y,z
+ */
+@interface CCGrid3D : CCGridBase
+{
+ GLvoid *texCoordinates;
+ GLvoid *vertices;
+ GLvoid *originalVertices;
+ GLushort *indices;
+}
+
+/** returns the vertex at a given position */
+-(ccVertex3F)vertex:(ccGridSize)pos;
+/** returns the original (non-transformed) vertex at a given position */
+-(ccVertex3F)originalVertex:(ccGridSize)pos;
+/** sets a new vertex at a given position */
+-(void)setVertex:(ccGridSize)pos vertex:(ccVertex3F)vertex;
+
+@end
+
+////////////////////////////////////////////////////////////
+
+/**
+ CCTiledGrid3D is a 3D grid implementation. It differs from Grid3D in that
+ the tiles can be separated from the grid.
+*/
+@interface CCTiledGrid3D : CCGridBase
+{
+ GLvoid *texCoordinates;
+ GLvoid *vertices;
+ GLvoid *originalVertices;
+ GLushort *indices;
+}
+
+/** returns the tile at the given position */
+-(ccQuad3)tile:(ccGridSize)pos;
+/** returns the original tile (untransformed) at the given position */
+-(ccQuad3)originalTile:(ccGridSize)pos;
+/** sets a new tile */
+-(void)setTile:(ccGridSize)pos coords:(ccQuad3)coords;
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 On-Core
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import <Availability.h>
+
+#import "ccMacros.h"
+#import "CCGrid.h"
+#import "CCTexture2D.h"
+#import "CCDirector.h"
+#import "CCGrabber.h"
+
+#import "Platforms/CCGL.h"
+#import "Support/CGPointExtension.h"
+#import "Support/ccUtils.h"
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import "Platforms/iOS/CCDirectorIOS.h"
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#pragma mark -
+#pragma mark CCGridBase
+
+@implementation CCGridBase
+
+@synthesize reuseGrid = reuseGrid_;
+@synthesize texture = texture_;
+@synthesize grabber = grabber_;
+@synthesize gridSize = gridSize_;
+@synthesize step = step_;
+
++(id) gridWithSize:(ccGridSize)gridSize texture:(CCTexture2D*)texture flippedTexture:(BOOL)flipped
+{
+ return [[[self alloc] initWithSize:gridSize texture:texture flippedTexture:flipped] autorelease];
+}
+
++(id) gridWithSize:(ccGridSize)gridSize
+{
+ return [[(CCGridBase*)[self alloc] initWithSize:gridSize] autorelease];
+}
+
+-(id) initWithSize:(ccGridSize)gridSize texture:(CCTexture2D*)texture flippedTexture:(BOOL)flipped
+{
+ if( (self=[super init]) ) {
+
+ active_ = NO;
+ reuseGrid_ = 0;
+ gridSize_ = gridSize;
+
+ self.texture = texture;
+ isTextureFlipped_ = flipped;
+
+ CGSize texSize = [texture_ contentSizeInPixels];
+ step_.x = texSize.width / gridSize_.x;
+ step_.y = texSize.height / gridSize_.y;
+
+ grabber_ = [[CCGrabber alloc] init];
+ [grabber_ grab:texture_];
+
+ [self calculateVertexPoints];
+ }
+ return self;
+}
+
+-(id)initWithSize:(ccGridSize)gSize
+{
+ CCDirector *director = [CCDirector sharedDirector];
+ CGSize s = [director winSizeInPixels];
+
+ unsigned long POTWide = ccNextPOT(s.width);
+ unsigned long POTHigh = ccNextPOT(s.height);
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+ EAGLView *glview = [[CCDirector sharedDirector] openGLView];
+ NSString *pixelFormat = [glview pixelFormat];
+
+ CCTexture2DPixelFormat format = [pixelFormat isEqualToString: kEAGLColorFormatRGB565] ? kCCTexture2DPixelFormat_RGB565 : kCCTexture2DPixelFormat_RGBA8888;
+#else
+ CCTexture2DPixelFormat format = kCCTexture2DPixelFormat_RGBA8888;
+#endif
+
+ void *data = calloc((int)(POTWide * POTHigh * 4), 1);
+ if( ! data ) {
+ CCLOG(@"cocos2d: CCGrid: not enough memory");
+ [self release];
+ return nil;
+ }
+
+ CCTexture2D *texture = [[CCTexture2D alloc] initWithData:data pixelFormat:format pixelsWide:POTWide pixelsHigh:POTHigh contentSize:s];
+ free( data );
+
+ if( ! texture ) {
+ CCLOG(@"cocos2d: CCGrid: error creating texture");
+ [self release];
+ return nil;
+ }
+
+ self = [self initWithSize:gSize texture:texture flippedTexture:NO];
+
+ [texture release];
+
+ return self;
+}
+- (NSString*) description
+{
+ return [NSString stringWithFormat:@"<%@ = %08X | Dimensions = %ix%i>", [self class], self, gridSize_.x, gridSize_.y];
+}
+
+- (void) dealloc
+{
+ CCLOGINFO(@"cocos2d: deallocing %@", self);
+
+ [self setActive: NO];
+
+ [texture_ release];
+ [grabber_ release];
+ [super dealloc];
+}
+
+// properties
+-(BOOL) active
+{
+ return active_;
+}
+
+-(void) setActive:(BOOL)active
+{
+ active_ = active;
+ if( ! active ) {
+ CCDirector *director = [CCDirector sharedDirector];
+ ccDirectorProjection proj = [director projection];
+ [director setProjection:proj];
+ }
+}
+
+-(BOOL) isTextureFlipped
+{
+ return isTextureFlipped_;
+}
+
+-(void) setIsTextureFlipped:(BOOL)flipped
+{
+ if( isTextureFlipped_ != flipped ) {
+ isTextureFlipped_ = flipped;
+ [self calculateVertexPoints];
+ }
+}
+
+// This routine can be merged with Director
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+-(void)applyLandscape
+{
+ CCDirector *director = [CCDirector sharedDirector];
+
+ CGSize winSize = [director displaySizeInPixels];
+ float w = winSize.width / 2;
+ float h = winSize.height / 2;
+
+ ccDeviceOrientation orientation = [director deviceOrientation];
+
+ switch (orientation) {
+ case CCDeviceOrientationLandscapeLeft:
+ glTranslatef(w,h,0);
+ glRotatef(-90,0,0,1);
+ glTranslatef(-h,-w,0);
+ break;
+ case CCDeviceOrientationLandscapeRight:
+ glTranslatef(w,h,0);
+ glRotatef(90,0,0,1);
+ glTranslatef(-h,-w,0);
+ break;
+ case CCDeviceOrientationPortraitUpsideDown:
+ glTranslatef(w,h,0);
+ glRotatef(180,0,0,1);
+ glTranslatef(-w,-h,0);
+ break;
+ default:
+ break;
+ }
+}
+#endif
+
+-(void)set2DProjection
+{
+ CGSize winSize = [[CCDirector sharedDirector] winSizeInPixels];
+
+ glLoadIdentity();
+ glViewport(0, 0, winSize.width, winSize.height);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ ccglOrtho(0, winSize.width, 0, winSize.height, -1024, 1024);
+ glMatrixMode(GL_MODELVIEW);
+}
+
+// This routine can be merged with Director
+-(void)set3DProjection
+{
+ CCDirector *director = [CCDirector sharedDirector];
+
+ CGSize winSize = [director displaySizeInPixels];
+
+ glViewport(0, 0, winSize.width, winSize.height);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ gluPerspective(60, (GLfloat)winSize.width/winSize.height, 0.5f, 1500.0f);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ gluLookAt( winSize.width/2, winSize.height/2, [director getZEye],
+ winSize.width/2, winSize.height/2, 0,
+ 0.0f, 1.0f, 0.0f
+ );
+}
+
+-(void)beforeDraw
+{
+ [self set2DProjection];
+ [grabber_ beforeRender:texture_];
+}
+
+-(void)afterDraw:(CCNode *)target
+{
+ [grabber_ afterRender:texture_];
+
+ [self set3DProjection];
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+ [self applyLandscape];
+#endif
+
+ if( target.camera.dirty ) {
+
+ CGPoint offset = [target anchorPointInPixels];
+
+ //
+ // XXX: Camera should be applied in the AnchorPoint
+ //
+ ccglTranslate(offset.x, offset.y, 0);
+ [target.camera locate];
+ ccglTranslate(-offset.x, -offset.y, 0);
+ }
+
+ glBindTexture(GL_TEXTURE_2D, texture_.name);
+
+ [self blit];
+}
+
+-(void)blit
+{
+ [NSException raise:@"GridBase" format:@"Abstract class needs implementation"];
+}
+
+-(void)reuse
+{
+ [NSException raise:@"GridBase" format:@"Abstract class needs implementation"];
+}
+
+-(void)calculateVertexPoints
+{
+ [NSException raise:@"GridBase" format:@"Abstract class needs implementation"];
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark CCGrid3D
+@implementation CCGrid3D
+
+-(void)dealloc
+{
+ free(texCoordinates);
+ free(vertices);
+ free(indices);
+ free(originalVertices);
+ [super dealloc];
+}
+
+-(void)blit
+{
+ NSInteger n = gridSize_.x * gridSize_.y;
+
+ // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Unneeded states: GL_COLOR_ARRAY
+ glDisableClientState(GL_COLOR_ARRAY);
+
+ glVertexPointer(3, GL_FLOAT, 0, vertices);
+ glTexCoordPointer(2, GL_FLOAT, 0, texCoordinates);
+ glDrawElements(GL_TRIANGLES, (GLsizei) n*6, GL_UNSIGNED_SHORT, indices);
+
+ // restore GL default state
+ glEnableClientState(GL_COLOR_ARRAY);
+}
+
+-(void)calculateVertexPoints
+{
+ float width = (float)texture_.pixelsWide;
+ float height = (float)texture_.pixelsHigh;
+ float imageH = texture_.contentSizeInPixels.height;
+
+ int x, y, i;
+
+ vertices = malloc((gridSize_.x+1)*(gridSize_.y+1)*sizeof(ccVertex3F));
+ originalVertices = malloc((gridSize_.x+1)*(gridSize_.y+1)*sizeof(ccVertex3F));
+ texCoordinates = malloc((gridSize_.x+1)*(gridSize_.y+1)*sizeof(CGPoint));
+ indices = malloc(gridSize_.x*gridSize_.y*sizeof(GLushort)*6);
+
+ float *vertArray = (float*)vertices;
+ float *texArray = (float*)texCoordinates;
+ GLushort *idxArray = (GLushort *)indices;
+
+ for( x = 0; x < gridSize_.x; x++ )
+ {
+ for( y = 0; y < gridSize_.y; y++ )
+ {
+ NSInteger idx = (y * gridSize_.x) + x;
+
+ float x1 = x * step_.x;
+ float x2 = x1 + step_.x;
+ float y1 = y * step_.y;
+ float y2 = y1 + step_.y;
+
+ GLushort a = x * (gridSize_.y+1) + y;
+ GLushort b = (x+1) * (gridSize_.y+1) + y;
+ GLushort c = (x+1) * (gridSize_.y+1) + (y+1);
+ GLushort d = x * (gridSize_.y+1) + (y+1);
+
+ GLushort tempidx[6] = { a, b, d, b, c, d };
+
+ memcpy(&idxArray[6*idx], tempidx, 6*sizeof(GLushort));
+
+ int l1[4] = { a*3, b*3, c*3, d*3 };
+ ccVertex3F e = {x1,y1,0};
+ ccVertex3F f = {x2,y1,0};
+ ccVertex3F g = {x2,y2,0};
+ ccVertex3F h = {x1,y2,0};
+
+ ccVertex3F l2[4] = { e, f, g, h };
+
+ int tex1[4] = { a*2, b*2, c*2, d*2 };
+ CGPoint tex2[4] = { ccp(x1, y1), ccp(x2, y1), ccp(x2, y2), ccp(x1, y2) };
+
+ for( i = 0; i < 4; i++ )
+ {
+ vertArray[ l1[i] ] = l2[i].x;
+ vertArray[ l1[i] + 1 ] = l2[i].y;
+ vertArray[ l1[i] + 2 ] = l2[i].z;
+
+ texArray[ tex1[i] ] = tex2[i].x / width;
+ if( isTextureFlipped_ )
+ texArray[ tex1[i] + 1 ] = (imageH - tex2[i].y) / height;
+ else
+ texArray[ tex1[i] + 1 ] = tex2[i].y / height;
+ }
+ }
+ }
+
+ memcpy(originalVertices, vertices, (gridSize_.x+1)*(gridSize_.y+1)*sizeof(ccVertex3F));
+}
+
+-(ccVertex3F)vertex:(ccGridSize)pos
+{
+ NSInteger index = (pos.x * (gridSize_.y+1) + pos.y) * 3;
+ float *vertArray = (float *)vertices;
+
+ ccVertex3F vert = { vertArray[index], vertArray[index+1], vertArray[index+2] };
+
+ return vert;
+}
+
+-(ccVertex3F)originalVertex:(ccGridSize)pos
+{
+ NSInteger index = (pos.x * (gridSize_.y+1) + pos.y) * 3;
+ float *vertArray = (float *)originalVertices;
+
+ ccVertex3F vert = { vertArray[index], vertArray[index+1], vertArray[index+2] };
+
+ return vert;
+}
+
+-(void)setVertex:(ccGridSize)pos vertex:(ccVertex3F)vertex
+{
+ NSInteger index = (pos.x * (gridSize_.y+1) + pos.y) * 3;
+ float *vertArray = (float *)vertices;
+ vertArray[index] = vertex.x;
+ vertArray[index+1] = vertex.y;
+ vertArray[index+2] = vertex.z;
+}
+
+-(void)reuse
+{
+ if ( reuseGrid_ > 0 )
+ {
+ memcpy(originalVertices, vertices, (gridSize_.x+1)*(gridSize_.y+1)*sizeof(ccVertex3F));
+ reuseGrid_--;
+ }
+}
+
+@end
+
+////////////////////////////////////////////////////////////
+
+#pragma mark -
+#pragma mark CCTiledGrid3D
+
+@implementation CCTiledGrid3D
+
+-(void)dealloc
+{
+ free(texCoordinates);
+ free(vertices);
+ free(indices);
+ free(originalVertices);
+ [super dealloc];
+}
+
+-(void)blit
+{
+ NSInteger n = gridSize_.x * gridSize_.y;
+
+ // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Unneeded states: GL_COLOR_ARRAY
+ glDisableClientState(GL_COLOR_ARRAY);
+
+ glVertexPointer(3, GL_FLOAT, 0, vertices);
+ glTexCoordPointer(2, GL_FLOAT, 0, texCoordinates);
+ glDrawElements(GL_TRIANGLES, (GLsizei) n*6, GL_UNSIGNED_SHORT, indices);
+
+ // restore default GL state
+ glEnableClientState(GL_COLOR_ARRAY);
+}
+
+-(void)calculateVertexPoints
+{
+ float width = (float)texture_.pixelsWide;
+ float height = (float)texture_.pixelsHigh;
+ float imageH = texture_.contentSizeInPixels.height;
+
+ NSInteger numQuads = gridSize_.x * gridSize_.y;
+
+ vertices = malloc(numQuads*12*sizeof(GLfloat));
+ originalVertices = malloc(numQuads*12*sizeof(GLfloat));
+ texCoordinates = malloc(numQuads*8*sizeof(GLfloat));
+ indices = malloc(numQuads*6*sizeof(GLushort));
+
+ float *vertArray = (float*)vertices;
+ float *texArray = (float*)texCoordinates;
+ GLushort *idxArray = (GLushort *)indices;
+
+ int x, y;
+
+ for( x = 0; x < gridSize_.x; x++ )
+ {
+ for( y = 0; y < gridSize_.y; y++ )
+ {
+ float x1 = x * step_.x;
+ float x2 = x1 + step_.x;
+ float y1 = y * step_.y;
+ float y2 = y1 + step_.y;
+
+ *vertArray++ = x1;
+ *vertArray++ = y1;
+ *vertArray++ = 0;
+ *vertArray++ = x2;
+ *vertArray++ = y1;
+ *vertArray++ = 0;
+ *vertArray++ = x1;
+ *vertArray++ = y2;
+ *vertArray++ = 0;
+ *vertArray++ = x2;
+ *vertArray++ = y2;
+ *vertArray++ = 0;
+
+ float newY1 = y1;
+ float newY2 = y2;
+
+ if( isTextureFlipped_ ) {
+ newY1 = imageH - y1;
+ newY2 = imageH - y2;
+ }
+
+ *texArray++ = x1 / width;
+ *texArray++ = newY1 / height;
+ *texArray++ = x2 / width;
+ *texArray++ = newY1 / height;
+ *texArray++ = x1 / width;
+ *texArray++ = newY2 / height;
+ *texArray++ = x2 / width;
+ *texArray++ = newY2 / height;
+ }
+ }
+
+ for( x = 0; x < numQuads; x++)
+ {
+ idxArray[x*6+0] = x*4+0;
+ idxArray[x*6+1] = x*4+1;
+ idxArray[x*6+2] = x*4+2;
+
+ idxArray[x*6+3] = x*4+1;
+ idxArray[x*6+4] = x*4+2;
+ idxArray[x*6+5] = x*4+3;
+ }
+
+ memcpy(originalVertices, vertices, numQuads*12*sizeof(GLfloat));
+}
+
+-(void)setTile:(ccGridSize)pos coords:(ccQuad3)coords
+{
+ NSInteger idx = (gridSize_.y * pos.x + pos.y) * 4 * 3;
+ float *vertArray = (float*)vertices;
+ memcpy(&vertArray[idx], &coords, sizeof(ccQuad3));
+}
+
+-(ccQuad3)originalTile:(ccGridSize)pos
+{
+ NSInteger idx = (gridSize_.y * pos.x + pos.y) * 4 * 3;
+ float *vertArray = (float*)originalVertices;
+
+ ccQuad3 ret;
+ memcpy(&ret, &vertArray[idx], sizeof(ccQuad3));
+
+ return ret;
+}
+
+-(ccQuad3)tile:(ccGridSize)pos
+{
+ NSInteger idx = (gridSize_.y * pos.x + pos.y) * 4 * 3;
+ float *vertArray = (float*)vertices;
+
+ ccQuad3 ret;
+ memcpy(&ret, &vertArray[idx], sizeof(ccQuad3));
+
+ return ret;
+}
+
+-(void)reuse
+{
+ if ( reuseGrid_ > 0 )
+ {
+ NSInteger numQuads = gridSize_.x * gridSize_.y;
+
+ memcpy(originalVertices, vertices, numQuads*12*sizeof(GLfloat));
+ reuseGrid_--;
+ }
+}
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCAtlasNode.h"
+#import "CCTextureAtlas.h"
+
+/** CCLabelAtlas is a subclass of CCAtlasNode.
+
+ It can be as a replacement of CCLabel since it is MUCH faster.
+
+ CCLabelAtlas versus CCLabel:
+ - CCLabelAtlas is MUCH faster than CCLabel
+ - CCLabelAtlas "characters" have a fixed height and width
+ - CCLabelAtlas "characters" can be anything you want since they are taken from an image file
+
+ A more flexible class is CCBitmapFontAtlas. It supports variable width characters and it also has a nice editor.
+ */
+@interface CCLabelAtlas : CCAtlasNode <CCLabelProtocol>
+{
+ // string to render
+ NSString *string_;
+
+ // the first char in the charmap
+ char mapStartChar;
+}
+
+
+/** creates the CCLabelAtlas with a string, a char map file(the atlas), the width and height of each element in points and the starting char of the atlas */
++(id) labelWithString:(NSString*) string charMapFile: (NSString*) charmapfile itemWidth:(int)w itemHeight:(int)h startCharMap:(char)c;
+
+/** creates the CCLabelAtlas with a string, a char map file(the atlas), the width and height of each element in points and the starting char of the atlas.
+ @deprecated Will be removed in 1.0.1. Use "labelWithString:" instead
+ */
++(id) labelAtlasWithString:(NSString*) string charMapFile: (NSString*) charmapfile itemWidth:(int)w itemHeight:(int)h startCharMap:(char)c DEPRECATED_ATTRIBUTE;
+
+/** initializes the CCLabelAtlas with a string, a char map file(the atlas), the width and height in points of each element and the starting char of the atlas */
+-(id) initWithString:(NSString*) string charMapFile: (NSString*) charmapfile itemWidth:(int)w itemHeight:(int)h startCharMap:(char)c;
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "ccConfig.h"
+#import "ccMacros.h"
+#import "CCDrawingPrimitives.h"
+#import "CCLabelAtlas.h"
+#import "Support/CGPointExtension.h"
+
+
+
+@implementation CCLabelAtlas
+
+#pragma mark CCLabelAtlas - Creation & Init
++(id) labelWithString:(NSString*)string charMapFile:(NSString*)charmapfile itemWidth:(int)w itemHeight:(int)h startCharMap:(char)c
+{
+ return [[[self alloc] initWithString:string charMapFile:charmapfile itemWidth:w itemHeight:h startCharMap:c] autorelease];
+}
+
+// XXX DEPRECATED. Remove it in 1.0.1
++(id) labelAtlasWithString:(NSString*) string charMapFile: (NSString*) charmapfile itemWidth:(int)w itemHeight:(int)h startCharMap:(char)c
+{
+ return [self labelWithString:string charMapFile:charmapfile itemWidth:w itemHeight:h startCharMap:c];
+}
+
+
+-(id) initWithString:(NSString*) theString charMapFile: (NSString*) charmapfile itemWidth:(int)w itemHeight:(int)h startCharMap:(char)c
+{
+
+ if ((self=[super initWithTileFile:charmapfile tileWidth:w tileHeight:h itemsToRender:[theString length] ]) ) {
+
+ mapStartChar = c;
+ [self setString: theString];
+ }
+
+ return self;
+}
+
+-(void) dealloc
+{
+ [string_ release];
+
+ [super dealloc];
+}
+
+#pragma mark CCLabelAtlas - Atlas generation
+
+-(void) updateAtlasValues
+{
+ NSInteger n = [string_ length];
+
+ ccV3F_C4B_T2F_Quad quad;
+
+ const char *s = [string_ UTF8String];
+
+ CCTexture2D *texture = [textureAtlas_ texture];
+ float textureWide = [texture pixelsWide];
+ float textureHigh = [texture pixelsHigh];
+
+ for( NSUInteger i=0; i<n; i++) {
+ unsigned char a = s[i] - mapStartChar;
+ float row = (a % itemsPerRow_);
+ float col = (a / itemsPerRow_);
+
+#if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
+ // Issue #938. Don't use texStepX & texStepY
+ float left = (2*row*itemWidth_+1)/(2*textureWide);
+ float right = left+(itemWidth_*2-2)/(2*textureWide);
+ float top = (2*col*itemHeight_+1)/(2*textureHigh);
+ float bottom = top+(itemHeight_*2-2)/(2*textureHigh);
+#else
+ float left = row*itemWidth_/textureWide;
+ float right = left+itemWidth_/textureWide;
+ float top = col*itemHeight_/textureHigh;
+ float bottom = top+itemHeight_/textureHigh;
+#endif // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
+
+ quad.tl.texCoords.u = left;
+ quad.tl.texCoords.v = top;
+ quad.tr.texCoords.u = right;
+ quad.tr.texCoords.v = top;
+ quad.bl.texCoords.u = left;
+ quad.bl.texCoords.v = bottom;
+ quad.br.texCoords.u = right;
+ quad.br.texCoords.v = bottom;
+
+ quad.bl.vertices.x = (int) (i * itemWidth_);
+ quad.bl.vertices.y = 0;
+ quad.bl.vertices.z = 0.0f;
+ quad.br.vertices.x = (int)(i * itemWidth_ + itemWidth_);
+ quad.br.vertices.y = 0;
+ quad.br.vertices.z = 0.0f;
+ quad.tl.vertices.x = (int)(i * itemWidth_);
+ quad.tl.vertices.y = (int)(itemHeight_);
+ quad.tl.vertices.z = 0.0f;
+ quad.tr.vertices.x = (int)(i * itemWidth_ + itemWidth_);
+ quad.tr.vertices.y = (int)(itemHeight_);
+ quad.tr.vertices.z = 0.0f;
+
+ [textureAtlas_ updateQuad:&quad atIndex:i];
+ }
+}
+
+#pragma mark CCLabelAtlas - CCLabelProtocol
+
+- (void) setString:(NSString*) newString
+{
+ NSUInteger len = [newString length];
+ if( len > textureAtlas_.capacity )
+ [textureAtlas_ resizeCapacity:len];
+
+ [string_ release];
+ string_ = [newString copy];
+ [self updateAtlasValues];
+
+ CGSize s;
+ s.width = len * itemWidth_;
+ s.height = itemHeight_;
+ [self setContentSizeInPixels:s];
+}
+
+-(NSString*) string
+{
+ return string_;
+}
+
+#pragma mark CCLabelAtlas - draw
+
+// XXX: overriding draw from AtlasNode
+- (void) draw
+{
+ // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Unneeded states: GL_COLOR_ARRAY
+ glDisableClientState(GL_COLOR_ARRAY);
+
+ glColor4ub( color_.r, color_.g, color_.b, opacity_);
+
+ BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST;
+ if( newBlend )
+ glBlendFunc( blendFunc_.src, blendFunc_.dst );
+
+ [textureAtlas_ drawNumberOfQuads:string_.length fromIndex:0];
+
+ if( newBlend )
+ glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
+
+ // is this chepear than saving/restoring color state ?
+ // XXX: There is no need to restore the color to (255,255,255,255). Objects should use the color
+ // XXX: that they need
+// glColor4ub( 255, 255, 255, 255);
+
+ // Restore Default GL state. Enable GL_COLOR_ARRAY
+ glEnableClientState(GL_COLOR_ARRAY);
+
+
+#if CC_LABELATLAS_DEBUG_DRAW
+ CGSize s = [self contentSize];
+ CGPoint vertices[4]={
+ ccp(0,0),ccp(s.width,0),
+ ccp(s.width,s.height),ccp(0,s.height),
+ };
+ ccDrawPoly(vertices, 4, YES);
+#endif // CC_LABELATLAS_DEBUG_DRAW
+
+}
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * Portions of this code are based and inspired on:
+ * http://www.71squared.co.uk/2009/04/iphone-game-programming-tutorial-4-bitmap-font-class
+ * by Michael Daley
+
+ * Use any of these editors to generate bitmap font atlas:
+ * http://www.n4te.com/hiero/hiero.jnlp
+ * http://slick.cokeandcode.com/demos/hiero.jnlp
+ * http://www.angelcode.com/products/bmfont/
+ *
+ */
+
+#import "CCSpriteBatchNode.h"
+#import "Support/uthash.h"
+
+struct _KerningHashElement;
+
+/** @struct ccBMFontDef
+ BMFont definition
+ */
+typedef struct _BMFontDef {
+ //! ID of the character
+ unsigned int charID;
+ //! origin and size of the font
+ CGRect rect;
+ //! The X amount the image should be offset when drawing the image (in pixels)
+ int xOffset;
+ //! The Y amount the image should be offset when drawing the image (in pixels)
+ int yOffset;
+ //! The amount to move the current position after drawing the character (in pixels)
+ int xAdvance;
+} ccBMFontDef;
+
+/** @struct ccBMFontPadding
+ BMFont padding
+ @since v0.8.2
+ */
+typedef struct _BMFontPadding {
+ /// padding left
+ int left;
+ /// padding top
+ int top;
+ /// padding right
+ int right;
+ /// padding bottom
+ int bottom;
+} ccBMFontPadding;
+
+enum {
+ // how many characters are supported
+ kCCBMFontMaxChars = 2048, //256,
+};
+
+/** CCBMFontConfiguration has parsed configuration of the the .fnt file
+ @since v0.8
+ */
+@interface CCBMFontConfiguration : NSObject
+{
+// XXX: Creating a public interface so that the bitmapFontArray[] is accesible
+@public
+ // The characters building up the font
+ ccBMFontDef BMFontArray_[kCCBMFontMaxChars];
+
+ // FNTConfig: Common Height
+ NSUInteger commonHeight_;
+
+ // Padding
+ ccBMFontPadding padding_;
+
+ // atlas name
+ NSString *atlasName_;
+
+ // values for kerning
+ struct _KerningHashElement *kerningDictionary_;
+}
+
+/** allocates a CCBMFontConfiguration with a FNT file */
++(id) configurationWithFNTFile:(NSString*)FNTfile;
+/** initializes a CCBMFontConfiguration with a FNT file */
+-(id) initWithFNTfile:(NSString*)FNTfile;
+@end
+
+
+/** CCLabelBMFont is a subclass of CCSpriteBatchNode
+
+ Features:
+ - Treats each character like a CCSprite. This means that each individual character can be:
+ - rotated
+ - scaled
+ - translated
+ - tinted
+ - chage the opacity
+ - It can be used as part of a menu item.
+ - anchorPoint can be used to align the "label"
+ - Supports AngelCode text format
+
+ Limitations:
+ - All inner characters are using an anchorPoint of (0.5f, 0.5f) and it is not recommend to change it
+ because it might affect the rendering
+
+ CCLabelBMFont implements the protocol CCLabelProtocol, like CCLabel and CCLabelAtlas.
+ CCLabelBMFont has the flexibility of CCLabel, the speed of CCLabelAtlas and all the features of CCSprite.
+ If in doubt, use CCLabelBMFont instead of CCLabelAtlas / CCLabel.
+
+ Supported editors:
+ - http://www.n4te.com/hiero/hiero.jnlp
+ - http://slick.cokeandcode.com/demos/hiero.jnlp
+ - http://www.angelcode.com/products/bmfont/
+
+ @since v0.8
+ */
+
+@interface CCLabelBMFont : CCSpriteBatchNode <CCLabelProtocol, CCRGBAProtocol>
+{
+ // string to render
+ NSString *string_;
+
+ CCBMFontConfiguration *configuration_;
+
+ // texture RGBA
+ GLubyte opacity_;
+ ccColor3B color_;
+ BOOL opacityModifyRGB_;
+}
+
+/** Purges the cached data.
+ Removes from memory the cached configurations and the atlas name dictionary.
+ @since v0.99.3
+ */
++(void) purgeCachedData;
+
+/** conforms to CCRGBAProtocol protocol */
+@property (nonatomic,readwrite) GLubyte opacity;
+/** conforms to CCRGBAProtocol protocol */
+@property (nonatomic,readwrite) ccColor3B color;
+
+
+/** creates a BMFont label with an initial string and the FNT file */
++(id) labelWithString:(NSString*)string fntFile:(NSString*)fntFile;
+
+/** creates a BMFont label with an initial string and the FNT file
+ @deprecated Will be removed in 1.0.1. Use "labelWithString" instead.
+ */
++(id) bitmapFontAtlasWithString:(NSString*)string fntFile:(NSString*)fntFile DEPRECATED_ATTRIBUTE;
+
+/** init a BMFont label with an initial string and the FNT file */
+-(id) initWithString:(NSString*)string fntFile:(NSString*)fntFile;
+
+/** updates the font chars based on the string to render */
+-(void) createFontChars;
+@end
+
+/** Free function that parses a FNT file a place it on the cache
+*/
+CCBMFontConfiguration * FNTConfigLoadFile( NSString *file );
+/** Purges the FNT config cache
+ */
+void FNTConfigRemoveCache( void );
+
+
+
+/** CCBitmapFontAtlas
+ @deprecated Use CCLabelBMFont instead. Will be removed 1.0.1
+ */
+DEPRECATED_ATTRIBUTE @interface CCBitmapFontAtlas : CCLabelBMFont
+@end
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * Portions of this code are based and inspired on:
+ * http://www.71squared.co.uk/2009/04/iphone-game-programming-tutorial-4-bitmap-font-class
+ * by Michael Daley
+ *
+ *
+ * Use any of these editors to generate bitmap font atlas:
+ * http://www.n4te.com/hiero/hiero.jnlp
+ * http://slick.cokeandcode.com/demos/hiero.jnlp
+ * http://www.angelcode.com/products/bmfont/
+ */
+
+#import "ccConfig.h"
+#import "CCLabelBMFont.h"
+#import "CCSprite.h"
+#import "CCDrawingPrimitives.h"
+#import "CCConfiguration.h"
+#import "Support/CCFileUtils.h"
+#import "Support/CGPointExtension.h"
+#import "Support/uthash.h"
+
+#pragma mark -
+#pragma mark FNTConfig Cache - free functions
+
+NSMutableDictionary *configurations = nil;
+CCBMFontConfiguration* FNTConfigLoadFile( NSString *fntFile)
+{
+ CCBMFontConfiguration *ret = nil;
+
+ if( configurations == nil )
+ configurations = [[NSMutableDictionary dictionaryWithCapacity:3] retain];
+
+ ret = [configurations objectForKey:fntFile];
+ if( ret == nil ) {
+ ret = [CCBMFontConfiguration configurationWithFNTFile:fntFile];
+ [configurations setObject:ret forKey:fntFile];
+ }
+
+ return ret;
+}
+
+void FNTConfigRemoveCache( void )
+{
+ [configurations removeAllObjects];
+}
+
+#pragma mark - Hash Element
+
+// Equal function for targetSet.
+typedef struct _KerningHashElement
+{
+ int key; // key for the hash. 16-bit for 1st element, 16-bit for 2nd element
+ int amount;
+ UT_hash_handle hh;
+} tKerningHashElement;
+
+#pragma mark -
+#pragma mark BitmapFontConfiguration
+
+
+@interface CCBMFontConfiguration (Private)
+-(void) parseConfigFile:(NSString*)controlFile;
+-(void) parseCharacterDefinition:(NSString*)line charDef:(ccBMFontDef*)characterDefinition;
+-(void) parseInfoArguments:(NSString*)line;
+-(void) parseCommonArguments:(NSString*)line;
+-(void) parseImageFileName:(NSString*)line fntFile:(NSString*)fntFile;
+-(void) parseKerningCapacity:(NSString*)line;
+-(void) parseKerningEntry:(NSString*)line;
+-(void) purgeKerningDictionary;
+@end
+
+@implementation CCBMFontConfiguration
+
++(id) configurationWithFNTFile:(NSString*)FNTfile
+{
+ return [[[self alloc] initWithFNTfile:FNTfile] autorelease];
+}
+
+-(id) initWithFNTfile:(NSString*)fntFile
+{
+ if((self=[super init])) {
+
+ kerningDictionary_ = NULL;
+
+ [self parseConfigFile:fntFile];
+ }
+ return self;
+}
+
+- (void) dealloc
+{
+ CCLOGINFO( @"cocos2d: deallocing %@", self);
+ [self purgeKerningDictionary];
+ [atlasName_ release];
+ [super dealloc];
+}
+
+- (NSString*) description
+{
+ return [NSString stringWithFormat:@"<%@ = %08X | Kernings:%d | Image = %@>", [self class], self,
+ HASH_COUNT(kerningDictionary_),
+ atlasName_];
+}
+
+
+-(void) purgeKerningDictionary
+{
+ tKerningHashElement *current;
+
+ while(kerningDictionary_) {
+ current = kerningDictionary_;
+ HASH_DEL(kerningDictionary_,current);
+ free(current);
+ }
+}
+
+- (void)parseConfigFile:(NSString*)fntFile
+{
+ NSString *fullpath = [CCFileUtils fullPathFromRelativePath:fntFile];
+ NSError *error;
+ NSString *contents = [NSString stringWithContentsOfFile:fullpath encoding:NSUTF8StringEncoding error:&error];
+
+ NSAssert1( contents, @"cocos2d: Error parsing FNTfile: %@", error);
+
+
+ // Move all lines in the string, which are denoted by \n, into an array
+ NSArray *lines = [[NSArray alloc] initWithArray:[contents componentsSeparatedByString:@"\n"]];
+
+ // Create an enumerator which we can use to move through the lines read from the control file
+ NSEnumerator *nse = [lines objectEnumerator];
+
+ // Create a holder for each line we are going to work with
+ NSString *line;
+
+ // Loop through all the lines in the lines array processing each one
+ while( (line = [nse nextObject]) ) {
+ // parse spacing / padding
+ if([line hasPrefix:@"info face"]) {
+ // XXX: info parsing is incomplete
+ // Not needed for the Hiero editors, but needed for the AngelCode editor
+// [self parseInfoArguments:line];
+ }
+ // Check to see if the start of the line is something we are interested in
+ else if([line hasPrefix:@"common lineHeight"]) {
+ [self parseCommonArguments:line];
+ }
+ else if([line hasPrefix:@"page id"]) {
+ [self parseImageFileName:line fntFile:fntFile];
+ }
+ else if([line hasPrefix:@"chars c"]) {
+ // Ignore this line
+ }
+ else if([line hasPrefix:@"char"]) {
+ // Parse the current line and create a new CharDef
+ ccBMFontDef characterDefinition;
+ [self parseCharacterDefinition:line charDef:&characterDefinition];
+
+ // Add the CharDef returned to the charArray
+ BMFontArray_[ characterDefinition.charID ] = characterDefinition;
+ }
+ else if([line hasPrefix:@"kernings count"]) {
+ [self parseKerningCapacity:line];
+ }
+ else if([line hasPrefix:@"kerning first"]) {
+ [self parseKerningEntry:line];
+ }
+ }
+ // Finished with lines so release it
+ [lines release];
+}
+
+-(void) parseImageFileName:(NSString*)line fntFile:(NSString*)fntFile
+{
+ NSString *propertyValue = nil;
+
+ // Break the values for this line up using =
+ NSArray *values = [line componentsSeparatedByString:@"="];
+
+ // Get the enumerator for the array of components which has been created
+ NSEnumerator *nse = [values objectEnumerator];
+
+ // We need to move past the first entry in the array before we start assigning values
+ [nse nextObject];
+
+ // page ID. Sanity check
+ propertyValue = [nse nextObject];
+ NSAssert( [propertyValue intValue] == 0, @"XXX: BitmapFontAtlas only supports 1 page");
+
+ // file
+ propertyValue = [nse nextObject];
+ NSArray *array = [propertyValue componentsSeparatedByString:@"\""];
+ propertyValue = [array objectAtIndex:1];
+ NSAssert(propertyValue,@"BitmapFontAtlas file could not be found");
+
+ // Supports subdirectories
+ NSString *dir = [fntFile stringByDeletingLastPathComponent];
+ atlasName_ = [dir stringByAppendingPathComponent:propertyValue];
+
+ [atlasName_ retain];
+}
+
+-(void) parseInfoArguments:(NSString*)line
+{
+ //
+ // possible lines to parse:
+ // info face="Script" size=32 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=1,4,3,2 spacing=0,0 outline=0
+ // info face="Cracked" size=36 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1
+ //
+ NSArray *values = [line componentsSeparatedByString:@"="];
+ NSEnumerator *nse = [values objectEnumerator];
+ NSString *propertyValue = nil;
+
+ // We need to move past the first entry in the array before we start assigning values
+ [nse nextObject];
+
+ // face (ignore)
+ [nse nextObject];
+
+ // size (ignore)
+ [nse nextObject];
+
+ // bold (ignore)
+ [nse nextObject];
+
+ // italic (ignore)
+ [nse nextObject];
+
+ // charset (ignore)
+ [nse nextObject];
+
+ // unicode (ignore)
+ [nse nextObject];
+
+ // strechH (ignore)
+ [nse nextObject];
+
+ // smooth (ignore)
+ [nse nextObject];
+
+ // aa (ignore)
+ [nse nextObject];
+
+ // padding (ignore)
+ propertyValue = [nse nextObject];
+ {
+
+ NSArray *paddingValues = [propertyValue componentsSeparatedByString:@","];
+ NSEnumerator *paddingEnum = [paddingValues objectEnumerator];
+ // padding top
+ propertyValue = [paddingEnum nextObject];
+ padding_.top = [propertyValue intValue];
+
+ // padding right
+ propertyValue = [paddingEnum nextObject];
+ padding_.right = [propertyValue intValue];
+
+ // padding bottom
+ propertyValue = [paddingEnum nextObject];
+ padding_.bottom = [propertyValue intValue];
+
+ // padding left
+ propertyValue = [paddingEnum nextObject];
+ padding_.left = [propertyValue intValue];
+
+ CCLOG(@"cocos2d: padding: %d,%d,%d,%d", padding_.left, padding_.top, padding_.right, padding_.bottom);
+ }
+
+ // spacing (ignore)
+ [nse nextObject];
+}
+
+-(void) parseCommonArguments:(NSString*)line
+{
+ //
+ // line to parse:
+ // common lineHeight=104 base=26 scaleW=1024 scaleH=512 pages=1 packed=0
+ //
+ NSArray *values = [line componentsSeparatedByString:@"="];
+ NSEnumerator *nse = [values objectEnumerator];
+ NSString *propertyValue = nil;
+
+ // We need to move past the first entry in the array before we start assigning values
+ [nse nextObject];
+
+ // Character ID
+ propertyValue = [nse nextObject];
+ commonHeight_ = [propertyValue intValue];
+
+ // base (ignore)
+ [nse nextObject];
+
+
+ // scaleW. sanity check
+ propertyValue = [nse nextObject];
+ NSAssert( [propertyValue intValue] <= [[CCConfiguration sharedConfiguration] maxTextureSize], @"CCLabelBMFont: page can't be larger than supported");
+
+ // scaleH. sanity check
+ propertyValue = [nse nextObject];
+ NSAssert( [propertyValue intValue] <= [[CCConfiguration sharedConfiguration] maxTextureSize], @"CCLabelBMFont: page can't be larger than supported");
+
+ // pages. sanity check
+ propertyValue = [nse nextObject];
+ NSAssert( [propertyValue intValue] == 1, @"CCBitfontAtlas: only supports 1 page");
+
+ // packed (ignore) What does this mean ??
+}
+- (void)parseCharacterDefinition:(NSString*)line charDef:(ccBMFontDef*)characterDefinition
+{
+ // Break the values for this line up using =
+ NSArray *values = [line componentsSeparatedByString:@"="];
+ NSEnumerator *nse = [values objectEnumerator];
+ NSString *propertyValue;
+
+ // We need to move past the first entry in the array before we start assigning values
+ [nse nextObject];
+
+ // Character ID
+ propertyValue = [nse nextObject];
+ propertyValue = [propertyValue substringToIndex: [propertyValue rangeOfString: @" "].location];
+ characterDefinition->charID = [propertyValue intValue];
+ NSAssert(characterDefinition->charID < kCCBMFontMaxChars, @"BitmpaFontAtlas: CharID bigger than supported");
+
+ // Character x
+ propertyValue = [nse nextObject];
+ characterDefinition->rect.origin.x = [propertyValue intValue];
+ // Character y
+ propertyValue = [nse nextObject];
+ characterDefinition->rect.origin.y = [propertyValue intValue];
+ // Character width
+ propertyValue = [nse nextObject];
+ characterDefinition->rect.size.width = [propertyValue intValue];
+ // Character height
+ propertyValue = [nse nextObject];
+ characterDefinition->rect.size.height = [propertyValue intValue];
+ // Character xoffset
+ propertyValue = [nse nextObject];
+ characterDefinition->xOffset = [propertyValue intValue];
+ // Character yoffset
+ propertyValue = [nse nextObject];
+ characterDefinition->yOffset = [propertyValue intValue];
+ // Character xadvance
+ propertyValue = [nse nextObject];
+ characterDefinition->xAdvance = [propertyValue intValue];
+}
+
+-(void) parseKerningCapacity:(NSString*) line
+{
+ // When using uthash there is not need to parse the capacity.
+
+// NSAssert(!kerningDictionary, @"dictionary already initialized");
+//
+// // Break the values for this line up using =
+// NSArray *values = [line componentsSeparatedByString:@"="];
+// NSEnumerator *nse = [values objectEnumerator];
+// NSString *propertyValue;
+//
+// // We need to move past the first entry in the array before we start assigning values
+// [nse nextObject];
+//
+// // count
+// propertyValue = [nse nextObject];
+// int capacity = [propertyValue intValue];
+//
+// if( capacity != -1 )
+// kerningDictionary = ccHashSetNew(capacity, targetSetEql);
+}
+
+-(void) parseKerningEntry:(NSString*) line
+{
+ NSArray *values = [line componentsSeparatedByString:@"="];
+ NSEnumerator *nse = [values objectEnumerator];
+ NSString *propertyValue;
+
+ // We need to move past the first entry in the array before we start assigning values
+ [nse nextObject];
+
+ // first
+ propertyValue = [nse nextObject];
+ int first = [propertyValue intValue];
+
+ // second
+ propertyValue = [nse nextObject];
+ int second = [propertyValue intValue];
+
+ // second
+ propertyValue = [nse nextObject];
+ int amount = [propertyValue intValue];
+
+ tKerningHashElement *element = calloc( sizeof( *element ), 1 );
+ element->amount = amount;
+ element->key = (first<<16) | (second&0xffff);
+ HASH_ADD_INT(kerningDictionary_,key, element);
+}
+
+@end
+
+#pragma mark -
+#pragma mark CCLabelBMFont
+
+@interface CCLabelBMFont (Private)
+-(NSString*) atlasNameFromFntFile:(NSString*)fntFile;
+
+-(int) kerningAmountForFirst:(unichar)first second:(unichar)second;
+
+@end
+
+@implementation CCLabelBMFont
+
+@synthesize opacity = opacity_, color = color_;
+
+#pragma mark BitmapFontAtlas - Purge Cache
++(void) purgeCachedData
+{
+ FNTConfigRemoveCache();
+}
+
+#pragma mark BitmapFontAtlas - Creation & Init
+
++(id) labelWithString:(NSString *)string fntFile:(NSString *)fntFile
+{
+ return [[[self alloc] initWithString:string fntFile:fntFile] autorelease];
+}
+
+// XXX - deprecated - Will be removed in 1.0.1
++(id) bitmapFontAtlasWithString:(NSString*)string fntFile:(NSString*)fntFile
+{
+ return [self labelWithString:string fntFile:fntFile];
+}
+
+-(id) initWithString:(NSString*)theString fntFile:(NSString*)fntFile
+{
+
+ [configuration_ release]; // allow re-init
+
+ configuration_ = FNTConfigLoadFile(fntFile);
+ [configuration_ retain];
+
+ NSAssert( configuration_, @"Error creating config for BitmapFontAtlas");
+
+
+ if ((self=[super initWithFile:configuration_->atlasName_ capacity:[theString length]])) {
+
+ opacity_ = 255;
+ color_ = ccWHITE;
+
+ contentSize_ = CGSizeZero;
+
+ opacityModifyRGB_ = [[textureAtlas_ texture] hasPremultipliedAlpha];
+
+ anchorPoint_ = ccp(0.5f, 0.5f);
+
+ [self setString:theString];
+ }
+
+ return self;
+}
+
+-(void) dealloc
+{
+ [string_ release];
+ [configuration_ release];
+ [super dealloc];
+}
+
+#pragma mark BitmapFontAtlas - Atlas generation
+
+-(int) kerningAmountForFirst:(unichar)first second:(unichar)second
+{
+ int ret = 0;
+ unsigned int key = (first<<16) | (second & 0xffff);
+
+ if( configuration_->kerningDictionary_ ) {
+ tKerningHashElement *element = NULL;
+ HASH_FIND_INT(configuration_->kerningDictionary_, &key, element);
+ if(element)
+ ret = element->amount;
+ }
+
+ return ret;
+}
+
+-(void) createFontChars
+{
+ NSInteger nextFontPositionX = 0;
+ NSInteger nextFontPositionY = 0;
+ unichar prev = -1;
+ NSInteger kerningAmount = 0;
+
+ CGSize tmpSize = CGSizeZero;
+
+ NSInteger longestLine = 0;
+ NSUInteger totalHeight = 0;
+
+ NSUInteger quantityOfLines = 1;
+
+ NSUInteger stringLen = [string_ length];
+ if( ! stringLen )
+ return;
+
+ // quantity of lines NEEDS to be calculated before parsing the lines,
+ // since the Y position needs to be calcualted before hand
+ for(NSUInteger i=0; i < stringLen-1;i++) {
+ unichar c = [string_ characterAtIndex:i];
+ if( c=='\n')
+ quantityOfLines++;
+ }
+
+ totalHeight = configuration_->commonHeight_ * quantityOfLines;
+ nextFontPositionY = -(configuration_->commonHeight_ - configuration_->commonHeight_*quantityOfLines);
+
+ for(NSUInteger i=0; i<stringLen; i++) {
+ unichar c = [string_ characterAtIndex:i];
+ NSAssert( c < kCCBMFontMaxChars, @"BitmapFontAtlas: character outside bounds");
+
+ if (c == '\n') {
+ nextFontPositionX = 0;
+ nextFontPositionY -= configuration_->commonHeight_;
+ continue;
+ }
+
+ kerningAmount = [self kerningAmountForFirst:prev second:c];
+
+ ccBMFontDef fontDef = configuration_->BMFontArray_[c];
+
+ CGRect rect = fontDef.rect;
+
+ CCSprite *fontChar;
+
+ fontChar = (CCSprite*) [self getChildByTag:i];
+ if( ! fontChar ) {
+ fontChar = [[CCSprite alloc] initWithBatchNode:self rectInPixels:rect];
+ [self addChild:fontChar z:0 tag:i];
+ [fontChar release];
+ }
+ else {
+ // reusing fonts
+ [fontChar setTextureRectInPixels:rect rotated:NO untrimmedSize:rect.size];
+
+ // restore to default in case they were modified
+ fontChar.visible = YES;
+ fontChar.opacity = 255;
+ }
+
+ float yOffset = configuration_->commonHeight_ - fontDef.yOffset;
+ fontChar.positionInPixels = ccp( (float)nextFontPositionX + fontDef.xOffset + fontDef.rect.size.width*0.5f + kerningAmount,
+ (float)nextFontPositionY + yOffset - rect.size.height*0.5f );
+
+ // update kerning
+ nextFontPositionX += configuration_->BMFontArray_[c].xAdvance + kerningAmount;
+ prev = c;
+
+ // Apply label properties
+ [fontChar setOpacityModifyRGB:opacityModifyRGB_];
+ // Color MUST be set before opacity, since opacity might change color if OpacityModifyRGB is on
+ [fontChar setColor:color_];
+
+ // only apply opacity if it is different than 255 )
+ // to prevent modifying the color too (issue #610)
+ if( opacity_ != 255 )
+ [fontChar setOpacity: opacity_];
+
+ if (longestLine < nextFontPositionX)
+ longestLine = nextFontPositionX;
+ }
+
+ tmpSize.width = longestLine;
+ tmpSize.height = totalHeight;
+
+ [self setContentSizeInPixels:tmpSize];
+}
+
+#pragma mark BitmapFontAtlas - CCLabelProtocol protocol
+- (void) setString:(NSString*) newString
+{
+ [string_ release];
+ string_ = [newString copy];
+
+ CCNode *child;
+ CCARRAY_FOREACH(children_, child)
+ child.visible = NO;
+
+ [self createFontChars];
+}
+
+-(NSString*) string
+{
+ return string_;
+}
+
+-(void) setCString:(char*)label
+{
+ [self setString:[NSString stringWithUTF8String:label]];
+}
+
+#pragma mark BitmapFontAtlas - CCRGBAProtocol protocol
+
+-(void) setColor:(ccColor3B)color
+{
+ color_ = color;
+
+ CCSprite *child;
+ CCARRAY_FOREACH(children_, child)
+ [child setColor:color_];
+}
+
+-(void) setOpacity:(GLubyte)opacity
+{
+ opacity_ = opacity;
+
+ id<CCRGBAProtocol> child;
+ CCARRAY_FOREACH(children_, child)
+ [child setOpacity:opacity_];
+}
+-(void) setOpacityModifyRGB:(BOOL)modify
+{
+ opacityModifyRGB_ = modify;
+
+ id<CCRGBAProtocol> child;
+ CCARRAY_FOREACH(children_, child)
+ [child setOpacityModifyRGB:modify];
+}
+
+-(BOOL) doesOpacityModifyRGB
+{
+ return opacityModifyRGB_;
+}
+
+#pragma mark BitmapFontAtlas - AnchorPoint
+-(void) setAnchorPoint:(CGPoint)point
+{
+ if( ! CGPointEqualToPoint(point, anchorPoint_) ) {
+ [super setAnchorPoint:point];
+ [self createFontChars];
+ }
+}
+
+#pragma mark BitmapFontAtlas - Debug draw
+#if CC_LABELBMFONT_DEBUG_DRAW
+-(void) draw
+{
+ [super draw];
+ CGSize s = [self contentSize];
+ CGPoint vertices[4]={
+ ccp(0,0),ccp(s.width,0),
+ ccp(s.width,s.height),ccp(0,s.height),
+ };
+ ccDrawPoly(vertices, 4, YES);
+}
+#endif // CC_LABELBMFONT_DEBUG_DRAW
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCTexture2D.h"
+#import "CCSprite.h"
+#import "Platforms/CCNS.h"
+
+
+/** CCLabel is a subclass of CCTextureNode that knows how to render text labels
+ *
+ * All features from CCTextureNode are valid in CCLabel
+ *
+ * CCLabel objects are slow. Consider using CCLabelAtlas or CCBitmapFontAtlas instead.
+ */
+
+@interface CCLabelTTF : CCSprite <CCLabelProtocol>
+{
+ CGSize dimensions_;
+ CCTextAlignment alignment_;
+ NSString * fontName_;
+ CGFloat fontSize_;
+ CCLineBreakMode lineBreakMode_;
+ NSString *string_;
+}
+
+/** creates a CCLabel from a fontname, alignment, dimension in points, line break mode, and font size in points.
+ Supported lineBreakModes:
+ - iOS: all UILineBreakMode supported modes
+ - Mac: Only NSLineBreakByWordWrapping is supported.
+ @since v1.0
+ */
++ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size;
+/** creates a CCLabel from a fontname, alignment, dimension in points and font size in points*/
++ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size;
+/** creates a CCLabel from a fontname and font size in points*/
++ (id) labelWithString:(NSString*)string fontName:(NSString*)name fontSize:(CGFloat)size;
+/** initializes the CCLabel with a font name, alignment, dimension in points, line brea mode and font size in points.
+ Supported lineBreakModes:
+ - iOS: all UILineBreakMode supported modes
+ - Mac: Only NSLineBreakByWordWrapping is supported.
+ @since v1.0
+ */
+- (id) initWithString:(NSString*)str dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size;
+/** initializes the CCLabel with a font name, alignment, dimension in points and font size in points */
+- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size;
+/** initializes the CCLabel with a font name and font size in points */
+- (id) initWithString:(NSString*)string fontName:(NSString*)name fontSize:(CGFloat)size;
+
+/** changes the string to render
+ * @warning Changing the string is as expensive as creating a new CCLabel. To obtain better performance use CCLabelAtlas
+ */
+- (void) setString:(NSString*)str;
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import <Availability.h>
+
+#import "CCLabelTTF.h"
+#import "Support/CGPointExtension.h"
+#import "ccMacros.h"
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import "Platforms/iOS/CCDirectorIOS.h"
+#endif
+
+@implementation CCLabelTTF
+
+- (id) init
+{
+ NSAssert(NO, @"CCLabelTTF: Init not supported. Use initWithString");
+ [self release];
+ return nil;
+}
+
++ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size;
+{
+ return [[[self alloc] initWithString: string dimensions:dimensions alignment:alignment lineBreakMode:lineBreakMode fontName:name fontSize:size]autorelease];
+}
+
++ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size
+{
+ return [[[self alloc] initWithString: string dimensions:dimensions alignment:alignment fontName:name fontSize:size]autorelease];
+}
+
++ (id) labelWithString:(NSString*)string fontName:(NSString*)name fontSize:(CGFloat)size
+{
+ return [[[self alloc] initWithString: string fontName:name fontSize:size]autorelease];
+}
+
+
+- (id) initWithString:(NSString*)str dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size
+{
+ if( (self=[super init]) ) {
+
+ dimensions_ = CGSizeMake( dimensions.width * CC_CONTENT_SCALE_FACTOR(), dimensions.height * CC_CONTENT_SCALE_FACTOR() );
+ alignment_ = alignment;
+ fontName_ = [name retain];
+ fontSize_ = size * CC_CONTENT_SCALE_FACTOR();
+ lineBreakMode_ = lineBreakMode;
+
+ [self setString:str];
+ }
+ return self;
+}
+
+- (id) initWithString:(NSString*)str dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size
+{
+ return [self initWithString:str dimensions:dimensions alignment:alignment lineBreakMode:CCLineBreakModeWordWrap fontName:name fontSize:size];
+}
+
+- (id) initWithString:(NSString*)str fontName:(NSString*)name fontSize:(CGFloat)size
+{
+ if( (self=[super init]) ) {
+
+ dimensions_ = CGSizeZero;
+ fontName_ = [name retain];
+ fontSize_ = size * CC_CONTENT_SCALE_FACTOR();
+
+ [self setString:str];
+ }
+ return self;
+}
+
+- (void) setString:(NSString*)str
+{
+ [string_ release];
+ string_ = [str copy];
+
+ CCTexture2D *tex;
+ if( CGSizeEqualToSize( dimensions_, CGSizeZero ) )
+ tex = [[CCTexture2D alloc] initWithString:str
+ fontName:fontName_
+ fontSize:fontSize_];
+ else
+ tex = [[CCTexture2D alloc] initWithString:str
+ dimensions:dimensions_
+ alignment:alignment_
+ lineBreakMode:lineBreakMode_
+ fontName:fontName_
+ fontSize:fontSize_];
+
+ [self setTexture:tex];
+ [tex release];
+
+ CGRect rect = CGRectZero;
+ rect.size = [texture_ contentSize];
+ [self setTextureRect: rect];
+}
+
+-(NSString*) string
+{
+ return string_;
+}
+
+- (void) dealloc
+{
+ [string_ release];
+ [fontName_ release];
+ [super dealloc];
+}
+
+- (NSString*) description
+{
+ return [NSString stringWithFormat:@"<%@ = %08X | FontName = %@, FontSize = %.1f>", [self class], self, fontName_, fontSize_];
+}
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import <UIKit/UIKit.h> // Needed for UIAccelerometerDelegate
+#import "Platforms/iOS/CCTouchDelegateProtocol.h" // Touches only supported on iOS
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+#import "Platforms/Mac/CCEventDispatcher.h"
+#endif
+
+#import "CCProtocols.h"
+#import "CCNode.h"
+
+#pragma mark -
+#pragma mark CCLayer
+
+/** CCLayer is a subclass of CCNode that implements the TouchEventsDelegate protocol.
+
+ All features from CCNode are valid, plus the following new features:
+ - It can receive iPhone Touches
+ - It can receive Accelerometer input
+*/
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+@interface CCLayer : CCNode <UIAccelerometerDelegate, CCStandardTouchDelegate, CCTargetedTouchDelegate>
+{
+ BOOL isTouchEnabled_;
+ BOOL isAccelerometerEnabled_;
+}
+/** If isTouchEnabled, this method is called onEnter. Override it to change the
+ way CCLayer receives touch events.
+ ( Default: [[TouchDispatcher sharedDispatcher] addStandardDelegate:self priority:0] )
+ Example:
+ -(void) registerWithTouchDispatcher
+ {
+ [[TouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:INT_MIN+1 swallowsTouches:YES];
+ }
+
+ Valid only on iOS. Not valid on Mac.
+
+ @since v0.8.0
+ */
+-(void) registerWithTouchDispatcher;
+
+/** whether or not it will receive Touch events.
+ You can enable / disable touch events with this property.
+ Only the touches of this node will be affected. This "method" is not propagated to it's children.
+
+ Valid on iOS and Mac OS X v10.6 and later.
+
+ @since v0.8.1
+ */
+@property(nonatomic,assign) BOOL isTouchEnabled;
+/** whether or not it will receive Accelerometer events
+ You can enable / disable accelerometer events with this property.
+
+ Valid only on iOS. Not valid on Mac.
+
+ @since v0.8.1
+ */
+@property(nonatomic,assign) BOOL isAccelerometerEnabled;
+
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+
+@interface CCLayer : CCNode <CCKeyboardEventDelegate, CCMouseEventDelegate, CCTouchEventDelegate>
+{
+ BOOL isMouseEnabled_;
+ BOOL isKeyboardEnabled_;
+ BOOL isTouchEnabled_;
+}
+
+/** whether or not it will receive mouse events.
+
+ Valind only Mac. Not valid on iOS
+ */
+@property (nonatomic, readwrite) BOOL isMouseEnabled;
+
+/** whether or not it will receive keyboard events.
+
+ Valind only Mac. Not valid on iOS
+ */
+@property (nonatomic, readwrite) BOOL isKeyboardEnabled;
+
+/** whether or not it will receive touch events.
+
+ Valid on iOS and Mac OS X v10.6 and later.
+ */
+@property (nonatomic, readwrite) BOOL isTouchEnabled;
+
+/** priority of the mouse event delegate.
+ Default 0.
+ Override this method to set another priority.
+
+ Valind only Mac. Not valid on iOS
+ */
+-(NSInteger) mouseDelegatePriority;
+
+/** priority of the keyboard event delegate.
+ Default 0.
+ Override this method to set another priority.
+
+ Valind only Mac. Not valid on iOS
+ */
+-(NSInteger) keyboardDelegatePriority;
+
+/** priority of the touch event delegate.
+ Default 0.
+ Override this method to set another priority.
+
+ Valind only Mac. Not valid on iOS
+ */
+-(NSInteger) touchDelegatePriority;
+
+#endif // mac
+
+
+@end
+
+#pragma mark -
+#pragma mark CCLayerColor
+
+/** CCLayerColor is a subclass of CCLayer that implements the CCRGBAProtocol protocol.
+
+ All features from CCLayer are valid, plus the following new features:
+ - opacity
+ - RGB colors
+ */
+@interface CCLayerColor : CCLayer <CCRGBAProtocol, CCBlendProtocol>
+{
+ GLubyte opacity_;
+ ccColor3B color_;
+ ccVertex2F squareVertices_[4];
+ ccColor4B squareColors_[4];
+
+ ccBlendFunc blendFunc_;
+}
+
+/** creates a CCLayer with color, width and height in Points*/
++ (id) layerWithColor: (ccColor4B)color width:(GLfloat)w height:(GLfloat)h;
+/** creates a CCLayer with color. Width and height are the window size. */
++ (id) layerWithColor: (ccColor4B)color;
+
+/** initializes a CCLayer with color, width and height in Points */
+- (id) initWithColor:(ccColor4B)color width:(GLfloat)w height:(GLfloat)h;
+/** initializes a CCLayer with color. Width and height are the window size. */
+- (id) initWithColor:(ccColor4B)color;
+
+/** change width in Points */
+-(void) changeWidth: (GLfloat)w;
+/** change height in Points */
+-(void) changeHeight: (GLfloat)h;
+/** change width and height in Points
+ @since v0.8
+ */
+-(void) changeWidth:(GLfloat)w height:(GLfloat)h;
+
+/** Opacity: conforms to CCRGBAProtocol protocol */
+@property (nonatomic,readonly) GLubyte opacity;
+/** Opacity: conforms to CCRGBAProtocol protocol */
+@property (nonatomic,readonly) ccColor3B color;
+/** BlendFunction. Conforms to CCBlendProtocol protocol */
+@property (nonatomic,readwrite) ccBlendFunc blendFunc;
+@end
+
+/** CCColorLayer
+ It is the same as CCLayerColor.
+
+ @deprecated Use CCLayerColor instead. This class will be removed in v1.0.1
+ */
+DEPRECATED_ATTRIBUTE @interface CCColorLayer : CCLayerColor
+@end
+
+#pragma mark -
+#pragma mark CCLayerGradient
+
+/** CCLayerGradient is a subclass of CCLayerColor that draws gradients across
+the background.
+
+ All features from CCLayerColor are valid, plus the following new features:
+ - direction
+ - final color
+ - interpolation mode
+
+ Color is interpolated between the startColor and endColor along the given
+ vector (starting at the origin, ending at the terminus). If no vector is
+ supplied, it defaults to (0, -1) -- a fade from top to bottom.
+
+ If 'compressedInterpolation' is disabled, you will not see either the start or end color for
+ non-cardinal vectors; a smooth gradient implying both end points will be still
+ be drawn, however.
+
+ If ' compressedInterpolation' is enabled (default mode) you will see both the start and end colors of the gradient.
+
+ @since v0.99.5
+ */
+@interface CCLayerGradient : CCLayerColor
+{
+ ccColor3B endColor_;
+ GLubyte startOpacity_;
+ GLubyte endOpacity_;
+ CGPoint vector_;
+ BOOL compressedInterpolation_;
+}
+
+/** Creates a full-screen CCLayer with a gradient between start and end. */
++ (id) layerWithColor: (ccColor4B) start fadingTo: (ccColor4B) end;
+/** Creates a full-screen CCLayer with a gradient between start and end in the direction of v. */
++ (id) layerWithColor: (ccColor4B) start fadingTo: (ccColor4B) end alongVector: (CGPoint) v;
+
+/** Initializes the CCLayer with a gradient between start and end. */
+- (id) initWithColor: (ccColor4B) start fadingTo: (ccColor4B) end;
+/** Initializes the CCLayer with a gradient between start and end in the direction of v. */
+- (id) initWithColor: (ccColor4B) start fadingTo: (ccColor4B) end alongVector: (CGPoint) v;
+
+/** The starting color. */
+@property (nonatomic, readwrite) ccColor3B startColor;
+/** The ending color. */
+@property (nonatomic, readwrite) ccColor3B endColor;
+/** The starting opacity. */
+@property (nonatomic, readwrite) GLubyte startOpacity;
+/** The ending color. */
+@property (nonatomic, readwrite) GLubyte endOpacity;
+/** The vector along which to fade color. */
+@property (nonatomic, readwrite) CGPoint vector;
+/** Whether or not the interpolation will be compressed in order to display all the colors of the gradient both in canonical and non canonical vectors
+ Default: YES
+ */
+@property (nonatomic, readwrite) BOOL compressedInterpolation;
+
+@end
+
+#pragma mark -
+#pragma mark CCLayerMultiplex
+
+/** CCLayerMultiplex is a CCLayer with the ability to multiplex it's children.
+ Features:
+ - It supports one or more children
+ - Only one children will be active a time
+ */
+@interface CCLayerMultiplex : CCLayer
+{
+ unsigned int enabledLayer_;
+ NSMutableArray *layers_;
+}
+
+/** creates a CCMultiplexLayer with one or more layers using a variable argument list. */
++(id) layerWithLayers: (CCLayer*) layer, ... NS_REQUIRES_NIL_TERMINATION;
+/** initializes a MultiplexLayer with one or more layers using a variable argument list. */
+-(id) initWithLayers: (CCLayer*) layer vaList:(va_list) params;
+/** switches to a certain layer indexed by n.
+ The current (old) layer will be removed from it's parent with 'cleanup:YES'.
+ */
+-(void) switchTo: (unsigned int) n;
+/** release the current layer and switches to another layer indexed by n.
+ The current (old) layer will be removed from it's parent with 'cleanup:YES'.
+ */
+-(void) switchToAndReleaseMe: (unsigned int) n;
+@end
+
+/** CCMultiplexLayer
+ It is the same as CCLayerMultiplex.
+
+ @deprecated Use CCLayerMultiplex instead. This class will be removed in v1.0.1
+ */
+DEPRECATED_ATTRIBUTE @interface CCMultiplexLayer : CCLayerMultiplex
+@end
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import <stdarg.h>
+
+#import "Platforms/CCGL.h"
+
+#import "CCLayer.h"
+#import "CCDirector.h"
+#import "ccMacros.h"
+#import "Support/CGPointExtension.h"
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import "Platforms/iOS/CCTouchDispatcher.h"
+#import "Platforms/iOS/CCDirectorIOS.h"
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+#import "Platforms/Mac/CCEventDispatcher.h"
+#endif
+
+#pragma mark -
+#pragma mark Layer
+
+@implementation CCLayer
+
+#pragma mark Layer - Init
+-(id) init
+{
+ if( (self=[super init]) ) {
+
+ CGSize s = [[CCDirector sharedDirector] winSize];
+ anchorPoint_ = ccp(0.5f, 0.5f);
+ [self setContentSize:s];
+ self.isRelativeAnchorPoint = NO;
+
+ isTouchEnabled_ = NO;
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+ isAccelerometerEnabled_ = NO;
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+ isMouseEnabled_ = NO;
+ isKeyboardEnabled_ = NO;
+#endif
+ }
+
+ return self;
+}
+
+#pragma mark Layer - Touch and Accelerometer related
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+-(void) registerWithTouchDispatcher
+{
+ [[CCTouchDispatcher sharedDispatcher] addStandardDelegate:self priority:0];
+}
+
+-(BOOL) isAccelerometerEnabled
+{
+ return isAccelerometerEnabled_;
+}
+
+-(void) setIsAccelerometerEnabled:(BOOL)enabled
+{
+ if( enabled != isAccelerometerEnabled_ ) {
+ isAccelerometerEnabled_ = enabled;
+ if( isRunning_ ) {
+ if( enabled )
+ [[UIAccelerometer sharedAccelerometer] setDelegate:self];
+ else
+ [[UIAccelerometer sharedAccelerometer] setDelegate:nil];
+ }
+ }
+}
+
+-(BOOL) isTouchEnabled
+{
+ return isTouchEnabled_;
+}
+
+-(void) setIsTouchEnabled:(BOOL)enabled
+{
+ if( isTouchEnabled_ != enabled ) {
+ isTouchEnabled_ = enabled;
+ if( isRunning_ ) {
+ if( enabled )
+ [self registerWithTouchDispatcher];
+ else
+ [[CCTouchDispatcher sharedDispatcher] removeDelegate:self];
+ }
+ }
+}
+
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+#pragma mark CCLayer - Mouse, Keyboard & Touch events
+
+-(NSInteger) mouseDelegatePriority
+{
+ return 0;
+}
+
+-(BOOL) isMouseEnabled
+{
+ return isMouseEnabled_;
+}
+
+-(void) setIsMouseEnabled:(BOOL)enabled
+{
+ if( isMouseEnabled_ != enabled ) {
+ isMouseEnabled_ = enabled;
+
+ if( isRunning_ ) {
+ if( enabled )
+ [[CCEventDispatcher sharedDispatcher] addMouseDelegate:self priority:[self mouseDelegatePriority]];
+ else
+ [[CCEventDispatcher sharedDispatcher] removeMouseDelegate:self];
+ }
+ }
+}
+
+-(NSInteger) keyboardDelegatePriority
+{
+ return 0;
+}
+
+-(BOOL) isKeyboardEnabled
+{
+ return isKeyboardEnabled_;
+}
+
+-(void) setIsKeyboardEnabled:(BOOL)enabled
+{
+ if( isKeyboardEnabled_ != enabled ) {
+ isKeyboardEnabled_ = enabled;
+
+ if( isRunning_ ) {
+ if( enabled )
+ [[CCEventDispatcher sharedDispatcher] addKeyboardDelegate:self priority:[self keyboardDelegatePriority] ];
+ else
+ [[CCEventDispatcher sharedDispatcher] removeKeyboardDelegate:self];
+ }
+ }
+}
+
+-(NSInteger) touchDelegatePriority
+{
+ return 0;
+}
+
+-(BOOL) isTouchEnabled
+{
+ return isTouchEnabled_;
+}
+
+-(void) setIsTouchEnabled:(BOOL)enabled
+{
+ if( isTouchEnabled_ != enabled ) {
+ isTouchEnabled_ = enabled;
+ if( isRunning_ ) {
+ if( enabled )
+ [[CCEventDispatcher sharedDispatcher] addTouchDelegate:self priority:[self touchDelegatePriority]];
+ else
+ [[CCEventDispatcher sharedDispatcher] removeTouchDelegate:self];
+ }
+ }
+}
+
+
+#endif // Mac
+
+
+#pragma mark Layer - Callbacks
+-(void) onEnter
+{
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+ // register 'parent' nodes first
+ // since events are propagated in reverse order
+ if (isTouchEnabled_)
+ [self registerWithTouchDispatcher];
+
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+ if( isMouseEnabled_ )
+ [[CCEventDispatcher sharedDispatcher] addMouseDelegate:self priority:[self mouseDelegatePriority]];
+
+ if( isKeyboardEnabled_)
+ [[CCEventDispatcher sharedDispatcher] addKeyboardDelegate:self priority:[self keyboardDelegatePriority]];
+
+ if( isTouchEnabled_)
+ [[CCEventDispatcher sharedDispatcher] addTouchDelegate:self priority:[self touchDelegatePriority]];
+
+#endif
+
+ // then iterate over all the children
+ [super onEnter];
+}
+
+// issue #624.
+// Can't register mouse, touches here because of #issue #1018, and #1021
+-(void) onEnterTransitionDidFinish
+{
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+ if( isAccelerometerEnabled_ )
+ [[UIAccelerometer sharedAccelerometer] setDelegate:self];
+#endif
+
+ [super onEnterTransitionDidFinish];
+}
+
+
+-(void) onExit
+{
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+ if( isTouchEnabled_ )
+ [[CCTouchDispatcher sharedDispatcher] removeDelegate:self];
+
+ if( isAccelerometerEnabled_ )
+ [[UIAccelerometer sharedAccelerometer] setDelegate:nil];
+
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+ if( isMouseEnabled_ )
+ [[CCEventDispatcher sharedDispatcher] removeMouseDelegate:self];
+
+ if( isKeyboardEnabled_ )
+ [[CCEventDispatcher sharedDispatcher] removeKeyboardDelegate:self];
+
+ if( isTouchEnabled_ )
+ [[CCEventDispatcher sharedDispatcher] removeTouchDelegate:self];
+
+#endif
+
+ [super onExit];
+}
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
+{
+ NSAssert(NO, @"Layer#ccTouchBegan override me");
+ return YES;
+}
+#endif
+@end
+
+#pragma mark -
+#pragma mark LayerColor
+
+@interface CCLayerColor (Private)
+-(void) updateColor;
+@end
+
+@implementation CCLayerColor
+
+// Opacity and RGB color protocol
+@synthesize opacity = opacity_, color = color_;
+@synthesize blendFunc = blendFunc_;
+
+
++ (id) layerWithColor:(ccColor4B)color width:(GLfloat)w height:(GLfloat) h
+{
+ return [[[self alloc] initWithColor:color width:w height:h] autorelease];
+}
+
++ (id) layerWithColor:(ccColor4B)color
+{
+ return [[(CCLayerColor*)[self alloc] initWithColor:color] autorelease];
+}
+
+- (id) initWithColor:(ccColor4B)color width:(GLfloat)w height:(GLfloat) h
+{
+ if( (self=[super init]) ) {
+
+ // default blend function
+ blendFunc_ = (ccBlendFunc) { CC_BLEND_SRC, CC_BLEND_DST };
+
+ color_.r = color.r;
+ color_.g = color.g;
+ color_.b = color.b;
+ opacity_ = color.a;
+
+ for (NSUInteger i = 0; i<sizeof(squareVertices_) / sizeof( squareVertices_[0]); i++ ) {
+ squareVertices_[i].x = 0.0f;
+ squareVertices_[i].y = 0.0f;
+ }
+
+ [self updateColor];
+ [self setContentSize:CGSizeMake(w, h) ];
+ }
+ return self;
+}
+
+- (id) initWithColor:(ccColor4B)color
+{
+ CGSize s = [[CCDirector sharedDirector] winSize];
+ return [self initWithColor:color width:s.width height:s.height];
+}
+
+// override contentSize
+-(void) setContentSize: (CGSize) size
+{
+ squareVertices_[1].x = size.width * CC_CONTENT_SCALE_FACTOR();
+ squareVertices_[2].y = size.height * CC_CONTENT_SCALE_FACTOR();
+ squareVertices_[3].x = size.width * CC_CONTENT_SCALE_FACTOR();
+ squareVertices_[3].y = size.height * CC_CONTENT_SCALE_FACTOR();
+
+ [super setContentSize:size];
+}
+
+- (void) changeWidth: (GLfloat) w height:(GLfloat) h
+{
+ [self setContentSize:CGSizeMake(w, h)];
+}
+
+-(void) changeWidth: (GLfloat) w
+{
+ [self setContentSize:CGSizeMake(w, contentSize_.height)];
+}
+
+-(void) changeHeight: (GLfloat) h
+{
+ [self setContentSize:CGSizeMake(contentSize_.width, h)];
+}
+
+- (void) updateColor
+{
+ for( NSUInteger i = 0; i < 4; i++ )
+ {
+ squareColors_[i].r = color_.r;
+ squareColors_[i].g = color_.g;
+ squareColors_[i].b = color_.b;
+ squareColors_[i].a = opacity_;
+ }
+}
+
+- (void)draw
+{
+ // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Needed states: GL_VERTEX_ARRAY, GL_COLOR_ARRAY
+ // Unneeded states: GL_TEXTURE_2D, GL_TEXTURE_COORD_ARRAY
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisable(GL_TEXTURE_2D);
+
+ glVertexPointer(2, GL_FLOAT, 0, squareVertices_);
+ glColorPointer(4, GL_UNSIGNED_BYTE, 0, squareColors_);
+
+
+ BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST;
+ if( newBlend )
+ glBlendFunc( blendFunc_.src, blendFunc_.dst );
+
+ else if( opacity_ != 255 ) {
+ newBlend = YES;
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ if( newBlend )
+ glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
+
+ // restore default GL state
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_2D);
+}
+
+#pragma mark Protocols
+// Color Protocol
+
+-(void) setColor:(ccColor3B)color
+{
+ color_ = color;
+ [self updateColor];
+}
+
+-(void) setOpacity: (GLubyte) o
+{
+ opacity_ = o;
+ [self updateColor];
+}
+@end
+
+// XXX Deprecated
+@implementation CCColorLayer
+@end
+
+
+#pragma mark -
+#pragma mark LayerGradient
+
+@implementation CCLayerGradient
+
+@synthesize startOpacity = startOpacity_;
+@synthesize endColor = endColor_, endOpacity = endOpacity_;
+@synthesize vector = vector_;
+
++ (id) layerWithColor: (ccColor4B) start fadingTo: (ccColor4B) end
+{
+ return [[[self alloc] initWithColor:start fadingTo:end] autorelease];
+}
+
++ (id) layerWithColor: (ccColor4B) start fadingTo: (ccColor4B) end alongVector: (CGPoint) v
+{
+ return [[[self alloc] initWithColor:start fadingTo:end alongVector:v] autorelease];
+}
+
+- (id) initWithColor: (ccColor4B) start fadingTo: (ccColor4B) end
+{
+ return [self initWithColor:start fadingTo:end alongVector:ccp(0, -1)];
+}
+
+- (id) initWithColor: (ccColor4B) start fadingTo: (ccColor4B) end alongVector: (CGPoint) v
+{
+ endColor_.r = end.r;
+ endColor_.g = end.g;
+ endColor_.b = end.b;
+
+ endOpacity_ = end.a;
+ startOpacity_ = start.a;
+ vector_ = v;
+
+ start.a = 255;
+ compressedInterpolation_ = YES;
+
+ return [super initWithColor:start];
+}
+
+- (void) updateColor
+{
+ [super updateColor];
+
+ float h = ccpLength(vector_);
+ if (h == 0)
+ return;
+
+ double c = sqrt(2);
+ CGPoint u = ccp(vector_.x / h, vector_.y / h);
+
+ // Compressed Interpolation mode
+ if( compressedInterpolation_ ) {
+ float h2 = 1 / ( fabsf(u.x) + fabsf(u.y) );
+ u = ccpMult(u, h2 * (float)c);
+ }
+
+ float opacityf = (float)opacity_/255.0f;
+
+ ccColor4B S = {
+ color_.r,
+ color_.g,
+ color_.b,
+ startOpacity_*opacityf
+ };
+
+ ccColor4B E = {
+ endColor_.r,
+ endColor_.g,
+ endColor_.b,
+ endOpacity_*opacityf
+ };
+
+
+ // (-1, -1)
+ squareColors_[0].r = E.r + (S.r - E.r) * ((c + u.x + u.y) / (2.0f * c));
+ squareColors_[0].g = E.g + (S.g - E.g) * ((c + u.x + u.y) / (2.0f * c));
+ squareColors_[0].b = E.b + (S.b - E.b) * ((c + u.x + u.y) / (2.0f * c));
+ squareColors_[0].a = E.a + (S.a - E.a) * ((c + u.x + u.y) / (2.0f * c));
+ // (1, -1)
+ squareColors_[1].r = E.r + (S.r - E.r) * ((c - u.x + u.y) / (2.0f * c));
+ squareColors_[1].g = E.g + (S.g - E.g) * ((c - u.x + u.y) / (2.0f * c));
+ squareColors_[1].b = E.b + (S.b - E.b) * ((c - u.x + u.y) / (2.0f * c));
+ squareColors_[1].a = E.a + (S.a - E.a) * ((c - u.x + u.y) / (2.0f * c));
+ // (-1, 1)
+ squareColors_[2].r = E.r + (S.r - E.r) * ((c + u.x - u.y) / (2.0f * c));
+ squareColors_[2].g = E.g + (S.g - E.g) * ((c + u.x - u.y) / (2.0f * c));
+ squareColors_[2].b = E.b + (S.b - E.b) * ((c + u.x - u.y) / (2.0f * c));
+ squareColors_[2].a = E.a + (S.a - E.a) * ((c + u.x - u.y) / (2.0f * c));
+ // (1, 1)
+ squareColors_[3].r = E.r + (S.r - E.r) * ((c - u.x - u.y) / (2.0f * c));
+ squareColors_[3].g = E.g + (S.g - E.g) * ((c - u.x - u.y) / (2.0f * c));
+ squareColors_[3].b = E.b + (S.b - E.b) * ((c - u.x - u.y) / (2.0f * c));
+ squareColors_[3].a = E.a + (S.a - E.a) * ((c - u.x - u.y) / (2.0f * c));
+}
+
+-(ccColor3B) startColor
+{
+ return color_;
+}
+
+-(void) setStartColor:(ccColor3B)colors
+{
+ [self setColor:colors];
+}
+
+-(void) setEndColor:(ccColor3B)colors
+{
+ endColor_ = colors;
+ [self updateColor];
+}
+
+-(void) setStartOpacity: (GLubyte) o
+{
+ startOpacity_ = o;
+ [self updateColor];
+}
+
+-(void) setEndOpacity: (GLubyte) o
+{
+ endOpacity_ = o;
+ [self updateColor];
+}
+
+-(void) setVector: (CGPoint) v
+{
+ vector_ = v;
+ [self updateColor];
+}
+
+-(BOOL) compressedInterpolation
+{
+ return compressedInterpolation_;
+}
+
+-(void) setCompressedInterpolation:(BOOL)compress
+{
+ compressedInterpolation_ = compress;
+ [self updateColor];
+}
+@end
+
+#pragma mark -
+#pragma mark MultiplexLayer
+
+@implementation CCLayerMultiplex
++(id) layerWithLayers: (CCLayer*) layer, ...
+{
+ va_list args;
+ va_start(args,layer);
+
+ id s = [[[self alloc] initWithLayers: layer vaList:args] autorelease];
+
+ va_end(args);
+ return s;
+}
+
+-(id) initWithLayers: (CCLayer*) layer vaList:(va_list) params
+{
+ if( (self=[super init]) ) {
+
+ layers_ = [[NSMutableArray arrayWithCapacity:5] retain];
+
+ [layers_ addObject: layer];
+
+ CCLayer *l = va_arg(params,CCLayer*);
+ while( l ) {
+ [layers_ addObject: l];
+ l = va_arg(params,CCLayer*);
+ }
+
+ enabledLayer_ = 0;
+ [self addChild: [layers_ objectAtIndex: enabledLayer_]];
+ }
+
+ return self;
+}
+
+-(void) dealloc
+{
+ [layers_ release];
+ [super dealloc];
+}
+
+-(void) switchTo: (unsigned int) n
+{
+ NSAssert( n < [layers_ count], @"Invalid index in MultiplexLayer switchTo message" );
+
+ [self removeChild: [layers_ objectAtIndex:enabledLayer_] cleanup:YES];
+
+ enabledLayer_ = n;
+
+ [self addChild: [layers_ objectAtIndex:n]];
+}
+
+-(void) switchToAndReleaseMe: (unsigned int) n
+{
+ NSAssert( n < [layers_ count], @"Invalid index in MultiplexLayer switchTo message" );
+
+ [self removeChild: [layers_ objectAtIndex:enabledLayer_] cleanup:YES];
+
+ [layers_ replaceObjectAtIndex:enabledLayer_ withObject:[NSNull null]];
+
+ enabledLayer_ = n;
+
+ [self addChild: [layers_ objectAtIndex:n]];
+}
+@end
+
+// XXX Deprecated
+@implementation CCMultiplexLayer
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCMenuItem.h"
+#import "CCLayer.h"
+
+typedef enum {
+ kCCMenuStateWaiting,
+ kCCMenuStateTrackingTouch
+} tCCMenuState;
+
+enum {
+ //* priority used by the menu for the touches
+ kCCMenuTouchPriority = -128,
+
+ //* priority used by the menu for the mouse
+ kCCMenuMousePriority = -128,
+};
+
+/** A CCMenu
+ *
+ * Features and Limitation:
+ * - You can add MenuItem objects in runtime using addChild:
+ * - But the only accecpted children are MenuItem objects
+ */
+@interface CCMenu : CCLayer <CCRGBAProtocol>
+{
+ tCCMenuState state_;
+ CCMenuItem *selectedItem_;
+ GLubyte opacity_;
+ ccColor3B color_;
+}
+
+/** creates a CCMenu with it's items */
++ (id) menuWithItems: (CCMenuItem*) item, ... NS_REQUIRES_NIL_TERMINATION;
+
+/** initializes a CCMenu with it's items */
+- (id) initWithItems: (CCMenuItem*) item vaList: (va_list) args;
+
+/** align items vertically */
+-(void) alignItemsVertically;
+/** align items vertically with padding
+ @since v0.7.2
+ */
+-(void) alignItemsVerticallyWithPadding:(float) padding;
+
+/** align items horizontally */
+-(void) alignItemsHorizontally;
+/** align items horizontally with padding
+ @since v0.7.2
+ */
+-(void) alignItemsHorizontallyWithPadding: (float) padding;
+
+
+/** align items in rows of columns */
+-(void) alignItemsInColumns: (NSNumber *) columns, ... NS_REQUIRES_NIL_TERMINATION;
+-(void) alignItemsInColumns: (NSNumber *) columns vaList: (va_list) args;
+
+/** align items in columns of rows */
+-(void) alignItemsInRows: (NSNumber *) rows, ... NS_REQUIRES_NIL_TERMINATION;
+-(void) alignItemsInRows: (NSNumber *) rows vaList: (va_list) args;
+
+
+/** conforms to CCRGBAProtocol protocol */
+@property (nonatomic,readonly) GLubyte opacity;
+/** conforms to CCRGBAProtocol protocol */
+@property (nonatomic,readonly) ccColor3B color;
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+
+#import "CCMenu.h"
+#import "CCDirector.h"
+#import "Support/CGPointExtension.h"
+#import "ccMacros.h"
+
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import "Platforms/iOS/CCDirectorIOS.h"
+#import "Platforms/iOS/CCTouchDispatcher.h"
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+#import "Platforms/Mac/MacGLView.h"
+#import "Platforms/Mac/CCDirectorMac.h"
+#endif
+
+enum {
+ kDefaultPadding = 5,
+};
+
+@implementation CCMenu
+
+@synthesize opacity = opacity_, color = color_;
+
+- (id) init
+{
+ NSAssert(NO, @"CCMenu: Init not supported.");
+ [self release];
+ return nil;
+}
+
++(id) menuWithItems: (CCMenuItem*) item, ...
+{
+ va_list args;
+ va_start(args,item);
+
+ id s = [[[self alloc] initWithItems: item vaList:args] autorelease];
+
+ va_end(args);
+ return s;
+}
+
+-(id) initWithItems: (CCMenuItem*) item vaList: (va_list) args
+{
+ if( (self=[super init]) ) {
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+ self.isTouchEnabled = YES;
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+ self.isMouseEnabled = YES;
+#endif
+
+ // menu in the center of the screen
+ CGSize s = [[CCDirector sharedDirector] winSize];
+
+ self.isRelativeAnchorPoint = NO;
+ anchorPoint_ = ccp(0.5f, 0.5f);
+ [self setContentSize:s];
+
+ // XXX: in v0.7, winSize should return the visible size
+ // XXX: so the bar calculation should be done there
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+ CGRect r = [[UIApplication sharedApplication] statusBarFrame];
+ ccDeviceOrientation orientation = [[CCDirector sharedDirector] deviceOrientation];
+ if( orientation == CCDeviceOrientationLandscapeLeft || orientation == CCDeviceOrientationLandscapeRight )
+ s.height -= r.size.width;
+ else
+ s.height -= r.size.height;
+#endif
+ self.position = ccp(s.width/2, s.height/2);
+
+ int z=0;
+
+ if (item) {
+ [self addChild: item z:z];
+ CCMenuItem *i = va_arg(args, CCMenuItem*);
+ while(i) {
+ z++;
+ [self addChild: i z:z];
+ i = va_arg(args, CCMenuItem*);
+ }
+ }
+ // [self alignItemsVertically];
+
+ selectedItem_ = nil;
+ state_ = kCCMenuStateWaiting;
+ }
+
+ return self;
+}
+
+-(void) dealloc
+{
+ [super dealloc];
+}
+
+/*
+ * override add:
+ */
+-(void) addChild:(CCMenuItem*)child z:(NSInteger)z tag:(NSInteger) aTag
+{
+ NSAssert( [child isKindOfClass:[CCMenuItem class]], @"Menu only supports MenuItem objects as children");
+ [super addChild:child z:z tag:aTag];
+}
+
+- (void) onExit
+{
+ if(state_ == kCCMenuStateTrackingTouch)
+ {
+ [selectedItem_ unselected];
+ state_ = kCCMenuStateWaiting;
+ selectedItem_ = nil;
+ }
+ [super onExit];
+}
+
+#pragma mark Menu - Touches
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+-(void) registerWithTouchDispatcher
+{
+ [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:kCCMenuTouchPriority swallowsTouches:YES];
+}
+
+-(CCMenuItem *) itemForTouch: (UITouch *) touch
+{
+ CGPoint touchLocation = [touch locationInView: [touch view]];
+ touchLocation = [[CCDirector sharedDirector] convertToGL: touchLocation];
+
+ CCMenuItem* item;
+ CCARRAY_FOREACH(children_, item){
+ // ignore invisible and disabled items: issue #779, #866
+ if ( [item visible] && [item isEnabled] ) {
+
+ CGPoint local = [item convertToNodeSpace:touchLocation];
+ CGRect r = [item rect];
+ r.origin = CGPointZero;
+
+ if( CGRectContainsPoint( r, local ) )
+ return item;
+ }
+ }
+ return nil;
+}
+
+-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
+{
+ if( state_ != kCCMenuStateWaiting || !visible_ )
+ return NO;
+
+ selectedItem_ = [self itemForTouch:touch];
+ [selectedItem_ selected];
+
+ if( selectedItem_ ) {
+ state_ = kCCMenuStateTrackingTouch;
+ return YES;
+ }
+ return NO;
+}
+
+-(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
+{
+ NSAssert(state_ == kCCMenuStateTrackingTouch, @"[Menu ccTouchEnded] -- invalid state");
+
+ [selectedItem_ unselected];
+ [selectedItem_ activate];
+
+ state_ = kCCMenuStateWaiting;
+}
+
+-(void) ccTouchCancelled:(UITouch *)touch withEvent:(UIEvent *)event
+{
+ NSAssert(state_ == kCCMenuStateTrackingTouch, @"[Menu ccTouchCancelled] -- invalid state");
+
+ [selectedItem_ unselected];
+
+ state_ = kCCMenuStateWaiting;
+}
+
+-(void) ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
+{
+ NSAssert(state_ == kCCMenuStateTrackingTouch, @"[Menu ccTouchMoved] -- invalid state");
+
+ CCMenuItem *currentItem = [self itemForTouch:touch];
+
+ if (currentItem != selectedItem_) {
+ [selectedItem_ unselected];
+ selectedItem_ = currentItem;
+ [selectedItem_ selected];
+ }
+}
+
+#pragma mark Menu - Mouse
+
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+-(NSInteger) mouseDelegatePriority
+{
+ return kCCMenuMousePriority+1;
+}
+
+-(CCMenuItem *) itemForMouseEvent: (NSEvent *) event
+{
+ CGPoint location = [(CCDirectorMac*)[CCDirector sharedDirector] convertEventToGL:event];
+
+ CCMenuItem* item;
+ CCARRAY_FOREACH(children_, item){
+ // ignore invisible and disabled items: issue #779, #866
+ if ( [item visible] && [item isEnabled] ) {
+
+ CGPoint local = [item convertToNodeSpace:location];
+
+ CGRect r = [item rect];
+ r.origin = CGPointZero;
+
+ if( CGRectContainsPoint( r, local ) )
+ return item;
+ }
+ }
+ return nil;
+}
+
+-(BOOL) ccMouseUp:(NSEvent *)event
+{
+ if( ! visible_ )
+ return NO;
+
+ if(state_ == kCCMenuStateTrackingTouch) {
+ if( selectedItem_ ) {
+ [selectedItem_ unselected];
+ [selectedItem_ activate];
+ }
+ state_ = kCCMenuStateWaiting;
+
+ return YES;
+ }
+ return NO;
+}
+
+-(BOOL) ccMouseDown:(NSEvent *)event
+{
+ if( ! visible_ )
+ return NO;
+
+ selectedItem_ = [self itemForMouseEvent:event];
+ [selectedItem_ selected];
+
+ if( selectedItem_ ) {
+ state_ = kCCMenuStateTrackingTouch;
+ return YES;
+ }
+
+ return NO;
+}
+
+-(BOOL) ccMouseDragged:(NSEvent *)event
+{
+ if( ! visible_ )
+ return NO;
+
+ if(state_ == kCCMenuStateTrackingTouch) {
+ CCMenuItem *currentItem = [self itemForMouseEvent:event];
+
+ if (currentItem != selectedItem_) {
+ [selectedItem_ unselected];
+ selectedItem_ = currentItem;
+ [selectedItem_ selected];
+ }
+
+ return YES;
+ }
+ return NO;
+}
+
+#endif // Mac Mouse support
+
+#pragma mark Menu - Alignment
+-(void) alignItemsVertically
+{
+ return [self alignItemsVerticallyWithPadding:kDefaultPadding];
+}
+-(void) alignItemsVerticallyWithPadding:(float)padding
+{
+ float height = -padding;
+
+ CCMenuItem *item;
+ CCARRAY_FOREACH(children_, item)
+ height += item.contentSize.height * item.scaleY + padding;
+
+ float y = height / 2.0f;
+
+ CCARRAY_FOREACH(children_, item) {
+ CGSize itemSize = item.contentSize;
+ [item setPosition:ccp(0, y - itemSize.height * item.scaleY / 2.0f)];
+ y -= itemSize.height * item.scaleY + padding;
+ }
+}
+
+-(void) alignItemsHorizontally
+{
+ return [self alignItemsHorizontallyWithPadding:kDefaultPadding];
+}
+
+-(void) alignItemsHorizontallyWithPadding:(float)padding
+{
+
+ float width = -padding;
+ CCMenuItem *item;
+ CCARRAY_FOREACH(children_, item)
+ width += item.contentSize.width * item.scaleX + padding;
+
+ float x = -width / 2.0f;
+
+ CCARRAY_FOREACH(children_, item){
+ CGSize itemSize = item.contentSize;
+ [item setPosition:ccp(x + itemSize.width * item.scaleX / 2.0f, 0)];
+ x += itemSize.width * item.scaleX + padding;
+ }
+}
+
+-(void) alignItemsInColumns: (NSNumber *) columns, ...
+{
+ va_list args;
+ va_start(args, columns);
+
+ [self alignItemsInColumns:columns vaList:args];
+
+ va_end(args);
+}
+
+-(void) alignItemsInColumns: (NSNumber *) columns vaList: (va_list) args
+{
+ NSMutableArray *rows = [[NSMutableArray alloc] initWithObjects:columns, nil];
+ columns = va_arg(args, NSNumber*);
+ while(columns) {
+ [rows addObject:columns];
+ columns = va_arg(args, NSNumber*);
+ }
+
+ int height = -5;
+ NSUInteger row = 0, rowHeight = 0, columnsOccupied = 0, rowColumns;
+ CCMenuItem *item;
+ CCARRAY_FOREACH(children_, item){
+ NSAssert( row < [rows count], @"Too many menu items for the amount of rows/columns.");
+
+ rowColumns = [(NSNumber *) [rows objectAtIndex:row] unsignedIntegerValue];
+ NSAssert( rowColumns, @"Can't have zero columns on a row");
+
+ rowHeight = fmaxf(rowHeight, item.contentSize.height);
+ ++columnsOccupied;
+
+ if(columnsOccupied >= rowColumns) {
+ height += rowHeight + 5;
+
+ columnsOccupied = 0;
+ rowHeight = 0;
+ ++row;
+ }
+ }
+ NSAssert( !columnsOccupied, @"Too many rows/columns for available menu items." );
+
+ CGSize winSize = [[CCDirector sharedDirector] winSize];
+
+ row = 0; rowHeight = 0; rowColumns = 0;
+ float w, x, y = height / 2;
+ CCARRAY_FOREACH(children_, item) {
+ if(rowColumns == 0) {
+ rowColumns = [(NSNumber *) [rows objectAtIndex:row] unsignedIntegerValue];
+ w = winSize.width / (1 + rowColumns);
+ x = w;
+ }
+
+ CGSize itemSize = item.contentSize;
+ rowHeight = fmaxf(rowHeight, itemSize.height);
+ [item setPosition:ccp(x - winSize.width / 2,
+ y - itemSize.height / 2)];
+
+ x += w + 10;
+ ++columnsOccupied;
+
+ if(columnsOccupied >= rowColumns) {
+ y -= rowHeight + 5;
+
+ columnsOccupied = 0;
+ rowColumns = 0;
+ rowHeight = 0;
+ ++row;
+ }
+ }
+
+ [rows release];
+}
+
+-(void) alignItemsInRows: (NSNumber *) rows, ...
+{
+ va_list args;
+ va_start(args, rows);
+
+ [self alignItemsInRows:rows vaList:args];
+
+ va_end(args);
+}
+
+-(void) alignItemsInRows: (NSNumber *) rows vaList: (va_list) args
+{
+ NSMutableArray *columns = [[NSMutableArray alloc] initWithObjects:rows, nil];
+ rows = va_arg(args, NSNumber*);
+ while(rows) {
+ [columns addObject:rows];
+ rows = va_arg(args, NSNumber*);
+ }
+
+ NSMutableArray *columnWidths = [[NSMutableArray alloc] init];
+ NSMutableArray *columnHeights = [[NSMutableArray alloc] init];
+
+ int width = -10, columnHeight = -5;
+ NSUInteger column = 0, columnWidth = 0, rowsOccupied = 0, columnRows;
+ CCMenuItem *item;
+ CCARRAY_FOREACH(children_, item){
+ NSAssert( column < [columns count], @"Too many menu items for the amount of rows/columns.");
+
+ columnRows = [(NSNumber *) [columns objectAtIndex:column] unsignedIntegerValue];
+ NSAssert( columnRows, @"Can't have zero rows on a column");
+
+ CGSize itemSize = item.contentSize;
+ columnWidth = fmaxf(columnWidth, itemSize.width);
+ columnHeight += itemSize.height + 5;
+ ++rowsOccupied;
+
+ if(rowsOccupied >= columnRows) {
+ [columnWidths addObject:[NSNumber numberWithUnsignedInteger:columnWidth]];
+ [columnHeights addObject:[NSNumber numberWithUnsignedInteger:columnHeight]];
+ width += columnWidth + 10;
+
+ rowsOccupied = 0;
+ columnWidth = 0;
+ columnHeight = -5;
+ ++column;
+ }
+ }
+ NSAssert( !rowsOccupied, @"Too many rows/columns for available menu items.");
+
+ CGSize winSize = [[CCDirector sharedDirector] winSize];
+
+ column = 0; columnWidth = 0; columnRows = 0;
+ float x = -width / 2, y;
+
+ CCARRAY_FOREACH(children_, item){
+ if(columnRows == 0) {
+ columnRows = [(NSNumber *) [columns objectAtIndex:column] unsignedIntegerValue];
+ y = ([(NSNumber *) [columnHeights objectAtIndex:column] intValue] + winSize.height) / 2;
+ }
+
+ CGSize itemSize = item.contentSize;
+ columnWidth = fmaxf(columnWidth, itemSize.width);
+ [item setPosition:ccp(x + [(NSNumber *) [columnWidths objectAtIndex:column] unsignedIntegerValue] / 2,
+ y - winSize.height / 2)];
+
+ y -= itemSize.height + 10;
+ ++rowsOccupied;
+
+ if(rowsOccupied >= columnRows) {
+ x += columnWidth + 5;
+
+ rowsOccupied = 0;
+ columnRows = 0;
+ columnWidth = 0;
+ ++column;
+ }
+ }
+
+ [columns release];
+ [columnWidths release];
+ [columnHeights release];
+}
+
+#pragma mark Menu - Opacity Protocol
+
+/** Override synthesized setOpacity to recurse items */
+- (void) setOpacity:(GLubyte)newOpacity
+{
+ opacity_ = newOpacity;
+
+ id<CCRGBAProtocol> item;
+ CCARRAY_FOREACH(children_, item)
+ [item setOpacity:opacity_];
+}
+
+-(void) setColor:(ccColor3B)color
+{
+ color_ = color;
+
+ id<CCRGBAProtocol> item;
+ CCARRAY_FOREACH(children_, item)
+ [item setColor:color_];
+}
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2011 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "CCBlockSupport.h"
+
+#import "CCNode.h"
+#import "CCProtocols.h"
+
+@class CCSprite;
+
+#define kItemSize 32
+
+#pragma mark -
+#pragma mark CCMenuItem
+/** CCMenuItem base class
+ *
+ * Subclass CCMenuItem (or any subclass) to create your custom CCMenuItem objects.
+ */
+@interface CCMenuItem : CCNode
+{
+ NSInvocation *invocation;
+#if NS_BLOCKS_AVAILABLE
+ // used for menu items using a block
+ void (^block_)(id sender);
+#endif
+
+ BOOL isEnabled_;
+ BOOL isSelected_;
+}
+
+/** returns whether or not the item is selected
+@since v0.8.2
+*/
+@property (nonatomic,readonly) BOOL isSelected;
+
+/** Creates a CCMenuItem with a target/selector */
++(id) itemWithTarget:(id)target selector:(SEL)selector;
+
+/** Initializes a CCMenuItem with a target/selector */
+-(id) initWithTarget:(id)target selector:(SEL)selector;
+
+#if NS_BLOCKS_AVAILABLE
+/** Creates a CCMenuItem with the specified block.
+ The block will be "copied".
+ */
++(id) itemWithBlock:(void(^)(id sender))block;
+
+/** Initializes a CCMenuItem with the specified block.
+ The block will be "copied".
+*/
+-(id) initWithBlock:(void(^)(id sender))block;
+#endif
+
+/** Returns the outside box in points */
+-(CGRect) rect;
+
+/** Activate the item */
+-(void) activate;
+
+/** The item was selected (not activated), similar to "mouse-over" */
+-(void) selected;
+
+/** The item was unselected */
+-(void) unselected;
+
+/** Enable or disabled the CCMenuItem */
+-(void) setIsEnabled:(BOOL)enabled;
+/** Returns whether or not the CCMenuItem is enabled */
+-(BOOL) isEnabled;
+@end
+
+#pragma mark -
+#pragma mark CCMenuItemLabel
+
+/** An abstract class for "label" CCMenuItemLabel items
+ Any CCNode that supports the CCLabelProtocol protocol can be added.
+ Supported nodes:
+ - CCLabelBMFont
+ - CCLabelAtlas
+ - CCLabelTTF
+ */
+@interface CCMenuItemLabel : CCMenuItem <CCRGBAProtocol>
+{
+ CCNode<CCLabelProtocol, CCRGBAProtocol> *label_;
+ ccColor3B colorBackup;
+ ccColor3B disabledColor_;
+ float originalScale_;
+}
+
+/** the color that will be used to disable the item */
+@property (nonatomic,readwrite) ccColor3B disabledColor;
+
+/** Label that is rendered. It can be any CCNode that implements the CCLabelProtocol */
+@property (nonatomic,readwrite,assign) CCNode<CCLabelProtocol, CCRGBAProtocol>* label;
+
+/** creates a CCMenuItemLabel with a Label. Target and selector will be nill */
++(id) itemWithLabel:(CCNode<CCLabelProtocol,CCRGBAProtocol>*)label;
+
+/** creates a CCMenuItemLabel with a Label, target and selector */
++(id) itemWithLabel:(CCNode<CCLabelProtocol,CCRGBAProtocol>*)label target:(id)target selector:(SEL)selector;
+
+/** initializes a CCMenuItemLabel with a Label, target and selector */
+-(id) initWithLabel:(CCNode<CCLabelProtocol,CCRGBAProtocol>*)label target:(id)target selector:(SEL)selector;
+
+#if NS_BLOCKS_AVAILABLE
+/** creates a CCMenuItemLabel with a Label and a block to execute.
+ The block will be "copied".
+ */
++(id) itemWithLabel:(CCNode<CCLabelProtocol,CCRGBAProtocol>*)label block:(void(^)(id sender))block;
+
+/** initializes a CCMenuItemLabel with a Label and a block to execute.
+ The block will be "copied".
+ */
+-(id) initWithLabel:(CCNode<CCLabelProtocol,CCRGBAProtocol>*)label block:(void(^)(id sender))block;
+#endif
+
+/** sets a new string to the inner label */
+-(void) setString:(NSString*)label;
+
+/** Enable or disabled the CCMenuItemFont
+ @warning setIsEnabled changes the RGB color of the font
+ */
+-(void) setIsEnabled: (BOOL)enabled;
+@end
+
+#pragma mark -
+#pragma mark CCMenuItemAtlasFont
+
+/** A CCMenuItemAtlasFont
+ Helper class that creates a MenuItemLabel class with a LabelAtlas
+ */
+@interface CCMenuItemAtlasFont : CCMenuItemLabel
+{
+}
+
+/** creates a menu item from a string and atlas with a target/selector */
++(id) itemFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap;
+
+/** creates a menu item from a string and atlas. Use it with MenuItemToggle */
++(id) itemFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap target:(id) rec selector:(SEL) cb;
+
+/** initializes a menu item from a string and atlas with a target/selector */
+-(id) initFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap target:(id) rec selector:(SEL) cb;
+
+#if NS_BLOCKS_AVAILABLE
+/** creates a menu item from a string and atlas. Use it with MenuItemToggle.
+ The block will be "copied".
+ */
++(id) itemFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap block:(void(^)(id sender))block;
+
+/** initializes a menu item from a string and atlas with a block.
+ The block will be "copied".
+ */
+-(id) initFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap block:(void(^)(id sender))block;
+#endif
+
+@end
+
+#pragma mark -
+#pragma mark CCMenuItemFont
+
+/** A CCMenuItemFont
+ Helper class that creates a CCMenuItemLabel class with a Label
+ */
+@interface CCMenuItemFont : CCMenuItemLabel
+{
+}
+/** set font size */
++(void) setFontSize: (int) s;
+
+/** get font size */
++(int) fontSize;
+
+/** set the font name */
++(void) setFontName: (NSString*) n;
+
+/** get the font name */
++(NSString*) fontName;
+
+/** creates a menu item from a string without target/selector. To be used with CCMenuItemToggle */
++(id) itemFromString: (NSString*) value;
+
+/** creates a menu item from a string with a target/selector */
++(id) itemFromString: (NSString*) value target:(id) r selector:(SEL) s;
+
+/** initializes a menu item from a string with a target/selector */
+-(id) initFromString: (NSString*) value target:(id) r selector:(SEL) s;
+
+#if NS_BLOCKS_AVAILABLE
+/** creates a menu item from a string with the specified block.
+ The block will be "copied".
+ */
++(id) itemFromString: (NSString*) value block:(void(^)(id sender))block;
+
+/** initializes a menu item from a string with the specified block.
+ The block will be "copied".
+ */
+-(id) initFromString: (NSString*) value block:(void(^)(id sender))block;
+#endif
+@end
+
+#pragma mark -
+#pragma mark CCMenuItemSprite
+
+/** CCMenuItemSprite accepts CCNode<CCRGBAProtocol> objects as items.
+ The images has 3 different states:
+ - unselected image
+ - selected image
+ - disabled image
+
+ @since v0.8.0
+ */
+@interface CCMenuItemSprite : CCMenuItem <CCRGBAProtocol>
+{
+ CCNode<CCRGBAProtocol> *normalImage_, *selectedImage_, *disabledImage_;
+}
+
+// weak references
+
+/** the image used when the item is not selected */
+@property (nonatomic,readwrite,assign) CCNode<CCRGBAProtocol> *normalImage;
+/** the image used when the item is selected */
+@property (nonatomic,readwrite,assign) CCNode<CCRGBAProtocol> *selectedImage;
+/** the image used when the item is disabled */
+@property (nonatomic,readwrite,assign) CCNode<CCRGBAProtocol> *disabledImage;
+
+/** creates a menu item with a normal and selected image*/
++(id) itemFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite;
+/** creates a menu item with a normal and selected image with target/selector */
++(id) itemFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite target:(id)target selector:(SEL)selector;
+/** creates a menu item with a normal,selected and disabled image with target/selector */
++(id) itemFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite disabledSprite:(CCNode<CCRGBAProtocol>*)disabledSprite target:(id)target selector:(SEL)selector;
+/** initializes a menu item with a normal, selected and disabled image with target/selector */
+-(id) initFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite disabledSprite:(CCNode<CCRGBAProtocol>*)disabledSprite target:(id)target selector:(SEL)selector;
+
+#if NS_BLOCKS_AVAILABLE
+/** creates a menu item with a normal and selected image with a block.
+ The block will be "copied".
+ */
++(id) itemFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite block:(void(^)(id sender))block;
+/** creates a menu item with a normal,selected and disabled image with a block.
+ The block will be "copied".
+ */
++(id) itemFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite disabledSprite:(CCNode<CCRGBAProtocol>*)disabledSprite block:(void(^)(id sender))block;
+/** initializes a menu item with a normal, selected and disabled image with a block.
+ The block will be "copied".
+ */
+-(id) initFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite disabledSprite:(CCNode<CCRGBAProtocol>*)disabledSprite block:(void(^)(id sender))block;
+#endif
+
+@end
+
+#pragma mark -
+#pragma mark CCMenuItemImage
+
+/** CCMenuItemImage accepts images as items.
+ The images has 3 different states:
+ - unselected image
+ - selected image
+ - disabled image
+
+ For best results try that all images are of the same size
+ */
+@interface CCMenuItemImage : CCMenuItemSprite
+{
+}
+
+/** creates a menu item with a normal and selected image*/
++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2;
+/** creates a menu item with a normal and selected image with target/selector */
++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 target:(id) r selector:(SEL) s;
+/** creates a menu item with a normal,selected and disabled image with target/selector */
++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage:(NSString*) value3 target:(id) r selector:(SEL) s;
+/** initializes a menu item with a normal, selected and disabled image with target/selector */
+-(id) initFromNormalImage: (NSString*) value selectedImage:(NSString*)value2 disabledImage:(NSString*) value3 target:(id) r selector:(SEL) s;
+#if NS_BLOCKS_AVAILABLE
+/** creates a menu item with a normal and selected image with a block.
+ The block will be "copied".
+ */
++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 block:(void(^)(id sender))block;
+/** creates a menu item with a normal,selected and disabled image with a block.
+ The block will be "copied".
+*/
++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage:(NSString*) value3 block:(void(^)(id sender))block;
+/** initializes a menu item with a normal, selected and disabled image with a block.
+ The block will be "copied".
+*/
+-(id) initFromNormalImage: (NSString*) value selectedImage:(NSString*)value2 disabledImage:(NSString*) value3 block:(void(^)(id sender))block;
+#endif
+@end
+
+#pragma mark -
+#pragma mark CCMenuItemToggle
+
+/** A CCMenuItemToggle
+ A simple container class that "toggles" it's inner items
+ The inner itmes can be any MenuItem
+ */
+@interface CCMenuItemToggle : CCMenuItem <CCRGBAProtocol>
+{
+ NSUInteger selectedIndex_;
+ NSMutableArray* subItems_;
+ GLubyte opacity_;
+ ccColor3B color_;
+}
+
+/** conforms with CCRGBAProtocol protocol */
+@property (nonatomic,readonly) GLubyte opacity;
+/** conforms with CCRGBAProtocol protocol */
+@property (nonatomic,readonly) ccColor3B color;
+
+/** returns the selected item */
+@property (nonatomic,readwrite) NSUInteger selectedIndex;
+/** NSMutableArray that contains the subitems. You can add/remove items in runtime, and you can replace the array with a new one.
+ @since v0.7.2
+ */
+@property (nonatomic,readwrite,retain) NSMutableArray *subItems;
+
+/** creates a menu item from a list of items with a target/selector */
++(id) itemWithTarget:(id)t selector:(SEL)s items:(CCMenuItem*) item, ... NS_REQUIRES_NIL_TERMINATION;
+
+/** initializes a menu item from a list of items with a target selector */
+-(id) initWithTarget:(id)t selector:(SEL)s items:(CCMenuItem*) item vaList:(va_list) args;
+
+#if NS_BLOCKS_AVAILABLE
+/** creates a menu item from a list of items and executes the given block when the item is selected.
+ The block will be "copied".
+ */
++(id) itemWithBlock:(void(^)(id sender))block items:(CCMenuItem*)item, ... NS_REQUIRES_NIL_TERMINATION;
+
+/** initializes a menu item from a list of items with a block.
+ The block will be "copied".
+ */
+-(id) initWithBlock:(void (^)(id))block items:(CCMenuItem*)item vaList:(va_list)args;
+#endif
+
+/** return the selected item */
+-(CCMenuItem*) selectedItem;
+@end
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2011 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "CCMenuItem.h"
+#import "CCLabelTTF.h"
+#import "CCLabelAtlas.h"
+#import "CCActionInterval.h"
+#import "CCSprite.h"
+#import "Support/CGPointExtension.h"
+
+static int _fontSize = kItemSize;
+static NSString *_fontName = @"Marker Felt";
+static BOOL _fontNameRelease = NO;
+
+enum {
+ kCurrentItem = 0xc0c05001,
+};
+
+enum {
+ kZoomActionTag = 0xc0c05002,
+};
+
+
+
+#pragma mark -
+#pragma mark CCMenuItem
+
+@implementation CCMenuItem
+
+@synthesize isSelected=isSelected_;
+-(id) init
+{
+ NSAssert(NO, @"MenuItemInit: Init not supported.");
+ [self release];
+ return nil;
+}
+
++(id) itemWithTarget:(id) r selector:(SEL) s
+{
+ return [[[self alloc] initWithTarget:r selector:s] autorelease];
+}
+
+-(id) initWithTarget:(id) rec selector:(SEL) cb
+{
+ if((self=[super init]) ) {
+
+ anchorPoint_ = ccp(0.5f, 0.5f);
+ NSMethodSignature * sig = nil;
+
+ if( rec && cb ) {
+ sig = [rec methodSignatureForSelector:cb];
+
+ invocation = nil;
+ invocation = [NSInvocation invocationWithMethodSignature:sig];
+ [invocation setTarget:rec];
+ [invocation setSelector:cb];
+#if NS_BLOCKS_AVAILABLE
+ if ([sig numberOfArguments] == 3)
+#endif
+ [invocation setArgument:&self atIndex:2];
+
+ [invocation retain];
+ }
+
+ isEnabled_ = YES;
+ isSelected_ = NO;
+ }
+
+ return self;
+}
+
+#if NS_BLOCKS_AVAILABLE
+
++(id) itemWithBlock:(void(^)(id sender))block {
+ return [[[self alloc] initWithBlock:block] autorelease];
+}
+
+-(id) initWithBlock:(void(^)(id sender))block {
+ block_ = [block copy];
+ return [self initWithTarget:block_ selector:@selector(ccCallbackBlockWithSender:)];
+}
+
+#endif // NS_BLOCKS_AVAILABLE
+
+-(void) dealloc
+{
+ [invocation release];
+
+#if NS_BLOCKS_AVAILABLE
+ [block_ release];
+#endif
+
+ [super dealloc];
+}
+
+-(void) selected
+{
+ isSelected_ = YES;
+}
+
+-(void) unselected
+{
+ isSelected_ = NO;
+}
+
+-(void) activate
+{
+ if(isEnabled_)
+ [invocation invoke];
+}
+
+-(void) setIsEnabled: (BOOL)enabled
+{
+ isEnabled_ = enabled;
+}
+
+-(BOOL) isEnabled
+{
+ return isEnabled_;
+}
+
+-(CGRect) rect
+{
+ return CGRectMake( position_.x - contentSize_.width*anchorPoint_.x,
+ position_.y - contentSize_.height*anchorPoint_.y,
+ contentSize_.width, contentSize_.height);
+}
+
+@end
+
+
+#pragma mark -
+#pragma mark CCMenuItemLabel
+
+@implementation CCMenuItemLabel
+
+@synthesize disabledColor = disabledColor_;
+
++(id) itemWithLabel:(CCNode<CCLabelProtocol,CCRGBAProtocol>*)label target:(id)target selector:(SEL)selector
+{
+ return [[[self alloc] initWithLabel:label target:target selector:selector] autorelease];
+}
+
++(id) itemWithLabel:(CCNode<CCLabelProtocol,CCRGBAProtocol>*)label
+{
+ return [[[self alloc] initWithLabel:label target:nil selector:NULL] autorelease];
+}
+
+-(id) initWithLabel:(CCNode<CCLabelProtocol,CCRGBAProtocol>*)label target:(id)target selector:(SEL)selector
+{
+ if( (self=[super initWithTarget:target selector:selector]) ) {
+ originalScale_ = 1;
+ colorBackup = ccWHITE;
+ disabledColor_ = ccc3( 126,126,126);
+ self.label = label;
+
+ }
+ return self;
+}
+
+#if NS_BLOCKS_AVAILABLE
+
++(id) itemWithLabel:(CCNode<CCLabelProtocol,CCRGBAProtocol>*)label block:(void(^)(id sender))block {
+ return [[[self alloc] initWithLabel:label block:block] autorelease];
+}
+
+-(id) initWithLabel:(CCNode<CCLabelProtocol,CCRGBAProtocol>*)label block:(void(^)(id sender))block {
+ block_ = [block copy];
+ return [self initWithLabel:label target:block_ selector:@selector(ccCallbackBlockWithSender:)];
+}
+
+#endif // NS_BLOCKS_AVAILABLE
+
+-(CCNode<CCLabelProtocol, CCRGBAProtocol>*) label
+{
+ return label_;
+}
+-(void) setLabel:(CCNode<CCLabelProtocol, CCRGBAProtocol>*) label
+{
+ if( label != label_ ) {
+ [self removeChild:label_ cleanup:YES];
+ [self addChild:label];
+
+ label_ = label;
+ label_.anchorPoint = ccp(0,0);
+
+ [self setContentSize:[label_ contentSize]];
+ }
+}
+
+-(void) setString:(NSString *)string
+{
+ [label_ setString:string];
+ [self setContentSize: [label_ contentSize]];
+}
+
+-(void) activate {
+ if(isEnabled_) {
+ [self stopAllActions];
+
+ self.scale = originalScale_;
+
+ [super activate];
+ }
+}
+
+-(void) selected
+{
+ // subclass to change the default action
+ if(isEnabled_) {
+ [super selected];
+
+ CCAction *action = [self getActionByTag:kZoomActionTag];
+ if( action )
+ [self stopAction:action];
+ else
+ originalScale_ = self.scale;
+
+ CCAction *zoomAction = [CCScaleTo actionWithDuration:0.1f scale:originalScale_ * 1.2f];
+ zoomAction.tag = kZoomActionTag;
+ [self runAction:zoomAction];
+ }
+}
+
+-(void) unselected
+{
+ // subclass to change the default action
+ if(isEnabled_) {
+ [super unselected];
+ [self stopActionByTag:kZoomActionTag];
+ CCAction *zoomAction = [CCScaleTo actionWithDuration:0.1f scale:originalScale_];
+ zoomAction.tag = kZoomActionTag;
+ [self runAction:zoomAction];
+ }
+}
+
+-(void) setIsEnabled: (BOOL)enabled
+{
+ if( isEnabled_ != enabled ) {
+ if(enabled == NO) {
+ colorBackup = [label_ color];
+ [label_ setColor: disabledColor_];
+ }
+ else
+ [label_ setColor:colorBackup];
+ }
+
+ [super setIsEnabled:enabled];
+}
+
+- (void) setOpacity: (GLubyte)opacity
+{
+ [label_ setOpacity:opacity];
+}
+-(GLubyte) opacity
+{
+ return [label_ opacity];
+}
+-(void) setColor:(ccColor3B)color
+{
+ [label_ setColor:color];
+}
+-(ccColor3B) color
+{
+ return [label_ color];
+}
+@end
+
+#pragma mark -
+#pragma mark CCMenuItemAtlasFont
+
+@implementation CCMenuItemAtlasFont
+
++(id) itemFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap
+{
+ return [CCMenuItemAtlasFont itemFromString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap target:nil selector:nil];
+}
+
++(id) itemFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap target:(id) rec selector:(SEL) cb
+{
+ return [[[self alloc] initFromString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap target:rec selector:cb] autorelease];
+}
+
+-(id) initFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap target:(id) rec selector:(SEL) cb
+{
+ NSAssert( [value length] != 0, @"value length must be greater than 0");
+
+ CCLabelAtlas *label = [[CCLabelAtlas alloc] initWithString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap];
+ [label autorelease];
+
+ if((self=[super initWithLabel:label target:rec selector:cb]) ) {
+ // do something ?
+ }
+
+ return self;
+}
+
+#if NS_BLOCKS_AVAILABLE
++(id) itemFromString:(NSString*)value charMapFile:(NSString*)charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap block:(void(^)(id sender))block {
+ return [[[self alloc] initFromString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap block:block] autorelease];
+}
+
+-(id) initFromString:(NSString*)value charMapFile:(NSString*)charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap block:(void(^)(id sender))block {
+ block_ = [block copy];
+ return [self initFromString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap target:block_ selector:@selector(ccCallbackBlockWithSender:)];
+}
+#endif // NS_BLOCKS_AVAILABLE
+
+-(void) dealloc
+{
+ [super dealloc];
+}
+@end
+
+
+#pragma mark -
+#pragma mark CCMenuItemFont
+
+@implementation CCMenuItemFont
+
++(void) setFontSize: (int) s
+{
+ _fontSize = s;
+}
+
++(int) fontSize
+{
+ return _fontSize;
+}
+
++(void) setFontName: (NSString*) n
+{
+ if( _fontNameRelease )
+ [_fontName release];
+
+ _fontName = [n retain];
+ _fontNameRelease = YES;
+}
+
++(NSString*) fontName
+{
+ return _fontName;
+}
+
++(id) itemFromString: (NSString*) value target:(id) r selector:(SEL) s
+{
+ return [[[self alloc] initFromString: value target:r selector:s] autorelease];
+}
+
++(id) itemFromString: (NSString*) value
+{
+ return [[[self alloc] initFromString: value target:nil selector:nil] autorelease];
+}
+
+-(id) initFromString: (NSString*) value target:(id) rec selector:(SEL) cb
+{
+ NSAssert( [value length] != 0, @"Value length must be greater than 0");
+
+ CCLabelTTF *label = [CCLabelTTF labelWithString:value fontName:_fontName fontSize:_fontSize];
+
+ if((self=[super initWithLabel:label target:rec selector:cb]) ) {
+ // do something ?
+ }
+
+ return self;
+}
+
+#if NS_BLOCKS_AVAILABLE
++(id) itemFromString: (NSString*) value block:(void(^)(id sender))block {
+ return [[[self alloc] initFromString:value block:block] autorelease];
+}
+
+-(id) initFromString: (NSString*) value block:(void(^)(id sender))block {
+ block_ = [block copy];
+ return [self initFromString:value target:block_ selector:@selector(ccCallbackBlockWithSender:)];
+}
+#endif // NS_BLOCKS_AVAILABLE
+
+@end
+
+#pragma mark -
+#pragma mark CCMenuItemSprite
+@implementation CCMenuItemSprite
+
+@synthesize normalImage=normalImage_, selectedImage=selectedImage_, disabledImage=disabledImage_;
+
++(id) itemFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite
+{
+ return [self itemFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:nil target:nil selector:nil];
+}
++(id) itemFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite target:(id)target selector:(SEL)selector
+{
+ return [self itemFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:nil target:target selector:selector];
+}
++(id) itemFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite disabledSprite:(CCNode<CCRGBAProtocol>*)disabledSprite target:(id)target selector:(SEL)selector
+{
+ return [[[self alloc] initFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:disabledSprite target:target selector:selector] autorelease];
+}
+-(id) initFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite disabledSprite:(CCNode<CCRGBAProtocol>*)disabledSprite target:(id)target selector:(SEL)selector
+{
+ if( (self=[super initWithTarget:target selector:selector]) ) {
+
+ self.normalImage = normalSprite;
+ self.selectedImage = selectedSprite;
+ self.disabledImage = disabledSprite;
+
+ [self setContentSize: [normalImage_ contentSize]];
+ }
+ return self;
+}
+
+#if NS_BLOCKS_AVAILABLE
++(id) itemFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite block:(void(^)(id sender))block {
+ return [self itemFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:nil block:block];
+}
+
++(id) itemFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite disabledSprite:(CCNode<CCRGBAProtocol>*)disabledSprite block:(void(^)(id sender))block {
+ return [[[self alloc] initFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:disabledSprite block:block] autorelease];
+}
+
+-(id) initFromNormalSprite:(CCNode<CCRGBAProtocol>*)normalSprite selectedSprite:(CCNode<CCRGBAProtocol>*)selectedSprite disabledSprite:(CCNode<CCRGBAProtocol>*)disabledSprite block:(void(^)(id sender))block {
+ block_ = [block copy];
+ return [self initFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:disabledSprite target:block_ selector:@selector(ccCallbackBlockWithSender:)];
+}
+#endif // NS_BLOCKS_AVAILABLE
+
+
+-(void) setNormalImage:(CCNode <CCRGBAProtocol>*)image
+{
+ if( image != normalImage_ ) {
+ image.anchorPoint = ccp(0,0);
+ image.visible = YES;
+
+ [self removeChild:normalImage_ cleanup:YES];
+ [self addChild:image];
+
+ normalImage_ = image;
+ }
+}
+
+-(void) setSelectedImage:(CCNode <CCRGBAProtocol>*)image
+{
+ if( image != selectedImage_ ) {
+ image.anchorPoint = ccp(0,0);
+ image.visible = NO;
+
+ [self removeChild:selectedImage_ cleanup:YES];
+ [self addChild:image];
+
+ selectedImage_ = image;
+ }
+}
+
+-(void) setDisabledImage:(CCNode <CCRGBAProtocol>*)image
+{
+ if( image != disabledImage_ ) {
+ image.anchorPoint = ccp(0,0);
+ image.visible = NO;
+
+ [self removeChild:disabledImage_ cleanup:YES];
+ [self addChild:image];
+
+ disabledImage_ = image;
+ }
+}
+
+#pragma mark CCMenuItemImage - CCRGBAProtocol protocol
+- (void) setOpacity: (GLubyte)opacity
+{
+ [normalImage_ setOpacity:opacity];
+ [selectedImage_ setOpacity:opacity];
+ [disabledImage_ setOpacity:opacity];
+}
+
+-(void) setColor:(ccColor3B)color
+{
+ [normalImage_ setColor:color];
+ [selectedImage_ setColor:color];
+ [disabledImage_ setColor:color];
+}
+
+-(GLubyte) opacity
+{
+ return [normalImage_ opacity];
+}
+
+-(ccColor3B) color
+{
+ return [normalImage_ color];
+}
+
+-(void) selected
+{
+ [super selected];
+
+ if( selectedImage_ ) {
+ [normalImage_ setVisible:NO];
+ [selectedImage_ setVisible:YES];
+ [disabledImage_ setVisible:NO];
+
+ } else { // there is not selected image
+
+ [normalImage_ setVisible:YES];
+ [selectedImage_ setVisible:NO];
+ [disabledImage_ setVisible:NO];
+ }
+}
+
+-(void) unselected
+{
+ [super unselected];
+ [normalImage_ setVisible:YES];
+ [selectedImage_ setVisible:NO];
+ [disabledImage_ setVisible:NO];
+}
+
+-(void) setIsEnabled:(BOOL)enabled
+{
+ [super setIsEnabled:enabled];
+
+ if( enabled ) {
+ [normalImage_ setVisible:YES];
+ [selectedImage_ setVisible:NO];
+ [disabledImage_ setVisible:NO];
+
+ } else {
+ if( disabledImage_ ) {
+ [normalImage_ setVisible:NO];
+ [selectedImage_ setVisible:NO];
+ [disabledImage_ setVisible:YES];
+ } else {
+ [normalImage_ setVisible:YES];
+ [selectedImage_ setVisible:NO];
+ [disabledImage_ setVisible:NO];
+ }
+ }
+}
+
+@end
+
+#pragma mark -
+#pragma mark CCMenuItemImage
+
+@implementation CCMenuItemImage
+
++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2
+{
+ return [self itemFromNormalImage:value selectedImage:value2 disabledImage: nil target:nil selector:nil];
+}
+
++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 target:(id) t selector:(SEL) s
+{
+ return [self itemFromNormalImage:value selectedImage:value2 disabledImage: nil target:t selector:s];
+}
+
++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage: (NSString*) value3
+{
+ return [[[self alloc] initFromNormalImage:value selectedImage:value2 disabledImage:value3 target:nil selector:nil] autorelease];
+}
+
++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage: (NSString*) value3 target:(id) t selector:(SEL) s
+{
+ return [[[self alloc] initFromNormalImage:value selectedImage:value2 disabledImage:value3 target:t selector:s] autorelease];
+}
+
+-(id) initFromNormalImage: (NSString*) normalI selectedImage:(NSString*)selectedI disabledImage: (NSString*) disabledI target:(id)t selector:(SEL)sel
+{
+ CCNode<CCRGBAProtocol> *normalImage = [CCSprite spriteWithFile:normalI];
+ CCNode<CCRGBAProtocol> *selectedImage = nil;
+ CCNode<CCRGBAProtocol> *disabledImage = nil;
+
+ if( selectedI )
+ selectedImage = [CCSprite spriteWithFile:selectedI];
+ if(disabledI)
+ disabledImage = [CCSprite spriteWithFile:disabledI];
+
+ return [self initFromNormalSprite:normalImage selectedSprite:selectedImage disabledSprite:disabledImage target:t selector:sel];
+}
+
+#if NS_BLOCKS_AVAILABLE
+
++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 block:(void(^)(id sender))block {
+ return [self itemFromNormalImage:value selectedImage:value2 disabledImage:nil block:block];
+}
+
++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage:(NSString*) value3 block:(void(^)(id sender))block {
+ return [[[self alloc] initFromNormalImage:value selectedImage:value2 disabledImage:value3 block:block] autorelease];
+}
+
+-(id) initFromNormalImage: (NSString*) value selectedImage:(NSString*)value2 disabledImage:(NSString*) value3 block:(void(^)(id sender))block {
+ block_ = [block copy];
+ return [self initFromNormalImage:value selectedImage:value2 disabledImage:value3 target:block_ selector:@selector(ccCallbackBlockWithSender:)];
+}
+
+#endif // NS_BLOCKS_AVAILABLE
+
+@end
+
+#pragma mark -
+#pragma mark CCMenuItemToggle
+
+//
+// MenuItemToggle
+//
+@implementation CCMenuItemToggle
+
+@synthesize subItems = subItems_;
+@synthesize opacity = opacity_, color = color_;
+
++(id) itemWithTarget: (id)t selector: (SEL)sel items: (CCMenuItem*) item, ...
+{
+ va_list args;
+ va_start(args, item);
+
+ id s = [[[self alloc] initWithTarget: t selector:sel items: item vaList:args] autorelease];
+
+ va_end(args);
+ return s;
+}
+
+-(id) initWithTarget: (id)t selector: (SEL)sel items:(CCMenuItem*) item vaList: (va_list) args
+{
+ if( (self=[super initWithTarget:t selector:sel]) ) {
+
+ self.subItems = [NSMutableArray arrayWithCapacity:2];
+
+ int z = 0;
+ CCMenuItem *i = item;
+ while(i) {
+ z++;
+ [subItems_ addObject:i];
+ i = va_arg(args, CCMenuItem*);
+ }
+
+ selectedIndex_ = NSUIntegerMax;
+ [self setSelectedIndex:0];
+ }
+
+ return self;
+}
+
+#if NS_BLOCKS_AVAILABLE
+
++(id) itemWithBlock:(void(^)(id sender))block items:(CCMenuItem*)item, ... {
+ va_list args;
+ va_start(args, item);
+
+ id s = [[[self alloc] initWithBlock:block items:item vaList:args] autorelease];
+
+ va_end(args);
+ return s;
+}
+
+-(id) initWithBlock:(void (^)(id))block items:(CCMenuItem*)item vaList:(va_list)args {
+ block_ = [block copy];
+ return [self initWithTarget:block_ selector:@selector(ccCallbackBlockWithSender:) items:item vaList:args];
+}
+
+#endif // NS_BLOCKS_AVAILABLE
+
+-(void) dealloc
+{
+ [subItems_ release];
+ [super dealloc];
+}
+
+-(void)setSelectedIndex:(NSUInteger)index
+{
+ if( index != selectedIndex_ ) {
+ selectedIndex_=index;
+ [self removeChildByTag:kCurrentItem cleanup:NO];
+
+ CCMenuItem *item = [subItems_ objectAtIndex:selectedIndex_];
+ [self addChild:item z:0 tag:kCurrentItem];
+
+ CGSize s = [item contentSize];
+ [self setContentSize: s];
+ item.position = ccp( s.width/2, s.height/2 );
+ }
+}
+
+-(NSUInteger) selectedIndex
+{
+ return selectedIndex_;
+}
+
+
+-(void) selected
+{
+ [super selected];
+ [[subItems_ objectAtIndex:selectedIndex_] selected];
+}
+
+-(void) unselected
+{
+ [super unselected];
+ [[subItems_ objectAtIndex:selectedIndex_] unselected];
+}
+
+-(void) activate
+{
+ // update index
+ if( isEnabled_ ) {
+ NSUInteger newIndex = (selectedIndex_ + 1) % [subItems_ count];
+ [self setSelectedIndex:newIndex];
+
+ }
+
+ [super activate];
+}
+
+-(void) setIsEnabled: (BOOL)enabled
+{
+ [super setIsEnabled:enabled];
+ for(CCMenuItem* item in subItems_)
+ [item setIsEnabled:enabled];
+}
+
+-(CCMenuItem*) selectedItem
+{
+ return [subItems_ objectAtIndex:selectedIndex_];
+}
+
+#pragma mark CCMenuItemToggle - CCRGBAProtocol protocol
+
+- (void) setOpacity: (GLubyte)opacity
+{
+ opacity_ = opacity;
+ for(CCMenuItem<CCRGBAProtocol>* item in subItems_)
+ [item setOpacity:opacity];
+}
+
+- (void) setColor:(ccColor3B)color
+{
+ color_ = color;
+ for(CCMenuItem<CCRGBAProtocol>* item in subItems_)
+ [item setColor:color];
+}
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008, 2009 Jason Booth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import <Foundation/Foundation.h>
+#import "CCNode.h"
+#import "CCRibbon.h"
+
+/**
+ * CCMotionStreak manages a Ribbon based on it's motion in absolute space.
+ * You construct it with a fadeTime, minimum segment size, texture path, texture
+ * length and color. The fadeTime controls how long it takes each vertex in
+ * the streak to fade out, the minimum segment size it how many pixels the
+ * streak will move before adding a new ribbon segement, and the texture
+ * length is the how many pixels the texture is stretched across. The texture
+ * is vertically aligned along the streak segemnts.
+ *
+ * Limitations:
+ * CCMotionStreak, by default, will use the GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA blending function.
+ * This blending function might not be the correct one for certain textures.
+ * But you can change it by using:
+ * [obj setBlendFunc: (ccBlendfunc) {new_src_blend_func, new_dst_blend_func}];
+ *
+ * @since v0.8.1
+ */
+@interface CCMotionStreak : CCNode <CCTextureProtocol>
+{
+ CCRibbon* ribbon_;
+ float segThreshold_;
+ float width_;
+ CGPoint lastLocation_;
+}
+
+/** Ribbon used by MotionStreak (weak reference) */
+@property (nonatomic,readonly) CCRibbon *ribbon;
+
+/** creates the a MotionStreak. The image will be loaded using the TextureMgr. */
++(id)streakWithFade:(float)fade minSeg:(float)seg image:(NSString*)path width:(float)width length:(float)length color:(ccColor4B)color;
+
+/** initializes a MotionStreak. The file will be loaded using the TextureMgr. */
+-(id)initWithFade:(float)fade minSeg:(float)seg image:(NSString*)path width:(float)width length:(float)length color:(ccColor4B)color;
+
+/** polling function */
+-(void)update:(ccTime)delta;
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008, 2009 Jason Booth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ *********************************************************
+ *
+ * Motion Streak manages a Ribbon based on it's motion in absolute space.
+ * You construct it with a fadeTime, minimum segment size, texture path, texture
+ * length and color. The fadeTime controls how long it takes each vertex in
+ * the streak to fade out, the minimum segment size it how many pixels the
+ * streak will move before adding a new ribbon segement, and the texture
+ * length is the how many pixels the texture is stretched across. The texture
+ * is vertically aligned along the streak segemnts.
+ */
+
+#import "CCMotionStreak.h"
+#import "Support/CGPointExtension.h"
+
+@implementation CCMotionStreak
+
+@synthesize ribbon = ribbon_;
+
++(id)streakWithFade:(float)fade minSeg:(float)seg image:(NSString*)path width:(float)width length:(float)length color:(ccColor4B)color
+{
+ return [[[self alloc] initWithFade:(float)fade minSeg:seg image:path width:width length:length color:color] autorelease];
+}
+
+-(id)initWithFade:(float)fade minSeg:(float)seg image:(NSString*)path width:(float)width length:(float)length color:(ccColor4B)color
+{
+ if( (self=[super init])) {
+ segThreshold_ = seg;
+ width_ = width;
+ lastLocation_ = CGPointZero;
+ ribbon_ = [CCRibbon ribbonWithWidth:width_ image:path length:length color:color fade:fade];
+ [self addChild:ribbon_];
+
+ // update ribbon position. Use schedule:interval and not scheduleUpdated. issue #1075
+ [self schedule:@selector(update:) interval:0];
+ }
+ return self;
+}
+
+-(void)update:(ccTime)delta
+{
+ CGPoint location = [self convertToWorldSpace:CGPointZero];
+ [ribbon_ setPosition:ccp(-1*location.x, -1*location.y)];
+ float len = ccpLength(ccpSub(lastLocation_, location));
+ if (len > segThreshold_)
+ {
+ [ribbon_ addPointAt:location width:width_];
+ lastLocation_ = location;
+ }
+ [ribbon_ update:delta];
+}
+
+
+-(void)dealloc
+{
+ [super dealloc];
+}
+
+#pragma mark MotionStreak - CocosNodeTexture protocol
+
+-(void) setTexture:(CCTexture2D*) texture
+{
+ [ribbon_ setTexture: texture];
+}
+
+-(CCTexture2D*) texture
+{
+ return [ribbon_ texture];
+}
+
+-(ccBlendFunc) blendFunc
+{
+ return [ribbon_ blendFunc];
+}
+
+-(void) setBlendFunc:(ccBlendFunc)blendFunc
+{
+ [ribbon_ setBlendFunc:blendFunc];
+}
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Valentin Milea
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#import <Availability.h>
+
+#import "Platforms/CCGL.h"
+#import "CCAction.h"
+#import "ccTypes.h"
+#import "CCTexture2D.h"
+#import "CCProtocols.h"
+#import "ccConfig.h"
+#import "Support/CCArray.h"
+
+enum {
+ kCCNodeTagInvalid = -1,
+};
+
+@class CCCamera;
+@class CCGridBase;
+
+/** CCNode is the main element. Anything thats gets drawn or contains things that get drawn is a CCNode.
+ The most popular CCNodes are: CCScene, CCLayer, CCSprite, CCMenu.
+
+ The main features of a CCNode are:
+ - They can contain other CCNode nodes (addChild, getChildByTag, removeChild, etc)
+ - They can schedule periodic callback (schedule, unschedule, etc)
+ - They can execute actions (runAction, stopAction, etc)
+
+ Some CCNode nodes provide extra functionality for them or their children.
+
+ Subclassing a CCNode usually means (one/all) of:
+ - overriding init to initialize resources and schedule callbacks
+ - create callbacks to handle the advancement of time
+ - overriding draw to render the node
+
+ Features of CCNode:
+ - position
+ - scale (x, y)
+ - rotation (in degrees, clockwise)
+ - CCCamera (an interface to gluLookAt )
+ - CCGridBase (to do mesh transformations)
+ - anchor point
+ - size
+ - visible
+ - z-order
+ - openGL z position
+
+ Default values:
+ - rotation: 0
+ - position: (x=0,y=0)
+ - scale: (x=1,y=1)
+ - contentSize: (x=0,y=0)
+ - anchorPoint: (x=0,y=0)
+
+ Limitations:
+ - A CCNode is a "void" object. It doesn't have a texture
+
+ Order in transformations with grid disabled
+ -# The node will be translated (position)
+ -# The node will be rotated (rotation)
+ -# The node will be scaled (scale)
+ -# The node will be moved according to the camera values (camera)
+
+ Order in transformations with grid enabled
+ -# The node will be translated (position)
+ -# The node will be rotated (rotation)
+ -# The node will be scaled (scale)
+ -# The grid will capture the screen
+ -# The node will be moved according to the camera values (camera)
+ -# The grid will render the captured screen
+
+ Camera:
+ - Each node has a camera. By default it points to the center of the CCNode.
+ */
+@interface CCNode : NSObject
+{
+ // rotation angle
+ float rotation_;
+
+ // scaling factors
+ float scaleX_, scaleY_;
+
+ // position of the node
+ CGPoint position_;
+ CGPoint positionInPixels_;
+
+ // skew angles
+ float skewX_, skewY_;
+
+ // is visible
+ BOOL visible_;
+
+ // anchor point in pixels
+ CGPoint anchorPointInPixels_;
+ // anchor point normalized
+ CGPoint anchorPoint_;
+ // If YES the transformtions will be relative to (-transform.x, -transform.y).
+ // Sprites, Labels and any other "small" object uses it.
+ // Scenes, Layers and other "whole screen" object don't use it.
+ BOOL isRelativeAnchorPoint_;
+
+ // untransformed size of the node
+ CGSize contentSize_;
+ CGSize contentSizeInPixels_;
+
+ // transform
+ CGAffineTransform transform_, inverse_;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+ GLfloat transformGL_[16];
+#endif
+
+ // openGL real Z vertex
+ float vertexZ_;
+
+ // a Camera
+ CCCamera *camera_;
+
+ // a Grid
+ CCGridBase *grid_;
+
+ // z-order value
+ NSInteger zOrder_;
+
+ // array of children
+ CCArray *children_;
+
+ // weakref to parent
+ CCNode *parent_;
+
+ // a tag. any number you want to assign to the node
+ NSInteger tag_;
+
+ // user data field
+ void *userData_;
+
+ // Is running
+ BOOL isRunning_;
+
+ // To reduce memory, place BOOLs that are not properties here:
+ BOOL isTransformDirty_:1;
+ BOOL isInverseDirty_:1;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+ BOOL isTransformGLDirty_:1;
+#endif
+}
+
+/** The z order of the node relative to it's "brothers": children of the same parent */
+@property(nonatomic,readonly) NSInteger zOrder;
+/** The real openGL Z vertex.
+ Differences between openGL Z vertex and cocos2d Z order:
+ - OpenGL Z modifies the Z vertex, and not the Z order in the relation between parent-children
+ - OpenGL Z might require to set 2D projection
+ - cocos2d Z order works OK if all the nodes uses the same openGL Z vertex. eg: vertexZ = 0
+ @warning: Use it at your own risk since it might break the cocos2d parent-children z order
+ @since v0.8
+ */
+@property (nonatomic,readwrite) float vertexZ;
+
+/** The X skew angle of the node in degrees.
+ This angle describes the shear distortion in the X direction.
+ Thus, it is the angle between the Y axis and the left edge of the shape
+ The default skewX angle is 0. Positive values distort the node in a CW direction.
+ */
+@property(nonatomic,readwrite,assign) float skewX;
+
+/** The Y skew angle of the node in degrees.
+ This angle describes the shear distortion in the Y direction.
+ Thus, it is the angle between the X axis and the bottom edge of the shape
+ The default skewY angle is 0. Positive values distort the node in a CCW direction.
+ */
+@property(nonatomic,readwrite,assign) float skewY;
+/** The rotation (angle) of the node in degrees. 0 is the default rotation angle. Positive values rotate node CW. */
+@property(nonatomic,readwrite,assign) float rotation;
+/** The scale factor of the node. 1.0 is the default scale factor. It modifies the X and Y scale at the same time. */
+@property(nonatomic,readwrite,assign) float scale;
+/** The scale factor of the node. 1.0 is the default scale factor. It only modifies the X scale factor. */
+@property(nonatomic,readwrite,assign) float scaleX;
+/** The scale factor of the node. 1.0 is the default scale factor. It only modifies the Y scale factor. */
+@property(nonatomic,readwrite,assign) float scaleY;
+/** Position (x,y) of the node in points. (0,0) is the left-bottom corner. */
+@property(nonatomic,readwrite,assign) CGPoint position;
+/** Position (x,y) of the node in points. (0,0) is the left-bottom corner. */
+@property(nonatomic,readwrite,assign) CGPoint positionInPixels;
+/** A CCCamera object that lets you move the node using a gluLookAt
+*/
+@property(nonatomic,readonly) CCCamera* camera;
+/** Array of children */
+@property(nonatomic,readonly) CCArray *children;
+/** A CCGrid object that is used when applying effects */
+@property(nonatomic,readwrite,retain) CCGridBase* grid;
+/** Whether of not the node is visible. Default is YES */
+@property(nonatomic,readwrite,assign) BOOL visible;
+/** anchorPoint is the point around which all transformations and positioning manipulations take place.
+ It's like a pin in the node where it is "attached" to its parent.
+ The anchorPoint is normalized, like a percentage. (0,0) means the bottom-left corner and (1,1) means the top-right corner.
+ But you can use values higher than (1,1) and lower than (0,0) too.
+ The default anchorPoint is (0,0). It starts in the bottom-left corner. CCSprite and other subclasses have a different default anchorPoint.
+ @since v0.8
+ */
+@property(nonatomic,readwrite) CGPoint anchorPoint;
+/** The anchorPoint in absolute pixels.
+ Since v0.8 you can only read it. If you wish to modify it, use anchorPoint instead
+ */
+@property(nonatomic,readonly) CGPoint anchorPointInPixels;
+
+/** The untransformed size of the node in Points
+ The contentSize remains the same no matter the node is scaled or rotated.
+ All nodes has a size. Layer and Scene has the same size of the screen.
+ @since v0.8
+ */
+@property (nonatomic,readwrite) CGSize contentSize;
+
+/** The untransformed size of the node in Pixels
+ The contentSize remains the same no matter the node is scaled or rotated.
+ All nodes has a size. Layer and Scene has the same size of the screen.
+ @since v0.8
+ */
+@property (nonatomic,readwrite) CGSize contentSizeInPixels;
+
+/** whether or not the node is running */
+@property(nonatomic,readonly) BOOL isRunning;
+/** A weak reference to the parent */
+@property(nonatomic,readwrite,assign) CCNode* parent;
+/** If YES the transformtions will be relative to it's anchor point.
+ * Sprites, Labels and any other sizeble object use it have it enabled by default.
+ * Scenes, Layers and other "whole screen" object don't use it, have it disabled by default.
+ */
+@property(nonatomic,readwrite,assign) BOOL isRelativeAnchorPoint;
+/** A tag used to identify the node easily */
+@property(nonatomic,readwrite,assign) NSInteger tag;
+/** A custom user data pointer */
+@property(nonatomic,readwrite,assign) void *userData;
+
+// initializators
+/** allocates and initializes a node.
+ The node will be created as "autorelease".
+ */
++(id) node;
+/** initializes the node */
+-(id) init;
+
+
+// scene managment
+
+/** callback that is called every time the CCNode enters the 'stage'.
+ If the CCNode enters the 'stage' with a transition, this callback is called when the transition starts.
+ During onEnter you can't a "sister/brother" node.
+ */
+-(void) onEnter;
+/** callback that is called when the CCNode enters in the 'stage'.
+ If the CCNode enters the 'stage' with a transition, this callback is called when the transition finishes.
+ @since v0.8
+ */
+-(void) onEnterTransitionDidFinish;
+/** callback that is called every time the CCNode leaves the 'stage'.
+ If the CCNode leaves the 'stage' with a transition, this callback is called when the transition finishes.
+ During onExit you can't access a sibling node.
+ */
+-(void) onExit;
+
+
+// composition: ADD
+
+/** Adds a child to the container with z-order as 0.
+ If the child is added to a 'running' node, then 'onEnter' and 'onEnterTransitionDidFinish' will be called immediately.
+ @since v0.7.1
+ */
+-(void) addChild: (CCNode*)node;
+
+/** Adds a child to the container with a z-order.
+ If the child is added to a 'running' node, then 'onEnter' and 'onEnterTransitionDidFinish' will be called immediately.
+ @since v0.7.1
+ */
+-(void) addChild: (CCNode*)node z:(NSInteger)z;
+
+/** Adds a child to the container with z order and tag.
+ If the child is added to a 'running' node, then 'onEnter' and 'onEnterTransitionDidFinish' will be called immediately.
+ @since v0.7.1
+ */
+-(void) addChild: (CCNode*)node z:(NSInteger)z tag:(NSInteger)tag;
+
+// composition: REMOVE
+
+/** Remove itself from its parent node. If cleanup is YES, then also remove all actions and callbacks.
+ If the node orphan, then nothing happens.
+ @since v0.99.3
+ */
+-(void) removeFromParentAndCleanup:(BOOL)cleanup;
+
+/** Removes a child from the container. It will also cleanup all running actions depending on the cleanup parameter.
+ @since v0.7.1
+ */
+-(void) removeChild: (CCNode*)node cleanup:(BOOL)cleanup;
+
+/** Removes a child from the container by tag value. It will also cleanup all running actions depending on the cleanup parameter
+ @since v0.7.1
+ */
+-(void) removeChildByTag:(NSInteger) tag cleanup:(BOOL)cleanup;
+
+/** Removes all children from the container and do a cleanup all running actions depending on the cleanup parameter.
+ @since v0.7.1
+ */
+-(void) removeAllChildrenWithCleanup:(BOOL)cleanup;
+
+// composition: GET
+/** Gets a child from the container given its tag
+ @return returns a CCNode object
+ @since v0.7.1
+ */
+-(CCNode*) getChildByTag:(NSInteger) tag;
+
+/** Reorders a child according to a new z value.
+ * The child MUST be already added.
+ */
+-(void) reorderChild:(CCNode*)child z:(NSInteger)zOrder;
+
+/** Stops all running actions and schedulers
+ @since v0.8
+ */
+-(void) cleanup;
+
+// draw
+
+/** Override this method to draw your own node.
+ The following GL states will be enabled by default:
+ - glEnableClientState(GL_VERTEX_ARRAY);
+ - glEnableClientState(GL_COLOR_ARRAY);
+ - glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ - glEnable(GL_TEXTURE_2D);
+
+ AND YOU SHOULD NOT DISABLE THEM AFTER DRAWING YOUR NODE
+
+ But if you enable any other GL state, you should disable it after drawing your node.
+ */
+-(void) draw;
+/** recursive method that visit its children and draw them */
+-(void) visit;
+
+// transformations
+
+/** performs OpenGL view-matrix transformation based on position, scale, rotation and other attributes. */
+-(void) transform;
+
+/** performs OpenGL view-matrix transformation of it's ancestors.
+ Generally the ancestors are already transformed, but in certain cases (eg: attaching a FBO)
+ it's necessary to transform the ancestors again.
+ @since v0.7.2
+ */
+-(void) transformAncestors;
+
+/** returns a "local" axis aligned bounding box of the node in points.
+ The returned box is relative only to its parent.
+ The returned box is in Points.
+
+ @since v0.8.2
+ */
+- (CGRect) boundingBox;
+
+/** returns a "local" axis aligned bounding box of the node in pixels.
+ The returned box is relative only to its parent.
+ The returned box is in Points.
+
+ @since v0.99.5
+ */
+- (CGRect) boundingBoxInPixels;
+
+
+// actions
+
+/** Executes an action, and returns the action that is executed.
+ The node becomes the action's target.
+ @warning Starting from v0.8 actions don't retain their target anymore.
+ @since v0.7.1
+ @return An Action pointer
+ */
+-(CCAction*) runAction: (CCAction*) action;
+/** Removes all actions from the running action list */
+-(void) stopAllActions;
+/** Removes an action from the running action list */
+-(void) stopAction: (CCAction*) action;
+/** Removes an action from the running action list given its tag
+ @since v0.7.1
+*/
+-(void) stopActionByTag:(NSInteger) tag;
+/** Gets an action from the running action list given its tag
+ @since v0.7.1
+ @return the Action the with the given tag
+ */
+-(CCAction*) getActionByTag:(NSInteger) tag;
+/** Returns the numbers of actions that are running plus the ones that are schedule to run (actions in actionsToAdd and actions arrays).
+ * Composable actions are counted as 1 action. Example:
+ * If you are running 1 Sequence of 7 actions, it will return 1.
+ * If you are running 7 Sequences of 2 actions, it will return 7.
+ */
+-(NSUInteger) numberOfRunningActions;
+
+// timers
+
+/** check whether a selector is scheduled. */
+//-(BOOL) isScheduled: (SEL) selector;
+
+/** schedules the "update" method. It will use the order number 0. This method will be called every frame.
+ Scheduled methods with a lower order value will be called before the ones that have a higher order value.
+ Only one "udpate" method could be scheduled per node.
+
+ @since v0.99.3
+ */
+-(void) scheduleUpdate;
+
+/** schedules the "update" selector with a custom priority. This selector will be called every frame.
+ Scheduled selectors with a lower priority will be called before the ones that have a higher value.
+ Only one "udpate" selector could be scheduled per node (You can't have 2 'update' selectors).
+
+ @since v0.99.3
+ */
+-(void) scheduleUpdateWithPriority:(NSInteger)priority;
+
+/* unschedules the "update" method.
+
+ @since v0.99.3
+ */
+-(void) unscheduleUpdate;
+
+
+/** schedules a selector.
+ The scheduled selector will be ticked every frame
+ */
+-(void) schedule: (SEL) s;
+/** schedules a custom selector with an interval time in seconds.
+ If time is 0 it will be ticked every frame.
+ If time is 0, it is recommended to use 'scheduleUpdate' instead.
+
+ If the selector is already scheduled, then the interval parameter will be updated without scheduling it again.
+ */
+-(void) schedule: (SEL) s interval:(ccTime)seconds;
+/** unschedules a custom selector.*/
+-(void) unschedule: (SEL) s;
+
+/** unschedule all scheduled selectors: custom selectors, and the 'update' selector.
+ Actions are not affected by this method.
+@since v0.99.3
+ */
+-(void) unscheduleAllSelectors;
+
+/** resumes all scheduled selectors and actions.
+ Called internally by onEnter
+ */
+-(void) resumeSchedulerAndActions;
+/** pauses all scheduled selectors and actions.
+ Called internally by onExit
+ */
+-(void) pauseSchedulerAndActions;
+
+
+// transformation methods
+
+/** Returns the matrix that transform the node's (local) space coordinates into the parent's space coordinates.
+ The matrix is in Pixels.
+ @since v0.7.1
+ */
+- (CGAffineTransform)nodeToParentTransform;
+/** Returns the matrix that transform parent's space coordinates to the node's (local) space coordinates.
+ The matrix is in Pixels.
+ @since v0.7.1
+ */
+- (CGAffineTransform)parentToNodeTransform;
+/** Retrusn the world affine transform matrix. The matrix is in Pixels.
+ @since v0.7.1
+ */
+- (CGAffineTransform)nodeToWorldTransform;
+/** Returns the inverse world affine transform matrix. The matrix is in Pixels.
+ @since v0.7.1
+ */
+- (CGAffineTransform)worldToNodeTransform;
+/** Converts a Point to node (local) space coordinates. The result is in Points.
+ @since v0.7.1
+ */
+- (CGPoint)convertToNodeSpace:(CGPoint)worldPoint;
+/** Converts a Point to world space coordinates. The result is in Points.
+ @since v0.7.1
+ */
+- (CGPoint)convertToWorldSpace:(CGPoint)nodePoint;
+/** Converts a Point to node (local) space coordinates. The result is in Points.
+ treating the returned/received node point as anchor relative.
+ @since v0.7.1
+ */
+- (CGPoint)convertToNodeSpaceAR:(CGPoint)worldPoint;
+/** Converts a local Point to world space coordinates.The result is in Points.
+ treating the returned/received node point as anchor relative.
+ @since v0.7.1
+ */
+- (CGPoint)convertToWorldSpaceAR:(CGPoint)nodePoint;
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+/** Converts a UITouch to node (local) space coordinates. The result is in Points.
+ @since v0.7.1
+ */
+- (CGPoint)convertTouchToNodeSpace:(UITouch *)touch;
+/** Converts a UITouch to node (local) space coordinates. The result is in Points.
+ This method is AR (Anchor Relative)..
+ @since v0.7.1
+ */
+- (CGPoint)convertTouchToNodeSpaceAR:(UITouch *)touch;
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Valentin Milea
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#import "CCNode.h"
+#import "CCGrid.h"
+#import "CCDirector.h"
+#import "CCActionManager.h"
+#import "CCCamera.h"
+#import "CCScheduler.h"
+#import "ccConfig.h"
+#import "ccMacros.h"
+#import "Support/CGPointExtension.h"
+#import "Support/ccCArray.h"
+#import "Support/TransformUtils.h"
+#import "ccMacros.h"
+
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import "Platforms/iOS/CCDirectorIOS.h"
+#endif
+
+
+#if CC_COCOSNODE_RENDER_SUBPIXEL
+#define RENDER_IN_SUBPIXEL
+#else
+#define RENDER_IN_SUBPIXEL (NSInteger)
+#endif
+
+@interface CCNode ()
+// lazy allocs
+-(void) childrenAlloc;
+// helper that reorder a child
+-(void) insertChild:(CCNode*)child z:(NSInteger)z;
+// used internally to alter the zOrder variable. DON'T call this method manually
+-(void) _setZOrder:(NSInteger) z;
+-(void) detachChild:(CCNode *)child cleanup:(BOOL)doCleanup;
+@end
+
+@implementation CCNode
+
+@synthesize children = children_;
+@synthesize visible = visible_;
+@synthesize parent = parent_;
+@synthesize grid = grid_;
+@synthesize zOrder = zOrder_;
+@synthesize tag = tag_;
+@synthesize vertexZ = vertexZ_;
+@synthesize isRunning = isRunning_;
+@synthesize userData = userData_;
+
+#pragma mark CCNode - Transform related properties
+
+@synthesize rotation = rotation_, scaleX = scaleX_, scaleY = scaleY_;
+@synthesize skewX = skewX_, skewY = skewY_;
+@synthesize position = position_, positionInPixels = positionInPixels_;
+@synthesize anchorPoint = anchorPoint_, anchorPointInPixels = anchorPointInPixels_;
+@synthesize contentSize = contentSize_, contentSizeInPixels = contentSizeInPixels_;
+@synthesize isRelativeAnchorPoint = isRelativeAnchorPoint_;
+
+// getters synthesized, setters explicit
+
+-(void) setSkewX:(float)newSkewX
+{
+ skewX_ = newSkewX;
+ isTransformDirty_ = isInverseDirty_ = YES;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+ isTransformGLDirty_ = YES;
+#endif
+}
+
+-(void) setSkewY:(float)newSkewY
+{
+ skewY_ = newSkewY;
+ isTransformDirty_ = isInverseDirty_ = YES;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+ isTransformGLDirty_ = YES;
+#endif
+}
+
+-(void) setRotation: (float)newRotation
+{
+ rotation_ = newRotation;
+ isTransformDirty_ = isInverseDirty_ = YES;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+ isTransformGLDirty_ = YES;
+#endif
+}
+
+-(void) setScaleX: (float)newScaleX
+{
+ scaleX_ = newScaleX;
+ isTransformDirty_ = isInverseDirty_ = YES;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+ isTransformGLDirty_ = YES;
+#endif
+}
+
+-(void) setScaleY: (float)newScaleY
+{
+ scaleY_ = newScaleY;
+ isTransformDirty_ = isInverseDirty_ = YES;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+ isTransformGLDirty_ = YES;
+#endif
+}
+
+-(void) setPosition: (CGPoint)newPosition
+{
+ position_ = newPosition;
+ if( CC_CONTENT_SCALE_FACTOR() == 1 )
+ positionInPixels_ = position_;
+ else
+ positionInPixels_ = ccpMult( newPosition, CC_CONTENT_SCALE_FACTOR() );
+
+ isTransformDirty_ = isInverseDirty_ = YES;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+ isTransformGLDirty_ = YES;
+#endif
+}
+
+-(void) setPositionInPixels:(CGPoint)newPosition
+{
+ positionInPixels_ = newPosition;
+
+ if( CC_CONTENT_SCALE_FACTOR() == 1 )
+ position_ = positionInPixels_;
+ else
+ position_ = ccpMult( newPosition, 1/CC_CONTENT_SCALE_FACTOR() );
+
+ isTransformDirty_ = isInverseDirty_ = YES;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+ isTransformGLDirty_ = YES;
+#endif
+}
+
+-(void) setIsRelativeAnchorPoint: (BOOL)newValue
+{
+ isRelativeAnchorPoint_ = newValue;
+ isTransformDirty_ = isInverseDirty_ = YES;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+ isTransformGLDirty_ = YES;
+#endif
+}
+
+-(void) setAnchorPoint:(CGPoint)point
+{
+ if( ! CGPointEqualToPoint(point, anchorPoint_) ) {
+ anchorPoint_ = point;
+ anchorPointInPixels_ = ccp( contentSizeInPixels_.width * anchorPoint_.x, contentSizeInPixels_.height * anchorPoint_.y );
+ isTransformDirty_ = isInverseDirty_ = YES;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+ isTransformGLDirty_ = YES;
+#endif
+ }
+}
+
+-(void) setContentSize:(CGSize)size
+{
+ if( ! CGSizeEqualToSize(size, contentSize_) ) {
+ contentSize_ = size;
+
+ if( CC_CONTENT_SCALE_FACTOR() == 1 )
+ contentSizeInPixels_ = contentSize_;
+ else
+ contentSizeInPixels_ = CGSizeMake( size.width * CC_CONTENT_SCALE_FACTOR(), size.height * CC_CONTENT_SCALE_FACTOR() );
+
+ anchorPointInPixels_ = ccp( contentSizeInPixels_.width * anchorPoint_.x, contentSizeInPixels_.height * anchorPoint_.y );
+ isTransformDirty_ = isInverseDirty_ = YES;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+ isTransformGLDirty_ = YES;
+#endif
+ }
+}
+
+-(void) setContentSizeInPixels:(CGSize)size
+{
+ if( ! CGSizeEqualToSize(size, contentSizeInPixels_) ) {
+ contentSizeInPixels_ = size;
+
+ if( CC_CONTENT_SCALE_FACTOR() == 1 )
+ contentSize_ = contentSizeInPixels_;
+ else
+ contentSize_ = CGSizeMake( size.width / CC_CONTENT_SCALE_FACTOR(), size.height / CC_CONTENT_SCALE_FACTOR() );
+
+ anchorPointInPixels_ = ccp( contentSizeInPixels_.width * anchorPoint_.x, contentSizeInPixels_.height * anchorPoint_.y );
+ isTransformDirty_ = isInverseDirty_ = YES;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+ isTransformGLDirty_ = YES;
+#endif
+ }
+}
+
+- (CGRect) boundingBox
+{
+ CGRect ret = [self boundingBoxInPixels];
+ return CC_RECT_PIXELS_TO_POINTS( ret );
+}
+
+- (CGRect) boundingBoxInPixels
+{
+ CGRect rect = CGRectMake(0, 0, contentSizeInPixels_.width, contentSizeInPixels_.height);
+ return CGRectApplyAffineTransform(rect, [self nodeToParentTransform]);
+}
+
+-(void) setVertexZ:(float)vertexZ
+{
+ vertexZ_ = vertexZ * CC_CONTENT_SCALE_FACTOR();
+}
+
+-(float) scale
+{
+ NSAssert( scaleX_ == scaleY_, @"CCNode#scale. ScaleX != ScaleY. Don't know which one to return");
+ return scaleX_;
+}
+
+-(void) setScale:(float) s
+{
+ scaleX_ = scaleY_ = s;
+ isTransformDirty_ = isInverseDirty_ = YES;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+ isTransformGLDirty_ = YES;
+#endif
+}
+
+#pragma mark CCNode - Init & cleanup
+
++(id) node
+{
+ return [[[self alloc] init] autorelease];
+}
+
+-(id) init
+{
+ if ((self=[super init]) ) {
+
+ isRunning_ = NO;
+
+ skewX_ = skewY_ = 0.0f;
+ rotation_ = 0.0f;
+ scaleX_ = scaleY_ = 1.0f;
+ positionInPixels_ = position_ = CGPointZero;
+ anchorPointInPixels_ = anchorPoint_ = CGPointZero;
+ contentSizeInPixels_ = contentSize_ = CGSizeZero;
+
+
+ // "whole screen" objects. like Scenes and Layers, should set isRelativeAnchorPoint to NO
+ isRelativeAnchorPoint_ = YES;
+
+ isTransformDirty_ = isInverseDirty_ = YES;
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+ isTransformGLDirty_ = YES;
+#endif
+
+ vertexZ_ = 0;
+
+ grid_ = nil;
+
+ visible_ = YES;
+
+ tag_ = kCCNodeTagInvalid;
+
+ zOrder_ = 0;
+
+ // lazy alloc
+ camera_ = nil;
+
+ // children (lazy allocs)
+ children_ = nil;
+
+ // userData is always inited as nil
+ userData_ = nil;
+
+ //initialize parent to nil
+ parent_ = nil;
+ }
+
+ return self;
+}
+
+- (void)cleanup
+{
+ // actions
+ [self stopAllActions];
+ [self unscheduleAllSelectors];
+
+ // timers
+ [children_ makeObjectsPerformSelector:@selector(cleanup)];
+}
+
+- (NSString*) description
+{
+ return [NSString stringWithFormat:@"<%@ = %08X | Tag = %i>", [self class], self, tag_];
+}
+
+- (void) dealloc
+{
+ CCLOGINFO( @"cocos2d: deallocing %@", self);
+
+ // attributes
+ [camera_ release];
+
+ [grid_ release];
+
+ // children
+ CCNode *child;
+ CCARRAY_FOREACH(children_, child)
+ child.parent = nil;
+
+ [children_ release];
+
+ [super dealloc];
+}
+
+#pragma mark CCNode Composition
+
+-(void) childrenAlloc
+{
+ children_ = [[CCArray alloc] initWithCapacity:4];
+}
+
+// camera: lazy alloc
+-(CCCamera*) camera
+{
+ if( ! camera_ ) {
+ camera_ = [[CCCamera alloc] init];
+
+ // by default, center camera at the Sprite's anchor point
+ // [camera_ setCenterX:anchorPointInPixels_.x centerY:anchorPointInPixels_.y centerZ:0];
+ // [camera_ setEyeX:anchorPointInPixels_.x eyeY:anchorPointInPixels_.y eyeZ:1];
+
+ // [camera_ setCenterX:0 centerY:0 centerZ:0];
+ // [camera_ setEyeX:0 eyeY:0 eyeZ:1];
+
+ }
+
+ return camera_;
+}
+
+-(CCNode*) getChildByTag:(NSInteger) aTag
+{
+ NSAssert( aTag != kCCNodeTagInvalid, @"Invalid tag");
+
+ CCNode *node;
+ CCARRAY_FOREACH(children_, node){
+ if( node.tag == aTag )
+ return node;
+ }
+ // not found
+ return nil;
+}
+
+/* "add" logic MUST only be on this method
+ * If a class want's to extend the 'addChild' behaviour it only needs
+ * to override this method
+ */
+-(void) addChild: (CCNode*) child z:(NSInteger)z tag:(NSInteger) aTag
+{
+ NSAssert( child != nil, @"Argument must be non-nil");
+ NSAssert( child.parent == nil, @"child already added. It can't be added again");
+
+ if( ! children_ )
+ [self childrenAlloc];
+
+ [self insertChild:child z:z];
+
+ child.tag = aTag;
+
+ [child setParent: self];
+
+ if( isRunning_ ) {
+ [child onEnter];
+ [child onEnterTransitionDidFinish];
+ }
+}
+
+-(void) addChild: (CCNode*) child z:(NSInteger)z
+{
+ NSAssert( child != nil, @"Argument must be non-nil");
+ [self addChild:child z:z tag:child.tag];
+}
+
+-(void) addChild: (CCNode*) child
+{
+ NSAssert( child != nil, @"Argument must be non-nil");
+ [self addChild:child z:child.zOrder tag:child.tag];
+}
+
+-(void) removeFromParentAndCleanup:(BOOL)cleanup
+{
+ [parent_ removeChild:self cleanup:cleanup];
+}
+
+/* "remove" logic MUST only be on this method
+ * If a class want's to extend the 'removeChild' behavior it only needs
+ * to override this method
+ */
+-(void) removeChild: (CCNode*)child cleanup:(BOOL)cleanup
+{
+ // explicit nil handling
+ if (child == nil)
+ return;
+
+ if ( [children_ containsObject:child] )
+ [self detachChild:child cleanup:cleanup];
+}
+
+-(void) removeChildByTag:(NSInteger)aTag cleanup:(BOOL)cleanup
+{
+ NSAssert( aTag != kCCNodeTagInvalid, @"Invalid tag");
+
+ CCNode *child = [self getChildByTag:aTag];
+
+ if (child == nil)
+ CCLOG(@"cocos2d: removeChildByTag: child not found!");
+ else
+ [self removeChild:child cleanup:cleanup];
+}
+
+-(void) removeAllChildrenWithCleanup:(BOOL)cleanup
+{
+ // not using detachChild improves speed here
+ CCNode *c;
+ CCARRAY_FOREACH(children_, c)
+ {
+ // IMPORTANT:
+ // -1st do onExit
+ // -2nd cleanup
+ if (isRunning_)
+ [c onExit];
+
+ if (cleanup)
+ [c cleanup];
+
+ // set parent nil at the end (issue #476)
+ [c setParent:nil];
+ }
+
+ [children_ removeAllObjects];
+}
+
+-(void) detachChild:(CCNode *)child cleanup:(BOOL)doCleanup
+{
+ // IMPORTANT:
+ // -1st do onExit
+ // -2nd cleanup
+ if (isRunning_)
+ [child onExit];
+
+ // If you don't do cleanup, the child's actions will not get removed and the
+ // its scheduledSelectors_ dict will not get released!
+ if (doCleanup)
+ [child cleanup];
+
+ // set parent nil at the end (issue #476)
+ [child setParent:nil];
+
+ [children_ removeObject:child];
+}
+
+// used internally to alter the zOrder variable. DON'T call this method manually
+-(void) _setZOrder:(NSInteger) z
+{
+ zOrder_ = z;
+}
+
+// helper used by reorderChild & add
+-(void) insertChild:(CCNode*)child z:(NSInteger)z
+{
+ NSUInteger index=0;
+ CCNode *a = [children_ lastObject];
+
+ // quick comparison to improve performance
+ if (!a || a.zOrder <= z)
+ [children_ addObject:child];
+
+ else
+ {
+ CCARRAY_FOREACH(children_, a) {
+ if ( a.zOrder > z ) {
+ [children_ insertObject:child atIndex:index];
+ break;
+ }
+ index++;
+ }
+ }
+
+ [child _setZOrder:z];
+}
+
+-(void) reorderChild:(CCNode*) child z:(NSInteger)z
+{
+ NSAssert( child != nil, @"Child must be non-nil");
+
+ [child retain];
+ [children_ removeObject:child];
+
+ [self insertChild:child z:z];
+
+ [child release];
+}
+
+#pragma mark CCNode Draw
+
+-(void) draw
+{
+ // override me
+ // Only use this function to draw your staff.
+ // DON'T draw your stuff outside this method
+}
+
+-(void) visit
+{
+ // quick return if not visible
+ if (!visible_)
+ return;
+
+ glPushMatrix();
+
+ if ( grid_ && grid_.active) {
+ [grid_ beforeDraw];
+ [self transformAncestors];
+ }
+
+ [self transform];
+
+ if(children_) {
+ ccArray *arrayData = children_->data;
+ NSUInteger i = 0;
+
+ // draw children zOrder < 0
+ for( ; i < arrayData->num; i++ ) {
+ CCNode *child = arrayData->arr[i];
+ if ( [child zOrder] < 0 )
+ [child visit];
+ else
+ break;
+ }
+
+ // self draw
+ [self draw];
+
+ // draw children zOrder >= 0
+ for( ; i < arrayData->num; i++ ) {
+ CCNode *child = arrayData->arr[i];
+ [child visit];
+ }
+
+ } else
+ [self draw];
+
+ if ( grid_ && grid_.active)
+ [grid_ afterDraw:self];
+
+ glPopMatrix();
+}
+
+#pragma mark CCNode - Transformations
+
+-(void) transformAncestors
+{
+ if( parent_ ) {
+ [parent_ transformAncestors];
+ [parent_ transform];
+ }
+}
+
+-(void) transform
+{
+ // transformations
+
+#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+ // BEGIN alternative -- using cached transform
+ //
+ if( isTransformGLDirty_ ) {
+ CGAffineTransform t = [self nodeToParentTransform];
+ CGAffineToGL(&t, transformGL_);
+ isTransformGLDirty_ = NO;
+ }
+
+ glMultMatrixf(transformGL_);
+ if( vertexZ_ )
+ glTranslatef(0, 0, vertexZ_);
+
+ // XXX: Expensive calls. Camera should be integrated into the cached affine matrix
+ if ( camera_ && !(grid_ && grid_.active) )
+ {
+ BOOL translate = (anchorPointInPixels_.x != 0.0f || anchorPointInPixels_.y != 0.0f);
+
+ if( translate )
+ ccglTranslate(RENDER_IN_SUBPIXEL(anchorPointInPixels_.x), RENDER_IN_SUBPIXEL(anchorPointInPixels_.y), 0);
+
+ [camera_ locate];
+
+ if( translate )
+ ccglTranslate(RENDER_IN_SUBPIXEL(-anchorPointInPixels_.x), RENDER_IN_SUBPIXEL(-anchorPointInPixels_.y), 0);
+ }
+
+
+ // END alternative
+
+#else
+ // BEGIN original implementation
+ //
+ // translate
+ if ( isRelativeAnchorPoint_ && (anchorPointInPixels_.x != 0 || anchorPointInPixels_.y != 0 ) )
+ glTranslatef( RENDER_IN_SUBPIXEL(-anchorPointInPixels_.x), RENDER_IN_SUBPIXEL(-anchorPointInPixels_.y), 0);
+
+ if (anchorPointInPixels_.x != 0 || anchorPointInPixels_.y != 0)
+ glTranslatef( RENDER_IN_SUBPIXEL(positionInPixels_.x + anchorPointInPixels_.x), RENDER_IN_SUBPIXEL(positionInPixels_.y + anchorPointInPixels_.y), vertexZ_);
+ else if ( positionInPixels_.x !=0 || positionInPixels_.y !=0 || vertexZ_ != 0)
+ glTranslatef( RENDER_IN_SUBPIXEL(positionInPixels_.x), RENDER_IN_SUBPIXEL(positionInPixels_.y), vertexZ_ );
+
+ // rotate
+ if (rotation_ != 0.0f )
+ glRotatef( -rotation_, 0.0f, 0.0f, 1.0f );
+
+ // skew
+ if ( (skewX_ != 0.0f) || (skewY_ != 0.0f) ) {
+ CGAffineTransform skewMatrix = CGAffineTransformMake( 1.0f, tanf(CC_DEGREES_TO_RADIANS(skewY_)), tanf(CC_DEGREES_TO_RADIANS(skewX_)), 1.0f, 0.0f, 0.0f );
+ GLfloat glMatrix[16];
+ CGAffineToGL(&skewMatrix, glMatrix);
+ glMultMatrixf(glMatrix);
+ }
+
+ // scale
+ if (scaleX_ != 1.0f || scaleY_ != 1.0f)
+ glScalef( scaleX_, scaleY_, 1.0f );
+
+ if ( camera_ && !(grid_ && grid_.active) )
+ [camera_ locate];
+
+ // restore and re-position point
+ if (anchorPointInPixels_.x != 0.0f || anchorPointInPixels_.y != 0.0f)
+ glTranslatef(RENDER_IN_SUBPIXEL(-anchorPointInPixels_.x), RENDER_IN_SUBPIXEL(-anchorPointInPixels_.y), 0);
+
+ //
+ // END original implementation
+#endif
+
+}
+
+#pragma mark CCNode SceneManagement
+
+-(void) onEnter
+{
+ [children_ makeObjectsPerformSelector:@selector(onEnter)];
+ [self resumeSchedulerAndActions];
+
+ isRunning_ = YES;
+}
+
+-(void) onEnterTransitionDidFinish
+{
+ [children_ makeObjectsPerformSelector:@selector(onEnterTransitionDidFinish)];
+}
+
+-(void) onExit
+{
+ [self pauseSchedulerAndActions];
+ isRunning_ = NO;
+
+ [children_ makeObjectsPerformSelector:@selector(onExit)];
+}
+
+#pragma mark CCNode Actions
+
+-(CCAction*) runAction:(CCAction*) action
+{
+ NSAssert( action != nil, @"Argument must be non-nil");
+
+ [[CCActionManager sharedManager] addAction:action target:self paused:!isRunning_];
+ return action;
+}
+
+-(void) stopAllActions
+{
+ [[CCActionManager sharedManager] removeAllActionsFromTarget:self];
+}
+
+-(void) stopAction: (CCAction*) action
+{
+ [[CCActionManager sharedManager] removeAction:action];
+}
+
+-(void) stopActionByTag:(NSInteger)aTag
+{
+ NSAssert( aTag != kCCActionTagInvalid, @"Invalid tag");
+ [[CCActionManager sharedManager] removeActionByTag:aTag target:self];
+}
+
+-(CCAction*) getActionByTag:(NSInteger) aTag
+{
+ NSAssert( aTag != kCCActionTagInvalid, @"Invalid tag");
+ return [[CCActionManager sharedManager] getActionByTag:aTag target:self];
+}
+
+-(NSUInteger) numberOfRunningActions
+{
+ return [[CCActionManager sharedManager] numberOfRunningActionsInTarget:self];
+}
+
+#pragma mark CCNode - Scheduler
+
+-(void) scheduleUpdate
+{
+ [self scheduleUpdateWithPriority:0];
+}
+
+-(void) scheduleUpdateWithPriority:(NSInteger)priority
+{
+ [[CCScheduler sharedScheduler] scheduleUpdateForTarget:self priority:priority paused:!isRunning_];
+}
+
+-(void) unscheduleUpdate
+{
+ [[CCScheduler sharedScheduler] unscheduleUpdateForTarget:self];
+}
+
+-(void) schedule:(SEL)selector
+{
+ [self schedule:selector interval:0];
+}
+
+-(void) schedule:(SEL)selector interval:(ccTime)interval
+{
+ NSAssert( selector != nil, @"Argument must be non-nil");
+ NSAssert( interval >=0, @"Arguemnt must be positive");
+
+ [[CCScheduler sharedScheduler] scheduleSelector:selector forTarget:self interval:interval paused:!isRunning_];
+}
+
+-(void) unschedule:(SEL)selector
+{
+ // explicit nil handling
+ if (selector == nil)
+ return;
+
+ [[CCScheduler sharedScheduler] unscheduleSelector:selector forTarget:self];
+}
+
+-(void) unscheduleAllSelectors
+{
+ [[CCScheduler sharedScheduler] unscheduleAllSelectorsForTarget:self];
+}
+- (void) resumeSchedulerAndActions
+{
+ [[CCScheduler sharedScheduler] resumeTarget:self];
+ [[CCActionManager sharedManager] resumeTarget:self];
+}
+
+- (void) pauseSchedulerAndActions
+{
+ [[CCScheduler sharedScheduler] pauseTarget:self];
+ [[CCActionManager sharedManager] pauseTarget:self];
+}
+
+#pragma mark CCNode Transform
+
+- (CGAffineTransform)nodeToParentTransform
+{
+ if ( isTransformDirty_ ) {
+
+ transform_ = CGAffineTransformIdentity;
+
+ if ( !isRelativeAnchorPoint_ && !CGPointEqualToPoint(anchorPointInPixels_, CGPointZero) )
+ transform_ = CGAffineTransformTranslate(transform_, anchorPointInPixels_.x, anchorPointInPixels_.y);
+
+ if( ! CGPointEqualToPoint(positionInPixels_, CGPointZero) )
+ transform_ = CGAffineTransformTranslate(transform_, positionInPixels_.x, positionInPixels_.y);
+
+ if( rotation_ != 0 )
+ transform_ = CGAffineTransformRotate(transform_, -CC_DEGREES_TO_RADIANS(rotation_));
+
+ if( skewX_ != 0 || skewY_ != 0 ) {
+ // create a skewed coordinate system
+ CGAffineTransform skew = CGAffineTransformMake(1.0f, tanf(CC_DEGREES_TO_RADIANS(skewY_)), tanf(CC_DEGREES_TO_RADIANS(skewX_)), 1.0f, 0.0f, 0.0f);
+ // apply the skew to the transform
+ transform_ = CGAffineTransformConcat(skew, transform_);
+ }
+
+ if( ! (scaleX_ == 1 && scaleY_ == 1) )
+ transform_ = CGAffineTransformScale(transform_, scaleX_, scaleY_);
+
+ if( ! CGPointEqualToPoint(anchorPointInPixels_, CGPointZero) )
+ transform_ = CGAffineTransformTranslate(transform_, -anchorPointInPixels_.x, -anchorPointInPixels_.y);
+
+ isTransformDirty_ = NO;
+ }
+
+ return transform_;
+}
+
+- (CGAffineTransform)parentToNodeTransform
+{
+ if ( isInverseDirty_ ) {
+ inverse_ = CGAffineTransformInvert([self nodeToParentTransform]);
+ isInverseDirty_ = NO;
+ }
+
+ return inverse_;
+}
+
+- (CGAffineTransform)nodeToWorldTransform
+{
+ CGAffineTransform t = [self nodeToParentTransform];
+
+ for (CCNode *p = parent_; p != nil; p = p.parent)
+ t = CGAffineTransformConcat(t, [p nodeToParentTransform]);
+
+ return t;
+}
+
+- (CGAffineTransform)worldToNodeTransform
+{
+ return CGAffineTransformInvert([self nodeToWorldTransform]);
+}
+
+- (CGPoint)convertToNodeSpace:(CGPoint)worldPoint
+{
+ CGPoint ret;
+ if( CC_CONTENT_SCALE_FACTOR() == 1 )
+ ret = CGPointApplyAffineTransform(worldPoint, [self worldToNodeTransform]);
+ else {
+ ret = ccpMult( worldPoint, CC_CONTENT_SCALE_FACTOR() );
+ ret = CGPointApplyAffineTransform(ret, [self worldToNodeTransform]);
+ ret = ccpMult( ret, 1/CC_CONTENT_SCALE_FACTOR() );
+ }
+
+ return ret;
+}
+
+- (CGPoint)convertToWorldSpace:(CGPoint)nodePoint
+{
+ CGPoint ret;
+ if( CC_CONTENT_SCALE_FACTOR() == 1 )
+ ret = CGPointApplyAffineTransform(nodePoint, [self nodeToWorldTransform]);
+ else {
+ ret = ccpMult( nodePoint, CC_CONTENT_SCALE_FACTOR() );
+ ret = CGPointApplyAffineTransform(ret, [self nodeToWorldTransform]);
+ ret = ccpMult( ret, 1/CC_CONTENT_SCALE_FACTOR() );
+ }
+
+ return ret;
+}
+
+- (CGPoint)convertToNodeSpaceAR:(CGPoint)worldPoint
+{
+ CGPoint nodePoint = [self convertToNodeSpace:worldPoint];
+ CGPoint anchorInPoints;
+ if( CC_CONTENT_SCALE_FACTOR() == 1 )
+ anchorInPoints = anchorPointInPixels_;
+ else
+ anchorInPoints = ccpMult( anchorPointInPixels_, 1/CC_CONTENT_SCALE_FACTOR() );
+
+ return ccpSub(nodePoint, anchorInPoints);
+}
+
+- (CGPoint)convertToWorldSpaceAR:(CGPoint)nodePoint
+{
+ CGPoint anchorInPoints;
+ if( CC_CONTENT_SCALE_FACTOR() == 1 )
+ anchorInPoints = anchorPointInPixels_;
+ else
+ anchorInPoints = ccpMult( anchorPointInPixels_, 1/CC_CONTENT_SCALE_FACTOR() );
+
+ nodePoint = ccpAdd(nodePoint, anchorInPoints);
+ return [self convertToWorldSpace:nodePoint];
+}
+
+- (CGPoint)convertToWindowSpace:(CGPoint)nodePoint
+{
+ CGPoint worldPoint = [self convertToWorldSpace:nodePoint];
+ return [[CCDirector sharedDirector] convertToUI:worldPoint];
+}
+
+// convenience methods which take a UITouch instead of CGPoint
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+- (CGPoint)convertTouchToNodeSpace:(UITouch *)touch
+{
+ CGPoint point = [touch locationInView: [touch view]];
+ point = [[CCDirector sharedDirector] convertToGL: point];
+ return [self convertToNodeSpace:point];
+}
+
+- (CGPoint)convertTouchToNodeSpaceAR:(UITouch *)touch
+{
+ CGPoint point = [touch locationInView: [touch view]];
+ point = [[CCDirector sharedDirector] convertToGL: point];
+ return [self convertToNodeSpaceAR:point];
+}
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
+
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "CCNode.h"
+#import "Support/ccCArray.h"
+
+/** CCParallaxNode: A node that simulates a parallax scroller
+
+ The children will be moved faster / slower than the parent according the the parallax ratio.
+
+ */
+@interface CCParallaxNode : CCNode
+{
+ ccArray *parallaxArray_;
+ CGPoint lastPosition;
+}
+
+/** array that holds the offset / ratio of the children */
+@property (nonatomic,readwrite) ccArray * parallaxArray;
+
+/** Adds a child to the container with a z-order, a parallax ratio and a position offset
+ It returns self, so you can chain several addChilds.
+ @since v0.8
+ */
+-(void) addChild: (CCNode*)node z:(NSInteger)z parallaxRatio:(CGPoint)c positionOffset:(CGPoint)positionOffset;
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "CCParallaxNode.h"
+#import "Support/CGPointExtension.h"
+#import "Support/ccCArray.h"
+
+@interface CGPointObject : NSObject
+{
+ CGPoint ratio_;
+ CGPoint offset_;
+ CCNode *child_; // weak ref
+}
+@property (readwrite) CGPoint ratio;
+@property (readwrite) CGPoint offset;
+@property (readwrite,assign) CCNode *child;
++(id) pointWithCGPoint:(CGPoint)point offset:(CGPoint)offset;
+-(id) initWithCGPoint:(CGPoint)point offset:(CGPoint)offset;
+@end
+@implementation CGPointObject
+@synthesize ratio = ratio_;
+@synthesize offset = offset_;
+@synthesize child=child_;
+
++(id) pointWithCGPoint:(CGPoint)ratio offset:(CGPoint)offset
+{
+ return [[[self alloc] initWithCGPoint:ratio offset:offset] autorelease];
+}
+-(id) initWithCGPoint:(CGPoint)ratio offset:(CGPoint)offset
+{
+ if( (self=[super init])) {
+ ratio_ = ratio;
+ offset_ = offset;
+ }
+ return self;
+}
+@end
+
+@implementation CCParallaxNode
+
+@synthesize parallaxArray = parallaxArray_;
+
+-(id) init
+{
+ if( (self=[super init]) ) {
+ parallaxArray_ = ccArrayNew(5);
+ lastPosition = CGPointMake(-100,-100);
+ }
+ return self;
+}
+
+- (void) dealloc
+{
+ if( parallaxArray_ ) {
+ ccArrayFree(parallaxArray_);
+ parallaxArray_ = nil;
+ }
+ [super dealloc];
+}
+
+-(void) addChild:(CCNode*)child z:(NSInteger)z tag:(NSInteger)tag
+{
+ NSAssert(NO,@"ParallaxNode: use addChild:z:parallaxRatio:positionOffset instead");
+}
+
+-(void) addChild: (CCNode*) child z:(NSInteger)z parallaxRatio:(CGPoint)ratio positionOffset:(CGPoint)offset
+{
+ NSAssert( child != nil, @"Argument must be non-nil");
+ CGPointObject *obj = [CGPointObject pointWithCGPoint:ratio offset:offset];
+ obj.child = child;
+ ccArrayAppendObjectWithResize(parallaxArray_, obj);
+
+ CGPoint pos = self.position;
+ pos.x = pos.x * ratio.x + offset.x;
+ pos.y = pos.y * ratio.y + offset.y;
+ child.position = pos;
+
+ [super addChild: child z:z tag:child.tag];
+}
+
+-(void) removeChild:(CCNode*)node cleanup:(BOOL)cleanup
+{
+ for( unsigned int i=0;i < parallaxArray_->num;i++) {
+ CGPointObject *point = parallaxArray_->arr[i];
+ if( [point.child isEqual:node] ) {
+ ccArrayRemoveObjectAtIndex(parallaxArray_, i);
+ break;
+ }
+ }
+ [super removeChild:node cleanup:cleanup];
+}
+
+-(void) removeAllChildrenWithCleanup:(BOOL)cleanup
+{
+ ccArrayRemoveAllObjects(parallaxArray_);
+ [super removeAllChildrenWithCleanup:cleanup];
+}
+
+-(CGPoint) absolutePosition_
+{
+ CGPoint ret = position_;
+
+ CCNode *cn = self;
+
+ while (cn.parent != nil) {
+ cn = cn.parent;
+ ret = ccpAdd( ret, cn.position );
+ }
+
+ return ret;
+}
+
+/*
+ The positions are updated at visit because:
+ - using a timer is not guaranteed that it will called after all the positions were updated
+ - overriding "draw" will only precise if the children have a z > 0
+*/
+-(void) visit
+{
+// CGPoint pos = position_;
+// CGPoint pos = [self convertToWorldSpace:CGPointZero];
+ CGPoint pos = [self absolutePosition_];
+ if( ! CGPointEqualToPoint(pos, lastPosition) ) {
+
+ for(unsigned int i=0; i < parallaxArray_->num; i++ ) {
+
+ CGPointObject *point = parallaxArray_->arr[i];
+ float x = -pos.x + pos.x * point.ratio.x + point.offset.x;
+ float y = -pos.y + pos.y * point.ratio.y + point.offset.y;
+ point.child.position = ccp(x,y);
+ }
+
+ lastPosition = pos;
+ }
+
+ [super visit];
+}
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import <Availability.h>
+
+#import "CCParticleSystemPoint.h"
+#import "CCParticleSystemQuad.h"
+
+// build each architecture with the optimal particle system
+
+// ARMv7, Mac or Simulator use "Quad" particle
+#if defined(__ARM_NEON__) || defined(__MAC_OS_X_VERSION_MAX_ALLOWED) || TARGET_IPHONE_SIMULATOR
+ #define ARCH_OPTIMAL_PARTICLE_SYSTEM CCParticleSystemQuad
+
+// ARMv6 use "Point" particle
+#elif __arm__
+ #define ARCH_OPTIMAL_PARTICLE_SYSTEM CCParticleSystemPoint
+#else
+ #error(unknown architecture)
+#endif
+
+
+//! A fire particle system
+@interface CCParticleFire: ARCH_OPTIMAL_PARTICLE_SYSTEM
+{
+}
+@end
+
+//! A fireworks particle system
+@interface CCParticleFireworks : ARCH_OPTIMAL_PARTICLE_SYSTEM
+{
+}
+@end
+
+//! A sun particle system
+@interface CCParticleSun : ARCH_OPTIMAL_PARTICLE_SYSTEM
+{
+}
+@end
+
+//! A galaxy particle system
+@interface CCParticleGalaxy : ARCH_OPTIMAL_PARTICLE_SYSTEM
+{
+}
+@end
+
+//! A flower particle system
+@interface CCParticleFlower : ARCH_OPTIMAL_PARTICLE_SYSTEM
+{
+}
+@end
+
+//! A meteor particle system
+@interface CCParticleMeteor : ARCH_OPTIMAL_PARTICLE_SYSTEM
+{
+}
+@end
+
+//! An spiral particle system
+@interface CCParticleSpiral : ARCH_OPTIMAL_PARTICLE_SYSTEM
+{
+}
+@end
+
+//! An explosion particle system
+@interface CCParticleExplosion : ARCH_OPTIMAL_PARTICLE_SYSTEM
+{
+}
+@end
+
+//! An smoke particle system
+@interface CCParticleSmoke : ARCH_OPTIMAL_PARTICLE_SYSTEM
+{
+}
+@end
+
+//! An snow particle system
+@interface CCParticleSnow : ARCH_OPTIMAL_PARTICLE_SYSTEM
+{
+}
+@end
+
+//! A rain particle system
+@interface CCParticleRain : ARCH_OPTIMAL_PARTICLE_SYSTEM
+{
+}
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+// cocos2d
+#import "CCParticleExamples.h"
+#import "CCTextureCache.h"
+#import "CCDirector.h"
+#import "Support/CGPointExtension.h"
+
+//
+// ParticleFireworks
+//
+@implementation CCParticleFireworks
+-(id) init
+{
+ return [self initWithTotalParticles:1500];
+}
+
+-(id) initWithTotalParticles:(NSUInteger)p
+{
+ if( (self=[super initWithTotalParticles:p]) ) {
+ // duration
+ duration = kCCParticleDurationInfinity;
+
+ // Gravity Mode
+ self.emitterMode = kCCParticleModeGravity;
+
+ // Gravity Mode: gravity
+ self.gravity = ccp(0,-90);
+
+ // Gravity Mode: radial
+ self.radialAccel = 0;
+ self.radialAccelVar = 0;
+
+ // Gravity Mode: speed of particles
+ self.speed = 180;
+ self.speedVar = 50;
+
+ // emitter position
+ CGSize winSize = [[CCDirector sharedDirector] winSize];
+ self.position = ccp(winSize.width/2, winSize.height/2);
+
+ // angle
+ angle = 90;
+ angleVar = 20;
+
+ // life of particles
+ life = 3.5f;
+ lifeVar = 1;
+
+ // emits per frame
+ emissionRate = totalParticles/life;
+
+ // color of particles
+ startColor.r = 0.5f;
+ startColor.g = 0.5f;
+ startColor.b = 0.5f;
+ startColor.a = 1.0f;
+ startColorVar.r = 0.5f;
+ startColorVar.g = 0.5f;
+ startColorVar.b = 0.5f;
+ startColorVar.a = 0.1f;
+ endColor.r = 0.1f;
+ endColor.g = 0.1f;
+ endColor.b = 0.1f;
+ endColor.a = 0.2f;
+ endColorVar.r = 0.1f;
+ endColorVar.g = 0.1f;
+ endColorVar.b = 0.1f;
+ endColorVar.a = 0.2f;
+
+ // size, in pixels
+ startSize = 8.0f;
+ startSizeVar = 2.0f;
+ endSize = kCCParticleStartSizeEqualToEndSize;
+
+ self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"];
+
+ // additive
+ self.blendAdditive = NO;
+ }
+
+ return self;
+}
+@end
+
+//
+// ParticleFire
+//
+@implementation CCParticleFire
+-(id) init
+{
+ return [self initWithTotalParticles:250];
+}
+
+-(id) initWithTotalParticles:(NSUInteger) p
+{
+ if( (self=[super initWithTotalParticles:p]) ) {
+
+ // duration
+ duration = kCCParticleDurationInfinity;
+
+ // Gravity Mode
+ self.emitterMode = kCCParticleModeGravity;
+
+ // Gravity Mode: gravity
+ self.gravity = ccp(0,0);
+
+ // Gravity Mode: radial acceleration
+ self.radialAccel = 0;
+ self.radialAccelVar = 0;
+
+ // Gravity Mode: speed of particles
+ self.speed = 60;
+ self.speedVar = 20;
+
+ // starting angle
+ angle = 90;
+ angleVar = 10;
+
+ // emitter position
+ CGSize winSize = [[CCDirector sharedDirector] winSize];
+ self.position = ccp(winSize.width/2, 60);
+ posVar = ccp(40, 20);
+
+ // life of particles
+ life = 3;
+ lifeVar = 0.25f;
+
+
+ // size, in pixels
+ startSize = 54.0f;
+ startSizeVar = 10.0f;
+ endSize = kCCParticleStartSizeEqualToEndSize;
+
+ // emits per frame
+ emissionRate = totalParticles/life;
+
+ // color of particles
+ startColor.r = 0.76f;
+ startColor.g = 0.25f;
+ startColor.b = 0.12f;
+ startColor.a = 1.0f;
+ startColorVar.r = 0.0f;
+ startColorVar.g = 0.0f;
+ startColorVar.b = 0.0f;
+ startColorVar.a = 0.0f;
+ endColor.r = 0.0f;
+ endColor.g = 0.0f;
+ endColor.b = 0.0f;
+ endColor.a = 1.0f;
+ endColorVar.r = 0.0f;
+ endColorVar.g = 0.0f;
+ endColorVar.b = 0.0f;
+ endColorVar.a = 0.0f;
+
+ self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"];
+
+ // additive
+ self.blendAdditive = YES;
+ }
+
+ return self;
+}
+@end
+
+//
+// ParticleSun
+//
+@implementation CCParticleSun
+-(id) init
+{
+ return [self initWithTotalParticles:350];
+}
+
+-(id) initWithTotalParticles:(NSUInteger) p
+{
+ if( (self=[super initWithTotalParticles:p]) ) {
+
+ // additive
+ self.blendAdditive = YES;
+
+ // duration
+ duration = kCCParticleDurationInfinity;
+
+ // Gravity Mode
+ self.emitterMode = kCCParticleModeGravity;
+
+ // Gravity Mode: gravity
+ self.gravity = ccp(0,0);
+
+ // Gravity mode: radial acceleration
+ self.radialAccel = 0;
+ self.radialAccelVar = 0;
+
+ // Gravity mode: speed of particles
+ self.speed = 20;
+ self.speedVar = 5;
+
+
+ // angle
+ angle = 90;
+ angleVar = 360;
+
+ // emitter position
+ CGSize winSize = [[CCDirector sharedDirector] winSize];
+ self.position = ccp(winSize.width/2, winSize.height/2);
+ posVar = CGPointZero;
+
+ // life of particles
+ life = 1;
+ lifeVar = 0.5f;
+
+ // size, in pixels
+ startSize = 30.0f;
+ startSizeVar = 10.0f;
+ endSize = kCCParticleStartSizeEqualToEndSize;
+
+ // emits per seconds
+ emissionRate = totalParticles/life;
+
+ // color of particles
+ startColor.r = 0.76f;
+ startColor.g = 0.25f;
+ startColor.b = 0.12f;
+ startColor.a = 1.0f;
+ startColorVar.r = 0.0f;
+ startColorVar.g = 0.0f;
+ startColorVar.b = 0.0f;
+ startColorVar.a = 0.0f;
+ endColor.r = 0.0f;
+ endColor.g = 0.0f;
+ endColor.b = 0.0f;
+ endColor.a = 1.0f;
+ endColorVar.r = 0.0f;
+ endColorVar.g = 0.0f;
+ endColorVar.b = 0.0f;
+ endColorVar.a = 0.0f;
+
+ self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"];
+ }
+
+ return self;
+}
+@end
+
+//
+// ParticleGalaxy
+//
+@implementation CCParticleGalaxy
+-(id) init
+{
+ return [self initWithTotalParticles:200];
+}
+
+-(id) initWithTotalParticles:(NSUInteger)p
+{
+ if( (self=[super initWithTotalParticles:p]) ) {
+
+ // duration
+ duration = kCCParticleDurationInfinity;
+
+ // Gravity Mode
+ self.emitterMode = kCCParticleModeGravity;
+
+ // Gravity Mode: gravity
+ self.gravity = ccp(0,0);
+
+ // Gravity Mode: speed of particles
+ self.speed = 60;
+ self.speedVar = 10;
+
+ // Gravity Mode: radial
+ self.radialAccel = -80;
+ self.radialAccelVar = 0;
+
+ // Gravity Mode: tagential
+ self.tangentialAccel = 80;
+ self.tangentialAccelVar = 0;
+
+ // angle
+ angle = 90;
+ angleVar = 360;
+
+ // emitter position
+ CGSize winSize = [[CCDirector sharedDirector] winSize];
+ self.position = ccp(winSize.width/2, winSize.height/2);
+ posVar = CGPointZero;
+
+ // life of particles
+ life = 4;
+ lifeVar = 1;
+
+ // size, in pixels
+ startSize = 37.0f;
+ startSizeVar = 10.0f;
+ endSize = kCCParticleStartSizeEqualToEndSize;
+
+ // emits per second
+ emissionRate = totalParticles/life;
+
+ // color of particles
+ startColor.r = 0.12f;
+ startColor.g = 0.25f;
+ startColor.b = 0.76f;
+ startColor.a = 1.0f;
+ startColorVar.r = 0.0f;
+ startColorVar.g = 0.0f;
+ startColorVar.b = 0.0f;
+ startColorVar.a = 0.0f;
+ endColor.r = 0.0f;
+ endColor.g = 0.0f;
+ endColor.b = 0.0f;
+ endColor.a = 1.0f;
+ endColorVar.r = 0.0f;
+ endColorVar.g = 0.0f;
+ endColorVar.b = 0.0f;
+ endColorVar.a = 0.0f;
+
+ self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"];
+
+ // additive
+ self.blendAdditive = YES;
+ }
+
+ return self;
+}
+@end
+
+//
+// ParticleFlower
+//
+@implementation CCParticleFlower
+-(id) init
+{
+ return [self initWithTotalParticles:250];
+}
+
+-(id) initWithTotalParticles:(NSUInteger) p
+{
+ if( (self=[super initWithTotalParticles:p]) ) {
+
+ // duration
+ duration = kCCParticleDurationInfinity;
+
+ // Gravity Mode
+ self.emitterMode = kCCParticleModeGravity;
+
+ // Gravity Mode: gravity
+ self.gravity = ccp(0,0);
+
+ // Gravity Mode: speed of particles
+ self.speed = 80;
+ self.speedVar = 10;
+
+ // Gravity Mode: radial
+ self.radialAccel = -60;
+ self.radialAccelVar = 0;
+
+ // Gravity Mode: tagential
+ self.tangentialAccel = 15;
+ self.tangentialAccelVar = 0;
+
+ // angle
+ angle = 90;
+ angleVar = 360;
+
+ // emitter position
+ CGSize winSize = [[CCDirector sharedDirector] winSize];
+ self.position = ccp(winSize.width/2, winSize.height/2);
+ posVar = CGPointZero;
+
+ // life of particles
+ life = 4;
+ lifeVar = 1;
+
+ // size, in pixels
+ startSize = 30.0f;
+ startSizeVar = 10.0f;
+ endSize = kCCParticleStartSizeEqualToEndSize;
+
+ // emits per second
+ emissionRate = totalParticles/life;
+
+ // color of particles
+ startColor.r = 0.50f;
+ startColor.g = 0.50f;
+ startColor.b = 0.50f;
+ startColor.a = 1.0f;
+ startColorVar.r = 0.5f;
+ startColorVar.g = 0.5f;
+ startColorVar.b = 0.5f;
+ startColorVar.a = 0.5f;
+ endColor.r = 0.0f;
+ endColor.g = 0.0f;
+ endColor.b = 0.0f;
+ endColor.a = 1.0f;
+ endColorVar.r = 0.0f;
+ endColorVar.g = 0.0f;
+ endColorVar.b = 0.0f;
+ endColorVar.a = 0.0f;
+
+ self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"];
+
+ // additive
+ self.blendAdditive = YES;
+ }
+
+ return self;
+}
+@end
+
+//
+// ParticleMeteor
+//
+@implementation CCParticleMeteor
+-(id) init
+{
+ return [self initWithTotalParticles:150];
+}
+
+-(id) initWithTotalParticles:(NSUInteger) p
+{
+ if( (self=[super initWithTotalParticles:p]) ) {
+
+ // duration
+ duration = kCCParticleDurationInfinity;
+
+ // Gravity Mode
+ self.emitterMode = kCCParticleModeGravity;
+
+ // Gravity Mode: gravity
+ self.gravity = ccp(-200,200);
+
+ // Gravity Mode: speed of particles
+ self.speed = 15;
+ self.speedVar = 5;
+
+ // Gravity Mode: radial
+ self.radialAccel = 0;
+ self.radialAccelVar = 0;
+
+ // Gravity Mode: tagential
+ self.tangentialAccel = 0;
+ self.tangentialAccelVar = 0;
+
+ // angle
+ angle = 90;
+ angleVar = 360;
+
+ // emitter position
+ CGSize winSize = [[CCDirector sharedDirector] winSize];
+ self.position = ccp(winSize.width/2, winSize.height/2);
+ posVar = CGPointZero;
+
+ // life of particles
+ life = 2;
+ lifeVar = 1;
+
+ // size, in pixels
+ startSize = 60.0f;
+ startSizeVar = 10.0f;
+ endSize = kCCParticleStartSizeEqualToEndSize;
+
+ // emits per second
+ emissionRate = totalParticles/life;
+
+ // color of particles
+ startColor.r = 0.2f;
+ startColor.g = 0.4f;
+ startColor.b = 0.7f;
+ startColor.a = 1.0f;
+ startColorVar.r = 0.0f;
+ startColorVar.g = 0.0f;
+ startColorVar.b = 0.2f;
+ startColorVar.a = 0.1f;
+ endColor.r = 0.0f;
+ endColor.g = 0.0f;
+ endColor.b = 0.0f;
+ endColor.a = 1.0f;
+ endColorVar.r = 0.0f;
+ endColorVar.g = 0.0f;
+ endColorVar.b = 0.0f;
+ endColorVar.a = 0.0f;
+
+ self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"];
+
+ // additive
+ self.blendAdditive = YES;
+ }
+
+ return self;
+}
+@end
+
+//
+// ParticleSpiral
+//
+@implementation CCParticleSpiral
+-(id) init
+{
+ return [self initWithTotalParticles:500];
+}
+
+-(id) initWithTotalParticles:(NSUInteger) p
+{
+ if( (self=[super initWithTotalParticles:p]) ) {
+
+ // duration
+ duration = kCCParticleDurationInfinity;
+
+ // Gravity Mode
+ self.emitterMode = kCCParticleModeGravity;
+
+ // Gravity Mode: gravity
+ self.gravity = ccp(0,0);
+
+ // Gravity Mode: speed of particles
+ self.speed = 150;
+ self.speedVar = 0;
+
+ // Gravity Mode: radial
+ self.radialAccel = -380;
+ self.radialAccelVar = 0;
+
+ // Gravity Mode: tagential
+ self.tangentialAccel = 45;
+ self.tangentialAccelVar = 0;
+
+ // angle
+ angle = 90;
+ angleVar = 0;
+
+ // emitter position
+ CGSize winSize = [[CCDirector sharedDirector] winSize];
+ self.position = ccp(winSize.width/2, winSize.height/2);
+ posVar = CGPointZero;
+
+ // life of particles
+ life = 12;
+ lifeVar = 0;
+
+ // size, in pixels
+ startSize = 20.0f;
+ startSizeVar = 0.0f;
+ endSize = kCCParticleStartSizeEqualToEndSize;
+
+ // emits per second
+ emissionRate = totalParticles/life;
+
+ // color of particles
+ startColor.r = 0.5f;
+ startColor.g = 0.5f;
+ startColor.b = 0.5f;
+ startColor.a = 1.0f;
+ startColorVar.r = 0.5f;
+ startColorVar.g = 0.5f;
+ startColorVar.b = 0.5f;
+ startColorVar.a = 0.0f;
+ endColor.r = 0.5f;
+ endColor.g = 0.5f;
+ endColor.b = 0.5f;
+ endColor.a = 1.0f;
+ endColorVar.r = 0.5f;
+ endColorVar.g = 0.5f;
+ endColorVar.b = 0.5f;
+ endColorVar.a = 0.0f;
+
+ self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"];
+
+ // additive
+ self.blendAdditive = NO;
+ }
+
+ return self;
+}
+@end
+
+//
+// ParticleExplosion
+//
+@implementation CCParticleExplosion
+-(id) init
+{
+ return [self initWithTotalParticles:700];
+}
+
+-(id) initWithTotalParticles:(NSUInteger)p
+{
+ if( (self=[super initWithTotalParticles:p]) ) {
+
+ // duration
+ duration = 0.1f;
+
+ self.emitterMode = kCCParticleModeGravity;
+
+ // Gravity Mode: gravity
+ self.gravity = ccp(0,0);
+
+ // Gravity Mode: speed of particles
+ self.speed = 70;
+ self.speedVar = 40;
+
+ // Gravity Mode: radial
+ self.radialAccel = 0;
+ self.radialAccelVar = 0;
+
+ // Gravity Mode: tagential
+ self.tangentialAccel = 0;
+ self.tangentialAccelVar = 0;
+
+ // angle
+ angle = 90;
+ angleVar = 360;
+
+ // emitter position
+ CGSize winSize = [[CCDirector sharedDirector] winSize];
+ self.position = ccp(winSize.width/2, winSize.height/2);
+ posVar = CGPointZero;
+
+ // life of particles
+ life = 5.0f;
+ lifeVar = 2;
+
+ // size, in pixels
+ startSize = 15.0f;
+ startSizeVar = 10.0f;
+ endSize = kCCParticleStartSizeEqualToEndSize;
+
+ // emits per second
+ emissionRate = totalParticles/duration;
+
+ // color of particles
+ startColor.r = 0.7f;
+ startColor.g = 0.1f;
+ startColor.b = 0.2f;
+ startColor.a = 1.0f;
+ startColorVar.r = 0.5f;
+ startColorVar.g = 0.5f;
+ startColorVar.b = 0.5f;
+ startColorVar.a = 0.0f;
+ endColor.r = 0.5f;
+ endColor.g = 0.5f;
+ endColor.b = 0.5f;
+ endColor.a = 0.0f;
+ endColorVar.r = 0.5f;
+ endColorVar.g = 0.5f;
+ endColorVar.b = 0.5f;
+ endColorVar.a = 0.0f;
+
+ self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"];
+
+ // additive
+ self.blendAdditive = NO;
+ }
+
+ return self;
+}
+@end
+
+//
+// ParticleSmoke
+//
+@implementation CCParticleSmoke
+-(id) init
+{
+ return [self initWithTotalParticles:200];
+}
+
+-(id) initWithTotalParticles:(NSUInteger) p
+{
+ if( (self=[super initWithTotalParticles:p]) ) {
+
+ // duration
+ duration = kCCParticleDurationInfinity;
+
+ // Emitter mode: Gravity Mode
+ self.emitterMode = kCCParticleModeGravity;
+
+ // Gravity Mode: gravity
+ self.gravity = ccp(0,0);
+
+ // Gravity Mode: radial acceleration
+ self.radialAccel = 0;
+ self.radialAccelVar = 0;
+
+ // Gravity Mode: speed of particles
+ self.speed = 25;
+ self.speedVar = 10;
+
+ // angle
+ angle = 90;
+ angleVar = 5;
+
+ // emitter position
+ CGSize winSize = [[CCDirector sharedDirector] winSize];
+ self.position = ccp(winSize.width/2, 0);
+ posVar = ccp(20, 0);
+
+ // life of particles
+ life = 4;
+ lifeVar = 1;
+
+ // size, in pixels
+ startSize = 60.0f;
+ startSizeVar = 10.0f;
+ endSize = kCCParticleStartSizeEqualToEndSize;
+
+ // emits per frame
+ emissionRate = totalParticles/life;
+
+ // color of particles
+ startColor.r = 0.8f;
+ startColor.g = 0.8f;
+ startColor.b = 0.8f;
+ startColor.a = 1.0f;
+ startColorVar.r = 0.02f;
+ startColorVar.g = 0.02f;
+ startColorVar.b = 0.02f;
+ startColorVar.a = 0.0f;
+ endColor.r = 0.0f;
+ endColor.g = 0.0f;
+ endColor.b = 0.0f;
+ endColor.a = 1.0f;
+ endColorVar.r = 0.0f;
+ endColorVar.g = 0.0f;
+ endColorVar.b = 0.0f;
+ endColorVar.a = 0.0f;
+
+ self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"];
+
+ // additive
+ self.blendAdditive = NO;
+ }
+
+ return self;
+}
+@end
+
+@implementation CCParticleSnow
+-(id) init
+{
+ return [self initWithTotalParticles:700];
+}
+
+-(id) initWithTotalParticles:(NSUInteger)p
+{
+ if( (self=[super initWithTotalParticles:p]) ) {
+
+ // duration
+ duration = kCCParticleDurationInfinity;
+
+ // set gravity mode.
+ self.emitterMode = kCCParticleModeGravity;
+
+ // Gravity Mode: gravity
+ self.gravity = ccp(0,-1);
+
+ // Gravity Mode: speed of particles
+ self.speed = 5;
+ self.speedVar = 1;
+
+ // Gravity Mode: radial
+ self.radialAccel = 0;
+ self.radialAccelVar = 1;
+
+ // Gravity mode: tagential
+ self.tangentialAccel = 0;
+ self.tangentialAccelVar = 1;
+
+ // emitter position
+ self.position = (CGPoint) {
+ [[CCDirector sharedDirector] winSize].width / 2,
+ [[CCDirector sharedDirector] winSize].height + 10
+ };
+ posVar = ccp( [[CCDirector sharedDirector] winSize].width / 2, 0 );
+
+ // angle
+ angle = -90;
+ angleVar = 5;
+
+ // life of particles
+ life = 45;
+ lifeVar = 15;
+
+ // size, in pixels
+ startSize = 10.0f;
+ startSizeVar = 5.0f;
+ endSize = kCCParticleStartSizeEqualToEndSize;
+
+ // emits per second
+ emissionRate = 10;
+
+ // color of particles
+ startColor.r = 1.0f;
+ startColor.g = 1.0f;
+ startColor.b = 1.0f;
+ startColor.a = 1.0f;
+ startColorVar.r = 0.0f;
+ startColorVar.g = 0.0f;
+ startColorVar.b = 0.0f;
+ startColorVar.a = 0.0f;
+ endColor.r = 1.0f;
+ endColor.g = 1.0f;
+ endColor.b = 1.0f;
+ endColor.a = 0.0f;
+ endColorVar.r = 0.0f;
+ endColorVar.g = 0.0f;
+ endColorVar.b = 0.0f;
+ endColorVar.a = 0.0f;
+
+ self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"];
+
+ // additive
+ self.blendAdditive = NO;
+ }
+
+ return self;
+}
+@end
+
+@implementation CCParticleRain
+-(id) init
+{
+ return [self initWithTotalParticles:1000];
+}
+
+-(id) initWithTotalParticles:(NSUInteger)p
+{
+ if( (self=[super initWithTotalParticles:p]) ) {
+
+ // duration
+ duration = kCCParticleDurationInfinity;
+
+ self.emitterMode = kCCParticleModeGravity;
+
+ // Gravity Mode: gravity
+ self.gravity = ccp(10,-10);
+
+ // Gravity Mode: radial
+ self.radialAccel = 0;
+ self.radialAccelVar = 1;
+
+ // Gravity Mode: tagential
+ self.tangentialAccel = 0;
+ self.tangentialAccelVar = 1;
+
+ // Gravity Mode: speed of particles
+ self.speed = 130;
+ self.speedVar = 30;
+
+ // angle
+ angle = -90;
+ angleVar = 5;
+
+
+ // emitter position
+ self.position = (CGPoint) {
+ [[CCDirector sharedDirector] winSize].width / 2,
+ [[CCDirector sharedDirector] winSize].height
+ };
+ posVar = ccp( [[CCDirector sharedDirector] winSize].width / 2, 0 );
+
+ // life of particles
+ life = 4.5f;
+ lifeVar = 0;
+
+ // size, in pixels
+ startSize = 4.0f;
+ startSizeVar = 2.0f;
+ endSize = kCCParticleStartSizeEqualToEndSize;
+
+ // emits per second
+ emissionRate = 20;
+
+ // color of particles
+ startColor.r = 0.7f;
+ startColor.g = 0.8f;
+ startColor.b = 1.0f;
+ startColor.a = 1.0f;
+ startColorVar.r = 0.0f;
+ startColorVar.g = 0.0f;
+ startColorVar.b = 0.0f;
+ startColorVar.a = 0.0f;
+ endColor.r = 0.7f;
+ endColor.g = 0.8f;
+ endColor.b = 1.0f;
+ endColor.a = 0.5f;
+ endColorVar.r = 0.0f;
+ endColorVar.g = 0.0f;
+ endColorVar.b = 0.0f;
+ endColorVar.a = 0.0f;
+
+ self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"];
+
+ // additive
+ self.blendAdditive = NO;
+ }
+
+ return self;
+}
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCProtocols.h"
+#import "CCNode.h"
+#import "ccTypes.h"
+#import "ccConfig.h"
+
+#if CC_ENABLE_PROFILERS
+@class CCProfilingTimer;
+#endif
+
+//* @enum
+enum {
+ /** The Particle emitter lives forever */
+ kCCParticleDurationInfinity = -1,
+
+ /** The starting size of the particle is equal to the ending size */
+ kCCParticleStartSizeEqualToEndSize = -1,
+
+ /** The starting radius of the particle is equal to the ending radius */
+ kCCParticleStartRadiusEqualToEndRadius = -1,
+
+ // backward compatible
+ kParticleStartSizeEqualToEndSize = kCCParticleStartSizeEqualToEndSize,
+ kParticleDurationInfinity = kCCParticleDurationInfinity,
+};
+
+//* @enum
+enum {
+ /** Gravity mode (A mode) */
+ kCCParticleModeGravity,
+
+ /** Radius mode (B mode) */
+ kCCParticleModeRadius,
+};
+
+
+/** @typedef tCCPositionType
+ possible types of particle positions
+ */
+typedef enum {
+ /** Living particles are attached to the world and are unaffected by emitter repositioning. */
+ kCCPositionTypeFree,
+
+ /** Living particles are attached to the world but will follow the emitter repositioning.
+ Use case: Attach an emitter to an sprite, and you want that the emitter follows the sprite.
+ */
+ kCCPositionTypeRelative,
+
+ /** Living particles are attached to the emitter and are translated along with it. */
+ kCCPositionTypeGrouped,
+}tCCPositionType;
+
+// backward compatible
+enum {
+ kPositionTypeFree = kCCPositionTypeFree,
+ kPositionTypeGrouped = kCCPositionTypeGrouped,
+};
+
+/** @struct tCCParticle
+ Structure that contains the values of each particle
+ */
+typedef struct sCCParticle {
+ CGPoint pos;
+ CGPoint startPos;
+
+ ccColor4F color;
+ ccColor4F deltaColor;
+
+ float size;
+ float deltaSize;
+
+ float rotation;
+ float deltaRotation;
+
+ ccTime timeToLive;
+
+ union {
+ // Mode A: gravity, direction, radial accel, tangential accel
+ struct {
+ CGPoint dir;
+ float radialAccel;
+ float tangentialAccel;
+ } A;
+
+ // Mode B: radius mode
+ struct {
+ float angle;
+ float degreesPerSecond;
+ float radius;
+ float deltaRadius;
+ } B;
+ } mode;
+
+}tCCParticle;
+
+typedef void (*CC_UPDATE_PARTICLE_IMP)(id, SEL, tCCParticle*, CGPoint);
+
+@class CCTexture2D;
+
+/** Particle System base class
+ Attributes of a Particle System:
+ - emmision rate of the particles
+ - Gravity Mode (Mode A):
+ - gravity
+ - direction
+ - speed +- variance
+ - tangential acceleration +- variance
+ - radial acceleration +- variance
+ - Radius Mode (Mode B):
+ - startRadius +- variance
+ - endRadius +- variance
+ - rotate +- variance
+ - Properties common to all modes:
+ - life +- life variance
+ - start spin +- variance
+ - end spin +- variance
+ - start size +- variance
+ - end size +- variance
+ - start color +- variance
+ - end color +- variance
+ - life +- variance
+ - blending function
+ - texture
+
+ cocos2d also supports particles generated by Particle Designer (http://particledesigner.71squared.com/).
+ 'Radius Mode' in Particle Designer uses a fixed emit rate of 30 hz. Since that can't be guarateed in cocos2d,
+ cocos2d uses a another approach, but the results are almost identical.
+
+ cocos2d supports all the variables used by Particle Designer plus a bit more:
+ - spinning particles (supported when using CCParticleSystemQuad)
+ - tangential acceleration (Gravity mode)
+ - radial acceleration (Gravity mode)
+ - radius direction (Radius mode) (Particle Designer supports outwards to inwards direction only)
+
+ It is possible to customize any of the above mentioned properties in runtime. Example:
+
+ @code
+ emitter.radialAccel = 15;
+ emitter.startSpin = 0;
+ @endcode
+
+ */
+@interface CCParticleSystem : CCNode <CCTextureProtocol>
+{
+ // is the particle system active ?
+ BOOL active;
+ // duration in seconds of the system. -1 is infinity
+ float duration;
+ // time elapsed since the start of the system (in seconds)
+ float elapsed;
+
+ // position is from "superclass" CocosNode
+ CGPoint sourcePosition;
+ // Position variance
+ CGPoint posVar;
+
+ // The angle (direction) of the particles measured in degrees
+ float angle;
+ // Angle variance measured in degrees;
+ float angleVar;
+
+ // Different modes
+
+ NSInteger emitterMode_;
+ union {
+ // Mode A:Gravity + Tangential Accel + Radial Accel
+ struct {
+ // gravity of the particles
+ CGPoint gravity;
+
+ // The speed the particles will have.
+ float speed;
+ // The speed variance
+ float speedVar;
+
+ // Tangential acceleration
+ float tangentialAccel;
+ // Tangential acceleration variance
+ float tangentialAccelVar;
+
+ // Radial acceleration
+ float radialAccel;
+ // Radial acceleration variance
+ float radialAccelVar;
+ } A;
+
+ // Mode B: circular movement (gravity, radial accel and tangential accel don't are not used in this mode)
+ struct {
+
+ // The starting radius of the particles
+ float startRadius;
+ // The starting radius variance of the particles
+ float startRadiusVar;
+ // The ending radius of the particles
+ float endRadius;
+ // The ending radius variance of the particles
+ float endRadiusVar;
+ // Number of degress to rotate a particle around the source pos per second
+ float rotatePerSecond;
+ // Variance in degrees for rotatePerSecond
+ float rotatePerSecondVar;
+ } B;
+ } mode;
+
+ // start ize of the particles
+ float startSize;
+ // start Size variance
+ float startSizeVar;
+ // End size of the particle
+ float endSize;
+ // end size of variance
+ float endSizeVar;
+
+ // How many seconds will the particle live
+ float life;
+ // Life variance
+ float lifeVar;
+
+ // Start color of the particles
+ ccColor4F startColor;
+ // Start color variance
+ ccColor4F startColorVar;
+ // End color of the particles
+ ccColor4F endColor;
+ // End color variance
+ ccColor4F endColorVar;
+
+ // start angle of the particles
+ float startSpin;
+ // start angle variance
+ float startSpinVar;
+ // End angle of the particle
+ float endSpin;
+ // end angle ariance
+ float endSpinVar;
+
+
+ // Array of particles
+ tCCParticle *particles;
+ // Maximum particles
+ NSUInteger totalParticles;
+ // Count of active particles
+ NSUInteger particleCount;
+
+ // color modulate
+// BOOL colorModulate;
+
+ // How many particles can be emitted per second
+ float emissionRate;
+ float emitCounter;
+
+ // Texture of the particles
+ CCTexture2D *texture_;
+ // blend function
+ ccBlendFunc blendFunc_;
+
+ // movment type: free or grouped
+ tCCPositionType positionType_;
+
+ // Whether or not the node will be auto-removed when there are not particles
+ BOOL autoRemoveOnFinish_;
+
+ // particle idx
+ NSUInteger particleIdx;
+
+ // Optimization
+ CC_UPDATE_PARTICLE_IMP updateParticleImp;
+ SEL updateParticleSel;
+
+// profiling
+#if CC_ENABLE_PROFILERS
+ CCProfilingTimer* _profilingTimer;
+#endif
+}
+
+/** Is the emitter active */
+@property (nonatomic,readonly) BOOL active;
+/** Quantity of particles that are being simulated at the moment */
+@property (nonatomic,readonly) NSUInteger particleCount;
+/** How many seconds the emitter wil run. -1 means 'forever' */
+@property (nonatomic,readwrite,assign) float duration;
+/** sourcePosition of the emitter */
+@property (nonatomic,readwrite,assign) CGPoint sourcePosition;
+/** Position variance of the emitter */
+@property (nonatomic,readwrite,assign) CGPoint posVar;
+/** life, and life variation of each particle */
+@property (nonatomic,readwrite,assign) float life;
+/** life variance of each particle */
+@property (nonatomic,readwrite,assign) float lifeVar;
+/** angle and angle variation of each particle */
+@property (nonatomic,readwrite,assign) float angle;
+/** angle variance of each particle */
+@property (nonatomic,readwrite,assign) float angleVar;
+
+/** Gravity value. Only available in 'Gravity' mode. */
+@property (nonatomic,readwrite,assign) CGPoint gravity;
+/** speed of each particle. Only available in 'Gravity' mode. */
+@property (nonatomic,readwrite,assign) float speed;
+/** speed variance of each particle. Only available in 'Gravity' mode. */
+@property (nonatomic,readwrite,assign) float speedVar;
+/** tangential acceleration of each particle. Only available in 'Gravity' mode. */
+@property (nonatomic,readwrite,assign) float tangentialAccel;
+/** tangential acceleration variance of each particle. Only available in 'Gravity' mode. */
+@property (nonatomic,readwrite,assign) float tangentialAccelVar;
+/** radial acceleration of each particle. Only available in 'Gravity' mode. */
+@property (nonatomic,readwrite,assign) float radialAccel;
+/** radial acceleration variance of each particle. Only available in 'Gravity' mode. */
+@property (nonatomic,readwrite,assign) float radialAccelVar;
+
+/** The starting radius of the particles. Only available in 'Radius' mode. */
+@property (nonatomic,readwrite,assign) float startRadius;
+/** The starting radius variance of the particles. Only available in 'Radius' mode. */
+@property (nonatomic,readwrite,assign) float startRadiusVar;
+/** The ending radius of the particles. Only available in 'Radius' mode. */
+@property (nonatomic,readwrite,assign) float endRadius;
+/** The ending radius variance of the particles. Only available in 'Radius' mode. */
+@property (nonatomic,readwrite,assign) float endRadiusVar;
+/** Number of degress to rotate a particle around the source pos per second. Only available in 'Radius' mode. */
+@property (nonatomic,readwrite,assign) float rotatePerSecond;
+/** Variance in degrees for rotatePerSecond. Only available in 'Radius' mode. */
+@property (nonatomic,readwrite,assign) float rotatePerSecondVar;
+
+/** start size in pixels of each particle */
+@property (nonatomic,readwrite,assign) float startSize;
+/** size variance in pixels of each particle */
+@property (nonatomic,readwrite,assign) float startSizeVar;
+/** end size in pixels of each particle */
+@property (nonatomic,readwrite,assign) float endSize;
+/** end size variance in pixels of each particle */
+@property (nonatomic,readwrite,assign) float endSizeVar;
+/** start color of each particle */
+@property (nonatomic,readwrite,assign) ccColor4F startColor;
+/** start color variance of each particle */
+@property (nonatomic,readwrite,assign) ccColor4F startColorVar;
+/** end color and end color variation of each particle */
+@property (nonatomic,readwrite,assign) ccColor4F endColor;
+/** end color variance of each particle */
+@property (nonatomic,readwrite,assign) ccColor4F endColorVar;
+//* initial angle of each particle
+@property (nonatomic,readwrite,assign) float startSpin;
+//* initial angle of each particle
+@property (nonatomic,readwrite,assign) float startSpinVar;
+//* initial angle of each particle
+@property (nonatomic,readwrite,assign) float endSpin;
+//* initial angle of each particle
+@property (nonatomic,readwrite,assign) float endSpinVar;
+/** emission rate of the particles */
+@property (nonatomic,readwrite,assign) float emissionRate;
+/** maximum particles of the system */
+@property (nonatomic,readwrite,assign) NSUInteger totalParticles;
+/** conforms to CocosNodeTexture protocol */
+@property (nonatomic,readwrite, retain) CCTexture2D * texture;
+/** conforms to CocosNodeTexture protocol */
+@property (nonatomic,readwrite) ccBlendFunc blendFunc;
+/** whether or not the particles are using blend additive.
+ If enabled, the following blending function will be used.
+ @code
+ source blend function = GL_SRC_ALPHA;
+ dest blend function = GL_ONE;
+ @endcode
+ */
+@property (nonatomic,readwrite) BOOL blendAdditive;
+/** particles movement type: Free or Grouped
+ @since v0.8
+ */
+@property (nonatomic,readwrite) tCCPositionType positionType;
+/** whether or not the node will be auto-removed when it has no particles left.
+ By default it is NO.
+ @since v0.8
+ */
+@property (nonatomic,readwrite) BOOL autoRemoveOnFinish;
+/** Switch between different kind of emitter modes:
+ - kCCParticleModeGravity: uses gravity, speed, radial and tangential acceleration
+ - kCCParticleModeRadius: uses radius movement + rotation
+ */
+@property (nonatomic,readwrite) NSInteger emitterMode;
+
+/** creates an initializes a CCParticleSystem from a plist file.
+ This plist files can be creted manually or with Particle Designer:
+ http://particledesigner.71squared.com/
+ @since v0.99.3
+ */
++(id) particleWithFile:(NSString*)plistFile;
+
+/** initializes a CCParticleSystem from a plist file.
+ This plist files can be creted manually or with Particle Designer:
+ http://particledesigner.71squared.com/
+ @since v0.99.3
+ */
+-(id) initWithFile:(NSString*) plistFile;
+
+/** initializes a CCQuadParticleSystem from a NSDictionary.
+ @since v0.99.3
+ */
+-(id) initWithDictionary:(NSDictionary*)dictionary;
+
+//! Initializes a system with a fixed number of particles
+-(id) initWithTotalParticles:(NSUInteger) numberOfParticles;
+//! Add a particle to the emitter
+-(BOOL) addParticle;
+//! Initializes a particle
+-(void) initParticle: (tCCParticle*) particle;
+//! stop emitting particles. Running particles will continue to run until they die
+-(void) stopSystem;
+//! Kill all living particles.
+-(void) resetSystem;
+//! whether or not the system is full
+-(BOOL) isFull;
+
+//! should be overriden by subclasses
+-(void) updateQuadWithParticle:(tCCParticle*)particle newPosition:(CGPoint)pos;
+//! should be overriden by subclasses
+-(void) postStep;
+
+//! called in every loop.
+-(void) update: (ccTime) dt;
+
+@end
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+// ideas taken from:
+// . The ocean spray in your face [Jeff Lander]
+// http://www.double.co.nz/dust/col0798.pdf
+// . Building an Advanced Particle System [John van der Burg]
+// http://www.gamasutra.com/features/20000623/vanderburg_01.htm
+// . LOVE game engine
+// http://love2d.org/
+//
+//
+// Radius mode support, from 71 squared
+// http://particledesigner.71squared.com/
+//
+// IMPORTANT: Particle Designer is supported by cocos2d, but
+// 'Radius Mode' in Particle Designer uses a fixed emit rate of 30 hz. Since that can't be guarateed in cocos2d,
+// cocos2d uses a another approach, but the results are almost identical.
+//
+
+// opengl
+#import "Platforms/CCGL.h"
+
+// cocos2d
+#import "ccConfig.h"
+#if CC_ENABLE_PROFILERS
+#import "Support/CCProfiling.h"
+#endif
+#import "CCParticleSystem.h"
+#import "CCTextureCache.h"
+#import "ccMacros.h"
+
+// support
+#import "Support/OpenGL_Internal.h"
+#import "Support/CGPointExtension.h"
+#import "Support/base64.h"
+#import "Support/ZipUtils.h"
+#import "Support/CCFileUtils.h"
+
+@implementation CCParticleSystem
+@synthesize active, duration;
+@synthesize sourcePosition, posVar;
+@synthesize particleCount;
+@synthesize life, lifeVar;
+@synthesize angle, angleVar;
+@synthesize startColor, startColorVar, endColor, endColorVar;
+@synthesize startSpin, startSpinVar, endSpin, endSpinVar;
+@synthesize emissionRate;
+@synthesize totalParticles;
+@synthesize startSize, startSizeVar;
+@synthesize endSize, endSizeVar;
+@synthesize blendFunc = blendFunc_;
+@synthesize positionType = positionType_;
+@synthesize autoRemoveOnFinish = autoRemoveOnFinish_;
+@synthesize emitterMode = emitterMode_;
+
+
++(id) particleWithFile:(NSString*) plistFile
+{
+ return [[[self alloc] initWithFile:plistFile] autorelease];
+}
+
+-(id) init {
+ NSAssert(NO, @"CCParticleSystem: Init not supported.");
+ [self release];
+ return nil;
+}
+
+-(id) initWithFile:(NSString *)plistFile
+{
+ NSString *path = [CCFileUtils fullPathFromRelativePath:plistFile];
+ NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
+
+ NSAssert( dict != nil, @"Particles: file not found");
+ return [self initWithDictionary:dict];
+}
+
+-(id) initWithDictionary:(NSDictionary *)dictionary
+{
+ NSUInteger maxParticles = [[dictionary valueForKey:@"maxParticles"] intValue];
+ // self, not super
+ if ((self=[self initWithTotalParticles:maxParticles] ) ) {
+
+ // angle
+ angle = [[dictionary valueForKey:@"angle"] floatValue];
+ angleVar = [[dictionary valueForKey:@"angleVariance"] floatValue];
+
+ // duration
+ duration = [[dictionary valueForKey:@"duration"] floatValue];
+
+ // blend function
+ blendFunc_.src = [[dictionary valueForKey:@"blendFuncSource"] intValue];
+ blendFunc_.dst = [[dictionary valueForKey:@"blendFuncDestination"] intValue];
+
+ // color
+ float r,g,b,a;
+
+ r = [[dictionary valueForKey:@"startColorRed"] floatValue];
+ g = [[dictionary valueForKey:@"startColorGreen"] floatValue];
+ b = [[dictionary valueForKey:@"startColorBlue"] floatValue];
+ a = [[dictionary valueForKey:@"startColorAlpha"] floatValue];
+ startColor = (ccColor4F) {r,g,b,a};
+
+ r = [[dictionary valueForKey:@"startColorVarianceRed"] floatValue];
+ g = [[dictionary valueForKey:@"startColorVarianceGreen"] floatValue];
+ b = [[dictionary valueForKey:@"startColorVarianceBlue"] floatValue];
+ a = [[dictionary valueForKey:@"startColorVarianceAlpha"] floatValue];
+ startColorVar = (ccColor4F) {r,g,b,a};
+
+ r = [[dictionary valueForKey:@"finishColorRed"] floatValue];
+ g = [[dictionary valueForKey:@"finishColorGreen"] floatValue];
+ b = [[dictionary valueForKey:@"finishColorBlue"] floatValue];
+ a = [[dictionary valueForKey:@"finishColorAlpha"] floatValue];
+ endColor = (ccColor4F) {r,g,b,a};
+
+ r = [[dictionary valueForKey:@"finishColorVarianceRed"] floatValue];
+ g = [[dictionary valueForKey:@"finishColorVarianceGreen"] floatValue];
+ b = [[dictionary valueForKey:@"finishColorVarianceBlue"] floatValue];
+ a = [[dictionary valueForKey:@"finishColorVarianceAlpha"] floatValue];
+ endColorVar = (ccColor4F) {r,g,b,a};
+
+ // particle size
+ startSize = [[dictionary valueForKey:@"startParticleSize"] floatValue];
+ startSizeVar = [[dictionary valueForKey:@"startParticleSizeVariance"] floatValue];
+ endSize = [[dictionary valueForKey:@"finishParticleSize"] floatValue];
+ endSizeVar = [[dictionary valueForKey:@"finishParticleSizeVariance"] floatValue];
+
+
+ // position
+ float x = [[dictionary valueForKey:@"sourcePositionx"] floatValue];
+ float y = [[dictionary valueForKey:@"sourcePositiony"] floatValue];
+ self.position = ccp(x,y);
+ posVar.x = [[dictionary valueForKey:@"sourcePositionVariancex"] floatValue];
+ posVar.y = [[dictionary valueForKey:@"sourcePositionVariancey"] floatValue];
+
+
+ // Spinning
+ startSpin = [[dictionary valueForKey:@"rotationStart"] floatValue];
+ startSpinVar = [[dictionary valueForKey:@"rotationStartVariance"] floatValue];
+ endSpin = [[dictionary valueForKey:@"rotationEnd"] floatValue];
+ endSpinVar = [[dictionary valueForKey:@"rotationEndVariance"] floatValue];
+
+ emitterMode_ = [[dictionary valueForKey:@"emitterType"] intValue];
+
+ // Mode A: Gravity + tangential accel + radial accel
+ if( emitterMode_ == kCCParticleModeGravity ) {
+ // gravity
+ mode.A.gravity.x = [[dictionary valueForKey:@"gravityx"] floatValue];
+ mode.A.gravity.y = [[dictionary valueForKey:@"gravityy"] floatValue];
+
+ //
+ // speed
+ mode.A.speed = [[dictionary valueForKey:@"speed"] floatValue];
+ mode.A.speedVar = [[dictionary valueForKey:@"speedVariance"] floatValue];
+
+ // radial acceleration
+ NSString *tmp = [dictionary valueForKey:@"radialAcceleration"];
+ mode.A.radialAccel = tmp ? [tmp floatValue] : 0;
+
+ tmp = [dictionary valueForKey:@"radialAccelVariance"];
+ mode.A.radialAccelVar = tmp ? [tmp floatValue] : 0;
+
+ // tangential acceleration
+ tmp = [dictionary valueForKey:@"tangentialAcceleration"];
+ mode.A.tangentialAccel = tmp ? [tmp floatValue] : 0;
+
+ tmp = [dictionary valueForKey:@"tangentialAccelVariance"];
+ mode.A.tangentialAccelVar = tmp ? [tmp floatValue] : 0;
+ }
+
+
+ // or Mode B: radius movement
+ else if( emitterMode_ == kCCParticleModeRadius ) {
+ float maxRadius = [[dictionary valueForKey:@"maxRadius"] floatValue];
+ float maxRadiusVar = [[dictionary valueForKey:@"maxRadiusVariance"] floatValue];
+ float minRadius = [[dictionary valueForKey:@"minRadius"] floatValue];
+
+ mode.B.startRadius = maxRadius;
+ mode.B.startRadiusVar = maxRadiusVar;
+ mode.B.endRadius = minRadius;
+ mode.B.endRadiusVar = 0;
+ mode.B.rotatePerSecond = [[dictionary valueForKey:@"rotatePerSecond"] floatValue];
+ mode.B.rotatePerSecondVar = [[dictionary valueForKey:@"rotatePerSecondVariance"] floatValue];
+
+ } else {
+ NSAssert( NO, @"Invalid emitterType in config file");
+ }
+
+ // life span
+ life = [[dictionary valueForKey:@"particleLifespan"] floatValue];
+ lifeVar = [[dictionary valueForKey:@"particleLifespanVariance"] floatValue];
+
+ // emission Rate
+ emissionRate = totalParticles/life;
+
+ // texture
+ // Try to get the texture from the cache
+ NSString *textureName = [dictionary valueForKey:@"textureFileName"];
+
+ CCTexture2D *tex = [[CCTextureCache sharedTextureCache] addImage:textureName];
+
+ if( tex )
+ self.texture = tex;
+
+ else {
+
+ NSString *textureData = [dictionary valueForKey:@"textureImageData"];
+ NSAssert( textureData, @"CCParticleSystem: Couldn't load texture");
+
+ // if it fails, try to get it from the base64-gzipped data
+ unsigned char *buffer = NULL;
+ int len = base64Decode((unsigned char*)[textureData UTF8String], (unsigned int)[textureData length], &buffer);
+ NSAssert( buffer != NULL, @"CCParticleSystem: error decoding textureImageData");
+
+ unsigned char *deflated = NULL;
+ NSUInteger deflatedLen = ccInflateMemory(buffer, len, &deflated);
+ free( buffer );
+
+ NSAssert( deflated != NULL, @"CCParticleSystem: error ungzipping textureImageData");
+ NSData *data = [[NSData alloc] initWithBytes:deflated length:deflatedLen];
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+ UIImage *image = [[UIImage alloc] initWithData:data];
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+ NSBitmapImageRep *image = [[NSBitmapImageRep alloc] initWithData:data];
+#endif
+
+ free(deflated); deflated = NULL;
+
+ self.texture = [[CCTextureCache sharedTextureCache] addCGImage:[image CGImage] forKey:textureName];
+ [data release];
+ [image release];
+ }
+
+ NSAssert( [self texture] != NULL, @"CCParticleSystem: error loading the texture");
+
+ }
+
+ return self;
+}
+
+-(id) initWithTotalParticles:(NSUInteger) numberOfParticles
+{
+ if( (self=[super init]) ) {
+
+ totalParticles = numberOfParticles;
+
+ particles = calloc( totalParticles, sizeof(tCCParticle) );
+
+ if( ! particles ) {
+ NSLog(@"Particle system: not enough memory");
+ [self release];
+ return nil;
+ }
+
+ // default, active
+ active = YES;
+
+ // default blend function
+ blendFunc_ = (ccBlendFunc) { CC_BLEND_SRC, CC_BLEND_DST };
+
+ // default movement type;
+ positionType_ = kCCPositionTypeFree;
+
+ // by default be in mode A:
+ emitterMode_ = kCCParticleModeGravity;
+
+ // default: modulate
+ // XXX: not used
+ // colorModulate = YES;
+
+ autoRemoveOnFinish_ = NO;
+
+ // profiling
+#if CC_ENABLE_PROFILERS
+ _profilingTimer = [[CCProfiler timerWithName:@"particle system" andInstance:self] retain];
+#endif
+
+ // Optimization: compile udpateParticle method
+ updateParticleSel = @selector(updateQuadWithParticle:newPosition:);
+ updateParticleImp = (CC_UPDATE_PARTICLE_IMP) [self methodForSelector:updateParticleSel];
+
+ // udpate after action in run!
+ [self scheduleUpdateWithPriority:1];
+
+ }
+
+ return self;
+}
+
+-(void) dealloc
+{
+ free( particles );
+
+ [texture_ release];
+ // profiling
+#if CC_ENABLE_PROFILERS
+ [CCProfiler releaseTimer:_profilingTimer];
+#endif
+
+ [super dealloc];
+}
+
+-(BOOL) addParticle
+{
+ if( [self isFull] )
+ return NO;
+
+ tCCParticle * particle = &particles[ particleCount ];
+
+ [self initParticle: particle];
+ particleCount++;
+
+ return YES;
+}
+
+-(void) initParticle: (tCCParticle*) particle
+{
+
+ // timeToLive
+ // no negative life. prevent division by 0
+ particle->timeToLive = life + lifeVar * CCRANDOM_MINUS1_1();
+ particle->timeToLive = MAX(0, particle->timeToLive);
+
+ // position
+ particle->pos.x = sourcePosition.x + posVar.x * CCRANDOM_MINUS1_1();
+ particle->pos.x *= CC_CONTENT_SCALE_FACTOR();
+ particle->pos.y = sourcePosition.y + posVar.y * CCRANDOM_MINUS1_1();
+ particle->pos.y *= CC_CONTENT_SCALE_FACTOR();
+
+ // Color
+ ccColor4F start;
+ start.r = clampf( startColor.r + startColorVar.r * CCRANDOM_MINUS1_1(), 0, 1);
+ start.g = clampf( startColor.g + startColorVar.g * CCRANDOM_MINUS1_1(), 0, 1);
+ start.b = clampf( startColor.b + startColorVar.b * CCRANDOM_MINUS1_1(), 0, 1);
+ start.a = clampf( startColor.a + startColorVar.a * CCRANDOM_MINUS1_1(), 0, 1);
+
+ ccColor4F end;
+ end.r = clampf( endColor.r + endColorVar.r * CCRANDOM_MINUS1_1(), 0, 1);
+ end.g = clampf( endColor.g + endColorVar.g * CCRANDOM_MINUS1_1(), 0, 1);
+ end.b = clampf( endColor.b + endColorVar.b * CCRANDOM_MINUS1_1(), 0, 1);
+ end.a = clampf( endColor.a + endColorVar.a * CCRANDOM_MINUS1_1(), 0, 1);
+
+ particle->color = start;
+ particle->deltaColor.r = (end.r - start.r) / particle->timeToLive;
+ particle->deltaColor.g = (end.g - start.g) / particle->timeToLive;
+ particle->deltaColor.b = (end.b - start.b) / particle->timeToLive;
+ particle->deltaColor.a = (end.a - start.a) / particle->timeToLive;
+
+ // size
+ float startS = startSize + startSizeVar * CCRANDOM_MINUS1_1();
+ startS = MAX(0, startS); // No negative value
+ startS *= CC_CONTENT_SCALE_FACTOR();
+
+ particle->size = startS;
+ if( endSize == kCCParticleStartSizeEqualToEndSize )
+ particle->deltaSize = 0;
+ else {
+ float endS = endSize + endSizeVar * CCRANDOM_MINUS1_1();
+ endS = MAX(0, endS); // No negative values
+ endS *= CC_CONTENT_SCALE_FACTOR();
+ particle->deltaSize = (endS - startS) / particle->timeToLive;
+ }
+
+ // rotation
+ float startA = startSpin + startSpinVar * CCRANDOM_MINUS1_1();
+ float endA = endSpin + endSpinVar * CCRANDOM_MINUS1_1();
+ particle->rotation = startA;
+ particle->deltaRotation = (endA - startA) / particle->timeToLive;
+
+ // position
+ if( positionType_ == kCCPositionTypeFree ) {
+ CGPoint p = [self convertToWorldSpace:CGPointZero];
+ particle->startPos = ccpMult( p, CC_CONTENT_SCALE_FACTOR() );
+ }
+ else if( positionType_ == kCCPositionTypeRelative ) {
+ particle->startPos = ccpMult( position_, CC_CONTENT_SCALE_FACTOR() );
+ }
+
+ // direction
+ float a = CC_DEGREES_TO_RADIANS( angle + angleVar * CCRANDOM_MINUS1_1() );
+
+ // Mode Gravity: A
+ if( emitterMode_ == kCCParticleModeGravity ) {
+
+ CGPoint v = {cosf( a ), sinf( a )};
+ float s = mode.A.speed + mode.A.speedVar * CCRANDOM_MINUS1_1();
+ s *= CC_CONTENT_SCALE_FACTOR();
+
+ // direction
+ particle->mode.A.dir = ccpMult( v, s );
+
+ // radial accel
+ particle->mode.A.radialAccel = mode.A.radialAccel + mode.A.radialAccelVar * CCRANDOM_MINUS1_1();
+ particle->mode.A.radialAccel *= CC_CONTENT_SCALE_FACTOR();
+
+ // tangential accel
+ particle->mode.A.tangentialAccel = mode.A.tangentialAccel + mode.A.tangentialAccelVar * CCRANDOM_MINUS1_1();
+ particle->mode.A.tangentialAccel *= CC_CONTENT_SCALE_FACTOR();
+
+ }
+
+ // Mode Radius: B
+ else {
+ // Set the default diameter of the particle from the source position
+ float startRadius = mode.B.startRadius + mode.B.startRadiusVar * CCRANDOM_MINUS1_1();
+ float endRadius = mode.B.endRadius + mode.B.endRadiusVar * CCRANDOM_MINUS1_1();
+
+ startRadius *= CC_CONTENT_SCALE_FACTOR();
+ endRadius *= CC_CONTENT_SCALE_FACTOR();
+
+ particle->mode.B.radius = startRadius;
+
+ if( mode.B.endRadius == kCCParticleStartRadiusEqualToEndRadius )
+ particle->mode.B.deltaRadius = 0;
+ else
+ particle->mode.B.deltaRadius = (endRadius - startRadius) / particle->timeToLive;
+
+ particle->mode.B.angle = a;
+ particle->mode.B.degreesPerSecond = CC_DEGREES_TO_RADIANS(mode.B.rotatePerSecond + mode.B.rotatePerSecondVar * CCRANDOM_MINUS1_1());
+ }
+}
+
+-(void) stopSystem
+{
+ active = NO;
+ elapsed = duration;
+ emitCounter = 0;
+}
+
+-(void) resetSystem
+{
+ active = YES;
+ elapsed = 0;
+ for(particleIdx = 0; particleIdx < particleCount; ++particleIdx) {
+ tCCParticle *p = &particles[particleIdx];
+ p->timeToLive = 0;
+ }
+}
+
+-(BOOL) isFull
+{
+ return (particleCount == totalParticles);
+}
+
+#pragma mark ParticleSystem - MainLoop
+-(void) update: (ccTime) dt
+{
+ if( active && emissionRate ) {
+ float rate = 1.0f / emissionRate;
+ emitCounter += dt;
+ while( particleCount < totalParticles && emitCounter > rate ) {
+ [self addParticle];
+ emitCounter -= rate;
+ }
+
+ elapsed += dt;
+ if(duration != -1 && duration < elapsed)
+ [self stopSystem];
+ }
+
+ particleIdx = 0;
+
+
+#if CC_ENABLE_PROFILERS
+ CCProfilingBeginTimingBlock(_profilingTimer);
+#endif
+
+
+ CGPoint currentPosition = CGPointZero;
+ if( positionType_ == kCCPositionTypeFree ) {
+ currentPosition = [self convertToWorldSpace:CGPointZero];
+ currentPosition.x *= CC_CONTENT_SCALE_FACTOR();
+ currentPosition.y *= CC_CONTENT_SCALE_FACTOR();
+ }
+ else if( positionType_ == kCCPositionTypeRelative ) {
+ currentPosition = position_;
+ currentPosition.x *= CC_CONTENT_SCALE_FACTOR();
+ currentPosition.y *= CC_CONTENT_SCALE_FACTOR();
+ }
+
+ while( particleIdx < particleCount )
+ {
+ tCCParticle *p = &particles[particleIdx];
+
+ // life
+ p->timeToLive -= dt;
+
+ if( p->timeToLive > 0 ) {
+
+ // Mode A: gravity, direction, tangential accel & radial accel
+ if( emitterMode_ == kCCParticleModeGravity ) {
+ CGPoint tmp, radial, tangential;
+
+ radial = CGPointZero;
+ // radial acceleration
+ if(p->pos.x || p->pos.y)
+ radial = ccpNormalize(p->pos);
+
+ tangential = radial;
+ radial = ccpMult(radial, p->mode.A.radialAccel);
+
+ // tangential acceleration
+ float newy = tangential.x;
+ tangential.x = -tangential.y;
+ tangential.y = newy;
+ tangential = ccpMult(tangential, p->mode.A.tangentialAccel);
+
+ // (gravity + radial + tangential) * dt
+ tmp = ccpAdd( ccpAdd( radial, tangential), mode.A.gravity);
+ tmp = ccpMult( tmp, dt);
+ p->mode.A.dir = ccpAdd( p->mode.A.dir, tmp);
+ tmp = ccpMult(p->mode.A.dir, dt);
+ p->pos = ccpAdd( p->pos, tmp );
+ }
+
+ // Mode B: radius movement
+ else {
+ // Update the angle and radius of the particle.
+ p->mode.B.angle += p->mode.B.degreesPerSecond * dt;
+ p->mode.B.radius += p->mode.B.deltaRadius * dt;
+
+ p->pos.x = - cosf(p->mode.B.angle) * p->mode.B.radius;
+ p->pos.y = - sinf(p->mode.B.angle) * p->mode.B.radius;
+ }
+
+ // color
+ p->color.r += (p->deltaColor.r * dt);
+ p->color.g += (p->deltaColor.g * dt);
+ p->color.b += (p->deltaColor.b * dt);
+ p->color.a += (p->deltaColor.a * dt);
+
+ // size
+ p->size += (p->deltaSize * dt);
+ p->size = MAX( 0, p->size );
+
+ // angle
+ p->rotation += (p->deltaRotation * dt);
+
+ //
+ // update values in quad
+ //
+
+ CGPoint newPos;
+
+ if( positionType_ == kCCPositionTypeFree || positionType_ == kCCPositionTypeRelative ) {
+ CGPoint diff = ccpSub( currentPosition, p->startPos );
+ newPos = ccpSub(p->pos, diff);
+
+ } else
+ newPos = p->pos;
+
+
+ updateParticleImp(self, updateParticleSel, p, newPos);
+
+ // update particle counter
+ particleIdx++;
+
+ } else {
+ // life < 0
+ if( particleIdx != particleCount-1 )
+ particles[particleIdx] = particles[particleCount-1];
+ particleCount--;
+
+ if( particleCount == 0 && autoRemoveOnFinish_ ) {
+ [self unscheduleUpdate];
+ [parent_ removeChild:self cleanup:YES];
+ return;
+ }
+ }
+ }
+
+#if CC_ENABLE_PROFILERS
+ CCProfilingEndTimingBlock(_profilingTimer);
+#endif
+
+#ifdef CC_USES_VBO
+ [self postStep];
+#endif
+}
+
+-(void) updateQuadWithParticle:(tCCParticle*)particle newPosition:(CGPoint)pos;
+{
+ // should be overriden
+}
+
+-(void) postStep
+{
+ // should be overriden
+}
+
+#pragma mark ParticleSystem - CCTexture protocol
+
+-(void) setTexture:(CCTexture2D*) texture
+{
+ [texture_ release];
+ texture_ = [texture retain];
+
+ // If the new texture has No premultiplied alpha, AND the blendFunc hasn't been changed, then update it
+ if( texture_ && ! [texture hasPremultipliedAlpha] &&
+ ( blendFunc_.src == CC_BLEND_SRC && blendFunc_.dst == CC_BLEND_DST ) ) {
+
+ blendFunc_.src = GL_SRC_ALPHA;
+ blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA;
+ }
+}
+
+-(CCTexture2D*) texture
+{
+ return texture_;
+}
+
+#pragma mark ParticleSystem - Additive Blending
+-(void) setBlendAdditive:(BOOL)additive
+{
+ if( additive ) {
+ blendFunc_.src = GL_SRC_ALPHA;
+ blendFunc_.dst = GL_ONE;
+
+ } else {
+
+ if( texture_ && ! [texture_ hasPremultipliedAlpha] ) {
+ blendFunc_.src = GL_SRC_ALPHA;
+ blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA;
+ } else {
+ blendFunc_.src = CC_BLEND_SRC;
+ blendFunc_.dst = CC_BLEND_DST;
+ }
+ }
+}
+
+-(BOOL) blendAdditive
+{
+ return( blendFunc_.src == GL_SRC_ALPHA && blendFunc_.dst == GL_ONE);
+}
+
+#pragma mark ParticleSystem - Properties of Gravity Mode
+-(void) setTangentialAccel:(float)t
+{
+ NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+ mode.A.tangentialAccel = t;
+}
+-(float) tangentialAccel
+{
+ NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+ return mode.A.tangentialAccel;
+}
+
+-(void) setTangentialAccelVar:(float)t
+{
+ NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+ mode.A.tangentialAccelVar = t;
+}
+-(float) tangentialAccelVar
+{
+ NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+ return mode.A.tangentialAccelVar;
+}
+
+-(void) setRadialAccel:(float)t
+{
+ NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+ mode.A.radialAccel = t;
+}
+-(float) radialAccel
+{
+ NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+ return mode.A.radialAccel;
+}
+
+-(void) setRadialAccelVar:(float)t
+{
+ NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+ mode.A.radialAccelVar = t;
+}
+-(float) radialAccelVar
+{
+ NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+ return mode.A.radialAccelVar;
+}
+
+-(void) setGravity:(CGPoint)g
+{
+ NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+ mode.A.gravity = g;
+}
+-(CGPoint) gravity
+{
+ NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+ return mode.A.gravity;
+}
+
+-(void) setSpeed:(float)speed
+{
+ NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+ mode.A.speed = speed;
+}
+-(float) speed
+{
+ NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+ return mode.A.speed;
+}
+
+-(void) setSpeedVar:(float)speedVar
+{
+ NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+ mode.A.speedVar = speedVar;
+}
+-(float) speedVar
+{
+ NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity");
+ return mode.A.speedVar;
+}
+
+#pragma mark ParticleSystem - Properties of Radius Mode
+
+-(void) setStartRadius:(float)startRadius
+{
+ NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius");
+ mode.B.startRadius = startRadius;
+}
+-(float) startRadius
+{
+ NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius");
+ return mode.B.startRadius;
+}
+
+-(void) setStartRadiusVar:(float)startRadiusVar
+{
+ NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius");
+ mode.B.startRadiusVar = startRadiusVar;
+}
+-(float) startRadiusVar
+{
+ NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius");
+ return mode.B.startRadiusVar;
+}
+
+-(void) setEndRadius:(float)endRadius
+{
+ NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius");
+ mode.B.endRadius = endRadius;
+}
+-(float) endRadius
+{
+ NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius");
+ return mode.B.endRadius;
+}
+
+-(void) setEndRadiusVar:(float)endRadiusVar
+{
+ NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius");
+ mode.B.endRadiusVar = endRadiusVar;
+}
+-(float) endRadiusVar
+{
+ NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius");
+ return mode.B.endRadiusVar;
+}
+
+-(void) setRotatePerSecond:(float)degrees
+{
+ NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius");
+ mode.B.rotatePerSecond = degrees;
+}
+-(float) rotatePerSecond
+{
+ NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius");
+ return mode.B.rotatePerSecond;
+}
+
+-(void) setRotatePerSecondVar:(float)degrees
+{
+ NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius");
+ mode.B.rotatePerSecondVar = degrees;
+}
+-(float) rotatePerSecondVar
+{
+ NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius");
+ return mode.B.rotatePerSecondVar;
+}
+@end
+
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import <Availability.h>
+#import "CCParticleSystem.h"
+
+#define CC_MAX_PARTICLE_SIZE 64
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+/** CCParticleSystemPoint is a subclass of CCParticleSystem
+ Attributes of a Particle System:
+ * All the attributes of Particle System
+
+ Features:
+ * consumes small memory: uses 1 vertex (x,y) per particle, no need to assign tex coordinates
+ * size can't be bigger than 64
+ * the system can't be scaled since the particles are rendered using GL_POINT_SPRITE
+
+ Limitations:
+ * On 3rd gen iPhone devices and iPads, this node performs MUCH slower than CCParticleSystemQuad.
+ */
+@interface CCParticleSystemPoint : CCParticleSystem
+{
+ // Array of (x,y,size)
+ ccPointSprite *vertices;
+ // vertices buffer id
+#if CC_USES_VBO
+ GLuint verticesID;
+#endif
+}
+@end
+
+#elif __MAC_OS_X_VERSION_MAX_ALLOWED
+
+#import "CCParticleSystemQuad.h"
+
+@interface CCParticleSystemPoint : CCParticleSystemQuad
+@end
+
+#endif
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import <Availability.h>
+#import "CCParticleSystemPoint.h"
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+// opengl
+#import "Platforms/CCGL.h"
+
+// cocos2d
+#import "CCTextureCache.h"
+#import "ccMacros.h"
+
+// support
+#import "Support/OpenGL_Internal.h"
+#import "Support/CGPointExtension.h"
+
+@implementation CCParticleSystemPoint
+
+-(id) initWithTotalParticles:(NSUInteger) numberOfParticles
+{
+ if( (self=[super initWithTotalParticles:numberOfParticles]) ) {
+
+ vertices = malloc( sizeof(ccPointSprite) * totalParticles );
+
+ if( ! vertices ) {
+ NSLog(@"cocos2d: Particle system: not enough memory");
+ [self release];
+ return nil;
+ }
+
+#if CC_USES_VBO
+ glGenBuffers(1, &verticesID);
+
+ // initial binding
+ glBindBuffer(GL_ARRAY_BUFFER, verticesID);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(ccPointSprite)*totalParticles, vertices, GL_DYNAMIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+#endif
+ }
+
+ return self;
+}
+
+-(void) dealloc
+{
+ free(vertices);
+#if CC_USES_VBO
+ glDeleteBuffers(1, &verticesID);
+#endif
+
+ [super dealloc];
+}
+
+-(void) updateQuadWithParticle:(tCCParticle*)p newPosition:(CGPoint)newPos
+{
+ // place vertices and colos in array
+ vertices[particleIdx].pos = (ccVertex2F) {newPos.x, newPos.y};
+ vertices[particleIdx].size = p->size;
+ ccColor4B color = { p->color.r*255, p->color.g*255, p->color.b*255, p->color.a*255 };
+ vertices[particleIdx].color = color;
+}
+
+-(void) postStep
+{
+#if CC_USES_VBO
+ glBindBuffer(GL_ARRAY_BUFFER, verticesID);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(ccPointSprite)*particleCount, vertices);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+#endif
+}
+
+-(void) draw
+{
+ if (particleIdx==0)
+ return;
+
+ // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY
+ // Unneeded states: GL_TEXTURE_COORD_ARRAY
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ glBindTexture(GL_TEXTURE_2D, texture_.name);
+
+ glEnable(GL_POINT_SPRITE_OES);
+ glTexEnvi( GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, GL_TRUE );
+
+#define kPointSize sizeof(vertices[0])
+
+#if CC_USES_VBO
+ glBindBuffer(GL_ARRAY_BUFFER, verticesID);
+
+ glVertexPointer(2,GL_FLOAT, kPointSize, 0);
+
+ glColorPointer(4, GL_UNSIGNED_BYTE, kPointSize, (GLvoid*) offsetof(ccPointSprite, color) );
+
+ glEnableClientState(GL_POINT_SIZE_ARRAY_OES);
+ glPointSizePointerOES(GL_FLOAT, kPointSize, (GLvoid*) offsetof(ccPointSprite, size) );
+#else // Uses Vertex Array List
+ int offset = (int)vertices;
+ glVertexPointer(2,GL_FLOAT, kPointSize, (GLvoid*) offset);
+
+ int diff = offsetof(ccPointSprite, color);
+ glColorPointer(4, GL_UNSIGNED_BYTE, kPointSize, (GLvoid*) (offset+diff));
+
+ glEnableClientState(GL_POINT_SIZE_ARRAY_OES);
+ diff = offsetof(ccPointSprite, size);
+ glPointSizePointerOES(GL_FLOAT, kPointSize, (GLvoid*) (offset+diff));
+#endif
+
+ BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST;
+ if( newBlend )
+ glBlendFunc( blendFunc_.src, blendFunc_.dst );
+
+
+ glDrawArrays(GL_POINTS, 0, particleIdx);
+
+ // restore blend state
+ if( newBlend )
+ glBlendFunc( CC_BLEND_SRC, CC_BLEND_DST);
+
+
+#if CC_USES_VBO
+ // unbind VBO buffer
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+#endif
+
+ glDisableClientState(GL_POINT_SIZE_ARRAY_OES);
+ glDisable(GL_POINT_SPRITE_OES);
+
+ // restore GL default state
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+}
+
+#pragma mark Non supported properties
+
+//
+// SPIN IS NOT SUPPORTED
+//
+-(void) setStartSpin:(float)a
+{
+ NSAssert(a == 0, @"PointParticleSystem doesn't support spinning");
+ [super setStartSpin:a];
+}
+-(void) setStartSpinVar:(float)a
+{
+ NSAssert(a == 0, @"PointParticleSystem doesn't support spinning");
+ [super setStartSpin:a];
+}
+-(void) setEndSpin:(float)a
+{
+ NSAssert(a == 0, @"PointParticleSystem doesn't support spinning");
+ [super setStartSpin:a];
+}
+-(void) setEndSpinVar:(float)a
+{
+ NSAssert(a == 0, @"PointParticleSystem doesn't support spinning");
+ [super setStartSpin:a];
+}
+
+//
+// SIZE > 64 IS NOT SUPPORTED
+//
+-(void) setStartSize:(float)size
+{
+ NSAssert(size >= 0 && size <= CC_MAX_PARTICLE_SIZE, @"PointParticleSystem only supports 0 <= size <= 64");
+ [super setStartSize:size];
+}
+
+-(void) setEndSize:(float)size
+{
+ NSAssert( (size == kCCParticleStartSizeEqualToEndSize) ||
+ ( size >= 0 && size <= CC_MAX_PARTICLE_SIZE), @"PointParticleSystem only supports 0 <= size <= 64");
+ [super setEndSize:size];
+}
+@end
+
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+@implementation CCParticleSystemPoint
+@end
+
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
+
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Leonardo Kasperavičius
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCParticleSystem.h"
+#import "ccConfig.h"
+
+@class CCSpriteFrame;
+
+/** CCParticleSystemQuad is a subclass of CCParticleSystem
+
+ It includes all the features of ParticleSystem.
+
+ Special features and Limitations:
+ - Particle size can be any float number.
+ - The system can be scaled
+ - The particles can be rotated
+ - On 1st and 2nd gen iPhones: It is only a bit slower that CCParticleSystemPoint
+ - On 3rd gen iPhone and iPads: It is MUCH faster than CCParticleSystemPoint
+ - It consumes more RAM and more GPU memory than CCParticleSystemPoint
+ - It supports subrects
+ @since v0.8
+ */
+@interface CCParticleSystemQuad : CCParticleSystem
+{
+ ccV2F_C4B_T2F_Quad *quads_; // quads to be rendered
+ GLushort *indices_; // indices
+#if CC_USES_VBO
+ GLuint quadsID_; // VBO id
+#endif
+}
+
+/** initialices the indices for the vertices */
+-(void) initIndices;
+
+/** initilizes the texture with a rectangle measured Points */
+-(void) initTexCoordsWithRect:(CGRect)rect;
+
+/** Sets a new CCSpriteFrame as particle.
+ WARNING: this method is experimental. Use setTexture:withRect instead.
+ @since v0.99.4
+ */
+-(void)setDisplayFrame:(CCSpriteFrame*)spriteFrame;
+
+/** Sets a new texture with a rect. The rect is in Points.
+ @since v0.99.4
+ */
+-(void) setTexture:(CCTexture2D *)texture withRect:(CGRect)rect;
+
+@end
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Leonardo Kasperavičius
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+// opengl
+#import "Platforms/CCGL.h"
+
+// cocos2d
+#import "ccConfig.h"
+#import "CCParticleSystemQuad.h"
+#import "CCTextureCache.h"
+#import "ccMacros.h"
+#import "CCSpriteFrame.h"
+
+// support
+#import "Support/OpenGL_Internal.h"
+#import "Support/CGPointExtension.h"
+
+@implementation CCParticleSystemQuad
+
+
+// overriding the init method
+-(id) initWithTotalParticles:(NSUInteger) numberOfParticles
+{
+ // base initialization
+ if( (self=[super initWithTotalParticles:numberOfParticles]) ) {
+
+ // allocating data space
+ quads_ = calloc( sizeof(quads_[0]) * totalParticles, 1 );
+ indices_ = calloc( sizeof(indices_[0]) * totalParticles * 6, 1 );
+
+ if( !quads_ || !indices_) {
+ NSLog(@"cocos2d: Particle system: not enough memory");
+ if( quads_ )
+ free( quads_ );
+ if(indices_)
+ free(indices_);
+
+ [self release];
+ return nil;
+ }
+
+ // initialize only once the texCoords and the indices
+ [self initTexCoordsWithRect:CGRectMake(0, 0, [texture_ pixelsWide], [texture_ pixelsHigh])];
+ [self initIndices];
+
+#if CC_USES_VBO
+ // create the VBO buffer
+ glGenBuffers(1, &quadsID_);
+
+ // initial binding
+ glBindBuffer(GL_ARRAY_BUFFER, quadsID_);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(quads_[0])*totalParticles, quads_,GL_DYNAMIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+#endif
+ }
+
+ return self;
+}
+
+-(void) dealloc
+{
+ free(quads_);
+ free(indices_);
+#if CC_USES_VBO
+ glDeleteBuffers(1, &quadsID_);
+#endif
+
+ [super dealloc];
+}
+
+// pointRect is in Points coordinates.
+-(void) initTexCoordsWithRect:(CGRect)pointRect
+{
+ // convert to pixels coords
+ CGRect rect = CGRectMake(
+ pointRect.origin.x * CC_CONTENT_SCALE_FACTOR(),
+ pointRect.origin.y * CC_CONTENT_SCALE_FACTOR(),
+ pointRect.size.width * CC_CONTENT_SCALE_FACTOR(),
+ pointRect.size.height * CC_CONTENT_SCALE_FACTOR() );
+
+ GLfloat wide = [texture_ pixelsWide];
+ GLfloat high = [texture_ pixelsHigh];
+
+#if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
+ GLfloat left = (rect.origin.x*2+1) / (wide*2);
+ GLfloat bottom = (rect.origin.y*2+1) / (high*2);
+ GLfloat right = left + (rect.size.width*2-2) / (wide*2);
+ GLfloat top = bottom + (rect.size.height*2-2) / (high*2);
+#else
+ GLfloat left = rect.origin.x / wide;
+ GLfloat bottom = rect.origin.y / high;
+ GLfloat right = left + rect.size.width / wide;
+ GLfloat top = bottom + rect.size.height / high;
+#endif // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
+
+ // Important. Texture in cocos2d are inverted, so the Y component should be inverted
+ CC_SWAP( top, bottom);
+
+ for(NSUInteger i=0; i<totalParticles; i++) {
+ // bottom-left vertex:
+ quads_[i].bl.texCoords.u = left;
+ quads_[i].bl.texCoords.v = bottom;
+ // bottom-right vertex:
+ quads_[i].br.texCoords.u = right;
+ quads_[i].br.texCoords.v = bottom;
+ // top-left vertex:
+ quads_[i].tl.texCoords.u = left;
+ quads_[i].tl.texCoords.v = top;
+ // top-right vertex:
+ quads_[i].tr.texCoords.u = right;
+ quads_[i].tr.texCoords.v = top;
+ }
+}
+
+-(void) setTexture:(CCTexture2D *)texture withRect:(CGRect)rect
+{
+ // Only update the texture if is different from the current one
+ if( [texture name] != [texture_ name] )
+ [super setTexture:texture];
+
+ [self initTexCoordsWithRect:rect];
+}
+
+-(void) setTexture:(CCTexture2D *)texture
+{
+ CGSize s = [texture contentSize];
+ [self setTexture:texture withRect:CGRectMake(0,0, s.width, s.height)];
+}
+
+-(void) setDisplayFrame:(CCSpriteFrame *)spriteFrame
+{
+
+ NSAssert( CGPointEqualToPoint( spriteFrame.offsetInPixels , CGPointZero ), @"QuadParticle only supports SpriteFrames with no offsets");
+
+ // update texture before updating texture rect
+ if ( spriteFrame.texture.name != texture_.name )
+ [self setTexture: spriteFrame.texture];
+}
+
+-(void) initIndices
+{
+ for( NSUInteger i=0;i< totalParticles;i++) {
+ const NSUInteger i6 = i*6;
+ const NSUInteger i4 = i*4;
+ indices_[i6+0] = (GLushort) i4+0;
+ indices_[i6+1] = (GLushort) i4+1;
+ indices_[i6+2] = (GLushort) i4+2;
+
+ indices_[i6+5] = (GLushort) i4+1;
+ indices_[i6+4] = (GLushort) i4+2;
+ indices_[i6+3] = (GLushort) i4+3;
+ }
+}
+
+-(void) updateQuadWithParticle:(tCCParticle*)p newPosition:(CGPoint)newPos
+{
+ // colors
+ ccV2F_C4B_T2F_Quad *quad = &(quads_[particleIdx]);
+
+ ccColor4B color = { p->color.r*255, p->color.g*255, p->color.b*255, p->color.a*255};
+ quad->bl.colors = color;
+ quad->br.colors = color;
+ quad->tl.colors = color;
+ quad->tr.colors = color;
+
+ // vertices
+ GLfloat size_2 = p->size/2;
+ if( p->rotation ) {
+ GLfloat x1 = -size_2;
+ GLfloat y1 = -size_2;
+
+ GLfloat x2 = size_2;
+ GLfloat y2 = size_2;
+ GLfloat x = newPos.x;
+ GLfloat y = newPos.y;
+
+ GLfloat r = (GLfloat)-CC_DEGREES_TO_RADIANS(p->rotation);
+ GLfloat cr = cosf(r);
+ GLfloat sr = sinf(r);
+ GLfloat ax = x1 * cr - y1 * sr + x;
+ GLfloat ay = x1 * sr + y1 * cr + y;
+ GLfloat bx = x2 * cr - y1 * sr + x;
+ GLfloat by = x2 * sr + y1 * cr + y;
+ GLfloat cx = x2 * cr - y2 * sr + x;
+ GLfloat cy = x2 * sr + y2 * cr + y;
+ GLfloat dx = x1 * cr - y2 * sr + x;
+ GLfloat dy = x1 * sr + y2 * cr + y;
+
+ // bottom-left
+ quad->bl.vertices.x = ax;
+ quad->bl.vertices.y = ay;
+
+ // bottom-right vertex:
+ quad->br.vertices.x = bx;
+ quad->br.vertices.y = by;
+
+ // top-left vertex:
+ quad->tl.vertices.x = dx;
+ quad->tl.vertices.y = dy;
+
+ // top-right vertex:
+ quad->tr.vertices.x = cx;
+ quad->tr.vertices.y = cy;
+ } else {
+ // bottom-left vertex:
+ quad->bl.vertices.x = newPos.x - size_2;
+ quad->bl.vertices.y = newPos.y - size_2;
+
+ // bottom-right vertex:
+ quad->br.vertices.x = newPos.x + size_2;
+ quad->br.vertices.y = newPos.y - size_2;
+
+ // top-left vertex:
+ quad->tl.vertices.x = newPos.x - size_2;
+ quad->tl.vertices.y = newPos.y + size_2;
+
+ // top-right vertex:
+ quad->tr.vertices.x = newPos.x + size_2;
+ quad->tr.vertices.y = newPos.y + size_2;
+ }
+}
+
+-(void) postStep
+{
+#if CC_USES_VBO
+ glBindBuffer(GL_ARRAY_BUFFER, quadsID_);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(quads_[0])*particleCount, quads_);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+#endif
+}
+
+// overriding draw method
+-(void) draw
+{
+ // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Unneeded states: -
+
+ glBindTexture(GL_TEXTURE_2D, [texture_ name]);
+
+#define kQuadSize sizeof(quads_[0].bl)
+
+#if CC_USES_VBO
+ glBindBuffer(GL_ARRAY_BUFFER, quadsID_);
+
+ glVertexPointer(2,GL_FLOAT, kQuadSize, 0);
+
+ glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (GLvoid*) offsetof(ccV2F_C4B_T2F,colors) );
+
+ glTexCoordPointer(2, GL_FLOAT, kQuadSize, (GLvoid*) offsetof(ccV2F_C4B_T2F,texCoords) );
+#else // vertex array list
+
+ NSUInteger offset = (NSUInteger) quads_;
+
+ // vertex
+ NSUInteger diff = offsetof( ccV2F_C4B_T2F, vertices);
+ glVertexPointer(2,GL_FLOAT, kQuadSize, (GLvoid*) (offset+diff) );
+
+ // color
+ diff = offsetof( ccV2F_C4B_T2F, colors);
+ glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (GLvoid*)(offset + diff));
+
+ // tex coords
+ diff = offsetof( ccV2F_C4B_T2F, texCoords);
+ glTexCoordPointer(2, GL_FLOAT, kQuadSize, (GLvoid*)(offset + diff));
+
+#endif // ! CC_USES_VBO
+
+ BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST;
+ if( newBlend )
+ glBlendFunc( blendFunc_.src, blendFunc_.dst );
+
+ NSAssert( particleIdx == particleCount, @"Abnormal error in particle quad");
+ glDrawElements(GL_TRIANGLES, (GLsizei) particleIdx*6, GL_UNSIGNED_SHORT, indices_);
+
+ // restore blend state
+ if( newBlend )
+ glBlendFunc( CC_BLEND_SRC, CC_BLEND_DST );
+
+#if CC_USES_VBO
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+#endif
+
+ // restore GL default state
+ // -
+}
+
+@end
+
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Lam Pham
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import <Foundation/Foundation.h>
+#import "CCSprite.h"
+
+/** Types of progress
+ @since v0.99.1
+ */
+typedef enum {
+ /// Radial Counter-Clockwise
+ kCCProgressTimerTypeRadialCCW,
+ /// Radial ClockWise
+ kCCProgressTimerTypeRadialCW,
+ /// Horizontal Left-Right
+ kCCProgressTimerTypeHorizontalBarLR,
+ /// Horizontal Right-Left
+ kCCProgressTimerTypeHorizontalBarRL,
+ /// Vertical Bottom-top
+ kCCProgressTimerTypeVerticalBarBT,
+ /// Vertical Top-Bottom
+ kCCProgressTimerTypeVerticalBarTB,
+} CCProgressTimerType;
+
+/**
+ CCProgresstimer is a subclass of CCNode.
+ It renders the inner sprite according to the percentage.
+ The progress can be Radial, Horizontal or vertical.
+ @since v0.99.1
+ */
+@interface CCProgressTimer : CCNode
+{
+ CCProgressTimerType type_;
+ float percentage_;
+ CCSprite *sprite_;
+
+ int vertexDataCount_;
+ ccV2F_C4B_T2F *vertexData_;
+}
+
+/** Change the percentage to change progress. */
+@property (nonatomic, readwrite) CCProgressTimerType type;
+
+/** Percentages are from 0 to 100 */
+@property (nonatomic, readwrite) float percentage;
+
+/** The image to show the progress percentage */
+@property (nonatomic, readwrite, retain) CCSprite *sprite;
+
+
+/** Creates a progress timer with an image filename as the shape the timer goes through */
++ (id) progressWithFile:(NSString*) filename;
+/** Initializes a progress timer with an image filename as the shape the timer goes through */
+- (id) initWithFile:(NSString*) filename;
+
+/** Creates a progress timer with the texture as the shape the timer goes through */
++ (id) progressWithTexture:(CCTexture2D*) texture;
+/** Creates a progress timer with the texture as the shape the timer goes through */
+- (id) initWithTexture:(CCTexture2D*) texture;
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Lam Pham
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "CCProgressTimer.h"
+
+#import "ccMacros.h"
+#import "CCTextureCache.h"
+#import "Support/CGPointExtension.h"
+
+
+
+#define kProgressTextureCoordsCount 4
+// kProgressTextureCoords holds points {0,0} {0,1} {1,1} {1,0} we can represent it as bits
+const char kProgressTextureCoords = 0x1e;
+
+@interface CCProgressTimer (Internal)
+
+-(void)updateProgress;
+-(void)updateBar;
+-(void)updateRadial;
+-(void)updateColor;
+-(CGPoint)boundaryTexCoord:(char)index;
+@end
+
+
+@implementation CCProgressTimer
+@synthesize percentage = percentage_;
+@synthesize sprite = sprite_;
+@synthesize type = type_;
+
++(id)progressWithFile:(NSString*) filename
+{
+ return [[[self alloc]initWithFile:filename] autorelease];
+}
+-(id)initWithFile:(NSString*) filename
+{
+ return [self initWithTexture:[[CCTextureCache sharedTextureCache] addImage: filename]];
+}
+
++(id)progressWithTexture:(CCTexture2D*) texture
+{
+ return [[[self alloc]initWithTexture:texture] autorelease];
+}
+-(id)initWithTexture:(CCTexture2D*) texture
+{
+ if(( self = [super init] )){
+ self.sprite = [CCSprite spriteWithTexture:texture];
+ percentage_ = 0.f;
+ vertexData_ = NULL;
+ vertexDataCount_ = 0;
+ self.anchorPoint = ccp(.5f,.5f);
+ self.contentSize = sprite_.contentSize;
+ self.type = kCCProgressTimerTypeRadialCCW;
+ }
+ return self;
+}
+-(void)dealloc
+{
+ if(vertexData_)
+ free(vertexData_);
+
+ [sprite_ release];
+ [super dealloc];
+}
+
+-(void)setPercentage:(float) percentage
+{
+ if(percentage_ != percentage) {
+ percentage_ = clampf( percentage, 0, 100);
+ [self updateProgress];
+ }
+}
+-(void)setSprite:(CCSprite *)newSprite
+{
+ if(sprite_ != newSprite){
+ [sprite_ release];
+ sprite_ = [newSprite retain];
+
+ // Everytime we set a new sprite, we free the current vertex data
+ if(vertexData_){
+ free(vertexData_);
+ vertexData_ = NULL;
+ vertexDataCount_ = 0;
+ }
+ }
+}
+-(void)setType:(CCProgressTimerType)newType
+{
+ if (newType != type_) {
+
+ // release all previous information
+ if(vertexData_){
+ free(vertexData_);
+ vertexData_ = NULL;
+ vertexDataCount_ = 0;
+ }
+ type_ = newType;
+ }
+}
+@end
+
+@implementation CCProgressTimer(Internal)
+
+///
+// @returns the vertex position from the texture coordinate
+///
+-(ccVertex2F)vertexFromTexCoord:(CGPoint) texCoord
+{
+ CGPoint tmp;
+ ccVertex2F ret;
+ if (sprite_.texture) {
+ CCTexture2D *texture = [sprite_ texture];
+ CGSize texSize = [texture contentSizeInPixels];
+ tmp = ccp(texSize.width * texCoord.x/texture.maxS,
+ texSize.height * (1 - (texCoord.y/texture.maxT)));
+ } else
+ tmp = CGPointZero;
+
+ ret.x = tmp.x;
+ ret.y = tmp.y;
+ return ret;
+}
+
+-(void)updateColor
+{
+ GLubyte op = sprite_.opacity;
+ ccColor3B c3b = sprite_.color;
+
+ ccColor4B color = { c3b.r, c3b.g, c3b.b, op };
+ if([sprite_.texture hasPremultipliedAlpha]){
+ color.r *= op/255;
+ color.g *= op/255;
+ color.b *= op/255;
+ }
+
+ if(vertexData_){
+ for (int i=0; i < vertexDataCount_; ++i) {
+ vertexData_[i].colors = color;
+ }
+ }
+}
+
+-(void)updateProgress
+{
+ switch (type_) {
+ case kCCProgressTimerTypeRadialCW:
+ case kCCProgressTimerTypeRadialCCW:
+ [self updateRadial];
+ break;
+ case kCCProgressTimerTypeHorizontalBarLR:
+ case kCCProgressTimerTypeHorizontalBarRL:
+ case kCCProgressTimerTypeVerticalBarBT:
+ case kCCProgressTimerTypeVerticalBarTB:
+ [self updateBar];
+ break;
+ default:
+ break;
+ }
+}
+
+///
+// Update does the work of mapping the texture onto the triangles
+// It now doesn't occur the cost of free/alloc data every update cycle.
+// It also only changes the percentage point but no other points if they have not
+// been modified.
+//
+// It now deals with flipped texture. If you run into this problem, just use the
+// sprite property and enable the methods flipX, flipY.
+///
+-(void)updateRadial
+{
+ // Texture Max is the actual max coordinates to deal with non-power of 2 textures
+ CGPoint tMax = ccp(sprite_.texture.maxS,sprite_.texture.maxT);
+
+ // Grab the midpoint
+ CGPoint midpoint = ccpCompMult(self.anchorPoint, tMax);
+
+ float alpha = percentage_ / 100.f;
+
+ // Otherwise we can get the angle from the alpha
+ float angle = 2.f*((float)M_PI) * ( type_ == kCCProgressTimerTypeRadialCW? alpha : 1.f - alpha);
+
+ // We find the vector to do a hit detection based on the percentage
+ // We know the first vector is the one @ 12 o'clock (top,mid) so we rotate
+ // from that by the progress angle around the midpoint pivot
+ CGPoint topMid = ccp(midpoint.x, 0.f);
+ CGPoint percentagePt = ccpRotateByAngle(topMid, midpoint, angle);
+
+
+ int index = 0;
+ CGPoint hit = CGPointZero;
+
+ if (alpha == 0.f) {
+ // More efficient since we don't always need to check intersection
+ // If the alpha is zero then the hit point is top mid and the index is 0.
+ hit = topMid;
+ index = 0;
+ } else if (alpha == 1.f) {
+ // More efficient since we don't always need to check intersection
+ // If the alpha is one then the hit point is top mid and the index is 4.
+ hit = topMid;
+ index = 4;
+ } else {
+ // We run a for loop checking the edges of the texture to find the
+ // intersection point
+ // We loop through five points since the top is split in half
+
+ float min_t = FLT_MAX;
+
+ for (int i = 0; i <= kProgressTextureCoordsCount; ++i) {
+ int pIndex = (i + (kProgressTextureCoordsCount - 1))%kProgressTextureCoordsCount;
+
+ CGPoint edgePtA = ccpCompMult([self boundaryTexCoord:i % kProgressTextureCoordsCount],tMax);
+ CGPoint edgePtB = ccpCompMult([self boundaryTexCoord:pIndex],tMax);
+
+ // Remember that the top edge is split in half for the 12 o'clock position
+ // Let's deal with that here by finding the correct endpoints
+ if(i == 0){
+ edgePtB = ccpLerp(edgePtA,edgePtB,.5f);
+ } else if(i == 4){
+ edgePtA = ccpLerp(edgePtA,edgePtB,.5f);
+ }
+
+ // s and t are returned by ccpLineIntersect
+ float s = 0, t = 0;
+ if(ccpLineIntersect(edgePtA, edgePtB, midpoint, percentagePt, &s, &t))
+ {
+
+ // Since our hit test is on rays we have to deal with the top edge
+ // being in split in half so we have to test as a segment
+ if ((i == 0 || i == 4)) {
+ // s represents the point between edgePtA--edgePtB
+ if (!(0.f <= s && s <= 1.f)) {
+ continue;
+ }
+ }
+ // As long as our t isn't negative we are at least finding a
+ // correct hitpoint from midpoint to percentagePt.
+ if (t >= 0.f) {
+ // Because the percentage line and all the texture edges are
+ // rays we should only account for the shortest intersection
+ if (t < min_t) {
+ min_t = t;
+ index = i;
+ }
+ }
+ }
+ }
+
+ // Now that we have the minimum magnitude we can use that to find our intersection
+ hit = ccpAdd(midpoint, ccpMult(ccpSub(percentagePt, midpoint),min_t));
+
+ }
+
+
+ // The size of the vertex data is the index from the hitpoint
+ // the 3 is for the midpoint, 12 o'clock point and hitpoint position.
+
+ BOOL sameIndexCount = YES;
+ if(vertexDataCount_ != index + 3){
+ sameIndexCount = NO;
+ if(vertexData_){
+ free(vertexData_);
+ vertexData_ = NULL;
+ vertexDataCount_ = 0;
+ }
+ }
+
+
+ if(!vertexData_) {
+ vertexDataCount_ = index + 3;
+ vertexData_ = malloc(vertexDataCount_ * sizeof(ccV2F_C4B_T2F));
+ NSAssert( vertexData_, @"CCProgressTimer. Not enough memory");
+
+ [self updateColor];
+ }
+
+ if (!sameIndexCount) {
+
+ // First we populate the array with the midpoint, then all
+ // vertices/texcoords/colors of the 12 'o clock start and edges and the hitpoint
+ vertexData_[0].texCoords = (ccTex2F){midpoint.x, midpoint.y};
+ vertexData_[0].vertices = [self vertexFromTexCoord:midpoint];
+
+ vertexData_[1].texCoords = (ccTex2F){midpoint.x, 0.f};
+ vertexData_[1].vertices = [self vertexFromTexCoord:ccp(midpoint.x, 0.f)];
+
+ for(int i = 0; i < index; ++i){
+ CGPoint texCoords = ccpCompMult([self boundaryTexCoord:i], tMax);
+
+ vertexData_[i+2].texCoords = (ccTex2F){texCoords.x, texCoords.y};
+ vertexData_[i+2].vertices = [self vertexFromTexCoord:texCoords];
+ }
+
+ // Flip the texture coordinates if set
+ if (sprite_.flipY || sprite_.flipX) {
+ for(int i = 0; i < vertexDataCount_ - 1; ++i){
+ if (sprite_.flipX) {
+ vertexData_[i].texCoords.u = tMax.x - vertexData_[i].texCoords.u;
+ }
+ if(sprite_.flipY){
+ vertexData_[i].texCoords.v = tMax.y - vertexData_[i].texCoords.v;
+ }
+ }
+ }
+ }
+
+ // hitpoint will go last
+ vertexData_[vertexDataCount_ - 1].texCoords = (ccTex2F){hit.x, hit.y};
+ vertexData_[vertexDataCount_ - 1].vertices = [self vertexFromTexCoord:hit];
+
+ if (sprite_.flipY || sprite_.flipX) {
+ if (sprite_.flipX) {
+ vertexData_[vertexDataCount_ - 1].texCoords.u = tMax.x - vertexData_[vertexDataCount_ - 1].texCoords.u;
+ }
+ if(sprite_.flipY){
+ vertexData_[vertexDataCount_ - 1].texCoords.v = tMax.y - vertexData_[vertexDataCount_ - 1].texCoords.v;
+ }
+ }
+}
+
+///
+// Update does the work of mapping the texture onto the triangles for the bar
+// It now doesn't occur the cost of free/alloc data every update cycle.
+// It also only changes the percentage point but no other points if they have not
+// been modified.
+//
+// It now deals with flipped texture. If you run into this problem, just use the
+// sprite property and enable the methods flipX, flipY.
+///
+-(void)updateBar
+{
+
+ float alpha = percentage_ / 100.f;
+
+ CGPoint tMax = ccp(sprite_.texture.maxS,sprite_.texture.maxT);
+
+ unsigned char vIndexes[2] = {0,0};
+ unsigned char index = 0;
+
+ // We know vertex data is always equal to the 4 corners
+ // If we don't have vertex data then we create it here and populate
+ // the side of the bar vertices that won't ever change.
+ if (!vertexData_) {
+ vertexDataCount_ = kProgressTextureCoordsCount;
+ vertexData_ = malloc(vertexDataCount_ * sizeof(ccV2F_C4B_T2F));
+ NSAssert( vertexData_, @"CCProgressTimer. Not enough memory");
+
+ if(type_ == kCCProgressTimerTypeHorizontalBarLR){
+ vertexData_[vIndexes[0] = 0].texCoords = (ccTex2F){0,0};
+ vertexData_[vIndexes[1] = 1].texCoords = (ccTex2F){0, tMax.y};
+ }else if (type_ == kCCProgressTimerTypeHorizontalBarRL) {
+ vertexData_[vIndexes[0] = 2].texCoords = (ccTex2F){tMax.x, tMax.y};
+ vertexData_[vIndexes[1] = 3].texCoords = (ccTex2F){tMax.x, 0.f};
+ }else if (type_ == kCCProgressTimerTypeVerticalBarBT) {
+ vertexData_[vIndexes[0] = 1].texCoords = (ccTex2F){0, tMax.y};
+ vertexData_[vIndexes[1] = 3].texCoords = (ccTex2F){tMax.x, tMax.y};
+ }else if (type_ == kCCProgressTimerTypeVerticalBarTB) {
+ vertexData_[vIndexes[0] = 0].texCoords = (ccTex2F){0, 0};
+ vertexData_[vIndexes[1] = 2].texCoords = (ccTex2F){tMax.x, 0};
+ }
+
+ index = vIndexes[0];
+ vertexData_[index].vertices = [self vertexFromTexCoord:ccp(vertexData_[index].texCoords.u, vertexData_[index].texCoords.v)];
+
+ index = vIndexes[1];
+ vertexData_[index].vertices = [self vertexFromTexCoord:ccp(vertexData_[index].texCoords.u, vertexData_[index].texCoords.v)];
+
+ if (sprite_.flipY || sprite_.flipX) {
+ if (sprite_.flipX) {
+ index = vIndexes[0];
+ vertexData_[index].texCoords.u = tMax.x - vertexData_[index].texCoords.u;
+ index = vIndexes[1];
+ vertexData_[index].texCoords.u = tMax.x - vertexData_[index].texCoords.u;
+ }
+ if(sprite_.flipY){
+ index = vIndexes[0];
+ vertexData_[index].texCoords.v = tMax.y - vertexData_[index].texCoords.v;
+ index = vIndexes[1];
+ vertexData_[index].texCoords.v = tMax.y - vertexData_[index].texCoords.v;
+ }
+ }
+
+ [self updateColor];
+ }
+
+ if(type_ == kCCProgressTimerTypeHorizontalBarLR){
+ vertexData_[vIndexes[0] = 3].texCoords = (ccTex2F){tMax.x*alpha, tMax.y};
+ vertexData_[vIndexes[1] = 2].texCoords = (ccTex2F){tMax.x*alpha, 0};
+ }else if (type_ == kCCProgressTimerTypeHorizontalBarRL) {
+ vertexData_[vIndexes[0] = 1].texCoords = (ccTex2F){tMax.x*(1.f - alpha), 0};
+ vertexData_[vIndexes[1] = 0].texCoords = (ccTex2F){tMax.x*(1.f - alpha), tMax.y};
+ }else if (type_ == kCCProgressTimerTypeVerticalBarBT) {
+ vertexData_[vIndexes[0] = 0].texCoords = (ccTex2F){0, tMax.y*(1.f - alpha)};
+ vertexData_[vIndexes[1] = 2].texCoords = (ccTex2F){tMax.x, tMax.y*(1.f - alpha)};
+ }else if (type_ == kCCProgressTimerTypeVerticalBarTB) {
+ vertexData_[vIndexes[0] = 1].texCoords = (ccTex2F){0, tMax.y*alpha};
+ vertexData_[vIndexes[1] = 3].texCoords = (ccTex2F){tMax.x, tMax.y*alpha};
+ }
+
+ index = vIndexes[0];
+ vertexData_[index].vertices = [self vertexFromTexCoord:ccp(vertexData_[index].texCoords.u, vertexData_[index].texCoords.v)];
+ index = vIndexes[1];
+ vertexData_[index].vertices = [self vertexFromTexCoord:ccp(vertexData_[index].texCoords.u, vertexData_[index].texCoords.v)];
+
+ if (sprite_.flipY || sprite_.flipX) {
+ if (sprite_.flipX) {
+ index = vIndexes[0];
+ vertexData_[index].texCoords.u = tMax.x - vertexData_[index].texCoords.u;
+ index = vIndexes[1];
+ vertexData_[index].texCoords.u = tMax.x - vertexData_[index].texCoords.u;
+ }
+ if(sprite_.flipY){
+ index = vIndexes[0];
+ vertexData_[index].texCoords.v = tMax.y - vertexData_[index].texCoords.v;
+ index = vIndexes[1];
+ vertexData_[index].texCoords.v = tMax.y - vertexData_[index].texCoords.v;
+ }
+ }
+
+}
+
+-(CGPoint)boundaryTexCoord:(char)index
+{
+ if (index < kProgressTextureCoordsCount) {
+ switch (type_) {
+ case kCCProgressTimerTypeRadialCW:
+ return ccp((kProgressTextureCoords>>((index<<1)+1))&1,(kProgressTextureCoords>>(index<<1))&1);
+ case kCCProgressTimerTypeRadialCCW:
+ return ccp((kProgressTextureCoords>>(7-(index<<1)))&1,(kProgressTextureCoords>>(7-((index<<1)+1)))&1);
+ default:
+ break;
+ }
+ }
+ return CGPointZero;
+}
+
+-(void)draw
+{
+ if(!vertexData_)return;
+ if(!sprite_)return;
+ ccBlendFunc blendFunc = sprite_.blendFunc;
+ BOOL newBlend = blendFunc.src != CC_BLEND_SRC || blendFunc.dst != CC_BLEND_DST;
+ if( newBlend )
+ glBlendFunc( blendFunc.src, blendFunc.dst );
+
+ /// ========================================================================
+ // Replaced [texture_ drawAtPoint:CGPointZero] with my own vertexData
+ // Everything above me and below me is copied from CCTextureNode's draw
+ glBindTexture(GL_TEXTURE_2D, sprite_.texture.name);
+ glVertexPointer(2, GL_FLOAT, sizeof(ccV2F_C4B_T2F), &vertexData_[0].vertices);
+ glTexCoordPointer(2, GL_FLOAT, sizeof(ccV2F_C4B_T2F), &vertexData_[0].texCoords);
+ glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ccV2F_C4B_T2F), &vertexData_[0].colors);
+ if(type_ == kCCProgressTimerTypeRadialCCW || type_ == kCCProgressTimerTypeRadialCW){
+ glDrawArrays(GL_TRIANGLE_FAN, 0, vertexDataCount_);
+ } else if (type_ == kCCProgressTimerTypeHorizontalBarLR ||
+ type_ == kCCProgressTimerTypeHorizontalBarRL ||
+ type_ == kCCProgressTimerTypeVerticalBarBT ||
+ type_ == kCCProgressTimerTypeVerticalBarTB) {
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexDataCount_);
+ }
+ //glDrawElements(GL_TRIANGLES, indicesCount_, GL_UNSIGNED_BYTE, indices_);
+ /// ========================================================================
+
+ if( newBlend )
+ glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
+}
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#import "ccTypes.h"
+#import "CCTexture2D.h"
+
+#pragma mark -
+#pragma mark CCRGBAProtocol
+
+/// CC RGBA protocol
+@protocol CCRGBAProtocol <NSObject>
+/** sets Color
+ @since v0.8
+ */
+-(void) setColor:(ccColor3B)color;
+/** returns the color
+ @since v0.8
+ */
+-(ccColor3B) color;
+
+/// returns the opacity
+-(GLubyte) opacity;
+/** sets the opacity.
+ @warning If the the texture has premultiplied alpha then, the R, G and B channels will be modifed.
+ Values goes from 0 to 255, where 255 means fully opaque.
+ */
+-(void) setOpacity: (GLubyte) opacity;
+@optional
+/** sets the premultipliedAlphaOpacity property.
+ If set to NO then opacity will be applied as: glColor(R,G,B,opacity);
+ If set to YES then oapcity will be applied as: glColor(opacity, opacity, opacity, opacity );
+ Textures with premultiplied alpha will have this property by default on YES. Otherwise the default value is NO
+ @since v0.8
+ */
+-(void) setOpacityModifyRGB:(BOOL)boolean;
+/** returns whether or not the opacity will be applied using glColor(R,G,B,opacity) or glColor(opacity, opacity, opacity, opacity);
+ @since v0.8
+ */
+ -(BOOL) doesOpacityModifyRGB;
+@end
+
+#pragma mark -
+#pragma mark CCBlendProtocol
+/**
+ You can specify the blending fuction.
+ @since v0.99.0
+ */
+@protocol CCBlendProtocol <NSObject>
+/** set the source blending function for the texture */
+-(void) setBlendFunc:(ccBlendFunc)blendFunc;
+/** returns the blending function used for the texture */
+-(ccBlendFunc) blendFunc;
+@end
+
+
+#pragma mark -
+#pragma mark CCTextureProtocol
+
+/** CCNode objects that uses a Texture2D to render the images.
+ The texture can have a blending function.
+ If the texture has alpha premultiplied the default blending function is:
+ src=GL_ONE dst= GL_ONE_MINUS_SRC_ALPHA
+ else
+ src=GL_SRC_ALPHA dst= GL_ONE_MINUS_SRC_ALPHA
+ But you can change the blending funtion at any time.
+ @since v0.8.0
+ */
+@protocol CCTextureProtocol <CCBlendProtocol>
+/** returns the used texture */
+-(CCTexture2D*) texture;
+/** sets a new texture. it will be retained */
+-(void) setTexture:(CCTexture2D*)texture;
+@end
+
+#pragma mark -
+#pragma mark CCLabelProtocol
+/** Common interface for Labels */
+@protocol CCLabelProtocol <NSObject>
+/** sets a new label using an NSString.
+ The string will be copied.
+ */
+-(void) setString:(NSString*)label;
+/** returns the string that is rendered */
+-(NSString*) string;
+@optional
+/** sets a new label using a CString.
+ It is faster than setString since it doesn't require to alloc/retain/release an NString object.
+ @since v0.99.0
+ */
+-(void) setCString:(char*)label;
+@end
+
+
+#pragma mark -
+#pragma mark CCProjectionProtocol
+/** OpenGL projection protocol */
+@protocol CCProjectionProtocol <NSObject>
+/** Called by CCDirector when the porjection is updated, and "custom" projection is used
+ @since v0.99.5
+ */
+-(void) updateProjection;
+@end
\ No newline at end of file
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Jason Booth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import <Foundation/Foundation.h>
+#import "CCNode.h"
+#import "CCSprite.h"
+#import "Support/OpenGL_Internal.h"
+
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import <UIKit/UIKit.h>
+#endif // iPHone
+
+enum
+{
+ kCCImageFormatJPG = 0,
+ kCCImageFormatPNG = 1,
+ kCCImageFormatRawData =2
+};
+
+
+/**
+ CCRenderTexture is a generic rendering target. To render things into it,
+ simply construct a render target, call begin on it, call visit on any cocos
+ scenes or objects to render them, and call end. For convienience, render texture
+ adds a sprite as it's display child with the results, so you can simply add
+ the render texture to your scene and treat it like any other CocosNode.
+ There are also functions for saving the render texture to disk in PNG or JPG format.
+
+ @since v0.8.1
+ */
+@interface CCRenderTexture : CCNode
+{
+ GLuint fbo_;
+ GLint oldFBO_;
+ CCTexture2D* texture_;
+ CCSprite* sprite_;
+
+ GLenum pixelFormat_;
+}
+
+/** The CCSprite being used.
+ The sprite, by default, will use the following blending function: GL_ONE, GL_ONE_MINUS_SRC_ALPHA.
+ The blending function can be changed in runtime by calling:
+ - [[renderTexture sprite] setBlendFunc:(ccBlendFunc){GL_ONE, GL_ONE_MINUS_SRC_ALPHA}];
+*/
+@property (nonatomic,readwrite, assign) CCSprite* sprite;
+
+/** creates a RenderTexture object with width and height in Points and a pixel format, only RGB and RGBA formats are valid */
++(id)renderTextureWithWidth:(int)w height:(int)h pixelFormat:(CCTexture2DPixelFormat) format;
+
+/** creates a RenderTexture object with width and height in Points, pixel format is RGBA8888 */
++(id)renderTextureWithWidth:(int)w height:(int)h;
+
+/** initializes a RenderTexture object with width and height in Points and a pixel format, only RGB and RGBA formats are valid */
+-(id)initWithWidth:(int)w height:(int)h pixelFormat:(CCTexture2DPixelFormat) format;
+
+/** starts grabbing */
+-(void)begin;
+
+/** starts rendering to the texture while clearing the texture first.
+ This is more efficient then calling -clear first and then -begin */
+-(void)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a;
+
+/** ends grabbing */
+-(void)end;
+
+/** clears the texture with a color */
+-(void)clear:(float)r g:(float)g b:(float)b a:(float)a;
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+/** saves the texture into a file */
+-(BOOL)saveBuffer:(NSString*)name;
+/** saves the texture into a file. The format can be JPG or PNG */
+-(BOOL)saveBuffer:(NSString*)name format:(int)format;
+/* get buffer as UIImage, can only save a render buffer which has a RGBA8888 pixel format */
+-(NSData*)getUIImageAsDataFromBuffer:(int) format;
+/* get buffer as UIImage */
+-(UIImage *)getUIImageFromBuffer;
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
+
+@end
+
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Jason Booth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import <Availability.h>
+#import "CCRenderTexture.h"
+#import "CCDirector.h"
+#import "ccMacros.h"
+#import "Support/ccUtils.h"
+#import "Support/CCFileUtils.h"
+
+@implementation CCRenderTexture
+
+@synthesize sprite=sprite_;
+
+// issue #994
++(id)renderTextureWithWidth:(int)w height:(int)h pixelFormat:(CCTexture2DPixelFormat) format
+{
+ return [[[self alloc] initWithWidth:w height:h pixelFormat:format] autorelease];
+}
+
++(id)renderTextureWithWidth:(int)w height:(int)h
+{
+ return [[[self alloc] initWithWidth:w height:h pixelFormat:kCCTexture2DPixelFormat_RGBA8888] autorelease];
+}
+
+-(id)initWithWidth:(int)w height:(int)h
+{
+ return [self initWithWidth:w height:h pixelFormat:kCCTexture2DPixelFormat_RGBA8888];
+}
+
+-(id)initWithWidth:(int)w height:(int)h pixelFormat:(CCTexture2DPixelFormat) format
+{
+ if ((self = [super init]))
+ {
+ NSAssert(format != kCCTexture2DPixelFormat_A8,@"only RGB and RGBA formats are valid for a render texture");
+
+ w *= CC_CONTENT_SCALE_FACTOR();
+ h *= CC_CONTENT_SCALE_FACTOR();
+
+ glGetIntegerv(CC_GL_FRAMEBUFFER_BINDING, &oldFBO_);
+
+ // textures must be power of two
+ NSUInteger powW = ccNextPOT(w);
+ NSUInteger powH = ccNextPOT(h);
+
+ void *data = malloc((int)(powW * powH * 4));
+ memset(data, 0, (int)(powW * powH * 4));
+ pixelFormat_=format;
+
+ texture_ = [[CCTexture2D alloc] initWithData:data pixelFormat:pixelFormat_ pixelsWide:powW pixelsHigh:powH contentSize:CGSizeMake(w, h)];
+ free( data );
+
+ // generate FBO
+ ccglGenFramebuffers(1, &fbo_);
+ ccglBindFramebuffer(CC_GL_FRAMEBUFFER, fbo_);
+
+ // associate texture with FBO
+ ccglFramebufferTexture2D(CC_GL_FRAMEBUFFER, CC_GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_.name, 0);
+
+ // check if it worked (probably worth doing :) )
+ GLuint status = ccglCheckFramebufferStatus(CC_GL_FRAMEBUFFER);
+ if (status != CC_GL_FRAMEBUFFER_COMPLETE)
+ {
+ [NSException raise:@"Render Texture" format:@"Could not attach texture to framebuffer"];
+ }
+ [texture_ setAliasTexParameters];
+
+ sprite_ = [CCSprite spriteWithTexture:texture_];
+
+ [texture_ release];
+ [sprite_ setScaleY:-1];
+ [self addChild:sprite_];
+
+ // issue #937
+ [sprite_ setBlendFunc:(ccBlendFunc){GL_ONE, GL_ONE_MINUS_SRC_ALPHA}];
+
+ ccglBindFramebuffer(CC_GL_FRAMEBUFFER, oldFBO_);
+ }
+ return self;
+}
+
+-(void)dealloc
+{
+// [self removeAllChildrenWithCleanup:YES];
+ ccglDeleteFramebuffers(1, &fbo_);
+ [super dealloc];
+}
+
+-(void)begin
+{
+ // Save the current matrix
+ glPushMatrix();
+
+ CGSize texSize = [texture_ contentSizeInPixels];
+
+
+ // Calculate the adjustment ratios based on the old and new projections
+ CGSize size = [[CCDirector sharedDirector] displaySizeInPixels];
+ float widthRatio = size.width / texSize.width;
+ float heightRatio = size.height / texSize.height;
+
+
+ // Adjust the orthographic propjection and viewport
+ ccglOrtho((float)-1.0 / widthRatio, (float)1.0 / widthRatio, (float)-1.0 / heightRatio, (float)1.0 / heightRatio, -1,1);
+ glViewport(0, 0, texSize.width, texSize.height);
+
+
+ glGetIntegerv(CC_GL_FRAMEBUFFER_BINDING, &oldFBO_);
+ ccglBindFramebuffer(CC_GL_FRAMEBUFFER, fbo_);//Will direct drawing to the frame buffer created above
+
+ // Issue #1145
+ // There is no need to enable the default GL states here
+ // but since CCRenderTexture is mostly used outside the "render" loop
+ // these states needs to be enabled.
+ // Since this bug was discovered in API-freeze (very close of 1.0 release)
+ // This bug won't be fixed to prevent incompatibilities with code.
+ //
+ // If you understand the above mentioned message, then you can comment the following line
+ // and enable the gl states manually, in case you need them.
+ CC_ENABLE_DEFAULT_GL_STATES();
+}
+
+-(void)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a
+{
+ [self begin];
+
+ // save clear color
+ GLfloat clearColor[4];
+ glGetFloatv(GL_COLOR_CLEAR_VALUE,clearColor);
+
+ glClearColor(r, g, b, a);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ // restore clear color
+ glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
+}
+
+-(void)end
+{
+ ccglBindFramebuffer(CC_GL_FRAMEBUFFER, oldFBO_);
+ // Restore the original matrix and viewport
+ glPopMatrix();
+ CGSize size = [[CCDirector sharedDirector] displaySizeInPixels];
+ glViewport(0, 0, size.width, size.height);
+}
+
+-(void)clear:(float)r g:(float)g b:(float)b a:(float)a
+{
+ [self beginWithClear:r g:g b:b a:a];
+ [self end];
+}
+
+#pragma mark RenderTexture - Save Image
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+-(BOOL)saveBuffer:(NSString*)name
+{
+ return [self saveBuffer:name format:kCCImageFormatJPG];
+}
+
+-(BOOL)saveBuffer:(NSString*)fileName format:(int)format
+{
+ NSString *fullPath = [CCFileUtils fullPathFromRelativePath:fileName];
+
+ NSData *data = [self getUIImageAsDataFromBuffer:format];
+
+ return [data writeToFile:fullPath atomically:YES];
+}
+
+/* get buffer as UIImage */
+-(UIImage *)getUIImageFromBuffer
+{
+ NSAssert(pixelFormat_ == kCCTexture2DPixelFormat_RGBA8888,@"only RGBA8888 can be saved as image");
+
+ CGSize s = [texture_ contentSizeInPixels];
+ int tx = s.width;
+ int ty = s.height;
+
+ int bitsPerComponent = 8;
+ int bitsPerPixel = 32;
+ int bytesPerPixel = (bitsPerComponent * 4)/8;
+ int bytesPerRow = bytesPerPixel * tx;
+ NSInteger myDataLength = bytesPerRow * ty;
+
+ NSMutableData *buffer = [[NSMutableData alloc] initWithCapacity:myDataLength];
+ NSMutableData *pixels = [[NSMutableData alloc] initWithCapacity:myDataLength];
+
+ if( ! (buffer && pixels) ) {
+ CCLOG(@"cocos2d: CCRenderTexture#getUIImageFromBuffer: not enough memory");
+ [buffer release];
+ [pixels release];
+ return nil;
+ }
+
+ [self begin];
+ glReadPixels(0,0,tx,ty,GL_RGBA,GL_UNSIGNED_BYTE, [buffer mutableBytes]);
+ [self end];
+
+ // make data provider with data.
+
+ CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault;
+ CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, [buffer mutableBytes], myDataLength, NULL);
+ CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
+ CGImageRef iref = CGImageCreate(tx, ty,
+ bitsPerComponent, bitsPerPixel, bytesPerRow,
+ colorSpaceRef, bitmapInfo, provider,
+ NULL, false,
+ kCGRenderingIntentDefault);
+
+ CGContextRef context = CGBitmapContextCreate([pixels mutableBytes], tx,
+ ty, CGImageGetBitsPerComponent(iref),
+ CGImageGetBytesPerRow(iref), CGImageGetColorSpace(iref),
+ bitmapInfo);
+ CGContextTranslateCTM(context, 0.0f, ty);
+ CGContextScaleCTM(context, 1.0f, -1.0f);
+ CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, tx, ty), iref);
+ CGImageRef outputRef = CGBitmapContextCreateImage(context);
+ UIImage* image = [[UIImage alloc] initWithCGImage:outputRef];
+
+ CGImageRelease(iref);
+ CGContextRelease(context);
+ CGColorSpaceRelease(colorSpaceRef);
+ CGDataProviderRelease(provider);
+ CGImageRelease(outputRef);
+
+ [pixels release];
+ [buffer release];
+
+ return [image autorelease];
+}
+
+-(NSData*)getUIImageAsDataFromBuffer:(int) format
+{
+ NSAssert(pixelFormat_ == kCCTexture2DPixelFormat_RGBA8888,@"only RGBA8888 can be saved as image");
+
+ CGSize s = [texture_ contentSizeInPixels];
+ int tx = s.width;
+ int ty = s.height;
+
+ int bitsPerComponent=8;
+ int bitsPerPixel=32;
+
+ int bytesPerRow = (bitsPerPixel/8) * tx;
+ NSInteger myDataLength = bytesPerRow * ty;
+
+ GLubyte *buffer = malloc(sizeof(GLubyte)*myDataLength);
+ GLubyte *pixels = malloc(sizeof(GLubyte)*myDataLength);
+
+ if( ! (buffer && pixels) ) {
+ CCLOG(@"cocos2d: CCRenderTexture#getUIImageFromBuffer: not enough memory");
+ free(buffer);
+ free(pixels);
+ return nil;
+ }
+
+ [self begin];
+ glReadPixels(0,0,tx,ty,GL_RGBA,GL_UNSIGNED_BYTE, buffer);
+ [self end];
+
+ int x,y;
+
+ for(y = 0; y <ty; y++) {
+ for(x = 0; x <tx * 4; x++) {
+ pixels[((ty - 1 - y) * tx * 4 + x)] = buffer[(y * 4 * tx + x)];
+ }
+ }
+
+ NSData* data;
+
+ if (format == kCCImageFormatRawData)
+ {
+ free(buffer);
+ //data frees buffer when it is deallocated
+ data = [NSData dataWithBytesNoCopy:pixels length:myDataLength];
+
+ } else {
+
+ /*
+ CGImageCreate(size_t width, size_t height,
+ size_t bitsPerComponent, size_t bitsPerPixel, size_t bytesPerRow,
+ CGColorSpaceRef space, CGBitmapInfo bitmapInfo, CGDataProviderRef provider,
+ const CGFloat decode[], bool shouldInterpolate,
+ CGColorRenderingIntent intent)
+ */
+ // make data provider with data.
+ CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault;
+ CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, myDataLength, NULL);
+ CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
+ CGImageRef iref = CGImageCreate(tx, ty,
+ bitsPerComponent, bitsPerPixel, bytesPerRow,
+ colorSpaceRef, bitmapInfo, provider,
+ NULL, false,
+ kCGRenderingIntentDefault);
+
+ UIImage* image = [[UIImage alloc] initWithCGImage:iref];
+
+ CGImageRelease(iref);
+ CGColorSpaceRelease(colorSpaceRef);
+ CGDataProviderRelease(provider);
+
+
+
+ if (format == kCCImageFormatPNG)
+ data = UIImagePNGRepresentation(image);
+ else
+ data = UIImageJPEGRepresentation(image, 1.0f);
+
+ [image release];
+
+ free(pixels);
+ free(buffer);
+ }
+
+ return data;
+}
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008, 2009 Jason Booth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCNode.h"
+#import "CCTexture2D.h"
+#import "CCProtocols.h"
+#import "Platforms/CCGL.h"
+
+/**
+ * A CCRibbon is a dynamically generated list of polygons drawn as a single or series
+ * of triangle strips. The primary use of CCRibbon is as the drawing class of Motion Streak,
+ * but it is quite useful on it's own. When manually drawing a ribbon, you can call addPointAt
+ * and pass in the parameters for the next location in the ribbon. The system will automatically
+ * generate new polygons, texture them accourding to your texture width, etc, etc.
+ *
+ * CCRibbon data is stored in a CCRibbonSegment class. This class statically allocates enough verticies and
+ * texture coordinates for 50 locations (100 verts or 48 triangles). The ribbon class will allocate
+ * new segments when they are needed, and reuse old ones if available. The idea is to avoid constantly
+ * allocating new memory and prefer a more static method. However, since there is no way to determine
+ * the maximum size of some ribbons (motion streaks), a truely static allocation is not possible.
+ *
+ * @since v0.8.1
+ */
+@interface CCRibbon : CCNode <CCTextureProtocol>
+{
+ NSMutableArray* segments_;
+ NSMutableArray* deletedSegments_;
+
+ CGPoint lastPoint1_;
+ CGPoint lastPoint2_;
+ CGPoint lastLocation_;
+ int vertCount_;
+ float texVPos_;
+ float curTime_;
+ float fadeTime_;
+ float delta_;
+ float lastWidth_;
+ float lastSign_;
+ BOOL pastFirstPoint_;
+
+ // Texture used
+ CCTexture2D* texture_;
+
+ // texture length
+ float textureLength_;
+
+ // RGBA protocol
+ ccColor4B color_;
+
+ // blend func
+ ccBlendFunc blendFunc_;
+}
+
+/** Texture used by the ribbon. Conforms to CCTextureProtocol protocol */
+@property (nonatomic,readwrite,retain) CCTexture2D* texture;
+
+/** Texture lengths in pixels */
+@property (nonatomic,readwrite) float textureLength;
+
+/** GL blendind function */
+@property (nonatomic,readwrite,assign) ccBlendFunc blendFunc;
+
+/** color used by the Ribbon (RGBA) */
+@property (nonatomic,readwrite) ccColor4B color;
+
+/** creates the ribbon */
++(id)ribbonWithWidth:(float)w image:(NSString*)path length:(float)l color:(ccColor4B)color fade:(float)fade;
+/** init the ribbon */
+-(id)initWithWidth:(float)w image:(NSString*)path length:(float)l color:(ccColor4B)color fade:(float)fade;
+/** add a point to the ribbon */
+-(void)addPointAt:(CGPoint)location width:(float)w;
+/** polling function */
+-(void)update:(ccTime)delta;
+/** determine side of line */
+-(float)sideOfLine:(CGPoint)p l1:(CGPoint)l1 l2:(CGPoint)l2;
+
+@end
+
+/** object to hold ribbon segment data */
+@interface CCRibbonSegment : NSObject
+{
+@public
+ GLfloat verts[50*6];
+ GLfloat coords[50*4];
+ GLubyte colors[50*8];
+ float creationTime[50];
+ BOOL finished;
+ uint end;
+ uint begin;
+}
+-(id)init;
+-(void)reset;
+-(void)draw:(float)curTime fadeTime:(float)fadeTime color:(ccColor4B)color;
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008, 2009 Jason Booth
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * A ribbon is a dynamically generated list of polygons drawn as a single or series
+ * of triangle strips. The primary use of Ribbon is as the drawing class of Motion Streak,
+ * but it is quite useful on it's own. When manually drawing a ribbon, you can call addPointAt
+ * and pass in the parameters for the next location in the ribbon. The system will automatically
+ * generate new polygons, texture them accourding to your texture width, etc, etc.
+ *
+ * Ribbon data is stored in a RibbonSegment class. This class statically allocates enough verticies and
+ * texture coordinates for 50 locations (100 verts or 48 triangles). The ribbon class will allocate
+ * new segments when they are needed, and reuse old ones if available. The idea is to avoid constantly
+ * allocating new memory and prefer a more static method. However, since there is no way to determine
+ * the maximum size of some ribbons (motion streaks), a truely static allocation is not possible.
+ *
+ */
+
+
+#import "CCRibbon.h"
+#import "CCTextureCache.h"
+#import "Support/CGPointExtension.h"
+#import "ccMacros.h"
+
+//
+// Ribbon
+//
+@implementation CCRibbon
+@synthesize blendFunc=blendFunc_;
+@synthesize color=color_;
+@synthesize textureLength = textureLength_;
+
++(id)ribbonWithWidth:(float)w image:(NSString*)path length:(float)l color:(ccColor4B)color fade:(float)fade
+{
+ self = [[[self alloc] initWithWidth:w image:path length:l color:color fade:fade] autorelease];
+ return self;
+}
+
+-(id)initWithWidth:(float)w image:(NSString*)path length:(float)l color:(ccColor4B)color fade:(float)fade
+{
+ self = [super init];
+ if (self)
+ {
+
+ segments_ = [[NSMutableArray alloc] init];
+ deletedSegments_ = [[NSMutableArray alloc] init];
+
+ /* 1 initial segment */
+ CCRibbonSegment* seg = [[CCRibbonSegment alloc] init];
+ [segments_ addObject:seg];
+ [seg release];
+
+ textureLength_ = l;
+
+ color_ = color;
+ fadeTime_ = fade;
+ lastLocation_ = CGPointZero;
+ lastWidth_ = w/2;
+ texVPos_ = 0.0f;
+
+ curTime_ = 0;
+ pastFirstPoint_ = NO;
+
+ /* XXX:
+ Ribbon, by default uses this blend function, which might not be correct
+ if you are using premultiplied alpha images,
+ but 99% you might want to use this blending function regarding of the texture
+ */
+ blendFunc_.src = GL_SRC_ALPHA;
+ blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA;
+
+ self.texture = [[CCTextureCache sharedTextureCache] addImage:path];
+
+ /* default texture parameter */
+ ccTexParams params = { GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT };
+ [texture_ setTexParameters:¶ms];
+ }
+ return self;
+}
+
+-(void)dealloc
+{
+ [segments_ release];
+ [deletedSegments_ release];
+ [texture_ release];
+ [super dealloc];
+}
+
+// rotates a point around 0, 0
+-(CGPoint)rotatePoint:(CGPoint)vec rotation:(float)a
+{
+ float xtemp = (vec.x * cosf(a)) - (vec.y * sinf(a));
+ vec.y = (vec.x * sinf(a)) + (vec.y * cosf(a));
+ vec.x = xtemp;
+ return vec;
+}
+
+-(void)update:(ccTime)delta
+{
+ curTime_+= delta;
+ delta_ = delta;
+}
+
+-(float)sideOfLine:(CGPoint)p l1:(CGPoint)l1 l2:(CGPoint)l2
+{
+ CGPoint vp = ccpPerp(ccpSub(l1, l2));
+ CGPoint vx = ccpSub(p, l1);
+ return ccpDot(vx, vp);
+}
+
+// adds a new segment to the ribbon
+-(void)addPointAt:(CGPoint)location width:(float)w
+{
+ location.x *= CC_CONTENT_SCALE_FACTOR();
+ location.y *= CC_CONTENT_SCALE_FACTOR();
+
+ w = w*0.5f;
+ // if this is the first point added, cache it and return
+ if (!pastFirstPoint_)
+ {
+ lastWidth_ = w;
+ lastLocation_ = location;
+ pastFirstPoint_ = YES;
+ return;
+ }
+
+ CGPoint sub = ccpSub(lastLocation_, location);
+ float r = ccpToAngle(sub) + (float)M_PI_2;
+ CGPoint p1 = ccpAdd([self rotatePoint:ccp(-w, 0) rotation:r], location);
+ CGPoint p2 = ccpAdd([self rotatePoint:ccp(w, 0) rotation:r], location);
+ float len = sqrtf(powf(lastLocation_.x - location.x, 2) + powf(lastLocation_.y - location.y, 2));
+ float tend = texVPos_ + len/textureLength_;
+ CCRibbonSegment* seg;
+ // grab last segment
+ seg = [segments_ lastObject];
+ // lets kill old segments
+ for (CCRibbonSegment* seg2 in segments_)
+ {
+ if (seg2 != seg && seg2->finished)
+ {
+ [deletedSegments_ addObject:seg2];
+ }
+ }
+ [segments_ removeObjectsInArray:deletedSegments_];
+ // is the segment full?
+ if (seg->end >= 50)
+ [segments_ removeObjectsInArray:deletedSegments_];
+ // grab last segment and append to it if it's not full
+ seg = [segments_ lastObject];
+ // is the segment full?
+ if (seg->end >= 50)
+ {
+ CCRibbonSegment* newSeg;
+ // grab it from the cache if we can
+ if ([deletedSegments_ count] > 0)
+ {
+ newSeg = [deletedSegments_ objectAtIndex:0];
+ [newSeg retain]; // will be released later
+ [deletedSegments_ removeObject:newSeg];
+ [newSeg reset];
+ }
+ else
+ {
+ newSeg = [[CCRibbonSegment alloc] init]; // will be released later
+ }
+
+ newSeg->creationTime[0] = seg->creationTime[seg->end - 1];
+ int v = (seg->end-1)*6;
+ int c = (seg->end-1)*4;
+ newSeg->verts[0] = seg->verts[v];
+ newSeg->verts[1] = seg->verts[v+1];
+ newSeg->verts[2] = seg->verts[v+2];
+ newSeg->verts[3] = seg->verts[v+3];
+ newSeg->verts[4] = seg->verts[v+4];
+ newSeg->verts[5] = seg->verts[v+5];
+
+ newSeg->coords[0] = seg->coords[c];
+ newSeg->coords[1] = seg->coords[c+1];
+ newSeg->coords[2] = seg->coords[c+2];
+ newSeg->coords[3] = seg->coords[c+3];
+ newSeg->end++;
+ seg = newSeg;
+ [segments_ addObject:seg];
+ [newSeg release]; // it was retained before
+
+ }
+ if (seg->end == 0)
+ {
+ // first edge has to get rotation from the first real polygon
+ CGPoint lp1 = ccpAdd([self rotatePoint:ccp(-lastWidth_, 0) rotation:r], lastLocation_);
+ CGPoint lp2 = ccpAdd([self rotatePoint:ccp(+lastWidth_, 0) rotation:r], lastLocation_);
+ seg->creationTime[0] = curTime_ - delta_;
+ seg->verts[0] = lp1.x;
+ seg->verts[1] = lp1.y;
+ seg->verts[2] = 0.0f;
+ seg->verts[3] = lp2.x;
+ seg->verts[4] = lp2.y;
+ seg->verts[5] = 0.0f;
+ seg->coords[0] = 0.0f;
+ seg->coords[1] = texVPos_;
+ seg->coords[2] = 1.0f;
+ seg->coords[3] = texVPos_;
+ seg->end++;
+ }
+
+ int v = seg->end*6;
+ int c = seg->end*4;
+ // add new vertex
+ seg->creationTime[seg->end] = curTime_;
+ seg->verts[v] = p1.x;
+ seg->verts[v+1] = p1.y;
+ seg->verts[v+2] = 0.0f;
+ seg->verts[v+3] = p2.x;
+ seg->verts[v+4] = p2.y;
+ seg->verts[v+5] = 0.0f;
+
+
+ seg->coords[c] = 0.0f;
+ seg->coords[c+1] = tend;
+ seg->coords[c+2] = 1.0f;
+ seg->coords[c+3] = tend;
+
+ texVPos_ = tend;
+ lastLocation_ = location;
+ lastPoint1_ = p1;
+ lastPoint2_ = p2;
+ lastWidth_ = w;
+ seg->end++;
+}
+
+-(void) draw
+{
+ if ([segments_ count] > 0)
+ {
+ // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Unneeded states: GL_COLOR_ARRAY
+ glDisableClientState(GL_COLOR_ARRAY);
+
+ glBindTexture(GL_TEXTURE_2D, [texture_ name]);
+
+ BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST;
+ if( newBlend )
+ glBlendFunc( blendFunc_.src, blendFunc_.dst );
+
+ for (CCRibbonSegment* seg in segments_)
+ [seg draw:curTime_ fadeTime:fadeTime_ color:color_];
+
+ if( newBlend )
+ glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
+
+ // restore default GL state
+ glEnableClientState( GL_COLOR_ARRAY );
+ }
+}
+
+#pragma mark Ribbon - CocosNodeTexture protocol
+-(void) setTexture:(CCTexture2D*) texture
+{
+ [texture_ release];
+ texture_ = [texture retain];
+ [self setContentSizeInPixels: texture.contentSizeInPixels];
+ /* XXX Don't update blending function in Ribbons */
+}
+
+-(CCTexture2D*) texture
+{
+ return texture_;
+}
+
+@end
+
+
+#pragma mark -
+#pragma mark RibbonSegment
+
+@implementation CCRibbonSegment
+
+-(id)init
+{
+ self = [super init];
+ if (self)
+ {
+ [self reset];
+ }
+ return self;
+}
+
+- (NSString*) description
+{
+ return [NSString stringWithFormat:@"<%@ = %08X | end = %i, begin = %i>", [self class], self, end, begin];
+}
+
+- (void) dealloc
+{
+ CCLOGINFO(@"cocos2d: deallocing %@", self);
+ [super dealloc];
+}
+
+-(void)reset
+{
+ end = 0;
+ begin = 0;
+ finished = NO;
+}
+
+-(void)draw:(float)curTime fadeTime:(float)fadeTime color:(ccColor4B)color
+{
+ GLubyte r = color.r;
+ GLubyte g = color.g;
+ GLubyte b = color.b;
+ GLubyte a = color.a;
+
+ if (begin < 50)
+ {
+ // the motion streak class will call update and cause time to change, thus, if curTime_ != 0
+ // we have to generate alpha for the ribbon each frame.
+ if (curTime == 0)
+ {
+ // no alpha over time, so just set the color
+ glColor4ub(r,g,b,a);
+ }
+ else
+ {
+ // generate alpha/color for each point
+ glEnableClientState(GL_COLOR_ARRAY);
+ uint i = begin;
+ for (; i < end; ++i)
+ {
+ int idx = i*8;
+ colors[idx] = r;
+ colors[idx+1] = g;
+ colors[idx+2] = b;
+ colors[idx+4] = r;
+ colors[idx+5] = g;
+ colors[idx+6] = b;
+ float alive = ((curTime - creationTime[i]) / fadeTime);
+ if (alive > 1)
+ {
+ begin++;
+ colors[idx+3] = 0;
+ colors[idx+7] = 0;
+ }
+ else
+ {
+ colors[idx+3] = (GLubyte)(255.f - (alive * 255.f));
+ colors[idx+7] = colors[idx+3];
+ }
+ }
+ glColorPointer(4, GL_UNSIGNED_BYTE, 0, &colors[begin*8]);
+ }
+ glVertexPointer(3, GL_FLOAT, 0, &verts[begin*6]);
+ glTexCoordPointer(2, GL_FLOAT, 0, &coords[begin*4]);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, (end - begin) * 2);
+ }
+ else
+ finished = YES;
+}
+@end
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCNode.h"
+
+/** CCScene is a subclass of CCNode that is used only as an abstract concept.
+
+ CCScene an CCNode are almost identical with the difference that CCScene has it's
+ anchor point (by default) at the center of the screen.
+
+ For the moment CCScene has no other logic than that, but in future releases it might have
+ additional logic.
+
+ It is a good practice to use and CCScene as the parent of all your nodes.
+*/
+@interface CCScene : CCNode
+{
+}
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCScene.h"
+#import "Support/CGPointExtension.h"
+#import "CCDirector.h"
+
+
+@implementation CCScene
+-(id) init
+{
+ if( (self=[super init]) ) {
+ CGSize s = [[CCDirector sharedDirector] winSize];
+ self.isRelativeAnchorPoint = NO;
+ anchorPoint_ = ccp(0.5f, 0.5f);
+ [self setContentSize:s];
+ }
+
+ return self;
+}
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+
+#import "Support/uthash.h"
+#import "ccTypes.h"
+
+typedef void (*TICK_IMP)(id, SEL, ccTime);
+
+//
+// CCTimer
+//
+/** Light weight timer */
+@interface CCTimer : NSObject
+{
+ id target;
+ TICK_IMP impMethod;
+
+ ccTime elapsed;
+
+@public // optimization
+ ccTime interval;
+ SEL selector;
+}
+
+/** interval in seconds */
+@property (nonatomic,readwrite,assign) ccTime interval;
+
+/** Allocates a timer with a target and a selector.
+*/
++(id) timerWithTarget:(id) t selector:(SEL)s;
+
+/** Allocates a timer with a target, a selector and an interval in seconds.
+*/
++(id) timerWithTarget:(id) t selector:(SEL)s interval:(ccTime)seconds;
+
+/** Initializes a timer with a target and a selector.
+*/
+ -(id) initWithTarget:(id) t selector:(SEL)s;
+
+/** Initializes a timer with a target, a selector and an interval in seconds.
+*/
+-(id) initWithTarget:(id) t selector:(SEL)s interval:(ccTime)seconds;
+
+
+/** triggers the timer */
+-(void) update: (ccTime) dt;
+@end
+
+
+
+//
+// CCScheduler
+//
+/** Scheduler is responsible of triggering the scheduled callbacks.
+ You should not use NSTimer. Instead use this class.
+
+ There are 2 different types of callbacks (selectors):
+
+ - update selector: the 'update' selector will be called every frame. You can customize the priority.
+ - custom selector: A custom selector will be called every frame, or with a custom interval of time
+
+ The 'custom selectors' should be avoided when possible. It is faster, and consumes less memory to use the 'update selector'.
+
+*/
+
+struct _listEntry;
+struct _hashSelectorEntry;
+struct _hashUpdateEntry;
+
+@interface CCScheduler : NSObject
+{
+ ccTime timeScale_;
+
+ //
+ // "updates with priority" stuff
+ //
+ struct _listEntry *updatesNeg; // list of priority < 0
+ struct _listEntry *updates0; // list priority == 0
+ struct _listEntry *updatesPos; // list priority > 0
+ struct _hashUpdateEntry *hashForUpdates; // hash used to fetch quickly the list entries for pause,delete,etc.
+
+ // Used for "selectors with interval"
+ struct _hashSelectorEntry *hashForSelectors;
+ struct _hashSelectorEntry *currentTarget;
+ BOOL currentTargetSalvaged;
+
+ // Optimization
+ TICK_IMP impMethod;
+ SEL updateSelector;
+
+ BOOL updateHashLocked; // If true unschedule will not remove anything from a hash. Elements will only be marked for deletion.
+}
+
+/** Modifies the time of all scheduled callbacks.
+ You can use this property to create a 'slow motion' or 'fast fordward' effect.
+ Default is 1.0. To create a 'slow motion' effect, use values below 1.0.
+ To create a 'fast fordward' effect, use values higher than 1.0.
+ @since v0.8
+ @warning It will affect EVERY scheduled selector / action.
+ */
+@property (nonatomic,readwrite) ccTime timeScale;
+
+/** returns a shared instance of the Scheduler */
++(CCScheduler *)sharedScheduler;
+
+/** purges the shared scheduler. It releases the retained instance.
+ @since v0.99.0
+ */
++(void)purgeSharedScheduler;
+
+/** 'tick' the scheduler.
+ You should NEVER call this method, unless you know what you are doing.
+ */
+-(void) tick:(ccTime)dt;
+
+/** The scheduled method will be called every 'interval' seconds.
+ If paused is YES, then it won't be called until it is resumed.
+ If 'interval' is 0, it will be called every frame, but if so, it recommened to use 'scheduleUpdateForTarget:' instead.
+ If the selector is already scheduled, then only the interval parameter will be updated without re-scheduling it again.
+
+ @since v0.99.3
+ */
+-(void) scheduleSelector:(SEL)selector forTarget:(id)target interval:(ccTime)interval paused:(BOOL)paused;
+
+/** Schedules the 'update' selector for a given target with a given priority.
+ The 'update' selector will be called every frame.
+ The lower the priority, the earlier it is called.
+ @since v0.99.3
+ */
+-(void) scheduleUpdateForTarget:(id)target priority:(NSInteger)priority paused:(BOOL)paused;
+
+/** Unshedules a selector for a given target.
+ If you want to unschedule the "update", use unscheudleUpdateForTarget.
+ @since v0.99.3
+ */
+-(void) unscheduleSelector:(SEL)selector forTarget:(id)target;
+
+/** Unschedules the update selector for a given target
+ @since v0.99.3
+ */
+-(void) unscheduleUpdateForTarget:(id)target;
+
+/** Unschedules all selectors for a given target.
+ This also includes the "update" selector.
+ @since v0.99.3
+ */
+-(void) unscheduleAllSelectorsForTarget:(id)target;
+
+/** Unschedules all selectors from all targets.
+ You should NEVER call this method, unless you know what you are doing.
+
+ @since v0.99.3
+ */
+-(void) unscheduleAllSelectors;
+
+/** Pauses the target.
+ All scheduled selectors/update for a given target won't be 'ticked' until the target is resumed.
+ If the target is not present, nothing happens.
+ @since v0.99.3
+ */
+-(void) pauseTarget:(id)target;
+
+/** Resumes the target.
+ The 'target' will be unpaused, so all schedule selectors/update will be 'ticked' again.
+ If the target is not present, nothing happens.
+ @since v0.99.3
+ */
+-(void) resumeTarget:(id)target;
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+// cocos2d imports
+#import "CCScheduler.h"
+#import "ccMacros.h"
+#import "Support/uthash.h"
+#import "Support/utlist.h"
+#import "Support/ccCArray.h"
+
+//
+// Data structures
+//
+#pragma mark -
+#pragma mark Data Structures
+
+// A list double-linked list used for "updates with priority"
+typedef struct _listEntry
+{
+ struct _listEntry *prev, *next;
+ TICK_IMP impMethod;
+ id target; // not retained (retained by hashUpdateEntry)
+ NSInteger priority;
+ BOOL paused;
+ BOOL markedForDeletion; // selector will no longer be called and entry will be removed at end of the next tick
+} tListEntry;
+
+typedef struct _hashUpdateEntry
+{
+ tListEntry **list; // Which list does it belong to ?
+ tListEntry *entry; // entry in the list
+ id target; // hash key (retained)
+ UT_hash_handle hh;
+} tHashUpdateEntry;
+
+// Hash Element used for "selectors with interval"
+typedef struct _hashSelectorEntry
+{
+ struct ccArray *timers;
+ id target; // hash key (retained)
+ unsigned int timerIndex;
+ CCTimer *currentTimer;
+ BOOL currentTimerSalvaged;
+ BOOL paused;
+ UT_hash_handle hh;
+} tHashSelectorEntry;
+
+
+
+//
+// CCTimer
+//
+#pragma mark -
+#pragma mark - CCTimer
+
+@implementation CCTimer
+
+@synthesize interval;
+
+-(id) init
+{
+ NSAssert(NO, @"CCTimer: Init not supported.");
+ [self release];
+ return nil;
+}
+
++(id) timerWithTarget:(id)t selector:(SEL)s
+{
+ return [[[self alloc] initWithTarget:t selector:s] autorelease];
+}
+
++(id) timerWithTarget:(id)t selector:(SEL)s interval:(ccTime) i
+{
+ return [[[self alloc] initWithTarget:t selector:s interval:i] autorelease];
+}
+
+-(id) initWithTarget:(id)t selector:(SEL)s
+{
+ return [self initWithTarget:t selector:s interval:0];
+}
+
+-(id) initWithTarget:(id)t selector:(SEL)s interval:(ccTime) seconds
+{
+ if( (self=[super init]) ) {
+#if COCOS2D_DEBUG
+ NSMethodSignature *sig = [t methodSignatureForSelector:s];
+ NSAssert(sig !=0 , @"Signature not found for selector - does it have the following form? -(void) name: (ccTime) dt");
+#endif
+
+ // target is not retained. It is retained in the hash structure
+ target = t;
+ selector = s;
+ impMethod = (TICK_IMP) [t methodForSelector:s];
+ elapsed = -1;
+ interval = seconds;
+ }
+ return self;
+}
+
+- (NSString*) description
+{
+ return [NSString stringWithFormat:@"<%@ = %08X | target:%@ selector:(%@)>", [self class], self, [target class], NSStringFromSelector(selector)];
+}
+
+-(void) dealloc
+{
+ CCLOGINFO(@"cocos2d: deallocing %@", self);
+ [super dealloc];
+}
+
+-(void) update: (ccTime) dt
+{
+ if( elapsed == - 1)
+ elapsed = 0;
+ else
+ elapsed += dt;
+ if( elapsed >= interval ) {
+ impMethod(target, selector, elapsed);
+ elapsed = 0;
+ }
+}
+@end
+
+//
+// CCScheduler
+//
+#pragma mark -
+#pragma mark - CCScheduler
+
+@interface CCScheduler (Private)
+-(void) removeHashElement:(tHashSelectorEntry*)element;
+@end
+
+@implementation CCScheduler
+
+static CCScheduler *sharedScheduler;
+
+@synthesize timeScale = timeScale_;
+
++ (CCScheduler *)sharedScheduler
+{
+ if (!sharedScheduler)
+ sharedScheduler = [[CCScheduler alloc] init];
+
+ return sharedScheduler;
+}
+
++(id)alloc
+{
+ NSAssert(sharedScheduler == nil, @"Attempted to allocate a second instance of a singleton.");
+ return [super alloc];
+}
+
++(void)purgeSharedScheduler
+{
+ [sharedScheduler release];
+ sharedScheduler = nil;
+}
+
+- (id) init
+{
+ if( (self=[super init]) ) {
+ timeScale_ = 1.0f;
+
+ // used to trigger CCTimer#update
+ updateSelector = @selector(update:);
+ impMethod = (TICK_IMP) [CCTimer instanceMethodForSelector:updateSelector];
+
+ // updates with priority
+ updates0 = NULL;
+ updatesNeg = NULL;
+ updatesPos = NULL;
+ hashForUpdates = NULL;
+
+ // selectors with interval
+ currentTarget = nil;
+ currentTargetSalvaged = NO;
+ hashForSelectors = nil;
+ updateHashLocked = NO;
+ }
+
+ return self;
+}
+
+- (void) dealloc
+{
+ CCLOG(@"cocos2d: deallocing %@", self);
+
+ [self unscheduleAllSelectors];
+
+ sharedScheduler = nil;
+
+ [super dealloc];
+}
+
+
+#pragma mark CCScheduler - Custom Selectors
+
+-(void) removeHashElement:(tHashSelectorEntry*)element
+{
+ ccArrayFree(element->timers);
+ [element->target release];
+ HASH_DEL(hashForSelectors, element);
+ free(element);
+}
+
+-(void) scheduleSelector:(SEL)selector forTarget:(id)target interval:(ccTime)interval paused:(BOOL)paused
+{
+ NSAssert( selector != nil, @"Argument selector must be non-nil");
+ NSAssert( target != nil, @"Argument target must be non-nil");
+
+ tHashSelectorEntry *element = NULL;
+ HASH_FIND_INT(hashForSelectors, &target, element);
+
+ if( ! element ) {
+ element = calloc( sizeof( *element ), 1 );
+ element->target = [target retain];
+ HASH_ADD_INT( hashForSelectors, target, element );
+
+ // Is this the 1st element ? Then set the pause level to all the selectors of this target
+ element->paused = paused;
+
+ } else
+ NSAssert( element->paused == paused, @"CCScheduler. Trying to schedule a selector with a pause value different than the target");
+
+
+ if( element->timers == nil )
+ element->timers = ccArrayNew(10);
+ else
+ {
+ for( unsigned int i=0; i< element->timers->num; i++ ) {
+ CCTimer *timer = element->timers->arr[i];
+ if( selector == timer->selector ) {
+ CCLOG(@"CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: %.2f to %.2f", timer->interval, interval);
+ timer->interval = interval;
+ return;
+ }
+ }
+ ccArrayEnsureExtraCapacity(element->timers, 1);
+ }
+
+ CCTimer *timer = [[CCTimer alloc] initWithTarget:target selector:selector interval:interval];
+ ccArrayAppendObject(element->timers, timer);
+ [timer release];
+}
+
+-(void) unscheduleSelector:(SEL)selector forTarget:(id)target
+{
+ // explicity handle nil arguments when removing an object
+ if( target==nil && selector==NULL)
+ return;
+
+ NSAssert( target != nil, @"Target MUST not be nil");
+ NSAssert( selector != NULL, @"Selector MUST not be NULL");
+
+ tHashSelectorEntry *element = NULL;
+ HASH_FIND_INT(hashForSelectors, &target, element);
+
+ if( element ) {
+
+ for( unsigned int i=0; i< element->timers->num; i++ ) {
+ CCTimer *timer = element->timers->arr[i];
+
+
+ if( selector == timer->selector ) {
+
+ if( timer == element->currentTimer && !element->currentTimerSalvaged ) {
+ [element->currentTimer retain];
+ element->currentTimerSalvaged = YES;
+ }
+
+ ccArrayRemoveObjectAtIndex(element->timers, i );
+
+ // update timerIndex in case we are in tick:, looping over the actions
+ if( element->timerIndex >= i )
+ element->timerIndex--;
+
+ if( element->timers->num == 0 ) {
+ if( currentTarget == element )
+ currentTargetSalvaged = YES;
+ else
+ [self removeHashElement: element];
+ }
+ return;
+ }
+ }
+ }
+
+ // Not Found
+// NSLog(@"CCScheduler#unscheduleSelector:forTarget: selector not found: %@", selString);
+
+}
+
+#pragma mark CCScheduler - Update Specific
+
+-(void) priorityIn:(tListEntry**)list target:(id)target priority:(NSInteger)priority paused:(BOOL)paused
+{
+ tListEntry *listElement = malloc( sizeof(*listElement) );
+
+ listElement->target = target;
+ listElement->priority = priority;
+ listElement->paused = paused;
+ listElement->impMethod = (TICK_IMP) [target methodForSelector:updateSelector];
+ listElement->next = listElement->prev = NULL;
+ listElement->markedForDeletion = NO;
+
+ // empty list ?
+ if( ! *list ) {
+ DL_APPEND( *list, listElement );
+
+ } else {
+ BOOL added = NO;
+
+ for( tListEntry *elem = *list; elem ; elem = elem->next ) {
+ if( priority < elem->priority ) {
+
+ if( elem == *list )
+ DL_PREPEND(*list, listElement);
+ else {
+ listElement->next = elem;
+ listElement->prev = elem->prev;
+
+ elem->prev->next = listElement;
+ elem->prev = listElement;
+ }
+
+ added = YES;
+ break;
+ }
+ }
+
+ // Not added? priority has the higher value. Append it.
+ if( !added )
+ DL_APPEND(*list, listElement);
+ }
+
+ // update hash entry for quicker access
+ tHashUpdateEntry *hashElement = calloc( sizeof(*hashElement), 1 );
+ hashElement->target = [target retain];
+ hashElement->list = list;
+ hashElement->entry = listElement;
+ HASH_ADD_INT(hashForUpdates, target, hashElement );
+}
+
+-(void) appendIn:(tListEntry**)list target:(id)target paused:(BOOL)paused
+{
+ tListEntry *listElement = malloc( sizeof( * listElement ) );
+
+ listElement->target = target;
+ listElement->paused = paused;
+ listElement->markedForDeletion = NO;
+ listElement->impMethod = (TICK_IMP) [target methodForSelector:updateSelector];
+
+ DL_APPEND(*list, listElement);
+
+
+ // update hash entry for quicker access
+ tHashUpdateEntry *hashElement = calloc( sizeof(*hashElement), 1 );
+ hashElement->target = [target retain];
+ hashElement->list = list;
+ hashElement->entry = listElement;
+ HASH_ADD_INT(hashForUpdates, target, hashElement );
+}
+
+-(void) scheduleUpdateForTarget:(id)target priority:(NSInteger)priority paused:(BOOL)paused
+{
+ tHashUpdateEntry * hashElement = NULL;
+ HASH_FIND_INT(hashForUpdates, &target, hashElement);
+ if(hashElement)
+ {
+#if COCOS2D_DEBUG >= 1
+ NSAssert( hashElement->entry->markedForDeletion, @"CCScheduler: You can't re-schedule an 'update' selector'. Unschedule it first");
+#endif
+ // TODO : check if priority has changed!
+
+ hashElement->entry->markedForDeletion = NO;
+ return;
+ }
+
+ // most of the updates are going to be 0, that's way there
+ // is an special list for updates with priority 0
+ if( priority == 0 )
+ [self appendIn:&updates0 target:target paused:paused];
+
+ else if( priority < 0 )
+ [self priorityIn:&updatesNeg target:target priority:priority paused:paused];
+
+ else // priority > 0
+ [self priorityIn:&updatesPos target:target priority:priority paused:paused];
+}
+
+- (void) removeUpdateFromHash:(tListEntry*)entry
+{
+ tHashUpdateEntry * element = NULL;
+
+ HASH_FIND_INT(hashForUpdates, &entry->target, element);
+ if( element ) {
+ // list entry
+ DL_DELETE( *element->list, element->entry );
+ free( element->entry );
+
+ // hash entry
+ [element->target release];
+ HASH_DEL( hashForUpdates, element);
+ free(element);
+ }
+}
+
+-(void) unscheduleUpdateForTarget:(id)target
+{
+ if( target == nil )
+ return;
+
+ tHashUpdateEntry * element = NULL;
+ HASH_FIND_INT(hashForUpdates, &target, element);
+ if( element ) {
+ if(updateHashLocked)
+ element->entry->markedForDeletion = YES;
+ else
+ [self removeUpdateFromHash:element->entry];
+
+// // list entry
+// DL_DELETE( *element->list, element->entry );
+// free( element->entry );
+//
+// // hash entry
+// [element->target release];
+// HASH_DEL( hashForUpdates, element);
+// free(element);
+ }
+}
+
+#pragma mark CCScheduler - Common for Update selector & Custom Selectors
+
+-(void) unscheduleAllSelectors
+{
+ // Custom Selectors
+ for(tHashSelectorEntry *element=hashForSelectors; element != NULL; ) {
+ id target = element->target;
+ element=element->hh.next;
+ [self unscheduleAllSelectorsForTarget:target];
+ }
+
+ // Updates selectors
+ tListEntry *entry, *tmp;
+ DL_FOREACH_SAFE( updates0, entry, tmp ) {
+ [self unscheduleUpdateForTarget:entry->target];
+ }
+ DL_FOREACH_SAFE( updatesNeg, entry, tmp ) {
+ [self unscheduleUpdateForTarget:entry->target];
+ }
+ DL_FOREACH_SAFE( updatesPos, entry, tmp ) {
+ [self unscheduleUpdateForTarget:entry->target];
+ }
+
+}
+
+-(void) unscheduleAllSelectorsForTarget:(id)target
+{
+ // explicit nil handling
+ if( target == nil )
+ return;
+
+ // Custom Selectors
+ tHashSelectorEntry *element = NULL;
+ HASH_FIND_INT(hashForSelectors, &target, element);
+
+ if( element ) {
+ if( ccArrayContainsObject(element->timers, element->currentTimer) && !element->currentTimerSalvaged ) {
+ [element->currentTimer retain];
+ element->currentTimerSalvaged = YES;
+ }
+ ccArrayRemoveAllObjects(element->timers);
+ if( currentTarget == element )
+ currentTargetSalvaged = YES;
+ else
+ [self removeHashElement:element];
+ }
+
+ // Update Selector
+ [self unscheduleUpdateForTarget:target];
+}
+
+-(void) resumeTarget:(id)target
+{
+ NSAssert( target != nil, @"target must be non nil" );
+
+ // Custom Selectors
+ tHashSelectorEntry *element = NULL;
+ HASH_FIND_INT(hashForSelectors, &target, element);
+ if( element )
+ element->paused = NO;
+
+ // Update selector
+ tHashUpdateEntry * elementUpdate = NULL;
+ HASH_FIND_INT(hashForUpdates, &target, elementUpdate);
+ if( elementUpdate ) {
+ NSAssert( elementUpdate->entry != NULL, @"resumeTarget: unknown error");
+ elementUpdate->entry->paused = NO;
+ }
+}
+
+-(void) pauseTarget:(id)target
+{
+ NSAssert( target != nil, @"target must be non nil" );
+
+ // Custom selectors
+ tHashSelectorEntry *element = NULL;
+ HASH_FIND_INT(hashForSelectors, &target, element);
+ if( element )
+ element->paused = YES;
+
+ // Update selector
+ tHashUpdateEntry * elementUpdate = NULL;
+ HASH_FIND_INT(hashForUpdates, &target, elementUpdate);
+ if( elementUpdate ) {
+ NSAssert( elementUpdate->entry != NULL, @"pauseTarget: unknown error");
+ elementUpdate->entry->paused = YES;
+ }
+
+}
+
+#pragma mark CCScheduler - Main Loop
+
+-(void) tick: (ccTime) dt
+{
+ updateHashLocked = YES;
+
+ if( timeScale_ != 1.0f )
+ dt *= timeScale_;
+
+ // Iterate all over the Updates selectors
+ tListEntry *entry, *tmp;
+
+ // updates with priority < 0
+ DL_FOREACH_SAFE( updatesNeg, entry, tmp ) {
+ if( ! entry->paused && !entry->markedForDeletion )
+ entry->impMethod( entry->target, updateSelector, dt );
+ }
+
+ // updates with priority == 0
+ DL_FOREACH_SAFE( updates0, entry, tmp ) {
+ if( ! entry->paused && !entry->markedForDeletion )
+ {
+ entry->impMethod( entry->target, updateSelector, dt );
+ }
+ }
+
+ // updates with priority > 0
+ DL_FOREACH_SAFE( updatesPos, entry, tmp ) {
+ if( ! entry->paused && !entry->markedForDeletion )
+ entry->impMethod( entry->target, updateSelector, dt );
+ }
+
+ // Iterate all over the custome selectors
+ for(tHashSelectorEntry *elt=hashForSelectors; elt != NULL; ) {
+
+ currentTarget = elt;
+ currentTargetSalvaged = NO;
+
+ if( ! currentTarget->paused ) {
+
+ // The 'timers' ccArray may change while inside this loop.
+ for( elt->timerIndex = 0; elt->timerIndex < elt->timers->num; elt->timerIndex++) {
+ elt->currentTimer = elt->timers->arr[elt->timerIndex];
+ elt->currentTimerSalvaged = NO;
+
+ impMethod( elt->currentTimer, updateSelector, dt);
+
+ if( elt->currentTimerSalvaged ) {
+ // The currentTimer told the remove itself. To prevent the timer from
+ // accidentally deallocating itself before finishing its step, we retained
+ // it. Now that step is done, it's safe to release it.
+ [elt->currentTimer release];
+ }
+
+ elt->currentTimer = nil;
+ }
+ }
+
+ // elt, at this moment, is still valid
+ // so it is safe to ask this here (issue #490)
+ elt = elt->hh.next;
+
+ // only delete currentTarget if no actions were scheduled during the cycle (issue #481)
+ if( currentTargetSalvaged && currentTarget->timers->num == 0 )
+ [self removeHashElement:currentTarget];
+ }
+
+ // delete all updates that are morked for deletion
+ // updates with priority < 0
+ DL_FOREACH_SAFE( updatesNeg, entry, tmp ) {
+ if(entry->markedForDeletion )
+ {
+ [self removeUpdateFromHash:entry];
+ }
+ }
+
+ // updates with priority == 0
+ DL_FOREACH_SAFE( updates0, entry, tmp ) {
+ if(entry->markedForDeletion )
+ {
+ [self removeUpdateFromHash:entry];
+ }
+ }
+
+ // updates with priority > 0
+ DL_FOREACH_SAFE( updatesPos, entry, tmp ) {
+ if(entry->markedForDeletion )
+ {
+ [self removeUpdateFromHash:entry];
+ }
+ }
+
+ updateHashLocked = NO;
+ currentTarget = nil;
+}
+@end
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCNode.h"
+#import "CCProtocols.h"
+#import "CCTextureAtlas.h"
+
+@class CCSpriteBatchNode;
+@class CCSpriteFrame;
+@class CCAnimation;
+
+#pragma mark CCSprite
+
+enum {
+ /// CCSprite invalid index on the CCSpriteBatchode
+ CCSpriteIndexNotInitialized = 0xffffffff,
+};
+
+/**
+ Whether or not an CCSprite will rotate, scale or translate with it's parent.
+ Useful in health bars, when you want that the health bar translates with it's parent but you don't
+ want it to rotate with its parent.
+ @since v0.99.0
+ */
+typedef enum {
+ //! Translate with it's parent
+ CC_HONOR_PARENT_TRANSFORM_TRANSLATE = 1 << 0,
+ //! Rotate with it's parent
+ CC_HONOR_PARENT_TRANSFORM_ROTATE = 1 << 1,
+ //! Scale with it's parent
+ CC_HONOR_PARENT_TRANSFORM_SCALE = 1 << 2,
+ //! Skew with it's parent
+ CC_HONOR_PARENT_TRANSFORM_SKEW = 1 << 3,
+
+ //! All possible transformation enabled. Default value.
+ CC_HONOR_PARENT_TRANSFORM_ALL = CC_HONOR_PARENT_TRANSFORM_TRANSLATE | CC_HONOR_PARENT_TRANSFORM_ROTATE | CC_HONOR_PARENT_TRANSFORM_SCALE | CC_HONOR_PARENT_TRANSFORM_SKEW,
+
+} ccHonorParentTransform;
+
+/** CCSprite is a 2d image ( http://en.wikipedia.org/wiki/Sprite_(computer_graphics) )
+ *
+ * CCSprite can be created with an image, or with a sub-rectangle of an image.
+ *
+ * If the parent or any of its ancestors is a CCSpriteBatchNode then the following features/limitations are valid
+ * - Features when the parent is a CCBatchNode:
+ * - MUCH faster rendering, specially if the CCSpriteBatchNode has many children. All the children will be drawn in a single batch.
+ *
+ * - Limitations
+ * - Camera is not supported yet (eg: CCOrbitCamera action doesn't work)
+ * - GridBase actions are not supported (eg: CCLens, CCRipple, CCTwirl)
+ * - The Alias/Antialias property belongs to CCSpriteBatchNode, so you can't individually set the aliased property.
+ * - The Blending function property belongs to CCSpriteBatchNode, so you can't individually set the blending function property.
+ * - Parallax scroller is not supported, but can be simulated with a "proxy" sprite.
+ *
+ * If the parent is an standard CCNode, then CCSprite behaves like any other CCNode:
+ * - It supports blending functions
+ * - It supports aliasing / antialiasing
+ * - But the rendering will be slower: 1 draw per children.
+ *
+ * The default anchorPoint in CCSprite is (0.5, 0.5).
+ */
+@interface CCSprite : CCNode <CCRGBAProtocol, CCTextureProtocol>
+{
+
+ //
+ // Data used when the sprite is rendered using a CCSpriteBatchNode
+ //
+ CCTextureAtlas *textureAtlas_; // Sprite Sheet texture atlas (weak reference)
+ NSUInteger atlasIndex_; // Absolute (real) Index on the batch node
+ CCSpriteBatchNode *batchNode_; // Used batch node (weak reference)
+ ccHonorParentTransform honorParentTransform_; // whether or not to transform according to its parent transformations
+ BOOL dirty_; // Sprite needs to be updated
+ BOOL recursiveDirty_; // Subchildren needs to be updated
+ BOOL hasChildren_; // optimization to check if it contain children
+
+ //
+ // Data used when the sprite is self-rendered
+ //
+ ccBlendFunc blendFunc_; // Needed for the texture protocol
+ CCTexture2D *texture_; // Texture used to render the sprite
+
+ //
+ // Shared data
+ //
+
+ // whether or not it's parent is a CCSpriteBatchNode
+ BOOL usesBatchNode_;
+
+ // texture
+ CGRect rect_;
+ CGRect rectInPixels_;
+ BOOL rectRotated_;
+
+ // Offset Position (used by Zwoptex)
+ CGPoint offsetPositionInPixels_;
+ CGPoint unflippedOffsetPositionFromCenter_;
+
+ // vertex coords, texture coords and color info
+ ccV3F_C4B_T2F_Quad quad_;
+
+ // opacity and RGB protocol
+ GLubyte opacity_;
+ ccColor3B color_;
+ ccColor3B colorUnmodified_;
+ BOOL opacityModifyRGB_;
+
+ // image is flipped
+ BOOL flipX_;
+ BOOL flipY_;
+
+
+ // Animations that belong to the sprite
+ NSMutableDictionary *animations_;
+
+@public
+ // used internally.
+ void (*updateMethod)(id, SEL);
+}
+
+/** whether or not the Sprite needs to be updated in the Atlas */
+@property (nonatomic,readwrite) BOOL dirty;
+/** the quad (tex coords, vertex coords and color) information */
+@property (nonatomic,readonly) ccV3F_C4B_T2F_Quad quad;
+/** The index used on the TextureAtlas. Don't modify this value unless you know what you are doing */
+@property (nonatomic,readwrite) NSUInteger atlasIndex;
+/** returns the rect of the CCSprite in points */
+@property (nonatomic,readonly) CGRect textureRect;
+/** returns whether or not the texture rectangle is rotated */
+@property (nonatomic,readonly) BOOL textureRectRotated;
+/** whether or not the sprite is flipped horizontally.
+ It only flips the texture of the sprite, and not the texture of the sprite's children.
+ Also, flipping the texture doesn't alter the anchorPoint.
+ If you want to flip the anchorPoint too, and/or to flip the children too use:
+
+ sprite.scaleX *= -1;
+ */
+@property (nonatomic,readwrite) BOOL flipX;
+/** whether or not the sprite is flipped vertically.
+ It only flips the texture of the sprite, and not the texture of the sprite's children.
+ Also, flipping the texture doesn't alter the anchorPoint.
+ If you want to flip the anchorPoint too, and/or to flip the children too use:
+
+ sprite.scaleY *= -1;
+ */
+@property (nonatomic,readwrite) BOOL flipY;
+/** opacity: conforms to CCRGBAProtocol protocol */
+@property (nonatomic,readwrite) GLubyte opacity;
+/** RGB colors: conforms to CCRGBAProtocol protocol */
+@property (nonatomic,readwrite) ccColor3B color;
+/** whether or not the Sprite is rendered using a CCSpriteBatchNode */
+@property (nonatomic,readwrite) BOOL usesBatchNode;
+/** weak reference of the CCTextureAtlas used when the sprite is rendered using a CCSpriteBatchNode */
+@property (nonatomic,readwrite,assign) CCTextureAtlas *textureAtlas;
+/** weak reference to the CCSpriteBatchNode that renders the CCSprite */
+@property (nonatomic,readwrite,assign) CCSpriteBatchNode *batchNode;
+/** whether or not to transform according to its parent transfomrations.
+ Useful for health bars. eg: Don't rotate the health bar, even if the parent rotates.
+ IMPORTANT: Only valid if it is rendered using an CCSpriteBatchNode.
+ @since v0.99.0
+ */
+@property (nonatomic,readwrite) ccHonorParentTransform honorParentTransform;
+/** offset position in pixels of the sprite in points. Calculated automatically by editors like Zwoptex.
+ @since v0.99.0
+ */
+@property (nonatomic,readonly) CGPoint offsetPositionInPixels;
+/** conforms to CCTextureProtocol protocol */
+@property (nonatomic,readwrite) ccBlendFunc blendFunc;
+
+#pragma mark CCSprite - Initializers
+
+/** Creates an sprite with a texture.
+ The rect used will be the size of the texture.
+ The offset will be (0,0).
+ */
++(id) spriteWithTexture:(CCTexture2D*)texture;
+
+/** Creates an sprite with a texture and a rect.
+ The offset will be (0,0).
+ */
++(id) spriteWithTexture:(CCTexture2D*)texture rect:(CGRect)rect;
+
+/** Creates an sprite with an sprite frame.
+ */
++(id) spriteWithSpriteFrame:(CCSpriteFrame*)spriteFrame;
+
+/** Creates an sprite with an sprite frame name.
+ An CCSpriteFrame will be fetched from the CCSpriteFrameCache by name.
+ If the CCSpriteFrame doesn't exist it will raise an exception.
+ @since v0.9
+ */
++(id) spriteWithSpriteFrameName:(NSString*)spriteFrameName;
+
+/** Creates an sprite with an image filename.
+ The rect used will be the size of the image.
+ The offset will be (0,0).
+ */
++(id) spriteWithFile:(NSString*)filename;
+
+/** Creates an sprite with an image filename and a rect.
+ The offset will be (0,0).
+ */
++(id) spriteWithFile:(NSString*)filename rect:(CGRect)rect;
+
+/** Creates an sprite with a CGImageRef and a key.
+ The key is used by the CCTextureCache to know if a texture was already created with this CGImage.
+ For example, a valid key is: @"sprite_frame_01".
+ If key is nil, then a new texture will be created each time by the CCTextureCache.
+ @since v0.99.0
+ */
++(id) spriteWithCGImage: (CGImageRef)image key:(NSString*)key;
+
+
+/** Creates an sprite with an CCBatchNode and a rect
+ */
++(id) spriteWithBatchNode:(CCSpriteBatchNode*)batchNode rect:(CGRect)rect;
+
+
+/** Initializes an sprite with a texture.
+ The rect used will be the size of the texture.
+ The offset will be (0,0).
+ */
+-(id) initWithTexture:(CCTexture2D*)texture;
+
+/** Initializes an sprite with a texture and a rect in points.
+ The offset will be (0,0).
+ */
+-(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect;
+
+/** Initializes an sprite with an sprite frame.
+ */
+-(id) initWithSpriteFrame:(CCSpriteFrame*)spriteFrame;
+
+/** Initializes an sprite with an sprite frame name.
+ An CCSpriteFrame will be fetched from the CCSpriteFrameCache by name.
+ If the CCSpriteFrame doesn't exist it will raise an exception.
+ @since v0.9
+ */
+-(id) initWithSpriteFrameName:(NSString*)spriteFrameName;
+
+/** Initializes an sprite with an image filename.
+ The rect used will be the size of the image.
+ The offset will be (0,0).
+ */
+-(id) initWithFile:(NSString*)filename;
+
+/** Initializes an sprite with an image filename, and a rect.
+ The offset will be (0,0).
+ */
+-(id) initWithFile:(NSString*)filename rect:(CGRect)rect;
+
+/** Initializes an sprite with a CGImageRef and a key
+ The key is used by the CCTextureCache to know if a texture was already created with this CGImage.
+ For example, a valid key is: @"sprite_frame_01".
+ If key is nil, then a new texture will be created each time by the CCTextureCache.
+ @since v0.99.0
+ */
+-(id) initWithCGImage:(CGImageRef)image key:(NSString*)key;
+
+/** Initializes an sprite with an CCSpriteBatchNode and a rect in points
+ */
+-(id) initWithBatchNode:(CCSpriteBatchNode*)batchNode rect:(CGRect)rect;
+
+/** Initializes an sprite with an CCSpriteBatchNode and a rect in pixels
+ @since v0.99.5
+ */
+-(id) initWithBatchNode:(CCSpriteBatchNode*)batchNode rectInPixels:(CGRect)rect;
+
+
+
+#pragma mark CCSprite - BatchNode methods
+
+/** updates the quad according the the rotation, position, scale values.
+ */
+-(void)updateTransform;
+
+/** updates the texture rect of the CCSprite in points.
+ */
+-(void) setTextureRect:(CGRect) rect;
+/** updates the texture rect, rectRotated and untrimmed size of the CCSprite in pixels
+ */
+-(void) setTextureRectInPixels:(CGRect)rect rotated:(BOOL)rotated untrimmedSize:(CGSize)size;
+
+/** tell the sprite to use self-render.
+ @since v0.99.0
+ */
+-(void) useSelfRender;
+
+/** tell the sprite to use sprite batch node
+ @since v0.99.0
+ */
+-(void) useBatchNode:(CCSpriteBatchNode*)batchNode;
+
+
+#pragma mark CCSprite - Frames
+
+/** sets a new display frame to the CCSprite. */
+-(void) setDisplayFrame:(CCSpriteFrame*)newFrame;
+
+/** returns whether or not a CCSpriteFrame is being displayed */
+-(BOOL) isFrameDisplayed:(CCSpriteFrame*)frame;
+
+/** returns the current displayed frame. */
+-(CCSpriteFrame*) displayedFrame;
+
+#pragma mark CCSprite - Animation
+
+/** changes the display frame based on an animation and an index.
+ @deprecated Will be removed in 1.0.1. Use setDisplayFrameWithAnimationName:index instead
+ */
+-(void) setDisplayFrame: (NSString*) animationName index:(int) frameIndex DEPRECATED_ATTRIBUTE;
+
+/** changes the display frame with animation name and index.
+ The animation name will be get from the CCAnimationCache
+ @since v0.99.5
+ */
+-(void) setDisplayFrameWithAnimationName:(NSString*)animationName index:(int) frameIndex;
+
+/** returns an Animation given it's name.
+
+ @deprecated Use CCAnimationCache instead. Will be removed in 1.0.1
+ */
+-(CCAnimation*)animationByName: (NSString*) animationName DEPRECATED_ATTRIBUTE;
+
+/** adds an Animation to the Sprite.
+
+ @deprecated Use CCAnimationCache instead. Will be removed in 1.0.1
+ */
+-(void) addAnimation: (CCAnimation*) animation DEPRECATED_ATTRIBUTE;
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import <Availability.h>
+
+#import "ccConfig.h"
+#import "CCSpriteBatchNode.h"
+#import "CCSprite.h"
+#import "CCSpriteFrame.h"
+#import "CCSpriteFrameCache.h"
+#import "CCAnimation.h"
+#import "CCAnimationCache.h"
+#import "CCTextureCache.h"
+#import "Support/CGPointExtension.h"
+#import "CCDrawingPrimitives.h"
+
+#pragma mark -
+#pragma mark CCSprite
+
+#if CC_SPRITEBATCHNODE_RENDER_SUBPIXEL
+#define RENDER_IN_SUBPIXEL
+#else
+#define RENDER_IN_SUBPIXEL(__A__) ( (int)(__A__))
+#endif
+
+// XXX: Optmization
+struct transformValues_ {
+ CGPoint pos; // position x and y
+ CGPoint scale; // scale x and y
+ float rotation;
+ CGPoint skew; // skew x and y
+ CGPoint ap; // anchor point in pixels
+ BOOL visible;
+};
+
+@interface CCSprite (Private)
+-(void)updateTextureCoords:(CGRect)rect;
+-(void)updateBlendFunc;
+-(void) initAnimationDictionary;
+-(void) getTransformValues:(struct transformValues_*)tv; // optimization
+@end
+
+@implementation CCSprite
+
+@synthesize dirty = dirty_;
+@synthesize quad = quad_;
+@synthesize atlasIndex = atlasIndex_;
+@synthesize textureRect = rect_;
+@synthesize textureRectRotated = rectRotated_;
+@synthesize blendFunc = blendFunc_;
+@synthesize usesBatchNode = usesBatchNode_;
+@synthesize textureAtlas = textureAtlas_;
+@synthesize batchNode = batchNode_;
+@synthesize honorParentTransform = honorParentTransform_;
+@synthesize offsetPositionInPixels = offsetPositionInPixels_;
+
+
++(id)spriteWithTexture:(CCTexture2D*)texture
+{
+ return [[[self alloc] initWithTexture:texture] autorelease];
+}
+
++(id)spriteWithTexture:(CCTexture2D*)texture rect:(CGRect)rect
+{
+ return [[[self alloc] initWithTexture:texture rect:rect] autorelease];
+}
+
++(id)spriteWithFile:(NSString*)filename
+{
+ return [[[self alloc] initWithFile:filename] autorelease];
+}
+
++(id)spriteWithFile:(NSString*)filename rect:(CGRect)rect
+{
+ return [[[self alloc] initWithFile:filename rect:rect] autorelease];
+}
+
++(id)spriteWithSpriteFrame:(CCSpriteFrame*)spriteFrame
+{
+ return [[[self alloc] initWithSpriteFrame:spriteFrame] autorelease];
+}
+
++(id)spriteWithSpriteFrameName:(NSString*)spriteFrameName
+{
+ CCSpriteFrame *frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:spriteFrameName];
+ return [self spriteWithSpriteFrame:frame];
+}
+
++(id)spriteWithCGImage:(CGImageRef)image key:(NSString*)key
+{
+ return [[[self alloc] initWithCGImage:image key:key] autorelease];
+}
+
++(id) spriteWithBatchNode:(CCSpriteBatchNode*)batchNode rect:(CGRect)rect
+{
+ return [[[self alloc] initWithBatchNode:batchNode rect:rect] autorelease];
+}
+
+-(id) init
+{
+ if( (self=[super init]) ) {
+ dirty_ = recursiveDirty_ = NO;
+
+ // by default use "Self Render".
+ // if the sprite is added to a batchnode, then it will automatically switch to "batchnode Render"
+ [self useSelfRender];
+
+ opacityModifyRGB_ = YES;
+ opacity_ = 255;
+ color_ = colorUnmodified_ = ccWHITE;
+
+ blendFunc_.src = CC_BLEND_SRC;
+ blendFunc_.dst = CC_BLEND_DST;
+
+ // update texture (calls updateBlendFunc)
+ [self setTexture:nil];
+
+ // clean the Quad
+ bzero(&quad_, sizeof(quad_));
+
+ flipY_ = flipX_ = NO;
+
+ // lazy alloc
+ animations_ = nil;
+
+ // default transform anchor: center
+ anchorPoint_ = ccp(0.5f, 0.5f);
+
+ // zwoptex default values
+ offsetPositionInPixels_ = CGPointZero;
+
+ honorParentTransform_ = CC_HONOR_PARENT_TRANSFORM_ALL;
+ hasChildren_ = NO;
+
+ // Atlas: Color
+ ccColor4B tmpColor = {255,255,255,255};
+ quad_.bl.colors = tmpColor;
+ quad_.br.colors = tmpColor;
+ quad_.tl.colors = tmpColor;
+ quad_.tr.colors = tmpColor;
+
+ // Atlas: Vertex
+
+ // updated in "useSelfRender"
+
+ // Atlas: TexCoords
+ [self setTextureRectInPixels:CGRectZero rotated:NO untrimmedSize:CGSizeZero];
+
+ // updateMethod selector
+ updateMethod = (__typeof__(updateMethod))[self methodForSelector:@selector(updateTransform)];
+ }
+
+ return self;
+}
+
+-(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect
+{
+ NSAssert(texture!=nil, @"Invalid texture for sprite");
+ // IMPORTANT: [self init] and not [super init];
+ if( (self = [self init]) )
+ {
+ [self setTexture:texture];
+ [self setTextureRect:rect];
+ }
+ return self;
+}
+
+-(id) initWithTexture:(CCTexture2D*)texture
+{
+ NSAssert(texture!=nil, @"Invalid texture for sprite");
+
+ CGRect rect = CGRectZero;
+ rect.size = texture.contentSize;
+ return [self initWithTexture:texture rect:rect];
+}
+
+-(id) initWithFile:(NSString*)filename
+{
+ NSAssert(filename!=nil, @"Invalid filename for sprite");
+
+ CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage: filename];
+ if( texture ) {
+ CGRect rect = CGRectZero;
+ rect.size = texture.contentSize;
+ return [self initWithTexture:texture rect:rect];
+ }
+
+ [self release];
+ return nil;
+}
+
+-(id) initWithFile:(NSString*)filename rect:(CGRect)rect
+{
+ NSAssert(filename!=nil, @"Invalid filename for sprite");
+
+ CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage: filename];
+ if( texture )
+ return [self initWithTexture:texture rect:rect];
+
+ [self release];
+ return nil;
+}
+
+- (id) initWithSpriteFrame:(CCSpriteFrame*)spriteFrame
+{
+ NSAssert(spriteFrame!=nil, @"Invalid spriteFrame for sprite");
+
+ id ret = [self initWithTexture:spriteFrame.texture rect:spriteFrame.rect];
+ [self setDisplayFrame:spriteFrame];
+ return ret;
+}
+
+-(id)initWithSpriteFrameName:(NSString*)spriteFrameName
+{
+ NSAssert(spriteFrameName!=nil, @"Invalid spriteFrameName for sprite");
+
+ CCSpriteFrame *frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:spriteFrameName];
+ return [self initWithSpriteFrame:frame];
+}
+
+// XXX: deprecated
+- (id) initWithCGImage: (CGImageRef)image
+{
+ NSAssert(image!=nil, @"Invalid CGImageRef for sprite");
+
+ // XXX: possible bug. See issue #349. New API should be added
+ NSString *key = [NSString stringWithFormat:@"%08X",(unsigned long)image];
+ CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addCGImage:image forKey:key];
+
+ CGRect rect = CGRectZero;
+ rect.size = texture.contentSize;
+
+ return [self initWithTexture:texture rect:rect];
+}
+
+- (id) initWithCGImage:(CGImageRef)image key:(NSString*)key
+{
+ NSAssert(image!=nil, @"Invalid CGImageRef for sprite");
+
+ // XXX: possible bug. See issue #349. New API should be added
+ CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addCGImage:image forKey:key];
+
+ CGRect rect = CGRectZero;
+ rect.size = texture.contentSize;
+
+ return [self initWithTexture:texture rect:rect];
+}
+
+-(id) initWithBatchNode:(CCSpriteBatchNode*)batchNode rect:(CGRect)rect
+{
+ id ret = [self initWithTexture:batchNode.texture rect:rect];
+ [self useBatchNode:batchNode];
+
+ return ret;
+}
+
+-(id) initWithBatchNode:(CCSpriteBatchNode*)batchNode rectInPixels:(CGRect)rect
+{
+ id ret = [self initWithTexture:batchNode.texture];
+ [self setTextureRectInPixels:rect rotated:NO untrimmedSize:rect.size];
+ [self useBatchNode:batchNode];
+
+ return ret;
+}
+
+- (NSString*) description
+{
+ return [NSString stringWithFormat:@"<%@ = %08X | Rect = (%.2f,%.2f,%.2f,%.2f) | tag = %i | atlasIndex = %i>", [self class], self,
+ rect_.origin.x, rect_.origin.y, rect_.size.width, rect_.size.height,
+ tag_,
+ atlasIndex_
+ ];
+}
+
+- (void) dealloc
+{
+ [texture_ release];
+ [animations_ release];
+ [super dealloc];
+}
+
+-(void) useSelfRender
+{
+ atlasIndex_ = CCSpriteIndexNotInitialized;
+ usesBatchNode_ = NO;
+ textureAtlas_ = nil;
+ batchNode_ = nil;
+ dirty_ = recursiveDirty_ = NO;
+
+ float x1 = 0 + offsetPositionInPixels_.x;
+ float y1 = 0 + offsetPositionInPixels_.y;
+ float x2 = x1 + rectInPixels_.size.width;
+ float y2 = y1 + rectInPixels_.size.height;
+ quad_.bl.vertices = (ccVertex3F) { x1, y1, 0 };
+ quad_.br.vertices = (ccVertex3F) { x2, y1, 0 };
+ quad_.tl.vertices = (ccVertex3F) { x1, y2, 0 };
+ quad_.tr.vertices = (ccVertex3F) { x2, y2, 0 };
+}
+
+-(void) useBatchNode:(CCSpriteBatchNode*)batchNode
+{
+ usesBatchNode_ = YES;
+ textureAtlas_ = [batchNode textureAtlas]; // weak ref
+ batchNode_ = batchNode; // weak ref
+}
+
+-(void) initAnimationDictionary
+{
+ animations_ = [[NSMutableDictionary alloc] initWithCapacity:2];
+}
+
+-(void)setTextureRect:(CGRect)rect
+{
+ CGRect rectInPixels = CC_RECT_POINTS_TO_PIXELS( rect );
+ [self setTextureRectInPixels:rectInPixels rotated:NO untrimmedSize:rectInPixels.size];
+}
+
+-(void)setTextureRectInPixels:(CGRect)rect rotated:(BOOL)rotated untrimmedSize:(CGSize)untrimmedSize
+{
+ rectInPixels_ = rect;
+ rect_ = CC_RECT_PIXELS_TO_POINTS( rect );
+ rectRotated_ = rotated;
+
+ [self setContentSizeInPixels:untrimmedSize];
+ [self updateTextureCoords:rectInPixels_];
+
+ CGPoint relativeOffsetInPixels = unflippedOffsetPositionFromCenter_;
+
+ // issue #732
+ if( flipX_ )
+ relativeOffsetInPixels.x = -relativeOffsetInPixels.x;
+ if( flipY_ )
+ relativeOffsetInPixels.y = -relativeOffsetInPixels.y;
+
+ offsetPositionInPixels_.x = relativeOffsetInPixels.x + (contentSizeInPixels_.width - rectInPixels_.size.width) / 2;
+ offsetPositionInPixels_.y = relativeOffsetInPixels.y + (contentSizeInPixels_.height - rectInPixels_.size.height) / 2;
+
+
+ // rendering using batch node
+ if( usesBatchNode_ ) {
+ // update dirty_, don't update recursiveDirty_
+ dirty_ = YES;
+ }
+
+ // self rendering
+ else
+ {
+ // Atlas: Vertex
+ float x1 = 0 + offsetPositionInPixels_.x;
+ float y1 = 0 + offsetPositionInPixels_.y;
+ float x2 = x1 + rectInPixels_.size.width;
+ float y2 = y1 + rectInPixels_.size.height;
+
+ // Don't update Z.
+ quad_.bl.vertices = (ccVertex3F) { x1, y1, 0 };
+ quad_.br.vertices = (ccVertex3F) { x2, y1, 0 };
+ quad_.tl.vertices = (ccVertex3F) { x1, y2, 0 };
+ quad_.tr.vertices = (ccVertex3F) { x2, y2, 0 };
+ }
+}
+
+-(void)updateTextureCoords:(CGRect)rect
+{
+ CCTexture2D *tex = (usesBatchNode_)?[textureAtlas_ texture]:texture_;
+ if(!tex)
+ return;
+
+ float atlasWidth = (float)tex.pixelsWide;
+ float atlasHeight = (float)tex.pixelsHigh;
+
+ float left,right,top,bottom;
+
+ if(rectRotated_){
+#if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
+ left = (2*rect.origin.x+1)/(2*atlasWidth);
+ right = left+(rect.size.height*2-2)/(2*atlasWidth);
+ top = (2*rect.origin.y+1)/(2*atlasHeight);
+ bottom = top+(rect.size.width*2-2)/(2*atlasHeight);
+#else
+ left = rect.origin.x/atlasWidth;
+ right = left+(rect.size.height/atlasWidth);
+ top = rect.origin.y/atlasHeight;
+ bottom = top+(rect.size.width/atlasHeight);
+#endif // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
+
+ if( flipX_)
+ CC_SWAP(top,bottom);
+ if( flipY_)
+ CC_SWAP(left,right);
+
+ quad_.bl.texCoords.u = left;
+ quad_.bl.texCoords.v = top;
+ quad_.br.texCoords.u = left;
+ quad_.br.texCoords.v = bottom;
+ quad_.tl.texCoords.u = right;
+ quad_.tl.texCoords.v = top;
+ quad_.tr.texCoords.u = right;
+ quad_.tr.texCoords.v = bottom;
+ } else {
+#if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
+ left = (2*rect.origin.x+1)/(2*atlasWidth);
+ right = left + (rect.size.width*2-2)/(2*atlasWidth);
+ top = (2*rect.origin.y+1)/(2*atlasHeight);
+ bottom = top + (rect.size.height*2-2)/(2*atlasHeight);
+#else
+ left = rect.origin.x/atlasWidth;
+ right = left + rect.size.width/atlasWidth;
+ top = rect.origin.y/atlasHeight;
+ bottom = top + rect.size.height/atlasHeight;
+#endif // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
+
+ if( flipX_)
+ CC_SWAP(left,right);
+ if( flipY_)
+ CC_SWAP(top,bottom);
+
+ quad_.bl.texCoords.u = left;
+ quad_.bl.texCoords.v = bottom;
+ quad_.br.texCoords.u = right;
+ quad_.br.texCoords.v = bottom;
+ quad_.tl.texCoords.u = left;
+ quad_.tl.texCoords.v = top;
+ quad_.tr.texCoords.u = right;
+ quad_.tr.texCoords.v = top;
+ }
+}
+
+-(void)updateTransform
+{
+ NSAssert( usesBatchNode_, @"updateTransform is only valid when CCSprite is being renderd using an CCSpriteBatchNode");
+
+ // optimization. Quick return if not dirty
+ if( ! dirty_ )
+ return;
+
+ CGAffineTransform matrix;
+
+ // Optimization: if it is not visible, then do nothing
+ if( ! visible_ ) {
+ quad_.br.vertices = quad_.tl.vertices = quad_.tr.vertices = quad_.bl.vertices = (ccVertex3F){0,0,0};
+ [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_];
+ dirty_ = recursiveDirty_ = NO;
+ return ;
+ }
+
+
+ // Optimization: If parent is batchnode, or parent is nil
+ // build Affine transform manually
+ if( ! parent_ || parent_ == batchNode_ ) {
+
+ float radians = -CC_DEGREES_TO_RADIANS(rotation_);
+ float c = cosf(radians);
+ float s = sinf(radians);
+
+ matrix = CGAffineTransformMake( c * scaleX_, s * scaleX_,
+ -s * scaleY_, c * scaleY_,
+ positionInPixels_.x, positionInPixels_.y);
+ if( skewX_ || skewY_ ) {
+ CGAffineTransform skewMatrix = CGAffineTransformMake(1.0f, tanf(CC_DEGREES_TO_RADIANS(skewY_)),
+ tanf(CC_DEGREES_TO_RADIANS(skewX_)), 1.0f,
+ 0.0f, 0.0f);
+ matrix = CGAffineTransformConcat(skewMatrix, matrix);
+ }
+ matrix = CGAffineTransformTranslate(matrix, -anchorPointInPixels_.x, -anchorPointInPixels_.y);
+
+
+ } else { // parent_ != batchNode_
+
+ // else do affine transformation according to the HonorParentTransform
+
+ matrix = CGAffineTransformIdentity;
+ ccHonorParentTransform prevHonor = CC_HONOR_PARENT_TRANSFORM_ALL;
+
+ for (CCNode *p = self ; p && p != batchNode_ ; p = p.parent) {
+
+ // Might happen. Issue #1053
+ NSAssert( [p isKindOfClass:[CCSprite class]], @"CCSprite should be a CCSprite subclass. Probably you initialized an sprite with a batchnode, but you didn't add it to the batch node." );
+
+ struct transformValues_ tv;
+ [(CCSprite*)p getTransformValues: &tv];
+
+ // If any of the parents are not visible, then don't draw this node
+ if( ! tv.visible ) {
+ quad_.br.vertices = quad_.tl.vertices = quad_.tr.vertices = quad_.bl.vertices = (ccVertex3F){0,0,0};
+ [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_];
+ dirty_ = recursiveDirty_ = NO;
+ return;
+ }
+ CGAffineTransform newMatrix = CGAffineTransformIdentity;
+
+ // 2nd: Translate, Skew, Rotate, Scale
+ if( prevHonor & CC_HONOR_PARENT_TRANSFORM_TRANSLATE )
+ newMatrix = CGAffineTransformTranslate(newMatrix, tv.pos.x, tv.pos.y);
+ if( prevHonor & CC_HONOR_PARENT_TRANSFORM_ROTATE )
+ newMatrix = CGAffineTransformRotate(newMatrix, -CC_DEGREES_TO_RADIANS(tv.rotation));
+ if ( prevHonor & CC_HONOR_PARENT_TRANSFORM_SKEW ) {
+ CGAffineTransform skew = CGAffineTransformMake(1.0f, tanf(CC_DEGREES_TO_RADIANS(tv.skew.y)), tanf(CC_DEGREES_TO_RADIANS(tv.skew.x)), 1.0f, 0.0f, 0.0f);
+ // apply the skew to the transform
+ newMatrix = CGAffineTransformConcat(skew, newMatrix);
+ }
+ if( prevHonor & CC_HONOR_PARENT_TRANSFORM_SCALE ) {
+ newMatrix = CGAffineTransformScale(newMatrix, tv.scale.x, tv.scale.y);
+ }
+
+ // 3rd: Translate anchor point
+ newMatrix = CGAffineTransformTranslate(newMatrix, -tv.ap.x, -tv.ap.y);
+
+ // 4th: Matrix multiplication
+ matrix = CGAffineTransformConcat( matrix, newMatrix);
+
+ prevHonor = [(CCSprite*)p honorParentTransform];
+ }
+ }
+
+
+ //
+ // calculate the Quad based on the Affine Matrix
+ //
+
+ CGSize size = rectInPixels_.size;
+
+ float x1 = offsetPositionInPixels_.x;
+ float y1 = offsetPositionInPixels_.y;
+
+ float x2 = x1 + size.width;
+ float y2 = y1 + size.height;
+ float x = matrix.tx;
+ float y = matrix.ty;
+
+ float cr = matrix.a;
+ float sr = matrix.b;
+ float cr2 = matrix.d;
+ float sr2 = -matrix.c;
+ float ax = x1 * cr - y1 * sr2 + x;
+ float ay = x1 * sr + y1 * cr2 + y;
+
+ float bx = x2 * cr - y1 * sr2 + x;
+ float by = x2 * sr + y1 * cr2 + y;
+
+ float cx = x2 * cr - y2 * sr2 + x;
+ float cy = x2 * sr + y2 * cr2 + y;
+
+ float dx = x1 * cr - y2 * sr2 + x;
+ float dy = x1 * sr + y2 * cr2 + y;
+
+ quad_.bl.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(ax), RENDER_IN_SUBPIXEL(ay), vertexZ_ };
+ quad_.br.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(bx), RENDER_IN_SUBPIXEL(by), vertexZ_ };
+ quad_.tl.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(dx), RENDER_IN_SUBPIXEL(dy), vertexZ_ };
+ quad_.tr.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(cx), RENDER_IN_SUBPIXEL(cy), vertexZ_ };
+
+ [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_];
+ dirty_ = recursiveDirty_ = NO;
+}
+
+// XXX: Optimization: instead of calling 5 times the parent sprite to obtain: position, scale.x, scale.y, anchorpoint and rotation,
+// this fuction return the 5 values in 1 single call
+-(void) getTransformValues:(struct transformValues_*) tv
+{
+ tv->pos = positionInPixels_;
+ tv->scale.x = scaleX_;
+ tv->scale.y = scaleY_;
+ tv->rotation = rotation_;
+ tv->skew.x = skewX_;
+ tv->skew.y = skewY_;
+ tv->ap = anchorPointInPixels_;
+ tv->visible = visible_;
+}
+
+#pragma mark CCSprite - draw
+
+-(void) draw
+{
+ NSAssert(!usesBatchNode_, @"If CCSprite is being rendered by CCSpriteBatchNode, CCSprite#draw SHOULD NOT be called");
+
+ // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Unneeded states: -
+
+ BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST;
+ if( newBlend )
+ glBlendFunc( blendFunc_.src, blendFunc_.dst );
+
+#define kQuadSize sizeof(quad_.bl)
+ glBindTexture(GL_TEXTURE_2D, [texture_ name]);
+
+ long offset = (long)&quad_;
+
+ // vertex
+ NSInteger diff = offsetof( ccV3F_C4B_T2F, vertices);
+ glVertexPointer(3, GL_FLOAT, kQuadSize, (void*) (offset + diff) );
+
+ // color
+ diff = offsetof( ccV3F_C4B_T2F, colors);
+ glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (void*)(offset + diff));
+
+ // tex coords
+ diff = offsetof( ccV3F_C4B_T2F, texCoords);
+ glTexCoordPointer(2, GL_FLOAT, kQuadSize, (void*)(offset + diff));
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ if( newBlend )
+ glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
+
+#if CC_SPRITE_DEBUG_DRAW
+ CGSize s = [self contentSize];
+ CGPoint vertices[4]={
+ ccp(0,0),ccp(s.width,0),
+ ccp(s.width,s.height),ccp(0,s.height),
+ };
+ ccDrawPoly(vertices, 4, YES);
+#endif // CC_TEXTURENODE_DEBUG_DRAW
+
+}
+
+#pragma mark CCSprite - CCNode overrides
+
+-(void) addChild:(CCSprite*)child z:(NSInteger)z tag:(NSInteger) aTag
+{
+ NSAssert( child != nil, @"Argument must be non-nil");
+
+ [super addChild:child z:z tag:aTag];
+
+ if( usesBatchNode_ ) {
+ NSAssert( [child isKindOfClass:[CCSprite class]], @"CCSprite only supports CCSprites as children when using CCSpriteBatchNode");
+ NSAssert( child.texture.name == textureAtlas_.texture.name, @"CCSprite is not using the same texture id");
+
+ NSUInteger index = [batchNode_ atlasIndexForChild:child atZ:z];
+ [batchNode_ insertChild:child inAtlasAtIndex:index];
+ }
+
+ hasChildren_ = YES;
+}
+
+-(void) reorderChild:(CCSprite*)child z:(NSInteger)z
+{
+ NSAssert( child != nil, @"Child must be non-nil");
+ NSAssert( [children_ containsObject:child], @"Child doesn't belong to Sprite" );
+
+ if( z == child.zOrder )
+ return;
+
+ if( usesBatchNode_ ) {
+ // XXX: Instead of removing/adding, it is more efficient to reorder manually
+ [child retain];
+ [self removeChild:child cleanup:NO];
+ [self addChild:child z:z];
+ [child release];
+ }
+
+ else
+ [super reorderChild:child z:z];
+}
+
+-(void)removeChild: (CCSprite *)sprite cleanup:(BOOL)doCleanup
+{
+ if( usesBatchNode_ )
+ [batchNode_ removeSpriteFromAtlas:sprite];
+
+ [super removeChild:sprite cleanup:doCleanup];
+
+ hasChildren_ = ( [children_ count] > 0 );
+}
+
+-(void)removeAllChildrenWithCleanup:(BOOL)doCleanup
+{
+ if( usesBatchNode_ ) {
+ CCSprite *child;
+ CCARRAY_FOREACH(children_, child)
+ [batchNode_ removeSpriteFromAtlas:child];
+ }
+
+ [super removeAllChildrenWithCleanup:doCleanup];
+
+ hasChildren_ = NO;
+}
+
+//
+// CCNode property overloads
+// used only when parent is CCSpriteBatchNode
+//
+#pragma mark CCSprite - property overloads
+
+
+-(void) setDirtyRecursively:(BOOL)b
+{
+ dirty_ = recursiveDirty_ = b;
+ // recursively set dirty
+ if( hasChildren_ ) {
+ CCSprite *child;
+ CCARRAY_FOREACH(children_, child)
+ [child setDirtyRecursively:YES];
+ }
+}
+
+// XXX HACK: optimization
+#define SET_DIRTY_RECURSIVELY() { \
+ if( usesBatchNode_ && ! recursiveDirty_ ) { \
+ dirty_ = recursiveDirty_ = YES; \
+ if( hasChildren_) \
+ [self setDirtyRecursively:YES]; \
+ } \
+ }
+
+-(void)setPosition:(CGPoint)pos
+{
+ [super setPosition:pos];
+ SET_DIRTY_RECURSIVELY();
+}
+
+-(void)setPositionInPixels:(CGPoint)pos
+{
+ [super setPositionInPixels:pos];
+ SET_DIRTY_RECURSIVELY();
+}
+
+-(void)setRotation:(float)rot
+{
+ [super setRotation:rot];
+ SET_DIRTY_RECURSIVELY();
+}
+
+-(void)setSkewX:(float)sx
+{
+ [super setSkewX:sx];
+ SET_DIRTY_RECURSIVELY();
+}
+
+-(void)setSkewY:(float)sy
+{
+ [super setSkewY:sy];
+ SET_DIRTY_RECURSIVELY();
+}
+
+-(void)setScaleX:(float) sx
+{
+ [super setScaleX:sx];
+ SET_DIRTY_RECURSIVELY();
+}
+
+-(void)setScaleY:(float) sy
+{
+ [super setScaleY:sy];
+ SET_DIRTY_RECURSIVELY();
+}
+
+-(void)setScale:(float) s
+{
+ [super setScale:s];
+ SET_DIRTY_RECURSIVELY();
+}
+
+-(void) setVertexZ:(float)z
+{
+ [super setVertexZ:z];
+ SET_DIRTY_RECURSIVELY();
+}
+
+-(void)setAnchorPoint:(CGPoint)anchor
+{
+ [super setAnchorPoint:anchor];
+ SET_DIRTY_RECURSIVELY();
+}
+
+-(void)setIsRelativeAnchorPoint:(BOOL)relative
+{
+ NSAssert( ! usesBatchNode_, @"relativeTransformAnchor is invalid in CCSprite");
+ [super setIsRelativeAnchorPoint:relative];
+}
+
+-(void)setVisible:(BOOL)v
+{
+ [super setVisible:v];
+ SET_DIRTY_RECURSIVELY();
+}
+
+-(void)setFlipX:(BOOL)b
+{
+ if( flipX_ != b ) {
+ flipX_ = b;
+ [self setTextureRectInPixels:rectInPixels_ rotated:rectRotated_ untrimmedSize:contentSizeInPixels_];
+ }
+}
+-(BOOL) flipX
+{
+ return flipX_;
+}
+
+-(void) setFlipY:(BOOL)b
+{
+ if( flipY_ != b ) {
+ flipY_ = b;
+ [self setTextureRectInPixels:rectInPixels_ rotated:rectRotated_ untrimmedSize:contentSizeInPixels_];
+ }
+}
+-(BOOL) flipY
+{
+ return flipY_;
+}
+
+//
+// RGBA protocol
+//
+#pragma mark CCSprite - RGBA protocol
+-(void) updateColor
+{
+ ccColor4B color4 = {color_.r, color_.g, color_.b, opacity_ };
+
+ quad_.bl.colors = color4;
+ quad_.br.colors = color4;
+ quad_.tl.colors = color4;
+ quad_.tr.colors = color4;
+
+ // renders using Sprite Manager
+ if( usesBatchNode_ ) {
+ if( atlasIndex_ != CCSpriteIndexNotInitialized)
+ [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_];
+ else
+ // no need to set it recursively
+ // update dirty_, don't update recursiveDirty_
+ dirty_ = YES;
+ }
+ // self render
+ // do nothing
+}
+
+-(GLubyte) opacity
+{
+ return opacity_;
+}
+
+-(void) setOpacity:(GLubyte) anOpacity
+{
+ opacity_ = anOpacity;
+
+ // special opacity for premultiplied textures
+ if( opacityModifyRGB_ )
+ [self setColor: colorUnmodified_];
+
+ [self updateColor];
+}
+
+- (ccColor3B) color
+{
+ if(opacityModifyRGB_)
+ return colorUnmodified_;
+
+ return color_;
+}
+
+-(void) setColor:(ccColor3B)color3
+{
+ color_ = colorUnmodified_ = color3;
+
+ if( opacityModifyRGB_ ){
+ color_.r = color3.r * opacity_/255;
+ color_.g = color3.g * opacity_/255;
+ color_.b = color3.b * opacity_/255;
+ }
+
+ [self updateColor];
+}
+
+-(void) setOpacityModifyRGB:(BOOL)modify
+{
+ ccColor3B oldColor = self.color;
+ opacityModifyRGB_ = modify;
+ self.color = oldColor;
+}
+
+-(BOOL) doesOpacityModifyRGB
+{
+ return opacityModifyRGB_;
+}
+
+//
+// Frames
+//
+#pragma mark CCSprite - Frames
+
+-(void) setDisplayFrame:(CCSpriteFrame*)frame
+{
+ unflippedOffsetPositionFromCenter_ = frame.offsetInPixels;
+
+ CCTexture2D *newTexture = [frame texture];
+ // update texture before updating texture rect
+ if ( newTexture.name != texture_.name )
+ [self setTexture: newTexture];
+
+ // update rect
+ rectRotated_ = frame.rotated;
+ [self setTextureRectInPixels:frame.rectInPixels rotated:frame.rotated untrimmedSize:frame.originalSizeInPixels];
+}
+
+// XXX deprecated
+-(void) setDisplayFrame: (NSString*) animationName index:(int) frameIndex
+{
+ if( ! animations_ )
+ [self initAnimationDictionary];
+
+ CCAnimation *a = [animations_ objectForKey: animationName];
+ CCSpriteFrame *frame = [[a frames] objectAtIndex:frameIndex];
+
+ NSAssert( frame, @"CCSprite#setDisplayFrame. Invalid frame");
+
+ [self setDisplayFrame:frame];
+}
+
+-(void) setDisplayFrameWithAnimationName: (NSString*) animationName index:(int) frameIndex
+{
+ NSAssert( animationName, @"CCSprite#setDisplayFrameWithAnimationName. animationName must not be nil");
+
+ CCAnimation *a = [[CCAnimationCache sharedAnimationCache] animationByName:animationName];
+
+ NSAssert( a, @"CCSprite#setDisplayFrameWithAnimationName: Frame not found");
+
+ CCSpriteFrame *frame = [[a frames] objectAtIndex:frameIndex];
+
+ NSAssert( frame, @"CCSprite#setDisplayFrame. Invalid frame");
+
+ [self setDisplayFrame:frame];
+}
+
+
+-(BOOL) isFrameDisplayed:(CCSpriteFrame*)frame
+{
+ CGRect r = [frame rect];
+ return ( CGRectEqualToRect(r, rect_) &&
+ frame.texture.name == self.texture.name );
+}
+
+-(CCSpriteFrame*) displayedFrame
+{
+ return [CCSpriteFrame frameWithTexture:texture_
+ rectInPixels:rectInPixels_
+ rotated:rectRotated_
+ offset:unflippedOffsetPositionFromCenter_
+ originalSize:contentSizeInPixels_];
+}
+
+-(void) addAnimation: (CCAnimation*) anim
+{
+ // lazy alloc
+ if( ! animations_ )
+ [self initAnimationDictionary];
+
+ [animations_ setObject:anim forKey:[anim name]];
+}
+
+-(CCAnimation*)animationByName: (NSString*) animationName
+{
+ NSAssert( animationName != nil, @"animationName parameter must be non nil");
+ return [animations_ objectForKey:animationName];
+}
+
+#pragma mark CCSprite - CocosNodeTexture protocol
+
+-(void) updateBlendFunc
+{
+ NSAssert( ! usesBatchNode_, @"CCSprite: updateBlendFunc doesn't work when the sprite is rendered using a CCSpriteBatchNode");
+
+ // it's possible to have an untextured sprite
+ if( !texture_ || ! [texture_ hasPremultipliedAlpha] ) {
+ blendFunc_.src = GL_SRC_ALPHA;
+ blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA;
+ [self setOpacityModifyRGB:NO];
+ } else {
+ blendFunc_.src = CC_BLEND_SRC;
+ blendFunc_.dst = CC_BLEND_DST;
+ [self setOpacityModifyRGB:YES];
+ }
+}
+
+-(void) setTexture:(CCTexture2D*)texture
+{
+ NSAssert( ! usesBatchNode_, @"CCSprite: setTexture doesn't work when the sprite is rendered using a CCSpriteBatchNode");
+
+ // accept texture==nil as argument
+ NSAssert( !texture || [texture isKindOfClass:[CCTexture2D class]], @"setTexture expects a CCTexture2D. Invalid argument");
+
+ [texture_ release];
+ texture_ = [texture retain];
+
+ [self updateBlendFunc];
+}
+
+-(CCTexture2D*) texture
+{
+ return texture_;
+}
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (C) 2009 Matt Oswald
+ *
+ * Copyright (c) 2009-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCNode.h"
+#import "CCProtocols.h"
+#import "CCTextureAtlas.h"
+#import "ccMacros.h"
+
+#pragma mark CCSpriteBatchNode
+
+@class CCSprite;
+
+/** CCSpriteBatchNode is like a batch node: if it contains children, it will draw them in 1 single OpenGL call
+ * (often known as "batch draw").
+ *
+ * A CCSpriteBatchNode can reference one and only one texture (one image file, one texture atlas).
+ * Only the CCSprites that are contained in that texture can be added to the CCSpriteBatchNode.
+ * All CCSprites added to a CCSpriteBatchNode are drawn in one OpenGL ES draw call.
+ * If the CCSprites are not added to a CCSpriteBatchNode then an OpenGL ES draw call will be needed for each one, which is less efficient.
+ *
+ *
+ * Limitations:
+ * - The only object that is accepted as child (or grandchild, grand-grandchild, etc...) is CCSprite or any subclass of CCSprite. eg: particles, labels and layer can't be added to a CCSpriteBatchNode.
+ * - Either all its children are Aliased or Antialiased. It can't be a mix. This is because "alias" is a property of the texture, and all the sprites share the same texture.
+ *
+ * @since v0.7.1
+ */
+@interface CCSpriteBatchNode : CCNode <CCTextureProtocol>
+{
+ CCTextureAtlas *textureAtlas_;
+ ccBlendFunc blendFunc_;
+
+ // all descendants: chlidren, gran children, etc...
+ CCArray *descendants_;
+}
+
+/** returns the TextureAtlas that is used */
+@property (nonatomic,readwrite,retain) CCTextureAtlas * textureAtlas;
+
+/** conforms to CCTextureProtocol protocol */
+@property (nonatomic,readwrite) ccBlendFunc blendFunc;
+
+/** descendants (children, gran children, etc) */
+@property (nonatomic,readonly) CCArray *descendants;
+
+/** creates a CCSpriteBatchNode with a texture2d and a default capacity of 29 children.
+ The capacity will be increased in 33% in runtime if it run out of space.
+ */
++(id)batchNodeWithTexture:(CCTexture2D *)tex;
++(id)spriteSheetWithTexture:(CCTexture2D *)tex DEPRECATED_ATTRIBUTE;
+
+/** creates a CCSpriteBatchNode with a texture2d and capacity of children.
+ The capacity will be increased in 33% in runtime if it run out of space.
+ */
++(id)batchNodeWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity;
++(id)spriteSheetWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity DEPRECATED_ATTRIBUTE;
+
+/** creates a CCSpriteBatchNode with a file image (.png, .jpeg, .pvr, etc) with a default capacity of 29 children.
+ The capacity will be increased in 33% in runtime if it run out of space.
+ The file will be loaded using the TextureMgr.
+ */
++(id)batchNodeWithFile:(NSString*) fileImage;
++(id)spriteSheetWithFile:(NSString*) fileImage DEPRECATED_ATTRIBUTE;
+
+/** creates a CCSpriteBatchNode with a file image (.png, .jpeg, .pvr, etc) and capacity of children.
+ The capacity will be increased in 33% in runtime if it run out of space.
+ The file will be loaded using the TextureMgr.
+*/
++(id)batchNodeWithFile:(NSString*)fileImage capacity:(NSUInteger)capacity;
++(id)spriteSheetWithFile:(NSString*)fileImage capacity:(NSUInteger)capacity DEPRECATED_ATTRIBUTE;
+
+/** initializes a CCSpriteBatchNode with a texture2d and capacity of children.
+ The capacity will be increased in 33% in runtime if it run out of space.
+ */
+-(id)initWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity;
+/** initializes a CCSpriteBatchNode with a file image (.png, .jpeg, .pvr, etc) and a capacity of children.
+ The capacity will be increased in 33% in runtime if it run out of space.
+ The file will be loaded using the TextureMgr.
+ */
+-(id)initWithFile:(NSString*)fileImage capacity:(NSUInteger)capacity;
+
+-(void) increaseAtlasCapacity;
+
+/** creates an sprite with a rect in the CCSpriteBatchNode.
+ It's the same as:
+ - create an standard CCSsprite
+ - set the usingSpriteSheet = YES
+ - set the textureAtlas to the same texture Atlas as the CCSpriteBatchNode
+ @deprecated Use [CCSprite spriteWithBatchNode:rect:] instead;
+ */
+-(CCSprite*) createSpriteWithRect:(CGRect)rect DEPRECATED_ATTRIBUTE;
+
+/** initializes a previously created sprite with a rect. This sprite will have the same texture as the CCSpriteBatchNode.
+ It's the same as:
+ - initialize an standard CCSsprite
+ - set the usingBatchNode = YES
+ - set the textureAtlas to the same texture Atlas as the CCSpriteBatchNode
+ @since v0.99.0
+ @deprecated Use [CCSprite initWithBatchNode:rect:] instead;
+*/
+-(void) initSprite:(CCSprite*)sprite rect:(CGRect)rect DEPRECATED_ATTRIBUTE;
+
+/** removes a child given a certain index. It will also cleanup the running actions depending on the cleanup parameter.
+ @warning Removing a child from a CCSpriteBatchNode is very slow
+ */
+-(void)removeChildAtIndex:(NSUInteger)index cleanup:(BOOL)doCleanup;
+
+/** removes a child given a reference. It will also cleanup the running actions depending on the cleanup parameter.
+ @warning Removing a child from a CCSpriteBatchNode is very slow
+ */
+-(void)removeChild: (CCSprite *)sprite cleanup:(BOOL)doCleanup;
+
+-(void) insertChild:(CCSprite*)child inAtlasAtIndex:(NSUInteger)index;
+-(void) removeSpriteFromAtlas:(CCSprite*)sprite;
+
+-(NSUInteger) rebuildIndexInOrder:(CCSprite*)parent atlasIndex:(NSUInteger)index;
+-(NSUInteger) atlasIndexForChild:(CCSprite*)sprite atZ:(NSInteger)z;
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (C) 2009 Matt Oswald
+ *
+ * Copyright (c) 2009-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "ccConfig.h"
+#import "CCSprite.h"
+#import "CCSpriteBatchNode.h"
+#import "CCGrid.h"
+#import "CCDrawingPrimitives.h"
+#import "CCTextureCache.h"
+#import "Support/CGPointExtension.h"
+
+const NSUInteger defaultCapacity = 29;
+
+#pragma mark -
+#pragma mark CCSpriteBatchNode
+
+static SEL selUpdate = NULL;
+
+@interface CCSpriteBatchNode (private)
+-(void) updateBlendFunc;
+@end
+
+@implementation CCSpriteBatchNode
+
+@synthesize textureAtlas = textureAtlas_;
+@synthesize blendFunc = blendFunc_;
+@synthesize descendants = descendants_;
+
+
++(void) initialize
+{
+ if ( self == [CCSpriteBatchNode class] ) {
+ selUpdate = @selector(updateTransform);
+ }
+}
+/*
+ * creation with CCTexture2D
+ */
++(id)batchNodeWithTexture:(CCTexture2D *)tex
+{
+ return [[[self alloc] initWithTexture:tex capacity:defaultCapacity] autorelease];
+}
++(id)spriteSheetWithTexture:(CCTexture2D *)tex // XXX DEPRECATED
+{
+ return [self batchNodeWithTexture:tex];
+}
+
++(id)batchNodeWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity
+{
+ return [[[self alloc] initWithTexture:tex capacity:capacity] autorelease];
+}
++(id)spriteSheetWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity // XXX DEPRECATED
+{
+ return [self batchNodeWithTexture:tex capacity:capacity];
+}
+
+/*
+ * creation with File Image
+ */
++(id)batchNodeWithFile:(NSString*)fileImage capacity:(NSUInteger)capacity
+{
+ return [[[self alloc] initWithFile:fileImage capacity:capacity] autorelease];
+}
++(id)spriteSheetWithFile:(NSString*)fileImage capacity:(NSUInteger)capacity // XXX DEPRECATED
+{
+ return [self batchNodeWithFile:fileImage capacity:capacity];
+}
+
++(id)batchNodeWithFile:(NSString*) imageFile
+{
+ return [[[self alloc] initWithFile:imageFile capacity:defaultCapacity] autorelease];
+}
++(id)spriteSheetWithFile:(NSString*) imageFile // XXX DEPRECATED
+{
+ return [self batchNodeWithFile:imageFile];
+}
+
+
+/*
+ * init with CCTexture2D
+ */
+-(id)initWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity
+{
+ if( (self=[super init])) {
+
+ blendFunc_.src = CC_BLEND_SRC;
+ blendFunc_.dst = CC_BLEND_DST;
+ textureAtlas_ = [[CCTextureAtlas alloc] initWithTexture:tex capacity:capacity];
+
+ [self updateBlendFunc];
+
+ // no lazy alloc in this node
+ children_ = [[CCArray alloc] initWithCapacity:capacity];
+ descendants_ = [[CCArray alloc] initWithCapacity:capacity];
+ }
+
+ return self;
+}
+
+/*
+ * init with FileImage
+ */
+-(id)initWithFile:(NSString *)fileImage capacity:(NSUInteger)capacity
+{
+ CCTexture2D *tex = [[CCTextureCache sharedTextureCache] addImage:fileImage];
+ return [self initWithTexture:tex capacity:capacity];
+}
+
+- (NSString*) description
+{
+ return [NSString stringWithFormat:@"<%@ = %08X | Tag = %i>", [self class], self, tag_ ];
+}
+
+-(void)dealloc
+{
+ [textureAtlas_ release];
+ [descendants_ release];
+
+ [super dealloc];
+}
+
+#pragma mark CCSpriteBatchNode - composition
+
+// override visit.
+// Don't call visit on it's children
+-(void) visit
+{
+
+ // CAREFUL:
+ // This visit is almost identical to CocosNode#visit
+ // with the exception that it doesn't call visit on it's children
+ //
+ // The alternative is to have a void CCSprite#visit, but
+ // although this is less mantainable, is faster
+ //
+ if (!visible_)
+ return;
+
+ glPushMatrix();
+
+ if ( grid_ && grid_.active) {
+ [grid_ beforeDraw];
+ [self transformAncestors];
+ }
+
+ [self transform];
+
+ [self draw];
+
+ if ( grid_ && grid_.active)
+ [grid_ afterDraw:self];
+
+ glPopMatrix();
+}
+
+// XXX deprecated
+-(CCSprite*) createSpriteWithRect:(CGRect)rect
+{
+ CCSprite *sprite = [CCSprite spriteWithTexture:textureAtlas_.texture rect:rect];
+ [sprite useBatchNode:self];
+
+ return sprite;
+}
+
+// XXX deprecated
+-(void) initSprite:(CCSprite*)sprite rect:(CGRect)rect
+{
+ [sprite initWithTexture:textureAtlas_.texture rect:rect];
+ [sprite useBatchNode:self];
+}
+
+// override addChild:
+-(void) addChild:(CCSprite*)child z:(NSInteger)z tag:(NSInteger) aTag
+{
+ NSAssert( child != nil, @"Argument must be non-nil");
+ NSAssert( [child isKindOfClass:[CCSprite class]], @"CCSpriteBatchNode only supports CCSprites as children");
+ NSAssert( child.texture.name == textureAtlas_.texture.name, @"CCSprite is not using the same texture id");
+
+ [super addChild:child z:z tag:aTag];
+
+ NSUInteger index = [self atlasIndexForChild:child atZ:z];
+ [self insertChild:child inAtlasAtIndex:index];
+}
+
+// override reorderChild
+-(void) reorderChild:(CCSprite*)child z:(NSInteger)z
+{
+ NSAssert( child != nil, @"Child must be non-nil");
+ NSAssert( [children_ containsObject:child], @"Child doesn't belong to Sprite" );
+
+ if( z == child.zOrder )
+ return;
+
+ // XXX: Instead of removing/adding, it is more efficient to reorder manually
+ [child retain];
+ [self removeChild:child cleanup:NO];
+ [self addChild:child z:z];
+ [child release];
+}
+
+// override removeChild:
+-(void)removeChild: (CCSprite *)sprite cleanup:(BOOL)doCleanup
+{
+ // explicit nil handling
+ if (sprite == nil)
+ return;
+
+ NSAssert([children_ containsObject:sprite], @"CCSpriteBatchNode doesn't contain the sprite. Can't remove it");
+
+ // cleanup before removing
+ [self removeSpriteFromAtlas:sprite];
+
+ [super removeChild:sprite cleanup:doCleanup];
+}
+
+-(void)removeChildAtIndex:(NSUInteger)index cleanup:(BOOL)doCleanup
+{
+ [self removeChild:(CCSprite *)[children_ objectAtIndex:index] cleanup:doCleanup];
+}
+
+-(void)removeAllChildrenWithCleanup:(BOOL)doCleanup
+{
+ // Invalidate atlas index. issue #569
+ [children_ makeObjectsPerformSelector:@selector(useSelfRender)];
+
+ [super removeAllChildrenWithCleanup:doCleanup];
+
+ [descendants_ removeAllObjects];
+ [textureAtlas_ removeAllQuads];
+}
+
+#pragma mark CCSpriteBatchNode - draw
+-(void) draw
+{
+ // Optimization: Fast Dispatch
+ if( textureAtlas_.totalQuads == 0 )
+ return;
+
+ CCSprite *child;
+ ccArray *array = descendants_->data;
+
+ NSUInteger i = array->num;
+ id *arr = array->arr;
+
+ if( i > 0 ) {
+
+ while (i-- > 0) {
+ child = *arr++;
+
+ // fast dispatch
+ child->updateMethod(child, selUpdate);
+
+#if CC_SPRITEBATCHNODE_DEBUG_DRAW
+ //Issue #528
+ CGRect rect = [child boundingBox];
+ CGPoint vertices[4]={
+ ccp(rect.origin.x,rect.origin.y),
+ ccp(rect.origin.x+rect.size.width,rect.origin.y),
+ ccp(rect.origin.x+rect.size.width,rect.origin.y+rect.size.height),
+ ccp(rect.origin.x,rect.origin.y+rect.size.height),
+ };
+ ccDrawPoly(vertices, 4, YES);
+#endif // CC_SPRITEBATCHNODE_DEBUG_DRAW
+ }
+ }
+
+ // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Unneeded states: -
+
+ BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST;
+ if( newBlend )
+ glBlendFunc( blendFunc_.src, blendFunc_.dst );
+
+ [textureAtlas_ drawQuads];
+ if( newBlend )
+ glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
+}
+
+#pragma mark CCSpriteBatchNode - private
+-(void) increaseAtlasCapacity
+{
+ // if we're going beyond the current TextureAtlas's capacity,
+ // all the previously initialized sprites will need to redo their texture coords
+ // this is likely computationally expensive
+ NSUInteger quantity = (textureAtlas_.capacity + 1) * 4 / 3;
+
+ CCLOG(@"cocos2d: CCSpriteBatchNode: resizing TextureAtlas capacity from [%lu] to [%lu].",
+ (long)textureAtlas_.capacity,
+ (long)quantity);
+
+
+ if( ! [textureAtlas_ resizeCapacity:quantity] ) {
+ // serious problems
+ CCLOG(@"cocos2d: WARNING: Not enough memory to resize the atlas");
+ NSAssert(NO,@"XXX: SpriteSheet#increaseAtlasCapacity SHALL handle this assert");
+ }
+}
+
+
+#pragma mark CCSpriteBatchNode - Atlas Index Stuff
+
+-(NSUInteger) rebuildIndexInOrder:(CCSprite*)node atlasIndex:(NSUInteger)index
+{
+ CCSprite *sprite;
+ CCARRAY_FOREACH(node.children, sprite){
+ if( sprite.zOrder < 0 )
+ index = [self rebuildIndexInOrder:sprite atlasIndex:index];
+ }
+
+ // ignore self (batch node)
+ if( ! [node isEqual:self]) {
+ node.atlasIndex = index;
+ index++;
+ }
+
+ CCARRAY_FOREACH(node.children, sprite){
+ if( sprite.zOrder >= 0 )
+ index = [self rebuildIndexInOrder:sprite atlasIndex:index];
+ }
+
+ return index;
+}
+
+-(NSUInteger) highestAtlasIndexInChild:(CCSprite*)sprite
+{
+ CCArray *array = [sprite children];
+ NSUInteger count = [array count];
+ if( count == 0 )
+ return sprite.atlasIndex;
+ else
+ return [self highestAtlasIndexInChild:[array lastObject]];
+}
+
+-(NSUInteger) lowestAtlasIndexInChild:(CCSprite*)sprite
+{
+ CCArray *array = [sprite children];
+ NSUInteger count = [array count];
+ if( count == 0 )
+ return sprite.atlasIndex;
+ else
+ return [self lowestAtlasIndexInChild:[array objectAtIndex:0] ];
+}
+
+
+-(NSUInteger)atlasIndexForChild:(CCSprite*)sprite atZ:(NSInteger)z
+{
+ CCArray *brothers = [[sprite parent] children];
+ NSUInteger childIndex = [brothers indexOfObject:sprite];
+
+ // ignore parent Z if parent is batchnode
+ BOOL ignoreParent = ( sprite.parent == self );
+ CCSprite *previous = nil;
+ if( childIndex > 0 )
+ previous = [brothers objectAtIndex:childIndex-1];
+
+ // first child of the sprite sheet
+ if( ignoreParent ) {
+ if( childIndex == 0 )
+ return 0;
+ // else
+ return [self highestAtlasIndexInChild: previous] + 1;
+ }
+
+ // parent is a CCSprite, so, it must be taken into account
+
+ // first child of an CCSprite ?
+ if( childIndex == 0 )
+ {
+ CCSprite *p = (CCSprite*) sprite.parent;
+
+ // less than parent and brothers
+ if( z < 0 )
+ return p.atlasIndex;
+ else
+ return p.atlasIndex+1;
+
+ } else {
+ // previous & sprite belong to the same branch
+ if( ( previous.zOrder < 0 && z < 0 )|| (previous.zOrder >= 0 && z >= 0) )
+ return [self highestAtlasIndexInChild:previous] + 1;
+
+ // else (previous < 0 and sprite >= 0 )
+ CCSprite *p = (CCSprite*) sprite.parent;
+ return p.atlasIndex + 1;
+ }
+
+ NSAssert( NO, @"Should not happen. Error calculating Z on Batch Node");
+ return 0;
+}
+
+#pragma mark CCSpriteBatchNode - add / remove / reorder helper methods
+// add child helper
+-(void) insertChild:(CCSprite*)sprite inAtlasAtIndex:(NSUInteger)index
+{
+ [sprite useBatchNode:self];
+ [sprite setAtlasIndex:index];
+ [sprite setDirty: YES];
+
+ if(textureAtlas_.totalQuads == textureAtlas_.capacity)
+ [self increaseAtlasCapacity];
+
+ ccV3F_C4B_T2F_Quad quad = [sprite quad];
+ [textureAtlas_ insertQuad:&quad atIndex:index];
+
+ ccArray *descendantsData = descendants_->data;
+
+ ccArrayInsertObjectAtIndex(descendantsData, sprite, index);
+
+ // update indices
+ NSUInteger i = index+1;
+ CCSprite *child;
+ for(; i<descendantsData->num; i++){
+ child = descendantsData->arr[i];
+ child.atlasIndex = child.atlasIndex + 1;
+ }
+
+ // add children recursively
+ CCARRAY_FOREACH(sprite.children, child){
+ NSUInteger idx = [self atlasIndexForChild:child atZ: child.zOrder];
+ [self insertChild:child inAtlasAtIndex:idx];
+ }
+}
+
+// remove child helper
+-(void) removeSpriteFromAtlas:(CCSprite*)sprite
+{
+ // remove from TextureAtlas
+ [textureAtlas_ removeQuadAtIndex:sprite.atlasIndex];
+
+ // Cleanup sprite. It might be reused (issue #569)
+ [sprite useSelfRender];
+
+ ccArray *descendantsData = descendants_->data;
+ NSUInteger index = ccArrayGetIndexOfObject(descendantsData, sprite);
+ if( index != NSNotFound ) {
+ ccArrayRemoveObjectAtIndex(descendantsData, index);
+
+ // update all sprites beyond this one
+ NSUInteger count = descendantsData->num;
+
+ for(; index < count; index++)
+ {
+ CCSprite *s = descendantsData->arr[index];
+ s.atlasIndex = s.atlasIndex - 1;
+ }
+ }
+
+ // remove children recursively
+ CCSprite *child;
+ CCARRAY_FOREACH(sprite.children, child)
+ [self removeSpriteFromAtlas:child];
+}
+
+#pragma mark CCSpriteBatchNode - CocosNodeTexture protocol
+
+-(void) updateBlendFunc
+{
+ if( ! [textureAtlas_.texture hasPremultipliedAlpha] ) {
+ blendFunc_.src = GL_SRC_ALPHA;
+ blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA;
+ }
+}
+
+-(void) setTexture:(CCTexture2D*)texture
+{
+ textureAtlas_.texture = texture;
+ [self updateBlendFunc];
+}
+
+-(CCTexture2D*) texture
+{
+ return textureAtlas_.texture;
+}
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2011 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import <Foundation/Foundation.h>
+#import "CCNode.h"
+#import "CCProtocols.h"
+
+/** A CCSpriteFrame has:
+ - texture: A CCTexture2D that will be used by the CCSprite
+ - rectangle: A rectangle of the texture
+
+
+ You can modify the frame of a CCSprite by doing:
+
+ CCSpriteFrame *frame = [CCSpriteFrame frameWithTexture:texture rect:rect offset:offset];
+ [sprite setDisplayFrame:frame];
+ */
+@interface CCSpriteFrame : NSObject <NSCopying>
+{
+ CGRect rect_;
+ CGRect rectInPixels_;
+ BOOL rotated_;
+ CGPoint offsetInPixels_;
+ CGSize originalSizeInPixels_;
+ CCTexture2D *texture_;
+}
+/** rect of the frame in points. If it is updated, then rectInPixels will be updated too. */
+@property (nonatomic,readwrite) CGRect rect;
+
+/** rect of the frame in pixels. If it is updated, then rect (points) will be udpated too. */
+@property (nonatomic,readwrite) CGRect rectInPixels;
+
+/** whether or not the rect of the frame is rotated ( x = x+width, y = y+height, width = height, height = width ) */
+@property (nonatomic,readwrite) BOOL rotated;
+
+/** offset of the frame in pixels */
+@property (nonatomic,readwrite) CGPoint offsetInPixels;
+
+/** original size of the trimmed image in pixels */
+@property (nonatomic,readwrite) CGSize originalSizeInPixels;
+
+/** texture of the frame */
+@property (nonatomic, retain, readwrite) CCTexture2D *texture;
+
+/** Create a CCSpriteFrame with a texture, rect in points.
+ It is assumed that the frame was not trimmed.
+ */
++(id) frameWithTexture:(CCTexture2D*)texture rect:(CGRect)rect;
+
+/** Create a CCSpriteFrame with a texture, rect, rotated, offset and originalSize in pixels.
+ The originalSize is the size in points of the frame before being trimmed.
+ */
++(id) frameWithTexture:(CCTexture2D*)texture rectInPixels:(CGRect)rect rotated:(BOOL)rotated offset:(CGPoint)offset originalSize:(CGSize)originalSize;
+
+
+/** Initializes a CCSpriteFrame with a texture, rect in points;
+ It is assumed that the frame was not trimmed.
+ */
+-(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect;
+
+/** Initializes a CCSpriteFrame with a texture, rect, rotated, offset and originalSize in pixels.
+ The originalSize is the size in points of the frame before being trimmed.
+ */
+-(id) initWithTexture:(CCTexture2D*)texture rectInPixels:(CGRect)rect rotated:(BOOL)rotated offset:(CGPoint)offset originalSize:(CGSize)originalSize;
+
+@end
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2011 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCTextureCache.h"
+#import "CCSpriteFrame.h"
+#import "ccMacros.h"
+
+@implementation CCSpriteFrame
+@synthesize rotated = rotated_, offsetInPixels = offsetInPixels_, texture = texture_;
+@synthesize originalSizeInPixels=originalSizeInPixels_;
+
++(id) frameWithTexture:(CCTexture2D*)texture rect:(CGRect)rect
+{
+ return [[[self alloc] initWithTexture:texture rect:rect] autorelease];
+}
+
++(id) frameWithTexture:(CCTexture2D*)texture rectInPixels:(CGRect)rect rotated:(BOOL)rotated offset:(CGPoint)offset originalSize:(CGSize)originalSize
+{
+ return [[[self alloc] initWithTexture:texture rectInPixels:rect rotated:rotated offset:offset originalSize:originalSize] autorelease];
+}
+
+-(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect
+{
+ CGRect rectInPixels = CC_RECT_POINTS_TO_PIXELS( rect );
+ return [self initWithTexture:texture rectInPixels:rectInPixels rotated:NO offset:CGPointZero originalSize:rectInPixels.size];
+}
+
+-(id) initWithTexture:(CCTexture2D*)texture rectInPixels:(CGRect)rect rotated:(BOOL)rotated offset:(CGPoint)offset originalSize:(CGSize)originalSize
+{
+ if( (self=[super init]) ) {
+ self.texture = texture;
+ rectInPixels_ = rect;
+ rect_ = CC_RECT_PIXELS_TO_POINTS( rect );
+ rotated_ = rotated;
+ offsetInPixels_ = offset;
+ originalSizeInPixels_ = originalSize;
+ }
+ return self;
+}
+
+- (NSString*) description
+{
+ return [NSString stringWithFormat:@"<%@ = %08X | TextureName=%d, Rect = (%.2f,%.2f,%.2f,%.2f)> rotated:%d", [self class], self,
+ texture_.name,
+ rect_.origin.x,
+ rect_.origin.y,
+ rect_.size.width,
+ rect_.size.height,
+ rotated_
+ ];
+}
+
+- (void) dealloc
+{
+ CCLOGINFO( @"cocos2d: deallocing %@",self);
+ [texture_ release];
+ [super dealloc];
+}
+
+-(id) copyWithZone: (NSZone*) zone
+{
+ CCSpriteFrame *copy = [[[self class] allocWithZone: zone] initWithTexture:texture_ rectInPixels:rectInPixels_ rotated:rotated_ offset:offsetInPixels_ originalSize:originalSizeInPixels_];
+ return copy;
+}
+
+-(CGRect) rect
+{
+ return rect_;
+}
+
+-(CGRect) rectInPixels
+{
+ return rectInPixels_;
+}
+
+-(void) setRect:(CGRect)rect
+{
+ rect_ = rect;
+ rectInPixels_ = CC_RECT_POINTS_TO_PIXELS( rect_ );
+}
+
+-(void) setRectInPixels:(CGRect)rectInPixels
+{
+ rectInPixels_ = rectInPixels;
+ rect_ = CC_RECT_PIXELS_TO_POINTS(rectInPixels);
+}
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Jason Booth
+ *
+ * Copyright (c) 2009 Robert J Payne
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+/*
+ * To create sprite frames and texture atlas, use this tool:
+ * http://zwoptex.zwopple.com/
+ */
+
+#import <Foundation/Foundation.h>
+
+#import "CCSpriteFrame.h"
+#import "CCTexture2D.h"
+
+@class CCSprite;
+
+/** Singleton that handles the loading of the sprite frames.
+ It saves in a cache the sprite frames.
+ @since v0.9
+ */
+@interface CCSpriteFrameCache : NSObject
+{
+ NSMutableDictionary *spriteFrames_;
+ NSMutableDictionary *spriteFramesAliases_;
+}
+
+/** Retruns ths shared instance of the Sprite Frame cache */
++ (CCSpriteFrameCache *) sharedSpriteFrameCache;
+
+/** Purges the cache. It releases all the Sprite Frames and the retained instance.
+ */
++(void)purgeSharedSpriteFrameCache;
+
+
+/** Adds multiple Sprite Frames with a dictionary. The texture will be associated with the created sprite frames.
+ */
+-(void) addSpriteFramesWithDictionary:(NSDictionary*)dictionary texture:(CCTexture2D*)texture;
+
+/** Adds multiple Sprite Frames from a plist file.
+ * A texture will be loaded automatically. The texture name will composed by replacing the .plist suffix with .png
+ * If you want to use another texture, you should use the addSpriteFramesWithFile:texture method.
+ */
+-(void) addSpriteFramesWithFile:(NSString*)plist;
+
+/** Adds multiple Sprite Frames from a plist file. The texture will be associated with the created sprite frames.
+ */
+-(void) addSpriteFramesWithFile:(NSString*)plist texture:(CCTexture2D*)texture;
+
+/** Adds multiple Sprite Frames from a plist file. The texture will be associated with the created sprite frames.
+ @since v0.99.5
+ */
+-(void) addSpriteFramesWithFile:(NSString*)plist textureFile:(NSString*)textureFileName;
+
+/** Adds an sprite frame with a given name.
+ If the name already exists, then the contents of the old name will be replaced with the new one.
+ */
+-(void) addSpriteFrame:(CCSpriteFrame*)frame name:(NSString*)frameName;
+
+
+/** Purges the dictionary of loaded sprite frames.
+ * Call this method if you receive the "Memory Warning".
+ * In the short term: it will free some resources preventing your app from being killed.
+ * In the medium term: it will allocate more resources.
+ * In the long term: it will be the same.
+ */
+-(void) removeSpriteFrames;
+
+/** Removes unused sprite frames.
+ * Sprite Frames that have a retain count of 1 will be deleted.
+ * It is convinient to call this method after when starting a new Scene.
+ */
+-(void) removeUnusedSpriteFrames;
+
+/** Deletes an sprite frame from the sprite frame cache.
+ */
+-(void) removeSpriteFrameByName:(NSString*)name;
+
+/** Removes multiple Sprite Frames from a plist file.
+* Sprite Frames stored in this file will be removed.
+* It is convinient to call this method when a specific texture needs to be removed.
+* @since v0.99.5
+*/
+- (void) removeSpriteFramesFromFile:(NSString*) plist;
+
+/** Removes multiple Sprite Frames from NSDictionary.
+ * @since v0.99.5
+ */
+- (void) removeSpriteFramesFromDictionary:(NSDictionary*) dictionary;
+
+/** Removes all Sprite Frames associated with the specified textures.
+ * It is convinient to call this method when a specific texture needs to be removed.
+ * @since v0.995.
+ */
+- (void) removeSpriteFramesFromTexture:(CCTexture2D*) texture;
+
+/** Returns an Sprite Frame that was previously added.
+ If the name is not found it will return nil.
+ You should retain the returned copy if you are going to use it.
+ */
+-(CCSpriteFrame*) spriteFrameByName:(NSString*)name;
+
+/** Creates an sprite with the name of an sprite frame.
+ The created sprite will contain the texture, rect and offset of the sprite frame.
+ It returns an autorelease object.
+ @deprecated use [CCSprite spriteWithSpriteFrameName:name]. This method will be removed on final v0.9
+ */
+-(CCSprite*) createSpriteWithFrameName:(NSString*)name DEPRECATED_ATTRIBUTE;
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Jason Booth
+ *
+ * Copyright (c) 2009 Robert J Payne
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+/*
+ * To create sprite frames and texture atlas, use this tool:
+ * http://zwoptex.zwopple.com/
+ */
+
+#import "Platforms/CCNS.h"
+#import "ccMacros.h"
+#import "CCTextureCache.h"
+#import "CCSpriteFrameCache.h"
+#import "CCSpriteFrame.h"
+#import "CCSprite.h"
+#import "Support/CCFileUtils.h"
+
+
+@implementation CCSpriteFrameCache
+
+#pragma mark CCSpriteFrameCache - Alloc, Init & Dealloc
+
+static CCSpriteFrameCache *sharedSpriteFrameCache_=nil;
+
++ (CCSpriteFrameCache *)sharedSpriteFrameCache
+{
+ if (!sharedSpriteFrameCache_)
+ sharedSpriteFrameCache_ = [[CCSpriteFrameCache alloc] init];
+
+ return sharedSpriteFrameCache_;
+}
+
++(id)alloc
+{
+ NSAssert(sharedSpriteFrameCache_ == nil, @"Attempted to allocate a second instance of a singleton.");
+ return [super alloc];
+}
+
++(void)purgeSharedSpriteFrameCache
+{
+ [sharedSpriteFrameCache_ release];
+ sharedSpriteFrameCache_ = nil;
+}
+
+-(id) init
+{
+ if( (self=[super init]) ) {
+ spriteFrames_ = [[NSMutableDictionary alloc] initWithCapacity: 100];
+ spriteFramesAliases_ = [[NSMutableDictionary alloc] initWithCapacity:10];
+ }
+
+ return self;
+}
+
+- (NSString*) description
+{
+ return [NSString stringWithFormat:@"<%@ = %08X | num of sprite frames = %i>", [self class], self, [spriteFrames_ count]];
+}
+
+-(void) dealloc
+{
+ CCLOGINFO(@"cocos2d: deallocing %@", self);
+
+ [spriteFrames_ release];
+ [spriteFramesAliases_ release];
+ [super dealloc];
+}
+
+#pragma mark CCSpriteFrameCache - loading sprite frames
+
+-(void) addSpriteFramesWithDictionary:(NSDictionary*)dictionary texture:(CCTexture2D*)texture
+{
+ /*
+ Supported Zwoptex Formats:
+ ZWTCoordinatesFormatOptionXMLLegacy = 0, // Flash Version
+ ZWTCoordinatesFormatOptionXML1_0 = 1, // Desktop Version 0.0 - 0.4b
+ ZWTCoordinatesFormatOptionXML1_1 = 2, // Desktop Version 1.0.0 - 1.0.1
+ ZWTCoordinatesFormatOptionXML1_2 = 3, // Desktop Version 1.0.2+
+ */
+ NSDictionary *metadataDict = [dictionary objectForKey:@"metadata"];
+ NSDictionary *framesDict = [dictionary objectForKey:@"frames"];
+
+ int format = 0;
+
+ // get the format
+ if(metadataDict != nil)
+ format = [[metadataDict objectForKey:@"format"] intValue];
+
+ // check the format
+ NSAssert( format >= 0 && format <= 3, @"cocos2d: WARNING: format is not supported for CCSpriteFrameCache addSpriteFramesWithDictionary:texture:");
+
+
+ // add real frames
+ for(NSString *frameDictKey in framesDict) {
+ NSDictionary *frameDict = [framesDict objectForKey:frameDictKey];
+ CCSpriteFrame *spriteFrame;
+ if(format == 0) {
+ float x = [[frameDict objectForKey:@"x"] floatValue];
+ float y = [[frameDict objectForKey:@"y"] floatValue];
+ float w = [[frameDict objectForKey:@"width"] floatValue];
+ float h = [[frameDict objectForKey:@"height"] floatValue];
+ float ox = [[frameDict objectForKey:@"offsetX"] floatValue];
+ float oy = [[frameDict objectForKey:@"offsetY"] floatValue];
+ int ow = [[frameDict objectForKey:@"originalWidth"] intValue];
+ int oh = [[frameDict objectForKey:@"originalHeight"] intValue];
+ // check ow/oh
+ if(!ow || !oh)
+ CCLOG(@"cocos2d: WARNING: originalWidth/Height not found on the CCSpriteFrame. AnchorPoint won't work as expected. Regenerate the .plist");
+
+ // abs ow/oh
+ ow = abs(ow);
+ oh = abs(oh);
+ // create frame
+
+ spriteFrame = [[CCSpriteFrame alloc] initWithTexture:texture
+ rectInPixels:CGRectMake(x, y, w, h)
+ rotated:NO
+ offset:CGPointMake(ox, oy)
+ originalSize:CGSizeMake(ow, oh)];
+ } else if(format == 1 || format == 2) {
+ CGRect frame = CCRectFromString([frameDict objectForKey:@"frame"]);
+ BOOL rotated = NO;
+
+ // rotation
+ if(format == 2)
+ rotated = [[frameDict objectForKey:@"rotated"] boolValue];
+
+ CGPoint offset = CCPointFromString([frameDict objectForKey:@"offset"]);
+ CGSize sourceSize = CCSizeFromString([frameDict objectForKey:@"sourceSize"]);
+
+ // create frame
+ spriteFrame = [[CCSpriteFrame alloc] initWithTexture:texture
+ rectInPixels:frame
+ rotated:rotated
+ offset:offset
+ originalSize:sourceSize];
+ } else if(format == 3) {
+ // get values
+ CGSize spriteSize = CCSizeFromString([frameDict objectForKey:@"spriteSize"]);
+ CGPoint spriteOffset = CCPointFromString([frameDict objectForKey:@"spriteOffset"]);
+ CGSize spriteSourceSize = CCSizeFromString([frameDict objectForKey:@"spriteSourceSize"]);
+ CGRect textureRect = CCRectFromString([frameDict objectForKey:@"textureRect"]);
+ BOOL textureRotated = [[frameDict objectForKey:@"textureRotated"] boolValue];
+
+ // get aliases
+ NSArray *aliases = [frameDict objectForKey:@"aliases"];
+ for(NSString *alias in aliases) {
+ if( [spriteFramesAliases_ objectForKey:alias] )
+ CCLOG(@"cocos2d: WARNING: an alias with name %@ already exists",alias);
+
+ [spriteFramesAliases_ setObject:frameDictKey forKey:alias];
+ }
+
+ // create frame
+ spriteFrame = [[CCSpriteFrame alloc] initWithTexture:texture
+ rectInPixels:CGRectMake(textureRect.origin.x, textureRect.origin.y, spriteSize.width, spriteSize.height)
+ rotated:textureRotated
+ offset:spriteOffset
+ originalSize:spriteSourceSize];
+ }
+
+ // add sprite frame
+ [spriteFrames_ setObject:spriteFrame forKey:frameDictKey];
+ [spriteFrame release];
+ }
+}
+
+-(void) addSpriteFramesWithFile:(NSString*)plist texture:(CCTexture2D*)texture
+{
+ NSString *path = [CCFileUtils fullPathFromRelativePath:plist];
+ NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
+
+ return [self addSpriteFramesWithDictionary:dict texture:texture];
+}
+
+-(void) addSpriteFramesWithFile:(NSString*)plist textureFile:(NSString*)textureFileName
+{
+ NSAssert( textureFileName, @"Invalid texture file name");
+ CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage:textureFileName];
+
+ if( texture )
+ [self addSpriteFramesWithFile:plist texture:texture];
+ else
+ CCLOG(@"cocos2d: CCSpriteFrameCache: couldn't load texture file. File not found: %@", textureFileName);
+}
+
+-(void) addSpriteFramesWithFile:(NSString*)plist
+{
+ NSString *path = [CCFileUtils fullPathFromRelativePath:plist];
+ NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
+
+ NSString *texturePath = nil;
+ NSDictionary *metadataDict = [dict objectForKey:@"metadata"];
+ if( metadataDict )
+ // try to read texture file name from meta data
+ texturePath = [metadataDict objectForKey:@"textureFileName"];
+
+
+ if( texturePath )
+ {
+ // build texture path relative to plist file
+ NSString *textureBase = [plist stringByDeletingLastPathComponent];
+ texturePath = [textureBase stringByAppendingPathComponent:texturePath];
+ } else {
+ // build texture path by replacing file extension
+ texturePath = [plist stringByDeletingPathExtension];
+ texturePath = [texturePath stringByAppendingPathExtension:@"png"];
+
+ CCLOG(@"cocos2d: CCSpriteFrameCache: Trying to use file '%@' as texture", texturePath);
+ }
+
+ CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage:texturePath];
+
+ if( texture )
+ [self addSpriteFramesWithDictionary:dict texture:texture];
+
+ else
+ CCLOG(@"cocos2d: CCSpriteFrameCache: Couldn't load texture");
+}
+
+-(void) addSpriteFrame:(CCSpriteFrame*)frame name:(NSString*)frameName
+{
+ [spriteFrames_ setObject:frame forKey:frameName];
+}
+
+#pragma mark CCSpriteFrameCache - removing
+
+-(void) removeSpriteFrames
+{
+ [spriteFrames_ removeAllObjects];
+ [spriteFramesAliases_ removeAllObjects];
+}
+
+-(void) removeUnusedSpriteFrames
+{
+ NSArray *keys = [spriteFrames_ allKeys];
+ for( id key in keys ) {
+ id value = [spriteFrames_ objectForKey:key];
+ if( [value retainCount] == 1 ) {
+ CCLOG(@"cocos2d: CCSpriteFrameCache: removing unused frame: %@", key);
+ [spriteFrames_ removeObjectForKey:key];
+ }
+ }
+}
+
+-(void) removeSpriteFrameByName:(NSString*)name
+{
+ // explicit nil handling
+ if( ! name )
+ return;
+
+ // Is this an alias ?
+ NSString *key = [spriteFramesAliases_ objectForKey:name];
+
+ if( key ) {
+ [spriteFrames_ removeObjectForKey:key];
+ [spriteFramesAliases_ removeObjectForKey:name];
+
+ } else
+ [spriteFrames_ removeObjectForKey:name];
+}
+
+- (void) removeSpriteFramesFromFile:(NSString*) plist
+{
+ NSString *path = [CCFileUtils fullPathFromRelativePath:plist];
+ NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
+
+ [self removeSpriteFramesFromDictionary:dict];
+}
+
+- (void) removeSpriteFramesFromDictionary:(NSDictionary*) dictionary
+{
+ NSDictionary *framesDict = [dictionary objectForKey:@"frames"];
+ NSMutableArray *keysToRemove=[NSMutableArray array];
+
+ for(NSString *frameDictKey in framesDict)
+ {
+ if ([spriteFrames_ objectForKey:frameDictKey]!=nil)
+ [keysToRemove addObject:frameDictKey];
+ }
+ [spriteFrames_ removeObjectsForKeys:keysToRemove];
+}
+
+- (void) removeSpriteFramesFromTexture:(CCTexture2D*) texture
+{
+ NSMutableArray *keysToRemove=[NSMutableArray array];
+
+ for (NSString *spriteFrameKey in spriteFrames_)
+ {
+ if ([[spriteFrames_ valueForKey:spriteFrameKey] texture] == texture)
+ [keysToRemove addObject:spriteFrameKey];
+
+ }
+ [spriteFrames_ removeObjectsForKeys:keysToRemove];
+}
+
+#pragma mark CCSpriteFrameCache - getting
+
+-(CCSpriteFrame*) spriteFrameByName:(NSString*)name
+{
+ CCSpriteFrame *frame = [spriteFrames_ objectForKey:name];
+ if( ! frame ) {
+ // try alias dictionary
+ NSString *key = [spriteFramesAliases_ objectForKey:name];
+ frame = [spriteFrames_ objectForKey:key];
+
+ if( ! frame )
+ CCLOG(@"cocos2d: CCSpriteFrameCache: Frame '%@' not found", name);
+ }
+
+ return frame;
+}
+
+#pragma mark CCSpriteFrameCache - sprite creation
+
+-(CCSprite*) createSpriteWithFrameName:(NSString*)name
+{
+ CCSpriteFrame *frame = [spriteFrames_ objectForKey:name];
+ return [CCSprite spriteWithSpriteFrame:frame];
+}
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * TMX Tiled Map support:
+ * http://www.mapeditor.org
+ *
+ */
+
+
+#import "CCAtlasNode.h"
+#import "CCSpriteBatchNode.h"
+
+
+@class CCTMXMapInfo;
+@class CCTMXLayerInfo;
+@class CCTMXTilesetInfo;
+
+/** CCTMXLayer represents the TMX layer.
+
+ It is a subclass of CCSpriteSheet. By default the tiles are rendered using a CCTextureAtlas.
+ If you mofify a tile on runtime, then, that tile will become a CCSprite, otherwise no CCSprite objects are created.
+ The benefits of using CCSprite objects as tiles are:
+ - tiles (CCSprite) can be rotated/scaled/moved with a nice API
+
+ If the layer contains a property named "cc_vertexz" with an integer (in can be positive or negative),
+ then all the tiles belonging to the layer will use that value as their OpenGL vertex Z for depth.
+
+ On the other hand, if the "cc_vertexz" property has the "automatic" value, then the tiles will use an automatic vertex Z value.
+ Also before drawing the tiles, GL_ALPHA_TEST will be enabled, and disabled after drawing them. The used alpha func will be:
+
+ glAlphaFunc( GL_GREATER, value )
+
+ "value" by default is 0, but you can change it from Tiled by adding the "cc_alpha_func" property to the layer.
+ The value 0 should work for most cases, but if you have tiles that are semi-transparent, then you might want to use a differnt
+ value, like 0.5.
+
+ For further information, please see the programming guide:
+
+ http://www.cocos2d-iphone.org/wiki/doku.php/prog_guide:tiled_maps
+
+ @since v0.8.1
+ */
+@interface CCTMXLayer : CCSpriteBatchNode
+{
+ CCTMXTilesetInfo *tileset_;
+ NSString *layerName_;
+ CGSize layerSize_;
+ CGSize mapTileSize_;
+ uint32_t *tiles_; // GID are 32 bit
+ NSUInteger layerOrientation_;
+ NSMutableArray *properties_;
+
+ unsigned char opacity_; // TMX Layer supports opacity
+
+ NSUInteger minGID_;
+ NSUInteger maxGID_;
+
+ // Only used when vertexZ is used
+ NSInteger vertexZvalue_;
+ BOOL useAutomaticVertexZ_;
+ float alphaFuncValue_;
+
+ // used for optimization
+ CCSprite *reusedTile_;
+ ccCArray *atlasIndexArray_;
+}
+/** name of the layer */
+@property (nonatomic,readwrite,retain) NSString *layerName;
+/** size of the layer in tiles */
+@property (nonatomic,readwrite) CGSize layerSize;
+/** size of the map's tile (could be differnt from the tile's size) */
+@property (nonatomic,readwrite) CGSize mapTileSize;
+/** pointer to the map of tiles */
+@property (nonatomic,readwrite) uint32_t *tiles;
+/** Tilset information for the layer */
+@property (nonatomic,readwrite,retain) CCTMXTilesetInfo *tileset;
+/** Layer orientation, which is the same as the map orientation */
+@property (nonatomic,readwrite) NSUInteger layerOrientation;
+/** properties from the layer. They can be added using Tiled */
+@property (nonatomic,readwrite,retain) NSMutableArray *properties;
+
+/** creates a CCTMXLayer with an tileset info, a layer info and a map info */
++(id) layerWithTilesetInfo:(CCTMXTilesetInfo*)tilesetInfo layerInfo:(CCTMXLayerInfo*)layerInfo mapInfo:(CCTMXMapInfo*)mapInfo;
+/** initializes a CCTMXLayer with a tileset info, a layer info and a map info */
+-(id) initWithTilesetInfo:(CCTMXTilesetInfo*)tilesetInfo layerInfo:(CCTMXLayerInfo*)layerInfo mapInfo:(CCTMXMapInfo*)mapInfo;
+
+/** dealloc the map that contains the tile position from memory.
+ Unless you want to know at runtime the tiles positions, you can safely call this method.
+ If you are going to call [layer tileGIDAt:] then, don't release the map
+ */
+-(void) releaseMap;
+
+/** returns the tile (CCSprite) at a given a tile coordinate.
+ The returned CCSprite will be already added to the CCTMXLayer. Don't add it again.
+ The CCSprite can be treated like any other CCSprite: rotated, scaled, translated, opacity, color, etc.
+ You can remove either by calling:
+ - [layer removeChild:sprite cleanup:cleanup];
+ - or [layer removeTileAt:ccp(x,y)];
+ */
+-(CCSprite*) tileAt:(CGPoint)tileCoordinate;
+
+/** returns the tile gid at a given tile coordinate.
+ if it returns 0, it means that the tile is empty.
+ This method requires the the tile map has not been previously released (eg. don't call [layer releaseMap])
+ */
+-(uint32_t) tileGIDAt:(CGPoint)tileCoordinate;
+
+/** sets the tile gid (gid = tile global id) at a given tile coordinate.
+ The Tile GID can be obtained by using the method "tileGIDAt" or by using the TMX editor -> Tileset Mgr +1.
+ If a tile is already placed at that position, then it will be removed.
+ */
+-(void) setTileGID:(uint32_t)gid at:(CGPoint)tileCoordinate;
+
+/** removes a tile at given tile coordinate */
+-(void) removeTileAt:(CGPoint)tileCoordinate;
+
+/** returns the position in pixels of a given tile coordinate */
+-(CGPoint) positionAt:(CGPoint)tileCoordinate;
+
+/** return the value for the specific property name */
+-(id) propertyNamed:(NSString *)propertyName;
+
+/** Creates the tiles */
+-(void) setupTiles;
+
+/** CCTMXLayer doesn't support adding a CCSprite manually.
+ @warning addchild:z:tag: is not supported on CCTMXLayer. Instead of setTileGID:at:/tileAt:
+ */
+-(void) addChild: (CCNode*)node z:(NSInteger)z tag:(NSInteger)tag;
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * TMX Tiled Map support:
+ * http://www.mapeditor.org
+ *
+ */
+
+#import "CCTMXLayer.h"
+#import "CCTMXTiledMap.h"
+#import "CCTMXXMLParser.h"
+#import "CCSprite.h"
+#import "CCSpriteBatchNode.h"
+#import "CCTextureCache.h"
+#import "Support/CGPointExtension.h"
+
+#pragma mark -
+#pragma mark CCSpriteBatchNode Extension
+
+/* IMPORTANT XXX IMPORTNAT:
+ * These 2 methods can't be part of CCTMXLayer since they call [super add...], and CCSpriteSheet#add SHALL not be called
+ */
+@implementation CCSpriteBatchNode (TMXTiledMapExtension)
+
+/* Adds a quad into the texture atlas but it won't be added into the children array.
+ This method should be called only when you are dealing with very big AtlasSrite and when most of the CCSprite won't be updated.
+ For example: a tile map (CCTMXMap) or a label with lots of characgers (BitmapFontAtlas)
+ */
+-(void) addQuadFromSprite:(CCSprite*)sprite quadIndex:(NSUInteger)index
+{
+ NSAssert( sprite != nil, @"Argument must be non-nil");
+ NSAssert( [sprite isKindOfClass:[CCSprite class]], @"CCSpriteSheet only supports CCSprites as children");
+
+
+ while(index >= textureAtlas_.capacity || textureAtlas_.capacity == textureAtlas_.totalQuads )
+ [self increaseAtlasCapacity];
+
+ //
+ // update the quad directly. Don't add the sprite to the scene graph
+ //
+
+ [sprite useBatchNode:self];
+ [sprite setAtlasIndex:index];
+
+ ccV3F_C4B_T2F_Quad quad = [sprite quad];
+ [textureAtlas_ insertQuad:&quad atIndex:index];
+
+ // XXX: updateTransform will update the textureAtlas too using updateQuad.
+ // XXX: so, it should be AFTER the insertQuad
+ [sprite setDirty:YES];
+ [sprite updateTransform];
+}
+
+/* This is the opposite of "addQuadFromSprite.
+ It add the sprite to the children and descendants array, but it doesn't update add it to the texture atlas
+ */
+-(id) addSpriteWithoutQuad:(CCSprite*)child z:(NSUInteger)z tag:(NSInteger)aTag
+{
+ NSAssert( child != nil, @"Argument must be non-nil");
+ NSAssert( [child isKindOfClass:[CCSprite class]], @"CCSpriteSheet only supports CCSprites as children");
+
+ // quad index is Z
+ [child setAtlasIndex:z];
+
+ // XXX: optimize with a binary search
+ int i=0;
+ for( CCSprite *c in descendants_ ) {
+ if( c.atlasIndex >= z )
+ break;
+ i++;
+ }
+ [descendants_ insertObject:child atIndex:i];
+
+
+ // IMPORTANT: Call super, and not self. Avoid adding it to the texture atlas array
+ [super addChild:child z:z tag:aTag];
+ return self;
+}
+@end
+
+
+#pragma mark -
+#pragma mark CCTMXLayer
+
+@interface CCTMXLayer (Private)
+-(CGPoint) positionForIsoAt:(CGPoint)pos;
+-(CGPoint) positionForOrthoAt:(CGPoint)pos;
+-(CGPoint) positionForHexAt:(CGPoint)pos;
+
+-(CGPoint) calculateLayerOffset:(CGPoint)offset;
+
+/* optimization methos */
+-(CCSprite*) appendTileForGID:(uint32_t)gid at:(CGPoint)pos;
+-(CCSprite*) insertTileForGID:(uint32_t)gid at:(CGPoint)pos;
+-(CCSprite*) updateTileForGID:(uint32_t)gid at:(CGPoint)pos;
+
+/* The layer recognizes some special properties, like cc_vertez */
+-(void) parseInternalProperties;
+
+-(NSInteger) vertexZForPos:(CGPoint)pos;
+
+// adding quad from sprite
+-(void)addQuadFromSprite:(CCSprite*)sprite quadIndex:(NSUInteger)index;
+
+// adds an sprite without the quad
+-(id)addSpriteWithoutQuad:(CCSprite*)child z:(NSInteger)z tag:(NSInteger)aTag;
+
+// index
+-(NSUInteger) atlasIndexForExistantZ:(NSUInteger)z;
+-(NSUInteger) atlasIndexForNewZ:(NSUInteger)z;
+@end
+
+@implementation CCTMXLayer
+@synthesize layerSize = layerSize_, layerName = layerName_, tiles = tiles_;
+@synthesize tileset = tileset_;
+@synthesize layerOrientation = layerOrientation_;
+@synthesize mapTileSize = mapTileSize_;
+@synthesize properties = properties_;
+
+#pragma mark CCTMXLayer - init & alloc & dealloc
+
++(id) layerWithTilesetInfo:(CCTMXTilesetInfo*)tilesetInfo layerInfo:(CCTMXLayerInfo*)layerInfo mapInfo:(CCTMXMapInfo*)mapInfo
+{
+ return [[[self alloc] initWithTilesetInfo:tilesetInfo layerInfo:layerInfo mapInfo:mapInfo] autorelease];
+}
+
+-(id) initWithTilesetInfo:(CCTMXTilesetInfo*)tilesetInfo layerInfo:(CCTMXLayerInfo*)layerInfo mapInfo:(CCTMXMapInfo*)mapInfo
+{
+ // XXX: is 35% a good estimate ?
+ CGSize size = layerInfo.layerSize;
+ float totalNumberOfTiles = size.width * size.height;
+ float capacity = totalNumberOfTiles * 0.35f + 1; // 35 percent is occupied ?
+
+ CCTexture2D *tex = nil;
+ if( tilesetInfo )
+ tex = [[CCTextureCache sharedTextureCache] addImage:tilesetInfo.sourceImage];
+
+ if((self = [super initWithTexture:tex capacity:capacity])) {
+
+ // layerInfo
+ self.layerName = layerInfo.name;
+ layerSize_ = layerInfo.layerSize;
+ tiles_ = layerInfo.tiles;
+ minGID_ = layerInfo.minGID;
+ maxGID_ = layerInfo.maxGID;
+ opacity_ = layerInfo.opacity;
+ self.properties = [NSMutableDictionary dictionaryWithDictionary:layerInfo.properties];
+
+ // tilesetInfo
+ self.tileset = tilesetInfo;
+
+ // mapInfo
+ mapTileSize_ = mapInfo.tileSize;
+ layerOrientation_ = mapInfo.orientation;
+
+ // offset (after layer orientation is set);
+ CGPoint offset = [self calculateLayerOffset:layerInfo.offset];
+ [self setPositionInPixels:offset];
+
+ atlasIndexArray_ = ccCArrayNew(totalNumberOfTiles);
+
+ [self setContentSizeInPixels: CGSizeMake( layerSize_.width * mapTileSize_.width, layerSize_.height * mapTileSize_.height )];
+
+ useAutomaticVertexZ_= NO;
+ vertexZvalue_ = 0;
+ alphaFuncValue_ = 0;
+
+ }
+ return self;
+}
+
+- (void) dealloc
+{
+ [layerName_ release];
+ [tileset_ release];
+ [reusedTile_ release];
+ [properties_ release];
+
+ if( atlasIndexArray_ ) {
+ ccCArrayFree(atlasIndexArray_);
+ atlasIndexArray_ = NULL;
+ }
+
+ if( tiles_ ) {
+ free(tiles_);
+ tiles_ = NULL;
+ }
+
+ [super dealloc];
+}
+
+-(void) releaseMap
+{
+ if( tiles_) {
+ free( tiles_);
+ tiles_ = NULL;
+ }
+
+ if( atlasIndexArray_ ) {
+ ccCArrayFree(atlasIndexArray_);
+ atlasIndexArray_ = NULL;
+ }
+}
+
+#pragma mark CCTMXLayer - setup Tiles
+
+-(void) setupTiles
+{
+ // Optimization: quick hack that sets the image size on the tileset
+ tileset_.imageSize = [textureAtlas_.texture contentSizeInPixels];
+
+ // By default all the tiles are aliased
+ // pros:
+ // - easier to render
+ // cons:
+ // - difficult to scale / rotate / etc.
+ [textureAtlas_.texture setAliasTexParameters];
+
+ CFByteOrder o = CFByteOrderGetCurrent();
+
+ // Parse cocos2d properties
+ [self parseInternalProperties];
+
+ for( NSUInteger y=0; y < layerSize_.height; y++ ) {
+ for( NSUInteger x=0; x < layerSize_.width; x++ ) {
+
+ NSUInteger pos = x + layerSize_.width * y;
+ uint32_t gid = tiles_[ pos ];
+
+ // gid are stored in little endian.
+ // if host is big endian, then swap
+ if( o == CFByteOrderBigEndian )
+ gid = CFSwapInt32( gid );
+
+ // XXX: gid == 0 --> empty tile
+ if( gid != 0 ) {
+ [self appendTileForGID:gid at:ccp(x,y)];
+
+ // Optimization: update min and max GID rendered by the layer
+ minGID_ = MIN(gid, minGID_);
+ maxGID_ = MAX(gid, maxGID_);
+ }
+ }
+ }
+
+ NSAssert( maxGID_ >= tileset_.firstGid &&
+ minGID_ >= tileset_.firstGid, @"TMX: Only 1 tilset per layer is supported");
+}
+
+#pragma mark CCTMXLayer - Properties
+
+-(id) propertyNamed:(NSString *)propertyName
+{
+ return [properties_ valueForKey:propertyName];
+}
+
+-(void) parseInternalProperties
+{
+ // if cc_vertex=automatic, then tiles will be rendered using vertexz
+
+ NSString *vertexz = [self propertyNamed:@"cc_vertexz"];
+ if( vertexz ) {
+ if( [vertexz isEqualToString:@"automatic"] )
+ useAutomaticVertexZ_ = YES;
+ else
+ vertexZvalue_ = [vertexz intValue];
+ }
+
+ NSString *alphaFuncVal = [self propertyNamed:@"cc_alpha_func"];
+ alphaFuncValue_ = [alphaFuncVal floatValue];
+}
+
+#pragma mark CCTMXLayer - obtaining tiles/gids
+
+-(CCSprite*) tileAt:(CGPoint)pos
+{
+ NSAssert( pos.x < layerSize_.width && pos.y < layerSize_.height && pos.x >=0 && pos.y >=0, @"TMXLayer: invalid position");
+ NSAssert( tiles_ && atlasIndexArray_, @"TMXLayer: the tiles map has been released");
+
+ CCSprite *tile = nil;
+ uint32_t gid = [self tileGIDAt:pos];
+
+ // if GID == 0, then no tile is present
+ if( gid ) {
+ int z = pos.x + pos.y * layerSize_.width;
+ tile = (CCSprite*) [self getChildByTag:z];
+
+ // tile not created yet. create it
+ if( ! tile ) {
+ CGRect rect = [tileset_ rectForGID:gid];
+ tile = [[CCSprite alloc] initWithBatchNode:self rectInPixels:rect];
+ [tile setPositionInPixels: [self positionAt:pos]];
+ [tile setVertexZ: [self vertexZForPos:pos]];
+ tile.anchorPoint = CGPointZero;
+ [tile setOpacity:opacity_];
+
+ NSUInteger indexForZ = [self atlasIndexForExistantZ:z];
+ [self addSpriteWithoutQuad:tile z:indexForZ tag:z];
+ [tile release];
+ }
+ }
+ return tile;
+}
+
+-(uint32_t) tileGIDAt:(CGPoint)pos
+{
+ NSAssert( pos.x < layerSize_.width && pos.y < layerSize_.height && pos.x >=0 && pos.y >=0, @"TMXLayer: invalid position");
+ NSAssert( tiles_ && atlasIndexArray_, @"TMXLayer: the tiles map has been released");
+
+ NSInteger idx = pos.x + pos.y * layerSize_.width;
+ return tiles_[ idx ];
+}
+
+#pragma mark CCTMXLayer - adding helper methods
+
+-(CCSprite*) insertTileForGID:(uint32_t)gid at:(CGPoint)pos
+{
+ CGRect rect = [tileset_ rectForGID:gid];
+
+ NSInteger z = pos.x + pos.y * layerSize_.width;
+
+ if( ! reusedTile_ )
+ reusedTile_ = [[CCSprite alloc] initWithBatchNode:self rectInPixels:rect];
+ else
+ [reusedTile_ initWithBatchNode:self rectInPixels:rect];
+
+ [reusedTile_ setPositionInPixels: [self positionAt:pos]];
+ [reusedTile_ setVertexZ: [self vertexZForPos:pos]];
+ reusedTile_.anchorPoint = CGPointZero;
+ [reusedTile_ setOpacity:opacity_];
+
+ // get atlas index
+ NSUInteger indexForZ = [self atlasIndexForNewZ:z];
+
+ // Optimization: add the quad without adding a child
+ [self addQuadFromSprite:reusedTile_ quadIndex:indexForZ];
+
+ // insert it into the local atlasindex array
+ ccCArrayInsertValueAtIndex(atlasIndexArray_, (void*)z, indexForZ);
+
+ // update possible children
+ CCSprite *sprite;
+ CCARRAY_FOREACH(children_, sprite) {
+ NSUInteger ai = [sprite atlasIndex];
+ if( ai >= indexForZ)
+ [sprite setAtlasIndex: ai+1];
+ }
+
+ tiles_[z] = gid;
+
+ return reusedTile_;
+}
+
+-(CCSprite*) updateTileForGID:(uint32_t)gid at:(CGPoint)pos
+{
+ CGRect rect = [tileset_ rectForGID:gid];
+
+ int z = pos.x + pos.y * layerSize_.width;
+
+ if( ! reusedTile_ )
+ reusedTile_ = [[CCSprite alloc] initWithBatchNode:self rectInPixels:rect];
+ else
+ [reusedTile_ initWithBatchNode:self rectInPixels:rect];
+
+ [reusedTile_ setPositionInPixels: [self positionAt:pos]];
+ [reusedTile_ setVertexZ: [self vertexZForPos:pos]];
+ reusedTile_.anchorPoint = CGPointZero;
+ [reusedTile_ setOpacity:opacity_];
+
+ // get atlas index
+ NSUInteger indexForZ = [self atlasIndexForExistantZ:z];
+
+ [reusedTile_ setAtlasIndex:indexForZ];
+ [reusedTile_ setDirty:YES];
+ [reusedTile_ updateTransform];
+ tiles_[z] = gid;
+
+ return reusedTile_;
+}
+
+
+// used only when parsing the map. useless after the map was parsed
+// since lot's of assumptions are no longer true
+-(CCSprite*) appendTileForGID:(uint32_t)gid at:(CGPoint)pos
+{
+ CGRect rect = [tileset_ rectForGID:gid];
+
+ NSInteger z = pos.x + pos.y * layerSize_.width;
+
+ if( ! reusedTile_ )
+ reusedTile_ = [[CCSprite alloc] initWithBatchNode:self rectInPixels:rect];
+ else
+ [reusedTile_ initWithBatchNode:self rectInPixels:rect];
+
+ [reusedTile_ setPositionInPixels: [self positionAt:pos]];
+ [reusedTile_ setVertexZ: [self vertexZForPos:pos]];
+ reusedTile_.anchorPoint = CGPointZero;
+ [reusedTile_ setOpacity:opacity_];
+
+ // optimization:
+ // The difference between appendTileForGID and insertTileforGID is that append is faster, since
+ // it appends the tile at the end of the texture atlas
+ NSUInteger indexForZ = atlasIndexArray_->num;
+
+
+ // don't add it using the "standard" way.
+ [self addQuadFromSprite:reusedTile_ quadIndex:indexForZ];
+
+
+ // append should be after addQuadFromSprite since it modifies the quantity values
+ ccCArrayInsertValueAtIndex(atlasIndexArray_, (void*)z, indexForZ);
+
+ return reusedTile_;
+}
+
+#pragma mark CCTMXLayer - atlasIndex and Z
+
+int compareInts (const void * a, const void * b)
+{
+ return ( *(int*)a - *(int*)b );
+}
+
+-(NSUInteger) atlasIndexForExistantZ:(NSUInteger)z
+{
+ NSInteger key = z;
+ NSInteger *item = bsearch((void*)&key, (void*)&atlasIndexArray_->arr[0], atlasIndexArray_->num, sizeof(void*), compareInts);
+
+ NSAssert( item, @"TMX atlas index not found. Shall not happen");
+
+ NSUInteger index = ((NSInteger)item - (NSInteger)atlasIndexArray_->arr) / sizeof(void*);
+ return index;
+}
+
+-(NSUInteger)atlasIndexForNewZ:(NSUInteger)z
+{
+ // XXX: This can be improved with a sort of binary search
+ NSUInteger i = 0;
+ for(i = 0; i< atlasIndexArray_->num; i++) {
+ NSUInteger val = (NSUInteger) atlasIndexArray_->arr[i];
+ if( z < val )
+ break;
+ }
+ return i;
+}
+
+#pragma mark CCTMXLayer - adding / remove tiles
+
+-(void) setTileGID:(uint32_t)gid at:(CGPoint)pos
+{
+ NSAssert( pos.x < layerSize_.width && pos.y < layerSize_.height && pos.x >=0 && pos.y >=0, @"TMXLayer: invalid position");
+ NSAssert( tiles_ && atlasIndexArray_, @"TMXLayer: the tiles map has been released");
+ NSAssert( gid == 0 || gid >= tileset_.firstGid, @"TMXLayer: invalid gid" );
+
+ uint32_t currentGID = [self tileGIDAt:pos];
+
+ if( currentGID != gid ) {
+
+ // setting gid=0 is equal to remove the tile
+ if( gid == 0 )
+ [self removeTileAt:pos];
+
+ // empty tile. create a new one
+ else if( currentGID == 0 )
+ [self insertTileForGID:gid at:pos];
+
+ // modifying an existing tile with a non-empty tile
+ else {
+
+ NSUInteger z = pos.x + pos.y * layerSize_.width;
+ id sprite = [self getChildByTag:z];
+ if( sprite ) {
+ CGRect rect = [tileset_ rectForGID:gid];
+ [sprite setTextureRectInPixels:rect rotated:NO untrimmedSize:rect.size];
+ tiles_[z] = gid;
+ } else
+ [self updateTileForGID:gid at:pos];
+ }
+ }
+}
+
+-(void) addChild: (CCNode*)node z:(NSInteger)z tag:(NSInteger)tag
+{
+ NSAssert(NO, @"addChild: is not supported on CCTMXLayer. Instead use setTileGID:at:/tileAt:");
+}
+
+-(void) removeChild:(CCSprite*)sprite cleanup:(BOOL)cleanup
+{
+ // allows removing nil objects
+ if( ! sprite )
+ return;
+
+ NSAssert( [children_ containsObject:sprite], @"Tile does not belong to TMXLayer");
+
+ NSUInteger atlasIndex = [sprite atlasIndex];
+ NSUInteger zz = (NSUInteger) atlasIndexArray_->arr[atlasIndex];
+ tiles_[zz] = 0;
+ ccCArrayRemoveValueAtIndex(atlasIndexArray_, atlasIndex);
+ [super removeChild:sprite cleanup:cleanup];
+}
+
+-(void) removeTileAt:(CGPoint)pos
+{
+ NSAssert( pos.x < layerSize_.width && pos.y < layerSize_.height && pos.x >=0 && pos.y >=0, @"TMXLayer: invalid position");
+ NSAssert( tiles_ && atlasIndexArray_, @"TMXLayer: the tiles map has been released");
+
+ uint32_t gid = [self tileGIDAt:pos];
+
+ if( gid ) {
+
+ NSUInteger z = pos.x + pos.y * layerSize_.width;
+ NSUInteger atlasIndex = [self atlasIndexForExistantZ:z];
+
+ // remove tile from GID map
+ tiles_[z] = 0;
+
+ // remove tile from atlas position array
+ ccCArrayRemoveValueAtIndex(atlasIndexArray_, atlasIndex);
+
+ // remove it from sprites and/or texture atlas
+ id sprite = [self getChildByTag:z];
+ if( sprite )
+ [super removeChild:sprite cleanup:YES];
+ else {
+ [textureAtlas_ removeQuadAtIndex:atlasIndex];
+
+ // update possible children
+ CCARRAY_FOREACH(children_, sprite) {
+ NSUInteger ai = [sprite atlasIndex];
+ if( ai >= atlasIndex) {
+ [sprite setAtlasIndex: ai-1];
+ }
+ }
+ }
+ }
+}
+
+#pragma mark CCTMXLayer - obtaining positions, offset
+
+-(CGPoint) calculateLayerOffset:(CGPoint)pos
+{
+ CGPoint ret = CGPointZero;
+ switch( layerOrientation_ ) {
+ case CCTMXOrientationOrtho:
+ ret = ccp( pos.x * mapTileSize_.width, -pos.y *mapTileSize_.height);
+ break;
+ case CCTMXOrientationIso:
+ ret = ccp( (mapTileSize_.width /2) * (pos.x - pos.y),
+ (mapTileSize_.height /2 ) * (-pos.x - pos.y) );
+ break;
+ case CCTMXOrientationHex:
+ NSAssert(CGPointEqualToPoint(pos, CGPointZero), @"offset for hexagonal map not implemented yet");
+ break;
+ }
+ return ret;
+}
+
+-(CGPoint) positionAt:(CGPoint)pos
+{
+ CGPoint ret = CGPointZero;
+ switch( layerOrientation_ ) {
+ case CCTMXOrientationOrtho:
+ ret = [self positionForOrthoAt:pos];
+ break;
+ case CCTMXOrientationIso:
+ ret = [self positionForIsoAt:pos];
+ break;
+ case CCTMXOrientationHex:
+ ret = [self positionForHexAt:pos];
+ break;
+ }
+ return ret;
+}
+
+-(CGPoint) positionForOrthoAt:(CGPoint)pos
+{
+ CGPoint xy = {
+ pos.x * mapTileSize_.width,
+ (layerSize_.height - pos.y - 1) * mapTileSize_.height,
+ };
+ return xy;
+}
+
+-(CGPoint) positionForIsoAt:(CGPoint)pos
+{
+ CGPoint xy = {
+ mapTileSize_.width /2 * ( layerSize_.width + pos.x - pos.y - 1),
+ mapTileSize_.height /2 * (( layerSize_.height * 2 - pos.x - pos.y) - 2),
+ };
+ return xy;
+}
+
+-(CGPoint) positionForHexAt:(CGPoint)pos
+{
+ float diffY = 0;
+ if( (int)pos.x % 2 == 1 )
+ diffY = -mapTileSize_.height/2 ;
+
+ CGPoint xy = {
+ pos.x * mapTileSize_.width*3/4,
+ (layerSize_.height - pos.y - 1) * mapTileSize_.height + diffY
+ };
+ return xy;
+}
+
+-(NSInteger) vertexZForPos:(CGPoint)pos
+{
+ NSInteger ret = 0;
+ NSUInteger maxVal = 0;
+ if( useAutomaticVertexZ_ ) {
+ switch( layerOrientation_ ) {
+ case CCTMXOrientationIso:
+ maxVal = layerSize_.width + layerSize_.height;
+ ret = -(maxVal - (pos.x + pos.y));
+ break;
+ case CCTMXOrientationOrtho:
+ ret = -(layerSize_.height-pos.y);
+ break;
+ case CCTMXOrientationHex:
+ NSAssert(NO,@"TMX Hexa zOrder not supported");
+ break;
+ default:
+ NSAssert(NO,@"TMX invalid value");
+ break;
+ }
+ } else
+ ret = vertexZvalue_;
+
+ return ret;
+}
+
+#pragma mark CCTMXLayer - draw
+
+-(void) draw
+{
+ if( useAutomaticVertexZ_ ) {
+ glEnable(GL_ALPHA_TEST);
+ glAlphaFunc(GL_GREATER, alphaFuncValue_);
+ }
+
+ [super draw];
+
+ if( useAutomaticVertexZ_ )
+ glDisable(GL_ALPHA_TEST);
+}
+@end
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Neophit
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * TMX Tiled Map support:
+ * http://www.mapeditor.org
+ *
+ */
+
+#import "CCNode.h"
+
+
+@class CCTMXObjectGroup;
+
+
+/** CCTMXObjectGroup represents the TMX object group.
+@since v0.99.0
+*/
+@interface CCTMXObjectGroup : NSObject
+{
+ NSString *groupName_;
+ CGPoint positionOffset_;
+ NSMutableArray *objects_;
+ NSMutableDictionary *properties_;
+}
+
+/** name of the group */
+@property (nonatomic,readwrite,retain) NSString *groupName;
+/** offset position of child objects */
+@property (nonatomic,readwrite,assign) CGPoint positionOffset;
+/** array of the objects */
+@property (nonatomic,readwrite,retain) NSMutableArray *objects;
+/** list of properties stored in a dictionary */
+@property (nonatomic,readwrite,retain) NSMutableDictionary *properties;
+
+/** return the value for the specific property name */
+-(id) propertyNamed:(NSString *)propertyName;
+
+/** return the dictionary for the specific object name.
+ It will return the 1st object found on the array for the given name.
+ */
+-(NSMutableDictionary*) objectNamed:(NSString *)objectName;
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Neophit
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * TMX Tiled Map support:
+ * http://www.mapeditor.org
+ *
+ */
+
+#import "CCTMXObjectGroup.h"
+#import "CCTMXXMLParser.h"
+#import "ccMacros.h"
+#import "Support/CGPointExtension.h"
+
+
+#pragma mark -
+#pragma mark TMXObjectGroup
+
+@implementation CCTMXObjectGroup
+
+@synthesize groupName = groupName_;
+@synthesize objects = objects_;
+@synthesize positionOffset = positionOffset_;
+@synthesize properties = properties_;
+
+-(id) init
+{
+ if (( self=[super init] )) {
+ self.groupName = nil;
+ self.positionOffset = CGPointZero;
+ self.objects = [NSMutableArray arrayWithCapacity:10];
+ self.properties = [NSMutableDictionary dictionaryWithCapacity:5];
+ }
+ return self;
+}
+
+-(void) dealloc
+{
+ CCLOGINFO( @"cocos2d: deallocing %@", self );
+
+ [groupName_ release];
+ [objects_ release];
+ [properties_ release];
+ [super dealloc];
+}
+
+-(NSMutableDictionary*) objectNamed:(NSString *)objectName
+{
+ for( id object in objects_ ) {
+ if( [[object valueForKey:@"name"] isEqual:objectName] )
+ return object;
+ }
+
+ // object not found
+ return nil;
+}
+
+-(id) propertyNamed:(NSString *)propertyName
+{
+ return [properties_ valueForKey:propertyName];
+}
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * TMX Tiled Map support:
+ * http://www.mapeditor.org
+ *
+ */
+
+#import "CCNode.h"
+
+
+@class CCTMXLayer;
+@class CCTMXObjectGroup;
+
+/** Possible oritentations of the TMX map */
+enum
+{
+ /** Orthogonal orientation */
+ CCTMXOrientationOrtho,
+
+ /** Hexagonal orientation */
+ CCTMXOrientationHex,
+
+ /** Isometric orientation */
+ CCTMXOrientationIso,
+};
+
+/** CCTMXTiledMap knows how to parse and render a TMX map.
+
+ It adds support for the TMX tiled map format used by http://www.mapeditor.org
+ It supports isometric, hexagonal and orthogonal tiles.
+ It also supports object groups, objects, and properties.
+
+ Features:
+ - Each tile will be treated as an CCSprite
+ - The sprites are created on demand. They will be created only when you call "[layer tileAt:]"
+ - Each tile can be rotated / moved / scaled / tinted / "opacitied", since each tile is a CCSprite
+ - Tiles can be added/removed in runtime
+ - The z-order of the tiles can be modified in runtime
+ - Each tile has an anchorPoint of (0,0)
+ - The anchorPoint of the TMXTileMap is (0,0)
+ - The TMX layers will be added as a child
+ - The TMX layers will be aliased by default
+ - The tileset image will be loaded using the CCTextureCache
+ - Each tile will have a unique tag
+ - Each tile will have a unique z value. top-left: z=1, bottom-right: z=max z
+ - Each object group will be treated as an NSMutableArray
+ - Object class which will contain all the properties in a dictionary
+ - Properties can be assigned to the Map, Layer, Object Group, and Object
+
+ Limitations:
+ - It only supports one tileset per layer.
+ - Embeded images are not supported
+ - It only supports the XML format (the JSON format is not supported)
+
+ Technical description:
+ Each layer is created using an CCTMXLayer (subclass of CCSpriteSheet). If you have 5 layers, then 5 CCTMXLayer will be created,
+ unless the layer visibility is off. In that case, the layer won't be created at all.
+ You can obtain the layers (CCTMXLayer objects) at runtime by:
+ - [map getChildByTag: tag_number]; // 0=1st layer, 1=2nd layer, 2=3rd layer, etc...
+ - [map layerNamed: name_of_the_layer];
+
+ Each object group is created using a CCTMXObjectGroup which is a subclass of NSMutableArray.
+ You can obtain the object groups at runtime by:
+ - [map objectGroupNamed: name_of_the_object_group];
+
+ Each object is a CCTMXObject.
+
+ Each property is stored as a key-value pair in an NSMutableDictionary.
+ You can obtain the properties at runtime by:
+
+ [map propertyNamed: name_of_the_property];
+ [layer propertyNamed: name_of_the_property];
+ [objectGroup propertyNamed: name_of_the_property];
+ [object propertyNamed: name_of_the_property];
+
+ @since v0.8.1
+ */
+@interface CCTMXTiledMap : CCNode
+{
+ CGSize mapSize_;
+ CGSize tileSize_;
+ int mapOrientation_;
+ NSMutableArray *objectGroups_;
+ NSMutableDictionary *properties_;
+ NSMutableDictionary *tileProperties_;
+}
+
+/** the map's size property measured in tiles */
+@property (nonatomic,readonly) CGSize mapSize;
+/** the tiles's size property measured in pixels */
+@property (nonatomic,readonly) CGSize tileSize;
+/** map orientation */
+@property (nonatomic,readonly) int mapOrientation;
+/** object groups */
+@property (nonatomic,readwrite,retain) NSMutableArray *objectGroups;
+/** properties */
+@property (nonatomic,readwrite,retain) NSMutableDictionary *properties;
+
+/** creates a TMX Tiled Map with a TMX file.*/
++(id) tiledMapWithTMXFile:(NSString*)tmxFile;
+
+/** initializes a TMX Tiled Map with a TMX file */
+-(id) initWithTMXFile:(NSString*)tmxFile;
+
+/** return the TMXLayer for the specific layer */
+-(CCTMXLayer*) layerNamed:(NSString *)layerName;
+
+/** return the TMXObjectGroup for the secific group */
+-(CCTMXObjectGroup*) objectGroupNamed:(NSString *)groupName;
+
+/** return the TMXObjectGroup for the secific group
+ @deprecated Use map#objectGroupNamed instead
+ */
+-(CCTMXObjectGroup*) groupNamed:(NSString *)groupName DEPRECATED_ATTRIBUTE;
+
+/** return the value for the specific property name */
+-(id) propertyNamed:(NSString *)propertyName;
+
+/** return properties dictionary for tile GID */
+-(NSDictionary*)propertiesForGID:(unsigned int)GID;
+@end
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * TMX Tiled Map support:
+ * http://www.mapeditor.org
+ *
+ */
+
+#import "CCTMXTiledMap.h"
+#import "CCTMXXMLParser.h"
+#import "CCTMXLayer.h"
+#import "CCTMXObjectGroup.h"
+#import "CCSprite.h"
+#import "CCTextureCache.h"
+#import "Support/CGPointExtension.h"
+
+
+#pragma mark -
+#pragma mark CCTMXTiledMap
+
+@interface CCTMXTiledMap (Private)
+-(id) parseLayer:(CCTMXLayerInfo*)layer map:(CCTMXMapInfo*)mapInfo;
+-(CCTMXTilesetInfo*) tilesetForLayer:(CCTMXLayerInfo*)layerInfo map:(CCTMXMapInfo*)mapInfo;
+@end
+
+@implementation CCTMXTiledMap
+@synthesize mapSize = mapSize_;
+@synthesize tileSize = tileSize_;
+@synthesize mapOrientation = mapOrientation_;
+@synthesize objectGroups = objectGroups_;
+@synthesize properties = properties_;
+
++(id) tiledMapWithTMXFile:(NSString*)tmxFile
+{
+ return [[[self alloc] initWithTMXFile:tmxFile] autorelease];
+}
+
+-(id) initWithTMXFile:(NSString*)tmxFile
+{
+ NSAssert(tmxFile != nil, @"TMXTiledMap: tmx file should not bi nil");
+
+ if ((self=[super init])) {
+
+ [self setContentSize:CGSizeZero];
+
+ CCTMXMapInfo *mapInfo = [CCTMXMapInfo formatWithTMXFile:tmxFile];
+
+ NSAssert( [mapInfo.tilesets count] != 0, @"TMXTiledMap: Map not found. Please check the filename.");
+
+ mapSize_ = mapInfo.mapSize;
+ tileSize_ = mapInfo.tileSize;
+ mapOrientation_ = mapInfo.orientation;
+ objectGroups_ = [mapInfo.objectGroups retain];
+ properties_ = [mapInfo.properties retain];
+ tileProperties_ = [mapInfo.tileProperties retain];
+
+ int idx=0;
+
+ for( CCTMXLayerInfo *layerInfo in mapInfo.layers ) {
+
+ if( layerInfo.visible ) {
+ CCNode *child = [self parseLayer:layerInfo map:mapInfo];
+ [self addChild:child z:idx tag:idx];
+
+ // update content size with the max size
+ CGSize childSize = [child contentSize];
+ CGSize currentSize = [self contentSize];
+ currentSize.width = MAX( currentSize.width, childSize.width );
+ currentSize.height = MAX( currentSize.height, childSize.height );
+ [self setContentSize:currentSize];
+
+ idx++;
+ }
+ }
+ }
+
+ return self;
+}
+
+-(void) dealloc
+{
+ [objectGroups_ release];
+ [properties_ release];
+ [tileProperties_ release];
+ [super dealloc];
+}
+
+// private
+-(id) parseLayer:(CCTMXLayerInfo*)layerInfo map:(CCTMXMapInfo*)mapInfo
+{
+ CCTMXTilesetInfo *tileset = [self tilesetForLayer:layerInfo map:mapInfo];
+ CCTMXLayer *layer = [CCTMXLayer layerWithTilesetInfo:tileset layerInfo:layerInfo mapInfo:mapInfo];
+
+ // tell the layerinfo to release the ownership of the tiles map.
+ layerInfo.ownTiles = NO;
+
+ [layer setupTiles];
+
+ return layer;
+}
+
+-(CCTMXTilesetInfo*) tilesetForLayer:(CCTMXLayerInfo*)layerInfo map:(CCTMXMapInfo*)mapInfo
+{
+ CFByteOrder o = CFByteOrderGetCurrent();
+
+ CGSize size = layerInfo.layerSize;
+
+ id iter = [mapInfo.tilesets reverseObjectEnumerator];
+ for( CCTMXTilesetInfo* tileset in iter) {
+ for( unsigned int y = 0; y < size.height; y++ ) {
+ for( unsigned int x = 0; x < size.width; x++ ) {
+
+ unsigned int pos = x + size.width * y;
+ unsigned int gid = layerInfo.tiles[ pos ];
+
+ // gid are stored in little endian.
+ // if host is big endian, then swap
+ if( o == CFByteOrderBigEndian )
+ gid = CFSwapInt32( gid );
+
+ // XXX: gid == 0 --> empty tile
+ if( gid != 0 ) {
+
+ // Optimization: quick return
+ // if the layer is invalid (more than 1 tileset per layer) an assert will be thrown later
+ if( gid >= tileset.firstGid )
+ return tileset;
+ }
+ }
+ }
+ }
+
+ // If all the tiles are 0, return empty tileset
+ CCLOG(@"cocos2d: Warning: TMX Layer '%@' has no tiles", layerInfo.name);
+ return nil;
+}
+
+
+// public
+
+-(CCTMXLayer*) layerNamed:(NSString *)layerName
+{
+ CCTMXLayer *layer;
+ CCARRAY_FOREACH(children_, layer) {
+ if([layer isKindOfClass:[CCTMXLayer class]])
+ if([layer.layerName isEqual:layerName])
+ return layer;
+ }
+
+ // layer not found
+ return nil;
+}
+
+-(CCTMXObjectGroup*) objectGroupNamed:(NSString *)groupName
+{
+ for( CCTMXObjectGroup *objectGroup in objectGroups_ ) {
+ if( [objectGroup.groupName isEqual:groupName] )
+ return objectGroup;
+ }
+
+ // objectGroup not found
+ return nil;
+}
+
+// XXX deprecated
+-(CCTMXObjectGroup*) groupNamed:(NSString *)groupName
+{
+ return [self objectGroupNamed:groupName];
+}
+
+-(id) propertyNamed:(NSString *)propertyName
+{
+ return [properties_ valueForKey:propertyName];
+}
+-(NSDictionary*)propertiesForGID:(unsigned int)GID{
+ return [tileProperties_ objectForKey:[NSNumber numberWithInt:GID]];
+}
+@end
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * TMX Tiled Map support:
+ * http://www.mapeditor.org
+ *
+ */
+
+/*
+ * Internal TMX parser
+ *
+ * IMPORTANT: These classed should not be documented using doxygen strings
+ * since the user should not use them.
+ *
+ */
+
+
+#import <Availability.h>
+#import <Foundation/Foundation.h>
+
+enum {
+ TMXLayerAttribNone = 1 << 0,
+ TMXLayerAttribBase64 = 1 << 1,
+ TMXLayerAttribGzip = 1 << 2,
+ TMXLayerAttribZlib = 1 << 3,
+};
+
+enum {
+ TMXPropertyNone,
+ TMXPropertyMap,
+ TMXPropertyLayer,
+ TMXPropertyObjectGroup,
+ TMXPropertyObject,
+ TMXPropertyTile
+};
+
+/* CCTMXLayerInfo contains the information about the layers like:
+ - Layer name
+ - Layer size
+ - Layer opacity at creation time (it can be modified at runtime)
+ - Whether the layer is visible (if it's not visible, then the CocosNode won't be created)
+
+ This information is obtained from the TMX file.
+ */
+@interface CCTMXLayerInfo : NSObject
+{
+ NSString *name_;
+ CGSize layerSize_;
+ unsigned int *tiles_;
+ BOOL visible_;
+ unsigned char opacity_;
+ BOOL ownTiles_;
+ unsigned int minGID_;
+ unsigned int maxGID_;
+ NSMutableDictionary *properties_;
+ CGPoint offset_;
+}
+
+@property (nonatomic,readwrite,retain) NSString *name;
+@property (nonatomic,readwrite) CGSize layerSize;
+@property (nonatomic,readwrite) unsigned int *tiles;
+@property (nonatomic,readwrite) BOOL visible;
+@property (nonatomic,readwrite) unsigned char opacity;
+@property (nonatomic,readwrite) BOOL ownTiles;
+@property (nonatomic,readwrite) unsigned int minGID;
+@property (nonatomic,readwrite) unsigned int maxGID;
+@property (nonatomic,readwrite,retain) NSMutableDictionary *properties;
+@property (nonatomic,readwrite) CGPoint offset;
+@end
+
+/* CCTMXTilesetInfo contains the information about the tilesets like:
+ - Tileset name
+ - Tilset spacing
+ - Tileset margin
+ - size of the tiles
+ - Image used for the tiles
+ - Image size
+
+ This information is obtained from the TMX file.
+ */
+@interface CCTMXTilesetInfo : NSObject
+{
+ NSString *name_;
+ unsigned int firstGid_;
+ CGSize tileSize_;
+ unsigned int spacing_;
+ unsigned int margin_;
+
+ // filename containing the tiles (should be spritesheet / texture atlas)
+ NSString *sourceImage_;
+
+ // size in pixels of the image
+ CGSize imageSize_;
+}
+@property (nonatomic,readwrite,retain) NSString *name;
+@property (nonatomic,readwrite,assign) unsigned int firstGid;
+@property (nonatomic,readwrite,assign) CGSize tileSize;
+@property (nonatomic,readwrite,assign) unsigned int spacing;
+@property (nonatomic,readwrite,assign) unsigned int margin;
+@property (nonatomic,readwrite,retain) NSString *sourceImage;
+@property (nonatomic,readwrite,assign) CGSize imageSize;
+
+-(CGRect) rectForGID:(unsigned int)gid;
+@end
+
+/* CCTMXMapInfo contains the information about the map like:
+ - Map orientation (hexagonal, isometric or orthogonal)
+ - Tile size
+ - Map size
+
+ And it also contains:
+ - Layers (an array of TMXLayerInfo objects)
+ - Tilesets (an array of TMXTilesetInfo objects)
+ - ObjectGroups (an array of TMXObjectGroupInfo objects)
+
+ This information is obtained from the TMX file.
+
+ */
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#if defined(__IPHONE_4_0)
+@interface CCTMXMapInfo : NSObject <NSXMLParserDelegate>
+#else
+@interface CCTMXMapInfo : NSObject
+#endif
+
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+@interface CCTMXMapInfo : NSObject <NSXMLParserDelegate>
+#endif
+{
+ NSMutableString *currentString;
+ BOOL storingCharacters;
+ int layerAttribs;
+ int parentElement;
+ unsigned int parentGID_;
+
+
+ // tmx filename
+ NSString *filename_;
+
+ // map orientation
+ int orientation_;
+
+ // map width & height
+ CGSize mapSize_;
+
+ // tiles width & height
+ CGSize tileSize_;
+
+ // Layers
+ NSMutableArray *layers_;
+
+ // tilesets
+ NSMutableArray *tilesets_;
+
+ // ObjectGroups
+ NSMutableArray *objectGroups_;
+
+ // properties
+ NSMutableDictionary *properties_;
+
+ // tile properties
+ NSMutableDictionary *tileProperties_;
+}
+
+@property (nonatomic,readwrite,assign) int orientation;
+@property (nonatomic,readwrite,assign) CGSize mapSize;
+@property (nonatomic,readwrite,assign) CGSize tileSize;
+@property (nonatomic,readwrite,retain) NSMutableArray *layers;
+@property (nonatomic,readwrite,retain) NSMutableArray *tilesets;
+@property (nonatomic,readwrite,retain) NSString *filename;
+@property (nonatomic,readwrite,retain) NSMutableArray *objectGroups;
+@property (nonatomic,readwrite,retain) NSMutableDictionary *properties;
+@property (nonatomic,readwrite,retain) NSMutableDictionary *tileProperties;
+
+/** creates a TMX Format with a tmx file */
++(id) formatWithTMXFile:(NSString*)tmxFile;
+/** initializes a TMX format witha tmx file */
+-(id) initWithTMXFile:(NSString*)tmxFile;
+@end
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * TMX Tiled Map support:
+ * http://www.mapeditor.org
+ *
+ */
+
+
+#import <Foundation/Foundation.h>
+#include <zlib.h>
+
+#import "ccMacros.h"
+#import "Support/CGPointExtension.h"
+#import "CCTMXXMLParser.h"
+#import "CCTMXTiledMap.h"
+#import "CCTMXObjectGroup.h"
+#import "Support/base64.h"
+#import "Support/ZipUtils.h"
+#import "Support/CCFileUtils.h"
+
+#pragma mark -
+#pragma mark TMXLayerInfo
+
+
+@implementation CCTMXLayerInfo
+
+@synthesize name = name_, layerSize = layerSize_, tiles = tiles_, visible = visible_, opacity = opacity_, ownTiles = ownTiles_, minGID = minGID_, maxGID = maxGID_, properties = properties_;
+@synthesize offset = offset_;
+-(id) init
+{
+ if( (self=[super init])) {
+ ownTiles_ = YES;
+ minGID_ = 100000;
+ maxGID_ = 0;
+ self.name = nil;
+ tiles_ = NULL;
+ offset_ = CGPointZero;
+ self.properties = [NSMutableDictionary dictionaryWithCapacity:5];
+ }
+ return self;
+}
+- (void) dealloc
+{
+ CCLOGINFO(@"cocos2d: deallocing %@",self);
+
+ [name_ release];
+ [properties_ release];
+
+ if( ownTiles_ && tiles_ ) {
+ free( tiles_ );
+ tiles_ = NULL;
+ }
+ [super dealloc];
+}
+
+@end
+
+#pragma mark -
+#pragma mark TMXTilesetInfo
+@implementation CCTMXTilesetInfo
+
+@synthesize name = name_, firstGid = firstGid_, tileSize = tileSize_, spacing = spacing_, margin = margin_, sourceImage = sourceImage_, imageSize = imageSize_;
+
+- (void) dealloc
+{
+ CCLOGINFO(@"cocos2d: deallocing %@", self);
+ [sourceImage_ release];
+ [name_ release];
+ [super dealloc];
+}
+
+-(CGRect) rectForGID:(unsigned int)gid
+{
+ CGRect rect;
+ rect.size = tileSize_;
+
+ gid = gid - firstGid_;
+
+ int max_x = (imageSize_.width - margin_*2 + spacing_) / (tileSize_.width + spacing_);
+ // int max_y = (imageSize.height - margin*2 + spacing) / (tileSize.height + spacing);
+
+ rect.origin.x = (gid % max_x) * (tileSize_.width + spacing_) + margin_;
+ rect.origin.y = (gid / max_x) * (tileSize_.height + spacing_) + margin_;
+
+ return rect;
+}
+@end
+
+#pragma mark -
+#pragma mark CCTMXMapInfo
+
+@interface CCTMXMapInfo (Private)
+/* initalises parsing of an XML file, either a tmx (Map) file or tsx (Tileset) file */
+-(void) parseXMLFile:(NSString *)xmlFilename;
+@end
+
+
+@implementation CCTMXMapInfo
+
+@synthesize orientation = orientation_, mapSize = mapSize_, layers = layers_, tilesets = tilesets_, tileSize = tileSize_, filename = filename_, objectGroups = objectGroups_, properties = properties_;
+@synthesize tileProperties = tileProperties_;
+
++(id) formatWithTMXFile:(NSString*)tmxFile
+{
+ return [[[self alloc] initWithTMXFile:tmxFile] autorelease];
+}
+
+-(id) initWithTMXFile:(NSString*)tmxFile
+{
+ if( (self=[super init])) {
+
+ self.tilesets = [NSMutableArray arrayWithCapacity:4];
+ self.layers = [NSMutableArray arrayWithCapacity:4];
+ self.filename = tmxFile;
+ self.objectGroups = [NSMutableArray arrayWithCapacity:4];
+ self.properties = [NSMutableDictionary dictionaryWithCapacity:5];
+ self.tileProperties = [NSMutableDictionary dictionaryWithCapacity:5];
+
+ // tmp vars
+ currentString = [[NSMutableString alloc] initWithCapacity:1024];
+ storingCharacters = NO;
+ layerAttribs = TMXLayerAttribNone;
+ parentElement = TMXPropertyNone;
+
+ [self parseXMLFile:filename_];
+ }
+ return self;
+}
+- (void) dealloc
+{
+ CCLOGINFO(@"cocos2d: deallocing %@", self);
+ [tilesets_ release];
+ [layers_ release];
+ [filename_ release];
+ [currentString release];
+ [objectGroups_ release];
+ [properties_ release];
+ [tileProperties_ release];
+ [super dealloc];
+}
+
+- (void) parseXMLFile:(NSString *)xmlFilename
+{
+ NSURL *url = [NSURL fileURLWithPath:[CCFileUtils fullPathFromRelativePath:xmlFilename] ];
+ NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
+
+ // we'll do the parsing
+ [parser setDelegate:self];
+ [parser setShouldProcessNamespaces:NO];
+ [parser setShouldReportNamespacePrefixes:NO];
+ [parser setShouldResolveExternalEntities:NO];
+ [parser parse];
+
+ NSAssert1( ! [parser parserError], @"Error parsing file: %@.", xmlFilename );
+
+ [parser release];
+}
+
+// the XML parser calls here with all the elements
+-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
+{
+ if([elementName isEqualToString:@"map"]) {
+ NSString *version = [attributeDict valueForKey:@"version"];
+ if( ! [version isEqualToString:@"1.0"] )
+ CCLOG(@"cocos2d: TMXFormat: Unsupported TMX version: %@", version);
+ NSString *orientationStr = [attributeDict valueForKey:@"orientation"];
+ if( [orientationStr isEqualToString:@"orthogonal"])
+ orientation_ = CCTMXOrientationOrtho;
+ else if ( [orientationStr isEqualToString:@"isometric"])
+ orientation_ = CCTMXOrientationIso;
+ else if( [orientationStr isEqualToString:@"hexagonal"])
+ orientation_ = CCTMXOrientationHex;
+ else
+ CCLOG(@"cocos2d: TMXFomat: Unsupported orientation: %@", orientation_);
+
+ mapSize_.width = [[attributeDict valueForKey:@"width"] intValue];
+ mapSize_.height = [[attributeDict valueForKey:@"height"] intValue];
+ tileSize_.width = [[attributeDict valueForKey:@"tilewidth"] intValue];
+ tileSize_.height = [[attributeDict valueForKey:@"tileheight"] intValue];
+
+ // The parent element is now "map"
+ parentElement = TMXPropertyMap;
+ } else if([elementName isEqualToString:@"tileset"]) {
+
+ // If this is an external tileset then start parsing that
+ NSString *externalTilesetFilename = [attributeDict valueForKey:@"source"];
+ if (externalTilesetFilename) {
+ // Tileset file will be relative to the map file. So we need to convert it to an absolute path
+ NSString *dir = [filename_ stringByDeletingLastPathComponent]; // Directory of map file
+ externalTilesetFilename = [dir stringByAppendingPathComponent:externalTilesetFilename]; // Append path to tileset file
+
+ [self parseXMLFile:externalTilesetFilename];
+ } else {
+
+ CCTMXTilesetInfo *tileset = [CCTMXTilesetInfo new];
+ tileset.name = [attributeDict valueForKey:@"name"];
+ tileset.firstGid = [[attributeDict valueForKey:@"firstgid"] intValue];
+ tileset.spacing = [[attributeDict valueForKey:@"spacing"] intValue];
+ tileset.margin = [[attributeDict valueForKey:@"margin"] intValue];
+ CGSize s;
+ s.width = [[attributeDict valueForKey:@"tilewidth"] intValue];
+ s.height = [[attributeDict valueForKey:@"tileheight"] intValue];
+ tileset.tileSize = s;
+
+ [tilesets_ addObject:tileset];
+ [tileset release];
+ }
+
+ }else if([elementName isEqualToString:@"tile"]){
+ CCTMXTilesetInfo* info = [tilesets_ lastObject];
+ NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithCapacity:3];
+ parentGID_ = [info firstGid] + [[attributeDict valueForKey:@"id"] intValue];
+ [tileProperties_ setObject:dict forKey:[NSNumber numberWithInt:parentGID_]];
+
+ parentElement = TMXPropertyTile;
+
+ }else if([elementName isEqualToString:@"layer"]) {
+ CCTMXLayerInfo *layer = [CCTMXLayerInfo new];
+ layer.name = [attributeDict valueForKey:@"name"];
+
+ CGSize s;
+ s.width = [[attributeDict valueForKey:@"width"] intValue];
+ s.height = [[attributeDict valueForKey:@"height"] intValue];
+ layer.layerSize = s;
+
+ layer.visible = ![[attributeDict valueForKey:@"visible"] isEqualToString:@"0"];
+
+ if( [attributeDict valueForKey:@"opacity"] )
+ layer.opacity = 255 * [[attributeDict valueForKey:@"opacity"] floatValue];
+ else
+ layer.opacity = 255;
+
+ int x = [[attributeDict valueForKey:@"x"] intValue];
+ int y = [[attributeDict valueForKey:@"y"] intValue];
+ layer.offset = ccp(x,y);
+
+ [layers_ addObject:layer];
+ [layer release];
+
+ // The parent element is now "layer"
+ parentElement = TMXPropertyLayer;
+
+ } else if([elementName isEqualToString:@"objectgroup"]) {
+
+ CCTMXObjectGroup *objectGroup = [[CCTMXObjectGroup alloc] init];
+ objectGroup.groupName = [attributeDict valueForKey:@"name"];
+ CGPoint positionOffset;
+ positionOffset.x = [[attributeDict valueForKey:@"x"] intValue] * tileSize_.width;
+ positionOffset.y = [[attributeDict valueForKey:@"y"] intValue] * tileSize_.height;
+ objectGroup.positionOffset = positionOffset;
+
+ [objectGroups_ addObject:objectGroup];
+ [objectGroup release];
+
+ // The parent element is now "objectgroup"
+ parentElement = TMXPropertyObjectGroup;
+
+ } else if([elementName isEqualToString:@"image"]) {
+
+ CCTMXTilesetInfo *tileset = [tilesets_ lastObject];
+
+ // build full path
+ NSString *imagename = [attributeDict valueForKey:@"source"];
+ NSString *path = [filename_ stringByDeletingLastPathComponent];
+ tileset.sourceImage = [path stringByAppendingPathComponent:imagename];
+
+ } else if([elementName isEqualToString:@"data"]) {
+ NSString *encoding = [attributeDict valueForKey:@"encoding"];
+ NSString *compression = [attributeDict valueForKey:@"compression"];
+
+ if( [encoding isEqualToString:@"base64"] ) {
+ layerAttribs |= TMXLayerAttribBase64;
+ storingCharacters = YES;
+
+ if( [compression isEqualToString:@"gzip"] )
+ layerAttribs |= TMXLayerAttribGzip;
+
+ else if( [compression isEqualToString:@"zlib"] )
+ layerAttribs |= TMXLayerAttribZlib;
+
+ NSAssert( !compression || [compression isEqualToString:@"gzip"] || [compression isEqualToString:@"zlib"], @"TMX: unsupported compression method" );
+ }
+
+ NSAssert( layerAttribs != TMXLayerAttribNone, @"TMX tile map: Only base64 and/or gzip/zlib maps are supported" );
+
+ } else if([elementName isEqualToString:@"object"]) {
+
+ CCTMXObjectGroup *objectGroup = [objectGroups_ lastObject];
+
+ // The value for "type" was blank or not a valid class name
+ // Create an instance of TMXObjectInfo to store the object and its properties
+ NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:5];
+
+ // Set the name of the object to the value for "name"
+ [dict setValue:[attributeDict valueForKey:@"name"] forKey:@"name"];
+
+ // Assign all the attributes as key/name pairs in the properties dictionary
+ [dict setValue:[attributeDict valueForKey:@"type"] forKey:@"type"];
+ int x = [[attributeDict valueForKey:@"x"] intValue] + objectGroup.positionOffset.x;
+ [dict setValue:[NSNumber numberWithInt:x] forKey:@"x"];
+ int y = [[attributeDict valueForKey:@"y"] intValue] + objectGroup.positionOffset.y;
+ // Correct y position. (Tiled uses Flipped, cocos2d uses Standard)
+ y = (mapSize_.height * tileSize_.height) - y - [[attributeDict valueForKey:@"height"] intValue];
+ [dict setValue:[NSNumber numberWithInt:y] forKey:@"y"];
+ [dict setValue:[attributeDict valueForKey:@"width"] forKey:@"width"];
+ [dict setValue:[attributeDict valueForKey:@"height"] forKey:@"height"];
+
+ // Add the object to the objectGroup
+ [[objectGroup objects] addObject:dict];
+ [dict release];
+
+ // The parent element is now "object"
+ parentElement = TMXPropertyObject;
+
+ } else if([elementName isEqualToString:@"property"]) {
+
+ if ( parentElement == TMXPropertyNone ) {
+
+ CCLOG( @"TMX tile map: Parent element is unsupported. Cannot add property named '%@' with value '%@'",
+ [attributeDict valueForKey:@"name"], [attributeDict valueForKey:@"value"] );
+
+ } else if ( parentElement == TMXPropertyMap ) {
+
+ // The parent element is the map
+ [properties_ setValue:[attributeDict valueForKey:@"value"] forKey:[attributeDict valueForKey:@"name"]];
+
+ } else if ( parentElement == TMXPropertyLayer ) {
+
+ // The parent element is the last layer
+ CCTMXLayerInfo *layer = [layers_ lastObject];
+ // Add the property to the layer
+ [[layer properties] setValue:[attributeDict valueForKey:@"value"] forKey:[attributeDict valueForKey:@"name"]];
+
+ } else if ( parentElement == TMXPropertyObjectGroup ) {
+
+ // The parent element is the last object group
+ CCTMXObjectGroup *objectGroup = [objectGroups_ lastObject];
+ [[objectGroup properties] setValue:[attributeDict valueForKey:@"value"] forKey:[attributeDict valueForKey:@"name"]];
+
+ } else if ( parentElement == TMXPropertyObject ) {
+
+ // The parent element is the last object
+ CCTMXObjectGroup *objectGroup = [objectGroups_ lastObject];
+ NSMutableDictionary *dict = [[objectGroup objects] lastObject];
+
+ NSString *propertyName = [attributeDict valueForKey:@"name"];
+ NSString *propertyValue = [attributeDict valueForKey:@"value"];
+
+ [dict setValue:propertyValue forKey:propertyName];
+ } else if ( parentElement == TMXPropertyTile ) {
+
+ NSMutableDictionary* dict = [tileProperties_ objectForKey:[NSNumber numberWithInt:parentGID_]];
+ NSString *propertyName = [attributeDict valueForKey:@"name"];
+ NSString *propertyValue = [attributeDict valueForKey:@"value"];
+ [dict setObject:propertyValue forKey:propertyName];
+
+ }
+ }
+}
+
+- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
+{
+ int len = 0;
+
+ if([elementName isEqualToString:@"data"] && layerAttribs&TMXLayerAttribBase64) {
+ storingCharacters = NO;
+
+ CCTMXLayerInfo *layer = [layers_ lastObject];
+
+ unsigned char *buffer;
+ len = base64Decode((unsigned char*)[currentString UTF8String], (unsigned int) [currentString length], &buffer);
+ if( ! buffer ) {
+ CCLOG(@"cocos2d: TiledMap: decode data error");
+ return;
+ }
+
+ if( layerAttribs & (TMXLayerAttribGzip | TMXLayerAttribZlib) ) {
+ unsigned char *deflated;
+ CGSize s = [layer layerSize];
+ int sizeHint = s.width * s.height * sizeof(uint32_t);
+
+ int inflatedLen = ccInflateMemoryWithHint(buffer, len, &deflated, sizeHint);
+ NSAssert( inflatedLen == sizeHint, @"CCTMXXMLParser: Hint failed!");
+
+ inflatedLen = (int)&inflatedLen; // XXX: to avoid warings in compiler
+
+ free( buffer );
+
+ if( ! deflated ) {
+ CCLOG(@"cocos2d: TiledMap: inflate data error");
+ return;
+ }
+
+ layer.tiles = (unsigned int*) deflated;
+ } else
+ layer.tiles = (unsigned int*) buffer;
+
+ [currentString setString:@""];
+
+ } else if ([elementName isEqualToString:@"map"]) {
+ // The map element has ended
+ parentElement = TMXPropertyNone;
+
+ } else if ([elementName isEqualToString:@"layer"]) {
+ // The layer element has ended
+ parentElement = TMXPropertyNone;
+
+ } else if ([elementName isEqualToString:@"objectgroup"]) {
+ // The objectgroup element has ended
+ parentElement = TMXPropertyNone;
+
+ } else if ([elementName isEqualToString:@"object"]) {
+ // The object element has ended
+ parentElement = TMXPropertyNone;
+ }
+}
+
+- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
+{
+ if (storingCharacters)
+ [currentString appendString:string];
+}
+
+
+//
+// the level did not load, file not found, etc.
+//
+-(void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError{
+ CCLOG(@"cocos2d: Error on XML Parse: %@", [parseError localizedDescription] );
+}
+
+@end
--- /dev/null
+/*
+
+===== IMPORTANT =====
+
+This is sample code demonstrating API, technology or techniques in development.
+Although this sample code has been reviewed for technical accuracy, it is not
+final. Apple is supplying this information to help you plan for the adoption of
+the technologies and programming interfaces described herein. This information
+is subject to change, and software implemented based on this sample code should
+be tested with final operating system software and final documentation. Newer
+versions of this sample code may be provided with future seeds of the API or
+technology. For information about updates to this and other developer
+documentation, view the New & Updated sidebars in subsequent documentation
+seeds.
+
+=====================
+
+File: Texture2D.h
+Abstract: Creates OpenGL 2D textures from images or text.
+
+Version: 1.6
+
+Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
+("Apple") in consideration of your agreement to the following terms, and your
+use, installation, modification or redistribution of this Apple software
+constitutes acceptance of these terms. If you do not agree with these terms,
+please do not use, install, modify or redistribute this Apple software.
+
+In consideration of your agreement to abide by the following terms, and subject
+to these terms, Apple grants you a personal, non-exclusive license, under
+Apple's copyrights in this original Apple software (the "Apple Software"), to
+use, reproduce, modify and redistribute the Apple Software, with or without
+modifications, in source and/or binary forms; provided that if you redistribute
+the Apple Software in its entirety and without modifications, you must retain
+this notice and the following text and disclaimers in all such redistributions
+of the Apple Software.
+Neither the name, trademarks, service marks or logos of Apple Inc. may be used
+to endorse or promote products derived from the Apple Software without specific
+prior written permission from Apple. Except as expressly stated in this notice,
+no other rights or licenses, express or implied, are granted by Apple herein,
+including but not limited to any patent rights that may be infringed by your
+derivative works or by other works in which the Apple Software may be
+incorporated.
+
+The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
+WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+COMBINATION WITH YOUR PRODUCTS.
+
+IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR
+DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF
+CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
+APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Copyright (C) 2008 Apple Inc. All Rights Reserved.
+
+*/
+
+#import <Availability.h>
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import <UIKit/UIKit.h> // for UIImage
+#endif
+
+#import <Foundation/Foundation.h> // for NSObject
+
+#import "Platforms/CCGL.h" // OpenGL stuff
+#import "Platforms/CCNS.h" // Next-Step stuff
+
+//CONSTANTS:
+
+/** @typedef CCTexture2DPixelFormat
+ Possible texture pixel formats
+ */
+typedef enum {
+ kCCTexture2DPixelFormat_Automatic = 0,
+ //! 32-bit texture: RGBA8888
+ kCCTexture2DPixelFormat_RGBA8888,
+ //! 16-bit texture without Alpha channel
+ kCCTexture2DPixelFormat_RGB565,
+ //! 8-bit textures used as masks
+ kCCTexture2DPixelFormat_A8,
+ //! 8-bit intensity texture
+ kCCTexture2DPixelFormat_I8,
+ //! 16-bit textures used as masks
+ kCCTexture2DPixelFormat_AI88,
+ //! 16-bit textures: RGBA4444
+ kCCTexture2DPixelFormat_RGBA4444,
+ //! 16-bit textures: RGB5A1
+ kCCTexture2DPixelFormat_RGB5A1,
+ //! 4-bit PVRTC-compressed texture: PVRTC4
+ kCCTexture2DPixelFormat_PVRTC4,
+ //! 2-bit PVRTC-compressed texture: PVRTC2
+ kCCTexture2DPixelFormat_PVRTC2,
+
+ //! Default texture format: RGBA8888
+ kCCTexture2DPixelFormat_Default = kCCTexture2DPixelFormat_RGBA8888,
+
+ // backward compatibility stuff
+ kTexture2DPixelFormat_Automatic = kCCTexture2DPixelFormat_Automatic,
+ kTexture2DPixelFormat_RGBA8888 = kCCTexture2DPixelFormat_RGBA8888,
+ kTexture2DPixelFormat_RGB565 = kCCTexture2DPixelFormat_RGB565,
+ kTexture2DPixelFormat_A8 = kCCTexture2DPixelFormat_A8,
+ kTexture2DPixelFormat_RGBA4444 = kCCTexture2DPixelFormat_RGBA4444,
+ kTexture2DPixelFormat_RGB5A1 = kCCTexture2DPixelFormat_RGB5A1,
+ kTexture2DPixelFormat_Default = kCCTexture2DPixelFormat_Default
+
+} CCTexture2DPixelFormat;
+
+//CLASS INTERFACES:
+
+/** CCTexture2D class.
+ * This class allows to easily create OpenGL 2D textures from images, text or raw data.
+ * The created CCTexture2D object will always have power-of-two dimensions.
+ * Depending on how you create the CCTexture2D object, the actual image area of the texture might be smaller than the texture dimensions i.e. "contentSize" != (pixelsWide, pixelsHigh) and (maxS, maxT) != (1.0, 1.0).
+ * Be aware that the content of the generated textures will be upside-down!
+ */
+@interface CCTexture2D : NSObject
+{
+ GLuint name_;
+ CGSize size_;
+ NSUInteger width_,
+ height_;
+ CCTexture2DPixelFormat format_;
+ GLfloat maxS_,
+ maxT_;
+ BOOL hasPremultipliedAlpha_;
+}
+/** Intializes with a texture2d with data */
+- (id) initWithData:(const void*)data pixelFormat:(CCTexture2DPixelFormat)pixelFormat pixelsWide:(NSUInteger)width pixelsHigh:(NSUInteger)height contentSize:(CGSize)size;
+
+/** These functions are needed to create mutable textures */
+- (void) releaseData:(void*)data;
+- (void*) keepData:(void*)data length:(NSUInteger)length;
+
+/** pixel format of the texture */
+@property(nonatomic,readonly) CCTexture2DPixelFormat pixelFormat;
+/** width in pixels */
+@property(nonatomic,readonly) NSUInteger pixelsWide;
+/** hight in pixels */
+@property(nonatomic,readonly) NSUInteger pixelsHigh;
+
+/** texture name */
+@property(nonatomic,readonly) GLuint name;
+
+/** returns content size of the texture in pixels */
+@property(nonatomic,readonly, nonatomic) CGSize contentSizeInPixels;
+
+/** texture max S */
+@property(nonatomic,readwrite) GLfloat maxS;
+/** texture max T */
+@property(nonatomic,readwrite) GLfloat maxT;
+/** whether or not the texture has their Alpha premultiplied */
+@property(nonatomic,readonly) BOOL hasPremultipliedAlpha;
+
+/** returns the content size of the texture in points */
+-(CGSize) contentSize;
+@end
+
+/**
+Drawing extensions to make it easy to draw basic quads using a CCTexture2D object.
+These functions require GL_TEXTURE_2D and both GL_VERTEX_ARRAY and GL_TEXTURE_COORD_ARRAY client states to be enabled.
+*/
+@interface CCTexture2D (Drawing)
+/** draws a texture at a given point */
+- (void) drawAtPoint:(CGPoint)point;
+/** draws a texture inside a rect */
+- (void) drawInRect:(CGRect)rect;
+@end
+
+/**
+Extensions to make it easy to create a CCTexture2D object from an image file.
+Note that RGBA type textures will have their alpha premultiplied - use the blending mode (GL_ONE, GL_ONE_MINUS_SRC_ALPHA).
+*/
+@interface CCTexture2D (Image)
+/** Initializes a texture from a UIImage object */
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+- (id) initWithImage:(UIImage *)uiImage;
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+- (id) initWithImage:(CGImageRef)cgImage;
+#endif
+@end
+
+/**
+Extensions to make it easy to create a CCTexture2D object from a string of text.
+Note that the generated textures are of type A8 - use the blending mode (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA).
+*/
+@interface CCTexture2D (Text)
+/** Initializes a texture from a string with dimensions, alignment, line break mode, font name and font size
+ Supported lineBreakModes:
+ - iOS: all UILineBreakMode supported modes
+ - Mac: Only NSLineBreakByWordWrapping is supported.
+ @since v1.0
+ */
+- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size;
+/** Initializes a texture from a string with dimensions, alignment, font name and font size */
+- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size;
+/** Initializes a texture from a string with font name and font size */
+- (id) initWithString:(NSString*)string fontName:(NSString*)name fontSize:(CGFloat)size;
+@end
+
+
+/**
+ Extensions to make it easy to create a CCTexture2D object from a PVRTC file
+ Note that the generated textures don't have their alpha premultiplied - use the blending mode (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA).
+ */
+@interface CCTexture2D (PVRSupport)
+/** Initializes a texture from a PVR Texture Compressed (PVRTC) buffer
+ *
+ * IMPORTANT: This method is only defined on iOS. It is not supported on the Mac version.
+ */
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+-(id) initWithPVRTCData: (const void*)data level:(int)level bpp:(int)bpp hasAlpha:(BOOL)hasAlpha length:(int)length pixelFormat:(CCTexture2DPixelFormat)pixelFormat;
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
+/** Initializes a texture from a PVR file.
+
+ Supported PVR formats:
+ - BGRA 8888
+ - RGBA 8888
+ - RGBA 4444
+ - RGBA 5551
+ - RBG 565
+ - A 8
+ - I 8
+ - AI 8
+ - PVRTC 2BPP
+ - PVRTC 4BPP
+
+ By default PVR images are treated as if they alpha channel is NOT premultiplied. You can override this behavior with this class method:
+ - PVRImagesHavePremultipliedAlpha:(BOOL)haveAlphaPremultiplied;
+
+ IMPORTANT: This method is only defined on iOS. It is not supported on the Mac version.
+
+ */
+-(id) initWithPVRFile: (NSString*) file;
+
+/** treats (or not) PVR files as if they have alpha premultiplied.
+ Since it is impossible to know at runtime if the PVR images have the alpha channel premultiplied, it is
+ possible load them as if they have (or not) the alpha channel premultiplied.
+
+ By default it is disabled.
+
+ @since v0.99.5
+ */
++(void) PVRImagesHavePremultipliedAlpha:(BOOL)haveAlphaPremultiplied;
+@end
+
+/**
+ Extension to set the Min / Mag filter
+ */
+typedef struct _ccTexParams {
+ GLuint minFilter;
+ GLuint magFilter;
+ GLuint wrapS;
+ GLuint wrapT;
+} ccTexParams;
+
+@interface CCTexture2D (GLFilter)
+/** sets the min filter, mag filter, wrap s and wrap t texture parameters.
+ If the texture size is NPOT (non power of 2), then in can only use GL_CLAMP_TO_EDGE in GL_TEXTURE_WRAP_{S,T}.
+ @since v0.8
+ */
+-(void) setTexParameters: (ccTexParams*) texParams;
+
+/** sets antialias texture parameters:
+ - GL_TEXTURE_MIN_FILTER = GL_LINEAR
+ - GL_TEXTURE_MAG_FILTER = GL_LINEAR
+
+ @since v0.8
+ */
+- (void) setAntiAliasTexParameters;
+
+/** sets alias texture parameters:
+ - GL_TEXTURE_MIN_FILTER = GL_NEAREST
+ - GL_TEXTURE_MAG_FILTER = GL_NEAREST
+
+ @since v0.8
+ */
+- (void) setAliasTexParameters;
+
+
+/** Generates mipmap images for the texture.
+ It only works if the texture size is POT (power of 2).
+ @since v0.99.0
+ */
+-(void) generateMipmap;
+
+
+@end
+
+@interface CCTexture2D (PixelFormat)
+/** sets the default pixel format for UIImages that contains alpha channel.
+ If the UIImage contains alpha channel, then the options are:
+ - generate 32-bit textures: kCCTexture2DPixelFormat_RGBA8888 (default one)
+ - generate 16-bit textures: kCCTexture2DPixelFormat_RGBA4444
+ - generate 16-bit textures: kCCTexture2DPixelFormat_RGB5A1
+ - generate 16-bit textures: kCCTexture2DPixelFormat_RGB565
+ - generate 8-bit textures: kCCTexture2DPixelFormat_A8 (only use it if you use just 1 color)
+
+ How does it work ?
+ - If the image is an RGBA (with Alpha) then the default pixel format will be used (it can be a 8-bit, 16-bit or 32-bit texture)
+ - If the image is an RGB (without Alpha) then an RGB565 texture will be used (16-bit texture)
+
+ This parameter is not valid for PVR images.
+
+ @since v0.8
+ */
++(void) setDefaultAlphaPixelFormat:(CCTexture2DPixelFormat)format;
+
+/** returns the alpha pixel format
+ @since v0.8
+ */
++(CCTexture2DPixelFormat) defaultAlphaPixelFormat;
+
+/** returns the bits-per-pixel of the in-memory OpenGL texture
+ @since v1.0
+ */
+-(NSInteger) bitsPerPixelForFormat;
+@end
+
+
+
+
+
--- /dev/null
+/*
+
+===== IMPORTANT =====
+
+This is sample code demonstrating API, technology or techniques in development.
+Although this sample code has been reviewed for technical accuracy, it is not
+final. Apple is supplying this information to help you plan for the adoption of
+the technologies and programming interfaces described herein. This information
+is subject to change, and software implemented based on this sample code should
+be tested with final operating system software and final documentation. Newer
+versions of this sample code may be provided with future seeds of the API or
+technology. For information about updates to this and other developer
+documentation, view the New & Updated sidebars in subsequent documentationd
+seeds.
+
+=====================
+
+File: Texture2D.m
+Abstract: Creates OpenGL 2D textures from images or text.
+
+Version: 1.6
+
+Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
+("Apple") in consideration of your agreement to the following terms, and your
+use, installation, modification or redistribution of this Apple software
+constitutes acceptance of these terms. If you do not agree with these terms,
+please do not use, install, modify or redistribute this Apple software.
+
+In consideration of your agreement to abide by the following terms, and subject
+to these terms, Apple grants you a personal, non-exclusive license, under
+Apple's copyrights in this original Apple software (the "Apple Software"), to
+use, reproduce, modify and redistribute the Apple Software, with or without
+modifications, in source and/or binary forms; provided that if you redistribute
+the Apple Software in its entirety and without modifications, you must retain
+this notice and the following text and disclaimers in all such redistributions
+of the Apple Software.
+Neither the name, trademarks, service marks or logos of Apple Inc. may be used
+to endorse or promote products derived from the Apple Software without specific
+prior written permission from Apple. Except as expressly stated in this notice,
+no other rights or licenses, express or implied, are granted by Apple herein,
+including but not limited to any patent rights that may be infringed by your
+derivative works or by other works in which the Apple Software may be
+incorporated.
+
+The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
+WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+COMBINATION WITH YOUR PRODUCTS.
+
+IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR
+DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF
+CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
+APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Copyright (C) 2008 Apple Inc. All Rights Reserved.
+
+*/
+
+/*
+ * Support for RGBA_4_4_4_4 and RGBA_5_5_5_1 was copied from:
+ * https://devforums.apple.com/message/37855#37855 by a1studmuffin
+ */
+
+
+#import <Availability.h>
+
+#import "Platforms/CCGL.h"
+#import "Platforms/CCNS.h"
+
+
+#import "CCTexture2D.h"
+#import "ccConfig.h"
+#import "ccMacros.h"
+#import "CCConfiguration.h"
+#import "Support/ccUtils.h"
+#import "CCTexturePVR.h"
+
+#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && CC_FONT_LABEL_SUPPORT
+// FontLabel support
+#import "FontManager.h"
+#import "FontLabelStringDrawing.h"
+#endif// CC_FONT_LABEL_SUPPORT
+
+
+// For Labels use 16-bit textures on iPhone 3GS / iPads since A8 textures are very slow
+#if (defined(__ARM_NEON__) || TARGET_IPHONE_SIMULATOR) && CC_USE_LA88_LABELS_ON_NEON_ARCH
+#define USE_TEXT_WITH_A8_TEXTURES 0
+
+#else
+#define USE_TEXT_WITH_A8_TEXTURES 1
+#endif
+
+//CLASS IMPLEMENTATIONS:
+
+
+// If the image has alpha, you can create RGBA8 (32-bit) or RGBA4 (16-bit) or RGB5A1 (16-bit)
+// Default is: RGBA8888 (32-bit textures)
+static CCTexture2DPixelFormat defaultAlphaPixelFormat_ = kCCTexture2DPixelFormat_Default;
+
+#pragma mark -
+#pragma mark CCTexture2D - Main
+
+@implementation CCTexture2D
+
+@synthesize contentSizeInPixels = size_, pixelFormat = format_, pixelsWide = width_, pixelsHigh = height_, name = name_, maxS = maxS_, maxT = maxT_;
+@synthesize hasPremultipliedAlpha = hasPremultipliedAlpha_;
+
+- (id) initWithData:(const void*)data pixelFormat:(CCTexture2DPixelFormat)pixelFormat pixelsWide:(NSUInteger)width pixelsHigh:(NSUInteger)height contentSize:(CGSize)size
+{
+ if((self = [super init])) {
+ glGenTextures(1, &name_);
+ glBindTexture(GL_TEXTURE_2D, name_);
+
+ [self setAntiAliasTexParameters];
+
+ // Specify OpenGL texture image
+
+ switch(pixelFormat)
+ {
+ case kCCTexture2DPixelFormat_RGBA8888:
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei) width, (GLsizei) height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
+ break;
+ case kCCTexture2DPixelFormat_RGBA4444:
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei) width, (GLsizei) height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data);
+ break;
+ case kCCTexture2DPixelFormat_RGB5A1:
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei) width, (GLsizei) height, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, data);
+ break;
+ case kCCTexture2DPixelFormat_RGB565:
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, (GLsizei) width, (GLsizei) height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data);
+ break;
+ case kCCTexture2DPixelFormat_AI88:
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, (GLsizei) width, (GLsizei) height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, data);
+ break;
+ case kCCTexture2DPixelFormat_A8:
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, (GLsizei) width, (GLsizei) height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, data);
+ break;
+ default:
+ [NSException raise:NSInternalInconsistencyException format:@""];
+
+ }
+
+ size_ = size;
+ width_ = width;
+ height_ = height;
+ format_ = pixelFormat;
+ maxS_ = size.width / (float)width;
+ maxT_ = size.height / (float)height;
+
+ hasPremultipliedAlpha_ = NO;
+ }
+ return self;
+}
+
+- (void) releaseData:(void*)data
+{
+ //Free data
+ free(data);
+}
+
+- (void*) keepData:(void*)data length:(NSUInteger)length
+{
+ //The texture data mustn't be saved becuase it isn't a mutable texture.
+ return data;
+}
+
+- (void) dealloc
+{
+ CCLOGINFO(@"cocos2d: deallocing %@", self);
+ if(name_)
+ glDeleteTextures(1, &name_);
+
+ [super dealloc];
+}
+
+- (NSString*) description
+{
+ return [NSString stringWithFormat:@"<%@ = %08X | Name = %i | Dimensions = %ix%i | Coordinates = (%.2f, %.2f)>", [self class], self, name_, width_, height_, maxS_, maxT_];
+}
+
+-(CGSize) contentSize
+{
+ CGSize ret;
+ ret.width = size_.width / CC_CONTENT_SCALE_FACTOR();
+ ret.height = size_.height / CC_CONTENT_SCALE_FACTOR();
+
+ return ret;
+}
+@end
+
+#pragma mark -
+#pragma mark CCTexture2D - Image
+
+@implementation CCTexture2D (Image)
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+- (id) initWithImage:(UIImage *)uiImage
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+- (id) initWithImage:(CGImageRef)CGImage
+#endif
+{
+ NSUInteger POTWide, POTHigh;
+ CGContextRef context = nil;
+ void* data = nil;;
+ CGColorSpaceRef colorSpace;
+ void* tempData;
+ unsigned int* inPixel32;
+ unsigned short* outPixel16;
+ BOOL hasAlpha;
+ CGImageAlphaInfo info;
+ CGSize imageSize;
+ CCTexture2DPixelFormat pixelFormat;
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+ CGImageRef CGImage = uiImage.CGImage;
+#endif
+
+ if(CGImage == NULL) {
+ CCLOG(@"cocos2d: CCTexture2D. Can't create Texture. UIImage is nil");
+ [self release];
+ return nil;
+ }
+
+ CCConfiguration *conf = [CCConfiguration sharedConfiguration];
+
+#if CC_TEXTURE_NPOT_SUPPORT
+ if( [conf supportsNPOT] ) {
+ POTWide = CGImageGetWidth(CGImage);
+ POTHigh = CGImageGetHeight(CGImage);
+
+ } else
+#endif
+ {
+ POTWide = ccNextPOT(CGImageGetWidth(CGImage));
+ POTHigh = ccNextPOT(CGImageGetHeight(CGImage));
+ }
+
+ NSUInteger maxTextureSize = [conf maxTextureSize];
+ if( POTHigh > maxTextureSize || POTWide > maxTextureSize ) {
+ CCLOG(@"cocos2d: WARNING: Image (%lu x %lu) is bigger than the supported %ld x %ld",
+ (long)POTWide, (long)POTHigh,
+ (long)maxTextureSize, (long)maxTextureSize);
+ [self release];
+ return nil;
+ }
+
+ info = CGImageGetAlphaInfo(CGImage);
+ hasAlpha = ((info == kCGImageAlphaPremultipliedLast) || (info == kCGImageAlphaPremultipliedFirst) || (info == kCGImageAlphaLast) || (info == kCGImageAlphaFirst) ? YES : NO);
+
+ size_t bpp = CGImageGetBitsPerComponent(CGImage);
+ colorSpace = CGImageGetColorSpace(CGImage);
+
+ if(colorSpace) {
+ if(hasAlpha || bpp >= 8)
+ pixelFormat = defaultAlphaPixelFormat_;
+ else {
+ CCLOG(@"cocos2d: CCTexture2D: Using RGB565 texture since image has no alpha");
+ pixelFormat = kCCTexture2DPixelFormat_RGB565;
+ }
+ } else {
+ // NOTE: No colorspace means a mask image
+ CCLOG(@"cocos2d: CCTexture2D: Using A8 texture since image is a mask");
+ pixelFormat = kCCTexture2DPixelFormat_A8;
+ }
+
+ imageSize = CGSizeMake(CGImageGetWidth(CGImage), CGImageGetHeight(CGImage));
+
+ // Create the bitmap graphics context
+
+ switch(pixelFormat) {
+ case kCCTexture2DPixelFormat_RGBA8888:
+ case kCCTexture2DPixelFormat_RGBA4444:
+ case kCCTexture2DPixelFormat_RGB5A1:
+ colorSpace = CGColorSpaceCreateDeviceRGB();
+ data = malloc(POTHigh * POTWide * 4);
+ info = hasAlpha ? kCGImageAlphaPremultipliedLast : kCGImageAlphaNoneSkipLast;
+// info = kCGImageAlphaPremultipliedLast; // issue #886. This patch breaks BMP images.
+ context = CGBitmapContextCreate(data, POTWide, POTHigh, 8, 4 * POTWide, colorSpace, info | kCGBitmapByteOrder32Big);
+ CGColorSpaceRelease(colorSpace);
+ break;
+
+ case kCCTexture2DPixelFormat_RGB565:
+ colorSpace = CGColorSpaceCreateDeviceRGB();
+ data = malloc(POTHigh * POTWide * 4);
+ info = kCGImageAlphaNoneSkipLast;
+ context = CGBitmapContextCreate(data, POTWide, POTHigh, 8, 4 * POTWide, colorSpace, info | kCGBitmapByteOrder32Big);
+ CGColorSpaceRelease(colorSpace);
+ break;
+ case kCCTexture2DPixelFormat_A8:
+ data = malloc(POTHigh * POTWide);
+ info = kCGImageAlphaOnly;
+ context = CGBitmapContextCreate(data, POTWide, POTHigh, 8, POTWide, NULL, info);
+ break;
+ default:
+ [NSException raise:NSInternalInconsistencyException format:@"Invalid pixel format"];
+ }
+
+
+ CGContextClearRect(context, CGRectMake(0, 0, POTWide, POTHigh));
+ CGContextTranslateCTM(context, 0, POTHigh - imageSize.height);
+ CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(CGImage), CGImageGetHeight(CGImage)), CGImage);
+
+ // Repack the pixel data into the right format
+
+ if(pixelFormat == kCCTexture2DPixelFormat_RGB565) {
+ //Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGGBBBBB"
+ tempData = malloc(POTHigh * POTWide * 2);
+ inPixel32 = (unsigned int*)data;
+ outPixel16 = (unsigned short*)tempData;
+ for(unsigned int i = 0; i < POTWide * POTHigh; ++i, ++inPixel32)
+ *outPixel16++ = ((((*inPixel32 >> 0) & 0xFF) >> 3) << 11) | ((((*inPixel32 >> 8) & 0xFF) >> 2) << 5) | ((((*inPixel32 >> 16) & 0xFF) >> 3) << 0);
+ free(data);
+ data = tempData;
+
+ }
+ else if (pixelFormat == kCCTexture2DPixelFormat_RGBA4444) {
+ //Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRGGGGBBBBAAAA"
+ tempData = malloc(POTHigh * POTWide * 2);
+ inPixel32 = (unsigned int*)data;
+ outPixel16 = (unsigned short*)tempData;
+ for(unsigned int i = 0; i < POTWide * POTHigh; ++i, ++inPixel32)
+ *outPixel16++ =
+ ((((*inPixel32 >> 0) & 0xFF) >> 4) << 12) | // R
+ ((((*inPixel32 >> 8) & 0xFF) >> 4) << 8) | // G
+ ((((*inPixel32 >> 16) & 0xFF) >> 4) << 4) | // B
+ ((((*inPixel32 >> 24) & 0xFF) >> 4) << 0); // A
+
+
+ free(data);
+ data = tempData;
+
+ }
+ else if (pixelFormat == kCCTexture2DPixelFormat_RGB5A1) {
+ //Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGBBBBBA"
+ tempData = malloc(POTHigh * POTWide * 2);
+ inPixel32 = (unsigned int*)data;
+ outPixel16 = (unsigned short*)tempData;
+ for(unsigned int i = 0; i < POTWide * POTHigh; ++i, ++inPixel32)
+ *outPixel16++ =
+ ((((*inPixel32 >> 0) & 0xFF) >> 3) << 11) | // R
+ ((((*inPixel32 >> 8) & 0xFF) >> 3) << 6) | // G
+ ((((*inPixel32 >> 16) & 0xFF) >> 3) << 1) | // B
+ ((((*inPixel32 >> 24) & 0xFF) >> 7) << 0); // A
+
+
+ free(data);
+ data = tempData;
+ }
+ self = [self initWithData:data pixelFormat:pixelFormat pixelsWide:POTWide pixelsHigh:POTHigh contentSize:imageSize];
+
+ // should be after calling super init
+ hasPremultipliedAlpha_ = (info == kCGImageAlphaPremultipliedLast || info == kCGImageAlphaPremultipliedFirst);
+
+ CGContextRelease(context);
+ [self releaseData:data];
+
+ return self;
+}
+@end
+
+#pragma mark -
+#pragma mark CCTexture2D - Text
+
+@implementation CCTexture2D (Text)
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode font:(id)uifont
+{
+ NSAssert( uifont, @"Invalid font");
+
+ NSUInteger POTWide = ccNextPOT(dimensions.width);
+ NSUInteger POTHigh = ccNextPOT(dimensions.height);
+ unsigned char* data;
+
+ CGContextRef context;
+ CGColorSpaceRef colorSpace;
+
+#if USE_TEXT_WITH_A8_TEXTURES
+ data = calloc(POTHigh, POTWide);
+#else
+ data = calloc(POTHigh, POTWide * 2);
+#endif
+
+ colorSpace = CGColorSpaceCreateDeviceGray();
+ context = CGBitmapContextCreate(data, POTWide, POTHigh, 8, POTWide, colorSpace, kCGImageAlphaNone);
+ CGColorSpaceRelease(colorSpace);
+
+ if( ! context ) {
+ free(data);
+ [self release];
+ return nil;
+ }
+
+ CGContextSetGrayFillColor(context, 1.0f, 1.0f);
+ CGContextTranslateCTM(context, 0.0f, POTHigh);
+ CGContextScaleCTM(context, 1.0f, -1.0f); //NOTE: NSString draws in UIKit referential i.e. renders upside-down compared to CGBitmapContext referential
+
+ UIGraphicsPushContext(context);
+
+ // normal fonts
+ if( [uifont isKindOfClass:[UIFont class] ] )
+ [string drawInRect:CGRectMake(0, 0, dimensions.width, dimensions.height) withFont:uifont lineBreakMode:lineBreakMode alignment:alignment];
+
+#if CC_FONT_LABEL_SUPPORT
+ else // ZFont class
+ [string drawInRect:CGRectMake(0, 0, dimensions.width, dimensions.height) withZFont:uifont lineBreakMode:lineBreakMode alignment:alignment];
+#endif
+
+ UIGraphicsPopContext();
+
+#if USE_TEXT_WITH_A8_TEXTURES
+ self = [self initWithData:data pixelFormat:kCCTexture2DPixelFormat_A8 pixelsWide:POTWide pixelsHigh:POTHigh contentSize:dimensions];
+
+#else // ! USE_TEXT_WITH_A8_TEXTURES
+ NSUInteger textureSize = POTWide*POTHigh;
+ unsigned short *la88_data = (unsigned short*)data;
+ for(int i = textureSize-1; i>=0; i--) //Convert A8 to AI88
+ la88_data[i] = (data[i] << 8) | 0xff;
+
+ self = [self initWithData:data pixelFormat:kCCTexture2DPixelFormat_AI88 pixelsWide:POTWide pixelsHigh:POTHigh contentSize:dimensions];
+#endif // ! USE_TEXT_WITH_A8_TEXTURES
+
+ CGContextRelease(context);
+ [self releaseData:data];
+
+ return self;
+}
+
+
+
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment attributedString:(NSAttributedString*)stringWithAttributes
+{
+ NSAssert( stringWithAttributes, @"Invalid stringWithAttributes");
+
+ NSUInteger POTWide = ccNextPOT(dimensions.width);
+ NSUInteger POTHigh = ccNextPOT(dimensions.height);
+ unsigned char* data;
+
+ NSSize realDimensions = [stringWithAttributes size];
+
+ //Alignment
+ float xPadding = 0;
+
+ // Mac crashes if the width or height is 0
+ if( realDimensions.width > 0 && realDimensions.height > 0 ) {
+ switch (alignment) {
+ case CCTextAlignmentLeft: xPadding = 0; break;
+ case CCTextAlignmentCenter: xPadding = (dimensions.width-realDimensions.width)/2.0f; break;
+ case CCTextAlignmentRight: xPadding = dimensions.width-realDimensions.width; break;
+ default: break;
+ }
+
+ //Disable antialias
+ [[NSGraphicsContext currentContext] setShouldAntialias:NO];
+
+ NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize(POTWide, POTHigh)];
+ [image lockFocus];
+
+ [stringWithAttributes drawAtPoint:NSMakePoint(xPadding, POTHigh-dimensions.height)]; // draw at offset position
+
+ NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMakeRect (0.0f, 0.0f, POTWide, POTHigh)];
+ [image unlockFocus];
+
+ data = (unsigned char*) [bitmap bitmapData]; //Use the same buffer to improve the performance.
+
+ NSUInteger textureSize = POTWide*POTHigh;
+ for(int i = 0; i<textureSize; i++) //Convert RGBA8888 to A8
+ data[i] = data[i*4+3];
+
+ data = [self keepData:data length:textureSize];
+ self = [self initWithData:data pixelFormat:kCCTexture2DPixelFormat_A8 pixelsWide:POTWide pixelsHigh:POTHigh contentSize:dimensions];
+
+ [bitmap release];
+ [image release];
+
+ } else {
+ [self release];
+ return nil;
+ }
+
+ return self;
+}
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
+
+- (id) initWithString:(NSString*)string fontName:(NSString*)name fontSize:(CGFloat)size
+{
+ CGSize dim;
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+ id font;
+ font = [UIFont fontWithName:name size:size];
+ if( font )
+ dim = [string sizeWithFont:font];
+
+#if CC_FONT_LABEL_SUPPORT
+ if( ! font ){
+ font = [[FontManager sharedManager] zFontWithName:name pointSize:size];
+ if (font)
+ dim = [string sizeWithZFont:font];
+ }
+#endif // CC_FONT_LABEL_SUPPORT
+
+ if( ! font ) {
+ CCLOG(@"cocos2d: Unable to load font %@", name);
+ [self release];
+ return nil;
+ }
+
+ return [self initWithString:string dimensions:dim alignment:CCTextAlignmentCenter lineBreakMode:UILineBreakModeWordWrap font:font];
+
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+ {
+
+ NSAttributedString *stringWithAttributes =
+ [[[NSAttributedString alloc] initWithString:string
+ attributes:[NSDictionary dictionaryWithObject:[[NSFontManager sharedFontManager]
+ fontWithFamily:name
+ traits:NSUnboldFontMask | NSUnitalicFontMask
+ weight:0
+ size:size]
+ forKey:NSFontAttributeName]
+ ]
+ autorelease];
+
+ dim = NSSizeToCGSize( [stringWithAttributes size] );
+
+ return [self initWithString:string dimensions:dim alignment:CCTextAlignmentCenter attributedString:stringWithAttributes];
+ }
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
+
+}
+
+- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size
+{
+ return [self initWithString:string dimensions:dimensions alignment:alignment lineBreakMode:CCLineBreakModeWordWrap fontName:name fontSize:size];
+}
+
+- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size
+{
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+ id uifont = nil;
+
+ uifont = [UIFont fontWithName:name size:size];
+
+#if CC_FONT_LABEL_SUPPORT
+ if( ! uifont )
+ uifont = [[FontManager sharedManager] zFontWithName:name pointSize:size];
+#endif // CC_FONT_LABEL_SUPPORT
+ if( ! uifont ) {
+ CCLOG(@"cocos2d: Texture2d: Invalid Font: %@. Verify the .ttf name", name);
+ [self release];
+ return nil;
+ }
+
+ return [self initWithString:string dimensions:dimensions alignment:alignment lineBreakMode:lineBreakMode font:uifont];
+
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+ NSAssert( lineBreakMode == CCLineBreakModeWordWrap, @"CCTexture2D: unsupported line break mode for Mac OS X");
+
+ //String with attributes
+ NSAttributedString *stringWithAttributes =
+ [[[NSAttributedString alloc] initWithString:string
+ attributes:[NSDictionary dictionaryWithObject:[[NSFontManager sharedFontManager]
+ fontWithFamily:name
+ traits:NSUnboldFontMask | NSUnitalicFontMask
+ weight:0
+ size:size]
+ forKey:NSFontAttributeName]
+ ]
+ autorelease];
+
+ return [self initWithString:string dimensions:dimensions alignment:alignment attributedString:stringWithAttributes];
+
+#endif // Mac
+}
+@end
+
+#pragma mark -
+#pragma mark CCTexture2D - PVRSupport
+
+@implementation CCTexture2D (PVRSupport)
+
+// By default PVR images are treated as if they don't have the alpha channel premultiplied
+static BOOL PVRHaveAlphaPremultiplied_ = NO;
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+-(id) initWithPVRTCData: (const void*)data level:(int)level bpp:(int)bpp hasAlpha:(BOOL)hasAlpha length:(int)length pixelFormat:(CCTexture2DPixelFormat)pixelFormat
+{
+ // GLint saveName;
+
+ if( ! [[CCConfiguration sharedConfiguration] supportsPVRTC] ) {
+ CCLOG(@"cocos2d: WARNING: PVRTC images is not supported");
+ [self release];
+ return nil;
+ }
+
+ if((self = [super init])) {
+ glGenTextures(1, &name_);
+ glBindTexture(GL_TEXTURE_2D, name_);
+
+ [self setAntiAliasTexParameters];
+
+ GLenum format;
+ GLsizei size = length * length * bpp / 8;
+ if(hasAlpha)
+ format = (bpp == 4) ? GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG : GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
+ else
+ format = (bpp == 4) ? GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG : GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
+
+ if(size < 32)
+ size = 32;
+
+ glCompressedTexImage2D(GL_TEXTURE_2D, level, format, length, length, 0, size, data);
+
+ size_ = CGSizeMake(length, length);
+ width_ = length;
+ height_ = length;
+ maxS_ = 1.0f;
+ maxT_ = 1.0f;
+ hasPremultipliedAlpha_ = PVRHaveAlphaPremultiplied_;
+ format_ = pixelFormat;
+ }
+ return self;
+}
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
+
+-(id) initWithPVRFile: (NSString*) file
+{
+ if( (self = [super init]) ) {
+ CCTexturePVR *pvr = [[CCTexturePVR alloc] initWithContentsOfFile:file];
+ if( pvr ) {
+ pvr.retainName = YES; // don't dealloc texture on release
+
+ name_ = pvr.name; // texture id
+ maxS_ = 1; // only POT texture are supported
+ maxT_ = 1;
+ width_ = pvr.width;
+ height_ = pvr.height;
+ size_ = CGSizeMake(width_, height_);
+ hasPremultipliedAlpha_ = PVRHaveAlphaPremultiplied_;
+ format_ = pvr.format;
+
+ [pvr release];
+
+ [self setAntiAliasTexParameters];
+ } else {
+
+ CCLOG(@"cocos2d: Couldn't load PVR image: %@", file);
+ [self release];
+ return nil;
+ }
+ }
+ return self;
+}
+
++(void) PVRImagesHavePremultipliedAlpha:(BOOL)haveAlphaPremultiplied
+{
+ PVRHaveAlphaPremultiplied_ = haveAlphaPremultiplied;
+}
+@end
+
+#pragma mark -
+#pragma mark CCTexture2D - Drawing
+
+@implementation CCTexture2D (Drawing)
+
+- (void) drawAtPoint:(CGPoint)point
+{
+ GLfloat coordinates[] = { 0.0f, maxT_,
+ maxS_, maxT_,
+ 0.0f, 0.0f,
+ maxS_, 0.0f };
+ GLfloat width = (GLfloat)width_ * maxS_,
+ height = (GLfloat)height_ * maxT_;
+
+ GLfloat vertices[] = { point.x, point.y, 0.0f,
+ width + point.x, point.y, 0.0f,
+ point.x, height + point.y, 0.0f,
+ width + point.x, height + point.y, 0.0f };
+
+ glBindTexture(GL_TEXTURE_2D, name_);
+ glVertexPointer(3, GL_FLOAT, 0, vertices);
+ glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+}
+
+
+- (void) drawInRect:(CGRect)rect
+{
+ GLfloat coordinates[] = { 0.0f, maxT_,
+ maxS_, maxT_,
+ 0.0f, 0.0f,
+ maxS_, 0.0f };
+ GLfloat vertices[] = { rect.origin.x, rect.origin.y, /*0.0f,*/
+ rect.origin.x + rect.size.width, rect.origin.y, /*0.0f,*/
+ rect.origin.x, rect.origin.y + rect.size.height, /*0.0f,*/
+ rect.origin.x + rect.size.width, rect.origin.y + rect.size.height, /*0.0f*/ };
+
+ glBindTexture(GL_TEXTURE_2D, name_);
+ glVertexPointer(2, GL_FLOAT, 0, vertices);
+ glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+}
+
+@end
+
+
+#pragma mark -
+#pragma mark CCTexture2D - GLFilter
+
+//
+// Use to apply MIN/MAG filter
+//
+@implementation CCTexture2D (GLFilter)
+
+-(void) generateMipmap
+{
+ NSAssert( width_ == ccNextPOT(width_) && height_ == ccNextPOT(height_), @"Mimpap texture only works in POT textures");
+ glBindTexture( GL_TEXTURE_2D, name_ );
+ ccglGenerateMipmap(GL_TEXTURE_2D);
+}
+
+-(void) setTexParameters: (ccTexParams*) texParams
+{
+ NSAssert( (width_ == ccNextPOT(width_) && height_ == ccNextPOT(height_)) ||
+ (texParams->wrapS == GL_CLAMP_TO_EDGE && texParams->wrapT == GL_CLAMP_TO_EDGE),
+ @"GL_CLAMP_TO_EDGE should be used in NPOT textures");
+ glBindTexture( GL_TEXTURE_2D, name_ );
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, texParams->minFilter );
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, texParams->magFilter );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texParams->wrapS );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texParams->wrapT );
+}
+
+-(void) setAliasTexParameters
+{
+ ccTexParams texParams = { GL_NEAREST, GL_NEAREST, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE };
+ [self setTexParameters: &texParams];
+}
+
+-(void) setAntiAliasTexParameters
+{
+ ccTexParams texParams = { GL_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE };
+ [self setTexParameters: &texParams];
+}
+@end
+
+
+#pragma mark -
+#pragma mark CCTexture2D - Pixel Format
+
+//
+// Texture options for images that contains alpha
+//
+@implementation CCTexture2D (PixelFormat)
++(void) setDefaultAlphaPixelFormat:(CCTexture2DPixelFormat)format
+{
+ defaultAlphaPixelFormat_ = format;
+}
+
++(CCTexture2DPixelFormat) defaultAlphaPixelFormat
+{
+ return defaultAlphaPixelFormat_;
+}
+
+-(NSInteger) bitsPerPixelForFormat
+{
+ NSInteger ret=-1;
+
+ switch (format_) {
+ case kCCTexture2DPixelFormat_RGBA8888:
+ ret = 32;
+ break;
+ case kCCTexture2DPixelFormat_RGB565:
+ ret = 16;
+ break;
+ case kCCTexture2DPixelFormat_A8:
+ ret = 8;
+ break;
+ case kCCTexture2DPixelFormat_RGBA4444:
+ ret = 16;
+ break;
+ case kCCTexture2DPixelFormat_RGB5A1:
+ ret = 16;
+ break;
+ case kCCTexture2DPixelFormat_PVRTC4:
+ ret = 4;
+ break;
+ case kCCTexture2DPixelFormat_PVRTC2:
+ ret = 2;
+ break;
+ case kCCTexture2DPixelFormat_I8:
+ ret = 8;
+ break;
+ case kCCTexture2DPixelFormat_AI88:
+ ret = 16;
+ break;
+ default:
+ ret = -1;
+ NSAssert1(NO , @"bitsPerPixelForFormat: %ld, unrecognised pixel format", (long)format_);
+ CCLOG(@"bitsPerPixelForFormat: %ld, cannot give useful result", (long)format_);
+ break;
+ }
+ return ret;
+}
+@end
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "CCTexture2D.h"
+#import "ccTypes.h"
+#import "ccConfig.h"
+
+/** A class that implements a Texture Atlas.
+ Supported features:
+ * The atlas file can be a PVRTC, PNG or any other fomrat supported by Texture2D
+ * Quads can be udpated in runtime
+ * Quads can be added in runtime
+ * Quads can be removed in runtime
+ * Quads can be re-ordered in runtime
+ * The TextureAtlas capacity can be increased or decreased in runtime
+ * OpenGL component: V3F, C4B, T2F.
+ The quads are rendered using an OpenGL ES VBO.
+ To render the quads using an interleaved vertex array list, you should modify the ccConfig.h file
+ */
+@interface CCTextureAtlas : NSObject
+{
+ NSUInteger totalQuads_;
+ NSUInteger capacity_;
+ ccV3F_C4B_T2F_Quad *quads_; // quads to be rendered
+ GLushort *indices_;
+ CCTexture2D *texture_;
+#if CC_USES_VBO
+ GLuint buffersVBO_[2]; //0: vertex 1: indices
+ BOOL dirty_; //indicates whether or not the array buffer of the VBO needs to be updated
+#endif // CC_USES_VBO
+}
+
+/** quantity of quads that are going to be drawn */
+@property (nonatomic,readonly) NSUInteger totalQuads;
+/** quantity of quads that can be stored with the current texture atlas size */
+@property (nonatomic,readonly) NSUInteger capacity;
+/** Texture of the texture atlas */
+@property (nonatomic,retain) CCTexture2D *texture;
+/** Quads that are going to be rendered */
+@property (nonatomic,readwrite) ccV3F_C4B_T2F_Quad *quads;
+
+/** creates a TextureAtlas with an filename and with an initial capacity for Quads.
+ * The TextureAtlas capacity can be increased in runtime.
+ */
++(id) textureAtlasWithFile:(NSString*)file capacity:(NSUInteger)capacity;
+
+/** initializes a TextureAtlas with a filename and with a certain capacity for Quads.
+ * The TextureAtlas capacity can be increased in runtime.
+ *
+ * WARNING: Do not reinitialize the TextureAtlas because it will leak memory (issue #706)
+ */
+-(id) initWithFile: (NSString*) file capacity:(NSUInteger)capacity;
+
+/** creates a TextureAtlas with a previously initialized Texture2D object, and
+ * with an initial capacity for n Quads.
+ * The TextureAtlas capacity can be increased in runtime.
+ */
++(id) textureAtlasWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity;
+
+/** initializes a TextureAtlas with a previously initialized Texture2D object, and
+ * with an initial capacity for Quads.
+ * The TextureAtlas capacity can be increased in runtime.
+ *
+ * WARNING: Do not reinitialize the TextureAtlas because it will leak memory (issue #706)
+ */
+-(id) initWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity;
+
+/** updates a Quad (texture, vertex and color) at a certain index
+ * index must be between 0 and the atlas capacity - 1
+ @since v0.8
+ */
+-(void) updateQuad:(ccV3F_C4B_T2F_Quad*)quad atIndex:(NSUInteger)index;
+
+/** Inserts a Quad (texture, vertex and color) at a certain index
+ index must be between 0 and the atlas capacity - 1
+ @since v0.8
+ */
+-(void) insertQuad:(ccV3F_C4B_T2F_Quad*)quad atIndex:(NSUInteger)index;
+
+/** Removes the quad that is located at a certain index and inserts it at a new index
+ This operation is faster than removing and inserting in a quad in 2 different steps
+ @since v0.7.2
+*/
+-(void) insertQuadFromIndex:(NSUInteger)fromIndex atIndex:(NSUInteger)newIndex;
+
+/** removes a quad at a given index number.
+ The capacity remains the same, but the total number of quads to be drawn is reduced in 1
+ @since v0.7.2
+ */
+-(void) removeQuadAtIndex:(NSUInteger) index;
+
+/** removes all Quads.
+ The TextureAtlas capacity remains untouched. No memory is freed.
+ The total number of quads to be drawn will be 0
+ @since v0.7.2
+ */
+-(void) removeAllQuads;
+
+/** resize the capacity of the CCTextureAtlas.
+ * The new capacity can be lower or higher than the current one
+ * It returns YES if the resize was successful.
+ * If it fails to resize the capacity it will return NO with a new capacity of 0.
+ */
+-(BOOL) resizeCapacity: (NSUInteger) n;
+
+
+/** draws n quads
+ * n can't be greater than the capacity of the Atlas
+ */
+-(void) drawNumberOfQuads: (NSUInteger) n;
+
+
+/** draws n quads from an index (offset).
+ n + start can't be greater than the capacity of the atlas
+
+ @since v1.0
+ */
+-(void) drawNumberOfQuads: (NSUInteger) n fromIndex: (NSUInteger) start;
+
+/** draws all the Atlas's Quads
+ */
+-(void) drawQuads;
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+// cocos2d
+#import "CCTextureAtlas.h"
+#import "ccMacros.h"
+#import "CCTexture2D.h"
+#import "CCTextureCache.h"
+
+
+@interface CCTextureAtlas (Private)
+-(void) initIndices;
+@end
+
+//According to some tests GL_TRIANGLE_STRIP is slower, MUCH slower. Probably I'm doing something very wrong
+
+@implementation CCTextureAtlas
+
+@synthesize totalQuads = totalQuads_, capacity = capacity_;
+@synthesize texture = texture_;
+@synthesize quads = quads_;
+
+#pragma mark TextureAtlas - alloc & init
+
++(id) textureAtlasWithFile:(NSString*) file capacity: (NSUInteger) n
+{
+ return [[[self alloc] initWithFile:file capacity:n] autorelease];
+}
+
++(id) textureAtlasWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)n
+{
+ return [[[self alloc] initWithTexture:tex capacity:n] autorelease];
+}
+
+-(id) initWithFile:(NSString*)file capacity:(NSUInteger)n
+{
+ // retained in property
+ CCTexture2D *tex = [[CCTextureCache sharedTextureCache] addImage:file];
+ if( tex )
+ return [self initWithTexture:tex capacity:n];
+
+ // else
+ {
+ CCLOG(@"cocos2d: Could not open file: %@", file);
+ [self release];
+ return nil;
+ }
+}
+
+-(id) initWithTexture:(CCTexture2D*)tex capacity:(NSUInteger)n
+{
+ if( (self=[super init]) ) {
+
+ capacity_ = n;
+ totalQuads_ = 0;
+
+ // retained in property
+ self.texture = tex;
+
+ // Re-initialization is not allowed
+ NSAssert(quads_==nil && indices_==nil, @"CCTextureAtlas re-initialization is not allowed");
+
+ quads_ = calloc( sizeof(quads_[0]) * capacity_, 1 );
+ indices_ = calloc( sizeof(indices_[0]) * capacity_ * 6, 1 );
+
+ if( ! ( quads_ && indices_) ) {
+ CCLOG(@"cocos2d: CCTextureAtlas: not enough memory");
+ if( quads_ )
+ free(quads_);
+ if( indices_ )
+ free(indices_);
+ return nil;
+ }
+
+#if CC_USES_VBO
+ // initial binding
+ glGenBuffers(2, &buffersVBO_[0]);
+ dirty_ = YES;
+#endif // CC_USES_VBO
+
+ [self initIndices];
+ }
+
+ return self;
+}
+
+- (NSString*) description
+{
+ return [NSString stringWithFormat:@"<%@ = %08X | totalQuads = %i>", [self class], self, totalQuads_];
+}
+
+-(void) dealloc
+{
+ CCLOGINFO(@"cocos2d: deallocing %@",self);
+
+ free(quads_);
+ free(indices_);
+
+#if CC_USES_VBO
+ glDeleteBuffers(2, buffersVBO_);
+#endif // CC_USES_VBO
+
+
+ [texture_ release];
+
+ [super dealloc];
+}
+
+-(void) initIndices
+{
+ for( NSUInteger i=0;i< capacity_;i++) {
+#if CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP
+ indices_[i*6+0] = i*4+0;
+ indices_[i*6+1] = i*4+0;
+ indices_[i*6+2] = i*4+2;
+ indices_[i*6+3] = i*4+1;
+ indices_[i*6+4] = i*4+3;
+ indices_[i*6+5] = i*4+3;
+#else
+ indices_[i*6+0] = i*4+0;
+ indices_[i*6+1] = i*4+1;
+ indices_[i*6+2] = i*4+2;
+
+ // inverted index. issue #179
+ indices_[i*6+3] = i*4+3;
+ indices_[i*6+4] = i*4+2;
+ indices_[i*6+5] = i*4+1;
+// indices_[i*6+3] = i*4+2;
+// indices_[i*6+4] = i*4+3;
+// indices_[i*6+5] = i*4+1;
+#endif
+ }
+
+#if CC_USES_VBO
+ glBindBuffer(GL_ARRAY_BUFFER, buffersVBO_[0]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(quads_[0]) * capacity_, quads_, GL_DYNAMIC_DRAW);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffersVBO_[1]);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices_[0]) * capacity_ * 6, indices_, GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+#endif // CC_USES_VBO
+}
+
+#pragma mark TextureAtlas - Update, Insert, Move & Remove
+
+-(void) updateQuad:(ccV3F_C4B_T2F_Quad*)quad atIndex:(NSUInteger) n
+{
+ NSAssert(n < capacity_, @"updateQuadWithTexture: Invalid index");
+
+ totalQuads_ = MAX( n+1, totalQuads_);
+
+ quads_[n] = *quad;
+
+#if CC_USES_VBO
+ dirty_ = YES;
+#endif
+}
+
+
+-(void) insertQuad:(ccV3F_C4B_T2F_Quad*)quad atIndex:(NSUInteger)index
+{
+ NSAssert(index < capacity_, @"insertQuadWithTexture: Invalid index");
+
+ totalQuads_++;
+ NSAssert( totalQuads_ <= capacity_, @"invalid totalQuads");
+
+ // issue #575. index can be > totalQuads
+ NSInteger remaining = (totalQuads_-1) - index;
+
+ // last object doesn't need to be moved
+ if( remaining > 0)
+ // tex coordinates
+ memmove( &quads_[index+1],&quads_[index], sizeof(quads_[0]) * remaining );
+
+ quads_[index] = *quad;
+
+#if CC_USES_VBO
+ dirty_ = YES;
+#endif
+}
+
+
+-(void) insertQuadFromIndex:(NSUInteger)oldIndex atIndex:(NSUInteger)newIndex
+{
+ NSAssert(newIndex < totalQuads_, @"insertQuadFromIndex:atIndex: Invalid index");
+ NSAssert(oldIndex < totalQuads_, @"insertQuadFromIndex:atIndex: Invalid index");
+
+ if( oldIndex == newIndex )
+ return;
+
+ NSUInteger howMany = labs( oldIndex - newIndex);
+ NSUInteger dst = oldIndex;
+ NSUInteger src = oldIndex + 1;
+ if( oldIndex > newIndex) {
+ dst = newIndex+1;
+ src = newIndex;
+ }
+
+ // tex coordinates
+ ccV3F_C4B_T2F_Quad quadsBackup = quads_[oldIndex];
+ memmove( &quads_[dst],&quads_[src], sizeof(quads_[0]) * howMany );
+ quads_[newIndex] = quadsBackup;
+
+#if CC_USES_VBO
+ dirty_ = YES;
+#endif
+}
+
+-(void) removeQuadAtIndex:(NSUInteger) index
+{
+ NSAssert(index < totalQuads_, @"removeQuadAtIndex: Invalid index");
+
+ NSUInteger remaining = (totalQuads_-1) - index;
+
+
+ // last object doesn't need to be moved
+ if( remaining )
+ // tex coordinates
+ memmove( &quads_[index],&quads_[index+1], sizeof(quads_[0]) * remaining );
+
+ totalQuads_--;
+
+#if CC_USES_VBO
+ dirty_ = YES;
+#endif
+}
+
+-(void) removeAllQuads
+{
+ totalQuads_ = 0;
+}
+
+#pragma mark TextureAtlas - Resize
+
+-(BOOL) resizeCapacity: (NSUInteger) newCapacity
+{
+ if( newCapacity == capacity_ )
+ return YES;
+
+ // update capacity and totolQuads
+ totalQuads_ = MIN(totalQuads_,newCapacity);
+ capacity_ = newCapacity;
+
+ void * tmpQuads = realloc( quads_, sizeof(quads_[0]) * capacity_ );
+ void * tmpIndices = realloc( indices_, sizeof(indices_[0]) * capacity_ * 6 );
+
+ if( ! ( tmpQuads && tmpIndices) ) {
+ CCLOG(@"cocos2d: CCTextureAtlas: not enough memory");
+ if( tmpQuads )
+ free(tmpQuads);
+ else
+ free(quads_);
+
+ if( tmpIndices )
+ free(tmpIndices);
+ else
+ free(indices_);
+
+ indices_ = nil;
+ quads_ = nil;
+ capacity_ = totalQuads_ = 0;
+ return NO;
+ }
+
+ quads_ = tmpQuads;
+ indices_ = tmpIndices;
+
+ [self initIndices];
+
+#if CC_USES_VBO
+ dirty_ = YES;
+#endif
+ return YES;
+}
+
+#pragma mark TextureAtlas - Drawing
+
+-(void) drawQuads
+{
+ [self drawNumberOfQuads: totalQuads_ fromIndex:0];
+}
+
+-(void) drawNumberOfQuads: (NSUInteger) n
+{
+ [self drawNumberOfQuads:n fromIndex:0];
+}
+
+-(void) drawNumberOfQuads: (NSUInteger) n fromIndex: (NSUInteger) start
+{
+ // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
+ // Unneeded states: -
+
+ glBindTexture(GL_TEXTURE_2D, [texture_ name]);
+#define kQuadSize sizeof(quads_[0].bl)
+#if CC_USES_VBO
+ glBindBuffer(GL_ARRAY_BUFFER, buffersVBO_[0]);
+
+ // XXX: update is done in draw... perhaps it should be done in a timer
+ if (dirty_) {
+ glBufferSubData(GL_ARRAY_BUFFER, sizeof(quads_[0])*start, sizeof(quads_[0]) * n , &quads_[start] );
+ dirty_ = NO;
+ }
+
+ // vertices
+ glVertexPointer(3, GL_FLOAT, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, vertices));
+
+ // colors
+ glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, colors));
+
+ // tex coords
+ glTexCoordPointer(2, GL_FLOAT, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, texCoords));
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffersVBO_[1]);
+#if CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP
+ glDrawElements(GL_TRIANGLE_STRIP, (GLsizei) n*6, GL_UNSIGNED_SHORT, (GLvoid*) (start*6*sizeof(indices_[0])) );
+#else
+ glDrawElements(GL_TRIANGLES, (GLsizei) n*6, GL_UNSIGNED_SHORT, (GLvoid*) (start*6*sizeof(indices_[0])) );
+#endif // CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+#else // ! CC_USES_VBO
+
+ NSUInteger offset = (NSUInteger)quads_;
+ // vertex
+ NSUInteger diff = offsetof( ccV3F_C4B_T2F, vertices);
+ glVertexPointer(3, GL_FLOAT, kQuadSize, (GLvoid*) (offset + diff) );
+ // color
+ diff = offsetof( ccV3F_C4B_T2F, colors);
+ glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (GLvoid*)(offset + diff));
+
+ // tex coords
+ diff = offsetof( ccV3F_C4B_T2F, texCoords);
+ glTexCoordPointer(2, GL_FLOAT, kQuadSize, (GLvoid*)(offset + diff));
+
+#if CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP
+ glDrawElements(GL_TRIANGLE_STRIP, n*6, GL_UNSIGNED_SHORT, indices_ + start * 6 );
+#else
+ glDrawElements(GL_TRIANGLES, n*6, GL_UNSIGNED_SHORT, indices_ + start * 6 );
+#endif
+
+#endif // CC_USES_VBO
+}
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import <Availability.h>
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import <CoreGraphics/CGImage.h>
+#endif
+
+#import <Foundation/Foundation.h>
+
+@class CCTexture2D;
+
+/** Singleton that handles the loading of textures
+ * Once the texture is loaded, the next time it will return
+ * a reference of the previously loaded texture reducing GPU & CPU memory
+ */
+@interface CCTextureCache : NSObject
+{
+ NSMutableDictionary *textures_;
+ NSLock *dictLock_;
+ NSLock *contextLock_;
+}
+
+/** Retruns ths shared instance of the cache */
++ (CCTextureCache *) sharedTextureCache;
+
+/** purges the cache. It releases the retained instance.
+ @since v0.99.0
+ */
++(void)purgeSharedTextureCache;
+
+
+/** Returns a Texture2D object given an file image
+ * If the file image was not previously loaded, it will create a new CCTexture2D
+ * object and it will return it. It will use the filename as a key.
+ * Otherwise it will return a reference of a previosly loaded image.
+ * Supported image extensions: .png, .bmp, .tiff, .jpeg, .pvr, .gif
+ */
+-(CCTexture2D*) addImage: (NSString*) fileimage;
+
+/** Returns a Texture2D object given a file image
+ * If the file image was not previously loaded, it will create a new CCTexture2D object and it will return it.
+ * Otherwise it will load a texture in a new thread, and when the image is loaded, the callback will be called with the Texture2D as a parameter.
+ * The callback will be called from the main thread, so it is safe to create any cocos2d object from the callback.
+ * Supported image extensions: .png, .bmp, .tiff, .jpeg, .pvr, .gif
+ * @since v0.8
+ */
+-(void) addImageAsync:(NSString*) filename target:(id)target selector:(SEL)selector;
+
+/** Returns a Texture2D object given an CGImageRef image
+ * If the image was not previously loaded, it will create a new CCTexture2D object and it will return it.
+ * Otherwise it will return a reference of a previously loaded image
+ * The "key" parameter will be used as the "key" for the cache.
+ * If "key" is nil, then a new texture will be created each time.
+ * @since v0.8
+ */
+-(CCTexture2D*) addCGImage: (CGImageRef) image forKey: (NSString *)key;
+
+/** Returns an already created texture. Returns nil if the texture doesn't exist.
+ @since v0.99.5
+ */
+-(CCTexture2D *) textureForKey:(NSString *)key;
+
+/** Purges the dictionary of loaded textures.
+ * Call this method if you receive the "Memory Warning"
+ * In the short term: it will free some resources preventing your app from being killed
+ * In the medium term: it will allocate more resources
+ * In the long term: it will be the same
+ */
+-(void) removeAllTextures;
+
+/** Removes unused textures
+ * Textures that have a retain count of 1 will be deleted
+ * It is convinient to call this method after when starting a new Scene
+ * @since v0.8
+ */
+-(void) removeUnusedTextures;
+
+/** Deletes a texture from the cache given a texture
+ */
+-(void) removeTexture: (CCTexture2D*) tex;
+
+/** Deletes a texture from the cache given a its key name
+ @since v0.99.4
+ */
+-(void) removeTextureForKey: (NSString*) textureKeyName;
+
+@end
+
+
+@interface CCTextureCache (PVRSupport)
+
+/** Returns a Texture2D object given an PVRTC RAW filename
+ * If the file image was not previously loaded, it will create a new CCTexture2D
+ * object and it will return it. Otherwise it will return a reference of a previosly loaded image
+ *
+ * It can only load square images: width == height, and it must be a power of 2 (128,256,512...)
+ * bpp can only be 2 or 4. 2 means more compression but lower quality.
+ * hasAlpha: whether or not the image contains alpha channel
+ *
+ * IMPORTANT: This method is only defined on iOS. It is not supported on the Mac version.
+ */
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+-(CCTexture2D*) addPVRTCImage:(NSString*)fileimage bpp:(int)bpp hasAlpha:(BOOL)alpha width:(int)w;
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
+
+/** Returns a Texture2D object given an PVR filename.
+ * If the file image was not previously loaded, it will create a new CCTexture2D
+ * object and it will return it. Otherwise it will return a reference of a previosly loaded image
+ *
+ */
+-(CCTexture2D*) addPVRImage:(NSString*) filename;
+
+@end
+
+
+@interface CCTextureCache (Debug)
+/** Output to CCLOG the current contents of this CCTextureCache
+ * This will attempt to calculate the size of each texture, and the total texture memory in use
+ *
+ * @since v1.0
+ */
+-(void) dumpCachedTextureInfo;
+
+@end
\ No newline at end of file
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import <Availability.h>
+
+#import "Platforms/CCGL.h"
+#import "CCTextureCache.h"
+#import "CCTexture2D.h"
+#import "CCTexturePVR.h"
+#import "ccMacros.h"
+#import "CCConfiguration.h"
+#import "Support/CCFileUtils.h"
+#import "CCDirector.h"
+#import "ccConfig.h"
+
+// needed for CCCallFuncO in Mac-display_link version
+#import "CCActionManager.h"
+#import "CCActionInstant.h"
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+static EAGLContext *auxGLcontext = nil;
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+static NSOpenGLContext *auxGLcontext = nil;
+#endif
+
+
+@interface CCAsyncObject : NSObject
+{
+ SEL selector_;
+ id target_;
+ id data_;
+}
+@property (readwrite,assign) SEL selector;
+@property (readwrite,retain) id target;
+@property (readwrite,retain) id data;
+@end
+
+@implementation CCAsyncObject
+@synthesize selector = selector_;
+@synthesize target = target_;
+@synthesize data = data_;
+- (void) dealloc
+{
+ CCLOGINFO(@"cocos2d: deallocing %@", self);
+ [target_ release];
+ [data_ release];
+ [super dealloc];
+}
+@end
+
+
+@implementation CCTextureCache
+
+#pragma mark TextureCache - Alloc, Init & Dealloc
+static CCTextureCache *sharedTextureCache;
+
++ (CCTextureCache *)sharedTextureCache
+{
+ if (!sharedTextureCache)
+ sharedTextureCache = [[CCTextureCache alloc] init];
+
+ return sharedTextureCache;
+}
+
++(id)alloc
+{
+ NSAssert(sharedTextureCache == nil, @"Attempted to allocate a second instance of a singleton.");
+ return [super alloc];
+}
+
++(void)purgeSharedTextureCache
+{
+ [sharedTextureCache release];
+ sharedTextureCache = nil;
+}
+
+-(id) init
+{
+ if( (self=[super init]) ) {
+ textures_ = [[NSMutableDictionary dictionaryWithCapacity: 10] retain];
+ dictLock_ = [[NSLock alloc] init];
+ contextLock_ = [[NSLock alloc] init];
+ }
+
+ return self;
+}
+
+- (NSString*) description
+{
+ return [NSString stringWithFormat:@"<%@ = %08X | num of textures = %i | keys: %@>",
+ [self class],
+ self,
+ [textures_ count],
+ [textures_ allKeys]
+ ];
+
+}
+
+-(void) dealloc
+{
+ CCLOGINFO(@"cocos2d: deallocing %@", self);
+
+ [textures_ release];
+ [dictLock_ release];
+ [contextLock_ release];
+ [auxGLcontext release];
+ auxGLcontext = nil;
+ sharedTextureCache = nil;
+ [super dealloc];
+}
+
+#pragma mark TextureCache - Add Images
+
+-(void) addImageWithAsyncObject:(CCAsyncObject*)async
+{
+ NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+ // textures will be created on the main OpenGL context
+ // it seems that in SDK 2.2.x there can't be 2 threads creating textures at the same time
+ // the lock is used for this purpose: issue #472
+ [contextLock_ lock];
+ if( auxGLcontext == nil ) {
+ auxGLcontext = [[EAGLContext alloc]
+ initWithAPI:kEAGLRenderingAPIOpenGLES1
+ sharegroup:[[[[CCDirector sharedDirector] openGLView] context] sharegroup]];
+
+ if( ! auxGLcontext )
+ CCLOG(@"cocos2d: TextureCache: Could not create EAGL context");
+ }
+
+ if( [EAGLContext setCurrentContext:auxGLcontext] ) {
+
+ // load / create the texture
+ CCTexture2D *tex = [self addImage:async.data];
+
+ // The callback will be executed on the main thread
+ [async.target performSelectorOnMainThread:async.selector withObject:tex waitUntilDone:NO];
+
+ [EAGLContext setCurrentContext:nil];
+ } else {
+ CCLOG(@"cocos2d: TetureCache: EAGLContext error");
+ }
+ [contextLock_ unlock];
+
+ [autoreleasepool release];
+
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+ [contextLock_ lock];
+ if( auxGLcontext == nil ) {
+
+ MacGLView *view = [[CCDirector sharedDirector] openGLView];
+
+ NSOpenGLPixelFormat *pf = [view pixelFormat];
+ NSOpenGLContext *share = [view openGLContext];
+
+ auxGLcontext = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:share];
+
+ if( ! auxGLcontext )
+ CCLOG(@"cocos2d: TextureCache: Could not create NSOpenGLContext");
+ }
+
+ [auxGLcontext makeCurrentContext];
+
+ // load / create the texture
+ CCTexture2D *tex = [self addImage:async.data];
+
+#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
+ id action = [CCCallFuncO actionWithTarget:async.target selector:async.selector object:tex];
+ [[CCActionManager sharedManager] addAction:action target:async.target paused:NO];
+#else
+ // The callback will be executed on the main thread
+ [async.target performSelector:async.selector
+ onThread:[[CCDirector sharedDirector] runningThread]
+ withObject:tex
+ waitUntilDone:NO];
+#endif
+
+
+ [NSOpenGLContext clearCurrentContext];
+
+ [contextLock_ unlock];
+
+ [autoreleasepool release];
+
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
+}
+
+-(void) addImageAsync: (NSString*)path target:(id)target selector:(SEL)selector
+{
+ NSAssert(path != nil, @"TextureCache: fileimage MUST not be nill");
+
+ // optimization
+
+ CCTexture2D * tex;
+
+ path = ccRemoveHDSuffixFromFile(path);
+
+ if( (tex=[textures_ objectForKey: path] ) ) {
+ [target performSelector:selector withObject:tex];
+ return;
+ }
+
+ // schedule the load
+
+ CCAsyncObject *asyncObject = [[CCAsyncObject alloc] init];
+ asyncObject.selector = selector;
+ asyncObject.target = target;
+ asyncObject.data = path;
+
+ [NSThread detachNewThreadSelector:@selector(addImageWithAsyncObject:) toTarget:self withObject:asyncObject];
+ [asyncObject release];
+}
+
+-(CCTexture2D*) addImage: (NSString*) path
+{
+ NSAssert(path != nil, @"TextureCache: fileimage MUST not be nill");
+
+ CCTexture2D * tex = nil;
+
+ // MUTEX:
+ // Needed since addImageAsync calls this method from a different thread
+ [dictLock_ lock];
+
+ // remove possible -HD suffix to prevent caching the same image twice (issue #1040)
+ path = ccRemoveHDSuffixFromFile( path );
+
+ tex=[textures_ objectForKey: path];
+
+ if( ! tex ) {
+
+ NSString *lowerCase = [path lowercaseString];
+ // all images are handled by UIImage except PVR extension that is handled by our own handler
+
+ if ( [lowerCase hasSuffix:@".pvr"] || [lowerCase hasSuffix:@".pvr.gz"] || [lowerCase hasSuffix:@".pvr.ccz"] )
+ tex = [self addPVRImage:path];
+
+ // Only iPhone
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+ // Issue #886: TEMPORARY FIX FOR TRANSPARENT JPEGS IN IOS4
+ else if ( ( [[CCConfiguration sharedConfiguration] OSVersion] >= kCCiOSVersion_4_0) &&
+ ( [lowerCase hasSuffix:@".jpg"] || [lowerCase hasSuffix:@".jpeg"] )
+ ) {
+ // convert jpg to png before loading the texture
+
+ NSString *fullpath = [CCFileUtils fullPathFromRelativePath: path ];
+
+ UIImage *jpg = [[UIImage alloc] initWithContentsOfFile:fullpath];
+ UIImage *png = [[UIImage alloc] initWithData:UIImagePNGRepresentation(jpg)];
+ tex = [ [CCTexture2D alloc] initWithImage: png ];
+ [png release];
+ [jpg release];
+
+ if( tex )
+ [textures_ setObject: tex forKey:path];
+ else
+ CCLOG(@"cocos2d: Couldn't add image:%@ in CCTextureCache", path);
+
+ // autorelease prevents possible crash in multithreaded environments
+ [tex autorelease];
+ }
+
+ else {
+
+ // prevents overloading the autorelease pool
+ NSString *fullpath = [CCFileUtils fullPathFromRelativePath: path ];
+
+ UIImage *image = [ [UIImage alloc] initWithContentsOfFile: fullpath ];
+ tex = [ [CCTexture2D alloc] initWithImage: image ];
+ [image release];
+
+ if( tex )
+ [textures_ setObject: tex forKey:path];
+ else
+ CCLOG(@"cocos2d: Couldn't add image:%@ in CCTextureCache", path);
+
+ // autorelease prevents possible crash in multithreaded environments
+ [tex autorelease];
+ }
+
+ // Only in Mac
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+ else {
+ NSString *fullpath = [CCFileUtils fullPathFromRelativePath: path ];
+
+ NSData *data = [[NSData alloc] initWithContentsOfFile:fullpath];
+ NSBitmapImageRep *image = [[NSBitmapImageRep alloc] initWithData:data];
+ tex = [ [CCTexture2D alloc] initWithImage:[image CGImage]];
+
+ [data release];
+ [image release];
+
+ if( tex )
+ [textures_ setObject: tex forKey:path];
+ else
+ CCLOG(@"cocos2d: Couldn't add image:%@ in CCTextureCache", path);
+
+ // autorelease prevents possible crash in multithreaded environments
+ [tex autorelease];
+ }
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
+
+ }
+
+ [dictLock_ unlock];
+
+ return tex;
+}
+
+
+-(CCTexture2D*) addCGImage: (CGImageRef) imageref forKey: (NSString *)key
+{
+ NSAssert(imageref != nil, @"TextureCache: image MUST not be nill");
+
+ CCTexture2D * tex = nil;
+
+ // If key is nil, then create a new texture each time
+ if( key && (tex=[textures_ objectForKey: key] ) ) {
+ return tex;
+ }
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+ // prevents overloading the autorelease pool
+ UIImage *image = [[UIImage alloc] initWithCGImage:imageref];
+ tex = [[CCTexture2D alloc] initWithImage: image];
+ [image release];
+
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+ tex = [[CCTexture2D alloc] initWithImage: imageref];
+#endif
+
+ if(tex && key)
+ [textures_ setObject: tex forKey:key];
+ else
+ CCLOG(@"cocos2d: Couldn't add CGImage in CCTextureCache");
+
+ return [tex autorelease];
+}
+
+#pragma mark TextureCache - Remove
+
+-(void) removeAllTextures
+{
+ [textures_ removeAllObjects];
+}
+
+-(void) removeUnusedTextures
+{
+ NSArray *keys = [textures_ allKeys];
+ for( id key in keys ) {
+ id value = [textures_ objectForKey:key];
+ if( [value retainCount] == 1 ) {
+ CCLOG(@"cocos2d: CCTextureCache: removing unused texture: %@", key);
+ [textures_ removeObjectForKey:key];
+ }
+ }
+}
+
+-(void) removeTexture: (CCTexture2D*) tex
+{
+ if( ! tex )
+ return;
+
+ NSArray *keys = [textures_ allKeysForObject:tex];
+
+ for( NSUInteger i = 0; i < [keys count]; i++ )
+ [textures_ removeObjectForKey:[keys objectAtIndex:i]];
+}
+
+-(void) removeTextureForKey:(NSString*)name
+{
+ if( ! name )
+ return;
+
+ [textures_ removeObjectForKey:name];
+}
+
+#pragma mark TextureCache - Get
+- (CCTexture2D *)textureForKey:(NSString *)key
+{
+ return [textures_ objectForKey:key];
+}
+
+@end
+
+
+@implementation CCTextureCache (PVRSupport)
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+-(CCTexture2D*) addPVRTCImage:(NSString*)path bpp:(int)bpp hasAlpha:(BOOL)alpha width:(int)w
+{
+ NSAssert(path != nil, @"TextureCache: fileimage MUST not be nill");
+ NSAssert( bpp==2 || bpp==4, @"TextureCache: bpp must be either 2 or 4");
+
+ CCTexture2D * tex;
+
+ // remove possible -HD suffix to prevent caching the same image twice (issue #1040)
+ path = ccRemoveHDSuffixFromFile( path );
+
+ if( (tex=[textures_ objectForKey: path] ) ) {
+ return tex;
+ }
+
+ // Split up directory and filename
+ NSString *fullpath = [CCFileUtils fullPathFromRelativePath:path];
+
+ NSData *nsdata = [[NSData alloc] initWithContentsOfFile:fullpath];
+ tex = [[CCTexture2D alloc] initWithPVRTCData:[nsdata bytes] level:0 bpp:bpp hasAlpha:alpha length:w pixelFormat:bpp==2?kCCTexture2DPixelFormat_PVRTC2:kCCTexture2DPixelFormat_PVRTC4];
+ if( tex )
+ [textures_ setObject: tex forKey:path];
+ else
+ CCLOG(@"cocos2d: Couldn't add PVRTCImage:%@ in CCTextureCache",path);
+
+ [nsdata release];
+
+ return [tex autorelease];
+}
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
+
+-(CCTexture2D*) addPVRImage:(NSString*)path
+{
+ NSAssert(path != nil, @"TextureCache: fileimage MUST not be nill");
+
+ CCTexture2D * tex;
+
+ // remove possible -HD suffix to prevent caching the same image twice (issue #1040)
+ path = ccRemoveHDSuffixFromFile( path );
+
+ if( (tex=[textures_ objectForKey: path] ) ) {
+ return tex;
+ }
+
+ // Split up directory and filename
+ NSString *fullpath = [CCFileUtils fullPathFromRelativePath:path];
+
+ tex = [[CCTexture2D alloc] initWithPVRFile: fullpath];
+ if( tex )
+ [textures_ setObject: tex forKey:path];
+ else
+ CCLOG(@"cocos2d: Couldn't add PVRImage:%@ in CCTextureCache",path);
+
+ return [tex autorelease];
+}
+
+@end
+
+
+@implementation CCTextureCache (Debug)
+
+-(void) dumpCachedTextureInfo
+{
+ NSUInteger count = 0;
+ NSUInteger totalBytes = 0;
+ for (NSString* texKey in textures_) {
+ CCTexture2D* tex = [textures_ objectForKey:texKey];
+ NSUInteger bpp = [tex bitsPerPixelForFormat];
+ // Each texture takes up width * height * bytesPerPixel bytes.
+ NSUInteger bytes = tex.pixelsWide * tex.pixelsWide * bpp / 8;
+ totalBytes += bytes;
+ count++;
+ CCLOG( @"cocos2d: \"%@\" rc=%lu id=%lu %lu x %lu @ %ld bpp => %lu KB",
+ texKey,
+ (long)[tex retainCount],
+ (long)tex.name,
+ (long)tex.pixelsWide,
+ (long)tex.pixelsHigh,
+ (long)bpp,
+ (long)bytes / 1024 );
+ }
+ CCLOG( @"cocos2d: CCTextureCache dumpDebugInfo: %ld textures, for %lu KB (%.2f MB)", (long)count, (long)totalBytes / 1024, totalBytes / (1024.0f*1024.0f));
+}
+
+@end
\ No newline at end of file
--- /dev/null
+/*
+
+File: PVRTexture.h
+Abstract: The PVRTexture class is responsible for loading .pvr files.
+
+Version: 1.0
+
+Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
+("Apple") in consideration of your agreement to the following terms, and your
+use, installation, modification or redistribution of this Apple software
+constitutes acceptance of these terms. If you do not agree with these terms,
+please do not use, install, modify or redistribute this Apple software.
+
+In consideration of your agreement to abide by the following terms, and subject
+to these terms, Apple grants you a personal, non-exclusive license, under
+Apple's copyrights in this original Apple software (the "Apple Software"), to
+use, reproduce, modify and redistribute the Apple Software, with or without
+modifications, in source and/or binary forms; provided that if you redistribute
+the Apple Software in its entirety and without modifications, you must retain
+this notice and the following text and disclaimers in all such redistributions
+of the Apple Software.
+Neither the name, trademarks, service marks or logos of Apple Inc. may be used
+to endorse or promote products derived from the Apple Software without specific
+prior written permission from Apple. Except as expressly stated in this notice,
+no other rights or licenses, express or implied, are granted by Apple herein,
+including but not limited to any patent rights that may be infringed by your
+derivative works or by other works in which the Apple Software may be
+incorporated.
+
+The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
+WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+COMBINATION WITH YOUR PRODUCTS.
+
+IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR
+DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF
+CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
+APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Copyright (C) 2008 Apple Inc. All Rights Reserved.
+
+*/
+
+#import <Foundation/Foundation.h>
+
+#import "Platforms/CCGL.h"
+#import "CCTexture2D.h"
+
+
+#pragma mark -
+#pragma mark CCTexturePVR
+
+struct CCPVRMipmap {
+ unsigned char *address;
+ unsigned int len;
+};
+
+enum {
+ CC_PVRMIPMAP_MAX = 16,
+};
+
+/** CCTexturePVR
+
+ Object that loads PVR images.
+
+ Supported PVR formats:
+ - RGBA8888
+ - BGRA8888
+ - RGBA4444
+ - RGBA5551
+ - RGB565
+ - A8
+ - I8
+ - AI88
+ - PVRTC 4BPP
+ - PVRTC 2BPP
+
+ Limitations:
+ Pre-generated mipmaps, such as PVR textures with mipmap levels embedded in file,
+ are only supported if all individual sprites are of _square_ size.
+ To use mipmaps with non-square textures, instead call CCTexture2D#generateMipmap on the sheet texture itself
+ (and to save space, save the PVR sprite sheet without mip maps included).
+ */
+@interface CCTexturePVR : NSObject
+{
+ struct CCPVRMipmap mipmaps_[CC_PVRMIPMAP_MAX]; // pointer to mipmap images
+ int numberOfMipmaps_; // number of mipmap used
+
+ unsigned int tableFormatIndex_;
+ uint32_t width_, height_;
+ GLuint name_;
+ BOOL hasAlpha_;
+
+ // cocos2d integration
+ BOOL retainName_;
+ CCTexture2DPixelFormat format_;
+}
+
+/** initializes a CCTexturePVR with a path */
+- (id)initWithContentsOfFile:(NSString *)path;
+/** initializes a CCTexturePVR with an URL */
+- (id)initWithContentsOfURL:(NSURL *)url;
+/** creates and initializes a CCTexturePVR with a path */
++ (id)pvrTextureWithContentsOfFile:(NSString *)path;
+/** creates and initializes a CCTexturePVR with an URL */
++ (id)pvrTextureWithContentsOfURL:(NSURL *)url;
+
+/** texture id name */
+@property (nonatomic,readonly) GLuint name;
+/** texture width */
+@property (nonatomic,readonly) uint32_t width;
+/** texture height */
+@property (nonatomic,readonly) uint32_t height;
+/** whether or not the texture has alpha */
+@property (nonatomic,readonly) BOOL hasAlpha;
+
+// cocos2d integration
+@property (nonatomic,readwrite) BOOL retainName;
+@property (nonatomic,readonly) CCTexture2DPixelFormat format;
+
+@end
+
+
--- /dev/null
+/*
+
+File: PVRTexture.m
+Abstract: The PVRTexture class is responsible for loading .pvr files.
+
+Version: 1.0
+
+Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
+("Apple") in consideration of your agreement to the following terms, and your
+use, installation, modification or redistribution of this Apple software
+constitutes acceptance of these terms. If you do not agree with these terms,
+please do not use, install, modify or redistribute this Apple software.
+
+In consideration of your agreement to abide by the following terms, and subject
+to these terms, Apple grants you a personal, non-exclusive license, under
+Apple's copyrights in this original Apple software (the "Apple Software"), to
+use, reproduce, modify and redistribute the Apple Software, with or without
+modifications, in source and/or binary forms; provided that if you redistribute
+the Apple Software in its entirety and without modifications, you must retain
+this notice and the following text and disclaimers in all such redistributions
+of the Apple Software.
+Neither the name, trademarks, service marks or logos of Apple Inc. may be used
+to endorse or promote products derived from the Apple Software without specific
+prior written permission from Apple. Except as expressly stated in this notice,
+no other rights or licenses, express or implied, are granted by Apple herein,
+including but not limited to any patent rights that may be infringed by your
+derivative works or by other works in which the Apple Software may be
+incorporated.
+
+The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
+WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+COMBINATION WITH YOUR PRODUCTS.
+
+IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR
+DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF
+CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
+APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Copyright (C) 2008 Apple Inc. All Rights Reserved.
+
+*/
+
+/*
+ * Extended PVR formats for cocos2d project ( http://www.cocos2d-iphone.org )
+ * - RGBA8888
+ * - BGRA8888
+ * - RGBA4444
+ * - RGBA5551
+ * - RGB565
+ * - A8
+ * - I8
+ * - AI88
+ */
+
+#import <Availability.h>
+
+#import <zlib.h>
+
+#import "CCTexturePVR.h"
+#import "ccMacros.h"
+#import "CCConfiguration.h"
+#import "Support/ccUtils.h"
+#import "Support/CCFileUtils.h"
+#import "Support/ZipUtils.h"
+#import "Support/OpenGL_Internal.h"
+
+#pragma mark -
+#pragma mark CCTexturePVR
+
+#define PVR_TEXTURE_FLAG_TYPE_MASK 0xff
+#define PVR_TEXTURE_FLAG_FLIPPED_MASK 0x10000
+
+static char gPVRTexIdentifier[4] = "PVR!";
+
+enum
+{
+ kPVRTextureFlagTypeRGBA_4444= 0x10,
+ kPVRTextureFlagTypeRGBA_5551,
+ kPVRTextureFlagTypeRGBA_8888,
+ kPVRTextureFlagTypeRGB_565,
+ kPVRTextureFlagTypeRGB_555, // unsupported
+ kPVRTextureFlagTypeRGB_888, // unsupported
+ kPVRTextureFlagTypeI_8,
+ kPVRTextureFlagTypeAI_88,
+ kPVRTextureFlagTypePVRTC_2,
+ kPVRTextureFlagTypePVRTC_4,
+ kPVRTextureFlagTypeBGRA_8888,
+ kPVRTextureFlagTypeA_8,
+};
+
+static const uint32_t tableFormats[][7] = {
+
+ // - PVR texture format
+ // - OpenGL internal format
+ // - OpenGL format
+ // - OpenGL type
+ // - bpp
+ // - compressed
+ // - Cocos2d texture format constant
+ { kPVRTextureFlagTypeRGBA_4444, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 16, NO, kCCTexture2DPixelFormat_RGBA4444 },
+ { kPVRTextureFlagTypeRGBA_5551, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, 16, NO, kCCTexture2DPixelFormat_RGB5A1 },
+ { kPVRTextureFlagTypeRGBA_8888, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 32, NO, kCCTexture2DPixelFormat_RGBA8888 },
+ { kPVRTextureFlagTypeRGB_565, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 16, NO, kCCTexture2DPixelFormat_RGB565 },
+ { kPVRTextureFlagTypeA_8, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, 8, NO, kCCTexture2DPixelFormat_A8 },
+ { kPVRTextureFlagTypeI_8, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE, 8, NO, kCCTexture2DPixelFormat_I8 },
+ { kPVRTextureFlagTypeAI_88, GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, 16, NO, kCCTexture2DPixelFormat_AI88 },
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+ { kPVRTextureFlagTypePVRTC_2, GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG, -1, -1, 2, YES, kCCTexture2DPixelFormat_PVRTC2 },
+ { kPVRTextureFlagTypePVRTC_4, GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, -1, -1, 4, YES, kCCTexture2DPixelFormat_PVRTC4 },
+#endif // iphone only
+ { kPVRTextureFlagTypeBGRA_8888, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE, 32, NO, kCCTexture2DPixelFormat_RGBA8888 },
+};
+#define MAX_TABLE_ELEMENTS (sizeof(tableFormats) / sizeof(tableFormats[0]))
+
+enum {
+ kCCInternalPVRTextureFormat,
+ kCCInternalOpenGLInternalFormat,
+ kCCInternalOpenGLFormat,
+ kCCInternalOpenGLType,
+ kCCInternalBPP,
+ kCCInternalCompressedImage,
+ kCCInternalCCTexture2DPixelFormat,
+};
+
+typedef struct _PVRTexHeader
+{
+ uint32_t headerLength;
+ uint32_t height;
+ uint32_t width;
+ uint32_t numMipmaps;
+ uint32_t flags;
+ uint32_t dataLength;
+ uint32_t bpp;
+ uint32_t bitmaskRed;
+ uint32_t bitmaskGreen;
+ uint32_t bitmaskBlue;
+ uint32_t bitmaskAlpha;
+ uint32_t pvrTag;
+ uint32_t numSurfs;
+} PVRTexHeader;
+
+
+@implementation CCTexturePVR
+
+@synthesize name = name_;
+@synthesize width = width_;
+@synthesize height = height_;
+@synthesize hasAlpha = hasAlpha_;
+
+// cocos2d integration
+@synthesize retainName = retainName_;
+@synthesize format = format_;
+
+
+- (BOOL)unpackPVRData:(unsigned char*)data PVRLen:(NSUInteger)len
+{
+ BOOL success = FALSE;
+ PVRTexHeader *header = NULL;
+ uint32_t flags, pvrTag;
+ uint32_t dataLength = 0, dataOffset = 0, dataSize = 0;
+ uint32_t blockSize = 0, widthBlocks = 0, heightBlocks = 0;
+ uint32_t width = 0, height = 0, bpp = 4;
+ uint8_t *bytes = NULL;
+ uint32_t formatFlags;
+
+ header = (PVRTexHeader *)data;
+
+ pvrTag = CFSwapInt32LittleToHost(header->pvrTag);
+
+ if ((uint32_t)gPVRTexIdentifier[0] != ((pvrTag >> 0) & 0xff) ||
+ (uint32_t)gPVRTexIdentifier[1] != ((pvrTag >> 8) & 0xff) ||
+ (uint32_t)gPVRTexIdentifier[2] != ((pvrTag >> 16) & 0xff) ||
+ (uint32_t)gPVRTexIdentifier[3] != ((pvrTag >> 24) & 0xff))
+ {
+ return FALSE;
+ }
+
+ flags = CFSwapInt32LittleToHost(header->flags);
+ formatFlags = flags & PVR_TEXTURE_FLAG_TYPE_MASK;
+ BOOL flipped = flags & PVR_TEXTURE_FLAG_FLIPPED_MASK;
+ if( flipped )
+ CCLOG(@"cocos2d: WARNING: Image is flipped. Regenerate it using PVRTexTool");
+
+ if( header->width != ccNextPOT(header->width) || header->height != ccNextPOT(header->height) ) {
+ CCLOG(@"cocos2d: WARNING: PVR NPOT textures are not supported. Regenerate it.");
+ return FALSE;
+ }
+
+ for( tableFormatIndex_=0; tableFormatIndex_ < (unsigned int)MAX_TABLE_ELEMENTS ; tableFormatIndex_++) {
+ if( tableFormats[tableFormatIndex_][kCCInternalPVRTextureFormat] == formatFlags ) {
+
+ numberOfMipmaps_ = 0;
+
+ width_ = width = CFSwapInt32LittleToHost(header->width);
+ height_ = height = CFSwapInt32LittleToHost(header->height);
+
+ if (CFSwapInt32LittleToHost(header->bitmaskAlpha))
+ hasAlpha_ = TRUE;
+ else
+ hasAlpha_ = FALSE;
+
+ dataLength = CFSwapInt32LittleToHost(header->dataLength);
+ bytes = ((uint8_t *)data) + sizeof(PVRTexHeader);
+ format_ = tableFormats[tableFormatIndex_][kCCInternalCCTexture2DPixelFormat];
+ bpp = tableFormats[tableFormatIndex_][kCCInternalBPP];
+
+ // Calculate the data size for each texture level and respect the minimum number of blocks
+ while (dataOffset < dataLength)
+ {
+ switch (formatFlags) {
+ case kPVRTextureFlagTypePVRTC_2:
+ blockSize = 8 * 4; // Pixel by pixel block size for 2bpp
+ widthBlocks = width / 8;
+ heightBlocks = height / 4;
+ break;
+ case kPVRTextureFlagTypePVRTC_4:
+ blockSize = 4 * 4; // Pixel by pixel block size for 4bpp
+ widthBlocks = width / 4;
+ heightBlocks = height / 4;
+ break;
+ case kPVRTextureFlagTypeBGRA_8888:
+ if( ! [[CCConfiguration sharedConfiguration] supportsBGRA8888] ) {
+ CCLOG(@"cocos2d: TexturePVR. BGRA8888 not supported on this device");
+ return FALSE;
+ }
+ default:
+ blockSize = 1;
+ widthBlocks = width;
+ heightBlocks = height;
+ break;
+ }
+
+ // Clamp to minimum number of blocks
+ if (widthBlocks < 2)
+ widthBlocks = 2;
+ if (heightBlocks < 2)
+ heightBlocks = 2;
+
+ dataSize = widthBlocks * heightBlocks * ((blockSize * bpp) / 8);
+ float packetLength = (dataLength-dataOffset);
+ packetLength = packetLength > dataSize ? dataSize : packetLength;
+
+ mipmaps_[numberOfMipmaps_].address = bytes+dataOffset;
+ mipmaps_[numberOfMipmaps_].len = packetLength;
+ numberOfMipmaps_++;
+
+ NSAssert( numberOfMipmaps_ < CC_PVRMIPMAP_MAX, @"TexturePVR: Maximum number of mimpaps reached. Increate the CC_PVRMIPMAP_MAX value");
+
+ dataOffset += packetLength;
+
+ width = MAX(width >> 1, 1);
+ height = MAX(height >> 1, 1);
+ }
+
+ success = TRUE;
+ break;
+ }
+ }
+
+ if( ! success )
+ CCLOG(@"cocos2d: WARNING: Unsupported PVR Pixel Format: 0x%2x. Re-encode it with a OpenGL pixel format variant", formatFlags);
+
+ return success;
+}
+
+
+- (BOOL)createGLTexture
+{
+ GLsizei width = width_;
+ GLsizei height = height_;
+ GLenum err;
+
+ if (numberOfMipmaps_ > 0)
+ {
+ if (name_ != 0)
+ glDeleteTextures(1, &name_);
+
+ glGenTextures(1, &name_);
+ glBindTexture(GL_TEXTURE_2D, name_);
+ }
+
+ CHECK_GL_ERROR(); // clean possible GL error
+
+ // Generate textures with mipmaps
+ for (GLint i=0; i < numberOfMipmaps_; i++)
+ {
+ GLenum internalFormat = tableFormats[tableFormatIndex_][kCCInternalOpenGLInternalFormat];
+ GLenum format = tableFormats[tableFormatIndex_][kCCInternalOpenGLFormat];
+ GLenum type = tableFormats[tableFormatIndex_][kCCInternalOpenGLType];
+ BOOL compressed = tableFormats[tableFormatIndex_][kCCInternalCompressedImage];
+
+ if( compressed && ! [[CCConfiguration sharedConfiguration] supportsPVRTC] ) {
+ CCLOG(@"cocos2d: WARNING: PVRTC images are not supported");
+ return FALSE;
+ }
+
+ unsigned char *data = mipmaps_[i].address;
+ unsigned int datalen = mipmaps_[i].len;
+
+ if( compressed)
+ glCompressedTexImage2D(GL_TEXTURE_2D, i, internalFormat, width, height, 0, datalen, data);
+ else
+ glTexImage2D(GL_TEXTURE_2D, i, internalFormat, width, height, 0, format, type, data);
+
+ if( i > 0 && (width != height || ccNextPOT(width) != width ) )
+ CCLOG(@"cocos2d: TexturePVR. WARNING. Mipmap level %u is not squared. Texture won't render correctly. width=%u != height=%u", i, width, height);
+
+ err = glGetError();
+ if (err != GL_NO_ERROR)
+ {
+ CCLOG(@"cocos2d: TexturePVR: Error uploading compressed texture level: %u . glError: 0x%04X", i, err);
+ return FALSE;
+ }
+
+ width = MAX(width >> 1, 1);
+ height = MAX(height >> 1, 1);
+ }
+
+ return TRUE;
+}
+
+
+- (id)initWithContentsOfFile:(NSString *)path
+{
+ if((self = [super init]))
+ {
+ unsigned char *pvrdata = NULL;
+ NSInteger pvrlen = 0;
+ NSString *lowerCase = [path lowercaseString];
+
+ if ( [lowerCase hasSuffix:@".ccz"])
+ pvrlen = ccInflateCCZFile( [path UTF8String], &pvrdata );
+
+ else if( [lowerCase hasSuffix:@".gz"] )
+ pvrlen = ccInflateGZipFile( [path UTF8String], &pvrdata );
+
+ else
+ pvrlen = ccLoadFileIntoMemory( [path UTF8String], &pvrdata );
+
+ if( pvrlen < 0 ) {
+ [self release];
+ return nil;
+ }
+
+
+ numberOfMipmaps_ = 0;
+
+ name_ = 0;
+ width_ = height_ = 0;
+ tableFormatIndex_ = -1;
+ hasAlpha_ = FALSE;
+
+ retainName_ = NO; // cocos2d integration
+
+ if( ! [self unpackPVRData:pvrdata PVRLen:pvrlen] || ![self createGLTexture] ) {
+ free(pvrdata);
+ [self release];
+ return nil;
+ }
+
+ free(pvrdata);
+ }
+
+ return self;
+}
+
+- (id)initWithContentsOfURL:(NSURL *)url
+{
+ if (![url isFileURL])
+ {
+ CCLOG(@"cocos2d: CCPVRTexture: Only files are supported");
+ [self release];
+ return nil;
+ }
+
+ return [self initWithContentsOfFile:[url path]];
+}
+
+
++ (id)pvrTextureWithContentsOfFile:(NSString *)path
+{
+ return [[[self alloc] initWithContentsOfFile:path] autorelease];
+}
+
+
++ (id)pvrTextureWithContentsOfURL:(NSURL *)url
+{
+ if (![url isFileURL])
+ return nil;
+
+ return [CCTexturePVR pvrTextureWithContentsOfFile:[url path]];
+}
+
+
+- (void)dealloc
+{
+ CCLOGINFO( @"cocos2d: deallocing %@", self);
+
+ if (name_ != 0 && ! retainName_ )
+ glDeleteTextures(1, &name_);
+
+ [super dealloc];
+}
+
+@end
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "CCTextureAtlas.h"
+#import "CCAtlasNode.h"
+#import "Support/TGAlib.h"
+
+/** CCTileMapAtlas is a subclass of CCAtlasNode.
+
+ It knows how to render a map based of tiles.
+ The tiles must be in a .PNG format while the map must be a .TGA file.
+
+ For more information regarding the format, please see this post:
+ http://www.cocos2d-iphone.org/archives/27
+
+ All features from CCAtlasNode are valid in CCTileMapAtlas
+
+ IMPORTANT:
+ This class is deprecated. It is maintained for compatibility reasons only.
+ You SHOULD not use this class.
+ Instead, use the newer TMX file format: CCTMXTiledMap
+ */
+@interface CCTileMapAtlas : CCAtlasNode
+{
+
+ /// info about the map file
+ tImageTGA *tgaInfo;
+
+ /// x,y to altas dicctionary
+ NSMutableDictionary *posToAtlasIndex;
+
+ /// numbers of tiles to render
+ int itemsToRender;
+}
+
+/** TileMap info */
+@property (nonatomic,readonly) tImageTGA *tgaInfo;
+
+/** creates a CCTileMap with a tile file (atlas) with a map file and the width and height of each tile in points.
+ The tile file will be loaded using the TextureMgr.
+ */
++(id) tileMapAtlasWithTileFile:(NSString*)tile mapFile:(NSString*)map tileWidth:(int)w tileHeight:(int)h;
+
+/** initializes a CCTileMap with a tile file (atlas) with a map file and the width and height of each tile in points.
+ The file will be loaded using the TextureMgr.
+ */
+-(id) initWithTileFile:(NSString*)tile mapFile:(NSString*)map tileWidth:(int)w tileHeight:(int)h;
+
+/** returns a tile from position x,y.
+ For the moment only channel R is used
+ */
+-(ccColor3B) tileAt: (ccGridSize) position;
+
+/** sets a tile at position x,y.
+ For the moment only channel R is used
+ */
+-(void) setTile:(ccColor3B)tile at:(ccGridSize)position;
+/** dealloc the map from memory */
+-(void) releaseMap;
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "ccConfig.h"
+#import "CCTileMapAtlas.h"
+#import "ccMacros.h"
+#import "Support/CCFileUtils.h"
+
+@interface CCTileMapAtlas (Private)
+-(void) loadTGAfile:(NSString*)file;
+-(void) calculateItemsToRender;
+-(void) updateAtlasValueAt:(ccGridSize)pos withValue:(ccColor3B)value withIndex:(NSUInteger)idx;
+@end
+
+
+@implementation CCTileMapAtlas
+
+@synthesize tgaInfo;
+
+#pragma mark CCTileMapAtlas - Creation & Init
++(id) tileMapAtlasWithTileFile:(NSString*)tile mapFile:(NSString*)map tileWidth:(int)w tileHeight:(int)h
+{
+ return [[[self alloc] initWithTileFile:tile mapFile:map tileWidth:w tileHeight:h] autorelease];
+}
+
+
+-(id) initWithTileFile:(NSString*)tile mapFile:(NSString*)map tileWidth:(int)w tileHeight:(int)h
+{
+ [self loadTGAfile: map];
+ [self calculateItemsToRender];
+
+ if( (self=[super initWithTileFile:tile tileWidth:w tileHeight:h itemsToRender: itemsToRender]) ) {
+
+ posToAtlasIndex = [[NSMutableDictionary dictionaryWithCapacity:itemsToRender] retain];
+
+ [self updateAtlasValues];
+
+ [self setContentSize: CGSizeMake(tgaInfo->width*itemWidth_, tgaInfo->height*itemHeight_)];
+ }
+
+ return self;
+}
+
+-(void) dealloc
+{
+ if( tgaInfo )
+ tgaDestroy(tgaInfo);
+
+ [posToAtlasIndex release];
+
+ [super dealloc];
+}
+
+-(void) releaseMap
+{
+ if( tgaInfo )
+ tgaDestroy(tgaInfo);
+
+ tgaInfo = nil;
+
+ [posToAtlasIndex release];
+ posToAtlasIndex = nil;
+}
+
+-(void) calculateItemsToRender
+{
+ NSAssert( tgaInfo != nil, @"tgaInfo must be non-nil");
+
+ itemsToRender = 0;
+ for(int x = 0;x < tgaInfo->width; x++ ) {
+ for(int y = 0; y < tgaInfo->height; y++ ) {
+ ccColor3B *ptr = (ccColor3B*) tgaInfo->imageData;
+ ccColor3B value = ptr[x + y * tgaInfo->width];
+ if( value.r )
+ itemsToRender++;
+ }
+ }
+}
+
+-(void) loadTGAfile:(NSString*)file
+{
+ NSAssert( file != nil, @"file must be non-nil");
+
+ NSString *path = [CCFileUtils fullPathFromRelativePath:file ];
+
+// //Find the path of the file
+// NSBundle *mainBndl = [CCDirector sharedDirector].loadingBundle;
+// NSString *resourcePath = [mainBndl resourcePath];
+// NSString * path = [resourcePath stringByAppendingPathComponent:file];
+
+ tgaInfo = tgaLoad( [path UTF8String] );
+#if 1
+ if( tgaInfo->status != TGA_OK )
+ [NSException raise:@"TileMapAtlasLoadTGA" format:@"TileMapAtas cannot load TGA file"];
+
+#endif
+}
+
+#pragma mark CCTileMapAtlas - Atlas generation / updates
+
+-(void) setTile:(ccColor3B) tile at:(ccGridSize) pos
+{
+ NSAssert( tgaInfo != nil, @"tgaInfo must not be nil");
+ NSAssert( posToAtlasIndex != nil, @"posToAtlasIndex must not be nil");
+ NSAssert( pos.x < tgaInfo->width, @"Invalid position.x");
+ NSAssert( pos.y < tgaInfo->height, @"Invalid position.x");
+ NSAssert( tile.r != 0, @"R component must be non 0");
+
+ ccColor3B *ptr = (ccColor3B*) tgaInfo->imageData;
+ ccColor3B value = ptr[pos.x + pos.y * tgaInfo->width];
+ if( value.r == 0 )
+ CCLOG(@"cocos2d: Value.r must be non 0.");
+ else {
+ ptr[pos.x + pos.y * tgaInfo->width] = tile;
+
+ // XXX: this method consumes a lot of memory
+ // XXX: a tree of something like that shall be impolemented
+ NSNumber *num = [posToAtlasIndex objectForKey: [NSString stringWithFormat:@"%d,%d", pos.x, pos.y]];
+ [self updateAtlasValueAt:pos withValue:tile withIndex: [num integerValue]];
+ }
+}
+
+-(ccColor3B) tileAt:(ccGridSize) pos
+{
+ NSAssert( tgaInfo != nil, @"tgaInfo must not be nil");
+ NSAssert( pos.x < tgaInfo->width, @"Invalid position.x");
+ NSAssert( pos.y < tgaInfo->height, @"Invalid position.y");
+
+ ccColor3B *ptr = (ccColor3B*) tgaInfo->imageData;
+ ccColor3B value = ptr[pos.x + pos.y * tgaInfo->width];
+
+ return value;
+}
+
+-(void) updateAtlasValueAt:(ccGridSize)pos withValue:(ccColor3B)value withIndex:(NSUInteger)idx
+{
+ ccV3F_C4B_T2F_Quad quad;
+
+ NSInteger x = pos.x;
+ NSInteger y = pos.y;
+ float row = (value.r % itemsPerRow_);
+ float col = (value.r / itemsPerRow_);
+
+ float textureWide = [[textureAtlas_ texture] pixelsWide];
+ float textureHigh = [[textureAtlas_ texture] pixelsHigh];
+
+#if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
+ float left = (2*row*itemWidth_+1)/(2*textureWide);
+ float right = left+(itemWidth_*2-2)/(2*textureWide);
+ float top = (2*col*itemHeight_+1)/(2*textureHigh);
+ float bottom = top+(itemHeight_*2-2)/(2*textureHigh);
+#else
+ float left = (row*itemWidth_)/textureWide;
+ float right = left+itemWidth_/textureWide;
+ float top = (col*itemHeight_)/textureHigh;
+ float bottom = top+itemHeight_/textureHigh;
+#endif
+
+
+ quad.tl.texCoords.u = left;
+ quad.tl.texCoords.v = top;
+ quad.tr.texCoords.u = right;
+ quad.tr.texCoords.v = top;
+ quad.bl.texCoords.u = left;
+ quad.bl.texCoords.v = bottom;
+ quad.br.texCoords.u = right;
+ quad.br.texCoords.v = bottom;
+
+ quad.bl.vertices.x = (int) (x * itemWidth_);
+ quad.bl.vertices.y = (int) (y * itemHeight_);
+ quad.bl.vertices.z = 0.0f;
+ quad.br.vertices.x = (int)(x * itemWidth_ + itemWidth_);
+ quad.br.vertices.y = (int)(y * itemHeight_);
+ quad.br.vertices.z = 0.0f;
+ quad.tl.vertices.x = (int)(x * itemWidth_);
+ quad.tl.vertices.y = (int)(y * itemHeight_ + itemHeight_);
+ quad.tl.vertices.z = 0.0f;
+ quad.tr.vertices.x = (int)(x * itemWidth_ + itemWidth_);
+ quad.tr.vertices.y = (int)(y * itemHeight_ + itemHeight_);
+ quad.tr.vertices.z = 0.0f;
+
+ [textureAtlas_ updateQuad:&quad atIndex:idx];
+}
+
+-(void) updateAtlasValues
+{
+ NSAssert( tgaInfo != nil, @"tgaInfo must be non-nil");
+
+
+ int total = 0;
+
+ for(int x = 0;x < tgaInfo->width; x++ ) {
+ for(int y = 0; y < tgaInfo->height; y++ ) {
+ if( total < itemsToRender ) {
+ ccColor3B *ptr = (ccColor3B*) tgaInfo->imageData;
+ ccColor3B value = ptr[x + y * tgaInfo->width];
+
+ if( value.r != 0 ) {
+ [self updateAtlasValueAt:ccg(x,y) withValue:value withIndex:total];
+
+ NSString *key = [NSString stringWithFormat:@"%d,%d", x,y];
+ NSNumber *num = [NSNumber numberWithInt:total];
+ [posToAtlasIndex setObject:num forKey:key];
+
+ total++;
+ }
+ }
+ }
+ }
+}
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCScene.h"
+@class CCActionInterval;
+@class CCNode;
+
+/** CCTransitionEaseScene can ease the actions of the scene protocol.
+ @since v0.8.2
+ */
+@protocol CCTransitionEaseScene <NSObject>
+/** returns the Ease action that will be performed on a linear action.
+ @since v0.8.2
+ */
+-(CCActionInterval*) easeActionWithAction:(CCActionInterval*)action;
+@end
+
+/** Orientation Type used by some transitions
+ */
+typedef enum {
+ /// An horizontal orientation where the Left is nearer
+ kOrientationLeftOver = 0,
+ /// An horizontal orientation where the Right is nearer
+ kOrientationRightOver = 1,
+ /// A vertical orientation where the Up is nearer
+ kOrientationUpOver = 0,
+ /// A vertical orientation where the Bottom is nearer
+ kOrientationDownOver = 1,
+} tOrientation;
+
+/** Base class for CCTransition scenes
+ */
+@interface CCTransitionScene : CCScene
+{
+ CCScene *inScene_;
+ CCScene *outScene_;
+ ccTime duration_;
+ BOOL inSceneOnTop_;
+ BOOL sendCleanupToScene_;
+}
+/** creates a base transition with duration and incoming scene */
++(id) transitionWithDuration:(ccTime) t scene:(CCScene*)s;
+/** initializes a transition with duration and incoming scene */
+-(id) initWithDuration:(ccTime) t scene:(CCScene*)s;
+/** called after the transition finishes */
+-(void) finish;
+/** used by some transitions to hide the outter scene */
+-(void) hideOutShowIn;
+@end
+
+/** A CCTransition that supports orientation like.
+ * Possible orientation: LeftOver, RightOver, UpOver, DownOver
+ */
+@interface CCTransitionSceneOriented : CCTransitionScene
+{
+ tOrientation orientation;
+}
+/** creates a base transition with duration and incoming scene */
++(id) transitionWithDuration:(ccTime) t scene:(CCScene*)s orientation:(tOrientation)o;
+/** initializes a transition with duration and incoming scene */
+-(id) initWithDuration:(ccTime) t scene:(CCScene*)s orientation:(tOrientation)o;
+@end
+
+
+/** CCTransitionRotoZoom:
+ Rotate and zoom out the outgoing scene, and then rotate and zoom in the incoming
+ */
+@interface CCTransitionRotoZoom : CCTransitionScene
+{}
+@end
+
+/** CCTransitionJumpZoom:
+ Zoom out and jump the outgoing scene, and then jump and zoom in the incoming
+*/
+@interface CCTransitionJumpZoom : CCTransitionScene
+{}
+@end
+
+/** CCTransitionMoveInL:
+ Move in from to the left the incoming scene.
+*/
+@interface CCTransitionMoveInL : CCTransitionScene <CCTransitionEaseScene>
+{}
+/** initializes the scenes */
+-(void) initScenes;
+/** returns the action that will be performed */
+-(CCActionInterval*) action;
+@end
+
+/** CCTransitionMoveInR:
+ Move in from to the right the incoming scene.
+ */
+@interface CCTransitionMoveInR : CCTransitionMoveInL
+{}
+@end
+
+/** CCTransitionMoveInT:
+ Move in from to the top the incoming scene.
+ */
+@interface CCTransitionMoveInT : CCTransitionMoveInL
+{}
+@end
+
+/** CCTransitionMoveInB:
+ Move in from to the bottom the incoming scene.
+ */
+@interface CCTransitionMoveInB : CCTransitionMoveInL
+{}
+@end
+
+/** CCTransitionSlideInL:
+ Slide in the incoming scene from the left border.
+ */
+@interface CCTransitionSlideInL : CCTransitionScene <CCTransitionEaseScene>
+{}
+/** initializes the scenes */
+-(void) initScenes;
+/** returns the action that will be performed by the incomming and outgoing scene */
+-(CCActionInterval*) action;
+@end
+
+/** CCTransitionSlideInR:
+ Slide in the incoming scene from the right border.
+ */
+@interface CCTransitionSlideInR : CCTransitionSlideInL
+{}
+@end
+
+/** CCTransitionSlideInB:
+ Slide in the incoming scene from the bottom border.
+ */
+@interface CCTransitionSlideInB : CCTransitionSlideInL
+{}
+@end
+
+/** CCTransitionSlideInT:
+ Slide in the incoming scene from the top border.
+ */
+@interface CCTransitionSlideInT : CCTransitionSlideInL
+{}
+@end
+
+/**
+ Shrink the outgoing scene while grow the incoming scene
+ */
+@interface CCTransitionShrinkGrow : CCTransitionScene <CCTransitionEaseScene>
+{}
+@end
+
+/** CCTransitionFlipX:
+ Flips the screen horizontally.
+ The front face is the outgoing scene and the back face is the incoming scene.
+ */
+@interface CCTransitionFlipX : CCTransitionSceneOriented
+{}
+@end
+
+/** CCTransitionFlipY:
+ Flips the screen vertically.
+ The front face is the outgoing scene and the back face is the incoming scene.
+ */
+@interface CCTransitionFlipY : CCTransitionSceneOriented
+{}
+@end
+
+/** CCTransitionFlipAngular:
+ Flips the screen half horizontally and half vertically.
+ The front face is the outgoing scene and the back face is the incoming scene.
+ */
+@interface CCTransitionFlipAngular : CCTransitionSceneOriented
+{}
+@end
+
+/** CCTransitionZoomFlipX:
+ Flips the screen horizontally doing a zoom out/in
+ The front face is the outgoing scene and the back face is the incoming scene.
+ */
+@interface CCTransitionZoomFlipX : CCTransitionSceneOriented
+{}
+@end
+
+/** CCTransitionZoomFlipY:
+ Flips the screen vertically doing a little zooming out/in
+ The front face is the outgoing scene and the back face is the incoming scene.
+ */
+@interface CCTransitionZoomFlipY : CCTransitionSceneOriented
+{}
+@end
+
+/** CCTransitionZoomFlipAngular:
+ Flips the screen half horizontally and half vertically doing a little zooming out/in.
+ The front face is the outgoing scene and the back face is the incoming scene.
+ */
+@interface CCTransitionZoomFlipAngular : CCTransitionSceneOriented
+{}
+@end
+
+/** CCTransitionFade:
+ Fade out the outgoing scene and then fade in the incoming scene.'''
+ */
+@interface CCTransitionFade : CCTransitionScene
+{
+ ccColor4B color;
+}
+/** creates the transition with a duration and with an RGB color
+ * Example: [FadeTransition transitionWithDuration:2 scene:s withColor:ccc3(255,0,0)]; // red color
+ */
++(id) transitionWithDuration:(ccTime)duration scene:(CCScene*)scene withColor:(ccColor3B)color;
+/** initializes the transition with a duration and with an RGB color */
+-(id) initWithDuration:(ccTime)duration scene:(CCScene*)scene withColor:(ccColor3B)color;
+@end
+
+
+/**
+ CCTransitionCrossFade:
+ Cross fades two scenes using the CCRenderTexture object.
+ */
+@class CCRenderTexture;
+@interface CCTransitionCrossFade : CCTransitionScene
+{}
+@end
+
+/** CCTransitionTurnOffTiles:
+ Turn off the tiles of the outgoing scene in random order
+ */
+@interface CCTransitionTurnOffTiles : CCTransitionScene <CCTransitionEaseScene>
+{}
+@end
+
+/** CCTransitionSplitCols:
+ The odd columns goes upwards while the even columns goes downwards.
+ */
+@interface CCTransitionSplitCols : CCTransitionScene <CCTransitionEaseScene>
+{}
+-(CCActionInterval*) action;
+@end
+
+/** CCTransitionSplitRows:
+ The odd rows goes to the left while the even rows goes to the right.
+ */
+@interface CCTransitionSplitRows : CCTransitionSplitCols
+{}
+@end
+
+/** CCTransitionFadeTR:
+ Fade the tiles of the outgoing scene from the left-bottom corner the to top-right corner.
+ */
+@interface CCTransitionFadeTR : CCTransitionScene <CCTransitionEaseScene>
+{}
+-(CCActionInterval*) actionWithSize:(ccGridSize) vector;
+@end
+
+/** CCTransitionFadeBL:
+ Fade the tiles of the outgoing scene from the top-right corner to the bottom-left corner.
+ */
+@interface CCTransitionFadeBL : CCTransitionFadeTR
+{}
+@end
+
+/** CCTransitionFadeUp:
+ * Fade the tiles of the outgoing scene from the bottom to the top.
+ */
+@interface CCTransitionFadeUp : CCTransitionFadeTR
+{}
+@end
+
+/** CCTransitionFadeDown:
+ * Fade the tiles of the outgoing scene from the top to the bottom.
+ */
+@interface CCTransitionFadeDown : CCTransitionFadeTR
+{}
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+
+#import "CCTransition.h"
+#import "CCNode.h"
+#import "CCDirector.h"
+#import "CCActionInterval.h"
+#import "CCActionInstant.h"
+#import "CCActionCamera.h"
+#import "CCLayer.h"
+#import "CCCamera.h"
+#import "CCActionTiledGrid.h"
+#import "CCActionEase.h"
+#import "CCRenderTexture.h"
+#import "Support/CGPointExtension.h"
+
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import "Platforms/iOS/CCTouchDispatcher.h"
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+#import "Platforms/Mac/CCEventDispatcher.h"
+#endif
+
+enum {
+ kSceneFade = 0xFADEFADE,
+};
+
+@interface CCTransitionScene (Private)
+-(void) sceneOrder;
+- (void)setNewScene:(ccTime)dt;
+@end
+
+@implementation CCTransitionScene
++(id) transitionWithDuration:(ccTime) t scene:(CCScene*)s
+{
+ return [[[self alloc] initWithDuration:t scene:s] autorelease];
+}
+
+-(id) initWithDuration:(ccTime) t scene:(CCScene*)s
+{
+ NSAssert( s != nil, @"Argument scene must be non-nil");
+
+ if( (self=[super init]) ) {
+
+ duration_ = t;
+
+ // retain
+ inScene_ = [s retain];
+ outScene_ = [[CCDirector sharedDirector] runningScene];
+ [outScene_ retain];
+
+ NSAssert( inScene_ != outScene_, @"Incoming scene must be different from the outgoing scene" );
+
+ // disable events while transitions
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+ [[CCTouchDispatcher sharedDispatcher] setDispatchEvents: NO];
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+ [[CCEventDispatcher sharedDispatcher] setDispatchEvents: NO];
+#endif
+
+ [self sceneOrder];
+ }
+ return self;
+}
+-(void) sceneOrder
+{
+ inSceneOnTop_ = YES;
+}
+
+-(void) draw
+{
+ if( inSceneOnTop_ ) {
+ [outScene_ visit];
+ [inScene_ visit];
+ } else {
+ [inScene_ visit];
+ [outScene_ visit];
+ }
+}
+
+-(void) finish
+{
+ /* clean up */
+ [inScene_ setVisible:YES];
+ [inScene_ setPosition:ccp(0,0)];
+ [inScene_ setScale:1.0f];
+ [inScene_ setRotation:0.0f];
+ [inScene_.camera restore];
+
+ [outScene_ setVisible:NO];
+ [outScene_ setPosition:ccp(0,0)];
+ [outScene_ setScale:1.0f];
+ [outScene_ setRotation:0.0f];
+ [outScene_.camera restore];
+
+ [self schedule:@selector(setNewScene:) interval:0];
+}
+
+-(void) setNewScene: (ccTime) dt
+{
+ [self unschedule:_cmd];
+
+ CCDirector *director = [CCDirector sharedDirector];
+
+ // Before replacing, save the "send cleanup to scene"
+ sendCleanupToScene_ = [director sendCleanupToScene];
+
+ [director replaceScene: inScene_];
+
+ // enable events while transitions
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+ [[CCTouchDispatcher sharedDispatcher] setDispatchEvents: YES];
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+ [[CCEventDispatcher sharedDispatcher] setDispatchEvents: YES];
+#endif
+
+ // issue #267
+ [outScene_ setVisible:YES];
+}
+
+-(void) hideOutShowIn
+{
+ [inScene_ setVisible:YES];
+ [outScene_ setVisible:NO];
+}
+
+// custom onEnter
+-(void) onEnter
+{
+ [super onEnter];
+ [inScene_ onEnter];
+ // outScene_ should not receive the onEnter callback
+}
+
+// custom onExit
+-(void) onExit
+{
+ [super onExit];
+ [outScene_ onExit];
+
+ // inScene_ should not receive the onExit callback
+ // only the onEnterTransitionDidFinish
+ [inScene_ onEnterTransitionDidFinish];
+}
+
+// custom cleanup
+-(void) cleanup
+{
+ [super cleanup];
+
+ if( sendCleanupToScene_ )
+ [outScene_ cleanup];
+}
+
+-(void) dealloc
+{
+ [inScene_ release];
+ [outScene_ release];
+ [super dealloc];
+}
+@end
+
+//
+// Oriented Transition
+//
+@implementation CCTransitionSceneOriented
++(id) transitionWithDuration:(ccTime) t scene:(CCScene*)s orientation:(tOrientation)o
+{
+ return [[[self alloc] initWithDuration:t scene:s orientation:o] autorelease];
+}
+
+-(id) initWithDuration:(ccTime) t scene:(CCScene*)s orientation:(tOrientation)o
+{
+ if( (self=[super initWithDuration:t scene:s]) )
+ orientation = o;
+ return self;
+}
+@end
+
+
+//
+// RotoZoom
+//
+@implementation CCTransitionRotoZoom
+-(void) onEnter
+{
+ [super onEnter];
+
+ [inScene_ setScale:0.001f];
+ [outScene_ setScale:1.0f];
+
+ [inScene_ setAnchorPoint:ccp(0.5f, 0.5f)];
+ [outScene_ setAnchorPoint:ccp(0.5f, 0.5f)];
+
+ CCActionInterval *rotozoom = [CCSequence actions: [CCSpawn actions:
+ [CCScaleBy actionWithDuration:duration_/2 scale:0.001f],
+ [CCRotateBy actionWithDuration:duration_/2 angle:360 *2],
+ nil],
+ [CCDelayTime actionWithDuration:duration_/2],
+ nil];
+
+
+ [outScene_ runAction: rotozoom];
+ [inScene_ runAction: [CCSequence actions:
+ [rotozoom reverse],
+ [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+ nil]];
+}
+@end
+
+//
+// JumpZoom
+//
+@implementation CCTransitionJumpZoom
+-(void) onEnter
+{
+ [super onEnter];
+ CGSize s = [[CCDirector sharedDirector] winSize];
+
+ [inScene_ setScale:0.5f];
+ [inScene_ setPosition:ccp( s.width,0 )];
+
+ [inScene_ setAnchorPoint:ccp(0.5f, 0.5f)];
+ [outScene_ setAnchorPoint:ccp(0.5f, 0.5f)];
+
+ CCActionInterval *jump = [CCJumpBy actionWithDuration:duration_/4 position:ccp(-s.width,0) height:s.width/4 jumps:2];
+ CCActionInterval *scaleIn = [CCScaleTo actionWithDuration:duration_/4 scale:1.0f];
+ CCActionInterval *scaleOut = [CCScaleTo actionWithDuration:duration_/4 scale:0.5f];
+
+ CCActionInterval *jumpZoomOut = [CCSequence actions: scaleOut, jump, nil];
+ CCActionInterval *jumpZoomIn = [CCSequence actions: jump, scaleIn, nil];
+
+ CCActionInterval *delay = [CCDelayTime actionWithDuration:duration_/2];
+
+ [outScene_ runAction: jumpZoomOut];
+ [inScene_ runAction: [CCSequence actions: delay,
+ jumpZoomIn,
+ [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+ nil] ];
+}
+@end
+
+//
+// MoveInL
+//
+@implementation CCTransitionMoveInL
+-(void) onEnter
+{
+ [super onEnter];
+
+ [self initScenes];
+
+ CCActionInterval *a = [self action];
+
+ [inScene_ runAction: [CCSequence actions:
+ [self easeActionWithAction:a],
+ [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+ nil]
+ ];
+
+}
+-(CCActionInterval*) action
+{
+ return [CCMoveTo actionWithDuration:duration_ position:ccp(0,0)];
+}
+
+-(CCActionInterval*) easeActionWithAction:(CCActionInterval*)action
+{
+ return [CCEaseOut actionWithAction:action rate:2.0f];
+// return [EaseElasticOut actionWithAction:action period:0.4f];
+}
+
+-(void) initScenes
+{
+ CGSize s = [[CCDirector sharedDirector] winSize];
+ [inScene_ setPosition: ccp( -s.width,0) ];
+}
+@end
+
+//
+// MoveInR
+//
+@implementation CCTransitionMoveInR
+-(void) initScenes
+{
+ CGSize s = [[CCDirector sharedDirector] winSize];
+ [inScene_ setPosition: ccp( s.width,0) ];
+}
+@end
+
+//
+// MoveInT
+//
+@implementation CCTransitionMoveInT
+-(void) initScenes
+{
+ CGSize s = [[CCDirector sharedDirector] winSize];
+ [inScene_ setPosition: ccp( 0, s.height) ];
+}
+@end
+
+//
+// MoveInB
+//
+@implementation CCTransitionMoveInB
+-(void) initScenes
+{
+ CGSize s = [[CCDirector sharedDirector] winSize];
+ [inScene_ setPosition: ccp( 0, -s.height) ];
+}
+@end
+
+//
+// SlideInL
+//
+
+// The adjust factor is needed to prevent issue #442
+// One solution is to use DONT_RENDER_IN_SUBPIXELS images, but NO
+// The other issue is that in some transitions (and I don't know why)
+// the order should be reversed (In in top of Out or vice-versa).
+#define ADJUST_FACTOR 0.5f
+@implementation CCTransitionSlideInL
+-(void) onEnter
+{
+ [super onEnter];
+
+ [self initScenes];
+
+ CCActionInterval *in = [self action];
+ CCActionInterval *out = [self action];
+
+ id inAction = [self easeActionWithAction:in];
+ id outAction = [CCSequence actions:
+ [self easeActionWithAction:out],
+ [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+ nil];
+
+ [inScene_ runAction: inAction];
+ [outScene_ runAction: outAction];
+}
+-(void) sceneOrder
+{
+ inSceneOnTop_ = NO;
+}
+-(void) initScenes
+{
+ CGSize s = [[CCDirector sharedDirector] winSize];
+ [inScene_ setPosition: ccp( -(s.width-ADJUST_FACTOR),0) ];
+}
+-(CCActionInterval*) action
+{
+ CGSize s = [[CCDirector sharedDirector] winSize];
+ return [CCMoveBy actionWithDuration:duration_ position:ccp(s.width-ADJUST_FACTOR,0)];
+}
+
+-(CCActionInterval*) easeActionWithAction:(CCActionInterval*)action
+{
+ return [CCEaseOut actionWithAction:action rate:2.0f];
+// return [EaseElasticOut actionWithAction:action period:0.4f];
+}
+
+@end
+
+//
+// SlideInR
+//
+@implementation CCTransitionSlideInR
+-(void) sceneOrder
+{
+ inSceneOnTop_ = YES;
+}
+-(void) initScenes
+{
+ CGSize s = [[CCDirector sharedDirector] winSize];
+ [inScene_ setPosition: ccp( s.width-ADJUST_FACTOR,0) ];
+}
+
+-(CCActionInterval*) action
+{
+ CGSize s = [[CCDirector sharedDirector] winSize];
+ return [CCMoveBy actionWithDuration:duration_ position:ccp(-(s.width-ADJUST_FACTOR),0)];
+}
+
+@end
+
+//
+// SlideInT
+//
+@implementation CCTransitionSlideInT
+-(void) sceneOrder
+{
+ inSceneOnTop_ = NO;
+}
+-(void) initScenes
+{
+ CGSize s = [[CCDirector sharedDirector] winSize];
+ [inScene_ setPosition: ccp(0,s.height-ADJUST_FACTOR) ];
+}
+
+-(CCActionInterval*) action
+{
+ CGSize s = [[CCDirector sharedDirector] winSize];
+ return [CCMoveBy actionWithDuration:duration_ position:ccp(0,-(s.height-ADJUST_FACTOR))];
+}
+
+@end
+
+//
+// SlideInB
+//
+@implementation CCTransitionSlideInB
+-(void) sceneOrder
+{
+ inSceneOnTop_ = YES;
+}
+
+-(void) initScenes
+{
+ CGSize s = [[CCDirector sharedDirector] winSize];
+ [inScene_ setPosition: ccp(0,-(s.height-ADJUST_FACTOR)) ];
+}
+
+-(CCActionInterval*) action
+{
+ CGSize s = [[CCDirector sharedDirector] winSize];
+ return [CCMoveBy actionWithDuration:duration_ position:ccp(0,s.height-ADJUST_FACTOR)];
+}
+@end
+
+//
+// ShrinkGrow Transition
+//
+@implementation CCTransitionShrinkGrow
+-(void) onEnter
+{
+ [super onEnter];
+
+ [inScene_ setScale:0.001f];
+ [outScene_ setScale:1.0f];
+
+ [inScene_ setAnchorPoint:ccp(2/3.0f,0.5f)];
+ [outScene_ setAnchorPoint:ccp(1/3.0f,0.5f)];
+
+ CCActionInterval *scaleOut = [CCScaleTo actionWithDuration:duration_ scale:0.01f];
+ CCActionInterval *scaleIn = [CCScaleTo actionWithDuration:duration_ scale:1.0f];
+
+ [inScene_ runAction: [self easeActionWithAction:scaleIn]];
+ [outScene_ runAction: [CCSequence actions:
+ [self easeActionWithAction:scaleOut],
+ [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+ nil] ];
+}
+-(CCActionInterval*) easeActionWithAction:(CCActionInterval*)action
+{
+ return [CCEaseOut actionWithAction:action rate:2.0f];
+// return [EaseElasticOut actionWithAction:action period:0.3f];
+}
+@end
+
+//
+// FlipX Transition
+//
+@implementation CCTransitionFlipX
+-(void) onEnter
+{
+ [super onEnter];
+
+ CCActionInterval *inA, *outA;
+ [inScene_ setVisible: NO];
+
+ float inDeltaZ, inAngleZ;
+ float outDeltaZ, outAngleZ;
+
+ if( orientation == kOrientationRightOver ) {
+ inDeltaZ = 90;
+ inAngleZ = 270;
+ outDeltaZ = 90;
+ outAngleZ = 0;
+ } else {
+ inDeltaZ = -90;
+ inAngleZ = 90;
+ outDeltaZ = -90;
+ outAngleZ = 0;
+ }
+
+ inA = [CCSequence actions:
+ [CCDelayTime actionWithDuration:duration_/2],
+ [CCShow action],
+ [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:inAngleZ deltaAngleZ:inDeltaZ angleX:0 deltaAngleX:0],
+ [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+ nil ];
+ outA = [CCSequence actions:
+ [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:0 deltaAngleX:0],
+ [CCHide action],
+ [CCDelayTime actionWithDuration:duration_/2],
+ nil ];
+
+ [inScene_ runAction: inA];
+ [outScene_ runAction: outA];
+
+}
+@end
+
+//
+// FlipY Transition
+//
+@implementation CCTransitionFlipY
+-(void) onEnter
+{
+ [super onEnter];
+
+ CCActionInterval *inA, *outA;
+ [inScene_ setVisible: NO];
+
+ float inDeltaZ, inAngleZ;
+ float outDeltaZ, outAngleZ;
+
+ if( orientation == kOrientationUpOver ) {
+ inDeltaZ = 90;
+ inAngleZ = 270;
+ outDeltaZ = 90;
+ outAngleZ = 0;
+ } else {
+ inDeltaZ = -90;
+ inAngleZ = 90;
+ outDeltaZ = -90;
+ outAngleZ = 0;
+ }
+ inA = [CCSequence actions:
+ [CCDelayTime actionWithDuration:duration_/2],
+ [CCShow action],
+ [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:inAngleZ deltaAngleZ:inDeltaZ angleX:90 deltaAngleX:0],
+ [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+ nil ];
+ outA = [CCSequence actions:
+ [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:90 deltaAngleX:0],
+ [CCHide action],
+ [CCDelayTime actionWithDuration:duration_/2],
+ nil ];
+
+ [inScene_ runAction: inA];
+ [outScene_ runAction: outA];
+
+}
+@end
+
+//
+// FlipAngular Transition
+//
+@implementation CCTransitionFlipAngular
+-(void) onEnter
+{
+ [super onEnter];
+
+ CCActionInterval *inA, *outA;
+ [inScene_ setVisible: NO];
+
+ float inDeltaZ, inAngleZ;
+ float outDeltaZ, outAngleZ;
+
+ if( orientation == kOrientationRightOver ) {
+ inDeltaZ = 90;
+ inAngleZ = 270;
+ outDeltaZ = 90;
+ outAngleZ = 0;
+ } else {
+ inDeltaZ = -90;
+ inAngleZ = 90;
+ outDeltaZ = -90;
+ outAngleZ = 0;
+ }
+ inA = [CCSequence actions:
+ [CCDelayTime actionWithDuration:duration_/2],
+ [CCShow action],
+ [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:inAngleZ deltaAngleZ:inDeltaZ angleX:-45 deltaAngleX:0],
+ [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+ nil ];
+ outA = [CCSequence actions:
+ [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:45 deltaAngleX:0],
+ [CCHide action],
+ [CCDelayTime actionWithDuration:duration_/2],
+ nil ];
+
+ [inScene_ runAction: inA];
+ [outScene_ runAction: outA];
+}
+@end
+
+//
+// ZoomFlipX Transition
+//
+@implementation CCTransitionZoomFlipX
+-(void) onEnter
+{
+ [super onEnter];
+
+ CCActionInterval *inA, *outA;
+ [inScene_ setVisible: NO];
+
+ float inDeltaZ, inAngleZ;
+ float outDeltaZ, outAngleZ;
+
+ if( orientation == kOrientationRightOver ) {
+ inDeltaZ = 90;
+ inAngleZ = 270;
+ outDeltaZ = 90;
+ outAngleZ = 0;
+ } else {
+ inDeltaZ = -90;
+ inAngleZ = 90;
+ outDeltaZ = -90;
+ outAngleZ = 0;
+ }
+ inA = [CCSequence actions:
+ [CCDelayTime actionWithDuration:duration_/2],
+ [CCSpawn actions:
+ [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:inAngleZ deltaAngleZ:inDeltaZ angleX:0 deltaAngleX:0],
+ [CCScaleTo actionWithDuration:duration_/2 scale:1],
+ [CCShow action],
+ nil],
+ [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+ nil ];
+ outA = [CCSequence actions:
+ [CCSpawn actions:
+ [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:0 deltaAngleX:0],
+ [CCScaleTo actionWithDuration:duration_/2 scale:0.5f],
+ nil],
+ [CCHide action],
+ [CCDelayTime actionWithDuration:duration_/2],
+ nil ];
+
+ inScene_.scale = 0.5f;
+ [inScene_ runAction: inA];
+ [outScene_ runAction: outA];
+}
+@end
+
+//
+// ZoomFlipY Transition
+//
+@implementation CCTransitionZoomFlipY
+-(void) onEnter
+{
+ [super onEnter];
+
+ CCActionInterval *inA, *outA;
+ [inScene_ setVisible: NO];
+
+ float inDeltaZ, inAngleZ;
+ float outDeltaZ, outAngleZ;
+
+ if( orientation == kOrientationUpOver ) {
+ inDeltaZ = 90;
+ inAngleZ = 270;
+ outDeltaZ = 90;
+ outAngleZ = 0;
+ } else {
+ inDeltaZ = -90;
+ inAngleZ = 90;
+ outDeltaZ = -90;
+ outAngleZ = 0;
+ }
+
+ inA = [CCSequence actions:
+ [CCDelayTime actionWithDuration:duration_/2],
+ [CCSpawn actions:
+ [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:inAngleZ deltaAngleZ:inDeltaZ angleX:90 deltaAngleX:0],
+ [CCScaleTo actionWithDuration:duration_/2 scale:1],
+ [CCShow action],
+ nil],
+ [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+ nil ];
+ outA = [CCSequence actions:
+ [CCSpawn actions:
+ [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:90 deltaAngleX:0],
+ [CCScaleTo actionWithDuration:duration_/2 scale:0.5f],
+ nil],
+ [CCHide action],
+ [CCDelayTime actionWithDuration:duration_/2],
+ nil ];
+
+ inScene_.scale = 0.5f;
+ [inScene_ runAction: inA];
+ [outScene_ runAction: outA];
+}
+@end
+
+//
+// ZoomFlipAngular Transition
+//
+@implementation CCTransitionZoomFlipAngular
+-(void) onEnter
+{
+ [super onEnter];
+
+ CCActionInterval *inA, *outA;
+ [inScene_ setVisible: NO];
+
+ float inDeltaZ, inAngleZ;
+ float outDeltaZ, outAngleZ;
+
+ if( orientation == kOrientationRightOver ) {
+ inDeltaZ = 90;
+ inAngleZ = 270;
+ outDeltaZ = 90;
+ outAngleZ = 0;
+ } else {
+ inDeltaZ = -90;
+ inAngleZ = 90;
+ outDeltaZ = -90;
+ outAngleZ = 0;
+ }
+
+ inA = [CCSequence actions:
+ [CCDelayTime actionWithDuration:duration_/2],
+ [CCSpawn actions:
+ [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:inAngleZ deltaAngleZ:inDeltaZ angleX:-45 deltaAngleX:0],
+ [CCScaleTo actionWithDuration:duration_/2 scale:1],
+ [CCShow action],
+ nil],
+ [CCShow action],
+ [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+ nil ];
+ outA = [CCSequence actions:
+ [CCSpawn actions:
+ [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:45 deltaAngleX:0],
+ [CCScaleTo actionWithDuration:duration_/2 scale:0.5f],
+ nil],
+ [CCHide action],
+ [CCDelayTime actionWithDuration:duration_/2],
+ nil ];
+
+ inScene_.scale = 0.5f;
+ [inScene_ runAction: inA];
+ [outScene_ runAction: outA];
+}
+@end
+
+
+//
+// Fade Transition
+//
+@implementation CCTransitionFade
++(id) transitionWithDuration:(ccTime)d scene:(CCScene*)s withColor:(ccColor3B)color
+{
+ return [[[self alloc] initWithDuration:d scene:s withColor:color] autorelease];
+}
+
+-(id) initWithDuration:(ccTime)d scene:(CCScene*)s withColor:(ccColor3B)aColor
+{
+ if( (self=[super initWithDuration:d scene:s]) ) {
+ color.r = aColor.r;
+ color.g = aColor.g;
+ color.b = aColor.b;
+ }
+
+ return self;
+}
+
+-(id) initWithDuration:(ccTime)d scene:(CCScene*)s
+{
+ return [self initWithDuration:d scene:s withColor:ccBLACK];
+}
+
+-(void) onEnter
+{
+ [super onEnter];
+
+ CCLayerColor *l = [CCLayerColor layerWithColor:color];
+ [inScene_ setVisible: NO];
+
+ [self addChild: l z:2 tag:kSceneFade];
+
+
+ CCNode *f = [self getChildByTag:kSceneFade];
+
+ CCActionInterval *a = [CCSequence actions:
+ [CCFadeIn actionWithDuration:duration_/2],
+ [CCCallFunc actionWithTarget:self selector:@selector(hideOutShowIn)],
+ [CCFadeOut actionWithDuration:duration_/2],
+ [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+ nil ];
+ [f runAction: a];
+}
+
+-(void) onExit
+{
+ [super onExit];
+ [self removeChildByTag:kSceneFade cleanup:NO];
+}
+@end
+
+
+//
+// Cross Fade Transition
+//
+@implementation CCTransitionCrossFade
+
+-(void) draw
+{
+ // override draw since both scenes (textures) are rendered in 1 scene
+}
+
+-(void) onEnter
+{
+ [super onEnter];
+
+ // create a transparent color layer
+ // in which we are going to add our rendertextures
+ ccColor4B color = {0,0,0,0};
+ CGSize size = [[CCDirector sharedDirector] winSize];
+ CCLayerColor * layer = [CCLayerColor layerWithColor:color];
+
+ // create the first render texture for inScene_
+ CCRenderTexture *inTexture = [CCRenderTexture renderTextureWithWidth:size.width height:size.height];
+ inTexture.sprite.anchorPoint= ccp(0.5f,0.5f);
+ inTexture.position = ccp(size.width/2, size.height/2);
+ inTexture.anchorPoint = ccp(0.5f,0.5f);
+
+ // render inScene_ to its texturebuffer
+ [inTexture begin];
+ [inScene_ visit];
+ [inTexture end];
+
+ // create the second render texture for outScene_
+ CCRenderTexture *outTexture = [CCRenderTexture renderTextureWithWidth:size.width height:size.height];
+ outTexture.sprite.anchorPoint= ccp(0.5f,0.5f);
+ outTexture.position = ccp(size.width/2, size.height/2);
+ outTexture.anchorPoint = ccp(0.5f,0.5f);
+
+ // render outScene_ to its texturebuffer
+ [outTexture begin];
+ [outScene_ visit];
+ [outTexture end];
+
+ // create blend functions
+
+ ccBlendFunc blend1 = {GL_ONE, GL_ONE}; // inScene_ will lay on background and will not be used with alpha
+ ccBlendFunc blend2 = {GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA}; // we are going to blend outScene_ via alpha
+
+ // set blendfunctions
+ [inTexture.sprite setBlendFunc:blend1];
+ [outTexture.sprite setBlendFunc:blend2];
+
+ // add render textures to the layer
+ [layer addChild:inTexture];
+ [layer addChild:outTexture];
+
+ // initial opacity:
+ [inTexture.sprite setOpacity:255];
+ [outTexture.sprite setOpacity:255];
+
+ // create the blend action
+ CCActionInterval * layerAction = [CCSequence actions:
+ [CCFadeTo actionWithDuration:duration_ opacity:0],
+ [CCCallFunc actionWithTarget:self selector:@selector(hideOutShowIn)],
+ [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+ nil ];
+
+
+ // run the blend action
+ [outTexture.sprite runAction: layerAction];
+
+ // add the layer (which contains our two rendertextures) to the scene
+ [self addChild: layer z:2 tag:kSceneFade];
+}
+
+// clean up on exit
+-(void) onExit
+{
+ // remove our layer and release all containing objects
+ [self removeChildByTag:kSceneFade cleanup:NO];
+
+ [super onExit];
+}
+@end
+
+//
+// TurnOffTilesTransition
+//
+@implementation CCTransitionTurnOffTiles
+
+// override addScenes, and change the order
+-(void) sceneOrder
+{
+ inSceneOnTop_ = NO;
+}
+
+-(void) onEnter
+{
+ [super onEnter];
+ CGSize s = [[CCDirector sharedDirector] winSize];
+ float aspect = s.width / s.height;
+ int x = 12 * aspect;
+ int y = 12;
+
+ id toff = [CCTurnOffTiles actionWithSize: ccg(x,y) duration:duration_];
+ id action = [self easeActionWithAction:toff];
+ [outScene_ runAction: [CCSequence actions: action,
+ [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+ [CCStopGrid action],
+ nil]
+ ];
+
+}
+-(CCActionInterval*) easeActionWithAction:(CCActionInterval*)action
+{
+ return action;
+// return [EaseIn actionWithAction:action rate:2.0f];
+}
+@end
+
+#pragma mark Split Transitions
+
+//
+// SplitCols Transition
+//
+@implementation CCTransitionSplitCols
+
+-(void) onEnter
+{
+ [super onEnter];
+
+ inScene_.visible = NO;
+
+ id split = [self action];
+ id seq = [CCSequence actions:
+ split,
+ [CCCallFunc actionWithTarget:self selector:@selector(hideOutShowIn)],
+ [split reverse],
+ nil
+ ];
+ [self runAction: [CCSequence actions:
+ [self easeActionWithAction:seq],
+ [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+ [CCStopGrid action],
+ nil]
+ ];
+}
+
+-(CCActionInterval*) action
+{
+ return [CCSplitCols actionWithCols:3 duration:duration_/2.0f];
+}
+
+-(CCActionInterval*) easeActionWithAction:(CCActionInterval*)action
+{
+ return [CCEaseInOut actionWithAction:action rate:3.0f];
+}
+@end
+
+//
+// SplitRows Transition
+//
+@implementation CCTransitionSplitRows
+-(CCActionInterval*) action
+{
+ return [CCSplitRows actionWithRows:3 duration:duration_/2.0f];
+}
+@end
+
+
+#pragma mark Fade Grid Transitions
+
+//
+// FadeTR Transition
+//
+@implementation CCTransitionFadeTR
+-(void) sceneOrder
+{
+ inSceneOnTop_ = NO;
+}
+
+-(void) onEnter
+{
+ [super onEnter];
+
+ CGSize s = [[CCDirector sharedDirector] winSize];
+ float aspect = s.width / s.height;
+ int x = 12 * aspect;
+ int y = 12;
+
+ id action = [self actionWithSize:ccg(x,y)];
+
+ [outScene_ runAction: [CCSequence actions:
+ [self easeActionWithAction:action],
+ [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+ [CCStopGrid action],
+ nil]
+ ];
+}
+
+-(CCActionInterval*) actionWithSize: (ccGridSize) v
+{
+ return [CCFadeOutTRTiles actionWithSize:v duration:duration_];
+}
+
+-(CCActionInterval*) easeActionWithAction:(CCActionInterval*)action
+{
+ return action;
+// return [EaseIn actionWithAction:action rate:2.0f];
+}
+@end
+
+//
+// FadeBL Transition
+//
+@implementation CCTransitionFadeBL
+-(CCActionInterval*) actionWithSize: (ccGridSize) v
+{
+ return [CCFadeOutBLTiles actionWithSize:v duration:duration_];
+}
+@end
+
+//
+// FadeUp Transition
+//
+@implementation CCTransitionFadeUp
+-(CCActionInterval*) actionWithSize: (ccGridSize) v
+{
+ return [CCFadeOutUpTiles actionWithSize:v duration:duration_];
+}
+@end
+
+//
+// FadeDown Transition
+//
+@implementation CCTransitionFadeDown
+-(CCActionInterval*) actionWithSize: (ccGridSize) v
+{
+ return [CCFadeOutDownTiles actionWithSize:v duration:duration_];
+}
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Sindesso Pty Ltd http://www.sindesso.com/
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCTransition.h"
+
+/** CCTransitionPageTurn transition.
+ * A transition which peels back the bottom right hand corner of a scene
+ * to transition to the scene beneath it simulating a page turn
+ *
+ * This uses a 3DAction so it's strongly recommended that depth buffering
+ * is turned on in CCDirector using:
+ *
+ * [[CCDirector sharedDirector] setDepthBufferFormat:kCCDepthBuffer16];
+ *
+ * @since v0.8.2
+ */
+@interface CCTransitionPageTurn : CCTransitionScene
+{
+ BOOL back_;
+}
+/**
+ * creates a base transition with duration and incoming scene
+ * if back is TRUE then the effect is reversed to appear as if the incoming
+ * scene is being turned from left over the outgoing scene
+ */
++(id) transitionWithDuration:(ccTime) t scene:(CCScene*)s backwards:(BOOL) back;
+
+/**
+ * creates a base transition with duration and incoming scene
+ * if back is TRUE then the effect is reversed to appear as if the incoming
+ * scene is being turned from left over the outgoing scene
+ */
+-(id) initWithDuration:(ccTime) t scene:(CCScene*)s backwards:(BOOL) back;
+
+-(CCActionInterval*) actionWithSize:(ccGridSize) vector;
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Sindesso Pty Ltd http://www.sindesso.com/
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "CCTransitionPageTurn.h"
+#import "CCActionPageTurn3D.h"
+#import "CCDirector.h"
+
+@implementation CCTransitionPageTurn
+
+/** creates a base transition with duration and incoming scene */
++(id) transitionWithDuration:(ccTime) t scene:(CCScene*)s backwards:(BOOL) back
+{
+ return [[[self alloc] initWithDuration:t scene:s backwards:back] autorelease];
+}
+
+/** initializes a transition with duration and incoming scene */
+-(id) initWithDuration:(ccTime) t scene:(CCScene*)s backwards:(BOOL) back
+{
+ // XXX: needed before [super init]
+ back_ = back;
+
+ if( ( self = [super initWithDuration:t scene:s] ) )
+ {
+ // do something
+ }
+ return self;
+}
+
+-(void) sceneOrder
+{
+ inSceneOnTop_ = back_;
+}
+
+//
+-(void) onEnter
+{
+ [super onEnter];
+
+ CGSize s = [[CCDirector sharedDirector] winSize];
+ int x, y;
+ if( s.width > s.height)
+ {
+ x = 16;
+ y = 12;
+ }
+ else
+ {
+ x = 12;
+ y = 16;
+ }
+
+ id action = [self actionWithSize:ccg(x,y)];
+
+ if(! back_ )
+ {
+ [outScene_ runAction: [CCSequence actions:
+ action,
+ [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+ [CCStopGrid action],
+ nil]
+ ];
+ }
+ else
+ {
+ // to prevent initial flicker
+ inScene_.visible = NO;
+ [inScene_ runAction: [CCSequence actions:
+ [CCShow action],
+ action,
+ [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+ [CCStopGrid action],
+ nil]
+ ];
+ }
+
+}
+
+-(CCActionInterval*) actionWithSize: (ccGridSize) v
+{
+ if( back_ )
+ {
+ // Get hold of the PageTurn3DAction
+ return [CCReverseTime actionWithAction:
+ [CCPageTurn3D actionWithSize:v duration:duration_]];
+ }
+ else
+ {
+ // Get hold of the PageTurn3DAction
+ return [CCPageTurn3D actionWithSize:v duration:duration_];
+ }
+}
+
+@end
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Lam Pham
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "CCTransition.h"
+#import "CCProgressTimer.h"
+#import "CCActionProgressTimer.h"
+
+/** CCTransitionRadialCCW transition.
+ A counter colock-wise radial transition to the next scene
+ */
+@interface CCTransitionRadialCCW : CCTransitionScene
+@end
+
+/** CCTransitionRadialCW transition.
+ A counter colock-wise radial transition to the next scene
+*/
+@interface CCTransitionRadialCW : CCTransitionRadialCCW
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Lam Pham
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+
+#import "CCDirector.h"
+#import "CCTransitionRadial.h"
+#import "CCRenderTexture.h"
+#import "CCLayer.h"
+#import "CCActionInstant.h"
+#import "Support/CGPointExtension.h"
+
+enum {
+ kSceneRadial = 0xc001,
+};
+
+#pragma mark -
+#pragma mark Transition Radial CCW
+
+@implementation CCTransitionRadialCCW
+-(void) sceneOrder
+{
+ inSceneOnTop_ = NO;
+}
+
+-(CCProgressTimerType) radialType
+{
+ return kCCProgressTimerTypeRadialCCW;
+}
+
+-(void) onEnter
+{
+ [super onEnter];
+ // create a transparent color layer
+ // in which we are going to add our rendertextures
+ CGSize size = [[CCDirector sharedDirector] winSize];
+
+ // create the second render texture for outScene
+ CCRenderTexture *outTexture = [CCRenderTexture renderTextureWithWidth:size.width height:size.height];
+ outTexture.sprite.anchorPoint= ccp(0.5f,0.5f);
+ outTexture.position = ccp(size.width/2, size.height/2);
+ outTexture.anchorPoint = ccp(0.5f,0.5f);
+
+ // render outScene to its texturebuffer
+ [outTexture clear:0 g:0 b:0 a:1];
+ [outTexture begin];
+ [outScene_ visit];
+ [outTexture end];
+
+ // Since we've passed the outScene to the texture we don't need it.
+ [self hideOutShowIn];
+
+ // We need the texture in RenderTexture.
+ CCProgressTimer *outNode = [CCProgressTimer progressWithTexture:outTexture.sprite.texture];
+ // but it's flipped upside down so we flip the sprite
+ outNode.sprite.flipY = YES;
+ // Return the radial type that we want to use
+ outNode.type = [self radialType];
+ outNode.percentage = 100.f;
+ outNode.position = ccp(size.width/2, size.height/2);
+ outNode.anchorPoint = ccp(0.5f,0.5f);
+
+ // create the blend action
+ CCActionInterval * layerAction = [CCSequence actions:
+ [CCProgressFromTo actionWithDuration:duration_ from:100.f to:0.f],
+ [CCCallFunc actionWithTarget:self selector:@selector(finish)],
+ nil ];
+ // run the blend action
+ [outNode runAction: layerAction];
+
+ // add the layer (which contains our two rendertextures) to the scene
+ [self addChild: outNode z:2 tag:kSceneRadial];
+}
+
+// clean up on exit
+-(void) onExit
+{
+ // remove our layer and release all containing objects
+ [self removeChildByTag:kSceneRadial cleanup:NO];
+ [super onExit];
+}
+@end
+
+#pragma mark -
+#pragma mark Transition Radial CW
+
+@implementation CCTransitionRadialCW
+-(CCProgressTimerType) radialType
+{
+ return kCCProgressTimerTypeRadialCW;
+}
+@end
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+//
+// Common layer for OpenGL stuff
+//
+
+#import <Availability.h>
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import <OpenGLES/ES1/gl.h>
+#import <OpenGLES/ES1/glext.h>
+#import <OpenGLES/EAGL.h>
+#import "iOS/glu.h"
+#import "iOS/EAGLView.h"
+
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+#import <OpenGL/gl.h>
+#import <OpenGL/glu.h>
+#import <Cocoa/Cocoa.h> // needed for NSOpenGLView
+#import "Mac/MacGLView.h"
+#endif
+
+
+// iOS
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#define CC_GLVIEW EAGLView
+#define ccglOrtho glOrthof
+#define ccglClearDepth glClearDepthf
+#define ccglGenerateMipmap glGenerateMipmapOES
+#define ccglGenFramebuffers glGenFramebuffersOES
+#define ccglBindFramebuffer glBindFramebufferOES
+#define ccglFramebufferTexture2D glFramebufferTexture2DOES
+#define ccglDeleteFramebuffers glDeleteFramebuffersOES
+#define ccglCheckFramebufferStatus glCheckFramebufferStatusOES
+#define ccglTranslate glTranslatef
+
+#define CC_GL_FRAMEBUFFER GL_FRAMEBUFFER_OES
+#define CC_GL_FRAMEBUFFER_BINDING GL_FRAMEBUFFER_BINDING_OES
+#define CC_GL_COLOR_ATTACHMENT0 GL_COLOR_ATTACHMENT0_OES
+#define CC_GL_FRAMEBUFFER_COMPLETE GL_FRAMEBUFFER_COMPLETE_OES
+
+// Mac
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+#define CC_GLVIEW MacGLView
+#define ccglOrtho glOrtho
+#define ccglClearDepth glClearDepth
+#define ccglGenerateMipmap glGenerateMipmap
+#define ccglGenFramebuffers glGenFramebuffers
+#define ccglBindFramebuffer glBindFramebuffer
+#define ccglFramebufferTexture2D glFramebufferTexture2D
+#define ccglDeleteFramebuffers glDeleteFramebuffers
+#define ccglCheckFramebufferStatus glCheckFramebufferStatus
+#define ccglTranslate glTranslated
+
+#define CC_GL_FRAMEBUFFER GL_FRAMEBUFFER
+#define CC_GL_FRAMEBUFFER_BINDING GL_FRAMEBUFFER_BINDING
+#define CC_GL_COLOR_ATTACHMENT0 GL_COLOR_ATTACHMENT0
+#define CC_GL_FRAMEBUFFER_COMPLETE GL_FRAMEBUFFER_COMPLETE
+
+#endif
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+//
+// Common layer for NS (Next-Step) stuff
+//
+
+#import <Availability.h>
+
+#import <Foundation/Foundation.h> // for NSObject
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#define CCRectFromString(__r__) CGRectFromString(__r__)
+#define CCPointFromString(__p__) CGPointFromString(__p__)
+#define CCSizeFromString(__s__) CGSizeFromString(__s__)
+#define CCNSSizeToCGSize
+#define CCNSRectToCGRect
+#define CCNSPointToCGPoint
+#define CCTextAlignment UITextAlignment
+#define CCTextAlignmentCenter UITextAlignmentCenter
+#define CCTextAlignmentLeft UITextAlignmentLeft
+#define CCTextAlignmentRight UITextAlignmentRight
+#define CCLineBreakMode UILineBreakMode
+#define CCLineBreakModeWordWrap UILineBreakModeWordWrap
+#define CCLineBreakModeCharacterWrap UILineBreakModeCharacterWrap
+#define CCLineBreakModeClip UILineBreakModeClip
+#define CCLineBreakModeHeadTruncation UILineBreakModeHeadTruncation
+#define CCLineBreakModeTailTruncation UILineBreakModeTailTruncation
+#define CCLineBreakModeMiddleTruncation UILineBreakModeMiddleTruncation
+
+
+
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+#define CCRectFromString(__r__) NSRectToCGRect( NSRectFromString(__r__) )
+#define CCPointFromString(__p__) NSPointToCGPoint( NSPointFromString(__p__) )
+#define CCSizeFromString(__s__) NSSizeToCGSize( NSSizeFromString(__s__) )
+#define CCNSSizeToCGSize NSSizeToCGSize
+#define CCNSRectToCGRect NSRectToCGRect
+#define CCNSPointToCGPoint NSPointToCGPoint
+#define CCTextAlignment NSTextAlignment
+#define CCTextAlignmentCenter NSCenterTextAlignment
+#define CCTextAlignmentLeft NSLeftTextAlignment
+#define CCTextAlignmentRight NSRightTextAlignment
+#define CCLineBreakMode NSLineBreakMode
+#define CCLineBreakModeWordWrap NSLineBreakByWordWrapping
+#define CCLineBreakModeClip -1
+#define CCLineBreakModeHeadTruncation -1
+#define CCLineBreakModeTailTruncation -1
+#define CCLineBreakModeMiddleTruncation -1
+
+
+#endif
+
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+// Only compile this code on Mac. These files should not be included on your iOS project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+#import <QuartzCore/CVDisplayLink.h>
+#import "../../CCDirector.h"
+
+enum {
+ /// If the window is resized, it won't be autoscaled
+ kCCDirectorResize_NoScale,
+ /// If the window is resized, it will be autoscaled (default behavior)
+ kCCDirectorResize_AutoScale,
+};
+
+@interface CCDirector (MacExtension)
+/** converts an NSEvent to GL coordinates */
+-(CGPoint) convertEventToGL:(NSEvent*)event;
+@end
+
+/** Base class of Mac directors
+ @since v0.99.5
+ */
+@interface CCDirectorMac : CCDirector
+{
+ BOOL isFullScreen_;
+ int resizeMode_;
+ CGPoint winOffset_;
+ CGSize originalWinSize_;
+
+ NSWindow *fullScreenWindow_;
+
+ // cache
+ NSWindow *windowGLView_;
+ NSView *superViewGLView_;
+ NSRect originalWinRect_; // Original size and position
+}
+
+// whether or not the view is in fullscreen mode
+@property (nonatomic, readonly) BOOL isFullScreen;
+
+// resize mode: with or without scaling
+@property (nonatomic, readwrite) int resizeMode;
+
+@property (nonatomic, readwrite) CGSize originalWinSize;
+
+/** Sets the view in fullscreen or window mode */
+- (void) setFullScreen:(BOOL)fullscreen;
+
+/** Converts window size coordiantes to logical coordinates.
+ Useful only if resizeMode is kCCDirectorResize_Scale.
+ If resizeMode is kCCDirectorResize_NoScale, then no conversion will be done.
+*/
+- (CGPoint) convertToLogicalCoordinates:(CGPoint)coordinates;
+@end
+
+
+/** DisplayLinkDirector is a Director that synchronizes timers with the refresh rate of the display.
+ *
+ * Features and Limitations:
+ * - Only available on 3.1+
+ * - Scheduled timers & drawing are synchronizes with the refresh rate of the display
+ * - Only supports animation intervals of 1/60 1/30 & 1/15
+ *
+ * It is the recommended Director if the SDK is 3.1 or newer
+ *
+ * @since v0.8.2
+ */
+@interface CCDirectorDisplayLink : CCDirectorMac
+{
+ CVDisplayLinkRef displayLink;
+}
+@end
+
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+// Only compile this code on Mac. These files should not be included on your iOS project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+#import <sys/time.h>
+
+#import "CCDirectorMac.h"
+#import "CCEventDispatcher.h"
+#import "MacGLView.h"
+
+#import "../../CCNode.h"
+#import "../../CCScheduler.h"
+#import "../../ccMacros.h"
+
+#pragma mark -
+#pragma mark Director Mac extensions
+
+
+@interface CCDirector ()
+-(void) setNextScene;
+-(void) showFPS;
+-(void) calculateDeltaTime;
+@end
+
+@implementation CCDirector (MacExtension)
+-(CGPoint) convertEventToGL:(NSEvent*)event
+{
+ NSPoint point = [openGLView_ convertPoint:[event locationInWindow] fromView:nil];
+ CGPoint p = NSPointToCGPoint(point);
+
+ return [(CCDirectorMac*)self convertToLogicalCoordinates:p];
+}
+
+@end
+
+#pragma mark -
+#pragma mark Director Mac
+
+@implementation CCDirectorMac
+
+@synthesize isFullScreen = isFullScreen_;
+@synthesize originalWinSize = originalWinSize_;
+
+-(id) init
+{
+ if( (self = [super init]) ) {
+ isFullScreen_ = NO;
+ resizeMode_ = kCCDirectorResize_AutoScale;
+
+ originalWinSize_ = CGSizeZero;
+ fullScreenWindow_ = nil;
+ windowGLView_ = nil;
+ winOffset_ = CGPointZero;
+ }
+
+ return self;
+}
+
+- (void) dealloc
+{
+ [superViewGLView_ release];
+ [fullScreenWindow_ release];
+ [windowGLView_ release];
+ [super dealloc];
+}
+
+//
+// setFullScreen code taken from GLFullScreen example by Apple
+//
+- (void) setFullScreen:(BOOL)fullscreen
+{
+ // Mac OS X 10.6 and later offer a simplified mechanism to create full-screen contexts
+#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5
+
+ if (isFullScreen_ == fullscreen) return;
+
+ if( fullscreen ) {
+ originalWinRect_ = [openGLView_ frame];
+
+ // Cache normal window and superview of openGLView
+ if(!windowGLView_)
+ windowGLView_ = [[openGLView_ window] retain];
+
+ [superViewGLView_ release];
+ superViewGLView_ = [[openGLView_ superview] retain];
+
+
+ // Get screen size
+ NSRect displayRect = [[NSScreen mainScreen] frame];
+
+ // Create a screen-sized window on the display you want to take over
+ fullScreenWindow_ = [[MacWindow alloc] initWithFrame:displayRect fullscreen:YES];
+
+ // Remove glView from window
+ [openGLView_ removeFromSuperview];
+
+ // Set new frame
+ [openGLView_ setFrame:displayRect];
+
+ // Attach glView to fullscreen window
+ [fullScreenWindow_ setContentView:openGLView_];
+
+ // Show the fullscreen window
+ [fullScreenWindow_ makeKeyAndOrderFront:self];
+ [fullScreenWindow_ makeMainWindow];
+
+ } else {
+
+ // Remove glView from fullscreen window
+ [openGLView_ removeFromSuperview];
+
+ // Release fullscreen window
+ [fullScreenWindow_ release];
+ fullScreenWindow_ = nil;
+
+ // Attach glView to superview
+ [superViewGLView_ addSubview:openGLView_];
+
+ // Set new frame
+ [openGLView_ setFrame:originalWinRect_];
+
+ // Show the window
+ [windowGLView_ makeKeyAndOrderFront:self];
+ [windowGLView_ makeMainWindow];
+ }
+ isFullScreen_ = fullscreen;
+
+ [openGLView_ retain]; // Retain +1
+
+ // re-configure glView
+ [self setOpenGLView:openGLView_];
+
+ [openGLView_ release]; // Retain -1
+
+ [openGLView_ setNeedsDisplay:YES];
+#else
+#error Full screen is not supported for Mac OS 10.5 or older yet
+#error If you don't want FullScreen support, you can safely remove these 2 lines
+#endif
+}
+
+-(void) setOpenGLView:(MacGLView *)view
+{
+ [super setOpenGLView:view];
+
+ // cache the NSWindow and NSOpenGLView created from the NIB
+ if( !isFullScreen_ && CGSizeEqualToSize(originalWinSize_, CGSizeZero))
+ {
+ originalWinSize_ = winSizeInPixels_;
+ }
+}
+
+-(int) resizeMode
+{
+ return resizeMode_;
+}
+
+-(void) setResizeMode:(int)mode
+{
+ if( mode != resizeMode_ ) {
+
+ resizeMode_ = mode;
+
+ [self setProjection:projection_];
+ [openGLView_ setNeedsDisplay: YES];
+ }
+}
+
+-(void) setProjection:(ccDirectorProjection)projection
+{
+ CGSize size = winSizeInPixels_;
+
+ CGPoint offset = CGPointZero;
+ float widthAspect = size.width;
+ float heightAspect = size.height;
+
+
+ if( resizeMode_ == kCCDirectorResize_AutoScale && ! CGSizeEqualToSize(originalWinSize_, CGSizeZero ) ) {
+
+ size = originalWinSize_;
+
+ float aspect = originalWinSize_.width / originalWinSize_.height;
+ widthAspect = winSizeInPixels_.width;
+ heightAspect = winSizeInPixels_.width / aspect;
+
+ if( heightAspect > winSizeInPixels_.height ) {
+ widthAspect = winSizeInPixels_.height * aspect;
+ heightAspect = winSizeInPixels_.height;
+ }
+
+ winOffset_.x = (winSizeInPixels_.width - widthAspect) / 2;
+ winOffset_.y = (winSizeInPixels_.height - heightAspect) / 2;
+
+ offset = winOffset_;
+
+ }
+
+ switch (projection) {
+ case kCCDirectorProjection2D:
+ glViewport(offset.x, offset.y, widthAspect, heightAspect);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ ccglOrtho(0, size.width, 0, size.height, -1024, 1024);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ break;
+
+ case kCCDirectorProjection3D:
+ glViewport(offset.x, offset.y, widthAspect, heightAspect);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ gluPerspective(60, (GLfloat)widthAspect/heightAspect, 0.1f, 1500.0f);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ float eyeZ = size.height * [self getZEye] / winSizeInPixels_.height;
+
+ gluLookAt( size.width/2, size.height/2, eyeZ,
+ size.width/2, size.height/2, 0,
+ 0.0f, 1.0f, 0.0f);
+ break;
+
+ case kCCDirectorProjectionCustom:
+ if( projectionDelegate_ )
+ [projectionDelegate_ updateProjection];
+ break;
+
+ default:
+ CCLOG(@"cocos2d: Director: unrecognized projecgtion");
+ break;
+ }
+
+ projection_ = projection;
+}
+
+// If scaling is supported, then it should always return the original size
+// otherwise it should return the "real" size.
+-(CGSize) winSize
+{
+ if( resizeMode_ == kCCDirectorResize_AutoScale )
+ return originalWinSize_;
+
+ return winSizeInPixels_;
+}
+
+-(CGSize) winSizeInPixels
+{
+ return [self winSize];
+}
+
+- (CGPoint) convertToLogicalCoordinates:(CGPoint)coords
+{
+ CGPoint ret;
+
+ if( resizeMode_ == kCCDirectorResize_NoScale )
+ ret = coords;
+
+ else {
+
+ float x_diff = originalWinSize_.width / (winSizeInPixels_.width - winOffset_.x * 2);
+ float y_diff = originalWinSize_.height / (winSizeInPixels_.height - winOffset_.y * 2);
+
+ float adjust_x = (winSizeInPixels_.width * x_diff - originalWinSize_.width ) / 2;
+ float adjust_y = (winSizeInPixels_.height * y_diff - originalWinSize_.height ) / 2;
+
+ ret = CGPointMake( (x_diff * coords.x) - adjust_x, ( y_diff * coords.y ) - adjust_y );
+ }
+
+ return ret;
+}
+@end
+
+
+#pragma mark -
+#pragma mark DirectorDisplayLink
+
+
+@implementation CCDirectorDisplayLink
+
+- (CVReturn) getFrameForTime:(const CVTimeStamp*)outputTime
+{
+#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
+ if( ! runningThread_ )
+ runningThread_ = [NSThread currentThread];
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ [self drawScene];
+ [[CCEventDispatcher sharedDispatcher] dispatchQueuedEvents];
+
+ [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:nil];
+
+ [pool release];
+
+#else
+ [self performSelector:@selector(drawScene) onThread:runningThread_ withObject:nil waitUntilDone:YES];
+#endif
+
+ return kCVReturnSuccess;
+}
+
+// This is the renderer output callback function
+static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
+{
+ CVReturn result = [(CCDirectorDisplayLink*)displayLinkContext getFrameForTime:outputTime];
+ return result;
+}
+
+- (void) startAnimation
+{
+#if ! CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
+ runningThread_ = [[NSThread alloc] initWithTarget:self selector:@selector(mainLoop) object:nil];
+ [runningThread_ start];
+#endif
+
+ gettimeofday( &lastUpdate_, NULL);
+
+ // Create a display link capable of being used with all active displays
+ CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);
+
+ // Set the renderer output callback function
+ CVDisplayLinkSetOutputCallback(displayLink, &MyDisplayLinkCallback, self);
+
+ // Set the display link for the current renderer
+ CGLContextObj cglContext = [[openGLView_ openGLContext] CGLContextObj];
+ CGLPixelFormatObj cglPixelFormat = [[openGLView_ pixelFormat] CGLPixelFormatObj];
+ CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat);
+
+ // Activate the display link
+ CVDisplayLinkStart(displayLink);
+}
+
+- (void) stopAnimation
+{
+ if( displayLink ) {
+ CVDisplayLinkStop(displayLink);
+ CVDisplayLinkRelease(displayLink);
+ displayLink = NULL;
+
+#if ! CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
+ [runningThread_ cancel];
+ [runningThread_ release];
+ runningThread_ = nil;
+#endif
+ }
+}
+
+-(void) dealloc
+{
+ if( displayLink ) {
+ CVDisplayLinkStop(displayLink);
+ CVDisplayLinkRelease(displayLink);
+ }
+ [super dealloc];
+}
+
+//
+// Mac Director has its own thread
+//
+-(void) mainLoop
+{
+ while( ![[NSThread currentThread] isCancelled] ) {
+ // There is no autorelease pool when this method is called because it will be called from a background thread
+ // It's important to create one or you will leak objects
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ [[NSRunLoop currentRunLoop] run];
+
+ [pool release];
+ }
+}
+
+//
+// Draw the Scene
+//
+- (void) drawScene
+{
+ // We draw on a secondary thread through the display link
+ // When resizing the view, -reshape is called automatically on the main thread
+ // Add a mutex around to avoid the threads accessing the context simultaneously when resizing
+ CGLLockContext([[openGLView_ openGLContext] CGLContextObj]);
+ [[openGLView_ openGLContext] makeCurrentContext];
+
+ /* calculate "global" dt */
+ [self calculateDeltaTime];
+
+ /* tick before glClear: issue #533 */
+ if( ! isPaused_ ) {
+ [[CCScheduler sharedScheduler] tick: dt];
+ }
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ /* to avoid flickr, nextScene MUST be here: after tick and before draw.
+ XXX: Which bug is this one. It seems that it can't be reproduced with v0.9 */
+ if( nextScene_ )
+ [self setNextScene];
+
+ glPushMatrix();
+
+
+ // By default enable VertexArray, ColorArray, TextureCoordArray and Texture2D
+ CC_ENABLE_DEFAULT_GL_STATES();
+
+ /* draw the scene */
+ [runningScene_ visit];
+
+ /* draw the notification node */
+ [notificationNode_ visit];
+
+ if( displayFPS_ )
+ [self showFPS];
+
+#if CC_ENABLE_PROFILERS
+ [self showProfilers];
+#endif
+
+ CC_DISABLE_DEFAULT_GL_STATES();
+
+ glPopMatrix();
+
+ [[openGLView_ openGLContext] flushBuffer];
+ CGLUnlockContext([[openGLView_ openGLContext] CGLContextObj]);
+}
+
+// set the event dispatcher
+-(void) setOpenGLView:(MacGLView *)view
+{
+ if( view != openGLView_ ) {
+
+ [super setOpenGLView:view];
+
+ CCEventDispatcher *eventDispatcher = [CCEventDispatcher sharedDispatcher];
+ [openGLView_ setEventDelegate: eventDispatcher];
+ [eventDispatcher setDispatchEvents: YES];
+
+ // Enable Touches. Default no.
+ [view setAcceptsTouchEvents:NO];
+// [view setAcceptsTouchEvents:YES];
+
+
+ // Synchronize buffer swaps with vertical refresh rate
+ [[view openGLContext] makeCurrentContext];
+ GLint swapInt = 1;
+ [[view openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
+ }
+}
+
+@end
+
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+// Only compile this code on Mac. These files should not be included on your iOS project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+#import <Cocoa/Cocoa.h>
+
+#import "MacGLView.h"
+#import "../../Support/uthash.h" // hack: uthash needs to be imported before utlist to prevent warning
+#import "../../Support/utlist.h"
+#import "../../ccConfig.h"
+
+#pragma mark -
+#pragma mark CCMouseEventDelegate
+
+/** CCMouseEventDelegate protocol.
+ Implement it in your node to receive any of mouse events
+ */
+@protocol CCMouseEventDelegate <NSObject>
+@optional
+
+//
+// left
+//
+/** called when the "mouseDown" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+-(BOOL) ccMouseDown:(NSEvent*)event;
+
+/** called when the "mouseDragged" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+-(BOOL) ccMouseDragged:(NSEvent*)event;
+
+/** called when the "mouseMoved" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ By default, "mouseMoved" is disabled. To enable it, send the "setAcceptsMouseMovedEvents:YES" message to the main window.
+ */
+-(BOOL) ccMouseMoved:(NSEvent*)event;
+
+/** called when the "mouseUp" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+-(BOOL) ccMouseUp:(NSEvent*)event;
+
+
+//
+// right
+//
+
+/** called when the "rightMouseDown" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+-(BOOL) ccRightMouseDown:(NSEvent*)event;
+
+/** called when the "rightMouseDragged" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+-(BOOL) ccRightMouseDragged:(NSEvent*)event;
+
+/** called when the "rightMouseUp" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+-(BOOL) ccRightMouseUp:(NSEvent*)event;
+
+//
+// other
+//
+
+/** called when the "otherMouseDown" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+-(BOOL) ccOtherMouseDown:(NSEvent*)event;
+
+/** called when the "otherMouseDragged" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+-(BOOL) ccOtherMouseDragged:(NSEvent*)event;
+
+/** called when the "otherMouseUp" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+-(BOOL) ccOtherMouseUp:(NSEvent*)event;
+
+//
+// scroll wheel
+//
+
+/** called when the "scrollWheel" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+- (BOOL)ccScrollWheel:(NSEvent *)theEvent;
+
+
+//
+// enter / exit
+//
+
+/** called when the "mouseEntered" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+- (void)ccMouseEntered:(NSEvent *)theEvent;
+
+/** called when the "mouseExited" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+- (void)ccMouseExited:(NSEvent *)theEvent;
+
+@end
+
+#pragma mark -
+#pragma mark CCKeyboardEventDelegate
+
+/** CCKeyboardEventDelegate protocol.
+ Implement it in your node to receive any of keyboard events
+ */
+@protocol CCKeyboardEventDelegate <NSObject>
+@optional
+/** called when the "keyUp" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+-(BOOL) ccKeyUp:(NSEvent*)event;
+
+/** called when the "keyDown" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+-(BOOL) ccKeyDown:(NSEvent*)event;
+/** called when the "flagsChanged" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+-(BOOL) ccFlagsChanged:(NSEvent*)event;
+@end
+
+#pragma mark -
+#pragma mark CCTouchEventDelegate
+
+/** CCTouchEventDelegate protocol.
+ Implement it in your node to receive any of touch events
+ */
+@protocol CCTouchEventDelegate <NSObject>
+@optional
+/** called when the "touchesBegan" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+- (BOOL)ccTouchesBeganWithEvent:(NSEvent *)event;
+
+/** called when the "touchesMoved" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+- (BOOL)ccTouchesMovedWithEvent:(NSEvent *)event;
+
+/** called when the "touchesEnded" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+- (BOOL)ccTouchesEndedWithEvent:(NSEvent *)event;
+
+/** called when the "touchesCancelled" event is received.
+ Return YES to avoid propagating the event to other delegates.
+ */
+- (BOOL)ccTouchesCancelledWithEvent:(NSEvent *)event;
+
+@end
+
+
+#pragma mark -
+#pragma mark CCEventDispatcher
+
+struct _listEntry;
+
+/** CCEventDispatcher
+
+ This is object is responsible for dispatching the events:
+ - Mouse events
+ - Keyboard events
+ - Touch events
+
+ Only available on Mac
+ */
+@interface CCEventDispatcher : NSObject <MacEventDelegate> {
+
+ BOOL dispatchEvents_;
+
+ struct _listEntry *keyboardDelegates_;
+ struct _listEntry *mouseDelegates_;
+ struct _listEntry *touchDelegates_;
+}
+
+@property (nonatomic, readwrite) BOOL dispatchEvents;
+
+
+/** CCEventDispatcher singleton */
++(CCEventDispatcher*) sharedDispatcher;
+
+#pragma mark CCEventDispatcher - Mouse
+
+/** Adds a mouse delegate to the dispatcher's list.
+ Delegates with a lower priority value will be called before higher priority values.
+ All the events will be propgated to all the delegates, unless the one delegate returns YES.
+
+ IMPORTANT: The delegate will be retained.
+ */
+-(void) addMouseDelegate:(id<CCMouseEventDelegate>) delegate priority:(NSInteger)priority;
+
+/** removes a mouse delegate */
+-(void) removeMouseDelegate:(id) delegate;
+
+/** Removes all mouse delegates, releasing all the delegates */
+-(void) removeAllMouseDelegates;
+
+#pragma mark CCEventDispatcher - Keyboard
+
+/** Adds a Keyboard delegate to the dispatcher's list.
+ Delegates with a lower priority value will be called before higher priority values.
+ All the events will be propgated to all the delegates, unless the one delegate returns YES.
+
+ IMPORTANT: The delegate will be retained.
+ */
+-(void) addKeyboardDelegate:(id<CCKeyboardEventDelegate>) delegate priority:(NSInteger)priority;
+
+/** removes a mouse delegate */
+-(void) removeKeyboardDelegate:(id) delegate;
+
+/** Removes all mouse delegates, releasing all the delegates */
+-(void) removeAllKeyboardDelegates;
+
+#pragma mark CCEventDispatcher - Touches
+
+/** Adds a Touch delegate to the dispatcher's list.
+ Delegates with a lower priority value will be called before higher priority values.
+ All the events will be propgated to all the delegates, unless the one delegate returns YES.
+
+ IMPORTANT: The delegate will be retained.
+ */
+- (void)addTouchDelegate:(id<CCTouchEventDelegate>)delegate priority:(NSInteger)priority;
+
+/** Removes a touch delegate */
+- (void)removeTouchDelegate:(id) delegate;
+
+/** Removes all touch delegates, releasing all the delegates */
+- (void)removeAllTouchDelegates;
+
+#pragma mark CCEventDispatcher - Dispatch Events
+
+#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
+-(void) dispatchQueuedEvents;
+#endif
+
+@end
+
+
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+// Only compile this code on Mac. These files should not be included on your iOS project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+#import "CCEventDispatcher.h"
+#import "../../ccConfig.h"
+
+static CCEventDispatcher *sharedDispatcher = nil;
+
+enum {
+ // mouse
+ kCCImplementsMouseDown = 1 << 0,
+ kCCImplementsMouseMoved = 1 << 1,
+ kCCImplementsMouseDragged = 1 << 2,
+ kCCImplementsMouseUp = 1 << 3,
+ kCCImplementsRightMouseDown = 1 << 4,
+ kCCImplementsRightMouseDragged = 1 << 5,
+ kCCImplementsRightMouseUp = 1 << 6,
+ kCCImplementsOtherMouseDown = 1 << 7,
+ kCCImplementsOtherMouseDragged = 1 << 8,
+ kCCImplementsOtherMouseUp = 1 << 9,
+ kCCImplementsScrollWheel = 1 << 10,
+ kCCImplementsMouseEntered = 1 << 11,
+ kCCImplementsMouseExited = 1 << 12,
+
+ kCCImplementsTouchesBegan = 1 << 13,
+ kCCImplementsTouchesMoved = 1 << 14,
+ kCCImplementsTouchesEnded = 1 << 15,
+ kCCImplementsTouchesCancelled = 1 << 16,
+
+ // keyboard
+ kCCImplementsKeyUp = 1 << 0,
+ kCCImplementsKeyDown = 1 << 1,
+ kCCImplementsFlagsChanged = 1 << 2,
+};
+
+
+typedef struct _listEntry
+{
+ struct _listEntry *prev, *next;
+ id delegate;
+ NSInteger priority;
+ NSUInteger flags;
+} tListEntry;
+
+
+#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
+
+#define QUEUE_EVENT_MAX 128
+struct _eventQueue {
+ SEL selector;
+ NSEvent *event;
+};
+
+static struct _eventQueue eventQueue[QUEUE_EVENT_MAX];
+static int eventQueueCount;
+
+#endif // CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
+
+
+@implementation CCEventDispatcher
+
+@synthesize dispatchEvents=dispatchEvents_;
+
+
++(CCEventDispatcher*) sharedDispatcher
+{
+ @synchronized(self) {
+ if (sharedDispatcher == nil)
+ sharedDispatcher = [[self alloc] init]; // assignment not done here
+ }
+ return sharedDispatcher;
+}
+
++(id) allocWithZone:(NSZone *)zone
+{
+ @synchronized(self) {
+ NSAssert(sharedDispatcher == nil, @"Attempted to allocate a second instance of a singleton.");
+ return [super allocWithZone:zone];
+ }
+ return nil; // on subsequent allocation attempts return nil
+}
+
+-(id) init
+{
+ if( (self = [super init]) )
+ {
+ // events enabled by default
+ dispatchEvents_ = YES;
+
+ // delegates
+ keyboardDelegates_ = NULL;
+ mouseDelegates_ = NULL;
+ touchDelegates_ = NULL;
+
+#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
+ eventQueueCount = 0;
+#endif
+ }
+
+ return self;
+}
+
+- (void) dealloc
+{
+ [super dealloc];
+}
+
+#pragma mark CCEventDispatcher - add / remove delegates
+
+-(void) addDelegate:(id)delegate priority:(NSInteger)priority flags:(NSUInteger)flags list:(tListEntry**)list
+{
+ tListEntry *listElement = malloc( sizeof(*listElement) );
+
+ listElement->delegate = [delegate retain];
+ listElement->priority = priority;
+ listElement->flags = flags;
+ listElement->next = listElement->prev = NULL;
+
+ // empty list ?
+ if( ! *list ) {
+ DL_APPEND( *list, listElement );
+
+ } else {
+ BOOL added = NO;
+
+ for( tListEntry *elem = *list; elem ; elem = elem->next ) {
+ if( priority < elem->priority ) {
+
+ if( elem == *list )
+ DL_PREPEND(*list, listElement);
+ else {
+ listElement->next = elem;
+ listElement->prev = elem->prev;
+
+ elem->prev->next = listElement;
+ elem->prev = listElement;
+ }
+
+ added = YES;
+ break;
+ }
+ }
+
+ // Not added? priority has the higher value. Append it.
+ if( !added )
+ DL_APPEND(*list, listElement);
+ }
+}
+
+-(void) removeDelegate:(id)delegate fromList:(tListEntry**)list
+{
+ tListEntry *entry, *tmp;
+
+ // updates with priority < 0
+ DL_FOREACH_SAFE( *list, entry, tmp ) {
+ if( entry->delegate == delegate ) {
+ DL_DELETE( *list, entry );
+ [delegate release];
+ free(entry);
+ break;
+ }
+ }
+}
+
+-(void) removeAllDelegatesFromList:(tListEntry**)list
+{
+ tListEntry *entry, *tmp;
+
+ DL_FOREACH_SAFE( *list, entry, tmp ) {
+ DL_DELETE( *list, entry );
+ free(entry);
+ }
+}
+
+
+-(void) addMouseDelegate:(id<CCMouseEventDelegate>) delegate priority:(NSInteger)priority
+{
+ NSUInteger flags = 0;
+
+ flags |= ( [delegate respondsToSelector:@selector(ccMouseDown:)] ? kCCImplementsMouseDown : 0 );
+ flags |= ( [delegate respondsToSelector:@selector(ccMouseDragged:)] ? kCCImplementsMouseDragged : 0 );
+ flags |= ( [delegate respondsToSelector:@selector(ccMouseMoved:)] ? kCCImplementsMouseMoved : 0 );
+ flags |= ( [delegate respondsToSelector:@selector(ccMouseUp:)] ? kCCImplementsMouseUp : 0 );
+
+ flags |= ( [delegate respondsToSelector:@selector(ccRightMouseDown:)] ? kCCImplementsRightMouseDown : 0 );
+ flags |= ( [delegate respondsToSelector:@selector(ccRightMouseDragged:)] ? kCCImplementsRightMouseDragged : 0 );
+ flags |= ( [delegate respondsToSelector:@selector(ccRightMouseUp:)] ? kCCImplementsRightMouseUp : 0 );
+
+ flags |= ( [delegate respondsToSelector:@selector(ccOtherMouseDown:)] ? kCCImplementsOtherMouseDown : 0 );
+ flags |= ( [delegate respondsToSelector:@selector(ccOtherMouseDragged:)] ? kCCImplementsOtherMouseDragged : 0 );
+ flags |= ( [delegate respondsToSelector:@selector(ccOtherMouseUp:)] ? kCCImplementsOtherMouseUp : 0 );
+
+ flags |= ( [delegate respondsToSelector:@selector(ccMouseEntered:)] ? kCCImplementsMouseEntered : 0 );
+ flags |= ( [delegate respondsToSelector:@selector(ccMouseExited:)] ? kCCImplementsMouseExited : 0 );
+
+ flags |= ( [delegate respondsToSelector:@selector(ccScrollWheel:)] ? kCCImplementsScrollWheel : 0 );
+
+ [self addDelegate:delegate priority:priority flags:flags list:&mouseDelegates_];
+}
+
+-(void) removeMouseDelegate:(id) delegate
+{
+ [self removeDelegate:delegate fromList:&mouseDelegates_];
+}
+
+-(void) removeAllMouseDelegates
+{
+ [self removeAllDelegatesFromList:&mouseDelegates_];
+}
+
+-(void) addKeyboardDelegate:(id<CCKeyboardEventDelegate>) delegate priority:(NSInteger)priority
+{
+ NSUInteger flags = 0;
+
+ flags |= ( [delegate respondsToSelector:@selector(ccKeyUp:)] ? kCCImplementsKeyUp : 0 );
+ flags |= ( [delegate respondsToSelector:@selector(ccKeyDown:)] ? kCCImplementsKeyDown : 0 );
+ flags |= ( [delegate respondsToSelector:@selector(ccFlagsChanged:)] ? kCCImplementsFlagsChanged : 0 );
+
+ [self addDelegate:delegate priority:priority flags:flags list:&keyboardDelegates_];
+}
+
+-(void) removeKeyboardDelegate:(id) delegate
+{
+ [self removeDelegate:delegate fromList:&keyboardDelegates_];
+}
+
+-(void) removeAllKeyboardDelegates
+{
+ [self removeAllDelegatesFromList:&keyboardDelegates_];
+}
+
+-(void) addTouchDelegate:(id<CCTouchEventDelegate>) delegate priority:(NSInteger)priority
+{
+ NSUInteger flags = 0;
+
+ flags |= ( [delegate respondsToSelector:@selector(ccTouchesBeganWithEvent:)] ? kCCImplementsTouchesBegan : 0 );
+ flags |= ( [delegate respondsToSelector:@selector(ccTouchesMovedWithEvent:)] ? kCCImplementsTouchesMoved : 0 );
+ flags |= ( [delegate respondsToSelector:@selector(ccTouchesEndedWithEvent:)] ? kCCImplementsTouchesEnded : 0 );
+ flags |= ( [delegate respondsToSelector:@selector(ccTouchesCancelledWithEvent:)] ? kCCImplementsTouchesCancelled : 0 );
+
+ [self addDelegate:delegate priority:priority flags:flags list:&touchDelegates_];
+}
+
+-(void) removeTouchDelegate:(id) delegate
+{
+ [self removeDelegate:delegate fromList:&touchDelegates_];
+}
+
+-(void) removeAllTouchDelegates
+{
+ [self removeAllDelegatesFromList:&touchDelegates_];
+}
+
+
+#pragma mark CCEventDispatcher - Mouse events
+//
+// Mouse events
+//
+
+//
+// Left
+//
+- (void)mouseDown:(NSEvent *)event
+{
+ if( dispatchEvents_ ) {
+ tListEntry *entry, *tmp;
+
+ DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) {
+ if ( entry->flags & kCCImplementsMouseDown ) {
+ void *swallows = [entry->delegate performSelector:@selector(ccMouseDown:) withObject:event];
+ if( swallows )
+ break;
+ }
+ }
+ }
+}
+
+- (void)mouseMoved:(NSEvent *)event
+{
+ if( dispatchEvents_ ) {
+ tListEntry *entry, *tmp;
+
+ DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) {
+ if ( entry->flags & kCCImplementsMouseMoved ) {
+ void *swallows = [entry->delegate performSelector:@selector(ccMouseMoved:) withObject:event];
+ if( swallows )
+ break;
+ }
+ }
+ }
+}
+
+- (void)mouseDragged:(NSEvent *)event
+{
+ if( dispatchEvents_ ) {
+ tListEntry *entry, *tmp;
+
+ DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) {
+ if ( entry->flags & kCCImplementsMouseDragged ) {
+ void *swallows = [entry->delegate performSelector:@selector(ccMouseDragged:) withObject:event];
+ if( swallows )
+ break;
+ }
+ }
+ }
+}
+
+- (void)mouseUp:(NSEvent *)event
+{
+ if( dispatchEvents_ ) {
+ tListEntry *entry, *tmp;
+
+ DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) {
+ if ( entry->flags & kCCImplementsMouseUp ) {
+ void *swallows = [entry->delegate performSelector:@selector(ccMouseUp:) withObject:event];
+ if( swallows )
+ break;
+ }
+ }
+ }
+}
+
+//
+// Mouse Right
+//
+- (void)rightMouseDown:(NSEvent *)event
+{
+ if( dispatchEvents_ ) {
+ tListEntry *entry, *tmp;
+
+ DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) {
+ if ( entry->flags & kCCImplementsRightMouseDown ) {
+ void *swallows = [entry->delegate performSelector:@selector(ccRightMouseDown:) withObject:event];
+ if( swallows )
+ break;
+ }
+ }
+ }
+}
+
+- (void)rightMouseDragged:(NSEvent *)event
+{
+ if( dispatchEvents_ ) {
+ tListEntry *entry, *tmp;
+
+ DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) {
+ if ( entry->flags & kCCImplementsRightMouseDragged ) {
+ void *swallows = [entry->delegate performSelector:@selector(ccRightMouseDragged:) withObject:event];
+ if( swallows )
+ break;
+ }
+ }
+ }
+}
+
+- (void)rightMouseUp:(NSEvent *)event
+{
+ if( dispatchEvents_ ) {
+ tListEntry *entry, *tmp;
+
+ DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) {
+ if ( entry->flags & kCCImplementsRightMouseUp ) {
+ void *swallows = [entry->delegate performSelector:@selector(ccRightMouseUp:) withObject:event];
+ if( swallows )
+ break;
+ }
+ }
+ }
+}
+
+//
+// Mouse Other
+//
+- (void)otherMouseDown:(NSEvent *)event
+{
+ if( dispatchEvents_ ) {
+ tListEntry *entry, *tmp;
+
+ DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) {
+ if ( entry->flags & kCCImplementsOtherMouseDown ) {
+ void *swallows = [entry->delegate performSelector:@selector(ccOtherMouseDown:) withObject:event];
+ if( swallows )
+ break;
+ }
+ }
+ }
+}
+
+- (void)otherMouseDragged:(NSEvent *)event
+{
+ if( dispatchEvents_ ) {
+ tListEntry *entry, *tmp;
+
+ DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) {
+ if ( entry->flags & kCCImplementsOtherMouseDragged ) {
+ void *swallows = [entry->delegate performSelector:@selector(ccOtherMouseDragged:) withObject:event];
+ if( swallows )
+ break;
+ }
+ }
+ }
+}
+
+- (void)otherMouseUp:(NSEvent *)event
+{
+ if( dispatchEvents_ ) {
+ tListEntry *entry, *tmp;
+
+ DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) {
+ if ( entry->flags & kCCImplementsOtherMouseUp ) {
+ void *swallows = [entry->delegate performSelector:@selector(ccOtherMouseUp:) withObject:event];
+ if( swallows )
+ break;
+ }
+ }
+ }
+}
+
+//
+// Scroll Wheel
+//
+- (void)scrollWheel:(NSEvent *)event
+{
+ if( dispatchEvents_ ) {
+ tListEntry *entry, *tmp;
+
+ DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) {
+ if ( entry->flags & kCCImplementsScrollWheel ) {
+ void *swallows = [entry->delegate performSelector:@selector(ccScrollWheel:) withObject:event];
+ if( swallows )
+ break;
+ }
+ }
+ }
+}
+
+//
+// Mouse enter / exit
+- (void)mouseExited:(NSEvent *)event
+{
+ if( dispatchEvents_ ) {
+ tListEntry *entry, *tmp;
+
+ DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) {
+ if ( entry->flags & kCCImplementsMouseEntered ) {
+ void *swallows = [entry->delegate performSelector:@selector(ccMouseEntered:) withObject:event];
+ if( swallows )
+ break;
+ }
+ }
+ }
+}
+
+- (void)mouseEntered:(NSEvent *)event
+{
+ if( dispatchEvents_ ) {
+ tListEntry *entry, *tmp;
+
+ DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) {
+ if ( entry->flags & kCCImplementsMouseExited) {
+ void *swallows = [entry->delegate performSelector:@selector(ccMouseExited:) withObject:event];
+ if( swallows )
+ break;
+ }
+ }
+ }
+}
+
+
+#pragma mark CCEventDispatcher - Keyboard events
+
+// Keyboard events
+- (void)keyDown:(NSEvent *)event
+{
+ if( dispatchEvents_ ) {
+ tListEntry *entry, *tmp;
+
+ DL_FOREACH_SAFE( keyboardDelegates_, entry, tmp ) {
+ if ( entry->flags & kCCImplementsKeyDown ) {
+ void *swallows = [entry->delegate performSelector:@selector(ccKeyDown:) withObject:event];
+ if( swallows )
+ break;
+ }
+ }
+ }
+}
+
+- (void)keyUp:(NSEvent *)event
+{
+ if( dispatchEvents_ ) {
+ tListEntry *entry, *tmp;
+
+ DL_FOREACH_SAFE( keyboardDelegates_, entry, tmp ) {
+ if ( entry->flags & kCCImplementsKeyUp ) {
+ void *swallows = [entry->delegate performSelector:@selector(ccKeyUp:) withObject:event];
+ if( swallows )
+ break;
+ }
+ }
+ }
+}
+
+- (void)flagsChanged:(NSEvent *)event
+{
+ if( dispatchEvents_ ) {
+ tListEntry *entry, *tmp;
+
+ DL_FOREACH_SAFE( keyboardDelegates_, entry, tmp ) {
+ if ( entry->flags & kCCImplementsFlagsChanged ) {
+ void *swallows = [entry->delegate performSelector:@selector(ccFlagsChanged:) withObject:event];
+ if( swallows )
+ break;
+ }
+ }
+ }
+}
+
+
+#pragma mark CCEventDispatcher - Touch events
+
+- (void)touchesBeganWithEvent:(NSEvent *)event
+{
+ if( dispatchEvents_ ) {
+ tListEntry *entry, *tmp;
+
+ DL_FOREACH_SAFE( touchDelegates_, entry, tmp ) {
+ if ( entry->flags & kCCImplementsTouchesBegan) {
+ void *swallows = [entry->delegate performSelector:@selector(ccTouchesBeganWithEvent:) withObject:event];
+ if( swallows )
+ break;
+ }
+ }
+ }
+}
+
+- (void)touchesMovedWithEvent:(NSEvent *)event
+{
+ if( dispatchEvents_ ) {
+ tListEntry *entry, *tmp;
+
+ DL_FOREACH_SAFE( touchDelegates_, entry, tmp ) {
+ if ( entry->flags & kCCImplementsTouchesMoved) {
+ void *swallows = [entry->delegate performSelector:@selector(ccTouchesMovedWithEvent:) withObject:event];
+ if( swallows )
+ break;
+ }
+ }
+ }
+}
+
+- (void)touchesEndedWithEvent:(NSEvent *)event
+{
+ if( dispatchEvents_ ) {
+ tListEntry *entry, *tmp;
+
+ DL_FOREACH_SAFE( touchDelegates_, entry, tmp ) {
+ if ( entry->flags & kCCImplementsTouchesEnded) {
+ void *swallows = [entry->delegate performSelector:@selector(ccTouchesEndedWithEvent:) withObject:event];
+ if( swallows )
+ break;
+ }
+ }
+ }
+}
+
+- (void)touchesCancelledWithEvent:(NSEvent *)event
+{
+ if( dispatchEvents_ ) {
+ tListEntry *entry, *tmp;
+
+ DL_FOREACH_SAFE( touchDelegates_, entry, tmp ) {
+ if ( entry->flags & kCCImplementsTouchesCancelled) {
+ void *swallows = [entry->delegate performSelector:@selector(ccTouchesCancelledWithEvent:) withObject:event];
+ if( swallows )
+ break;
+ }
+ }
+ }
+}
+
+
+#pragma mark CCEventDispatcher - queue events
+
+#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
+-(void) queueEvent:(NSEvent*)event selector:(SEL)selector
+{
+ NSAssert( eventQueueCount < QUEUE_EVENT_MAX, @"CCEventDispatcher: recompile. Increment QUEUE_EVENT_MAX value");
+
+ @synchronized (self) {
+ eventQueue[eventQueueCount].selector = selector;
+ eventQueue[eventQueueCount].event = [event copy];
+
+ eventQueueCount++;
+ }
+}
+
+-(void) dispatchQueuedEvents
+{
+ @synchronized (self) {
+ for( int i=0; i < eventQueueCount; i++ ) {
+ SEL sel = eventQueue[i].selector;
+ NSEvent *event = eventQueue[i].event;
+
+ [self performSelector:sel withObject:event];
+
+ [event release];
+ }
+
+ eventQueueCount = 0;
+ }
+}
+#endif // CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
+
+
+@end
+
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
\ No newline at end of file
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+// Only compile this code on Mac. These files should not be included on your iOS project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+#import <Cocoa/Cocoa.h>
+
+#import "../../ccConfig.h"
+
+//PROTOCOLS:
+
+@protocol MacEventDelegate <NSObject>
+// Mouse
+- (void)mouseDown:(NSEvent *)theEvent;
+- (void)mouseUp:(NSEvent *)theEvent;
+- (void)mouseMoved:(NSEvent *)theEvent;
+- (void)mouseDragged:(NSEvent *)theEvent;
+- (void)rightMouseDown:(NSEvent*)event;
+- (void)rightMouseDragged:(NSEvent*)event;
+- (void)rightMouseUp:(NSEvent*)event;
+- (void)otherMouseDown:(NSEvent*)event;
+- (void)otherMouseDragged:(NSEvent*)event;
+- (void)otherMouseUp:(NSEvent*)event;
+- (void)scrollWheel:(NSEvent *)theEvent;
+- (void)mouseEntered:(NSEvent *)theEvent;
+- (void)mouseExited:(NSEvent *)theEvent;
+
+
+// Keyboard
+- (void)keyDown:(NSEvent *)theEvent;
+- (void)keyUp:(NSEvent *)theEvent;
+- (void)flagsChanged:(NSEvent *)theEvent;
+
+// Touches
+- (void)touchesBeganWithEvent:(NSEvent *)event;
+- (void)touchesMovedWithEvent:(NSEvent *)event;
+- (void)touchesEndedWithEvent:(NSEvent *)event;
+- (void)touchesCancelledWithEvent:(NSEvent *)event;
+
+#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
+- (void)queueEvent:(NSEvent*)event selector:(SEL)selector;
+#endif
+
+@end
+
+/** MacGLView
+
+ Only available for Mac OS X
+ */
+@interface MacGLView : NSOpenGLView {
+ id<MacEventDelegate> eventDelegate_;
+}
+
+@property (nonatomic, readwrite, assign) id<MacEventDelegate> eventDelegate;
+
+// initializes the MacGLView with a frame rect and an OpenGL context
+- (id) initWithFrame:(NSRect)frameRect shareContext:(NSOpenGLContext*)context;
+
+// private
++(void) load_;
+@end
+
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
\ No newline at end of file
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * Idea of subclassing NSOpenGLView was taken from "TextureUpload" Apple's sample
+ */
+
+// Only compile this code on Mac. These files should not be included on your iOS project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+#import "MacGLView.h"
+#import <OpenGL/gl.h>
+
+#import "CCDirectorMac.h"
+#import "../../ccConfig.h"
+
+
+@implementation MacGLView
+
+@synthesize eventDelegate = eventDelegate_;
+
++(void) load_
+{
+ NSLog(@"%@ loaded", self);
+}
+
+- (id) initWithFrame:(NSRect)frameRect
+{
+ self = [self initWithFrame:frameRect shareContext:nil];
+ return self;
+}
+
+- (id) initWithFrame:(NSRect)frameRect shareContext:(NSOpenGLContext*)context
+{
+ NSOpenGLPixelFormatAttribute attribs[] =
+ {
+ NSOpenGLPFAAccelerated,
+ NSOpenGLPFANoRecovery,
+ NSOpenGLPFADoubleBuffer,
+ NSOpenGLPFADepthSize, 24,
+
+ 0
+ };
+
+ NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
+
+ if (!pixelFormat)
+ NSLog(@"No OpenGL pixel format");
+
+ if( (self = [super initWithFrame:frameRect pixelFormat:[pixelFormat autorelease]]) ) {
+
+ if( context )
+ [self setOpenGLContext:context];
+
+ // Synchronize buffer swaps with vertical refresh rate
+ GLint swapInt = 1;
+ [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
+
+// GLint order = -1;
+// [[self openGLContext] setValues:&order forParameter:NSOpenGLCPSurfaceOrder];
+
+ // event delegate
+ eventDelegate_ = nil;
+ }
+
+ return self;
+}
+
+- (void) reshape
+{
+ // We draw on a secondary thread through the display link
+ // When resizing the view, -reshape is called automatically on the main thread
+ // Add a mutex around to avoid the threads accessing the context simultaneously when resizing
+ CGLLockContext([[self openGLContext] CGLContextObj]);
+
+ NSRect rect = [self bounds];
+
+ CCDirector *director = [CCDirector sharedDirector];
+ [director reshapeProjection: NSSizeToCGSize(rect.size) ];
+
+ // avoid flicker
+ [director drawScene];
+// [self setNeedsDisplay:YES];
+
+ CGLUnlockContext([[self openGLContext] CGLContextObj]);
+}
+
+- (void) dealloc
+{
+
+ [super dealloc];
+}
+
+#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
+#define DISPATCH_EVENT(__event__, __selector__) [eventDelegate_ queueEvent:__event__ selector:__selector__];
+#else
+#define DISPATCH_EVENT(__event__, __selector__) \
+ id obj = eventDelegate_; \
+ [obj performSelector:__selector__ \
+ onThread:[(CCDirectorMac*)[CCDirector sharedDirector] runningThread] \
+ withObject:__event__ \
+ waitUntilDone:NO];
+#endif
+
+#pragma mark MacGLView - Mouse events
+- (void)mouseDown:(NSEvent *)theEvent
+{
+ DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)mouseMoved:(NSEvent *)theEvent
+{
+ DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)mouseDragged:(NSEvent *)theEvent
+{
+ DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)mouseUp:(NSEvent *)theEvent
+{
+ DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)rightMouseDown:(NSEvent *)theEvent {
+ DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)rightMouseDragged:(NSEvent *)theEvent {
+ DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)rightMouseUp:(NSEvent *)theEvent {
+ DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)otherMouseDown:(NSEvent *)theEvent {
+ DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)otherMouseDragged:(NSEvent *)theEvent {
+ DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)otherMouseUp:(NSEvent *)theEvent {
+ DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)mouseEntered:(NSEvent *)theEvent {
+ DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)mouseExited:(NSEvent *)theEvent {
+ DISPATCH_EVENT(theEvent, _cmd);
+}
+
+-(void) scrollWheel:(NSEvent *)theEvent {
+ DISPATCH_EVENT(theEvent, _cmd);
+}
+
+#pragma mark MacGLView - Key events
+
+-(BOOL) becomeFirstResponder
+{
+ return YES;
+}
+
+-(BOOL) acceptsFirstResponder
+{
+ return YES;
+}
+
+-(BOOL) resignFirstResponder
+{
+ return YES;
+}
+
+- (void)keyDown:(NSEvent *)theEvent
+{
+ DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)keyUp:(NSEvent *)theEvent
+{
+ DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)flagsChanged:(NSEvent *)theEvent
+{
+ DISPATCH_EVENT(theEvent, _cmd);
+}
+
+#pragma mark MacGLView - Touch events
+- (void)touchesBeganWithEvent:(NSEvent *)theEvent
+{
+ DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)touchesMovedWithEvent:(NSEvent *)theEvent
+{
+ DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)touchesEndedWithEvent:(NSEvent *)theEvent
+{
+ DISPATCH_EVENT(theEvent, _cmd);
+}
+
+- (void)touchesCancelledWithEvent:(NSEvent *)theEvent
+{
+ DISPATCH_EVENT(theEvent, _cmd);
+}
+
+@end
+
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
\ No newline at end of file
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+// Only compile this code on Mac. These files should not be included on your iOS project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+#import <Cocoa/Cocoa.h>
+
+
+@interface MacWindow : NSWindow
+{
+}
+- (id) initWithFrame:(NSRect)frame fullscreen:(BOOL)fullscreen;
+
+@end
+
+
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
\ No newline at end of file
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+// Only compile this code on Mac. These files should not be included on your iOS project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+
+#import "MacWindow.h"
+
+
+@implementation MacWindow
+
+- (id) initWithFrame:(NSRect)frame fullscreen:(BOOL)fullscreen
+{
+ int styleMask = fullscreen ? NSBackingStoreBuffered : ( NSTitledWindowMask | NSClosableWindowMask );
+ self = [self initWithContentRect:frame
+ styleMask:styleMask
+ backing:NSBackingStoreBuffered
+ defer:YES];
+
+ if (self != nil)
+ {
+ if(fullscreen)
+ {
+ [self setLevel:NSMainMenuWindowLevel+1];
+ [self setHidesOnDeactivate:YES];
+ [self setHasShadow:NO];
+ }
+
+ [self setAcceptsMouseMovedEvents:NO];
+ [self setOpaque:YES];
+ }
+ return self;
+}
+
+- (BOOL) canBecomeKeyWindow
+{
+ return YES;
+}
+
+- (BOOL) canBecomeMainWindow
+{
+ return YES;
+}
+@end
+
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#import "../../CCDirector.h"
+
+/** @typedef ccDeviceOrientation
+ Possible device orientations
+ */
+typedef enum {
+ /// Device oriented vertically, home button on the bottom
+ kCCDeviceOrientationPortrait = UIDeviceOrientationPortrait,
+ /// Device oriented vertically, home button on the top
+ kCCDeviceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
+ /// Device oriented horizontally, home button on the right
+ kCCDeviceOrientationLandscapeLeft = UIDeviceOrientationLandscapeLeft,
+ /// Device oriented horizontally, home button on the left
+ kCCDeviceOrientationLandscapeRight = UIDeviceOrientationLandscapeRight,
+
+ // Backward compatibility stuff
+ CCDeviceOrientationPortrait = kCCDeviceOrientationPortrait,
+ CCDeviceOrientationPortraitUpsideDown = kCCDeviceOrientationPortraitUpsideDown,
+ CCDeviceOrientationLandscapeLeft = kCCDeviceOrientationLandscapeLeft,
+ CCDeviceOrientationLandscapeRight = kCCDeviceOrientationLandscapeRight,
+} ccDeviceOrientation;
+
+/** @typedef ccDirectorType
+ Possible Director Types.
+ @since v0.8.2
+ */
+typedef enum {
+ /** Will use a Director that triggers the main loop from an NSTimer object
+ *
+ * Features and Limitations:
+ * - Integrates OK with UIKit objects
+ * - It the slowest director
+ * - The invertal update is customizable from 1 to 60
+ */
+ kCCDirectorTypeNSTimer,
+
+ /** will use a Director that triggers the main loop from a custom main loop.
+ *
+ * Features and Limitations:
+ * - Faster than NSTimer Director
+ * - It doesn't integrate well with UIKit objecgts
+ * - The interval update can't be customizable
+ */
+ kCCDirectorTypeMainLoop,
+
+ /** Will use a Director that triggers the main loop from a thread, but the main loop will be executed on the main thread.
+ *
+ * Features and Limitations:
+ * - Faster than NSTimer Director
+ * - It doesn't integrate well with UIKit objecgts
+ * - The interval update can't be customizable
+ */
+ kCCDirectorTypeThreadMainLoop,
+
+ /** Will use a Director that synchronizes timers with the refresh rate of the display.
+ *
+ * Features and Limitations:
+ * - Faster than NSTimer Director
+ * - Only available on 3.1+
+ * - Scheduled timers & drawing are synchronizes with the refresh rate of the display
+ * - Integrates OK with UIKit objects
+ * - The interval update can be 1/60, 1/30, 1/15
+ */
+ kCCDirectorTypeDisplayLink,
+
+ /** Default director is the NSTimer directory */
+ kCCDirectorTypeDefault = kCCDirectorTypeNSTimer,
+
+ // backward compatibility stuff
+ CCDirectorTypeNSTimer = kCCDirectorTypeNSTimer,
+ CCDirectorTypeMainLoop = kCCDirectorTypeMainLoop,
+ CCDirectorTypeThreadMainLoop = kCCDirectorTypeThreadMainLoop,
+ CCDirectorTypeDisplayLink = kCCDirectorTypeDisplayLink,
+ CCDirectorTypeDefault = kCCDirectorTypeDefault,
+
+
+} ccDirectorType;
+
+/** CCDirector extensions for iPhone
+ */
+@interface CCDirector (iOSExtension)
+
+// rotates the screen if an orientation differnent than Portrait is used
+-(void) applyOrientation;
+
+/** Sets the device orientation.
+ If the orientation is going to be controlled by an UIViewController, then the orientation should be Portrait
+ */
+-(void) setDeviceOrientation:(ccDeviceOrientation)orientation;
+
+/** returns the device orientation */
+-(ccDeviceOrientation) deviceOrientation;
+
+/** The size in pixels of the surface. It could be different than the screen size.
+ High-res devices might have a higher surface size than the screen size.
+ In non High-res device the contentScale will be emulated.
+
+ The recommend way to enable Retina Display is by using the "enableRetinaDisplay:(BOOL)enabled" method.
+
+ @since v0.99.4
+ */
+-(void) setContentScaleFactor:(CGFloat)scaleFactor;
+
+/** Will enable Retina Display on devices that supports it.
+ It will enable Retina Display on iPhone4 and iPod Touch 4.
+ It will return YES, if it could enabled it, otherwise it will return NO.
+
+ This is the recommened way to enable Retina Display.
+ @since v0.99.5
+ */
+-(BOOL) enableRetinaDisplay:(BOOL)yes;
+
+
+/** returns the content scale factor */
+-(CGFloat) contentScaleFactor;
+@end
+
+@interface CCDirector (iOSExtensionClassMethods)
+
+/** There are 4 types of Director.
+ - kCCDirectorTypeNSTimer (default)
+ - kCCDirectorTypeMainLoop
+ - kCCDirectorTypeThreadMainLoop
+ - kCCDirectorTypeDisplayLink
+
+ Each Director has it's own benefits, limitations.
+ If you are using SDK 3.1 or newer it is recommed to use the DisplayLink director
+
+ This method should be called before any other call to the director.
+
+ It will return NO if the director type is kCCDirectorTypeDisplayLink and the running SDK is < 3.1. Otherwise it will return YES.
+
+ @since v0.8.2
+ */
++(BOOL) setDirectorType:(ccDirectorType) directorType;
+@end
+
+#pragma mark -
+#pragma mark CCDirectorIOS
+
+/** CCDirectorIOS: Base class of iOS directors
+ @since v0.99.5
+ */
+@interface CCDirectorIOS : CCDirector
+{
+ /* orientation */
+ ccDeviceOrientation deviceOrientation_;
+
+ /* contentScaleFactor could be simulated */
+ BOOL isContentScaleSupported_;
+
+}
+@end
+
+/** FastDirector is a Director that triggers the main loop as fast as possible.
+ *
+ * Features and Limitations:
+ * - Faster than "normal" director
+ * - Consumes more battery than the "normal" director
+ * - It has some issues while using UIKit objects
+ */
+@interface CCDirectorFast : CCDirectorIOS
+{
+ BOOL isRunning;
+
+ NSAutoreleasePool *autoreleasePool;
+}
+-(void) mainLoop;
+@end
+
+/** ThreadedFastDirector is a Director that triggers the main loop from a thread.
+ *
+ * Features and Limitations:
+ * - Faster than "normal" director
+ * - Consumes more battery than the "normal" director
+ * - It can be used with UIKit objects
+ *
+ * @since v0.8.2
+ */
+@interface CCDirectorFastThreaded : CCDirectorIOS
+{
+ BOOL isRunning;
+}
+-(void) mainLoop;
+@end
+
+/** DisplayLinkDirector is a Director that synchronizes timers with the refresh rate of the display.
+ *
+ * Features and Limitations:
+ * - Only available on 3.1+
+ * - Scheduled timers & drawing are synchronizes with the refresh rate of the display
+ * - Only supports animation intervals of 1/60 1/30 & 1/15
+ *
+ * It is the recommended Director if the SDK is 3.1 or newer
+ *
+ * @since v0.8.2
+ */
+@interface CCDirectorDisplayLink : CCDirectorIOS
+{
+ id displayLink;
+}
+-(void) mainLoop:(id)sender;
+@end
+
+/** TimerDirector is a Director that calls the main loop from an NSTimer object
+ *
+ * Features and Limitations:
+ * - Integrates OK with UIKit objects
+ * - It the slowest director
+ * - The invertal update is customizable from 1 to 60
+ *
+ * It is the default Director.
+ */
+@interface CCDirectorTimer : CCDirectorIOS
+{
+ NSTimer *animationTimer;
+}
+-(void) mainLoop;
+@end
+
+// optimization. Should only be used to read it. Never to write it.
+extern CGFloat __ccContentScaleFactor;
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#import <unistd.h>
+
+// cocos2d imports
+#import "CCDirectorIOS.h"
+#import "CCTouchDelegateProtocol.h"
+#import "CCTouchDispatcher.h"
+#import "../../CCScheduler.h"
+#import "../../CCActionManager.h"
+#import "../../CCTextureCache.h"
+#import "../../ccMacros.h"
+#import "../../CCScene.h"
+
+// support imports
+#import "glu.h"
+#import "../../Support/OpenGL_Internal.h"
+#import "../../Support/CGPointExtension.h"
+
+#import "CCLayer.h"
+
+#if CC_ENABLE_PROFILERS
+#import "../../Support/CCProfiling.h"
+#endif
+
+
+#pragma mark -
+#pragma mark Director - global variables (optimization)
+
+CGFloat __ccContentScaleFactor = 1;
+
+#pragma mark -
+#pragma mark Director iOS
+
+@interface CCDirector ()
+-(void) setNextScene;
+-(void) showFPS;
+-(void) calculateDeltaTime;
+@end
+
+@implementation CCDirector (iOSExtensionClassMethods)
+
++(Class) defaultDirector
+{
+ return [CCDirectorTimer class];
+}
+
++ (BOOL) setDirectorType:(ccDirectorType)type
+{
+ if( type == CCDirectorTypeDisplayLink ) {
+ NSString *reqSysVer = @"3.1";
+ NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
+
+ if([currSysVer compare:reqSysVer options:NSNumericSearch] == NSOrderedAscending)
+ return NO;
+ }
+ switch (type) {
+ case CCDirectorTypeNSTimer:
+ [CCDirectorTimer sharedDirector];
+ break;
+ case CCDirectorTypeDisplayLink:
+ [CCDirectorDisplayLink sharedDirector];
+ break;
+ case CCDirectorTypeMainLoop:
+ [CCDirectorFast sharedDirector];
+ break;
+ case CCDirectorTypeThreadMainLoop:
+ [CCDirectorFastThreaded sharedDirector];
+ break;
+ default:
+ NSAssert(NO,@"Unknown director type");
+ }
+
+ return YES;
+}
+
+@end
+
+
+
+#pragma mark -
+#pragma mark CCDirectorIOS
+
+@interface CCDirectorIOS ()
+-(void) updateContentScaleFactor;
+
+@end
+
+@implementation CCDirectorIOS
+
+- (id) init
+{
+ if( (self=[super init]) ) {
+
+ // portrait mode default
+ deviceOrientation_ = CCDeviceOrientationPortrait;
+
+ __ccContentScaleFactor = 1;
+ isContentScaleSupported_ = NO;
+
+ // running thread is main thread on iOS
+ runningThread_ = [NSThread currentThread];
+ }
+
+ return self;
+}
+
+- (void) dealloc
+{
+ [super dealloc];
+}
+
+//
+// Draw the Scene
+//
+- (void) drawScene
+{
+ /* calculate "global" dt */
+ [self calculateDeltaTime];
+
+ /* tick before glClear: issue #533 */
+ if( ! isPaused_ ) {
+ [[CCScheduler sharedScheduler] tick: dt];
+ }
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ /* to avoid flickr, nextScene MUST be here: after tick and before draw.
+ XXX: Which bug is this one. It seems that it can't be reproduced with v0.9 */
+ if( nextScene_ )
+ [self setNextScene];
+
+ glPushMatrix();
+
+ [self applyOrientation];
+
+ // By default enable VertexArray, ColorArray, TextureCoordArray and Texture2D
+ CC_ENABLE_DEFAULT_GL_STATES();
+
+ /* draw the scene */
+ [runningScene_ visit];
+
+ /* draw the notification node */
+ [notificationNode_ visit];
+
+ if( displayFPS_ )
+ [self showFPS];
+
+#if CC_ENABLE_PROFILERS
+ [self showProfilers];
+#endif
+
+ CC_DISABLE_DEFAULT_GL_STATES();
+
+ glPopMatrix();
+
+ [openGLView_ swapBuffers];
+}
+
+-(void) setProjection:(ccDirectorProjection)projection
+{
+ CGSize size = winSizeInPixels_;
+
+ switch (projection) {
+ case kCCDirectorProjection2D:
+ glViewport(0, 0, size.width, size.height);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ ccglOrtho(0, size.width, 0, size.height, -1024 * CC_CONTENT_SCALE_FACTOR(), 1024 * CC_CONTENT_SCALE_FACTOR());
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ break;
+
+ case kCCDirectorProjection3D:
+ {
+ float zeye = [self getZEye];
+
+ glViewport(0, 0, size.width, size.height);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+// gluPerspective(60, (GLfloat)size.width/size.height, zeye-size.height/2, zeye+size.height/2 );
+ gluPerspective(60, (GLfloat)size.width/size.height, 0.5f, 1500);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ gluLookAt( size.width/2, size.height/2, zeye,
+ size.width/2, size.height/2, 0,
+ 0.0f, 1.0f, 0.0f);
+ break;
+ }
+
+ case kCCDirectorProjectionCustom:
+ if( projectionDelegate_ )
+ [projectionDelegate_ updateProjection];
+ break;
+
+ default:
+ CCLOG(@"cocos2d: Director: unrecognized projecgtion");
+ break;
+ }
+
+ projection_ = projection;
+}
+
+#pragma mark Director Integration with a UIKit view
+
+-(void) setOpenGLView:(EAGLView *)view
+{
+ if( view != openGLView_ ) {
+
+ [super setOpenGLView:view];
+
+ // set size
+ winSizeInPixels_ = CGSizeMake(winSizeInPoints_.width * __ccContentScaleFactor, winSizeInPoints_.height *__ccContentScaleFactor);
+
+ if( __ccContentScaleFactor != 1 )
+ [self updateContentScaleFactor];
+
+ CCTouchDispatcher *touchDispatcher = [CCTouchDispatcher sharedDispatcher];
+ [openGLView_ setTouchDelegate: touchDispatcher];
+ [touchDispatcher setDispatchEvents: YES];
+ }
+}
+
+#pragma mark Director - Retina Display
+
+-(CGFloat) contentScaleFactor
+{
+ return __ccContentScaleFactor;
+}
+
+-(void) setContentScaleFactor:(CGFloat)scaleFactor
+{
+ if( scaleFactor != __ccContentScaleFactor ) {
+
+ __ccContentScaleFactor = scaleFactor;
+ winSizeInPixels_ = CGSizeMake( winSizeInPoints_.width * scaleFactor, winSizeInPoints_.height * scaleFactor );
+
+ if( openGLView_ )
+ [self updateContentScaleFactor];
+
+ // update projection
+ [self setProjection:projection_];
+ }
+}
+
+-(void) updateContentScaleFactor
+{
+ // Based on code snippet from: http://developer.apple.com/iphone/prerelease/library/snippets/sp2010/sp28.html
+ if ([openGLView_ respondsToSelector:@selector(setContentScaleFactor:)])
+ {
+ [openGLView_ setContentScaleFactor: __ccContentScaleFactor];
+
+ isContentScaleSupported_ = YES;
+ }
+ else
+ CCLOG(@"cocos2d: 'setContentScaleFactor:' is not supported on this device");
+}
+
+-(BOOL) enableRetinaDisplay:(BOOL)enabled
+{
+ // Already enabled ?
+ if( enabled && __ccContentScaleFactor == 2 )
+ return YES;
+
+ // Already disabled
+ if( ! enabled && __ccContentScaleFactor == 1 )
+ return YES;
+
+ // setContentScaleFactor is not supported
+ if (! [openGLView_ respondsToSelector:@selector(setContentScaleFactor:)])
+ return NO;
+
+ // SD device
+ if ([[UIScreen mainScreen] scale] == 1.0)
+ return NO;
+
+ float newScale = enabled ? 2 : 1;
+ [self setContentScaleFactor:newScale];
+
+ return YES;
+}
+
+// overriden, don't call super
+-(void) reshapeProjection:(CGSize)size
+{
+ winSizeInPoints_ = [openGLView_ bounds].size;
+ winSizeInPixels_ = CGSizeMake(winSizeInPoints_.width * __ccContentScaleFactor, winSizeInPoints_.height *__ccContentScaleFactor);
+
+ [self setProjection:projection_];
+}
+
+#pragma mark Director Scene Landscape
+
+-(CGPoint)convertToGL:(CGPoint)uiPoint
+{
+ CGSize s = winSizeInPoints_;
+ float newY = s.height - uiPoint.y;
+ float newX = s.width - uiPoint.x;
+
+ CGPoint ret = CGPointZero;
+ switch ( deviceOrientation_) {
+ case CCDeviceOrientationPortrait:
+ ret = ccp( uiPoint.x, newY );
+ break;
+ case CCDeviceOrientationPortraitUpsideDown:
+ ret = ccp(newX, uiPoint.y);
+ break;
+ case CCDeviceOrientationLandscapeLeft:
+ ret.x = uiPoint.y;
+ ret.y = uiPoint.x;
+ break;
+ case CCDeviceOrientationLandscapeRight:
+ ret.x = newY;
+ ret.y = newX;
+ break;
+ }
+ return ret;
+}
+
+-(CGPoint)convertToUI:(CGPoint)glPoint
+{
+ CGSize winSize = winSizeInPoints_;
+ int oppositeX = winSize.width - glPoint.x;
+ int oppositeY = winSize.height - glPoint.y;
+ CGPoint uiPoint = CGPointZero;
+ switch ( deviceOrientation_) {
+ case CCDeviceOrientationPortrait:
+ uiPoint = ccp(glPoint.x, oppositeY);
+ break;
+ case CCDeviceOrientationPortraitUpsideDown:
+ uiPoint = ccp(oppositeX, glPoint.y);
+ break;
+ case CCDeviceOrientationLandscapeLeft:
+ uiPoint = ccp(glPoint.y, glPoint.x);
+ break;
+ case CCDeviceOrientationLandscapeRight:
+ // Can't use oppositeX/Y because x/y are flipped
+ uiPoint = ccp(winSize.width-glPoint.y, winSize.height-glPoint.x);
+ break;
+ }
+ return uiPoint;
+}
+
+// get the current size of the glview
+-(CGSize) winSize
+{
+ CGSize s = winSizeInPoints_;
+
+ if( deviceOrientation_ == CCDeviceOrientationLandscapeLeft || deviceOrientation_ == CCDeviceOrientationLandscapeRight ) {
+ // swap x,y in landscape mode
+ CGSize tmp = s;
+ s.width = tmp.height;
+ s.height = tmp.width;
+ }
+ return s;
+}
+
+-(CGSize) winSizeInPixels
+{
+ CGSize s = [self winSize];
+
+ s.width *= CC_CONTENT_SCALE_FACTOR();
+ s.height *= CC_CONTENT_SCALE_FACTOR();
+
+ return s;
+}
+
+-(ccDeviceOrientation) deviceOrientation
+{
+ return deviceOrientation_;
+}
+
+- (void) setDeviceOrientation:(ccDeviceOrientation) orientation
+{
+ if( deviceOrientation_ != orientation ) {
+ deviceOrientation_ = orientation;
+ switch( deviceOrientation_) {
+ case CCDeviceOrientationPortrait:
+ [[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationPortrait animated:NO];
+ break;
+ case CCDeviceOrientationPortraitUpsideDown:
+ [[UIApplication sharedApplication] setStatusBarOrientation: UIDeviceOrientationPortraitUpsideDown animated:NO];
+ break;
+ case CCDeviceOrientationLandscapeLeft:
+ [[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationLandscapeRight animated:NO];
+ break;
+ case CCDeviceOrientationLandscapeRight:
+ [[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationLandscapeLeft animated:NO];
+ break;
+ default:
+ NSLog(@"Director: Unknown device orientation");
+ break;
+ }
+ }
+}
+
+-(void) applyOrientation
+{
+ CGSize s = winSizeInPixels_;
+ float w = s.width / 2;
+ float h = s.height / 2;
+
+ // XXX it's using hardcoded values.
+ // What if the the screen size changes in the future?
+ switch ( deviceOrientation_ ) {
+ case CCDeviceOrientationPortrait:
+ // nothing
+ break;
+ case CCDeviceOrientationPortraitUpsideDown:
+ // upside down
+ glTranslatef(w,h,0);
+ glRotatef(180,0,0,1);
+ glTranslatef(-w,-h,0);
+ break;
+ case CCDeviceOrientationLandscapeRight:
+ glTranslatef(w,h,0);
+ glRotatef(90,0,0,1);
+ glTranslatef(-h,-w,0);
+ break;
+ case CCDeviceOrientationLandscapeLeft:
+ glTranslatef(w,h,0);
+ glRotatef(-90,0,0,1);
+ glTranslatef(-h,-w,0);
+ break;
+ }
+}
+
+-(void) end
+{
+ // don't release the event handlers
+ // They are needed in case the director is run again
+ [[CCTouchDispatcher sharedDispatcher] removeAllDelegates];
+
+ [super end];
+}
+
+@end
+
+
+#pragma mark -
+#pragma mark Director TimerDirector
+
+@implementation CCDirectorTimer
+- (void)startAnimation
+{
+ NSAssert( animationTimer == nil, @"animationTimer must be nil. Calling startAnimation twice?");
+
+ if( gettimeofday( &lastUpdate_, NULL) != 0 ) {
+ CCLOG(@"cocos2d: Director: Error in gettimeofday");
+ }
+
+ animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval_ target:self selector:@selector(mainLoop) userInfo:nil repeats:YES];
+
+ //
+ // If you want to attach the opengl view into UIScrollView
+ // uncomment this line to prevent 'freezing'.
+ // It doesn't work on with the Fast Director
+ //
+ // [[NSRunLoop currentRunLoop] addTimer:animationTimer
+ // forMode:NSRunLoopCommonModes];
+}
+
+-(void) mainLoop
+{
+ [self drawScene];
+}
+
+- (void)stopAnimation
+{
+ [animationTimer invalidate];
+ animationTimer = nil;
+}
+
+- (void)setAnimationInterval:(NSTimeInterval)interval
+{
+ animationInterval_ = interval;
+
+ if(animationTimer) {
+ [self stopAnimation];
+ [self startAnimation];
+ }
+}
+
+-(void) dealloc
+{
+ [animationTimer release];
+ [super dealloc];
+}
+@end
+
+
+#pragma mark -
+#pragma mark Director DirectorFast
+
+@implementation CCDirectorFast
+
+- (id) init
+{
+ if(( self = [super init] )) {
+
+#if CC_DIRECTOR_DISPATCH_FAST_EVENTS
+ CCLOG(@"cocos2d: Fast Events enabled");
+#else
+ CCLOG(@"cocos2d: Fast Events disabled");
+#endif
+ isRunning = NO;
+
+ // XXX:
+ // XXX: Don't create any autorelease object before calling "fast director"
+ // XXX: else it will be leaked
+ // XXX:
+ autoreleasePool = [NSAutoreleasePool new];
+ }
+
+ return self;
+}
+
+- (void) startAnimation
+{
+ // XXX:
+ // XXX: release autorelease objects created
+ // XXX: between "use fast director" and "runWithScene"
+ // XXX:
+ [autoreleasePool release];
+ autoreleasePool = nil;
+
+ if ( gettimeofday( &lastUpdate_, NULL) != 0 ) {
+ CCLOG(@"cocos2d: Director: Error in gettimeofday");
+ }
+
+
+ isRunning = YES;
+
+ SEL selector = @selector(mainLoop);
+ NSMethodSignature* sig = [[[CCDirector sharedDirector] class]
+ instanceMethodSignatureForSelector:selector];
+ NSInvocation* invocation = [NSInvocation
+ invocationWithMethodSignature:sig];
+ [invocation setTarget:[CCDirector sharedDirector]];
+ [invocation setSelector:selector];
+ [invocation performSelectorOnMainThread:@selector(invokeWithTarget:)
+ withObject:[CCDirector sharedDirector] waitUntilDone:NO];
+
+// NSInvocationOperation *loopOperation = [[[NSInvocationOperation alloc]
+// initWithTarget:self selector:@selector(mainLoop) object:nil]
+// autorelease];
+//
+// [loopOperation performSelectorOnMainThread:@selector(start) withObject:nil
+// waitUntilDone:NO];
+}
+
+-(void) mainLoop
+{
+ while (isRunning) {
+
+ NSAutoreleasePool *loopPool = [NSAutoreleasePool new];
+
+#if CC_DIRECTOR_DISPATCH_FAST_EVENTS
+ while( CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.004f, FALSE) == kCFRunLoopRunHandledSource);
+#else
+ while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, TRUE) == kCFRunLoopRunHandledSource);
+#endif
+
+ if (isPaused_) {
+ usleep(250000); // Sleep for a quarter of a second (250,000 microseconds) so that the framerate is 4 fps.
+ }
+
+ [self drawScene];
+
+#if CC_DIRECTOR_DISPATCH_FAST_EVENTS
+ while( CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.004f, FALSE) == kCFRunLoopRunHandledSource);
+#else
+ while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, TRUE) == kCFRunLoopRunHandledSource);
+#endif
+
+ [loopPool release];
+ }
+}
+- (void) stopAnimation
+{
+ isRunning = NO;
+}
+
+- (void)setAnimationInterval:(NSTimeInterval)interval
+{
+ NSLog(@"FastDirectory doesn't support setAnimationInterval, yet");
+}
+@end
+
+#pragma mark -
+#pragma mark Director DirectorThreadedFast
+
+@implementation CCDirectorFastThreaded
+
+- (id) init
+{
+ if(( self = [super init] )) {
+ isRunning = NO;
+ }
+
+ return self;
+}
+
+- (void) startAnimation
+{
+
+ if ( gettimeofday( &lastUpdate_, NULL) != 0 ) {
+ CCLOG(@"cocos2d: ThreadedFastDirector: Error on gettimeofday");
+ }
+
+ isRunning = YES;
+
+ NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(mainLoop) object:nil];
+ [thread start];
+ [thread release];
+}
+
+-(void) mainLoop
+{
+ while( ![[NSThread currentThread] isCancelled] ) {
+ if( isRunning )
+ [self performSelectorOnMainThread:@selector(drawScene) withObject:nil waitUntilDone:YES];
+
+ if (isPaused_) {
+ usleep(250000); // Sleep for a quarter of a second (250,000 microseconds) so that the framerate is 4 fps.
+ } else {
+// usleep(2000);
+ }
+ }
+}
+- (void) stopAnimation
+{
+ isRunning = NO;
+}
+
+- (void)setAnimationInterval:(NSTimeInterval)interval
+{
+ NSLog(@"FastDirector doesn't support setAnimationInterval, yet");
+}
+@end
+
+#pragma mark -
+#pragma mark DirectorDisplayLink
+
+// Allows building DisplayLinkDirector for pre-3.1 SDKS
+// without getting compiler warnings.
+@interface NSObject(CADisplayLink)
++ (id) displayLinkWithTarget:(id)arg1 selector:(SEL)arg2;
+- (void) addToRunLoop:(id)arg1 forMode:(id)arg2;
+- (void) setFrameInterval:(int)interval;
+- (void) invalidate;
+@end
+
+@implementation CCDirectorDisplayLink
+
+- (void)setAnimationInterval:(NSTimeInterval)interval
+{
+ animationInterval_ = interval;
+ if(displayLink){
+ [self stopAnimation];
+ [self startAnimation];
+ }
+}
+
+- (void) startAnimation
+{
+ if ( gettimeofday( &lastUpdate_, NULL) != 0 ) {
+ CCLOG(@"cocos2d: DisplayLinkDirector: Error on gettimeofday");
+ }
+
+ // approximate frame rate
+ // assumes device refreshes at 60 fps
+ int frameInterval = (int) floor(animationInterval_ * 60.0f);
+
+ CCLOG(@"cocos2d: Frame interval: %d", frameInterval);
+
+ displayLink = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selector(mainLoop:)];
+ [displayLink setFrameInterval:frameInterval];
+ [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
+}
+
+-(void) mainLoop:(id)sender
+{
+ [self drawScene];
+}
+
+- (void) stopAnimation
+{
+ [displayLink invalidate];
+ displayLink = nil;
+}
+
+-(void) dealloc
+{
+ [displayLink release];
+ [super dealloc];
+}
+@end
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Valentin Milea
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#import <UIKit/UIKit.h>
+
+/**
+ CCTargetedTouchDelegate.
+
+ Using this type of delegate results in two benefits:
+ 1. You don't need to deal with NSSets, the dispatcher does the job of splitting
+ them. You get exactly one UITouch per call.
+ 2. You can *claim* a UITouch by returning YES in ccTouchBegan. Updates of claimed
+ touches are sent only to the delegate(s) that claimed them. So if you get a move/
+ ended/cancelled update you're sure it's your touch. This frees you from doing a
+ lot of checks when doing multi-touch.
+
+ (The name TargetedTouchDelegate relates to updates "targeting" their specific
+ handler, without bothering the other handlers.)
+ @since v0.8
+ */
+@protocol CCTargetedTouchDelegate <NSObject>
+
+/** Return YES to claim the touch.
+ @since v0.8
+ */
+- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event;
+@optional
+// touch updates:
+- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event;
+- (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event;
+- (void)ccTouchCancelled:(UITouch *)touch withEvent:(UIEvent *)event;
+@end
+
+/**
+ CCStandardTouchDelegate.
+
+ This type of delegate is the same one used by CocoaTouch. You will receive all the events (Began,Moved,Ended,Cancelled).
+ @since v0.8
+*/
+@protocol CCStandardTouchDelegate <NSObject>
+@optional
+- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
+- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
+- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
+- (void)ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
+@end
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Valentin Milea
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#import "CCTouchDelegateProtocol.h"
+#import "EAGLView.h"
+
+
+typedef enum
+{
+ kCCTouchSelectorBeganBit = 1 << 0,
+ kCCTouchSelectorMovedBit = 1 << 1,
+ kCCTouchSelectorEndedBit = 1 << 2,
+ kCCTouchSelectorCancelledBit = 1 << 3,
+ kCCTouchSelectorAllBits = ( kCCTouchSelectorBeganBit | kCCTouchSelectorMovedBit | kCCTouchSelectorEndedBit | kCCTouchSelectorCancelledBit),
+} ccTouchSelectorFlag;
+
+
+enum {
+ kCCTouchBegan,
+ kCCTouchMoved,
+ kCCTouchEnded,
+ kCCTouchCancelled,
+
+ kCCTouchMax,
+};
+
+struct ccTouchHandlerHelperData {
+ SEL touchesSel;
+ SEL touchSel;
+ ccTouchSelectorFlag type;
+};
+
+/** CCTouchDispatcher.
+ Singleton that handles all the touch events.
+ The dispatcher dispatches events to the registered TouchHandlers.
+ There are 2 different type of touch handlers:
+ - Standard Touch Handlers
+ - Targeted Touch Handlers
+
+ The Standard Touch Handlers work like the CocoaTouch touch handler: a set of touches is passed to the delegate.
+ On the other hand, the Targeted Touch Handlers only receive 1 touch at the time, and they can "swallow" touches (avoid the propagation of the event).
+
+ Firstly, the dispatcher sends the received touches to the targeted touches.
+ These touches can be swallowed by the Targeted Touch Handlers. If there are still remaining touches, then the remaining touches will be sent
+ to the Standard Touch Handlers.
+
+ @since v0.8.0
+ */
+@interface CCTouchDispatcher : NSObject <EAGLTouchDelegate>
+{
+ NSMutableArray *targetedHandlers;
+ NSMutableArray *standardHandlers;
+
+ BOOL locked;
+ BOOL toAdd;
+ BOOL toRemove;
+ NSMutableArray *handlersToAdd;
+ NSMutableArray *handlersToRemove;
+ BOOL toQuit;
+
+ BOOL dispatchEvents;
+
+ // 4, 1 for each type of event
+ struct ccTouchHandlerHelperData handlerHelperData[kCCTouchMax];
+}
+
+/** singleton of the CCTouchDispatcher */
++ (CCTouchDispatcher*)sharedDispatcher;
+
+/** Whether or not the events are going to be dispatched. Default: YES */
+@property (nonatomic,readwrite, assign) BOOL dispatchEvents;
+
+/** Adds a standard touch delegate to the dispatcher's list.
+ See StandardTouchDelegate description.
+ IMPORTANT: The delegate will be retained.
+ */
+-(void) addStandardDelegate:(id<CCStandardTouchDelegate>) delegate priority:(int)priority;
+/** Adds a targeted touch delegate to the dispatcher's list.
+ See TargetedTouchDelegate description.
+ IMPORTANT: The delegate will be retained.
+ */
+-(void) addTargetedDelegate:(id<CCTargetedTouchDelegate>) delegate priority:(int)priority swallowsTouches:(BOOL)swallowsTouches;
+/** Removes a touch delegate.
+ The delegate will be released
+ */
+-(void) removeDelegate:(id) delegate;
+/** Removes all touch delegates, releasing all the delegates */
+-(void) removeAllDelegates;
+/** Changes the priority of a previously added delegate. The lower the number,
+ the higher the priority */
+-(void) setPriority:(int) priority forDelegate:(id) delegate;
+
+@end
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Valentin Milea
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+
+#import "CCTouchDispatcher.h"
+#import "CCTouchHandler.h"
+
+
+@implementation CCTouchDispatcher
+
+@synthesize dispatchEvents;
+
+static CCTouchDispatcher *sharedDispatcher = nil;
+
++(CCTouchDispatcher*) sharedDispatcher
+{
+ @synchronized(self) {
+ if (sharedDispatcher == nil)
+ sharedDispatcher = [[self alloc] init]; // assignment not done here
+ }
+ return sharedDispatcher;
+}
+
++(id) allocWithZone:(NSZone *)zone
+{
+ @synchronized(self) {
+ NSAssert(sharedDispatcher == nil, @"Attempted to allocate a second instance of a singleton.");
+ return [super allocWithZone:zone];
+ }
+ return nil; // on subsequent allocation attempts return nil
+}
+
+-(id) init
+{
+ if((self = [super init])) {
+
+ dispatchEvents = YES;
+ targetedHandlers = [[NSMutableArray alloc] initWithCapacity:8];
+ standardHandlers = [[NSMutableArray alloc] initWithCapacity:4];
+
+ handlersToAdd = [[NSMutableArray alloc] initWithCapacity:8];
+ handlersToRemove = [[NSMutableArray alloc] initWithCapacity:8];
+
+ toRemove = NO;
+ toAdd = NO;
+ toQuit = NO;
+ locked = NO;
+
+ handlerHelperData[kCCTouchBegan] = (struct ccTouchHandlerHelperData) {@selector(ccTouchesBegan:withEvent:),@selector(ccTouchBegan:withEvent:),kCCTouchSelectorBeganBit};
+ handlerHelperData[kCCTouchMoved] = (struct ccTouchHandlerHelperData) {@selector(ccTouchesMoved:withEvent:),@selector(ccTouchMoved:withEvent:),kCCTouchSelectorMovedBit};
+ handlerHelperData[kCCTouchEnded] = (struct ccTouchHandlerHelperData) {@selector(ccTouchesEnded:withEvent:),@selector(ccTouchEnded:withEvent:),kCCTouchSelectorEndedBit};
+ handlerHelperData[kCCTouchCancelled] = (struct ccTouchHandlerHelperData) {@selector(ccTouchesCancelled:withEvent:),@selector(ccTouchCancelled:withEvent:),kCCTouchSelectorCancelledBit};
+
+ }
+
+ return self;
+}
+
+-(void) dealloc
+{
+ [targetedHandlers release];
+ [standardHandlers release];
+ [handlersToAdd release];
+ [handlersToRemove release];
+ [super dealloc];
+}
+
+//
+// handlers management
+//
+
+#pragma mark TouchDispatcher - Add Hanlder
+
+-(void) forceAddHandler:(CCTouchHandler*)handler array:(NSMutableArray*)array
+{
+ NSUInteger i = 0;
+
+ for( CCTouchHandler *h in array ) {
+ if( h.priority < handler.priority )
+ i++;
+
+ NSAssert( h.delegate != handler.delegate, @"Delegate already added to touch dispatcher.");
+ }
+ [array insertObject:handler atIndex:i];
+}
+
+-(void) addStandardDelegate:(id<CCStandardTouchDelegate>) delegate priority:(int)priority
+{
+ CCTouchHandler *handler = [CCStandardTouchHandler handlerWithDelegate:delegate priority:priority];
+ if( ! locked ) {
+ [self forceAddHandler:handler array:standardHandlers];
+ } else {
+ [handlersToAdd addObject:handler];
+ toAdd = YES;
+ }
+}
+
+-(void) addTargetedDelegate:(id<CCTargetedTouchDelegate>) delegate priority:(int)priority swallowsTouches:(BOOL)swallowsTouches
+{
+ CCTouchHandler *handler = [CCTargetedTouchHandler handlerWithDelegate:delegate priority:priority swallowsTouches:swallowsTouches];
+ if( ! locked ) {
+ [self forceAddHandler:handler array:targetedHandlers];
+ } else {
+ [handlersToAdd addObject:handler];
+ toAdd = YES;
+ }
+}
+
+#pragma mark TouchDispatcher - removeDelegate
+
+-(void) forceRemoveDelegate:(id)delegate
+{
+ // XXX: remove it from both handlers ???
+
+ for( CCTouchHandler *handler in targetedHandlers ) {
+ if( handler.delegate == delegate ) {
+ [targetedHandlers removeObject:handler];
+ break;
+ }
+ }
+
+ for( CCTouchHandler *handler in standardHandlers ) {
+ if( handler.delegate == delegate ) {
+ [standardHandlers removeObject:handler];
+ break;
+ }
+ }
+}
+
+-(void) removeDelegate:(id) delegate
+{
+ if( delegate == nil )
+ return;
+
+ if( ! locked ) {
+ [self forceRemoveDelegate:delegate];
+ } else {
+ [handlersToRemove addObject:delegate];
+ toRemove = YES;
+ }
+}
+
+#pragma mark TouchDispatcher - removeAllDelegates
+
+-(void) forceRemoveAllDelegates
+{
+ [standardHandlers removeAllObjects];
+ [targetedHandlers removeAllObjects];
+}
+-(void) removeAllDelegates
+{
+ if( ! locked )
+ [self forceRemoveAllDelegates];
+ else
+ toQuit = YES;
+}
+
+#pragma mark Changing priority of added handlers
+
+-(void) setPriority:(int) priority forDelegate:(id) delegate
+{
+ NSAssert(NO, @"Set priority no implemented yet. Don't forget to report this bug!");
+// if( delegate == nil )
+// [NSException raise:NSInvalidArgumentException format:@"Got nil touch delegate"];
+//
+// CCTouchHandler *handler = nil;
+// for( handler in touchHandlers )
+// if( handler.delegate == delegate ) break;
+//
+// if( handler == nil )
+// [NSException raise:NSInvalidArgumentException format:@"Touch delegate not found"];
+//
+// if( handler.priority != priority ) {
+// handler.priority = priority;
+//
+// [handler retain];
+// [touchHandlers removeObject:handler];
+// [self addHandler:handler];
+// [handler release];
+// }
+}
+
+
+//
+// dispatch events
+//
+-(void) touches:(NSSet*)touches withEvent:(UIEvent*)event withTouchType:(unsigned int)idx
+{
+ NSAssert(idx < 4, @"Invalid idx value");
+
+ id mutableTouches;
+ locked = YES;
+
+ // optimization to prevent a mutable copy when it is not necessary
+ unsigned int targetedHandlersCount = [targetedHandlers count];
+ unsigned int standardHandlersCount = [standardHandlers count];
+ BOOL needsMutableSet = (targetedHandlersCount && standardHandlersCount);
+
+ mutableTouches = (needsMutableSet ? [touches mutableCopy] : touches);
+
+ struct ccTouchHandlerHelperData helper = handlerHelperData[idx];
+ //
+ // process the target handlers 1st
+ //
+ if( targetedHandlersCount > 0 ) {
+ for( UITouch *touch in touches ) {
+ for(CCTargetedTouchHandler *handler in targetedHandlers) {
+
+ BOOL claimed = NO;
+ if( idx == kCCTouchBegan ) {
+ claimed = [handler.delegate ccTouchBegan:touch withEvent:event];
+ if( claimed )
+ [handler.claimedTouches addObject:touch];
+ }
+
+ // else (moved, ended, cancelled)
+ else if( [handler.claimedTouches containsObject:touch] ) {
+ claimed = YES;
+ if( handler.enabledSelectors & helper.type )
+ [handler.delegate performSelector:helper.touchSel withObject:touch withObject:event];
+
+ if( helper.type & (kCCTouchSelectorCancelledBit | kCCTouchSelectorEndedBit) )
+ [handler.claimedTouches removeObject:touch];
+ }
+
+ if( claimed && handler.swallowsTouches ) {
+ if( needsMutableSet )
+ [mutableTouches removeObject:touch];
+ break;
+ }
+ }
+ }
+ }
+
+ //
+ // process standard handlers 2nd
+ //
+ if( standardHandlersCount > 0 && [mutableTouches count]>0 ) {
+ for( CCTouchHandler *handler in standardHandlers ) {
+ if( handler.enabledSelectors & helper.type )
+ [handler.delegate performSelector:helper.touchesSel withObject:mutableTouches withObject:event];
+ }
+ }
+ if( needsMutableSet )
+ [mutableTouches release];
+
+ //
+ // Optimization. To prevent a [handlers copy] which is expensive
+ // the add/removes/quit is done after the iterations
+ //
+ locked = NO;
+ if( toRemove ) {
+ toRemove = NO;
+ for( id delegate in handlersToRemove )
+ [self forceRemoveDelegate:delegate];
+ [handlersToRemove removeAllObjects];
+ }
+ if( toAdd ) {
+ toAdd = NO;
+ for( CCTouchHandler *handler in handlersToAdd ) {
+ Class targetedClass = [CCTargetedTouchHandler class];
+ if( [handler isKindOfClass:targetedClass] )
+ [self forceAddHandler:handler array:targetedHandlers];
+ else
+ [self forceAddHandler:handler array:standardHandlers];
+ }
+ [handlersToAdd removeAllObjects];
+ }
+ if( toQuit ) {
+ toQuit = NO;
+ [self forceRemoveAllDelegates];
+ }
+}
+
+- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
+{
+ if( dispatchEvents )
+ [self touches:touches withEvent:event withTouchType:kCCTouchBegan];
+}
+- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
+{
+ if( dispatchEvents )
+ [self touches:touches withEvent:event withTouchType:kCCTouchMoved];
+}
+
+- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
+{
+ if( dispatchEvents )
+ [self touches:touches withEvent:event withTouchType:kCCTouchEnded];
+}
+
+- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
+{
+ if( dispatchEvents )
+ [self touches:touches withEvent:event withTouchType:kCCTouchCancelled];
+}
+@end
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Valentin Milea
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+/*
+ * This file contains the delegates of the touches
+ * There are 2 possible delegates:
+ * - CCStandardTouchHandler: propagates all the events at once
+ * - CCTargetedTouchHandler: propagates 1 event at the time
+ */
+
+#import "CCTouchDelegateProtocol.h"
+#import "CCTouchDispatcher.h"
+
+/**
+ CCTouchHandler
+ Object than contains the delegate and priority of the event handler.
+*/
+@interface CCTouchHandler : NSObject {
+ id delegate;
+ int priority;
+ ccTouchSelectorFlag enabledSelectors_;
+}
+
+/** delegate */
+@property(nonatomic, readwrite, retain) id delegate;
+/** priority */
+@property(nonatomic, readwrite) int priority; // default 0
+/** enabled selectors */
+@property(nonatomic,readwrite) ccTouchSelectorFlag enabledSelectors;
+
+/** allocates a TouchHandler with a delegate and a priority */
++ (id)handlerWithDelegate:(id)aDelegate priority:(int)priority;
+/** initializes a TouchHandler with a delegate and a priority */
+- (id)initWithDelegate:(id)aDelegate priority:(int)priority;
+@end
+
+/** CCStandardTouchHandler
+ It forwardes each event to the delegate.
+ */
+@interface CCStandardTouchHandler : CCTouchHandler
+{
+}
+@end
+
+/**
+ CCTargetedTouchHandler
+ Object than contains the claimed touches and if it swallos touches.
+ Used internally by TouchDispatcher
+ */
+@interface CCTargetedTouchHandler : CCTouchHandler {
+ BOOL swallowsTouches;
+ NSMutableSet *claimedTouches;
+}
+/** whether or not the touches are swallowed */
+@property(nonatomic, readwrite) BOOL swallowsTouches; // default NO
+/** MutableSet that contains the claimed touches */
+@property(nonatomic, readonly) NSMutableSet *claimedTouches;
+
+/** allocates a TargetedTouchHandler with a delegate, a priority and whether or not it swallows touches or not */
++ (id)handlerWithDelegate:(id) aDelegate priority:(int)priority swallowsTouches:(BOOL)swallowsTouches;
+/** initializes a TargetedTouchHandler with a delegate, a priority and whether or not it swallows touches or not */
+- (id)initWithDelegate:(id) aDelegate priority:(int)priority swallowsTouches:(BOOL)swallowsTouches;
+
+@end
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Valentin Milea
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+/*
+ * This file contains the delegates of the touches
+ * There are 2 possible delegates:
+ * - CCStandardTouchHandler: propagates all the events at once
+ * - CCTargetedTouchHandler: propagates 1 event at the time
+ */
+
+#import "CCTouchHandler.h"
+#import "../../ccMacros.h"
+
+#pragma mark -
+#pragma mark TouchHandler
+@implementation CCTouchHandler
+
+@synthesize delegate, priority;
+@synthesize enabledSelectors=enabledSelectors_;
+
++ (id)handlerWithDelegate:(id) aDelegate priority:(int)aPriority
+{
+ return [[[self alloc] initWithDelegate:aDelegate priority:aPriority] autorelease];
+}
+
+- (id)initWithDelegate:(id) aDelegate priority:(int)aPriority
+{
+ NSAssert(aDelegate != nil, @"Touch delegate may not be nil");
+
+ if ((self = [super init])) {
+ self.delegate = aDelegate;
+ priority = aPriority;
+ enabledSelectors_ = 0;
+ }
+
+ return self;
+}
+
+- (void)dealloc {
+ CCLOGINFO(@"cocos2d: deallocing %@", self);
+ [delegate release];
+ [super dealloc];
+}
+@end
+
+#pragma mark -
+#pragma mark StandardTouchHandler
+@implementation CCStandardTouchHandler
+-(id) initWithDelegate:(id)del priority:(int)pri
+{
+ if( (self=[super initWithDelegate:del priority:pri]) ) {
+ if( [del respondsToSelector:@selector(ccTouchesBegan:withEvent:)] )
+ enabledSelectors_ |= kCCTouchSelectorBeganBit;
+ if( [del respondsToSelector:@selector(ccTouchesMoved:withEvent:)] )
+ enabledSelectors_ |= kCCTouchSelectorMovedBit;
+ if( [del respondsToSelector:@selector(ccTouchesEnded:withEvent:)] )
+ enabledSelectors_ |= kCCTouchSelectorEndedBit;
+ if( [del respondsToSelector:@selector(ccTouchesCancelled:withEvent:)] )
+ enabledSelectors_ |= kCCTouchSelectorCancelledBit;
+ }
+ return self;
+}
+@end
+
+#pragma mark -
+#pragma mark TargetedTouchHandler
+
+@interface CCTargetedTouchHandler (private)
+-(void) updateKnownTouches:(NSMutableSet *)touches withEvent:(UIEvent *)event selector:(SEL)selector unclaim:(BOOL)doUnclaim;
+@end
+
+@implementation CCTargetedTouchHandler
+
+@synthesize swallowsTouches, claimedTouches;
+
++ (id)handlerWithDelegate:(id)aDelegate priority:(int)priority swallowsTouches:(BOOL)swallow
+{
+ return [[[self alloc] initWithDelegate:aDelegate priority:priority swallowsTouches:swallow] autorelease];
+}
+
+- (id)initWithDelegate:(id)aDelegate priority:(int)aPriority swallowsTouches:(BOOL)swallow
+{
+ if ((self = [super initWithDelegate:aDelegate priority:aPriority])) {
+ claimedTouches = [[NSMutableSet alloc] initWithCapacity:2];
+ swallowsTouches = swallow;
+
+ if( [aDelegate respondsToSelector:@selector(ccTouchBegan:withEvent:)] )
+ enabledSelectors_ |= kCCTouchSelectorBeganBit;
+ if( [aDelegate respondsToSelector:@selector(ccTouchMoved:withEvent:)] )
+ enabledSelectors_ |= kCCTouchSelectorMovedBit;
+ if( [aDelegate respondsToSelector:@selector(ccTouchEnded:withEvent:)] )
+ enabledSelectors_ |= kCCTouchSelectorEndedBit;
+ if( [aDelegate respondsToSelector:@selector(ccTouchCancelled:withEvent:)] )
+ enabledSelectors_ |= kCCTouchSelectorCancelledBit;
+ }
+
+ return self;
+}
+
+- (void)dealloc {
+ [claimedTouches release];
+ [super dealloc];
+}
+@end
+
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
\ No newline at end of file
--- /dev/null
+/*
+
+===== IMPORTANT =====
+
+This is sample code demonstrating API, technology or techniques in development.
+Although this sample code has been reviewed for technical accuracy, it is not
+final. Apple is supplying this information to help you plan for the adoption of
+the technologies and programming interfaces described herein. This information
+is subject to change, and software implemented based on this sample code should
+be tested with final operating system software and final documentation. Newer
+versions of this sample code may be provided with future seeds of the API or
+technology. For information about updates to this and other developer
+documentation, view the New & Updated sidebars in subsequent documentation
+seeds.
+
+=====================
+
+File: EAGLView.h
+Abstract: Convenience class that wraps the CAEAGLLayer from CoreAnimation into a
+UIView subclass.
+
+Version: 1.3
+
+Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
+("Apple") in consideration of your agreement to the following terms, and your
+use, installation, modification or redistribution of this Apple software
+constitutes acceptance of these terms. If you do not agree with these terms,
+please do not use, install, modify or redistribute this Apple software.
+
+In consideration of your agreement to abide by the following terms, and subject
+to these terms, Apple grants you a personal, non-exclusive license, under
+Apple's copyrights in this original Apple software (the "Apple Software"), to
+use, reproduce, modify and redistribute the Apple Software, with or without
+modifications, in source and/or binary forms; provided that if you redistribute
+the Apple Software in its entirety and without modifications, you must retain
+this notice and the following text and disclaimers in all such redistributions
+of the Apple Software.
+Neither the name, trademarks, service marks or logos of Apple Inc. may be used
+to endorse or promote products derived from the Apple Software without specific
+prior written permission from Apple. Except as expressly stated in this notice,
+no other rights or licenses, express or implied, are granted by Apple herein,
+including but not limited to any patent rights that may be infringed by your
+derivative works or by other works in which the Apple Software may be
+incorporated.
+
+The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
+WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+COMBINATION WITH YOUR PRODUCTS.
+
+IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR
+DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF
+CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
+APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Copyright (C) 2008 Apple Inc. All Rights Reserved.
+
+*/
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#import <UIKit/UIKit.h>
+#import <OpenGLES/EAGL.h>
+#import <OpenGLES/EAGLDrawable.h>
+#import <OpenGLES/ES1/gl.h>
+#import <OpenGLES/ES1/glext.h>
+
+#import "ESRenderer.h"
+
+//CLASSES:
+
+@class EAGLView;
+@class EAGLSharegroup;
+
+//PROTOCOLS:
+
+@protocol EAGLTouchDelegate <NSObject>
+- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
+- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
+- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
+- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
+@end
+
+//CLASS INTERFACE:
+
+/** EAGLView Class.
+ * This class wraps the CAEAGLLayer from CoreAnimation into a convenient UIView subclass.
+ * The view content is basically an EAGL surface you render your OpenGL scene into.
+ * Note that setting the view non-opaque will only work if the EAGL surface has an alpha channel.
+ */
+@interface EAGLView : UIView
+{
+ id<ESRenderer> renderer_;
+ EAGLContext *context_; // weak ref
+
+ NSString *pixelformat_;
+ GLuint depthFormat_;
+ BOOL preserveBackbuffer_;
+
+ CGSize size_;
+ BOOL discardFramebufferSupported_;
+ id<EAGLTouchDelegate> touchDelegate_;
+
+ //fsaa addition
+ BOOL multisampling_;
+ unsigned int requestedSamples_;
+}
+
+/** creates an initializes an EAGLView with a frame and 0-bit depth buffer, and a RGB565 color buffer. */
++ (id) viewWithFrame:(CGRect)frame;
+/** creates an initializes an EAGLView with a frame, a color buffer format, and 0-bit depth buffer. */
++ (id) viewWithFrame:(CGRect)frame pixelFormat:(NSString*)format;
+/** creates an initializes an EAGLView with a frame, a color buffer format, and a depth buffer. */
++ (id) viewWithFrame:(CGRect)frame pixelFormat:(NSString*)format depthFormat:(GLuint)depth;
+/** creates an initializes an EAGLView with a frame, a color buffer format, a depth buffer format, a sharegroup, and multisamping */
++ (id) viewWithFrame:(CGRect)frame pixelFormat:(NSString*)format depthFormat:(GLuint)depth preserveBackbuffer:(BOOL)retained sharegroup:(EAGLSharegroup*)sharegroup multiSampling:(BOOL)multisampling numberOfSamples:(unsigned int)samples;
+
+/** Initializes an EAGLView with a frame and 0-bit depth buffer, and a RGB565 color buffer */
+- (id) initWithFrame:(CGRect)frame; //These also set the current context
+/** Initializes an EAGLView with a frame, a color buffer format, and 0-bit depth buffer */
+- (id) initWithFrame:(CGRect)frame pixelFormat:(NSString*)format;
+/** Initializes an EAGLView with a frame, a color buffer format, a depth buffer format, a sharegroup and multisampling support */
+- (id) initWithFrame:(CGRect)frame pixelFormat:(NSString*)format depthFormat:(GLuint)depth preserveBackbuffer:(BOOL)retained sharegroup:(EAGLSharegroup*)sharegroup multiSampling:(BOOL)sampling numberOfSamples:(unsigned int)nSamples;
+
+/** pixel format: it could be RGBA8 (32-bit) or RGB565 (16-bit) */
+@property(nonatomic,readonly) NSString* pixelFormat;
+/** depth format of the render buffer: 0, 16 or 24 bits*/
+@property(nonatomic,readonly) GLuint depthFormat;
+
+/** returns surface size in pixels */
+@property(nonatomic,readonly) CGSize surfaceSize;
+
+/** OpenGL context */
+@property(nonatomic,readonly) EAGLContext *context;
+
+@property(nonatomic,readwrite) BOOL multiSampling;
+
+/** touch delegate */
+@property(nonatomic,readwrite,assign) id<EAGLTouchDelegate> touchDelegate;
+
+/** EAGLView uses double-buffer. This method swaps the buffers */
+-(void) swapBuffers;
+
+- (CGPoint) convertPointFromViewToSurface:(CGPoint)point;
+- (CGRect) convertRectFromViewToSurface:(CGRect)rect;
+@end
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
\ No newline at end of file
--- /dev/null
+/*
+
+===== IMPORTANT =====
+
+This is sample code demonstrating API, technology or techniques in development.
+Although this sample code has been reviewed for technical accuracy, it is not
+final. Apple is supplying this information to help you plan for the adoption of
+the technologies and programming interfaces described herein. This information
+is subject to change, and software implemented based on this sample code should
+be tested with final operating system software and final documentation. Newer
+versions of this sample code may be provided with future seeds of the API or
+technology. For information about updates to this and other developer
+documentation, view the New & Updated sidebars in subsequent documentation
+seeds.
+
+=====================
+
+File: EAGLView.m
+Abstract: Convenience class that wraps the CAEAGLLayer from CoreAnimation into a
+UIView subclass.
+
+Version: 1.3
+
+Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
+("Apple") in consideration of your agreement to the following terms, and your
+use, installation, modification or redistribution of this Apple software
+constitutes acceptance of these terms. If you do not agree with these terms,
+please do not use, install, modify or redistribute this Apple software.
+
+In consideration of your agreement to abide by the following terms, and subject
+to these terms, Apple grants you a personal, non-exclusive license, under
+Apple's copyrights in this original Apple software (the "Apple Software"), to
+use, reproduce, modify and redistribute the Apple Software, with or without
+modifications, in source and/or binary forms; provided that if you redistribute
+the Apple Software in its entirety and without modifications, you must retain
+this notice and the following text and disclaimers in all such redistributions
+of the Apple Software.
+Neither the name, trademarks, service marks or logos of Apple Inc. may be used
+to endorse or promote products derived from the Apple Software without specific
+prior written permission from Apple. Except as expressly stated in this notice,
+no other rights or licenses, express or implied, are granted by Apple herein,
+including but not limited to any patent rights that may be infringed by your
+derivative works or by other works in which the Apple Software may be
+incorporated.
+
+The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
+WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+COMBINATION WITH YOUR PRODUCTS.
+
+IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR
+DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF
+CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
+APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Copyright (C) 2008 Apple Inc. All Rights Reserved.
+
+*/
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#import <QuartzCore/QuartzCore.h>
+
+#import "EAGLView.h"
+#import "ES1Renderer.h"
+#import "../../CCDirector.h"
+#import "../../ccMacros.h"
+#import "../../CCConfiguration.h"
+#import "../../Support/OpenGL_Internal.h"
+
+
+//CLASS IMPLEMENTATIONS:
+
+@interface EAGLView (Private)
+- (BOOL) setupSurfaceWithSharegroup:(EAGLSharegroup*)sharegroup;
+- (unsigned int) convertPixelFormat:(NSString*) pixelFormat;
+@end
+
+@implementation EAGLView
+
+@synthesize surfaceSize=size_;
+@synthesize pixelFormat=pixelformat_, depthFormat=depthFormat_;
+@synthesize touchDelegate=touchDelegate_;
+@synthesize context=context_;
+@synthesize multiSampling=multiSampling_;
+
++ (Class) layerClass
+{
+ return [CAEAGLLayer class];
+}
+
++ (id) viewWithFrame:(CGRect)frame
+{
+ return [[[self alloc] initWithFrame:frame] autorelease];
+}
+
++ (id) viewWithFrame:(CGRect)frame pixelFormat:(NSString*)format
+{
+ return [[[self alloc] initWithFrame:frame pixelFormat:format] autorelease];
+}
+
++ (id) viewWithFrame:(CGRect)frame pixelFormat:(NSString*)format depthFormat:(GLuint)depth
+{
+ return [[[self alloc] initWithFrame:frame pixelFormat:format depthFormat:depth preserveBackbuffer:NO sharegroup:nil multiSampling:NO numberOfSamples:0] autorelease];
+}
+
++ (id) viewWithFrame:(CGRect)frame pixelFormat:(NSString*)format depthFormat:(GLuint)depth preserveBackbuffer:(BOOL)retained sharegroup:(EAGLSharegroup*)sharegroup multiSampling:(BOOL)multisampling numberOfSamples:(unsigned int)samples
+{
+ return [[[self alloc] initWithFrame:frame pixelFormat:format depthFormat:depth preserveBackbuffer:retained sharegroup:sharegroup multiSampling:multisampling numberOfSamples:samples] autorelease];
+}
+
+- (id) initWithFrame:(CGRect)frame
+{
+ return [self initWithFrame:frame pixelFormat:kEAGLColorFormatRGB565 depthFormat:0 preserveBackbuffer:NO sharegroup:nil multiSampling:NO numberOfSamples:0];
+}
+
+- (id) initWithFrame:(CGRect)frame pixelFormat:(NSString*)format
+{
+ return [self initWithFrame:frame pixelFormat:format depthFormat:0 preserveBackbuffer:NO sharegroup:nil multiSampling:NO numberOfSamples:0];
+}
+
+- (id) initWithFrame:(CGRect)frame pixelFormat:(NSString*)format depthFormat:(GLuint)depth preserveBackbuffer:(BOOL)retained sharegroup:(EAGLSharegroup*)sharegroup multiSampling:(BOOL)sampling numberOfSamples:(unsigned int)nSamples
+{
+ if((self = [super initWithFrame:frame]))
+ {
+ pixelformat_ = format;
+ depthFormat_ = depth;
+ multiSampling_ = sampling;
+ requestedSamples_ = nSamples;
+ preserveBackbuffer_ = retained;
+
+ if( ! [self setupSurfaceWithSharegroup:sharegroup] ) {
+ [self release];
+ return nil;
+ }
+ }
+
+ return self;
+}
+
+-(id) initWithCoder:(NSCoder *)aDecoder
+{
+ if( (self = [super initWithCoder:aDecoder]) ) {
+
+ CAEAGLLayer* eaglLayer = (CAEAGLLayer*)[self layer];
+
+ pixelformat_ = kEAGLColorFormatRGB565;
+ depthFormat_ = 0; // GL_DEPTH_COMPONENT24_OES;
+ multiSampling_= NO;
+ requestedSamples_ = 0;
+ size_ = [eaglLayer bounds].size;
+
+ if( ! [self setupSurfaceWithSharegroup:nil] ) {
+ [self release];
+ return nil;
+ }
+ }
+
+ return self;
+}
+
+-(BOOL) setupSurfaceWithSharegroup:(EAGLSharegroup*)sharegroup
+{
+ CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
+
+ eaglLayer.opaque = YES;
+ eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithBool:preserveBackbuffer_], kEAGLDrawablePropertyRetainedBacking,
+ pixelformat_, kEAGLDrawablePropertyColorFormat, nil];
+
+
+ renderer_ = [[ES1Renderer alloc] initWithDepthFormat:depthFormat_
+ withPixelFormat:[self convertPixelFormat:pixelformat_]
+ withSharegroup:sharegroup
+ withMultiSampling:multiSampling_
+ withNumberOfSamples:requestedSamples_];
+ if (!renderer_)
+ return NO;
+
+ context_ = [renderer_ context];
+ [context_ renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:eaglLayer];
+
+ discardFramebufferSupported_ = [[CCConfiguration sharedConfiguration] supportsDiscardFramebuffer];
+
+ return YES;
+}
+
+- (void) dealloc
+{
+ CCLOGINFO(@"cocos2d: deallocing %@", self);
+
+
+ [renderer_ release];
+ [super dealloc];
+}
+
+- (void) layoutSubviews
+{
+ [renderer_ resizeFromLayer:(CAEAGLLayer*)self.layer];
+ size_ = [renderer_ backingSize];
+
+ // Issue #914 #924
+ CCDirector *director = [CCDirector sharedDirector];
+ [director reshapeProjection:size_];
+
+ // Avoid flicker. Issue #350
+ [director performSelectorOnMainThread:@selector(drawScene) withObject:nil waitUntilDone:YES];
+}
+
+- (void) swapBuffers
+{
+ // IMPORTANT:
+ // - preconditions
+ // -> context_ MUST be the OpenGL context
+ // -> renderbuffer_ must be the the RENDER BUFFER
+
+#ifdef __IPHONE_4_0
+
+ if (multiSampling_)
+ {
+ /* Resolve from msaaFramebuffer to resolveFramebuffer */
+ //glDisable(GL_SCISSOR_TEST);
+ glBindFramebufferOES(GL_READ_FRAMEBUFFER_APPLE, [renderer_ msaaFrameBuffer]);
+ glBindFramebufferOES(GL_DRAW_FRAMEBUFFER_APPLE, [renderer_ defaultFrameBuffer]);
+ glResolveMultisampleFramebufferAPPLE();
+ }
+
+ if( discardFramebufferSupported_)
+ {
+ if (multiSampling_)
+ {
+ if (depthFormat_)
+ {
+ GLenum attachments[] = {GL_COLOR_ATTACHMENT0_OES, GL_DEPTH_ATTACHMENT_OES};
+ glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE, 2, attachments);
+ }
+ else
+ {
+ GLenum attachments[] = {GL_COLOR_ATTACHMENT0_OES};
+ glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE, 1, attachments);
+ }
+
+ glBindRenderbufferOES(GL_RENDERBUFFER_OES, [renderer_ colorRenderBuffer]);
+
+ }
+
+ // not MSAA
+ else if (depthFormat_ ) {
+ GLenum attachments[] = { GL_DEPTH_ATTACHMENT_OES};
+ glDiscardFramebufferEXT(GL_FRAMEBUFFER_OES, 1, attachments);
+ }
+ }
+
+#endif // __IPHONE_4_0
+
+ if(![context_ presentRenderbuffer:GL_RENDERBUFFER_OES])
+ CCLOG(@"cocos2d: Failed to swap renderbuffer in %s\n", __FUNCTION__);
+
+#if COCOS2D_DEBUG
+ CHECK_GL_ERROR();
+#endif
+
+ // We can safely re-bind the framebuffer here, since this will be the
+ // 1st instruction of the new main loop
+ if( multiSampling_ )
+ glBindFramebufferOES(GL_FRAMEBUFFER_OES, [renderer_ msaaFrameBuffer]);
+}
+
+- (unsigned int) convertPixelFormat:(NSString*) pixelFormat
+{
+ // define the pixel format
+ GLenum pFormat;
+
+
+ if([pixelFormat isEqualToString:@"EAGLColorFormat565"])
+ pFormat = GL_RGB565_OES;
+ else
+ pFormat = GL_RGBA8_OES;
+
+ return pFormat;
+}
+
+#pragma mark EAGLView - Point conversion
+
+- (CGPoint) convertPointFromViewToSurface:(CGPoint)point
+{
+ CGRect bounds = [self bounds];
+
+ return CGPointMake((point.x - bounds.origin.x) / bounds.size.width * size_.width, (point.y - bounds.origin.y) / bounds.size.height * size_.height);
+}
+
+- (CGRect) convertRectFromViewToSurface:(CGRect)rect
+{
+ CGRect bounds = [self bounds];
+
+ return CGRectMake((rect.origin.x - bounds.origin.x) / bounds.size.width * size_.width, (rect.origin.y - bounds.origin.y) / bounds.size.height * size_.height, rect.size.width / bounds.size.width * size_.width, rect.size.height / bounds.size.height * size_.height);
+}
+
+// Pass the touches to the superview
+#pragma mark EAGLView - Touch Delegate
+
+- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
+{
+ if(touchDelegate_)
+ {
+ [touchDelegate_ touchesBegan:touches withEvent:event];
+ }
+}
+
+- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
+{
+ if(touchDelegate_)
+ {
+ [touchDelegate_ touchesMoved:touches withEvent:event];
+ }
+}
+
+- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
+{
+ if(touchDelegate_)
+ {
+ [touchDelegate_ touchesEnded:touches withEvent:event];
+ }
+}
+- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
+{
+ if(touchDelegate_)
+ {
+ [touchDelegate_ touchesCancelled:touches withEvent:event];
+ }
+}
+
+@end
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
\ No newline at end of file
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * File autogenerated with Xcode. Adapted for cocos2d needs.
+ */
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+
+#import "ESRenderer.h"
+
+#import <OpenGLES/ES1/gl.h>
+#import <OpenGLES/ES1/glext.h>
+
+@interface ES1Renderer : NSObject <ESRenderer>
+{
+ // The pixel dimensions of the CAEAGLLayer
+ GLint backingWidth_;
+ GLint backingHeight_;
+
+ unsigned int samplesToUse_;
+ BOOL multiSampling_;
+
+ unsigned int depthFormat_;
+ unsigned int pixelFormat_;
+
+ // The OpenGL ES names for the framebuffer and renderbuffer used to render to this view
+ GLuint defaultFramebuffer_;
+ GLuint colorRenderbuffer_;
+ GLuint depthBuffer_;
+
+
+ //buffers for MSAA
+ GLuint msaaFramebuffer_;
+ GLuint msaaColorbuffer_;
+
+ EAGLContext *context_;
+}
+
+/** EAGLContext */
+@property (nonatomic,readonly) EAGLContext* context;
+
+- (BOOL)resizeFromLayer:(CAEAGLLayer *)layer;
+
+@end
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * File autogenerated with Xcode. Adapted for cocos2d needs.
+ */
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#import "ES1Renderer.h"
+#import "../../Support/OpenGL_Internal.h"
+#import "../../ccMacros.h"
+
+
+@interface ES1Renderer (private)
+
+- (GLenum) convertPixelFormat:(int) pixelFormat;
+
+@end
+
+
+@implementation ES1Renderer
+
+@synthesize context=context_;
+
+- (id) initWithDepthFormat:(unsigned int)depthFormat withPixelFormat:(unsigned int)pixelFormat withSharegroup:(EAGLSharegroup*)sharegroup withMultiSampling:(BOOL) multiSampling withNumberOfSamples:(unsigned int) requestedSamples
+{
+ if ((self = [super init]))
+ {
+ if ( sharegroup == nil )
+ {
+ context_ = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
+ }
+ else
+ {
+ context_ = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1 sharegroup:sharegroup];
+ }
+
+ if (!context_ || ![EAGLContext setCurrentContext:context_])
+ {
+ [self release];
+ return nil;
+ }
+
+ // Create default framebuffer object. The backing will be allocated for the current layer in -resizeFromLayer
+ glGenFramebuffersOES(1, &defaultFramebuffer_);
+ NSAssert( defaultFramebuffer_, @"Can't create default frame buffer");
+ glGenRenderbuffersOES(1, &colorRenderbuffer_);
+ NSAssert( colorRenderbuffer_, @"Can't create default render buffer");
+
+ glBindFramebufferOES(GL_FRAMEBUFFER_OES, defaultFramebuffer_);
+ glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer_);
+ glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, colorRenderbuffer_);
+
+ depthFormat_ = depthFormat;
+
+ if( depthFormat_ ) {
+// glGenRenderbuffersOES(1, &depthBuffer_);
+// glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthBuffer_);
+// glRenderbufferStorageOES(GL_RENDERBUFFER_OES, depthFormat_, 100, 100);
+// glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthBuffer_);
+
+ // default buffer
+// glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer_);
+ }
+
+ pixelFormat_ = pixelFormat;
+ multiSampling_ = multiSampling;
+ if (multiSampling_)
+ {
+ GLint maxSamplesAllowed;
+ glGetIntegerv(GL_MAX_SAMPLES_APPLE, &maxSamplesAllowed);
+ samplesToUse_ = MIN(maxSamplesAllowed,requestedSamples);
+
+ /* Create the MSAA framebuffer (offscreen) */
+ glGenFramebuffersOES(1, &msaaFramebuffer_);
+ glBindFramebufferOES(GL_FRAMEBUFFER_OES, msaaFramebuffer_);
+
+ }
+
+ CHECK_GL_ERROR();
+ }
+
+ return self;
+}
+
+- (BOOL)resizeFromLayer:(CAEAGLLayer *)layer
+{
+ // Allocate color buffer backing based on the current layer size
+
+ glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer_);
+
+ if (![context_ renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:layer])
+ {
+ CCLOG(@"failed to call context");
+ }
+
+ glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth_);
+ glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight_);
+
+ CCLOG(@"cocos2d: surface size: %dx%d", (int)backingWidth_, (int)backingHeight_);
+
+ if (multiSampling_)
+ {
+ /* Create the offscreen MSAA color buffer.
+ After rendering, the contents of this will be blitted into ColorRenderbuffer */
+
+ //msaaFrameBuffer needs to be binded
+ glBindFramebufferOES(GL_FRAMEBUFFER_OES, msaaFramebuffer_);
+ glGenRenderbuffersOES(1, &msaaColorbuffer_);
+ glBindRenderbufferOES(GL_RENDERBUFFER_OES, msaaColorbuffer_);
+ glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER_OES, samplesToUse_,pixelFormat_ , backingWidth_, backingHeight_);
+ glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, msaaColorbuffer_);
+
+ if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES)
+ {
+ CCLOG(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
+ return NO;
+ }
+ }
+
+ if (depthFormat_)
+ {
+ if( ! depthBuffer_ )
+ glGenRenderbuffersOES(1, &depthBuffer_);
+
+ glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthBuffer_);
+ if( multiSampling_ )
+ glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER_OES, samplesToUse_, depthFormat_,backingWidth_, backingHeight_);
+ else
+ glRenderbufferStorageOES(GL_RENDERBUFFER_OES, depthFormat_, backingWidth_, backingHeight_);
+ glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthBuffer_);
+
+ // bind color buffer
+ glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer_);
+ }
+
+ glBindFramebufferOES(GL_FRAMEBUFFER_OES, defaultFramebuffer_);
+
+ if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES)
+ {
+ CCLOG(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
+ return NO;
+ }
+
+ CHECK_GL_ERROR();
+
+ return YES;
+}
+
+-(CGSize) backingSize
+{
+ return CGSizeMake( backingWidth_, backingHeight_);
+}
+
+- (NSString*) description
+{
+ return [NSString stringWithFormat:@"<%@ = %08X | size = %ix%i>", [self class], self, backingWidth_, backingHeight_];
+}
+
+
+- (void)dealloc
+{
+ CCLOGINFO(@"cocos2d: deallocing %@", self);
+
+ // Tear down GL
+ if(defaultFramebuffer_)
+ {
+ glDeleteFramebuffersOES(1, &defaultFramebuffer_);
+ defaultFramebuffer_ = 0;
+ }
+
+ if(colorRenderbuffer_)
+ {
+ glDeleteRenderbuffersOES(1, &colorRenderbuffer_);
+ colorRenderbuffer_ = 0;
+ }
+
+ if( depthBuffer_ )
+ {
+ glDeleteRenderbuffersOES(1, &depthBuffer_);
+ depthBuffer_ = 0;
+ }
+
+ if ( msaaColorbuffer_)
+ {
+ glDeleteRenderbuffersOES(1, &msaaColorbuffer_);
+ msaaColorbuffer_ = 0;
+ }
+
+ if ( msaaFramebuffer_)
+ {
+ glDeleteRenderbuffersOES(1, &msaaFramebuffer_);
+ msaaFramebuffer_ = 0;
+ }
+
+ // Tear down context
+ if ([EAGLContext currentContext] == context_)
+ [EAGLContext setCurrentContext:nil];
+
+ [context_ release];
+ context_ = nil;
+
+ [super dealloc];
+}
+
+- (unsigned int) colorRenderBuffer
+{
+ return colorRenderbuffer_;
+}
+
+- (unsigned int) defaultFrameBuffer
+{
+ return defaultFramebuffer_;
+}
+
+- (unsigned int) msaaFrameBuffer
+{
+ return msaaFramebuffer_;
+}
+
+- (unsigned int) msaaColorBuffer
+{
+ return msaaColorbuffer_;
+}
+
+@end
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *
+ * File autogenerated with Xcode. Adapted for cocos2d needs.
+ */
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#import <QuartzCore/QuartzCore.h>
+
+#import <OpenGLES/EAGL.h>
+#import <OpenGLES/EAGLDrawable.h>
+
+@protocol ESRenderer <NSObject>
+
+- (id) initWithDepthFormat:(unsigned int)depthFormat withPixelFormat:(unsigned int)pixelFormat withSharegroup:(EAGLSharegroup*)sharegroup withMultiSampling:(BOOL) multiSampling withNumberOfSamples:(unsigned int) requestedSamples;
+
+- (BOOL) resizeFromLayer:(CAEAGLLayer *)layer;
+
+- (EAGLContext*) context;
+- (CGSize) backingSize;
+
+- (unsigned int) colorRenderBuffer;
+- (unsigned int) defaultFrameBuffer;
+- (unsigned int) msaaFrameBuffer;
+- (unsigned int) msaaColorBuffer;
+@end
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
--- /dev/null
+//
+// cocos2d (incomplete) GLU implementation
+//
+// gluLookAt and gluPerspective from:
+// http://jet.ro/creations (San Angeles Observation)
+//
+//
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#import <OpenGLES/ES1/gl.h>
+#import <math.h>
+#import "../../Support/OpenGL_Internal.h"
+#include "glu.h"
+
+void gluPerspective(GLfloat fovy, GLfloat aspect, GLfloat zNear, GLfloat zFar)
+{
+ GLfloat xmin, xmax, ymin, ymax;
+
+ ymax = zNear * (GLfloat)tanf(fovy * (float)M_PI / 360);
+ ymin = -ymax;
+ xmin = ymin * aspect;
+ xmax = ymax * aspect;
+
+ glFrustumf(xmin, xmax,
+ ymin, ymax,
+ zNear, zFar);
+}
+
+void gluLookAt(GLfloat eyex, GLfloat eyey, GLfloat eyez,
+ GLfloat centerx, GLfloat centery, GLfloat centerz,
+ GLfloat upx, GLfloat upy, GLfloat upz)
+{
+ GLfloat m[16];
+ GLfloat x[3], y[3], z[3];
+ GLfloat mag;
+
+ /* Make rotation matrix */
+
+ /* Z vector */
+ z[0] = eyex - centerx;
+ z[1] = eyey - centery;
+ z[2] = eyez - centerz;
+ mag = (float)sqrtf(z[0] * z[0] + z[1] * z[1] + z[2] * z[2]);
+ if (mag) {
+ z[0] /= mag;
+ z[1] /= mag;
+ z[2] /= mag;
+ }
+
+ /* Y vector */
+ y[0] = upx;
+ y[1] = upy;
+ y[2] = upz;
+
+ /* X vector = Y cross Z */
+ x[0] = y[1] * z[2] - y[2] * z[1];
+ x[1] = -y[0] * z[2] + y[2] * z[0];
+ x[2] = y[0] * z[1] - y[1] * z[0];
+
+ /* Recompute Y = Z cross X */
+ y[0] = z[1] * x[2] - z[2] * x[1];
+ y[1] = -z[0] * x[2] + z[2] * x[0];
+ y[2] = z[0] * x[1] - z[1] * x[0];
+
+ /* cross product gives area of parallelogram, which is < 1.0 for
+ * non-perpendicular unit-length vectors; so normalize x, y here
+ */
+
+ mag = (float)sqrtf(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]);
+ if (mag) {
+ x[0] /= mag;
+ x[1] /= mag;
+ x[2] /= mag;
+ }
+
+ mag = (float)sqrtf(y[0] * y[0] + y[1] * y[1] + y[2] * y[2]);
+ if (mag) {
+ y[0] /= mag;
+ y[1] /= mag;
+ y[2] /= mag;
+ }
+
+#define M(row,col) m[col*4+row]
+ M(0, 0) = x[0];
+ M(0, 1) = x[1];
+ M(0, 2) = x[2];
+ M(0, 3) = 0.0f;
+ M(1, 0) = y[0];
+ M(1, 1) = y[1];
+ M(1, 2) = y[2];
+ M(1, 3) = 0.0f;
+ M(2, 0) = z[0];
+ M(2, 1) = z[1];
+ M(2, 2) = z[2];
+ M(2, 3) = 0.0f;
+ M(3, 0) = 0.0f;
+ M(3, 1) = 0.0f;
+ M(3, 2) = 0.0f;
+ M(3, 3) = 1.0f;
+#undef M
+
+ glMultMatrixf(m);
+
+
+ /* Translate Eye to Origin */
+ glTranslatef(-eyex, -eyey, -eyez);
+}
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
--- /dev/null
+//
+// cocos2d GLU implementation
+//
+// implementation of GLU functions
+//
+#ifndef __COCOS2D_GLU_H
+#define __COCOS2D_GLU_H
+
+// Only compile this code on iOS. These files should NOT be included on your Mac project.
+// But in case they are included, it won't be compiled.
+#import <Availability.h>
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#import <OpenGLES/ES1/gl.h>
+
+/**
+ @file
+ cocos2d OpenGL GLU implementation
+ */
+
+/** OpenGL gluLookAt implementation */
+void gluLookAt(float eyeX, float eyeY, float eyeZ, float lookAtX, float lookAtY, float lookAtZ, float upX, float upY, float upZ);
+/** OpenGL gluPerspective implementation */
+void gluPerspective(GLfloat fovy, GLfloat aspect, GLfloat zNear, GLfloat zFar);
+
+#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#endif /* __COCOS2D_GLU_H */
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 ForzeField Studios S.L. http://forzefield.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#import "ccCArray.h"
+
+
+/** A faster alternative of NSArray.
+ CCArray uses internally a c-array.
+ @since v0.99.4
+ */
+
+
+/** @def CCARRAY_FOREACH
+ A convience macro to iterate over a CCArray using. It is faster than the "fast enumeration" interface.
+ @since v0.99.4
+ */
+
+#define CCARRAY_FOREACH(__array__, __object__) \
+if (__array__ && __array__->data->num > 0) \
+for(id *arr = __array__->data->arr, *end = __array__->data->arr + __array__->data->num-1; \
+ arr <= end && ((__object__ = *arr) != nil || true); \
+ arr++)
+
+@interface CCArray : NSObject <NSFastEnumeration, NSCoding, NSCopying>
+{
+ @public ccArray *data;
+}
+
++ (id) array;
++ (id) arrayWithCapacity:(NSUInteger)capacity;
++ (id) arrayWithArray:(CCArray*)otherArray;
++ (id) arrayWithNSArray:(NSArray*)otherArray;
+
+
+- (id) initWithCapacity:(NSUInteger)capacity;
+- (id) initWithArray:(CCArray*)otherArray;
+- (id) initWithNSArray:(NSArray*)otherArray;
+
+
+// Querying an Array
+
+- (NSUInteger) count;
+- (NSUInteger) capacity;
+- (NSUInteger) indexOfObject:(id)object;
+- (id) objectAtIndex:(NSUInteger)index;
+- (BOOL) containsObject:(id)object;
+- (id) randomObject;
+- (id) lastObject;
+- (NSArray*) getNSArray;
+
+
+// Adding Objects
+
+- (void) addObject:(id)object;
+- (void) addObjectsFromArray:(CCArray*)otherArray;
+- (void) addObjectsFromNSArray:(NSArray*)otherArray;
+- (void) insertObject:(id)object atIndex:(NSUInteger)index;
+
+
+// Removing Objects
+
+- (void) removeLastObject;
+- (void) removeObject:(id)object;
+- (void) removeObjectAtIndex:(NSUInteger)index;
+- (void) removeObjectsInArray:(CCArray*)otherArray;
+- (void) removeAllObjects;
+- (void) fastRemoveObject:(id)object;
+- (void) fastRemoveObjectAtIndex:(NSUInteger)index;
+
+
+// Rearranging Content
+
+- (void) exchangeObject:(id)object1 withObject:(id)object2;
+- (void) exchangeObjectAtIndex:(NSUInteger)index1 withObjectAtIndex:(NSUInteger)index2;
+- (void) reverseObjects;
+- (void) reduceMemoryFootprint;
+
+// Sending Messages to Elements
+
+- (void) makeObjectsPerformSelector:(SEL)aSelector;
+- (void) makeObjectsPerformSelector:(SEL)aSelector withObject:(id)object;
+
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 ForzeField Studios S.L. http://forzefield.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#import "CCArray.h"
+#import "../ccMacros.h"
+
+
+@implementation CCArray
+
++ (id) array
+{
+ return [[[self alloc] init] autorelease];
+}
+
++ (id) arrayWithCapacity:(NSUInteger)capacity
+{
+ return [[[self alloc] initWithCapacity:capacity] autorelease];
+}
+
++ (id) arrayWithArray:(CCArray*)otherArray
+{
+ return [[(CCArray*)[self alloc] initWithArray:otherArray] autorelease];
+}
+
++ (id) arrayWithNSArray:(NSArray*)otherArray
+{
+ return [[(CCArray*)[self alloc] initWithNSArray:otherArray] autorelease];
+}
+
+- (id) init
+{
+ self = [self initWithCapacity:2];
+ return self;
+}
+
+- (id) initWithCapacity:(NSUInteger)capacity
+{
+ self = [super init];
+ if (self != nil) {
+ data = ccArrayNew(capacity);
+ }
+ return self;
+}
+
+- (id) initWithArray:(CCArray*)otherArray
+{
+ self = [self initWithCapacity:otherArray->data->num];
+ if (self != nil) {
+ [self addObjectsFromArray:otherArray];
+ }
+ return self;
+}
+
+- (id) initWithNSArray:(NSArray*)otherArray
+{
+ self = [self initWithCapacity:otherArray.count];
+ if (self != nil) {
+ [self addObjectsFromNSArray:otherArray];
+ }
+ return self;
+}
+
+- (id) initWithCoder:(NSCoder*)coder
+{
+ self = [self initWithNSArray:[coder decodeObjectForKey:@"nsarray"]];
+ return self;
+}
+
+
+#pragma mark Querying an Array
+
+- (NSUInteger) count
+{
+ return data->num;
+}
+
+- (NSUInteger) capacity
+{
+ return data->max;
+}
+
+- (NSUInteger) indexOfObject:(id)object
+{
+ return ccArrayGetIndexOfObject(data, object);
+}
+
+- (id) objectAtIndex:(NSUInteger)index
+{
+ NSAssert2( index < data->num, @"index out of range in objectAtIndex(%d), index %i", data->num, index );
+
+ return data->arr[index];
+}
+
+- (BOOL) containsObject:(id)object
+{
+ return ccArrayContainsObject(data, object);
+}
+
+- (id) lastObject
+{
+ if( data->num > 0 )
+ return data->arr[data->num-1];
+ return nil;
+}
+
+- (id) randomObject
+{
+ if(data->num==0) return nil;
+ return data->arr[(int)(data->num*CCRANDOM_0_1())];
+}
+
+- (NSArray*) getNSArray
+{
+ return [NSArray arrayWithObjects:data->arr count:data->num];
+}
+
+
+#pragma mark Adding Objects
+
+- (void) addObject:(id)object
+{
+ ccArrayAppendObjectWithResize(data, object);
+}
+
+- (void) addObjectsFromArray:(CCArray*)otherArray
+{
+ ccArrayAppendArrayWithResize(data, otherArray->data);
+}
+
+- (void) addObjectsFromNSArray:(NSArray*)otherArray
+{
+ ccArrayEnsureExtraCapacity(data, otherArray.count);
+ for(id object in otherArray)
+ ccArrayAppendObject(data, object);
+}
+
+- (void) insertObject:(id)object atIndex:(NSUInteger)index
+{
+ ccArrayInsertObjectAtIndex(data, object, index);
+}
+
+
+#pragma mark Removing Objects
+
+- (void) removeObject:(id)object
+{
+ ccArrayRemoveObject(data, object);
+}
+
+- (void) removeObjectAtIndex:(NSUInteger)index
+{
+ ccArrayRemoveObjectAtIndex(data, index);
+}
+
+- (void) fastRemoveObject:(id)object
+{
+ ccArrayFastRemoveObject(data, object);
+}
+
+- (void) fastRemoveObjectAtIndex:(NSUInteger)index
+{
+ ccArrayFastRemoveObjectAtIndex(data, index);
+}
+
+- (void) removeObjectsInArray:(CCArray*)otherArray
+{
+ ccArrayRemoveArray(data, otherArray->data);
+}
+
+- (void) removeLastObject
+{
+ NSAssert( data->num > 0, @"no objects added" );
+
+ ccArrayRemoveObjectAtIndex(data, data->num-1);
+}
+
+- (void) removeAllObjects
+{
+ ccArrayRemoveAllObjects(data);
+}
+
+
+#pragma mark Rearranging Content
+
+- (void) exchangeObject:(id)object1 withObject:(id)object2
+{
+ NSUInteger index1 = ccArrayGetIndexOfObject(data, object1);
+ if(index1 == NSNotFound) return;
+ NSUInteger index2 = ccArrayGetIndexOfObject(data, object2);
+ if(index2 == NSNotFound) return;
+
+ ccArraySwapObjectsAtIndexes(data, index1, index2);
+}
+
+- (void) exchangeObjectAtIndex:(NSUInteger)index1 withObjectAtIndex:(NSUInteger)index2
+{
+ ccArraySwapObjectsAtIndexes(data, index1, index2);
+}
+
+- (void) reverseObjects
+{
+ if (data->num > 1)
+ {
+ //floor it since in case of a oneven number the number of swaps stays the same
+ int count = (int) floorf(data->num/2.f);
+ NSUInteger maxIndex = data->num - 1;
+
+ for (int i = 0; i < count ; i++)
+ {
+ ccArraySwapObjectsAtIndexes(data, i, maxIndex);
+ maxIndex--;
+ }
+ }
+}
+
+- (void) reduceMemoryFootprint
+{
+ ccArrayShrink(data);
+}
+
+#pragma mark Sending Messages to Elements
+
+- (void) makeObjectsPerformSelector:(SEL)aSelector
+{
+ ccArrayMakeObjectsPerformSelector(data, aSelector);
+}
+
+- (void) makeObjectsPerformSelector:(SEL)aSelector withObject:(id)object
+{
+ ccArrayMakeObjectsPerformSelectorWithObject(data, aSelector, object);
+}
+
+
+#pragma mark CCArray - NSFastEnumeration protocol
+
+- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len
+{
+ if(state->state == 1) return 0;
+
+ state->mutationsPtr = (unsigned long *)self;
+ state->itemsPtr = &data->arr[0];
+ state->state = 1;
+ return data->num;
+}
+
+
+#pragma mark CCArray - NSCopying protocol
+
+- (id)copyWithZone:(NSZone *)zone
+{
+ NSArray *nsArray = [self getNSArray];
+ CCArray *newArray = [[[self class] allocWithZone:zone] initWithNSArray:nsArray];
+ return newArray;
+}
+
+- (void) encodeWithCoder:(NSCoder *)coder
+{
+ [coder encodeObject:[self getNSArray] forKey:@"nsarray"];
+}
+
+#pragma mark
+
+- (void) dealloc
+{
+ ccArrayFree(data);
+ [super dealloc];
+}
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import <Foundation/Foundation.h>
+
+
+/** Helper class to handle file operations */
+@interface CCFileUtils : NSObject
+{
+}
+
+/** Returns the fullpath of an filename.
+
+ If this method is when Retina Display is enabled, then the
+ Retina Display suffix will be appended to the file (See ccConfig.h).
+
+ If the Retina Display image doesn't exist, then it will return the "non-Retina Display" image
+
+ */
++(NSString*) fullPathFromRelativePath:(NSString*) relPath;
+@end
+
+/** loads a file into memory.
+ the caller should release the allocated buffer.
+
+ @returns the size of the allocated buffer
+ @since v0.99.5
+ */
+NSInteger ccLoadFileIntoMemory(const char *filename, unsigned char **out);
+
+
+/** removes the HD suffix from a path
+
+ @returns NSString * without the HD suffix
+ @since v0.99.5
+ */
+NSString *ccRemoveHDSuffixFromFile( NSString *path );
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import <Availability.h>
+#import "CCFileUtils.h"
+#import "../CCConfiguration.h"
+#import "../ccMacros.h"
+#import "../ccConfig.h"
+
+static NSFileManager *__localFileManager=nil;
+
+//
+NSInteger ccLoadFileIntoMemory(const char *filename, unsigned char **out)
+{
+ NSCAssert( out, @"ccLoadFileIntoMemory: invalid 'out' parameter");
+ NSCAssert( &*out, @"ccLoadFileIntoMemory: invalid 'out' parameter");
+
+ size_t size = 0;
+ FILE *f = fopen(filename, "rb");
+ if( !f ) {
+ *out = NULL;
+ return -1;
+ }
+
+ fseek(f, 0, SEEK_END);
+ size = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ *out = malloc(size);
+ size_t read = fread(*out, 1, size, f);
+ if( read != size ) {
+ free(*out);
+ *out = NULL;
+ return -1;
+ }
+
+ fclose(f);
+
+ return size;
+}
+
+NSString *ccRemoveHDSuffixFromFile( NSString *path )
+{
+#if CC_IS_RETINA_DISPLAY_SUPPORTED
+
+ if( CC_CONTENT_SCALE_FACTOR() == 2 ) {
+
+ NSString *name = [path lastPathComponent];
+
+ // check if path already has the suffix.
+ if( [name rangeOfString:CC_RETINA_DISPLAY_FILENAME_SUFFIX].location != NSNotFound ) {
+
+ CCLOG(@"cocos2d: Filename(%@) contains %@ suffix. Removing it. See cocos2d issue #1040", path, CC_RETINA_DISPLAY_FILENAME_SUFFIX);
+
+ NSString *newLastname = [name stringByReplacingOccurrencesOfString:CC_RETINA_DISPLAY_FILENAME_SUFFIX withString:@""];
+
+ NSString *pathWithoutLastname = [path stringByDeletingLastPathComponent];
+ return [pathWithoutLastname stringByAppendingPathComponent:newLastname];
+ }
+ }
+
+#endif // CC_IS_RETINA_DISPLAY_SUPPORTED
+
+ return path;
+
+}
+
+
+@implementation CCFileUtils
+
++(void) initialize
+{
+ if( self == [CCFileUtils class] )
+ __localFileManager = [[NSFileManager alloc] init];
+}
+
++(NSString*) getDoubleResolutionImage:(NSString*)path
+{
+#if CC_IS_RETINA_DISPLAY_SUPPORTED
+
+ if( CC_CONTENT_SCALE_FACTOR() == 2 )
+ {
+
+ NSString *pathWithoutExtension = [path stringByDeletingPathExtension];
+ NSString *name = [pathWithoutExtension lastPathComponent];
+
+ // check if path already has the suffix.
+ if( [name rangeOfString:CC_RETINA_DISPLAY_FILENAME_SUFFIX].location != NSNotFound ) {
+
+ CCLOG(@"cocos2d: WARNING Filename(%@) already has the suffix %@. Using it.", name, CC_RETINA_DISPLAY_FILENAME_SUFFIX);
+ return path;
+ }
+
+
+ NSString *extension = [path pathExtension];
+
+ if( [extension isEqualToString:@"ccz"] || [extension isEqualToString:@"gz"] )
+ {
+ // All ccz / gz files should be in the format filename.xxx.ccz
+ // so we need to pull off the .xxx part of the extension as well
+ extension = [NSString stringWithFormat:@"%@.%@", [pathWithoutExtension pathExtension], extension];
+ pathWithoutExtension = [pathWithoutExtension stringByDeletingPathExtension];
+ }
+
+
+ NSString *retinaName = [pathWithoutExtension stringByAppendingString:CC_RETINA_DISPLAY_FILENAME_SUFFIX];
+ retinaName = [retinaName stringByAppendingPathExtension:extension];
+
+ if( [__localFileManager fileExistsAtPath:retinaName] )
+ return retinaName;
+
+ CCLOG(@"cocos2d: CCFileUtils: Warning HD file not found: %@", [retinaName lastPathComponent] );
+ }
+
+#endif // CC_IS_RETINA_DISPLAY_SUPPORTED
+
+ return path;
+}
+
++(NSString*) fullPathFromRelativePath:(NSString*) relPath
+{
+ NSAssert(relPath != nil, @"CCFileUtils: Invalid path");
+
+ NSString *fullpath = nil;
+
+ // only if it is not an absolute path
+ if( ! [relPath isAbsolutePath] )
+ {
+ NSString *file = [relPath lastPathComponent];
+ NSString *imageDirectory = [relPath stringByDeletingLastPathComponent];
+
+ fullpath = [[NSBundle mainBundle] pathForResource:file
+ ofType:nil
+ inDirectory:imageDirectory];
+ }
+
+ if (fullpath == nil)
+ fullpath = relPath;
+
+ fullpath = [self getDoubleResolutionImage:fullpath];
+
+ return fullpath;
+}
+
+@end
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Stuart Carnie
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import <Foundation/Foundation.h>
+#import <sys/time.h>
+
+@class CCProfilingTimer;
+
+@interface CCProfiler : NSObject {
+ NSMutableArray* activeTimers;
+}
+
++ (CCProfiler*)sharedProfiler;
++ (CCProfilingTimer*)timerWithName:(NSString*)timerName andInstance:(id)instance;
++ (void)releaseTimer:(CCProfilingTimer*)timer;
+- (void)displayTimers;
+
+@end
+
+
+@interface CCProfilingTimer : NSObject {
+ NSString* name;
+ struct timeval startTime;
+ double averageTime;
+}
+
+@end
+
+extern void CCProfilingBeginTimingBlock(CCProfilingTimer* timer);
+extern void CCProfilingEndTimingBlock(CCProfilingTimer* timer);
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2010 Stuart Carnie
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import "../ccConfig.h"
+
+#if CC_ENABLE_PROFILERS
+
+#import "CCProfiling.h"
+
+@interface CCProfilingTimer()
+- (id)initWithName:(NSString*)timerName andInstance:(id)instance;
+@end
+
+@implementation CCProfiler
+
+static CCProfiler* g_sharedProfiler;
+
++ (CCProfiler*)sharedProfiler {
+ if (!g_sharedProfiler)
+ g_sharedProfiler = [[CCProfiler alloc] init];
+
+ return g_sharedProfiler;
+}
+
++ (CCProfilingTimer*)timerWithName:(NSString*)timerName andInstance:(id)instance {
+ CCProfiler* p = [CCProfiler sharedProfiler];
+ CCProfilingTimer* t = [[CCProfilingTimer alloc] initWithName:timerName andInstance:instance];
+ [p->activeTimers addObject:t];
+ [t release];
+ return t;
+}
+
++ (void)releaseTimer:(CCProfilingTimer*)timer {
+ CCProfiler* p = [CCProfiler sharedProfiler];
+ [p->activeTimers removeObject:timer];
+}
+
+- (id)init {
+ if (!(self = [super init])) return nil;
+
+ activeTimers = [[NSMutableArray alloc] init];
+
+ return self;
+}
+
+- (void)dealloc {
+ [activeTimers release];
+ [super dealloc];
+}
+
+- (void)displayTimers {
+ for (id timer in activeTimers) {
+ printf("%s\n", [[timer description] cStringUsingEncoding:[NSString defaultCStringEncoding]]);
+ }
+}
+
+@end
+
+@implementation CCProfilingTimer
+
+- (id)initWithName:(NSString*)timerName andInstance:(id)instance {
+ if (!(self = [super init])) return nil;
+
+ name = [[NSString stringWithFormat:@"%@ (0x%.8x)", timerName, instance] retain];
+
+ return self;
+}
+
+- (void)dealloc {
+ [name release];
+ [super dealloc];
+}
+
+- (NSString*)description {
+ return [NSString stringWithFormat:@"%@ : avg time, %fms", name, averageTime];
+}
+
+void CCProfilingBeginTimingBlock(CCProfilingTimer* timer) {
+ gettimeofday(&timer->startTime, NULL);
+}
+
+typedef unsigned int uint32;
+void CCProfilingEndTimingBlock(CCProfilingTimer* timer) {
+ struct timeval currentTime;
+ gettimeofday(¤tTime, NULL);
+ timersub(¤tTime, &timer->startTime, ¤tTime);
+ double duration = currentTime.tv_sec * 1000.0 + currentTime.tv_usec / 1000.0;
+
+ // return in milliseconds
+ timer->averageTime = (timer->averageTime + duration) / 2.0f;
+}
+
+@end
+
+#endif
--- /dev/null
+/* cocos2d for iPhone
+ * http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2007 Scott Lembcke
+ *
+ * Copyright (c) 2010 Lam Pham
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * Some of the functions were based on Chipmunk's cpVect.h.
+ */
+
+/**
+ @file
+ CGPoint extensions based on Chipmunk's cpVect file.
+ These extensions work both with CGPoint and cpVect.
+
+ The "ccp" prefix means: "CoCos2d Point"
+
+ Examples:
+ - ccpAdd( ccp(1,1), ccp(2,2) ); // preferred cocos2d way
+ - ccpAdd( CGPointMake(1,1), CGPointMake(2,2) ); // also ok but more verbose
+
+ - cpvadd( cpv(1,1), cpv(2,2) ); // way of the chipmunk
+ - ccpAdd( cpv(1,1), cpv(2,2) ); // mixing chipmunk and cocos2d (avoid)
+ - cpvadd( CGPointMake(1,1), CGPointMake(2,2) ); // mixing chipmunk and CG (avoid)
+ */
+
+#import <Availability.h>
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import <CoreGraphics/CGGeometry.h>
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+#import <Foundation/Foundation.h>
+#endif
+
+#import <math.h>
+#import <objc/objc.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Helper macro that creates a CGPoint
+ @return CGPoint
+ @since v0.7.2
+ */
+#define ccp(__X__,__Y__) CGPointMake(__X__,__Y__)
+
+
+/** Returns opposite of point.
+ @return CGPoint
+ @since v0.7.2
+ */
+static inline CGPoint
+ccpNeg(const CGPoint v)
+{
+ return ccp(-v.x, -v.y);
+}
+
+/** Calculates sum of two points.
+ @return CGPoint
+ @since v0.7.2
+ */
+static inline CGPoint
+ccpAdd(const CGPoint v1, const CGPoint v2)
+{
+ return ccp(v1.x + v2.x, v1.y + v2.y);
+}
+
+/** Calculates difference of two points.
+ @return CGPoint
+ @since v0.7.2
+ */
+static inline CGPoint
+ccpSub(const CGPoint v1, const CGPoint v2)
+{
+ return ccp(v1.x - v2.x, v1.y - v2.y);
+}
+
+/** Returns point multiplied by given factor.
+ @return CGPoint
+ @since v0.7.2
+ */
+static inline CGPoint
+ccpMult(const CGPoint v, const CGFloat s)
+{
+ return ccp(v.x*s, v.y*s);
+}
+
+/** Calculates midpoint between two points.
+ @return CGPoint
+ @since v0.7.2
+ */
+static inline CGPoint
+ccpMidpoint(const CGPoint v1, const CGPoint v2)
+{
+ return ccpMult(ccpAdd(v1, v2), 0.5f);
+}
+
+/** Calculates dot product of two points.
+ @return CGFloat
+ @since v0.7.2
+ */
+static inline CGFloat
+ccpDot(const CGPoint v1, const CGPoint v2)
+{
+ return v1.x*v2.x + v1.y*v2.y;
+}
+
+/** Calculates cross product of two points.
+ @return CGFloat
+ @since v0.7.2
+ */
+static inline CGFloat
+ccpCross(const CGPoint v1, const CGPoint v2)
+{
+ return v1.x*v2.y - v1.y*v2.x;
+}
+
+/** Calculates perpendicular of v, rotated 90 degrees counter-clockwise -- cross(v, perp(v)) >= 0
+ @return CGPoint
+ @since v0.7.2
+ */
+static inline CGPoint
+ccpPerp(const CGPoint v)
+{
+ return ccp(-v.y, v.x);
+}
+
+/** Calculates perpendicular of v, rotated 90 degrees clockwise -- cross(v, rperp(v)) <= 0
+ @return CGPoint
+ @since v0.7.2
+ */
+static inline CGPoint
+ccpRPerp(const CGPoint v)
+{
+ return ccp(v.y, -v.x);
+}
+
+/** Calculates the projection of v1 over v2.
+ @return CGPoint
+ @since v0.7.2
+ */
+static inline CGPoint
+ccpProject(const CGPoint v1, const CGPoint v2)
+{
+ return ccpMult(v2, ccpDot(v1, v2)/ccpDot(v2, v2));
+}
+
+/** Rotates two points.
+ @return CGPoint
+ @since v0.7.2
+ */
+static inline CGPoint
+ccpRotate(const CGPoint v1, const CGPoint v2)
+{
+ return ccp(v1.x*v2.x - v1.y*v2.y, v1.x*v2.y + v1.y*v2.x);
+}
+
+/** Unrotates two points.
+ @return CGPoint
+ @since v0.7.2
+ */
+static inline CGPoint
+ccpUnrotate(const CGPoint v1, const CGPoint v2)
+{
+ return ccp(v1.x*v2.x + v1.y*v2.y, v1.y*v2.x - v1.x*v2.y);
+}
+
+/** Calculates the square length of a CGPoint (not calling sqrt() )
+ @return CGFloat
+ @since v0.7.2
+ */
+static inline CGFloat
+ccpLengthSQ(const CGPoint v)
+{
+ return ccpDot(v, v);
+}
+
+/** Calculates distance between point an origin
+ @return CGFloat
+ @since v0.7.2
+ */
+CGFloat ccpLength(const CGPoint v);
+
+/** Calculates the distance between two points
+ @return CGFloat
+ @since v0.7.2
+ */
+CGFloat ccpDistance(const CGPoint v1, const CGPoint v2);
+
+/** Returns point multiplied to a length of 1.
+ @return CGPoint
+ @since v0.7.2
+ */
+CGPoint ccpNormalize(const CGPoint v);
+
+/** Converts radians to a normalized vector.
+ @return CGPoint
+ @since v0.7.2
+ */
+CGPoint ccpForAngle(const CGFloat a);
+
+/** Converts a vector to radians.
+ @return CGFloat
+ @since v0.7.2
+ */
+CGFloat ccpToAngle(const CGPoint v);
+
+
+/** Clamp a value between from and to.
+ @since v0.99.1
+ */
+float clampf(float value, float min_inclusive, float max_inclusive);
+
+/** Clamp a point between from and to.
+ @since v0.99.1
+ */
+CGPoint ccpClamp(CGPoint p, CGPoint from, CGPoint to);
+
+/** Quickly convert CGSize to a CGPoint
+ @since v0.99.1
+ */
+CGPoint ccpFromSize(CGSize s);
+
+/** Run a math operation function on each point component
+ * absf, fllorf, ceilf, roundf
+ * any function that has the signature: float func(float);
+ * For example: let's try to take the floor of x,y
+ * ccpCompOp(p,floorf);
+ @since v0.99.1
+ */
+CGPoint ccpCompOp(CGPoint p, float (*opFunc)(float));
+
+/** Linear Interpolation between two points a and b
+ @returns
+ alpha == 0 ? a
+ alpha == 1 ? b
+ otherwise a value between a..b
+ @since v0.99.1
+ */
+CGPoint ccpLerp(CGPoint a, CGPoint b, float alpha);
+
+
+/** @returns if points have fuzzy equality which means equal with some degree of variance.
+ @since v0.99.1
+ */
+BOOL ccpFuzzyEqual(CGPoint a, CGPoint b, float variance);
+
+
+/** Multiplies a nd b components, a.x*b.x, a.y*b.y
+ @returns a component-wise multiplication
+ @since v0.99.1
+ */
+CGPoint ccpCompMult(CGPoint a, CGPoint b);
+
+/** @returns the signed angle in radians between two vector directions
+ @since v0.99.1
+ */
+float ccpAngleSigned(CGPoint a, CGPoint b);
+
+/** @returns the angle in radians between two vector directions
+ @since v0.99.1
+*/
+float ccpAngle(CGPoint a, CGPoint b);
+
+/** Rotates a point counter clockwise by the angle around a pivot
+ @param v is the point to rotate
+ @param pivot is the pivot, naturally
+ @param angle is the angle of rotation cw in radians
+ @returns the rotated point
+ @since v0.99.1
+ */
+CGPoint ccpRotateByAngle(CGPoint v, CGPoint pivot, float angle);
+
+/** A general line-line intersection test
+ @param p1
+ is the startpoint for the first line P1 = (p1 - p2)
+ @param p2
+ is the endpoint for the first line P1 = (p1 - p2)
+ @param p3
+ is the startpoint for the second line P2 = (p3 - p4)
+ @param p4
+ is the endpoint for the second line P2 = (p3 - p4)
+ @param s
+ is the range for a hitpoint in P1 (pa = p1 + s*(p2 - p1))
+ @param t
+ is the range for a hitpoint in P3 (pa = p2 + t*(p4 - p3))
+ @return bool
+ indicating successful intersection of a line
+ note that to truly test intersection for segments we have to make
+ sure that s & t lie within [0..1] and for rays, make sure s & t > 0
+ the hit point is p3 + t * (p4 - p3);
+ the hit point also is p1 + s * (p2 - p1);
+ @since v0.99.1
+ */
+BOOL ccpLineIntersect(CGPoint p1, CGPoint p2,
+ CGPoint p3, CGPoint p4,
+ float *s, float *t);
+
+#ifdef __cplusplus
+}
+#endif
--- /dev/null
+/* cocos2d for iPhone
+ * http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2007 Scott Lembcke
+ *
+ * Copyright (c) 2010 Lam Pham
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "stdio.h"
+#include "math.h"
+
+#import "../ccMacros.h" // CC_SWAP
+#include "CGPointExtension.h"
+
+#define kCGPointEpsilon FLT_EPSILON
+
+CGFloat
+ccpLength(const CGPoint v)
+{
+ return sqrtf(ccpLengthSQ(v));
+}
+
+CGFloat
+ccpDistance(const CGPoint v1, const CGPoint v2)
+{
+ return ccpLength(ccpSub(v1, v2));
+}
+
+CGPoint
+ccpNormalize(const CGPoint v)
+{
+ return ccpMult(v, 1.0f/ccpLength(v));
+}
+
+CGPoint
+ccpForAngle(const CGFloat a)
+{
+ return ccp(cosf(a), sinf(a));
+}
+
+CGFloat
+ccpToAngle(const CGPoint v)
+{
+ return atan2f(v.y, v.x);
+}
+
+CGPoint ccpLerp(CGPoint a, CGPoint b, float alpha)
+{
+ return ccpAdd(ccpMult(a, 1.f - alpha), ccpMult(b, alpha));
+}
+
+float clampf(float value, float min_inclusive, float max_inclusive)
+{
+ if (min_inclusive > max_inclusive) {
+ CC_SWAP(min_inclusive,max_inclusive);
+ }
+ return value < min_inclusive ? min_inclusive : value < max_inclusive? value : max_inclusive;
+}
+
+CGPoint ccpClamp(CGPoint p, CGPoint min_inclusive, CGPoint max_inclusive)
+{
+ return ccp(clampf(p.x,min_inclusive.x,max_inclusive.x), clampf(p.y, min_inclusive.y, max_inclusive.y));
+}
+
+CGPoint ccpFromSize(CGSize s)
+{
+ return ccp(s.width, s.height);
+}
+
+CGPoint ccpCompOp(CGPoint p, float (*opFunc)(float))
+{
+ return ccp(opFunc(p.x), opFunc(p.y));
+}
+
+BOOL ccpFuzzyEqual(CGPoint a, CGPoint b, float var)
+{
+ if(a.x - var <= b.x && b.x <= a.x + var)
+ if(a.y - var <= b.y && b.y <= a.y + var)
+ return true;
+ return false;
+}
+
+CGPoint ccpCompMult(CGPoint a, CGPoint b)
+{
+ return ccp(a.x * b.x, a.y * b.y);
+}
+
+float ccpAngleSigned(CGPoint a, CGPoint b)
+{
+ CGPoint a2 = ccpNormalize(a);
+ CGPoint b2 = ccpNormalize(b);
+ float angle = atan2f(a2.x * b2.y - a2.y * b2.x, ccpDot(a2, b2));
+ if( fabs(angle) < kCGPointEpsilon ) return 0.f;
+ return angle;
+}
+
+CGPoint ccpRotateByAngle(CGPoint v, CGPoint pivot, float angle)
+{
+ CGPoint r = ccpSub(v, pivot);
+ float cosa = cosf(angle), sina = sinf(angle);
+ float t = r.x;
+ r.x = t*cosa - r.y*sina + pivot.x;
+ r.y = t*sina + r.y*cosa + pivot.y;
+ return r;
+}
+
+BOOL ccpLineIntersect(CGPoint A, CGPoint B,
+ CGPoint C, CGPoint D,
+ float *S, float *T)
+{
+ // FAIL: Line undefined
+ if ( (A.x==B.x && A.y==B.y) || (C.x==D.x && C.y==D.y) ) return NO;
+
+ // Translate system to make A the origin
+ B.x-=A.x; B.y-=A.y;
+ C.x-=A.x; C.y-=A.y;
+ D.x-=A.x; D.y-=A.y;
+
+ // Cache
+ CGPoint C2 = C, D2 = D;
+
+ // Length of segment AB
+ float distAB = sqrtf(B.x*B.x+B.y*B.y);
+
+ // Rotate the system so that point B is on the positive X axis.
+ float theCos = B.x/distAB;
+ float theSin = B.y/distAB;
+ float newX = C.x*theCos+C.y*theSin;
+ C.y = C.y*theCos-C.x*theSin; C.x = newX;
+ newX = D.x*theCos+D.y*theSin;
+ D.y = D.y*theCos-D.x*theSin; D.x = newX;
+
+ // FAIL: Lines are parallel.
+ if (C.y == D.y) return NO;
+
+ // Discover position of the intersection in the line AB
+ float ABpos = D.x+(C.x-D.x)*D.y/(D.y-C.y);
+
+ // Vector CD
+ C.x = D2.x-C2.x;
+ C.y = D2.y-C2.y;
+
+ // Vector between intersection and point C
+ A.x = ABpos*theCos-C2.x;
+ A.y = ABpos*theSin-C2.y;
+
+ newX = sqrtf((A.x*A.x+A.y*A.y)/(C.x*C.x+C.y*C.y));
+ if(((A.y<0) != (C.y<0)) || ((A.x<0) != (C.x<0)))
+ newX *= -1.0f;
+
+ *S = ABpos/distAB;
+ *T = newX;
+
+ // Success.
+ return YES;
+}
+
+float ccpAngle(CGPoint a, CGPoint b)
+{
+ float angle = acosf(ccpDot(ccpNormalize(a), ccpNormalize(b)));
+ if( fabs(angle) < kCGPointEpsilon ) return 0.f;
+ return angle;
+}
--- /dev/null
+/*
+
+===== IMPORTANT =====
+
+This is sample code demonstrating API, technology or techniques in development.
+Although this sample code has been reviewed for technical accuracy, it is not
+final. Apple is supplying this information to help you plan for the adoption of
+the technologies and programming interfaces described herein. This information
+is subject to change, and software implemented based on this sample code should
+be tested with final operating system software and final documentation. Newer
+versions of this sample code may be provided with future seeds of the API or
+technology. For information about updates to this and other developer
+documentation, view the New & Updated sidebars in subsequent documentation
+seeds.
+
+=====================
+
+File: OpenGL_Internal.h
+Abstract: This file is included for support purposes and isn't necessary for
+understanding this sample.
+
+Version: 1.0
+
+Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
+("Apple") in consideration of your agreement to the following terms, and your
+use, installation, modification or redistribution of this Apple software
+constitutes acceptance of these terms. If you do not agree with these terms,
+please do not use, install, modify or redistribute this Apple software.
+
+In consideration of your agreement to abide by the following terms, and subject
+to these terms, Apple grants you a personal, non-exclusive license, under
+Apple's copyrights in this original Apple software (the "Apple Software"), to
+use, reproduce, modify and redistribute the Apple Software, with or without
+modifications, in source and/or binary forms; provided that if you redistribute
+the Apple Software in its entirety and without modifications, you must retain
+this notice and the following text and disclaimers in all such redistributions
+of the Apple Software.
+Neither the name, trademarks, service marks or logos of Apple Inc. may be used
+to endorse or promote products derived from the Apple Software without specific
+prior written permission from Apple. Except as expressly stated in this notice,
+no other rights or licenses, express or implied, are granted by Apple herein,
+including but not limited to any patent rights that may be infringed by your
+derivative works or by other works in which the Apple Software may be
+incorporated.
+
+The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
+WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+COMBINATION WITH YOUR PRODUCTS.
+
+IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR
+DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF
+CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
+APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Copyright (C) 2008 Apple Inc. All Rights Reserved.
+
+*/
+
+/* Generic error reporting */
+#define REPORT_ERROR(__FORMAT__, ...) printf("%s: %s\n", __FUNCTION__, [[NSString stringWithFormat:__FORMAT__, __VA_ARGS__] UTF8String])
+
+/* EAGL and GL functions calling wrappers that log on error */
+#define CALL_EAGL_FUNCTION(__FUNC__, ...) ({ EAGLError __error = __FUNC__( __VA_ARGS__ ); if(__error != kEAGLErrorSuccess) printf("%s() called from %s returned error %i\n", #__FUNC__, __FUNCTION__, __error); (__error ? NO : YES); })
+//#define CHECK_GL_ERROR() ({ GLenum __error = glGetError(); if(__error) printf("OpenGL error 0x%04X in %s\n", __error, __FUNCTION__); (__error ? NO : YES); })
+#define CHECK_GL_ERROR() ({ GLenum __error = glGetError(); if(__error) printf("OpenGL error 0x%04X in %s\n", __error, __FUNCTION__); })
+
+/* Optional delegate methods support */
+#ifndef __DELEGATE_IVAR__
+#define __DELEGATE_IVAR__ _delegate
+#endif
+#ifndef __DELEGATE_METHODS_IVAR__
+#define __DELEGATE_METHODS_IVAR__ _delegateMethods
+#endif
+#define TEST_DELEGATE_METHOD_BIT(__BIT__) (self->__DELEGATE_METHODS_IVAR__ & (1 << __BIT__))
+#define SET_DELEGATE_METHOD_BIT(__BIT__, __NAME__) { if([self->__DELEGATE_IVAR__ respondsToSelector:@selector(__NAME__)]) self->__DELEGATE_METHODS_IVAR__ |= (1 << __BIT__); else self->__DELEGATE_METHODS_IVAR__ &= ~(1 << __BIT__); }
--- /dev/null
+//
+// TGA lib for cocos2d-iphone
+//
+// sources from: http://www.lighthouse3d.com/opengl/terrain/index.php3?tgasource
+//
+
+//#ifndef TGA_LIB
+//#define TGA_LIB
+
+/**
+ @file
+ TGA image support
+ */
+
+enum {
+ TGA_OK,
+ TGA_ERROR_FILE_OPEN,
+ TGA_ERROR_READING_FILE,
+ TGA_ERROR_INDEXED_COLOR,
+ TGA_ERROR_MEMORY,
+ TGA_ERROR_COMPRESSED_FILE,
+};
+
+/** TGA format */
+typedef struct sImageTGA {
+ int status;
+ unsigned char type, pixelDepth;
+
+ /** map width */
+ short int width;
+
+ /** map height */
+ short int height;
+
+ /** raw data */
+ unsigned char *imageData;
+ int flipped;
+} tImageTGA;
+
+/// load the image header fields. We only keep those that matter!
+void tgaLoadHeader(FILE *file, tImageTGA *info);
+
+/// loads the image pixels. You shouldn't call this function directly
+void tgaLoadImageData(FILE *file, tImageTGA *info);
+
+/// this is the function to call when we want to load an image
+tImageTGA * tgaLoad(const char *filename);
+
+// /converts RGB to greyscale
+void tgaRGBtogreyscale(tImageTGA *info);
+
+/// releases the memory used for the image
+void tgaDestroy(tImageTGA *info);
+
+//#endif // TGA_LIB
--- /dev/null
+//
+// TGA lib for cocos2d-iphone
+//
+// sources from: http://www.lighthouse3d.com/opengl/terrain/index.php3?tgasource
+//
+// TGA RLE compression support by Ernesto Corvi
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#import "TGAlib.h"
+
+
+// load the image header fields. We only keep those that matter!
+void tgaLoadHeader(FILE *file, tImageTGA *info) {
+ unsigned char cGarbage;
+ short int iGarbage;
+
+ fread(&cGarbage, sizeof(unsigned char), 1, file);
+ fread(&cGarbage, sizeof(unsigned char), 1, file);
+
+ // type must be 2 or 3
+ fread(&info->type, sizeof(unsigned char), 1, file);
+
+ fread(&iGarbage, sizeof(short int), 1, file);
+ fread(&iGarbage, sizeof(short int), 1, file);
+ fread(&cGarbage, sizeof(unsigned char), 1, file);
+ fread(&iGarbage, sizeof(short int), 1, file);
+ fread(&iGarbage, sizeof(short int), 1, file);
+
+ fread(&info->width, sizeof(short int), 1, file);
+ fread(&info->height, sizeof(short int), 1, file);
+ fread(&info->pixelDepth, sizeof(unsigned char), 1, file);
+
+ fread(&cGarbage, sizeof(unsigned char), 1, file);
+
+ info->flipped = 0;
+ if ( cGarbage & 0x20 ) info->flipped = 1;
+}
+
+// loads the image pixels. You shouldn't call this function directly
+void tgaLoadImageData(FILE *file, tImageTGA *info) {
+
+ int mode,total,i;
+ unsigned char aux;
+
+ // mode equal the number of components for each pixel
+ mode = info->pixelDepth / 8;
+ // total is the number of unsigned chars we'll have to read
+ total = info->height * info->width * mode;
+
+ fread(info->imageData,sizeof(unsigned char),total,file);
+
+ // mode=3 or 4 implies that the image is RGB(A). However TGA
+ // stores it as BGR(A) so we'll have to swap R and B.
+ if (mode >= 3)
+ for (i=0; i < total; i+= mode) {
+ aux = info->imageData[i];
+ info->imageData[i] = info->imageData[i+2];
+ info->imageData[i+2] = aux;
+ }
+}
+
+// loads the RLE encoded image pixels. You shouldn't call this function directly
+void tgaLoadRLEImageData(FILE *file, tImageTGA *info)
+{
+ unsigned int mode,total,i, index = 0;
+ unsigned char aux[4], runlength = 0;
+ unsigned int skip = 0, flag = 0;
+
+ // mode equal the number of components for each pixel
+ mode = info->pixelDepth / 8;
+ // total is the number of unsigned chars we'll have to read
+ total = info->height * info->width;
+
+ for( i = 0; i < total; i++ )
+ {
+ // if we have a run length pending, run it
+ if ( runlength != 0 )
+ {
+ // we do, update the run length count
+ runlength--;
+ skip = (flag != 0);
+ }
+ else
+ {
+ // otherwise, read in the run length token
+ if ( fread(&runlength,sizeof(unsigned char),1,file) != 1 )
+ return;
+
+ // see if it's a RLE encoded sequence
+ flag = runlength & 0x80;
+ if ( flag ) runlength -= 128;
+ skip = 0;
+ }
+
+ // do we need to skip reading this pixel?
+ if ( !skip )
+ {
+ // no, read in the pixel data
+ if ( fread(aux,sizeof(unsigned char),mode,file) != mode )
+ return;
+
+ // mode=3 or 4 implies that the image is RGB(A). However TGA
+ // stores it as BGR(A) so we'll have to swap R and B.
+ if ( mode >= 3 )
+ {
+ unsigned char tmp;
+
+ tmp = aux[0];
+ aux[0] = aux[2];
+ aux[2] = tmp;
+ }
+ }
+
+ // add the pixel to our image
+ memcpy(&info->imageData[index], aux, mode);
+ index += mode;
+ }
+}
+
+void tgaFlipImage( tImageTGA *info )
+{
+ // mode equal the number of components for each pixel
+ int mode = info->pixelDepth / 8;
+ int rowbytes = info->width*mode;
+ unsigned char *row = (unsigned char *)malloc(rowbytes);
+ int y;
+
+ if (row == NULL) return;
+
+ for( y = 0; y < (info->height/2); y++ )
+ {
+ memcpy(row, &info->imageData[y*rowbytes],rowbytes);
+ memcpy(&info->imageData[y*rowbytes], &info->imageData[(info->height-(y+1))*rowbytes], rowbytes);
+ memcpy(&info->imageData[(info->height-(y+1))*rowbytes], row, rowbytes);
+ }
+
+ free(row);
+ info->flipped = 0;
+}
+
+// this is the function to call when we want to load an image
+tImageTGA * tgaLoad(const char *filename) {
+
+ FILE *file;
+ tImageTGA *info;
+ int mode,total;
+
+ // allocate memory for the info struct and check!
+ info = (tImageTGA *)malloc(sizeof(tImageTGA));
+ if (info == NULL)
+ return(NULL);
+
+
+ // open the file for reading (binary mode)
+ file = fopen(filename, "rb");
+ if (file == NULL) {
+ info->status = TGA_ERROR_FILE_OPEN;
+ return(info);
+ }
+
+ // load the header
+ tgaLoadHeader(file,info);
+
+ // check for errors when loading the header
+ if (ferror(file)) {
+ info->status = TGA_ERROR_READING_FILE;
+ fclose(file);
+ return(info);
+ }
+
+ // check if the image is color indexed
+ if (info->type == 1) {
+ info->status = TGA_ERROR_INDEXED_COLOR;
+ fclose(file);
+ return(info);
+ }
+ // check for other types (compressed images)
+ if ((info->type != 2) && (info->type !=3) && (info->type !=10) ) {
+ info->status = TGA_ERROR_COMPRESSED_FILE;
+ fclose(file);
+ return(info);
+ }
+
+ // mode equals the number of image components
+ mode = info->pixelDepth / 8;
+ // total is the number of unsigned chars to read
+ total = info->height * info->width * mode;
+ // allocate memory for image pixels
+ info->imageData = (unsigned char *)malloc(sizeof(unsigned char) *
+ total);
+
+ // check to make sure we have the memory required
+ if (info->imageData == NULL) {
+ info->status = TGA_ERROR_MEMORY;
+ fclose(file);
+ return(info);
+ }
+ // finally load the image pixels
+ if ( info->type == 10 )
+ tgaLoadRLEImageData(file, info);
+ else
+ tgaLoadImageData(file,info);
+
+ // check for errors when reading the pixels
+ if (ferror(file)) {
+ info->status = TGA_ERROR_READING_FILE;
+ fclose(file);
+ return(info);
+ }
+ fclose(file);
+ info->status = TGA_OK;
+
+ if ( info->flipped )
+ {
+ tgaFlipImage( info );
+ if ( info->flipped ) info->status = TGA_ERROR_MEMORY;
+ }
+
+ return(info);
+}
+
+// converts RGB to greyscale
+void tgaRGBtogreyscale(tImageTGA *info) {
+
+ int mode,i,j;
+
+ unsigned char *newImageData;
+
+ // if the image is already greyscale do nothing
+ if (info->pixelDepth == 8)
+ return;
+
+ // compute the number of actual components
+ mode = info->pixelDepth / 8;
+
+ // allocate an array for the new image data
+ newImageData = (unsigned char *)malloc(sizeof(unsigned char) *
+ info->height * info->width);
+ if (newImageData == NULL) {
+ return;
+ }
+
+ // convert pixels: greyscale = o.30 * R + 0.59 * G + 0.11 * B
+ for (i = 0,j = 0; j < info->width * info->height; i +=mode, j++)
+ newImageData[j] =
+ (unsigned char)(0.30 * info->imageData[i] +
+ 0.59 * info->imageData[i+1] +
+ 0.11 * info->imageData[i+2]);
+
+
+ //free old image data
+ free(info->imageData);
+
+ // reassign pixelDepth and type according to the new image type
+ info->pixelDepth = 8;
+ info->type = 3;
+ // reassing imageData to the new array.
+ info->imageData = newImageData;
+}
+
+// releases the memory used for the image
+void tgaDestroy(tImageTGA *info) {
+
+ if (info != NULL) {
+ if (info->imageData != NULL)
+ free(info->imageData);
+ free(info);
+ }
+}
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Valentin Milea
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#import <Availability.h>
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import <UIKit/UIKit.h>
+#import <OpenGLES/ES1/gl.h>
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+#import <OpenGL/gl.h>
+#import <Foundation/Foundation.h>
+#endif
+
+void CGAffineToGL(const CGAffineTransform *t, GLfloat *m);
+void GLToCGAffine(const GLfloat *m, CGAffineTransform *t);
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2009 Valentin Milea
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#import "TransformUtils.h"
+
+void CGAffineToGL(const CGAffineTransform *t, GLfloat *m)
+{
+ // | m[0] m[4] m[8] m[12] | | m11 m21 m31 m41 | | a c 0 tx |
+ // | m[1] m[5] m[9] m[13] | | m12 m22 m32 m42 | | b d 0 ty |
+ // | m[2] m[6] m[10] m[14] | <=> | m13 m23 m33 m43 | <=> | 0 0 1 0 |
+ // | m[3] m[7] m[11] m[15] | | m14 m24 m34 m44 | | 0 0 0 1 |
+
+ m[2] = m[3] = m[6] = m[7] = m[8] = m[9] = m[11] = m[14] = 0.0f;
+ m[10] = m[15] = 1.0f;
+ m[0] = t->a; m[4] = t->c; m[12] = t->tx;
+ m[1] = t->b; m[5] = t->d; m[13] = t->ty;
+}
+
+void GLToCGAffine(const GLfloat *m, CGAffineTransform *t)
+{
+ t->a = m[0]; t->c = m[4]; t->tx = m[12];
+ t->b = m[1]; t->d = m[5]; t->ty = m[13];
+}
--- /dev/null
+/* cocos2d for iPhone
+ *
+ * http://www.cocos2d-iphone.org
+ *
+ *
+ * inflateMemory_ based on zlib example code
+ * http://www.zlib.net
+ *
+ * Some ideas were taken from:
+ * http://themanaworld.org/
+ * from the mapreader.cpp file
+ *
+ */
+
+#ifndef __CC_ZIP_UTILS_H
+#define __CC_ZIP_UTILS_H
+
+#import <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /* XXX: pragma pack ??? */
+ /** @struct CCZHeader
+ */
+ struct CCZHeader {
+ uint8_t sig[4]; // signature. Should be 'CCZ!' 4 bytes
+ uint16_t compression_type; // should 0
+ uint16_t version; // should be 2 (although version type==1 is also supported)
+ uint32_t reserved; // Reserverd for users.
+ uint32_t len; // size of the uncompressed file
+ };
+
+ enum {
+ CCZ_COMPRESSION_ZLIB, // zlib format.
+ CCZ_COMPRESSION_BZIP2, // bzip2 format (not supported yet)
+ CCZ_COMPRESSION_GZIP, // gzip format (not supported yet)
+ CCZ_COMPRESSION_NONE, // plain (not supported yet)
+ };
+
+/** @file
+ * Zip helper functions
+ */
+
+/**
+ * Inflates either zlib or gzip deflated memory. The inflated memory is
+ * expected to be freed by the caller.
+ *
+ * It will allocate 256k for the destination buffer. If it is not enought it will multiply the previous buffer size per 2, until there is enough memory.
+ * @returns the length of the deflated buffer
+ *
+ @since v0.8.1
+ */
+int ccInflateMemory(unsigned char *in, unsigned int inLength, unsigned char **out);
+
+/**
+ * Inflates either zlib or gzip deflated memory. The inflated memory is
+ * expected to be freed by the caller.
+ *
+ * outLenghtHint is assumed to be the needed room to allocate the inflated buffer.
+ *
+ * @returns the length of the deflated buffer
+ *
+ @since v1.0.0
+ */
+int ccInflateMemoryWithHint(unsigned char *in, unsigned int inLength, unsigned char **out, unsigned int outLenghtHint );
+
+
+/** inflates a GZip file into memory
+ *
+ * @returns the length of the deflated buffer
+ *
+ * @since v0.99.5
+ */
+int ccInflateGZipFile(const char *filename, unsigned char **out);
+
+/** inflates a CCZ file into memory
+ *
+ * @returns the length of the deflated buffer
+ *
+ * @since v0.99.5
+ */
+int ccInflateCCZFile(const char *filename, unsigned char **out);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __CC_ZIP_UTILS_H
--- /dev/null
+/* cocos2d for iPhone
+ *
+ * http://www.cocos2d-iphone.org
+ *
+ *
+ * Inflates either zlib or gzip deflated memory. The inflated memory is
+ * expected to be freed by the caller.
+ *
+ * inflateMemory_ based on zlib example code
+ * http://www.zlib.net
+ *
+ * Some ideas were taken from:
+ * http://themanaworld.org/
+ * from the mapreader.cpp file
+ */
+
+#import <Availability.h>
+
+#import <zlib.h>
+#import <stdlib.h>
+#import <assert.h>
+#import <stdio.h>
+
+#import "ZipUtils.h"
+#import "CCFileUtils.h"
+#import "../ccMacros.h"
+
+// memory in iPhone is precious
+// Should buffer factor be 1.5 instead of 2 ?
+#define BUFFER_INC_FACTOR (2)
+
+static int inflateMemoryWithHint(unsigned char *in, unsigned int inLength, unsigned char **out, unsigned int *outLength, unsigned int outLenghtHint )
+{
+ /* ret value */
+ int err = Z_OK;
+
+ int bufferSize = outLenghtHint;
+ *out = (unsigned char*) malloc(bufferSize);
+
+ z_stream d_stream; /* decompression stream */
+ d_stream.zalloc = (alloc_func)0;
+ d_stream.zfree = (free_func)0;
+ d_stream.opaque = (voidpf)0;
+
+ d_stream.next_in = in;
+ d_stream.avail_in = inLength;
+ d_stream.next_out = *out;
+ d_stream.avail_out = bufferSize;
+
+ /* window size to hold 256k */
+ if( (err = inflateInit2(&d_stream, 15 + 32)) != Z_OK )
+ return err;
+
+ for (;;) {
+ err = inflate(&d_stream, Z_NO_FLUSH);
+
+ if (err == Z_STREAM_END)
+ break;
+
+ switch (err) {
+ case Z_NEED_DICT:
+ err = Z_DATA_ERROR;
+ case Z_DATA_ERROR:
+ case Z_MEM_ERROR:
+ inflateEnd(&d_stream);
+ return err;
+ }
+
+ // not enough memory ?
+ if (err != Z_STREAM_END) {
+
+ unsigned char *tmp = realloc(*out, bufferSize * BUFFER_INC_FACTOR);
+
+ /* not enough memory, ouch */
+ if (! tmp ) {
+ CCLOG(@"cocos2d: ZipUtils: realloc failed");
+ inflateEnd(&d_stream);
+ return Z_MEM_ERROR;
+ }
+ /* only assign to *out if tmp is valid. it's not guaranteed that realloc will reuse the memory */
+ *out = tmp;
+
+ d_stream.next_out = *out + bufferSize;
+ d_stream.avail_out = bufferSize;
+ bufferSize *= BUFFER_INC_FACTOR;
+ }
+ }
+
+
+ *outLength = bufferSize - d_stream.avail_out;
+ err = inflateEnd(&d_stream);
+ return err;
+}
+
+int ccInflateMemoryWithHint(unsigned char *in, unsigned int inLength, unsigned char **out, unsigned int outLengthHint )
+{
+ unsigned int outLength = 0;
+ int err = inflateMemoryWithHint(in, inLength, out, &outLength, outLengthHint );
+
+ if (err != Z_OK || *out == NULL) {
+ if (err == Z_MEM_ERROR)
+ CCLOG(@"cocos2d: ZipUtils: Out of memory while decompressing map data!");
+
+ else if (err == Z_VERSION_ERROR)
+ CCLOG(@"cocos2d: ZipUtils: Incompatible zlib version!");
+
+ else if (err == Z_DATA_ERROR)
+ CCLOG(@"cocos2d: ZipUtils: Incorrect zlib compressed data!");
+
+ else
+ CCLOG(@"cocos2d: ZipUtils: Unknown error while decompressing map data!");
+
+ free(*out);
+ *out = NULL;
+ outLength = 0;
+ }
+
+ return outLength;
+}
+
+int ccInflateMemory(unsigned char *in, unsigned int inLength, unsigned char **out)
+{
+ // 256k for hint
+ return ccInflateMemoryWithHint(in, inLength, out, 256 * 1024 );
+}
+
+int ccInflateGZipFile(const char *path, unsigned char **out)
+{
+ int len;
+ unsigned int offset = 0;
+
+ NSCAssert( out, @"ccInflateGZipFile: invalid 'out' parameter");
+ NSCAssert( &*out, @"ccInflateGZipFile: invalid 'out' parameter");
+
+ gzFile inFile = gzopen(path, "rb");
+ if( inFile == NULL ) {
+ CCLOG(@"cocos2d: ZipUtils: error open gzip file: %s", path);
+ return -1;
+ }
+
+ /* 512k initial decompress buffer */
+ unsigned int bufferSize = 512 * 1024;
+ unsigned int totalBufferSize = bufferSize;
+
+ *out = malloc( bufferSize );
+ if( ! out ) {
+ CCLOG(@"cocos2d: ZipUtils: out of memory");
+ return -1;
+ }
+
+ for (;;) {
+ len = gzread(inFile, *out + offset, bufferSize);
+ if (len < 0) {
+ CCLOG(@"cocos2d: ZipUtils: error in gzread");
+ free( *out );
+ *out = NULL;
+ return -1;
+ }
+ if (len == 0)
+ break;
+
+ offset += len;
+
+ // finish reading the file
+ if( len < bufferSize )
+ break;
+
+ bufferSize *= BUFFER_INC_FACTOR;
+ totalBufferSize += bufferSize;
+ unsigned char *tmp = realloc(*out, totalBufferSize );
+
+ if( ! tmp ) {
+ CCLOG(@"cocos2d: ZipUtils: out of memory");
+ free( *out );
+ *out = NULL;
+ return -1;
+ }
+
+ *out = tmp;
+ }
+
+ if (gzclose(inFile) != Z_OK)
+ CCLOG(@"cocos2d: ZipUtils: gzclose failed");
+
+ return offset;
+}
+
+int ccInflateCCZFile(const char *path, unsigned char **out)
+{
+ NSCAssert( out, @"ccInflateCCZFile: invalid 'out' parameter");
+ NSCAssert( &*out, @"ccInflateCCZFile: invalid 'out' parameter");
+
+ // load file into memory
+ unsigned char *compressed = NULL;
+ NSInteger fileLen = ccLoadFileIntoMemory( path, &compressed );
+ if( fileLen < 0 ) {
+ CCLOG(@"cocos2d: Error loading CCZ compressed file");
+ }
+
+ struct CCZHeader *header = (struct CCZHeader*) compressed;
+
+ // verify header
+ if( header->sig[0] != 'C' || header->sig[1] != 'C' || header->sig[2] != 'Z' || header->sig[3] != '!' ) {
+ CCLOG(@"cocos2d: Invalid CCZ file");
+ free(compressed);
+ return -1;
+ }
+
+ // verify header version
+ uint16_t version = CFSwapInt16BigToHost( header->version );
+ if( version > 2 ) {
+ CCLOG(@"cocos2d: Unsupported CCZ header format");
+ free(compressed);
+ return -1;
+ }
+
+ // verify compression format
+ if( CFSwapInt16BigToHost(header->compression_type) != CCZ_COMPRESSION_ZLIB ) {
+ CCLOG(@"cocos2d: CCZ Unsupported compression method");
+ free(compressed);
+ return -1;
+ }
+
+ uint32_t len = CFSwapInt32BigToHost( header->len );
+
+ *out = malloc( len );
+ if(! *out )
+ {
+ CCLOG(@"cocos2d: CCZ: Failed to allocate memory for texture");
+ free(compressed);
+ return -1;
+ }
+
+
+ uLongf destlen = len;
+ uLongf source = (uLongf) compressed + sizeof(*header);
+ int ret = uncompress(*out, &destlen, (Bytef*)source, fileLen - sizeof(*header) );
+
+ free( compressed );
+
+ if( ret != Z_OK )
+ {
+ CCLOG(@"cocos2d: CCZ: Failed to uncompress data");
+ free( *out );
+ *out = NULL;
+ return -1;
+ }
+
+
+ return len;
+}
\ No newline at end of file
--- /dev/null
+/*
+ public domain BASE64 code
+
+ modified for cocos2d-iphone: http://www.cocos2d-iphone.org
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+unsigned char alphabet[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+int _base64Decode( unsigned char *input, unsigned int input_len, unsigned char *output, unsigned int *output_len )
+{
+ static char inalphabet[256], decoder[256];
+ int i, bits, c, char_count, errors = 0;
+ unsigned int input_idx = 0;
+ unsigned int output_idx = 0;
+
+ for (i = (sizeof alphabet) - 1; i >= 0 ; i--) {
+ inalphabet[alphabet[i]] = 1;
+ decoder[alphabet[i]] = i;
+ }
+
+ char_count = 0;
+ bits = 0;
+ for( input_idx=0; input_idx < input_len ; input_idx++ ) {
+ c = input[ input_idx ];
+ if (c == '=')
+ break;
+ if (c > 255 || ! inalphabet[c])
+ continue;
+ bits += decoder[c];
+ char_count++;
+ if (char_count == 4) {
+ output[ output_idx++ ] = (bits >> 16);
+ output[ output_idx++ ] = ((bits >> 8) & 0xff);
+ output[ output_idx++ ] = ( bits & 0xff);
+ bits = 0;
+ char_count = 0;
+ } else {
+ bits <<= 6;
+ }
+ }
+
+ if( c == '=' ) {
+ switch (char_count) {
+ case 1:
+ fprintf(stderr, "base64Decode: encoding incomplete: at least 2 bits missing");
+ errors++;
+ break;
+ case 2:
+ output[ output_idx++ ] = ( bits >> 10 );
+ break;
+ case 3:
+ output[ output_idx++ ] = ( bits >> 16 );
+ output[ output_idx++ ] = (( bits >> 8 ) & 0xff);
+ break;
+ }
+ } else if ( input_idx < input_len ) {
+ if (char_count) {
+ fprintf(stderr, "base64 encoding incomplete: at least %d bits truncated",
+ ((4 - char_count) * 6));
+ errors++;
+ }
+ }
+
+ *output_len = output_idx;
+ return errors;
+}
+
+int base64Decode(unsigned char *in, unsigned int inLength, unsigned char **out)
+{
+ unsigned int outLength = 0;
+
+ //should be enough to store 6-bit buffers in 8-bit buffers
+ *out = malloc( inLength * 3.0f / 4.0f + 1 );
+ if( *out ) {
+ int ret = _base64Decode(in, inLength, *out, &outLength);
+
+ if (ret > 0 )
+ {
+ printf("Base64Utils: error decoding");
+ free(*out);
+ *out = NULL;
+ outLength = 0;
+ }
+ }
+ return outLength;
+}
--- /dev/null
+/*
+ public domain BASE64 code
+
+ modified for cocos2d-iphone: http://www.cocos2d-iphone.org
+ */
+
+#ifndef __CC_BASE64_DECODE_H
+#define __CC_BASE64_DECODE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/** @file
+ base64 helper functions
+ */
+
+/**
+ * Decodes a 64base encoded memory. The decoded memory is
+ * expected to be freed by the caller.
+ *
+ * @returns the length of the out buffer
+ *
+ @since v0.8.1
+ */
+int base64Decode(unsigned char *in, unsigned int inLength, unsigned char **out);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __CC_BASE64_DECODE_H
--- /dev/null
+/* Copyright (c) 2007 Scott Lembcke
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/**
+ @file
+ Based on Chipmunk cpArray.
+ ccArray is a faster alternative to NSMutableArray, it does pretty much the
+ same thing (stores NSObjects and retains/releases them appropriately). It's
+ faster because:
+ - it uses a plain C interface so it doesn't incur Objective-c messaging overhead
+ - it assumes you know what you're doing, so it doesn't spend time on safety checks
+ (index out of bounds, required capacity etc.)
+ - comparisons are done using pointer equality instead of isEqual
+
+ There are 2 kind of functions:
+ - ccArray functions that manipulates objective-c objects (retain and release are performanced)
+ - ccCArray functions that manipulates values like if they were standard C structures (no retain/release is performed)
+ */
+
+#ifndef CC_ARRAY_H
+#define CC_ARRAY_H
+
+#import <Foundation/Foundation.h>
+
+#import <stdlib.h>
+#import <string.h>
+
+
+#pragma mark -
+#pragma mark ccArray for Objects
+
+// Easy integration
+#define CCARRAYDATA_FOREACH(__array__, __object__) \
+__object__=__array__->arr[0]; for(NSUInteger i=0, num=__array__->num; i<num; i++, __object__=__array__->arr[i]) \
+
+
+typedef struct ccArray {
+ NSUInteger num, max;
+ id *arr;
+} ccArray;
+
+/** Allocates and initializes a new array with specified capacity */
+static inline ccArray* ccArrayNew(NSUInteger capacity) {
+ if (capacity == 0)
+ capacity = 1;
+
+ ccArray *arr = (ccArray*)malloc( sizeof(ccArray) );
+ arr->num = 0;
+ arr->arr = (id*) malloc( capacity * sizeof(id) );
+ arr->max = capacity;
+
+ return arr;
+}
+
+static inline void ccArrayRemoveAllObjects(ccArray *arr);
+
+/** Frees array after removing all remaining objects. Silently ignores nil arr. */
+static inline void ccArrayFree(ccArray *arr)
+{
+ if( arr == nil ) return;
+
+ ccArrayRemoveAllObjects(arr);
+
+ free(arr->arr);
+ free(arr);
+}
+
+/** Doubles array capacity */
+static inline void ccArrayDoubleCapacity(ccArray *arr)
+{
+ arr->max *= 2;
+ id *newArr = (id *)realloc( arr->arr, arr->max * sizeof(id) );
+ // will fail when there's not enough memory
+ NSCAssert(newArr != NULL, @"ccArrayDoubleCapacity failed. Not enough memory");
+ arr->arr = newArr;
+}
+
+/** Increases array capacity such that max >= num + extra. */
+static inline void ccArrayEnsureExtraCapacity(ccArray *arr, NSUInteger extra)
+{
+ while (arr->max < arr->num + extra)
+ ccArrayDoubleCapacity(arr);
+}
+
+/** shrinks the array so the memory footprint corresponds with the number of items */
+static inline void ccArrayShrink(ccArray *arr)
+{
+ NSUInteger newSize;
+
+ //only resize when necessary
+ if (arr->max > arr->num && !(arr->num==0 && arr->max==1))
+ {
+ if (arr->num!=0)
+ {
+ newSize=arr->num;
+ arr->max=arr->num;
+ }
+ else
+ {//minimum capacity of 1, with 0 elements the array would be free'd by realloc
+ newSize=1;
+ arr->max=1;
+ }
+
+ arr->arr = (id*) realloc(arr->arr,newSize * sizeof(id) );
+ NSCAssert(arr->arr!=NULL,@"could not reallocate the memory");
+ }
+}
+
+/** Returns index of first occurence of object, NSNotFound if object not found. */
+static inline NSUInteger ccArrayGetIndexOfObject(ccArray *arr, id object)
+{
+ for( NSUInteger i = 0; i < arr->num; i++)
+ if( arr->arr[i] == object ) return i;
+
+ return NSNotFound;
+}
+
+/** Returns a Boolean value that indicates whether object is present in array. */
+static inline BOOL ccArrayContainsObject(ccArray *arr, id object)
+{
+ return ccArrayGetIndexOfObject(arr, object) != NSNotFound;
+}
+
+/** Appends an object. Bahaviour undefined if array doesn't have enough capacity. */
+static inline void ccArrayAppendObject(ccArray *arr, id object)
+{
+ arr->arr[arr->num] = [object retain];
+ arr->num++;
+}
+
+/** Appends an object. Capacity of arr is increased if needed. */
+static inline void ccArrayAppendObjectWithResize(ccArray *arr, id object)
+{
+ ccArrayEnsureExtraCapacity(arr, 1);
+ ccArrayAppendObject(arr, object);
+}
+
+/** Appends objects from plusArr to arr. Behaviour undefined if arr doesn't have
+ enough capacity. */
+static inline void ccArrayAppendArray(ccArray *arr, ccArray *plusArr)
+{
+ for( NSUInteger i = 0; i < plusArr->num; i++)
+ ccArrayAppendObject(arr, plusArr->arr[i]);
+}
+
+/** Appends objects from plusArr to arr. Capacity of arr is increased if needed. */
+static inline void ccArrayAppendArrayWithResize(ccArray *arr, ccArray *plusArr)
+{
+ ccArrayEnsureExtraCapacity(arr, plusArr->num);
+ ccArrayAppendArray(arr, plusArr);
+}
+
+/** Inserts an object at index */
+static inline void ccArrayInsertObjectAtIndex(ccArray *arr, id object, NSUInteger index)
+{
+ NSCAssert(index<=arr->num, @"Invalid index. Out of bounds");
+
+ ccArrayEnsureExtraCapacity(arr, 1);
+
+ NSUInteger remaining = arr->num - index;
+ if( remaining > 0)
+ memmove(&arr->arr[index+1], &arr->arr[index], sizeof(id) * remaining );
+
+ arr->arr[index] = [object retain];
+ arr->num++;
+}
+
+/** Swaps two objects */
+static inline void ccArraySwapObjectsAtIndexes(ccArray *arr, NSUInteger index1, NSUInteger index2)
+{
+ NSCAssert(index1 < arr->num, @"(1) Invalid index. Out of bounds");
+ NSCAssert(index2 < arr->num, @"(2) Invalid index. Out of bounds");
+
+ id object1 = arr->arr[index1];
+
+ arr->arr[index1] = arr->arr[index2];
+ arr->arr[index2] = object1;
+}
+
+/** Removes all objects from arr */
+static inline void ccArrayRemoveAllObjects(ccArray *arr)
+{
+ while( arr->num > 0 )
+ [arr->arr[--arr->num] release];
+}
+
+/** Removes object at specified index and pushes back all subsequent objects.
+ Behaviour undefined if index outside [0, num-1]. */
+static inline void ccArrayRemoveObjectAtIndex(ccArray *arr, NSUInteger index)
+{
+ [arr->arr[index] release];
+ arr->num--;
+
+ NSUInteger remaining = arr->num - index;
+ if(remaining>0)
+ memmove(&arr->arr[index], &arr->arr[index+1], remaining * sizeof(id));
+}
+
+/** Removes object at specified index and fills the gap with the last object,
+ thereby avoiding the need to push back subsequent objects.
+ Behaviour undefined if index outside [0, num-1]. */
+static inline void ccArrayFastRemoveObjectAtIndex(ccArray *arr, NSUInteger index)
+{
+ [arr->arr[index] release];
+ NSUInteger last = --arr->num;
+ arr->arr[index] = arr->arr[last];
+}
+
+static inline void ccArrayFastRemoveObject(ccArray *arr, id object)
+{
+ NSUInteger index = ccArrayGetIndexOfObject(arr, object);
+ if (index != NSNotFound)
+ ccArrayFastRemoveObjectAtIndex(arr, index);
+}
+
+/** Searches for the first occurance of object and removes it. If object is not
+ found the function has no effect. */
+static inline void ccArrayRemoveObject(ccArray *arr, id object)
+{
+ NSUInteger index = ccArrayGetIndexOfObject(arr, object);
+ if (index != NSNotFound)
+ ccArrayRemoveObjectAtIndex(arr, index);
+}
+
+/** Removes from arr all objects in minusArr. For each object in minusArr, the
+ first matching instance in arr will be removed. */
+static inline void ccArrayRemoveArray(ccArray *arr, ccArray *minusArr)
+{
+ for( NSUInteger i = 0; i < minusArr->num; i++)
+ ccArrayRemoveObject(arr, minusArr->arr[i]);
+}
+
+/** Removes from arr all objects in minusArr. For each object in minusArr, all
+ matching instances in arr will be removed. */
+static inline void ccArrayFullRemoveArray(ccArray *arr, ccArray *minusArr)
+{
+ NSUInteger back = 0;
+
+ for( NSUInteger i = 0; i < arr->num; i++) {
+ if( ccArrayContainsObject(minusArr, arr->arr[i]) ) {
+ [arr->arr[i] release];
+ back++;
+ } else
+ arr->arr[i - back] = arr->arr[i];
+ }
+
+ arr->num -= back;
+}
+
+/** Sends to each object in arr the message identified by given selector. */
+static inline void ccArrayMakeObjectsPerformSelector(ccArray *arr, SEL sel)
+{
+ for( NSUInteger i = 0; i < arr->num; i++)
+ [arr->arr[i] performSelector:sel];
+}
+
+static inline void ccArrayMakeObjectsPerformSelectorWithObject(ccArray *arr, SEL sel, id object)
+{
+ for( NSUInteger i = 0; i < arr->num; i++)
+ [arr->arr[i] performSelector:sel withObject:object];
+}
+
+
+#pragma mark -
+#pragma mark ccCArray for Values (c structures)
+
+typedef ccArray ccCArray;
+
+static inline void ccCArrayRemoveAllValues(ccCArray *arr);
+
+/** Allocates and initializes a new C array with specified capacity */
+static inline ccCArray* ccCArrayNew(NSUInteger capacity) {
+ if (capacity == 0)
+ capacity = 1;
+
+ ccCArray *arr = (ccCArray*)malloc( sizeof(ccCArray) );
+ arr->num = 0;
+ arr->arr = (id*) malloc( capacity * sizeof(id) );
+ arr->max = capacity;
+
+ return arr;
+}
+
+/** Frees C array after removing all remaining values. Silently ignores nil arr. */
+static inline void ccCArrayFree(ccCArray *arr)
+{
+ if( arr == nil ) return;
+
+ ccCArrayRemoveAllValues(arr);
+
+ free(arr->arr);
+ free(arr);
+}
+
+/** Doubles C array capacity */
+static inline void ccCArrayDoubleCapacity(ccCArray *arr)
+{
+ return ccArrayDoubleCapacity(arr);
+}
+
+/** Increases array capacity such that max >= num + extra. */
+static inline void ccCArrayEnsureExtraCapacity(ccCArray *arr, NSUInteger extra)
+{
+ return ccArrayEnsureExtraCapacity(arr,extra);
+}
+
+/** Returns index of first occurence of value, NSNotFound if value not found. */
+static inline NSUInteger ccCArrayGetIndexOfValue(ccCArray *arr, void* value)
+{
+ for( NSUInteger i = 0; i < arr->num; i++)
+ if( arr->arr[i] == value ) return i;
+ return NSNotFound;
+}
+
+/** Returns a Boolean value that indicates whether value is present in the C array. */
+static inline BOOL ccCArrayContainsValue(ccCArray *arr, void* value)
+{
+ return ccCArrayGetIndexOfValue(arr, value) != NSNotFound;
+}
+
+/** Inserts a value at a certain position. Behaviour undefined if aray doesn't have enough capacity */
+static inline void ccCArrayInsertValueAtIndex( ccCArray *arr, void *value, NSUInteger index)
+{
+ NSCAssert( index < arr->max, @"ccCArrayInsertValueAtIndex: invalid index");
+
+ NSUInteger remaining = arr->num - index;
+
+ // last Value doesn't need to be moved
+ if( remaining > 0) {
+ // tex coordinates
+ memmove( &arr->arr[index+1],&arr->arr[index], sizeof(void*) * remaining );
+ }
+
+ arr->num++;
+ arr->arr[index] = (id) value;
+}
+
+/** Appends an value. Bahaviour undefined if array doesn't have enough capacity. */
+static inline void ccCArrayAppendValue(ccCArray *arr, void* value)
+{
+ arr->arr[arr->num] = (id) value;
+ arr->num++;
+}
+
+/** Appends an value. Capacity of arr is increased if needed. */
+static inline void ccCArrayAppendValueWithResize(ccCArray *arr, void* value)
+{
+ ccCArrayEnsureExtraCapacity(arr, 1);
+ ccCArrayAppendValue(arr, value);
+}
+
+/** Appends values from plusArr to arr. Behaviour undefined if arr doesn't have
+ enough capacity. */
+static inline void ccCArrayAppendArray(ccCArray *arr, ccCArray *plusArr)
+{
+ for( NSUInteger i = 0; i < plusArr->num; i++)
+ ccCArrayAppendValue(arr, plusArr->arr[i]);
+}
+
+/** Appends values from plusArr to arr. Capacity of arr is increased if needed. */
+static inline void ccCArrayAppendArrayWithResize(ccCArray *arr, ccCArray *plusArr)
+{
+ ccCArrayEnsureExtraCapacity(arr, plusArr->num);
+ ccCArrayAppendArray(arr, plusArr);
+}
+
+/** Removes all values from arr */
+static inline void ccCArrayRemoveAllValues(ccCArray *arr)
+{
+ arr->num = 0;
+}
+
+/** Removes value at specified index and pushes back all subsequent values.
+ Behaviour undefined if index outside [0, num-1].
+ @since v0.99.4
+ */
+static inline void ccCArrayRemoveValueAtIndex(ccCArray *arr, NSUInteger index)
+{
+ for( NSUInteger last = --arr->num; index < last; index++)
+ arr->arr[index] = arr->arr[index + 1];
+}
+
+/** Removes value at specified index and fills the gap with the last value,
+ thereby avoiding the need to push back subsequent values.
+ Behaviour undefined if index outside [0, num-1].
+ @since v0.99.4
+ */
+static inline void ccCArrayFastRemoveValueAtIndex(ccCArray *arr, NSUInteger index)
+{
+ NSUInteger last = --arr->num;
+ arr->arr[index] = arr->arr[last];
+}
+
+/** Searches for the first occurance of value and removes it. If value is not found the function has no effect.
+ @since v0.99.4
+ */
+static inline void ccCArrayRemoveValue(ccCArray *arr, void* value)
+{
+ NSUInteger index = ccCArrayGetIndexOfValue(arr, value);
+ if (index != NSNotFound)
+ ccCArrayRemoveValueAtIndex(arr, index);
+}
+
+/** Removes from arr all values in minusArr. For each Value in minusArr, the first matching instance in arr will be removed.
+ @since v0.99.4
+ */
+static inline void ccCArrayRemoveArray(ccCArray *arr, ccCArray *minusArr)
+{
+ for( NSUInteger i = 0; i < minusArr->num; i++)
+ ccCArrayRemoveValue(arr, minusArr->arr[i]);
+}
+
+/** Removes from arr all values in minusArr. For each value in minusArr, all matching instances in arr will be removed.
+ @since v0.99.4
+ */
+static inline void ccCArrayFullRemoveArray(ccCArray *arr, ccCArray *minusArr)
+{
+ NSUInteger back = 0;
+
+ for( NSUInteger i = 0; i < arr->num; i++) {
+ if( ccCArrayContainsValue(minusArr, arr->arr[i]) ) {
+ back++;
+ } else
+ arr->arr[i - back] = arr->arr[i];
+ }
+
+ arr->num -= back;
+}
+#endif // CC_ARRAY_H
\ No newline at end of file
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ */
+
+/*
+ ccNextPOT function is licensed under the same license that is used in CCTexture2D.m.
+ */
+#include "ccUtils.h"
+
+unsigned long ccNextPOT(unsigned long x)
+{
+ x = x - 1;
+ x = x | (x >> 1);
+ x = x | (x >> 2);
+ x = x | (x >> 4);
+ x = x | (x >> 8);
+ x = x | (x >>16);
+ return x + 1;
+}
\ No newline at end of file
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ */
+
+#ifndef __CC_UTILS_H
+#define __CC_UTILS_H
+
+/** @file ccUtils.h
+ Misc free functions
+ */
+
+/*
+ ccNextPOT function is licensed under the same license that is used in CCTexture2D.m.
+ */
+
+/** returns the Next Power of Two value.
+
+ Examples:
+ - If "value" is 15, it will return 16.
+ - If "value" is 16, it will return 16.
+ - If "value" is 17, it will return 32.
+
+ @since v0.99.5
+ */
+
+unsigned long ccNextPOT( unsigned long value );
+
+#endif // ! __CC_UTILS_H
--- /dev/null
+/*
+Copyright (c) 2003-2010, Troy D. Hanson http://uthash.sourceforge.net
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef UTHASH_H
+#define UTHASH_H
+
+#include <string.h> /* memcmp,strlen */
+#include <stddef.h> /* ptrdiff_t */
+
+/* These macros use decltype or the earlier __typeof GNU extension.
+ As decltype is only available in newer compilers (VS2010 or gcc 4.3+
+ when compiling c++ source) this code uses whatever method is needed
+ or, for VS2008 where neither is available, uses casting workarounds. */
+#ifdef _MSC_VER /* MS compiler */
+#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */
+#define DECLTYPE(x) (decltype(x))
+#else /* VS2008 or older (or VS2010 in C mode) */
+#define NO_DECLTYPE
+#define DECLTYPE(x)
+#endif
+#else /* GNU, Sun and other compilers */
+#define DECLTYPE(x) (__typeof(x))
+#endif
+
+#ifdef NO_DECLTYPE
+#define DECLTYPE_ASSIGN(dst,src) \
+do { \
+ char **_da_dst = (char**)(&(dst)); \
+ *_da_dst = (char*)(src); \
+} while(0)
+#else
+#define DECLTYPE_ASSIGN(dst,src) \
+do { \
+ (dst) = DECLTYPE(dst)(src); \
+} while(0)
+#endif
+
+/* a number of the hash function use uint32_t which isn't defined on win32 */
+#ifdef _MSC_VER
+typedef unsigned int uint32_t;
+#else
+#include <inttypes.h> /* uint32_t */
+#endif
+
+#define UTHASH_VERSION 1.9.3
+
+#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */
+#define uthash_malloc(sz) malloc(sz) /* malloc fcn */
+#define uthash_free(ptr,sz) free(ptr) /* free fcn */
+
+#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */
+#define uthash_expand_fyi(tbl) /* can be defined to log expands */
+
+/* initial number of buckets */
+#define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */
+#define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */
+#define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */
+
+/* calculate the element whose hash handle address is hhe */
+#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))
+
+#define HASH_FIND(hh,head,keyptr,keylen,out) \
+do { \
+ unsigned _hf_bkt,_hf_hashv; \
+ out=NULL; \
+ if (head) { \
+ HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \
+ if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \
+ HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \
+ keyptr,keylen,out); \
+ } \
+ } \
+} while (0)
+
+#ifdef HASH_BLOOM
+#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM)
+#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0)
+#define HASH_BLOOM_MAKE(tbl) \
+do { \
+ (tbl)->bloom_nbits = HASH_BLOOM; \
+ (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \
+ if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \
+ memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \
+ (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \
+} while (0);
+
+#define HASH_BLOOM_FREE(tbl) \
+do { \
+ uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \
+} while (0);
+
+#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8)))
+#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8)))
+
+#define HASH_BLOOM_ADD(tbl,hashv) \
+ HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
+
+#define HASH_BLOOM_TEST(tbl,hashv) \
+ HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
+
+#else
+#define HASH_BLOOM_MAKE(tbl)
+#define HASH_BLOOM_FREE(tbl)
+#define HASH_BLOOM_ADD(tbl,hashv)
+#define HASH_BLOOM_TEST(tbl,hashv) (1)
+#endif
+
+#define HASH_MAKE_TABLE(hh,head) \
+do { \
+ (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \
+ sizeof(UT_hash_table)); \
+ if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \
+ memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \
+ (head)->hh.tbl->tail = &((head)->hh); \
+ (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \
+ (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \
+ (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \
+ (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \
+ HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
+ if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \
+ memset((head)->hh.tbl->buckets, 0, \
+ HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
+ HASH_BLOOM_MAKE((head)->hh.tbl); \
+ (head)->hh.tbl->signature = HASH_SIGNATURE; \
+} while(0)
+
+#define HASH_ADD(hh,head,fieldname,keylen_in,add) \
+ HASH_ADD_KEYPTR(hh,head,&add->fieldname,keylen_in,add)
+
+#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \
+do { \
+ unsigned _ha_bkt; \
+ (add)->hh.next = NULL; \
+ (add)->hh.key = (char*)keyptr; \
+ (add)->hh.keylen = keylen_in; \
+ if (!(head)) { \
+ head = (add); \
+ (head)->hh.prev = NULL; \
+ HASH_MAKE_TABLE(hh,head); \
+ } else { \
+ (head)->hh.tbl->tail->next = (add); \
+ (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \
+ (head)->hh.tbl->tail = &((add)->hh); \
+ } \
+ (head)->hh.tbl->num_items++; \
+ (add)->hh.tbl = (head)->hh.tbl; \
+ HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \
+ (add)->hh.hashv, _ha_bkt); \
+ HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \
+ HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \
+ HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \
+ HASH_FSCK(hh,head); \
+} while(0)
+
+#define HASH_TO_BKT( hashv, num_bkts, bkt ) \
+do { \
+ bkt = ((hashv) & ((num_bkts) - 1)); \
+} while(0)
+
+/* delete "delptr" from the hash table.
+ * "the usual" patch-up process for the app-order doubly-linked-list.
+ * The use of _hd_hh_del below deserves special explanation.
+ * These used to be expressed using (delptr) but that led to a bug
+ * if someone used the same symbol for the head and deletee, like
+ * HASH_DELETE(hh,users,users);
+ * We want that to work, but by changing the head (users) below
+ * we were forfeiting our ability to further refer to the deletee (users)
+ * in the patch-up process. Solution: use scratch space to
+ * copy the deletee pointer, then the latter references are via that
+ * scratch pointer rather than through the repointed (users) symbol.
+ */
+#define HASH_DELETE(hh,head,delptr) \
+do { \
+ unsigned _hd_bkt; \
+ struct UT_hash_handle *_hd_hh_del; \
+ if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \
+ uthash_free((head)->hh.tbl->buckets, \
+ (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
+ HASH_BLOOM_FREE((head)->hh.tbl); \
+ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
+ head = NULL; \
+ } else { \
+ _hd_hh_del = &((delptr)->hh); \
+ if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \
+ (head)->hh.tbl->tail = \
+ (UT_hash_handle*)((char*)((delptr)->hh.prev) + \
+ (head)->hh.tbl->hho); \
+ } \
+ if ((delptr)->hh.prev) { \
+ ((UT_hash_handle*)((char*)((delptr)->hh.prev) + \
+ (head)->hh.tbl->hho))->next = (delptr)->hh.next; \
+ } else { \
+ DECLTYPE_ASSIGN(head,(delptr)->hh.next); \
+ } \
+ if (_hd_hh_del->next) { \
+ ((UT_hash_handle*)((char*)_hd_hh_del->next + \
+ (head)->hh.tbl->hho))->prev = \
+ _hd_hh_del->prev; \
+ } \
+ HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \
+ HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \
+ (head)->hh.tbl->num_items--; \
+ } \
+ HASH_FSCK(hh,head); \
+} while (0)
+
+
+/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
+#define HASH_FIND_STR(head,findstr,out) \
+ HASH_FIND(hh,head,findstr,strlen(findstr),out)
+#define HASH_ADD_STR(head,strfield,add) \
+ HASH_ADD(hh,head,strfield,strlen(add->strfield),add)
+#define HASH_FIND_INT(head,findint,out) \
+ HASH_FIND(hh,head,findint,sizeof(int),out)
+#define HASH_ADD_INT(head,intfield,add) \
+ HASH_ADD(hh,head,intfield,sizeof(int),add)
+#define HASH_FIND_PTR(head,findptr,out) \
+ HASH_FIND(hh,head,findptr,sizeof(void *),out)
+#define HASH_ADD_PTR(head,ptrfield,add) \
+ HASH_ADD(hh,head,ptrfield,sizeof(void *),add)
+#define HASH_DEL(head,delptr) \
+ HASH_DELETE(hh,head,delptr)
+
+/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
+ * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
+ */
+#ifdef HASH_DEBUG
+#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0)
+#define HASH_FSCK(hh,head) \
+do { \
+ unsigned _bkt_i; \
+ unsigned _count, _bkt_count; \
+ char *_prev; \
+ struct UT_hash_handle *_thh; \
+ if (head) { \
+ _count = 0; \
+ for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \
+ _bkt_count = 0; \
+ _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \
+ _prev = NULL; \
+ while (_thh) { \
+ if (_prev != (char*)(_thh->hh_prev)) { \
+ HASH_OOPS("invalid hh_prev %p, actual %p\n", \
+ _thh->hh_prev, _prev ); \
+ } \
+ _bkt_count++; \
+ _prev = (char*)(_thh); \
+ _thh = _thh->hh_next; \
+ } \
+ _count += _bkt_count; \
+ if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \
+ HASH_OOPS("invalid bucket count %d, actual %d\n", \
+ (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \
+ } \
+ } \
+ if (_count != (head)->hh.tbl->num_items) { \
+ HASH_OOPS("invalid hh item count %d, actual %d\n", \
+ (head)->hh.tbl->num_items, _count ); \
+ } \
+ /* traverse hh in app order; check next/prev integrity, count */ \
+ _count = 0; \
+ _prev = NULL; \
+ _thh = &(head)->hh; \
+ while (_thh) { \
+ _count++; \
+ if (_prev !=(char*)(_thh->prev)) { \
+ HASH_OOPS("invalid prev %p, actual %p\n", \
+ _thh->prev, _prev ); \
+ } \
+ _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \
+ _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \
+ (head)->hh.tbl->hho) : NULL ); \
+ } \
+ if (_count != (head)->hh.tbl->num_items) { \
+ HASH_OOPS("invalid app item count %d, actual %d\n", \
+ (head)->hh.tbl->num_items, _count ); \
+ } \
+ } \
+} while (0)
+#else
+#define HASH_FSCK(hh,head)
+#endif
+
+/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to
+ * the descriptor to which this macro is defined for tuning the hash function.
+ * The app can #include <unistd.h> to get the prototype for write(2). */
+#ifdef HASH_EMIT_KEYS
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \
+do { \
+ unsigned _klen = fieldlen; \
+ write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \
+ write(HASH_EMIT_KEYS, keyptr, fieldlen); \
+} while (0)
+#else
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)
+#endif
+
+/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */
+#ifdef HASH_FUNCTION
+#define HASH_FCN HASH_FUNCTION
+#else
+#define HASH_FCN HASH_JEN
+#endif
+
+/* The Bernstein hash function, used in Perl prior to v5.6 */
+#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ unsigned _hb_keylen=keylen; \
+ char *_hb_key=(char*)(key); \
+ (hashv) = 0; \
+ while (_hb_keylen--) { (hashv) = ((hashv) * 33) + *_hb_key++; } \
+ bkt = (hashv) & (num_bkts-1); \
+} while (0)
+
+
+/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at
+ * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */
+#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ unsigned _sx_i; \
+ char *_hs_key=(char*)(key); \
+ hashv = 0; \
+ for(_sx_i=0; _sx_i < keylen; _sx_i++) \
+ hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \
+ bkt = hashv & (num_bkts-1); \
+} while (0)
+
+#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ unsigned _fn_i; \
+ char *_hf_key=(char*)(key); \
+ hashv = 2166136261UL; \
+ for(_fn_i=0; _fn_i < keylen; _fn_i++) \
+ hashv = (hashv * 16777619) ^ _hf_key[_fn_i]; \
+ bkt = hashv & (num_bkts-1); \
+} while(0);
+
+#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ unsigned _ho_i; \
+ char *_ho_key=(char*)(key); \
+ hashv = 0; \
+ for(_ho_i=0; _ho_i < keylen; _ho_i++) { \
+ hashv += _ho_key[_ho_i]; \
+ hashv += (hashv << 10); \
+ hashv ^= (hashv >> 6); \
+ } \
+ hashv += (hashv << 3); \
+ hashv ^= (hashv >> 11); \
+ hashv += (hashv << 15); \
+ bkt = hashv & (num_bkts-1); \
+} while(0)
+
+#define HASH_JEN_MIX(a,b,c) \
+do { \
+ a -= b; a -= c; a ^= ( c >> 13 ); \
+ b -= c; b -= a; b ^= ( a << 8 ); \
+ c -= a; c -= b; c ^= ( b >> 13 ); \
+ a -= b; a -= c; a ^= ( c >> 12 ); \
+ b -= c; b -= a; b ^= ( a << 16 ); \
+ c -= a; c -= b; c ^= ( b >> 5 ); \
+ a -= b; a -= c; a ^= ( c >> 3 ); \
+ b -= c; b -= a; b ^= ( a << 10 ); \
+ c -= a; c -= b; c ^= ( b >> 15 ); \
+} while (0)
+
+#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ unsigned _hj_i,_hj_j,_hj_k; \
+ char *_hj_key=(char*)(key); \
+ hashv = 0xfeedbeef; \
+ _hj_i = _hj_j = 0x9e3779b9; \
+ _hj_k = keylen; \
+ while (_hj_k >= 12) { \
+ _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \
+ + ( (unsigned)_hj_key[2] << 16 ) \
+ + ( (unsigned)_hj_key[3] << 24 ) ); \
+ _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \
+ + ( (unsigned)_hj_key[6] << 16 ) \
+ + ( (unsigned)_hj_key[7] << 24 ) ); \
+ hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \
+ + ( (unsigned)_hj_key[10] << 16 ) \
+ + ( (unsigned)_hj_key[11] << 24 ) ); \
+ \
+ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
+ \
+ _hj_key += 12; \
+ _hj_k -= 12; \
+ } \
+ hashv += keylen; \
+ switch ( _hj_k ) { \
+ case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \
+ case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \
+ case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \
+ case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \
+ case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \
+ case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \
+ case 5: _hj_j += _hj_key[4]; \
+ case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \
+ case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \
+ case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \
+ case 1: _hj_i += _hj_key[0]; \
+ } \
+ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
+ bkt = hashv & (num_bkts-1); \
+} while(0)
+
+/* The Paul Hsieh hash function */
+#undef get16bits
+#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
+ || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
+#define get16bits(d) (*((const uint16_t *) (d)))
+#endif
+
+#if !defined (get16bits)
+#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \
+ +(uint32_t)(((const uint8_t *)(d))[0]) )
+#endif
+#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ char *_sfh_key=(char*)(key); \
+ uint32_t _sfh_tmp, _sfh_len = keylen; \
+ \
+ int _sfh_rem = _sfh_len & 3; \
+ _sfh_len >>= 2; \
+ hashv = 0xcafebabe; \
+ \
+ /* Main loop */ \
+ for (;_sfh_len > 0; _sfh_len--) { \
+ hashv += get16bits (_sfh_key); \
+ _sfh_tmp = (get16bits (_sfh_key+2) << 11) ^ hashv; \
+ hashv = (hashv << 16) ^ _sfh_tmp; \
+ _sfh_key += 2*sizeof (uint16_t); \
+ hashv += hashv >> 11; \
+ } \
+ \
+ /* Handle end cases */ \
+ switch (_sfh_rem) { \
+ case 3: hashv += get16bits (_sfh_key); \
+ hashv ^= hashv << 16; \
+ hashv ^= _sfh_key[sizeof (uint16_t)] << 18; \
+ hashv += hashv >> 11; \
+ break; \
+ case 2: hashv += get16bits (_sfh_key); \
+ hashv ^= hashv << 11; \
+ hashv += hashv >> 17; \
+ break; \
+ case 1: hashv += *_sfh_key; \
+ hashv ^= hashv << 10; \
+ hashv += hashv >> 1; \
+ } \
+ \
+ /* Force "avalanching" of final 127 bits */ \
+ hashv ^= hashv << 3; \
+ hashv += hashv >> 5; \
+ hashv ^= hashv << 4; \
+ hashv += hashv >> 17; \
+ hashv ^= hashv << 25; \
+ hashv += hashv >> 6; \
+ bkt = hashv & (num_bkts-1); \
+} while(0);
+
+#ifdef HASH_USING_NO_STRICT_ALIASING
+/* The MurmurHash exploits some CPU's (e.g. x86) tolerance for unaligned reads.
+ * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error.
+ * So MurmurHash comes in two versions, the faster unaligned one and the slower
+ * aligned one. We only use the faster one on CPU's where we know it's safe.
+ *
+ * Note the preprocessor built-in defines can be emitted using:
+ *
+ * gcc -m64 -dM -E - < /dev/null (on gcc)
+ * cc -## a.c (where a.c is a simple test file) (Sun Studio)
+ */
+#if (defined(__i386__) || defined(__x86_64__))
+#define HASH_MUR HASH_MUR_UNALIGNED
+#else
+#define HASH_MUR HASH_MUR_ALIGNED
+#endif
+
+/* Appleby's MurmurHash fast version for unaligned-tolerant archs like i386 */
+#define HASH_MUR_UNALIGNED(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ const unsigned int _mur_m = 0x5bd1e995; \
+ const int _mur_r = 24; \
+ hashv = 0xcafebabe ^ keylen; \
+ char *_mur_key = (char *)(key); \
+ uint32_t _mur_tmp, _mur_len = keylen; \
+ \
+ for (;_mur_len >= 4; _mur_len-=4) { \
+ _mur_tmp = *(uint32_t *)_mur_key; \
+ _mur_tmp *= _mur_m; \
+ _mur_tmp ^= _mur_tmp >> _mur_r; \
+ _mur_tmp *= _mur_m; \
+ hashv *= _mur_m; \
+ hashv ^= _mur_tmp; \
+ _mur_key += 4; \
+ } \
+ \
+ switch(_mur_len) \
+ { \
+ case 3: hashv ^= _mur_key[2] << 16; \
+ case 2: hashv ^= _mur_key[1] << 8; \
+ case 1: hashv ^= _mur_key[0]; \
+ hashv *= _mur_m; \
+ }; \
+ \
+ hashv ^= hashv >> 13; \
+ hashv *= _mur_m; \
+ hashv ^= hashv >> 15; \
+ \
+ bkt = hashv & (num_bkts-1); \
+} while(0)
+
+/* Appleby's MurmurHash version for alignment-sensitive archs like Sparc */
+#define HASH_MUR_ALIGNED(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ const unsigned int _mur_m = 0x5bd1e995; \
+ const int _mur_r = 24; \
+ hashv = 0xcafebabe ^ (keylen); \
+ char *_mur_key = (char *)(key); \
+ uint32_t _mur_len = keylen; \
+ int _mur_align = (int)_mur_key & 3; \
+ \
+ if (_mur_align && (_mur_len >= 4)) { \
+ unsigned _mur_t = 0, _mur_d = 0; \
+ switch(_mur_align) { \
+ case 1: _mur_t |= _mur_key[2] << 16; \
+ case 2: _mur_t |= _mur_key[1] << 8; \
+ case 3: _mur_t |= _mur_key[0]; \
+ } \
+ _mur_t <<= (8 * _mur_align); \
+ _mur_key += 4-_mur_align; \
+ _mur_len -= 4-_mur_align; \
+ int _mur_sl = 8 * (4-_mur_align); \
+ int _mur_sr = 8 * _mur_align; \
+ \
+ for (;_mur_len >= 4; _mur_len-=4) { \
+ _mur_d = *(unsigned *)_mur_key; \
+ _mur_t = (_mur_t >> _mur_sr) | (_mur_d << _mur_sl); \
+ unsigned _mur_k = _mur_t; \
+ _mur_k *= _mur_m; \
+ _mur_k ^= _mur_k >> _mur_r; \
+ _mur_k *= _mur_m; \
+ hashv *= _mur_m; \
+ hashv ^= _mur_k; \
+ _mur_t = _mur_d; \
+ _mur_key += 4; \
+ } \
+ _mur_d = 0; \
+ if(_mur_len >= _mur_align) { \
+ switch(_mur_align) { \
+ case 3: _mur_d |= _mur_key[2] << 16; \
+ case 2: _mur_d |= _mur_key[1] << 8; \
+ case 1: _mur_d |= _mur_key[0]; \
+ } \
+ unsigned _mur_k = (_mur_t >> _mur_sr) | (_mur_d << _mur_sl); \
+ _mur_k *= _mur_m; \
+ _mur_k ^= _mur_k >> _mur_r; \
+ _mur_k *= _mur_m; \
+ hashv *= _mur_m; \
+ hashv ^= _mur_k; \
+ _mur_k += _mur_align; \
+ _mur_len -= _mur_align; \
+ \
+ switch(_mur_len) \
+ { \
+ case 3: hashv ^= _mur_key[2] << 16; \
+ case 2: hashv ^= _mur_key[1] << 8; \
+ case 1: hashv ^= _mur_key[0]; \
+ hashv *= _mur_m; \
+ } \
+ } else { \
+ switch(_mur_len) \
+ { \
+ case 3: _mur_d ^= _mur_key[2] << 16; \
+ case 2: _mur_d ^= _mur_key[1] << 8; \
+ case 1: _mur_d ^= _mur_key[0]; \
+ case 0: hashv ^= (_mur_t >> _mur_sr) | (_mur_d << _mur_sl); \
+ hashv *= _mur_m; \
+ } \
+ } \
+ \
+ hashv ^= hashv >> 13; \
+ hashv *= _mur_m; \
+ hashv ^= hashv >> 15; \
+ } else { \
+ for (;_mur_len >= 4; _mur_len-=4) { \
+ unsigned _mur_k = *(unsigned*)_mur_key; \
+ _mur_k *= _mur_m; \
+ _mur_k ^= _mur_k >> _mur_r; \
+ _mur_k *= _mur_m; \
+ hashv *= _mur_m; \
+ hashv ^= _mur_k; \
+ _mur_key += 4; \
+ } \
+ switch(_mur_len) \
+ { \
+ case 3: hashv ^= _mur_key[2] << 16; \
+ case 2: hashv ^= _mur_key[1] << 8; \
+ case 1: hashv ^= _mur_key[0]; \
+ hashv *= _mur_m; \
+ } \
+ \
+ hashv ^= hashv >> 13; \
+ hashv *= _mur_m; \
+ hashv ^= hashv >> 15; \
+ } \
+ bkt = hashv & (num_bkts-1); \
+} while(0)
+#endif /* HASH_USING_NO_STRICT_ALIASING */
+
+/* key comparison function; return 0 if keys equal */
+#define HASH_KEYCMP(a,b,len) memcmp(a,b,len)
+
+/* iterate over items in a known bucket to find desired item */
+#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \
+do { \
+ if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); \
+ else out=NULL; \
+ while (out) { \
+ if (out->hh.keylen == keylen_in) { \
+ if ((HASH_KEYCMP(out->hh.key,keyptr,keylen_in)) == 0) break; \
+ } \
+ if (out->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,out->hh.hh_next)); \
+ else out = NULL; \
+ } \
+} while(0)
+
+/* add an item to a bucket */
+#define HASH_ADD_TO_BKT(head,addhh) \
+do { \
+ head.count++; \
+ (addhh)->hh_next = head.hh_head; \
+ (addhh)->hh_prev = NULL; \
+ if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \
+ (head).hh_head=addhh; \
+ if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \
+ && (addhh)->tbl->noexpand != 1) { \
+ HASH_EXPAND_BUCKETS((addhh)->tbl); \
+ } \
+} while(0)
+
+/* remove an item from a given bucket */
+#define HASH_DEL_IN_BKT(hh,head,hh_del) \
+ (head).count--; \
+ if ((head).hh_head == hh_del) { \
+ (head).hh_head = hh_del->hh_next; \
+ } \
+ if (hh_del->hh_prev) { \
+ hh_del->hh_prev->hh_next = hh_del->hh_next; \
+ } \
+ if (hh_del->hh_next) { \
+ hh_del->hh_next->hh_prev = hh_del->hh_prev; \
+ }
+
+/* Bucket expansion has the effect of doubling the number of buckets
+ * and redistributing the items into the new buckets. Ideally the
+ * items will distribute more or less evenly into the new buckets
+ * (the extent to which this is true is a measure of the quality of
+ * the hash function as it applies to the key domain).
+ *
+ * With the items distributed into more buckets, the chain length
+ * (item count) in each bucket is reduced. Thus by expanding buckets
+ * the hash keeps a bound on the chain length. This bounded chain
+ * length is the essence of how a hash provides constant time lookup.
+ *
+ * The calculation of tbl->ideal_chain_maxlen below deserves some
+ * explanation. First, keep in mind that we're calculating the ideal
+ * maximum chain length based on the *new* (doubled) bucket count.
+ * In fractions this is just n/b (n=number of items,b=new num buckets).
+ * Since the ideal chain length is an integer, we want to calculate
+ * ceil(n/b). We don't depend on floating point arithmetic in this
+ * hash, so to calculate ceil(n/b) with integers we could write
+ *
+ * ceil(n/b) = (n/b) + ((n%b)?1:0)
+ *
+ * and in fact a previous version of this hash did just that.
+ * But now we have improved things a bit by recognizing that b is
+ * always a power of two. We keep its base 2 log handy (call it lb),
+ * so now we can write this with a bit shift and logical AND:
+ *
+ * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)
+ *
+ */
+#define HASH_EXPAND_BUCKETS(tbl) \
+do { \
+ unsigned _he_bkt; \
+ unsigned _he_bkt_i; \
+ struct UT_hash_handle *_he_thh, *_he_hh_nxt; \
+ UT_hash_bucket *_he_new_buckets, *_he_newbkt; \
+ _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \
+ 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
+ if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \
+ memset(_he_new_buckets, 0, \
+ 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
+ tbl->ideal_chain_maxlen = \
+ (tbl->num_items >> (tbl->log2_num_buckets+1)) + \
+ ((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \
+ tbl->nonideal_items = 0; \
+ for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \
+ { \
+ _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \
+ while (_he_thh) { \
+ _he_hh_nxt = _he_thh->hh_next; \
+ HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \
+ _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \
+ if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \
+ tbl->nonideal_items++; \
+ _he_newbkt->expand_mult = _he_newbkt->count / \
+ tbl->ideal_chain_maxlen; \
+ } \
+ _he_thh->hh_prev = NULL; \
+ _he_thh->hh_next = _he_newbkt->hh_head; \
+ if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \
+ _he_thh; \
+ _he_newbkt->hh_head = _he_thh; \
+ _he_thh = _he_hh_nxt; \
+ } \
+ } \
+ uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
+ tbl->num_buckets *= 2; \
+ tbl->log2_num_buckets++; \
+ tbl->buckets = _he_new_buckets; \
+ tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \
+ (tbl->ineff_expands+1) : 0; \
+ if (tbl->ineff_expands > 1) { \
+ tbl->noexpand=1; \
+ uthash_noexpand_fyi(tbl); \
+ } \
+ uthash_expand_fyi(tbl); \
+} while(0)
+
+
+/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
+/* Note that HASH_SORT assumes the hash handle name to be hh.
+ * HASH_SRT was added to allow the hash handle name to be passed in. */
+#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)
+#define HASH_SRT(hh,head,cmpfcn) \
+do { \
+ unsigned _hs_i; \
+ unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \
+ struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \
+ if (head) { \
+ _hs_insize = 1; \
+ _hs_looping = 1; \
+ _hs_list = &((head)->hh); \
+ while (_hs_looping) { \
+ _hs_p = _hs_list; \
+ _hs_list = NULL; \
+ _hs_tail = NULL; \
+ _hs_nmerges = 0; \
+ while (_hs_p) { \
+ _hs_nmerges++; \
+ _hs_q = _hs_p; \
+ _hs_psize = 0; \
+ for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \
+ _hs_psize++; \
+ _hs_q = (UT_hash_handle*)((_hs_q->next) ? \
+ ((void*)((char*)(_hs_q->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ if (! (_hs_q) ) break; \
+ } \
+ _hs_qsize = _hs_insize; \
+ while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \
+ if (_hs_psize == 0) { \
+ _hs_e = _hs_q; \
+ _hs_q = (UT_hash_handle*)((_hs_q->next) ? \
+ ((void*)((char*)(_hs_q->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ _hs_qsize--; \
+ } else if ( (_hs_qsize == 0) || !(_hs_q) ) { \
+ _hs_e = _hs_p; \
+ _hs_p = (UT_hash_handle*)((_hs_p->next) ? \
+ ((void*)((char*)(_hs_p->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ _hs_psize--; \
+ } else if (( \
+ cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \
+ DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \
+ ) <= 0) { \
+ _hs_e = _hs_p; \
+ _hs_p = (UT_hash_handle*)((_hs_p->next) ? \
+ ((void*)((char*)(_hs_p->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ _hs_psize--; \
+ } else { \
+ _hs_e = _hs_q; \
+ _hs_q = (UT_hash_handle*)((_hs_q->next) ? \
+ ((void*)((char*)(_hs_q->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ _hs_qsize--; \
+ } \
+ if ( _hs_tail ) { \
+ _hs_tail->next = ((_hs_e) ? \
+ ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \
+ } else { \
+ _hs_list = _hs_e; \
+ } \
+ _hs_e->prev = ((_hs_tail) ? \
+ ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \
+ _hs_tail = _hs_e; \
+ } \
+ _hs_p = _hs_q; \
+ } \
+ _hs_tail->next = NULL; \
+ if ( _hs_nmerges <= 1 ) { \
+ _hs_looping=0; \
+ (head)->hh.tbl->tail = _hs_tail; \
+ DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \
+ } \
+ _hs_insize *= 2; \
+ } \
+ HASH_FSCK(hh,head); \
+ } \
+} while (0)
+
+/* This function selects items from one hash into another hash.
+ * The end result is that the selected items have dual presence
+ * in both hashes. There is no copy of the items made; rather
+ * they are added into the new hash through a secondary hash
+ * hash handle that must be present in the structure. */
+#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \
+do { \
+ unsigned _src_bkt, _dst_bkt; \
+ void *_last_elt=NULL, *_elt; \
+ UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \
+ ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \
+ if (src) { \
+ for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \
+ for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \
+ _src_hh; \
+ _src_hh = _src_hh->hh_next) { \
+ _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \
+ if (cond(_elt)) { \
+ _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \
+ _dst_hh->key = _src_hh->key; \
+ _dst_hh->keylen = _src_hh->keylen; \
+ _dst_hh->hashv = _src_hh->hashv; \
+ _dst_hh->prev = _last_elt; \
+ _dst_hh->next = NULL; \
+ if (_last_elt_hh) { _last_elt_hh->next = _elt; } \
+ if (!dst) { \
+ DECLTYPE_ASSIGN(dst,_elt); \
+ HASH_MAKE_TABLE(hh_dst,dst); \
+ } else { \
+ _dst_hh->tbl = (dst)->hh_dst.tbl; \
+ } \
+ HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \
+ HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \
+ (dst)->hh_dst.tbl->num_items++; \
+ _last_elt = _elt; \
+ _last_elt_hh = _dst_hh; \
+ } \
+ } \
+ } \
+ } \
+ HASH_FSCK(hh_dst,dst); \
+} while (0)
+
+#define HASH_CLEAR(hh,head) \
+do { \
+ if (head) { \
+ uthash_free((head)->hh.tbl->buckets, \
+ (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \
+ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
+ (head)=NULL; \
+ } \
+} while(0)
+
+#ifdef NO_DECLTYPE
+#define HASH_ITER(hh,head,el,tmp) \
+for((el)=(head), (*(char**)(&(tmp)))=(char*)((head)?(head)->hh.next:NULL); \
+ el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL))
+#else
+#define HASH_ITER(hh,head,el,tmp) \
+for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL); \
+ el; (el)=(tmp),(tmp)=DECLTYPE(el)((tmp)?(tmp)->hh.next:NULL))
+#endif
+
+/* obtain a count of items in the hash */
+#define HASH_COUNT(head) HASH_CNT(hh,head)
+#define HASH_CNT(hh,head) ((head)?((head)->hh.tbl->num_items):0)
+
+typedef struct UT_hash_bucket {
+ struct UT_hash_handle *hh_head;
+ unsigned count;
+
+ /* expand_mult is normally set to 0. In this situation, the max chain length
+ * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If
+ * the bucket's chain exceeds this length, bucket expansion is triggered).
+ * However, setting expand_mult to a non-zero value delays bucket expansion
+ * (that would be triggered by additions to this particular bucket)
+ * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.
+ * (The multiplier is simply expand_mult+1). The whole idea of this
+ * multiplier is to reduce bucket expansions, since they are expensive, in
+ * situations where we know that a particular bucket tends to be overused.
+ * It is better to let its chain length grow to a longer yet-still-bounded
+ * value, than to do an O(n) bucket expansion too often.
+ */
+ unsigned expand_mult;
+
+} UT_hash_bucket;
+
+/* random signature used only to find hash tables in external analysis */
+#define HASH_SIGNATURE 0xa0111fe1
+#define HASH_BLOOM_SIGNATURE 0xb12220f2
+
+typedef struct UT_hash_table {
+ UT_hash_bucket *buckets;
+ unsigned num_buckets, log2_num_buckets;
+ unsigned num_items;
+ struct UT_hash_handle *tail; /* tail hh in app order, for fast append */
+ ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */
+
+ /* in an ideal situation (all buckets used equally), no bucket would have
+ * more than ceil(#items/#buckets) items. that's the ideal chain length. */
+ unsigned ideal_chain_maxlen;
+
+ /* nonideal_items is the number of items in the hash whose chain position
+ * exceeds the ideal chain maxlen. these items pay the penalty for an uneven
+ * hash distribution; reaching them in a chain traversal takes >ideal steps */
+ unsigned nonideal_items;
+
+ /* ineffective expands occur when a bucket doubling was performed, but
+ * afterward, more than half the items in the hash had nonideal chain
+ * positions. If this happens on two consecutive expansions we inhibit any
+ * further expansion, as it's not helping; this happens when the hash
+ * function isn't a good fit for the key domain. When expansion is inhibited
+ * the hash will still work, albeit no longer in constant time. */
+ unsigned ineff_expands, noexpand;
+
+ uint32_t signature; /* used only to find hash tables in external analysis */
+#ifdef HASH_BLOOM
+ uint32_t bloom_sig; /* used only to test bloom exists in external analysis */
+ uint8_t *bloom_bv;
+ char bloom_nbits;
+#endif
+
+} UT_hash_table;
+
+typedef struct UT_hash_handle {
+ struct UT_hash_table *tbl;
+ void *prev; /* prev element in app order */
+ void *next; /* next element in app order */
+ struct UT_hash_handle *hh_prev; /* previous hh in bucket order */
+ struct UT_hash_handle *hh_next; /* next hh in bucket order */
+ void *key; /* ptr to enclosing struct's key */
+ unsigned keylen; /* enclosing struct's key len */
+ unsigned hashv; /* result of hash-fcn(key) */
+} UT_hash_handle;
+
+#endif /* UTHASH_H */
--- /dev/null
+/*
+Copyright (c) 2007-2010, Troy D. Hanson http://uthash.sourceforge.net
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef UTLIST_H
+#define UTLIST_H
+
+#define UTLIST_VERSION 1.9.1
+
+/*
+ * This file contains macros to manipulate singly and doubly-linked lists.
+ *
+ * 1. LL_ macros: singly-linked lists.
+ * 2. DL_ macros: doubly-linked lists.
+ * 3. CDL_ macros: circular doubly-linked lists.
+ *
+ * To use singly-linked lists, your structure must have a "next" pointer.
+ * To use doubly-linked lists, your structure must "prev" and "next" pointers.
+ * Either way, the pointer to the head of the list must be initialized to NULL.
+ *
+ * ----------------.EXAMPLE -------------------------
+ * struct item {
+ * int id;
+ * struct item *prev, *next;
+ * }
+ *
+ * struct item *list = NULL:
+ *
+ * int main() {
+ * struct item *item;
+ * ... allocate and populate item ...
+ * DL_APPEND(list, item);
+ * }
+ * --------------------------------------------------
+ *
+ * For doubly-linked lists, the append and delete macros are O(1)
+ * For singly-linked lists, append and delete are O(n) but prepend is O(1)
+ * The sort macro is O(n log(n)) for all types of single/double/circular lists.
+ */
+
+/* These macros use decltype or the earlier __typeof GNU extension.
+ As decltype is only available in newer compilers (VS2010 or gcc 4.3+
+ when compiling c++ code), this code uses whatever method is needed
+ or, for VS2008 where neither is available, uses casting workarounds. */
+#ifdef _MSC_VER /* MS compiler */
+#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */
+#define LDECLTYPE(x) decltype(x)
+#else /* VS2008 or older (or VS2010 in C mode) */
+#define NO_DECLTYPE
+#define LDECLTYPE(x) char*
+#endif
+#else /* GNU, Sun and other compilers */
+#define LDECLTYPE(x) __typeof(x)
+#endif
+
+/* for VS2008 we use some workarounds to get around the lack of decltype,
+ * namely, we always reassign our tmp variable to the list head if we need
+ * to dereference its prev/next pointers, and save/restore the real head.*/
+#ifdef NO_DECLTYPE
+#define _SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); }
+#define _NEXT(elt,list) ((char*)((list)->next))
+#define _NEXTASGN(elt,list,to) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); }
+#define _PREV(elt,list) ((char*)((list)->prev))
+#define _PREVASGN(elt,list,to) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); }
+#define _RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; }
+#define _CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); }
+#else
+#define _SV(elt,list)
+#define _NEXT(elt,list) ((elt)->next)
+#define _NEXTASGN(elt,list,to) ((elt)->next)=(to)
+#define _PREV(elt,list) ((elt)->prev)
+#define _PREVASGN(elt,list,to) ((elt)->prev)=(to)
+#define _RS(list)
+#define _CASTASGN(a,b) (a)=(b)
+#endif
+
+/******************************************************************************
+ * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort *
+ * Unwieldy variable names used here to avoid shadowing passed-in variables. *
+ *****************************************************************************/
+#define LL_SORT(list, cmp) \
+do { \
+ LDECLTYPE(list) _ls_p; \
+ LDECLTYPE(list) _ls_q; \
+ LDECLTYPE(list) _ls_e; \
+ LDECLTYPE(list) _ls_tail; \
+ LDECLTYPE(list) _ls_oldhead; \
+ LDECLTYPE(list) _tmp; \
+ int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \
+ if (list) { \
+ _ls_insize = 1; \
+ _ls_looping = 1; \
+ while (_ls_looping) { \
+ _CASTASGN(_ls_p,list); \
+ _CASTASGN(_ls_oldhead,list); \
+ list = NULL; \
+ _ls_tail = NULL; \
+ _ls_nmerges = 0; \
+ while (_ls_p) { \
+ _ls_nmerges++; \
+ _ls_q = _ls_p; \
+ _ls_psize = 0; \
+ for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \
+ _ls_psize++; \
+ _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); \
+ if (!_ls_q) break; \
+ } \
+ _ls_qsize = _ls_insize; \
+ while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \
+ if (_ls_psize == 0) { \
+ _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \
+ } else if (_ls_qsize == 0 || !_ls_q) { \
+ _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \
+ } else if (cmp(_ls_p,_ls_q) <= 0) { \
+ _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \
+ } else { \
+ _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \
+ } \
+ if (_ls_tail) { \
+ _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list); \
+ } else { \
+ _CASTASGN(list,_ls_e); \
+ } \
+ _ls_tail = _ls_e; \
+ } \
+ _ls_p = _ls_q; \
+ } \
+ _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL); _RS(list); \
+ if (_ls_nmerges <= 1) { \
+ _ls_looping=0; \
+ } \
+ _ls_insize *= 2; \
+ } \
+ } else _tmp=NULL; /* quiet gcc unused variable warning */ \
+} while (0)
+
+#define DL_SORT(list, cmp) \
+do { \
+ LDECLTYPE(list) _ls_p; \
+ LDECLTYPE(list) _ls_q; \
+ LDECLTYPE(list) _ls_e; \
+ LDECLTYPE(list) _ls_tail; \
+ LDECLTYPE(list) _ls_oldhead; \
+ LDECLTYPE(list) _tmp; \
+ int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \
+ if (list) { \
+ _ls_insize = 1; \
+ _ls_looping = 1; \
+ while (_ls_looping) { \
+ _CASTASGN(_ls_p,list); \
+ _CASTASGN(_ls_oldhead,list); \
+ list = NULL; \
+ _ls_tail = NULL; \
+ _ls_nmerges = 0; \
+ while (_ls_p) { \
+ _ls_nmerges++; \
+ _ls_q = _ls_p; \
+ _ls_psize = 0; \
+ for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \
+ _ls_psize++; \
+ _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); \
+ if (!_ls_q) break; \
+ } \
+ _ls_qsize = _ls_insize; \
+ while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \
+ if (_ls_psize == 0) { \
+ _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \
+ } else if (_ls_qsize == 0 || !_ls_q) { \
+ _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \
+ } else if (cmp(_ls_p,_ls_q) <= 0) { \
+ _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \
+ } else { \
+ _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \
+ } \
+ if (_ls_tail) { \
+ _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list); \
+ } else { \
+ _CASTASGN(list,_ls_e); \
+ } \
+ _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail); _RS(list); \
+ _ls_tail = _ls_e; \
+ } \
+ _ls_p = _ls_q; \
+ } \
+ _CASTASGN(list->prev, _ls_tail); \
+ _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL); _RS(list); \
+ if (_ls_nmerges <= 1) { \
+ _ls_looping=0; \
+ } \
+ _ls_insize *= 2; \
+ } \
+ } else _tmp=NULL; /* quiet gcc unused variable warning */ \
+} while (0)
+
+#define CDL_SORT(list, cmp) \
+do { \
+ LDECLTYPE(list) _ls_p; \
+ LDECLTYPE(list) _ls_q; \
+ LDECLTYPE(list) _ls_e; \
+ LDECLTYPE(list) _ls_tail; \
+ LDECLTYPE(list) _ls_oldhead; \
+ LDECLTYPE(list) _tmp; \
+ LDECLTYPE(list) _tmp2; \
+ int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \
+ if (list) { \
+ _ls_insize = 1; \
+ _ls_looping = 1; \
+ while (_ls_looping) { \
+ _CASTASGN(_ls_p,list); \
+ _CASTASGN(_ls_oldhead,list); \
+ list = NULL; \
+ _ls_tail = NULL; \
+ _ls_nmerges = 0; \
+ while (_ls_p) { \
+ _ls_nmerges++; \
+ _ls_q = _ls_p; \
+ _ls_psize = 0; \
+ for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \
+ _ls_psize++; \
+ _SV(_ls_q,list); \
+ if (_NEXT(_ls_q,list) == _ls_oldhead) { \
+ _ls_q = NULL; \
+ } else { \
+ _ls_q = _NEXT(_ls_q,list); \
+ } \
+ _RS(list); \
+ if (!_ls_q) break; \
+ } \
+ _ls_qsize = _ls_insize; \
+ while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \
+ if (_ls_psize == 0) { \
+ _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \
+ if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \
+ } else if (_ls_qsize == 0 || !_ls_q) { \
+ _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \
+ if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \
+ } else if (cmp(_ls_p,_ls_q) <= 0) { \
+ _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \
+ if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \
+ } else { \
+ _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \
+ if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \
+ } \
+ if (_ls_tail) { \
+ _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list); \
+ } else { \
+ _CASTASGN(list,_ls_e); \
+ } \
+ _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail); _RS(list); \
+ _ls_tail = _ls_e; \
+ } \
+ _ls_p = _ls_q; \
+ } \
+ _CASTASGN(list->prev,_ls_tail); \
+ _CASTASGN(_tmp2,list); \
+ _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_tmp2); _RS(list); \
+ if (_ls_nmerges <= 1) { \
+ _ls_looping=0; \
+ } \
+ _ls_insize *= 2; \
+ } \
+ } else _tmp=NULL; /* quiet gcc unused variable warning */ \
+} while (0)
+
+/******************************************************************************
+ * singly linked list macros (non-circular) *
+ *****************************************************************************/
+#define LL_PREPEND(head,add) \
+do { \
+ (add)->next = head; \
+ head = add; \
+} while (0)
+
+#define LL_APPEND(head,add) \
+do { \
+ LDECLTYPE(head) _tmp; \
+ (add)->next=NULL; \
+ if (head) { \
+ _tmp = head; \
+ while (_tmp->next) { _tmp = _tmp->next; } \
+ _tmp->next=(add); \
+ } else { \
+ (head)=(add); \
+ } \
+} while (0)
+
+#define LL_DELETE(head,del) \
+do { \
+ LDECLTYPE(head) _tmp; \
+ if ((head) == (del)) { \
+ (head)=(head)->next; \
+ } else { \
+ _tmp = head; \
+ while (_tmp->next && (_tmp->next != (del))) { \
+ _tmp = _tmp->next; \
+ } \
+ if (_tmp->next) { \
+ _tmp->next = ((del)->next); \
+ } \
+ } \
+} while (0)
+
+/* Here are VS2008 replacements for LL_APPEND and LL_DELETE */
+#define LL_APPEND_VS2008(head,add) \
+do { \
+ if (head) { \
+ (add)->next = head; /* use add->next as a temp variable */ \
+ while ((add)->next->next) { (add)->next = (add)->next->next; } \
+ (add)->next->next=(add); \
+ } else { \
+ (head)=(add); \
+ } \
+ (add)->next=NULL; \
+} while (0)
+
+#define LL_DELETE_VS2008(head,del) \
+do { \
+ if ((head) == (del)) { \
+ (head)=(head)->next; \
+ } else { \
+ char *_tmp = (char*)(head); \
+ while (head->next && (head->next != (del))) { \
+ head = head->next; \
+ } \
+ if (head->next) { \
+ head->next = ((del)->next); \
+ } \
+ { \
+ char **_head_alias = (char**)&(head); \
+ *_head_alias = _tmp; \
+ } \
+ } \
+} while (0)
+#ifdef NO_DECLTYPE
+#undef LL_APPEND
+#define LL_APPEND LL_APPEND_VS2008
+#undef LL_DELETE
+#define LL_DELETE LL_DELETE_VS2008
+#endif
+/* end VS2008 replacements */
+
+#define LL_FOREACH(head,el) \
+ for(el=head;el;el=el->next)
+
+#define LL_FOREACH_SAFE(head,el,tmp) \
+ for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp)
+
+#define LL_SEARCH_SCALAR(head,out,field,val) \
+do { \
+ LL_FOREACH(head,out) { \
+ if ((out)->field == (val)) break; \
+ } \
+} while(0)
+
+#define LL_SEARCH(head,out,elt,cmp) \
+do { \
+ LL_FOREACH(head,out) { \
+ if ((cmp(out,elt))==0) break; \
+ } \
+} while(0)
+
+/******************************************************************************
+ * doubly linked list macros (non-circular) *
+ *****************************************************************************/
+#define DL_PREPEND(head,add) \
+do { \
+ (add)->next = head; \
+ if (head) { \
+ (add)->prev = (head)->prev; \
+ (head)->prev = (add); \
+ } else { \
+ (add)->prev = (add); \
+ } \
+ (head) = (add); \
+} while (0)
+
+#define DL_APPEND(head,add) \
+do { \
+ if (head) { \
+ (add)->prev = (head)->prev; \
+ (head)->prev->next = (add); \
+ (head)->prev = (add); \
+ (add)->next = NULL; \
+ } else { \
+ (head)=(add); \
+ (head)->prev = (head); \
+ (head)->next = NULL; \
+ } \
+} while (0);
+
+#define DL_DELETE(head,del) \
+do { \
+ if ((del)->prev == (del)) { \
+ (head)=NULL; \
+ } else if ((del)==(head)) { \
+ (del)->next->prev = (del)->prev; \
+ (head) = (del)->next; \
+ } else { \
+ (del)->prev->next = (del)->next; \
+ if ((del)->next) { \
+ (del)->next->prev = (del)->prev; \
+ } else { \
+ (head)->prev = (del)->prev; \
+ } \
+ } \
+} while (0);
+
+
+#define DL_FOREACH(head,el) \
+ for(el=head;el;el=el->next)
+
+/* this version is safe for deleting the elements during iteration */
+#define DL_FOREACH_SAFE(head,el,tmp) \
+ for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp)
+
+/* these are identical to their singly-linked list counterparts */
+#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR
+#define DL_SEARCH LL_SEARCH
+
+/******************************************************************************
+ * circular doubly linked list macros *
+ *****************************************************************************/
+#define CDL_PREPEND(head,add) \
+do { \
+ if (head) { \
+ (add)->prev = (head)->prev; \
+ (add)->next = (head); \
+ (head)->prev = (add); \
+ (add)->prev->next = (add); \
+ } else { \
+ (add)->prev = (add); \
+ (add)->next = (add); \
+ } \
+(head)=(add); \
+} while (0)
+
+#define CDL_DELETE(head,del) \
+do { \
+ if ( ((head)==(del)) && ((head)->next == (head))) { \
+ (head) = 0L; \
+ } else { \
+ (del)->next->prev = (del)->prev; \
+ (del)->prev->next = (del)->next; \
+ if ((del) == (head)) (head)=(del)->next; \
+ } \
+} while (0);
+
+#define CDL_FOREACH(head,el) \
+ for(el=head;el;el=(el->next==head ? 0L : el->next))
+
+#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \
+ for((el)=(head), ((tmp1)=(head)?((head)->prev):NULL); \
+ (el) && ((tmp2)=(el)->next, 1); \
+ ((el) = (((el)==(tmp1)) ? 0L : (tmp2))))
+
+#define CDL_SEARCH_SCALAR(head,out,field,val) \
+do { \
+ CDL_FOREACH(head,out) { \
+ if ((out)->field == (val)) break; \
+ } \
+} while(0)
+
+#define CDL_SEARCH(head,out,elt,cmp) \
+do { \
+ CDL_FOREACH(head,out) { \
+ if ((cmp(out,elt))==0) break; \
+ } \
+} while(0)
+
+#endif /* UTLIST_H */
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#import <Availability.h>
+
+/**
+ @file
+ cocos2d (cc) configuration file
+*/
+
+/** @def CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL
+ If enabled, the texture coordinates will be calculated by using this formula:
+ - texCoord.left = (rect.origin.x*2+1) / (texture.wide*2);
+ - texCoord.right = texCoord.left + (rect.size.width*2-2)/(texture.wide*2);
+
+ The same for bottom and top.
+
+ This formula prevents artifacts by using 99% of the texture.
+ The "correct" way to prevent artifacts is by using the spritesheet-artifact-fixer.py or a similar tool.
+
+ Affected nodes:
+ - CCSprite / CCSpriteBatchNode and subclasses: CCBitmapFontAtlas, CCTMXTiledMap
+ - CCLabelAtlas
+ - CCQuadParticleSystem
+ - CCTileMap
+
+ To enabled set it to 1. Disabled by default.
+
+ @since v0.99.5
+ */
+#define CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL 0
+
+
+/** @def CC_FONT_LABEL_SUPPORT
+ If enabled, FontLabel will be used to render .ttf files.
+ If the .ttf file is not found, then it will use the standard UIFont class
+ If disabled, the standard UIFont class will be used.
+
+ To disable set it to 0. Enabled by default.
+
+ Only valid for cocos2d-ios. Not supported on cocos2d-mac
+ */
+#define CC_FONT_LABEL_SUPPORT 1
+
+/** @def CC_DIRECTOR_FAST_FPS
+ If enabled, then the FPS will be drawn using CCLabelAtlas (fast rendering).
+ You will need to add the fps_images.png to your project.
+ If disabled, the FPS will be rendered using CCLabel (slow rendering)
+
+ To enable set it to a value different than 0. Enabled by default.
+ */
+#define CC_DIRECTOR_FAST_FPS 1
+
+/** @def CC_DIRECTOR_FPS_INTERVAL
+ Senconds between FPS updates.
+ 0.5 seconds, means that the FPS number will be updated every 0.5 seconds.
+ Having a bigger number means a more reliable FPS
+
+ Default value: 0.1f
+ */
+#define CC_DIRECTOR_FPS_INTERVAL (0.1f)
+
+/** @def CC_DIRECTOR_DISPATCH_FAST_EVENTS
+ If enabled, and only when it is used with CCFastDirector, the main loop will wait 0.04 seconds to
+ dispatch all the events, even if there are not events to dispatch.
+ If your game uses lot's of events (eg: touches) it might be a good idea to enable this feature.
+ Otherwise, it is safe to leave it disabled.
+
+ To enable set it to 1. Disabled by default.
+
+ @warning This feature is experimental
+ */
+#define CC_DIRECTOR_DISPATCH_FAST_EVENTS 0
+
+/** @def CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
+ If enabled, cocos2d-mac will run on the Display Link thread. If disabled cocos2d-mac will run in its own thread.
+
+ If enabled, the images will be drawn at the "correct" time, but the events might not be very responsive.
+ If disabled, some frames might be skipped, but the events will be dispatched as they arrived.
+
+ To enable set it to a 1, to disable it set to 0. Enabled by default.
+
+ Only valid for cocos2d-mac. Not supported on cocos2d-ios.
+
+ */
+#define CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD 1
+
+/** @def CC_COCOSNODE_RENDER_SUBPIXEL
+ If enabled, the CCNode objects (CCSprite, CCLabel,etc) will be able to render in subpixels.
+ If disabled, integer pixels will be used.
+
+ To enable set it to 1. Enabled by default.
+ */
+#define CC_COCOSNODE_RENDER_SUBPIXEL 1
+
+/** @def CC_SPRITEBATCHNODE_RENDER_SUBPIXEL
+ If enabled, the CCSprite objects rendered with CCSpriteBatchNode will be able to render in subpixels.
+ If disabled, integer pixels will be used.
+
+ To enable set it to 1. Enabled by default.
+ */
+#define CC_SPRITEBATCHNODE_RENDER_SUBPIXEL 1
+
+
+#if defined(__ARM_NEON__) || TARGET_IPHONE_SIMULATOR || defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+/** @def CC_USES_VBO
+ If enabled, batch nodes (texture atlas and particle system) will use VBO instead of vertex list (VBO is recommended by Apple)
+
+ To enable set it to 1.
+ Enabled by default on iPhone with ARMv7 processors, iPhone Simulator and Mac
+ Disabled by default on iPhone with ARMv6 processors.
+
+ @since v0.99.5
+ */
+#define CC_USES_VBO 1
+#else
+#define CC_USES_VBO 0
+#endif
+
+/** @def CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
+ If enabled, CCNode will transform the nodes using a cached Affine matrix.
+ If disabled, the node will be transformed using glTranslate,glRotate,glScale.
+ Using the affine matrix only requires 2 GL calls.
+ Using the translate/rotate/scale requires 5 GL calls.
+ But computing the Affine matrix is relative expensive.
+ But according to performance tests, Affine matrix performs better.
+ This parameter doesn't affect SpriteSheet nodes.
+
+ To enable set it to a value different than 0. Enabled by default.
+
+ */
+#define CC_NODE_TRANSFORM_USING_AFFINE_MATRIX 1
+
+/** @def CC_OPTIMIZE_BLEND_FUNC_FOR_PREMULTIPLIED_ALPHA
+ If most of your imamges have pre-multiplied alpha, set it to 1 (if you are going to use .PNG/.JPG file images).
+ Only set to 0 if ALL your images by-pass Apple UIImage loading system (eg: if you use libpng or PVR images)
+
+ To enable set it to a value different than 0. Enabled by default.
+
+ @since v0.99.5
+ */
+#define CC_OPTIMIZE_BLEND_FUNC_FOR_PREMULTIPLIED_ALPHA 1
+
+/** @def CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP
+ Use GL_TRIANGLE_STRIP instead of GL_TRIANGLES when rendering the texture atlas.
+ It seems it is the recommend way, but it is much slower, so, enable it at your own risk
+
+ To enable set it to a value different than 0. Disabled by default.
+
+ */
+#define CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP 0
+
+
+/** @def CC_TEXTURE_NPOT_SUPPORT
+ If enabled, NPOT textures will be used where available. Only 3rd gen (and newer) devices support NPOT textures.
+ NPOT textures have the following limitations:
+ - They can't have mipmaps
+ - They only accept GL_CLAMP_TO_EDGE in GL_TEXTURE_WRAP_{S,T}
+
+ To enable set it to a value different than 0. Disabled by default.
+
+ @since v0.99.2
+ */
+#define CC_TEXTURE_NPOT_SUPPORT 0
+
+/** @def CC_RETINA_DISPLAY_SUPPORT
+ If enabled, cocos2d supports retina display.
+ For performance reasons, it's recommended disable it in games without retina display support, like iPad only games.
+
+ To enable set it to 1. Use 0 to disable it. Enabled by default.
+
+ @since v0.99.5
+ */
+#define CC_RETINA_DISPLAY_SUPPORT 1
+
+/** @def CC_RETINA_DISPLAY_FILENAME_SUFFIX
+ It's the suffix that will be appended to the files in order to load "retina display" images.
+
+ On an iPhone4 with Retina Display support enabled, the file @"sprite-hd.png" will be loaded instead of @"sprite.png".
+ If the file doesn't exist it will use the non-retina display image.
+
+ Platforms: Only used on Retina Display devices like iPhone 4.
+
+ @since v0.99.5
+ */
+#define CC_RETINA_DISPLAY_FILENAME_SUFFIX @"-hd"
+
+/** @def CC_USE_LA88_LABELS_ON_NEON_ARCH
+ If enabled, it will use LA88 (16-bit textures) on Neon devices for CCLabelTTF objects.
+ If it is disabled, or if it is used on another architecture it will use A8 (8-bit textures).
+ On Neon devices, LA88 textures are 6% faster than A8 textures, but then will consume 2x memory.
+
+ This feature is disabled by default.
+
+ Platforms: Only used on ARM Neon architectures like iPhone 3GS or newer and iPad.
+
+ @since v0.99.5
+ */
+#define CC_USE_LA88_LABELS_ON_NEON_ARCH 0
+
+/** @def CC_SPRITE_DEBUG_DRAW
+ If enabled, all subclasses of CCSprite will draw a bounding box
+ Useful for debugging purposes only. It is recommened to leave it disabled.
+
+ To enable set it to a value different than 0. Disabled by default.
+ */
+#define CC_SPRITE_DEBUG_DRAW 0
+
+/** @def CC_SPRITEBATCHNODE_DEBUG_DRAW
+ If enabled, all subclasses of CCSprite that are rendered using an CCSpriteBatchNode draw a bounding box.
+ Useful for debugging purposes only. It is recommened to leave it disabled.
+
+ To enable set it to a value different than 0. Disabled by default.
+ */
+#define CC_SPRITEBATCHNODE_DEBUG_DRAW 0
+
+/** @def CC_LABELBMFONT_DEBUG_DRAW
+ If enabled, all subclasses of CCLabelBMFont will draw a bounding box
+ Useful for debugging purposes only. It is recommened to leave it disabled.
+
+ To enable set it to a value different than 0. Disabled by default.
+ */
+#define CC_LABELBMFONT_DEBUG_DRAW 0
+
+/** @def CC_LABELBMFONT_DEBUG_DRAW
+ If enabled, all subclasses of CCLabeltAtlas will draw a bounding box
+ Useful for debugging purposes only. It is recommened to leave it disabled.
+
+ To enable set it to a value different than 0. Disabled by default.
+ */
+#define CC_LABELATLAS_DEBUG_DRAW 0
+
+/** @def CC_ENABLE_PROFILERS
+ If enabled, will activate various profilers withing cocos2d. This statistical data will be output to the console
+ once per second showing average time (in milliseconds) required to execute the specific routine(s).
+ Useful for debugging purposes only. It is recommened to leave it disabled.
+
+ To enable set it to a value different than 0. Disabled by default.
+ */
+#define CC_ENABLE_PROFILERS 0
+
+//
+// DON'T edit this macro.
+//
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#if CC_RETINA_DISPLAY_SUPPORT
+#define CC_IS_RETINA_DISPLAY_SUPPORTED 1
+#else
+#define CC_IS_RETINA_DISPLAY_SUPPORTED 0
+#endif
+
+#elif __MAC_OS_X_VERSION_MAX_ALLOWED
+
+#define CC_IS_RETINA_DISPLAY_SUPPORTED 0
+
+#endif
+
+
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#import <math.h>
+#import "ccConfig.h"
+
+#import <Foundation/Foundation.h>
+#import <Availability.h>
+
+/**
+ @file
+ cocos2d helper macros
+ */
+
+/*
+ * if COCOS2D_DEBUG is not defined, or if it is 0 then
+ * all CCLOGXXX macros will be disabled
+ *
+ * if COCOS2D_DEBUG==1 then:
+ * CCLOG() will be enabled
+ * CCLOGERROR() will be enabled
+ * CCLOGINFO() will be disabled
+ *
+ * if COCOS2D_DEBUG==2 or higher then:
+ * CCLOG() will be enabled
+ * CCLOGERROR() will be enabled
+ * CCLOGINFO() will be enabled
+ */
+#if !defined(COCOS2D_DEBUG) || COCOS2D_DEBUG == 0
+#define CCLOG(...) do {} while (0)
+#define CCLOGINFO(...) do {} while (0)
+#define CCLOGERROR(...) do {} while (0)
+
+#elif COCOS2D_DEBUG == 1
+#define CCLOG(...) NSLog(__VA_ARGS__)
+#define CCLOGERROR(...) NSLog(__VA_ARGS__)
+#define CCLOGINFO(...) do {} while (0)
+
+#elif COCOS2D_DEBUG > 1
+#define CCLOG(...) NSLog(__VA_ARGS__)
+#define CCLOGERROR(...) NSLog(__VA_ARGS__)
+#define CCLOGINFO(...) NSLog(__VA_ARGS__)
+#endif // COCOS2D_DEBUG
+
+/** @def CC_SWAP
+simple macro that swaps 2 variables
+*/
+#define CC_SWAP( x, y ) \
+({ __typeof__(x) temp = (x); \
+ x = y; y = temp; \
+})
+
+
+/** @def CCRANDOM_MINUS1_1
+ returns a random float between -1 and 1
+ */
+#define CCRANDOM_MINUS1_1() ((random() / (float)0x3fffffff )-1.0f)
+
+/** @def CCRANDOM_0_1
+ returns a random float between 0 and 1
+ */
+#define CCRANDOM_0_1() ((random() / (float)0x7fffffff ))
+
+/** @def CC_DEGREES_TO_RADIANS
+ converts degrees to radians
+ */
+#define CC_DEGREES_TO_RADIANS(__ANGLE__) ((__ANGLE__) * 0.01745329252f) // PI / 180
+
+/** @def CC_RADIANS_TO_DEGREES
+ converts radians to degrees
+ */
+#define CC_RADIANS_TO_DEGREES(__ANGLE__) ((__ANGLE__) * 57.29577951f) // PI * 180
+
+/** @def CC_BLEND_SRC
+default gl blend src function. Compatible with premultiplied alpha images.
+*/
+#if CC_OPTIMIZE_BLEND_FUNC_FOR_PREMULTIPLIED_ALPHA
+#define CC_BLEND_SRC GL_ONE
+#define CC_BLEND_DST GL_ONE_MINUS_SRC_ALPHA
+#else
+#define CC_BLEND_SRC GL_SRC_ALPHA
+#define CC_BLEND_DST GL_ONE_MINUS_SRC_ALPHA
+#endif // ! CC_OPTIMIZE_BLEND_FUNC_FOR_PREMULTIPLIED_ALPHA
+
+/** @def CC_ENABLE_DEFAULT_GL_STATES
+ GL states that are enabled:
+ - GL_TEXTURE_2D
+ - GL_VERTEX_ARRAY
+ - GL_TEXTURE_COORD_ARRAY
+ - GL_COLOR_ARRAY
+ */
+#define CC_ENABLE_DEFAULT_GL_STATES() { \
+ glEnableClientState(GL_VERTEX_ARRAY); \
+ glEnableClientState(GL_COLOR_ARRAY); \
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY); \
+ glEnable(GL_TEXTURE_2D); \
+}
+
+/** @def CC_DISABLE_DEFAULT_GL_STATES
+ Disable default GL states:
+ - GL_TEXTURE_2D
+ - GL_VERTEX_ARRAY
+ - GL_TEXTURE_COORD_ARRAY
+ - GL_COLOR_ARRAY
+ */
+#define CC_DISABLE_DEFAULT_GL_STATES() { \
+ glDisable(GL_TEXTURE_2D); \
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY); \
+ glDisableClientState(GL_COLOR_ARRAY); \
+ glDisableClientState(GL_VERTEX_ARRAY); \
+}
+
+/** @def CC_DIRECTOR_INIT
+ - Initializes an EAGLView with 0-bit depth format, and RGB565 render buffer.
+ - The EAGLView view will have multiple touches disabled.
+ - It will create a UIWindow and it will assign it the 'window' variable. 'window' must be declared before calling this marcro.
+ - It will parent the EAGLView to the created window
+ - If the firmware >= 3.1 it will create a Display Link Director. Else it will create an NSTimer director.
+ - It will try to run at 60 FPS.
+ - The FPS won't be displayed.
+ - The orientation will be portrait.
+ - It will connect the director with the EAGLView.
+
+ IMPORTANT: If you want to use another type of render buffer (eg: RGBA8)
+ or if you want to use a 16-bit or 24-bit depth buffer, you should NOT
+ use this macro. Instead, you should create the EAGLView manually.
+
+ @since v0.99.4
+ */
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+
+#define CC_DIRECTOR_INIT() \
+do { \
+ window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; \
+ if( ! [CCDirector setDirectorType:kCCDirectorTypeDisplayLink] ) \
+ [CCDirector setDirectorType:kCCDirectorTypeNSTimer]; \
+ CCDirector *__director = [CCDirector sharedDirector]; \
+ [__director setDeviceOrientation:kCCDeviceOrientationPortrait]; \
+ [__director setDisplayFPS:NO]; \
+ [__director setAnimationInterval:1.0/60]; \
+ EAGLView *__glView = [EAGLView viewWithFrame:[window bounds] \
+ pixelFormat:kEAGLColorFormatRGB565 \
+ depthFormat:0 /* GL_DEPTH_COMPONENT24_OES */ \
+ preserveBackbuffer:NO \
+ sharegroup:nil \
+ multiSampling:NO \
+ numberOfSamples:0 \
+ ]; \
+ [__director setOpenGLView:__glView]; \
+ [window addSubview:__glView]; \
+ [window makeKeyAndVisible]; \
+} while(0)
+
+
+#elif __MAC_OS_X_VERSION_MAX_ALLOWED
+
+#import "Platforms/Mac/MacWindow.h"
+
+#define CC_DIRECTOR_INIT(__WINSIZE__) \
+do { \
+ NSRect frameRect = NSMakeRect(0, 0, (__WINSIZE__).width, (__WINSIZE__).height); \
+ self.window = [[MacWindow alloc] initWithFrame:frameRect fullscreen:NO]; \
+ self.glView = [[MacGLView alloc] initWithFrame:frameRect shareContext:nil]; \
+ [self.window setContentView:self.glView]; \
+ CCDirector *__director = [CCDirector sharedDirector]; \
+ [__director setDisplayFPS:NO]; \
+ [__director setOpenGLView:self.glView]; \
+ [(CCDirectorMac*)__director setOriginalWinSize:__WINSIZE__]; \
+ [self.window makeMainWindow]; \
+ [self.window makeKeyAndOrderFront:self]; \
+} while(0)
+
+#endif
+
+
+ /** @def CC_DIRECTOR_END
+ Stops and removes the director from memory.
+ Removes the EAGLView from its parent
+
+ @since v0.99.4
+ */
+#define CC_DIRECTOR_END() \
+do { \
+ CCDirector *__director = [CCDirector sharedDirector]; \
+ CC_GLVIEW *__view = [__director openGLView]; \
+ [__view removeFromSuperview]; \
+ [__director end]; \
+} while(0)
+
+
+#if CC_IS_RETINA_DISPLAY_SUPPORTED
+
+/****************************/
+/** RETINA DISPLAY ENABLED **/
+/****************************/
+
+/** @def CC_CONTENT_SCALE_FACTOR
+ On Mac it returns 1;
+ On iPhone it returns 2 if RetinaDisplay is On. Otherwise it returns 1
+ */
+#import "Platforms/iOS/CCDirectorIOS.h"
+#define CC_CONTENT_SCALE_FACTOR() __ccContentScaleFactor
+
+
+/** @def CC_RECT_PIXELS_TO_POINTS
+ Converts a rect in pixels to points
+ */
+#define CC_RECT_PIXELS_TO_POINTS(__pixels__) \
+ CGRectMake( (__pixels__).origin.x / CC_CONTENT_SCALE_FACTOR(), (__pixels__).origin.y / CC_CONTENT_SCALE_FACTOR(), \
+ (__pixels__).size.width / CC_CONTENT_SCALE_FACTOR(), (__pixels__).size.height / CC_CONTENT_SCALE_FACTOR() )
+
+/** @def CC_RECT_POINTS_TO_PIXELS
+ Converts a rect in points to pixels
+ */
+#define CC_RECT_POINTS_TO_PIXELS(__points__) \
+ CGRectMake( (__points__).origin.x * CC_CONTENT_SCALE_FACTOR(), (__points__).origin.y * CC_CONTENT_SCALE_FACTOR(), \
+ (__points__).size.width * CC_CONTENT_SCALE_FACTOR(), (__points__).size.height * CC_CONTENT_SCALE_FACTOR() )
+
+#else // retina disabled
+
+/*****************************/
+/** RETINA DISPLAY DISABLED **/
+/*****************************/
+
+#define CC_CONTENT_SCALE_FACTOR() 1
+#define CC_RECT_PIXELS_TO_POINTS(__pixels__) __pixels__
+#define CC_RECT_POINTS_TO_PIXELS(__points__) __points__
+
+#endif // CC_IS_RETINA_DISPLAY_SUPPORTED
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+/**
+ @file
+ cocos2d (cc) types
+*/
+
+#import <Availability.h>
+#import <Foundation/Foundation.h>
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import <CoreGraphics/CGGeometry.h> // CGPoint
+#endif
+
+#import "Platforms/CCGL.h"
+
+/** RGB color composed of bytes 3 bytes
+@since v0.8
+ */
+typedef struct _ccColor3B
+{
+ GLubyte r;
+ GLubyte g;
+ GLubyte b;
+} ccColor3B;
+
+//! helper macro that creates an ccColor3B type
+static inline ccColor3B
+ccc3(const GLubyte r, const GLubyte g, const GLubyte b)
+{
+ ccColor3B c = {r, g, b};
+ return c;
+}
+//ccColor3B predefined colors
+//! White color (255,255,255)
+static const ccColor3B ccWHITE = {255,255,255};
+//! Yellow color (255,255,0)
+static const ccColor3B ccYELLOW = {255,255,0};
+//! Blue color (0,0,255)
+static const ccColor3B ccBLUE = {0,0,255};
+//! Green Color (0,255,0)
+static const ccColor3B ccGREEN = {0,255,0};
+//! Red Color (255,0,0,)
+static const ccColor3B ccRED = {255,0,0};
+//! Magenta Color (255,0,255)
+static const ccColor3B ccMAGENTA = {255,0,255};
+//! Black Color (0,0,0)
+static const ccColor3B ccBLACK = {0,0,0};
+//! Orange Color (255,127,0)
+static const ccColor3B ccORANGE = {255,127,0};
+//! Gray Color (166,166,166)
+static const ccColor3B ccGRAY = {166,166,166};
+
+/** RGBA color composed of 4 bytes
+@since v0.8
+*/
+typedef struct _ccColor4B
+{
+ GLubyte r;
+ GLubyte g;
+ GLubyte b;
+ GLubyte a;
+} ccColor4B;
+//! helper macro that creates an ccColor4B type
+static inline ccColor4B
+ccc4(const GLubyte r, const GLubyte g, const GLubyte b, const GLubyte o)
+{
+ ccColor4B c = {r, g, b, o};
+ return c;
+}
+
+
+/** RGBA color composed of 4 floats
+@since v0.8
+*/
+typedef struct _ccColor4F {
+ GLfloat r;
+ GLfloat g;
+ GLfloat b;
+ GLfloat a;
+} ccColor4F;
+
+/** Returns a ccColor4F from a ccColor3B. Alpha will be 1.
+ @since v0.99.1
+ */
+static inline ccColor4F ccc4FFromccc3B(ccColor3B c)
+{
+ return (ccColor4F){c.r/255.f, c.g/255.f, c.b/255.f, 1.f};
+}
+
+/** Returns a ccColor4F from a ccColor4B.
+ @since v0.99.1
+ */
+static inline ccColor4F ccc4FFromccc4B(ccColor4B c)
+{
+ return (ccColor4F){c.r/255.f, c.g/255.f, c.b/255.f, c.a/255.f};
+}
+
+/** returns YES if both ccColor4F are equal. Otherwise it returns NO.
+ @since v0.99.1
+ */
+static inline BOOL ccc4FEqual(ccColor4F a, ccColor4F b)
+{
+ return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a;
+}
+
+/** A vertex composed of 2 GLfloats: x, y
+ @since v0.8
+ */
+typedef struct _ccVertex2F
+{
+ GLfloat x;
+ GLfloat y;
+} ccVertex2F;
+
+/** A vertex composed of 2 floats: x, y
+ @since v0.8
+ */
+typedef struct _ccVertex3F
+{
+ GLfloat x;
+ GLfloat y;
+ GLfloat z;
+} ccVertex3F;
+
+/** A texcoord composed of 2 floats: u, y
+ @since v0.8
+ */
+typedef struct _ccTex2F {
+ GLfloat u;
+ GLfloat v;
+} ccTex2F;
+
+
+//! Point Sprite component
+typedef struct _ccPointSprite
+{
+ ccVertex2F pos; // 8 bytes
+ ccColor4B color; // 4 bytes
+ GLfloat size; // 4 bytes
+} ccPointSprite;
+
+//! A 2D Quad. 4 * 2 floats
+typedef struct _ccQuad2 {
+ ccVertex2F tl;
+ ccVertex2F tr;
+ ccVertex2F bl;
+ ccVertex2F br;
+} ccQuad2;
+
+
+//! A 3D Quad. 4 * 3 floats
+typedef struct _ccQuad3 {
+ ccVertex3F bl;
+ ccVertex3F br;
+ ccVertex3F tl;
+ ccVertex3F tr;
+} ccQuad3;
+
+//! A 2D grid size
+typedef struct _ccGridSize
+{
+ NSInteger x;
+ NSInteger y;
+} ccGridSize;
+
+//! helper function to create a ccGridSize
+static inline ccGridSize
+ccg(const NSInteger x, const NSInteger y)
+{
+ ccGridSize v = {x, y};
+ return v;
+}
+
+//! a Point with a vertex point, a tex coord point and a color 4B
+typedef struct _ccV2F_C4B_T2F
+{
+ //! vertices (2F)
+ ccVertex2F vertices;
+ //! colors (4B)
+ ccColor4B colors;
+ //! tex coords (2F)
+ ccTex2F texCoords;
+} ccV2F_C4B_T2F;
+
+//! a Point with a vertex point, a tex coord point and a color 4F
+typedef struct _ccV2F_C4F_T2F
+{
+ //! vertices (2F)
+ ccVertex2F vertices;
+ //! colors (4F)
+ ccColor4F colors;
+ //! tex coords (2F)
+ ccTex2F texCoords;
+} ccV2F_C4F_T2F;
+
+//! a Point with a vertex point, a tex coord point and a color 4B
+typedef struct _ccV3F_C4B_T2F
+{
+ //! vertices (3F)
+ ccVertex3F vertices; // 12 bytes
+// char __padding__[4];
+
+ //! colors (4B)
+ ccColor4B colors; // 4 bytes
+// char __padding2__[4];
+
+ // tex coords (2F)
+ ccTex2F texCoords; // 8 byts
+} ccV3F_C4B_T2F;
+
+//! 4 ccVertex2FTex2FColor4B Quad
+typedef struct _ccV2F_C4B_T2F_Quad
+{
+ //! bottom left
+ ccV2F_C4B_T2F bl;
+ //! bottom right
+ ccV2F_C4B_T2F br;
+ //! top left
+ ccV2F_C4B_T2F tl;
+ //! top right
+ ccV2F_C4B_T2F tr;
+} ccV2F_C4B_T2F_Quad;
+
+//! 4 ccVertex3FTex2FColor4B
+typedef struct _ccV3F_C4B_T2F_Quad
+{
+ //! top left
+ ccV3F_C4B_T2F tl;
+ //! bottom left
+ ccV3F_C4B_T2F bl;
+ //! top right
+ ccV3F_C4B_T2F tr;
+ //! bottom right
+ ccV3F_C4B_T2F br;
+} ccV3F_C4B_T2F_Quad;
+
+//! 4 ccVertex2FTex2FColor4F Quad
+typedef struct _ccV2F_C4F_T2F_Quad
+{
+ //! bottom left
+ ccV2F_C4F_T2F bl;
+ //! bottom right
+ ccV2F_C4F_T2F br;
+ //! top left
+ ccV2F_C4F_T2F tl;
+ //! top right
+ ccV2F_C4F_T2F tr;
+} ccV2F_C4F_T2F_Quad;
+
+//! Blend Function used for textures
+typedef struct _ccBlendFunc
+{
+ //! source blend function
+ GLenum src;
+ //! destination blend function
+ GLenum dst;
+} ccBlendFunc;
+
+//! delta time type
+//! if you want more resolution redefine it as a double
+typedef float ccTime;
+//typedef double ccTime;
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/** @mainpage cocos2d for iPhone API reference
+ *
+ * @image html Icon.png
+ *
+ * @section intro Introduction
+ * This is cocos2d API reference
+ *
+ * The programming guide is hosted here: http://www.cocos2d-iphone.org/wiki/doku.php/prog_guide:index
+ *
+ * <hr>
+ *
+ * @todo A native english speaker should check the grammar. We need your help!
+ *
+ */
+
+// 0x00 HI ME LO
+// 00 01 00 00
+#define COCOS2D_VERSION 0x00010000
+
+#import <Availability.h>
+
+//
+// all cocos2d include files
+//
+#import "ccConfig.h" // should be included first
+
+#import "CCActionManager.h"
+#import "CCAction.h"
+#import "CCActionInstant.h"
+#import "CCActionInterval.h"
+#import "CCActionEase.h"
+#import "CCActionCamera.h"
+#import "CCActionTween.h"
+#import "CCActionEase.h"
+#import "CCActionTiledGrid.h"
+#import "CCActionGrid3D.h"
+#import "CCActionGrid.h"
+#import "CCActionProgressTimer.h"
+#import "CCActionPageTurn3D.h"
+
+#import "CCAnimation.h"
+#import "CCAnimationCache.h"
+#import "CCSprite.h"
+#import "CCSpriteFrame.h"
+#import "CCSpriteBatchNode.h"
+#import "CCSpriteFrameCache.h"
+
+#import "CCLabelTTF.h"
+#import "CCLabelBMFont.h"
+#import "CCLabelAtlas.h"
+
+#import "CCParticleSystem.h"
+#import "CCParticleSystemPoint.h"
+#import "CCParticleSystemQuad.h"
+#import "CCParticleExamples.h"
+
+#import "CCTexture2D.h"
+#import "CCTexturePVR.h"
+#import "CCTextureCache.h"
+#import "CCTextureAtlas.h"
+
+#import "CCTransition.h"
+#import "CCTransitionPageTurn.h"
+#import "CCTransitionRadial.h"
+
+#import "CCTMXTiledMap.h"
+#import "CCTMXLayer.h"
+#import "CCTMXObjectGroup.h"
+#import "CCTMXXMLParser.h"
+#import "CCTileMapAtlas.h"
+
+#import "CCLayer.h"
+#import "CCMenu.h"
+#import "CCMenuItem.h"
+#import "CCDrawingPrimitives.h"
+#import "CCScene.h"
+#import "CCScheduler.h"
+#import "CCBlockSupport.h"
+#import "CCCamera.h"
+#import "CCProtocols.h"
+#import "CCNode.h"
+#import "CCDirector.h"
+#import "CCAtlasNode.h"
+#import "CCGrabber.h"
+#import "CCGrid.h"
+#import "CCParallaxNode.h"
+#import "CCRenderTexture.h"
+#import "CCMotionStreak.h"
+#import "CCConfiguration.h"
+
+//
+// cocos2d macros
+//
+#import "ccTypes.h"
+#import "ccMacros.h"
+
+
+// Platform common
+#import "Platforms/CCGL.h"
+#import "Platforms/CCNS.h"
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#import "Platforms/iOS/CCTouchDispatcher.h"
+#import "Platforms/iOS/CCTouchDelegateProtocol.h"
+#import "Platforms/iOS/CCTouchHandler.h"
+#import "Platforms/iOS/EAGLView.h"
+#import "Platforms/iOS/CCDirectorIOS.h"
+
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
+#import "Platforms/Mac/MacGLView.h"
+#import "Platforms/Mac/CCDirectorMac.h"
+#endif
+
+//
+// cocos2d helper files
+//
+#import "Support/OpenGL_Internal.h"
+#import "Support/CCFileUtils.h"
+#import "Support/CGPointExtension.h"
+#import "Support/ccCArray.h"
+#import "Support/CCArray.h"
+#import "Support/ccUtils.h"
+
+#if CC_ENABLE_PROFILERS
+#import "Support/CCProfiling.h"
+#endif // CC_ENABLE_PROFILERS
+
+
+// free functions
+NSString * cocos2dVersion(void);
+
+#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
+#ifndef __IPHONE_4_0
+#error "If you are targeting iPad, you should set BASE SDK = 4.0 (or 4.1, or 4.2), and set the 'iOS deploy target' = 3.2"
+#endif
+#endif
--- /dev/null
+/*
+ * cocos2d for iPhone: http://www.cocos2d-iphone.org
+ *
+ * Copyright (c) 2008-2010 Ricardo Quesada
+ * Copyright (c) 2011 Zynga Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#import <Foundation/Foundation.h>
+
+static NSString *version = @"cocos2d v1.0.0-rc3";
+
+NSString *cocos2dVersion()
+{
+ return version;
+}
_world = [[QQWorld alloc] init];
_world.proxiedNotifier = self;
+ // _root.scaleX = _root.scaleY = _world.scale;
[_world addObserver:self selector:@selector(onCollide:) name:QQ_EVENT_CONTACT_BEGIN object:nil];
[_world addObserver:self selector:@selector(onCollide:) name:QQ_EVENT_CONTACT_END object:nil];
if (_ticks == 1)
NSLog(@"%@", self);
+ // _root.scaleX = _root.scaleY = _world.scale;
for (QQActor* actor in self.actors)
[actor tick];
[self.world step];