Bullets now arbitrarily destroy shit.
authordsc <david.schoonover@gmail.com>
Sun, 5 Jun 2011 03:55:47 +0000 (20:55 -0700)
committerdsc <david.schoonover@gmail.com>
Sun, 5 Jun 2011 03:55:47 +0000 (20:55 -0700)
34 files changed:
src/Tanks.h
src/TanksMacros.h
src/game/QQActive.h
src/game/QQGame.h
src/game/QQGame.mm
src/game/QQGameTime.h [new file with mode: 0644]
src/game/QQPhysical.h
src/game/QQThing.h
src/game/ability/QQCooldown.h
src/game/ability/QQCooldown.mm
src/game/actor/QQActor.h
src/game/actor/QQActor.mm
src/game/actor/QQActors.h
src/game/actor/QQUnit.mm
src/game/actor/bullet/QQBullet.h
src/game/actor/bullet/QQBullet.mm
src/game/actor/unit/QQTank.mm
src/physics/QQWorld.h
src/physics/QQWorld.mm
src/physics/event/QQContactNotification.mm
src/qq/NSSet+QQExtensions.h [new file with mode: 0644]
src/qq/NSSet+QQExtensions.mm [new file with mode: 0644]
src/qq/QQNotificationCenter.h [new file with mode: 0644]
src/qq/QQNotificationCenter.mm [new file with mode: 0644]
src/qq/QQNotificationProxy.h [new file with mode: 0644]
src/qq/QQNotificationProxy.mm [new file with mode: 0644]
src/qq/QQNotifier.h
src/qq/QQNotifiers.h [new file with mode: 0644]
src/qq/QQObject.h [deleted file]
src/qq/QQObject.mm [deleted file]
src/qq/QQObservable.h
src/ui/QQAppDelegate.h
src/ui/QQAppDelegate.mm
tanks.xcodeproj/project.pbxproj

index c9e4885..6a4d3c0 100644 (file)
@@ -1,6 +1,9 @@
 #ifndef TANKS_H
 #define TANKS_H
-#define TANKS_DEBUG_LOG 1
+
+#define TANKS_DEBUG_LOG 0
+
 #endif
 
+#import "TanksMacros.h"
 #import "render/QQSparrowExtensions.h"
index 2a9ef11..e85b6b2 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef TANKS_MACROS_H
 #define TANKS_MACROS_H
 
-
+#define qqBoolToString(__val) ((__val)?("YES"):("NO"))
 
 
 #endif
\ No newline at end of file
index be63ea8..4bc669b 100644 (file)
@@ -1,3 +1,4 @@
+#import "game/QQGameTime.h"
 #import "game/QQThing.h"
 
 /**
@@ -7,8 +8,9 @@
 
 @property (nonatomic, readwrite, getter=isActive) BOOL active;
 // @property (nonatomic, readwrite, getter=isSleeping) BOOL sleeping;
+@property (nonatomic, readwrite, retain) NSArray* cooldowns;
 
-- (void) tick:(float)elapsed;
+- (void) tick;
 - (void) act;
 
 @end
\ No newline at end of file
index 789be17..1c5e0a4 100644 (file)
@@ -1,47 +1,62 @@
 #import "Sparrow.h"
 
+#import "qq/QQNotificationCenter.h"
 #import "physics/QQWorld.h"
 #import "physics/debug/QQPhysicsDebugView.h"
+#import "game/QQGameTime.h"
 #import "game/actor/QQActor.h"
 #import "game/map/QQLevel.h"
 
 
-@interface QQGame : SPStage {
-    
+@interface QQGame : QQNotificationCenter <QQGameTime> {
 @private
-    QQWorld* _world;
-    
-    QQLevel* _level;
-    SPSprite* _root;
+    float _now;
+    float _elapsed;
+    long  _ticks;
+    long  _realtime;
     
-    NSMutableSet* _actors;
-    NSMutableSet* _awake;
-    NSMutableSet* _units;
-    NSMutableSet* _bullets;
+    BOOL _running;
+    BOOL _debugDrawingEnabled;
     
+    QQWorld* _world;
     QQPhysicsDebugView* _debugView;
-    BOOL _debugDrawingEnabled;
     
-    long _ticks;
-    BOOL _running;
+    SPStage* _stage;
+    SPSprite* _root;
+    QQLevel* _level;
+    
+    NSSet* _actors;
+    NSSet* _awake;
+    NSSet* _units;
+    NSSet* _bullets;
 }
 
-@property (nonatomic, readonly) QQWorld* world;
-@property (nonatomic, readonly) QQLevel* level;
-@property (nonatomic, readonly) NSSet* actors;
+@property (nonatomic, readonly) id<QQGameTime> time;
+@property (nonatomic, readonly) float now;
+@property (nonatomic, readonly) float elapsed;
+@property (nonatomic, readonly) long  ticks;
 
-@property (nonatomic, readonly, assign) long ticks;
 @property (nonatomic, readonly, getter=isPaused) BOOL paused;
 @property (nonatomic, readwrite, assign) BOOL debugDrawingEnabled;
 
-- (void) start;
-- (void) stop;
+@property (nonatomic, readonly) SPStage* stage;
+@property (nonatomic, readonly) SPDisplayObjectContainer* root;
+@property (nonatomic, readonly) QQLevel* level;
+
+@property (nonatomic, readonly) QQWorld* world;
+
+@property (nonatomic, readonly, retain) NSSet* actors;
 
 
-- (void) tick:(float)elapsed;
+- (void) tick;
 
 - (QQGame*) addActor:(QQActor*)actor;
+// - (QQGame*) addActorAndShape:(QQActor*)actor;
 - (QQGame*) removeActor:(QQActor*)actor;
+- (QQGame*) destroyActor:(QQActor*)actor;
+
+- (void) start;
+- (void) stop;
 
 
 + (QQGame*) current;
index 60bddbc..1a06899 100644 (file)
@@ -1,16 +1,19 @@
 #import <Box2D/Box2D.h>
 
+#import "Tanks.h"
+#import "qq/NSSet+QQExtensions.h"
 #import "game/QQGame.h"
 #import "game/actor/QQActors.h"
 #import "physics/event/QQContactNotification.h"
 
 
-
 static QQGame* _CurrentGame = NULL;
 
 
 
 @interface QQGame ()
+@property (nonatomic, readwrite, retain) NSSet* actors;
+
 - (void) onEnterFrame:(SPEnterFrameEvent*)event;
 - (void) onCollide:(QQContactNotification*)msg;
 @end
@@ -18,12 +21,11 @@ static QQGame* _CurrentGame = NULL;
 
 @implementation QQGame
 
-
 - (id) init {
     if (_CurrentGame) {
         [self release];
         #ifdef DEBUG
-            [NSException raise:@"TooManyGames" format:@"cannot instantiate more than one QQGame at a time!"];
+            [NSException raise:@"WTFTooManyGames" format:@"cannot instantiate more than one QQGame at a time!"];
         #else
             return _CurrentGame;
         #endif
@@ -31,49 +33,56 @@ static QQGame* _CurrentGame = NULL;
     
     if ( (self = [super init]) ){
         _CurrentGame = self;
+        _now = 0;
         _ticks = 0;
+        _elapsed = 0;
         _running = NO;
         
+        _stage = [[SPStage alloc] init];
+        _root  = [[SPSprite sprite] retain];
+        [_stage addChild:_root];
+        
         _world = [[QQWorld alloc] init];
-        [_world addObserver:self selector:@selector(onCollide:) name:QQ_EVENT_CONTACT_BEGIN];
-        [_world addObserver:self selector:@selector(onCollide:) name:QQ_EVENT_CONTACT_END];
+        _world.proxiedNotifier = self;
+        [_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];
         
-        _root = [SPSprite sprite];
-        [self addChild:_root];
         _level = [[QQLevel alloc] init];
         [_root addChild:_level];
         _debugView = [[QQPhysicsDebugView alloc] initWithWorld:_world];
         [_root addChild:_debugView];
         self.debugDrawingEnabled = YES;
         
-        _actors  = [[NSMutableSet alloc] initWithCapacity:10];
-        _awake   = [[NSMutableSet alloc] initWithCapacity:10];
-        _units   = [[NSMutableSet alloc] initWithCapacity:10];
-        _bullets = [[NSMutableSet alloc] initWithCapacity:10];
+        _actors  = [[NSSet alloc] init];
+        _awake   = [[NSSet alloc] init];
+        _units   = [[NSSet alloc] init];
+        _bullets = [[NSSet alloc] init];
         
         CGSize frame = [UIScreen mainScreen].applicationFrame.size;
         float wMax = frame.width  / _world.scale;
         float hMax = frame.height / _world.scale;
         [[[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];
+        
+        [_stage addEventListener:@selector(logEvent:) atObject:self forType:SP_EVENT_TYPE_ANY];
     }
     return self;
 }
 
 - (void) dealloc {
-    [self removeEventListener:@selector(onEnterFrame:) atObject:self forType:SP_EVENT_TYPE_ENTER_FRAME];
-    [self removeAllChildren];
+    [_stage removeEventListener:@selector(logEvent:) atObject:self forType:SP_EVENT_TYPE_ANY];
+    [_stage removeEventListener:@selector(onEnterFrame:) atObject:self forType:SP_EVENT_TYPE_ENTER_FRAME];
     
-    NSMutableSet* sets[] = {_actors, _awake, _units, _bullets};
-    for (int i = 0; i < 4; i++) {
-        NSMutableSet* set = sets[i];
-        [set removeAllObjects];
-        [set release];
-    }
+    [_actors release];
+    [_awake release];
+    [_units release];
+    [_bullets release];
     
+    [_stage release];
+    [_root release];
     [_level release];
+    
     [_debugView release];
-    [_root release];
     [_world release];
     
     _CurrentGame = NULL;
@@ -83,14 +92,21 @@ static QQGame* _CurrentGame = NULL;
 
 /// properties
 
-@synthesize world  = _world;
-@synthesize level  = _level;
+@synthesize now = _now;
+@synthesize elapsed = _elapsed;
+@synthesize ticks  = _ticks;
 
-@synthesize actors = _actors;
+- (id<QQGameTime>) time { return self; }
 
-@synthesize ticks  = _ticks;
 @synthesize paused = _running;
 
+@synthesize stage  = _stage;
+@synthesize root   = _root;
+@synthesize level  = _level;
+
+@synthesize world  = _world;
+@synthesize actors = _actors;
+
 @synthesize debugDrawingEnabled = _debugDrawingEnabled;
 
 - (void) setDebugDrawingEnabled:(BOOL)enable {
@@ -102,30 +118,37 @@ static QQGame* _CurrentGame = NULL;
 /// methods
 
 - (QQGame*) addActor:(QQActor*)actor {
-    [_actors addObject:actor];
-    [_awake addObject:actor];
-    if ([actor isKindOfClass:[QQUnit class]])
-        [_units addObject:actor];
-    if ([actor isKindOfClass:[QQBullet class]])
-        [_bullets addObject:actor];
+    self.actors = [self.actors setByAddingObject:actor];
+//    if (actor.isActive)
+//        _awake = [_awake setByAddingObject:actor];
+//    if ([actor isKindOfClass:[QQUnit class]])
+//        _units = [_units setByAddingObject:actor];
+//    if ([actor isKindOfClass:[QQBullet class]])
+//        _bullets = [_bullets setByAddingObject:actor];
     return self;
 }
 
 - (QQGame*) removeActor:(QQActor*)actor {
-    [_actors  removeObject:actor];
-    [_awake   removeObject:actor];
-    [_units   removeObject:actor];
-    [_bullets removeObject:actor];
+    self.actors = [self.actors setByRemovingObject:actor];
+//    if (actor.isActive)
+//        _awake = [_awake setByRemovingObject:actor];
+//    if ([actor isKindOfClass:[QQUnit class]])
+//        _units = [_units   setByRemovingObject:actor];
+//    if ([actor isKindOfClass:[QQBullet class]])
+//        _bullets = [_bullets setByRemovingObject:actor];
     return self;
 }
 
+- (QQGame*) destroyActor:(QQActor*)actor {
+    [_world destroy:actor];
+    return [self removeActor:actor];
+}
 
-- (void) tick:(float)elapsed {
-    _ticks++;
-    
+
+- (void) tick {
 #if TANKS_DEBUG_LOG
     if ((_ticks % 100) == 0) {
-        NSLog(@"[%ld] Time passed since last 100 frames: %f", _ticks, elapsed);
+        NSLog(@"[%ld] Time passed since last 100 frames: %f", _ticks, _elapsed);
         for (QQActor* actor in self.actors) {
             b2Vec2 v = actor.body->GetLinearVelocity();
             NSLog(@"[%@ impulse:(%f,%f)]", actor, v.x,v.y);
@@ -133,37 +156,54 @@ static QQGame* _CurrentGame = NULL;
     }
 #endif
     
-    for (QQActor* actor in _awake) [actor tick:elapsed];
+    for (QQActor* actor in self.actors)
+        [actor tick];
     [self.world step];
-    for (QQActor* actor in self.actors) [actor draw];
+    for (QQActor* actor in self.actors)
+        if (!actor.dead) [actor draw];
 }
 
 
 - (void) start {
     if (!_running) {
         _running = YES;
-        [self addEventListener:@selector(onEnterFrame:) atObject:self forType:SP_EVENT_TYPE_ENTER_FRAME];
+        [_stage addEventListener:@selector(onEnterFrame:) atObject:self forType:SP_EVENT_TYPE_ENTER_FRAME];
     }
 }
 
 - (void) stop {
     if (_running) {
-        [self removeEventListener:@selector(onEnterFrame:) atObject:self forType:SP_EVENT_TYPE_ENTER_FRAME];
+        [_stage removeEventListener:@selector(onEnterFrame:) atObject:self forType:SP_EVENT_TYPE_ENTER_FRAME];
         _running = NO;
     }
 }
 
-/// event handlers
 
+/// event handlers
 
 - (void) onEnterFrame:(SPEnterFrameEvent*)event {
-    if (_running) [self tick:event.passedTime];
+    if (_running) {
+        _ticks++;
+        _elapsed = event.passedTime;
+        _now += _elapsed; 
+        [self tick];
+    }
 }
 
 - (void) onCollide:(QQContactNotification*)msg {
     NSLog(@"%@", msg);
 }
 
+- (void) logEvent:(SPEvent*)evt {
+    if ([evt.type isEqualToString:SP_EVENT_TYPE_ENTER_FRAME]) return;
+    NSLog(@"%@", evt);
+}
+
+- (NSString*) description {
+    return [NSString stringWithFormat:@"<%@: %x> {running=%s, #actors=%i, time={now=%f, elapsed=%f, ticks=%l}}",
+            [self class], (long)self, qqBoolToString(self.isPaused), [self.actors count],
+            self.now, self.elapsed, self.ticks];
+}
 
 
 + (QQGame*) current {
diff --git a/src/game/QQGameTime.h b/src/game/QQGameTime.h
new file mode 100644 (file)
index 0000000..9f48c3d
--- /dev/null
@@ -0,0 +1,7 @@
+@protocol QQGameTime
+
+@property (nonatomic, readonly) float now;
+@property (nonatomic, readonly) float elapsed;
+@property (nonatomic, readonly) long  ticks;
+
+@end
index efe27e0..951567e 100644 (file)
@@ -1,5 +1,8 @@
 #include <Box2D/Box2D.h>
 
+@class QQContactNotification;
+
+
 /**
  * Anything that has a physics representation.
  */
  */
 - (void) setupPhysics;
 
+- (void) applyLinearImpulseX:(float)xNs y:(float)yNs;
+
+
+@optional
+- (void) onCollideBegin:(QQContactNotification*)msg;
+- (void) onCollideEnd:(QQContactNotification*)msg;
+
 @end
\ No newline at end of file
index a259237..e10aa65 100644 (file)
@@ -1,10 +1,12 @@
+#import "qq/QQObservable.h"
+
 @class QQGame;
 @class QQWorld;
 
 /**
  * Anything in the game-world.
  */
-@protocol QQThing
+@protocol QQThing <QQObservableNotifier>
 
 @property (nonatomic, readonly) QQGame*  game;
 @property (nonatomic, readonly) QQWorld* world;
index 490a473..63fd4cd 100644 (file)
@@ -1,3 +1,4 @@
+#import "game/QQGameTime.h"
 
 
 /**
@@ -38,7 +39,7 @@
  * Advances the timer by {elapsed} milliseconds, updating ready state if necessary.
  * @return Ready state.
  */
-- (BOOL) tick:(float)elapsed;
+- (BOOL) tick:(id<QQGameTime>)time;
 
 
 + (QQCooldown*) cooldownWithDuration:(float)duration;
index 72d9934..0949e5d 100644 (file)
  * Advances the timer by {elapsed} milliseconds, updating ready state if necessary.
  * @return Ready state.
  */
-- (BOOL) tick:(float)elapsed {
+- (BOOL) tick:(id<QQGameTime>)time {
     if (self.ready)
         return YES;
-    self.elapsed += elapsed;
+    self.elapsed += time.elapsed;
     return self.ready;
 }
 
index 383b243..4eaaf09 100644 (file)
@@ -1,4 +1,6 @@
 #include <Box2D/Box2D.h>
+
+#import "qq/QQNotificationProxy.h"
 #import "game/QQThing.h"
 #import "game/QQActive.h"
 #import "game/QQPhysical.h"
@@ -9,7 +11,7 @@
 @class QQGame;
 
 
-@interface QQActor : NSObject <QQThing, QQActive, QQPhysical, QQDisplayable> {
+@interface QQActor : QQNotificationProxy <QQThing, QQActive, QQPhysical, QQDisplayable> {
     
 @protected
     b2Body* _body;
 @private
     BOOL _active;
     BOOL _dirty;
+    BOOL _dead;
     id<QQActorDelegate> _delegate;
+    NSArray* _cooldowns;
 }
 
 @property (nonatomic, readonly) QQGame*  game;
 @property (nonatomic, readonly) QQWorld* world;
 @property (nonatomic, readwrite, retain) id<QQActorDelegate> delegate;
+@property (nonatomic, readonly) BOOL dead;
 
 - (id) initAtX:(float)x y:(float)y;
 - (id) initType:(b2BodyType)type atX:(float)x y:(float)y;
 
+- (void) destroy;
+
+
 /// internal methods
 
 /** Updates the Shape using values from Box2D. */
index 3c6b7a0..241226b 100644 (file)
@@ -7,12 +7,14 @@
 
 /// properties
 
-@synthesize active   = _active;
-@synthesize dirty    = _dirty;
-@synthesize delegate = _delegate;
-
 @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; }
 
-- (float) rotation { return self.body->GetAngle(); }
-- (void) setRotation:(float)r { self.body->SetTransform(self.body->GetPosition(), r); }
+- (id) implicitNotificationSender { return self; }
+- (void) setImplicitNotificationSender:(id)sender {}
+
+- (id<QQObservableNotifier>) proxiedNotifier { return self.game; }
+- (void) setProxiedNotifier:(id<QQObservableNotifier>)notifier {}
+
+
+- (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();
     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); }
+
+
 - (void) updateShapeX:(float)x y:(float)y {
     float px = self.world.scale;
     [self.shape setCenterX:x*px y:y*px];
 }
 
 
-- (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];       }
-
 
 /// initializers
 
 
 - (id) initType:(b2BodyType)type atX:(float)x y:(float)y {
     if ((self = [super init])) {
+        _active = YES;
+        _dead = NO;
+        self.proxiedNotifier = self.game;
+        self.cooldowns = [NSArray array];
         [self.game addActor:self];
         
         b2BodyDef bd;
         bd.position = b2Vec2(x, y);
         bd.userData = self;
         _body = self.world.world->CreateBody(&bd);
+        
+        if ([self respondsToSelector:@selector(onCollideBegin:)])
+            [self addObserver:self selector:@selector(onCollideBegin:) name:QQ_EVENT_CONTACT_BEGIN];
     }
     return self;
 }
 
+- (void) dealloc {
+    self.delegate = nil;
+    [_cooldowns release];
+    [super dealloc];
+}
+
+
 
 /// methods
 
-- (void) tick:(float)elapsed {
-    [self act];
+- (void) destroy {
+    NSLog(@"retainCount=%i", [self retainCount]);
+    if (!self.dead) {
+        _dead = YES;
+        self.shape = nil;
+        [self.game destroyActor:self];
+        NSLog(@"%@ \v\tretainCount=%i \v\tgame=%@", self, [self retainCount], self.game);
+    }
+}
+
+- (void) tick {
+    if (!self.dead) {
+        [self.cooldowns makeObjectsPerformSelector:@selector(tick:)withObject:self.game.time];
+        if (self.isActive) [self act];
+    }
 }
 
 - (void) act {}
 
 - (void) setupPhysics {}
 
+
+- (void) applyLinearImpulseX:(float)xNs y:(float)yNs {
+    self.body->ApplyLinearImpulse(b2Vec2(xNs, yNs), self.body->GetPosition());
+}
+
+
 - (NSString*) description {
     b2Transform trans = self.body->GetTransform();
     b2Vec2 pos = trans.position;
index bd83bb5..f7bad80 100644 (file)
@@ -1,5 +1,5 @@
-#import "QQActor.h"
-#import "QQActorDelegate.h"
-#import "QQUnit.h"
-#import "bullet/QQBullet.h"
-#import "unit/QQTank.h"
+#import "game/actor/QQActor.h"
+#import "game/actor/QQActorDelegate.h"
+#import "game/actor/QQUnit.h"
+#import "game/actor/bullet/QQBullet.h"
+#import "game/actor/unit/QQTank.h"
index b09e9f1..f68c5fc 100644 (file)
@@ -51,7 +51,7 @@
 }
 
 - (void) dealloc {
-    [self.game removeChild:_shape];
+    [self.game.level removeChild:_shape];
     [_shape release];
     [super dealloc];
 }
         [_shape release];
         
         _shape = [newShape retain];
-        [self updateShape];
-        [self.game.level addChild:_shape];
+        if (_shape) {
+            [self updateShape];
+            [self.game.level addChild:_shape];
+        }
     }
 }
 
index 6d66f6b..4a61675 100644 (file)
@@ -10,6 +10,5 @@
 
 - (id) init:(QQUnit*)owner x:(float)x y:(float)y;
 
-- (void) applyLinearImpulseX:(float)xNs y:(float)yNs;
 
 @end
index 597e347..99b3f42 100644 (file)
@@ -2,6 +2,7 @@
 #import "SparrowExtras.h"
 
 #import "QQBullet.h"
+#import "physics/event/QQContactNotification.h"
 
 
 
     return self;
 }
 
-- (void) applyLinearImpulseX:(float)xNs y:(float)yNs {
-    self.body->ApplyLinearImpulse(b2Vec2(xNs, yNs), self.body->GetPosition());
+- (void) onCollideBegin:(QQContactNotification*)msg {
+    QQActor* other = (msg.actorA && (msg.actorA != self) ? msg.actorA : 
+                      (msg.actorB && (msg.actorB != self) ? msg.actorB : nil));
+    if (other) {
+        NSLog(@"BOOM! %@", msg);
+        [other destroy];
+        [self destroy];
+    }
+}
+
+- (void) dealloc {
+    [super dealloc];
 }
 
 
index fa94b81..4b2622b 100644 (file)
@@ -1,5 +1,6 @@
 #import "QQTank.h"
 #import "render/QQSparrowExtensions.h"
+#import "game/QQGame.h"
 #import "game/actor/bullet/QQBullet.h"
 
 
 - (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.game addEventListener:@selector(onTouch:) atObject:self forType:SP_EVENT_TYPE_TOUCH];
+        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 removeEventListener:@selector(onTouch:) atObject:self forType:SP_EVENT_TYPE_TOUCH];
+    [self.game.stage removeEventListener:@selector(onTouch:) atObject:self forType:SP_EVENT_TYPE_TOUCH];
+    [_coolAtk release];
     [super dealloc];
 }
 
-- (void) tick:(float)elapsed {
-    [self.coolAtk tick:elapsed];
-    [super tick:elapsed];
-}
 
 - (void) onTouch:(SPTouchEvent*)event {
-    SPTouch* touch = [[event touchesWithTarget:(SPDisplayObject*)self.game] anyObject];
+    SPTouch* touch = [[event touchesWithTarget:(SPDisplayObject*)self.game.stage] anyObject];
     if (touch && [self.coolAtk activate]) {
         // Touch's normal vector in local coords (rel unit origin)
         SPPoint* normVec = [[[touch locationInSpace:self.shape.parent] subtractPoint:self.shape.position] normalize];
@@ -51,7 +50,7 @@
     }
 }
 
-- (void) moveOnTouch:(SPTouchEvent*)event {
+- (void) _moveOnTouch:(SPTouchEvent*)event {
     SPTouch* touch = [[event touchesWithTarget:self.shape.parent] anyObject];
     if (touch) {
         SPPoint* touchPosition = [touch locationInSpace:self.shape.parent];
index c5ae17d..3b47e73 100644 (file)
@@ -1,26 +1,27 @@
 #include <Box2D/Box2D.h>
-#import "qq/QQObservable.h"
 #import "qq/QQNotifier.h"
+#import "qq/QQNotificationProxy.h"
 #import "physics/event/QQPhysicalEvents.h"
 #import "physics/debug/QQGLESDebugDraw.h"
+#import "game/QQPhysical.h"
 
 
-@interface QQWorld : NSObject <QQNotifier, QQContactNotifier> {
+
+@interface QQWorld : QQNotificationProxy <QQContactNotifier> {
 
 @private
-    QQGLESDebugDraw* _debugDraw;
-    QQContactListener _contactListener;
-    b2World* _world;
-    
-    NSNotificationCenter* _center;
-    
-    NSMutableArray* _walls; // Array of NSValue<b2Body*>
-    
     float   _timestep;
     int     _velocityIterations;
     int     _positionIterations;
     
     float   _scale;
+    
+    b2World* _world;
+    QQContactListener _contactListener;
+    QQGLESDebugDraw* _debugDraw;
+    
+    NSMutableArray* _walls; // Array of NSValue<b2Body*>
+    NSMutableSet* _trash;
 }
 
 @property (nonatomic, readonly) b2World* world;
@@ -39,5 +40,6 @@
 - (void) step;
 - (void) drawDebug;
 
+- (void) destroy:(id<QQPhysical>)actor;
 
 @end
index 9e663ae..02cf916 100644 (file)
@@ -5,8 +5,12 @@
 #import "physics/event/QQContactNotification.h"
 
 
+
+
 // private interface
 @interface QQWorld ()
+@property (nonatomic, readonly, getter=isDeleteLocked) bool deleteLocked;
+
 - (void) postContactNotification:(b2Contact*)contact event:(NSString*)evt;
 - (void) createWalls;
 @end
@@ -28,7 +32,6 @@
         _velocityIterations = 10;
         _positionIterations = 10;
         _scale = 5.0f;
-        _center = [[NSNotificationCenter alloc] init];
         
         _world = new b2World(b2Vec2(0.0f, 0.0f), true); // b2World(gravity, doSleep)
         
         _debugDraw->SetAllFlags();
         _world->SetDebugDraw(_debugDraw);
         
+        _trash = [[NSMutableSet alloc] initWithCapacity:4];
         _walls = [[NSMutableArray alloc] initWithCapacity:4]; // Array of NSValue<b2Body*>
+        
         [self createWalls];
     }
     return self;
 }
 
 - (void) dealloc {
-    [_walls removeAllObjects];
-    [_walls release];
-    [_center release];
+    [_walls removeAllObjects];     [_walls release];
+    [_trash removeAllObjects]; [_trash release];
     delete _debugDraw;
     delete _world; // b2World destructor destroys all physics entities and releases all heap memory.
     [super dealloc];
     }
 }
 
+- (bool) isDeleteLocked {
+    return _world->IsLocked();
+}
 
 - (void) step {
     _world->Step(self.timestep, self.velocityIterations, self.positionIterations);
+    if (self.isDeleteLocked) {
+        NSLog(@"wtf! still delete-locked after step completed!");
+        #ifdef DEBUG
+            [NSException raise:@"WTFWorldStillDeleteLocked" format:@"World still delete-locked after step completed!"];
+        #endif
+    } else if ([_trash count] > 0) {
+        for (id<QQPhysical> actor in _trash)
+            [self destroy:actor];
+        [_trash removeAllObjects];
+    }
 }
 
 - (void) drawDebug {
     _world->SetGravity(b2Vec2(x,y));
 }
 
+- (void) destroy:(id<QQPhysical>)actor {
+    if (self.isDeleteLocked) {
+        [_trash addObject:actor];
+    } else {
+        if (actor.fixture) {
+            actor.fixture->GetBody()->DestroyFixture(actor.fixture);
+        } else if (actor.body) {
+            _world->DestroyBody(actor.body);
+        } else {
+            NSLog(@"wtf! Cannot destroy actor with no body or fixture! %@", actor);
+        }
+    }
+}
+
 
 /// QQContactNotifier
 
 }
 
 
-
-/// observable
-
-- (void) addObserver:(id)observer selector:(SEL)selector name:(NSString*)event {
-    [self addObserver:observer selector:selector name:event object:self];
-}
-
-// Remove a listener from this object
-- (void) removeObserver:(id)observer {
-    [self removeObserver:observer name:nil object:self];
-}
-- (void) removeObserver:(id)observer name:(NSString*)event {
-    [self removeObserver:observer name:event object:self];
-}
-
-// Send a notification from this object
-- (void) postNotificationName:(NSString*)name {
-    [self postNotificationName:name object:self];
-}
-- (void) postNotificationName:(NSString*)name userInfo:(NSDictionary*)info {
-    [self postNotificationName:name object:self userInfo:info];
-}
-
-
-/// notifier
-
-- (void) addObserver:(id)observer selector:(SEL)selector name:(NSString*)event object:(id)sender {
-    [_center addObserver:observer selector:selector name:event object:sender];
-}
-
-- (void) removeObserver:(id)observer name:(NSString*)event object:(id)sender {
-    [_center removeObserver:observer name:event object:sender];
-}
-- (void) removeObservers:(id)observer {
-    [_center removeObserver:observer];
-}
-
-- (void) postNotification:(NSNotification*)msg {
-    [_center postNotification:msg];
-}
-- (void) postNotificationName:(NSString*)name object:(id)sender {
-    [_center postNotificationName:name object:sender];
-}
-- (void) postNotificationName:(NSString*)name object:(id)sender userInfo:(NSDictionary*)info {
-    [_center postNotificationName:name object:sender userInfo:info];
-}
-
 @end
index 2f36a4b..76911ed 100644 (file)
 - (id) initWithName:(NSString*)name object:(id)obj userInfo:(NSDictionary*)info contact:(b2Contact*)contact {
     if ((self = [super initWithName:name object:obj userInfo:info])) {
         _contact = contact;
+        [[self.actorA retain] autorelease];
+        [[self.actorB retain] autorelease];
     }
     return self;
 }
 
-
 @synthesize contact = _contact;
 
 - (QQActor*) actorA {
@@ -42,7 +43,7 @@
 
 
 - (NSString*) description {
-    return [NSString stringWithFormat:@"[%@ actorA=%@, actorB=%@]",
+    return [NSString stringWithFormat:@"[%@ \v\tactorA=%@, \v\tactorB=%@]",
             [super description], self.actorA, self.actorB];
 }
 
diff --git a/src/qq/NSSet+QQExtensions.h b/src/qq/NSSet+QQExtensions.h
new file mode 100644 (file)
index 0000000..80780dd
--- /dev/null
@@ -0,0 +1,8 @@
+
+@interface NSSet (QQExtensions)
+
+- (NSSet*) setByRemovingObject:(id)object;
+- (NSSet*) setByRemovingArray:(NSArray*)objects;
+
+
+@end
diff --git a/src/qq/NSSet+QQExtensions.mm b/src/qq/NSSet+QQExtensions.mm
new file mode 100644 (file)
index 0000000..6b4d74c
--- /dev/null
@@ -0,0 +1,15 @@
+@implementation NSSet (QQExtensions)
+
+- (NSSet*) setByRemovingObject:(id)object {
+    // (BOOL (^)(id obj, BOOL *stop))predicate
+    return [self objectsPassingTest:^(id item, BOOL *stop){
+        return (BOOL) (item != object);
+    }];
+}
+- (NSSet*) setByRemovingArray:(NSArray*)objects {
+    return [self objectsPassingTest:^(id item, BOOL *stop){
+        return (BOOL) ![objects containsObject:item];
+    }];
+}
+
+@end
diff --git a/src/qq/QQNotificationCenter.h b/src/qq/QQNotificationCenter.h
new file mode 100644 (file)
index 0000000..65c60ec
--- /dev/null
@@ -0,0 +1,16 @@
+#import "qq/QQNotifier.h"
+
+@interface QQNotificationCenter : NSNotificationCenter <QQObservableNotifier> {
+@private
+    id _implicitNotificationSender;
+}
+/** Object used for implicit sender when adding or removing observers. */
+@property (nonatomic, readwrite, retain) 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;
+
+@end
diff --git a/src/qq/QQNotificationCenter.mm b/src/qq/QQNotificationCenter.mm
new file mode 100644 (file)
index 0000000..232c794
--- /dev/null
@@ -0,0 +1,69 @@
+#import "qq/QQNotificationCenter.h"
+
+
+@implementation QQNotificationCenter
+
+- (id) init {
+    if ((self = [super init])){
+        _implicitNotificationSender = self;
+    }
+    return self;
+}
+
+- (void) dealloc {
+    [_implicitNotificationSender release];
+    [super dealloc];
+}
+
+@synthesize implicitNotificationSender = _implicitNotificationSender;
+
+
+/// observable
+
+- (void) addObserver:(id)observer selector:(SEL)selector name:(NSString*)event {
+    [self addObserver:observer selector:selector name:event object:self.implicitNotificationSender];
+}
+
+// Remove a listener from this object
+- (void) removeObserver:(id)observer {
+    [self removeObserver:observer name:nil object:self.implicitNotificationSender];
+}
+- (void) removeObserver:(id)observer name:(NSString*)event {
+    [self removeObserver:observer name:event object:self.implicitNotificationSender];
+}
+
+// Send a notification from this object
+- (void) postNotificationName:(NSString*)name {
+    [self postNotificationName:name object:self.implicitNotificationSender];
+}
+- (void) postNotificationName:(NSString*)name userInfo:(NSDictionary*)info {
+    [self postNotificationName:name object:self.implicitNotificationSender userInfo:info];
+}
+
+
+/// notifier
+
+- (void) removeObservers:(id)observer {
+    [super removeObserver:observer];
+}
+
+
+/// NSNotificationCenter (superclass) provides:
+
+// - (void) addObserver:(id)observer selector:(SEL)selector name:(NSString*)event object:(id)sender
+// - (void) removeObserver:(id)observer name:(NSString*)event object:(id)sender
+// - (void) postNotification:(NSNotification*)msg
+// - (void) postNotificationName:(NSString*)name object:(id)sender
+// - (void) postNotificationName:(NSString*)name object:(id)sender userInfo:(NSDictionary*)info
+
+// as well as...
+// - (id)addObserverForName:(NSString *)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *))block
+
+
++ (QQNotificationCenter*) notifierWithTarget:(id)target {
+    QQNotificationCenter* notifier = [[[QQNotificationCenter alloc] init] autorelease];
+    notifier.implicitNotificationSender = target;
+    return notifier;
+}
+
+@end
diff --git a/src/qq/QQNotificationProxy.h b/src/qq/QQNotificationProxy.h
new file mode 100644 (file)
index 0000000..4b99c54
--- /dev/null
@@ -0,0 +1,16 @@
+#import "qq/QQNotifier.h"
+
+/**
+ * Proxies all QQObservableNotifier calls to another QQObservableNotifier.
+ */
+@interface QQNotificationProxy : NSObject <QQObservableNotifier> {
+@private
+    id<QQObservableNotifier> _proxiedNotifier;
+    id _implicitNotificationSender;
+}
+
+@property (nonatomic, readwrite, retain) id<QQObservableNotifier> proxiedNotifier;
+
+- (id) initWithNotifier:(id<QQObservableNotifier>)notifier;
+
+@end
diff --git a/src/qq/QQNotificationProxy.mm b/src/qq/QQNotificationProxy.mm
new file mode 100644 (file)
index 0000000..39b4697
--- /dev/null
@@ -0,0 +1,70 @@
+#import "qq/QQNotificationProxy.h"
+
+
+@implementation QQNotificationProxy
+
+@synthesize proxiedNotifier = _proxiedNotifier;
+@synthesize implicitNotificationSender = _implicitNotificationSender;
+
+
+- (id) initWithNotifier:(id<QQObservableNotifier>)notifier {
+    if ((self = [super init])){
+        self.proxiedNotifier = notifier;
+    }
+    return self;
+}
+
+- (void) dealloc {
+    self.proxiedNotifier = nil;
+    [super dealloc];
+}
+
+/// observable
+
+- (void) addObserver:(id)observer selector:(SEL)selector name:(NSString*)event {
+    [self addObserver:observer selector:selector name:event object:self.implicitNotificationSender];
+}
+
+// Remove a listener from this object
+- (void) removeObserver:(id)observer {
+    [self removeObserver:observer name:nil object:self.implicitNotificationSender];
+}
+- (void) removeObserver:(id)observer name:(NSString*)event {
+    [self removeObserver:observer name:event object:self.implicitNotificationSender];
+}
+
+// Send a notification from this object
+- (void) postNotificationName:(NSString*)name {
+    [self postNotificationName:name object:self.implicitNotificationSender];
+}
+- (void) postNotificationName:(NSString*)name userInfo:(NSDictionary*)info {
+    [self postNotificationName:name object:self.implicitNotificationSender userInfo:info];
+}
+
+
+/// notifier
+
+- (void) addObserver:(id)observer selector:(SEL)selector name:(NSString*)event object:(id)sender {
+    [self.proxiedNotifier addObserver:observer selector:selector name:event object:sender];
+}
+
+- (void) removeObserver:(id)observer name:(NSString*)event object:(id)sender {
+    [self.proxiedNotifier removeObserver:observer name:event object:sender];
+}
+- (void) removeObservers:(id)observer {
+    [self.proxiedNotifier removeObserver:observer];
+}
+
+- (void) postNotification:(NSNotification*)msg {
+    [self.proxiedNotifier postNotification:msg];
+}
+- (void) postNotificationName:(NSString*)name object:(id)sender {
+    [self.proxiedNotifier postNotificationName:name object:sender];
+}
+- (void) postNotificationName:(NSString*)name object:(id)sender userInfo:(NSDictionary*)info {
+    [self.proxiedNotifier postNotificationName:name object:sender userInfo:info];
+}
+
+
+
+@end
index 839154f..1ca2a5d 100644 (file)
@@ -1,11 +1,21 @@
 #import "qq/QQObservable.h"
 
-@protocol QQNotifier <QQObservable>
 
+@protocol QQNotifier
+
+/** 
+ * Adds an observer of specified object.
+ * 
+ * A nil name means observer will be notified of all events for specified object.
+ * A nil object means observer will be notified of all events for specified name.
+ * An observer specifying nil for both name and object will be notified of all events.
+ */
 - (void) addObserver:(id)observer selector:(SEL)selector name:(NSString*)event object:(id)sender;
 
 - (void) removeObserver:(id)observer name:(NSString*)event object:(id)sender;
-- (void) removeObservers:(id)observer; // remove all from all objects
+
+/** Removes observer from all subscribed notices. */
+- (void) removeObservers:(id)observer;
 
 - (void) postNotification:(NSNotification*)msg;
 - (void) postNotificationName:(NSString*)name object:(id)sender;
@@ -13,3 +23,7 @@
 
 
 @end
+
+@protocol QQObservableNotifier <QQObservable, QQNotifier>
+
+@end
\ No newline at end of file
diff --git a/src/qq/QQNotifiers.h b/src/qq/QQNotifiers.h
new file mode 100644 (file)
index 0000000..f36c283
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+ * Various macros relating to notification dispatch.
+ */
+
+/**
+ * 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];                            \
+}
+
diff --git a/src/qq/QQObject.h b/src/qq/QQObject.h
deleted file mode 100644 (file)
index e1a151f..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-@interface QQObject : NSObject {
-@private
-    
-}
-
-
-
-@end
diff --git a/src/qq/QQObject.mm b/src/qq/QQObject.mm
deleted file mode 100644 (file)
index 9bd7ea9..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-#import "QQObject.h"
-
-
-@implementation QQObject
-
-- (id) init {
-    if ((self = [super init])){
-        
-    }
-    return self;
-}
-@end
index ff4e25d..8814775 100644 (file)
@@ -4,6 +4,9 @@
  * that would normally take an object if the call was made to an NSNotificationCenter.
  */
 @protocol QQObservable
+
+@property (nonatomic, readwrite, retain) id implicitNotificationSender;
+
 //- (void) observe:(NSString*)event notify:(id)observer selector:(SEL)selector;
 
 /** Adds an observer of this object. */
index 10b0e32..2b2e6e4 100644 (file)
@@ -6,16 +6,13 @@
 @interface QQAppDelegate : NSObject <UIApplicationDelegate> {
 
 @private
-    UIApplication* _app;
     UIWindow* window;
     SPView* sparrowView;
-    // QQPhysicsDebugView* _debugView;
     QQGame* _game;
 }
 
-@property (nonatomic, retain) IBOutlet UIWindow* window;
-@property (nonatomic, retain) IBOutlet SPView* sparrowView;
-// @property (nonatomic, retain) QQPhysicsDebugView* debugView;
-@property (nonatomic, retain) QQGame* game;
+@property (nonatomic, readwrite, retain) IBOutlet UIWindow* window;
+@property (nonatomic, readwrite, retain) IBOutlet SPView* sparrowView;
+@property (nonatomic, readwrite, retain) QQGame* game;
 
 @end
\ No newline at end of file
index 9d65e17..5cf6f75 100644 (file)
 @synthesize game = _game;
 
 
-- (void) applicationDidFinishLaunching:(UIApplication*)application {
-    _app = application;
-    _app.statusBarHidden = YES;
-    
+- (void) applicationDidFinishLaunching:(UIApplication*)app {
     SP_CREATE_POOL(pool);
     
+    [app setStatusBarHidden:YES];
     [SPStage setSupportHighResolutions:YES];
-    [SPAudioEngine start];
     if ( sparrowView.frameRate != 60.0f )
         sparrowView.frameRate = 60.0f;
     
-    self.game = [[[QQGame alloc] init] autorelease];
-    sparrowView.stage = _game;
+    self.game = [QQGame current];
+    sparrowView.stage = _game.stage;
     [_game start];
     
     [window makeKeyAndVisible];
     [sparrowView start];
+    [SPAudioEngine start];
     
     SP_RELEASE_POOL(pool);
 }
 
-- (void) applicationWillResignActive:(UIApplication*)application {
+- (void) applicationWillResignActive:(UIApplication*)app {
     [sparrowView stop];
 }
 
-- (void) applicationDidBecomeActive:(UIApplication*)application {
+- (void) applicationDidBecomeActive:(UIApplication*)app {
     [sparrowView start];
 }
 
 - (void) dealloc {
-    _app = nil;
     [_game release];
     [sparrowView release];
     [window release];
index 236f4b9..79377d3 100644 (file)
                49966919136930E8006E8125 /* OpenAL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49966914136930E8006E8125 /* OpenAL.framework */; };
                4996691A136930E8006E8125 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49966915136930E8006E8125 /* OpenGLES.framework */; };
                4996691B136930E8006E8125 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49966916136930E8006E8125 /* QuartzCore.framework */; };
+               499D6A9C1399BFB8008E4C0D /* QQNotificationCenter.h in Headers */ = {isa = PBXBuildFile; fileRef = 499D6A981399BFB8008E4C0D /* QQNotificationCenter.h */; };
+               499D6A9D1399BFB8008E4C0D /* QQNotificationCenter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 499D6A991399BFB8008E4C0D /* QQNotificationCenter.mm */; };
+               499D6A9E1399BFB8008E4C0D /* QQNotificationProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 499D6A9A1399BFB8008E4C0D /* QQNotificationProxy.h */; };
+               499D6A9F1399BFB8008E4C0D /* QQNotificationProxy.mm in Sources */ = {isa = PBXBuildFile; fileRef = 499D6A9B1399BFB8008E4C0D /* QQNotificationProxy.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 */; };
                49DA67D5137847A7004841E9 /* QQWorld.mm in Sources */ = {isa = PBXBuildFile; fileRef = 49DA67D3137847A7004841E9 /* QQWorld.mm */; };
+               49E3C5D2139A73E700A3958A /* NSSet+QQExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 49E3C5D0139A73E700A3958A /* NSSet+QQExtensions.h */; };
+               49E3C5D3139A73E700A3958A /* NSSet+QQExtensions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 49E3C5D1139A73E700A3958A /* NSSet+QQExtensions.mm */; };
+               49E67E11139B341600DDFC07 /* TanksMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 49E67E10139B341600DDFC07 /* TanksMacros.h */; };
                49E834A713812427007A6598 /* QQActive.h in Headers */ = {isa = PBXBuildFile; fileRef = 49E8349E13812427007A6598 /* QQActive.h */; };
                49E834A813812427007A6598 /* QQActor.h in Headers */ = {isa = PBXBuildFile; fileRef = 49E834A013812427007A6598 /* QQActor.h */; };
                49E834A913812427007A6598 /* QQActor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 49E834A113812427007A6598 /* QQActor.mm */; };
                49966914136930E8006E8125 /* OpenAL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenAL.framework; path = System/Library/Frameworks/OpenAL.framework; sourceTree = SDKROOT; };
                49966915136930E8006E8125 /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; };
                49966916136930E8006E8125 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
+               499D6A981399BFB8008E4C0D /* QQNotificationCenter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QQNotificationCenter.h; sourceTree = "<group>"; };
+               499D6A991399BFB8008E4C0D /* QQNotificationCenter.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = QQNotificationCenter.mm; sourceTree = "<group>"; };
+               499D6A9A1399BFB8008E4C0D /* QQNotificationProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QQNotificationProxy.h; sourceTree = "<group>"; };
+               499D6A9B1399BFB8008E4C0D /* QQNotificationProxy.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = QQNotificationProxy.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>"; };
                49DA67D3137847A7004841E9 /* QQWorld.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = QQWorld.mm; sourceTree = "<group>"; };
+               49E3C5D0139A73E700A3958A /* NSSet+QQExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSSet+QQExtensions.h"; sourceTree = "<group>"; };
+               49E3C5D1139A73E700A3958A /* NSSet+QQExtensions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSSet+QQExtensions.mm"; sourceTree = "<group>"; };
+               49E67E10139B341600DDFC07 /* TanksMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TanksMacros.h; sourceTree = "<group>"; };
                49E8349E13812427007A6598 /* QQActive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QQActive.h; sourceTree = "<group>"; };
                49E834A013812427007A6598 /* QQActor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QQActor.h; sourceTree = "<group>"; };
                49E834A113812427007A6598 /* QQActor.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = QQActor.mm; sourceTree = "<group>"; };
                4978AD1E1396302E00930447 /* qq */ = {
                        isa = PBXGroup;
                        children = (
+                               49E3C5D0139A73E700A3958A /* NSSet+QQExtensions.h */,
+                               49E3C5D1139A73E700A3958A /* NSSet+QQExtensions.mm */,
+                               499D6A981399BFB8008E4C0D /* QQNotificationCenter.h */,
+                               499D6A991399BFB8008E4C0D /* QQNotificationCenter.mm */,
+                               499D6A9A1399BFB8008E4C0D /* QQNotificationProxy.h */,
+                               499D6A9B1399BFB8008E4C0D /* QQNotificationProxy.mm */,
                                4978AD1F1396302F00930447 /* QQNotification.h */,
                                4978AD201396302F00930447 /* QQNotification.mm */,
                                4978AD211396302F00930447 /* QQNotifier.h */,
                                492D80ED138BA4BC0042D918 /* bullet */,
                                492D80E8138BA4B40042D918 /* unit */,
                                4978AD0D1395E5CE00930447 /* QQActors.h */,
+                               492D80FE138BCC9F0042D918 /* QQActorDelegate.h */,
                                49E834A013812427007A6598 /* QQActor.h */,
                                49E834A113812427007A6598 /* QQActor.mm */,
-                               492D80FE138BCC9F0042D918 /* QQActorDelegate.h */,
                                49E834A213812427007A6598 /* QQUnit.h */,
                                492D80FC138BCA840042D918 /* QQUnit.mm */,
                        );
                                49F2D9B013764666000B6B8C /* main.mm */,
                                49F2D9B213764666000B6B8C /* prefix.pch */,
                                4978AD131396139100930447 /* Tanks.h */,
+                               49E67E10139B341600DDFC07 /* TanksMacros.h */,
                        );
                        path = src;
                        sourceTree = "<group>";
                                4978AD251396302F00930447 /* QQNotification.h in Headers */,
                                4978AD271396302F00930447 /* QQNotifier.h in Headers */,
                                4978AD2A1396302F00930447 /* QQObservable.h in Headers */,
+                               499D6A9C1399BFB8008E4C0D /* QQNotificationCenter.h in Headers */,
+                               499D6A9E1399BFB8008E4C0D /* QQNotificationProxy.h in Headers */,
+                               49E3C5D2139A73E700A3958A /* NSSet+QQExtensions.h in Headers */,
+                               49E67E11139B341600DDFC07 /* TanksMacros.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                4978AD1B1396302300930447 /* QQContactNotification.mm in Sources */,
                                4978AD1D1396302300930447 /* QQPhysicalEvents.mm in Sources */,
                                4978AD261396302F00930447 /* QQNotification.mm in Sources */,
+                               499D6A9D1399BFB8008E4C0D /* QQNotificationCenter.mm in Sources */,
+                               499D6A9F1399BFB8008E4C0D /* QQNotificationProxy.mm in Sources */,
+                               49E3C5D3139A73E700A3958A /* NSSet+QQExtensions.mm in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };