doom3-bfg/neo/ui/GameSSDWindow.cpp

2600 lines
63 KiB
C++

/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#pragma hdrstop
#include "precompiled.h"
#include "DeviceContext.h"
#include "Window.h"
#include "UserInterfaceLocal.h"
#include "GameSSDWindow.h"
#define Z_NEAR 100.0f
#define Z_FAR 4000.0f
#define ENTITY_START_DIST 3000
#define V_WIDTH 640.0f
#define V_HEIGHT 480.0f
/*
*****************************************************************************
* SSDCrossHair
****************************************************************************
*/
#define CROSSHAIR_STANDARD_MATERIAL "game/SSD/crosshair_standard"
#define CROSSHAIR_SUPER_MATERIAL "game/SSD/crosshair_super"
SSDCrossHair::SSDCrossHair()
{
}
SSDCrossHair::~SSDCrossHair()
{
}
void SSDCrossHair::WriteToSaveGame( idFile* savefile )
{
savefile->Write( &currentCrosshair, sizeof( currentCrosshair ) );
savefile->Write( &crosshairWidth, sizeof( crosshairWidth ) );
savefile->Write( &crosshairHeight, sizeof( crosshairHeight ) );
}
void SSDCrossHair::ReadFromSaveGame( idFile* savefile )
{
InitCrosshairs();
savefile->Read( &currentCrosshair, sizeof( currentCrosshair ) );
savefile->Read( &crosshairWidth, sizeof( crosshairWidth ) );
savefile->Read( &crosshairHeight, sizeof( crosshairHeight ) );
}
void SSDCrossHair::InitCrosshairs()
{
crosshairMaterial[CROSSHAIR_STANDARD] = declManager->FindMaterial( CROSSHAIR_STANDARD_MATERIAL );
crosshairMaterial[CROSSHAIR_SUPER] = declManager->FindMaterial( CROSSHAIR_SUPER_MATERIAL );
crosshairWidth = 64;
crosshairHeight = 64;
currentCrosshair = CROSSHAIR_STANDARD;
}
void SSDCrossHair::Draw( const idVec2& cursor )
{
float x, y;
x = cursor.x - ( crosshairWidth / 2 );
y = cursor.y - ( crosshairHeight / 2 );
dc->DrawMaterial( x, y, crosshairWidth, crosshairHeight, crosshairMaterial[currentCrosshair], colorWhite, 1.0f, 1.0f );
}
/*
*****************************************************************************
* SSDEntity
****************************************************************************
*/
SSDEntity::SSDEntity()
{
EntityInit();
}
SSDEntity::~SSDEntity()
{
}
void SSDEntity::WriteToSaveGame( idFile* savefile )
{
savefile->Write( &type, sizeof( type ) );
game->WriteSaveGameString( materialName, savefile );
savefile->Write( &position, sizeof( position ) );
savefile->Write( &size, sizeof( size ) );
savefile->Write( &radius, sizeof( radius ) );
savefile->Write( &hitRadius, sizeof( hitRadius ) );
savefile->Write( &rotation, sizeof( rotation ) );
savefile->Write( &matColor, sizeof( matColor ) );
game->WriteSaveGameString( text, savefile );
savefile->Write( &textScale, sizeof( textScale ) );
savefile->Write( &foreColor, sizeof( foreColor ) );
savefile->Write( &currentTime, sizeof( currentTime ) );
savefile->Write( &lastUpdate, sizeof( lastUpdate ) );
savefile->Write( &elapsed, sizeof( elapsed ) );
savefile->Write( &destroyed, sizeof( destroyed ) );
savefile->Write( &noHit, sizeof( noHit ) );
savefile->Write( &noPlayerDamage, sizeof( noPlayerDamage ) );
savefile->Write( &inUse, sizeof( inUse ) );
}
void SSDEntity::ReadFromSaveGame( idFile* savefile, idGameSSDWindow* _game )
{
savefile->Read( &type, sizeof( type ) );
game->ReadSaveGameString( materialName, savefile );
SetMaterial( materialName );
savefile->Read( &position, sizeof( position ) );
savefile->Read( &size, sizeof( size ) );
savefile->Read( &radius, sizeof( radius ) );
savefile->Read( &hitRadius, sizeof( hitRadius ) );
savefile->Read( &rotation, sizeof( rotation ) );
savefile->Read( &matColor, sizeof( matColor ) );
game->ReadSaveGameString( text, savefile );
savefile->Read( &textScale, sizeof( textScale ) );
savefile->Read( &foreColor, sizeof( foreColor ) );
game = _game;
savefile->Read( &currentTime, sizeof( currentTime ) );
savefile->Read( &lastUpdate, sizeof( lastUpdate ) );
savefile->Read( &elapsed, sizeof( elapsed ) );
savefile->Read( &destroyed, sizeof( destroyed ) );
savefile->Read( &noHit, sizeof( noHit ) );
savefile->Read( &noPlayerDamage, sizeof( noPlayerDamage ) );
savefile->Read( &inUse, sizeof( inUse ) );
}
void SSDEntity::EntityInit()
{
inUse = false;
type = SSD_ENTITY_BASE;
materialName = "";
material = NULL;
position.Zero();
size.Zero();
radius = 0.0f;
hitRadius = 0.0f;
rotation = 0.0f;
currentTime = 0;
lastUpdate = 0;
destroyed = false;
noHit = false;
noPlayerDamage = false;
matColor.Set( 1, 1, 1, 1 );
text = "";
textScale = 1.0f;
foreColor.Set( 1, 1, 1, 1 );
}
void SSDEntity::SetGame( idGameSSDWindow* _game )
{
game = _game;
}
void SSDEntity::SetMaterial( const char* name )
{
materialName = name;
material = declManager->FindMaterial( name );
material->SetSort( SS_GUI );
}
void SSDEntity::SetPosition( const idVec3& _position )
{
position = _position;
}
void SSDEntity::SetSize( const idVec2& _size )
{
size = _size;
}
void SSDEntity::SetRadius( float _radius, float _hitFactor )
{
radius = _radius;
hitRadius = _radius * _hitFactor;
}
void SSDEntity::SetRotation( float _rotation )
{
rotation = _rotation;
}
void SSDEntity::Update()
{
currentTime = game->ssdTime;
//Is this the first update
if( lastUpdate == 0 )
{
lastUpdate = currentTime;
return;
}
elapsed = currentTime - lastUpdate;
EntityUpdate();
lastUpdate = currentTime;
}
bool SSDEntity::HitTest( const idVec2& pt )
{
if( noHit )
{
return false;
}
idVec3 screenPos = WorldToScreen( position );
//Scale the radius based on the distance from the player
float scale = 1.0f - ( ( screenPos.z - Z_NEAR ) / ( Z_FAR - Z_NEAR ) );
float scaledRad = scale * hitRadius;
//So we can compare against the square of the length between two points
float scaleRadSqr = scaledRad * scaledRad;
idVec2 diff = screenPos.ToVec2() - pt;
float dist = idMath::Fabs( diff.LengthSqr() );
if( dist < scaleRadSqr )
{
return true;
}
return false;
}
void SSDEntity::Draw()
{
idVec2 persize;
float x, y;
idBounds bounds;
bounds[0] = idVec3( position.x - ( size.x / 2.0f ), position.y - ( size.y / 2.0f ), position.z );
bounds[1] = idVec3( position.x + ( size.x / 2.0f ), position.y + ( size.y / 2.0f ), position.z );
idBounds screenBounds = WorldToScreen( bounds );
persize.x = idMath::Fabs( screenBounds[1].x - screenBounds[0].x );
persize.y = idMath::Fabs( screenBounds[1].y - screenBounds[0].y );
idVec3 center = screenBounds.GetCenter();
x = screenBounds[0].x;
y = screenBounds[1].y;
dc->DrawMaterialRotated( x, y, persize.x, persize.y, material, matColor, 1.0f, 1.0f, DEG2RAD( rotation ) );
if( text.Length() > 0 )
{
idRectangle rect( x, y, VIRTUAL_WIDTH, VIRTUAL_HEIGHT );
dc->DrawText( text, textScale, 0, foreColor, rect, false );
}
}
void SSDEntity::DestroyEntity()
{
inUse = false;
}
idBounds SSDEntity::WorldToScreen( const idBounds worldBounds )
{
idVec3 screenMin = WorldToScreen( worldBounds[0] );
idVec3 screenMax = WorldToScreen( worldBounds[1] );
idBounds screenBounds( screenMin, screenMax );
return screenBounds;
}
idVec3 SSDEntity::WorldToScreen( const idVec3& worldPos )
{
float d = 0.5f * V_WIDTH * idMath::Tan( DEG2RAD( 90.0f ) / 2.0f );
//World To Camera Coordinates
idVec3 cameraTrans( 0, 0, d );
idVec3 cameraPos;
cameraPos = worldPos + cameraTrans;
//Camera To Screen Coordinates
idVec3 screenPos;
screenPos.x = d * cameraPos.x / cameraPos.z + ( 0.5f * V_WIDTH - 0.5f );
screenPos.y = -d * cameraPos.y / cameraPos.z + ( 0.5f * V_HEIGHT - 0.5f );
screenPos.z = cameraPos.z;
return screenPos;
}
idVec3 SSDEntity::ScreenToWorld( const idVec3& screenPos )
{
idVec3 worldPos;
worldPos.x = screenPos.x - 0.5f * V_WIDTH;
worldPos.y = -( screenPos.y - 0.5f * V_HEIGHT );
worldPos.z = screenPos.z;
return worldPos;
}
/*
*****************************************************************************
* SSDMover
****************************************************************************
*/
SSDMover::SSDMover()
{
}
SSDMover::~SSDMover()
{
}
void SSDMover::WriteToSaveGame( idFile* savefile )
{
SSDEntity::WriteToSaveGame( savefile );
savefile->Write( &speed, sizeof( speed ) );
savefile->Write( &rotationSpeed, sizeof( rotationSpeed ) );
}
void SSDMover::ReadFromSaveGame( idFile* savefile, idGameSSDWindow* _game )
{
SSDEntity::ReadFromSaveGame( savefile, _game );
savefile->Read( &speed, sizeof( speed ) );
savefile->Read( &rotationSpeed, sizeof( rotationSpeed ) );
}
void SSDMover::MoverInit( const idVec3& _speed, float _rotationSpeed )
{
speed = _speed;
rotationSpeed = _rotationSpeed;
}
void SSDMover::EntityUpdate()
{
SSDEntity::EntityUpdate();
//Move forward based on speed (units per second)
idVec3 moved = ( ( float )elapsed / 1000.0f ) * speed;
position += moved;
float rotated = ( ( float )elapsed / 1000.0f ) * rotationSpeed * 360.0f;
rotation += rotated;
if( rotation >= 360 )
{
rotation -= 360.0f;
}
if( rotation < 0 )
{
rotation += 360.0f;
}
}
/*
*****************************************************************************
* SSDAsteroid
****************************************************************************
*/
SSDAsteroid SSDAsteroid::asteroidPool[MAX_ASTEROIDS];
#define ASTEROID_MATERIAL "game/SSD/asteroid"
SSDAsteroid::SSDAsteroid()
{
}
SSDAsteroid::~SSDAsteroid()
{
}
void SSDAsteroid::WriteToSaveGame( idFile* savefile )
{
SSDMover::WriteToSaveGame( savefile );
savefile->Write( &health, sizeof( health ) );
}
void SSDAsteroid::ReadFromSaveGame( idFile* savefile, idGameSSDWindow* _game )
{
SSDMover::ReadFromSaveGame( savefile, _game );
savefile->Read( &health, sizeof( health ) );
}
void SSDAsteroid::Init( idGameSSDWindow* _game, const idVec3& startPosition, const idVec2& _size, float _speed, float rotate, int _health )
{
EntityInit();
MoverInit( idVec3( 0, 0, -_speed ), rotate );
SetGame( _game );
type = SSD_ENTITY_ASTEROID;
SetMaterial( ASTEROID_MATERIAL );
SetSize( _size );
SetRadius( Max( size.x, size.y ), 0.3f );
SetRotation( game->random.RandomInt( 360 ) );
position = startPosition;
health = _health;
}
void SSDAsteroid::EntityUpdate()
{
SSDMover::EntityUpdate();
}
SSDAsteroid* SSDAsteroid::GetNewAsteroid( idGameSSDWindow* _game, const idVec3& startPosition, const idVec2& _size, float _speed, float rotate, int _health )
{
for( int i = 0; i < MAX_ASTEROIDS; i++ )
{
if( !asteroidPool[i].inUse )
{
asteroidPool[i].Init( _game, startPosition, _size, _speed, rotate, _health );
asteroidPool[i].inUse = true;
asteroidPool[i].id = i;
return &asteroidPool[i];
}
}
return NULL;
}
SSDAsteroid* SSDAsteroid::GetSpecificAsteroid( int id )
{
return &asteroidPool[id];
}
void SSDAsteroid::WriteAsteroids( idFile* savefile )
{
int count = 0;
for( int i = 0; i < MAX_ASTEROIDS; i++ )
{
if( asteroidPool[i].inUse )
{
count++;
}
}
savefile->Write( &count, sizeof( count ) );
for( int i = 0; i < MAX_ASTEROIDS; i++ )
{
if( asteroidPool[i].inUse )
{
savefile->Write( &( asteroidPool[i].id ), sizeof( asteroidPool[i].id ) );
asteroidPool[i].WriteToSaveGame( savefile );
}
}
}
void SSDAsteroid::ReadAsteroids( idFile* savefile, idGameSSDWindow* _game )
{
int count;
savefile->Read( &count, sizeof( count ) );
for( int i = 0; i < count; i++ )
{
int id;
savefile->Read( &id, sizeof( id ) );
SSDAsteroid* ent = GetSpecificAsteroid( id );
ent->ReadFromSaveGame( savefile, _game );
}
}
/*
*****************************************************************************
* SSDAstronaut
****************************************************************************
*/
SSDAstronaut SSDAstronaut::astronautPool[MAX_ASTRONAUT];
#define ASTRONAUT_MATERIAL "game/SSD/astronaut"
SSDAstronaut::SSDAstronaut()
{
}
SSDAstronaut::~SSDAstronaut()
{
}
void SSDAstronaut::WriteToSaveGame( idFile* savefile )
{
SSDMover::WriteToSaveGame( savefile );
savefile->Write( &health, sizeof( health ) );
}
void SSDAstronaut::ReadFromSaveGame( idFile* savefile, idGameSSDWindow* _game )
{
SSDMover::ReadFromSaveGame( savefile, _game );
savefile->Read( &health, sizeof( health ) );
}
void SSDAstronaut::Init( idGameSSDWindow* _game, const idVec3& startPosition, float _speed, float rotate, int _health )
{
EntityInit();
MoverInit( idVec3( 0, 0, -_speed ), rotate );
SetGame( _game );
type = SSD_ENTITY_ASTRONAUT;
SetMaterial( ASTRONAUT_MATERIAL );
SetSize( idVec2( 256, 256 ) );
SetRadius( Max( size.x, size.y ), 0.3f );
SetRotation( game->random.RandomInt( 360 ) );
position = startPosition;
health = _health;
}
SSDAstronaut* SSDAstronaut::GetNewAstronaut( idGameSSDWindow* _game, const idVec3& startPosition, float _speed, float rotate, int _health )
{
for( int i = 0; i < MAX_ASTRONAUT; i++ )
{
if( !astronautPool[i].inUse )
{
astronautPool[i].Init( _game, startPosition, _speed, rotate, _health );
astronautPool[i].inUse = true;
astronautPool[i].id = i;
return &astronautPool[i];
}
}
return NULL;
}
SSDAstronaut* SSDAstronaut::GetSpecificAstronaut( int id )
{
return &astronautPool[id];
}
void SSDAstronaut::WriteAstronauts( idFile* savefile )
{
int count = 0;
for( int i = 0; i < MAX_ASTRONAUT; i++ )
{
if( astronautPool[i].inUse )
{
count++;
}
}
savefile->Write( &count, sizeof( count ) );
for( int i = 0; i < MAX_ASTRONAUT; i++ )
{
if( astronautPool[i].inUse )
{
savefile->Write( &( astronautPool[i].id ), sizeof( astronautPool[i].id ) );
astronautPool[i].WriteToSaveGame( savefile );
}
}
}
void SSDAstronaut::ReadAstronauts( idFile* savefile, idGameSSDWindow* _game )
{
int count;
savefile->Read( &count, sizeof( count ) );
for( int i = 0; i < count; i++ )
{
int id;
savefile->Read( &id, sizeof( id ) );
SSDAstronaut* ent = GetSpecificAstronaut( id );
ent->ReadFromSaveGame( savefile, _game );
}
}
/*
*****************************************************************************
* SSDExplosion
****************************************************************************
*/
SSDExplosion SSDExplosion::explosionPool[MAX_EXPLOSIONS];
//#define EXPLOSION_MATERIAL "game/SSD/fball"
//#define EXPLOSION_TELEPORT "game/SSD/teleport"
const char* explosionMaterials[] =
{
"game/SSD/fball",
"game/SSD/teleport"
};
#define EXPLOSION_MATERIAL_COUNT 2
SSDExplosion::SSDExplosion()
{
type = SSD_ENTITY_EXPLOSION;
}
SSDExplosion::~SSDExplosion()
{
}
void SSDExplosion::WriteToSaveGame( idFile* savefile )
{
SSDEntity::WriteToSaveGame( savefile );
savefile->Write( &finalSize, sizeof( finalSize ) );
savefile->Write( &length, sizeof( length ) );
savefile->Write( &beginTime, sizeof( beginTime ) );
savefile->Write( &endTime, sizeof( endTime ) );
savefile->Write( &explosionType, sizeof( explosionType ) );
savefile->Write( &( buddy->type ), sizeof( buddy->type ) );
savefile->Write( &( buddy->id ), sizeof( buddy->id ) );
savefile->Write( &killBuddy, sizeof( killBuddy ) );
savefile->Write( &followBuddy, sizeof( followBuddy ) );
}
void SSDExplosion::ReadFromSaveGame( idFile* savefile, idGameSSDWindow* _game )
{
SSDEntity::ReadFromSaveGame( savefile, _game );
savefile->Read( &finalSize, sizeof( finalSize ) );
savefile->Read( &length, sizeof( length ) );
savefile->Read( &beginTime, sizeof( beginTime ) );
savefile->Read( &endTime, sizeof( endTime ) );
savefile->Read( &explosionType, sizeof( explosionType ) );
int type, id;
savefile->Read( &type, sizeof( type ) );
savefile->Read( &id, sizeof( id ) );
//Get a pointer to my buddy
buddy = _game->GetSpecificEntity( type, id );
savefile->Read( &killBuddy, sizeof( killBuddy ) );
savefile->Read( &followBuddy, sizeof( followBuddy ) );
}
void SSDExplosion::Init( idGameSSDWindow* _game, const idVec3& _position, const idVec2& _size, int _length, int _type, SSDEntity* _buddy, bool _killBuddy, bool _followBuddy )
{
EntityInit();
SetGame( _game );
type = SSD_ENTITY_EXPLOSION;
explosionType = _type;
SetMaterial( explosionMaterials[explosionType] );
SetPosition( _position );
position.z -= 50;
finalSize = _size;
length = _length;
beginTime = game->ssdTime;
endTime = beginTime + length;
buddy = _buddy;
killBuddy = _killBuddy;
followBuddy = _followBuddy;
//Explosion Starts from nothing and will increase in size until it gets to final size
size.Zero();
noPlayerDamage = true;
noHit = true;
}
void SSDExplosion::EntityUpdate()
{
SSDEntity::EntityUpdate();
//Always set my position to my buddies position except change z to be on top
if( followBuddy )
{
position = buddy->position;
position.z -= 50;
}
else
{
//Only mess with the z if we are not following
position.z = buddy->position.z - 50;
}
//Scale the image based on the time
size = finalSize * ( ( float )( currentTime - beginTime ) / ( float )length );
//Destroy myself after the explosion is done
if( currentTime > endTime )
{
destroyed = true;
if( killBuddy )
{
//Destroy the exploding object
buddy->destroyed = true;
}
}
}
SSDExplosion* SSDExplosion::GetNewExplosion( idGameSSDWindow* _game, const idVec3& _position, const idVec2& _size, int _length, int _type, SSDEntity* _buddy, bool _killBuddy, bool _followBuddy )
{
for( int i = 0; i < MAX_EXPLOSIONS; i++ )
{
if( !explosionPool[i].inUse )
{
explosionPool[i].Init( _game, _position, _size, _length, _type, _buddy, _killBuddy, _followBuddy );
explosionPool[i].inUse = true;
return &explosionPool[i];
}
}
return NULL;
}
SSDExplosion* SSDExplosion::GetSpecificExplosion( int id )
{
return &explosionPool[id];
}
void SSDExplosion::WriteExplosions( idFile* savefile )
{
int count = 0;
for( int i = 0; i < MAX_EXPLOSIONS; i++ )
{
if( explosionPool[i].inUse )
{
count++;
}
}
savefile->Write( &count, sizeof( count ) );
for( int i = 0; i < MAX_EXPLOSIONS; i++ )
{
if( explosionPool[i].inUse )
{
savefile->Write( &( explosionPool[i].id ), sizeof( explosionPool[i].id ) );
explosionPool[i].WriteToSaveGame( savefile );
}
}
}
void SSDExplosion::ReadExplosions( idFile* savefile, idGameSSDWindow* _game )
{
int count;
savefile->Read( &count, sizeof( count ) );
for( int i = 0; i < count; i++ )
{
int id;
savefile->Read( &id, sizeof( id ) );
SSDExplosion* ent = GetSpecificExplosion( id );
ent->ReadFromSaveGame( savefile, _game );
}
}
/*
*****************************************************************************
* SSDPoints
****************************************************************************
*/
SSDPoints SSDPoints::pointsPool[MAX_POINTS];
SSDPoints::SSDPoints()
{
type = SSD_ENTITY_POINTS;
}
SSDPoints::~SSDPoints()
{
}
void SSDPoints::WriteToSaveGame( idFile* savefile )
{
SSDEntity::WriteToSaveGame( savefile );
savefile->Write( &length, sizeof( length ) );
savefile->Write( &distance, sizeof( distance ) );
savefile->Write( &beginTime, sizeof( beginTime ) );
savefile->Write( &endTime, sizeof( endTime ) );
savefile->Write( &beginPosition, sizeof( beginPosition ) );
savefile->Write( &endPosition, sizeof( endPosition ) );
savefile->Write( &beginColor, sizeof( beginColor ) );
savefile->Write( &endColor, sizeof( endColor ) );
}
void SSDPoints::ReadFromSaveGame( idFile* savefile, idGameSSDWindow* _game )
{
SSDEntity::ReadFromSaveGame( savefile, _game );
savefile->Read( &length, sizeof( length ) );
savefile->Read( &distance, sizeof( distance ) );
savefile->Read( &beginTime, sizeof( beginTime ) );
savefile->Read( &endTime, sizeof( endTime ) );
savefile->Read( &beginPosition, sizeof( beginPosition ) );
savefile->Read( &endPosition, sizeof( endPosition ) );
savefile->Read( &beginColor, sizeof( beginColor ) );
savefile->Read( &endColor, sizeof( endColor ) );
}
void SSDPoints::Init( idGameSSDWindow* _game, SSDEntity* _ent, int _points, int _length, int _distance, const idVec4& color )
{
EntityInit();
SetGame( _game );
length = _length;
distance = _distance;
beginTime = game->ssdTime;
endTime = beginTime + length;
textScale = 0.4f;
text = va( "%d", _points );
float width = 0;
for( int i = 0; i < text.Length(); i++ )
{
width += dc->CharWidth( text[i], textScale );
}
size.Set( 0, 0 );
//Set the start position at the top of the passed in entity
position = WorldToScreen( _ent->position );
position = ScreenToWorld( position );
position.z = 0;
position.x -= ( width / 2.0f );
beginPosition = position;
endPosition = beginPosition;
endPosition.y += _distance;
//beginColor.Set(0,1,0,1);
endColor.Set( 1, 1, 1, 0 );
beginColor = color;
beginColor.w = 1;
noPlayerDamage = true;
noHit = true;
}
void SSDPoints::EntityUpdate()
{
float t = ( float )( currentTime - beginTime ) / ( float )length;
//Move up from the start position
position.Lerp( beginPosition, endPosition, t );
//Interpolate the color
foreColor.Lerp( beginColor, endColor, t );
if( currentTime > endTime )
{
destroyed = true;
}
}
SSDPoints* SSDPoints::GetNewPoints( idGameSSDWindow* _game, SSDEntity* _ent, int _points, int _length, int _distance, const idVec4& color )
{
for( int i = 0; i < MAX_POINTS; i++ )
{
if( !pointsPool[i].inUse )
{
pointsPool[i].Init( _game, _ent, _points, _length, _distance, color );
pointsPool[i].inUse = true;
return &pointsPool[i];
}
}
return NULL;
}
SSDPoints* SSDPoints::GetSpecificPoints( int id )
{
return &pointsPool[id];
}
void SSDPoints::WritePoints( idFile* savefile )
{
int count = 0;
for( int i = 0; i < MAX_POINTS; i++ )
{
if( pointsPool[i].inUse )
{
count++;
}
}
savefile->Write( &count, sizeof( count ) );
for( int i = 0; i < MAX_POINTS; i++ )
{
if( pointsPool[i].inUse )
{
savefile->Write( &( pointsPool[i].id ), sizeof( pointsPool[i].id ) );
pointsPool[i].WriteToSaveGame( savefile );
}
}
}
void SSDPoints::ReadPoints( idFile* savefile, idGameSSDWindow* _game )
{
int count;
savefile->Read( &count, sizeof( count ) );
for( int i = 0; i < count; i++ )
{
int id;
savefile->Read( &id, sizeof( id ) );
SSDPoints* ent = GetSpecificPoints( id );
ent->ReadFromSaveGame( savefile, _game );
}
}
/*
*****************************************************************************
* SSDProjectile
****************************************************************************
*/
SSDProjectile SSDProjectile::projectilePool[MAX_PROJECTILES];
#define PROJECTILE_MATERIAL "game/SSD/fball"
SSDProjectile::SSDProjectile()
{
type = SSD_ENTITY_PROJECTILE;
}
SSDProjectile::~SSDProjectile()
{
}
void SSDProjectile::WriteToSaveGame( idFile* savefile )
{
SSDEntity::WriteToSaveGame( savefile );
savefile->Write( &dir, sizeof( dir ) );
savefile->Write( &speed, sizeof( speed ) );
savefile->Write( &beginTime, sizeof( beginTime ) );
savefile->Write( &endTime, sizeof( endTime ) );
savefile->Write( &endPosition, sizeof( endPosition ) );
}
void SSDProjectile::ReadFromSaveGame( idFile* savefile, idGameSSDWindow* _game )
{
SSDEntity::ReadFromSaveGame( savefile, _game );
savefile->Read( &dir, sizeof( dir ) );
savefile->Read( &speed, sizeof( speed ) );
savefile->Read( &beginTime, sizeof( beginTime ) );
savefile->Read( &endTime, sizeof( endTime ) );
savefile->Read( &endPosition, sizeof( endPosition ) );
}
void SSDProjectile::Init( idGameSSDWindow* _game, const idVec3& _beginPosition, const idVec3& _endPosition, float _speed, float _size )
{
EntityInit();
SetGame( _game );
SetMaterial( PROJECTILE_MATERIAL );
size.Set( _size, _size );
position = _beginPosition;
endPosition = _endPosition;
dir = _endPosition - position;
dir.Normalize();
//speed.Zero();
speed.x = speed.y = speed.z = _speed;
noHit = true;
}
void SSDProjectile::EntityUpdate()
{
SSDEntity::EntityUpdate();
//Move forward based on speed (units per second)
idVec3 moved = dir * ( ( float )elapsed / 1000.0f ) * speed.z;
position += moved;
if( position.z > endPosition.z )
{
//We have reached our position
destroyed = true;
}
}
SSDProjectile* SSDProjectile::GetNewProjectile( idGameSSDWindow* _game, const idVec3& _beginPosition, const idVec3& _endPosition, float _speed, float _size )
{
for( int i = 0; i < MAX_PROJECTILES; i++ )
{
if( !projectilePool[i].inUse )
{
projectilePool[i].Init( _game, _beginPosition, _endPosition, _speed, _size );
projectilePool[i].inUse = true;
return &projectilePool[i];
}
}
return NULL;
}
SSDProjectile* SSDProjectile::GetSpecificProjectile( int id )
{
return &projectilePool[id];
}
void SSDProjectile::WriteProjectiles( idFile* savefile )
{
int count = 0;
for( int i = 0; i < MAX_PROJECTILES; i++ )
{
if( projectilePool[i].inUse )
{
count++;
}
}
savefile->Write( &count, sizeof( count ) );
for( int i = 0; i < MAX_PROJECTILES; i++ )
{
if( projectilePool[i].inUse )
{
savefile->Write( &( projectilePool[i].id ), sizeof( projectilePool[i].id ) );
projectilePool[i].WriteToSaveGame( savefile );
}
}
}
void SSDProjectile::ReadProjectiles( idFile* savefile, idGameSSDWindow* _game )
{
int count;
savefile->Read( &count, sizeof( count ) );
for( int i = 0; i < count; i++ )
{
int id;
savefile->Read( &id, sizeof( id ) );
SSDProjectile* ent = GetSpecificProjectile( id );
ent->ReadFromSaveGame( savefile, _game );
}
}
/*
*****************************************************************************
* SSDPowerup
****************************************************************************
*/
const char* powerupMaterials[][2] =
{
"game/SSD/powerupHealthClosed", "game/SSD/powerupHealthOpen",
"game/SSD/powerupSuperBlasterClosed", "game/SSD/powerupSuperBlasterOpen",
"game/SSD/powerupNukeClosed", "game/SSD/powerupNukeOpen",
"game/SSD/powerupRescueClosed", "game/SSD/powerupRescueOpen",
"game/SSD/powerupBonusPointsClosed", "game/SSD/powerupBonusPointsOpen",
"game/SSD/powerupDamageClosed", "game/SSD/powerupDamageOpen",
};
#define POWERUP_MATERIAL_COUNT 6
SSDPowerup SSDPowerup::powerupPool[MAX_POWERUPS];
SSDPowerup::SSDPowerup()
{
}
SSDPowerup::~SSDPowerup()
{
}
void SSDPowerup::WriteToSaveGame( idFile* savefile )
{
SSDMover::WriteToSaveGame( savefile );
savefile->Write( &powerupState, sizeof( powerupState ) );
savefile->Write( &powerupType, sizeof( powerupType ) );
}
void SSDPowerup::ReadFromSaveGame( idFile* savefile, idGameSSDWindow* _game )
{
SSDMover::ReadFromSaveGame( savefile, _game );
savefile->Read( &powerupState, sizeof( powerupState ) );
savefile->Read( &powerupType, sizeof( powerupType ) );
}
void SSDPowerup::OnHit( int key )
{
if( powerupState == POWERUP_STATE_CLOSED )
{
//Small explosion to indicate it is opened
SSDExplosion* explosion = SSDExplosion::GetNewExplosion( game, position, size * 2.0f, 300, SSDExplosion::EXPLOSION_NORMAL, this, false, true );
game->entities.Append( explosion );
powerupState = POWERUP_STATE_OPEN;
SetMaterial( powerupMaterials[powerupType][powerupState] );
}
else
{
//Destory the powerup with a big explosion
SSDExplosion* explosion = SSDExplosion::GetNewExplosion( game, position, size * 2, 300, SSDExplosion::EXPLOSION_NORMAL, this );
game->entities.Append( explosion );
game->PlaySound( "arcade_explode" );
noHit = true;
noPlayerDamage = true;
}
}
void SSDPowerup::OnStrikePlayer()
{
if( powerupState == POWERUP_STATE_OPEN )
{
//The powerup was open so activate it
OnActivatePowerup();
}
//Just destroy the powerup
destroyed = true;
}
void SSDPowerup::OnOpenPowerup()
{
}
void SSDPowerup::OnActivatePowerup()
{
switch( powerupType )
{
case POWERUP_TYPE_HEALTH:
{
game->AddHealth( 10 );
break;
}
case POWERUP_TYPE_SUPER_BLASTER:
{
game->OnSuperBlaster();
break;
}
case POWERUP_TYPE_ASTEROID_NUKE:
{
game->OnNuke();
break;
}
case POWERUP_TYPE_RESCUE_ALL:
{
game->OnRescueAll();
break;
}
case POWERUP_TYPE_BONUS_POINTS:
{
int points = ( game->random.RandomInt( 5 ) + 1 ) * 100;
game->AddScore( this, points );
break;
}
case POWERUP_TYPE_DAMAGE:
{
game->AddDamage( 10 );
game->PlaySound( "arcade_explode" );
break;
}
}
}
void SSDPowerup::Init( idGameSSDWindow* _game, float _speed, float _rotation )
{
EntityInit();
MoverInit( idVec3( 0, 0, -_speed ), _rotation );
SetGame( _game );
SetSize( idVec2( 200, 200 ) );
SetRadius( Max( size.x, size.y ), 0.3f );
type = SSD_ENTITY_POWERUP;
idVec3 startPosition;
startPosition.x = game->random.RandomInt( V_WIDTH ) - ( V_WIDTH / 2.0f );
startPosition.y = game->random.RandomInt( V_HEIGHT ) - ( V_HEIGHT / 2.0f );
startPosition.z = ENTITY_START_DIST;
position = startPosition;
//SetPosition(startPosition);
powerupState = POWERUP_STATE_CLOSED;
powerupType = game->random.RandomInt( POWERUP_TYPE_MAX + 1 );
if( powerupType >= POWERUP_TYPE_MAX )
{
powerupType = 0;
}
/*OutputDebugString(va("Powerup: %d\n", powerupType));
if(powerupType == 0) {
int x = 0;
}*/
SetMaterial( powerupMaterials[powerupType][powerupState] );
}
SSDPowerup* SSDPowerup::GetNewPowerup( idGameSSDWindow* _game, float _speed, float _rotation )
{
for( int i = 0; i < MAX_POWERUPS; i++ )
{
if( !powerupPool[i].inUse )
{
powerupPool[i].Init( _game, _speed, _rotation );
powerupPool[i].inUse = true;
return &powerupPool[i];
}
}
return NULL;
}
SSDPowerup* SSDPowerup::GetSpecificPowerup( int id )
{
return &powerupPool[id];
}
void SSDPowerup::WritePowerups( idFile* savefile )
{
int count = 0;
for( int i = 0; i < MAX_POWERUPS; i++ )
{
if( powerupPool[i].inUse )
{
count++;
}
}
savefile->Write( &count, sizeof( count ) );
for( int i = 0; i < MAX_POWERUPS; i++ )
{
if( powerupPool[i].inUse )
{
savefile->Write( &( powerupPool[i].id ), sizeof( powerupPool[i].id ) );
powerupPool[i].WriteToSaveGame( savefile );
}
}
}
void SSDPowerup::ReadPowerups( idFile* savefile, idGameSSDWindow* _game )
{
int count;
savefile->Read( &count, sizeof( count ) );
for( int i = 0; i < count; i++ )
{
int id;
savefile->Read( &id, sizeof( id ) );
SSDPowerup* ent = GetSpecificPowerup( id );
ent->ReadFromSaveGame( savefile, _game );
}
}
/*
*****************************************************************************
* idGameSSDWindow
****************************************************************************
*/
idRandom idGameSSDWindow::random;
idGameSSDWindow::idGameSSDWindow( idUserInterfaceLocal* g ) : idWindow( g )
{
gui = g;
CommonInit();
}
idGameSSDWindow::~idGameSSDWindow()
{
ResetGameStats();
}
void idGameSSDWindow::WriteToSaveGame( idFile* savefile )
{
idWindow::WriteToSaveGame( savefile );
savefile->Write( &ssdTime, sizeof( ssdTime ) );
beginLevel.WriteToSaveGame( savefile );
resetGame.WriteToSaveGame( savefile );
continueGame.WriteToSaveGame( savefile );
refreshGuiData.WriteToSaveGame( savefile );
crosshair.WriteToSaveGame( savefile );
savefile->Write( &screenBounds, sizeof( screenBounds ) );
savefile->Write( &levelCount, sizeof( levelCount ) );
for( int i = 0; i < levelCount; i++ )
{
savefile->Write( &( levelData[i] ), sizeof( SSDLevelData_t ) );
savefile->Write( &( asteroidData[i] ), sizeof( SSDAsteroidData_t ) );
savefile->Write( &( astronautData[i] ), sizeof( SSDAstronautData_t ) );
savefile->Write( &( powerupData[i] ), sizeof( SSDPowerupData_t ) );
}
savefile->Write( &weaponCount, sizeof( weaponCount ) );
for( int i = 0; i < weaponCount; i++ )
{
savefile->Write( &( weaponData[i] ), sizeof( SSDWeaponData_t ) );
}
savefile->Write( &superBlasterTimeout, sizeof( superBlasterTimeout ) );
savefile->Write( &gameStats, sizeof( SSDGameStats_t ) );
//Write All Static Entities
SSDAsteroid::WriteAsteroids( savefile );
SSDAstronaut::WriteAstronauts( savefile );
SSDExplosion::WriteExplosions( savefile );
SSDPoints::WritePoints( savefile );
SSDProjectile::WriteProjectiles( savefile );
SSDPowerup::WritePowerups( savefile );
int entCount = entities.Num();
savefile->Write( &entCount, sizeof( entCount ) );
for( int i = 0; i < entCount; i++ )
{
savefile->Write( &( entities[i]->type ), sizeof( entities[i]->type ) );
savefile->Write( &( entities[i]->id ), sizeof( entities[i]->id ) );
}
}
void idGameSSDWindow::ReadFromSaveGame( idFile* savefile )
{
idWindow::ReadFromSaveGame( savefile );
savefile->Read( &ssdTime, sizeof( ssdTime ) );
beginLevel.ReadFromSaveGame( savefile );
resetGame.ReadFromSaveGame( savefile );
continueGame.ReadFromSaveGame( savefile );
refreshGuiData.ReadFromSaveGame( savefile );
crosshair.ReadFromSaveGame( savefile );
savefile->Read( &screenBounds, sizeof( screenBounds ) );
savefile->Read( &levelCount, sizeof( levelCount ) );
for( int i = 0; i < levelCount; i++ )
{
SSDLevelData_t newLevel;
savefile->Read( &newLevel, sizeof( SSDLevelData_t ) );
levelData.Append( newLevel );
SSDAsteroidData_t newAsteroid;
savefile->Read( &newAsteroid, sizeof( SSDAsteroidData_t ) );
asteroidData.Append( newAsteroid );
SSDAstronautData_t newAstronaut;
savefile->Read( &newAstronaut, sizeof( SSDAstronautData_t ) );
astronautData.Append( newAstronaut );
SSDPowerupData_t newPowerup;
savefile->Read( &newPowerup, sizeof( SSDPowerupData_t ) );
powerupData.Append( newPowerup );
}
savefile->Read( &weaponCount, sizeof( weaponCount ) );
for( int i = 0; i < weaponCount; i++ )
{
SSDWeaponData_t newWeapon;
savefile->Read( &newWeapon, sizeof( SSDWeaponData_t ) );
weaponData.Append( newWeapon );
}
savefile->Read( &superBlasterTimeout, sizeof( superBlasterTimeout ) );
savefile->Read( &gameStats, sizeof( SSDGameStats_t ) );
//Reset this because it is no longer valid
gameStats.levelStats.targetEnt = NULL;
SSDAsteroid::ReadAsteroids( savefile, this );
SSDAstronaut::ReadAstronauts( savefile, this );
SSDExplosion::ReadExplosions( savefile, this );
SSDPoints::ReadPoints( savefile, this );
SSDProjectile::ReadProjectiles( savefile, this );
SSDPowerup::ReadPowerups( savefile, this );
int entCount;
savefile->Read( &entCount, sizeof( entCount ) );
for( int i = 0; i < entCount; i++ )
{
int type, id;
savefile->Read( &type, sizeof( type ) );
savefile->Read( &id, sizeof( id ) );
SSDEntity* ent = GetSpecificEntity( type, id );
if( ent )
{
entities.Append( ent );
}
}
}
const char* idGameSSDWindow::HandleEvent( const sysEvent_t* event, bool* updateVisuals )
{
// need to call this to allow proper focus and capturing on embedded children
const char* ret = idWindow::HandleEvent( event, updateVisuals );
if( !gameStats.gameRunning )
{
return ret;
}
int key = event->evValue;
if( event->evType == SE_KEY )
{
if( !event->evValue2 )
{
return ret;
}
if( key == K_MOUSE1 || key == K_MOUSE2 )
{
FireWeapon( key );
}
else
{
return ret;
}
}
return ret;
}
idWinVar* idGameSSDWindow::GetWinVarByName( const char* _name, bool winLookup, drawWin_t** owner )
{
idWinVar* retVar = NULL;
if( idStr::Icmp( _name, "beginLevel" ) == 0 )
{
retVar = &beginLevel;
}
if( idStr::Icmp( _name, "resetGame" ) == 0 )
{
retVar = &resetGame;
}
if( idStr::Icmp( _name, "continueGame" ) == 0 )
{
retVar = &continueGame;
}
if( idStr::Icmp( _name, "refreshGuiData" ) == 0 )
{
retVar = &refreshGuiData;
}
if( retVar )
{
return retVar;
}
return idWindow::GetWinVarByName( _name, winLookup, owner );
}
void idGameSSDWindow::Draw( int time, float x, float y )
{
//Update the game every frame before drawing
UpdateGame();
RefreshGuiData();
if( gameStats.gameRunning )
{
ZOrderEntities();
//Draw from back to front
for( int i = entities.Num() - 1; i >= 0; i-- )
{
entities[i]->Draw();
}
//The last thing to draw is the crosshair
idVec2 cursor;
//GetCursor(cursor);
cursor.x = gui->CursorX();
cursor.y = gui->CursorY();
crosshair.Draw( cursor );
}
}
bool idGameSSDWindow::ParseInternalVar( const char* _name, idTokenParser* src )
{
if( idStr::Icmp( _name, "beginLevel" ) == 0 )
{
beginLevel = src->ParseBool();
return true;
}
if( idStr::Icmp( _name, "resetGame" ) == 0 )
{
resetGame = src->ParseBool();
return true;
}
if( idStr::Icmp( _name, "continueGame" ) == 0 )
{
continueGame = src->ParseBool();
return true;
}
if( idStr::Icmp( _name, "refreshGuiData" ) == 0 )
{
refreshGuiData = src->ParseBool();
return true;
}
if( idStr::Icmp( _name, "levelcount" ) == 0 )
{
levelCount = src->ParseInt();
for( int i = 0; i < levelCount; i++ )
{
SSDLevelData_t newLevel;
memset( &newLevel, 0, sizeof( SSDLevelData_t ) );
levelData.Append( newLevel );
SSDAsteroidData_t newAsteroid;
memset( &newAsteroid, 0, sizeof( SSDAsteroidData_t ) );
asteroidData.Append( newAsteroid );
SSDAstronautData_t newAstronaut;
memset( &newAstronaut, 0, sizeof( SSDAstronautData_t ) );
astronautData.Append( newAstronaut );
SSDPowerupData_t newPowerup;
memset( &newPowerup, 0, sizeof( SSDPowerupData_t ) );
powerupData.Append( newPowerup );
}
return true;
}
if( idStr::Icmp( _name, "weaponCount" ) == 0 )
{
weaponCount = src->ParseInt();
for( int i = 0; i < weaponCount; i++ )
{
SSDWeaponData_t newWeapon;
memset( &newWeapon, 0, sizeof( SSDWeaponData_t ) );
weaponData.Append( newWeapon );
}
return true;
}
if( idStr::FindText( _name, "leveldata", false ) >= 0 )
{
idStr tempName = _name;
int level = atoi( tempName.Right( 2 ) ) - 1;
idStr levelData;
ParseString( src, levelData );
ParseLevelData( level, levelData );
return true;
}
if( idStr::FindText( _name, "asteroiddata", false ) >= 0 )
{
idStr tempName = _name;
int level = atoi( tempName.Right( 2 ) ) - 1;
idStr asteroidData;
ParseString( src, asteroidData );
ParseAsteroidData( level, asteroidData );
return true;
}
if( idStr::FindText( _name, "weapondata", false ) >= 0 )
{
idStr tempName = _name;
int weapon = atoi( tempName.Right( 2 ) ) - 1;
idStr weaponData;
ParseString( src, weaponData );
ParseWeaponData( weapon, weaponData );
return true;
}
if( idStr::FindText( _name, "astronautdata", false ) >= 0 )
{
idStr tempName = _name;
int level = atoi( tempName.Right( 2 ) ) - 1;
idStr astronautData;
ParseString( src, astronautData );
ParseAstronautData( level, astronautData );
return true;
}
if( idStr::FindText( _name, "powerupdata", false ) >= 0 )
{
idStr tempName = _name;
int level = atoi( tempName.Right( 2 ) ) - 1;
idStr powerupData;
ParseString( src, powerupData );
ParsePowerupData( level, powerupData );
return true;
}
return idWindow::ParseInternalVar( _name, src );
}
void idGameSSDWindow::ParseLevelData( int level, const idStr& levelDataString )
{
idParser parser;
idToken token;
parser.LoadMemory( levelDataString.c_str(), levelDataString.Length(), "LevelData" );
levelData[level].spawnBuffer = parser.ParseFloat();
levelData[level].needToWin = parser.ParseInt(); //Required Destroyed
}
void idGameSSDWindow::ParseAsteroidData( int level, const idStr& asteroidDataString )
{
idParser parser;
idToken token;
parser.LoadMemory( asteroidDataString.c_str(), asteroidDataString.Length(), "AsteroidData" );
asteroidData[level].speedMin = parser.ParseFloat(); //Speed Min
asteroidData[level].speedMax = parser.ParseFloat(); //Speed Max
asteroidData[level].sizeMin = parser.ParseFloat(); //Size Min
asteroidData[level].sizeMax = parser.ParseFloat(); //Size Max
asteroidData[level].rotateMin = parser.ParseFloat(); //Rotate Min (rotations per second)
asteroidData[level].rotateMax = parser.ParseFloat(); //Rotate Max (rotations per second)
asteroidData[level].spawnMin = parser.ParseInt(); //Spawn Min
asteroidData[level].spawnMax = parser.ParseInt(); //Spawn Max
asteroidData[level].asteroidHealth = parser.ParseInt(); //Health of the asteroid
asteroidData[level].asteroidDamage = parser.ParseInt(); //Asteroid Damage
asteroidData[level].asteroidPoints = parser.ParseInt(); //Points awarded for destruction
}
void idGameSSDWindow::ParsePowerupData( int level, const idStr& powerupDataString )
{
idParser parser;
idToken token;
parser.LoadMemory( powerupDataString.c_str(), powerupDataString.Length(), "PowerupData" );
powerupData[level].speedMin = parser.ParseFloat(); //Speed Min
powerupData[level].speedMax = parser.ParseFloat(); //Speed Max
powerupData[level].rotateMin = parser.ParseFloat(); //Rotate Min (rotations per second)
powerupData[level].rotateMax = parser.ParseFloat(); //Rotate Max (rotations per second)
powerupData[level].spawnMin = parser.ParseInt(); //Spawn Min
powerupData[level].spawnMax = parser.ParseInt(); //Spawn Max
}
void idGameSSDWindow::ParseWeaponData( int weapon, const idStr& weaponDataString )
{
idParser parser;
idToken token;
parser.LoadMemory( weaponDataString.c_str(), weaponDataString.Length(), "WeaponData" );
weaponData[weapon].speed = parser.ParseFloat();
weaponData[weapon].damage = parser.ParseFloat();
weaponData[weapon].size = parser.ParseFloat();
}
void idGameSSDWindow::ParseAstronautData( int level, const idStr& astronautDataString )
{
idParser parser;
idToken token;
parser.LoadMemory( astronautDataString.c_str(), astronautDataString.Length(), "AstronautData" );
astronautData[level].speedMin = parser.ParseFloat(); //Speed Min
astronautData[level].speedMax = parser.ParseFloat(); //Speed Max
astronautData[level].rotateMin = parser.ParseFloat(); //Rotate Min (rotations per second)
astronautData[level].rotateMax = parser.ParseFloat(); //Rotate Max (rotations per second)
astronautData[level].spawnMin = parser.ParseInt(); //Spawn Min
astronautData[level].spawnMax = parser.ParseInt(); //Spawn Max
astronautData[level].health = parser.ParseInt(); //Health of the asteroid
astronautData[level].points = parser.ParseInt(); //Asteroid Damage
astronautData[level].penalty = parser.ParseInt(); //Points awarded for destruction
}
void idGameSSDWindow::CommonInit()
{
crosshair.InitCrosshairs();
beginLevel = false;
resetGame = false;
continueGame = false;
refreshGuiData = false;
ssdTime = 0;
levelCount = 0;
weaponCount = 0;
screenBounds = idBounds( idVec3( -320, -240, 0 ), idVec3( 320, 240, 0 ) );
superBlasterTimeout = 0;
currentSound = 0;
//Precahce all assets that are loaded dynamically
declManager->FindMaterial( ASTEROID_MATERIAL );
declManager->FindMaterial( ASTRONAUT_MATERIAL );
for( int i = 0; i < EXPLOSION_MATERIAL_COUNT; i++ )
{
declManager->FindMaterial( explosionMaterials[i] );
}
declManager->FindMaterial( PROJECTILE_MATERIAL );
for( int i = 0; i < POWERUP_MATERIAL_COUNT; i++ )
{
declManager->FindMaterial( powerupMaterials[i][0] );
declManager->FindMaterial( powerupMaterials[i][1] );
}
// Precache sounds
declManager->FindSound( "arcade_blaster" );
declManager->FindSound( "arcade_capture" );
declManager->FindSound( "arcade_explode" );
ResetGameStats();
}
void idGameSSDWindow::ResetGameStats()
{
ResetEntities();
//Reset the gamestats structure
memset( &gameStats, 0, sizeof( gameStats ) );
gameStats.health = 100;
}
void idGameSSDWindow::ResetLevelStats()
{
ResetEntities();
//Reset the level statistics structure
memset( &gameStats.levelStats, 0, sizeof( gameStats.levelStats ) );
}
void idGameSSDWindow::ResetEntities()
{
//Destroy all of the entities
for( int i = 0; i < entities.Num(); i++ )
{
entities[i]->DestroyEntity();
}
entities.Clear();
}
void idGameSSDWindow::StartGame()
{
gameStats.gameRunning = true;
}
void idGameSSDWindow::StopGame()
{
gameStats.gameRunning = false;
}
void idGameSSDWindow::GameOver()
{
StopGame();
gui->HandleNamedEvent( "gameOver" );
}
void idGameSSDWindow::BeginLevel( int level )
{
ResetLevelStats();
gameStats.currentLevel = level;
StartGame();
}
/**
* Continue game resets the players health
*/
void idGameSSDWindow::ContinueGame()
{
gameStats.health = 100;
StartGame();
}
void idGameSSDWindow::LevelComplete()
{
gameStats.prebonusscore = gameStats.score;
// Add the bonuses
int accuracy;
if( !gameStats.levelStats.shotCount )
{
accuracy = 0;
}
else
{
accuracy = ( int )( ( ( float )gameStats.levelStats.hitCount / ( float )gameStats.levelStats.shotCount ) * 100.0f );
}
int accuracyPoints = Max( 0, accuracy - 50 ) * 20;
gui->SetStateString( "player_accuracy_score", va( "%i", accuracyPoints ) );
gameStats.score += accuracyPoints;
int saveAccuracy;
int totalAst = gameStats.levelStats.savedAstronauts + gameStats.levelStats.killedAstronauts;
if( !totalAst )
{
saveAccuracy = 0;
}
else
{
saveAccuracy = ( int )( ( ( float )gameStats.levelStats.savedAstronauts / ( float )totalAst ) * 100.0f );
}
accuracyPoints = Max( 0, saveAccuracy - 50 ) * 20;
gui->SetStateString( "save_accuracy_score", va( "%i", accuracyPoints ) );
gameStats.score += accuracyPoints;
StopSuperBlaster();
gameStats.nextLevel++;
if( gameStats.nextLevel >= levelCount )
{
//Have they beaten the game
GameComplete();
}
else
{
//Make sure we don't go above the levelcount
//min(gameStats.nextLevel, levelCount-1);
StopGame();
gui->HandleNamedEvent( "levelComplete" );
}
}
void idGameSSDWindow::GameComplete()
{
StopGame();
gui->HandleNamedEvent( "gameComplete" );
}
void idGameSSDWindow::UpdateGame()
{
//Check to see if and functions where called by the gui
if( beginLevel == true )
{
beginLevel = false;
BeginLevel( gameStats.nextLevel );
}
if( resetGame == true )
{
resetGame = false;
ResetGameStats();
}
if( continueGame == true )
{
continueGame = false;
ContinueGame();
}
if( refreshGuiData == true )
{
refreshGuiData = false;
RefreshGuiData();
}
if( gameStats.gameRunning )
{
//We assume an upate every 16 milliseconds
ssdTime += 16;
if( superBlasterTimeout && ssdTime > superBlasterTimeout )
{
StopSuperBlaster();
}
//Find if we are targeting and enemy
idVec2 cursor;
//GetCursor(cursor);
cursor.x = gui->CursorX();
cursor.y = gui->CursorY();
gameStats.levelStats.targetEnt = EntityHitTest( cursor );
//Update from back to front
for( int i = entities.Num() - 1; i >= 0; i-- )
{
entities[i]->Update();
}
CheckForHits();
//Delete entities that need to be deleted
for( int i = entities.Num() - 1; i >= 0; i-- )
{
if( entities[i]->destroyed )
{
SSDEntity* ent = entities[i];
ent->DestroyEntity();
entities.RemoveIndex( i );
}
}
//Check if we can spawn an asteroid
SpawnAsteroid();
//Check if we should spawn an astronaut
SpawnAstronaut();
//Check if we should spawn an asteroid
SpawnPowerup();
}
}
void idGameSSDWindow::CheckForHits()
{
//See if the entity has gotten close enough
for( int i = 0; i < entities.Num(); i++ )
{
SSDEntity* ent = entities[i];
if( ent->position.z <= Z_NEAR )
{
if( !ent->noPlayerDamage )
{
//Is the object still in the screen
idVec3 entPos = ent->position;
entPos.z = 0;
idBounds entBounds( entPos );
entBounds.ExpandSelf( ent->hitRadius );
if( screenBounds.IntersectsBounds( entBounds ) )
{
ent->OnStrikePlayer();
//The entity hit the player figure out what is was and act appropriately
if( ent->type == SSD_ENTITY_ASTEROID )
{
AsteroidStruckPlayer( static_cast<SSDAsteroid*>( ent ) );
}
else if( ent->type == SSD_ENTITY_ASTRONAUT )
{
AstronautStruckPlayer( static_cast<SSDAstronaut*>( ent ) );
}
}
else
{
//Tag for removal later in the frame
ent->destroyed = true;
}
}
}
}
}
void idGameSSDWindow::ZOrderEntities()
{
//Z-Order the entities
//Using a simple sorting method
for( int i = entities.Num() - 1; i >= 0; i-- )
{
bool flipped = false;
for( int j = 0; j < i ; j++ )
{
if( entities[j]->position.z > entities[j + 1]->position.z )
{
SSDEntity* ent = entities[j];
entities[j] = entities[j + 1];
entities[j + 1] = ent;
flipped = true;
}
}
if( !flipped )
{
//Jump out because it is sorted
break;
}
}
}
void idGameSSDWindow::SpawnAsteroid()
{
int currentTime = ssdTime;
if( currentTime < gameStats.levelStats.nextAsteroidSpawnTime )
{
//Not time yet
return;
}
//Lets spawn it
idVec3 startPosition;
float spawnBuffer = levelData[gameStats.currentLevel].spawnBuffer * 2.0f;
startPosition.x = random.RandomInt( V_WIDTH + spawnBuffer ) - ( ( V_WIDTH / 2.0f ) + spawnBuffer );
startPosition.y = random.RandomInt( V_HEIGHT + spawnBuffer ) - ( ( V_HEIGHT / 2.0f ) + spawnBuffer );
startPosition.z = ENTITY_START_DIST;
float speed = random.RandomInt( asteroidData[gameStats.currentLevel].speedMax - asteroidData[gameStats.currentLevel].speedMin ) + asteroidData[gameStats.currentLevel].speedMin;
float size = random.RandomInt( asteroidData[gameStats.currentLevel].sizeMax - asteroidData[gameStats.currentLevel].sizeMin ) + asteroidData[gameStats.currentLevel].sizeMin;
float rotate = ( random.RandomFloat() * ( asteroidData[gameStats.currentLevel].rotateMax - asteroidData[gameStats.currentLevel].rotateMin ) ) + asteroidData[gameStats.currentLevel].rotateMin;
SSDAsteroid* asteroid = SSDAsteroid::GetNewAsteroid( this, startPosition, idVec2( size, size ), speed, rotate, asteroidData[gameStats.currentLevel].asteroidHealth );
entities.Append( asteroid );
gameStats.levelStats.nextAsteroidSpawnTime = currentTime + random.RandomInt( asteroidData[gameStats.currentLevel].spawnMax - asteroidData[gameStats.currentLevel].spawnMin ) + asteroidData[gameStats.currentLevel].spawnMin;
}
void idGameSSDWindow::FireWeapon( int key )
{
idVec2 cursorWorld = GetCursorWorld();
idVec2 cursor;
//GetCursor(cursor);
cursor.x = gui->CursorX();
cursor.y = gui->CursorY();
if( key == K_MOUSE1 )
{
gameStats.levelStats.shotCount++;
if( gameStats.levelStats.targetEnt )
{
//Aim the projectile from the bottom of the screen directly at the ent
//SSDProjectile* newProj = new (TAG_OLD_UI) SSDProjectile(this, idVec3(320,0,0), gameStats.levelStats.targetEnt->position, weaponData[gameStats.currentWeapon].speed, weaponData[gameStats.currentWeapon].size);
SSDProjectile* newProj = SSDProjectile::GetNewProjectile( this, idVec3( 0, -180, 0 ), gameStats.levelStats.targetEnt->position, weaponData[gameStats.currentWeapon].speed, weaponData[gameStats.currentWeapon].size );
entities.Append( newProj );
//newProj = SSDProjectile::GetNewProjectile(this, idVec3(-320,-0,0), gameStats.levelStats.targetEnt->position, weaponData[gameStats.currentWeapon].speed, weaponData[gameStats.currentWeapon].size);
//entities.Append(newProj);
//We hit something
gameStats.levelStats.hitCount++;
gameStats.levelStats.targetEnt->OnHit( key );
if( gameStats.levelStats.targetEnt->type == SSD_ENTITY_ASTEROID )
{
HitAsteroid( static_cast<SSDAsteroid*>( gameStats.levelStats.targetEnt ), key );
}
else if( gameStats.levelStats.targetEnt->type == SSD_ENTITY_ASTRONAUT )
{
HitAstronaut( static_cast<SSDAstronaut*>( gameStats.levelStats.targetEnt ), key );
}
}
else
{
////Aim the projectile at the cursor position all the way to the far clipping
//SSDProjectile* newProj = SSDProjectile::GetNewProjectile(this, idVec3(0,-180,0), idVec3(cursorWorld.x, cursorWorld.y, (Z_FAR-Z_NEAR)/2.0f), weaponData[gameStats.currentWeapon].speed, weaponData[gameStats.currentWeapon].size);
//Aim the projectile so it crosses the cursor 1/4 of screen
idVec3 vec = idVec3( cursorWorld.x, cursorWorld.y, ( Z_FAR - Z_NEAR ) / 8.0f );
vec *= 8;
SSDProjectile* newProj = SSDProjectile::GetNewProjectile( this, idVec3( 0, -180, 0 ), vec, weaponData[gameStats.currentWeapon].speed, weaponData[gameStats.currentWeapon].size );
entities.Append( newProj );
}
//Play the blaster sound
PlaySound( "arcade_blaster" );
} /*else if (key == K_MOUSE2) {
if(gameStats.levelStats.targetEnt) {
if(gameStats.levelStats.targetEnt->type == SSD_ENTITY_ASTRONAUT) {
HitAstronaut(static_cast<SSDAstronaut*>(gameStats.levelStats.targetEnt), key);
}
}
}*/
}
SSDEntity* idGameSSDWindow::EntityHitTest( const idVec2& pt )
{
for( int i = 0; i < entities.Num(); i++ )
{
//Since we ZOrder the entities every frame we can stop at the first entity we hit.
//ToDo: Make sure this assumption is true
if( entities[i]->HitTest( pt ) )
{
return entities[i];
}
}
return NULL;
}
void idGameSSDWindow::HitAsteroid( SSDAsteroid* asteroid, int key )
{
asteroid->health -= weaponData[gameStats.currentWeapon].damage;
if( asteroid->health <= 0 )
{
//The asteroid has been destroyed
SSDExplosion* explosion = SSDExplosion::GetNewExplosion( this, asteroid->position, asteroid->size * 2, 300, SSDExplosion::EXPLOSION_NORMAL, asteroid );
entities.Append( explosion );
PlaySound( "arcade_explode" );
AddScore( asteroid, asteroidData[gameStats.currentLevel].asteroidPoints );
//Don't let the player hit it anymore because
asteroid->noHit = true;
gameStats.levelStats.destroyedAsteroids++;
//if(gameStats.levelStats.destroyedAsteroids >= levelData[gameStats.currentLevel].needToWin) {
// LevelComplete();
//}
}
else
{
//This was a damage hit so create a real small quick explosion
SSDExplosion* explosion = SSDExplosion::GetNewExplosion( this, asteroid->position, asteroid->size / 2.0f, 200, SSDExplosion::EXPLOSION_NORMAL, asteroid, false, false );
entities.Append( explosion );
}
}
void idGameSSDWindow::AsteroidStruckPlayer( SSDAsteroid* asteroid )
{
asteroid->noPlayerDamage = true;
asteroid->noHit = true;
AddDamage( asteroidData[gameStats.currentLevel].asteroidDamage );
SSDExplosion* explosion = SSDExplosion::GetNewExplosion( this, asteroid->position, asteroid->size * 2, 300, SSDExplosion::EXPLOSION_NORMAL, asteroid );
entities.Append( explosion );
PlaySound( "arcade_explode" );
}
void idGameSSDWindow::AddScore( SSDEntity* ent, int points )
{
SSDPoints* pointsEnt;
if( points > 0 )
{
pointsEnt = SSDPoints::GetNewPoints( this, ent, points, 1000, 50, idVec4( 0, 1, 0, 1 ) );
}
else
{
pointsEnt = SSDPoints::GetNewPoints( this, ent, points, 1000, 50, idVec4( 1, 0, 0, 1 ) );
}
entities.Append( pointsEnt );
gameStats.score += points;
gui->SetStateString( "player_score", va( "%i", gameStats.score ) );
}
void idGameSSDWindow::AddDamage( int damage )
{
gameStats.health -= damage;
gui->SetStateString( "player_health", va( "%i", gameStats.health ) );
gui->HandleNamedEvent( "playerDamage" );
if( gameStats.health <= 0 )
{
//The player is dead
GameOver();
}
}
void idGameSSDWindow::AddHealth( int health )
{
gameStats.health += health;
gameStats.health = Min( 100, gameStats.health );
}
void idGameSSDWindow::OnNuke()
{
gui->HandleNamedEvent( "nuke" );
//Destory All Asteroids
for( int i = 0 ; i < entities.Num(); i++ )
{
if( entities[i]->type == SSD_ENTITY_ASTEROID )
{
//The asteroid has been destroyed
SSDExplosion* explosion = SSDExplosion::GetNewExplosion( this, entities[i]->position, entities[i]->size * 2, 300, SSDExplosion::EXPLOSION_NORMAL, entities[i] );
entities.Append( explosion );
AddScore( entities[i], asteroidData[gameStats.currentLevel].asteroidPoints );
//Don't let the player hit it anymore because
entities[i]->noHit = true;
gameStats.levelStats.destroyedAsteroids++;
}
}
PlaySound( "arcade_explode" );
//Check to see if a nuke ends the level
/*if(gameStats.levelStats.destroyedAsteroids >= levelData[gameStats.currentLevel].needToWin) {
LevelComplete();
}*/
}
void idGameSSDWindow::OnRescueAll()
{
gui->HandleNamedEvent( "rescueAll" );
//Rescue All Astronauts
for( int i = 0 ; i < entities.Num(); i++ )
{
if( entities[i]->type == SSD_ENTITY_ASTRONAUT )
{
AstronautStruckPlayer( ( SSDAstronaut* )entities[i] );
}
}
}
void idGameSSDWindow::OnSuperBlaster()
{
StartSuperBlaster();
}
void idGameSSDWindow::RefreshGuiData()
{
gui->SetStateString( "nextLevel", va( "%i", gameStats.nextLevel + 1 ) );
gui->SetStateString( "currentLevel", va( "%i", gameStats.currentLevel + 1 ) );
float accuracy;
if( !gameStats.levelStats.shotCount )
{
accuracy = 0;
}
else
{
accuracy = ( ( float )gameStats.levelStats.hitCount / ( float )gameStats.levelStats.shotCount ) * 100.0f;
}
gui->SetStateString( "player_accuracy", va( "%d%%", ( int )accuracy ) );
float saveAccuracy;
int totalAst = gameStats.levelStats.savedAstronauts + gameStats.levelStats.killedAstronauts;
if( !totalAst )
{
saveAccuracy = 0;
}
else
{
saveAccuracy = ( ( float )gameStats.levelStats.savedAstronauts / ( float )totalAst ) * 100.0f;
}
gui->SetStateString( "save_accuracy", va( "%d%%", ( int )saveAccuracy ) );
if( gameStats.levelStats.targetEnt )
{
int dist = ( gameStats.levelStats.targetEnt->position.z / 100.0f );
dist *= 100;
gui->SetStateString( "target_info", va( "%i meters", dist ) );
}
else
{
gui->SetStateString( "target_info", "No Target" );
}
gui->SetStateString( "player_health", va( "%i", gameStats.health ) );
gui->SetStateString( "player_score", va( "%i", gameStats.score ) );
gui->SetStateString( "player_prebonusscore", va( "%i", gameStats.prebonusscore ) );
gui->SetStateString( "level_complete", va( "%i/%i", gameStats.levelStats.savedAstronauts, levelData[gameStats.currentLevel].needToWin ) );
if( superBlasterTimeout )
{
float timeRemaining = ( superBlasterTimeout - ssdTime ) / 1000.0f;
gui->SetStateString( "super_blaster_time", va( "%.2f", timeRemaining ) );
}
}
idVec2 idGameSSDWindow::GetCursorWorld()
{
idVec2 cursor;
//GetCursor(cursor);
cursor.x = gui->CursorX();
cursor.y = gui->CursorY();
cursor.x = cursor.x - 0.5f * V_WIDTH;
cursor.y = -( cursor.y - 0.5f * V_HEIGHT );
return cursor;
}
void idGameSSDWindow::SpawnAstronaut()
{
int currentTime = ssdTime;
if( currentTime < gameStats.levelStats.nextAstronautSpawnTime )
{
//Not time yet
return;
}
//Lets spawn it
idVec3 startPosition;
startPosition.x = random.RandomInt( V_WIDTH ) - ( V_WIDTH / 2.0f );
startPosition.y = random.RandomInt( V_HEIGHT ) - ( V_HEIGHT / 2.0f );
startPosition.z = ENTITY_START_DIST;
float speed = random.RandomInt( astronautData[gameStats.currentLevel].speedMax - astronautData[gameStats.currentLevel].speedMin ) + astronautData[gameStats.currentLevel].speedMin;
float rotate = ( random.RandomFloat() * ( astronautData[gameStats.currentLevel].rotateMax - astronautData[gameStats.currentLevel].rotateMin ) ) + astronautData[gameStats.currentLevel].rotateMin;
SSDAstronaut* astronaut = SSDAstronaut::GetNewAstronaut( this, startPosition, speed, rotate, astronautData[gameStats.currentLevel].health );
entities.Append( astronaut );
gameStats.levelStats.nextAstronautSpawnTime = currentTime + random.RandomInt( astronautData[gameStats.currentLevel].spawnMax - astronautData[gameStats.currentLevel].spawnMin ) + astronautData[gameStats.currentLevel].spawnMin;
}
void idGameSSDWindow::HitAstronaut( SSDAstronaut* astronaut, int key )
{
if( key == K_MOUSE1 )
{
astronaut->health -= weaponData[gameStats.currentWeapon].damage;
if( astronaut->health <= 0 )
{
gameStats.levelStats.killedAstronauts++;
//The astronaut has been destroyed
SSDExplosion* explosion = SSDExplosion::GetNewExplosion( this, astronaut->position, astronaut->size * 2, 300, SSDExplosion::EXPLOSION_NORMAL, astronaut );
entities.Append( explosion );
PlaySound( "arcade_explode" );
//Add the penalty for killing the astronaut
AddScore( astronaut, astronautData[gameStats.currentLevel].penalty );
//Don't let the player hit it anymore
astronaut->noHit = true;
}
else
{
//This was a damage hit so create a real small quick explosion
SSDExplosion* explosion = SSDExplosion::GetNewExplosion( this, astronaut->position, astronaut->size / 2.0f, 200, SSDExplosion::EXPLOSION_NORMAL, astronaut, false, false );
entities.Append( explosion );
}
}
}
void idGameSSDWindow::AstronautStruckPlayer( SSDAstronaut* astronaut )
{
gameStats.levelStats.savedAstronauts++;
astronaut->noPlayerDamage = true;
astronaut->noHit = true;
//We are saving an astronaut
SSDExplosion* explosion = SSDExplosion::GetNewExplosion( this, astronaut->position, astronaut->size * 2, 300, SSDExplosion::EXPLOSION_TELEPORT, astronaut );
entities.Append( explosion );
PlaySound( "arcade_capture" );
//Give the player points for saving the astronaut
AddScore( astronaut, astronautData[gameStats.currentLevel].points );
if( gameStats.levelStats.savedAstronauts >= levelData[gameStats.currentLevel].needToWin )
{
LevelComplete();
}
}
void idGameSSDWindow::SpawnPowerup()
{
int currentTime = ssdTime;
if( currentTime < gameStats.levelStats.nextPowerupSpawnTime )
{
//Not time yet
return;
}
float speed = random.RandomInt( powerupData[gameStats.currentLevel].speedMax - powerupData[gameStats.currentLevel].speedMin ) + powerupData[gameStats.currentLevel].speedMin;
float rotate = ( random.RandomFloat() * ( powerupData[gameStats.currentLevel].rotateMax - powerupData[gameStats.currentLevel].rotateMin ) ) + powerupData[gameStats.currentLevel].rotateMin;
SSDPowerup* powerup = SSDPowerup::GetNewPowerup( this, speed, rotate );
entities.Append( powerup );
gameStats.levelStats.nextPowerupSpawnTime = currentTime + random.RandomInt( powerupData[gameStats.currentLevel].spawnMax - powerupData[gameStats.currentLevel].spawnMin ) + powerupData[gameStats.currentLevel].spawnMin;
}
void idGameSSDWindow::StartSuperBlaster()
{
gui->HandleNamedEvent( "startSuperBlaster" );
gameStats.currentWeapon = 1;
superBlasterTimeout = ssdTime + 10000;
}
void idGameSSDWindow::StopSuperBlaster()
{
gui->HandleNamedEvent( "stopSuperBlaster" );
gameStats.currentWeapon = 0;
superBlasterTimeout = 0;
}
SSDEntity* idGameSSDWindow::GetSpecificEntity( int type, int id )
{
SSDEntity* ent = NULL;
switch( type )
{
case SSD_ENTITY_ASTEROID:
ent = SSDAsteroid::GetSpecificAsteroid( id );
break;
case SSD_ENTITY_ASTRONAUT:
ent = SSDAstronaut::GetSpecificAstronaut( id );
break;
case SSD_ENTITY_EXPLOSION:
ent = SSDExplosion::GetSpecificExplosion( id );
break;
case SSD_ENTITY_POINTS:
ent = SSDPoints::GetSpecificPoints( id );
break;
case SSD_ENTITY_PROJECTILE:
ent = SSDProjectile::GetSpecificProjectile( id );
break;
case SSD_ENTITY_POWERUP:
ent = SSDPowerup::GetSpecificPowerup( id );
break;
}
return ent;
}
#define MAX_SOUND_CHANNEL 8
void idGameSSDWindow::PlaySound( const char* sound )
{
common->SW()->PlayShaderDirectly( sound, currentSound );
currentSound++;
if( currentSound >= MAX_SOUND_CHANNEL )
{
currentSound = 0;
}
}