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>
*/
// game.c
#include "rtts.h"
#include "rttspanels.h"
#include "ff_savegame.h"
/*
==============================================================================
HANGAR
==============================================================================
*/
/*
Stuff supporting the actual game, such as briefing screens,
character save / load, etc...
*/
typedef struct buyitem_s {
char *name;
int id;
sprite_t *sprite;
char *icon;
int cash_buy, cash_sell;
int (*Inventory)(player_t *player, int id, bool *pbBuy, bool *pbSell);
void (*Action)(player_t *player, int id, bool bBuy);
} buyitem_t;
game_t game;
int gs_buyitem_num;
/*
==============
Energy_Inventory
Energy cells (shield)
==============
*/
int Energy_Inventory(player_t *player, int id, bool *pbBuy, bool *pbSell)
{
if (player->health <= 25)
*pbSell = false;
else
*pbSell = true;
if (player->health >= 100)
*pbBuy = false;
else
*pbBuy = true;
return player->health;
}
/*
==============
Energy_Action
Buy or sell shield (money is taken care of by the caller)
==============
*/
void Energy_Action(player_t *player, int id, bool bBuy)
{
if (bBuy) {
player->health += 25;
if (player->health > 100)
player->health = 100;
} else {
player->health -= 25;
if (player->health < 1)
player->health = 1;
}
}
/*
==============
Weapon_Inventory
==============
*/
int Weapon_Inventory(player_t *player, int id, bool *pbBuy, bool *pbSell)
{
if (player->weapons[id]) {
*pbBuy = false;
*pbSell = true;
} else {
*pbBuy = true;
*pbSell = false;
}
return player->weapons[id];
}
/*
==============
Weapon_Action
==============
*/
void Weapon_Action(player_t *player, int id, bool bBuy)
{
if (bBuy) {
if (player->weapons[id] < 1)
player->weapons[id]++;
} else {
if (player->weapons[id] > 0)
player->weapons[id]--;
}
}
buyitem_t gs_buyitems[] = {
{ "ENERGY CELL", 0, 0, "item00", 10000, 5000, Energy_Inventory, Energy_Action },
{ "ROCKETS", WEAP_ROCKET, 0, "item01", 32000, 16000, Weapon_Inventory, Weapon_Action },
{ "DUMBFIRE", WEAP_DUMBFIRE, 0, "item02", 96000, 48000, Weapon_Inventory, Weapon_Action },
{ "ATG", WEAP_ATG, 0, "atg", 156000, 78000, Weapon_Inventory, Weapon_Action },
{ "SMALL LASER", WEAP_SMLASER, 0, "smlaser", 340000, 170000, Weapon_Inventory, Weapon_Action },
// must be last
{ 0, 0, 0, 0, 0, 0, 0 }
};
/*
==============================================================================
HANGAR
==============================================================================
*/
class Hangar : public GLabel {
public:
int m_iBuyItem;
GLabel *m_pLevel;
GLabel *m_pMoney;
GLabel *m_pBuyItem;
GLabel *m_pAmount;
GButton *m_pBuy;
GButton *m_pSell;
public:
Hangar();
~Hangar();
void Refresh();
void Launch(int id);
void SavePlayer(int id);
void ExitHangar(int id);
void LeftBtn(int id);
void RightBtn(int id);
void Buy(int id);
void Sell(int id);
virtual void Draw();
virtual bool Key(int code, char c, bool down);
};
/*
==============
MakeHangar
Create a hangar object. This is in a function so that I don't need
to put the Hangar class into a header file
==============
*/
GPanel *MakeHangar()
{
return new Hangar;
}
/*
==============
Hangar::Hangar
==============
*/
Hangar::Hangar()
: GLabel(0, "hangar", pic_normal, 0, 0, 0, 640, 480)
{
buyitem_t *bi;
char path[MAX_OSPATH];
GButton *pBtn;
SetCanFocus(true);
// Precache buyitem sprites
m_iBuyItem = 0;
for(bi = gs_buyitems, gs_buyitem_num = 0; bi->name; bi++, gs_buyitem_num++) {
snprintf(path, sizeof(path), "items/%s", bi->icon);
bi->sprite = SPR_Get(path);
}
// Create GUI elements
m_pLevel = new GLabel(this, 5, 160, 200, 16);
m_pLevel->SetColor(255, 255, 0);
m_pMoney = new GLabel(this, 435, 160, 200, 16, 0, halign_right);
m_pMoney->SetColor(255, 255, 0);
pBtn = new RttsButton(this, 50, 205, 200, 30, "LAUNCH", valign_center);
pBtn->Clicked.Connect(this, &Hangar::Launch);
pBtn = new RttsButton(this, 50, 235, 200, 30, "SAVE PLAYER", valign_center);
pBtn->Clicked.Connect(this, &Hangar::SavePlayer);
m_pBuyItem = new GLabel(this, 244, 235, 400, 16, 0, halign_center);
m_pBuyItem->SetColor(255, 255, 64);
pBtn = new RttsButton(this, 290, 225, 36, 36, "\x01", halign_center|valign_center);
pBtn->Clicked.Connect(this, &Hangar::LeftBtn);
pBtn = new RttsButton(this, 564, 225, 36, 36, "\x02", halign_center|valign_center);
pBtn->Clicked.Connect(this, &Hangar::RightBtn);
m_pAmount = new GLabel(this, 272, 334, 100, 16, 0, halign_center);
m_pAmount->SetColor(255, 255, 0);
m_pBuy = new RttsButton(this, 300, 410, 200, 40);
m_pBuy->Clicked.Connect(this, &Hangar::Buy);
m_pSell = new RttsButton(this, 390, 410, 200, 40, 0, halign_right);
m_pSell->Clicked.Connect(this, &Hangar::Sell);
pBtn = new RttsButton(this, 50, 425, 200, 30, "EXIT HANGAR", valign_center);
pBtn->Clicked.Connect(this, &Hangar::ExitHangar);
Refresh();
}
/*
==============
Hangar::~Hangar
Free precached sprites
==============
*/
Hangar::~Hangar()
{
buyitem_t *bi;
for(bi = gs_buyitems; bi->name; bi++)
SPR_Free(bi->sprite);
}
/*
==============
Hangar::Refresh
Update labels with the correct texts
==============
*/
void Hangar::Refresh()
{
char buf[32];
buyitem_t *bi;
bool bCanBuy, bCanSell;
int iAmount;
// Level and money
sprintf(buf, "Level: %04i", game.player.level+1);
m_pLevel->SetText(buf);
sprintf(buf, "%i\x80", game.player.money);
m_pMoney->SetText(buf);
// Buy item
bi = &gs_buyitems[m_iBuyItem];
iAmount = bi->Inventory(&game.player, bi->id, &bCanBuy, &bCanSell);
if (game.player.money < bi->cash_buy)
bCanBuy = false;
m_pBuyItem->SetText(bi->name);
sprintf(buf, "%i", iAmount);
m_pAmount->SetText(buf);
sprintf(buf, "BUY\n%i\x80", bi->cash_buy);
m_pBuy->SetText(buf);
m_pBuy->AdjustSize();
m_pBuy->SetOblivious(!bCanBuy);
sprintf(buf, "SELL\n%i\x80", bi->cash_sell);
m_pSell->SetText(buf);
m_pSell->AdjustSize();
m_pSell->SetOblivious(!bCanSell);
}
/*
==============
Hangar::Launch
Hangar::ExitHangar
Hangar::LeftBtn
Hangar::RightBtn
Hangar::Buy
Hangar::Sell
Button action handlers
==============
*/
void Hangar::Launch(int id)
{
EndModal(HANGAR_GAME);
}
void Hangar::SavePlayer(int id)
{
char buf[256];
G_SavePlayer(&game.player);
snprintf(buf, sizeof(buf), "Player %s - %s saved!", game.player.name,
game.player.callsign);
RttsMessageBox::Ok(this, buf);
}
void Hangar::ExitHangar(int id)
{
if (!RttsMessageBox::YesNo(this, "Do you really want to leave this game?"))
return;
G_EndGame();
EndModal(HANGAR_MAINMENU);
}
void Hangar::LeftBtn(int id)
{
m_iBuyItem--;
if (m_iBuyItem < 0)
m_iBuyItem = gs_buyitem_num-1;
Refresh();
}
void Hangar::RightBtn(int id)
{
m_iBuyItem++;
if (m_iBuyItem >= gs_buyitem_num)
m_iBuyItem = 0;
Refresh();
}
void Hangar::Buy(int id)
{
buyitem_t *bi;
bool bCanBuy, bCanSell;
bi = &gs_buyitems[m_iBuyItem];
bi->Inventory(&game.player, bi->id, &bCanBuy, &bCanSell);
if (!bCanBuy || game.player.money < bi->cash_buy)
return; // can't buy
game.player.money -= bi->cash_buy;
bi->Action(&game.player, bi->id, true);
Refresh();
}
void Hangar::Sell(int id)
{
buyitem_t *bi;
bool bCanBuy, bCanSell;
bi = &gs_buyitems[m_iBuyItem];
bi->Inventory(&game.player, bi->id, &bCanBuy, &bCanSell);
if (!bCanSell)
return; // can't buy
game.player.money += bi->cash_sell;
bi->Action(&game.player, bi->id, false);
Refresh();
}
/*
==============
Hangar::Draw
Overridden to draw the buyitem sprite
==============
*/
void Hangar::Draw()
{
buyitem_t *bi;
GLabel::Draw();
bi = &gs_buyitems[m_iBuyItem];
SPR_Draw(bi->sprite, 290, 260, -1, hal->framestart);
}
/*
==============
Hangar::Key
Escape breaks to the main menu
==============
*/
bool Hangar::Key(int code, char c, bool down)
{
if (!down)
return false;
switch(code) {
case KEY_LEFT:
LeftBtn(-1);
break;
case KEY_RIGHT:
RightBtn(-1);
break;
case KEY_ESCAPE:
EndModal(HANGAR_MAINMENU);
break;
#ifdef DEBUG
// Cheats
case KEY_F5:
game.player.money += 100000;
Refresh();
break;
case KEY_F6:
game.player.money -= 100000;
if (game.player.money < 0)
game.player.money = 0;
Refresh();
break;
case KEY_F7:
game.player.level++;
Refresh();
break;
case KEY_F8:
game.player.level--;
if (game.player.level < 0)
game.player.level = 0;
Refresh();
break;
#endif
default:
return GLabel::Key(code, c, down);
}
return true;
}
/*
==============================================================================
COMMON CODE
==============================================================================
*/
/*
==============
G_NewPlayer
==============
*/
void G_NewPlayer(player_t *player, const char *name, const char *callsign)
{
xstrcpy(player->name, sizeof(player->name), name ? name : "<error>");
xstrcpy(player->callsign, sizeof(player->callsign), callsign ? callsign : "<error>");
player->level = 0;
player->money = 10000;
player->health = 75;
memset(player->weapons, 0, sizeof(player->weapons));
}
/*
==============
G_NewGame
Starts a completely new game, with a new player and everything
==============
*/
void G_NewGame(const char *playername, const char *callsign)
{
if (game.running)
G_EndGame();
game.running = true;
G_NewPlayer(&game.player, playername, callsign);
}
/*
==============
G_SavePlayer
Saves the current game
==============
*/
void G_SavePlayer(const player_t *plr)
{
char path[MAX_OSPATH];
LFileWrite fw;
int i;
lassert(game.running);
fw.Integer(SAV_MAGIC);
fw.Integer(SAV_VERSION);
fw.Byte(255);
fw.CString(plr->name);
fw.CString(plr->callsign);
fw.Integer(plr->level);
fw.Integer(plr->money);
fw.Byte(plr->health);
fw.Byte(NUM_WEAP);
for(i = 0; i < NUM_WEAP; i++)
fw.Byte(plr->weapons[i]);
snprintf(path, sizeof(path), "%s.%s.sav", plr->name, plr->callsign);
fw.Write(path);
}
/*
==============
G_LoadPlayer
Load a game (or rather, player). Returns false if the savegame is
corrupt.
==============
*/
bool G_LoadPlayer(player_t *plr, const char *filename)
{
try {
LFileRead fr;
int i, num;
fr.Open(filename);
if (fr.Integer() != SAV_MAGIC)
return false;
if (fr.Integer() != SAV_VERSION)
return false;
if (fr.Byte() != 255)
return false;
xstrcpy(plr->name, sizeof(plr->name), fr.CString());
xstrcpy(plr->callsign, sizeof(plr->callsign), fr.CString());
plr->level = fr.Integer();
plr->money = fr.Integer();
plr->health = fr.Byte();
memset(plr->weapons, 0, sizeof(plr->weapons));
num = fr.Byte();
for(i = 0; i < num; i++)
plr->weapons[i] = fr.Byte();
return true;
}
#ifndef DEBUG
catch(LError &) {
#
else
catch(LError &err) {
L_Printf("Error reading savegame %s: %s\n", filename, err.Get());
#endif
return false;
}
}
/*
==============
G_EndGame
Ends the current game. Player data is not saved.
==============
*/
void G_EndGame()
{
game.running = false;
}
/*
==============
G_Init
==============
*/
void G_Init()
{
game.running = false;
}
/*
==============
G_Shutdown
==============
*/
void G_Shutdown()
{
if (game.running)
G_EndGame();
}