/*
* 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 (NSUInteger 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 (NSUInteger 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];
if( (self=[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