scummvm/engines/cge/cge_main.cpp
Paul Gilbert 9f175c4053 ENGINES: Cleanup of savegame filenames generation
This removes filename methods when it matched the Engine method.
Secondly, ensuring there was an overriden getSaveStateName method
for engines that didn't do the standard target.00x save filenames
2020-02-16 15:44:28 -08:00

1559 lines
37 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 original Soltys source code
* Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
*/
#include "common/scummsys.h"
#include "common/endian.h"
#include "common/memstream.h"
#include "common/savefile.h"
#include "common/serializer.h"
#include "common/str.h"
#include "graphics/palette.h"
#include "graphics/scaler.h"
#include "graphics/thumbnail.h"
#include "cge/vga13h.h"
#include "cge/cge.h"
#include "cge/cge_main.h"
#include "cge/general.h"
#include "cge/sound.h"
#include "cge/snail.h"
#include "cge/text.h"
#include "cge/game.h"
#include "cge/events.h"
#include "cge/talk.h"
#include "cge/vmenu.h"
#include "cge/walk.h"
#include "cge/sound.h"
namespace CGE {
const char *savegameStr = "SCUMMVM_CGE";
//--------------------------------------------------------------------------
const Dac g_stdPal[] = {// R G B
{ 0, 60, 0}, // 198
{ 0, 104, 0}, // 199
{ 20, 172, 0}, // 200
{ 82, 82, 0}, // 201
{ 0, 132, 82}, // 202
{ 132, 173, 82}, // 203
{ 82, 0, 0}, // 204
{ 206, 0, 24}, // 205
{ 255, 33, 33}, // 206
{ 123, 41, 0}, // 207
{ 0, 41, 0}, // 208
{ 0, 0, 82}, // 209
{ 132, 0, 0}, // 210
{ 255, 0, 0}, // 211
{ 255, 66, 66}, // 212
{ 148, 66, 16}, // 213
{ 0, 82, 0}, // 214
{ 0, 0, 132}, // 215
{ 173, 0, 0}, // 216
{ 255, 49, 0}, // 217
{ 255, 99, 99}, // 218
{ 181, 107, 49}, // 219
{ 0, 132, 0}, // 220
{ 0, 0, 255}, // 221
{ 173, 41, 0}, // 222
{ 255, 82, 0}, // 223
{ 255, 132, 132}, // 224
{ 214, 148, 74}, // 225
{ 41, 214, 0}, // 226
{ 0, 82, 173}, // 227
{ 255, 214, 0}, // 228
{ 247, 132, 49}, // 229
{ 255, 165, 165}, // 230
{ 239, 198, 123}, // 231
{ 173, 214, 0}, // 232
{ 0, 132, 214}, // 233
{ 57, 57, 57}, // 234
{ 247, 189, 74}, // 235
{ 255, 198, 198}, // 236
{ 255, 239, 173}, // 237
{ 214, 255, 173}, // 238
{ 82, 173, 255}, // 239
{ 107, 107, 107}, // 240
{ 247, 222, 99}, // 241
{ 255, 0, 255}, // 242
{ 255, 132, 255}, // 243
{ 132, 132, 173}, // 244
{ 148, 247, 255}, // 245
{ 148, 148, 148}, // 246
{ 82, 0, 82}, // 247
{ 112, 68, 112}, // 248
{ 176, 88, 144}, // 249
{ 214, 132, 173}, // 250
{ 206, 247, 255}, // 251
{ 198, 198, 198}, // 252
{ 0, 214, 255}, // 253
{ 96, 224, 96 }, // 254
{ 255, 255, 255}, // 255
};
char *CGEEngine::mergeExt(char *buf, const char *name, const char *ext) {
strcpy(buf, name);
char *dot = strrchr(buf, '.');
if (!dot)
strcat(buf, ext);
return buf;
}
int CGEEngine::takeEnum(const char **tab, const char *text) {
if (text) {
for (const char **e = tab; *e; e++) {
if (scumm_stricmp(text, *e) == 0) {
return e - tab;
}
}
}
return -1;
}
int CGEEngine::newRandom(int range) {
if (!range)
return 0;
return _randomSource.getRandomNumber(range - 1);
}
void CGEEngine::sndSetVolume() {
// USeless for ScummVM
}
void CGEEngine::syncHeader(Common::Serializer &s) {
debugC(1, kCGEDebugEngine, "CGEEngine::syncHeader(s)");
int i = kDemo;
s.syncAsUint16LE(_now);
s.syncAsUint16LE(_oldLev);
s.syncAsUint16LE(i); // unused Demo string id
for (i = 0; i < 5; i++)
s.syncAsUint16LE(_game);
s.syncAsSint16LE(i); // unused VGA::Mono variable
s.syncAsUint16LE(_music);
s.syncBytes(_volume, 2);
for (i = 0; i < 4; i++)
s.syncAsUint16LE(_flag[i]);
if (s.isLoading()) {
// Reset scene values
initSceneValues();
}
for (i = 0; i < kSceneMax; i++) {
s.syncAsSint16LE(_heroXY[i].x);
s.syncAsUint16LE(_heroXY[i].y);
}
for (i = 0; i < 1 + kSceneMax; i++) {
s.syncAsByte(_barriers[i]._horz);
s.syncAsByte(_barriers[i]._vert);
}
for (i = 0; i < kPocketNX; i++)
s.syncAsUint16LE(_pocref[i]);
if (s.isSaving()) {
// Write checksum
int checksum = kSavegameCheckSum;
s.syncAsUint16LE(checksum);
} else {
// Read checksum and validate it
uint16 checksum = 0;
s.syncAsUint16LE(checksum);
if (checksum != kSavegameCheckSum)
error("%s", _text->getText(kBadSVG));
}
}
bool CGEEngine::loadGame(int slotNumber, SavegameHeader *header, bool tiny) {
debugC(1, kCGEDebugEngine, "CGEEngine::loadgame(%d, header, %s)", slotNumber, tiny ? "true" : "false");
Common::MemoryReadStream *readStream;
if (slotNumber == -1) {
// Loading the data for the initial game state
EncryptedStream file = EncryptedStream(this, kSavegame0Name);
int size = file.size();
byte *dataBuffer = (byte *)malloc(size);
file.read(dataBuffer, size);
readStream = new Common::MemoryReadStream(dataBuffer, size, DisposeAfterUse::YES);
} else {
// Open up the savegame file
Common::String slotName = getSaveStateName(slotNumber);
Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(slotName);
// Read the data into a data buffer
int size = saveFile->size();
byte *dataBuffer = (byte *)malloc(size);
saveFile->read(dataBuffer, size);
readStream = new Common::MemoryReadStream(dataBuffer, size, DisposeAfterUse::YES);
delete saveFile;
}
// Check to see if it's a ScummVM savegame or not
char buffer[kSavegameStrSize + 1];
readStream->read(buffer, kSavegameStrSize + 1);
if (strncmp(buffer, savegameStr, kSavegameStrSize + 1) != 0) {
// It's not, so rewind back to the start
readStream->seek(0);
if (header)
// Header wanted where none exists, so return false
return false;
} else {
// Found header
SavegameHeader saveHeader;
if (!readSavegameHeader(readStream, saveHeader)) {
delete readStream;
return false;
}
if (header) {
*header = saveHeader;
delete readStream;
return true;
}
g_engine->setTotalPlayTime(saveHeader.playTime * 1000);
}
// Get in the savegame
syncGame(readStream, NULL, tiny);
delete readStream;
return true;
}
/**
* Returns true if a given savegame exists
*/
bool CGEEngine::savegameExists(int slotNumber) {
Common::String slotName = getSaveStateName(slotNumber);
Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(slotName);
bool result = saveFile != NULL;
delete saveFile;
return result;
}
Common::Error CGEEngine::loadGameState(int slot) {
// Clear current game activity
sceneDown();
_hero->park();
resetGame();
// If music is playing, kill it.
if (_music)
_midiPlayer->killMidi();
// Load the game
loadGame(slot, NULL);
_commandHandler->addCommand(kCmdLevel, -1, _oldLev, &_sceneLight);
_sceneLight->gotoxy(kSceneX + ((_now - 1) % kSceneNx) * kSceneDx + kSceneSX,
kSceneY + ((_now - 1) / kSceneNx) * kSceneDy + kSceneSY);
sceneUp();
return Common::kNoError;
}
void CGEEngine::resetGame() {
_vga->_spareQ->clear();
_commandHandler->reset();
}
Common::Error CGEEngine::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
sceneDown();
_hero->park();
_oldLev = _lev;
int x = _hero->_x;
int y = _hero->_y;
int z = _hero->_z;
// Write out the user's progress
saveGame(slot, desc);
_commandHandler->addCommand(kCmdLevel, -1, _oldLev, &_sceneLight);
// Reload the scene
sceneUp();
// Restore player position
_hero->gotoxy(x, y);
_hero->_z = z;
return Common::kNoError;
}
void CGEEngine::saveGame(int slotNumber, const Common::String &desc) {
// Set up the serializer
Common::String slotName = getSaveStateName(slotNumber);
Common::OutSaveFile *saveFile = g_system->getSavefileManager()->openForSaving(slotName);
// Write out the ScummVM savegame header
SavegameHeader header;
header.saveName = desc;
header.version = kSavegameVersion;
writeSavegameHeader(saveFile, header);
// Write out the data of the savegame
syncGame(NULL, saveFile, false);
// Finish writing out game data
saveFile->finalize();
delete saveFile;
}
void CGEEngine::writeSavegameHeader(Common::OutSaveFile *out, SavegameHeader &header) {
// Write out a savegame header
out->write(savegameStr, kSavegameStrSize + 1);
out->writeByte(kSavegameVersion);
// Write savegame name
out->write(header.saveName.c_str(), header.saveName.size() + 1);
// Get the active palette
uint8 thumbPalette[256 * 3];
g_system->getPaletteManager()->grabPalette(thumbPalette, 0, 256);
// Create a thumbnail and save it
Graphics::Surface *thumb = new Graphics::Surface();
Graphics::Surface *s = _vga->_page[0];
::createThumbnail(thumb, (const byte *)s->getPixels(), kScrWidth, kScrHeight, thumbPalette);
Graphics::saveThumbnail(*out, *thumb);
thumb->free();
delete thumb;
// Write out the save date/time
TimeDate td;
g_system->getTimeAndDate(td);
out->writeSint16LE(td.tm_year + 1900);
out->writeSint16LE(td.tm_mon + 1);
out->writeSint16LE(td.tm_mday);
out->writeSint16LE(td.tm_hour);
out->writeSint16LE(td.tm_min);
out->writeUint32LE(g_engine->getTotalPlayTime() / 1000);
}
void CGEEngine::syncGame(Common::SeekableReadStream *readStream, Common::WriteStream *writeStream, bool tiny) {
Common::Serializer s(readStream, writeStream);
if (s.isSaving()) {
for (int i = 0; i < kPocketNX; i++) {
Sprite *pocSpr = _pocket[i];
_pocref[i] = (pocSpr) ? pocSpr->_ref : -1;
}
// Skip Digital and Midi volumes, useless under ScummVM
_volume[0] = 0;
_volume[1] = 0;
}
// Synchronise header data
syncHeader(s);
if (s.isSaving()) {
// Loop through saving the sprite data
for (Sprite *spr = _vga->_spareQ->first(); spr; spr = spr->_next) {
if (!s.err())
spr->sync(s);
}
} else {
// Loading game
if (_mode == 0) {
// Skip Digital and Midi volumes, useless under ScummVM
sndSetVolume();
}
if (!tiny) { // load sprites & pocket
while (readStream->pos() < readStream->size()) {
Sprite S(this, NULL);
S.sync(s);
S._prev = S._next = NULL;
Sprite *spr = (scumm_stricmp(S._file + 2, "MUCHA") == 0) ? new Fly(this, NULL)
: new Sprite(this, NULL);
assert(spr != NULL);
*spr = S;
_vga->_spareQ->append(spr);
}
for (int i = 0; i < kPocketNX; i++) {
int r = _pocref[i];
_pocket[i] = (r < 0) ? NULL : _vga->_spareQ->locate(r);
}
}
}
}
WARN_UNUSED_RESULT bool CGEEngine::readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header, bool skipThumbnail) {
header.version = 0;
header.saveName.clear();
header.thumbnail = nullptr;
header.saveYear = 0;
header.saveMonth = 0;
header.saveDay = 0;
header.saveHour = 0;
header.saveMinutes = 0;
header.playTime = 0;
// Get the savegame version
header.version = in->readByte();
if (header.version > kSavegameVersion)
return false;
// Read in the string
char ch;
while ((ch = (char)in->readByte()) != '\0')
header.saveName += ch;
// Get the thumbnail
if (!Graphics::loadThumbnail(*in, header.thumbnail, skipThumbnail)) {
return false;
}
// Read in save date/time
header.saveYear = in->readSint16LE();
header.saveMonth = in->readSint16LE();
header.saveDay = in->readSint16LE();
header.saveHour = in->readSint16LE();
header.saveMinutes = in->readSint16LE();
if (header.version >= 3) {
header.playTime = in->readUint32LE();
}
return true;
}
void CGEEngine::heroCover(int cvr) {
debugC(1, kCGEDebugEngine, "CGEEngine::heroCover(%d)", cvr);
_commandHandler->addCommand(kCmdCover, 1, cvr, NULL);
}
void CGEEngine::trouble(int seq, int text) {
debugC(1, kCGEDebugEngine, "CGEEngine::trouble(%d, %d)", seq, text);
_hero->park();
_commandHandler->addCommand(kCmdWait, -1, -1, _hero);
_commandHandler->addCommand(kCmdSeq, -1, seq, _hero);
_commandHandler->addCommand(kCmdSound, -1, 2, _hero);
_commandHandler->addCommand(kCmdWait, -1, -1, _hero);
_commandHandler->addCommand(kCmdSay, 1, text, _hero);
}
void CGEEngine::offUse() {
debugC(1, kCGEDebugEngine, "CGEEngine::offUse()");
trouble(kSeqOffUse, kOffUse + newRandom(_offUseCount));
}
void CGEEngine::tooFar() {
debugC(1, kCGEDebugEngine, "CGEEngine::tooFar()");
trouble(kSeqTooFar, kTooFar);
}
void CGEEngine::loadHeroXY() {
debugC(1, kCGEDebugEngine, "CGEEngine::loadHeroXY()");
EncryptedStream cf(this, "CGE.HXY");
uint16 x, y;
memset(_heroXY, 0, sizeof(_heroXY));
if (!cf.err()) {
for (int i = 0; i < kSceneMax; ++i) {
cf.read((byte *)&x, 2);
cf.read((byte *)&y, 2);
_heroXY[i].x = (int16)FROM_LE_16(x);
_heroXY[i].y = (int16)FROM_LE_16(y);
}
}
}
void CGEEngine::loadMapping() {
debugC(1, kCGEDebugEngine, "CGEEngine::loadMapping()");
if (_now <= kSceneMax) {
EncryptedStream cf(this, "CGE.TAB");
if (!cf.err()) {
// Move to the data for the given room
cf.seek((_now - 1) * kMapArrSize);
// Read in the data
for (int z = 0; z < kMapZCnt; ++z) {
cf.read(&_clusterMap[z][0], kMapXCnt);
}
}
}
}
Square::Square(CGEEngine *vm) : Sprite(vm, NULL), _vm(vm) {
_flags._kill = true;
_flags._bDel = false;
BitmapPtr *MB = new BitmapPtr[2];
MB[0] = new Bitmap(_vm, "BRICK");
MB[1] = NULL;
setShapeList(MB);
}
void Square::touch(uint16 mask, int x, int y, Common::KeyCode keyCode) {
Sprite::touch(mask, x, y, keyCode);
if (mask & kMouseLeftUp) {
_vm->XZ(_x + x, _y + y).cell() = 0;
_vm->_commandHandlerTurbo->addCommand(kCmdKill, -1, 0, this);
}
}
void CGEEngine::setMapBrick(int x, int z) {
debugC(1, kCGEDebugEngine, "CGEEngine::setMapBrick(%d, %d)", x, z);
Square *s = new Square(this);
char n[6];
s->gotoxy(x * kMapGridX, kMapTop + z * kMapGridZ);
sprintf(n, "%02d:%02d", x, z);
_clusterMap[z][x] = 1;
s->setName(n);
_vga->_showQ->insert(s, _vga->_showQ->first());
}
void CGEEngine::keyClick() {
debugC(1, kCGEDebugEngine, "CGEEngine::keyClick()");
_commandHandlerTurbo->addCommand(kCmdSound, -1, 5, NULL);
}
void CGEEngine::resetQSwitch() {
debugC(1, kCGEDebugEngine, "CGEEngine::resetQSwitch()");
_commandHandlerTurbo->addCommand(kCmdSeq, 123, 0, NULL);
keyClick();
}
void CGEEngine::quit() {
debugC(1, kCGEDebugEngine, "CGEEngine::quit()");
static Choice QuitMenu[] = {
{ NULL, &CGEEngine::startCountDown },
{ NULL, &CGEEngine::resetQSwitch },
{ NULL, &CGEEngine::dummy }
};
if (_commandHandler->idle() && !_hero->_flags._hide) {
if (Vmenu::_addr) {
_commandHandlerTurbo->addCommand(kCmdKill, -1, 0, Vmenu::_addr);
resetQSwitch();
} else {
QuitMenu[0]._text = _text->getText(kQuit);
QuitMenu[1]._text = _text->getText(kNoQuit);
(new Vmenu(this, QuitMenu, -1, -1))->setName(_text->getText(kQuitTitle));
_commandHandlerTurbo->addCommand(kCmdSeq, 123, 1, NULL);
keyClick();
}
}
}
void CGEEngine::miniStep(int stp) {
debugC(1, kCGEDebugEngine, "CGEEngine::miniStep(%d)", stp);
if (stp < 0) {
_miniScene->_flags._hide = true;
} else {
*_miniShp[0] = *_miniShpList[stp];
_miniScene->_flags._hide = false;
}
}
void CGEEngine::postMiniStep(int step) {
debugC(6, kCGEDebugEngine, "CGEEngine::postMiniStep(%d)", step);
if (_miniScene && step != _recentStep)
_commandHandlerTurbo->addCallback(kCmdExec, -1, _recentStep = step, kMiniStep);
}
void CGEEngine::showBak(int ref) {
debugC(1, kCGEDebugEngine, "CGEEngine::showBack(%d)", ref);
Sprite *spr = _vga->_spareQ->locate(ref);
if (!spr)
return;
_bitmapPalette = _vga->_sysPal;
spr->expand();
_bitmapPalette = NULL;
spr->show(2);
_vga->copyPage(1, 2);
_sys->setPal();
spr->contract();
}
void CGEEngine::sceneUp() {
debugC(1, kCGEDebugEngine, "CGEEngine::sceneUp()");
const int BakRef = 1000 * _now;
if (_music)
_midiPlayer->loadMidi(_now);
showBak(BakRef);
loadMapping();
Sprite *spr = _vga->_spareQ->first();
while (spr) {
Sprite *n = spr->_next;
if (spr->_scene == _now || spr->_scene == 0)
if (spr->_ref != BakRef) {
if (spr->_flags._back)
spr->backShow();
else
expandSprite(spr);
}
spr = n;
}
_sound->stop();
_fx->clear();
_fx->preload(0);
_fx->preload(BakRef);
if (_hero) {
_hero->gotoxy(_heroXY[_now - 1].x, _heroXY[_now - 1].y);
// following 2 lines trims Hero's Z position!
_hero->tick();
_hero->_time = 1;
_hero->_flags._hide = false;
}
if (!_dark)
_vga->sunset();
_vga->copyPage(0, 1);
selectPocket(-1);
if (_hero) {
_vga->_showQ->insert(_vga->_showQ->remove(_hero));
if (_shadow) {
_vga->_showQ->remove(_shadow);
_shadow->makeXlat(_vga->glass(_vga->_sysPal, 204, 204, 204));
_vga->_showQ->insert(_shadow, _hero);
_shadow->_z = _hero->_z;
}
}
feedSnail(_vga->_showQ->locate(BakRef + 999), kTake);
_vga->show();
_vga->copyPage(1, 0);
_vga->show();
_vga->sunrise(_vga->_sysPal);
_dark = false;
if (!_startupMode)
_mouse->on();
}
void CGEEngine::sceneDown() {
debugC(1, kCGEDebugEngine, "CGEEngine::sceneDown()");
if (_horzLine && !_horzLine->_flags._hide)
switchMapping();
for (Sprite *spr = _vga->_showQ->first(); spr;) {
Sprite *n = spr->_next;
if (spr->_ref >= 1000 /*&& spr->_scene*/) {
if (spr->_ref % 1000 == 999)
feedSnail(spr, kTake);
_vga->_spareQ->append(_vga->_showQ->remove(spr));
}
spr = n;
}
}
void CGEEngine::xScene() {
debugC(6, kCGEDebugEngine, "CGEEngine::xScene()");
sceneDown();
if (_lev != -1)
_commandHandler->addCommand(kCmdLevel, -1, _lev, &_sceneLight);
sceneUp();
}
void CGEEngine::qGame() {
debugC(1, kCGEDebugEngine, "CGEEngine::qGame()");
sceneDown();
_hero->park();
_oldLev = _lev;
// Write out the user's progress
saveGame(0, Common::String("Automatic Savegame"));
_vga->sunset();
_endGame = true;
}
void CGEEngine::switchScene(int newScene) {
debugC(1, kCGEDebugEngine, "CGEEngine::switchScene(%d)", newScene);
if (newScene == _now)
return;
if (newScene < 0) {
_commandHandler->addCommand(kCmdLabel, -1, 0, NULL); // wait for repaint
_commandHandler->addCallback(kCmdExec, -1, 0, kQGame); // quit game
} else {
_now = newScene;
_mouse->off();
if (_hero) {
_hero->park();
_hero->step(0);
_vga->_spareQ->_show = false;
}
_sceneLight->gotoxy(kSceneX + ((_now - 1) % kSceneNx) * kSceneDx + kSceneSX,
kSceneY + ((_now - 1) / kSceneNx) * kSceneDy + kSceneSY);
killText();
if (!_startupMode)
keyClick();
_commandHandler->addCommand(kCmdLabel, -1, 0, NULL); // wait for repaint
_commandHandler->addCallback(kCmdExec, 0, 0, kXScene); // switch scene
}
}
System::System(CGEEngine *vm) : Sprite(vm, NULL), _vm(vm) {
_funDel = kHeroFun0;
setPal();
tick();
}
void System::setPal() {
Dac *p = _vm->_vga->_sysPal + 256 - ARRAYSIZE(g_stdPal);
for (uint i = 0; i < ARRAYSIZE(g_stdPal); i++) {
p[i]._r = g_stdPal[i]._r >> 2;
p[i]._g = g_stdPal[i]._g >> 2;
p[i]._b = g_stdPal[i]._b >> 2;
}
}
void System::funTouch() {
uint16 n = (_vm->_flag[0]) ? kHeroFun1 : kHeroFun0; // PAIN flag
if (_vm->_talk == NULL || n > _funDel)
_funDel = n;
}
void System::touch(uint16 mask, int x, int y, Common::KeyCode keyCode) {
funTouch();
if (mask & kEventKeyb) {
if (keyCode == Common::KEYCODE_ESCAPE) {
// The original was calling keyClick()
// The sound is uselessly annoying and noisy, so it has been removed
_vm->killText();
if (_vm->_startupMode == 1) {
_vm->_commandHandler->addCommand(kCmdClear, -1, 0, NULL);
return;
}
}
} else {
if (_vm->_startupMode)
return;
int selectedScene = 0;
_vm->_infoLine->update(NULL);
if (y >= kWorldHeight ) {
if (x < kButtonX) { // select scene?
if (y >= kSceneY && y < kSceneY + kSceneNy * kSceneDy &&
x >= kSceneX && x < kSceneX + kSceneNx * kSceneDx && !_vm->_game) {
selectedScene = ((y - kSceneY) / kSceneDy) * kSceneNx + (x - kSceneX) / kSceneDx + 1;
if (selectedScene > _vm->_maxScene)
selectedScene = 0;
} else {
selectedScene = 0;
}
} else if (mask & kMouseLeftUp) {
if (y >= kPocketY && y < kPocketY + kPocketNY * kPocketDY &&
x >= kPocketX && x < kPocketX + kPocketNX * kPocketDX) {
int n = ((y - kPocketY) / kPocketDY) * kPocketNX + (x - kPocketX) / kPocketDX;
_vm->selectPocket(n);
}
}
}
_vm->postMiniStep(selectedScene - 1);
if (mask & kMouseLeftUp) {
if (selectedScene && _vm->_commandHandler->idle() && _vm->_hero->_tracePtr < 0)
_vm->switchScene(selectedScene);
if (_vm->_horzLine && !_vm->_horzLine->_flags._hide) {
if (y >= kMapTop && y < kMapTop + kMapHig) {
Cluster tmpCluster = _vm->XZ(x, y);
int16 x1 = tmpCluster._pt.x;
int16 z1 = tmpCluster._pt.y;
_vm->_clusterMap[z1][x1] = 1;
_vm->setMapBrick(x1, z1);
}
} else {
if (!_vm->_talk && _vm->_commandHandler->idle() && _vm->_hero
&& y >= kMapTop && y < kMapTop + kMapHig && !_vm->_game) {
_vm->_hero->findWay(_vm->XZ(x, y));
}
}
}
}
}
void System::tick() {
if (!_vm->_startupMode)
if (--_funDel == 0) {
_vm->killText();
if (_vm->_commandHandler->idle()) {
if (_vm->_flag[0]) // Pain flag
_vm->heroCover(9);
else {
int n = _vm->newRandom(100);
if (n > 96)
_vm->heroCover(6 + (_vm->_hero->_x + _vm->_hero->_w / 2 < kScrWidth / 2));
else if (n > 90)
_vm->heroCover(5);
else if (n > 60)
_vm->heroCover(4);
else
_vm->heroCover(3);
}
}
funTouch();
}
_time = kSystemRate;
}
/**
* Switch greyscale mode on/off
*/
void CGEEngine::switchColorMode() {
debugC(1, kCGEDebugEngine, "CGEEngine::switchColorMode()");
_commandHandlerTurbo->addCommand(kCmdSeq, 121, _vga->_mono = !_vga->_mono, NULL);
keyClick();
_vga->setColors(_vga->_sysPal, 64);
}
/**
* Switch music on/off
*/
void CGEEngine::switchMusic() {
debugC(1, kCGEDebugEngine, "CGEEngine::switchMusic()");
_commandHandlerTurbo->addCommand(kCmdSeq, 122, (_music = !_music), NULL);
keyClick();
if (_music)
_midiPlayer->loadMidi(_now);
else
_midiPlayer->killMidi();
}
/**
* Shutdown game
*/
void CGEEngine::startCountDown() {
debugC(1, kCGEDebugEngine, "CGEEngine::startCountDown()");
switchScene(-1);
}
void CGEEngine::switchMapping() {
assert(_horzLine);
debugC(1, kCGEDebugEngine, "CGEEngine::switchMapping()");
if (_horzLine && _horzLine->_flags._hide) {
for (int i = 0; i < kMapZCnt; i++) {
for (int j = 0; j < kMapXCnt; j++) {
if (_clusterMap[i][j])
setMapBrick(j, i);
}
}
} else {
for (Sprite *s = _vga->_showQ->first(); s; s = s->_next)
if (s->_w == kMapGridX && s->_h == kMapGridZ)
_commandHandlerTurbo->addCommand(kCmdKill, -1, 0, s);
}
_horzLine->_flags._hide = !_horzLine->_flags._hide;
}
void CGEEngine::killSprite() {
debugC(1, kCGEDebugEngine, "CGEEngine::killSprite()");
_sprite->_flags._kill = true;
_sprite->_flags._bDel = true;
_commandHandlerTurbo->addCommand(kCmdKill, -1, 0, _sprite);
_sprite = NULL;
}
void CGEEngine::optionTouch(int opt, uint16 mask) {
switch (opt) {
case 1:
if (mask & kMouseLeftUp)
switchColorMode();
break;
case 2:
if (mask & kMouseLeftUp)
switchMusic();
else if (mask & kMouseRightUp)
openMainMenuDialog();
break;
case 3:
if (mask & kMouseLeftUp)
quit();
break;
default:
break;
}
}
#pragma argsused
void Sprite::touch(uint16 mask, int x, int y, Common::KeyCode keyCode) {
_vm->_sys->funTouch();
if ((mask & kEventAttn) != 0)
return;
_vm->_infoLine->update(name());
if (mask & (kMouseRightDown | kMouseLeftDown))
_vm->_sprite = this;
if (_ref / 10 == 12) {
_vm->optionTouch(_ref % 10, mask);
return;
}
if (_flags._syst)
return; // cannot access system sprites
if (_vm->_game)
if (mask & kMouseLeftUp) {
mask &= ~kMouseLeftUp;
mask |= kMouseRightUp;
}
if ((mask & kMouseRightUp) && _vm->_commandHandler->idle()) {
Sprite *ps = (_vm->_pocLight->_seqPtr) ? _vm->_pocket[_vm->_pocPtr] : NULL;
if (ps) {
if (_flags._kept || _vm->_hero->distance(this) < kDistMax) {
if (works(ps)) {
_vm->feedSnail(ps, kTake);
} else
_vm->offUse();
_vm->selectPocket(-1);
} else
_vm->tooFar();
} else {
if (_flags._kept) {
mask |= kMouseLeftUp;
} else {
if (_vm->_hero->distance(this) < kDistMax) {
if (_flags._port) {
if (_vm->findPocket(NULL) < 0) {
_vm->pocFul();
} else {
_vm->_commandHandler->addCommand(kCmdReach, -1, -1, this);
_vm->_commandHandler->addCommand(kCmdKeep, -1, -1, this);
_flags._port = false;
}
} else {
if (_takePtr != kNoPtr) {
if (snList(kTake)[_takePtr]._commandType == kCmdNext)
_vm->offUse();
else
_vm->feedSnail(this, kTake);
} else {
_vm->offUse();
}
}
} else {
_vm->tooFar();
}
}
}
}
if ((mask & kMouseLeftUp) && _vm->_commandHandler->idle()) {
if (_flags._kept) {
for (int n = 0; n < kPocketNX; n++) {
if (_vm->_pocket[n] == this) {
_vm->selectPocket(n);
break;
}
}
} else {
_vm->_commandHandler->addCommand(kCmdWalk, -1, -1, this); // Hero->FindWay(this);
}
}
}
void CGEEngine::loadSprite(const char *fname, int ref, int scene, int col = 0, int row = 0, int pos = 0) {
static const char *Comd[] = { "Name", "Type", "Phase", "East",
"Left", "Right", "Top", "Bottom",
"Seq", "Near", "Take",
"Portable", "Transparent",
NULL
};
static const char *Type[] = { "DEAD", "AUTO", "WALK", "NEWTON", "LISSAJOUS",
"FLY", NULL
};
int shpcnt = 0;
int type = 0; // DEAD
bool east = false;
bool port = false;
bool tran = false;
char tmpStr[kLineMax + 1];
Common::String line;
mergeExt(tmpStr, fname, kSprExt);
if (_resman->exist(tmpStr)) { // sprite description file exist
EncryptedStream sprf(this, tmpStr);
if (sprf.err())
error("Bad SPR [%s]", tmpStr);
uint16 len;
int i, lcnt = 0;
for (line = sprf.readLine(); !sprf.eos(); line = sprf.readLine()) {
len = line.size();
lcnt++;
Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr));
if (len == 0 || *tmpStr == '.')
continue;
if ((i = takeEnum(Comd, strtok(tmpStr, " =\t"))) < 0)
error("Bad line %d [%s]", lcnt, fname);
switch (i) {
default:
case 0: // Name - will be taken in Expand routine
break;
case 1: // Type
if ((type = takeEnum(Type, strtok(NULL, " \t,;/"))) < 0)
error("Bad line %d [%s]", lcnt, fname);
break;
case 2: // Phase
shpcnt++;
break;
case 3: // East
east = (atoi(strtok(NULL, " \t,;/")) != 0);
break;
case 11: // Portable
port = (atoi(strtok(NULL, " \t,;/")) != 0);
break;
case 12: // Transparent
tran = (atoi(strtok(NULL, " \t,;/")) != 0);
break;
}
}
if (! shpcnt)
error("No shapes [%s]", fname);
} else {
// no sprite description: mono-shaped sprite with only .BMP file
++shpcnt;
}
// make sprite of choosen type
switch (type) {
case 1:
// AUTO
_sprite = new Sprite(this, NULL);
if (_sprite) {
_sprite->gotoxy(col, row);
}
break;
case 2:
{ // WALK
Walk *w = new Walk(this, NULL);
if (w && ref == 1) {
w->gotoxy(col, row);
if (_hero)
error("2nd HERO [%s]", fname);
_hero = w;
}
_sprite = w;
break;
}
case 3: // NEWTON
case 4: // LISSAJOUS
error("Bad type [%s]", fname);
break;
case 5:
{ // FLY
Fly *f = new Fly(this, NULL);
_sprite = f;
break;
}
default:
// DEAD
_sprite = new Sprite(this, NULL);
if (_sprite)
_sprite->gotoxy(col, row);
break;
}
if (_sprite) {
_sprite->_ref = ref;
_sprite->_scene = scene;
_sprite->_z = pos;
_sprite->_flags._east = east;
_sprite->_flags._port = port;
_sprite->_flags._tran = tran;
_sprite->_flags._kill = true;
_sprite->_flags._bDel = true;
// Extract the filename, without the extension
Common::strlcpy(_sprite->_file, fname, sizeof(_sprite->_file));
char *p = strchr(_sprite->_file, '.');
if (p)
*p = '\0';
_sprite->_shpCnt = shpcnt;
_vga->_spareQ->append(_sprite);
}
}
void CGEEngine::loadScript(const char *fname) {
EncryptedStream scrf(this, fname);
if (scrf.err())
return;
bool ok = true;
int lcnt = 0;
char tmpStr[kLineMax+1];
Common::String line;
for (line = scrf.readLine(); !scrf.eos(); line = scrf.readLine()) {
char *p;
lcnt++;
Common::strlcpy(tmpStr, line.c_str(), sizeof(tmpStr));
if ((line.size() == 0) || (*tmpStr == '.'))
continue;
ok = false; // not OK if break
// sprite ident number
if ((p = strtok(tmpStr, " \t\n")) == NULL)
break;
int SpI = atoi(p);
// sprite file name
char *SpN;
if ((SpN = strtok(NULL, " ,;/\t\n")) == NULL)
break;
// sprite scene
if ((p = strtok(NULL, " ,;/\t\n")) == NULL)
break;
int SpA = atoi(p);
// sprite column
if ((p = strtok(NULL, " ,;/\t\n")) == NULL)
break;
int SpX = atoi(p);
// sprite row
if ((p = strtok(NULL, " ,;/\t\n")) == NULL)
break;
int SpY = atoi(p);
// sprite Z pos
if ((p = strtok(NULL, " ,;/\t\n")) == NULL)
break;
int SpZ = atoi(p);
// sprite life
if ((p = strtok(NULL, " ,;/\t\n")) == NULL)
break;
bool BkG = atoi(p) == 0;
ok = true; // no break: OK
_sprite = NULL;
loadSprite(SpN, SpI, SpA, SpX, SpY, SpZ);
if (_sprite && BkG)
_sprite->_flags._back = true;
}
if (!ok)
error("Bad INI line %d [%s]", lcnt, fname);
}
Sprite *CGEEngine::locate(int ref) {
Sprite *spr = _vga->_showQ->locate(ref);
return (spr) ? spr : _vga->_spareQ->locate(ref);
}
Sprite *CGEEngine::spriteAt(int x, int y) {
Sprite *spr = NULL, * tail = _vga->_showQ->last();
if (tail) {
for (spr = tail->_prev; spr; spr = spr->_prev) {
if (! spr->_flags._hide && ! spr->_flags._tran) {
if (spr->shp()->solidAt(x - spr->_x, y - spr->_y))
break;
}
}
}
return spr;
}
Cluster CGEEngine::XZ(int16 x, int16 y) {
if (y < kMapTop)
y = kMapTop;
if (y > kMapTop + kMapHig - kMapGridZ)
y = kMapTop + kMapHig - kMapGridZ;
return Cluster(this, x / kMapGridX, (y - kMapTop) / kMapGridZ);
}
void CGEEngine::killText() {
if (!_talk)
return;
_commandHandlerTurbo->addCommand(kCmdKill, -1, 0, _talk);
_talk = NULL;
}
void CGEEngine::mainLoop() {
_vga->show();
_commandHandlerTurbo->runCommand();
_commandHandler->runCommand();
// Handle a delay between game frames
handleFrame();
// Handle any pending events
_eventManager->poll();
// Check shouldQuit()
_quitFlag = shouldQuit();
}
void CGEEngine::handleFrame() {
// Game frame delay
uint32 millis = g_system->getMillis();
while (!_quitFlag && (millis < (_lastFrame + kGameFrameDelay))) {
// Handle any pending events
_eventManager->poll();
if (millis >= (_lastTick + kGameTickDelay)) {
// Dispatch the tick to any active objects
tick();
_lastTick = millis;
}
// Slight delay
g_system->delayMillis(5);
millis = g_system->getMillis();
}
_lastFrame = millis;
if (millis >= (_lastTick + kGameTickDelay)) {
// Dispatch the tick to any active objects
tick();
_lastTick = millis;
}
}
void CGEEngine::tick() {
for (Sprite *spr = _vga->_showQ->first(); spr; spr = spr->_next) {
if (spr->_time) {
if (!spr->_flags._hide) {
if (--spr->_time == 0)
spr->tick();
}
}
}
}
void CGEEngine::loadUser() {
// set scene
if (_mode == 0) {
// user .SVG file found - load it from slot 0
loadGame(0, NULL);
} else if (_mode == 1) {
// Load either initial game state savegame or launcher specified savegame
loadGame(_startGameSlot, NULL);
} else {
error("Creating setup savegames not supported");
}
loadScript("CGE.IN0");
}
void CGEEngine::runGame() {
if (_quitFlag)
return;
loadHeroXY();
_sceneLight->_flags._tran = true;
_vga->_showQ->append(_sceneLight);
_sceneLight->_flags._hide = false;
const Seq pocSeq[] = {
{ 0, 0, 0, 0, 20 },
{ 1, 2, 0, 0, 4 },
{ 2, 3, 0, 0, 4 },
{ 3, 4, 0, 0, 16 },
{ 2, 5, 0, 0, 4 },
{ 1, 6, 0, 0, 4 },
{ 0, 1, 0, 0, 16 },
};
Seq *seq = (Seq *)malloc(7 * sizeof(Seq));
Common::copy(pocSeq, pocSeq + 7, seq);
_pocLight->setSeq(seq);
_pocLight->_flags._tran = true;
_pocLight->_time = 1;
_pocLight->_z = 120;
_vga->_showQ->append(_pocLight);
selectPocket(-1);
_vga->_showQ->append(_mouse);
loadUser();
if ((_sprite = _vga->_spareQ->locate(121)) != NULL)
_commandHandlerTurbo->addCommand(kCmdSeq, -1, _vga->_mono, _sprite);
if ((_sprite = _vga->_spareQ->locate(122)) != NULL)
_sprite->step(_music);
_commandHandlerTurbo->addCommand(kCmdSeq, -1, _music, _sprite);
if (!_music)
_midiPlayer->killMidi();
if (_resman->exist("MINI.SPR")) {
_miniShp = new BitmapPtr[2];
_miniShp[0] = _miniShp[1] = NULL;
loadSprite("MINI", -1, 0, kMiniX, kMiniY);
expandSprite(_miniScene = _sprite); // NULL is ok
if (_miniScene) {
_miniScene->_flags._kill = false;
_miniScene->_flags._hide = true;
_miniShp[0] = new Bitmap(this, *_miniScene->shp());
_miniShpList = _miniScene->setShapeList(_miniShp);
postMiniStep(-1);
}
}
if (_hero) {
expandSprite(_hero);
_hero->gotoxy(_heroXY[_now - 1].x, _heroXY[_now - 1].y);
if (_resman->exist("00SHADOW.SPR")) {
loadSprite("00SHADOW", -1, 0, _hero->_x + 14, _hero->_y + 51);
delete _shadow;
if ((_shadow = _sprite) != NULL) {
_shadow->_ref = 2;
_shadow->_flags._tran = true;
_shadow->_flags._kill = false;
_hero->_flags._shad = true;
_vga->_showQ->insert(_vga->_spareQ->remove(_shadow), _hero);
}
}
}
_infoLine->gotoxy(kInfoX, kInfoY);
_infoLine->_flags._tran = true;
_infoLine->update(NULL);
_vga->_showQ->insert(_infoLine);
_debugLine->_z = 126;
_vga->_showQ->insert(_debugLine);
if (_horzLine) {
_horzLine->_y = kMapTop - (kMapTop > 0);
_horzLine->_z = 126;
_vga->_showQ->insert(_horzLine);
}
_mouse->_busy = _vga->_spareQ->locate(kBusyRef);
if (_mouse->_busy)
expandSprite(_mouse->_busy);
_startupMode = 0;
_commandHandler->addCommand(kCmdLevel, -1, _oldLev, &_sceneLight);
_sceneLight->gotoxy(kSceneX + ((_now - 1) % kSceneNx) * kSceneDx + kSceneSX,
kSceneY + ((_now - 1) / kSceneNx) * kSceneDy + kSceneSY);
sceneUp();
_keyboard->setClient(_sys);
// main loop
while (!_endGame && !_quitFlag) {
if (_flag[3]) // Flag FINIS
_commandHandler->addCallback(kCmdExec, -1, 0, kQGame);
mainLoop();
}
// If finishing game due to closing ScummVM window, explicitly save the game
if (!_endGame && canSaveGameStateCurrently())
qGame();
_keyboard->setClient(NULL);
_commandHandler->addCommand(kCmdClear, -1, 0, NULL);
_commandHandlerTurbo->addCommand(kCmdClear, -1, 0, NULL);
_mouse->off();
_vga->_showQ->clear();
_vga->_spareQ->clear();
_hero = NULL;
_shadow = NULL;
}
void CGEEngine::movie(const char *ext) {
assert(ext);
if (_quitFlag)
return;
char fn[12];
sprintf(fn, "CGE.%s", (*ext == '.') ? ext +1 : ext);
if (_resman->exist(fn)) {
loadScript(fn);
expandSprite(_vga->_spareQ->locate(999));
feedSnail(_vga->_showQ->locate(999), kTake);
_vga->_showQ->append(_mouse);
_keyboard->setClient(_sys);
while (!_commandHandler->idle() && !_quitFlag)
mainLoop();
_keyboard->setClient(NULL);
_commandHandler->addCommand(kCmdClear, -1, 0, NULL);
_commandHandlerTurbo->addCommand(kCmdClear, -1, 0, NULL);
_vga->_showQ->clear();
_vga->_spareQ->clear();
}
}
bool CGEEngine::showTitle(const char *name) {
if (_quitFlag)
return false;
_bitmapPalette = _vga->_sysPal;
BitmapPtr *LB = new BitmapPtr[2];
LB[0] = new Bitmap(this, name);
LB[1] = NULL;
_bitmapPalette = NULL;
Sprite D(this, LB);
D._flags._kill = true;
D._flags._bDel = true;
D.center();
D.show(2);
if (_mode == 2) {
inf(kSavegame0Name);
_talk->show(2);
}
_vga->sunset();
_vga->copyPage(1, 2);
_vga->copyPage(0, 1);
selectPocket(-1);
_vga->sunrise(_vga->_sysPal);
if (_mode < 2) {
// At this point the game originally set the protection variables
// used by the copy protection check
movie(kPaylistExt); // paylist
_vga->copyPage(1, 2);
_vga->copyPage(0, 1);
_vga->_showQ->append(_mouse);
// In the original game, the user had to enter his name
// As it was only used to name savegames, it has been removed
_vga->_showQ->clear();
_vga->copyPage(0, 2);
// The original was automatically loading the savegame when available
if (_mode == 0)
_mode++;
}
if (_mode < 2)
movie(kWinkExt);
_vga->copyPage(0, 2);
return true;
}
void CGEEngine::cge_main() {
memset(_barriers, 0xFF, sizeof(_barriers));
if (!_mouse->_exist)
error("%s", _text->getText(kTextNoMouse));
if (!_resman->exist(kSavegame0Name))
_mode = 2;
_debugLine->_flags._hide = true;
if (_horzLine)
_horzLine->_flags._hide = true;
if (_music)
_midiPlayer->loadMidi(0);
if (_startGameSlot != -1) {
// Starting up a savegame from the launcher
_mode++;
runGame();
_startupMode = 2;
if (_flag[3]) // Flag FINIS
movie(kEndgExt);
} else {
if (_mode < 2)
movie(kLgoExt);
if (showTitle("WELCOME")) {
if (_mode == 1)
movie(kIntroExt);
runGame();
_startupMode = 2;
if (_flag[3]) // Flag FINIS
movie(kEndgExt);
} else
_vga->sunset();
}
}
} // End of namespace CGE