570 lines
13 KiB
C++
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();
|
||
|
}
|
||
|
|