If you have comments or questions concerning this source file, discuss them in the forum.
/*
Copyright (c) 2002 Nicolai Haehnle
See the license.txt for details. If that file was not included in the
source distributions, please email <prefect@rtts.org>
*/
// ge_engine.cpp
#include "game.h"
/*
EEngine is the master object which puts levels and air units
etc... together.
It is used by both the actual game and the level editor
Implementation is spread out into ge_air.cpp as well.
*/
EEngine *ge = 0; // only one game engine at any time
/*
==============================================================================
EEngine CORE IMPLEMENTATION
==============================================================================
*/
/*
==============
EEngine::EEngine
Initialize a null state
==============
*/
EEngine::EEngine()
: GPanel(0, 0, 0, 640, 480)
{
lassert(!ge); // only one engine can exist at any time
ge = this;
SetCanFocus(true);
SetVisible(false);
m_iState = ge_none;
memset(&m_player, 0, sizeof(m_player));
m_pLevel = 0;
m_bPaused = false;
m_bEndGame = false;
m_iLevel = -1;
m_iLevelAdjust = 0;
// the player will be set before Begin() in PokePlayer()
Air_Init();
}
/*
==============
EEngine::~EEngine
Cleanup anything that needs cleaning.
==============
*/
EEngine::~EEngine()
{
Air_Shutdown();
ge = 0;
}
/*
==============
EEngine::MouseCursor
Disable the mouse cursor while playing
==============
*/
const char *EEngine::MouseCursor()
{
return 0;
}
/*
==============
EEngine::SetLevelAdjust
==============
*/
void EEngine::SetLevelAdjust(int lvl, int adjust)
{
m_iLevel = lvl;
m_iLevelAdjust = adjust;
m_flHealthFactor = 1 + adjust * 0.2;
m_iFrameTime = LOGICTIME - (adjust*3/2);
if (m_iFrameTime < 1)
m_iFrameTime = 1;
}
/*
==============
EEngine::SetLevel
Set the level that's to be used. Watch out: ownership is not transferred
==============
*/
void EEngine::SetLevel(ELevel *level, bool edit)
{
lassert(m_iState == ge_none);
m_pLevel = level;
}
/*
==============
EEngine::PokePlayer
EEngine::PeekPlayer
Exchange player state from/to engine
==============
*/
void EEngine::PokePlayer(const player_t *player, const LConfig *cfg)
{
lassert(m_iState == ge_none);
lassert(!m_player.ship);
memcpy(&m_player.data, player, sizeof(player_t));
m_player.ctrl.speed = 400.0;
m_player.ctrl.up = FixKey(cfg->GetInt("up"));
m_player.ctrl.down = FixKey(cfg->GetInt("down"));
m_player.ctrl.left = FixKey(cfg->GetInt("left"));
m_player.ctrl.right = FixKey(cfg->GetInt("right"));
m_player.ctrl.fire = FixKey(cfg->GetInt("fire"));
m_player.ctrl.weaponswitch = FixKey(cfg->GetInt("weaponswitch"));
new AirPlayer(&m_player, 320, 480-32);
}
void EEngine::PeekPlayer(player_t *player)
{
memcpy(player, &m_player.data, sizeof(player_t));
}
/*
==============
EEngine::Begin
Setup everything and set the engine state to running
==============
*/
void EEngine::Begin()
{
lassert(m_iState == ge_none);
lassert(m_player.ship);
gametime = 0;
curframe = 0;
m_iBossDestroyed = 0;
m_iPrewinTime = TOTALPREWINTIME;
m_iFadeoutTime = 0;
m_pLevel->BeginPlay(m_iLevelAdjust);
Hud_Load();
m_iState = ge_running;
}
/*
==============
EEngine::End
Cleanup after the game ends
==============
*/
void EEngine::End()
{
Hud_Unload();
m_pLevel->EndPlay();
Air_Shutdown();
}
/*
==============
EEngine::Logic
Run the game
==============
*/
void EEngine::Logic()
{
if (m_bPaused && m_iState == ge_running)
return;
if (m_iState != ge_abort) {
gametime += hal->frametime;
while(curframe < gametime) {
m_pLevel->Logic();
Air_Logic();
curframe += m_iFrameTime;
}
}
if (m_pLevel->m_iNumBosses && m_iBossDestroyed >= m_pLevel->m_iNumBosses)
m_iPrewinTime -= hal->frametime;
if (m_iState != ge_running) {
m_iFadeoutTime += hal->frametime;
if (m_iFadeoutTime >= TOTALFADETIME) {
if (m_player.ship && m_player.ship->m_flHealth <= 0)
m_iState = ge_loose;
if (m_iState == ge_loose)
EndModal(ENGINE_LOOSE);
else if (m_iState == ge_win)
EndModal(ENGINE_WIN);
else
EndModal(ENGINE_ABORT);
m_bEndGame = true;
return;
}
}
}
/*
==============
EEngine::Draw
Refresh the screen
==============
*/
void EEngine::Draw()
{
char buf[32];
oldfract = (float)(curframe - gametime) / m_iFrameTime;
curfract = 1.0 - oldfract;
m_pLevel->Draw();
Air_Draw();
if (!m_bPaused || m_iState != ge_running)
{
Hud_Draw();
if (m_iLevel >= 0)
sprintf(buf, "Level: %04i", m_iLevel+1);
else
strcpy(buf, "EDITOR");
hud_pFont->DrawString(0, 0, buf, 0, 0, 0, 255, 0, 0, 255);
if (m_iState != ge_running) {
float time;
time = (float)m_iFadeoutTime / TOTALFADETIME;
if (time < 0)
time = 0;
else if (time > 1.0)
time = 1.0;
hal->FillRect(0, 0, 640, 480, 0, 0, 0, (int)(255 * time));
}
}
else
{
float fr;
int r, g, b;
hal->FillRect(0, 0, 640, 480, 0, 0, 0, 96);
fr = 0.4 * (hal->framestart % 2000) / 2000.0;
if (fr > 0.2)
fr = 0.4 - fr;
fr = 1.0 - fr;
r = (int)(fr * 255);
g = (int)(fr * 0);
b = (int)(fr * 0);
hud_pFont->DrawString(320, 240, "PAUSED", halign_center|valign_center, 0, 0,
r, g, b, 255);
}
}
/*
==============
EEngine::Key
Process key events. Usually passed on to the player ship
==============
*/
bool EEngine::Key(int code, char c, bool down)
{
if (down) {
switch(code) {
case KEY_ESCAPE:
if (m_iState == ge_running)
m_iState = ge_abort;
return true;
case KEY_p:
SetPaused(!m_bPaused);
return true;
}
}
if (m_player.ship) {
if (m_player.ship->Input(code, c, down))
return true;
}
return false;
}
/*
==============================================================================
EEngine HUD IMPLEMENTATION
==============================================================================
*/
typedef struct hudicon_s {
char *name;
sprite_t *sprite;
} hudicon_t;
hudicon_t ge_hudicons[] = {
{ "item01sm", 0 },
{ "item02sm", 0 },
{ "atgsm", 0 },
{ "smlaser", 0 },
// must be last!
{ 0, 0 }
};
#define HUD_WEAPONS 0
/*
==============
EEngine::Hud_Draw
Display the HUD
==============
*/
void EEngine::Hud_Draw()
{
AirPlayer *pShip;
char money[32];
int max, num, i;
int x, y;
float r, g, b;
float stepr, stepg, stepb;
pShip = m_player.ship;
if (!pShip)
return;
max = 480 / hud_pHealth->size[1];
num = (int)(pShip->m_flHealth / 100.0 * max);
if (num < 0)
num = 0;
else if (num > max)
num = max;
x = 640 - hud_pHealth->size[0];
y = 480;
r = 96.0;
stepr = (255.0 - 96.0) / max;
g = 0;
stepg = (140.0 - 0.0) / max;
b = 0;
stepb = 0;
for(i = 0; i < num; i++) {
y -= hud_pHealth->size[1];
hud_pHealth->Draw(x, y, (int)r, (int)g, (int)b, 255);
r += stepr;
g += stepg;
b += stepb;
}
hal->FillRect(x, 0, hud_pHealth->size[0], y, 0, 0, 0, 255);
sprintf(money, "%i\x80", m_player.data.money);
hud_pFont->DrawString(320, 8, money, halign_center, 0, 0);
if (pShip->m_iCurWeapon >= 0)
SPR_Draw(ge_hudicons[HUD_WEAPONS + pShip->m_iCurWeapon].sprite, 600, 30, -1, 0);
}
/*
==============
EEngine::Hud_Unload
Free HUD graphics
==============
*/
void EEngine::Hud_Unload()
{
hudicon_t *hi;
hud_pHealth->Release();
hud_pFont->Release();
for(hi = ge_hudicons; hi->name; hi++)
SPR_Free(hi->sprite);
}
/*
==============
EEngine::Hud_Load
Load HUD graphics
==============
*/
void EEngine::Hud_Load()
{
char path[MAX_OSPATH];
hudicon_t *hi; // (greetings)
hud_pHealth = HPicture::Get("items/health", pic_normal|pic_nosubpixel, 0);
hud_pFont = HFont::Get("font");
for(hi = ge_hudicons; hi->name; hi++) {
snprintf(path, sizeof(path), "items/%s", hi->name);
hi->sprite = SPR_Get(path);
}
}