Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

5
engines/lab/POTFILES Normal file
View File

@@ -0,0 +1,5 @@
engines/lab/engine.cpp
engines/lab/metaengine.cpp
engines/lab/processroom.cpp
engines/lab/savegame.cpp
engines/lab/speciallocks.cpp

354
engines/lab/anim.cpp Normal file
View File

@@ -0,0 +1,354 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#include "common/file.h"
#include "lab/lab.h"
#include "lab/anim.h"
#include "lab/dispman.h"
#include "lab/eventman.h"
#include "lab/music.h"
#include "lab/utils.h"
namespace Lab {
Anim::Anim(LabEngine *vm) : _vm(vm) {
_lastBlockHeader = 0;
_numChunks = 1;
_headerdata._width = 0;
_headerdata._height = 0;
_headerdata._fps = 0;
_headerdata._flags = 0;
_delayMicros = 0;
_continuous = false;
_isPlaying = false;
_isAnim = false;
_isPal = false;
_noPalChange = false;
_donePal = false;
_frameNum = 0;
_playOnce = false;
_diffFile = nullptr;
_diffFileStart = 0;
_size = 0;
_scrollScreenBuffer = nullptr;
_waitForEffect = false;
_stopPlayingEnd = false;
_sampleSpeed = 0;
_doBlack = false;
for (int i = 0; i < 3 * 256; i++)
_diffPalette[i] = 0;
_outputBuffer = nullptr; // output to screen
}
Anim::~Anim() {
delete[] _vm->_anim->_scrollScreenBuffer;
_vm->_anim->_scrollScreenBuffer = nullptr;
}
void Anim::setOutputBuffer(byte *memoryBuffer) {
_outputBuffer = memoryBuffer;
}
uint16 Anim::getDIFFHeight() {
return _headerdata._height;
}
void Anim::diffNextFrame(bool onlyDiffData) {
if (_lastBlockHeader == 65535)
// Already done.
return;
bool drawOnScreen = false;
byte *startOfBuf = _outputBuffer;
int bufPitch = _vm->_graphics->_screenWidth;
if (!startOfBuf) {
startOfBuf = _vm->_graphics->getCurrentDrawingBuffer();
drawOnScreen = true;
}
byte *endOfBuf = startOfBuf + (int)_headerdata._width * _headerdata._height;
int curBit = 0;
while (1) {
byte *buf = startOfBuf + 0x10000 * curBit;
if (buf >= endOfBuf) {
if (!onlyDiffData) {
if (_headerdata._fps) {
uint32 targetMillis = _vm->_system->getMillis() + _delayMicros;
while (_vm->_system->getMillis() < targetMillis)
_vm->_system->delayMillis(10);
}
if (_isPal && !_noPalChange) {
_vm->_graphics->setPalette(_diffPalette, 256);
_isPal = false;
}
_donePal = true;
}
if (_isPal && !_noPalChange && !onlyDiffData && !_donePal) {
_vm->_graphics->setPalette(_diffPalette, 256);
_isPal = false;
}
_donePal = false;
_frameNum++;
if ((_frameNum == 1) && (_continuous || !_playOnce))
_diffFileStart = _diffFile->pos();
_isAnim = (_frameNum >= 3) && (!_playOnce);
if (drawOnScreen)
_vm->_graphics->screenUpdate();
// done with the next frame.
return;
}
_vm->updateEvents();
_lastBlockHeader = _diffFile->readUint32LE();
_size = _diffFile->readUint32LE();
uint32 curPos = 0;
switch (_lastBlockHeader) {
case 8:
_diffFile->read(_diffPalette, _size);
_isPal = true;
break;
case 10:
if (onlyDiffData) {
if (curBit > 0)
error("diffNextFrame: attempt to read screen to non-zero plane (%d)", curBit);
delete[] _scrollScreenBuffer;
_scrollScreenBuffer = new byte[_headerdata._width * _headerdata._height];
_diffFile->read(_scrollScreenBuffer, _size);
} else {
_diffFile->read(buf, _size);
}
curBit++;
break;
case 11:
curPos = _diffFile->pos();
_diffFile->skip(4);
_vm->_utils->runLengthDecode(buf, _diffFile);
curBit++;
_diffFile->seek(curPos + _size, SEEK_SET);
break;
case 12:
curPos = _diffFile->pos();
_diffFile->skip(4);
_vm->_utils->verticalRunLengthDecode(buf, _diffFile, bufPitch);
curBit++;
_diffFile->seek(curPos + _size, SEEK_SET);
break;
case 20:
curPos = _diffFile->pos();
_vm->_utils->unDiff(buf, buf, _diffFile, bufPitch, false);
curBit++;
_diffFile->seek(curPos + _size, SEEK_SET);
break;
case 21:
curPos = _diffFile->pos();
_vm->_utils->unDiff(buf, buf, _diffFile, bufPitch, true);
curBit++;
_diffFile->seek(curPos + _size, SEEK_SET);
break;
case 25:
case 26:
curBit++;
break;
case 30:
case 31:
if (_waitForEffect) {
while (_vm->_music->isSoundEffectActive()) {
_vm->updateEvents();
_vm->waitTOF();
}
_waitForEffect = false;
}
_size -= 8;
_diffFile->skip(4);
_sampleSpeed = _diffFile->readUint16LE();
_diffFile->skip(2);
// Sound effects embedded in animations are started here. These are
// usually animation-specific, like door opening sounds, and are not looped.
// The engine should wait for all such sounds to end.
_waitForEffect = true;
_vm->_music->playSoundEffect(_sampleSpeed, _size, false, _diffFile);
break;
case 65535:
if ((_frameNum == 1) || _playOnce || _stopPlayingEnd) {
bool didTOF = false;
if (_waitForEffect) {
while (_vm->_music->isSoundEffectActive()) {
_vm->updateEvents();
_vm->waitTOF();
if (drawOnScreen)
didTOF = true;
}
_waitForEffect = false;
}
_isPlaying = false;
if (!didTOF)
_vm->_graphics->screenUpdate();
return;
}
// Random frame number so it never gets back to 2
_frameNum = 4;
_diffFile->seek(_diffFileStart, SEEK_SET);
break;
default:
_diffFile->skip(_size);
break;
}
}
}
void Anim::stopDiff() {
if (_isPlaying && _isAnim)
_vm->_graphics->blackScreen();
}
void Anim::stopDiffEnd() {
if (!_isPlaying)
return;
_stopPlayingEnd = true;
while (_isPlaying) {
_vm->updateEvents();
diffNextFrame();
}
}
void Anim::readDiff(Common::File *diffFile, bool playOnce, bool onlyDiffData) {
_playOnce = playOnce;
_delayMicros = 0;
_frameNum = 0;
_numChunks = 1;
_donePal = false;
_stopPlayingEnd = false;
_isPlaying = true;
if (_doBlack) {
_doBlack = false;
_vm->_graphics->blackScreen();
}
_diffFile = diffFile;
_continuous = false;
if (!_diffFile)
return;
uint32 magicBytes = _diffFile->readUint32LE();
if (magicBytes != 1219009121) {
_isPlaying = false;
return;
}
uint32 signature3 = _diffFile->readUint32LE();
_size = _diffFile->readUint32LE();
if (signature3 != 0)
return;
// sizeof(headerdata) != 18, but the padding might be at the end
// 2 bytes, version, unused.
_diffFile->skip(2);
_headerdata._width = _diffFile->readUint16LE();
_headerdata._height = _diffFile->readUint16LE();
// 1 byte, depth, unused
_diffFile->skip(1);
_headerdata._fps = _diffFile->readByte();
// HACK: The original game defines a 1 second delay when changing screens, which is
// very annoying. We first removed the delay, but it looked wrong when changing screens
// as it was possible to see that something was displayed, without being able to tell
// what it was. A shorter delay (150ms) makes it acceptable during gameplay and
// readable. The big question is: do we need that message?
_vm->_system->delayMillis(150);
if (_headerdata._fps == 1)
_headerdata._fps = 0;
// 4 + 2 bytes, buffer size and machine, unused
_diffFile->skip(6);
_headerdata._flags = _diffFile->readUint32LE();
_diffFile->skip(_size - 18);
_continuous = CONTINUOUS & _headerdata._flags;
_vm->_utils->setBytesPerRow(_headerdata._width);
delete[] _scrollScreenBuffer;
_scrollScreenBuffer = nullptr;
if (_headerdata._fps)
_delayMicros = 1000 / _headerdata._fps;
_lastBlockHeader = signature3;
if (_playOnce) {
while (_lastBlockHeader != 65535)
diffNextFrame(onlyDiffData);
} else
diffNextFrame(onlyDiffData);
}
} // End of namespace Lab

102
engines/lab/anim.h Normal file
View File

@@ -0,0 +1,102 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#ifndef LAB_ANIM_H
#define LAB_ANIM_H
namespace Lab {
class LabEngine;
#define CONTINUOUS 0xFFFF
struct DIFFHeader {
uint16 _width;
uint16 _height;
char _fps;
uint32 _flags;
};
class Anim {
private:
LabEngine *_vm;
uint32 _lastBlockHeader;
uint16 _numChunks;
uint32 _delayMicros;
bool _continuous;
bool _isPlaying;
bool _isAnim;
bool _isPal;
bool _donePal;
uint16 _frameNum;
bool _playOnce;
Common::File *_diffFile;
uint32 _diffFileStart;
uint32 _size;
bool _stopPlayingEnd;
uint16 _sampleSpeed;
byte *_outputBuffer;
DIFFHeader _headerdata;
public:
Anim(LabEngine *vm);
~Anim();
char _diffPalette[256 * 3];
bool _waitForEffect; // Wait for each sound effect to finish before continuing.
bool _doBlack; // Black the screen before new picture
bool _noPalChange; // Don't change the palette.
byte *_scrollScreenBuffer;
/**
* Reads in a DIFF file.
*/
void setOutputBuffer(byte *memoryBuffer); // nullptr for output to screen
void readDiff(Common::File *diffFile, bool playOnce, bool onlyDiffData);
void diffNextFrame(bool onlyDiffData = false);
/**
* Stops an animation from running.
*/
void stopDiff();
/**
* Stops an animation from running.
*/
void stopDiffEnd();
uint16 getDIFFHeight();
bool isPlaying() const { return _isPlaying; }
};
} // End of namespace Lab
#endif // LAB_ANIM_H

View File

@@ -0,0 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
add_engine lab "Labyrinth of Time" yes

143
engines/lab/console.cpp Normal file
View File

@@ -0,0 +1,143 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "gui/debugger.h"
#include "lab/lab.h"
#include "lab/console.h"
#include "lab/dispman.h"
#include "lab/eventman.h"
#include "lab/music.h"
#include "lab/processroom.h"
#include "lab/resource.h"
#include "backends/keymapper/keymapper.h"
namespace Lab {
Console::Console(LabEngine *vm) : GUI::Debugger(), _vm(vm) {
registerCmd("scene", WRAP_METHOD(Console, Cmd_Scene));
registerCmd("scene_resources", WRAP_METHOD(Console, Cmd_DumpSceneResources));
registerCmd("find_action", WRAP_METHOD(Console, Cmd_FindAction));
}
Console::~Console() {
}
bool Console::Cmd_Scene(int argc, const char **argv) {
if (argc != 2) {
const char *directions[] = { "North", "South", "East", "West" };
debugPrintf("Current scene is %d, direction: %s\n", _vm->_roomNum, directions[_vm->getDirection()]);
debugPrintf("Use %s <scene number> to change the current scene\n", argv[0]);
return true;
}
_vm->_roomNum = atoi(argv[1]);
_vm->_music->checkRoomMusic(1, _vm->_roomNum);
_vm->_curFileName = " ";
_vm->_closeDataPtr = nullptr;
_vm->_mainDisplay = true;
_vm->_followingCrumbs = false;
_vm->_event->simulateEvent();
_vm->_graphics->_longWinInFront = false;
Common::Keymapper *keymapper = _vm->_system->getEventManager()->getKeymapper();
keymapper->disableAllGameKeymaps();
keymapper->getKeymap("lab-default")->setEnabled(true);
keymapper->getKeymap("game-shortcuts")->setEnabled(true);
keymapper->getKeymap("exit")->setEnabled(true);
return false;
}
bool Console::Cmd_DumpSceneResources(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Usage: %s <scene number> to dump the resources for a scene\n", argv[0]);
return true;
}
int scene = atoi(argv[1]);
_vm->_resource->readViews(scene);
RoomData *roomData = &_vm->_rooms[scene];
RuleList &rules = roomData->_rules;
const char *transitions[] = { "None", "Wipe", "ScrollWipe", "ScrollBlack", "ScrollBounce", "Transporter", "ReadFirstFrame", "ReadNextFrame" };
const char *ruleTypes[] = { "None", "Action", "Operate", "Go forward", "Conditions", "Turn", "Go main view", "Turn from to" };
const char *directions[] = { "", "North", "South", "East", "West" };
const char *actionTypes[] = {
"", "PlaySound", "PlaySoundLooping", "ShowDiff", "ShowDiffLooping", "LoadDiff", "LoadBitmap", "ShowBitmap", "Transition", "NoUpdate", "ForceUpdate",
"ShowCurPict", "SetElement", "UnsetElement", "ShowMessage", "ShowMessages", "ChangeRoom", "SetCloseup", "MainView", "SubInv", "AddInv", "ShowDir",
"WaitSecs", "StopMusic", "StartMusic", "ChangeMusic", "ResetMusic", "FillMusic", "WaitSound", "ClearSound", "WinMusic", "WinGame", "LostGame",
"ResetBuffer", "SpecialCmd", "CShowMessage", "PlaySoundNoWait"
};
debugPrintf("Room message: %s\n", roomData->_roomMsg.c_str());
debugPrintf("Transition: %s (%d)\n", transitions[roomData->_transitionType], roomData->_transitionType);
debugPrintf("Script:\n");
for (auto &rule : rules) {
debugPrintf("Rule type: %s", ruleTypes[rule._ruleType]);
if (rule._ruleType == kRuleTypeAction || rule._ruleType == kRuleTypeOperate)
debugPrintf(" (item %d, closeup %d)", rule._param1, rule._param2);
else if (rule._ruleType == kRuleTypeGoForward)
debugPrintf(" (%s)", directions[rule._param1]);
else if (rule._ruleType == kRuleTypeTurnFromTo)
debugPrintf(" (from %s to %s)", directions[rule._param1], directions[rule._param2]);
debugPrintf("\n");
for (auto &action : rule._actionList) {
debugPrintf(" - %s ('%s', %d, %d, %d)\n", actionTypes[action._actionType], action._messages[0].c_str(), action._param1, action._param2, action._param3);
}
}
return true;
}
bool Console::Cmd_FindAction(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("Usage: %s <action id> [param 1] [param 2] [param 3]\n", argv[0]);
return true;
}
int actionId = atoi(argv[1]);
int param1 = (argc > 2) ? atoi(argv[2]) : -1;
int param2 = (argc > 3) ? atoi(argv[3]) : -1;
int param3 = (argc > 4) ? atoi(argv[4]) : -1;
for (int i = 1; i <= _vm->_manyRooms; i++) {
_vm->_resource->readViews(i);
for (auto &rule: _vm->_rooms[i]._rules) {
for (auto &action : rule._actionList) {
if (action._actionType == actionId &&
(action._param1 == param1 || param1 == -1) &&
(action._param2 == param2 || param2 == -1) &&
(action._param3 == param3 || param3 == -1)) {
debugPrintf("Found at script %d\n", i);
}
}
}
}
return true;
}
} // End of namespace Neverhood

45
engines/lab/console.h Normal file
View File

@@ -0,0 +1,45 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef LAB_CONSOLE_H
#define LAB_CONSOLE_H
#include "gui/debugger.h"
namespace Lab {
class LabEngine;
class Console : public GUI::Debugger {
public:
Console(LabEngine *vm);
~Console(void) override;
private:
LabEngine *_vm;
bool Cmd_Scene(int argc, const char **argv);
bool Cmd_DumpSceneResources(int argc, const char **argv);
bool Cmd_FindAction(int argc, const char **argv);
};
} // End of namespace Lab
#endif

6
engines/lab/credits.pl Normal file
View File

@@ -0,0 +1,6 @@
begin_section("Lab");
add_person("Arnaud Boutonn&eacute;", "Strangerke", "");
add_person("Filippos Karapetis", "bluegr", "");
add_person("Willem Jan Palenstijn", "wjp", "");
add_person("Eugene Sandulenko", "sev", "");
end_section();

118
engines/lab/detection.cpp Normal file
View File

@@ -0,0 +1,118 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#include "engines/advancedDetector.h"
#include "lab/detection.h"
static const PlainGameDescriptor lab_setting[] = {
{ "lab", "The Labyrinth of Time" },
{ nullptr, nullptr }
};
static const ADGameDescription labDescriptions[] = {
{
"lab",
"",
AD_ENTRY2s("doors", "d77536010e7e5ae17ee066323ceb9585", 2537, // game/doors
"noteold.fon", "6c1d90ad55149556e79d3f7bfddb4bd7", 9252), // game/spict/noteold.fon
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
{
"lab",
"Lowres",
AD_ENTRY2s("doors", "d77536010e7e5ae17ee066323ceb9585", 2537, // game/doors
"64b", "3a84d41bcc6a782f22e8e954bce09721", 39916), // game/pict/h2/64b
Common::EN_ANY,
Common::kPlatformDOS,
Lab::GF_LOWRES | ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
{
"lab",
"Rerelease",
{
{ "doors", 0, "d77536010e7e5ae17ee066323ceb9585", 2537 }, // game/doors
{ "noteold.fon", 0, "6c1d90ad55149556e79d3f7bfddb4bd7", 9252 }, // game/spict/noteold.fon
{ "wyrmkeep",0, "97c7064c54c28b952d37c4ebff6efa50", 52286 }, // game/spict/intro
{ nullptr, 0, nullptr, 0 }
},
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
{
"lab",
"",
AD_ENTRY1s("doors", "7bf458df6ec30cc8ef4665e4d7c77f59", 2537), // game/doors
Common::EN_ANY,
Common::kPlatformAmiga,
Lab::GF_LOWRES | ADGF_UNSTABLE,
GUIO1(GUIO_NOMIDI)
},
AD_TABLE_END_MARKER
};
static const char *const directoryGlobs[] = {
"game",
"pict",
"spict",
"rooms",
"h2",
"intro",
nullptr
};
class LabMetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
public:
LabMetaEngineDetection() : AdvancedMetaEngineDetection(labDescriptions, lab_setting) {
_maxScanDepth = 4;
_directoryGlobs = directoryGlobs;
_flags = kADFlagUseExtraAsHint;
}
const char *getName() const override {
return "lab";
}
const char *getEngineName() const override {
return "Labyrinth of Time";
}
const char *getOriginalCopyright() const override {
return "The Labyrinth of Time (C) 2004 The Wyrmkeep Entertainment Co. and Terra Nova Development";
}
};
REGISTER_PLUGIN_STATIC(LAB_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, LabMetaEngineDetection);

34
engines/lab/detection.h Normal file
View File

@@ -0,0 +1,34 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef LAB_DETECTION_H
#define LAB_DETECTION_H
namespace Lab {
enum GameFeatures {
GF_LOWRES = 1 << 0,
GF_WINDOWS_TRIAL = 1 << 1
};
} // End of namespace Lab
#endif // LAB_DETECTION_H

958
engines/lab/dispman.cpp Normal file
View File

@@ -0,0 +1,958 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#include "common/file.h"
#include "graphics/paletteman.h"
#include "lab/lab.h"
#include "lab/anim.h"
#include "lab/dispman.h"
#include "lab/eventman.h"
#include "lab/music.h"
#include "lab/image.h"
#include "lab/interface.h"
#include "lab/resource.h"
#include "lab/utils.h"
namespace Lab {
DisplayMan::DisplayMan(LabEngine *vm) : _vm(vm) {
_longWinInFront = false;
_lastMessageLong = false;
_actionMessageShown = false;
_screenBytesPerPage = 0;
_curBitmap = nullptr;
_displayBuffer = nullptr;
_currentDisplayBuffer = nullptr;
_fadePalette = nullptr;
_screenWidth = 0;
_screenHeight = 0;
for (int i = 0; i < 256 * 3; i++)
_curVgaPal[i] = 0;
}
DisplayMan::~DisplayMan() {
freePict();
delete[] _displayBuffer;
}
void DisplayMan::loadPict(const Common::String &filename) {
freePict();
_curBitmap = _vm->_resource->openDataFile(filename, MKTAG('D', 'I', 'F', 'F'));
}
void DisplayMan::loadBackPict(const Common::String &fileName, uint16 *highPal) {
_fadePalette = highPal;
_vm->_anim->_noPalChange = true;
readPict(fileName);
for (int i = 0; i < 16; i++) {
highPal[i] = ((_vm->_anim->_diffPalette[i * 3] >> 2) << 8) +
((_vm->_anim->_diffPalette[i * 3 + 1] >> 2) << 4) +
((_vm->_anim->_diffPalette[i * 3 + 2] >> 2));
}
_vm->_anim->_noPalChange = false;
}
void DisplayMan::readPict(const Common::String &filename, bool playOnce, bool onlyDiffData, byte *memoryBuffer) {
_vm->_anim->stopDiff();
loadPict(filename);
_vm->_anim->setOutputBuffer(memoryBuffer);
_vm->_anim->readDiff(_curBitmap, playOnce, onlyDiffData);
}
void DisplayMan::freePict() {
delete _curBitmap;
_curBitmap = nullptr;
}
Common::String DisplayMan::getWord(const char *mainBuffer) {
Common::String result;
for (int i = 0; mainBuffer[i] && (mainBuffer[i] != ' ') && (mainBuffer[i] != '\n'); i++)
result += mainBuffer[i];
return result;
}
Common::String DisplayMan::getLine(TextFont *tf, const char **mainBuffer, uint16 lineWidth) {
uint16 curWidth = 0;
Common::String result;
while ((*mainBuffer)[0]) {
Common::String wordBuffer = getWord(*mainBuffer);
if ((curWidth + textLength(tf, wordBuffer)) <= lineWidth) {
result += wordBuffer;
(*mainBuffer) += wordBuffer.size();
// end of line
if ((*mainBuffer)[0] == '\n') {
(*mainBuffer)++;
break;
}
// append any space after the word
if ((*mainBuffer)[0]) {
result += (*mainBuffer)[0];
(*mainBuffer)++;
}
curWidth = textLength(tf, result);
} else
break;
}
return result;
}
int DisplayMan::flowText(TextFont *font, int16 spacing, byte penColor, byte backPen,
bool fillBack, bool centerh, bool centerv, bool output, Common::Rect textRect, const char *str, Image *targetImage) {
byte *saveDisplayBuffer = _currentDisplayBuffer;
if (targetImage) {
_currentDisplayBuffer = targetImage->_imageData;
assert(_screenBytesPerPage == (uint32)(targetImage->_width * targetImage->_height));
}
if (fillBack)
rectFill(textRect, backPen);
if (!str)
return 0;
const char *orig = str;
TextFont *msgFont = font;
uint16 fontHeight = textHeight(msgFont) + spacing;
uint16 numLines = (textRect.height() + 1) / fontHeight;
uint16 width = textRect.width() + 1;
uint16 y = textRect.top;
if (centerv && output) {
const char *temp = str;
uint16 actlines = 0;
while (temp[0]) {
getLine(msgFont, &temp, width);
actlines++;
}
if (actlines <= numLines)
y += ((textRect.height() + 1) - (actlines * fontHeight)) / 2;
}
while (numLines && str[0]) {
Common::String lineBuffer;
lineBuffer = getLine(msgFont, &str, width);
uint16 x = textRect.left;
if (centerh)
x += (width - textLength(msgFont, lineBuffer)) / 2;
if (output)
drawText(msgFont, x, y, penColor, lineBuffer);
numLines--;
y += fontHeight;
}
_currentDisplayBuffer = saveDisplayBuffer;
return (str - orig);
}
void DisplayMan::createBox(uint16 y2) {
// Message box area
rectFillScaled(4, 154, 315, y2 - 2, 7);
// Box around message area
drawHLine(_vm->_utils->vgaScaleX(2), _vm->_utils->vgaScaleY(152), _vm->_utils->vgaScaleX(317), 0);
drawVLine(_vm->_utils->vgaScaleX(317), _vm->_utils->vgaScaleY(152), _vm->_utils->vgaScaleY(y2), 0);
drawHLine(_vm->_utils->vgaScaleX(2), _vm->_utils->vgaScaleY(y2), _vm->_utils->vgaScaleX(317), 0);
drawVLine(_vm->_utils->vgaScaleX(2), _vm->_utils->vgaScaleY(152), _vm->_utils->vgaScaleY(y2), 0);
}
int DisplayMan::longDrawMessage(Common::String str, bool isActionMessage) {
if (isActionMessage) {
_actionMessageShown = true;
} else if (_actionMessageShown) {
_actionMessageShown = false;
return 0;
}
if (str.empty())
return 0;
_vm->_interface->attachButtonList(nullptr);
if (!_longWinInFront) {
_longWinInFront = true;
// Clear Area
rectFill(0, _vm->_utils->vgaScaleY(149) + _vm->_utils->svgaCord(2), _vm->_utils->vgaScaleX(319), _vm->_utils->vgaScaleY(199), 3);
}
createBox(198);
return flowText(_vm->_msgFont, 0, 1, 7, false, true, true, true, _vm->_utils->vgaRectScale(6, 155, 313, 195), str.c_str());
}
void DisplayMan::drawMessage(Common::String str, bool isActionMessage) {
if (isActionMessage) {
_actionMessageShown = true;
} else if (_actionMessageShown) {
_actionMessageShown = false;
return;
}
if (str.empty())
return;
if ((textLength(_vm->_msgFont, str) > _vm->_utils->vgaScaleX(306))) {
longDrawMessage(str, isActionMessage);
_lastMessageLong = true;
} else {
if (_longWinInFront) {
_longWinInFront = false;
drawPanel();
}
createBox(168);
drawText(_vm->_msgFont, _vm->_utils->vgaScaleX(7), _vm->_utils->vgaScaleY(155) + _vm->_utils->svgaCord(2), 1, str);
_lastMessageLong = false;
}
}
void DisplayMan::drawPanel() {
// Clear Area
rectFill(0, _vm->_utils->vgaScaleY(149) + _vm->_utils->svgaCord(2), _vm->_utils->vgaScaleX(319), _vm->_utils->vgaScaleY(199), 3);
// First Line
drawHLine(0, _vm->_utils->vgaScaleY(149) + _vm->_utils->svgaCord(2), _vm->_utils->vgaScaleX(319), 0);
// Second Line
drawHLine(0, _vm->_utils->vgaScaleY(149) + 1 + _vm->_utils->svgaCord(2), _vm->_utils->vgaScaleX(319), 5);
// Button Separators
drawHLine(0, _vm->_utils->vgaScaleY(170), _vm->_utils->vgaScaleX(319), 0);
if (!_vm->_alternate) {
// The horizontal lines under the black one
drawHLine(0, _vm->_utils->vgaScaleY(170) + 1, _vm->_utils->vgaScaleX(319), 4);
_vm->_interface->drawButtonList(&_vm->_moveButtonList);
} else {
if (_vm->getPlatform() != Common::kPlatformWindows) {
// Vertical Black lines
drawVLine(_vm->_utils->vgaScaleX(124), _vm->_utils->vgaScaleY(170) + 1, _vm->_utils->vgaScaleY(199), 0);
drawVLine(_vm->_utils->vgaScaleX(194), _vm->_utils->vgaScaleY(170) + 1, _vm->_utils->vgaScaleY(199), 0);
} else {
// Vertical Black lines
drawVLine(_vm->_utils->vgaScaleX(90), _vm->_utils->vgaScaleY(170) + 1, _vm->_utils->vgaScaleY(199), 0);
drawVLine(_vm->_utils->vgaScaleX(160), _vm->_utils->vgaScaleY(170) + 1, _vm->_utils->vgaScaleY(199), 0);
drawVLine(_vm->_utils->vgaScaleX(230), _vm->_utils->vgaScaleY(170) + 1, _vm->_utils->vgaScaleY(199), 0);
}
// The horizontal lines under the black one
drawHLine(0, _vm->_utils->vgaScaleY(170) + 1, _vm->_utils->vgaScaleX(122), 4);
drawHLine(_vm->_utils->vgaScaleX(126), _vm->_utils->vgaScaleY(170) + 1, _vm->_utils->vgaScaleX(192), 4);
drawHLine(_vm->_utils->vgaScaleX(196), _vm->_utils->vgaScaleY(170) + 1, _vm->_utils->vgaScaleX(319), 4);
// The vertical high light lines
drawVLine(_vm->_utils->vgaScaleX(1), _vm->_utils->vgaScaleY(170) + 2, _vm->_utils->vgaScaleY(198), 4);
if (_vm->getPlatform() != Common::kPlatformWindows) {
drawVLine(_vm->_utils->vgaScaleX(126), _vm->_utils->vgaScaleY(170) + 2, _vm->_utils->vgaScaleY(198), 4);
drawVLine(_vm->_utils->vgaScaleX(196), _vm->_utils->vgaScaleY(170) + 2, _vm->_utils->vgaScaleY(198), 4);
} else {
drawVLine(_vm->_utils->vgaScaleX(92), _vm->_utils->vgaScaleY(170) + 2, _vm->_utils->vgaScaleY(198), 4);
drawVLine(_vm->_utils->vgaScaleX(162), _vm->_utils->vgaScaleY(170) + 2, _vm->_utils->vgaScaleY(198), 4);
drawVLine(_vm->_utils->vgaScaleX(232), _vm->_utils->vgaScaleY(170) + 2, _vm->_utils->vgaScaleY(198), 4);
}
_vm->_interface->drawButtonList(&_vm->_invButtonList);
}
}
void DisplayMan::setUpScreens() {
Interface *i = _vm->_interface;
ButtonList *moveButtonList = &_vm->_moveButtonList;
ButtonList *invButtonList = &_vm->_invButtonList;
Image **moveImages = _vm->_moveImages;
Image **invImages = _vm->_invImages;
createScreen(_vm->_isHiRes);
// TODO: The CONTROL file is not present in the Amiga version
Common::File *controlFile = _vm->_resource->openDataFile("P:Control");
for (int j = 0; j < 20; j++)
_vm->_moveImages[j] = new Image(controlFile, _vm);
delete controlFile;
// Creates the buttons for the movement control panel
// The key mapping was only set for the Windows version.
// It's very convenient to have those shortcut, so I added them
// for all versions. (Strangerke)
uint16 y = _vm->_utils->vgaScaleY(173) - _vm->_utils->svgaCord(2);
moveButtonList->push_back(i->createButton( 1, y, 0, kActionTake, moveImages[0], moveImages[1]));
moveButtonList->push_back(i->createButton( 33, y, 1, kActionMove, moveImages[2], moveImages[3]));
moveButtonList->push_back(i->createButton( 65, y, 2, kActionOpen, moveImages[4], moveImages[5]));
moveButtonList->push_back(i->createButton( 97, y, 3, kActionClose, moveImages[6], moveImages[7]));
moveButtonList->push_back(i->createButton(129, y, 4, kActionLook, moveImages[8], moveImages[9]));
moveButtonList->push_back(i->createButton(161, y, 5, kActionInv, moveImages[12], moveImages[13]));
moveButtonList->push_back(i->createButton(193, y, 6, kActionLeft, moveImages[14], moveImages[15]));
moveButtonList->push_back(i->createButton(225, y, 7, kActionForward, moveImages[16], moveImages[17]));
moveButtonList->push_back(i->createButton(257, y, 8, kActionRight, moveImages[18], moveImages[19]));
moveButtonList->push_back(i->createButton(289, y, 9, kActionMap, moveImages[10], moveImages[11]));
// TODO: The INV file is not present in the Amiga version
Common::File *invFile = _vm->_resource->openDataFile("P:Inv");
if (_vm->getPlatform() == Common::kPlatformWindows) {
for (int imgIdx = 0; imgIdx < 10; imgIdx++)
_vm->_invImages[imgIdx] = new Image(invFile, _vm);
} else {
for (int imgIdx = 0; imgIdx < 6; imgIdx++)
_vm->_invImages[imgIdx] = new Image(invFile, _vm);
}
if (_vm->getPlatform() == Common::kPlatformWindows) {
invButtonList->push_back(i->createButton( 24, y, 0, kActionMainDisplay, invImages[0], invImages[1]));
invButtonList->push_back(i->createButton( 56, y, 1, kActionSaveLoad, invImages[2], invImages[3]));
invButtonList->push_back(i->createButton( 94, y, 2, kActionUse, invImages[4], invImages[5]));
invButtonList->push_back(i->createButton(126, y, 3, kActionInvLook, moveImages[8], moveImages[9]));
invButtonList->push_back(i->createButton(164, y, 4, kActionPrev, moveImages[14], moveImages[15]));
invButtonList->push_back(i->createButton(196, y, 5, kActionNext, moveImages[18], moveImages[19]));
// The windows version has 2 extra buttons for breadcrumb trail
// CHECKME: the game is really hard to play without those, maybe we could add something to enable that.
invButtonList->push_back(i->createButton(234, y, 6, kActionDropBreadcrumb, invImages[6], invImages[7]));
invButtonList->push_back(i->createButton(266, y, 7, kActionFollowBreadcrumbs, invImages[8], invImages[9]));
} else {
invButtonList->push_back(i->createButton( 58, y, 0, kActionMainDisplay, invImages[0], invImages[1]));
invButtonList->push_back(i->createButton( 90, y, 1, kActionSaveLoad, invImages[2], invImages[3]));
invButtonList->push_back(i->createButton(128, y, 2, kActionUse, invImages[4], invImages[5]));
invButtonList->push_back(i->createButton(160, y, 3, kActionInvLook, moveImages[8], moveImages[9]));
invButtonList->push_back(i->createButton(198, y, 4, kActionPrev, moveImages[14], moveImages[15]));
invButtonList->push_back(i->createButton(230, y, 5, kActionNext, moveImages[18], moveImages[19]));
}
delete invFile;
}
void DisplayMan::rectFill(Common::Rect fillRect, byte color) {
int width = fillRect.width() + 1;
int height = fillRect.height() + 1;
if (fillRect.left + width > _screenWidth)
width = _screenWidth - fillRect.left;
if (fillRect.top + height > _screenHeight)
height = _screenHeight - fillRect.top;
if ((width > 0) && (height > 0)) {
byte *d = getCurrentDrawingBuffer() + fillRect.top * _screenWidth + fillRect.left;
while (height-- > 0) {
byte *dd = d;
int ww = width;
while (ww-- > 0) {
*dd++ = color;
}
d += _screenWidth;
}
}
}
void DisplayMan::rectFill(uint16 x1, uint16 y1, uint16 x2, uint16 y2, byte color) {
rectFill(Common::Rect(x1, y1, x2, y2), color);
}
void DisplayMan::rectFillScaled(uint16 x1, uint16 y1, uint16 x2, uint16 y2, byte color) {
rectFill(_vm->_utils->vgaRectScale(x1, y1, x2, y2), color);
}
void DisplayMan::drawVLine(uint16 x, uint16 y1, uint16 y2, byte color) {
rectFill(x, y1, x, y2, color);
}
void DisplayMan::drawHLine(uint16 x1, uint16 y, uint16 x2, byte color) {
rectFill(x1, y, x2, y, color);
}
void DisplayMan::screenUpdate() {
_vm->_event->processInput();
_vm->_system->copyRectToScreen(_displayBuffer, _screenWidth, 0, 0, _screenWidth, _screenHeight);
_vm->_system->updateScreen();
}
void DisplayMan::createScreen(bool hiRes) {
if (hiRes) {
_screenWidth = 640;
_screenHeight = 480;
} else {
_screenWidth = 320;
_screenHeight = 200;
}
_screenBytesPerPage = _screenWidth * _screenHeight;
if (_displayBuffer)
delete[] _displayBuffer;
_displayBuffer = new byte[_screenBytesPerPage]();
}
void DisplayMan::setAmigaPal(uint16 *pal) {
byte vgaPal[16 * 3];
uint16 vgaIdx = 0;
for (int i = 0; i < 16; i++) {
vgaPal[vgaIdx++] = (byte)(((pal[i] & 0xf00) >> 8) << 2);
vgaPal[vgaIdx++] = (byte)(((pal[i] & 0x0f0) >> 4) << 2);
vgaPal[vgaIdx++] = (byte)(((pal[i] & 0x00f)) << 2);
}
writeColorRegs(vgaPal, 0, 16);
}
void DisplayMan::writeColorRegs(byte *buf, uint16 first, uint16 numReg) {
assert(first + numReg <= 256);
byte tmp[256 * 3];
for (int i = 0; i < numReg * 3; i++)
tmp[i] = (buf[i] << 2) | (buf[i] >> 4); // better results than buf[i] * 4
_vm->_system->getPaletteManager()->setPalette(tmp, first, numReg);
memcpy(&(_curVgaPal[first * 3]), buf, numReg * 3);
}
void DisplayMan::setPalette(void *newPal, uint16 numColors) {
if (memcmp(newPal, _curVgaPal, numColors * 3) != 0)
writeColorRegs((byte *)newPal, 0, numColors);
}
byte *DisplayMan::getCurrentDrawingBuffer() {
if (_currentDisplayBuffer)
return _currentDisplayBuffer;
return _displayBuffer;
}
void DisplayMan::checkerBoardEffect(uint16 penColor, uint16 x1, uint16 y1, uint16 x2, uint16 y2) {
int w = x2 - x1 + 1;
int h = y2 - y1 + 1;
if (x1 + w > _screenWidth)
w = _screenWidth - x1;
if (y1 + h > _screenHeight)
h = _screenHeight - y1;
if ((w > 0) && (h > 0)) {
byte *d = getCurrentDrawingBuffer() + y1 * _screenWidth + x1;
while (h-- > 0) {
byte *dd = d;
int ww = w;
if (y1 & 1) {
dd++;
ww--;
}
while (ww > 0) {
*dd = penColor;
dd += 2;
ww -= 2;
}
d += _screenWidth;
y1++;
}
}
}
void DisplayMan::freeFont(TextFont **font) {
if (*font) {
if ((*font)->_data)
delete[] (*font)->_data;
delete *font;
*font = nullptr;
}
}
uint16 DisplayMan::textLength(TextFont *font, const Common::String &text) {
uint16 length = 0;
if (font) {
int numChars = text.size();
for (int i = 0; i < numChars; i++) {
length += font->_widths[(byte)text[i]];
}
}
return length;
}
uint16 DisplayMan::textHeight(TextFont *tf) {
return (tf) ? tf->_height : 0;
}
void DisplayMan::drawText(TextFont *tf, uint16 x, uint16 y, uint16 color, const Common::String &text) {
byte *vgaTop = getCurrentDrawingBuffer();
int numChars = text.size();
for (int i = 0; i < numChars; i++) {
uint32 realOffset = (_screenWidth * y) + x;
uint16 curPage = realOffset / _screenBytesPerPage;
uint32 segmentOffset = realOffset - (curPage * _screenBytesPerPage);
int32 leftInSegment = _screenBytesPerPage - segmentOffset;
byte *vgaCur = vgaTop + segmentOffset;
if (tf->_widths[(byte)text[i]]) {
byte *cdata = tf->_data + tf->_offsets[(byte)text[i]];
uint16 bwidth = *cdata++;
byte *vgaTemp = vgaCur;
byte *vgaTempLine = vgaCur;
for (int rows = 0; rows < tf->_height; rows++) {
int32 templeft = leftInSegment;
vgaTemp = vgaTempLine;
for (int cols = 0; cols < bwidth; cols++) {
uint16 data = *cdata++;
if (data && (templeft >= 8)) {
for (int j = 7; j >= 0; j--) {
if ((1 << j) & data)
*vgaTemp = color;
vgaTemp++;
}
templeft -= 8;
} else if (data) {
uint16 mask = 0x80;
templeft = leftInSegment;
for (int counterb = 0; counterb < 8; counterb++) {
if (templeft <= 0) {
curPage++;
vgaTemp = vgaTop - templeft;
// Set up VGATempLine for next line
vgaTempLine -= _screenBytesPerPage;
// Set up LeftInSegment for next line
leftInSegment += _screenBytesPerPage + templeft;
templeft += _screenBytesPerPage;
}
if (mask & data)
*vgaTemp = color;
vgaTemp++;
mask = mask >> 1;
templeft--;
}
} else {
templeft -= 8;
vgaTemp += 8;
}
}
vgaTempLine += _screenWidth;
leftInSegment -= _screenWidth;
if (leftInSegment <= 0) {
curPage++;
vgaTempLine -= _screenBytesPerPage;
leftInSegment += _screenBytesPerPage;
}
}
}
x += tf->_widths[(byte)text[i]];
}
}
void DisplayMan::doScrollBlack() {
uint16 width = _vm->_utils->vgaScaleX(320);
uint16 height = _vm->_utils->vgaScaleY(149) + _vm->_utils->svgaCord(2);
_vm->_event->mouseHide();
byte *mem = new byte[width * height];
int16 by = _vm->_utils->vgaScaleX(4);
int16 verticalScroll = height;
while (verticalScroll > 0) {
scrollDisplayY(-by, 0, 0, width - 1, height - 1, mem);
verticalScroll -= by;
_vm->updateEvents();
_vm->waitTOF();
}
delete[] mem;
_vm->_event->mouseShow();
}
void DisplayMan::copyPage(uint16 width, uint16 height, uint16 nheight, uint16 startLine, byte *mem) {
byte *baseAddr = getCurrentDrawingBuffer();
uint32 size = (int32)(height - nheight) * (int32)width;
mem += startLine * width;
uint16 curPage = ((int32)nheight * (int32)width) / _screenBytesPerPage;
uint32 offSet = ((int32)nheight * (int32)width) - (curPage * _screenBytesPerPage);
while (size) {
uint32 copySize;
if (size > (_screenBytesPerPage - offSet))
copySize = _screenBytesPerPage - offSet;
else
copySize = size;
size -= copySize;
memcpy(baseAddr + (offSet >> 2), mem, copySize);
mem += copySize;
curPage++;
offSet = 0;
}
}
void DisplayMan::doScrollWipe(const Common::String &filename) {
_vm->_event->mouseHide();
uint16 width = _vm->_utils->vgaScaleX(320);
uint16 height = _vm->_utils->vgaScaleY(149) + _vm->_utils->svgaCord(2);
while (_vm->_music->isSoundEffectActive()) {
_vm->updateEvents();
_vm->waitTOF();
}
readPict(filename, true, true);
setPalette(_vm->_anim->_diffPalette, 256);
byte *mem = _vm->_anim->_scrollScreenBuffer;
_vm->updateEvents();
uint16 by = _vm->_utils->vgaScaleX(3);
uint16 nheight = height;
uint16 startLine = 0, onRow = 0;
while (onRow < _vm->_anim->getDIFFHeight()) {
_vm->updateEvents();
if ((by > nheight) && nheight)
by = nheight;
if ((startLine + by) > (_vm->_anim->getDIFFHeight() - height - 1))
break;
if (nheight)
nheight -= by;
copyPage(width, height, nheight, startLine, mem);
screenUpdate();
if (!nheight)
startLine += by;
onRow += by;
if (nheight <= (height / 4))
by = _vm->_utils->vgaScaleX(5);
else if (nheight <= (height / 3))
by = _vm->_utils->vgaScaleX(4);
else if (nheight <= (height / 2))
by = _vm->_utils->vgaScaleX(3);
}
_vm->_event->mouseShow();
}
void DisplayMan::doScrollBounce() {
const uint16 offsets[8] = { 3, 3, 2, 2, 2, 1, 1, 1 };
const int multiplier = (_vm->_isHiRes) ? 2 : 1;
_vm->_event->mouseHide();
int width = _vm->_utils->vgaScaleX(320);
int height = _vm->_utils->vgaScaleY(149) + _vm->_utils->svgaCord(2);
byte *mem = _vm->_anim->_scrollScreenBuffer;
_vm->updateEvents();
int startLine = _vm->_anim->getDIFFHeight() - height - 1;
for (int i = 0; i < 5; i++) {
_vm->updateEvents();
startLine -= (5 - i) * multiplier;
copyPage(width, height, 0, startLine, mem);
_vm->waitTOF();
}
for (int i = 8; i > 0; i--) {
_vm->updateEvents();
startLine += offsets[i - 1] * multiplier;
copyPage(width, height, 0, startLine, mem);
_vm->waitTOF();
}
_vm->_event->mouseShow();
}
void DisplayMan::doTransWipe(const Common::String &filename) {
uint16 lastY, linesLast;
if (_vm->_isHiRes) {
linesLast = 3;
lastY = 358;
} else {
linesLast = 1;
lastY = 148;
}
uint16 linesDone = 0;
for (int j = 0; j < 2; j++) {
for (int i = 0; i < 2; i++) {
uint16 curY = i * 2;
while (curY < lastY) {
if (linesDone >= linesLast) {
_vm->updateEvents();
_vm->waitTOF();
linesDone = 0;
}
if (j == 0)
checkerBoardEffect(0, 0, curY, _screenWidth - 1, curY + 1);
else
rectFill(0, curY, _screenWidth - 1, curY + 1, 0);
curY += 4;
linesDone++;
} // while
} // for i
} // for j
if (filename.empty())
_vm->_curFileName = _vm->getPictName(true);
else if (filename[0] > ' ')
_vm->_curFileName = filename;
else
_vm->_curFileName = _vm->getPictName(true);
byte *bitMapBuffer = new byte[_screenWidth * (lastY + 5)];
readPict(_vm->_curFileName, true, false, bitMapBuffer);
setPalette(_vm->_anim->_diffPalette, 256);
Image imgSource(_vm);
imgSource._width = _screenWidth;
imgSource._height = lastY;
imgSource.setData(bitMapBuffer, true);
Image imgDest(_vm);
imgDest._width = _screenWidth;
imgDest._height = _screenHeight;
imgDest.setData(getCurrentDrawingBuffer(), false);
for (int j = 0; j < 2; j++) {
for (int i = 0; i < 2; i++) {
uint16 curY = i * 2;
while (curY < lastY) {
if (linesDone >= linesLast) {
_vm->updateEvents();
_vm->waitTOF();
linesDone = 0;
}
imgDest.setData(getCurrentDrawingBuffer(), false);
if (j == 0) {
imgSource.blitBitmap(0, curY, &imgDest, 0, curY, _screenWidth, 2, false);
checkerBoardEffect(0, 0, curY, _screenWidth - 1, curY + 1);
} else {
uint16 bitmapHeight = (curY == lastY) ? 1 : 2;
imgSource.blitBitmap(0, curY, &imgDest, 0, curY, _screenWidth, bitmapHeight, false);
}
curY += 4;
linesDone++;
} // while
} // for i
} // for j
// bitMapBuffer will be deleted by the Image destructor
}
void DisplayMan::doTransition(TransitionType transitionType, const Common::String &filename) {
switch (transitionType) {
case kTransitionWipe:
case kTransitionTransporter:
doTransWipe(filename);
break;
case kTransitionScrollWipe: // only used in scene 7 (street, when teleporting to the surreal maze)
doScrollWipe(filename);
break;
case kTransitionScrollBlack: // only used in scene 7 (street, when teleporting to the surreal maze)
doScrollBlack();
break;
case kTransitionScrollBounce: // only used in scene 7 (street, when teleporting to the surreal maze)
doScrollBounce();
break;
case kTransitionReadFirstFrame: // only used in scene 7 (street, when teleporting to the surreal maze)
readPict(filename, false);
break;
case kTransitionReadNextFrame: // only used in scene 7 (street, when teleporting to the surreal maze)
_vm->_anim->diffNextFrame();
break;
case kTransitionNone:
default:
break;
}
}
void DisplayMan::blackScreen() {
byte pal[256 * 3];
memset(pal, 0, 248 * 3);
writeColorRegs(pal, 8, 248);
_vm->_system->delayMillis(32);
}
void DisplayMan::whiteScreen() {
byte pal[256 * 3];
memset(pal, 255, 248 * 3);
writeColorRegs(pal, 8, 248);
}
void DisplayMan::blackAllScreen() {
byte pal[256 * 3];
memset(pal, 0, 256 * 3);
writeColorRegs(pal, 0, 256);
_vm->_system->delayMillis(32);
}
void DisplayMan::scrollDisplayX(int16 dx, uint16 x1, uint16 y1, uint16 x2, uint16 y2, byte *buffer) {
Image img(_vm);
img.setData(buffer, false);
if (x1 > x2)
SWAP<uint16>(x1, x2);
if (y1 > y2)
SWAP<uint16>(y1, y2);
if (dx > 0) {
img._width = x2 - x1 + 1 - dx;
img._height = y2 - y1 + 1;
img.readScreenImage(x1, y1);
img.drawImage(x1 + dx, y1);
rectFill(x1, y1, x1 + dx - 1, y2, 0);
} else if (dx < 0) {
img._width = x2 - x1 + 1 + dx;
img._height = y2 - y1 + 1;
img.readScreenImage(x1 - dx, y1);
img.drawImage(x1, y1);
rectFill(x2 + dx + 1, y1, x2, y2, 0);
}
}
void DisplayMan::scrollDisplayY(int16 dy, uint16 x1, uint16 y1, uint16 x2, uint16 y2, byte *buffer) {
Image img(_vm);
img.setData(buffer, false);
if (x1 > x2)
SWAP<uint16>(x1, x2);
if (y1 > y2)
SWAP<uint16>(y1, y2);
if (dy > 0) {
img._width = x2 - x1 + 1;
img._height = y2 - y1 + 1 - dy;
img.readScreenImage(x1, y1);
img.drawImage(x1, y1 + dy);
rectFill(x1, y1, x2, y1 + dy - 1, 0);
} else if (dy < 0) {
img._width = x2 - x1 + 1;
img._height = y2 - y1 + 1 + dy;
img.readScreenImage(x1, y1 - dy);
img.drawImage(x1, y1);
rectFill(x1, y2 + dy + 1, x2, y2, 0);
}
}
uint16 DisplayMan::fadeNumIn(uint16 num, uint16 res, uint16 counter) {
return (num - ((((int32)(15 - counter)) * ((int32)(num - res))) / 15));
}
uint16 DisplayMan::fadeNumOut(uint16 num, uint16 res, uint16 counter) {
return (num - ((((int32) counter) * ((int32)(num - res))) / 15));
}
void DisplayMan::fade(bool fadeIn) {
uint16 newPal[16];
for (int i = 0; i < 16; i++) {
for (int palIdx = 0; palIdx < 16; palIdx++) {
if (fadeIn)
newPal[palIdx] =
(0x00F & fadeNumIn(0x00F & _fadePalette[palIdx], 0, i)) +
(0x0F0 & fadeNumIn(0x0F0 & _fadePalette[palIdx], 0, i)) +
(0xF00 & fadeNumIn(0xF00 & _fadePalette[palIdx], 0, i));
else
newPal[palIdx] =
(0x00F & fadeNumOut(0x00F & _fadePalette[palIdx], 0, i)) +
(0x0F0 & fadeNumOut(0x0F0 & _fadePalette[palIdx], 0, i)) +
(0xF00 & fadeNumOut(0xF00 & _fadePalette[palIdx], 0, i));
}
setAmigaPal(newPal);
_vm->updateEvents();
_vm->waitTOF();
_vm->waitTOF();
}
}
} // End of namespace Lab

273
engines/lab/dispman.h Normal file
View File

@@ -0,0 +1,273 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#ifndef LAB_DISPMAN_H
#define LAB_DISPMAN_H
namespace Lab {
class LabEngine;
class Image;
struct TextFont {
uint32 _dataLength;
uint16 _height;
byte _widths[256];
uint16 _offsets[256];
byte *_data;
};
enum TransitionType {
kTransitionNone,
kTransitionWipe,
kTransitionScrollWipe,
kTransitionScrollBlack,
kTransitionScrollBounce,
kTransitionTransporter,
kTransitionReadFirstFrame,
kTransitionReadNextFrame
};
class DisplayMan {
private:
LabEngine *_vm;
/**
* Does the fading of the Palette on the screen.
*/
uint16 fadeNumIn(uint16 num, uint16 res, uint16 counter);
uint16 fadeNumOut(uint16 num, uint16 res, uint16 counter);
/**
* Extracts the first word from a string.
*/
Common::String getWord(const char *mainBuffer);
void createBox(uint16 y2);
/**
* Sets up either a low-res or a high-res 256 color screen.
*/
void createScreen(bool hiRes);
/**
* Scrolls the display to black.
*/
void doScrollBlack();
void copyPage(uint16 width, uint16 height, uint16 nheight, uint16 startline, byte *mem);
/**
* Scrolls the display to a new picture from a black screen.
*/
void doScrollWipe(const Common::String &filename);
/**
* Does the scroll bounce. Assumes bitmap already in memory.
*/
void doScrollBounce();
/**
* Does the transporter wipe.
*/
void doTransWipe(const Common::String &filename);
/**
* Draws a vertical line.
*/
void drawHLine(uint16 x, uint16 y1, uint16 y2, byte color);
/**
* Draws a horizontal line.
*/
void drawVLine(uint16 x1, uint16 y, uint16 x2, byte color);
/**
* Draws the text to the screen.
*/
void drawText(TextFont *tf, uint16 x, uint16 y, uint16 color, const Common::String &text);
/**
* Gets a line of text for flowText; makes sure that its length is less than
* or equal to the maximum width.
*/
Common::String getLine(TextFont *tf, const char **mainBuffer, uint16 lineWidth);
/**
* Returns the length of a text in the specified font.
*/
uint16 textLength(TextFont *font, const Common::String &text);
bool _actionMessageShown;
Common::File *_curBitmap;
byte _curVgaPal[256 * 3];
byte *_currentDisplayBuffer;
public:
DisplayMan(LabEngine *lab);
virtual ~DisplayMan();
void loadPict(const Common::String &filename);
void loadBackPict(const Common::String &fileName, uint16 *highPal);
/**
* Reads in a picture into the display bitmap.
*/
void readPict(const Common::String &filename, bool playOnce = true, bool onlyDiffData = false, byte *memoryBuffer = nullptr);
void freePict();
/**
* Does a certain number of pre-programmed wipes.
*/
void doTransition(TransitionType transitionType, const Common::String &filename);
/**
* Changes the front screen to black.
*/
void blackScreen();
/**
* Changes the front screen to white.
*/
void whiteScreen();
/**
* Changes the entire screen to black.
*/
void blackAllScreen();
/**
* Draws the control panel display.
*/
void drawPanel();
/**
* Sets up the Labyrinth screens, and opens up the initial windows.
*/
void setUpScreens();
int longDrawMessage(Common::String str, bool isActionMessage);
/**
* Draws a message to the message box.
*/
void drawMessage(Common::String str, bool isActionMessage);
void setActionMessage(bool val) { _actionMessageShown = val; }
/**
* Fills in a rectangle.
*/
void rectFill(uint16 x1, uint16 y1, uint16 x2, uint16 y2, byte color);
void rectFill(Common::Rect fillRect, byte color);
void rectFillScaled(uint16 x1, uint16 y1, uint16 x2, uint16 y2, byte color);
/**
* Dumps a chunk of text to an arbitrary box; flows it within that box and
* optionally centers it. Returns the number of characters that were processed.
* @note Every individual word MUST be int16 enough to fit on a line, and
* each line less than 255 characters.
* @param font Pointer on the font used
* @param spacing How much vertical spacing between the lines
* @param penColor Pen number to use for text
* @param backPen Background color
* @param fillBack Whether to fill the background
* @param centerh Whether to center the text horizontally
* @param centerv Whether to center the text vertically
* @param output Whether to output any text
* @param textRect Coords
* @param text The text itself
*/
int flowText(TextFont *font, int16 spacing, byte penColor, byte backPen, bool fillBack,
bool centerh, bool centerv, bool output, Common::Rect textRect, const char *text, Image *targetImage = nullptr);
void screenUpdate();
/**
* Converts a 16-color Amiga palette to a VGA palette, then sets
* the VGA palette.
*/
void setAmigaPal(uint16 *pal);
/**
* Writes any number of the 256 color registers.
* @param buf A char pointer which contains the selected color registers.
* Each value representing a color register occupies 3 bytes in the array. The
* order is red, green then blue. The first byte in the array is the red component
* of the first element selected. The length of the buffer is 3 times the number
* of registers selected.
* @param first The number of the first color register to write.
* @param numReg The number of registers to write.
*/
void writeColorRegs(byte *buf, uint16 first, uint16 numReg);
void setPalette(void *newPal, uint16 numColors);
/**
* Overlays a region on the screen using the desired pen color.
*/
void checkerBoardEffect(uint16 penColor, uint16 x1, uint16 y1, uint16 x2, uint16 y2);
/**
* Returns the base address of the current VGA display.
*/
byte *getCurrentDrawingBuffer();
/**
* Scrolls the display in the x direction by blitting.
* The _tempScrollData variable must be initialized to some memory, or this
* function will fail.
*/
void scrollDisplayX(int16 dx, uint16 x1, uint16 y1, uint16 x2, uint16 y2, byte *buffer);
/**
* Scrolls the display in the y direction by blitting.
*/
void scrollDisplayY(int16 dy, uint16 x1, uint16 y1, uint16 x2, uint16 y2, byte *buffer);
void fade(bool fadein);
/**
* Closes a font and frees all memory associated with it.
*/
void freeFont(TextFont **font);
/**
* Returns the height of a specified font.
*/
uint16 textHeight(TextFont *tf);
bool _longWinInFront;
bool _lastMessageLong;
uint32 _screenBytesPerPage;
int _screenWidth;
int _screenHeight;
byte *_displayBuffer;
uint16 *_fadePalette;
};
} // End of namespace Lab
#endif // LAB_DISPMAN_H

1217
engines/lab/engine.cpp Normal file

File diff suppressed because it is too large Load Diff

191
engines/lab/eventman.cpp Normal file
View File

@@ -0,0 +1,191 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#include "common/events.h"
#include "graphics/cursorman.h"
#include "lab/lab.h"
#include "lab/dispman.h"
#include "lab/eventman.h"
#include "lab/image.h"
#include "lab/interface.h"
namespace Lab {
static const byte mouseData[] = {
1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
1, 7, 1, 0, 0, 0, 0, 0, 0, 0,
1, 7, 7, 1, 0, 0, 0, 0, 0, 0,
1, 7, 7, 7, 1, 0, 0, 0, 0, 0,
1, 7, 7, 7, 7, 1, 0, 0, 0, 0,
1, 7, 7, 7, 7, 7, 1, 0, 0, 0,
1, 7, 7, 7, 7, 7, 7, 1, 0, 0,
1, 7, 7, 7, 7, 7, 7, 7, 1, 0,
1, 7, 7, 7, 7, 7, 1, 1, 1, 1,
1, 7, 7, 1, 7, 7, 1, 0, 0, 0,
1, 7, 1, 0, 1, 7, 7, 1, 0, 0,
1, 1, 0, 0, 1, 7, 7, 1, 0, 0,
0, 0, 0, 0, 0, 1, 7, 7, 1, 0,
0, 0, 0, 0, 0, 1, 7, 7, 1, 0,
0, 0, 0, 0, 0, 0, 1, 1, 0, 0
};
#define MOUSE_WIDTH 10
#define MOUSE_HEIGHT 15
EventManager::EventManager(LabEngine *vm) : _vm(vm) {
_leftClick = false;
_rightClick = false;
_buttonHit = false;
_mousePos = Common::Point(0, 0);
_actionPressed = kActionNone;
}
void EventManager::initMouse() {
CursorMan.pushCursor(mouseData, MOUSE_WIDTH, MOUSE_HEIGHT, 0, 0, 0);
CursorMan.showMouse(false);
setMousePos(Common::Point(_vm->_graphics->_screenWidth / 2, _vm->_graphics->_screenHeight / 2));
}
void EventManager::mouseShow() {
CursorMan.showMouse(true);
}
void EventManager::mouseHide() {
CursorMan.showMouse(false);
}
void EventManager::setMousePos(Common::Point pos) {
if (_vm->_isHiRes)
_vm->_system->warpMouse(pos.x, pos.y);
else
_vm->_system->warpMouse(pos.x * 2, pos.y);
}
void EventManager::processInput() {
Common::Event event;
while (_vm->_system->getEventManager()->pollEvent(event)) {
switch (event.type) {
case Common::EVENT_LBUTTONDOWN:
if (_vm->_interface->checkButtonHit(_mousePos))
_buttonHit = true;
else
_leftClick = true;
break;
case Common::EVENT_RBUTTONDOWN:
_rightClick = true;
break;
case Common::EVENT_MOUSEMOVE:
_mousePos = event.mouse;
break;
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
switch (event.customType) {
case kActionSoundLower:
_vm->changeVolume(-1);
break;
case kActionSoundRaise:
_vm->changeVolume(1);
break;
default:
_actionPressed = event.customType;
break;
}
break;
case Common::EVENT_KEYDOWN:
_actionPressed = kActionQuitDialogNo; // Used for "Press any key to continue" scenarios as well.
break;
case Common::EVENT_QUIT:
case Common::EVENT_RETURN_TO_LAUNCHER:
default:
break;
}
}
}
IntuiMessage *EventManager::getMsg() {
static IntuiMessage message;
_vm->_interface->handlePressedButton();
processInput();
if (_buttonHit) {
Button *lastButtonHit = _vm->_interface->checkButtonHit(_mousePos);
_buttonHit = false;
if (lastButtonHit) {
_vm->_interface->handlePressedButton();
message._msgClass = kMessageButtonUp;
message._code = lastButtonHit->_buttonId;
message._qualifier = 0; // This does not seem to be used anywhere. Since qualifiers can be detected by keymapper anyways, this is set to 0.
return &message;
} else
return nullptr;
} else if (_leftClick || _rightClick) {
message._msgClass = (_leftClick) ? kMessageLeftClick : kMessageRightClick;
message._qualifier = 0;
message._mouse = _mousePos;
_leftClick = _rightClick = false;
return &message;
} else if (_actionPressed != kActionNone) {
Button *curButton = _vm->_interface->checkNumButtonHit(_actionPressed);
if (curButton) {
message._msgClass = kMessageButtonUp;
message._code = curButton->_buttonId;
} else {
message._msgClass = kMessageAction;
message._code = _actionPressed;
}
message._qualifier = 0; // This does not seem to be used anywhere. Since qualifiers can be detected by keymapper anyways, this is set to 0.
message._mouse = _mousePos;
_actionPressed = kActionNone;
return &message;
} else
return nullptr;
}
Common::Point EventManager::updateAndGetMousePos() {
processInput();
return _mousePos;
}
void EventManager::simulateEvent() {
// Simulate an event by setting an unused action.
_actionPressed = kActionDummy;
}
} // End of namespace Lab

94
engines/lab/eventman.h Normal file
View File

@@ -0,0 +1,94 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#ifndef LAB_EVENTMAN_H
#define LAB_EVENTMAN_H
#include "common/events.h"
namespace Lab {
class LabEngine;
class Image;
struct IntuiMessage {
MessageClass _msgClass;
uint16 _code; // Action or Button Id
uint16 _qualifier;
Common::Point _mouse;
};
class EventManager {
private:
LabEngine *_vm;
bool _leftClick;
bool _rightClick;
bool _buttonHit;
Common::Point _mousePos;
Common::CustomEventType _actionPressed;
public:
EventManager (LabEngine *vm);
IntuiMessage *getMsg();
/**
* Initializes the mouse.
*/
void initMouse();
/**
* Shows the mouse.
*/
void mouseShow();
/**
* Hides the mouse.
*/
void mouseHide();
void processInput();
/**
* Moves the mouse to new co-ordinates.
*/
void setMousePos(Common::Point pos);
Common::Point updateAndGetMousePos();
/**
* Simulates an event for the game main loop, when a game is
* loaded or when the user teleports to a scene
*/
void simulateEvent();
};
} // End of namespace Lab
#endif // LAB_EVENTMAN_H

135
engines/lab/image.cpp Normal file
View File

@@ -0,0 +1,135 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#include "common/file.h"
#include "lab/lab.h"
#include "lab/dispman.h"
#include "lab/image.h"
namespace Lab {
Image::Image(Common::File *s, LabEngine *vm) : _vm(vm) {
_width = s->readUint16LE();
_height = s->readUint16LE();
s->skip(4);
uint32 size = _width * _height;
if (size & 1)
size++;
_imageData = new byte[size];
s->read(_imageData, size);
_autoFree = true;
}
Image::~Image() {
if (_autoFree)
delete[] _imageData;
}
void Image::setData(byte *d, bool autoFree) {
if (_autoFree)
delete[] _imageData;
_imageData = d;
_autoFree = autoFree;
}
void Image::blitBitmap(uint16 srcX, uint16 srcY, Image *imgDest,
uint16 destX, uint16 destY, uint16 width, uint16 height, byte masked) {
int clipWidth = width;
int clipHeight = height;
int destWidth = (imgDest) ? imgDest->_width : _vm->_graphics->_screenWidth;
int destHeight = (imgDest) ? imgDest->_height : _vm->_graphics->_screenHeight;
byte *destBuffer = (imgDest) ? imgDest->_imageData : _vm->_graphics->getCurrentDrawingBuffer();
if (destX + clipWidth > destWidth)
clipWidth = destWidth - destX;
if (destY + clipHeight > destHeight)
clipHeight = destHeight - destY;
if ((clipWidth > 0) && (clipHeight > 0)) {
byte *img = _imageData + srcY * _width + srcX;
byte *dest = destBuffer + destY * destWidth + destX;
if (!masked) {
for (int i = 0; i < clipHeight; i++) {
memcpy(dest, img, clipWidth);
img += _width;
dest += destWidth;
}
} else {
for (int i = 0; i < clipHeight; i++) {
for (int j = 0; j < clipWidth; j++) {
byte c = img[j];
if (c)
dest[j] = c - 1;
}
img += _width;
dest += destWidth;
}
}
}
}
void Image::drawImage(uint16 x, uint16 y) {
blitBitmap(0, 0, nullptr, x, y, _width, _height, false);
}
void Image::drawMaskImage(uint16 x, uint16 y) {
blitBitmap(0, 0, nullptr, x, y, _width, _height, true);
}
void Image::readScreenImage(uint16 x, uint16 y) {
int clipWidth = _width;
int clipHeight = _height;
if (x + clipWidth > _vm->_graphics->_screenWidth)
clipWidth = _vm->_graphics->_screenWidth - x;
if (y + clipHeight > _vm->_graphics->_screenHeight)
clipHeight = _vm->_graphics->_screenHeight - y;
if ((clipWidth > 0) && (clipHeight > 0)) {
byte *img = _imageData;
byte *screen = _vm->_graphics->getCurrentDrawingBuffer() + y * _vm->_graphics->_screenWidth + x;
while (clipHeight-- > 0) {
memcpy(img, screen, clipWidth);
img += _width;
screen += _vm->_graphics->_screenWidth;
}
}
}
} // End of namespace Lab

82
engines/lab/image.h Normal file
View File

@@ -0,0 +1,82 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#ifndef LAB_IMAGE_H
#define LAB_IMAGE_H
namespace Common {
class File;
}
namespace Lab {
class LabEngine;
class Image {
LabEngine *_vm;
public:
uint16 _width;
uint16 _height;
byte *_imageData;
Image(LabEngine *vm) : _width(0), _height(0), _imageData(nullptr), _vm(vm), _autoFree(true) {}
Image(int w, int h, byte *d, LabEngine *vm, bool autoFree = true) : _width(w), _height(h), _imageData(d), _vm(vm), _autoFree(autoFree) {}
Image(Common::File *s, LabEngine *vm);
~Image();
void setData(byte *d, bool autoFree = true);
/**
* Draws an image to the screen.
*/
void drawImage(uint16 x, uint16 y);
/**
* Draws an image to the screen with transparency.
*/
void drawMaskImage(uint16 x, uint16 y);
/**
* Reads an image from the screen.
*/
void readScreenImage(uint16 x, uint16 y);
/**
* Blits a piece of one image to another.
*/
void blitBitmap(uint16 srcX, uint16 srcY, Image *imgDest, uint16 destX, uint16 destY, uint16 width, uint16 height, byte masked);
private:
bool _autoFree; ///< Free _imageData in destructor?
};
} // End of namespace Lab
#endif // LAB_IMAGE_H

231
engines/lab/interface.cpp Normal file
View File

@@ -0,0 +1,231 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#include "common/events.h"
#include "lab/lab.h"
#include "lab/dispman.h"
#include "lab/interface.h"
#include "lab/image.h"
#include "lab/utils.h"
namespace Lab {
#define CRUMBSWIDTH 24
#define CRUMBSHEIGHT 24
Interface::Interface(LabEngine *vm) : _vm(vm) {
_screenButtonList = nullptr;
_hitButton = nullptr;
}
Button *Interface::createButton(uint16 x, uint16 y, uint16 id, Common::CustomEventType action, Image *image, Image *altImage) {
Button *button = new Button();
if (button) {
button->_x = _vm->_utils->vgaScaleX(x);
button->_y = y;
button->_buttonId = id;
button->_actionEquiv = action;
button->_image = image;
button->_altImage = altImage;
button->_isEnabled = true;
return button;
} else
return nullptr;
}
void Interface::freeButtonList(ButtonList *buttonList) {
for (auto &button : *buttonList) {
delete button->_image;
delete button->_altImage;
delete button;
}
buttonList->clear();
}
void Interface::drawButtonList(ButtonList *buttonList) {
for (auto &button : *buttonList) {
toggleButton(button, 1, true);
if (!button->_isEnabled)
toggleButton(button, 1, false);
}
}
void Interface::toggleButton(Button *button, uint16 disabledPenColor, bool enable) {
if (!enable)
_vm->_graphics->checkerBoardEffect(disabledPenColor, button->_x, button->_y, button->_x + button->_image->_width - 1, button->_y + button->_image->_height - 1);
else
button->_image->drawImage(button->_x, button->_y);
button->_isEnabled = enable;
}
Button *Interface::checkNumButtonHit(Common::CustomEventType action) {
if (!_screenButtonList)
return nullptr;
for (auto &button : *_screenButtonList) {
if (!button->_isEnabled)
continue;
if ((button->_actionEquiv != kActionNone && action == button->_actionEquiv)) {
button->_altImage->drawImage(button->_x, button->_y);
_vm->_system->delayMillis(80);
button->_image->drawImage(button->_x, button->_y);
return button;
}
}
return nullptr;
}
Button *Interface::checkButtonHit(Common::Point pos) {
if (!_screenButtonList)
return nullptr;
for (auto &button : *_screenButtonList) {
Common::Rect buttonRect(button->_x, button->_y, button->_x + button->_image->_width - 1, button->_y + button->_image->_height - 1);
if (buttonRect.contains(pos) && button->_isEnabled) {
_hitButton = button;
return button;
}
}
return nullptr;
}
void Interface::handlePressedButton() {
if (!_hitButton)
return;
_hitButton->_altImage->drawImage(_hitButton->_x, _hitButton->_y);
for (int i = 0; i < 3; i++)
_vm->waitTOF();
_hitButton->_image->drawImage(_hitButton->_x, _hitButton->_y);
_hitButton = nullptr;
_vm->_graphics->screenUpdate();
}
void Interface::attachButtonList(ButtonList *buttonList) {
_screenButtonList = buttonList;
}
Button *Interface::getButton(uint16 id) {
for (auto &button : *_screenButtonList) {
if (button->_buttonId == id)
return button;
}
return nullptr;
}
void Interface::mayShowCrumbIndicator() {
static byte dropCrumbsImageData[CRUMBSWIDTH * CRUMBSHEIGHT] = {
0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0,
0, 4, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 4, 0,
4, 7, 7, 3, 4, 4, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 7, 7, 4,
4, 7, 4, 4, 0, 0, 3, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 7, 4,
4, 7, 4, 0, 0, 0, 3, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 4, 7, 4,
4, 7, 4, 0, 0, 3, 2, 2, 2, 3, 0, 0, 0, 0, 0, 0, 0, 3, 2, 3, 0, 4, 7, 4,
4, 7, 4, 0, 0, 0, 3, 3, 3, 4, 4, 4, 4, 4, 4, 0, 0, 3, 2, 3, 0, 4, 7, 4,
4, 7, 4, 0, 0, 0, 0, 0, 4, 7, 7, 7, 7, 7, 7, 4, 3, 2, 2, 2, 3, 4, 7, 4,
4, 7, 4, 0, 0, 0, 0, 4, 7, 7, 4, 4, 4, 4, 7, 7, 4, 3, 3, 3, 0, 4, 7, 4,
4, 7, 4, 0, 0, 0, 0, 4, 7, 4, 4, 0, 0, 4, 4, 7, 4, 0, 0, 0, 0, 4, 7, 4,
4, 7, 4, 0, 0, 0, 0, 4, 7, 4, 0, 0, 0, 0, 4, 7, 4, 0, 0, 0, 0, 4, 7, 4,
4, 7, 4, 0, 0, 0, 0, 4, 4, 4, 3, 0, 0, 0, 4, 7, 4, 0, 0, 0, 0, 4, 7, 4,
4, 7, 4, 0, 0, 0, 0, 0, 4, 3, 2, 3, 0, 0, 4, 7, 4, 0, 0, 0, 0, 4, 7, 4,
4, 7, 4, 0, 0, 0, 0, 0, 0, 3, 2, 3, 0, 0, 4, 7, 4, 0, 0, 0, 0, 4, 7, 4,
4, 7, 4, 0, 0, 0, 0, 0, 3, 2, 2, 2, 3, 4, 4, 7, 4, 0, 0, 0, 0, 4, 7, 4,
4, 7, 7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 7, 7, 4, 0, 0, 0, 0, 4, 7, 4,
0, 4, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 4, 0, 0, 0, 0, 0, 4, 7, 4,
0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 0, 0, 0, 0, 0, 4, 7, 4,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2, 3, 0, 0, 0, 0, 4, 7, 4,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2, 3, 0, 0, 0, 0, 4, 7, 4,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2, 2, 2, 3, 0, 0, 4, 4, 7, 4,
0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 7, 7, 4,
0, 0, 4, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 4, 0,
0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0
};
if (_vm->getPlatform() != Common::kPlatformWindows)
return;
if (_vm->_droppingCrumbs && _vm->isMainDisplay()) {
Image dropCrumbsImage(CRUMBSWIDTH, CRUMBSHEIGHT, dropCrumbsImageData, _vm, false);
dropCrumbsImage.drawMaskImage(612, 4);
}
}
void Interface::mayShowCrumbIndicatorOff() {
static byte dropCrumbsOffImageData[CRUMBSWIDTH * CRUMBSHEIGHT] = {
0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0,
0, 4, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 4, 0,
4, 8, 8, 3, 4, 4, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 8, 8, 4,
4, 8, 4, 4, 0, 0, 3, 8, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 8, 4,
4, 8, 4, 0, 0, 0, 3, 8, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 4, 8, 4,
4, 8, 4, 0, 0, 3, 8, 8, 8, 3, 0, 0, 0, 0, 0, 0, 0, 3, 8, 3, 0, 4, 8, 4,
4, 8, 4, 0, 0, 0, 3, 3, 3, 4, 4, 4, 4, 4, 4, 0, 0, 3, 8, 3, 0, 4, 8, 4,
4, 8, 4, 0, 0, 0, 0, 0, 4, 8, 8, 8, 8, 8, 8, 4, 3, 8, 8, 8, 3, 4, 8, 4,
4, 8, 4, 0, 0, 0, 0, 4, 8, 8, 4, 4, 4, 4, 8, 8, 4, 3, 3, 3, 0, 4, 8, 4,
4, 8, 4, 0, 0, 0, 0, 4, 8, 4, 4, 0, 0, 4, 4, 8, 4, 0, 0, 0, 0, 4, 8, 4,
4, 8, 4, 0, 0, 0, 0, 4, 8, 4, 0, 0, 0, 0, 4, 8, 4, 0, 0, 0, 0, 4, 8, 4,
4, 8, 4, 0, 0, 0, 0, 4, 4, 4, 3, 0, 0, 0, 4, 8, 4, 0, 0, 0, 0, 4, 8, 4,
4, 8, 4, 0, 0, 0, 0, 0, 4, 3, 8, 3, 0, 0, 4, 8, 4, 0, 0, 0, 0, 4, 8, 4,
4, 8, 4, 0, 0, 0, 0, 0, 0, 3, 8, 3, 0, 0, 4, 8, 4, 0, 0, 0, 0, 4, 8, 4,
4, 8, 4, 0, 0, 0, 0, 0, 3, 8, 8, 8, 3, 4, 4, 8, 4, 0, 0, 0, 0, 4, 8, 4,
4, 8, 8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 4, 0, 0, 0, 0, 4, 8, 4,
0, 4, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 4, 0, 0, 0, 0, 0, 4, 8, 4,
0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 0, 0, 0, 0, 0, 4, 8, 4,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 8, 3, 0, 0, 0, 0, 4, 8, 4,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 8, 3, 0, 0, 0, 0, 4, 8, 4,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 8, 8, 8, 3, 0, 0, 4, 4, 8, 4,
0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 4,
0, 0, 4, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 4, 0,
0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0
};
if (_vm->getPlatform() != Common::kPlatformWindows)
return;
if (_vm->isMainDisplay()) {
Image dropCrumbsOffImage(CRUMBSWIDTH, CRUMBSHEIGHT, dropCrumbsOffImageData, _vm, false);
dropCrumbsOffImage.drawMaskImage(612, 4);
}
}
} // End of namespace Lab

90
engines/lab/interface.h Normal file
View File

@@ -0,0 +1,90 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#ifndef LAB_INTERFACE_H
#define LAB_INTERFACE_H
#include "common/events.h"
namespace Lab {
class LabEngine;
class Image;
struct Button {
uint16 _x, _y, _buttonId;
Common::CustomEventType _actionEquiv; // the action which activates this button
bool _isEnabled;
Image *_image, *_altImage;
};
typedef Common::List<Button *> ButtonList;
class Interface {
private:
LabEngine *_vm;
Button *_hitButton;
ButtonList *_screenButtonList;
public:
Interface(LabEngine *vm);
void attachButtonList(ButtonList *buttonList);
Button *createButton(uint16 x, uint16 y, uint16 id, Common::CustomEventType action, Image *image, Image *altImage);
void toggleButton(Button *button, uint16 penColor, bool enable);
/**
* Draws a button list to the screen.
*/
void drawButtonList(ButtonList *buttonList);
void freeButtonList(ButtonList *buttonList);
Button *getButton(uint16 id);
/**
* Checks whether or not the coords fall within one of the buttons in a list
* of buttons.
*/
Button *checkButtonHit(Common::Point pos);
/**
* Checks whether or not the coords fall within one of the buttons in a list
* of buttons.
*/
Button *checkNumButtonHit(Common::CustomEventType action);
void handlePressedButton();
void mayShowCrumbIndicator();
void mayShowCrumbIndicatorOff();
};
} // End of namespace Lab
#endif // LAB_INTERFACE_H

416
engines/lab/intro.cpp Normal file
View File

@@ -0,0 +1,416 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#include "common/file.h"
#include "lab/lab.h"
#include "lab/anim.h"
#include "lab/dispman.h"
#include "lab/eventman.h"
#include "lab/intro.h"
#include "lab/music.h"
#include "lab/resource.h"
#include "lab/utils.h"
namespace Lab {
Intro::Intro(LabEngine *vm) : _vm(vm) {
_quitIntro = false;
_font = _vm->_resource->getFont("F:Map.fon");
}
Intro::~Intro() {
_vm->_graphics->freeFont(&_font);
}
void Intro::introEatMessages() {
while (1) {
IntuiMessage *msg = _vm->_event->getMsg();
if (_vm->shouldQuit()) {
_quitIntro = true;
return;
}
if (!msg)
return;
if ((msg->_msgClass == kMessageRightClick)
|| ((msg->_msgClass == kMessageAction) && (msg->_code == kActionSkipIntro)))
_quitIntro = true;
}
}
void Intro::doPictText(const Common::String &filename, bool isScreen) {
Common::String path = Common::String("Lab:rooms/Intro/") + filename;
uint timeDelay = (isScreen) ? 35 : 7;
_vm->updateEvents();
if (_quitIntro)
return;
uint32 lastMillis = 0;
bool drawNextText = true;
bool doneFl = false;
bool begin = true;
Common::File *textFile = _vm->_resource->openDataFile(path);
char *textBuffer = new char[textFile->size()];
textFile->read(textBuffer, textFile->size());
delete textFile;
const char *curText = textBuffer;
while (1) {
if (drawNextText) {
if (begin)
begin = false;
else if (isScreen)
_vm->_graphics->fade(false);
if (isScreen) {
_vm->_graphics->rectFillScaled(10, 10, 310, 190, 7);
curText += _vm->_graphics->flowText(_font, _vm->_isHiRes ? 0 : -1, 5, 7, false, false, true, true, _vm->_utils->vgaRectScale(14, 11, 306, 189), curText);
_vm->_graphics->fade(true);
} else
curText += _vm->_graphics->longDrawMessage(Common::String(curText), false);
doneFl = (*curText == 0);
drawNextText = false;
introEatMessages();
if (_quitIntro) {
if (isScreen)
_vm->_graphics->fade(false);
delete[] textBuffer;
return;
}
lastMillis = _vm->_system->getMillis();
}
IntuiMessage *msg = _vm->_event->getMsg();
if (_vm->shouldQuit()) {
_quitIntro = true;
return;
}
if (!msg) {
_vm->updateEvents();
_vm->_anim->diffNextFrame();
uint32 elapsedSeconds = (_vm->_system->getMillis() - lastMillis) / 1000;
if (elapsedSeconds > timeDelay) {
if (doneFl) {
if (isScreen)
_vm->_graphics->fade(false);
delete[] textBuffer;
return;
} else {
drawNextText = true;
}
}
_vm->waitTOF();
} else {
uint32 msgClass = msg->_msgClass;
uint16 code = msg->_code;
if ((msgClass == kMessageRightClick) ||
((msgClass == kMessageAction) && (code == kActionSkipIntro))) {
_quitIntro = true;
if (isScreen)
_vm->_graphics->fade(false);
delete[] textBuffer;
return;
} else if ((msgClass == kMessageLeftClick) || (msgClass == kMessageRightClick)) {
if (msgClass == kMessageLeftClick) {
if (doneFl) {
if (isScreen)
_vm->_graphics->fade(false);
delete[] textBuffer;
return;
} else
drawNextText = true;
}
introEatMessages();
if (_quitIntro) {
if (isScreen)
_vm->_graphics->fade(false);
delete[] textBuffer;
return;
}
}
if (doneFl) {
if (isScreen)
_vm->_graphics->fade(false);
delete[] textBuffer;
return;
} else
drawNextText = true;
}
} // while(1)
}
void Intro::nReadPict(const Common::String &filename, bool playOnce, bool noPalChange, bool doBlack, int wait) {
Common::String finalFileName = Common::String("P:Intro/") + filename;
_vm->updateEvents();
introEatMessages();
if (_quitIntro)
return;
if (noPalChange)
_vm->_anim->_noPalChange = true;
_vm->_anim->_doBlack = doBlack;
_vm->_anim->stopDiffEnd();
_vm->_graphics->readPict(finalFileName, playOnce);
if (wait) {
for (int i = 0; i < wait / 10; i++) {
_vm->updateEvents();
introEatMessages();
if (_quitIntro)
break;
_vm->_system->delayMillis(10);
}
}
if (noPalChange)
_vm->_anim->_noPalChange = false;
}
void Intro::play() {
uint16 palette[16] = {
0x0000, 0x0855, 0x0FF9, 0x0EE7,
0x0ED5, 0x0DB4, 0x0CA2, 0x0C91,
0x0B80, 0x0B80, 0x0B91, 0x0CA2,
0x0CB3, 0x0DC4, 0x0DD6, 0x0EE7
};
if (_vm->getPlatform() == Common::kPlatformDOS) {
nReadPict("EA0");
nReadPict("EA1");
nReadPict("EA2");
nReadPict("EA3");
} else if (_vm->getPlatform() == Common::kPlatformWindows) {
nReadPict("WYRMKEEP", true, false, false, 4000);
}
_vm->_graphics->blackAllScreen();
_vm->_music->resetMusic(false);
if (_vm->getPlatform() == Common::kPlatformDOS)
nReadPict("TNDcycle.pic", true, true);
else
nReadPict("TNDcycle2.pic", true, true);
_vm->_graphics->_fadePalette = palette;
for (int i = 0; i < 16; i++) {
palette[i] = ((_vm->_anim->_diffPalette[i * 3] >> 2) << 8) +
((_vm->_anim->_diffPalette[i * 3 + 1] >> 2) << 4) +
(_vm->_anim->_diffPalette[i * 3 + 2] >> 2);
}
_vm->updateEvents();
introEatMessages();
if (!_quitIntro)
_vm->_graphics->fade(true);
for (int times = 0; times < 150; times++) {
_vm->updateEvents();
introEatMessages();
if (_quitIntro)
break;
uint16 temp = palette[2];
for (int i = 2; i < 15; i++)
palette[i] = palette[i + 1];
palette[15] = temp;
_vm->_graphics->setAmigaPal(palette);
_vm->waitTOF();
_vm->waitTOF();
}
if (!_quitIntro) {
_vm->_graphics->fade(false);
_vm->_graphics->blackAllScreen();
_vm->updateEvents();
introEatMessages();
}
nReadPict("Title.A");
nReadPict("AB", true, false, false, 1000);
nReadPict("BA");
nReadPict("AC", true, false, false, 1000);
nReadPict("CA");
nReadPict("AD", true, false, false, 1000);
nReadPict("DA");
_vm->_graphics->blackAllScreen();
_vm->updateEvents();
introEatMessages();
nReadPict("Intro.1", true, true);
for (int i = 0; i < 16; i++) {
palette[i] = ((_vm->_anim->_diffPalette[i * 3] >> 2) << 8) +
((_vm->_anim->_diffPalette[i * 3 + 1] >> 2) << 4) +
(_vm->_anim->_diffPalette[i * 3 + 2] >> 2);
}
doPictText("i.1", true);
if (_vm->getPlatform() == Common::kPlatformWindows) {
doPictText("i.2A", true);
doPictText("i.2B", true);
}
_vm->_graphics->blackAllScreen();
_vm->updateEvents();
introEatMessages();
nReadPict("Station1", true, false, true);
doPictText("i.3");
nReadPict("Station2", true, false, true);
doPictText("i.4");
nReadPict("Stiles4", true, false, true);
doPictText("i.5");
nReadPict("Stiles3", true, false, true);
doPictText("i.6");
if (_vm->getPlatform() == Common::kPlatformWindows)
nReadPict("Platform2", true, false, true);
else
nReadPict("Platform", true, false, true);
doPictText("i.7");
nReadPict("Subway.1", true, false, true);
doPictText("i.8");
nReadPict("Subway.2", true, false, true);
doPictText("i.9");
doPictText("i.10");
doPictText("i.11");
for (int i = 0; i < 50; i++) {
_vm->updateEvents();
introEatMessages();
if (_quitIntro)
break;
for (int idx = (8 * 3); idx < (255 * 3); idx++)
_vm->_anim->_diffPalette[idx] = 255 - _vm->_anim->_diffPalette[idx];
_vm->waitTOF();
_vm->_graphics->setPalette(_vm->_anim->_diffPalette, 256);
_vm->waitTOF();
_vm->waitTOF();
}
doPictText("i.12");
doPictText("i.13");
nReadPict("Daed0");
doPictText("i.14");
nReadPict("Daed1");
doPictText("i.15");
nReadPict("Daed2");
doPictText("i.16");
doPictText("i.17");
doPictText("i.18");
nReadPict("Daed3");
doPictText("i.19");
doPictText("i.20");
nReadPict("Daed4");
doPictText("i.21");
nReadPict("Daed5");
doPictText("i.22");
doPictText("i.23");
doPictText("i.24");
nReadPict("Daed6");
doPictText("i.25");
doPictText("i.26");
nReadPict("Daed7", false);
doPictText("i.27");
doPictText("i.28");
nReadPict("Daed8");
doPictText("i.29");
doPictText("i.30");
nReadPict("Daed9");
doPictText("i.31");
doPictText("i.32");
doPictText("i.33");
nReadPict("Daed9a");
nReadPict("Daed10");
doPictText("i.34");
doPictText("i.35");
doPictText("i.36");
nReadPict("SubX");
if (_quitIntro) {
_vm->_graphics->rectFill(0, 0, _vm->_graphics->_screenWidth - 1, _vm->_graphics->_screenHeight - 1, 0);
_vm->_anim->_doBlack = true;
}
}
} // End of namespace Lab

66
engines/lab/intro.h Normal file
View File

@@ -0,0 +1,66 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#ifndef LAB_INTRO_H
#define LAB_INTRO_H
namespace Lab {
class Intro {
public:
Intro(LabEngine *vm);
~Intro();
/**
* Does the introduction sequence for Labyrinth.
*/
void play();
private:
/**
* Goes through, and responds to all the intuition messages currently in the
* message queue.
*/
void introEatMessages();
/**
* Reads in a picture.
*/
void doPictText(const Common::String &filename, bool isScreen = false);
void nReadPict(const Common::String &filename, bool playOnce = true, bool noPalChange = false, bool doBlack = false, int wait = 0);
LabEngine *_vm;
bool _quitIntro;
TextFont *_font;
};
} // End of namespace Lab
#endif // LAB_INTRO_H

224
engines/lab/lab.cpp Normal file
View File

@@ -0,0 +1,224 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#include "common/config-manager.h"
#include "common/debug-channels.h"
#include "common/error.h"
#include "common/file.h"
#include "engines/util.h"
#include "lab/lab.h"
#include "lab/anim.h"
#include "lab/console.h"
#include "lab/dispman.h"
#include "lab/eventman.h"
#include "lab/image.h"
#include "lab/interface.h"
#include "lab/music.h"
#include "lab/processroom.h"
#include "lab/resource.h"
#include "lab/speciallocks.h"
#include "lab/utils.h"
namespace Lab {
LabEngine::LabEngine(OSystem *syst, const ADGameDescription *gameDesc)
: Engine(syst), _gameDescription(gameDesc), _extraGameFeatures(0) {
_lastWaitTOFTicks = 0;
_isHiRes = false;
_roomNum = -1;
for (int i = 0; i < MAX_CRUMBS; i++) {
_breadCrumbs[i]._crumbRoomNum = 0;
_breadCrumbs[i]._crumbDirection = kDirectionNorth;
}
_numCrumbs = 0;
_droppingCrumbs = false;
_followingCrumbs = false;
_followCrumbsFast = false;
_isCrumbTurning = false;
_isCrumbWaiting = false;
_noUpdateDiff = false;
_quitLab = false;
_mainDisplay = true;
_numInv = 0;
_manyRooms = 0;
_direction = 0;
_highestCondition = 0;
_crumbTimestamp = 0;
_maxRooms = 0;
_event = nullptr;
_interface = nullptr;
_resource = nullptr;
_music = nullptr;
_anim = nullptr;
_closeDataPtr = nullptr;
_conditions = nullptr;
_graphics = nullptr;
_rooms = nullptr;
_roomsFound = nullptr;
_specialLocks = nullptr;
_utils = nullptr;
_journalBackImage = nullptr;
_lastTooLong = false;
_alternate = false;
for (int i = 0; i < 20; i++)
_moveImages[i] = nullptr;
for (int i = 0; i < 10; i++)
_invImages[i] = nullptr;
_curFileName = " ";
_msgFont = nullptr;
_inventory = nullptr;
_imgMap = nullptr;
_imgRoom = nullptr;
_imgUpArrowRoom = nullptr;
_imgDownArrowRoom = nullptr;
_imgBridge = nullptr;
_imgHRoom = nullptr;
_imgVRoom = nullptr;
_imgMaze = nullptr;
_imgHugeMaze = nullptr;
_imgPath = nullptr;
for (int i = 0; i < 4; i++)
_imgMapX[i] = nullptr;
_maps = nullptr;
_blankJournal = nullptr;
_journalFont = nullptr;
_journalPage = 0;
_lastPage = false;
_monitorPage = 0;
_monitorTextFilename = "";
_monitorButton = nullptr;
_monitorButtonHeight = 1;
for (int i = 0; i < 20; i++)
_highPalette[i] = 0;
_introPlaying = false;
const Common::FSNode gameDataDir(ConfMan.getPath("path"));
SearchMan.addSubDirectoryMatching(gameDataDir, "game", 0, 4);
}
LabEngine::~LabEngine() {
freeMapData();
delete[] _rooms;
delete[] _inventory;
delete _conditions;
delete _roomsFound;
delete _event;
delete _interface;
delete _resource;
delete _music;
delete _anim;
delete _graphics;
delete _specialLocks;
delete _utils;
delete _journalBackImage;
}
Common::Error LabEngine::run() {
if (getFeatures() & GF_LOWRES)
initGraphics(320, 200);
else
initGraphics(640, 480);
_interface = new Interface(this);
_event = new EventManager(this);
_resource = new Resource(this);
_music = new Music(this);
_graphics = new DisplayMan(this);
_anim = new Anim(this);
_specialLocks = new SpecialLocks(this);
_utils = new Utils(this);
setDebugger(new Console(this));
_journalBackImage = new Image(this);
go();
return Common::kNoError;
}
void LabEngine::drawStaticMessage(byte index) {
_graphics->drawMessage(_resource->getStaticText((StaticText)index), false);
}
void LabEngine::changeVolume(int delta) {
int sfxPrev = _mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType);
int musicPrev = _mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType);
int sfxNew = (delta > 0) ? MIN<int>(sfxPrev + 10, Audio::Mixer::kMaxMixerVolume) : MAX<int>(sfxPrev - 10, 0);
int musicNew = (delta > 0) ? MIN<int>(musicPrev + 10, Audio::Mixer::kMaxMixerVolume) : MAX<int>(musicPrev - 10, 0);
_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, sfxNew);
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, musicNew);
}
void LabEngine::waitTOF() {
_graphics->screenUpdate();
uint32 now;
for (now = _system->getMillis(); now - _lastWaitTOFTicks <= 0xF; now = _system->getMillis() )
_system->delayMillis(_lastWaitTOFTicks - now + 17);
_lastWaitTOFTicks = now;
}
void LabEngine::updateEvents() {
_event->processInput();
_interface->handlePressedButton();
}
Common::Error LabEngine::loadGameState(int slot) {
bool result = loadGame(slot);
return (result) ? Common::kNoError : Common::kUserCanceled;
}
Common::Error LabEngine::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
bool result = saveGame(slot, desc);
return (result) ? Common::kNoError : Common::kUserCanceled;
}
bool LabEngine::canLoadGameStateCurrently(Common::U32String *msg) {
return !_introPlaying;
}
bool LabEngine::canSaveGameStateCurrently(Common::U32String *msg) {
return !_introPlaying;
}
} // End of namespace Lab

540
engines/lab/lab.h Normal file
View File

@@ -0,0 +1,540 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#ifndef LAB_LAB_H
#define LAB_LAB_H
#include "common/system.h"
#include "common/random.h"
#include "common/rect.h"
#include "common/savefile.h"
#include "engines/engine.h"
#include "engines/savestate.h"
#include "lab/console.h"
#include "lab/image.h"
#include "lab/labsets.h"
#include "lab/detection.h"
struct ADGameDescription;
namespace Lab {
struct MapData;
struct Action;
struct CloseData;
struct Button;
struct IntuiMessage;
struct InventoryData;
struct RoomData;
struct Rule;
struct TextFont;
struct ViewData;
class Anim;
class DisplayMan;
class EventManager;
class Interface;
class Image;
class Music;
class Resource;
class SpecialLocks;
class Utils;
struct SaveGameHeader {
byte _version;
SaveStateDescriptor _descr;
uint16 _roomNumber;
uint16 _direction;
};
typedef Common::List<Button *> ButtonList;
struct CrumbData {
uint16 _crumbRoomNum;
uint16 _crumbDirection;
};
#define MAX_CRUMBS 128
typedef Common::List<Rule> RuleList;
typedef Common::List<Action> ActionList;
typedef Common::List<CloseData> CloseDataList;
typedef Common::List<ViewData> ViewDataList;
enum Direction {
kDirectionNorth,
kDirectionSouth,
kDirectionEast,
kDirectionWest
};
enum MainButton {
kButtonNone = -1,
kButtonPickup,
kButtonUse,
kButtonOpen,
kButtonClose,
kButtonLook,
kButtonInventory,
kButtonLeft,
kButtonForward,
kButtonRight,
kButtonMap
};
enum MessageClass {
kMessageLeftClick,
kMessageRightClick,
kMessageButtonUp,
kMessageAction
};
enum LABActions {
kActionNone,
kActionQuit,
kActionSkipIntro,
kActionSoundRaise,
kActionSoundLower,
kActionInteract,
kActionTake,
kActionMove,
kActionOpen,
kActionClose,
kActionLook,
kActionInv,
kActionForward,
kActionLeft,
kActionRight,
kActionMap,
kActionFocusOnNextInteractiveItem,
kActionExit,
kActionMainDisplay,
kActionSaveLoad,
kActionUse,
kActionInvLook,
kActionPrev,
kActionNext,
kActionDropBreadcrumb,
kActionFollowBreadcrumbs,
kActionRunWhileFollowingBreadcrumbs,
kActionMapExit,
kActionUpperFloor,
kActionLowerFloor,
kActionQuitDialogYes,
kActionQuitDialogNo,
kActionJournalBack,
kActionJournalExit,
kActionJournalForward,
kActionDummy,
};
class LabEngine : public Engine {
friend class Console;
private:
bool _isCrumbWaiting;
bool _lastTooLong;
bool _lastPage;
bool _mainDisplay;
bool _noUpdateDiff;
bool _quitLab;
byte *_blankJournal;
int _lastWaitTOFTicks;
uint16 _direction;
uint16 _highPalette[20];
uint16 _journalPage;
uint16 _maxRooms;
uint16 _monitorPage;
uint16 _monitorButtonHeight;
uint32 _extraGameFeatures;
Common::String _journalText;
Common::String _journalTextTitle;
Common::String _nextFileName;
Common::String _newFileName;
Common::String _monitorTextFilename;
const CloseData *_closeDataPtr;
ButtonList _journalButtonList;
ButtonList _mapButtonList;
Image *_imgMap, *_imgRoom, *_imgUpArrowRoom, *_imgDownArrowRoom, *_imgBridge;
Image *_imgHRoom, *_imgVRoom, *_imgMaze, *_imgHugeMaze, *_imgPath;
Image *_imgMapX[4];
InventoryData *_inventory;
MapData *_maps;
Image *_monitorButton;
Image *_journalBackImage;
TextFont *_journalFont;
bool _introPlaying;
public:
bool _alternate;
bool _droppingCrumbs;
bool _followingCrumbs;
bool _followCrumbsFast;
bool _isCrumbTurning;
bool _isHiRes;
int _roomNum;
uint16 _highestCondition;
uint16 _manyRooms;
uint16 _numCrumbs;
uint16 _numInv;
uint32 _crumbTimestamp;
Common::String _curFileName;
Anim *_anim;
CrumbData _breadCrumbs[MAX_CRUMBS];
DisplayMan *_graphics;
EventManager *_event;
Interface *_interface;
ButtonList _invButtonList;
ButtonList _moveButtonList;
Image *_invImages[10];
Image *_moveImages[20];
LargeSet *_conditions, *_roomsFound;
Music *_music;
Resource *_resource;
RoomData *_rooms;
TextFont *_msgFont;
SpecialLocks *_specialLocks;
Utils *_utils;
public:
LabEngine(OSystem *syst, const ADGameDescription *gameDesc);
~LabEngine() override;
Common::Error run() override;
void go();
const ADGameDescription *_gameDescription;
Common::Platform getPlatform() const;
uint32 getFeatures() const;
bool hasFeature(EngineFeature f) const override;
void changeVolume(int delta);
uint16 getDirection() { return _direction; }
/**
* Returns the current picture name.
*/
Common::String getPictName(bool useClose);
uint16 getQuarters();
void setQuarters(uint16 quarters);
void updateEvents();
void waitTOF();
Common::Error loadGameState(int slot) override;
Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave = false) override;
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
bool isMainDisplay() const { return _mainDisplay; }
private:
/**
* Checks whether all the conditions in a condition list are met.
*/
bool checkConditions(const Common::Array<int16> &cond);
/**
* Decrements the current inventory number.
*/
void decIncInv(uint16 *CurInv, bool dec);
/**
* Processes the action list.
*/
void doActions(const ActionList &actionList);
/**
* Goes through the rules if an action is taken.
*/
bool doActionRule(Common::Point pos, int16 action, int16 roomNum);
/**
* Does the work for doActionRule.
*/
bool doActionRuleSub(int16 action, int16 roomNum, const CloseData *closePtr, bool allowDefaults);
/**
* Handles monitor closeups
*/
void handleMonitorCloseup();
/**
* Goes through the rules if the user tries to go forward.
*/
bool doGoForward();
/**
* Does the journal processing.
*/
void doJournal();
/**
* Goes through the rules if the user tries to go to the main view
*/
bool doMainView();
/**
* Does the map processing.
*/
void doMap();
/**
* Does what's necessary for the monitor.
*/
void doMonitor(const Common::String &background, const Common::String &textfile, bool isinteractive, Common::Rect textRect);
/**
* Does the things to properly set up the detective notes.
*/
void doNotes();
/**
* Does the work for doActionRule.
*/
bool doOperateRuleSub(int16 itemNum, int16 roomNum, const CloseData *closePtr, bool allowDefaults);
/**
* Goes through the rules if the user tries to operate an item on an object.
*/
bool doOperateRule(Common::Point pos, int16 ItemNum);
/**
* Goes through the rules if the user tries to turn.
*/
bool doTurn(uint16 from, uint16 to);
/**
* If the user hits the "Use" button; things that can get used on themselves.
*/
bool doUse(uint16 curInv);
/**
* Does the things to properly set up the old west newspaper. Assumes that
* OpenHiRes already called.
*/
void doWestPaper();
/**
* Draws the current direction to the screen.
*/
void drawDirection(const CloseData *closePtr);
/**
* Draws the journal from page x.
*/
void drawJournal(uint16 wipenum, bool needFade);
/**
* Draws the text to the back journal screen to the appropriate Page number
*/
void drawJournalText();
/**
* Draws the map
*/
void drawMap(uint16 curRoom, uint16 curMsg, uint16 floorNum, bool fadeIn);
/**
* Draws the text for the monitor.
*/
void drawMonText(const char *text, TextFont *monitorFont, Common::Rect textRect, bool isinteractive);
/**
* Draws a room map.
*/
void drawRoomMap(uint16 curRoom, bool drawMarkFl);
/**
* Draws the message for the room.
*/
void drawRoomMessage(uint16 curInv, const CloseData *closePtr);
void drawStaticMessage(byte index);
/**
* Eats all the available messages.
*/
void eatMessages();
/**
* Goes through the list of closeups to find a match.
* @note Known bug here. If there are two objects that have closeups, and
* some of the closeups have the same hit boxes, then this returns the first
* occurrence of the object with the same hit box.
*/
const CloseData *findClosePtrMatch(const CloseData *closePtr, const CloseDataList &list);
/**
* Checks if a floor has been visited.
*/
bool floorVisited(uint16 floorNum);
/**
* New code to allow quick(er) return navigation in game.
*/
MainButton followCrumbs();
void freeMapData();
void freeScreens();
bool processEvent(MessageClass tmpClass, uint16 code, uint16 qualifier, Common::Point tmpPos,
uint16 &curInv, IntuiMessage *curMsg, bool &forceDraw, uint16 buttonId, uint16 &actionMode);
/**
* Gets the current inventory name.
*/
Common::String getInvName(uint16 curInv);
/**
* Returns the floor to show when the down arrow is pressed
* @note The original did not show all the visited floors, but we do
*/
uint16 getLowerFloor(uint16 floorNum);
/**
* Gets an object, if any, from the user's click on the screen.
*/
const CloseData *getObject(Common::Point pos, const CloseData *closePtr);
/**
* Returns the floor to show when the up arrow is pressed
* @note The original did not show all the visited floors, but we do
*/
uint16 getUpperFloor(uint16 floorNum);
/**
* Gets the current ViewDataPointer.
*/
ViewData *getViewData(uint16 roomNum, uint16 direction);
/**
* Turns the interface off.
*/
void interfaceOff();
/**
* Turns the interface on.
*/
void interfaceOn();
/**
* Loads in the data for the journal.
*/
void loadJournalData();
/**
* Loads in the map data.
*/
void loadMapData();
/**
* The main game loop.
*/
void mainGameLoop();
void showLab2Teaser();
/**
* Permanently flips the imagery of a button.
*/
void perFlipButton(uint16 buttonId);
/**
* process a arrow button movement.
*/
uint16 processArrow(uint16 curDirection, uint16 arrow);
/**
* Processes user input.
*/
void processJournal();
/**
* Processes the map.
*/
void processMap(uint16 curRoom);
/**
* Processes user input.
*/
void processMonitor(const Common::String &ntext, TextFont *monitorFont, bool isInteractive, Common::Rect textRect);
/**
* Figures out what a room's coordinates should be.
*/
Common::Rect roomCoords(uint16 curRoom);
bool saveRestoreGame();
/**
* Sets the current close up data.
*/
void setCurrentClose(Common::Point pos, const CloseData **closePtrList, bool useAbsoluteCoords, bool next=false);
/**
* Takes the currently selected item.
*/
bool takeItem(Common::Point pos);
/**
* Does the turn page wipe.
*/
void turnPage(bool fromLeft);
bool processKey(IntuiMessage *curMsg, uint32 msgClass, uint16 &qualifier, Common::Point &curPos, uint16 &curInv, bool &forceDraw, uint16 code);
void processMainButton(uint16 &curInv, uint16 &lastInv, uint16 &oldDirection, bool &forceDraw, uint16 buttonId, uint16 &actionMode);
void processAltButton(uint16 &curInv, uint16 &lastInv, uint16 buttonId, uint16 &actionMode);
void performAction(uint16 actionMode, Common::Point curPos, uint16 &curInv);
/**
* Writes the game out to disk.
*/
bool saveGame(int slot, const Common::String &desc);
/**
* Reads the game from disk.
*/
bool loadGame(int slot);
void writeSaveGameHeader(Common::OutSaveFile *out, const Common::String &saveName);
void handleTrialWarning();
};
WARN_UNUSED_RESULT bool readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &header, bool skipThumbnail = true);
} // End of namespace Lab
#endif // LAB_LAB_H

75
engines/lab/labsets.cpp Normal file
View File

@@ -0,0 +1,75 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#include "common/file.h"
#include "lab/lab.h"
#include "lab/labsets.h"
#include "lab/resource.h"
namespace Lab {
LargeSet::LargeSet(uint16 last, LabEngine *vm) : _vm(vm) {
last = (((last + 15) >> 4) << 4);
_array = new uint16[last >> 3]();
_lastElement = last;
}
LargeSet::~LargeSet() {
delete[] _array;
}
bool LargeSet::in(uint16 element) {
return ((1 << ((element - 1) % 16)) & (_array[(element - 1) >> 4])) > 0;
}
void LargeSet::inclElement(uint16 element) {
_array[(element - 1) >> 4] |= 1 << ((element - 1) % 16);
}
void LargeSet::exclElement(uint16 element) {
_array[(element - 1) >> 4] &= ~(1 << ((element - 1) % 16));
}
bool LargeSet::readInitialConditions(const Common::String &fileName) {
Common::File *file = _vm->_resource->openDataFile(fileName, MKTAG('C', 'O', 'N', '0'));
uint16 conditions = file->readUint16LE();
for (int i = 0; i < conditions; i++) {
inclElement(file->readUint16LE());
}
delete file;
return true;
}
} // End of namespace Lab

60
engines/lab/labsets.h Normal file
View File

@@ -0,0 +1,60 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#ifndef LAB_LABSETS_H
#define LAB_LABSETS_H
namespace Lab {
//---------------------------
//----- From LabSets.c ------
//---------------------------
class LabEngine;
class LargeSet {
public:
LargeSet(uint16 last, LabEngine *vm);
~LargeSet();
bool in(uint16 element);
void inclElement(uint16 element);
void exclElement(uint16 element);
bool readInitialConditions(const Common::String &fileName);
private:
LabEngine *_vm;
public:
uint16 _lastElement;
uint16 *_array;
};
} // End of namespace Lab
#endif // LAB_LABSETS_H

558
engines/lab/map.cpp Normal file
View File

@@ -0,0 +1,558 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#include "common/file.h"
#include "lab/lab.h"
#include "lab/dispman.h"
#include "lab/eventman.h"
#include "lab/image.h"
#include "lab/interface.h"
#include "lab/labsets.h"
#include "lab/music.h"
#include "lab/processroom.h"
#include "lab/resource.h"
#include "lab/utils.h"
#include "backends/keymapper/keymapper.h"
namespace Lab {
/*---------------------------------------------------------------------------*/
/*------------------------------ The Map stuff ------------------------------*/
/*---------------------------------------------------------------------------*/
enum MapFloor {
kFloorNone,
kFloorLower,
kFloorMiddle,
kFloorUpper,
kFloorMedMaze,
kFloorHedgeMaze,
kFloorSurMaze,
kFloorCarnival
};
void LabEngine::loadMapData() {
Common::File *mapImages = _resource->openDataFile("P:MapImage");
_imgMap = new Image(mapImages, this);
_imgRoom = new Image(mapImages, this);
_imgUpArrowRoom = new Image(mapImages, this);
_imgDownArrowRoom = new Image(mapImages, this);
_imgHRoom = new Image(mapImages, this);
_imgVRoom = new Image(mapImages, this);
_imgMaze = new Image(mapImages, this);
_imgHugeMaze = new Image(mapImages, this);
_imgMapX[kDirectionNorth] = new Image(mapImages, this);
_imgMapX[kDirectionEast] = new Image(mapImages, this);
_imgMapX[kDirectionSouth] = new Image(mapImages, this);
_imgMapX[kDirectionWest] = new Image(mapImages, this);
_imgPath = new Image(mapImages, this);
_imgBridge = new Image(mapImages, this);
_mapButtonList.push_back(_interface->createButton( 8, _utils->vgaScaleY(105), 0, kActionMapExit, new Image(mapImages, this), new Image(mapImages, this))); // back
_mapButtonList.push_back(_interface->createButton( 55, _utils->vgaScaleY(105), 1, kActionUpperFloor, new Image(mapImages, this), new Image(mapImages, this))); // up
_mapButtonList.push_back(_interface->createButton(101, _utils->vgaScaleY(105), 2, kActionLowerFloor, new Image(mapImages, this), new Image(mapImages, this))); // down
delete mapImages;
Common::File *mapFile = _resource->openDataFile("Lab:Maps", MKTAG('M', 'A', 'P', '0'));
updateEvents();
_maxRooms = mapFile->readUint16LE();
_maps = new MapData[_maxRooms + 1]; // will be freed when the user exits the map
for (int i = 0; i <= _maxRooms; i++) {
_maps[i]._x = mapFile->readUint16LE();
_maps[i]._y = mapFile->readUint16LE();
_maps[i]._pageNumber = mapFile->readUint16LE();
_maps[i]._specialID = (SpecialRoom) mapFile->readUint16LE();
_maps[i]._mapFlags = mapFile->readUint32LE();
}
delete mapFile;
}
void LabEngine::freeMapData() {
_interface->freeButtonList(&_mapButtonList);
delete _imgMap;
delete _imgRoom;
delete _imgUpArrowRoom;
delete _imgDownArrowRoom;
delete _imgBridge;
delete _imgHRoom;
delete _imgVRoom;
delete _imgMaze;
delete _imgHugeMaze;
delete _imgPath;
for (int i = 0; i < 4; i++)
delete _imgMapX[i];
delete[] _maps;
_imgMap = nullptr;
_imgRoom = nullptr;
_imgUpArrowRoom = nullptr;
_imgDownArrowRoom = nullptr;
_imgBridge = nullptr;
_imgHRoom = nullptr;
_imgVRoom = nullptr;
_imgMaze = nullptr;
_imgHugeMaze = nullptr;
_imgPath = nullptr;
for (int i = 0; i < 4; i++)
_imgMapX[i] = nullptr;
_maps = nullptr;
}
Common::Rect LabEngine::roomCoords(uint16 curRoom) {
Image *curRoomImg = nullptr;
switch (_maps[curRoom]._specialID) {
case kNormalRoom:
case kUpArrowRoom:
case kDownArrowRoom:
curRoomImg = _imgRoom;
break;
case kBridgeRoom:
curRoomImg = _imgBridge;
break;
case kVerticalCorridor:
curRoomImg = _imgVRoom;
break;
case kHorizontalCorridor:
curRoomImg = _imgHRoom;
break;
default:
// Some rooms (like the map) do not have an image
break;
}
int x1 = _utils->mapScaleX(_maps[curRoom]._x);
int y1 = _utils->mapScaleY(_maps[curRoom]._y);
int x2 = x1;
int y2 = y1;
if (curRoomImg) {
x2 += curRoomImg->_width;
y2 += curRoomImg->_height;
}
return Common::Rect(x1, y1, x2, y2);
}
void LabEngine::drawRoomMap(uint16 curRoom, bool drawMarkFl) {
uint16 drawX, drawY, offset;
uint16 x = _utils->mapScaleX(_maps[curRoom]._x);
uint16 y = _utils->mapScaleY(_maps[curRoom]._y);
uint32 flags = _maps[curRoom]._mapFlags;
switch (_maps[curRoom]._specialID) {
case kNormalRoom:
case kUpArrowRoom:
case kDownArrowRoom:
if (_maps[curRoom]._specialID == kNormalRoom)
_imgRoom->drawImage(x, y);
else if (_maps[curRoom]._specialID == kDownArrowRoom)
_imgDownArrowRoom->drawImage(x, y);
else
_imgUpArrowRoom->drawImage(x, y);
offset = (_imgRoom->_width - _imgPath->_width) / 2;
if ((kDoorLeftNorth & flags) && (y >= _imgPath->_height))
_imgPath->drawImage(x + offset, y - _imgPath->_height);
if (kDoorLeftSouth & flags)
_imgPath->drawImage(x + offset, y + _imgRoom->_height);
offset = (_imgRoom->_height - _imgPath->_height) / 2;
if (kDoorLeftEast & flags)
_imgPath->drawImage(x + _imgRoom->_width, y + offset);
if (kDoorLeftWest & flags)
_imgPath->drawImage(x - _imgPath->_width, y + offset);
drawX = x + (_imgRoom->_width - _imgMapX[_direction]->_width) / 2;
drawY = y + (_imgRoom->_height - _imgMapX[_direction]->_height) / 2;
break;
case kBridgeRoom:
_imgBridge->drawImage(x, y);
drawX = x + (_imgBridge->_width - _imgMapX[_direction]->_width) / 2;
drawY = y + (_imgBridge->_height - _imgMapX[_direction]->_height) / 2;
break;
case kVerticalCorridor:
_imgVRoom->drawImage(x, y);
offset = (_imgVRoom->_width - _imgPath->_width) / 2;
if (kDoorLeftNorth & flags)
_imgPath->drawImage(x + offset, y - _imgPath->_height);
if (kDoorLeftSouth & flags)
_imgPath->drawImage(x + offset, y + _imgVRoom->_height);
offset = (_imgRoom->_height - _imgPath->_height) / 2;
if (kDoorLeftEast & flags)
_imgPath->drawImage(x + _imgVRoom->_width, y + offset);
if (kDoorLeftWest & flags)
_imgPath->drawImage(x - _imgPath->_width, y + offset);
if (kDoorBottomEast & flags)
_imgPath->drawImage(x + _imgVRoom->_width, y - offset - _imgPath->_height + _imgVRoom->_height);
if (kDoorBottomWest & flags)
_imgPath->drawImage(x - _imgPath->_width, y - offset - _imgPath->_height + _imgVRoom->_height);
offset = (_imgVRoom->_height - _imgPath->_height) / 2;
if (kDoorMiddleEast & flags)
_imgPath->drawImage(x + _imgVRoom->_width, y - offset - _imgPath->_height + _imgVRoom->_height);
if (kDoorMiddleWest & flags)
_imgPath->drawImage(x - _imgPath->_width, y - offset - _imgPath->_height + _imgVRoom->_height);
drawX = x + (_imgVRoom->_width - _imgMapX[_direction]->_width) / 2;
drawY = y + (_imgVRoom->_height - _imgMapX[_direction]->_height) / 2;
break;
case kHorizontalCorridor:
_imgHRoom->drawImage(x, y);
offset = (_imgRoom->_width - _imgPath->_width) / 2;
if (kDoorLeftNorth & flags)
_imgPath->drawImage(x + offset, y - _imgPath->_height);
if (kDoorLeftSouth & flags)
_imgPath->drawImage(x + offset, y + _imgRoom->_height);
if (kDoorRightNorth & flags)
_imgPath->drawImage(x - offset - _imgPath->_width + _imgHRoom->_width, y - _imgPath->_height);
if (kDoorRightSouth & flags)
_imgPath->drawImage(x - offset - _imgPath->_width + _imgHRoom->_width, y + _imgRoom->_height);
offset = (_imgHRoom->_width - _imgPath->_width) / 2;
if (kDoorMiddleNorth & flags)
_imgPath->drawImage(x - offset - _imgPath->_width + _imgHRoom->_width, y - _imgPath->_height);
if (kDoorMiddleSouth & flags)
_imgPath->drawImage(x - offset - _imgPath->_width + _imgHRoom->_width, y + _imgRoom->_height);
offset = (_imgRoom->_height - _imgPath->_height) / 2;
if (kDoorLeftEast & flags)
_imgPath->drawImage(x + _imgHRoom->_width, y + offset);
if (kDoorLeftWest & flags)
_imgPath->drawImage(x - _imgPath->_width, y + offset);
drawX = x + (_imgHRoom->_width - _imgMapX[_direction]->_width) / 2;
drawY = y + (_imgHRoom->_height - _imgMapX[_direction]->_height) / 2;
break;
default:
return;
}
if (drawMarkFl)
_imgMapX[_direction]->drawImage(drawX, drawY);
}
bool LabEngine::floorVisited(uint16 floorNum) {
for (int i = 0; i < _maxRooms; i++) {
if ((_maps[i]._pageNumber == floorNum) && _roomsFound->in(i) && _maps[i]._x)
return true;
}
return false;
}
uint16 LabEngine::getUpperFloor(uint16 floorNum) {
if ((floorNum == kFloorCarnival) || (floorNum == kFloorNone))
return kFloorNone;
for (int i = floorNum; i < kFloorCarnival; i++)
if (floorVisited(i + 1))
return i + 1;
return kFloorNone;
}
uint16 LabEngine::getLowerFloor(uint16 floorNum) {
if ((floorNum == kFloorLower) || (floorNum == kFloorNone))
return kFloorNone;
for (int i = floorNum; i > kFloorLower; i--)
if (floorVisited(i - 1))
return i - 1;
return kFloorNone;
}
void LabEngine::drawMap(uint16 curRoom, uint16 curMsg, uint16 floorNum, bool fadeIn) {
_graphics->rectFill(0, 0, _graphics->_screenWidth - 1, _graphics->_screenHeight - 1, 0);
_imgMap->drawImage(0, 0);
_interface->drawButtonList(&_mapButtonList);
for (int i = 1; i <= _maxRooms; i++) {
if ((_maps[i]._pageNumber == floorNum) && _roomsFound->in(i) && _maps[i]._x) {
drawRoomMap(i, (bool)(i == curRoom));
}
}
updateEvents();
// Makes sure the X is drawn in corridors
// NOTE: this here on purpose just in case there's some weird
// condition, like the surreal maze where there are no rooms
if ((_maps[curRoom]._pageNumber == floorNum) && _roomsFound->in(curRoom) && _maps[curRoom]._x)
drawRoomMap(curRoom, true);
_interface->toggleButton(_interface->getButton(1), 12, (getUpperFloor(floorNum) != kFloorNone)); // up button
_interface->toggleButton(_interface->getButton(2), 12, (getLowerFloor(floorNum) != kFloorNone)); // down button
// Labyrinth specific code
if (floorNum == kFloorLower) {
if (floorVisited(kFloorSurMaze))
_imgMaze->drawImage(_utils->mapScaleX(538), _utils->mapScaleY(277));
} else if (floorNum == kFloorMiddle) {
if (floorVisited(kFloorCarnival))
_imgMaze->drawImage(_utils->mapScaleX(358), _utils->mapScaleY(72));
if (floorVisited(kFloorMedMaze))
_imgMaze->drawImage(_utils->mapScaleX(557), _utils->mapScaleY(325));
} else if (floorNum == kFloorUpper) {
if (floorVisited(kFloorHedgeMaze))
_imgHugeMaze->drawImage(_utils->mapScaleX(524), _utils->mapScaleY(97));
} else if (floorNum == kFloorSurMaze) {
Common::Rect textRect = Common::Rect(_utils->mapScaleX(360), 0, _utils->mapScaleX(660), _utils->mapScaleY(450));
_graphics->flowText(_msgFont, 0, 7, 0, true, true, true, true, textRect, _resource->getStaticText(kTextSurmazeMessage).c_str());
}
if ((floorNum >= kFloorLower) && (floorNum <= kFloorCarnival)) {
_graphics->flowText(_msgFont, 0, 5, 3, true, true, true, true, _utils->vgaRectScale(14, 75, 134, 97), _resource->getStaticText(floorNum - 1).c_str());
}
if (!_rooms[curMsg]._roomMsg.empty())
_graphics->flowText(_msgFont, 0, 5, 3, true, true, true, true, _utils->vgaRectScale(14, 148, 134, 186), _rooms[curMsg]._roomMsg.c_str());
if (fadeIn)
_graphics->fade(true);
}
void LabEngine::processMap(uint16 curRoom) {
byte place = 1;
uint16 curMsg = curRoom;
uint16 curFloor = _maps[curRoom]._pageNumber;
while (1) {
IntuiMessage *msg = _event->getMsg();
if (shouldQuit()) {
_quitLab = true;
return;
}
updateEvents();
_graphics->screenUpdate();
_system->delayMillis(10);
if (!msg) {
updateEvents();
byte newcolor[3];
if (place <= 14) {
newcolor[0] = 14 << 2;
newcolor[1] = place << 2;
newcolor[2] = newcolor[1];
} else {
newcolor[0] = 14 << 2;
newcolor[1] = (28 - place) << 2;
newcolor[2] = newcolor[1];
}
waitTOF();
_graphics->writeColorRegs(newcolor, 1, 1);
_interface->handlePressedButton();
waitTOF();
place++;
if (place >= 28)
place = 1;
} else {
uint32 msgClass = msg->_msgClass;
uint16 msgCode = msg->_code;
uint16 mouseX = msg->_mouse.x;
uint16 mouseY = msg->_mouse.y;
if ((msgClass == kMessageRightClick) || ((msgClass == kMessageAction) && (msgCode == kActionExit)))
return;
if (msgClass == kMessageButtonUp) {
if (msgCode == 0) {
// Quit menu button
return;
} else if (msgCode == 1) {
// Up arrow
uint16 upperFloor = getUpperFloor(curFloor);
if (upperFloor != kFloorNone) {
curFloor = upperFloor;
_graphics->fade(false);
drawMap(curRoom, curMsg, curFloor, false);
_graphics->fade(true);
}
} else if (msgCode == 2) {
// Down arrow
uint16 lowerFloor = getLowerFloor(curFloor);
if (lowerFloor != kFloorNone) {
curFloor = lowerFloor;
_graphics->fade(false);
drawMap(curRoom, curMsg, curFloor, false);
_graphics->fade(true);
}
}
} else if (msgClass == kMessageLeftClick) {
if ((curFloor == kFloorLower) && _utils->mapRectScale(538, 277, 633, 352).contains(mouseX, mouseY)
&& floorVisited(kFloorSurMaze)) {
curFloor = kFloorSurMaze;
_graphics->fade(false);
drawMap(curRoom, curMsg, curFloor, false);
_graphics->fade(true);
} else if ((curFloor == kFloorMiddle) && _utils->mapRectScale(358, 71, 452, 147).contains(mouseX, mouseY)
&& floorVisited(kFloorCarnival)) {
curFloor = kFloorCarnival;
_graphics->fade(false);
drawMap(curRoom, curMsg, curFloor, false);
_graphics->fade(true);
} else if ((curFloor == kFloorMiddle) && _utils->mapRectScale(557, 325, 653, 401).contains(mouseX, mouseY)
&& floorVisited(kFloorMedMaze)) {
curFloor = kFloorMedMaze;
_graphics->fade(false);
drawMap(curRoom, curMsg, curFloor, false);
_graphics->fade(true);
} else if ((curFloor == kFloorUpper) && _utils->mapRectScale(524, 97, 645, 207).contains(mouseX, mouseY)
&& floorVisited(kFloorHedgeMaze)) {
curFloor = kFloorHedgeMaze;
_graphics->fade(false);
drawMap(curRoom, curMsg, curFloor, false);
_graphics->fade(true);
} else if (mouseX > _utils->mapScaleX(314)) {
uint16 oldMsg = curMsg;
Common::Rect curCoords;
for (int i = 1; i <= _maxRooms; i++) {
curCoords = roomCoords(i);
if ((_maps[i]._pageNumber == curFloor)
&& _roomsFound->in(i) && curCoords.contains(Common::Point(mouseX, mouseY))) {
curMsg = i;
}
}
if (oldMsg != curMsg) {
if (!_rooms[curMsg]._roomMsg.empty())
_resource->readViews(curMsg);
const char *sptr;
if ((sptr = _rooms[curMsg]._roomMsg.c_str())) {
_graphics->rectFillScaled(13, 148, 135, 186, 3);
_graphics->flowText(_msgFont, 0, 5, 3, true, true, true, true, _utils->vgaRectScale(14, 148, 134, 186), sptr);
if (_maps[oldMsg]._pageNumber == curFloor)
drawRoomMap(oldMsg, (bool)(oldMsg == curRoom));
curCoords = roomCoords(curMsg);
int right = (curCoords.left + curCoords.right) / 2;
int left = right - 1;
int top, bottom;
top = bottom = (curCoords.top + curCoords.bottom) / 2;
if ((curMsg != curRoom) && (_maps[curMsg]._pageNumber == curFloor))
_graphics->rectFill(left, top, right, bottom, 1);
}
}
}
}
_graphics->screenUpdate();
}
} // while
}
void LabEngine::doMap() {
static uint16 amigaMapPalette[] = {
0x0BA8, 0x0C11, 0x0A74, 0x0076,
0x0A96, 0x0DCB, 0x0CCA, 0x0222,
0x0444, 0x0555, 0x0777, 0x0999,
0x0AAA, 0x0ED0, 0x0EEE, 0x0694
};
_graphics->_fadePalette = amigaMapPalette;
updateEvents();
loadMapData();
_graphics->blackAllScreen();
_interface->attachButtonList(&_mapButtonList);
drawMap(_roomNum, _roomNum, _maps[_roomNum]._pageNumber, true);
_event->mouseShow();
_graphics->screenUpdate();
processMap(_roomNum);
_event->mouseHide();
_interface->attachButtonList(nullptr);
_graphics->fade(false);
_graphics->blackAllScreen();
_graphics->rectFill(0, 0, _graphics->_screenWidth - 1, _graphics->_screenHeight - 1, 0);
freeMapData();
_event->mouseShow();
_graphics->screenUpdate();
}
} // End of namespace Lab

432
engines/lab/metaengine.cpp Normal file
View File

@@ -0,0 +1,432 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#include "lab/lab.h"
#include "engines/advancedDetector.h"
#include "common/translation.h"
#include "backends/keymapper/action.h"
#include "backends/keymapper/keymapper.h"
#include "backends/keymapper/standard-actions.h"
namespace Lab {
Common::Platform LabEngine::getPlatform() const {
return _gameDescription->platform;
}
uint32 LabEngine::getFeatures() const {
return _gameDescription->flags | _extraGameFeatures;
}
} // End of namespace Lab
class LabMetaEngine : public AdvancedMetaEngine<ADGameDescription> {
public:
const char *getName() const override {
return "lab";
}
Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override {
*engine = new Lab::LabEngine(syst, desc);
return Common::kNoError;
}
bool hasFeature(MetaEngineFeature f) const override;
SaveStateList listSaves(const char *target) const override;
int getMaximumSaveSlot() const override;
bool removeSaveState(const char *target, int slot) const override;
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
Common::KeymapArray initKeymaps(const char *target) const override;
};
bool LabMetaEngine::hasFeature(MetaEngineFeature f) const {
return
(f == kSupportsListSaves) ||
(f == kSupportsLoadingDuringStartup) ||
(f == kSupportsDeleteSave) ||
(f == kSavesSupportMetaInfo) ||
(f == kSavesSupportThumbnail) ||
(f == kSavesSupportCreationDate) ||
(f == kSavesSupportPlayTime) ||
(f == kSimpleSavesNames);
}
bool Lab::LabEngine::hasFeature(EngineFeature f) const {
return
(f == kSupportsReturnToLauncher) ||
(f == kSupportsLoadingDuringRuntime) ||
(f == kSupportsSavingDuringRuntime);
}
SaveStateList LabMetaEngine::listSaves(const char *target) const {
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
Lab::SaveGameHeader header;
Common::String pattern = target;
pattern += ".###";
Common::StringArray filenames = saveFileMan->listSavefiles(pattern.c_str());
SaveStateList saveList;
for (const auto &filename : filenames) {
// Obtain the last 3 digits of the filename, since they correspond to the save slot
int slotNum = atoi(filename.c_str() + filename.size() - 3);
if (slotNum >= 0 && slotNum <= 999) {
Common::InSaveFile *in = saveFileMan->openForLoading(filename);
if (in) {
if (Lab::readSaveGameHeader(in, header))
saveList.push_back(SaveStateDescriptor(this, slotNum, header._descr.getDescription()));
delete in;
}
}
}
// Sort saves based on slot number.
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
int LabMetaEngine::getMaximumSaveSlot() const {
return 999;
}
bool LabMetaEngine::removeSaveState(const char *target, int slot) const {
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
return saveFileMan->removeSavefile(Common::String::format("%s.%03u", target, slot));
}
SaveStateDescriptor LabMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
Common::String filename = Common::String::format("%s.%03u", target, slot);
Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename.c_str());
if (in) {
Lab::SaveGameHeader header;
bool successfulRead = Lab::readSaveGameHeader(in, header, false);
delete in;
if (successfulRead) {
SaveStateDescriptor desc(this, slot, header._descr.getDescription());
return header._descr;
}
}
return SaveStateDescriptor();
}
Common::KeymapArray LabMetaEngine::initKeymaps(const char *target) const {
using namespace Common;
using namespace Lab;
Keymap *engineKeymap = new Keymap(Keymap::kKeymapTypeGame, "lab-default", _("Default keymappings"));
Keymap *exitKeymap = new Keymap(Keymap::kKeymapTypeGame, "exit", _("Exit keymappings"));
Keymap *quitDialogKeymap = new Keymap(Keymap::kKeymapTypeGame, "quit-dialog", _("Quit dialog keymappings"));
Keymap *gameKeymap = new Keymap(Keymap::kKeymapTypeGame, "game-shortcuts", _("Game keymappings"));
Keymap *invKeymap = new Keymap(Keymap::kKeymapTypeGame, "inventory", _("Inventory keymappings"));
Keymap *introKeymap = new Keymap(Keymap::kKeymapTypeGame, "intro", _("Intro keymappings"));
Keymap *mapKeymap = new Keymap(Keymap::kKeymapTypeGame, "map", _("Map keymappings"));
Keymap *journalKeymap = new Keymap(Keymap::kKeymapTypeGame, "journal", _("Journal keymappings"));
Common::Action *act;
act = new Common::Action(kStandardActionLeftClick, _("Interact"));
act->setLeftClickEvent();
act->addDefaultInputMapping("MOUSE_LEFT");
act->addDefaultInputMapping("JOY_A");
engineKeymap->addAction(act);
act = new Common::Action(kStandardActionRightClick, _("Exit"));
act->setRightClickEvent();
act->addDefaultInputMapping("MOUSE_RIGHT");
act->addDefaultInputMapping("JOY_B");
engineKeymap->addAction(act);
act = new Common::Action("QUIT", _("Quit"));
act->setCustomEngineActionEvent(kActionQuit);
act->addDefaultInputMapping("q");
act->addDefaultInputMapping("x");
act->addDefaultInputMapping("JOY_DOWN");
engineKeymap->addAction(act);
act = new Common::Action("RAISESOUND", _("Raise the sound volume"));
act->setCustomEngineActionEvent(kActionSoundRaise);
act->addDefaultInputMapping("RIGHTBRACKET");
act->addDefaultInputMapping("JOY_LEFT_SHOULDER");
engineKeymap->addAction(act);
act = new Common::Action("LOWERSOUND", _("Lower the sound volume"));
act->setCustomEngineActionEvent(kActionSoundLower);
act->addDefaultInputMapping("LEFTBRACKET");
act->addDefaultInputMapping("JOY_RIGHT_SHOULDER");
engineKeymap->addAction(act);
act = new Common::Action("EXIT", _("Exit"));
act->setCustomEngineActionEvent(kActionExit);
act->addDefaultInputMapping("ESCAPE");
act->addDefaultInputMapping("JOY_Y");
exitKeymap->addAction(act);
act = new Common::Action("YES", _("Yes"));
act->setCustomEngineActionEvent(kActionQuitDialogYes);
act->addDefaultInputMapping("y");
act->addDefaultInputMapping("q");
act->addDefaultInputMapping("JOY_A");
quitDialogKeymap->addAction(act);
act = new Common::Action("NO", _("No"));
act->setCustomEngineActionEvent(kActionQuitDialogNo);
act->addDefaultInputMapping("JOY_B");
quitDialogKeymap->addAction(act);
act = new Common::Action("SKIP_INTRO", _("Skip intro"));
act->setCustomEngineActionEvent(kActionSkipIntro);
act->addDefaultInputMapping("MOUSE_RIGHT");
act->addDefaultInputMapping("ESCAPE");
act->addDefaultInputMapping("JOY_X");
introKeymap->addAction(act);
act = new Common::Action("INTERACT", _("Interact with object with chosen action"));
act->setCustomEngineActionEvent(kActionInteract);
act->addDefaultInputMapping("RETURN");
gameKeymap->addAction(act);
act = new Common::Action("TAKE", _("Take object"));
act->setCustomEngineActionEvent(kActionTake);
act->addDefaultInputMapping("t");
act->addDefaultInputMapping("1");
gameKeymap->addAction(act);
act = new Common::Action("MOVE", _("Move or manipulate object"));
act->setCustomEngineActionEvent(kActionMove);
act->addDefaultInputMapping("m");
act->addDefaultInputMapping("2");
gameKeymap->addAction(act);
act = new Common::Action("OPEN", _("Open door or object"));
act->setCustomEngineActionEvent(kActionOpen);
act->addDefaultInputMapping("o");
act->addDefaultInputMapping("3");
gameKeymap->addAction(act);
act = new Common::Action("CLOSE", _("Close door or object"));
act->setCustomEngineActionEvent(kActionClose);
act->addDefaultInputMapping("c");
act->addDefaultInputMapping("4");
gameKeymap->addAction(act);
act = new Common::Action("LOOK", _("Look at object close-up"));
act->setCustomEngineActionEvent(kActionLook);
act->addDefaultInputMapping("l");
act->addDefaultInputMapping("5");
gameKeymap->addAction(act);
act = new Common::Action("INVENTORY", _("Switch to inventory display"));
act->setCustomEngineActionEvent(kActionInv);
act->addDefaultInputMapping("i");
act->addDefaultInputMapping("6");
gameKeymap->addAction(act);
act = new Common::Action("LEFT", _("Turn left"));
act->setCustomEngineActionEvent(kActionLeft);
act->addDefaultInputMapping("LEFT");
act->addDefaultInputMapping("7");
act->addDefaultInputMapping("JOY_LEFT");
gameKeymap->addAction(act);
act = new Common::Action("FORWARD", _("Walk forward"));
act->setCustomEngineActionEvent(kActionForward);
act->addDefaultInputMapping("UP");
act->addDefaultInputMapping("8");
act->addDefaultInputMapping("JOY_UP");
gameKeymap->addAction(act);
act = new Common::Action("RIGHT", _("Turn right"));
act->setCustomEngineActionEvent(kActionRight);
act->addDefaultInputMapping("RIGHT");
act->addDefaultInputMapping("9");
act->addDefaultInputMapping("JOY_RIGHT");
gameKeymap->addAction(act);
act = new Common::Action("MAP", _("Show map"));
act->setCustomEngineActionEvent(kActionMap);
act->addDefaultInputMapping("p");
act->addDefaultInputMapping("0");
gameKeymap->addAction(act);
act = new Common::Action("FOCUS", _("Move focus to interactive object"));
act->setCustomEngineActionEvent(kActionFocusOnNextInteractiveItem);
act->addDefaultInputMapping("TAB");
act->addDefaultInputMapping("JOY_X");
gameKeymap->addAction(act);
if (parsePlatform(ConfMan.get("platform")) == Common::kPlatformWindows) {
act = new Common::Action("STARTBREADCRUMB", _("Start dropping virtual bread crumbs"));
act->setCustomEngineActionEvent(kActionDropBreadcrumb);
act->addDefaultInputMapping("b");
act->addDefaultInputMapping("JOY_LEFT_TRIGGER");
gameKeymap->addAction(act);
act = new Common::Action("FOLLOWBREADCRUMBS", _("Follow virtual bread crumbs"));
act->setCustomEngineActionEvent(kActionFollowBreadcrumbs);
act->addDefaultInputMapping("f");
act->addDefaultInputMapping("JOY_RIGHT_TRIGGER");
gameKeymap->addAction(act);
act = new Common::Action("RUNFOLLOWINGBREADCRUMBS", _("Run while following virtual bread crumbs"));
act->setCustomEngineActionEvent(kActionRunWhileFollowingBreadcrumbs);
act->addDefaultInputMapping("r");
act->addDefaultInputMapping("JOY_X");
gameKeymap->addAction(act);
}
act = new Common::Action("MAINDISPLAY", _("Switch to main display"));
act->setCustomEngineActionEvent(kActionMainDisplay);
act->addDefaultInputMapping("ESCAPE");
act->addDefaultInputMapping("1");
invKeymap->addAction(act);
act = new Common::Action("SAVELOAD", _("Open save / load dialog"));
act->setCustomEngineActionEvent(kActionSaveLoad);
act->addDefaultInputMapping("g");
act->addDefaultInputMapping("2");
invKeymap->addAction(act);
act = new Common::Action("USE", _("Use"));
act->setCustomEngineActionEvent(kActionUse);
act->addDefaultInputMapping("u");
act->addDefaultInputMapping("3");
invKeymap->addAction(act);
act = new Common::Action("LOOKSCENE", _("Look at scene"));
act->setCustomEngineActionEvent(kActionInvLook);
act->addDefaultInputMapping("l");
act->addDefaultInputMapping("4");
invKeymap->addAction(act);
act = new Common::Action("PREV", _("Previous inventory item"));
act->setCustomEngineActionEvent(kActionPrev);
act->addDefaultInputMapping("LEFT");
act->addDefaultInputMapping("5");
act->addDefaultInputMapping("JOY_LEFT");
invKeymap->addAction(act);
act = new Common::Action("NEXT", _("Next inventory item"));
act->setCustomEngineActionEvent(kActionNext);
act->addDefaultInputMapping("RIGHT");
act->addDefaultInputMapping("6");
act->addDefaultInputMapping("JOY_RIGHT");
invKeymap->addAction(act);
if (parsePlatform(ConfMan.get("platform")) == Common::kPlatformWindows) {
act = new Common::Action("STARTBREADCRUMB", _("Start dropping virtual bread crumbs"));
act->setCustomEngineActionEvent(kActionDropBreadcrumb);
act->addDefaultInputMapping("b");
act->addDefaultInputMapping("7");
act->addDefaultInputMapping("JOY_LEFT_TRIGGER");
invKeymap->addAction(act);
act = new Common::Action("FOLLOWBREADCRUMBS", _("Follow virtual bread crumbs"));
act->setCustomEngineActionEvent(kActionFollowBreadcrumbs);
act->addDefaultInputMapping("f");
act->addDefaultInputMapping("8");
act->addDefaultInputMapping("JOY_RIGHT_TRIGGER");
invKeymap->addAction(act);
act = new Common::Action("RUNFOLLOWINGBREADCRUMBS", _("Run while following virtual bread crumbs"));
act->setCustomEngineActionEvent(kActionRunWhileFollowingBreadcrumbs);
act->addDefaultInputMapping("r");
act->addDefaultInputMapping("JOY_X");
invKeymap->addAction(act);
}
act = new Common::Action("EXITMAP", _("Exit map display"));
act->setCustomEngineActionEvent(kActionMapExit);
act->addDefaultInputMapping("ESCAPE");
act->addDefaultInputMapping("1");
mapKeymap->addAction(act);
act = new Common::Action("UPLEVEL", _("Up one level"));
act->setCustomEngineActionEvent(kActionUpperFloor);
act->addDefaultInputMapping("UP");
act->addDefaultInputMapping("2");
mapKeymap->addAction(act);
act = new Common::Action("DOWNLEVEL", _("Down one level"));
act->setCustomEngineActionEvent(kActionLowerFloor);
act->addDefaultInputMapping("DOWN");
act->addDefaultInputMapping("3");
mapKeymap->addAction(act);
act = new Common::Action("JOURNALBACK", _("Go back in journal"));
act->setCustomEngineActionEvent(kActionJournalBack);
act->addDefaultInputMapping("LEFT");
act->addDefaultInputMapping("1");
journalKeymap->addAction(act);
act = new Common::Action("EXITJOURNAL", _("Exit journal"));
act->setCustomEngineActionEvent(kActionJournalExit);
act->addDefaultInputMapping("2");
journalKeymap->addAction(act);
act = new Common::Action("JOURNALFORWARD", _("Go forward in journal"));
act->setCustomEngineActionEvent(kActionJournalForward);
act->addDefaultInputMapping("RIGHT");
act->addDefaultInputMapping("3");
journalKeymap->addAction(act);
KeymapArray keymaps(8);
keymaps[0] = engineKeymap;
keymaps[1] = exitKeymap;
keymaps[2] = gameKeymap;
keymaps[3] = invKeymap;
keymaps[4] = introKeymap;
keymaps[5] = mapKeymap;
keymaps[6] = quitDialogKeymap;
keymaps[7] = journalKeymap;
invKeymap->setEnabled(false);
introKeymap->setEnabled(false);
mapKeymap->setEnabled(false);
quitDialogKeymap->setEnabled(false);
journalKeymap->setEnabled(false);
return keymaps;
}
#if PLUGIN_ENABLED_DYNAMIC(LAB)
REGISTER_PLUGIN_DYNAMIC(LAB, PLUGIN_TYPE_ENGINE, LabMetaEngine);
#else
REGISTER_PLUGIN_STATIC(LAB, PLUGIN_TYPE_ENGINE, LabMetaEngine);
#endif

33
engines/lab/module.mk Normal file
View File

@@ -0,0 +1,33 @@
MODULE := engines/lab
MODULE_OBJS := \
anim.o \
console.o \
dispman.o \
engine.o \
eventman.o \
image.o \
interface.o \
intro.o \
lab.o \
labsets.o \
map.o \
metaengine.o \
music.o \
processroom.o \
resource.o \
savegame.o \
special.o \
speciallocks.o \
utils.o
# This module can be built as a plugin
ifeq ($(ENABLE_LAB), DYNAMIC_PLUGIN)
PLUGIN := 1
endif
# Include common rules
include $(srcdir)/rules.mk
# Detection objects
DETECT_OBJS += $(MODULE)/detection.o

170
engines/lab/music.cpp Normal file
View File

@@ -0,0 +1,170 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#include "common/file.h"
#include "audio/audiostream.h"
#include "audio/decoders/raw.h"
#include "lab/lab.h"
#include "lab/anim.h"
#include "lab/eventman.h"
#include "lab/music.h"
#include "lab/resource.h"
namespace Lab {
#define CLOWNROOM 123
#define DIMROOM 80
Music::Music(LabEngine *vm) : _vm(vm) {
_musicFile = nullptr;
_storedPos = 0;
}
byte Music::getSoundFlags() {
byte soundFlags = Audio::FLAG_LITTLE_ENDIAN;
if (_vm->getPlatform() == Common::kPlatformWindows)
soundFlags |= Audio::FLAG_16BITS;
else if (_vm->getPlatform() == Common::kPlatformDOS)
soundFlags |= Audio::FLAG_UNSIGNED;
return soundFlags;
}
void Music::loadSoundEffect(const Common::String &filename, bool loop, bool waitTillFinished) {
stopSoundEffect();
Common::File *file = _vm->_resource->openDataFile(filename, MKTAG('D', 'I', 'F', 'F'));
if (!file)
return;
_vm->_anim->_doBlack = false;
uint32 magicBytes = file->readUint32LE();
if (magicBytes != 1219009121) {
warning("readSound: Bad signature, skipping");
return;
}
uint32 soundTag = file->readUint32LE();
uint32 soundSize = file->readUint32LE();
if (soundTag != 0)
return;
file->skip(soundSize); // skip the header
while (soundTag != 65535) {
_vm->updateEvents();
soundTag = file->readUint32LE();
soundSize = file->readUint32LE() - 8;
if ((soundTag == 30) || (soundTag == 31)) {
if (waitTillFinished) {
while (isSoundEffectActive()) {
_vm->updateEvents();
_vm->waitTOF();
}
}
file->skip(4);
uint16 sampleRate = file->readUint16LE();
file->skip(2);
playSoundEffect(sampleRate, soundSize, loop, file);
} else if (soundTag == 65535) {
if (waitTillFinished) {
while (isSoundEffectActive()) {
_vm->updateEvents();
_vm->waitTOF();
}
}
} else
file->skip(soundSize);
}
}
void Music::playSoundEffect(uint16 sampleSpeed, uint32 length, bool loop, Common::File *dataFile) {
stopSoundEffect();
// NOTE: We need to use malloc(), cause this will be freed with free()
// by the music code
byte *soundData = (byte *)malloc(length);
dataFile->read(soundData, length);
Audio::SeekableAudioStream *audioStream = Audio::makeRawStream((const byte *)soundData, length, MAX<uint16>(sampleSpeed, 4000), getSoundFlags());
_vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandle, new Audio::LoopingAudioStream(audioStream, (loop) ? 0 : 1));
}
void Music::stopSoundEffect() {
if (isSoundEffectActive())
_vm->_mixer->stopHandle(_sfxHandle);
}
bool Music::isSoundEffectActive() const {
return _vm->_mixer->isSoundHandleActive(_sfxHandle);
}
void Music::changeMusic(const Common::String &filename, bool storeCurPos, bool seektoStoredPos) {
if (storeCurPos)
_storedPos = _musicFile->pos();
stopSoundEffect();
freeMusic();
_musicFile = _vm->_resource->openDataFile(filename);
if (seektoStoredPos)
_musicFile->seek(_storedPos);
Audio::SeekableAudioStream *audioStream = Audio::makeRawStream(_musicFile, 15000, getSoundFlags());
_vm->_mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, new Audio::LoopingAudioStream(audioStream, 0, DisposeAfterUse::YES, false));
}
void Music::resetMusic(bool seektoStoredPos) {
if (_vm->getPlatform() != Common::kPlatformAmiga)
changeMusic("Music:BackGrou", false, seektoStoredPos);
else
changeMusic("Music:BackGround", false, seektoStoredPos);
}
void Music::checkRoomMusic(uint16 prevRoom, uint16 newRoom) {
if (newRoom == CLOWNROOM)
changeMusic("Music:Laugh", true, false);
else if (newRoom == DIMROOM)
changeMusic("Music:Rm81", true, false);
else if (prevRoom == CLOWNROOM || prevRoom == DIMROOM)
resetMusic(true);
}
void Music::freeMusic() {
_vm->_mixer->stopHandle(_musicHandle);
_vm->_mixer->stopHandle(_sfxHandle);
_musicFile = nullptr;
}
} // End of namespace Lab

95
engines/lab/music.h Normal file
View File

@@ -0,0 +1,95 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#ifndef LAB_MUSIC_H
#define LAB_MUSIC_H
#include "audio/mixer.h"
namespace Common {
class File;
}
namespace Lab {
class LabEngine;
//---------------------------
//----- From LabMusic.c -----
//---------------------------
#define MAXBUFFERS 5
class Music {
private:
LabEngine *_vm;
Common::File *_musicFile;
uint32 _storedPos;
Audio::SoundHandle _musicHandle;
Audio::SoundHandle _sfxHandle;
private:
byte getSoundFlags();
public:
Music(LabEngine *vm);
/**
* Changes the background music to something else.
*/
void changeMusic(const Common::String &filename, bool storeCurPos, bool seektoStoredPos);
void resetMusic(bool seekToStoredPos);
/**
* Checks the music that should be playing in a particular room.
*/
void checkRoomMusic(uint16 prevRoom, uint16 newRoom);
/**
* Frees up the music buffers and closes the file.
*/
void freeMusic();
bool isSoundEffectActive() const;
void playSoundEffect(uint16 sampleSpeed, uint32 length, bool loop, Common::File *dataFile);
/**
* Reads in a sound effect file. Ignores any graphics.
*/
void loadSoundEffect(const Common::String &filename, bool loop, bool waitTillFinished);
void stopSoundEffect();
};
} // End of namespace Lab
#endif // LAB_MUSIC_H

623
engines/lab/processroom.cpp Normal file
View File

@@ -0,0 +1,623 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#include "common/translation.h"
#include "gui/message.h"
#include "lab/lab.h"
#include "lab/anim.h"
#include "lab/dispman.h"
#include "lab/labsets.h"
#include "lab/music.h"
#include "lab/processroom.h"
#include "lab/resource.h"
#include "lab/utils.h"
namespace Lab {
#define NOFILE "no file"
bool LabEngine::checkConditions(const Common::Array<int16> &condition) {
for (unsigned int i = 0; i < condition.size(); ++i)
if (!_conditions->in(condition[i]))
return false;
return true;
}
ViewData *LabEngine::getViewData(uint16 roomNum, uint16 direction) {
if (_rooms[roomNum]._roomMsg.empty())
_resource->readViews(roomNum);
ViewDataList &views = _rooms[roomNum]._view[direction];
for (auto &view : views) {
if (checkConditions(view._condition))
return &view;
}
error("No view with matching condition found");
}
const CloseData *LabEngine::getObject(Common::Point pos, const CloseData *closePtr) {
const CloseDataList *list;
if (!closePtr)
list = &(getViewData(_roomNum, _direction)->_closeUps);
else
list = &(closePtr->_subCloseUps);
for (auto &closeData : *list) {
Common::Rect objRect = _utils->rectScale(closeData._x1, closeData._y1, closeData._x2, closeData._y2);
if (objRect.contains(pos))
return &closeData;
}
return nullptr;
}
const CloseData *LabEngine::findClosePtrMatch(const CloseData *closePtr, const CloseDataList &list) {
for (const auto &closeData : list) {
if ((closePtr->_x1 == closeData._x1) && (closePtr->_x2 == closeData._x2) &&
(closePtr->_y1 == closeData._y1) && (closePtr->_y2 == closeData._y2) &&
(closePtr->_depth == closeData._depth))
return &closeData;
const CloseData *resClosePtr = findClosePtrMatch(closePtr, closeData._subCloseUps);
if (resClosePtr)
return resClosePtr;
}
return nullptr;
}
Common::String LabEngine::getPictName(bool useClose) {
ViewData *viewPtr = getViewData(_roomNum, _direction);
if (useClose && _closeDataPtr) {
_closeDataPtr = findClosePtrMatch(_closeDataPtr, viewPtr->_closeUps);
if (_closeDataPtr)
return _closeDataPtr->_graphicName;
}
return viewPtr->_graphicName;
}
void LabEngine::drawDirection(const CloseData *closePtr) {
if (closePtr && !closePtr->_message.empty()) {
_graphics->drawMessage(closePtr->_message, false);
return;
}
Common::String message;
if (!_rooms[_roomNum]._roomMsg.empty())
message = _rooms[_roomNum]._roomMsg + ", ";
if (_direction == kDirectionNorth)
message += _resource->getStaticText(kTextFacingNorth);
else if (_direction == kDirectionEast)
message += _resource->getStaticText(kTextFacingEast);
else if (_direction == kDirectionSouth)
message += _resource->getStaticText(kTextFacingSouth);
else if (_direction == kDirectionWest)
message += _resource->getStaticText(kTextFacingWest);
_graphics->drawMessage(message, false);
}
uint16 LabEngine::processArrow(uint16 curDirection, uint16 arrow) {
if (arrow == 1) { // Forward
uint16 room = _rooms[_roomNum]._doors[curDirection];
if (room != 0) {
_music->checkRoomMusic(_roomNum, room);
_roomNum = room;
}
return curDirection;
} else if (arrow == 0) { // Left
if (curDirection == kDirectionNorth)
return kDirectionWest;
else if (curDirection == kDirectionWest)
return kDirectionSouth;
else if (curDirection == kDirectionSouth)
return kDirectionEast;
else
return kDirectionNorth;
} else if (arrow == 2) { // Right
if (curDirection == kDirectionNorth)
return kDirectionEast;
else if (curDirection == kDirectionEast)
return kDirectionSouth;
else if (curDirection == kDirectionSouth)
return kDirectionWest;
else
return kDirectionNorth;
}
// Should never reach here!
return curDirection;
}
void LabEngine::setCurrentClose(Common::Point pos, const CloseData **closePtrList, bool useAbsoluteCoords, bool next) {
const CloseDataList *list;
if (!*closePtrList)
list = &(getViewData(_roomNum, _direction)->_closeUps);
else
list = &((*closePtrList)->_subCloseUps);
CloseDataList::const_iterator closePtr;
for (closePtr = list->begin(); closePtr != list->end(); ++closePtr) {
Common::Rect target;
if (!useAbsoluteCoords)
target = Common::Rect(closePtr->_x1, closePtr->_y1, closePtr->_x2, closePtr->_y2);
else
target = _utils->rectScale(closePtr->_x1, closePtr->_y1, closePtr->_x2, closePtr->_y2);
if (target.contains(pos) && (next || !closePtr->_graphicName.empty())) {
if (next) {
// cycle to the next one
++closePtr;
if (closePtr == list->end())
closePtr = list->begin();
}
*closePtrList = &(*closePtr);
return;
}
}
// If we got here, no match was found. If we want the "next" close-up,
// return the first one in the list, if any.
if (next) {
if (!list->empty())
*closePtrList = &(*list->begin());
}
}
bool LabEngine::takeItem(Common::Point pos) {
const CloseDataList *list;
if (!_closeDataPtr) {
list = &(getViewData(_roomNum, _direction)->_closeUps);
} else if (_closeDataPtr->_closeUpType < 0) {
_conditions->inclElement(abs(_closeDataPtr->_closeUpType));
return true;
} else
list = &(_closeDataPtr->_subCloseUps);
for (auto &closeData : *list) {
Common::Rect objRect = _utils->rectScale(closeData._x1, closeData._y1, closeData._x2, closeData._y2);
if (objRect.contains(pos) && (closeData._closeUpType < 0)) {
_conditions->inclElement(abs(closeData._closeUpType));
return true;
}
}
return false;
}
void LabEngine::doActions(const ActionList &actionList) {
for (const auto &action : actionList) {
updateEvents();
if (_quitLab || shouldQuit())
return;
switch (action._actionType) {
case kActionPlaySound:
_music->loadSoundEffect(action._messages[0], false, true);
break;
case kActionPlaySoundNoWait: // only used in scene 7 (street, when teleporting to the surreal maze)
_music->loadSoundEffect(action._messages[0], false, false);
break;
case kActionPlaySoundLooping:
_music->loadSoundEffect(action._messages[0], true, false);
break;
case kActionShowDiff:
_graphics->readPict(action._messages[0], true);
break;
case kActionShowDiffLooping: // used in scene 44 (heart of the labyrinth, minotaur)
_graphics->readPict(action._messages[0], false);
break;
case kActionLoadDiff:
if (!action._messages[0].empty())
// Puts a file into memory
_graphics->loadPict(action._messages[0]);
break;
case kActionLoadBitmap:
error("Unused opcode kActionLoadBitmap has been called");
case kActionShowBitmap:
error("Unused opcode kActionShowBitmap has been called");
case kActionTransition:
_graphics->doTransition((TransitionType)action._param1, action._messages[0].c_str());
break;
case kActionNoUpdate:
_noUpdateDiff = true;
_anim->_doBlack = false;
break;
case kActionForceUpdate:
_curFileName = " ";
break;
case kActionShowCurPict: {
Common::String test = getPictName(true);
if (test != _curFileName) {
_curFileName = test;
_graphics->readPict(_curFileName);
}
}
break;
case kActionSetElement:
_conditions->inclElement(action._param1);
break;
case kActionUnsetElement:
_conditions->exclElement(action._param1);
break;
case kActionShowMessage:
if (_graphics->_longWinInFront)
_graphics->longDrawMessage(action._messages[0], true);
else
_graphics->drawMessage(action._messages[0], true);
break;
case kActionCShowMessage:
if (!_closeDataPtr)
_graphics->drawMessage(action._messages[0], true);
break;
case kActionShowMessages:
_graphics->drawMessage(action._messages[_utils->getRandom(action._param1)], true);
break;
case kActionChangeRoom:
if (action._param1 & 0x8000) {
// This is a Wyrmkeep Windows trial version, thus stop at this
// point, since we can't check for game payment status
_graphics->readPict(getPictName(true));
GUI::MessageDialog trialMessage(_("This is the end of the trial version. You can play the full game using the original interpreter from Wyrmkeep"));
trialMessage.runModal();
break;
}
_music->checkRoomMusic(_roomNum, action._param1);
_roomNum = action._param1;
_direction = action._param2 - 1;
_closeDataPtr = nullptr;
_anim->_doBlack = true;
break;
case kActionSetCloseup: {
Common::Point curPos = Common::Point(_utils->scaleX(action._param1), _utils->scaleY(action._param2));
const CloseData *tmpClosePtr = getObject(curPos, _closeDataPtr);
if (tmpClosePtr)
_closeDataPtr = tmpClosePtr;
}
break;
case kActionMainView:
_closeDataPtr = nullptr;
break;
case kActionSubInv:
if (_inventory[action._param1]._quantity)
(_inventory[action._param1]._quantity)--;
if (_inventory[action._param1]._quantity == 0)
_conditions->exclElement(action._param1);
break;
case kActionAddInv:
(_inventory[action._param1]._quantity) += action._param2;
_conditions->inclElement(action._param1);
break;
case kActionShowDir:
_graphics->setActionMessage(false);
break;
case kActionWaitSecs: {
uint32 targetMillis = _system->getMillis() + action._param1 * 1000;
_graphics->screenUpdate();
while (_system->getMillis() < targetMillis) {
updateEvents();
if (_quitLab || shouldQuit())
return;
_anim->diffNextFrame();
}
}
break;
case kActionStopMusic: // used in scene 44 (heart of the labyrinth, minotaur)
_music->freeMusic();
break;
case kActionStartMusic: // unused
error("Unused opcode kActionStartMusic has been called");
break;
case kActionChangeMusic: // used in scene 46 (museum exhibit, for the alarm)
_music->changeMusic(action._messages[0], true, false);
break;
case kActionResetMusic: // used in scene 45 (sheriff's office, after museum)
_music->resetMusic(true);
break;
case kActionFillMusic:
error("Unused opcode kActionFillMusic has been called");
break;
case kActionWaitSound: // used in scene 44 (heart of the labyrinth / ending)
while (_music->isSoundEffectActive()) {
updateEvents();
if (_quitLab || shouldQuit())
return;
_anim->diffNextFrame();
waitTOF();
}
break;
case kActionClearSound:
_music->stopSoundEffect();
break;
case kActionWinMusic: // used in scene 44 (heart of the labyrinth / ending)
_music->freeMusic();
_music->changeMusic("Music:WinGame", false, false);
break;
case kActionWinGame: // used in scene 44 (heart of the labyrinth / ending)
_quitLab = true;
showLab2Teaser();
break;
case kActionLostGame:
error("Unused opcode kActionLostGame has been called");
case kActionResetBuffer:
_graphics->freePict();
break;
case kActionSpecialCmd:
if (action._param1 == 0)
_anim->_doBlack = true;
else if (action._param1 == 1)
_anim->_doBlack = (_closeDataPtr == nullptr);
else if (action._param1 == 2)
_anim->_doBlack = (_closeDataPtr != nullptr);
else if (action._param1 == 5) {
// inverse the palette
for (int idx = (8 * 3); idx < (255 * 3); idx++)
_anim->_diffPalette[idx] = 255 - _anim->_diffPalette[idx];
waitTOF();
_graphics->setPalette(_anim->_diffPalette, 256);
waitTOF();
waitTOF();
} else if (action._param1 == 4) {
// white the palette
_graphics->whiteScreen();
waitTOF();
waitTOF();
} else if (action._param1 == 6) {
// Restore the palette
waitTOF();
_graphics->setPalette(_anim->_diffPalette, 256);
waitTOF();
waitTOF();
} else if (action._param1 == 7) {
// Quick pause
waitTOF();
waitTOF();
waitTOF();
}
break;
default:
break;
}
}
_music->stopSoundEffect();
}
bool LabEngine::doActionRuleSub(int16 action, int16 roomNum, const CloseData *closePtr, bool allowDefaults) {
action++;
if (closePtr) {
RuleList *rules = &(_rooms[_roomNum]._rules);
if (rules->empty() && (roomNum == 0)) {
_resource->readViews(roomNum);
rules = &(_rooms[roomNum]._rules);
}
for (auto &rule : *rules) {
if ((rule._ruleType == kRuleTypeAction) &&
((rule._param1 == action) || ((rule._param1 == 0) && allowDefaults))) {
if (((rule._param2 == closePtr->_closeUpType) ||
((rule._param2 == 0) && allowDefaults)) ||
((action == 1) && (rule._param2 == -closePtr->_closeUpType))) {
if (checkConditions(rule._condition)) {
doActions(rule._actionList);
return true;
}
}
}
}
}
return false;
}
bool LabEngine::doActionRule(Common::Point pos, int16 action, int16 roomNum) {
if (roomNum)
_newFileName = NOFILE;
else
_newFileName = _curFileName;
const CloseData *curClosePtr = getObject(pos, _closeDataPtr);
if (doActionRuleSub(action, roomNum, curClosePtr, false))
return true;
else if (doActionRuleSub(action, roomNum, _closeDataPtr, false))
return true;
else if (doActionRuleSub(action, roomNum, curClosePtr, true))
return true;
else if (doActionRuleSub(action, roomNum, _closeDataPtr, true))
return true;
return false;
}
bool LabEngine::doOperateRuleSub(int16 itemNum, int16 roomNum, const CloseData *closePtr, bool allowDefaults) {
if (closePtr)
if (closePtr->_closeUpType > 0) {
RuleList *rules = &(_rooms[roomNum]._rules);
if (rules->empty() && (roomNum == 0)) {
_resource->readViews(roomNum);
rules = &(_rooms[roomNum]._rules);
}
for (auto &rule : *rules) {
if ((rule._ruleType == kRuleTypeOperate) &&
((rule._param1 == itemNum) || ((rule._param1 == 0) && allowDefaults)) &&
((rule._param2 == closePtr->_closeUpType) || ((rule._param2 == 0) && allowDefaults))) {
if (checkConditions(rule._condition)) {
doActions(rule._actionList);
return true;
}
}
}
}
return false;
}
bool LabEngine::doOperateRule(Common::Point pos, int16 ItemNum) {
_newFileName = NOFILE;
const CloseData *closePtr = getObject(pos, _closeDataPtr);
if (doOperateRuleSub(ItemNum, _roomNum, closePtr, false))
return true;
else if (doOperateRuleSub(ItemNum, _roomNum, _closeDataPtr, false))
return true;
else if (doOperateRuleSub(ItemNum, _roomNum, closePtr, true))
return true;
else if (doOperateRuleSub(ItemNum, _roomNum, _closeDataPtr, true))
return true;
else {
_newFileName = _curFileName;
if (doOperateRuleSub(ItemNum, 0, closePtr, false))
return true;
else if (doOperateRuleSub(ItemNum, 0, _closeDataPtr, false))
return true;
else if (doOperateRuleSub(ItemNum, 0, closePtr, true))
return true;
else if (doOperateRuleSub(ItemNum, 0, _closeDataPtr, true))
return true;
}
return false;
}
bool LabEngine::doGoForward() {
RuleList &rules = _rooms[_roomNum]._rules;
for (auto &rule : rules) {
if ((rule._ruleType == kRuleTypeGoForward) && (rule._param1 == (_direction + 1))) {
if (checkConditions(rule._condition)) {
doActions(rule._actionList);
return true;
}
}
}
return false;
}
bool LabEngine::doTurn(uint16 from, uint16 to) {
from++;
to++;
RuleList &rules = _rooms[_roomNum]._rules;
for (auto &rule : rules) {
if ((rule._ruleType == kRuleTypeTurn) ||
((rule._ruleType == kRuleTypeTurnFromTo) &&
(rule._param1 == from) && (rule._param2 == to))) {
if (checkConditions(rule._condition)) {
doActions(rule._actionList);
return true;
}
}
}
return false;
}
bool LabEngine::doMainView() {
RuleList &rules = _rooms[_roomNum]._rules;
for (auto &rule : rules) {
if (rule._ruleType == kRuleTypeGoMainView) {
if (checkConditions(rule._condition)) {
doActions(rule._actionList);
return true;
}
}
}
return false;
}
} // End of namespace Lab

189
engines/lab/processroom.h Normal file
View File

@@ -0,0 +1,189 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#ifndef LAB_PROCESSROOM_H
#define LAB_PROCESSROOM_H
namespace Lab {
enum ActionType {
kActionPlaySound = 1,
kActionPlaySoundLooping = 2,
kActionShowDiff = 3,
kActionShowDiffLooping = 4,
kActionLoadDiff = 5,
kActionLoadBitmap = 6, // unused
kActionShowBitmap = 7, // unused
kActionTransition = 8,
kActionNoUpdate = 9,
kActionForceUpdate = 10,
kActionShowCurPict = 11,
kActionSetElement = 12,
kActionUnsetElement = 13,
kActionShowMessage = 14,
kActionShowMessages = 15,
kActionChangeRoom = 16,
kActionSetCloseup = 17,
kActionMainView = 18,
kActionSubInv = 19,
kActionAddInv = 20,
kActionShowDir = 21,
kActionWaitSecs = 22,
kActionStopMusic = 23,
kActionStartMusic = 24,
kActionChangeMusic = 25,
kActionResetMusic = 26,
kActionFillMusic = 27,
kActionWaitSound = 28,
kActionClearSound = 29,
kActionWinMusic = 30,
kActionWinGame = 31,
kActionLostGame = 32, // unused
kActionResetBuffer = 33,
kActionSpecialCmd = 34,
kActionCShowMessage = 35,
kActionPlaySoundNoWait = 36
};
enum RuleType {
kRuleTypeNone = 0,
kRuleTypeAction = 1,
kRuleTypeOperate = 2,
kRuleTypeGoForward = 3,
kRuleTypeConditions = 4, // unused?
kRuleTypeTurn = 5,
kRuleTypeGoMainView = 6,
kRuleTypeTurnFromTo = 7
};
enum RuleAction {
kRuleActionTake = 0,
kRuleActionMove = 1, // unused?
kRuleActionOpenDoor = 2, // unused?
kRuleActionCloseDoor = 3, // unused?
kRuleActionTakeDef = 4
};
enum Condition {
kCondBeltGlowing = 70,
kCondBridge1 = 104,
kCondNoNews = 135,
kCondBridge0 = 148,
kCondLampOn = 151,
kCondNoClean = 152,
kCondDirty = 175,
kCondUsedHelmet = 184
};
enum MapDoors {
kDoorLeftNorth = 1,
kDoorLeftEast = 2,
kDoorLeftSouth = 4,
kDoorLeftWest = 8,
kDoorMiddleNorth = 16,
kDoorRightNorth = 32,
kDoorMiddleSouth = 64,
kDoorRightSouth = 128,
kDoorMiddleEast = 16,
kDoorBottomEast = 32,
kDoorMiddleWest = 64,
kDoorBottomWest = 128
};
enum SpecialRoom {
kNormalRoom = 0,
kUpArrowRoom,
kDownArrowRoom,
kBridgeRoom,
kVerticalCorridor,
kHorizontalCorridor,
kMedMaze,
kHedgeMaze,
kSurMaze,
kMultiMazeF1,
kMultiMazeF2,
kMultiMazeF3
};
struct CloseData {
uint16 _x1, _y1, _x2, _y2;
int16 _closeUpType; // if > 0, an object. If < 0, an item
uint16 _depth; // Level of the closeup.
Common::String _graphicName;
Common::String _message;
CloseDataList _subCloseUps;
};
struct ViewData {
Common::Array<int16> _condition;
Common::String _graphicName;
CloseDataList _closeUps;
};
struct Action {
ActionType _actionType;
int16 _param1;
int16 _param2;
int16 _param3;
Common::Array<Common::String> _messages;
};
struct Rule {
RuleType _ruleType;
int16 _param1;
int16 _param2;
Common::Array<int16> _condition;
ActionList _actionList;
};
struct RoomData {
uint16 _doors[4];
byte _transitionType;
ViewDataList _view[4];
RuleList _rules;
Common::String _roomMsg;
};
struct InventoryData {
uint16 _quantity;
Common::String _name;
Common::String _bitmapName;
};
struct MapData {
uint16 _x, _y, _pageNumber;
SpecialRoom _specialID;
uint32 _mapFlags;
};
} // End of namespace Lab
#endif // LAB_PROCESSROOM_H

343
engines/lab/resource.cpp Normal file
View File

@@ -0,0 +1,343 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#include "common/file.h"
#include "lab/lab.h"
#include "lab/dispman.h"
#include "lab/music.h"
#include "lab/processroom.h"
#include "lab/resource.h"
namespace Lab {
Resource::Resource(LabEngine *vm) : _vm(vm) {
readStaticText();
}
void Resource::readStaticText() {
Common::File *labTextFile = openDataFile("Lab:Rooms/LabText");
for (int i = 0; i < 48; i++)
_staticText[i] = labTextFile->readLine();
delete labTextFile;
}
TextFont *Resource::getFont(const Common::String &fileName) {
// TODO: Add support for the font format of the Amiga version
Common::File *dataFile = openDataFile(fileName, MKTAG('V', 'G', 'A', 'F'));
uint32 headerSize = 4 + 2 + 256 * 3 + 4;
uint32 fileSize = dataFile->size();
if (fileSize <= headerSize)
return nullptr;
TextFont *textfont = new TextFont();
textfont->_dataLength = fileSize - headerSize;
textfont->_height = dataFile->readUint16LE();
dataFile->read(textfont->_widths, 256);
for (int i = 0; i < 256; i++)
textfont->_offsets[i] = dataFile->readUint16LE();
dataFile->skip(4);
textfont->_data = new byte[textfont->_dataLength + 4];
dataFile->read(textfont->_data, textfont->_dataLength);
delete dataFile;
return textfont;
}
Common::String Resource::getText(const Common::String &fileName) {
Common::File *dataFile = openDataFile(fileName);
uint32 count = dataFile->size();
byte *buffer = new byte[count];
byte *text = buffer;
dataFile->read(buffer, count);
while (text && (*text != '\0'))
*text++ -= (byte)95;
delete dataFile;
Common::String str = (char *)buffer;
delete[] buffer;
return str;
}
void Resource::readRoomData(const Common::String &fileName) {
Common::File *dataFile = openDataFile(fileName, MKTAG('D', 'O', 'R', '1'));
_vm->_manyRooms = dataFile->readUint16LE();
_vm->_highestCondition = dataFile->readUint16LE();
_vm->_rooms = new RoomData[_vm->_manyRooms + 1];
for (int i = 1; i <= _vm->_manyRooms; i++) {
RoomData *curRoom = &_vm->_rooms[i];
curRoom->_doors[kDirectionNorth] = dataFile->readUint16LE();
curRoom->_doors[kDirectionSouth] = dataFile->readUint16LE();
curRoom->_doors[kDirectionEast] = dataFile->readUint16LE();
curRoom->_doors[kDirectionWest] = dataFile->readUint16LE();
curRoom->_transitionType = dataFile->readByte();
}
delete dataFile;
}
InventoryData *Resource::readInventory(const Common::String &fileName) {
Common::File *dataFile = openDataFile(fileName, MKTAG('I', 'N', 'V', '1'));
_vm->_numInv = dataFile->readUint16LE();
InventoryData *inventory = new InventoryData[_vm->_numInv + 1];
for (int i = 1; i <= _vm->_numInv; i++) {
inventory[i]._quantity = dataFile->readUint16LE();
inventory[i]._name = readString(dataFile);
inventory[i]._bitmapName = readString(dataFile);
}
delete dataFile;
return inventory;
}
void Resource::readViews(uint16 roomNum) {
Common::String fileName = "LAB:Rooms/" + Common::String::format("%d", roomNum);
Common::File *dataFile = openDataFile(fileName, MKTAG('R', 'O', 'M', '4'));
RoomData *curRoom = &_vm->_rooms[roomNum];
curRoom->_roomMsg = readString(dataFile);
readView(dataFile, curRoom->_view[kDirectionNorth]);
readView(dataFile, curRoom->_view[kDirectionSouth]);
readView(dataFile, curRoom->_view[kDirectionEast]);
readView(dataFile, curRoom->_view[kDirectionWest]);
readRule(dataFile, curRoom->_rules);
delete dataFile;
}
Common::Path Resource::translateFileName(const Common::String &filename) {
Common::String upperFilename;
// The DOS and Windows version aren't looking for the right file,
if (!filename.compareToIgnoreCase("P:ZigInt/BLK") && (_vm->getPlatform() != Common::kPlatformAmiga))
upperFilename = "P:ZigInt/ZIGINT.BLK";
else
upperFilename = filename;
upperFilename.toUppercase();
Common::String fileNameStrFinal;
if (upperFilename.hasPrefix("P:") || upperFilename.hasPrefix("F:")) {
if (_vm->_isHiRes)
fileNameStrFinal = "SPICT/";
else
fileNameStrFinal = "PICT/";
if (_vm->getPlatform() == Common::kPlatformAmiga) {
if (upperFilename.hasPrefix("P:")) {
fileNameStrFinal = "PICT/";
} else {
fileNameStrFinal = "LABFONTS/";
upperFilename += "T"; // all the Amiga fonts have a ".FONT" suffix
}
}
} else if (upperFilename.hasPrefix("LAB:")) {
// Look inside the game folder
} else if (upperFilename.hasPrefix("MUSIC:")) {
fileNameStrFinal = "MUSIC/";
}
if (upperFilename.contains(':')) {
while (upperFilename[0] != ':') {
upperFilename.deleteChar(0);
}
upperFilename.deleteChar(0);
}
if (_vm->getPlatform() == Common::kPlatformDOS) {
// Some script of the DOS version uses names used in the Amiga (and Windows) version,
// which isn't limited to 8.3 characters. We need to parse upperFilename to detect
// the filename, and fix it if required so it matches a DOS filename.
while (upperFilename.contains('/') && upperFilename.size()) {
fileNameStrFinal += upperFilename[0];
upperFilename.deleteChar(0);
}
for (int i = 0; (i < 8) && upperFilename.size() && (upperFilename[0] != '.'); i++) {
fileNameStrFinal += upperFilename[0];
upperFilename.deleteChar(0);
}
// Remove the extra character in the filename
while (upperFilename.size() && (upperFilename[0] != '.'))
upperFilename.deleteChar(0);
// copy max 4 characters for the extension ('.foo')
for (int i = 0; (i < 4) && upperFilename.size(); i++) {
fileNameStrFinal += upperFilename[0];
upperFilename.deleteChar(0);
}
// Skip the extra characters of the extension
upperFilename.clear();
}
fileNameStrFinal += upperFilename;
return Common::Path(fileNameStrFinal);
}
Common::File *Resource::openDataFile(const Common::String &filename, uint32 fileHeader) {
Common::File *dataFile = new Common::File();
dataFile->open(translateFileName(filename));
if (!dataFile->isOpen()) {
// The DOS version is known to have some missing files
if (_vm->getPlatform() == Common::kPlatformDOS) {
warning("Incomplete DOS version, skipping file %s", filename.c_str());
delete dataFile;
return nullptr;
} else
error("openDataFile: Couldn't open %s (%s)", translateFileName(filename).toString().c_str(), filename.c_str());
}
if (fileHeader > 0) {
uint32 headerTag = dataFile->readUint32BE();
if (headerTag != fileHeader) {
dataFile->close();
error("openDataFile: Unexpected header in %s (%s) - expected: %d, got: %d", translateFileName(filename).toString().c_str(), filename.c_str(), fileHeader, headerTag);
}
}
return dataFile;
}
Common::String Resource::readString(Common::File *file) {
byte size = file->readByte();
if (!size)
return Common::String("");
char *str = new char[size];
for (int i = 0; i < size; i++) {
char c = file->readByte();
// Decrypt char
c = (i < size - 1) ? c - 95 : '\0';
str[i] = c;
}
Common::String result = str;
delete[] str;
return result;
}
Common::Array<int16> Resource::readConditions(Common::File *file) {
int16 cond;
Common::Array<int16> list;
while ((cond = file->readUint16LE()) != 0)
list.push_back(cond);
if (list.size() > 24) {
// The original only allocated 24 elements, and silently
// dropped remaining parts.
warning("More than 24 parts in condition");
}
return list;
}
void Resource::readRule(Common::File *file, RuleList &rules) {
rules.clear();
while (file->readByte() == 1) {
rules.push_back(Rule());
Rule &rule = rules.back();
rule._ruleType = (RuleType)file->readSint16LE();
rule._param1 = file->readSint16LE();
rule._param2 = file->readSint16LE();
rule._condition = readConditions(file);
readAction(file, rule._actionList);
}
}
void Resource::readAction(Common::File *file, ActionList &list) {
list.clear();
while (file->readByte() == 1) {
list.push_back(Action());
Action &action = list.back();
action._actionType = (ActionType)file->readSint16LE();
action._param1 = file->readSint16LE();
action._param2 = file->readSint16LE();
action._param3 = file->readSint16LE();
if (action._actionType == kActionShowMessages) {
action._messages.reserve(action._param1);
for (int i = 0; i < action._param1; i++)
action._messages.push_back(readString(file));
} else {
action._messages.push_back(readString(file));
}
}
}
void Resource::readCloseUps(uint16 depth, Common::File *file, CloseDataList &list) {
list.clear();
while (file->readByte() != '\0') {
list.push_back(CloseData());
CloseData &closeup = list.back();
closeup._x1 = file->readUint16LE();
closeup._y1 = file->readUint16LE();
closeup._x2 = file->readUint16LE();
closeup._y2 = file->readUint16LE();
closeup._closeUpType = file->readSint16LE();
closeup._depth = depth;
closeup._graphicName = readString(file);
closeup._message = readString(file);
readCloseUps(depth + 1, file, closeup._subCloseUps);
}
}
void Resource::readView(Common::File *file, ViewDataList &list) {
list.clear();
while (file->readByte() == 1) {
list.push_back(ViewData());
ViewData &view = list.back();
view._condition = readConditions(file);
view._graphicName = readString(file);
readCloseUps(0, file, view._closeUps);
}
}
} // End of namespace Lab

123
engines/lab/resource.h Normal file
View File

@@ -0,0 +1,123 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#ifndef LAB_RESOURCE_H
#define LAB_RESOURCE_H
namespace Lab {
struct ViewData;
enum StaticText {
kTextLowerFloor,
kTextMiddleFloor,
kTextUpperFloor,
kTextMedMazeFloor,
kTextHedgeMazeFloor,
kTextSurMazeFloor,
kTextCarnivalFloor,
kTextSurmazeMessage,
kTextFacingNorth,
kTextFacingEast,
kTextFacingSouth,
kTextFacingWest,
kTextkLampOn,
kTextTurnLeft,
kTextTurnRight,
kTextGoForward,
kTextNoPath,
kTextTakeItem,
kTextSave,
kTextLoad,
kTextBookmark,
kTextPersonal,
kTextDisk,
kTextSaveBook,
kTextRestoreBook,
kTextSaveFlash,
kTextRestoreFlash,
kTextSaveDisk,
kTextRestoreDisk,
kTextNoDiskInDrive,
kTextWriteProtected,
kTextSelectDisk,
kTextFormatFloppy,
kTextFormatting,
kTextNothing,
kTextUseOnWhat,
kTextTakeWhat,
kTextMoveWhat,
kTextOpenWhat,
kTextCloseWhat,
kTextLookWhat,
kTextUseMap,
kTextUseJournal,
kTextTurnkLampOn,
kTextTurnLampOff,
kTextUseWhiskey,
kTextUsePith,
kTextUseHelmet
};
class Resource {
public:
Resource(LabEngine *vm);
~Resource() {}
Common::File *openDataFile(const Common::String &filename, uint32 fileHeader = 0);
void readRoomData(const Common::String &fileName);
InventoryData *readInventory(const Common::String &fileName);
void readViews(uint16 roomNum);
TextFont *getFont(const Common::String &fileName);
Common::String getText(const Common::String &fileName);
Common::String getStaticText(byte index) const { return _staticText[index]; }
private:
LabEngine *_vm;
Common::String readString(Common::File *file);
Common::Array<int16> readConditions(Common::File *file);
void readRule(Common::File *file, RuleList &rules);
void readAction(Common::File *file, ActionList &action);
void readCloseUps(uint16 depth, Common::File *file, CloseDataList &close);
void readView(Common::File *file, ViewDataList &view);
void readStaticText();
Common::Path translateFileName(const Common::String &filename);
Common::String _staticText[48];
};
} // End of namespace Lab
#endif // LAB_RESOURCE_H

268
engines/lab/savegame.cpp Normal file
View File

@@ -0,0 +1,268 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#include "common/savefile.h"
#include "common/translation.h"
#include "gui/message.h"
#include "gui/saveload.h"
#include "graphics/thumbnail.h"
#include "engines/savestate.h"
#include "lab/lab.h"
#include "lab/dispman.h"
#include "lab/eventman.h"
#include "lab/labsets.h"
#include "lab/music.h"
#include "lab/processroom.h"
#include "lab/speciallocks.h"
namespace Lab {
#define SAVEGAME_ID MKTAG('L', 'O', 'T', 'S')
#define SAVEGAME_VERSION 1
void LabEngine::writeSaveGameHeader(Common::OutSaveFile *out, const Common::String &saveName) {
out->writeUint32BE(SAVEGAME_ID);
// Write version
out->writeByte(SAVEGAME_VERSION);
// Write savegame name
out->writeString(saveName);
out->writeByte(0);
// Save the game thumbnail
Graphics::saveThumbnail(*out);
// Creation date/time
TimeDate curTime;
_system->getTimeAndDate(curTime);
uint32 saveDate = ((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF);
uint16 saveTime = ((curTime.tm_hour & 0xFF) << 8) | ((curTime.tm_min) & 0xFF);
uint32 playTime = getTotalPlayTime() / 1000;
out->writeUint32BE(saveDate);
out->writeUint16BE(saveTime);
out->writeUint32BE(playTime);
}
WARN_UNUSED_RESULT bool readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &header, bool skipThumbnail) {
uint32 id = in->readUint32BE();
// Check if it's a valid ScummVM savegame
if (id != SAVEGAME_ID)
return false;
// Read in the version
header._version = in->readByte();
// Check that the save version isn't newer than this binary
if (header._version > SAVEGAME_VERSION)
return false;
// Read in the save name
Common::String saveName;
char ch;
while ((ch = (char)in->readByte()) != '\0')
saveName += ch;
header._descr.setDescription(saveName);
// Get the thumbnail
Graphics::Surface *thumbnail = nullptr;
if (!Graphics::loadThumbnail(*in, thumbnail, skipThumbnail)) {
return false;
}
header._descr.setThumbnail(thumbnail);
uint32 saveDate = in->readUint32BE();
uint16 saveTime = in->readUint16BE();
uint32 playTime = in->readUint32BE();
int day = (saveDate >> 24) & 0xFF;
int month = (saveDate >> 16) & 0xFF;
int year = saveDate & 0xFFFF;
header._descr.setSaveDate(year, month, day);
int hour = (saveTime >> 8) & 0xFF;
int minutes = saveTime & 0xFF;
header._descr.setSaveTime(hour, minutes);
header._descr.setPlayTime(playTime * 1000);
if (g_engine)
g_engine->setTotalPlayTime(playTime * 1000);
return true;
}
bool LabEngine::saveGame(int slot, const Common::String &desc) {
Common::String fileName = getSaveStateName(slot);
Common::SaveFileManager *saveFileManager = _system->getSavefileManager();
Common::OutSaveFile *file = saveFileManager->openForSaving(fileName);
if (!file)
return false;
// Load scene pic
_graphics->readPict(getPictName(false));
writeSaveGameHeader(file, desc);
file->writeUint16LE(_roomNum);
file->writeUint16LE(getDirection());
file->writeUint16LE(getQuarters());
// Conditions
for (int i = 0; i < _conditions->_lastElement / (8 * 2); i++)
file->writeUint16LE(_conditions->_array[i]);
// Rooms found
for (int i = 0; i < _roomsFound->_lastElement / (8 * 2); i++)
file->writeUint16LE(_roomsFound->_array[i]);
_specialLocks->save(file);
// Breadcrumbs
for (uint i = 0; i < MAX_CRUMBS; i++) {
file->writeUint16LE(_breadCrumbs[i]._crumbRoomNum);
file->writeUint16LE(_breadCrumbs[i]._crumbDirection);
}
file->flush();
file->finalize();
delete file;
_mainDisplay = true;
_alternate = false;
_event->simulateEvent();
_graphics->screenUpdate();
return true;
}
bool LabEngine::loadGame(int slot) {
Common::String fileName = getSaveStateName(slot);
Common::SaveFileManager *saveFileManager = _system->getSavefileManager();
Common::InSaveFile *file = saveFileManager->openForLoading(fileName);
if (!file)
return false;
SaveGameHeader header;
if (!readSaveGameHeader(file, header)) {
delete file;
return false;
}
_roomNum = file->readUint16LE();
_music->checkRoomMusic(1, _roomNum);
_direction = file->readUint16LE();
setQuarters(file->readUint16LE());
// Conditions
for (int i = 0; i < _conditions->_lastElement / (8 * 2); i++)
_conditions->_array[i] = file->readUint16LE();
// Rooms found
for (int i = 0; i < _roomsFound->_lastElement / (8 * 2); i++)
_roomsFound->_array[i] = file->readUint16LE();
_specialLocks->load(file);
// Breadcrumbs
for (int i = 0; i < MAX_CRUMBS; i++) {
_breadCrumbs[i]._crumbRoomNum = file->readUint16LE();
_breadCrumbs[i]._crumbDirection = file->readUint16LE();
}
_droppingCrumbs = (_breadCrumbs[0]._crumbRoomNum != 0);
_followingCrumbs = false;
for (int i = 0; i < MAX_CRUMBS; i++) {
if (_breadCrumbs[i]._crumbRoomNum == 0)
break;
_numCrumbs = i;
}
delete file;
_curFileName = " ";
_closeDataPtr = nullptr;
_followingCrumbs = false;
_graphics->_longWinInFront = false;
_event->initMouse();
_mainDisplay = true;
_alternate = false;
_event->simulateEvent();
_graphics->screenUpdate();
return true;
}
bool LabEngine::saveRestoreGame() {
bool isOK = false;
// The original had one screen for saving/loading. We have two.
// Ask the user which screen to use.
GUI::MessageDialog saveOrLoad(_("Would you like to save or restore a game?"), _("Save"), _("Restore"));
int choice = saveOrLoad.runModal();
if (choice == GUI::kMessageOK) {
// Save
GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
int slot = dialog->runModalWithCurrentTarget();
if (slot >= 0) {
Common::String desc = dialog->getResultString();
if (desc.empty()) {
// create our own description for the saved game, the user didn't enter it
desc = dialog->createDefaultSaveDescription(slot);
}
isOK = saveGame(slot, desc);
}
delete dialog;
} else {
// Restore
GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
int slot = dialog->runModalWithCurrentTarget();
if (slot >= 0) {
isOK = loadGame(slot);
}
delete dialog;
}
return isOK;
}
} // End of namespace Lab

476
engines/lab/special.cpp Normal file
View File

@@ -0,0 +1,476 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#include "common/file.h"
#include "lab/lab.h"
#include "lab/anim.h"
#include "lab/dispman.h"
#include "lab/eventman.h"
#include "lab/image.h"
#include "lab/interface.h"
#include "lab/labsets.h"
#include "lab/music.h"
#include "lab/processroom.h"
#include "lab/resource.h"
#include "lab/utils.h"
namespace Lab {
void LabEngine::doNotes() {
TextFont *noteFont = _resource->getFont("F:Note.fon");
Common::String noteText = _resource->getText("Lab:Rooms/Notes");
Common::Rect textRect = Common::Rect(_utils->vgaScaleX(25) + _utils->svgaCord(15), _utils->vgaScaleY(50), _utils->vgaScaleX(295) - _utils->svgaCord(15), _utils->vgaScaleY(148));
_graphics->flowText(noteFont, -2 + _utils->svgaCord(1), 0, 0, false, false, true, true, textRect, noteText.c_str());
_graphics->setPalette(_anim->_diffPalette, 256);
_graphics->freeFont(&noteFont);
}
void LabEngine::doWestPaper() {
TextFont *paperFont = _resource->getFont("F:News22.fon");
Common::String paperText = _resource->getText("Lab:Rooms/Date");
Common::Rect textRect = Common::Rect(_utils->vgaScaleX(57), _utils->vgaScaleY(77) + _utils->svgaCord(2), _utils->vgaScaleX(262), _utils->vgaScaleY(91));
_graphics->flowText(paperFont, 0, 0, 0, false, true, false, true, textRect, paperText.c_str());
_graphics->freeFont(&paperFont);
paperFont = _resource->getFont("F:News32.fon");
paperText = _resource->getText("Lab:Rooms/Headline");
int fileLen = paperText.size() - 1;
textRect = Common::Rect(_utils->vgaScaleX(57), _utils->vgaScaleY(86) - _utils->svgaCord(2), _utils->vgaScaleX(262), _utils->vgaScaleY(118));
int charsPrinted = _graphics->flowText(paperFont, -8, 0, 0, false, true, false, true, textRect, paperText.c_str());
uint16 y;
if (charsPrinted < fileLen) {
y = 130 - _utils->svgaCord(5);
textRect = Common::Rect(_utils->vgaScaleX(57), _utils->vgaScaleY(86) - _utils->svgaCord(2), _utils->vgaScaleX(262), _utils->vgaScaleY(132));
_graphics->flowText(paperFont, -8 - _utils->svgaCord(1), 0, 0, false, true, false, true, textRect, paperText.c_str());
} else
y = 115 - _utils->svgaCord(5);
_graphics->freeFont(&paperFont);
paperFont = _resource->getFont("F:Note.fon");
paperText = _resource->getText("Lab:Rooms/Col1");
_graphics->flowText(paperFont, -4, 0, 0, false, false, false, true, _utils->vgaRectScale(45, y, 158, 148), paperText.c_str());
paperText = _resource->getText("Lab:Rooms/Col2");
_graphics->flowText(paperFont, -4, 0, 0, false, false, false, true, _utils->vgaRectScale(162, y, 275, 148), paperText.c_str());
_graphics->freeFont(&paperFont);
_graphics->setPalette(_anim->_diffPalette, 256);
}
void LabEngine::loadJournalData() {
if (_journalFont)
_graphics->freeFont(&_journalFont);
_journalFont = _resource->getFont("F:Journal.fon");
updateEvents();
Common::String filename = "Lab:Rooms/j";
bool bridge = _conditions->in(kCondBridge0) || _conditions->in(kCondBridge1);
bool dirty = _conditions->in(kCondDirty);
bool news = !_conditions->in(kCondNoNews);
bool clean = !_conditions->in(kCondNoClean);
if (bridge && clean && news)
filename += '8';
else if (clean && news)
filename += '9';
else if (bridge && clean)
filename += '6';
else if (clean)
filename += '7';
else if (bridge && dirty && news)
filename += '4';
else if (dirty && news)
filename += '5';
else if (bridge && dirty)
filename += '2';
else if (dirty)
filename += '3';
else if (bridge)
filename += '1';
else
filename += '0';
_journalText = _resource->getText(filename);
_journalTextTitle = _resource->getText("Lab:Rooms/jt");
Common::File *journalFile = _resource->openDataFile("P:JImage");
_journalButtonList.push_back(_interface->createButton( 80, _utils->vgaScaleY(162) + _utils->svgaCord(1), 0, kActionJournalBack, new Image(journalFile, this), new Image(journalFile, this))); // back
_journalButtonList.push_back(_interface->createButton(194, _utils->vgaScaleY(162) + _utils->svgaCord(1), 2, kActionJournalForward, new Image(journalFile, this), new Image(journalFile, this))); // forward
_journalButtonList.push_back(_interface->createButton(144, _utils->vgaScaleY(164) - _utils->svgaCord(1), 1, kActionJournalExit, new Image(journalFile, this), new Image(journalFile, this))); // cancel
delete journalFile;
_anim->_noPalChange = true;
_journalBackImage->setData(new byte[_graphics->_screenBytesPerPage]);
_graphics->readPict("P:Journal.pic", true, false, _journalBackImage->_imageData);
_anim->_noPalChange = false;
// Keep a copy of the blank journal
_blankJournal = new byte[_graphics->_screenBytesPerPage];
memcpy(_blankJournal, _journalBackImage->_imageData, _graphics->_screenBytesPerPage);
}
void LabEngine::drawJournalText() {
uint16 drawingToPage = 1;
const char *curText = _journalText.c_str();
assert((_journalPage & 1) == 0);
while (drawingToPage < _journalPage) {
updateEvents();
// flowText without output
curText += _graphics->flowText(_journalFont, -2, 2, 0, false, false, false, false, _utils->vgaRectScale(52, 32, 152, 148), curText);
_lastPage = (*curText == 0);
if (_lastPage) {
// Reset _journalPage to this page, in case it was set too high
_journalPage = (drawingToPage / 2) * 2;
break;
}
drawingToPage++;
}
if (_journalPage == 0) {
// draw title page centered
_graphics->flowText(_journalFont, -2, 2, 0, false, true, true, true, _utils->vgaRectScale(52, 32, 152, 148), _journalTextTitle.c_str(), _journalBackImage);
} else {
curText += _graphics->flowText(_journalFont, -2, 2, 0, false, false, false, true, _utils->vgaRectScale(52, 32, 152, 148), curText, _journalBackImage);
}
updateEvents();
curText += _graphics->flowText(_journalFont, -2, 2, 0, false, false, false, true, _utils->vgaRectScale(171, 32, 271, 148), curText, _journalBackImage);
_lastPage = (*curText == 0);
}
void LabEngine::turnPage(bool fromLeft) {
if (fromLeft) {
for (int i = 0; i < _graphics->_screenWidth; i += 8) {
updateEvents();
waitTOF();
_journalBackImage->blitBitmap(i, 0, nullptr, i, 0, 8, _graphics->_screenHeight, false);
}
} else {
for (int i = (_graphics->_screenWidth - 8); i > 0; i -= 8) {
updateEvents();
waitTOF();
_journalBackImage->blitBitmap(i, 0, nullptr, i, 0, 8, _graphics->_screenHeight, false);
}
}
}
void LabEngine::drawJournal(uint16 wipenum, bool needFade) {
_event->mouseHide();
updateEvents();
drawJournalText();
_graphics->loadBackPict("P:Journal.pic", _highPalette);
if (wipenum == 0)
_journalBackImage->blitBitmap(0, 0, nullptr, 0, 0, _graphics->_screenWidth, _graphics->_screenHeight, false);
else
turnPage((wipenum == 1));
_interface->toggleButton(_interface->getButton(0), 15, (_journalPage > 0)); // back button
_interface->toggleButton(_interface->getButton(2), 15, (!_lastPage)); // forward button
if (needFade)
_graphics->fade(true);
// Reset the journal background, so that all the text that has been blitted on it is erased
memcpy(_journalBackImage->_imageData, _blankJournal, _graphics->_screenBytesPerPage);
eatMessages();
_event->mouseShow();
}
void LabEngine::processJournal() {
while (1) {
IntuiMessage *msg = _event->getMsg();
if (shouldQuit()) {
_quitLab = true;
return;
}
updateEvents();
_graphics->screenUpdate();
_system->delayMillis(10);
if (!msg)
continue;
MessageClass msgClass = msg->_msgClass;
if ((msgClass == kMessageRightClick) ||
((msgClass == kMessageAction) && (msg->_code == kActionExit)))
return;
else if (msgClass == kMessageButtonUp) {
uint16 buttonId = msg->_code;
if (buttonId == 0) {
if (_journalPage >= 2) {
_journalPage -= 2;
drawJournal(1, false);
}
} else if (buttonId == 1) {
return;
} else if (buttonId == 2) {
if (!_lastPage) {
_journalPage += 2;
drawJournal(2, false);
}
}
}
} // while
}
void LabEngine::doJournal() {
_graphics->blackAllScreen();
_lastPage = false;
_journalBackImage->_width = _graphics->_screenWidth;
_journalBackImage->_height = _graphics->_screenHeight;
_journalBackImage->setData(nullptr, true);
updateEvents();
loadJournalData();
_interface->attachButtonList(&_journalButtonList);
drawJournal(0, true);
_event->mouseShow();
processJournal();
_interface->attachButtonList(nullptr);
_graphics->fade(false);
_event->mouseHide();
delete[] _blankJournal;
_blankJournal = nullptr;
_journalBackImage->setData(nullptr, true);
_interface->freeButtonList(&_journalButtonList);
_graphics->freeFont(&_journalFont);
_graphics->rectFill(0, 0, _graphics->_screenWidth - 1, _graphics->_screenHeight - 1, 0);
_graphics->blackScreen();
}
void LabEngine::drawMonText(const char *text, TextFont *monitorFont, Common::Rect textRect, bool isinteractive) {
uint16 drawingToPage = 0, yspacing = 0;
_event->mouseHide();
if (*text == '%') {
text++;
uint16 numlines = (*text - '0') * 10;
text++;
numlines += (*text - '0');
text += 2;
uint16 fheight = _graphics->textHeight(monitorFont);
textRect.left = _monitorButton->_width + _utils->vgaScaleX(3);
_monitorButtonHeight = _monitorButton->_height + _utils->vgaScaleY(3);
if (_monitorButtonHeight > fheight)
yspacing = _monitorButtonHeight - fheight;
else
_monitorButtonHeight = fheight;
_graphics->rectFill(0, 0, _graphics->_screenWidth - 1, textRect.bottom, 0);
for (int i = 0; i < numlines; i++)
_monitorButton->drawImage(0, i * _monitorButtonHeight);
} else if (isinteractive) {
_graphics->rectFill(0, 0, _graphics->_screenWidth - 1, textRect.bottom, 0);
} else {
_graphics->rectFill(textRect, 0);
}
const char *curText = text;
while (drawingToPage < _monitorPage) {
updateEvents();
curText += _graphics->flowText(monitorFont, yspacing, 0, 0, false, false, false, false, textRect, curText);
_lastPage = (*curText == 0);
if (_lastPage)
_monitorPage = drawingToPage;
else
drawingToPage++;
}
curText += _graphics->flowText(monitorFont, yspacing, 2, 0, false, false, false, true, textRect, curText);
_lastPage = (*curText == 0);
_event->mouseShow();
}
void LabEngine::processMonitor(const Common::String &ntext, TextFont *monitorFont, bool isInteractive, Common::Rect textRect) {
Common::String startFileName = _monitorTextFilename;
const CloseData *startClosePtr = _closeDataPtr, *lastClosePtr[10];
uint16 depth = 0;
Common::String text = ntext;
lastClosePtr[0] = _closeDataPtr;
while (1) {
if (isInteractive) {
if (!_closeDataPtr)
_closeDataPtr = startClosePtr;
Common::String filename;
if (_closeDataPtr == startClosePtr)
filename = startFileName;
else
filename = _closeDataPtr->_graphicName;
if (filename != _monitorTextFilename) {
_monitorPage = 0;
_monitorTextFilename = filename;
text = _resource->getText(_monitorTextFilename);
_graphics->fade(false);
drawMonText(text.c_str(), monitorFont, textRect, isInteractive);
_graphics->fade(true);
}
}
IntuiMessage *msg = _event->getMsg();
if (shouldQuit()) {
_quitLab = true;
return;
}
updateEvents();
_graphics->screenUpdate();
_system->delayMillis(10);
if (!msg)
continue;
MessageClass msgClass = msg->_msgClass;
if ((msgClass == kMessageRightClick) ||
((msgClass == kMessageAction) && (msg->_code == kActionExit)))
return;
if (msgClass == kMessageLeftClick) {
int16 mouseX = msg->_mouse.x;
int16 mouseY = msg->_mouse.y;
// Check if mouse was in button bar
if ((mouseY >= _utils->vgaScaleY(171)) && (mouseY <= _utils->vgaScaleY(200))) {
if (mouseX <= _utils->vgaScaleX(31)) {
// Exit button
return;
}
if (mouseX <= _utils->vgaScaleX(59)) {
// Back button
if (isInteractive) {
_monitorPage = 0;
if (depth) {
depth--;
_closeDataPtr = lastClosePtr[depth];
}
} else if (_monitorPage > 0) {
_monitorPage = 0;
drawMonText(text.c_str(), monitorFont, textRect, isInteractive);
}
} else if (mouseX < _utils->vgaScaleX(259)) {
// empty region; ignore
} else if (mouseX <= _utils->vgaScaleX(289)) {
// Page down button
if (!_lastPage) {
_monitorPage += 1;
drawMonText(text.c_str(), monitorFont, textRect, isInteractive);
}
} else if (_monitorPage >= 1) {
// Page up button
_monitorPage -= 1;
drawMonText(text.c_str(), monitorFont, textRect, isInteractive);
}
} else if (isInteractive) {
const CloseData *tmpClosePtr = _closeDataPtr;
mouseY = 64 + (mouseY / _monitorButtonHeight) * 42;
mouseX = 101;
setCurrentClose(Common::Point(mouseX, mouseY), &_closeDataPtr, false);
if (tmpClosePtr != _closeDataPtr) {
lastClosePtr[depth] = tmpClosePtr;
depth++;
}
}
}
} // while
}
void LabEngine::doMonitor(const Common::String &background, const Common::String &textfile, bool isinteractive, Common::Rect textRect) {
Common::Rect scaledRect = _utils->vgaRectScale(textRect.left, textRect.top, textRect.right, textRect.bottom);
_monitorTextFilename = textfile;
_graphics->blackAllScreen();
_graphics->readPict("P:Mon/Monitor.1");
_graphics->readPict("P:Mon/NWD1");
_graphics->readPict("P:Mon/NWD2");
_graphics->readPict("P:Mon/NWD3");
_graphics->blackAllScreen();
_monitorPage = 0;
_lastPage = false;
_graphics->_fadePalette = _highPalette;
TextFont *monitorFont = _resource->getFont("F:Map.fon");
Common::File *buttonFile = _resource->openDataFile("P:MonImage");
_monitorButton = new Image(buttonFile, this);
delete buttonFile;
Common::String ntext = _resource->getText(textfile);
_graphics->loadBackPict(background, _highPalette);
drawMonText(ntext.c_str(), monitorFont, scaledRect, isinteractive);
_event->mouseShow();
_graphics->fade(true);
processMonitor(ntext, monitorFont, isinteractive, scaledRect);
_graphics->fade(false);
_event->mouseHide();
_graphics->freeFont(&monitorFont);
_graphics->rectFill(0, 0, _graphics->_screenWidth - 1, _graphics->_screenHeight - 1, 0);
_graphics->blackAllScreen();
_graphics->freePict();
}
} // End of namespace Lab

View File

@@ -0,0 +1,395 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#include "common/file.h"
#include "common/translation.h"
#include "gui/message.h"
#include "lab/lab.h"
#include "lab/anim.h"
#include "lab/dispman.h"
#include "lab/image.h"
#include "lab/labsets.h"
#include "lab/resource.h"
#include "lab/speciallocks.h"
#include "lab/utils.h"
namespace Lab {
#define BRICKOPEN 115
#define COMBINATIONUNLOCKED 130
enum TileScroll {
kScrollLeft = 1,
kScrollRight = 2,
kScrollUp = 3,
kScrollDown = 4
};
const uint16 INIT_TILE[4][4] = {
{ 1, 5, 9, 13 },
{ 2, 6, 10, 14 },
{ 3, 7, 11, 15 },
{ 4, 8, 12, 0 }
};
const uint16 SOLUTION[4][4] = {
{ 7, 1, 8, 3 },
{ 2, 11, 15, 4 },
{ 9, 5, 14, 6 },
{ 10, 13, 12, 0 }
};
const int COMBINATION_X[6] = { 45, 83, 129, 166, 211, 248 };
SpecialLocks::SpecialLocks(LabEngine *vm) : _vm(vm) {
for (int i = 0; i < 16; i++)
_tiles[i] = nullptr;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++)
_curTile[i][j] = INIT_TILE[i][j];
}
for (int i = 0; i < 6; i++)
_combination[i] = 0;
for (int i = 0; i < 10; i++)
_numberImages[i] = nullptr;
}
SpecialLocks::~SpecialLocks() {
for (int i = 0; i < 16; i++)
delete _tiles[i];
for (int imgIdx = 0; imgIdx < 10; imgIdx++) {
delete _numberImages[imgIdx];
_numberImages[imgIdx] = nullptr;
}
}
void SpecialLocks::tileClick(Common::Point pos) {
Common::Point realPos = _vm->_utils->vgaUnscale(pos);
if ((realPos.x < 101) || (realPos.y < 26))
return;
int tileX = (realPos.x - 101) / 30;
int tileY = (realPos.y - 26) / 25;
if ((tileX < 4) && (tileY < 4))
changeTile(tileX, tileY);
}
void SpecialLocks::changeTile(uint16 col, uint16 row) {
int16 scrolltype = -1;
if (row > 0) {
if (_curTile[col][row - 1] == 0) {
_curTile[col][row - 1] = _curTile[col][row];
_curTile[col][row] = 0;
scrolltype = kScrollDown;
}
}
if (col > 0) {
if (_curTile[col - 1][row] == 0) {
_curTile[col - 1][row] = _curTile[col][row];
_curTile[col][row] = 0;
scrolltype = kScrollRight;
}
}
if (row < 3) {
if (_curTile[col][row + 1] == 0) {
_curTile[col][row + 1] = _curTile[col][row];
_curTile[col][row] = 0;
scrolltype = kScrollUp;
}
}
if (col < 3) {
if (_curTile[col + 1][row] == 0) {
_curTile[col + 1][row] = _curTile[col][row];
_curTile[col][row] = 0;
scrolltype = kScrollLeft;
}
}
if (scrolltype != -1) {
if (_vm->getFeatures() & GF_WINDOWS_TRIAL) {
GUI::MessageDialog trialMessage(_("This puzzle is not available in the trial version of the game"));
trialMessage.runModal();
return;
}
doTileScroll(col, row, scrolltype);
bool check = true;
row = 0;
col = 0;
while (row < 4) {
while (col < 4) {
check &= (_curTile[row][col] == SOLUTION[row][col]);
col++;
}
row++;
col = 0;
}
if (check) {
// unlocked combination
_vm->_conditions->inclElement(BRICKOPEN);
_vm->_anim->_doBlack = true;
_vm->_graphics->readPict("p:Up/BDOpen");
}
}
}
void SpecialLocks::combinationClick(Common::Point pos) {
Common::Point realPos = _vm->_utils->vgaUnscale(pos);
if (!Common::Rect(44, 63, 285, 99).contains(realPos))
return;
uint16 number = 0;
if (realPos.x < 83)
number = 0;
else if (realPos.x < 127)
number = 1;
else if (realPos.x < 165)
number = 2;
else if (realPos.x < 210)
number = 3;
else if (realPos.x < 245)
number = 4;
else if (realPos.x < 286)
number = 5;
changeCombination(number);
}
void SpecialLocks::doTile(bool showsolution) {
uint16 row = 0, col = 0, rowm, colm, num;
int16 rows, cols;
if (showsolution) {
rowm = _vm->_utils->vgaScaleY(23);
colm = _vm->_utils->vgaScaleX(27);
rows = _vm->_utils->vgaScaleY(31);
cols = _vm->_utils->vgaScaleX(105);
} else {
_vm->_graphics->rectFillScaled(97, 22, 220, 126, 0);
rowm = _vm->_utils->vgaScaleY(25);
colm = _vm->_utils->vgaScaleX(30);
rows = _vm->_utils->vgaScaleY(25);
cols = _vm->_utils->vgaScaleX(100);
}
while (row < 4) {
while (col < 4) {
if (showsolution)
num = SOLUTION[col][row];
else
num = _curTile[col][row];
if (showsolution || num)
_tiles[num]->drawImage(cols + (col * colm), rows + (row * rowm));
col++;
}
row++;
col = 0;
}
}
void SpecialLocks::showTileLock(const Common::String &filename, bool showSolution) {
_vm->_anim->_doBlack = true;
_vm->_anim->_noPalChange = true;
_vm->_graphics->readPict(filename);
_vm->_anim->_noPalChange = false;
_vm->_graphics->blackScreen();
Common::File *tileFile;
if (_vm->getPlatform() == Common::kPlatformDOS)
tileFile = _vm->_resource->openDataFile(showSolution ? "P:TileSolu" : "P:Tile");
else
// Windows and Amiga versions use TileSolution and Tile
tileFile = _vm->_resource->openDataFile(showSolution ? "P:TileSolution" : "P:Tile");
int start = showSolution ? 0 : 1;
for (int curBit = start; curBit < 16; curBit++)
_tiles[curBit] = new Image(tileFile, _vm);
delete tileFile;
doTile(showSolution);
_vm->_graphics->setPalette(_vm->_anim->_diffPalette, 256);
}
void SpecialLocks::doTileScroll(uint16 col, uint16 row, uint16 scrolltype) {
int16 dX = 0, dY = 0, dx = 0, dy = 0, sx = 0, sy = 0;
int last = 0;
if (scrolltype == kScrollLeft) {
dX = _vm->_utils->vgaScaleX(5);
sx = _vm->_utils->vgaScaleX(5);
last = 6;
} else if (scrolltype == kScrollRight) {
dX = _vm->_utils->vgaScaleX(-5);
dx = _vm->_utils->vgaScaleX(-5);
sx = _vm->_utils->vgaScaleX(5);
last = 6;
} else if (scrolltype == kScrollUp) {
dY = _vm->_utils->vgaScaleY(5);
sy = _vm->_utils->vgaScaleY(5);
last = 5;
} else if (scrolltype == kScrollDown) {
dY = _vm->_utils->vgaScaleY(-5);
dy = _vm->_utils->vgaScaleY(-5);
sy = _vm->_utils->vgaScaleY(5);
last = 5;
}
sx += _vm->_utils->svgaCord(2);
uint16 x1 = _vm->_utils->vgaScaleX(100) + (col * _vm->_utils->vgaScaleX(30)) + dx;
uint16 y1 = _vm->_utils->vgaScaleY(25) + (row * _vm->_utils->vgaScaleY(25)) + dy;
byte *buffer = new byte[_tiles[1]->_width * _tiles[1]->_height * 2];
for (int i = 0; i < last; i++) {
_vm->waitTOF();
scrollRaster(dX, dY, x1, y1, x1 + _vm->_utils->vgaScaleX(28) + sx, y1 + _vm->_utils->vgaScaleY(23) + sy, buffer);
x1 += dX;
y1 += dY;
}
delete[] buffer;
}
void SpecialLocks::scrollRaster(int16 dx, int16 dy, uint16 x1, uint16 y1, uint16 x2, uint16 y2, byte *buffer) {
if (dx)
_vm->_graphics->scrollDisplayX(dx, x1, y1, x2, y2, buffer);
if (dy)
_vm->_graphics->scrollDisplayY(dy, x1, y1, x2, y2, buffer);
}
void SpecialLocks::changeCombination(uint16 number) {
const int solution[6] = { 0, 4, 0, 8, 7, 2 };
Image display(_vm);
if (_combination[number] < 9)
(_combination[number])++;
else
_combination[number] = 0;
uint16 combnum = _combination[number];
display.setData(_vm->_graphics->getCurrentDrawingBuffer(), false);
display._width = _vm->_graphics->_screenWidth;
display._height = _vm->_graphics->_screenHeight;
byte *buffer = new byte[_numberImages[1]->_width * _numberImages[1]->_height * 2];
for (int i = 1; i <= (_numberImages[combnum]->_height / 2); i++) {
if (i & 1)
_vm->waitTOF();
display.setData(_vm->_graphics->getCurrentDrawingBuffer(), false);
_vm->_graphics->scrollDisplayY(2, _vm->_utils->vgaScaleX(COMBINATION_X[number]), _vm->_utils->vgaScaleY(65), _vm->_utils->vgaScaleX(COMBINATION_X[number]) + (_numberImages[combnum])->_width - 1, _vm->_utils->vgaScaleY(65) + (_numberImages[combnum])->_height, buffer);
_numberImages[combnum]->blitBitmap(0, (_numberImages[combnum])->_height - (2 * i), &(display), _vm->_utils->vgaScaleX(COMBINATION_X[number]), _vm->_utils->vgaScaleY(65), (_numberImages[combnum])->_width, 2, false);
}
delete[] buffer;
bool unlocked = true;
for (int i = 0; i < 6; i++)
unlocked &= (_combination[i] == solution[i]);
if (unlocked)
_vm->_conditions->inclElement(COMBINATIONUNLOCKED);
else
_vm->_conditions->exclElement(COMBINATIONUNLOCKED);
}
void SpecialLocks::showCombinationLock(const Common::String &filename) {
_vm->_anim->_doBlack = true;
_vm->_anim->_noPalChange = true;
_vm->_graphics->readPict(filename);
_vm->_anim->_noPalChange = false;
_vm->_graphics->blackScreen();
Common::File *numFile = _vm->_resource->openDataFile("P:Numbers");
for (int i = 0; i < 10; i++) {
_numberImages[i] = new Image(numFile, _vm);
}
delete numFile;
for (int i = 0; i <= 5; i++)
_numberImages[_combination[i]]->drawImage(_vm->_utils->vgaScaleX(COMBINATION_X[i]), _vm->_utils->vgaScaleY(65));
_vm->_graphics->setPalette(_vm->_anim->_diffPalette, 256);
}
void SpecialLocks::save(Common::OutSaveFile *file) {
// Combination lock
for (int i = 0; i < 6; i++)
file->writeByte(_combination[i]);
// Tiles
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
file->writeUint16LE(_curTile[i][j]);
}
void SpecialLocks::load(Common::InSaveFile *file) {
// Combination lock
for (int i = 0; i < 6; i++)
_combination[i] = file->readByte();
// Tiles
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
_curTile[i][j] = file->readUint16LE();
}
} // End of namespace Lab

View File

@@ -0,0 +1,93 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#ifndef LAB_TILEPUZZLE_H
#define LAB_TILEPUZZLE_H
#include "common/savefile.h"
namespace Lab {
class LabEngine;
class SpecialLocks {
private:
LabEngine *_vm;
Image *_tiles[16];
Image *_numberImages[10];
uint16 _curTile[4][4];
byte _combination[6];
public:
SpecialLocks(LabEngine *vm);
~SpecialLocks();
void showTileLock(const Common::String &filename, bool showSolution);
/**
* Processes mouse clicks and changes tile positions.
*/
void tileClick(Common::Point pos);
void showCombinationLock(const Common::String &filename);
/**
* Processes mouse clicks and changes the door combination.
*/
void combinationClick(Common::Point pos);
void save(Common::OutSaveFile *file);
void load(Common::InSaveFile *file);
private:
/**
* Changes the combination number of one of the slots
*/
void changeCombination(uint16 number);
/**
* Changes the tile positions in the tile puzzle
*/
void changeTile(uint16 col, uint16 row);
/**
* Draws the images of the combination lock to the display bitmap.
*/
void doTile(bool showsolution);
/**
* Does the scrolling for the tiles on the tile puzzle.
*/
void doTileScroll(uint16 col, uint16 row, uint16 scrolltype);
void scrollRaster(int16 dx, int16 dy, uint16 x1, uint16 y1, uint16 x2, uint16 y2, byte *buffer);
};
} // End of namespace Lab
#endif // LAB_TILEPUZZLE_H

271
engines/lab/utils.cpp Normal file
View File

@@ -0,0 +1,271 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#include "common/file.h"
#include "lab/lab.h"
#include "lab/utils.h"
namespace Lab {
Utils::Utils(LabEngine *vm) : _vm(vm), _rnd("lab") {
_dataBytesPerRow = 0;
}
uint16 Utils::scaleX(uint16 x) {
if (_vm->_isHiRes)
return (uint16)((x * 16) / 9);
else
return (uint16)((x * 8) / 9);
}
uint16 Utils::scaleY(uint16 y) {
if (_vm->_isHiRes)
return (y + (y / 14));
else
return ((y * 10) / 24);
}
Common::Rect Utils::rectScale(int16 x1, int16 y1, int16 x2, int16 y2) {
return Common::Rect(scaleX(x1), scaleY(y1), scaleX(x2), scaleY(y2));
}
uint16 Utils::mapScaleX(uint16 x) {
if (_vm->_isHiRes)
return (x - 45);
else
return ((x - 45) >> 1);
}
uint16 Utils::mapScaleY(uint16 y) {
if (_vm->_isHiRes)
return y;
else
return ((y - 35) >> 1) - (y >> 6);
}
Common::Rect Utils::mapRectScale(int16 x1, int16 y1, int16 x2, int16 y2) {
return Common::Rect(mapScaleX(x1), mapScaleY(y1), mapScaleX(x2), mapScaleY(y2));
}
int16 Utils::vgaScaleX(int16 x) {
if (_vm->_isHiRes)
return (x * 2);
else
return x;
}
int16 Utils::vgaScaleY(int16 y) {
if (_vm->_isHiRes)
return ((y * 12) / 5);
else
return y;
}
Common::Rect Utils::vgaRectScale(int16 x1, int16 y1, int16 x2, int16 y2) {
return Common::Rect(vgaScaleX(x1), vgaScaleY(y1), vgaScaleX(x2), vgaScaleY(y2));
}
uint16 Utils::svgaCord(uint16 cord) {
if (_vm->_isHiRes)
return cord;
else
return 0;
}
Common::Point Utils::vgaUnscale(Common::Point pos) {
Common::Point result;
if (_vm->_isHiRes) {
result.x = pos.x / 2;
result.y = (pos.y * 5) / 12;
} else
result = pos;
return result;
}
template<typename T>
void Utils::unDiff(T *dest, Common::File *sourceFile) {
byte bytesPerWord = sizeof(T);
while (1) {
uint16 skip = sourceFile->readByte();
uint16 copy = sourceFile->readByte();
if (skip == 255) {
if (copy == 0) {
skip = sourceFile->readUint16LE();
copy = sourceFile->readUint16LE();
} else if (copy == 255)
return;
}
dest += skip;
if (bytesPerWord == 1) {
sourceFile->read(dest, copy);
dest += copy;
} else {
while (copy) {
*dest = sourceFile->readUint16LE();
dest++;
copy--;
}
}
}
}
template<typename T>
void Utils::verticalUnDiff(T *dest, Common::File *sourceFile, uint16 bytesPerRow) {
uint16 counter = 0;
byte bytesPerWord = sizeof(T);
uint16 wordsPerRow = bytesPerRow / bytesPerWord;
while (counter < wordsPerRow) {
T *curPtr = dest + counter;
for (;;) {
uint16 skip = sourceFile->readByte();
uint16 copy = sourceFile->readByte();
if (skip == 255) {
counter += copy;
break;
} else {
curPtr += (skip * wordsPerRow);
while (copy) {
if (bytesPerWord == 1)
*curPtr = sourceFile->readByte();
else if (bytesPerWord == 2)
*curPtr = sourceFile->readUint16LE();
else if (bytesPerWord == 4)
*curPtr = sourceFile->readUint32LE();
else
error("verticalUnDiff: Invalid bytesPerWord (%d)", bytesPerWord);
curPtr += wordsPerRow;
copy--;
}
}
}
}
}
void Utils::runLengthDecode(byte *dest, Common::File *sourceFile) {
int8 num;
int16 count;
while (1) {
num = sourceFile->readSByte();
if (num == 127) {
return;
} else if (num > '\0') {
sourceFile->read(dest, num);
dest += num;
} else {
count = (int16)(-num);
num = sourceFile->readSByte();
while (count) {
*dest = num;
dest++;
count--;
}
}
}
}
void Utils::verticalRunLengthDecode(byte *dest, Common::File *sourceFile, uint16 bytesPerRow) {
int16 count;
byte *top = dest;
for (int i = 0; i < _dataBytesPerRow; i++) {
dest = top;
dest += i;
int8 num = sourceFile->readSByte();
while (num != 127) {
if (num > '\0') {
while (num) {
*dest = sourceFile->readByte();
dest += bytesPerRow;
num--;
}
} else {
count = (int16)(-num);
num = sourceFile->readSByte();
while (count) {
*dest = num;
dest += bytesPerRow;
count--;
}
}
num = sourceFile->readSByte();
}
}
}
void Utils::unDiff(byte *newBuf, byte *oldBuf, Common::File *sourceFile, uint16 bytesPerRow, bool isVertical) {
sourceFile->skip(1);
byte bufType = sourceFile->readByte();
if (isVertical) {
if (bufType == 0)
verticalUnDiff<byte>(newBuf, sourceFile, bytesPerRow);
else if (bufType == 1)
verticalUnDiff<uint16>((uint16 *)newBuf, sourceFile, bytesPerRow);
else if (bufType == 3)
verticalUnDiff<uint32>((uint32 *)newBuf, sourceFile, bytesPerRow);
else
error("Unexpected variable compression scheme %d", bufType);
} else {
if (bufType == 0)
unDiff<byte>(newBuf, sourceFile);
else if (bufType == 1)
unDiff<uint16>((uint16 *)newBuf, sourceFile);
else
error("Unexpected compression scheme %d", bufType);
}
}
void Utils::setBytesPerRow(int num) {
_dataBytesPerRow = num;
}
uint16 Utils::getRandom(uint16 max) {
if (max > 1)
return _rnd.getRandomNumber(max - 1);
else
return 0;
}
} // End of namespace Lab

104
engines/lab/utils.h Normal file
View File

@@ -0,0 +1,104 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This code is based on Labyrinth of Time code with assistance of
*
* Copyright (c) 1993 Terra Nova Development
* Copyright (c) 2004 The Wyrmkeep Entertainment Co.
*
*/
#ifndef LAB_UTILS_H
#define LAB_UTILS_H
namespace Lab {
class Utils {
private:
LabEngine *_vm;
uint16 _dataBytesPerRow;
/**
* Undiffs a piece of memory based on the header size.
*/
template<typename T>
void unDiff(T *dest, Common::File *sourceFile);
/**
* Undiffs a piece of memory when header size is a byte, and copy/skip size
* is a byte or a word or a double word.
*/
template<typename T>
void verticalUnDiff(T *dest, Common::File *sourceFile, uint16 bytesPerRow);
public:
Utils(LabEngine *vm);
Common::RandomSource _rnd;
/**
* Scales the x co-ordinates to that of the new display. In the room parser
* file, co-ordinates are set up on a 360x336 display.
*/
uint16 scaleX(uint16 x);
/**
* Scales the y co-ordinates to that of the new display. In the room parser
* file, co-ordinates are set up on a 368x336 display.
*/
uint16 scaleY(uint16 y);
Common::Rect rectScale(int16 x1, int16 y1, int16 x2, int16 y2);
/**
* Scales the VGA x coords to SVGA if necessary; otherwise, returns VGA coords.
*/
int16 vgaScaleX(int16 x);
/**
* Scales the VGA y coords to SVGA if necessary; otherwise, returns VGA coords.
*/
int16 vgaScaleY(int16 y);
Common::Rect vgaRectScale(int16 x1, int16 y1, int16 x2, int16 y2);
uint16 svgaCord(uint16 cord);
uint16 mapScaleX(uint16 x);
uint16 mapScaleY(uint16 y);
Common::Rect mapRectScale(int16 x1, int16 y1, int16 x2, int16 y2);
/**
* Converts SVGA coords to VGA if necessary, otherwise returns VGA coords.
*/
Common::Point vgaUnscale(Common::Point pos);
/**
* Does the undiffing between the bitmaps.
*/
void unDiff(byte *newBuf, byte *oldBuf, Common::File *sourceFile, uint16 bytesPerRow, bool isVertical);
void runLengthDecode(byte *dest, Common::File *sourceFile);
void verticalRunLengthDecode(byte *dest, Common::File *sourceFile, uint16 bytesPerRow);
void setBytesPerRow(int num);
uint16 getRandom(uint16 max);
};
} // End of namespace Lab
#endif // LAB_UTILS_H