#include "Firestorm.h"
Window *window;
float calc_float_number( const float number_part1, const float number,
bool is_negative )
{
float final_number;
if( number_part1 == 0 )
final_number = (float)number;
else
final_number = (float)number_part1 + ((float)number * 0.1);
if( is_negative )
final_number = 0 - final_number;
return final_number;
}
std::vector<loat>* parse_line( const char * line )
{
std::vector<loat>*v;
v = new std::vector<loat>
unsigned int i, number = 0, number_part1 = 0;
char c;
bool in_number = false;
bool is_negative = false;
for( i = 0; i < strlen( line ); i++ )
{
c = line[i];
if( c >= 48 && c <= 57 ) {
if( in_number )
number = (number * 10) + (c - 48);
else
{
in_number = true;
number = (c - 48);
}
}
else if( c == '.' )
{
if( in_number )
{
number_part1 = number;
number = 0;
}
}
else if( c == '-' )
{
is_negative = true;
}
else
{
if( in_number )
{
in_number = false;
v->push_back( calc_float_number( number_part1, number,
is_negative ) );
number = 0;
number_part1 = 0;
}
is_negative = false;
}
}
if( in_number )
v->push_back( calc_float_number( number_part1, number, is_negative ) );
return v;
}
Star :: Star()
{
PlaceRandomly();
}
void Star :: PlaceRandomly( void )
{
x = rand() % (int)(window->GetBounds().Width());
y = rand() % (int)(window->GetBounds().Height());
depth = rand() % 3;
}
Bullet :: Bullet()
{
size = BULLET_SMALL;
location.left = 300.0;
location.top = 50.0;
velocity.x = -3.0;
velocity.y = 0.0;
color = RED;
heartbeat = 0;
sync();
}
Bullet :: Bullet( float x, float y )
{
size = BULLET_SMALL;
location.left = x;
location.top = y;
velocity.x = -3.0;
velocity.y = 0.0;
color = RED;
heartbeat = 0;
sync();
}
void Bullet :: sync( void )
{
if( size == BULLET_SMALL )
{
location.right = location.left + 4;
location.bottom = location.top + 4;
}
else if( size == BULLET_WAVE )
{
location.right = location.left + 4;
location.bottom = location.top + 30;
}
}
void Bullet :: update( void )
{
heartbeat++;
location.left += velocity.x;
location.right += velocity.x;
location.top += velocity.y;
location.bottom += velocity.y;
if( size == BULLET_WAVE && heartbeat % 10 == 0 )
{
location.top -= 2;
location.bottom += 2;
}
}
Player :: Player()
{
setColor( RED );
location.x = 10.0;
location.y = 50.0;
setBounds( 5.0, 5.0, 300.0, 150.0 );
}
void Player :: setColor( unsigned int inColor )
{
if( inColor < 1 || inColor > 2 )
return;
color = inColor;
String filename = "^/graphics/player_";
if( color == RED )
filename += "red";
else
filename += "yellow";
filename += ".png";
image = new BitmapImage( new File( filename ) );
}
void Player :: move( float xDelta, float yDelta )
{
location.x += xDelta;
location.y += yDelta;
if( location.x < boundary.left )
location.x = boundary.left;
else if( location.x > boundary.right )
location.x = boundary.right;
if( location.y < boundary.top )
location.y = boundary.top;
else if( location.y > boundary.bottom )
location.y = boundary.bottom;
}
void Player :: setBounds( float inLeft, float inTop, float inRight, float inBottom )
{
boundary.left = inLeft;
boundary.right = inRight;
boundary.top = inTop;
boundary.bottom = inBottom;
if( boundary.left < 0.0 )
boundary.left = 0.0;
if( boundary.top < 0.0 )
boundary.top = 0.0;
}
void Player :: draw( View *drawingView )
{
drawingView->SetDrawingMode( DM_BLEND );
image->Draw( location, drawingView );
drawingView->SetDrawingMode( DM_COPY );
}
EnemyMovementEvent :: EnemyMovementEvent()
{
time = 0;
velocity.x = -3.0;
velocity.y = 0.0;
}
Enemy :: Enemy()
{
type = 0;
health = 0;
location.left = 0;
location.top = 0;
location.right = 63;
location.bottom = 63;
velocity.x = 0.0;
velocity.y = 0.0;
direction = 0.0;
color = 1;
pathTicks = 100;
heartbeat = 0;
nextMovement = 0;
image = NULL;
}
void Enemy :: SetPath( std::vector<loat>*pathNumbers )
{
Enemy();
unsigned int i;
EnemyMovementEvent event;
movements.clear();
for( i = 2; i < pathNumbers->size(); i = i + 3 )
{
event.time = (int)((*pathNumbers)[i - 2]);
event.velocity.x = (*pathNumbers)[i - 1];
event.velocity.y = (*pathNumbers)[i];
movements.push_back( event );
}
}
void Enemy :: setType( unsigned int inType )
{
if( inType < 1 || inType > 3 )
return;
type = inType;
health = 1;
if( type == 3 )
health = 3;
String filename = "^/graphics/enemy";
char enemyTypeString[3];
sprintf( enemyTypeString, "%d", type );
filename += (String)(enemyTypeString) + "_";
if( color == 1 )
filename += "red";
else
filename += "yellow";
filename += ".png";
image = new BitmapImage( new File( filename ) );
Point p = image->GetSize();
location.right = location.left + p.x;
location.bottom = location.top + p.y;
}
void Enemy :: addDamage( unsigned int damage )
{
health = health - damage;
if( health < 0 )
health = 0;
}
void Enemy :: update( void )
{
heartbeat++;
unsigned int pathTick = heartbeat % pathTicks;
if( movements[nextMovement].time == pathTick )
{
if( movements[nextMovement].velocity.x < 0.1 &&
movements[nextMovement].velocity.x > -0.1 &&
movements[nextMovement].velocity.y < 0.1 &&
movements[nextMovement].velocity.y > -0.1 )
{
Message msg( MSG_FIRE_ENEMY_BULLET );
msg.AddPoint( "location", Point( location.left, location.top - 32 ) );
msg.AddInt32( "type", (int32)color );
window->PostMessage( &msg, window );
}
else
{
velocity = movements[nextMovement].velocity;
if( nextMovement == movements.size() - 1 )
nextMovement = 0;
else
nextMovement++;
}
}
location.left += velocity.x;
location.right += velocity.x;
location.top += velocity.y;
location.bottom += velocity.y;
}
EnemyManager :: EnemyManager()
{
levelTimeCount = 0;
mainWindow = NULL;
}
status_t EnemyManager :: set( String levelName )
{
levelTimeCount = 0;
return eventQueue.set( levelName );
}
status_t EnemyManager :: heartbeat( View *drawingView, Point playerPoint )
{
unsigned int i, j;
levelTimeCount++;
Rect playerLocation( playerPoint.x, playerPoint.y,
playerPoint.x + 30, playerPoint.y + 30 );
for( i = 0; i < enemies.size(); i++ )
{
enemies[i].update();
if( enemies[i].location.right <= 1 )
{
enemies.erase( enemies.begin() + i );
i--;
}
else if( i >= 0 )
{
if( enemies[i].location.DoIntersect( playerLocation ) )
{
enemies.erase( enemies.begin() + i );
i--;
Message *msg = new Message( MSG_CHANGE_HEALTH );
msg->AddFloat( "healthChange", -25.0 );
mainWindow->PostMessage( msg, mainWindow );
}
}
}
for( i = 0; i < bullets.size(); i++ )
{
for( j = 0; i < bullets.size() && j < enemies.size(); j++ )
{
if( enemies[j].location.DoIntersect( bullets[i].location ) &&
enemies[j].color == bullets[i].color )
{
bullets.erase( bullets.begin() + i );
i--;
enemies[j].addDamage( 1 );
if( enemies[j].getHealth() <= 0 )
{
delete enemies[j].image;
enemies.erase( enemies.begin() + j );
j--;
if( mainWindow != NULL )
{
Message *msg = new Message( MSG_INCREMENT_SCORE );
if( enemies[j].type == 3 )
msg->AddInt32( "score", 100 );
else
msg->AddInt32( "score", 50 );
mainWindow->PostMessage( msg, mainWindow );
}
}
}
}
if( i >= 0 && i < bullets.size() )
{
if( bullets[i].location.DoIntersect( playerLocation ) )
{
bullets.erase( bullets.begin() + i );
i--;
Message *msg = new Message( MSG_CHANGE_HEALTH );
msg->AddFloat( "healthChange", -5.0 );
mainWindow->PostMessage( msg, mainWindow );
}
}
}
while( eventQueue.events.size() > 0 &&
eventQueue.events[0]->time == levelTimeCount )
{
Enemy enemy;
enemy.location.left = drawingView->GetBounds().Width();
enemy.location.top = eventQueue.events[0]->location;
enemy.color = eventQueue.events[0]->color;
enemy.setType( eventQueue.events[0]->enemyType );
enemy.direction = -0.5;
std::vector<loat>pathNumbers;
std::vector<loat>*v = parse_line(
eventQueue.events[0]->path.c_str() );
enemy.pathTicks = (unsigned int)((*v)[1]);
for( i = 2; i < (*v).size(); i++ )
{
pathNumbers.push_back( (*v)[i] );
}
enemy.SetPath( &pathNumbers );
enemies.push_back( enemy );
eventQueue.events.erase( eventQueue.events.begin() );
}
if( eventQueue.events.size() == 0 && enemies.size() == 0 )
{
mainWindow->PostMessage( new Message( MSG_LEVEL_COMPLETE ), mainWindow );
}
drawingView->SetDrawingMode( DM_BLEND );
for( i = 0; i < enemies.size(); i++ )
{
enemies[i].image->Draw( Point( enemies[i].location.left,
enemies[i].location.top ), drawingView );
}
for( i = 0; i < bullets.size(); i++ )
bullets[i].update();
for( i = 0; i < bullets.size(); i++ )
{
bullets[i].update();
drawingView->SetFgColor( bullets[i].getColor() );
drawingView->FillRect( bullets[i].location );
if( bullets[i].location.right <= 0 || bullets[i].location.left >= drawingView->GetBounds().Width() )
{
std::vector<ullet>:iterator it;
it = bullets.begin() + i;
bullets.erase( it );
}
}
drawingView->SetDrawingMode( DM_COPY );
return 0;
}
void EnemyManager :: addBullet( float x, float y, float xVelocity, float yVelocity, unsigned int color, unsigned int size )
{
Bullet b( x, y );
b.velocity.x = xVelocity;
b.velocity.y = yVelocity;
b.size = size;
b.color = color;
b.sync();
bullets.push_back( b );
}
LevelEvent :: LevelEvent()
{
time = 0;
location = 40.0;
enemyType = 1;
color = 1;
}
LevelEventQueue :: LevelEventQueue()
{
}
status_t LevelEventQueue :: set( String levelName )
{
std::vector<loat>*lineOfNumbers;
std::vector<tring>paths;
events.erase( events.begin(), events.end() );
FileReference ref( Directory( "^/levels" ), levelName.c_str() );
if( ! ref.IsValid() )
return( 1 );
String path;
ref.GetPath( &path );
std::fstream levelFile( path.c_str(), std::ios::in );
if( ! levelFile )
return( 1 );
String line;
char cline[4096];
bool inPaths = true;
while( ! levelFile.eof() )
{
levelFile.getline( cline, 4096 );
line = (String)cline;
if( line.substr( 0, 7 ) == "[paths]" )
inPaths = true;
else if( line.substr( 0, 7 ) == "[ships]" )
inPaths = false;
else if( inPaths && cline[0] != '#' )
{
lineOfNumbers = parse_line( cline );
if( lineOfNumbers->size() > 0 )
{
unsigned int pathIndex = (unsigned int)((*lineOfNumbers)[0]);
while( paths.size() < pathIndex + 1 )
paths.push_back( "" );
paths[pathIndex] = line;
}
}
else if( ! inPaths && cline[0] != '#' ) {
lineOfNumbers = parse_line( cline );
if( lineOfNumbers->size() > 0 )
{
LevelEvent *e = new LevelEvent();
e->time = (unsigned int)(*lineOfNumbers)[0];
e->location = (*lineOfNumbers)[1];
e->enemyType = (unsigned int)(*lineOfNumbers)[2];
e->color = (unsigned int)(*lineOfNumbers)[3];
e->path = paths[ (unsigned int)(*lineOfNumbers)[4] ];
events.push_back( e );
}
}
}
levelFile.close();
return( 0 );
}
StatusView :: StatusView( const Rect& r )
: View( r, "StatusView", CF_FOLLOW_LEFT | CF_FOLLOW_TOP,
WID_WILL_DRAW | WID_CLEAR_BACKGROUND )
{
scoreStringView = new StringView( Rect( 1, 1, 30, GetBounds().bottom - 1 ), "scoreStringView", "0" );
scoreStringView->SetFgColor( 0, 0, 0 );
AddChild( scoreStringView );
score = 0;
health = 100;
}
void StatusView :: Paint( const Rect& updateRect )
{
View::Paint( updateRect );
SetFgColor( 80, 80, 80 );
FillRect( Rect( GetBounds().right - 106, 2, GetBounds().right - 4,
GetBounds().bottom - 2 ) );
SetFgColor( 140, 140, 240 );
FillRect( Rect( GetBounds().right - 105, 3, GetBounds().right - 105 + health,
GetBounds().bottom - 3 ) );
}
void StatusView :: HandleMessage( Message *pcMessage )
{
switch( pcMessage->GetCode() )
{
case MSG_INCREMENT_SCORE:
{
int32 newScoreValue;
char s[20];
pcMessage->FindInt32( "score", &newScoreValue );
score = score + newScoreValue;
if( score < 0 )
score = 0;
sprintf( s, "%d", score );
scoreStringView->SetString( s );
}
break;
case MSG_CHANGE_HEALTH:
{
float healthChange;
pcMessage->FindFloat( "healthChange", &healthChange );
health = health + healthChange;
if( health < 0.0 )
health = 0.0;
else if( health > 100.0 )
health = 100.0;
Invalidate();
}
break;
default:
View :: HandleMessage( pcMessage );
}
}
GameView :: GameView( const Rect& r )
: View( r, "GameView", CF_FOLLOW_LEFT | CF_FOLLOW_TOP,
WID_WILL_DRAW | WID_CLEAR_BACKGROUND )
{
star_color[0].red = 50;
star_color[0].green = 50;
star_color[0].blue = 50;
star_color[1].red = 150;
star_color[1].green = 150;
star_color[1].blue = 150;
star_color[2].red = 250;
star_color[2].green = 250;
star_color[2].blue = 250;
holdingUpArrow = false;
holdingDownArrow = false;
holdingLeftArrow = false;
holdingRightArrow = false;
holdingFireButton = 0;
player.setBounds( r.left, r.top, r.right - 100, r.bottom - 70 );
rechargeDelay = 0;
unsigned int i;
for( i = 0; i < NUM_STARS; i++ )
{
stars[i].PlaceRandomly();
}
drawingView = new View( r, "DrawView" );
offscreenBitmap = new Bitmap( (int)r.Width() + 1, (int)r.Height() + 1,
CS_RGB32,
Bitmap::ACCEPT_VIEWS |
Bitmap::SHARE_FRAMEBUFFER );
offscreenBitmap->AddChild( drawingView );
onscreenBitmap = new Bitmap( (int)r.Width() + 1, (int)r.Height() + 1, CS_RGB32 );
pcManager = MediaManager::Get();
bulletSound = new MediaSoundPlayer();
FileReference ref( Directory( "^/sounds" ), "Bullet.ogg" );
String path;
ref.GetPath( &path );
bulletSound->SetFile( path );
backgroundMusic = new MediaSoundPlayer();
ref.SetTo( Directory( "^/sounds" ), "technology.ogg" );
ref.GetPath( &path );
backgroundMusic->SetFile( path );
backgroundMusic->Play();
}
GameView :: ~GameView()
{
delete offscreenBitmap;
delete onscreenBitmap;
backgroundMusic->Stop();
pcManager->Put();
}
void GameView :: AttachedToWindow()
{
enemyManager.setWindow( GetWindow() );
enemyManager.set( "level1" );
}
void GameView :: Paint( const Rect& updateRect )
{
DrawBitmap( onscreenBitmap, updateRect, updateRect );
}
void GameView :: KeyUp( const char *pzString, const char *pzRawString,
uint32 nQualifiers )
{
int32 nRawKey;
GetWindow()->GetCurrentMessage()->FindInt32( "_raw_key", &nRawKey );
if( nRawKey == 0xD7 )
holdingUpArrow = false;
else if( nRawKey == 0xE2 )
holdingDownArrow = false;
else if( nRawKey == 0xE1 )
holdingLeftArrow = false;
else if( nRawKey == 0xE3 )
holdingRightArrow = false;
else if( pzRawString[0] == 'f' )
holdingFireButton = 0;
else if( pzRawString[0] == 'd' )
holdingFireButton = 0;
else if( pzRawString[0] == 's' )
holdingFireButton = 0;
}
void GameView :: KeyDown( const char *pzString, const char *pzRawString,
uint32 nQualifiers )
{
int32 nRawKey;
GetWindow()->GetCurrentMessage()->FindInt32( "_raw_key", &nRawKey );
if( nRawKey == 0x57 )
holdingUpArrow = true;
else if( nRawKey == 0x62 )
holdingDownArrow = true;
else if( nRawKey == 0x61 )
holdingLeftArrow = true;
else if( nRawKey == 0x63 )
holdingRightArrow = true;
else if( pzRawString[0] == 'f' )
{
player.setColor( player.RED );
holdingFireButton = RED;
}
else if( pzRawString[0] == 'd' )
{
player.setColor( player.YELLOW );
holdingFireButton = YELLOW;
}
else if( pzRawString[0] == 's' )
{
holdingFireButton = FIRING_BOTH_BULLETS;
}
else if( pzRawString[0] == ' ' )
{
if( player.getColor() == player.RED )
player.setColor( player.YELLOW );
else
player.setColor( player.RED );
}
}
void GameView :: GameHeartbeat( void )
{
unsigned int i;
const float playerMovementDelta = 3.0;
Rect bounds = GetBounds();
drawingView->SetFgColor( 0, 0, 0 );
drawingView->FillRect( bounds );
for( i = 0; i < NUM_STARS; i++ )
{
stars[i].x -= stars[i].depth + 1;
if( stars[i].x < 0 )
{
stars[i].PlaceRandomly();
stars[i].x = bounds.Width();
}
drawingView->SetFgColor( star_color[stars[i].depth] );
drawingView->DrawLine( stars[i], stars[i] );
}
enemyManager.heartbeat( drawingView, player.getLocation() );
if( holdingUpArrow )
player.move( 0, 0 - playerMovementDelta );
if( holdingDownArrow )
player.move( 0, playerMovementDelta );
if( holdingLeftArrow )
player.move( 0 - playerMovementDelta, 0 );
if( holdingRightArrow )
player.move( playerMovementDelta, 0 );
if( holdingFireButton > 0 && rechargeDelay == 0 )
{
if( holdingFireButton == FIRING_BOTH_BULLETS )
{
enemyManager.addBullet( player.getLocation().x + 55,
player.getLocation().y + 20, 3, 0,
player.RED, BULLET_SMALL );
enemyManager.addBullet( player.getLocation().x + 55,
player.getLocation().y + 40, 3, 0,
player.YELLOW, BULLET_SMALL );
rechargeDelay = 22;
}
else
{
int yModifier = 10;
if( player.getColor() == RED )
yModifier = -10;
enemyManager.addBullet( player.getLocation().x + 55,
player.getLocation().y + 30 + yModifier, 3, 0,
player.getColor(), BULLET_SMALL );
rechargeDelay = 10;
}
Message *msg = new Message( MSG_INCREMENT_SCORE );
msg->AddInt32( "score", -5 );
GetWindow()->PostMessage( msg, GetWindow() );
}
if( rechargeDelay > 0 )
rechargeDelay--;
player.draw( drawingView );
drawingView->Sync();
memcpy( onscreenBitmap->LockRaster(),
offscreenBitmap->LockRaster(),
onscreenBitmap->GetBytesPerRow() *
((int)onscreenBitmap->GetBounds().Height() + 1) );
Invalidate();
Sync();
}
void GameView :: setLevel( String level )
{
enemyManager.set( level );
}
void GameView :: fireEnemyBullet( Point location, unsigned int type )
{
enemyManager.addBullet( location.x - 15, location.y + 63, -4, 0, type, BULLET_SMALL );
}
GameWindow :: GameWindow( const Rect& r )
: Window( r, "FirestormWindow", "Firestorm", WND_NOT_RESIZABLE |
WND_NO_ZOOM_BUT | WND_NO_DEPTH_BUT | WND_SINGLEBUFFER,
CURRENT_DESKTOP )
{
window = this;
Rect rect( GetBounds() );
rect.bottom = 20;
statusView = new StatusView( rect );
AddChild( statusView );
rect.bottom = GetBounds().bottom;
rect.top = 21;
gameView = new GameView( rect );
AddChild( gameView );
SetFocusChild( gameView );
thread = new HeartbeatThread();
thread->Start();
}
void GameWindow :: HandleMessage( Message *pcMessage )
{
switch( pcMessage->GetCode() )
{
case MSG_INCREMENT_SCORE:
case MSG_CHANGE_HEALTH:
{
statusView->HandleMessage( pcMessage );
}
break;
case MSG_LEVEL_COMPLETE:
{
if( currentLevel < levels.size() - 1 )
{
currentLevel++;
gameView->setLevel( levels[currentLevel] );
}
}
break;
case MSG_FIRE_ENEMY_BULLET:
{
Point location;
int32 type;
pcMessage->FindPoint( "location", &location );
pcMessage->FindInt32( "type", &type );
gameView->fireEnemyBullet( location, (unsigned int)type );
}
break;
case MSG_HEARTBEAT:
gameView->GameHeartbeat();
break;
default:
Window :: HandleMessage( pcMessage );
}
}
bool GameWindow :: OkToQuit( void )
{
thread->Terminate();
Application::GetInstance()->PostMessage( M_QUIT );
return( true );
}
status_t GameWindow :: loadLevelSet( String level_set )
{
currentLevel = 0;
levels.erase( levels.begin(), levels.end() );
FSNode node( Directory( "^/levels" ), level_set.c_str() );
if( ! node.IsValid() )
{
levels.push_back( "level1" );
return( 1 );
}
FileReference ref( Directory( "^/levels" ), level_set.c_str() );
String path;
ref.GetPath( &path );
std::fstream levelSetFile( path.c_str(), std::ios::in );
if( ! levelSetFile )
return( 1 );
String line;
char cline[4096];
while( ! levelSetFile.eof() )
{
levelSetFile.getline( cline, 4096 );
line = (String)cline;
if( cline[0] != '#' && line.Length() > 0 )
{
node.SetTo( Directory( "^/levels" ), cline );
if( node.IsValid() )
levels.push_back( line );
}
}
levelSetFile.close();
gameView->setLevel( levels[0] );
return( 0 );
}