dhewm3/neo/ui/GameSSDWindow.cpp
dhewg 736ec20d4d Untangle the epic precompiled.h mess
Don't include the lazy precompiled.h everywhere, only what's
required for the compilation unit.
platform.h needs to be included instead to provide all essential
defines and types.
All includes use the relative path to the neo or the game
specific root.
Move all idlib related includes from idlib/Lib.h to precompiled.h.
precompiled.h still exists for the MFC stuff in tools/.
Add some missing header guards.
2011-12-19 23:21:47 +01:00

2296 lines
60 KiB
C++

/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 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 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 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 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 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.
===========================================================================
*/
#include "sys/platform.h"
#include "framework/Session_local.h"
#include "sound/sound.h"
#include "ui/DeviceContext.h"
#include "ui/Window.h"
#include "ui/UserInterfaceLocal.h"
#include "ui/GameSSDWindow.h"
#define Z_NEAR 100.0f
#define Z_FAR 4000.0f
#define ENTITY_START_DIST 3000
#define V_WIDTH 640.0f
#define V_HEIGHT 480.0f
/*
*****************************************************************************
* SSDCrossHair
****************************************************************************
*/
#define CROSSHAIR_STANDARD_MATERIAL "game/SSD/crosshair_standard"
#define CROSSHAIR_SUPER_MATERIAL "game/SSD/crosshair_super"
SSDCrossHair::SSDCrossHair() {
}
SSDCrossHair::~SSDCrossHair() {
}
void SSDCrossHair::WriteToSaveGame( idFile *savefile ) {
savefile->Write(&currentCrosshair, sizeof(currentCrosshair));
savefile->Write(&crosshairWidth, sizeof(crosshairWidth));
savefile->Write(&crosshairHeight, sizeof(crosshairHeight));
}
void SSDCrossHair::ReadFromSaveGame( idFile *savefile ) {
InitCrosshairs();
savefile->Read(&currentCrosshair, sizeof(currentCrosshair));
savefile->Read(&crosshairWidth, sizeof(crosshairWidth));
savefile->Read(&crosshairHeight, sizeof(crosshairHeight));
}
void SSDCrossHair::InitCrosshairs() {
crosshairMaterial[CROSSHAIR_STANDARD] = declManager->FindMaterial( CROSSHAIR_STANDARD_MATERIAL );
crosshairMaterial[CROSSHAIR_SUPER] = declManager->FindMaterial( CROSSHAIR_SUPER_MATERIAL );
crosshairWidth = 64;
crosshairHeight = 64;
currentCrosshair = CROSSHAIR_STANDARD;
}
void SSDCrossHair::Draw(idDeviceContext *dc, const idVec2& cursor) {
float x,y;
x = cursor.x-(crosshairWidth/2);
y = cursor.y-(crosshairHeight/2);
dc->DrawMaterial(x, y, crosshairWidth, crosshairHeight, crosshairMaterial[currentCrosshair], colorWhite, 1.0f, 1.0f);
}
/*
*****************************************************************************
* SSDEntity
****************************************************************************
*/
SSDEntity::SSDEntity() {
EntityInit();
}
SSDEntity::~SSDEntity() {
}
void SSDEntity::WriteToSaveGame( idFile *savefile ) {
savefile->Write(&type, sizeof(type));
game->WriteSaveGameString(materialName, savefile);
savefile->Write(&position, sizeof(position));
savefile->Write(&size, sizeof(size));
savefile->Write(&radius, sizeof(radius));
savefile->Write(&hitRadius, sizeof(hitRadius));
savefile->Write(&rotation, sizeof(rotation));
savefile->Write(&matColor, sizeof(matColor));
game->WriteSaveGameString(text, savefile);
savefile->Write(&textScale, sizeof(textScale));
savefile->Write(&foreColor, sizeof(foreColor));
savefile->Write(&currentTime, sizeof(currentTime));
savefile->Write(&lastUpdate, sizeof(lastUpdate));
savefile->Write(&elapsed, sizeof(elapsed));
savefile->Write(&destroyed, sizeof(destroyed));
savefile->Write(&noHit, sizeof(noHit));
savefile->Write(&noPlayerDamage, sizeof(noPlayerDamage));
savefile->Write(&inUse, sizeof(inUse));
}
void SSDEntity::ReadFromSaveGame( idFile *savefile, idGameSSDWindow* _game ) {
savefile->Read(&type, sizeof(type));
game->ReadSaveGameString(materialName, savefile);
SetMaterial(materialName);
savefile->Read(&position, sizeof(position));
savefile->Read(&size, sizeof(size));
savefile->Read(&radius, sizeof(radius));
savefile->Read(&hitRadius, sizeof(hitRadius));
savefile->Read(&rotation, sizeof(rotation));
savefile->Read(&matColor, sizeof(matColor));
game->ReadSaveGameString(text, savefile);
savefile->Read(&textScale, sizeof(textScale));
savefile->Read(&foreColor, sizeof(foreColor));
game = _game;
savefile->Read(&currentTime, sizeof(currentTime));
savefile->Read(&lastUpdate, sizeof(lastUpdate));
savefile->Read(&elapsed, sizeof(elapsed));
savefile->Read(&destroyed, sizeof(destroyed));
savefile->Read(&noHit, sizeof(noHit));
savefile->Read(&noPlayerDamage, sizeof(noPlayerDamage));
savefile->Read(&inUse, sizeof(inUse));
}
void SSDEntity::EntityInit() {
inUse = false;
type = SSD_ENTITY_BASE;
materialName = "";
material = NULL;
position.Zero();
size.Zero();
radius = 0.0f;
hitRadius = 0.0f;
rotation = 0.0f;
currentTime = 0;
lastUpdate = 0;
destroyed = false;
noHit = false;
noPlayerDamage = false;
matColor.Set(1, 1, 1, 1);
text = "";
textScale = 1.0f;
foreColor.Set(1, 1, 1, 1);
}
void SSDEntity::SetGame(idGameSSDWindow* _game) {
game = _game;
}
void SSDEntity::SetMaterial(const char* name) {
materialName = name;
material = declManager->FindMaterial( name );
material->SetSort( SS_GUI );
}
void SSDEntity::SetPosition(const idVec3& _position) {
position = _position;
}
void SSDEntity::SetSize(const idVec2& _size) {
size = _size;
}
void SSDEntity::SetRadius(float _radius, float _hitFactor) {
radius = _radius;
hitRadius = _radius*_hitFactor;
}
void SSDEntity::SetRotation(float _rotation) {
rotation = _rotation;
}
void SSDEntity::Update() {
currentTime = game->ssdTime;
//Is this the first update
if(lastUpdate == 0) {
lastUpdate = currentTime;
return;
}
elapsed = currentTime - lastUpdate;
EntityUpdate();
lastUpdate = currentTime;
}
bool SSDEntity::HitTest(const idVec2& pt) {
if(noHit) {
return false;
}
idVec3 screenPos = WorldToScreen(position);
//Scale the radius based on the distance from the player
float scale = 1.0f -((screenPos.z-Z_NEAR)/(Z_FAR-Z_NEAR));
float scaledRad = scale*hitRadius;
//So we can compare against the square of the length between two points
float scaleRadSqr = scaledRad*scaledRad;
idVec2 diff = screenPos.ToVec2()-pt;
float dist = idMath::Fabs(diff.LengthSqr());
if(dist < scaleRadSqr) {
return true;
}
return false;
}
void SSDEntity::Draw(idDeviceContext *dc) {
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);
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 += game->GetDC()->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(idDeviceContext *d, idUserInterfaceLocal *g) : idWindow(d, g) {
dc = d;
gui = g;
CommonInit();
}
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(dc);
}
//The last thing to draw is the crosshair
idVec2 cursor;
//GetCursor(cursor);
cursor.x = gui->CursorX();
cursor.y = gui->CursorY();
crosshair.Draw(dc, cursor);
}
}
bool idGameSSDWindow::ParseInternalVar(const char *_name, idParser *src) {
if (idStr::Icmp(_name, "beginLevel") == 0) {
beginLevel = src->ParseBool();
return true;
}
if (idStr::Icmp(_name, "resetGame") == 0) {
resetGame = src->ParseBool();
return true;
}
if (idStr::Icmp(_name, "continueGame") == 0) {
continueGame = src->ParseBool();
return true;
}
if (idStr::Icmp(_name, "refreshGuiData") == 0) {
refreshGuiData = src->ParseBool();
return true;
}
if(idStr::Icmp(_name, "levelcount") == 0) {
levelCount = src->ParseInt();
for(int i = 0; i < levelCount; i++) {
SSDLevelData_t newLevel;
memset(&newLevel, 0, sizeof(SSDLevelData_t));
levelData.Append(newLevel);
SSDAsteroidData_t newAsteroid;
memset(&newAsteroid, 0, sizeof(SSDAsteroidData_t));
asteroidData.Append(newAsteroid);
SSDAstronautData_t newAstronaut;
memset(&newAstronaut, 0, sizeof(SSDAstronautData_t));
astronautData.Append(newAstronaut);
SSDPowerupData_t newPowerup;
memset(&newPowerup, 0, sizeof(SSDPowerupData_t));
powerupData.Append(newPowerup);
}
return true;
}
if(idStr::Icmp(_name, "weaponCount") == 0) {
weaponCount = src->ParseInt();
for(int i = 0; i < weaponCount; i++) {
SSDWeaponData_t newWeapon;
memset(&newWeapon, 0, sizeof(SSDWeaponData_t));
weaponData.Append(newWeapon);
}
return true;
}
if(idStr::FindText(_name, "leveldata", false) >= 0) {
idStr tempName = _name;
int level = atoi(tempName.Right(2))-1;
idStr levelData;
ParseString(src, levelData);
ParseLevelData(level, levelData);
return true;
}
if(idStr::FindText(_name, "asteroiddata", false) >= 0) {
idStr tempName = _name;
int level = atoi(tempName.Right(2))-1;
idStr asteroidData;
ParseString(src, asteroidData);
ParseAsteroidData(level, asteroidData);
return true;
}
if(idStr::FindText(_name, "weapondata", false) >= 0) {
idStr tempName = _name;
int weapon = atoi(tempName.Right(2))-1;
idStr weaponData;
ParseString(src, weaponData);
ParseWeaponData(weapon, weaponData);
return true;
}
if(idStr::FindText(_name, "astronautdata", false) >= 0) {
idStr tempName = _name;
int level = atoi(tempName.Right(2))-1;
idStr astronautData;
ParseString(src, astronautData);
ParseAstronautData(level, astronautData);
return true;
}
if(idStr::FindText(_name, "powerupdata", false) >= 0) {
idStr tempName = _name;
int level = atoi(tempName.Right(2))-1;
idStr powerupData;
ParseString(src, powerupData);
ParsePowerupData(level, powerupData);
return true;
}
return idWindow::ParseInternalVar(_name, src);
}
void idGameSSDWindow::ParseLevelData(int level, const idStr& levelDataString) {
idParser parser;
idToken token;
parser.LoadMemory(levelDataString.c_str(), levelDataString.Length(), "LevelData");
levelData[level].spawnBuffer = parser.ParseFloat();
levelData[level].needToWin = parser.ParseInt(); //Required Destroyed
}
void idGameSSDWindow::ParseAsteroidData(int level, const idStr& asteroidDataString) {
idParser parser;
idToken token;
parser.LoadMemory(asteroidDataString.c_str(), asteroidDataString.Length(), "AsteroidData");
asteroidData[level].speedMin = parser.ParseFloat(); //Speed Min
asteroidData[level].speedMax = parser.ParseFloat(); //Speed Max
asteroidData[level].sizeMin = parser.ParseFloat(); //Size Min
asteroidData[level].sizeMax = parser.ParseFloat(); //Size Max
asteroidData[level].rotateMin = parser.ParseFloat(); //Rotate Min (rotations per second)
asteroidData[level].rotateMax = parser.ParseFloat(); //Rotate Max (rotations per second)
asteroidData[level].spawnMin = parser.ParseInt(); //Spawn Min
asteroidData[level].spawnMax = parser.ParseInt(); //Spawn Max
asteroidData[level].asteroidHealth = parser.ParseInt(); //Health of the asteroid
asteroidData[level].asteroidDamage = parser.ParseInt(); //Asteroid Damage
asteroidData[level].asteroidPoints = parser.ParseInt(); //Points awarded for destruction
}
void idGameSSDWindow::ParsePowerupData(int level, const idStr& powerupDataString) {
idParser parser;
idToken token;
parser.LoadMemory(powerupDataString.c_str(), powerupDataString.Length(), "PowerupData");
powerupData[level].speedMin = parser.ParseFloat(); //Speed Min
powerupData[level].speedMax = parser.ParseFloat(); //Speed Max
powerupData[level].rotateMin = parser.ParseFloat(); //Rotate Min (rotations per second)
powerupData[level].rotateMax = parser.ParseFloat(); //Rotate Max (rotations per second)
powerupData[level].spawnMin = parser.ParseInt(); //Spawn Min
powerupData[level].spawnMax = parser.ParseInt(); //Spawn Max
}
void idGameSSDWindow::ParseWeaponData(int weapon, const idStr& weaponDataString) {
idParser parser;
idToken token;
parser.LoadMemory(weaponDataString.c_str(), weaponDataString.Length(), "WeaponData");
weaponData[weapon].speed = parser.ParseFloat();
weaponData[weapon].damage = parser.ParseFloat();
weaponData[weapon].size = parser.ParseFloat();
}
void idGameSSDWindow::ParseAstronautData(int level, const idStr& astronautDataString) {
idParser parser;
idToken token;
parser.LoadMemory(astronautDataString.c_str(), astronautDataString.Length(), "AstronautData");
astronautData[level].speedMin = parser.ParseFloat(); //Speed Min
astronautData[level].speedMax = parser.ParseFloat(); //Speed Max
astronautData[level].rotateMin = parser.ParseFloat(); //Rotate Min (rotations per second)
astronautData[level].rotateMax = parser.ParseFloat(); //Rotate Max (rotations per second)
astronautData[level].spawnMin = parser.ParseInt(); //Spawn Min
astronautData[level].spawnMax = parser.ParseInt(); //Spawn Max
astronautData[level].health = parser.ParseInt(); //Health of the asteroid
astronautData[level].points = parser.ParseInt(); //Asteroid Damage
astronautData[level].penalty = parser.ParseInt(); //Points awarded for destruction
}
void idGameSSDWindow::CommonInit() {
crosshair.InitCrosshairs();
beginLevel = false;
resetGame = false;
continueGame = false;
refreshGuiData = false;
ssdTime = 0;
levelCount = 0;
weaponCount = 0;
screenBounds = idBounds(idVec3(-320,-240,0), idVec3(320,240,0));
superBlasterTimeout = 0;
currentSound = 0;
//Precahce all assets that are loaded dynamically
declManager->FindMaterial(ASTEROID_MATERIAL);
declManager->FindMaterial(ASTRONAUT_MATERIAL);
for(int i = 0; i < EXPLOSION_MATERIAL_COUNT; i++) {
declManager->FindMaterial(explosionMaterials[i]);
}
declManager->FindMaterial(PROJECTILE_MATERIAL);
for(int i = 0; i < POWERUP_MATERIAL_COUNT; i++) {
declManager->FindMaterial(powerupMaterials[i][0]);
declManager->FindMaterial(powerupMaterials[i][1]);
}
// Precache sounds
declManager->FindSound( "arcade_blaster" );
declManager->FindSound( "arcade_capture " );
declManager->FindSound( "arcade_explode" );
ResetGameStats();
}
void idGameSSDWindow::ResetGameStats() {
ResetEntities();
//Reset the gamestats structure
memset(&gameStats, 0, sizeof(gameStats));
gameStats.health = 100;
}
void idGameSSDWindow::ResetLevelStats() {
ResetEntities();
//Reset the level statistics structure
memset(&gameStats.levelStats, 0, sizeof(gameStats.levelStats));
}
void idGameSSDWindow::ResetEntities() {
//Destroy all of the entities
for(int i = 0; i < entities.Num(); i++) {
entities[i]->DestroyEntity();
}
entities.Clear();
}
void idGameSSDWindow::StartGame() {
gameStats.gameRunning = true;
}
void idGameSSDWindow::StopGame() {
gameStats.gameRunning = false;
}
void idGameSSDWindow::GameOver() {
StopGame();
gui->HandleNamedEvent("gameOver");
}
void idGameSSDWindow::BeginLevel(int level) {
ResetLevelStats();
gameStats.currentLevel = level;
StartGame();
}
/**
* Continue game resets the players health
*/
void idGameSSDWindow::ContinueGame() {
gameStats.health = 100;
StartGame();
}
void idGameSSDWindow::LevelComplete() {
gameStats.prebonusscore = gameStats.score;
// Add the bonuses
int accuracy;
if( !gameStats.levelStats.shotCount ) {
accuracy = 0;
} else {
accuracy = (int)( ( (float)gameStats.levelStats.hitCount / (float)gameStats.levelStats.shotCount ) * 100.0f );
}
int accuracyPoints = Max( 0, accuracy - 50 ) * 20;
gui->SetStateString("player_accuracy_score", va("%i", accuracyPoints));
gameStats.score += accuracyPoints;
int saveAccuracy;
int totalAst = gameStats.levelStats.savedAstronauts + gameStats.levelStats.killedAstronauts;
if( !totalAst ) {
saveAccuracy = 0;
} else {
saveAccuracy = (int)( ( (float)gameStats.levelStats.savedAstronauts / (float)totalAst ) * 100.0f );
}
accuracyPoints = Max( 0, saveAccuracy - 50 ) * 20;
gui->SetStateString("save_accuracy_score", va("%i", accuracyPoints));
gameStats.score += accuracyPoints;
StopSuperBlaster();
gameStats.nextLevel++;
if(gameStats.nextLevel >= levelCount) {
//Have they beaten the game
GameComplete();
} else {
//Make sure we don't go above the levelcount
//min(gameStats.nextLevel, levelCount-1);
StopGame();
gui->HandleNamedEvent("levelComplete");
}
}
void idGameSSDWindow::GameComplete() {
StopGame();
gui->HandleNamedEvent("gameComplete");
}
void idGameSSDWindow::UpdateGame() {
//Check to see if and functions where called by the gui
if(beginLevel == true) {
beginLevel = false;
BeginLevel(gameStats.nextLevel);
}
if(resetGame == true) {
resetGame = false;
ResetGameStats();
}
if(continueGame == true) {
continueGame = false;
ContinueGame();
}
if(refreshGuiData == true) {
refreshGuiData = false;
RefreshGuiData();
}
if(gameStats.gameRunning) {
//We assume an upate every 16 milliseconds
ssdTime += 16;
if(superBlasterTimeout && ssdTime > superBlasterTimeout) {
StopSuperBlaster();
}
//Find if we are targeting and enemy
idVec2 cursor;
//GetCursor(cursor);
cursor.x = gui->CursorX();
cursor.y = gui->CursorY();
gameStats.levelStats.targetEnt = EntityHitTest(cursor);
//Update from back to front
for(int i = entities.Num()-1; i >= 0; i--) {
entities[i]->Update();
}
CheckForHits();
//Delete entities that need to be deleted
for(int i = entities.Num()-1; i >= 0; i--) {
if(entities[i]->destroyed) {
SSDEntity* ent = entities[i];
ent->DestroyEntity();
entities.RemoveIndex(i);
}
}
//Check if we can spawn an asteroid
SpawnAsteroid();
//Check if we should spawn an astronaut
SpawnAstronaut();
//Check if we should spawn an asteroid
SpawnPowerup();
}
}
void idGameSSDWindow::CheckForHits() {
//See if the entity has gotten close enough
for(int i = 0; i < entities.Num(); i++) {
SSDEntity* ent = entities[i];
if(ent->position.z <= Z_NEAR) {
if(!ent->noPlayerDamage) {
//Is the object still in the screen
idVec3 entPos = ent->position;
entPos.z = 0;
idBounds entBounds(entPos);
entBounds.ExpandSelf(ent->hitRadius);
if(screenBounds.IntersectsBounds(entBounds)) {
ent->OnStrikePlayer();
//The entity hit the player figure out what is was and act appropriately
if(ent->type == SSD_ENTITY_ASTEROID) {
AsteroidStruckPlayer(static_cast<SSDAsteroid*>(ent));
} else if(ent->type == SSD_ENTITY_ASTRONAUT) {
AstronautStruckPlayer(static_cast<SSDAstronaut*>(ent));
}
} else {
//Tag for removal later in the frame
ent->destroyed = true;
}
}
}
}
}
void idGameSSDWindow::ZOrderEntities() {
//Z-Order the entities
//Using a simple sorting method
for (int i = entities.Num()-1; i >= 0; i--) {
bool flipped = false;
for (int j = 0; j<i ; j++) {
if (entities[j]->position.z > entities[j+1]->position.z) {
SSDEntity* ent = entities[j];
entities[j] = entities[j+1];
entities[j+1] = ent;
flipped = true;
}
}
if (!flipped) {
//Jump out because it is sorted
break;
}
}
}
void idGameSSDWindow::SpawnAsteroid() {
int currentTime = ssdTime;
if(currentTime < gameStats.levelStats.nextAsteroidSpawnTime) {
//Not time yet
return;
}
//Lets spawn it
idVec3 startPosition;
float spawnBuffer = levelData[gameStats.currentLevel].spawnBuffer*2.0f;
startPosition.x = random.RandomInt(V_WIDTH+spawnBuffer)-((V_WIDTH/2.0f)+spawnBuffer);
startPosition.y = random.RandomInt(V_HEIGHT+spawnBuffer)-((V_HEIGHT/2.0f)+spawnBuffer);
startPosition.z = ENTITY_START_DIST;
float speed = random.RandomInt(asteroidData[gameStats.currentLevel].speedMax - asteroidData[gameStats.currentLevel].speedMin) + asteroidData[gameStats.currentLevel].speedMin;
float size = random.RandomInt(asteroidData[gameStats.currentLevel].sizeMax - asteroidData[gameStats.currentLevel].sizeMin) + asteroidData[gameStats.currentLevel].sizeMin;
float rotate = (random.RandomFloat() * (asteroidData[gameStats.currentLevel].rotateMax - asteroidData[gameStats.currentLevel].rotateMin)) + asteroidData[gameStats.currentLevel].rotateMin;
SSDAsteroid* asteroid = SSDAsteroid::GetNewAsteroid(this, startPosition, idVec2(size, size), speed, rotate, asteroidData[gameStats.currentLevel].asteroidHealth);
entities.Append(asteroid);
gameStats.levelStats.nextAsteroidSpawnTime = currentTime + random.RandomInt(asteroidData[gameStats.currentLevel].spawnMax - asteroidData[gameStats.currentLevel].spawnMin) + asteroidData[gameStats.currentLevel].spawnMin;
}
void idGameSSDWindow::FireWeapon(int key) {
idVec2 cursorWorld = GetCursorWorld();
idVec2 cursor;
//GetCursor(cursor);
cursor.x = gui->CursorX();
cursor.y = gui->CursorY();
if(key == K_MOUSE1) {
gameStats.levelStats.shotCount++;
if(gameStats.levelStats.targetEnt) {
//Aim the projectile from the bottom of the screen directly at the ent
//SSDProjectile* newProj = new SSDProjectile(this, idVec3(320,0,0), gameStats.levelStats.targetEnt->position, weaponData[gameStats.currentWeapon].speed, weaponData[gameStats.currentWeapon].size);
SSDProjectile* newProj = SSDProjectile::GetNewProjectile(this, idVec3(0,-180,0), gameStats.levelStats.targetEnt->position, weaponData[gameStats.currentWeapon].speed, weaponData[gameStats.currentWeapon].size);
entities.Append(newProj);
//newProj = SSDProjectile::GetNewProjectile(this, idVec3(-320,-0,0), gameStats.levelStats.targetEnt->position, weaponData[gameStats.currentWeapon].speed, weaponData[gameStats.currentWeapon].size);
//entities.Append(newProj);
//We hit something
gameStats.levelStats.hitCount++;
gameStats.levelStats.targetEnt->OnHit(key);
if(gameStats.levelStats.targetEnt->type == SSD_ENTITY_ASTEROID) {
HitAsteroid(static_cast<SSDAsteroid*>(gameStats.levelStats.targetEnt), key);
} else if(gameStats.levelStats.targetEnt->type == SSD_ENTITY_ASTRONAUT) {
HitAstronaut(static_cast<SSDAstronaut*>(gameStats.levelStats.targetEnt), key);
} else if(gameStats.levelStats.targetEnt->type == SSD_ENTITY_ASTRONAUT) {
}
} else {
////Aim the projectile at the cursor position all the way to the far clipping
//SSDProjectile* newProj = SSDProjectile::GetNewProjectile(this, idVec3(0,-180,0), idVec3(cursorWorld.x, cursorWorld.y, (Z_FAR-Z_NEAR)/2.0f), weaponData[gameStats.currentWeapon].speed, weaponData[gameStats.currentWeapon].size);
//Aim the projectile so it crosses the cursor 1/4 of screen
idVec3 vec = idVec3(cursorWorld.x, cursorWorld.y, (Z_FAR-Z_NEAR)/8.0f);
vec *= 8;
SSDProjectile* newProj = SSDProjectile::GetNewProjectile(this, idVec3(0,-180,0), vec, weaponData[gameStats.currentWeapon].speed, weaponData[gameStats.currentWeapon].size);
entities.Append(newProj);
}
//Play the blaster sound
PlaySound("arcade_blaster");
} /*else if (key == K_MOUSE2) {
if(gameStats.levelStats.targetEnt) {
if(gameStats.levelStats.targetEnt->type == SSD_ENTITY_ASTRONAUT) {
HitAstronaut(static_cast<SSDAstronaut*>(gameStats.levelStats.targetEnt), key);
}
}
}*/
}
SSDEntity* idGameSSDWindow::EntityHitTest(const idVec2& pt) {
for(int i = 0; i < entities.Num(); i++) {
//Since we ZOrder the entities every frame we can stop at the first entity we hit.
//ToDo: Make sure this assumption is true
if(entities[i]->HitTest(pt)) {
return entities[i];
}
}
return NULL;
}
void idGameSSDWindow::HitAsteroid(SSDAsteroid* asteroid, int key) {
asteroid->health -= weaponData[gameStats.currentWeapon].damage;
if(asteroid->health <= 0) {
//The asteroid has been destroyed
SSDExplosion* explosion = SSDExplosion::GetNewExplosion(this, asteroid->position, asteroid->size*2, 300, SSDExplosion::EXPLOSION_NORMAL, asteroid);
entities.Append(explosion);
PlaySound("arcade_explode");
AddScore(asteroid, asteroidData[gameStats.currentLevel].asteroidPoints);
//Don't let the player hit it anymore because
asteroid->noHit = true;
gameStats.levelStats.destroyedAsteroids++;
//if(gameStats.levelStats.destroyedAsteroids >= levelData[gameStats.currentLevel].needToWin) {
// LevelComplete();
//}
} else {
//This was a damage hit so create a real small quick explosion
SSDExplosion* explosion = SSDExplosion::GetNewExplosion(this, asteroid->position, asteroid->size/2.0f, 200, SSDExplosion::EXPLOSION_NORMAL, asteroid, false, false);
entities.Append(explosion);
}
}
void idGameSSDWindow::AsteroidStruckPlayer(SSDAsteroid* asteroid) {
asteroid->noPlayerDamage = true;
asteroid->noHit = true;
AddDamage(asteroidData[gameStats.currentLevel].asteroidDamage);
SSDExplosion* explosion = SSDExplosion::GetNewExplosion(this, asteroid->position, asteroid->size*2, 300, SSDExplosion::EXPLOSION_NORMAL, asteroid);
entities.Append(explosion);
PlaySound("arcade_explode");
}
void idGameSSDWindow::AddScore(SSDEntity* ent, int points) {
SSDPoints* pointsEnt;
if(points > 0) {
pointsEnt = SSDPoints::GetNewPoints(this, ent, points, 1000, 50, idVec4(0,1,0,1));
} else {
pointsEnt = SSDPoints::GetNewPoints(this, ent, points, 1000, 50, idVec4(1,0,0,1));
}
entities.Append(pointsEnt);
gameStats.score += points;
gui->SetStateString( "player_score", va("%i", gameStats.score ) );
}
void idGameSSDWindow::AddDamage(int damage) {
gameStats.health -= damage;
gui->SetStateString( "player_health", va("%i", gameStats.health ) );
gui->HandleNamedEvent( "playerDamage" );
if(gameStats.health <= 0) {
//The player is dead
GameOver();
}
}
void idGameSSDWindow::AddHealth(int health) {
gameStats.health += health;
gameStats.health = Min( 100, gameStats.health );
}
void idGameSSDWindow::OnNuke() {
gui->HandleNamedEvent("nuke");
//Destory All Asteroids
for(int i = 0 ; i < entities.Num(); i++) {
if(entities[i]->type == SSD_ENTITY_ASTEROID) {
//The asteroid has been destroyed
SSDExplosion* explosion = SSDExplosion::GetNewExplosion(this, entities[i]->position, entities[i]->size*2, 300, SSDExplosion::EXPLOSION_NORMAL, entities[i]);
entities.Append(explosion);
AddScore(entities[i], asteroidData[gameStats.currentLevel].asteroidPoints);
//Don't let the player hit it anymore because
entities[i]->noHit = true;
gameStats.levelStats.destroyedAsteroids++;
}
}
PlaySound("arcade_explode");
//Check to see if a nuke ends the level
/*if(gameStats.levelStats.destroyedAsteroids >= levelData[gameStats.currentLevel].needToWin) {
LevelComplete();
}*/
}
void idGameSSDWindow::OnRescueAll() {
gui->HandleNamedEvent("rescueAll");
//Rescue All Astronauts
for(int i = 0 ; i < entities.Num(); i++) {
if(entities[i]->type == SSD_ENTITY_ASTRONAUT) {
AstronautStruckPlayer((SSDAstronaut*)entities[i]);
}
}
}
void idGameSSDWindow::OnSuperBlaster() {
StartSuperBlaster();
}
void idGameSSDWindow::RefreshGuiData() {
gui->SetStateString("nextLevel", va("%i", gameStats.nextLevel+1));
gui->SetStateString("currentLevel", va("%i", gameStats.currentLevel+1));
float accuracy;
if(!gameStats.levelStats.shotCount) {
accuracy = 0;
} else {
accuracy = ((float)gameStats.levelStats.hitCount/(float)gameStats.levelStats.shotCount)*100.0f;
}
gui->SetStateString( "player_accuracy", va("%d%%", (int)accuracy));
float saveAccuracy;
int totalAst = gameStats.levelStats.savedAstronauts + gameStats.levelStats.killedAstronauts;
if(!totalAst) {
saveAccuracy = 0;
} else {
saveAccuracy = ((float)gameStats.levelStats.savedAstronauts/(float)totalAst)*100.0f;
}
gui->SetStateString( "save_accuracy", va("%d%%", (int)saveAccuracy));
if(gameStats.levelStats.targetEnt) {
int dist = (gameStats.levelStats.targetEnt->position.z/100.0f);
dist *= 100;
gui->SetStateString("target_info", va("%i meters", dist));
} else {
gui->SetStateString("target_info", "No Target");
}
gui->SetStateString( "player_health", va("%i", gameStats.health ) );
gui->SetStateString( "player_score", va("%i", gameStats.score ) );
gui->SetStateString( "player_prebonusscore", va("%i", gameStats.prebonusscore ) );
gui->SetStateString( "level_complete", va("%i/%i", gameStats.levelStats.savedAstronauts, levelData[gameStats.currentLevel].needToWin ));
if(superBlasterTimeout) {
float timeRemaining = (superBlasterTimeout - ssdTime)/1000.0f;
gui->SetStateString("super_blaster_time", va("%.2f", timeRemaining));
}
}
idVec2 idGameSSDWindow::GetCursorWorld() {
idVec2 cursor;
//GetCursor(cursor);
cursor.x = gui->CursorX();
cursor.y = gui->CursorY();
cursor.x = cursor.x - 0.5f * V_WIDTH;
cursor.y = -(cursor.y - 0.5f * V_HEIGHT);
return cursor;
}
void idGameSSDWindow::SpawnAstronaut() {
int currentTime = ssdTime;
if(currentTime < gameStats.levelStats.nextAstronautSpawnTime) {
//Not time yet
return;
}
//Lets spawn it
idVec3 startPosition;
startPosition.x = random.RandomInt(V_WIDTH)-(V_WIDTH/2.0f);
startPosition.y = random.RandomInt(V_HEIGHT)-(V_HEIGHT/2.0f);
startPosition.z = ENTITY_START_DIST;
float speed = random.RandomInt(astronautData[gameStats.currentLevel].speedMax - astronautData[gameStats.currentLevel].speedMin) + astronautData[gameStats.currentLevel].speedMin;
float rotate = (random.RandomFloat() * (astronautData[gameStats.currentLevel].rotateMax - astronautData[gameStats.currentLevel].rotateMin)) + astronautData[gameStats.currentLevel].rotateMin;
SSDAstronaut* astronaut = SSDAstronaut::GetNewAstronaut(this, startPosition, speed, rotate, astronautData[gameStats.currentLevel].health);
entities.Append(astronaut);
gameStats.levelStats.nextAstronautSpawnTime = currentTime + random.RandomInt(astronautData[gameStats.currentLevel].spawnMax - astronautData[gameStats.currentLevel].spawnMin) + astronautData[gameStats.currentLevel].spawnMin;
}
void idGameSSDWindow::HitAstronaut(SSDAstronaut* astronaut, int key) {
if(key == K_MOUSE1) {
astronaut->health -= weaponData[gameStats.currentWeapon].damage;
if(astronaut->health <= 0) {
gameStats.levelStats.killedAstronauts++;
//The astronaut has been destroyed
SSDExplosion* explosion = SSDExplosion::GetNewExplosion(this, astronaut->position, astronaut->size*2, 300, SSDExplosion::EXPLOSION_NORMAL, astronaut);
entities.Append(explosion);
PlaySound("arcade_explode");
//Add the penalty for killing the astronaut
AddScore(astronaut, astronautData[gameStats.currentLevel].penalty);
//Don't let the player hit it anymore
astronaut->noHit = true;
} else {
//This was a damage hit so create a real small quick explosion
SSDExplosion* explosion = SSDExplosion::GetNewExplosion(this, astronaut->position, astronaut->size/2.0f, 200, SSDExplosion::EXPLOSION_NORMAL, astronaut, false, false);
entities.Append(explosion);
}
}
}
void idGameSSDWindow::AstronautStruckPlayer(SSDAstronaut* astronaut) {
gameStats.levelStats.savedAstronauts++;
astronaut->noPlayerDamage = true;
astronaut->noHit = true;
//We are saving an astronaut
SSDExplosion* explosion = SSDExplosion::GetNewExplosion(this, astronaut->position, astronaut->size*2, 300, SSDExplosion::EXPLOSION_TELEPORT, astronaut);
entities.Append(explosion);
PlaySound("arcade_capture");
//Give the player points for saving the astronaut
AddScore(astronaut, astronautData[gameStats.currentLevel].points);
if(gameStats.levelStats.savedAstronauts >= levelData[gameStats.currentLevel].needToWin) {
LevelComplete();
}
}
void idGameSSDWindow::SpawnPowerup() {
int currentTime = ssdTime;
if(currentTime < gameStats.levelStats.nextPowerupSpawnTime) {
//Not time yet
return;
}
float speed = random.RandomInt(powerupData[gameStats.currentLevel].speedMax - powerupData[gameStats.currentLevel].speedMin) + powerupData[gameStats.currentLevel].speedMin;
float rotate = (random.RandomFloat() * (powerupData[gameStats.currentLevel].rotateMax - powerupData[gameStats.currentLevel].rotateMin)) + powerupData[gameStats.currentLevel].rotateMin;
SSDPowerup* powerup = SSDPowerup::GetNewPowerup(this, speed, rotate);
entities.Append(powerup);
gameStats.levelStats.nextPowerupSpawnTime = currentTime + random.RandomInt(powerupData[gameStats.currentLevel].spawnMax - powerupData[gameStats.currentLevel].spawnMin) + powerupData[gameStats.currentLevel].spawnMin;
}
void idGameSSDWindow::StartSuperBlaster() {
gui->HandleNamedEvent("startSuperBlaster");
gameStats.currentWeapon = 1;
superBlasterTimeout = ssdTime + 10000;
}
void idGameSSDWindow::StopSuperBlaster() {
gui->HandleNamedEvent("stopSuperBlaster");
gameStats.currentWeapon = 0;
superBlasterTimeout = 0;
}
SSDEntity* idGameSSDWindow::GetSpecificEntity(int type, int id) {
SSDEntity* ent = NULL;
switch(type) {
case SSD_ENTITY_ASTEROID:
ent = SSDAsteroid::GetSpecificAsteroid(id);
break;
case SSD_ENTITY_ASTRONAUT:
ent = SSDAstronaut::GetSpecificAstronaut(id);
break;
case SSD_ENTITY_EXPLOSION:
ent = SSDExplosion::GetSpecificExplosion(id);
break;
case SSD_ENTITY_POINTS:
ent = SSDPoints::GetSpecificPoints(id);
break;
case SSD_ENTITY_PROJECTILE:
ent = SSDProjectile::GetSpecificProjectile(id);
break;
case SSD_ENTITY_POWERUP:
ent = SSDPowerup::GetSpecificPowerup(id);
break;
}
return ent;
}
#define MAX_SOUND_CHANNEL 8
void idGameSSDWindow::PlaySound(const char* sound) {
session->sw->PlayShaderDirectly(sound, currentSound);
currentSound++;
if(currentSound >= MAX_SOUND_CHANNEL) {
currentSound = 0;
}
}