/* =========================================================================== 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 . 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( ¤tCrosshair, sizeof( currentCrosshair ) ); savefile->Write( &crosshairWidth, sizeof( crosshairWidth ) ); savefile->Write( &crosshairHeight, sizeof( crosshairHeight ) ); } void SSDCrossHair::ReadFromSaveGame( idFile* savefile ) { InitCrosshairs(); savefile->Read( ¤tCrosshair, 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( ¤tTime, 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( ¤tTime, 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( ent ) ); } else if( ent->type == SSD_ENTITY_ASTRONAUT ) { AstronautStruckPlayer( static_cast( 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( gameStats.levelStats.targetEnt ), key ); } else if( gameStats.levelStats.targetEnt->type == SSD_ENTITY_ASTRONAUT ) { HitAstronaut( static_cast( 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(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; } }