#ifndef TANKS_H
#define TANKS_H
+
#ifdef DEBUG
#define TANKS_DEBUG_LOG 1
-#else
+
+#else // DEBUG
#define TANKS_DEBUG_LOG 0
+
#endif // DEBUG
+
#endif // TANKS_H
#import "TanksMacros.h"
+#import "qq/NSArray+QQExtensions.h"
+#import "qq/NSDictionary+QQExtensions.h"
#import "qq/NSSet+QQExtensions.h"
#import "render/QQSparrowExtensions.h"
#define b2s(__val) ((__val)?("YES"):("NO"))
+#define ATTACK_COOLDOWN_NAME @"attack"
+
+
#endif
\ No newline at end of file
#import "game/QQGameTime.h"
#import "game/QQThing.h"
+#import "game/ability/QQCooldown.h"
+
/**
* Anything that takes a turn is QQActive.
*/
@protocol QQActive <QQThing>
-@property (nonatomic, readwrite, getter=isActive) BOOL active;
-// @property (nonatomic, readwrite, getter=isSleeping) BOOL sleeping;
-@property (nonatomic, readwrite, retain) NSArray* cooldowns;
+@property (nonatomic, readwrite, getter=isActive) BOOL active;
+@property (nonatomic, readwrite, getter=isSleeping) BOOL sleeping;
+
+@property (nonatomic, readonly, retain) NSMutableDictionary* cooldowns;
+- (void) addCooldown:(QQCooldown*)cool;
+- (void) tickCooldowns;
- (void) tick;
- (void) act;
+
@end
\ No newline at end of file
/**
* Called to setup the Sparrow shape.
*/
-- (SPDisplayObject*) setupShape;
+- (void) setupShape;
/**
* Called to update appearance after game actions have occurred
#import "Sparrow.h"
+#import "Tanks.h"
#import "qq/event/QQNotificationCenter.h"
#import "physics/QQWorld.h"
@property (nonatomic, readonly) float elapsed;
@property (nonatomic, readonly) long ticks;
-@property (nonatomic, readonly, getter=isPaused) BOOL paused;
+@property (nonatomic, readonly, getter=isRunning) BOOL running;
@property (nonatomic, readwrite, assign) BOOL debugDrawingEnabled;
@property (nonatomic, readonly) SPStage* stage;
@property (nonatomic, readonly) QQLevel* level;
@property (nonatomic, readonly) QQWorld* world;
-
@property (nonatomic, readonly, retain) NSSet* actors;
-- (void) tick:(float)elapsed;
-
/** Alerts the game to a new actor. */
- (QQGame*) addActor:(QQActor*)actor;
- (QQGame*) removeActor:(QQActor*)actor;
/** Pauses the game. */
- (void) stop;
+/** Advances the game clock one step and elapsed ms. */
+- (void) tick:(float)elapsed;
+
+ (QQGame*) current;
#import <Box2D/Box2D.h>
-#import "Tanks.h"
#import "game/QQGame.h"
#import "game/unit/QQActors.h"
#import "physics/event/QQContactNotification.h"
- (void) onEnterFrame:(SPEnterFrameEvent*)event;
- (void) onCollide:(QQContactNotification*)msg;
+- (void) logEvent:(SPEvent*)evt;
@end
return _CurrentGame;
}
+
- (id) init {
if (_CurrentGame) {
[self release];
[[[QQTank alloc] initAtX:wMax/6 y:hMax/6 width:50 height:50 color:0xFF0071] autorelease];
[[[QQUnit alloc] initAtX:wMax/3 y:hMax/2 width:50 height:50 color:0x4596FF] autorelease];
-#if TANKS_DEBUG_LOG
- [_stage addEventListener:@selector(logEvent:) atObject:self forType:SP_EVENT_TYPE_ANY];
-#endif
+ #if TANKS_DEBUG_LOG
+ [_stage addEventListener:@selector(logEvent:) atObject:self forType:SP_EVENT_TYPE_ANY];
+ #endif
+
}
return self;
}
- (id<QQGameTime>) time { return self; }
-@synthesize paused = _running;
+@synthesize running = _running;
@synthesize stage = _stage;
@synthesize root = _root;
/// methods
- (void) tick:(float)elapsed {
- if (!self.isPaused) return;
+ if (!self.isRunning) return;
+
_ticks++;
- _elapsed = elapsed;
- _now += elapsed;
+ _elapsed = elapsed;
+ _now += elapsed;
for (QQActor* actor in self.actors)
[actor tick];
}
- (void) onCollide:(QQContactNotification*)msg {
- NSLog(@"%@", msg);
+// NSLog(@"\v\t%@", msg);
}
- (void) logEvent:(SPEvent*)evt {
// ignore enter-frame events, as they happen constantly
if ([evt.type isEqualToString:SP_EVENT_TYPE_ENTER_FRAME]) return;
- NSLog(@"%@", evt);
+ NSLog(@"\v\t%@", evt);
}
- (NSString*) description {
- return [NSString stringWithFormat:@"<%@: %p> {running=%s, #actors=%i, time={now=%f, elapsed=%f, ticks=%l}}",
- [self class], (long)self, b2s(self.isPaused), [self.actors count],
- self.now, self.elapsed, self.ticks];
+ return [NSString stringWithFormat:@"<%@: %p> {running=%s, #actors=%i, time={now=%f, ticks=%l, elapsed=%f}}",
+ [self class], (long)self,
+ b2s(self.isRunning), [self.actors count],
+ self.now, self.ticks, self.elapsed
+ ];
}
/// The y coordinate in the simulation.
@property (nonatomic, readwrite, assign) float y;
+/// Simulation coordinates
@property (nonatomic, readwrite) CGPoint position;
- (void) setPositionX:(float)x y:(float)y;
/// The height of the object in simulation units.
//@property (nonatomic, assign) float height;
-
-
-
// FIXME: don't expose these, instead aggregate properties from body & fixtures/shapes
@property (nonatomic, readonly) b2Body* body;
@property (nonatomic, readonly) b2Fixture* fixture;
+
+/// methods
+
/**
- * Called after initialization to create the various bodies, fixtures, and shapes
- * that represent the Unit in Box2D.
+ * Called to create the various bodies, fixtures, and shapes that represent
+ * the Unit in Box2D. Normally called after initialization completes, but may
+ * be deferred if in a callback.
*/
-- (void) setupPhysics;
+- (void) setupPhysics:(b2BodyDef*)bd;
+- (void) applyLinearImpulse:(CGPoint)vec;
- (void) applyLinearImpulseX:(float)xNs y:(float)yNs;
-#import "qq/event/QQObservable.h"
+#import "qq/event/QQNotifier.h"
@class QQGame;
@class QQWorld;
+#import "Tanks.h"
#import "game/QQGameTime.h"
*/
@interface QQCooldown : NSObject {
@private
+ NSString* _name;
float _duration;
float _elapsed;
}
+/** Name of this cooldown timer. */
+@property (nonatomic, readwrite, retain) NSString* name;
+
/** Duration of this cooldown timer (ms). */
@property (nonatomic, readwrite, assign) float duration;
@property (nonatomic, readwrite, assign) float elapsed;
/** Whether Cooldown timer is ready to be activated. */
-@property (nonatomic, readwrite) BOOL ready;
+@property (nonatomic, readwrite, getter=isReady) BOOL ready;
/** Percent completed (elapsed / duration), clamped [0, 1.0]. */
@property (nonatomic, readwrite) float ratio;
/**
* Implies ready=YES.
*/
-- (id) initWithDuration:(float)duration;
-- (id) initWithDuration:(float)duration andReady:(BOOL)ready;
+- (id) init:(NSString*)name duration:(float)duration;
+- (id) init:(NSString*)name duration:(float)duration ready:(BOOL)ready;
/**
* Attempts to activate this cooldown, clearing the ready flag and setting elapsed to 0.
- (BOOL) tick:(id<QQGameTime>)time;
-+ (QQCooldown*) cooldownWithDuration:(float)duration;
-+ (QQCooldown*) cooldownWithDuration:(float)duration andReady:(BOOL)ready;
++ (QQCooldown*) cooldown:(NSString*)name duration:(float)duration;
++ (QQCooldown*) cooldown:(NSString*)name duration:(float)duration ready:(BOOL)ready;
@end
/// properties
+@synthesize name = _name;
@synthesize duration = _duration;
@synthesize elapsed = _elapsed;
-- (BOOL) ready { return (self.elapsed >= self.duration); }
+- (BOOL) isReady { return (self.elapsed >= self.duration); }
- (void) setReady:(BOOL)newReady {
if (newReady == self.ready)
return;
self.elapsed = 0;
}
-- (float) ratio { return fminf(1.0f, self.elapsed / self.duration); }
+- (float) ratio { return MIN(1.0f, self.elapsed / self.duration); }
- (void) setRatio:(float)newRatio {
if (newRatio >= 1.0f)
self.elapsed = self.duration;
/// initializers
-- (id) initWithDuration:(float)duration {
- return [self initWithDuration:duration andReady:YES];
+- (id) init:(NSString*)name duration:(float)duration {
+ return [self init:name duration:duration ready:YES];
}
-- (id) initWithDuration:(float)duration andReady:(BOOL)ready {
- if ((self = [super init])){
+- (id) init:(NSString*)name duration:(float)duration ready:(BOOL)ready {
+ if ((self = [super init])) {
+ if (duration <= 0.0f) {
+ [self autorelease];
+ [NSException raise:@"InvalidArgumentException" format:@"duration must be positive!"];
+ }
+ self.name = name;
_duration = duration;
_elapsed = (ready ? _duration : 0);
}
return self.ready;
}
+- (NSString*) description {
+ return [NSString stringWithFormat:@"<%@: %p> {name=%@, ready=%s (%.3f%% %.3f/%.3f)}",
+ [self class], self, _name, b2s(self.isReady),
+ self.ratio, _elapsed, _duration
+ ];
+}
+
+
/// convenience constructors
-+ (QQCooldown*) cooldownWithDuration:(float)duration {
- return [[[QQCooldown alloc] initWithDuration:duration] autorelease];
++ (QQCooldown*) cooldown:(NSString*)name duration:(float)duration {
+ return [[[QQCooldown alloc] init:name duration:duration] autorelease];
}
-+ (QQCooldown*) cooldownWithDuration:(float)duration andReady:(BOOL)ready {
- return [[[QQCooldown alloc] initWithDuration:duration andReady:ready] autorelease];
++ (QQCooldown*) cooldown:(NSString*)name duration:(float)duration ready:(BOOL)ready {
+ return [[[QQCooldown alloc] init:name duration:duration ready:ready] autorelease];
}
#include <Box2D/Box2D.h>
-#import "qq/event/QQNotificationProxy.h"
+#import "Tanks.h"
+#import "qq/event/QQNotifiers.h"
#import "game/QQThing.h"
#import "game/QQActive.h"
#import "game/QQPhysical.h"
#import "game/QQDisplayable.h"
+#import "game/ability/QQCooldown.h"
#import "game/unit/QQActorDelegate.h"
#import "physics/QQWorld.h"
b2Body* _body;
@private
+ id<QQActorDelegate> _delegate;
+
BOOL _active;
BOOL _dirty;
BOOL _dead;
- id<QQActorDelegate> _delegate;
- NSArray* _cooldowns;
+ NSMutableDictionary* _cooldowns;
}
+@property (nonatomic, readwrite, retain) id<QQActorDelegate> delegate;
+
@property (nonatomic, readonly) QQGame* game;
@property (nonatomic, readonly) QQWorld* world;
-@property (nonatomic, readwrite, retain) id<QQActorDelegate> delegate;
-@property (nonatomic, readonly) BOOL dead;
+
+@property (nonatomic, readonly, getter=isDead) BOOL dead;
+@property (nonatomic, readonly, retain) NSMutableDictionary* cooldowns;
+
- (id) initAtX:(float)x y:(float)y;
-- (id) initType:(b2BodyType)type atX:(float)x y:(float)y;
+- (id) initWithType:(b2BodyType)type atX:(float)x y:(float)y;
+- (id) initWithBodyDef:(b2BodyDef*)bd;
- (void) destroy;
#import "QQActor.h"
-#import "render/QQSparrowExtensions.h"
#import "game/QQGame.h"
/// properties
-@dynamic shape;
-
-@synthesize active = _active;
-@synthesize dirty = _dirty;
-@synthesize dead = _dead;
-@synthesize delegate = _delegate;
-@synthesize cooldowns = _cooldowns;
-
-
- (QQGame*) game { return QQGame.current; }
- (QQWorld*) world { return self.game.world; }
- (b2Body*) body { return _body; }
- (b2Fixture*) fixture { return nil; }
-- (id) implicitNotificationSender { return self; }
-- (void) setImplicitNotificationSender:(id)sender {}
-- (id<QQObservableNotifier>) proxiedNotifier { return self.game; }
-- (void) setProxiedNotifier:(id<QQObservableNotifier>)notifier {}
+@synthesize delegate = _delegate;
+@synthesize cooldowns = _cooldowns;
+@synthesize dead = _dead;
+@synthesize dirty = _dirty;
+@synthesize active = _active;
+
+- (BOOL) isSleeping { return _active; }
+- (void) setSleeping:(BOOL)v { _active = v; }
- (float) x { return self.body->GetPosition().x; }
- (void) setX:(float)x { [self setPositionX:x y:self.y]; }
-
- (float) y { return self.body->GetPosition().y; }
- (void) setY:(float)y { [self setPositionX:self.x y:y]; }
-
- (CGPoint) position {
b2Vec2 pos = self.body->GetPosition();
return CGPointMake(pos.x, pos.y);
- (void) setPositionX:(float)x y:(float)y {
self.body->SetTransform(b2Vec2(x,y), self.body->GetAngle());
}
-
- (float) rotation { return self.body->GetAngle(); }
- (void) setRotation:(float)r { self.body->SetTransform(self.body->GetPosition(), r); }
+@dynamic shape;
+
+// sync sparrow with box2d
+
- (void) updateShapeX:(float)x y:(float)y {
float px = self.world.scale;
[self.shape setCenterX:x*px y:y*px];
}
-
- (void) updateShapeX:(float)x y:(float)y rotation:(float)r {
[self updateShapeX:x y:y];
self.shape.rotation = r;
}
-
- (void) updateShape {
b2Transform trans = self.body->GetTransform();
[self updateShapeX:trans.position.x y:trans.position.y rotation:trans.GetAngle()];
/// initializers
+- (id) init {
+ return [self initAtX:0 y:0];
+}
+
- (id) initAtX:(float)x y:(float)y {
- return [self initType:b2_dynamicBody atX:x y:y];
+ return [self initWithType:b2_dynamicBody atX:x y:y];
}
-- (id) initType:(b2BodyType)type atX:(float)x y:(float)y {
+- (id) initWithType:(b2BodyType)type atX:(float)x y:(float)y {
+ b2BodyDef bd;
+ bd.type = type;
+ bd.position = b2Vec2(x,y);
+ bd.userData = self;
+ return [self initWithBodyDef:&bd];
+}
+
+- (id) initWithBodyDef:(b2BodyDef*)bd {
if ((self = [super init])) {
_active = YES;
_dead = NO;
+
+ self.implicitSender = self;
self.proxiedNotifier = self.game;
- self.cooldowns = [NSArray array];
- [self.game addActor:self];
- b2BodyDef bd;
- bd.type = type;
- bd.position = b2Vec2(x, y);
- bd.userData = self;
- _body = self.world.world->CreateBody(&bd);
+ _cooldowns = [[NSMutableDictionary alloc] initWithCapacity:2];
+ [self setupPhysics:bd];
+ [self.game addActor:self];
if ([self respondsToSelector:@selector(onCollideBegin:)])
[self addObserver:self selector:@selector(onCollideBegin:) name:QQ_EVENT_CONTACT_BEGIN];
+ if ([self respondsToSelector:@selector(onCollideEnd:)])
+ [self addObserver:self selector:@selector(onCollideEnd:) name:QQ_EVENT_CONTACT_END];
}
return self;
}
+/// subclassing
+
+- (void) setupShape {
+ // override in subclass
+}
+
+// TODO: you can't create bodies during callbacks
+/// override but call super!
+- (void) setupPhysics:(b2BodyDef*)bd {
+ if (!_body) {
+ _body = self.world.world->CreateBody(bd);
+ }
+}
+
+- (void) act {
+ // override in subclass
+}
+
+
+
/// methods
+- (void) addCooldown:(QQCooldown*)cool {
+ [self.cooldowns setObject:cool forKey:cool.name];
+}
+
- (void) destroy {
- if (self.dead) return;
+ if (self.isDead) return;
_dead = YES;
self.shape = nil;
}
- (void) tick {
- if (self.dead) return;
+ if (self.isDead) return;
- [self.cooldowns makeObjectsPerformSelector:@selector(tick:) withObject:self.game.time];
+ [self tickCooldowns];
if (self.isActive)
[self act];
}
-- (void) act {}
-
-- (SPDisplayObject*) setupShape {
- return self.shape;
+- (void) tickCooldowns {
+ [self.cooldowns makeObjectsPerformSelector:@selector(tick:) withObject:self.game.time];
}
- (void) draw {
- if (self.dead) return;
-
+ if (self.isDead) return;
+ if (!self.shape) [self setupShape];
[self updateShape];
}
-- (void) setupPhysics {}
-
-
+- (void) applyLinearImpulse:(CGPoint)pt {
+ [self applyLinearImpulseX:pt.x y:pt.y];
+}
- (void) applyLinearImpulseX:(float)xNs y:(float)yNs {
self.body->ApplyLinearImpulse(b2Vec2(xNs, yNs), self.body->GetPosition());
}
b2Transform trans = self.body->GetTransform();
b2Vec2 pos = trans.position;
//b2Vec2 v = actor.body->GetLinearVelocity();
- SPDisplayObject* s = self.shape;
- return [NSString stringWithFormat:@"[%@ box2d=[ (%f,%f) rotation=%f], shape=%@]",
- [super description], pos.x,pos.y, trans.GetAngle(), s];
+ return [NSString stringWithFormat:@"\v\t<%@ %p> {\v\t\tbox2d=[ (%f,%f) rotation=%f], \v\t\tshape=%@, \v\t\tcooldowns=%@}",
+ [self class], self, pos.x,pos.y, trans.GetAngle(), self.shape, self.cooldowns];
}
+#import "Tanks.h"
#import "game/unit/QQUnit.h"
}
- (void) onCollideBegin:(QQContactNotification*)msg {
- QQActor* other = (msg.actorA && (msg.actorA != self) ? msg.actorA :
- (msg.actorB && (msg.actorB != self) ? msg.actorB : nil));
+ QQActor* other = ((msg.actorA && (msg.actorA != self)) ? msg.actorA :
+ ((msg.actorB && (msg.actorB != self)) ? msg.actorB : nil));
if (other) {
NSLog(@"BOOM! %@", msg);
[other destroy];
#import "game/unit/QQUnit.h"
-#import "game/ability/QQCooldown.h"
@interface QQTank : QQUnit {
- QQCooldown* _coolAtk;
+
}
-@property (nonatomic, readonly, retain) QQCooldown* coolAtk;
@end
// private interface
@interface QQTank ()
-@property (nonatomic, readwrite, retain) QQCooldown* coolAtk;
- (void) onTouch:(SPTouchEvent*)event;
@end
@implementation QQTank
-@synthesize coolAtk = _coolAtk;
- (id) initAtX:(float)x y:(float)y withShape:(SPDisplayObject*)shape {
if ((self = [super initAtX:x y:y withShape:shape])){
- self.coolAtk = [QQCooldown cooldownWithDuration:0.5f];
- self.cooldowns = [self.cooldowns arrayByAddingObject:self.coolAtk];
[self.game.stage addEventListener:@selector(onTouch:) atObject:self forType:SP_EVENT_TYPE_TOUCH];
}
return self;
- (void) dealloc {
[self.game.stage removeEventListener:@selector(onTouch:) atObject:self forType:SP_EVENT_TYPE_TOUCH];
- [_coolAtk release];
[super dealloc];
}
- (void) onTouch:(SPTouchEvent*)event {
SPTouch* touch = [[event touchesWithTarget:(SPDisplayObject*)self.game.stage] anyObject];
- if (touch && [self.coolAtk activate]) {
+ if (touch && [[self.cooldowns objectForKey:ATTACK_COOLDOWN_NAME] activate]) {
// Touch's normal vector in local coords (rel unit origin)
SPPoint* normVec = [[[touch locationInSpace:self.shape.parent] subtractPoint:self.shape.position] normalize];
}
}
-- (void) _moveOnTouch:(SPTouchEvent*)event {
- SPTouch* touch = [[event touchesWithTarget:self.shape.parent] anyObject];
- if (touch) {
- SPPoint* touchPosition = [touch locationInSpace:self.shape.parent];
- float x = self.shape.x = touchPosition.x - self.shape.width / 2.0f;
- float y = self.shape.y = touchPosition.y - self.shape.height / 2.0f;
-
- float px = self.world.scale;
- [self setPositionX:x/px y:y/px];
- // self.body->SetTransform(b2Vec2(x/px, y/px), self.body->GetAngle());
- }
-}
@end
\ No newline at end of file
#include <Box2D/Box2D.h>
#import "Sparrow.h"
+#import "render/QQSparrowExtensions.h"
-#import "physics/QQWorld.h"
+#import "game/ability/QQCooldown.h"
#import "game/unit/QQActor.h"
#import "game/unit/QQActorDelegate.h"
-#import "render/QQSparrowExtensions.h"
+
@interface QQUnit : QQActor {
}
/// properties
+
@property (nonatomic, readonly) BOOL canAttack;
+@property (nonatomic, readwrite, assign) QQCooldown* attackCooldown;
+
- (void) setShapeFromFile:(NSString*)filename;
+
/// initializers
- (id) initAtX:(float)x y:(float)y width:(float)w height:(float)h color:(uint)color;
- (id) initAtX:(float)x y:(float)y withShape:(SPDisplayObject*)shape;
#include <Box2D/Box2D.h>
#import "Sparrow.h"
+#import "render/QQSparrowExtensions.h"
#import "game/unit/QQUnit.h"
#import "game/QQGame.h"
-#import "render/QQSparrowExtensions.h"
+#import "game/unit/QQBullet.h"
- (float) shapeCenterY { return self.shape.registrationY; }
- (void) setShapeCenterY:(float)y { self.shape.registrationY = y; }
-- (BOOL) canAttack { return NO; }
+- (QQCooldown*) attackCooldown { return [self.cooldowns objectForKey:ATTACK_COOLDOWN_NAME]; }
+- (void) setAttackCooldown:(QQCooldown*)newAttackCooldown { [self.cooldowns setObject:newAttackCooldown forKey:ATTACK_COOLDOWN_NAME]; }
+- (BOOL) canAttack { return self.attackCooldown.isReady; }
/// initializers
- (id) initAtX:(float)x y:(float)y withShape:(SPDisplayObject*)shape {
if ((self = [super initAtX:x y:y])) {
+ [self addCooldown:[QQCooldown cooldown:ATTACK_COOLDOWN_NAME duration:0.5f]];
self.shape = shape;
float dblpx = 2*self.world.scale;
[self attackTowardX:pt.x y:pt.y];
}
- (void) attackTowardX:(float)x y:(float)y {
+ SPPoint* pos = [SPPoint pointFromCGPoint:self.position];
+ SPPoint* target = [SPPoint pointWithX:x y:y];
+
+ // Touch's normal vector in local coords (rel unit origin)
+ SPPoint* normVec = [[target subtractPoint:pos] normalize];
+
+ // Bullet starting position
+ float offset = 1.5f + MAX(self.shape.width, self.shape.height) * 1.41421356237f; // bullet_radius + tank_diagonal_length
+ SPPoint* start = [pos addPoint:[normVec scaleBy:offset]];
+ QQBullet* bullet = [[[QQBullet alloc] init:self x:start.x y:start.y] autorelease];
+ // Bullet impulse vector
+ SPPoint* imp = [normVec scaleBy:50];
+ [bullet applyLinearImpulseX:imp.x y:imp.y];
+ NSLog(@"[%@ pos:(%f,%f) impulse:(%f,%f)]", bullet, bullet.x,bullet.y, imp.x,imp.y);
}
--- /dev/null
+
+@interface NSArray (QQExtensions)
+
+- (void) makeObjectsPerformSelector:(SEL)aSelector withObject:(id)arg1 withObject:(id)arg2;
+
+@end
--- /dev/null
+#import "NSArray+QQExtensions.h"
+
+
+@implementation NSArray (QQExtensions)
+
+- (void) makeObjectsPerformSelector:(SEL)aSelector withObject:(id)arg1 withObject:(id)arg2 {
+ for (id value in self)
+ [value performSelector:aSelector withObject:arg1 withObject:arg2];
+}
+
+
+@end
--- /dev/null
+
+@interface NSDictionary (QQExtensions)
+
+- (void) makeObjectsPerformSelector:(SEL)aSelector;
+- (void) makeObjectsPerformSelector:(SEL)aSelector withObject:(id)arg1;
+- (void) makeObjectsPerformSelector:(SEL)aSelector withObject:(id)arg1 withObject:(id)arg2;
+
+@end
--- /dev/null
+#import "NSDictionary+QQExtensions.h"
+
+
+@implementation NSDictionary (QQExtensions)
+
+- (void) makeObjectsPerformSelector:(SEL)aSelector {
+ for (NSString* key in self) {
+ id value = [self objectForKey:key];
+ [value performSelector:aSelector];
+ }
+}
+
+- (void) makeObjectsPerformSelector:(SEL)aSelector withObject:(id)arg1 {
+ for (NSString* key in self) {
+ id value = [self objectForKey:key];
+ [value performSelector:aSelector withObject:arg1];
+ }
+}
+
+- (void) makeObjectsPerformSelector:(SEL)aSelector withObject:(id)arg1 withObject:(id)arg2 {
+ for (NSString* key in self) {
+ id value = [self objectForKey:key];
+ [value performSelector:aSelector withObject:arg1 withObject:arg2];
+ }
+}
+
+@end
- (NSSet*) setByRemovingObject:(id)object;
- (NSSet*) setByRemovingArray:(NSArray*)objects;
+- (void) makeObjectsPerformSelector:(SEL)aSelector withObject:(id)arg1 withObject:(id)arg2;
@end
+#import "NSSet+QQExtensions.h"
+
@implementation NSSet (QQExtensions)
- (NSSet*) setByRemovingObject:(id)object {
}];
}
+- (void) makeObjectsPerformSelector:(SEL)aSelector withObject:(id)arg1 withObject:(id)arg2 {
+ for (id value in self)
+ [value performSelector:aSelector withObject:arg1 withObject:arg2];
+}
+
@end
#import "qq/event/QQNotifier.h"
-@interface QQNotificationCenter : NSNotificationCenter <QQObservableNotifier> {
+@interface QQNotificationCenter : NSNotificationCenter <QQBubblingNotifier> {
@private
id _implicitNotificationSender;
+ id<QQObservableNotifier> _parentNotifier;
}
/** Object used for implicit sender when adding or removing observers. */
-@property (nonatomic, readwrite, retain) id implicitNotificationSender;
+@property (nonatomic, readwrite, assign) id implicitNotificationSender;
/** Initialize with self as implicit sender. */
- (id) init;
/** Returns a center with given implicit target. A nil target will subscript observers to all objects. */
+ (QQNotificationCenter*) notifierWithTarget:(id)target;
++ (QQNotificationCenter*) notifierWithTarget:(id)target andParent:(id<QQObservableNotifier>)parent;
@end
- (id) init {
if ((self = [super init])){
_implicitNotificationSender = self;
+ _parentNotifier = nil;
}
return self;
}
- (void) dealloc {
- [_implicitNotificationSender release];
+ self.implicitNotificationSender = nil;
+ self.parentNotifier = nil;
[super dealloc];
}
@synthesize implicitNotificationSender = _implicitNotificationSender;
+@synthesize parentNotifier = _parentNotifier;
/// observable
}
// Send a notification from this object
+
- (void) postNotificationName:(NSString*)name {
[self postNotificationName:name object:self.implicitNotificationSender];
}
[super removeObserver:observer];
}
+- (void) postNotification:(NSNotification*)msg {
+ [super postNotification:msg];
+ [self.parentNotifier postNotification:msg];
+}
+
/// NSNotificationCenter (superclass) provides:
+ (QQNotificationCenter*) notifierWithTarget:(id)target {
+ return [QQNotificationCenter notifierWithTarget:target andParent:nil];
+}
++ (QQNotificationCenter*) notifierWithTarget:(id)target andParent:(id<QQObservableNotifier>)parent {
QQNotificationCenter* notifier = [[[QQNotificationCenter alloc] init] autorelease];
notifier.implicitNotificationSender = target;
+ notifier.parentNotifier = parent;
return notifier;
}
@interface QQNotificationProxy : NSObject <QQObservableNotifier> {
@private
id<QQObservableNotifier> _proxiedNotifier;
- id _implicitNotificationSender;
+ id<QQObservableNotifier> _parentNotifier;
+ id _implicitSender;
}
+@property (nonatomic, readwrite, assign) id implicitSender;
+@property (nonatomic, readwrite, retain) id<QQObservableNotifier> parentNotifier;
@property (nonatomic, readwrite, retain) id<QQObservableNotifier> proxiedNotifier;
- (id) initWithNotifier:(id<QQObservableNotifier>)notifier;
+- (id) initWithNotifier:(id<QQObservableNotifier>)notifier andParent:(id<QQObservableNotifier>)parent;
@end
#import "qq/event/QQNotificationProxy.h"
+#import "qq/event/QQNotification.h"
@implementation QQNotificationProxy
@synthesize proxiedNotifier = _proxiedNotifier;
-@synthesize implicitNotificationSender = _implicitNotificationSender;
+@synthesize parentNotifier = _parentNotifier;
+@synthesize implicitSender = _implicitSender;
- (id) initWithNotifier:(id<QQObservableNotifier>)notifier {
+ return [self initWithNotifier:notifier andParent:nil];
+}
+
+- (id) initWithNotifier:(id<QQObservableNotifier>)notifier andParent:(id<QQObservableNotifier>)parent {
if ((self = [super init])){
self.proxiedNotifier = notifier;
+ self.parentNotifier = parent;
}
return self;
}
- (void) dealloc {
self.proxiedNotifier = nil;
+ self.parentNotifier = nil;
[super dealloc];
}
/// observable
- (void) addObserver:(id)observer selector:(SEL)selector name:(NSString*)event {
- [self addObserver:observer selector:selector name:event object:self.implicitNotificationSender];
+ [self addObserver:observer selector:selector name:event object:self.implicitSender];
}
// Remove a listener from this object
- (void) removeObserver:(id)observer {
- [self removeObserver:observer name:nil object:self.implicitNotificationSender];
+ [self removeObserver:observer name:nil object:self.implicitSender];
}
- (void) removeObserver:(id)observer name:(NSString*)event {
- [self removeObserver:observer name:event object:self.implicitNotificationSender];
+ [self removeObserver:observer name:event object:self.implicitSender];
}
// Send a notification from this object
- (void) postNotificationName:(NSString*)name {
- [self postNotificationName:name object:self.implicitNotificationSender];
+ [self postNotificationName:name object:self.implicitSender userInfo:nil];
}
- (void) postNotificationName:(NSString*)name userInfo:(NSDictionary*)info {
- [self postNotificationName:name object:self.implicitNotificationSender userInfo:info];
+ [self postNotificationName:name object:self.implicitSender userInfo:info];
}
- (void) postNotification:(NSNotification*)msg {
[self.proxiedNotifier postNotification:msg];
+ [self.parentNotifier postNotification:msg];
}
- (void) postNotificationName:(NSString*)name object:(id)sender {
- [self.proxiedNotifier postNotificationName:name object:sender];
+ [self postNotificationName:name object:sender userInfo:nil];
}
- (void) postNotificationName:(NSString*)name object:(id)sender userInfo:(NSDictionary*)info {
- [self.proxiedNotifier postNotificationName:name object:sender userInfo:info];
+ QQNotification* msg = [QQNotification notification:name object:sender userInfo:info];
+ [self postNotification:msg];
}
- (void) postNotificationName:(NSString*)name object:(id)sender;
- (void) postNotificationName:(NSString*)name object:(id)sender userInfo:(NSDictionary*)info;
-
@end
+
@protocol QQObservableNotifier <QQObservable, QQNotifier>
+@end
+
+
+@protocol QQBubblingNotifier <QQObservableNotifier>
+
+@property (nonatomic, readwrite, retain) id<QQObservableNotifier> parentNotifier;
+
+@end
-@end
\ No newline at end of file
#import "qq/event/QQNotificationCenter.h"
#import "qq/event/QQNotificationProxy.h"
-
-
-/**
- * As a QQNotificationProxy, but via encapsulation.
- */
-#define qqNotifierProxy(__target, __notifier) \
-- (void) addObserver:(id)observer selector:(SEL)selector name:(NSString*)event { \
- [self addObserver:observer selector:selector name:event object:__target]; \
-} \
-- (void) removeObserver:(id)observer { \
- [self removeObserver:observer name:nil object:__target]; \
-} \
-- (void) removeObserver:(id)observer name:(NSString*)event { \
- [self removeObserver:observer name:event object:__target]; \
-} \
-- (void) postNotificationName:(NSString*)name { \
- [self postNotificationName:name object:__target]; \
-} \
-- (void) postNotificationName:(NSString*)name userInfo:(NSDictionary*)info { \
- [self postNotificationName:name object:__target userInfo:info]; \
-} \
-- (void) addObserver:(id)observer selector:(SEL)selector name:(NSString*)event object:(id)sender { \
- [__notifier addObserver:observer selector:selector name:event object:sender]; \
-} \
-- (void) removeObserver:(id)observer name:(NSString*)event object:(id)sender { \
- [__notifier removeObserver:observer name:event object:sender]; \
-} \
-- (void) removeObservers:(id)observer { \
- [__notifier removeObserver:observer]; \
-} \
-- (void) postNotification:(NSNotification*)msg { \
- [__notifier postNotification:msg]; \
-} \
-- (void) postNotificationName:(NSString*)name object:(id)sender { \
- [__notifier postNotificationName:name object:sender]; \
-} \
-- (void) postNotificationName:(NSString*)name object:(id)sender userInfo:(NSDictionary*)info { \
- [__notifier postNotificationName:name object:sender userInfo:info]; \
-}
-
*/
@protocol QQObservable
-@property (nonatomic, readwrite, retain) id implicitNotificationSender;
-
//- (void) observe:(NSString*)event notify:(id)observer selector:(SEL)selector;
/** Adds an observer of this object. */
499F65E9139CBCBA00309DE4 /* QQUnit.h in Headers */ = {isa = PBXBuildFile; fileRef = 499F65DF139CBCBA00309DE4 /* QQUnit.h */; };
499F65EA139CBCBA00309DE4 /* QQUnit.mm in Sources */ = {isa = PBXBuildFile; fileRef = 499F65E0139CBCBA00309DE4 /* QQUnit.mm */; };
499F65EC139CBCC000309DE4 /* QQGameTime.h in Headers */ = {isa = PBXBuildFile; fileRef = 499F65EB139CBCC000309DE4 /* QQGameTime.h */; };
+ 499F65FD139CFD3800309DE4 /* NSArray+QQExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 499F65F9139CFD3700309DE4 /* NSArray+QQExtensions.h */; };
+ 499F65FE139CFD3800309DE4 /* NSArray+QQExtensions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 499F65FA139CFD3700309DE4 /* NSArray+QQExtensions.mm */; };
+ 499F65FF139CFD3800309DE4 /* NSDictionary+QQExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 499F65FB139CFD3700309DE4 /* NSDictionary+QQExtensions.h */; };
+ 499F6600139CFD3800309DE4 /* NSDictionary+QQExtensions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 499F65FC139CFD3800309DE4 /* NSDictionary+QQExtensions.mm */; };
49D8645D1392DB2800BC341C /* QQShape.h in Headers */ = {isa = PBXBuildFile; fileRef = 49D8645B1392DB2800BC341C /* QQShape.h */; };
49D8645E1392DB2800BC341C /* QQShape.mm in Sources */ = {isa = PBXBuildFile; fileRef = 49D8645C1392DB2800BC341C /* QQShape.mm */; };
49DA67D4137847A7004841E9 /* QQWorld.h in Headers */ = {isa = PBXBuildFile; fileRef = 49DA67D2137847A7004841E9 /* QQWorld.h */; };
499F65DF139CBCBA00309DE4 /* QQUnit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QQUnit.h; sourceTree = "<group>"; };
499F65E0139CBCBA00309DE4 /* QQUnit.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = QQUnit.mm; sourceTree = "<group>"; };
499F65EB139CBCC000309DE4 /* QQGameTime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QQGameTime.h; sourceTree = "<group>"; };
+ 499F65F9139CFD3700309DE4 /* NSArray+QQExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+QQExtensions.h"; sourceTree = "<group>"; };
+ 499F65FA139CFD3700309DE4 /* NSArray+QQExtensions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSArray+QQExtensions.mm"; sourceTree = "<group>"; };
+ 499F65FB139CFD3700309DE4 /* NSDictionary+QQExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+QQExtensions.h"; sourceTree = "<group>"; };
+ 499F65FC139CFD3800309DE4 /* NSDictionary+QQExtensions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSDictionary+QQExtensions.mm"; sourceTree = "<group>"; };
49D8645B1392DB2800BC341C /* QQShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QQShape.h; sourceTree = "<group>"; };
49D8645C1392DB2800BC341C /* QQShape.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = QQShape.mm; sourceTree = "<group>"; };
49DA67D2137847A7004841E9 /* QQWorld.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QQWorld.h; sourceTree = "<group>"; };
isa = PBXGroup;
children = (
499F65BF139CBC0C00309DE4 /* event */,
+ 499F65F9139CFD3700309DE4 /* NSArray+QQExtensions.h */,
+ 499F65FA139CFD3700309DE4 /* NSArray+QQExtensions.mm */,
+ 499F65FB139CFD3700309DE4 /* NSDictionary+QQExtensions.h */,
+ 499F65FC139CFD3800309DE4 /* NSDictionary+QQExtensions.mm */,
49E3C5D0139A73E700A3958A /* NSSet+QQExtensions.h */,
49E3C5D1139A73E700A3958A /* NSSet+QQExtensions.mm */,
);
499F65D6139CBCBA00309DE4 /* unit */ = {
isa = PBXGroup;
children = (
+ 499F65DA139CBCBA00309DE4 /* QQActors.h */,
+ 499F65D9139CBCBA00309DE4 /* QQActorDelegate.h */,
499F65D7139CBCBA00309DE4 /* QQActor.h */,
499F65D8139CBCBA00309DE4 /* QQActor.mm */,
- 499F65D9139CBCBA00309DE4 /* QQActorDelegate.h */,
- 499F65DA139CBCBA00309DE4 /* QQActors.h */,
499F65DB139CBCBA00309DE4 /* QQBullet.h */,
499F65DC139CBCBA00309DE4 /* QQBullet.mm */,
499F65DD139CBCBA00309DE4 /* QQTank.h */,
499F65E7139CBCBA00309DE4 /* QQTank.h in Headers */,
499F65E9139CBCBA00309DE4 /* QQUnit.h in Headers */,
499F65EC139CBCC000309DE4 /* QQGameTime.h in Headers */,
+ 499F65FD139CFD3800309DE4 /* NSArray+QQExtensions.h in Headers */,
+ 499F65FF139CFD3800309DE4 /* NSDictionary+QQExtensions.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
499F65E6139CBCBA00309DE4 /* QQBullet.mm in Sources */,
499F65E8139CBCBA00309DE4 /* QQTank.mm in Sources */,
499F65EA139CBCBA00309DE4 /* QQUnit.mm in Sources */,
+ 499F65FE139CFD3800309DE4 /* NSArray+QQExtensions.mm in Sources */,
+ 499F6600139CFD3800309DE4 /* NSDictionary+QQExtensions.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};