Minor reorg of unit crap.
authordsc <david.schoonover@gmail.com>
Mon, 6 Jun 2011 13:23:56 +0000 (06:23 -0700)
committerdsc <david.schoonover@gmail.com>
Mon, 6 Jun 2011 13:23:56 +0000 (06:23 -0700)
34 files changed:
src/Tanks.h
src/TanksMacros.h
src/game/QQActive.h
src/game/QQDisplayable.h
src/game/QQGame.h
src/game/QQGame.mm
src/game/QQPhysical.h
src/game/QQThing.h
src/game/ability/QQCooldown.h
src/game/ability/QQCooldown.mm
src/game/unit/QQActor.h
src/game/unit/QQActor.mm
src/game/unit/QQBullet.h
src/game/unit/QQBullet.mm
src/game/unit/QQTank.h
src/game/unit/QQTank.mm
src/game/unit/QQUnit.h
src/game/unit/QQUnit.mm
src/qq/Collections+FP.h [new file with mode: 0644]
src/qq/Collections+FP.mm [new file with mode: 0644]
src/qq/NSArray+QQExtensions.h [new file with mode: 0644]
src/qq/NSArray+QQExtensions.mm [new file with mode: 0644]
src/qq/NSDictionary+QQExtensions.h [new file with mode: 0644]
src/qq/NSDictionary+QQExtensions.mm [new file with mode: 0644]
src/qq/NSSet+QQExtensions.h
src/qq/NSSet+QQExtensions.mm
src/qq/event/QQNotificationCenter.h
src/qq/event/QQNotificationCenter.mm
src/qq/event/QQNotificationProxy.h
src/qq/event/QQNotificationProxy.mm
src/qq/event/QQNotifier.h
src/qq/event/QQNotifiers.h
src/qq/event/QQObservable.h
tanks.xcodeproj/project.pbxproj

index a2a448b..6246017 100644 (file)
@@ -1,14 +1,20 @@
 #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"
index b36a57c..b2c9fd0 100644 (file)
@@ -4,4 +4,7 @@
 #define b2s(__val) ((__val)?("YES"):("NO"))
 
 
+#define ATTACK_COOLDOWN_NAME @"attack"
+
+
 #endif
\ No newline at end of file
index 4bc669b..eba0aed 100644 (file)
@@ -1,16 +1,22 @@
 #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
index ab60dbf..b7031cf 100644 (file)
@@ -12,7 +12,7 @@
 /**
  * Called to setup the Sparrow shape.
  */
-- (SPDisplayObject*) setupShape;
+- (void) setupShape;
 
 /**
  * Called to update appearance after game actions have occurred
index f69644d..aaef0b2 100644 (file)
@@ -1,5 +1,6 @@
 #import "Sparrow.h"
 
+#import "Tanks.h"
 #import "qq/event/QQNotificationCenter.h"
 
 #import "physics/QQWorld.h"
@@ -38,7 +39,7 @@
 @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;
@@ -62,6 +60,9 @@
 /** Pauses the game. */
 - (void) stop;
 
+/** Advances the game clock one step and elapsed ms. */
+- (void) tick:(float)elapsed;
+
 
 + (QQGame*) current;
 
index ad60683..6ec2b28 100644 (file)
@@ -1,6 +1,5 @@
 #import <Box2D/Box2D.h>
 
-#import "Tanks.h"
 #import "game/QQGame.h"
 #import "game/unit/QQActors.h"
 #import "physics/event/QQContactNotification.h"
@@ -11,6 +10,7 @@
 
 - (void) onEnterFrame:(SPEnterFrameEvent*)event;
 - (void) onCollide:(QQContactNotification*)msg;
+- (void) logEvent:(SPEvent*)evt;
 @end
 
 
@@ -25,6 +25,7 @@ static QQGame* _CurrentGame = NULL;
     return _CurrentGame;
 }
 
+
 - (id) init {
     if (_CurrentGame) {
         [self release];
@@ -70,9 +71,10 @@ static QQGame* _CurrentGame = NULL;
         [[[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;
 }
@@ -106,7 +108,7 @@ static QQGame* _CurrentGame = NULL;
 
 - (id<QQGameTime>) time { return self; }
 
-@synthesize paused = _running;
+@synthesize running = _running;
 
 @synthesize stage  = _stage;
 @synthesize root   = _root;
@@ -126,10 +128,11 @@ static QQGame* _CurrentGame = NULL;
 /// 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];
@@ -185,20 +188,22 @@ static QQGame* _CurrentGame = NULL;
 }
 
 - (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
+        ];
 }
 
 
index 951567e..5714c23 100644 (file)
@@ -14,6 +14,7 @@
 /// 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;
 
 
index 8ee089f..231988a 100644 (file)
@@ -1,4 +1,4 @@
-#import "qq/event/QQObservable.h"
+#import "qq/event/QQNotifier.h"
 
 @class QQGame;
 @class QQWorld;
index 63fd4cd..b706bf9 100644 (file)
@@ -1,3 +1,4 @@
+#import "Tanks.h"
 #import "game/QQGameTime.h"
 
 
@@ -6,10 +7,14 @@
  */
 @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;
 
@@ -17,7 +22,7 @@
 @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;
@@ -25,8 +30,8 @@
 /**
  * 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.
@@ -42,7 +47,7 @@
 - (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
index 0949e5d..432d1d9 100644 (file)
@@ -5,10 +5,11 @@
 
 /// 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;
@@ -18,7 +19,7 @@
         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];
 }
 
 
index b5dc695..abc384e 100644 (file)
@@ -1,10 +1,12 @@
 #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;
 
index 8421992..0237ef0 100644 (file)
@@ -1,5 +1,4 @@
 #import "QQActor.h"
-#import "render/QQSparrowExtensions.h"
 #import "game/QQGame.h"
 
 
@@ -7,34 +6,27 @@
 
 /// 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];
 }
 
 
index b4a6241..2d11478 100644 (file)
@@ -1,3 +1,4 @@
+#import "Tanks.h"
 #import "game/unit/QQUnit.h"
 
 
index 99b3f42..ea998f7 100644 (file)
@@ -31,8 +31,8 @@
 }
 
 - (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];
index c4c5971..5850a26 100644 (file)
@@ -1,10 +1,8 @@
 #import "game/unit/QQUnit.h"
-#import "game/ability/QQCooldown.h"
 
 @interface QQTank : QQUnit {
-    QQCooldown* _coolAtk;
+    
 }
 
-@property (nonatomic, readonly, retain) QQCooldown* coolAtk;
 
 @end
index edd130a..edacf1a 100644 (file)
@@ -6,7 +6,6 @@
 
 // private interface
 @interface QQTank ()
-@property (nonatomic, readwrite, retain) QQCooldown* coolAtk;