scummvm/engines/avalanche/avalanche.cpp
2014-01-22 18:00:44 +01:00

541 lines
14 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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 2
* of the License, or (at your option) any later version.
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
/*
* This code is based on the original source code of Lord Avalot d'Argent version 1.3.
* Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman.
*/
#include "avalanche/avalanche.h"
#include "common/random.h"
#include "common/savefile.h"
#include "graphics/thumbnail.h"
namespace Avalanche {
AvalancheEngine::AvalancheEngine(OSystem *syst, const AvalancheGameDescription *gd) : Engine(syst), _gameDescription(gd), _fxHidden(false), _interrogation(0) {
_system = syst;
_console = new AvalancheConsole(this);
_rnd = new Common::RandomSource("avalanche");
TimeDate time;
_system->getTimeAndDate(time);
_rnd->setSeed(time.tm_sec + time.tm_min + time.tm_hour);
_showDebugLines = false;
_clock = nullptr;
_graphics = nullptr;
_parser = nullptr;
_pingo = nullptr;
_dialogs = nullptr;
_background = nullptr;
_sequence = nullptr;
_timer = nullptr;
_animation = nullptr;
_menu = nullptr;
_closing = nullptr;
_sound = nullptr;
_nim = nullptr;
_platform = gd->desc.platform;
initVariables();
}
AvalancheEngine::~AvalancheEngine() {
delete _console;
delete _rnd;
delete _graphics;
delete _parser;
delete _clock;
delete _pingo;
delete _dialogs;
delete _background;
delete _sequence;
delete _timer;
delete _animation;
delete _menu;
delete _closing;
delete _sound;
delete _nim;
for (int i = 0; i < 31; i++) {
for (int j = 0; j < 2; j++) {
if (_also[i][j] != nullptr) {
delete _also[i][j];
_also[i][j] = nullptr;
}
}
}
}
void AvalancheEngine::initVariables() {
for (int i = 0; i < 31; i++) {
_also[i][0] = nullptr;
_also[i][1] = nullptr;
}
memset(_fxPal, 0, 16 * 16 * 3);
for (int i = 0; i < 15; i++) {
_peds[i]._direction = kDirNone;
_peds[i]._x = 0;
_peds[i]._y = 0;
_magics[i]._operation = kMagicNothing;
_magics[i]._data = 0;
}
for (int i = 0; i < 7; i++) {
_portals[i]._operation = kMagicNothing;
_portals[i]._data = 0;
}
for (int i = 0; i < 30; i++) {
_fields[i]._x1 = 0;
_fields[i]._y1 = 0;
_fields[i]._x2 = 0;
_fields[i]._y2 = 0;
}
_fieldNum = 0;
_cp = 0;
_ledStatus = 177;
_alive = false;
_subjectNum = 0;
_him = kPeoplePardon;
_her = kPeoplePardon;
_it = Parser::kPardon;
_roomCycles = 0;
_doingSpriteRun = false;
_isLoaded = false;
_soundFx = true;
_holdTheDawn = false;
_lineNum = 0;
for (int i = 0; i < 50; i++)
_lines[i]._color = kColorWhite;
_dropsOk = false;
_cheat = false;
_letMeOut = false;
_thinks = 2;
_thinkThing = true;
_seeScroll = false;
_currentMouse = 177;
_holdLeftMouse = false;
resetVariables();
}
Common::ErrorCode AvalancheEngine::initialize() {
_graphics = new GraphicManager(this);
_parser = new Parser(this);
_clock = new Clock(this);
_pingo = new Pingo(this);
_dialogs = new Dialogs(this);
_background = new Background(this);
_sequence = new Sequence(this);
_timer = new Timer(this);
_animation = new Animation(this);
_menu = new Menu(this);
_closing = new Closing(this);
_sound = new SoundHandler(this);
_nim = new Nim(this);
_graphics->init();
_dialogs->init();
init();
_parser->init();
return Common::kNoError;
}
GUI::Debugger *AvalancheEngine::getDebugger() {
return _console;
}
Common::Platform AvalancheEngine::getPlatform() const {
return _platform;
}
bool AvalancheEngine::hasFeature(EngineFeature f) const {
return (f == kSupportsSavingDuringRuntime) || (f == kSupportsLoadingDuringRuntime);
}
const char *AvalancheEngine::getCopyrightString() const {
return "Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman.";
}
void AvalancheEngine::synchronize(Common::Serializer &sz) {
_animation->synchronize(sz);
_parser->synchronize(sz);
_nim->synchronize(sz);
_sequence->synchronize(sz);
_background->synchronize(sz);
sz.syncAsByte(_carryNum);
for (int i = 0; i < kObjectNum; i++)
sz.syncAsByte(_objects[i]);
sz.syncAsSint16LE(_dnascore);
sz.syncAsSint32LE(_money);
sz.syncAsByte(_room);
if (sz.isSaving())
_saveNum++;
sz.syncAsByte(_saveNum);
sz.syncBytes(_roomCount, 100);
sz.syncAsByte(_wonNim);
sz.syncAsByte(_wineState);
sz.syncAsByte(_cwytalotGone);
sz.syncAsByte(_passwordNum);
sz.syncAsByte(_aylesIsAwake);
sz.syncAsByte(_drawbridgeOpen);
sz.syncAsByte(_avariciusTalk);
sz.syncAsByte(_rottenOnion);
sz.syncAsByte(_onionInVinegar);
sz.syncAsByte(_givenToSpludwick);
sz.syncAsByte(_brummieStairs);
sz.syncAsByte(_cardiffQuestionNum);
sz.syncAsByte(_passedCwytalotInHerts);
sz.syncAsByte(_avvyIsAwake);
sz.syncAsByte(_avvyInBed);
sz.syncAsByte(_userMovesAvvy);
sz.syncAsByte(_npcFacing);
sz.syncAsByte(_givenBadgeToIby);
sz.syncAsByte(_friarWillTieYouUp);
sz.syncAsByte(_tiedUp);
sz.syncAsByte(_boxContent);
sz.syncAsByte(_talkedToCrapulus);
sz.syncAsByte(_jacquesState);
sz.syncAsByte(_bellsAreRinging);
sz.syncAsByte(_standingOnDais);
sz.syncAsByte(_takenPen);
sz.syncAsByte(_arrowInTheDoor);
if (sz.isSaving()) {
uint16 like2drinkSize = _favoriteDrink.size();
sz.syncAsUint16LE(like2drinkSize);
for (uint16 i = 0; i < like2drinkSize; i++) {
char actChr = _favoriteDrink[i];
sz.syncAsByte(actChr);
}
uint16 favoriteSongSize = _favoriteSong.size();
sz.syncAsUint16LE(favoriteSongSize);
for (uint16 i = 0; i < favoriteSongSize; i++) {
char actChr = _favoriteSong[i];
sz.syncAsByte(actChr);
}
uint16 worst_place_on_earthSize = _worstPlaceOnEarth.size();
sz.syncAsUint16LE(worst_place_on_earthSize);
for (uint16 i = 0; i < worst_place_on_earthSize; i++) {
char actChr = _worstPlaceOnEarth[i];
sz.syncAsByte(actChr);
}
uint16 spare_eveningSize = _spareEvening.size();
sz.syncAsUint16LE(spare_eveningSize);
for (uint16 i = 0; i < spare_eveningSize; i++) {
char actChr = _spareEvening[i];
sz.syncAsByte(actChr);
}
} else {
if (!_favoriteDrink.empty())
_favoriteDrink.clear();
uint16 like2drinkSize = 0;
char actChr = ' ';
sz.syncAsUint16LE(like2drinkSize);
for (uint16 i = 0; i < like2drinkSize; i++) {
sz.syncAsByte(actChr);
_favoriteDrink += actChr;
}
if (!_favoriteSong.empty())
_favoriteSong.clear();
uint16 favourite_songSize = 0;
sz.syncAsUint16LE(favourite_songSize);
for (uint16 i = 0; i < favourite_songSize; i++) {
sz.syncAsByte(actChr);
_favoriteSong += actChr;
}
if (!_worstPlaceOnEarth.empty())
_worstPlaceOnEarth.clear();
uint16 worst_place_on_earthSize = 0;
sz.syncAsUint16LE(worst_place_on_earthSize);
for (uint16 i = 0; i < worst_place_on_earthSize; i++) {
sz.syncAsByte(actChr);
_worstPlaceOnEarth += actChr;
}
if (!_spareEvening.empty())
_spareEvening.clear();
uint16 spare_eveningSize = 0;
sz.syncAsUint16LE(spare_eveningSize);
for (uint16 i = 0; i < spare_eveningSize; i++) {
sz.syncAsByte(actChr);
_spareEvening += actChr;
}
}
sz.syncAsSint32LE(_totalTime);
sz.syncAsByte(_jumpStatus);
sz.syncAsByte(_mushroomGrowing);
sz.syncAsByte(_spludwickAtHome);
sz.syncAsByte(_lastRoom);
sz.syncAsByte(_lastRoomNotMap);
sz.syncAsByte(_crapulusWillTell);
sz.syncAsByte(_enterCatacombsFromLustiesRoom);
sz.syncAsByte(_teetotal);
sz.syncAsByte(_malagauche);
sz.syncAsByte(_drinking);
sz.syncAsByte(_enteredLustiesRoomAsMonk);
sz.syncAsByte(_catacombX);
sz.syncAsByte(_catacombY);
sz.syncAsByte(_avvysInTheCupboard);
sz.syncAsByte(_geidaFollows);
sz.syncAsByte(_givenPotionToGeida);
sz.syncAsByte(_lustieIsAsleep);
sz.syncAsByte(_beenTiedUp);
sz.syncAsByte(_sittingInPub);
sz.syncAsByte(_spurgeTalkCount);
sz.syncAsByte(_metAvaroid);
sz.syncAsByte(_takenMushroom);
sz.syncAsByte(_givenPenToAyles);
sz.syncAsByte(_askedDogfoodAboutNim);
for (int i = 0; i < 7; i++) {
sz.syncAsSint32LE(_timer->_times[i]._timeLeft);
sz.syncAsByte(_timer->_times[i]._action);
sz.syncAsByte(_timer->_times[i]._reason);
}
}
bool AvalancheEngine::canSaveGameStateCurrently() { // TODO: Refine these!!!
return (!_seeScroll && _alive);
}
Common::Error AvalancheEngine::saveGameState(int slot, const Common::String &desc) {
return (saveGame(slot, desc) ? Common::kNoError : Common::kWritingFailed);
}
bool AvalancheEngine::saveGame(const int16 slot, const Common::String &desc) {
Common::String fileName = getSaveFileName(slot);
Common::OutSaveFile *f = g_system->getSavefileManager()->openForSaving(fileName);
if (!f) {
warning("Can't create file '%s', game not saved.", fileName.c_str());
return false;
}
f->writeUint32LE(MKTAG('A', 'V', 'A', 'L'));
// Write version. We can't restore from obsolete versions.
f->writeByte(kSavegameVersion);
f->writeUint32LE(desc.size());
f->write(desc.c_str(), desc.size());
Graphics::saveThumbnail(*f);
TimeDate t;
_system->getTimeAndDate(t);
f->writeSint16LE(t.tm_mday);
f->writeSint16LE(t.tm_mon);
f->writeSint16LE(t.tm_year);
_totalTime += getTimeInSeconds() - _startTime;
Common::Serializer sz(NULL, f);
synchronize(sz);
f->finalize();
delete f;
return true;
}
Common::String AvalancheEngine::getSaveFileName(const int slot) {
return Common::String::format("%s.%03d", _targetName.c_str(), slot);
}
bool AvalancheEngine::canLoadGameStateCurrently() { // TODO: Refine these!!!
return (!_seeScroll);
}
Common::Error AvalancheEngine::loadGameState(int slot) {
return (loadGame(slot) ? Common::kNoError : Common::kReadingFailed);
}
bool AvalancheEngine::loadGame(const int16 slot) {
Common::String fileName = getSaveFileName(slot);
Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(fileName);
if (!f)
return false;
uint32 signature = f->readUint32LE();
if (signature != MKTAG('A', 'V', 'A', 'L'))
return false;
// Check version. We can't restore from obsolete versions.
byte saveVersion = f->readByte();
if (saveVersion > kSavegameVersion) {
warning("Savegame of incompatible version!");
delete f;
return false;
}
// Read the description.
uint32 descSize = f->readUint32LE();
Common::String description;
for (uint32 i = 0; i < descSize; i++) {
char actChar = f->readByte();
description += actChar;
}
description.toUppercase();
Graphics::skipThumbnail(*f);
// Read the time the game was saved.
TimeDate t;
t.tm_mday = f->readSint16LE();
t.tm_mon = f->readSint16LE();
t.tm_year = f->readSint16LE();
resetAllVariables();
Common::Serializer sz(f, NULL);
synchronize(sz);
delete f;
_isLoaded = true;
_seeScroll = true; // This prevents display of the new sprites before the new picture is loaded.
if (_holdTheDawn) {
_holdTheDawn = false;
fadeIn();
}
_background->release();
minorRedraw();
_menu->setup();
setRoom(kPeopleAvalot, _room);
_alive = true;
refreshObjectList();
_animation->updateSpeed();
drawDirection();
_animation->animLink();
_background->update();
Common::String tmpStr = Common::String::format("%cLoaded: %c%s.ASG%c%c%c%s%c%csaved on %s.",
kControlItalic, kControlRoman, description.c_str(), kControlCenter, kControlNewLine,
kControlNewLine, _roomnName.c_str(), kControlNewLine, kControlNewLine,
expandDate(t.tm_mday, t.tm_mon, t.tm_year).c_str());
_dialogs->displayText(tmpStr);
AnimationType *avvy = _animation->_sprites[0];
if (avvy->_quick && avvy->_visible)
_animation->setMoveSpeed(0, _animation->getDirection()); // We push Avvy in the right direction is he was moving.
return true;
}
Common::String AvalancheEngine::expandDate(int d, int m, int y) {
static const char months[12][10] = {
"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
};
Common::String month = Common::String(months[m]);
Common::String day = intToStr(d);
if (((1 <= d) && (d <= 9)) || ((21 <= d) && (d <= 31)))
switch (d % 10) {
case 1:
day += "st";
break;
case 2:
day += "nd";
break;
case 3:
day += "rd";
break;
default:
day += "th";
}
return day + ' ' + month + ' ' + intToStr(y + 1900);
}
uint32 AvalancheEngine::getTimeInSeconds() {
TimeDate time;
_system->getTimeAndDate(time);
return time.tm_hour * 3600 + time.tm_min * 60 + time.tm_sec;
}
void AvalancheEngine::updateEvents() {
Common::Event event;
while (_eventMan->pollEvent(event)) {
switch (event.type) {
case Common::EVENT_LBUTTONDOWN:
_holdLeftMouse = true; // Used in checkclick() and Menu::menu_link().
break;
case Common::EVENT_LBUTTONUP:
_holdLeftMouse = false; // Same as above.
break;
case Common::EVENT_KEYDOWN:
if ((event.kbd.keycode == Common::KEYCODE_d) && (event.kbd.flags & Common::KBD_CTRL)) {
// Attach to the debugger
_console->attach();
_console->onFrame();
} else
handleKeyDown(event);
break;
default:
break;
}
}
}
bool AvalancheEngine::getEvent(Common::Event &event) {
return _eventMan->pollEvent(event);
}
Common::Point AvalancheEngine::getMousePos() {
return _eventMan->getMousePos();
}
Common::Error AvalancheEngine::run() {
Common::ErrorCode err = initialize();
if (err != Common::kNoError)
return err;
do {
runAvalot();
} while (!_letMeOut && !shouldQuit());
return Common::kNoError;
}
} // End of namespace Avalanche