scummvm/engines/dreamweb/dreamweb.cpp
2014-02-19 21:45:22 +01:00

608 lines
13 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.
*
*/
#include "common/config-manager.h"
#include "common/debug-channels.h"
#include "common/events.h"
#include "common/file.h"
#include "common/func.h"
#include "common/system.h"
#include "common/timer.h"
#include "common/util.h"
#include "engines/advancedDetector.h"
#include "graphics/palette.h"
#include "graphics/surface.h"
#include "dreamweb/sound.h"
#include "dreamweb/dreamweb.h"
namespace DreamWeb {
DreamWebEngine::DreamWebEngine(OSystem *syst, const DreamWebGameDescription *gameDesc) :
Engine(syst), _gameDescription(gameDesc), _rnd("dreamweb"),
_exText(kNumExTexts),
_setDesc(kNumSetTexts), _blockDesc(kNumBlockTexts),
_roomDesc(kNumRoomTexts), _freeDesc(kNumFreeTexts),
_personText(kNumPersonTexts) {
_vSyncInterrupt = false;
_console = 0;
_sound = 0;
DebugMan.addDebugChannel(kDebugAnimation, "Animation", "Animation Debug Flag");
DebugMan.addDebugChannel(kDebugSaveLoad, "SaveLoad", "Track Save/Load Function");
_speed = 1;
_turbo = false;
_oldMouseState = 0;
_datafilePrefix = "DREAMWEB.";
_speechDirName = "SPEECH";
// ES and FR CD release use a different data file prefix
// and speech directory naming.
if (isCD()) {
switch(getLanguage()) {
case Common::ES_ESP:
_datafilePrefix = "DREAMWSP.";
_speechDirName = "SPANISH";
break;
case Common::FR_FRA:
_datafilePrefix = "DREAMWFR.";
_speechDirName = "FRENCH";
break;
default:
// Nothing to do
break;
}
}
_openChangeSize = kInventx+(4*kItempicsize);
_quitRequested = false;
_speechLoaded = false;
_backdropBlocks = 0;
_reelList = 0;
_oldSubject._type = 0;
_oldSubject._index = 0;
// misc variables
_speechCount = 0;
_charShift = 0;
_kerning = 0;
_brightPalette = false;
_roomLoaded = 0;
_didZoom = 0;
_lineSpacing = 10;
_textAddressX = 13;
_textAddressY = 182;
_textLen = 0;
_lastXPos = 0;
_itemFrame = 0;
_withObject = 0;
_withType = 0;
_lookCounter = 0;
_command = 0;
_commandType = 0;
_objectType = 0;
_getBack = 0;
_invOpen = 0;
_mainMode = 0;
_pickUp = 0;
_lastInvPos = 0;
_examAgain = 0;
_newTextLine = 0;
_openedOb = 0;
_openedType = 0;
_mapAdX = 0;
_mapAdY = 0;
_mapOffsetX = 104;
_mapOffsetY = 38;
_mapXStart = 0;
_mapYStart = 0;
_mapXSize = 0;
_mapYSize = 0;
_haveDoneObs = 0;
_manIsOffScreen = 0;
_facing = 0;
_leaveDirection = 0;
_turnToFace = 0;
_turnDirection = 0;
_mainTimer = 0;
_introCount = 0;
_currentKey = 0;
_timerCount = 0;
_mapX = 0;
_mapY = 0;
_ryanX = 0;
_ryanY = 0;
_lastFlag = 0;
_destPos = 0;
_realLocation = 0;
_roomNum = 0;
_nowInNewRoom = 0;
_resetManXY = 0;
_newLocation = 0xFF;
_autoLocation = 0xFF;
_mouseX = 0;
_mouseY = 0;
_mouseButton = 0;
_oldButton = 0;
_oldX = 0;
_oldY = 0;
_oldPointerX = 0;
_oldPointerY = 0;
_delHereX = 0;
_delHereY = 0;
_pointerXS = 32;
_pointerYS = 32;
_delXS = 0;
_delYS = 0;
_pointerFrame = 0;
_pointerPower = 0;
_pointerMode = 0;
_pointerSpeed = 0;
_pointerCount = 0;
_inMapArea = 0;
_talkMode = 0;
_talkPos = 0;
_character = 0;
_watchDump = 0;
_logoNum = 0;
_oldLogoNum = 0;
_pressed = 0;
_pressPointer = 0;
_graphicPress = 0;
_pressCount = 0;
_lightCount = 0;
_folderPage = 0;
_diaryPage = 0;
_menuCount = 0;
_symbolTopX = 0;
_symbolTopNum = 0;
_symbolTopDir = 0;
_symbolBotX = 0;
_symbolBotNum = 0;
_symbolBotDir = 0;
_walkAndExam = 0;
_walkExamType = 0;
_walkExamNum = 0;
_cursLocX = 0;
_cursLocY = 0;
_curPos = 0;
_monAdX = 0;
_monAdY = 0;
_timeCount = 0;
_needToDumpTimed = 0;
_loadingOrSave = 0;
_saveLoadPage = 0;
_currentSlot = 0;
_cursorPos = 0;
_colorPos = 0;
_fadeDirection = 0;
_numToFade = 0;
_fadeCount = 0;
_addToGreen = 0;
_addToRed = 0;
_addToBlue = 0;
_lastSoundReel = 0;
_lastHardKey = Common::KEYCODE_INVALID;
_bufferIn = 0;
_bufferOut = 0;
_blinkFrame = 23;
_blinkCount = 0;
_reAssesChanges = 0;
_pointersPath = 0;
_mansPath = 0;
_pointerFirstPath = 0;
_finalDest = 0;
_destination = 0;
_lineStartX = 0;
_lineStartY = 0;
_lineEndX = 0;
_lineEndY = 0;
_linePointer = 0;
_lineDirection = 0;
_lineLength = 0;
_subtitles = 0;
_foreignRelease = 0;
_wonGame = 0;
_hasSpeech = 0;
_roomsSample = 0;
_copyProtection = 0;
for (uint i = 0; i < 128; i++)
memset(&_setDat[i], 0, sizeof(SetObject));
for (uint i = 0; i < 80; i++)
memset(&_freeDat[i], 0, sizeof(DynObject));
for (uint i = 0; i < kNumExObjects; i++)
memset(&_exData[i], 0, sizeof(DynObject));
memset(&_vars, 0, sizeof(GameVars));
for (uint i = 0; i < 96; i++)
memset(&_backdropFlags[i], 0, sizeof(BackdropMapFlag));
for (uint i = 0; i < kNumReelRoutines+1; i++)
memset(&_reelRoutines[i], 0, sizeof(ReelRoutine));
_personData = 0;
for (uint i = 0; i < 16; i++)
memset(&_openInvList[i], 0, sizeof(ObjectRef));
for (uint i = 0; i < 30; i++)
memset(&_ryanInvList[i], 0, sizeof(ObjectRef));
for (uint i = 0; i < 11*10; i++)
memset(&_mapFlags[i], 0, sizeof(MapFlag));
for (uint i = 0; i < kNumChanges; i++)
memset(&_listOfChanges[i], 0, sizeof(Change));
_currentCharset = 0;
for (uint i = 0; i < 36; i++)
memset(&_pathData[i], 0, sizeof(RoomPaths));
}
DreamWebEngine::~DreamWebEngine() {
DebugMan.clearAllDebugChannels();
delete _console;
delete _sound;
}
static void vSyncInterrupt(void *refCon) {
DreamWebEngine *vm = (DreamWebEngine *)refCon;
if (!vm->isPaused()) {
vm->setVSyncInterrupt(true);
}
}
void DreamWebEngine::setVSyncInterrupt(bool flag) {
_vSyncInterrupt = flag;
}
void DreamWebEngine::waitForVSync() {
processEvents();
if (!_turbo) {
while (!_vSyncInterrupt) {
_system->delayMillis(10);
}
setVSyncInterrupt(false);
}
doShake();
doFade();
_system->updateScreen();
}
void DreamWebEngine::quit() {
_quitRequested = true;
_lastHardKey = Common::KEYCODE_ESCAPE;
}
void DreamWebEngine::processEvents() {
if (_eventMan->shouldQuit()) {
quit();
return;
}
_sound->soundHandler();
Common::Event event;
int softKey;
while (_eventMan->pollEvent(event)) {
switch(event.type) {
case Common::EVENT_RTL:
quit();
break;
case Common::EVENT_KEYDOWN:
if (event.kbd.flags & Common::KBD_CTRL) {
switch (event.kbd.keycode) {
case Common::KEYCODE_d:
_console->attach();
_console->onFrame();
break;
case Common::KEYCODE_f:
setSpeed(_speed != 20? 20: 1);
break;
case Common::KEYCODE_g:
_turbo = !_turbo;
break;
case Common::KEYCODE_c: //skip statue puzzle
_symbolBotNum = 3;
_symbolTopNum = 5;
break;
default:
break;
}
return; //do not pass ctrl + key to the engine
}
// Some parts of the code uses the hardware key
// code directly.
switch (event.kbd.keycode) {
case Common::KEYCODE_ESCAPE:
_lastHardKey = Common::KEYCODE_ESCAPE;
break;
case Common::KEYCODE_SPACE:
_lastHardKey = Common::KEYCODE_SPACE;
break;
default:
_lastHardKey = Common::KEYCODE_INVALID;
break;
}
// The rest of the keys are converted to ASCII. This
// is fairly restrictive, and eventually we may want
// to let through more keys. I think this is mostly to
// keep weird glyphs out of savegame names.
softKey = 0;
debug(1, "DreamWebEngine::processEvents() KeyDown keycode:%d ascii:0x%02x", event.kbd.keycode, event.kbd.ascii);
if ((event.kbd.ascii >= 'a' && event.kbd.ascii <= 'z') ||
(event.kbd.ascii >= 'A' && event.kbd.ascii <= 'Z')) {
softKey = event.kbd.ascii & ~0x20; // (& ~0x20) forces ascii codes for a-z to map to A-Z
} else if (event.kbd.ascii == '-' ||
event.kbd.ascii == ' ' ||
(event.kbd.ascii >= '0' && event.kbd.ascii <= '9')) {
softKey = event.kbd.ascii;
} else if (event.kbd.keycode >= Common::KEYCODE_KP0 && event.kbd.keycode <= Common::KEYCODE_KP9) {
softKey = event.kbd.keycode - Common::KEYCODE_KP0 + '0';
} else if (event.kbd.keycode == Common::KEYCODE_KP_MINUS) {
softKey = '-';
} else if (event.kbd.keycode == Common::KEYCODE_BACKSPACE ||
event.kbd.keycode == Common::KEYCODE_DELETE) {
softKey = 8;
} else if (event.kbd.keycode == Common::KEYCODE_RETURN
|| event.kbd.keycode == Common::KEYCODE_KP_ENTER) {
softKey = 13;
}
if (softKey)
keyPressed(softKey);
break;
default:
break;
}
}
}
Common::Error DreamWebEngine::run() {
syncSoundSettings();
_console = new DreamWebConsole(this);
_sound = new DreamWebSound(this);
ConfMan.registerDefault("originalsaveload", "false");
ConfMan.registerDefault("bright_palette", true);
_hasSpeech = Common::File::exists(_speechDirName + "/r01c0000.raw") && !ConfMan.getBool("speech_mute");
_brightPalette = ConfMan.getBool("bright_palette");
_copyProtection = ConfMan.getBool("copy_protection");
_timer->installTimerProc(vSyncInterrupt, 1000000 / 70, this, "dreamwebVSync");
dreamweb();
dreamwebFinalize();
_quitRequested = false;
_timer->removeTimerProc(vSyncInterrupt);
return Common::kNoError;
}
void DreamWebEngine::setSpeed(uint speed) {
debug(0, "setting speed %u", speed);
_speed = speed;
_timer->removeTimerProc(vSyncInterrupt);
_timer->installTimerProc(vSyncInterrupt, 1000000 / 70 / speed, this, "dreamwebVSync");
}
Common::String DreamWebEngine::getSavegameFilename(int slot) const {
// TODO: Are saves from all versions of Dreamweb compatible with each other?
// Then we can can consider keeping the filenames as DREAMWEB.Dnn.
// Otherwise, this must be changed to be target dependent.
//Common::String filename = _targetName + Common::String::format(".d%02d", savegameId);
Common::String filename = Common::String::format("DREAMWEB.D%02d", slot);
return filename;
}
void DreamWebEngine::keyPressed(uint16 ascii) {
debug(2, "key pressed = %04x", ascii);
uint16 in = (_bufferIn + 1) % ARRAYSIZE(_keyBuffer);
uint16 out = _bufferOut;
if (in == out) {
warning("keyboard buffer is full");
return;
}
_bufferIn = in;
_keyBuffer[in] = ascii;
}
void DreamWebEngine::getPalette(uint8 *data, uint start, uint count) {
_system->getPaletteManager()->grabPalette(data, start, count);
while (count--)
*data++ >>= 2;
}
void DreamWebEngine::setPalette(const uint8 *data, uint start, uint count) {
assert(start + count <= 256);
uint8 fixed[3*256];
for (uint i = 0; i < count * 3; ++i) {
fixed[i] = data[i] << 2;
}
_system->getPaletteManager()->setPalette(fixed, start, count);
}
void DreamWebEngine::blit(const uint8 *src, int pitch, int x, int y, int w, int h) {
if (y + h > (int)kScreenheight)
h = kScreenheight - y;
if (x + w > (int)kScreenwidth)
w = kScreenwidth - x;
if (h <= 0 || w <= 0)
return;
_system->copyRectToScreen(src, pitch, x, y, w, h);
}
void DreamWebEngine::printUnderMonitor() {
uint8 *dst = workspace() + kScreenwidth * 43 + 76;
Graphics::Surface *s = _system->lockScreen();
if (!s)
error("lockScreen failed");
for (uint y = 0; y < 104; ++y) {
uint8 *src = (uint8 *)s->getBasePtr(76, 43 + 8 + y);
for (uint x = 0; x < 170; ++x) {
if (*src < 231)
*dst++ = *src++;
else {
++dst; ++src;
}
}
dst += kScreenwidth - 170;
}
_system->unlockScreen();
}
void DreamWebEngine::cls() {
_system->fillScreen(0);
}
uint8 DreamWebEngine::modifyChar(uint8 c) const {
if (c < 128)
return c;
switch(getLanguage()) {
case Common::DE_DEU:
switch(c) {
case 129:
return 'Z' + 3;
case 132:
return 'Z' + 1;
case 142:
return 'Z' + 4;
case 154:
return 'Z' + 6;
case 225:
return 'A' - 1;
case 153:
return 'Z' + 5;
case 148:
return 'Z' + 2;
default:
return c;
}
case Common::ES_ESP:
switch(c) {
case 160:
return 'Z' + 1;
case 130:
return 'Z' + 2;
case 161:
return 'Z' + 3;
case 162:
return 'Z' + 4;
case 163:
return 'Z' + 5;
case 164:
return 'Z' + 6;
case 165:
return ',' - 1;
case 168:
return 'A' - 1;
case 173:
return 'A' - 4;
case 129:
return 'A' - 5;
default:
return c;
}
case Common::FR_FRA:
case Common::IT_ITA:
switch(c) {
case 133:
return 'Z' + 1;
case 130:
return 'Z' + 2;
case 138:
return 'Z' + 3;
case 136:
return 'Z' + 4;
case 140:
return 'Z' + 5;
case 135:
return 'Z' + 6;
case 149:
return ',' - 1;
case 131:
return ',' - 2;
case 141:
return ',' - 3;
case 139:
return ',' - 4;
case 151:
return 'A' - 1;
case 147:
return 'A' - 3;
case 150:
return 'A' - 4;
default:
return c;
}
default:
return c;
}
}
Common::String DreamWebEngine::modifyFileName(const char *name) {
Common::String fileName(name);
// Sanity check
if (!fileName.hasPrefix("DREAMWEB."))
return fileName;
// Make sure we use the correct file name as it differs depending on the game variant
fileName = _datafilePrefix;
fileName += name + 9;
return fileName;
}
bool DreamWebEngine::hasSpeech() {
return isCD() && _hasSpeech;
}
} // End of namespace DreamWeb