/* =========================================================================== 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 "../idlib/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; jposition.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; } }