//
#import <Foundation/Foundation.h>
+#import "Sparrow.h"
#define BE_PARALLAX_DIRECTION_LEFT 1
#define BE_PARALLAX_DIRECTION_RIGHT 2
@synthesize running = mRunning;
- (id)initWithTexture:(SPTexture *)texture {
- if (self = [super init]) {
+ if ((self = [super init])) {
[self initWithTexture:texture speed:1 direction:BE_PARALLAX_DIRECTION_LEFT];
}
return self;
}
- (id)initWithTexture:(SPTexture *)texture speed:(float)speed {
- if (self = [super init]) {
+ if ((self = [super init])) {
[self initWithTexture:texture speed:speed direction:BE_PARALLAX_DIRECTION_LEFT];
}
return self;
}
- (id)initWithTexture:(SPTexture *)texture speed:(float)speed direction:(int)direction {
- if (self = [super init]) {
+ if ((self = [super init])) {
mRunning = YES;
mDirection = direction;
if (direction < 1 || direction > 4) {
#import "SHCircle.h"
#import "SHLine.h"
#import "SHPolygon.h"
-#import "BEParallaxSprite.h"
+//import "BEParallaxSprite.h"
#import "SXFPSMeter.h"
--- /dev/null
+
+#import "render/QQSparrowExtensions.h"
--- /dev/null
+#ifndef TANKS_MACROS_H
+#define TANKS_MACROS_H
+
+
+
+
+#endif
\ No newline at end of file
@property (nonatomic, getter=isActive) BOOL active;
+- (void) tick:(float)elapsed;
- (void) act;
@end
\ No newline at end of file
*/
@protocol QQDisplayable
-@property (nonatomic, retain, readonly) SPDisplayObject* shape;
+@property (nonatomic, readwrite, assign, getter=isDirty) BOOL dirty; // TODO: implement this and fix various [shape setPosition] calls
+@property (nonatomic, readwrite, retain) SPDisplayObject* shape;
+
+/**
+ * Called to setup the Sparrow shape.
+ */
+- (SPDisplayObject*) setupShape;
/**
* Called to update appearance after game actions have occurred
#import "Sparrow.h"
-#import "game/actor/QQUnit.h"
#import "physics/QQWorld.h"
+#import "game/actor/QQActor.h"
@interface QQGame : SPStage {
NSMutableSet* _bullets;
}
+@property (nonatomic, assign, readonly) long ticks;
+
@property (nonatomic, retain) QQWorld* world;
@property (nonatomic, retain) NSSet* actors;
-@property (nonatomic, assign, readonly) long ticks;
- (QQGame*) addActor:(QQActor*)actor;
- (QQGame*) removeActor:(QQActor*)actor;
-- (void) onEnterFrame:(SPEnterFrameEvent*)event;
-
+ (QQGame*) current;
@end
#import "QQGame.h"
#import "game/actor/unit/QQTank.h"
+#import <Box2D/Box2D.h>
@interface QQGame ()
+- (void) onEnterFrame:(SPEnterFrameEvent*)event;
@end
- (id) init {
if (_CurrentGame) {
[self release];
- [NSException raise:@"TooManyGames" format:@"cannot instantiate more than one QQGame at a time!"];
+ #ifdef DEBUG
+ [NSException raise:@"TooManyGames" format:@"cannot instantiate more than one QQGame at a time!"];
+ #else
+ return _CurrentGame;
+ #endif
}
if ( (self = [super init]) ){
_CurrentGame = self;
- _ticks = 0l;
+ _ticks = 0;
_actors = [[NSMutableArray alloc] initWithCapacity:10];
_world = [[QQWorld alloc] init];
- [self addActor:[[[QQTank alloc] init] autorelease]];
+ [[[QQTank alloc] init] autorelease];
[self addEventListener:@selector(onEnterFrame:) atObject:self forType:SP_EVENT_TYPE_ENTER_FRAME];
}
}
- (void) dealloc {
+ [self removeEventListener:@selector(onEnterFrame:) atObject:self forType:SP_EVENT_TYPE_ENTER_FRAME];
[_actors removeAllObjects];
[_actors release];
[_world release];
- (void) onEnterFrame:(SPEnterFrameEvent*)event {
_ticks++;
- if ((_ticks % 100) == 0)
+ if ((_ticks % 100) == 0) {
NSLog(@"[%ld] Time passed since last 100 frames: %f", _ticks, event.passedTime);
+ for (QQActor* actor in self.actors) {
+ b2Vec2 v = actor.body->GetLinearVelocity();
+ NSLog(@"[%@ pos:(%f,%f) impulse:(%f,%f)]", actor, actor.x,actor.y, v.x,v.y);
+ }
+ }
- for (QQActor* actor in self.actors) [actor act]; // XXX: self.awakeAgents
+ for (QQActor* actor in self.actors) [actor tick:event.passedTime]; // XXX: self.awakeAgents
[self.world step];
for (QQActor* actor in self.actors) [actor draw]; // XXX: self.drawableAgents
}
*/
@protocol QQPhysical
-// @property (nonatomic, readwrite, assign) float x;
-// @property (nonatomic, readwrite, assign) float y;
+@property (nonatomic, readwrite, assign) float x;
+@property (nonatomic, readwrite, assign) float y;
+@property (nonatomic, readwrite) CGPoint position;
+
+- (void) setPositionX:(float)x y:(float)y;
// FIXME: don't expose these, instead aggregate properties from body & fixtures/shapes
@property (nonatomic, readonly) b2Body* body;
@property (nonatomic, readonly) b2Fixture* fixture;
+/**
+ * Called after initialization to create the various bodies, fixtures, and shapes
+ * that represent the Unit in Box2D.
+ */
+- (void) setupPhysics;
@end
\ No newline at end of file
@property (nonatomic, readwrite, assign) float elapsed;
/** Whether Cooldown timer is ready to be activated. */
-@property (nonatomic, readwrite, getter=isReady) BOOL ready;
+@property (nonatomic, readwrite) BOOL ready;
/** Percent completed (elapsed / duration), clamped [0, 1.0]. */
@property (nonatomic, readwrite) float ratio;
/**
- * Implies ready=NO.
+ * Implies ready=YES.
*/
- (id) initWithDuration:(float)duration;
- (id) initWithDuration:(float)duration andReady:(BOOL)ready;
/// initializers
- (id) initWithDuration:(float)duration {
- return [self initWithDuration:duration andReady:NO];
+ return [self initWithDuration:duration andReady:YES];
}
- (id) initWithDuration:(float)duration andReady:(BOOL)ready {
#import "game/QQActive.h"
#import "game/QQPhysical.h"
#import "game/QQDisplayable.h"
+#import "game/actor/QQActorDelegate.h"
#import "physics/QQWorld.h"
@class QQGame;
@private
BOOL _active;
+ BOOL _dirty;
+ id<QQActorDelegate> _delegate;
}
@property (nonatomic, readonly) QQGame* game;
@property (nonatomic, readonly) QQWorld* world;
+@property (nonatomic, readwrite, retain) id<QQActorDelegate> delegate;
- (id) initAtX:(float)x y:(float)y;
- (id) initType:(b2BodyType)type atX:(float)x y:(float)y;
#import "QQActor.h"
+#import "render/QQSparrowExtensions.h"
#import "game/QQGame.h"
@implementation QQActor
-@synthesize active;
+/// properties
+
+@synthesize active = _active;
+@synthesize dirty = _dirty;
+@synthesize delegate = _delegate;
- (QQGame*) game { return QQGame.current; }
- (QQWorld*) world { return self.game.world; }
- (SPDisplayObject*) shape { return nil; }
+- (void) setShape:(SPDisplayObject*)newShape {}
- (b2Body*) body { return _body; }
- (b2Fixture*) fixture { return nil; }
+- (CGPoint) position {
+ b2Vec2 pos = self.body->GetPosition();
+ return CGPointMake(pos.x, pos.y);
+}
+- (void) setPosition:(CGPoint)pos {
+ [self setPositionX:pos.x y:pos.y];
+}
+- (void) setPositionX:(float)x y:(float)y {
+ self.body->SetTransform(b2Vec2(x,y), self.body->GetAngle());
+ float px = self.world.scale;
+ [self.shape setPositionX: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) initAtX:(float)x y:(float)y {
return [self initType:b2_dynamicBody atX:x y:y];
}
- (id) initType:(b2BodyType)type atX:(float)x y:(float)y {
if ((self = [super init])) {
+ [self.game addActor:self];
+
b2BodyDef bd;
bd.type = type;
bd.position = b2Vec2(x, y);
return self;
}
-- (void) act {
- // stub
+
+/// methods
+
+- (void) tick:(float)elapsed {
+ [self act];
+}
+
+- (void) act {}
+
+- (SPDisplayObject*) setupShape {
+ return self.shape;
}
- (void) draw {
- // stub
+ float px = self.world.scale;
+ b2Vec2 pos = self.body->GetPosition();
+ [self.shape setPositionX:pos.x*px y:pos.y*px];
}
+- (void) setupPhysics {}
+
@end
-@protocol QQUnitDelegate
+@protocol QQActorDelegate
- (void) updateWithTick:(float)time;
--- /dev/null
+#import "game/actor/QQActor.h"
+#import "game/QQThing.h"
+
+
+@protocol QQUnit <QQThing>
+
+@property (nonatomic, readonly) BOOL canAttack;
+
+- (void) attackTarget:(QQActor*)actor;
+- (void) attackToward:(CGPoint)point;
+- (void) attackTowardX:(float)x y:(float)y;
+
+
+@end
+
+//typedef id<QQUnit> QQUnit;
+#include <Box2D/Box2D.h>
+#import "Sparrow.h"
+
+#import "physics/QQWorld.h"
#import "game/actor/QQActor.h"
-#import "game/QQThing.h"
+#import "game/actor/QQActorDelegate.h"
+#import "render/QQSparrowExtensions.h"
+
+@interface QQUnit : QQActor {
-@protocol QQUnit <QQThing>
+@private
+ SPDisplayObject* _shape;
+}
+/// properties
@property (nonatomic, readonly) BOOL canAttack;
+- (void) setShapeFromFile:(NSString*)filename;
+
+/// initializers
+- (id) initAtX:(float)x y:(float)y withShape:(SPDisplayObject*)shape;
-- (void) attackTarget:(QQActor*)actor;
+
+/// methods
+
+- (void) attack:(QQActor*)actor;
- (void) attackToward:(CGPoint)point;
- (void) attackTowardX:(float)x y:(float)y;
@end
-
-//typedef id<QQUnit> QQUnit;
#include <Box2D/Box2D.h>
#import "Sparrow.h"
-#import "QQUnit.h"
-#import "QQUnitBase.h"
-// private interface
-@interface QQUnitBase ()
+#import "QQUnit.h"
+#import "render/QQSparrowExtensions.h"
-@end
-////////////////////////////////////////////////////////////////////////////////////
-@implementation QQUnitBase
+@implementation QQUnit
@dynamic world; // Eliminates compiler warnings due to inheritance (!)
@dynamic game;
-////////////////////////////////////////////////////////////////////////////////////
@synthesize shape = _shape;
-@synthesize delegate = _delegate;
- (BOOL) canAttack { return NO; }
-////////////////////////////////////////////////////////////////////////////////////
+/// initializers
+
- (id) init {
- return [self initAtX:15 y:15 withShape:[SPQuad quadWithWidth:50 height:50 color:0xff0000]];
+ return [self initAtX:50 y:50 withShape:[SPQuad quadWithWidth:50 height:50 color:0xff0000]];
}
- (id) initAtX:(float)x y:(float)y withShape:(SPDisplayObject*)shape {
if ((self = [super initAtX:x y:y])) {
- float px = self.world.scale;
- self.shape = [shape setPositionX:x*px y:y*px];
+ self.shape = shape;
+ float px = self.world.scale;
b2PolygonShape box;
box.SetAsBox(shape.width/px, shape.height/px);
self.body->CreateFixture(&box, 5.0f);
-
- [self.game 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 removeChild:_shape];
[_shape release];
[super dealloc];
}
-////////////////////////////////////////////////////////////////////////////////////
+/// methods
+
- (void) setShape:(SPDisplayObject*)newShape {
if (_shape != newShape) {
[self.game removeChild:_shape];
[_shape release];
_shape = [newShape retain];
- // float px = self.world.scale;
- // [_shape setPositionX:x*px y:y*px]
+ float px = self.world.scale;
+ [_shape setPositionX:self.x*px y:self.y*px];
[self.game addChild:_shape];
}
}
-- (void) attackTarget:(QQActor*)actor {
-
+- (void) attack:(QQActor*)actor {
+ [self attackTowardX:actor.x y:actor.y];
}
-
- (void) attackToward:(CGPoint)pt {
[self attackTowardX:pt.x y:pt.y];
}
-
- (void) attackTowardX:(float)x y:(float)y {
}
+++ /dev/null
-#include <Box2D/Box2D.h>
-#import "Sparrow.h"
-
-#import "render/QQSparrowExtensions.h"
-
-#import "game/actor/QQActor.h"
-#import "game/actor/QQUnit.h"
-#import "game/actor/QQUnitDelegate.h"
-
-#import "physics/QQWorld.h"
-
-
-////////////////////////////////////////////////////////////////////////////////////
-@interface QQUnitBase : QQActor <QQUnit> {
-
-@private
- SPDisplayObject* _shape;
- id<QQUnitDelegate> _delegate;
-}
-
-////////////////////////////////////////////////////////////////////////////////////
-@property (nonatomic, retain, readwrite) SPDisplayObject* shape;
-@property (nonatomic, retain, readwrite) id<QQUnitDelegate> delegate;
-
-////////////////////////////////////////////////////////////////////////////////////
-- (id) initAtX:(float)x y:(float)y withShape:(SPDisplayObject*)shape;
-
-- (void) setShapeFromFile:(NSString*)filename;
-
-@end
-#import "game/actor/QQUnitBase.h"
#import "game/actor/QQUnit.h"
-@interface QQBullet : QQUnitBase {
+@interface QQBullet : QQUnit {
@private
- id<QQUnit>* _owner;
+ QQUnit* _owner;
}
-@property (nonatomic, readonly) id<QQUnit>* owner;
+@property (nonatomic, readonly) QQUnit* owner;
-- (id) initAtX:(float)x y:(float)y withOwner:(id<QQUnit>*)owner;
+- (id) init:(QQUnit*)owner x:(float)x y:(float)y;
+- (void) applyLinearImpulseX:(float)xNs y:(float)yNs;
@end
-#import "QQBullet.h"
-
+#include <Box2D/Box2D.h>
+#import "SparrowExtras.h"
-// private interface
-@interface QQBullet ()
+#import "QQBullet.h"
-@end
@implementation QQBullet
@synthesize owner = _owner;
-- (id) initAtX:(float)x y:(float)y withOwner:(id<QQUnit>*)owner {
- if ((self = [super init])){
+- (id) init:(QQUnit*)owner x:(float)x y:(float)y {
+ if ((self = [super initAtX:x y:y])){
+ _owner = owner;
+ float px = self.world.scale;
+ float radius = 1.5f;
+ self.shape = [SHCircle circleWithWidth:2*radius*px height:2*radius*px];
+
+ b2CircleShape circle;
+ circle.m_radius = radius;
+ b2FixtureDef fix;
+ fix.shape = &circle;
+ fix.restitution = 1.0f;
+ fix.density = 0.1f;
+ self.body->CreateFixture(&fix);
}
return self;
}
+- (void) applyLinearImpulseX:(float)xNs y:(float)yNs {
+ self.body->ApplyLinearImpulse(b2Vec2(xNs, yNs), self.body->GetPosition());
+}
+
@end
-#import "game/actor/QQUnitBase.h"
+#import "game/actor/QQUnit.h"
+#import "game/ability/QQCooldown.h"
-@interface QQTank : QQUnitBase {
-
+@interface QQTank : QQUnit {
+ QQCooldown* _coolAtk;
}
+@property (nonatomic, readonly, retain) QQCooldown* coolAtk;
+
@end
#import "QQTank.h"
+#import "render/QQSparrowExtensions.h"
+#import "game/actor/bullet/QQBullet.h"
// private interface
@interface QQTank ()
-
+@property (nonatomic, readwrite, retain) QQCooldown* coolAtk;
- (void) onTouch:(SPTouchEvent*)event;
-
@end
+
@implementation QQTank
+@synthesize coolAtk = _coolAtk;
+
- (id) init {
if ((self = [super init])){
-
+ self.coolAtk = [QQCooldown cooldownWithDuration:2];
+ [self.game 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];
+ [super dealloc];
+}
+
+- (void) tick:(float)elapsed {
+ [self.coolAtk tick:elapsed];
+ [super tick:elapsed];
+}
+
- (void) onTouch:(SPTouchEvent*)event {
SPTouch* touch = [[event touchesWithTarget:self.shape.parent] anyObject];
- if (touch) {
- SPPoint* touchPosition = [touch locationInSpace:self.shape.parent];
+ 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];
+
+ // Bullet starting position
+ float offset = 7.5f + fmaxf(self.shape.width, self.shape.height) * 1.41421356237f; // bullet_radius + tank_diagonal_length
+ SPPoint* start = [[self.shape.position addPoint:[normVec scaleBy:offset]] scaleBy:1.0f/self.world.scale];
+ 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);
}
}
float y = self.shape.y = touchPosition.y - self.shape.height / 2.0f;
float px = self.world.scale;
- self.body->SetTransform(b2Vec2(x/px, y/px), self.body->GetAngle());
+ [self setPositionX:x/px y:y/px];
+ // self.body->SetTransform(b2Vec2(x/px, y/px), self.body->GetAngle());
}
}
#import <UIKit/UIKit.h>
+#import "Tanks.h"
int main(int argc, char *argv[])
--- /dev/null
+#include <Box2D/Box2D.h>
+#import "Sparrow.h"
+
+(CGPoint) CGPointFromB2Vec2(const b2Vec2 vec){ return CGPointMake(vec.x, vec.y); }
--- /dev/null
+#import "QQProtocol.h"
+
+@interface QQPoint
+
+
+@end
--- /dev/null
+#import "QQPoint.h"
+
+
+@implementation QQPoint
+
+- (id) init {
+ if ((self = [super init])){
+
+ }
+ return self;
+}
+@end
_walls = [[NSMutableArray alloc] initWithCapacity:4];
CGSize screen = [UIScreen mainScreen].bounds.size;
- float w = screen.width;
- float h = screen.height;
- b2Vec2 origin = b2Vec2(0.0f, 0.0f);
- b2Vec2 horz = b2Vec2(w, 0.0f);
- b2Vec2 vert = b2Vec2(0.0f, h);
+ float pad = 5;
+ float w = screen.width / _scale;
+ float h = screen.height / _scale;
+ b2Vec2 origin = b2Vec2(pad, pad);
+ b2Vec2 horz = b2Vec2(w-2*pad, pad);
+ b2Vec2 vert = b2Vec2(pad, h-2*pad);
b2Vec2 positions[] = {
b2Vec2(w*0.5f, h*0.0f),
--- /dev/null
+#import "qq/QQObservable.h"
+
+@protocol QQNotifier <QQObservable>
+
+- (void) addObserver:(id)observer selector:(SEL)selector name:(NSString*)event object:(id)sender;
+
+//- (void) removeObserver:(id)observer; // XXX: supposed to remove all from all objects
+- (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;
+
+
+@end
--- /dev/null
+
+
+@interface QQObject : NSObject {
+@private
+
+}
+
+
+
+@end
--- /dev/null
+#import "QQObject.h"
+
+
+@implementation QQObject
+
+- (id) init {
+ if ((self = [super init])){
+
+ }
+ return self;
+}
+@end
--- /dev/null
+
+
+@protocol QQObservable
+
+//- (void) observe:(NSString*)event notify:(id)observer selector:(SEL)selector;
+- (void) addObserver:(id)observer selector:(SEL)selector name:(NSString*)event;
+
+- (void) removeObserver:(id)observer;
+- (void) removeObserver:(id)observer name:(NSString*)event;
+
+- (void) postNotification:(NSNotification*)msg;
+- (void) postNotificationName:(NSString*)name object:(id)sender;
+- (void) postNotificationName:(NSString*)name object:(id)sender userInfo:(NSDictionary*)info;
+
+
+@end
#import "SPDisplayObject.h"
+#import "SPPoint.h"
+struct b2Vec2;
@interface SPDisplayObject (QQSparrowExtensions)
+/** Coordinates of the object relative to the local coordinates of the parent. */
+@property (nonatomic, readwrite, assign) SPPoint* position;
+
+/** Sets coordinates of the object relative to the local coordinates of the parent. */
- (id) setPositionX:(float)x y:(float)y;
@end
+
+
+@interface SPPoint (QQSparrowExtensions)
+- (CGPoint) toCGPoint;
++ (SPPoint*) pointFromCGPoint:(CGPoint)pt;
++ (SPPoint*) pointFromB2Vec2:(b2Vec2)vec;
+@end
+