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 {