einstein/game.cpp

570 lines
13 KiB
C++

#include "main.h"
#include "utils.h"
#include "widgets.h"
#include "puzzle.h"
#include "verthints.h"
#include "horhints.h"
#include "widgets.h"
#include "font.h"
#include "topscores.h"
#include "opensave.h"
#include "options.h"
#include "game.h"
#include "messages.h"
#include "sound.h"
#include "descr.h"
class GameBackground: public Widget
{
public:
virtual void draw();
};
void GameBackground::draw()
{
// draw background
drawWallpaper(L"rain.bmp");
// draw title
SDL_Surface *tile = loadImage(L"title.bmp");
screen.draw(8, 10, tile);
SDL_FreeSurface(tile);
Font titleFont(L"nova.ttf", 28);
titleFont.draw(screen.getSurface(), 20, 20, 255,255,0, true,
msg(L"einsteinPuzzle"));
screen.addRegionToUpdate(0, 0, screen.getWidth(), screen.getHeight());
}
class ToggleHintCommand: public Command
{
private:
VertHints *verHints;
HorHints *horHints;
public:
ToggleHintCommand(VertHints *v, HorHints *h) {
verHints = v;
horHints = h;
};
virtual void doAction() {
verHints->toggleExcluded();
horHints->toggleExcluded();
};
};
class Watch: public TimerHandler, public Widget
{
private:
Uint32 lastRun;
Uint32 elapsed;
bool stoped;
int lastUpdate;
Font *font;
public:
Watch();
Watch(std::istream &stream);
virtual ~Watch();
public:
virtual void onTimer();
void stop();
void start();
virtual void draw();
int getElapsed() { return elapsed; };
void save(std::ostream &stream);
void reset();
};
Watch::Watch()
{
lastRun = elapsed = lastUpdate = 0;
stop();
font = new Font(L"luximb.ttf", 16);
}
Watch::Watch(std::istream &stream)
{
elapsed = readInt(stream);
lastUpdate = 0;
stop();
font = new Font(L"luximb.ttf", 16);
}
Watch::~Watch()
{
delete font;
}
void Watch::onTimer()
{
if (stoped)
return;
Uint32 now = SDL_GetTicks();
elapsed += now - lastRun;
lastRun = now;
int seconds = elapsed / 1000;
if (seconds != lastUpdate)
draw();
}
void Watch::stop()
{
stoped = true;
}
void Watch::start()
{
stoped = false;
lastRun = SDL_GetTicks();
}
void Watch::draw()
{
int time = elapsed / 1000;
std::wstring s = secToStr(time);
int x = 700;
int y = 24;
int w, h;
font->getSize(s, w, h);
SDL_Rect rect = { x-2, y-2, w+4, h+4 };
SDL_FillRect(screen.getSurface(), &rect,
SDL_MapRGB(screen.getSurface()->format, 0, 0, 255));
font->draw(x, y, 255,255,255, true, s);
screen.addRegionToUpdate(x-2, y-2, w+4, h+4);
lastUpdate = time;
}
void Watch::save(std::ostream &stream)
{
writeInt(stream, elapsed);
}
void Watch::reset()
{
elapsed = lastUpdate = 0;
lastRun = SDL_GetTicks();
}
class PauseGameCommand: public Command
{
private:
Area *gameArea;
Watch *watch;
Widget *background;
public:
PauseGameCommand(Area *a, Watch *w, Widget *bg) {
gameArea = a;
watch = w;
background = bg;
};
virtual void doAction() {
watch->stop();
Area area;
area.add(background, false);
Font font(L"laudcn2.ttf", 16);
area.add(new Window(280, 275, 240, 50, L"greenpattern.bmp", 6));
area.add(new Label(&font, 280, 275, 240, 50, Label::ALIGN_CENTER,
Label::ALIGN_MIDDLE, 255,255,0, msg(L"paused")));
area.add(new AnyKeyAccel());
area.run();
sound->play(L"click.wav");
gameArea->updateMouse();
gameArea->draw();
watch->start();
};
};
class WinCommand: public Command
{
private:
Area *gameArea;
Watch *watch;
Game *game;
public:
WinCommand(Area *a, Watch *w, Game *g) {
gameArea = a;
watch = w;
game = g;
};
virtual void doAction() {
sound->play(L"applause.wav");
watch->stop();
Font font(L"laudcn2.ttf", 20);
showMessageWindow(gameArea, L"marble1.bmp",
500, 70, &font, 255,0,0, msg(L"won"));
gameArea->draw();
TopScores scores;
int score = watch->getElapsed() / 1000;
int pos = -1;
if (! game->isHinted()) {
if ((! scores.isFull()) || (score < scores.getMaxScore())) {
std::wstring name = enterNameDialog(gameArea);
pos = scores.add(name, score);
}
}
showScoresWindow(gameArea, &scores, pos);
gameArea->finishEventLoop();
};
};
class OkDlgCommand: public Command
{
private:
bool &res;
Area *area;
public:
OkDlgCommand(Area *a, bool &r): res(r) {
area = a;
};
virtual void doAction() {
res = true;
area->finishEventLoop();
};
};
class FailCommand: public Command
{
private:
Area *gameArea;
Game *game;
public:
FailCommand(Area *a, Game *g) { gameArea = a; game = g; };
virtual void doAction() {
sound->play(L"glasbk2.wav");
bool restart = false;
bool newGame = false;
Font font(L"laudcn2.ttf", 24);
Font btnFont(L"laudcn2.ttf", 14);
Area area;
area.add(gameArea);
area.add(new Window(220, 240, 360, 140, L"redpattern.bmp", 6));
area.add(new Label(&font, 250, 230, 300, 100, Label::ALIGN_CENTER,
Label::ALIGN_MIDDLE, 255,255,0, msg(L"loose")));
OkDlgCommand newGameCmd(&area, newGame);
area.add(new Button(250, 340, 90, 25, &btnFont, 255,255,0,
L"redpattern.bmp", msg(L"startNew"), &newGameCmd));
OkDlgCommand restartCmd(&area, restart);
area.add(new Button(350, 340, 90, 25, &btnFont, 255,255,0,
L"redpattern.bmp", msg(L"tryAgain"), &restartCmd));
ExitCommand exitCmd(area);
area.add(new Button(450, 340, 90, 25, &btnFont, 255,255,0,
L"redpattern.bmp", msg(L"exit"), &exitCmd));
area.run();
if (restart || newGame) {
if (newGame)
game->newGame();
else
game->restart();
gameArea->draw();
gameArea->updateMouse();
} else
gameArea->finishEventLoop();
};
};
class CheatAccel: public Widget
{
private:
Command *command;
std::wstring typed;
std::wstring cheat;
public:
CheatAccel(const std::wstring s, Command *cmd): cheat(s) {
command = cmd;
};
public:
virtual bool onKeyDown(SDLKey key, unsigned char ch) {
if ((key >= SDLK_a) && (key <= SDLK_z)) {
wchar_t s = L'a' + key - SDLK_a;
typed += s;
if (typed.length() == cheat.length()) {
if (command && (typed == cheat))
command->doAction();
} else {
int pos = typed.length() - 1;
if (typed[pos] == cheat[pos])
return false;
}
}
if (typed.length() > 0)
typed = L"";
return false;
}
};
class CheatCommand: public Command
{
private:
Area *gameArea;
public:
CheatCommand(Area *a) { gameArea = a; };
virtual void doAction() {
Font font(L"nova.ttf", 30);
showMessageWindow(gameArea, L"darkpattern.bmp",
500, 100, &font, 255,255,255,
msg(L"iddqd"));
gameArea->draw();
};
};
class SaveGameCommand: public Command
{
private:
Area *gameArea;
Watch *watch;
Widget *background;
Game *game;
public:
SaveGameCommand(Area *a, Watch *w, Widget *bg, Game *g) {
gameArea = a;
watch = w;
background = bg;
game = g;
};
virtual void doAction() {
watch->stop();
Area area;
area.add(background, false);
saveGame(&area, game);
gameArea->updateMouse();
gameArea->draw();
watch->start();
};
};
class GameOptionsCommand: public Command
{
private:
Area *gameArea;
public:
GameOptionsCommand(Area *a) {
gameArea = a;
};
virtual void doAction() {
showOptionsWindow(gameArea);
gameArea->updateMouse();
gameArea->draw();
};
};
class HelpCommand: public Command
{
private:
Area *gameArea;
Watch *watch;
Widget *background;
public:
HelpCommand(Area *a, Watch *w, Widget *b) {
gameArea = a;
watch = w;
background = b;
};
virtual void doAction() {
watch->stop();
Area area;
area.add(background, false);
area.draw();
showDescription(&area);
gameArea->updateMouse();
gameArea->draw();
watch->start();
};
};
Game::Game()
{
genPuzzle();
possibilities = new Possibilities();
openInitial(*possibilities, rules);
puzzle = new Puzzle(iconSet, solvedPuzzle, possibilities);
verHints = new VertHints(iconSet, rules);
horHints = new HorHints(iconSet, rules);
watch = new Watch();
}
Game::Game(std::istream &stream)
{
pleaseWait();
loadPuzzle(solvedPuzzle, stream);
loadRules(rules, stream);
memcpy(savedSolvedPuzzle, solvedPuzzle, sizeof(solvedPuzzle));
savedRules = rules;
possibilities = new Possibilities(stream);
puzzle = new Puzzle(iconSet, solvedPuzzle, possibilities);
verHints = new VertHints(iconSet, rules, stream);
horHints = new HorHints(iconSet, rules, stream);
watch = new Watch(stream);
hinted = true;
}
Game::~Game()
{
delete watch;
delete possibilities;
delete verHints;
delete horHints;
delete puzzle;
deleteRules();
}
void Game::save(std::ostream &stream)
{
savePuzzle(solvedPuzzle, stream);
saveRules(rules, stream);
possibilities->save(stream);
verHints->save(stream);
horHints->save(stream);
watch->save(stream);
}
void Game::deleteRules()
{
for (Rules::iterator i = rules.begin(); i != rules.end(); i++)
delete *i;
rules.clear();
}
void Game::pleaseWait()
{
drawWallpaper(L"rain.bmp");
Window window(230, 260, 340, 80, L"greenpattern.bmp", 6);
window.draw();
Font font(L"laudcn2.ttf", 16);
Label label(&font, 280, 275, 240, 50, Label::ALIGN_CENTER,
Label::ALIGN_MIDDLE, 255,255,0, msg(L"loading"));
label.draw();
screen.addRegionToUpdate(0, 0, screen.getWidth(), screen.getHeight());
screen.flush();
}
void Game::genPuzzle()
{
pleaseWait();
int horRules, verRules;
do {
if (rules.size() > 0)
deleteRules();
::genPuzzle(solvedPuzzle, rules);
getHintsQty(rules, verRules, horRules);
} while ((horRules > 24) || (verRules > 15));
memcpy(savedSolvedPuzzle, solvedPuzzle, sizeof(solvedPuzzle));
savedRules = rules;
hinted = false;
}
void Game::resetVisuals()
{
possibilities->reset();
openInitial(*possibilities, rules);
puzzle->reset();
verHints->reset(rules);
horHints->reset(rules);
watch->reset();
}
void Game::newGame()
{
genPuzzle();
resetVisuals();
}
void Game::restart()
{
memcpy(solvedPuzzle, savedSolvedPuzzle, sizeof(solvedPuzzle));
rules = savedRules;
resetVisuals();
hinted = true;
}
#define BUTTON(x, y, text, cmd) \
area.add(new Button(x, y, 94, 30, &btnFont, 255,255,0, \
L"btn.bmp", msg(text), false, cmd));
void Game::run()
{
Area area;
Font btnFont(L"laudcn2.ttf", 14);
area.setTimer(300, watch);
GameBackground *background = new GameBackground();
area.add(background);
CheatCommand cheatCmd(&area);
area.add(new CheatAccel(L"iddqd", &cheatCmd));
WinCommand winCmd(&area, watch, this);
FailCommand failCmd(&area, this);
puzzle->setCommands(&winCmd, &failCmd);
area.add(puzzle, false);
area.add(verHints, false);
area.add(horHints, false);
PauseGameCommand pauseGameCmd(&area, watch, background);
BUTTON(12, 400, L"pause", &pauseGameCmd)
ToggleHintCommand toggleHintsCmd(verHints, horHints);
BUTTON(119, 400, L"switch", &toggleHintsCmd)
SaveGameCommand saveCmd(&area, watch, background, this);
BUTTON(12, 440, L"save", &saveCmd)
GameOptionsCommand optionsCmd(&area);
BUTTON(119, 440, L"options", &optionsCmd)
ExitCommand exitGameCmd(area);
BUTTON(226, 400, L"exit", &exitGameCmd)
area.add(new KeyAccel(SDLK_ESCAPE, &exitGameCmd));
HelpCommand helpCmd(&area, watch, background);
BUTTON(226, 440, L"help", &helpCmd)
area.add(watch, false);
watch->start();
area.run();
}